ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
35.18k stars 2.56k forks source link

volatile semantics for embedded #1664

Open ManDeJan opened 6 years ago

ManDeJan commented 6 years ago

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.

andrewrk commented 6 years ago

Thank you for writing this up 👍

justinbalexander commented 5 years ago

I think this behavior is exactly what ManDeJan asked for. The compiler has no idea that the reserved field should be written as zeros, and can't assume that. He asked for a single field to be modified and that is exactly what it did.

The behavior would be the same in C as well.

Here are some commented examples in godbolt. https://zig.godbolt.org/z/WqFNHB

I work in embedded, but mostly my projects have been using the MSP430. In that architecture, the supplied header files do not define memory mapped registers using structs, probably for this reason. Also, I highly disagree that the compiler should be able to reorder accesses to memory through volatile pointers. That is kind of the point of volatile. I do think that the ability to have read only or write only pointers is a good idea. The compilers for embedded architectures always have to include their own intrinsics to enforce that behavior.

andrewrk commented 5 years ago

I highly disagree that the compiler should be able to reorder accesses to memory through volatile pointers. That is kind of the point of volatile.

It's not able to do this now, and it won't be able to do it in the future, because as you say, that is the point.