Skip to content

volatile semantics for embedded #1664

@ManDeJan

Description

@ManDeJan

Hello, we discussed this shortly on the live stream yesterday, but here is a more detailed issue so we can write everything down.

Right now if you have a struct that represents a special function register (SFR) on an embedded chip and you have a pointer with the type of that struct pointing to a location in memory. You need to make that pointer volatile in order to guarentee the SFR is written to.
Often the SFR's are write only memory (or read only) and because the pointer is marked volatile, the SFR is accessed via the pointer, the generated assembly first reads the write only memory, than orrs that with the value the value you want to write before writing the value. This is inefficient and undefined behavior on many chips.

Here is an example of inefficient (arm thumb) assembly generated:
code:

const SFR = packed struct {
    _reserved0_27: u28,
    field:          u4,
};

export fn initSomeSFR() void {
    var sfr1 = @intToPtr(*volatile SFR, 0xdeadbeef);
    sfr1.field= 0b0110;
}

assembly:

initSomeSFR:
        movs    r0, #15      -- Load bitmask
        lsls    r0, r0, #28  -- 0b11110000000000000000000000000000 
        ldr     r1, .LCPI0_0 -- Load address to write to
        ldr     r2, [r1]     -- Read address value
        bics    r2, r0       -- bit clear with the mask (and)
        movs    r0, #3       -- load value to write
        lsls    r0, r0, #29  -- 0b01100000000000000000000000000000 
        adds    r0, r2, r0   -- add it with the cleared register value (orr)
        str     r0, [r1]     -- store it
        bx      lr           -- return
.LCPI0_0:
        .long   3735928559

this is what optimal assembly would look like:

initSomeSFR:
        movs    r0, #3
        lsls    r0, r0, #29
        ldr     r1, .LCPI0_0
        str     r0, [r1]
        bx      lr
.LCPI0_0:
        .long   3735928559

We discussed some ways of solving this problem yesterday which where:
Solving it as has been done by this C++ library. That uses template meta programming to merge and reorder writes to registers. I think this is very possible to do with the metaprogramming that zig supports

Another option would be to expand volatile semantics and add something along the lines of volatiler volatilew that specify the read or write-onlyness of the memory. This would still not allow the compiler to reorder or merge accesses to a register, so if you would want that functionality you would need to write some compiletime library yourself anyway.

There are some other questions that remain.

  • What is the use of volatiler?, Are there cases where there are write instructions generated if you mark a read only register as normal volatile?
  • Are there cases where writing 0's to a field not accessed in a write only register is the wrong thing to do?

Metadata

Metadata

Assignees

No one assigned

    Labels

    use caseDescribes a real use case that is difficult or impossible, but does not propose a solution.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions