# MMIO access restriction using meta programming

**URL:** <https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171>\
**Category:** Explain\
**Tags:** embedded, type-system\
**Created:** [January 26, 2025, 4:06pm UTC](https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171 "2025-01-26T16:06:44Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![lufe](https://ziggit.dev/letter_avatar_proxy/v4/letter/l/b782af/32.png) [@lufe](https://ziggit.dev/u/lufe)\
**Post date:** [January 26, 2025, 4:06pm UTC](https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171/1 "2025-01-26T16:06:44Z")

</div>

Hi,

having an embedded background and trying to learn meta programming in zig, I came up with an idea on how to restrict access to individual bit fields in memory mapped IO registers. On the surface it looks like this:

```zig
// simple nonsense example for the structure of a memory mapped io register
const TimerCtrl = packed struct(u32) {
    read_only: Field(u8, .read),
    threshold: Field(u16, .readwrite),
    enabled: Field(u1, .readwrite),
    _reserved: Field(u2, .reserved),
    timer_mode: Field(
        enum(u2) {
            one_pulse = 0,
            pwm = 1,
        },
        .readwrite,
    ),
    _reserved2: Field(u3, .reserved),
};

export fn main(timer_ctrl: * volatile Register(TimerCtrl) ) void {

    // the following line causes a compile error, ensuring the
    // read_only bit field is not modified
    timer_ctrl.modify(.{.read_only = 0});

    // configuring some or all of the timer bit fields in a single call
    timer_ctrl.modify(.{.timer_mode=.pwm, .threshold=127});
    
    // Enable the timer in a second modification.
    timer_ctrl.modify(.{.enabled = 1});

    _ = timer_ctrl.read();

}

```

The full code is here: [Compiler Explorer](https://godbolt.org/z/KGsehnMqq)

On one hand I am amazed, how readable the code is -at least on the surface- and the safety guards you can provide using meta programming.

On the other hand I am worried that creating all these types slowes down complilation massively, once you have a device with hundreds or thousends of MMIO registers. Also, I am not sure if this is the intended usage for meta programming… it feels a bit messy.

I have several questions:

1. A side question: When I did not inline `.modify(...)` the generated binary becomes quite large, especially when optimized for ReleaseSmall. Why is inlining necessary/important here?
2. Does anyone have experience on how much creating huge amounts of types slowes down compilation?
3. Is this a “valid” or intended use case for zig’s meta prrogramming? Is it too messy?
4. Is there a better way of achieving all of this?

Many thanks for your help!

---

<div class="post-metadata">

**Author:** ![andy\_mango](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/96bed5/32.png) [@andy\_mango](https://ziggit.dev/u/andy_mango)\
**Post date:** [January 26, 2025, 5:18pm UTC](https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171/2 "2025-01-26T17:18:59Z")

</div>

If you are interested in another take on this area, this is the [code](https://repos.modelrealization.com/cgi-bin/fossil/bottom-up/file?name=projects/bottom_up/tangled/src/mmio.zig&ci=tip) I use for MMIO. It is based on ideas taken from [here](https://www.scattered-thoughts.net/writing/mmio-in-zig/). It works well for my use case. I have not noticed any significant compiler slowness. For comptime known field names, the generated code is as good as I could write by hand. The meta programming does what you might expect – disappears after compilation.

---

<div class="post-metadata">

**Author:** ![lufe](https://ziggit.dev/letter_avatar_proxy/v4/letter/l/b782af/32.png) [@lufe](https://ziggit.dev/u/lufe)\
**Post date:** [January 26, 2025, 5:20pm UTC](https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171/3 "2025-01-26T17:20:53Z")

</div>

Thank you, I will have a look!

---

<div class="post-metadata">

**Author:** ![lufe](https://ziggit.dev/letter_avatar_proxy/v4/letter/l/b782af/32.png) [@lufe](https://ziggit.dev/u/lufe)\
**Post date:** [January 26, 2025, 9:35pm UTC](https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171/4 "2025-01-26T21:35:27Z")

</div>

Very interesting to see the similarities and differences!

What I like about my approach, is that all information is combined in a single struct.  
Another advantage may be, that it is more easy to add new restrictions. While this probably makes no sense with MMIO, it might be usefull when using this scheme to apply some rule set to something else.  
Also, in theory a language server would be able to suggest possible arguments for `reg.modify(...)` etc, because it does not use `anytype` for those. However, in practice ZLS is not able to do so anyway.

So the only real plus, that I can see for my approach at the moment, is “all information in a single struct”. For everything else, the approach in the article achieves similar results with more simple code. So I am not sure, if it is worth all the meta magic.

Also interesting is, that the author mentions issues with unnecessary bit shifts and loads from constant sections. I had the same issues before inlining the `modify` function. Probably the same happens in your case. So maybe it is worth checking, if `inline` saves you a few instructions for each update/write call.

Last but not least, I probably should have had a look at `std.meta` when learning meta programming 😃 Somehow I managed to completely miss that.

Thank you again!

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [January 27, 2025, 5:10am UTC](https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171/5 "2025-01-27T05:10:11Z")

</div>

> [@lufe](#):
>
> in theory a language server would be able to suggest possible arguments for `reg.modify(...)` etc, because it does not use `anytype` for those. However, in practice ZLS is not able to do so anyway

zig comptime execution is rather complex, as it emulates the target your compiling to, zls can reason about simple comptime code

its not going to get much better becuase its currently planned for compiler to implement a protocal for communicating with tools, meaning zls could just ask the compiler about things that would otherwise require reimplementing the compiler.

---

<div class="post-metadata">

**Author:** ![Xion](https://ziggit.dev/user_avatar/ziggit.dev/xion/32/4383_2.png) [@Xion](https://ziggit.dev/u/Xion)\
**Post date:** [January 27, 2025, 6:27pm UTC](https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171/6 "2025-01-27T18:27:28Z")

</div>

> [@lufe](#):
>
> Also, in theory a language server would be able to suggest possible arguments for `reg.modify(...)` etc, because it does not use `anytype` for those. However, in practice ZLS is not able to do so anyway.

The method described in the article can actually dispense with `anytype` there as well. All that’s necessary is introducing a `Partial` type constructor that takes a struct and produces a new version with all its fields turned into optionals (`T` → `?T`). `modify` can then take `Partial(Write)` as argument and only set those bits which aren’t null.

With that, I’m not sure how much of a difference there is in terms of the amount of metaprogramming magic between these two solutions.

---

<div class="post-metadata">

**Author:** ![lufe](https://ziggit.dev/letter_avatar_proxy/v4/letter/l/b782af/32.png) [@lufe](https://ziggit.dev/u/lufe)\
**Post date:** [January 27, 2025, 9:03pm UTC](https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171/7 "2025-01-27T21:03:09Z")

</div>

True! If I understand you correctly, that would basically be the same approach that I used. So the meta programming would get very similar.

---

<div class="post-metadata">

**Author:** ![haydenridd](https://ziggit.dev/user_avatar/ziggit.dev/haydenridd/32/2375_2.png) [@haydenridd](https://ziggit.dev/u/haydenridd)\
**Post date:** [January 27, 2025, 11:10pm UTC](https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171/8 "2025-01-27T23:10:48Z")

</div>

You should check out the way we do it in the [MicroZig](https://github.com/ZigEmbeddedGroup/microzig) framework:

> <https://github.com/ZigEmbeddedGroup/microzig/blob/main/core/src/mmio.zig>

A lot of very similar concepts as discussed in this thread. All methods are marked `inline` to ensure that the generated code boils down to a simple address write, read, or read + modify + write. We use another tool in that repo `regz` to generate a `chip.zig` file containing the actual definitions for all the chip’s registers at compile time.

---

<div class="post-metadata">

**Author:** ![lufe](https://ziggit.dev/letter_avatar_proxy/v4/letter/l/b782af/32.png) [@lufe](https://ziggit.dev/u/lufe)\
**Post date:** [January 27, 2025, 11:49pm UTC](https://ziggit.dev/t/mmio-access-restriction-using-meta-programming/8171/9 "2025-01-27T23:49:27Z")

</div>

Actually I had a look at MicroZig some time ago. But not knowing zig too well, I was a bit overwhelmed by the size of the project and never dared looking at it again since then. So, many thanks you for pointing me to the right direction. I will definitely check it out, now that I understand a bit more.
