reg-generator
A FuseSoC generator wrapping OpenTitan’s regtool.py to automate the generation of control-register infrastructure for hardware peripherals. It is used in X-HEEP to replace per-peripheral generation shell scripts with a single, reusable FuseSoC generator.
Quick start
Add xheep:util:reg-generator as a dependency of your peripheral core and declare a generate block that invokes the regtool generator:
CAPI=2:
name: "vendor:ip:my_peripheral"
filesets:
files_rtl:
depend:
- xheep:util:reg-generator
files:
- rtl/my_peripheral.sv
file_type: systemVerilogSource
generate:
my-peripheral-regs:
generator: regtool
parameters:
name: my_peripheral # register block basename
config: data/my_peripheral.hjson # HJSON register description (possibly a .tpl)
rtl_dir: rtl # output directory for RTL files
sw_path: ../../../sw/drivers/my_peripheral/my_peripheral_regs.h
doc_path: ../../../sw/drivers/my_peripheral/my_peripheral_regs.md
targets:
default:
filesets:
- files_rtl
generate:
- my-peripheral-regs
All paths are relative to files_root, which FuseSoC sets to the directory containing your .core file.
Parameters
Parameter |
Required |
Description |
|---|---|---|
|
yes |
Name of the register block (used to derive output filenames). |
|
yes |
Path to the HJSON register description for |
|
no |
Output directory for the generated RTL files, relative to |
|
no |
Output file path for the generated C register-defines header, relative to |
|
no |
Output file path for the generated Markdown register documentation, relative to |
|
no |
Path to |
|
no |
Output file path for the C structs header, relative to |
|
no |
Path to the peripheral C-structs generator script, relative to |
|
no |
Path to the template for the C structs header, relative to |
|
no |
VLNV of the core whose semantic version is exposed as |
|
no |
Any additional key–value pairs are forwarded as template variables when rendering a Mako |
What the generator produces
Given a peripheral named my_peripheral, the generator produces:
Output |
Description |
|---|---|
|
SystemVerilog package with register field types and constants. |
|
SystemVerilog register-file top module (TL-UL interface). |
|
C header ( |
|
Markdown documentation of all registers and fields, generated by |
|
(optional) C structs header providing typed bit-field access to each register. Requires |
|
FuseSoC |
How FuseSoC invokes the generator
When FuseSoC encounters a generate block, it:
Collects all cores in the dependency graph and serializes the context into a YAML input file (e.g.
<inst-name>_input.yml) placed in a per-instance subdirectory underbuild/.Invokes the generator script, e.g.,
reg-generator.pywith that YAML file as its only argument.Expects the generator to write a
.corefile named after the generator instance in its working directory. FuseSoC reads this file to discover the generated RTL as new dependencies for the rest of the build.
The input YAML for reg-generator contains, among other things:
files_root: /absolute/path/to/the/directory/containing/your.core # base for all relative paths in 'parameters'
parameters: # key-value pairs from your 'generate' block
name: my_peripheral
config: data/my_peripheral.hjson
rtl_dir: rtl
sw_path: ../../../sw/drivers/my_peripheral/my_peripheral_regs.h
doc_path: ../../../sw/drivers/my_peripheral/my_peripheral_regs.md
vlnv: vendor:ip:my_peripheral-my-peripheral-regs:0 # VLNV of this generator instance
toplevel: vendor:systems:my_soc:1.0.0 # top-level core of the current FuseSoC run
cores: # full dependency graph — all cores with their resolved paths
xheep:util:reg-generator:
core_root: /absolute/path/to/reg-generator
...
...
files_root is the key field to understand when writing paths in parameters: every relative path is resolved against it, which is always the directory containing the invoking .core file, not the generator’s own directory or the build directory.
The .core file written by the generator back to FuseSoC lists the two generated RTL files with absolute paths, for example:
CAPI=2:
name: vendor:ip:my_peripheral-my-peripheral-regs:0
filesets:
rtl:
files:
- /absolute/path/to/rtl/my_peripheral_reg_pkg.sv
- /absolute/path/to/rtl/my_peripheral_reg_top.sv
file_type: systemVerilogSource
targets:
default:
filesets: [rtl]
Template-based HJSON
When config has a .tpl extension, the generator first renders it as a Mako template before passing it to regtool. This is useful for parametric register maps whose field count or addresses depend on design-time constants. All parameters key-value pairs (e.g, <others>) entries are forwarded to the template as variables, plus a pre-computed version_hex (a 0x00MMmmPP hex string derived from the target core’s semantic version).
Note: In those cases where a dedicated template renderer is used before calling FuseSoC, as it happens with MCU-GEN in X-HEEP, the rendered
.hjsonfile must be passed as theconfigparameter.
Caching
The generator skips all subprocess calls (regtool and the optional structs generator) when it can determine the outputs are already up to date. Generation is skipped if and only if both conditions hold:
The SHA-256 hash of the (rendered) HJSON input file matches the hash stored from the previous run.
Every expected output file exists on disk.
If either condition fails — the input changed, or any output was deleted — the full generation runs and the cache is updated on success. A failed run never updates the cache, so the next invocation always retries.
The cache is stored as a plain-text file named .{name}_reg_gen.cache in files_root (the directory containing the invoking .core file). The cache key is:
Template HJSON (
.tpl): SHA-256 of the template file content and all rendering kwargs (parameters +version_hex). Any change to the template source or its parameters invalidates the cache.Plain HJSON: SHA-256 of the HJSON file content.
The cache file lives in the source tree alongside the IP and persists across make clean (which only removes the build/ directory). It can be invalidated explicitly by deleting it, or implicitly by modifying the HJSON input (or template and its parameters) or removing any expected output file.
Path resolution
regtool.py
The generator locates regtool.py in this order:
REGTOOLenvironment variable — highest priority; useful in CI or when integrating reg-generator into a project that vendors its own copy of OpenTitan.regtool_pathparameter in the.corefile — relative tofiles_root.Built-in default — the plain name
regtool.py, resolved viaPATH.
periph_structs_gen.py
When structs_sw_path is set, The generator locates periph_structs_gen.py in this order:
PERIPH_STRUCTS_GENenvironment variable — highest priority; useful in CI or when integrating reg-generator into a project that vendors its own copy of X-HEEP.structs_gen_pathparameter in the.corefile — relative tofiles_root.Built-in default — the plain name
periph_structs_gen.py, resolved viaPATH.