From a8621731ec9726288614c8c1b88ae8293ebbf606 Mon Sep 17 00:00:00 2001
From: "kj4tmp@gmail.com"
@@ -2247,8 +2254,7 @@ or {#code|test_packed_structs.zig#}
- The backing integer is inferred from the fields' total bit width. - Optionally, it can be explicitly provided and enforced at compile time: + The backing integer can be inferred or explicitly provided. When inferred, it will be unsigned. When explicitly provided, its bit width will be enforced at compile time to exactly match the total bit width of the fields:
{#code|test_missized_packed_struct.zig#} @@ -2295,12 +2301,11 @@ or {#code|test_packed_struct_equality.zig#}- Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future. - For details on this subscribe to - this issue. - TODO update these docs with a recommendation on how to use packed structs with MMIO - (the use case for volatile packed structs) once this issue is resolved. - Don't worry, there will be a good solution for this use case in zig. + Packed structs can be used to interact with memory-mapped input-output (MMIO), which is + common in embedded applications. A pointer of the correct alignment and address to a packed struct + can be constructed to faciltiate manipulation of bit-packed registers without arduous bitshifting. + + {#code|packed_struct_mmio.zig#}
{#header_close#} diff --git a/doc/langref/packed_struct_mmio.zig b/doc/langref/packed_struct_mmio.zig new file mode 100644 index 0000000000..79236a76ea --- /dev/null +++ b/doc/langref/packed_struct_mmio.zig @@ -0,0 +1,16 @@ +pub const GPIORegister = packed struct(u8) { + GPIO0: bool, + GPIO1: bool, + GPIO2: bool, + GPIO3: bool, + _reserved: u4 = 0, +}; + +/// Write a new state to the memory-mapped IO. +pub fn writeToGPIO(new_states: GPIORegister) void { + const gpio_register_address = 0x0123; + const raw_ptr: *align(1) volatile GPIORegister = @ptrFromInt(gpio_register_address); + raw_ptr.* = new_states; +} + +// syntax From bd38c417fc9ba367b3cfcd49d29b52412ef04251 Mon Sep 17 00:00:00 2001 From: Andrew Kelley- Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout: + {#syntax#}packed{#endsyntax#} structs, like {#syntax#}enum{#endsyntax#}, are based on the concept + of interpreting integers differently. All packed structs have a backing integer, + which is implicitly determined by the total bit count of fields, or explicitly specified. + Packed structs have well-defined memory layout - exactly the same ABI as their backing integer. +
++ Each field of a packed struct is interpreted as a logical sequence of bits, arranged from + least to most significant. Allowed field types:
This means that a {#syntax#}packed struct{#endsyntax#} can participate @@ -2252,9 +2250,11 @@ or This even works at {#link|comptime#}:
{#code|test_packed_structs.zig#} -- The backing integer can be inferred or explicitly provided. When inferred, it will be unsigned. When explicitly provided, its bit width will be enforced at compile time to exactly match the total bit width of the fields: + The backing integer can be inferred or explicitly provided. When + inferred, it will be unsigned. When explicitly provided, its bit width + will be enforced at compile time to exactly match the total bit width of + the fields:
{#code|test_missized_packed_struct.zig#} @@ -2296,17 +2296,18 @@ orEquating packed structs results in a comparison of the backing integer, - and only works for the `==` and `!=` operators. + and only works for the {#syntax#}=={#endsyntax#} and {#syntax#}!={#endsyntax#} {#link|Operators#}.
{#code|test_packed_struct_equality.zig#}- Packed structs can be used to interact with memory-mapped input-output (MMIO), which is - common in embedded applications. A pointer of the correct alignment and address to a packed struct - can be constructed to faciltiate manipulation of bit-packed registers without arduous bitshifting. - - {#code|packed_struct_mmio.zig#} + Field access and assignment can be understood as shorthand for bitshifts + on the backing integer. These operations are not {#link|atomic|Atomics#}, + so beware using field access syntax when combined with memory-mapped + input-output (MMIO). Instead of field access on {#link|volatile#} {#link|Pointers#}, + construct a fully-formed new value first, then write that value to the volatile pointer.
+ {#code|packed_struct_mmio.zig#} {#header_close#} {#header_open|Struct Naming#} diff --git a/doc/langref/packed_struct_mmio.zig b/doc/langref/packed_struct_mmio.zig index 79236a76ea..18b57f83b2 100644 --- a/doc/langref/packed_struct_mmio.zig +++ b/doc/langref/packed_struct_mmio.zig @@ -1,16 +1,19 @@ -pub const GPIORegister = packed struct(u8) { +pub const GpioRegister = packed struct(u8) { GPIO0: bool, GPIO1: bool, GPIO2: bool, GPIO3: bool, - _reserved: u4 = 0, + reserved: u4 = 0, }; -/// Write a new state to the memory-mapped IO. -pub fn writeToGPIO(new_states: GPIORegister) void { - const gpio_register_address = 0x0123; - const raw_ptr: *align(1) volatile GPIORegister = @ptrFromInt(gpio_register_address); - raw_ptr.* = new_states; +const gpio: *volatile GpioRegister = @ptrFromInt(0x0123); + +pub fn writeToGpio(new_states: GpioRegister) void { + // Example of what not to do: + // BAD! gpio.GPIO0 = true; BAD! + + // Instead, do this: + gpio.* = new_states; } // syntax