yaml2pac

February 7, 2026 ยท View on GitHub

Multi-mode register code generator based on chiptool IR (YAML format).

Supports three generation modes from the same YAML register description:

ModeDescriptionRegister Access
pacStandard MMIO PACread_volatile/write_volatile pointer ops
rvcsrRISC-V CSR registerscsrrs/csrrw/csrrc inline asm
i2cdevI2C device registersTyped u8 addresses (bring your own transport)

All modes share the same fieldset/enum type system. Only the register access layer differs.

Installation

cargo install yaml2pac

Or from source:

cargo install --path .

Usage

# PAC mode (default) - standard MMIO peripheral access
yaml2pac --mode pac -i registers.yaml -o pac.rs --builtin-common

# RISC-V CSR mode - inline asm for CSR access
yaml2pac --mode rvcsr -i csr.yaml -o register.rs

# I2C device mode - typed register addresses
yaml2pac --mode i2cdev -i sensor.yaml -o regs.rs --builtin-common

Options

OptionDescription
-i, --input <FILE>...Input YAML file(s). Multiple files are merged.
-o, --output <FILE>Output .rs file (default: ./out/out.rs)
--mode <MODE>Generation mode: pac, rvcsr, i2cdev (default: pac)
--builtin-commonEmbed the common module into generated output (applies to pac, i2cdev)
--common-module-path <PATH>Rust path to common module (applies to pac, i2cdev; default: self::common with --builtin-common, crate::common otherwise)

Common module path

The --common-module-path option controls where generated code looks for the common module (Reg, access traits, etc.):

# Embedded as submodule (default with --builtin-common)
yaml2pac --mode pac -i registers.yaml -o pac.rs --builtin-common
# Generated code uses: self::common::Reg

# External module at crate root (default without --builtin-common)
yaml2pac --mode pac -i registers.yaml -o pac.rs
# Generated code uses: crate::common::Reg

# Custom path
yaml2pac --mode i2cdev -i sensor.yaml -o regs.rs --builtin-common --common-module-path "crate::register::common"
# Generated code uses: crate::register::common::Reg

rvcsr mode does not use Reg/common and ignores common module path options.

RVCSR output style

rvcsr generates module-per-CSR APIs (riscv crate style), for example:

// typed read
let m = register::mcache_ctl::read();

// single-bit atomic ops (single csrrs/csrrc instruction)
unsafe { register::mcache_ctl::set_dc_en(); }
unsafe { register::mcache_ctl::clear_dc_en(); }

// multi-bit field update (RMW)
unsafe { register::mcache_ctl::set_dc_warnd(0b11); }

YAML format

Uses the chiptool IR YAML format. The byte_offset field semantics vary by mode:

  • pac: Memory offset from peripheral base address
  • rvcsr: 12-bit CSR address (e.g. 0x300 for mstatus)
  • i2cdev: I2C register address (u8)

License

Licensed under either of

at your option.