Repository navigation
Add ARCHITECTURE.md #1005
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add ARCHITECTURE.md #1005
Changes from all commits
ce4b265
3a12b12
b7f6bcf
f95f22e
1d79ae1
3131ade
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| # 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 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 | ||
|
|
||
| ``` | ||
| microzig/ | ||
| ├── 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/ # Packages maintained by ZigEmbeddedGroup, but not depended on by microzig | ||
| ├── examples/ # Per-platform example applications | ||
| ├── sim/ # Simulators (Currently only our AVR simulator _aviron_) | ||
| ├── vendor/ # Vendored third-party code | ||
| ├── website/ # Documentation site (microzig.tech) | ||
| └── 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()` | ||
| 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. | ||
| - _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/<vendor>/<family>/ | ||
| ├── build.zig # Chip/board/target definitions, memory regions, SVD paths | ||
| ├── src/ | ||
| │ ├── hal.zig # HAL root: re-exports gpio, uart, i2c, spi, dma, etc. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that there may be multiple HALs in a port |
||
| │ │ # 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 needed | ||
| ``` | ||
|
|
||
| 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_), CH32H | ||
| (_wch/ch32h_). | ||
|
|
||
| ### _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 key types in `build-internals/build.zig`: | ||
|
|
||
| - `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), | ||
| `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`: 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). | ||
|
|
||
| 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 often show off different parts of the HAL. | ||
|
|
||
| ### _sim/_ | ||
|
|
||
| _sim/aviron/_ is an AVR instruction-level simulator for testing AVR firmware without hardware. | ||
|
|
||
| ## 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. `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_** 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. | ||
| 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`. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The above section is completely redundant, remove it because this is far better.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. which part? |
||
|
|
||
| ```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 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. | ||
|
|
||
| ## 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 | ||
| 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_. | ||
|
|
||
| **Styling**: We follow standard zig formatting. Run `zig fmt` for formatting. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the point of this document to list other files? The comments here barely do more than restate the file name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I can nix them if you want. I find it useful so you know what file to look for for each arch.