From ce4b265db02d896993558ad443f60ba216266d7e Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Wed, 5 Aug 2026 22:54:22 -0400 Subject: [PATCH 1/6] Add ARCHITECTURE.md --- ARCHITECTURE.md | 197 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 ARCHITECTURE.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 000000000..6137f37a0 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,197 @@ +# Architecture + +This document gives a high-level overview of MicroZig's codebase. If you want to contribute or understand how things fit +together, start here. + +MicroZig is a Zig framework for programming microcontrollers. It provides a unified build system, hardware abstraction +layers, and device drivers across 14+ microcontroller families (ARM Cortex-M, RISC-V, AVR, MSP430). The core idea: write +portable embedded code in Zig, with platform details handled by ports and a code-generating build system. + +## Codemap + +``` +microzig/ +├── build.zig # MicroBuild entry point (see "Build System" below) +├── build-internals/ # Target/Chip/HAL/Board type system, build-time tools +├── core/ # Foundational library: CPU, MMIO, interrupts, startup +├── port/ # Platform-specific HALs and chip definitions +├── drivers/ # HAL-agnostic device drivers +├── tools/ # Standalone CLI tools (regz, uf2, dfu, etc.) +├── modules/ # Shared libraries (libc, lwIP, FreeRTOS, RTT, etc.) +├── examples/ # Per-platform example applications +├── sim/ # AVR simulator (aviron) +├── vendor/ # Vendored third-party code +├── website/ # Documentation site +├── design/ # Logos and design assets +└── scripts/ # Utility scripts +``` + +### _core/_ + +The foundational library that every firmware links against. Key files in `core/src/`: + +- _microzig.zig_: Root API. Exposes `cpu`, `chip`, `hal`, `board`, `drivers` namespaces. Implements the default panic + handler, `export_startup()` for entry point generation, and `std_options()` for logging integration. +- _mmio.zig_: `Mmio` generic type that wraps a packed struct into a volatile MMIO register interface with `read()`, + `write()`, `modify()`, `toggle()` methods. Validates register sizes at comptime. +- _interrupt.zig_: `enable()`, `disable()`, critical sections, `Mutex` (HAL-provided or fallback + `CriticalSectionMutex`), `Handler` union type. +- _allocator.zig_: Multi-tier free-list allocator. +- _cpus/_: CPU architecture implementations: + - _cortex_m.zig_ (+ subdirectory with variants): interrupt vector tables, exception handling, FPU support, RAM + vector tables. + - _riscv32.zig_: RISC-V 32-bit support. + - _avr_common.zig_, _avr25.zig_, _avr5.zig_: AVR architecture. + - _msp430.zig_, _msp430x.zig_: TI MSP430. +- _usb.zig_: USB device stack with CDC and HID class drivers. + +### _port/_ + +Each port implements support for a microcontroller family. This includes creating a Microzig target for various chips +(and some well-known boards) as well as implementing a HAL to support peripherals. Ports follow a consistent structure +(using `port/raspberrypi/rp2xxx/` as the reference): + +``` +port/// +├── build.zig # Chip/board/target definitions, memory regions, SVD paths +├── src/ +│ ├── hal.zig # HAL root: re-exports gpio, uart, i2c, spi, dma, etc. +│ ├── hal/ # Individual HAL modules (one file per peripheral) +│ ├── boards/ # Board configs (pin mappings, oscillator freq, LED pin) +│ └── cpus/ # CPU variants if the family has multiple cores +├── patches/ # Corrections to vendor SVD/ATDF files +└── ld/ # Custom linker scripts (when auto-generation isn't enough) +``` + +The _build.zig_ in each port defines `Chip` values (SVD path, memory regions, patches) and `Board` values (root source +file with pin configs). These feed into the `Target` type from _build-internals/_. + +Current ports: ESP32 (_espressif/esp_), GD32 (_gigadevice/gd32_), ATmega/ATtiny/SAMD51 (_microchip/_), nRF5x +(_nordic/nrf5x_), LPC/MCX (_nxp/_), RP2040/RP2350 (_raspberrypi/rp2xxx_), STM32 (_stmicro/stm32_), MSP430/MSPM0/TM4C +(_texasinstruments/_), CH32V (_wch/ch32v_). + +### _drivers/_ + +Device-independent drivers that talk to hardware through base interface abstractions. Located in `drivers/src/`: + +- _base/_: Interface types using a pointer+vtable pattern: + - _Digital_IO.zig_: GPIO pin abstraction (direction, bias, read/write). + - _I2C_Device.zig_: I2C bus communication. + - _StreamDevice.zig_: Byte-stream I/O (UART-like). + - _DatagramDevice.zig_: Packet-based I/O. + - _BlockMemory.zig_: Block storage. + - _ClockDevice.zig_: Real-time clock. +- _time/_: `Absolute` (microseconds since boot), `Duration`, `Deadline`. +- Device drivers organized by category: _display/_ (SSD1306, ST77xx, HD44780), _input/_ (debounced buttons, rotary + encoders, keyboard matrices), _sensor/_ (temperature, motion, magnetic), _stepper/_ (A4988, DRV8825), _led/_ (WS2812), + _wireless/_ (CYW43 WiFi/BT), _io_expander/_ (PCF8574, PCA9685). + +The vtable approach lets drivers work with any HAL that implements the base interfaces. Drivers never import +port-specific code. + +### _build-internals/_ + +The type system that ties everything together at build time. The key types in `build-internals/build.zig`: + +- `Target`: A complete firmware compilation target. Bundles a `zig_target` query, `Chip`, optional + `HardwareAbstractionLayer`, optional `Board`, `LinkerScript`, `Stack` placement, and preferred `BinaryFormat`. + Supports `derive()` for creating variants. +- `Chip`: MCU definition: name, `register_definition` (SVD, ATDF, Zig, Embassy, or TargetDB source), + `memory_regions`, and `patch_files`. +- `HardwareAbstractionLayer`: Root source file + imports for the HAL. +- `Board`: Root source file + imports for board-specific config. +- `MemoryRegion`: Describes a memory region (flash/RAM) with offset, length, and access permissions. +- `BinaryFormat`: Output format: `.elf`, `.binary`, `.hex`, `.uf2`, `.dfu`, `.esp`, or `.custom`. +- `LinkerScript`: Auto-generated or custom, controls section placement and rodata location (flash vs RAM). + +This directory also contains the build-time tools that run during compilation: _regz/_ (register code generation), +_uf2/_, _dfu/_, _esp-image/_ (format converters), _linter/_, _sorcerer/_ (register visualizer), _printer/_, +_generate_linker_script.zig_. + +### _tools/_ + +Standalone versions of the build-time tools, usable outside the MicroZig build system. `regz` is the most important: +it parses vendor hardware description files (SVD, ATDF, Embassy, TI TargetDB) and generates type-safe Zig MMIO register +interfaces. `sorcerer` produces SVG visualizations of register maps. + +### _modules/_ + +Shared Zig libraries: _foundation-libc/_ (minimal libc), _freertos/_ (RTOS integration), _lwip/_ (TCP/IP stack), _rtt/_ +(Segger Real-Time Transfer for debug output), _network/_ (networking abstractions), _virtual-io/_, _bounded-array/_, +_riscv32-common/_. + +### _examples/_ + +One directory per platform, mirroring the _port/_ structure: _examples/raspberrypi/rp2xxx/_, _examples/nordic/nrf5x/_, +etc. Each has its own _build.zig_ that uses `MicroBuild` to define firmware targets. Examples range from _blinky.zig_ to +WiFi stacks and FreeRTOS integration. + +### _sim/_ + +_sim/aviron/_ is an AVR instruction-level simulator for testing AVR firmware without hardware. + +## Data Flow: SVD to `blink` + +The path from a vendor hardware description to running firmware: + +1. **Vendor provides SVD/ATDF** describing chip registers and memory layout. +2. **Port's _build.zig_** references the SVD and defines `Chip` (memory regions, patches) and `Target` values. +3. **At build time**, `regz` processes the SVD (applying patches) and generates a Zig module with type-safe MMIO + register definitions. +4. **_generate_linker_script.zig_** emits a linker script from the `Target`'s memory regions. +5. **MicroBuild's `add_firmware()`** wires everything: it creates Zig modules for `microzig` (core), `chip` (generated + registers), `cpu`, `hal`, `board`, and `drivers`, then compiles the user's `root_source_file` against them. +6. **Startup** (_core/src/cpus/_): the CPU module provides the entry point, initializes the stack, zeroes BSS, copies + data from flash to RAM, then calls `microzig_main` (the user's entry point). +7. **User code** calls into `hal` (e.g., `hal.gpio.init()`) which uses the generated `chip` registers to configure + hardware. Drivers use the `hal` through base interfaces. +8. **Output conversion** transforms the ELF into the target format (UF2, HEX, DFU, etc.) for flashing. + +## Build System + +The build system centers on `MicroBuild`, defined in the root `build.zig`. + +```zig +const MicroBuild = microzig.MicroBuild(.{ .rp2xxx = true }); +const mb = MicroBuild.init(b, mz_dep) orelse return; + +const fw = mb.add_firmware(.{ + .name = "blinky", + .root_source_file = b.path("src/blinky.zig"), + .target = mb.ports.rp2xxx.boards.raspberrypi.pico, +}); +mb.install_firmware(fw, .{}); +``` + +`add_firmware()` invokes `regz` on the chip's register definition, generates the linker script, creates all the Zig +modules, and sets up the compilation. `install_firmware()` adds the format conversion step (e.g. to _uf2_ or _bin_) and +copies the result to _zig-out/firmware/_. + +## Architectural Invariants + +- **Drivers never import port-specific code.** They operate exclusively through the base interfaces in + `drivers/src/base/`. This is what makes them portable. +- **Ports never import other ports.** Each port is self-contained. +- **Core has no knowledge of specific chips.** It receives chip/HAL/board configuration through comptime module imports + injected by the build system. +- **Register definitions are generated, not hand-written.** The source of truth is the vendor SVD/ATDF file plus + patches. Don't edit generated register code in your local build, and don't edit the _svd_ files. +- **No global mutable state in libraries.** Embedded code uses explicit initialization and passed handles. +- **Memory regions are the port's responsibility.** Core and drivers don't assume any particular memory layout. + +## Cross-Cutting Concerns + +**Comptime configuration**: The build system injects `microzig.config` as a comptime-known struct. User code queries it +to branch on chip features, CPU type, or board identity. HALs use it to select peripheral implementations, setup clocks, +etc. + +**Startup sequence**: Each CPU architecture in _core/src/cpus/_ provides a `_start` entry point. The sequence is: set +stack pointer → zero BSS → copy `.data` from flash to RAM → call `microzig_main`. Cortex-M uses the vector table for the +initial stack pointer; RISC-V and AVR set it in code. + +**Naming conventions**: The codebase uses _snake_case_ (C-style), not the typical Zig camelCase. Run `zig fmt` for +formatting. + +**MMIO access**: All register access should go through `Mmio`. The packed struct defines the register layout, the `Mmio` +wrapper prevents the compiler from optimizing away or reordering accesses. If a register is incorrect in the SVD, add a +patch. If a register is missing, you might have to use `@ptrFromInt`. From 3a12b120b97abd0813a526092f7ef70f5e28d9fb Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Fri, 7 Aug 2026 16:27:32 -0400 Subject: [PATCH 2/6] more rewording --- ARCHITECTURE.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 6137f37a0..8ae6659fc 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -123,20 +123,20 @@ _riscv32-common/_. ### _examples/_ One directory per platform, mirroring the _port/_ structure: _examples/raspberrypi/rp2xxx/_, _examples/nordic/nrf5x/_, -etc. Each has its own _build.zig_ that uses `MicroBuild` to define firmware targets. Examples range from _blinky.zig_ to -WiFi stacks and FreeRTOS integration. +etc. Each has its own _build.zig_ that uses `MicroBuild` to define firmware targets. Examples often show off different +parts of the HAL. ### _sim/_ _sim/aviron/_ is an AVR instruction-level simulator for testing AVR firmware without hardware. -## Data Flow: SVD to `blink` +## Build Flow: SVD to `blink` The path from a vendor hardware description to running firmware: 1. **Vendor provides SVD/ATDF** describing chip registers and memory layout. 2. **Port's _build.zig_** references the SVD and defines `Chip` (memory regions, patches) and `Target` values. -3. **At build time**, `regz` processes the SVD (applying patches) and generates a Zig module with type-safe MMIO +3. `regz` (at build time) processes the SVD (applying patches) and generates a Zig module with type-safe MMIO register definitions. 4. **_generate_linker_script.zig_** emits a linker script from the `Target`'s memory regions. 5. **MicroBuild's `add_firmware()`** wires everything: it creates Zig modules for `microzig` (core), `chip` (generated @@ -179,15 +179,14 @@ copies the result to _zig-out/firmware/_. - **No global mutable state in libraries.** Embedded code uses explicit initialization and passed handles. - **Memory regions are the port's responsibility.** Core and drivers don't assume any particular memory layout. -## Cross-Cutting Concerns +## Other Concerns/Conventions **Comptime configuration**: The build system injects `microzig.config` as a comptime-known struct. User code queries it to branch on chip features, CPU type, or board identity. HALs use it to select peripheral implementations, setup clocks, etc. -**Startup sequence**: Each CPU architecture in _core/src/cpus/_ provides a `_start` entry point. The sequence is: set -stack pointer → zero BSS → copy `.data` from flash to RAM → call `microzig_main`. Cortex-M uses the vector table for the -initial stack pointer; RISC-V and AVR set it in code. +**Startup sequence**: Each CPU architecture in _core/src/cpus/_ provides a `_start` entry point. The typical sequence +is: set stack pointer → zero BSS → copy `.data` from flash to RAM → call `microzig_main`. **Naming conventions**: The codebase uses _snake_case_ (C-style), not the typical Zig camelCase. Run `zig fmt` for formatting. From b7f6bcf5801668774b27c07e818865263e35dbab Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Thu, 13 Aug 2026 12:27:21 -0400 Subject: [PATCH 3/6] rewording --- ARCHITECTURE.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 8ae6659fc..597bceb86 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -171,9 +171,9 @@ copies the result to _zig-out/firmware/_. - **Drivers never import port-specific code.** They operate exclusively through the base interfaces in `drivers/src/base/`. This is what makes them portable. -- **Ports never import other ports.** Each port is self-contained. -- **Core has no knowledge of specific chips.** It receives chip/HAL/board configuration through comptime module imports - injected by the build system. +- **Ports are self-contained**. +- **Core is port-agnostic.** It receives chip/HAL/board configuration through comptime module imports injected by the + build system. - **Register definitions are generated, not hand-written.** The source of truth is the vendor SVD/ATDF file plus patches. Don't edit generated register code in your local build, and don't edit the _svd_ files. - **No global mutable state in libraries.** Embedded code uses explicit initialization and passed handles. @@ -193,4 +193,4 @@ formatting. **MMIO access**: All register access should go through `Mmio`. The packed struct defines the register layout, the `Mmio` wrapper prevents the compiler from optimizing away or reordering accesses. If a register is incorrect in the SVD, add a -patch. If a register is missing, you might have to use `@ptrFromInt`. +patch. If a register is missing, you might have to use `@ptrFromInt` on its raw address. From f95f22ec22b00557644ff8096c50d238bb8fa622 Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Tue, 20 Oct 2026 20:54:16 -0400 Subject: [PATCH 4/6] Address Matt's comments in the PR --- ARCHITECTURE.md | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 597bceb86..700d6024b 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -3,9 +3,8 @@ This document gives a high-level overview of MicroZig's codebase. If you want to contribute or understand how things fit together, start here. -MicroZig is a Zig framework for programming microcontrollers. It provides a unified build system, hardware abstraction -layers, and device drivers across 14+ microcontroller families (ARM Cortex-M, RISC-V, AVR, MSP430). The core idea: write -portable embedded code in Zig, with platform details handled by ports and a code-generating build system. +MicroZig is a framework for programming microcontrollers using Zig. It provides a unified build system, hardware +abstraction layers, and device drivers across 14+ microcontroller families (ARM Cortex-M, RISC-V, AVR, MSP430). ## Codemap @@ -31,12 +30,13 @@ microzig/ The foundational library that every firmware links against. Key files in `core/src/`: - _microzig.zig_: Root API. Exposes `cpu`, `chip`, `hal`, `board`, `drivers` namespaces. Implements the default panic - handler, `export_startup()` for entry point generation, and `std_options()` for logging integration. + handler, `export_startup()` for entry point generation, and `std_options()` overriding defaults (currently only + logging-related). - _mmio.zig_: `Mmio` generic type that wraps a packed struct into a volatile MMIO register interface with `read()`, - `write()`, `modify()`, `toggle()` methods. Validates register sizes at comptime. + `write()`, `modify()`, `toggle()` methods. Add register access should go through the generated `Mmio` types. - _interrupt.zig_: `enable()`, `disable()`, critical sections, `Mutex` (HAL-provided or fallback `CriticalSectionMutex`), `Handler` union type. -- _allocator.zig_: Multi-tier free-list allocator. +- _allocator.zig_: Fixed-buffer allocator. - _cpus/_: CPU architecture implementations: - _cortex_m.zig_ (+ subdirectory with variants): interrupt vector tables, exception handling, FPU support, RAM vector tables. @@ -174,8 +174,9 @@ copies the result to _zig-out/firmware/_. - **Ports are self-contained**. - **Core is port-agnostic.** It receives chip/HAL/board configuration through comptime module imports injected by the build system. -- **Register definitions are generated, not hand-written.** The source of truth is the vendor SVD/ATDF file plus - patches. Don't edit generated register code in your local build, and don't edit the _svd_ files. +- **Register definitions are generated.** The source of truth is the vendor SVD/ATDF file plus patches. Don't edit + generated register code in your local build, and don't edit the _svd_ files. Add patches in the port's _patches/_ + directory to make corrections. - **No global mutable state in libraries.** Embedded code uses explicit initialization and passed handles. - **Memory regions are the port's responsibility.** Core and drivers don't assume any particular memory layout. @@ -188,9 +189,7 @@ etc. **Startup sequence**: Each CPU architecture in _core/src/cpus/_ provides a `_start` entry point. The typical sequence is: set stack pointer → zero BSS → copy `.data` from flash to RAM → call `microzig_main`. -**Naming conventions**: The codebase uses _snake_case_ (C-style), not the typical Zig camelCase. Run `zig fmt` for -formatting. +**Naming conventions**: The codebase uses _snake_case_ (C-style), not the typical Zig camelCase for function names. +Types are in _PascalCase_. -**MMIO access**: All register access should go through `Mmio`. The packed struct defines the register layout, the `Mmio` -wrapper prevents the compiler from optimizing away or reordering accesses. If a register is incorrect in the SVD, add a -patch. If a register is missing, you might have to use `@ptrFromInt` on its raw address. +**Styling**: We follow standard zig formatting. Run `zig fmt` for formatting. From 1d79ae1614c1d74b7a2b647f1705df7e521fb00f Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Tue, 20 Oct 2026 20:56:41 -0400 Subject: [PATCH 5/6] Wrap lines at 100 --- ARCHITECTURE.md | 152 ++++++++++++++++++++++++++---------------------- 1 file changed, 83 insertions(+), 69 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 700d6024b..60b200b2e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,10 +1,11 @@ # Architecture -This document gives a high-level overview of MicroZig's codebase. If you want to contribute or understand how things fit -together, start here. +This document gives a high-level overview of MicroZig's codebase. If you want to contribute or +understand how things fit together, start here. -MicroZig is a framework for programming microcontrollers using Zig. It provides a unified build system, hardware -abstraction layers, and device drivers across 14+ microcontroller families (ARM Cortex-M, RISC-V, AVR, MSP430). +MicroZig is a framework for programming microcontrollers using Zig. It provides a unified build +system, hardware abstraction layers, and device drivers across 14+ microcontroller families (ARM +Cortex-M, RISC-V, AVR, MSP430). ## Codemap @@ -29,17 +30,18 @@ microzig/ The foundational library that every firmware links against. Key files in `core/src/`: -- _microzig.zig_: Root API. Exposes `cpu`, `chip`, `hal`, `board`, `drivers` namespaces. Implements the default panic - handler, `export_startup()` for entry point generation, and `std_options()` overriding defaults (currently only - logging-related). -- _mmio.zig_: `Mmio` generic type that wraps a packed struct into a volatile MMIO register interface with `read()`, - `write()`, `modify()`, `toggle()` methods. Add register access should go through the generated `Mmio` types. +- _microzig.zig_: Root API. Exposes `cpu`, `chip`, `hal`, `board`, `drivers` namespaces. Implements + the default panic handler, `export_startup()` for entry point generation, and `std_options()` + overriding defaults (currently only logging-related). +- _mmio.zig_: `Mmio` generic type that wraps a packed struct into a volatile MMIO register interface + with `read()`, `write()`, `modify()`, `toggle()` methods. Add register access should go through + the generated `Mmio` types. - _interrupt.zig_: `enable()`, `disable()`, critical sections, `Mutex` (HAL-provided or fallback `CriticalSectionMutex`), `Handler` union type. - _allocator.zig_: Fixed-buffer allocator. - _cpus/_: CPU architecture implementations: - - _cortex_m.zig_ (+ subdirectory with variants): interrupt vector tables, exception handling, FPU support, RAM - vector tables. + - _cortex_m.zig_ (+ subdirectory with variants): interrupt vector tables, exception handling, FPU + support, RAM vector tables. - _riscv32.zig_: RISC-V 32-bit support. - _avr_common.zig_, _avr25.zig_, _avr5.zig_: AVR architecture. - _msp430.zig_, _msp430x.zig_: TI MSP430. @@ -47,9 +49,9 @@ The foundational library that every firmware links against. Key files in `core/s ### _port/_ -Each port implements support for a microcontroller family. This includes creating a Microzig target for various chips -(and some well-known boards) as well as implementing a HAL to support peripherals. Ports follow a consistent structure -(using `port/raspberrypi/rp2xxx/` as the reference): +Each port implements support for a microcontroller family. This includes creating a Microzig target +for various chips (and some well-known boards) as well as implementing a HAL to support peripherals. + Ports follow a consistent structure (using `port/raspberrypi/rp2xxx/` as the reference): ``` port/// @@ -63,16 +65,19 @@ port/// └── ld/ # Custom linker scripts (when auto-generation isn't enough) ``` -The _build.zig_ in each port defines `Chip` values (SVD path, memory regions, patches) and `Board` values (root source -file with pin configs). These feed into the `Target` type from _build-internals/_. +The _build.zig_ in each port defines `Chip` values (SVD path, memory regions, patches) and `Board` +values (root source file with pin configs). These feed into the `Target` type from +_build-internals/_. -Current ports: ESP32 (_espressif/esp_), GD32 (_gigadevice/gd32_), ATmega/ATtiny/SAMD51 (_microchip/_), nRF5x -(_nordic/nrf5x_), LPC/MCX (_nxp/_), RP2040/RP2350 (_raspberrypi/rp2xxx_), STM32 (_stmicro/stm32_), MSP430/MSPM0/TM4C -(_texasinstruments/_), CH32V (_wch/ch32v_). +Current ports: ESP32 (_espressif/esp_), GD32 (_gigadevice/gd32_), ATmega/ATtiny/SAMD51 +(_microchip/_), nRF5x (_nordic/nrf5x_), LPC/MCX (_nxp/_), RP2040/RP2350 (_raspberrypi/rp2xxx_), +STM32 (_stmicro/stm32_), MSP430/MSPM0/TM4C (_texasinstruments/_), CH32V (_wch/ch32v_), CH32H +(_wch/ch32h_). ### _drivers/_ -Device-independent drivers that talk to hardware through base interface abstractions. Located in `drivers/src/`: +Device-independent drivers that talk to hardware through base interface abstractions. Located in +`drivers/src/`: - _base/_: Interface types using a pointer+vtable pattern: - _Digital_IO.zig_: GPIO pin abstraction (direction, bias, read/write). @@ -82,49 +87,52 @@ Device-independent drivers that talk to hardware through base interface abstract - _BlockMemory.zig_: Block storage. - _ClockDevice.zig_: Real-time clock. - _time/_: `Absolute` (microseconds since boot), `Duration`, `Deadline`. -- Device drivers organized by category: _display/_ (SSD1306, ST77xx, HD44780), _input/_ (debounced buttons, rotary - encoders, keyboard matrices), _sensor/_ (temperature, motion, magnetic), _stepper/_ (A4988, DRV8825), _led/_ (WS2812), - _wireless/_ (CYW43 WiFi/BT), _io_expander/_ (PCF8574, PCA9685). +- Device drivers organized by category: _display/_ (SSD1306, ST77xx, HD44780), _input/_ (debounced + buttons, rotary encoders, keyboard matrices), _sensor/_ (temperature, motion, magnetic), + _stepper/_ (A4988, DRV8825), _led/_ (WS2812), _wireless/_ (CYW43 WiFi/BT), _io_expander/_ + (PCF8574, PCA9685). -The vtable approach lets drivers work with any HAL that implements the base interfaces. Drivers never import -port-specific code. +The vtable approach lets drivers work with any HAL that implements the base interfaces. Drivers +never import port-specific code. ### _build-internals/_ The type system that ties everything together at build time. The key types in `build-internals/build.zig`: - `Target`: A complete firmware compilation target. Bundles a `zig_target` query, `Chip`, optional - `HardwareAbstractionLayer`, optional `Board`, `LinkerScript`, `Stack` placement, and preferred `BinaryFormat`. - Supports `derive()` for creating variants. + `HardwareAbstractionLayer`, optional `Board`, `LinkerScript`, `Stack` placement, and preferred + `BinaryFormat`. Supports `derive()` for creating variants. - `Chip`: MCU definition: name, `register_definition` (SVD, ATDF, Zig, Embassy, or TargetDB source), `memory_regions`, and `patch_files`. - `HardwareAbstractionLayer`: Root source file + imports for the HAL. - `Board`: Root source file + imports for board-specific config. - `MemoryRegion`: Describes a memory region (flash/RAM) with offset, length, and access permissions. - `BinaryFormat`: Output format: `.elf`, `.binary`, `.hex`, `.uf2`, `.dfu`, `.esp`, or `.custom`. -- `LinkerScript`: Auto-generated or custom, controls section placement and rodata location (flash vs RAM). +- `LinkerScript`: Auto-generated or custom, controls section placement and rodata location (flash vs + RAM). -This directory also contains the build-time tools that run during compilation: _regz/_ (register code generation), -_uf2/_, _dfu/_, _esp-image/_ (format converters), _linter/_, _sorcerer/_ (register visualizer), _printer/_, -_generate_linker_script.zig_. +This directory also contains the build-time tools that run during compilation: _regz/_ (register +code generation), _uf2/_, _dfu/_, _esp-image/_ (format converters), _linter/_, _sorcerer/_ (register +visualizer), _printer/_, _generate_linker_script.zig_. ### _tools/_ -Standalone versions of the build-time tools, usable outside the MicroZig build system. `regz` is the most important: -it parses vendor hardware description files (SVD, ATDF, Embassy, TI TargetDB) and generates type-safe Zig MMIO register -interfaces. `sorcerer` produces SVG visualizations of register maps. +Standalone versions of the build-time tools, usable outside the MicroZig build system. `regz` is the +most important: it parses vendor hardware description files (SVD, ATDF, Embassy, TI TargetDB) and +generates type-safe Zig MMIO register interfaces. `sorcerer` produces SVG visualizations of register +maps. ### _modules/_ -Shared Zig libraries: _foundation-libc/_ (minimal libc), _freertos/_ (RTOS integration), _lwip/_ (TCP/IP stack), _rtt/_ -(Segger Real-Time Transfer for debug output), _network/_ (networking abstractions), _virtual-io/_, _bounded-array/_, -_riscv32-common/_. +Shared Zig libraries: _foundation-libc/_ (minimal libc), _freertos/_ (RTOS integration), _lwip/_ +(TCP/IP stack), _rtt/_ (Segger Real-Time Transfer for debug output), _network/_ (networking +abstractions), _virtual-io/_, _bounded-array/_, _riscv32-common/_. ### _examples/_ -One directory per platform, mirroring the _port/_ structure: _examples/raspberrypi/rp2xxx/_, _examples/nordic/nrf5x/_, -etc. Each has its own _build.zig_ that uses `MicroBuild` to define firmware targets. Examples often show off different -parts of the HAL. +One directory per platform, mirroring the _port/_ structure: _examples/raspberrypi/rp2xxx/_, +_examples/nordic/nrf5x/_, etc. Each has its own _build.zig_ that uses `MicroBuild` to define +firmware targets. Examples often show off different parts of the HAL. ### _sim/_ @@ -135,17 +143,20 @@ _sim/aviron/_ is an AVR instruction-level simulator for testing AVR firmware wit The path from a vendor hardware description to running firmware: 1. **Vendor provides SVD/ATDF** describing chip registers and memory layout. -2. **Port's _build.zig_** references the SVD and defines `Chip` (memory regions, patches) and `Target` values. -3. `regz` (at build time) processes the SVD (applying patches) and generates a Zig module with type-safe MMIO - register definitions. +2. **Port's _build.zig_** references the SVD and defines `Chip` (memory regions, patches) and + `Target` values. +3. `regz` (at build time) processes the SVD (applying patches) and generates a Zig module with + type-safe MMIO register definitions. 4. **_generate_linker_script.zig_** emits a linker script from the `Target`'s memory regions. -5. **MicroBuild's `add_firmware()`** wires everything: it creates Zig modules for `microzig` (core), `chip` (generated - registers), `cpu`, `hal`, `board`, and `drivers`, then compiles the user's `root_source_file` against them. -6. **Startup** (_core/src/cpus/_): the CPU module provides the entry point, initializes the stack, zeroes BSS, copies - data from flash to RAM, then calls `microzig_main` (the user's entry point). -7. **User code** calls into `hal` (e.g., `hal.gpio.init()`) which uses the generated `chip` registers to configure - hardware. Drivers use the `hal` through base interfaces. -8. **Output conversion** transforms the ELF into the target format (UF2, HEX, DFU, etc.) for flashing. +5. **MicroBuild's `add_firmware()`** wires everything: it creates Zig modules for `microzig` (core), + `chip` (generated registers), `cpu`, `hal`, `board`, and `drivers`, then compiles the user's + `root_source_file` against them. +6. **Startup** (_core/src/cpus/_): the CPU module provides the entry point, initializes the stack, + zeroes BSS, copies data from flash to RAM, then calls `microzig_main` (the user's entry point). +7. **User code** calls into `hal` (e.g., `hal.gpio.init()`) which uses the generated `chip` + registers to configure hardware. Drivers use the `hal` through base interfaces. +8. **Output conversion** transforms the ELF into the target format (UF2, HEX, DFU, etc.) for + flashing. ## Build System @@ -163,33 +174,36 @@ const fw = mb.add_firmware(.{ mb.install_firmware(fw, .{}); ``` -`add_firmware()` invokes `regz` on the chip's register definition, generates the linker script, creates all the Zig -modules, and sets up the compilation. `install_firmware()` adds the format conversion step (e.g. to _uf2_ or _bin_) and -copies the result to _zig-out/firmware/_. +`add_firmware()` invokes `regz` on the chip's register definition, generates the linker script, +creates all the Zig modules, and sets up the compilation. `install_firmware()` adds the format +conversion step (e.g. to _uf2_ or _bin_) and copies the result to _zig-out/firmware/_. ## Architectural Invariants -- **Drivers never import port-specific code.** They operate exclusively through the base interfaces in - `drivers/src/base/`. This is what makes them portable. +- **Drivers never import port-specific code.** They operate exclusively through the base interfaces + in `drivers/src/base/`. This is what makes them portable. - **Ports are self-contained**. -- **Core is port-agnostic.** It receives chip/HAL/board configuration through comptime module imports injected by the - build system. -- **Register definitions are generated.** The source of truth is the vendor SVD/ATDF file plus patches. Don't edit - generated register code in your local build, and don't edit the _svd_ files. Add patches in the port's _patches/_ - directory to make corrections. -- **No global mutable state in libraries.** Embedded code uses explicit initialization and passed handles. -- **Memory regions are the port's responsibility.** Core and drivers don't assume any particular memory layout. +- **Core is port-agnostic.** It receives chip/HAL/board configuration through comptime module + imports injected by the build system. +- **Register definitions are generated.** The source of truth is the vendor SVD/ATDF file plus + patches. Don't edit generated register code in your local build, and don't edit the _svd_ files. + Add patches in the port's _patches/_ directory to make corrections. +- **No global mutable state in libraries.** Embedded code uses explicit initialization and passed + handles. +- **Memory regions are the port's responsibility.** Core and drivers don't assume any particular + memory layout. ## Other Concerns/Conventions -**Comptime configuration**: The build system injects `microzig.config` as a comptime-known struct. User code queries it -to branch on chip features, CPU type, or board identity. HALs use it to select peripheral implementations, setup clocks, -etc. +**Comptime configuration**: The build system injects `microzig.config` as a comptime-known struct. +User code queries it to branch on chip features, CPU type, or board identity. HALs use it to select +peripheral implementations, setup clocks, etc. -**Startup sequence**: Each CPU architecture in _core/src/cpus/_ provides a `_start` entry point. The typical sequence -is: set stack pointer → zero BSS → copy `.data` from flash to RAM → call `microzig_main`. +**Startup sequence**: Each CPU architecture in _core/src/cpus/_ provides a `_start` entry point. The +typical sequence is: set stack pointer → zero BSS → copy `.data` from flash to RAM → call +`microzig_main`. -**Naming conventions**: The codebase uses _snake_case_ (C-style), not the typical Zig camelCase for function names. -Types are in _PascalCase_. +**Naming conventions**: The codebase uses _snake_case_ (C-style), not the typical Zig camelCase for +function names. Types are in _PascalCase_. **Styling**: We follow standard zig formatting. Run `zig fmt` for formatting. From 3131ade3b1c0ba54bddccc98c3db0561bdef4d15 Mon Sep 17 00:00:00 2001 From: Graziano Misuraca Date: Tue, 20 Oct 2026 21:05:49 -0400 Subject: [PATCH 6/6] Address missed comments --- ARCHITECTURE.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 60b200b2e..6f057a3a5 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -11,18 +11,17 @@ Cortex-M, RISC-V, AVR, MSP430). ``` microzig/ -├── build.zig # MicroBuild entry point (see "Build System" below) +├── build.zig ├── build-internals/ # Target/Chip/HAL/Board type system, build-time tools ├── core/ # Foundational library: CPU, MMIO, interrupts, startup ├── port/ # Platform-specific HALs and chip definitions ├── drivers/ # HAL-agnostic device drivers ├── tools/ # Standalone CLI tools (regz, uf2, dfu, etc.) -├── modules/ # Shared libraries (libc, lwIP, FreeRTOS, RTT, etc.) +├── modules/ # Packages maintained by ZigEmbeddedGroup, but not depended on by microzig ├── examples/ # Per-platform example applications -├── sim/ # AVR simulator (aviron) +├── sim/ # Simulators (Currently only our AVR simulator _aviron_) ├── vendor/ # Vendored third-party code -├── website/ # Documentation site -├── design/ # Logos and design assets +├── website/ # Documentation site (microzig.tech) └── scripts/ # Utility scripts ``` @@ -58,11 +57,12 @@ port/// ├── build.zig # Chip/board/target definitions, memory regions, SVD paths ├── src/ │ ├── hal.zig # HAL root: re-exports gpio, uart, i2c, spi, dma, etc. -│ ├── hal/ # Individual HAL modules (one file per peripheral) -│ ├── boards/ # Board configs (pin mappings, oscillator freq, LED pin) +│ │ # A port may have more than one, such as in the case for rp2xxx +│ ├── hal/ # Individual HAL peripheral namespaces +│ ├── boards/ # Board definitions (pin mappings, oscillator freq, etc.) │ └── cpus/ # CPU variants if the family has multiple cores ├── patches/ # Corrections to vendor SVD/ATDF files -└── ld/ # Custom linker scripts (when auto-generation isn't enough) +└── ld/ # Custom linker scripts, when needed ``` The _build.zig_ in each port defines `Chip` values (SVD path, memory regions, patches) and `Board` @@ -97,9 +97,9 @@ never import port-specific code. ### _build-internals/_ -The type system that ties everything together at build time. The key types in `build-internals/build.zig`: +The key types in `build-internals/build.zig`: -- `Target`: A complete firmware compilation target. Bundles a `zig_target` query, `Chip`, optional +- `Target`: A complete firmware compilation target. Bundles a `zig_target` query, `Chip`, `HardwareAbstractionLayer`, optional `Board`, `LinkerScript`, `Stack` placement, and preferred `BinaryFormat`. Supports `derive()` for creating variants. - `Chip`: MCU definition: name, `register_definition` (SVD, ATDF, Zig, Embassy, or TargetDB source), @@ -107,7 +107,8 @@ The type system that ties everything together at build time. The key types in `b - `HardwareAbstractionLayer`: Root source file + imports for the HAL. - `Board`: Root source file + imports for board-specific config. - `MemoryRegion`: Describes a memory region (flash/RAM) with offset, length, and access permissions. -- `BinaryFormat`: Output format: `.elf`, `.binary`, `.hex`, `.uf2`, `.dfu`, `.esp`, or `.custom`. +- `BinaryFormat`: Default output format: `.elf`, `.binary`, `.hex`, `.uf2`, `.dfu`, `.esp`, or + `.custom`. - `LinkerScript`: Auto-generated or custom, controls section placement and rodata location (flash vs RAM). @@ -147,7 +148,7 @@ The path from a vendor hardware description to running firmware: `Target` values. 3. `regz` (at build time) processes the SVD (applying patches) and generates a Zig module with type-safe MMIO register definitions. -4. **_generate_linker_script.zig_** emits a linker script from the `Target`'s memory regions. +4. **_generate_linker_script.zig_** generates a linker script using the `Target`'s memory regions. 5. **MicroBuild's `add_firmware()`** wires everything: it creates Zig modules for `microzig` (core), `chip` (generated registers), `cpu`, `hal`, `board`, and `drivers`, then compiles the user's `root_source_file` against them.