Data Formats
Axion-HDL supports multiple input formats for defining register interfaces. This section covers all available attributes, keywords, and their usage across all supported formats.
Format Overview
Format |
Extension |
Use Case |
|---|---|---|
VHDL |
|
Embedded in existing RTL code |
SystemVerilog |
|
Embedded in existing RTL code |
YAML |
|
Human-readable, version control friendly |
XML |
|
IP-XACT compatible, tool integration |
JSON |
|
Automation and scripting |
TOML |
|
Clean syntax, Python ecosystem standard |
Module-Level Attributes
These attributes define module-wide settings like base address and CDC configuration.
VHDL (@axion_def)
Module-level configuration is defined with @axion_def comment anywhere in the file:
-- @axion_def BASE_ADDR=0x1000 CDC_EN CDC_STAGE=3
SystemVerilog (@axion_def)
Use // comments for module configuration:
// @axion_def BASE_ADDR=0x1000 CDC_EN CDC_STAGE=3
YAML/TOML/XML/JSON
Attribute |
YAML |
TOML |
XML |
JSON |
Description |
Default |
|---|---|---|---|---|---|---|
Module Name |
|
|
|
|
Module/entity name |
Required |
Base Address |
|
|
|
|
Starting address (hex string) |
|
CDC Enable |
|
|
|
|
Enable clock domain crossing |
|
CDC Stages |
|
|
|
|
Synchronizer stages (2-5) |
|
VHDL Module Attributes Table
Attribute |
Syntax |
Description |
Default |
|---|---|---|---|
|
|
Module base address |
|
|
|
Enable CDC synchronizers |
|
|
|
Number of sync stages (2-5) |
|
Register-Level Attributes
Access Modes
Mode |
Software |
Hardware |
Description |
|---|---|---|---|
|
Read |
Write |
Hardware writes, software reads |
|
Write |
Read |
Software writes, hardware reads |
|
Read/Write |
Read/Write |
Both can read and write |
Complete Attribute Reference
VHDL Register Attributes (@axion)
signal reg_name : std_logic_vector(31 downto 0); -- @axion ACCESS [OPTIONS]
Attributes are optional — a bare -- @axion annotation is valid and uses all defaults:
signal my_reg : std_logic_vector(31 downto 0); -- @axion
-- Defaults to: RW access, auto-assigned address, no strobes
SystemVerilog Register Attributes (@axion)
logic [31:0] reg_name; // @axion ACCESS [OPTIONS]
Attributes are optional — a bare // @axion annotation is valid and uses all defaults:
logic [31:0] my_reg; // @axion
// Defaults to: RW access, auto-assigned address, no strobes
Attribute |
Syntax |
Description |
Default |
|---|---|---|---|
Access Mode |
|
Register access type |
|
Address |
|
Manual address assignment |
Auto-assigned |
Description |
|
Register description |
Empty |
Read Strobe |
|
Generate read strobe signal |
|
Write Strobe |
|
Generate write strobe signal |
|
Default Value |
|
Reset value |
|
Register Name |
|
Group into packed register |
Signal name |
Bit Offset |
|
Bit position in packed reg |
|
YAML Register Attributes
registers:
- name: register_name
addr: "0x00" # Optional: manual address
access: RW # Required: RO, WO, RW
width: 32 # Optional: bit width (default: 32)
default: "0x00" # Optional: reset value
description: "text" # Optional: documentation
r_strobe: true # Optional: read strobe
w_strobe: true # Optional: write strobe
fields: # Optional: subregister fields
- name: field_name
bit_offset: 0
width: 8
access: RW
Attribute |
Type |
Description |
Default |
|---|---|---|---|
|
string |
Register/signal name |
Required |
|
string |
Address in hex (e.g., “0x04”) |
Auto-assigned |
|
string |
|
Required |
|
integer |
Bit width (1-1024) |
|
|
string/int |
Reset value |
|
|
string |
Documentation text |
Empty |
|
boolean |
Generate read strobe |
|
|
boolean |
Generate write strobe |
|
|
array |
Subregister field definitions |
None |
XML Register Attributes
<register name="reg_name" addr="0x00" access="RW" width="32"
default="0x00" r_strobe="true" w_strobe="true"
description="Description text"/>
Attribute |
Type |
Description |
Default |
|---|---|---|---|
|
string |
Register name |
Required |
|
string |
Address in hex |
Auto-assigned |
|
string |
|
Required |
|
integer |
Bit width |
|
|
string |
Reset value |
|
|
string |
Documentation |
Empty |
|
string |
|
|
|
string |
|
|
|
string |
Packed register name |
None |
|
integer |
Bit position for packed |
|
JSON Register Attributes
{
"name": "reg_name",
"addr": "0x00",
"access": "RW",
"width": 32,
"default": "0x00",
"description": "Description text",
"r_strobe": true,
"w_strobe": true
}
Attribute |
Type |
Description |
Default |
|---|---|---|---|
|
string |
Register name |
Required |
|
string |
Address in hex |
Auto-assigned |
|
string |
|
Required |
|
integer |
Bit width |
|
|
string/int |
Reset value |
|
|
string |
Documentation |
Empty |
|
boolean |
Generate read strobe |
|
|
boolean |
Generate write strobe |
|
|
array |
Subregister fields |
None |
TOML Register Attributes
[[registers]]
name = "register_name"
addr = "0x00" # Optional: manual address
access = "RW" # Required: RO, WO, RW
width = 32 # Optional: bit width (default: 32)
default = "0x00" # Optional: reset value
description = "text" # Optional: documentation
r_strobe = true # Optional: read strobe
w_strobe = true # Optional: write strobe
reg_name = "packed" # Optional: packed register name
bit_offset = 0 # Optional: bit position in packed reg
Attribute |
Type |
Description |
Default |
|---|---|---|---|
|
string |
Register/signal name |
Required |
|
string |
Address in hex (e.g., “0x04”) |
Auto-assigned |
|
string |
|
Required |
|
integer |
Bit width (1-1024) |
|
|
string/int |
Reset value |
|
|
string |
Documentation text |
Empty |
|
boolean |
Generate read strobe |
|
|
boolean |
Generate write strobe |
|
|
string |
Packed register name |
None |
|
integer |
Bit position for packed |
|
Advanced Features
Clock Domain Crossing (CDC)
Enable CDC synchronizers when your AXI bus and register logic use different clocks.
VHDL:
-- @axion_def BASE_ADDR=0x1000 CDC_EN CDC_STAGE=3
YAML:
module: my_module
base_addr: "0x1000"
config:
cdc_en: true
cdc_stage: 3
XML:
<register_map module="my_module" base_addr="0x1000">
<config cdc_en="true" cdc_stage="3"/>
</register_map>
JSON:
{
"module": "my_module",
"base_addr": "0x1000",
"config": {
"cdc_en": true,
"cdc_stage": 3
}
}
TOML:
module = "my_module"
base_addr = "0x1000"
[config]
cdc_en = true
cdc_stage = 3
Parameter |
Description |
Default |
|---|---|---|
|
Enable CDC synchronizers |
|
|
Number of synchronizer stages |
|
Strobe Signals
Strobe signals provide single-cycle pulses on read/write operations.
VHDL:
signal irq_status : std_logic_vector(31 downto 0); -- @axion RW R_STROBE W_STROBE DESC="Interrupt status"
YAML:
- name: irq_status
access: RW
r_strobe: true
w_strobe: true
description: "Interrupt status"
XML:
<register name="irq_status" access="RW" r_strobe="true" w_strobe="true" description="Interrupt status"/>
JSON:
{
"name": "irq_status",
"access": "RW",
"r_strobe": true,
"w_strobe": true,
"description": "Interrupt status"
}
TOML:
[[registers]]
name = "irq_status"
access = "RW"
r_strobe = true
w_strobe = true
description = "Interrupt status"
Use Cases:
R_STROBE: Clear-on-read status registersW_STROBE: Trigger actions on write (start transfer, clear flags)
Subregisters (Packed Fields)
Pack multiple fields into a single 32-bit register address.
VHDL:
-- Control register at address 0x00 with packed fields
signal enable : std_logic; -- @axion RW REG_NAME=control BIT_OFFSET=0 DESC="Enable bit"
signal mode : std_logic_vector(1 downto 0); -- @axion RW REG_NAME=control BIT_OFFSET=4 DESC="Mode select"
signal speed : std_logic_vector(7 downto 0); -- @axion RW REG_NAME=control BIT_OFFSET=8 DESC="Speed value"
YAML:
- name: control
addr: "0x00"
fields:
- name: enable
bit_offset: 0
width: 1
access: RW
description: "Enable bit"
- name: mode
bit_offset: 4
width: 2
access: RW
description: "Mode select"
- name: speed
bit_offset: 8
width: 8
access: RW
description: "Speed value"
XML:
<register name="enable" reg_name="control" addr="0x00" width="1" access="RW" bit_offset="0" description="Enable bit"/>
<register name="mode" reg_name="control" addr="0x00" width="2" access="RW" bit_offset="4" description="Mode select"/>
<register name="speed" reg_name="control" addr="0x00" width="8" access="RW" bit_offset="8" description="Speed value"/>
JSON:
{
"name": "control",
"addr": "0x00",
"fields": [
{"name": "enable", "bit_offset": 0, "width": 1, "access": "RW", "description": "Enable bit"},
{"name": "mode", "bit_offset": 4, "width": 2, "access": "RW", "description": "Mode select"},
{"name": "speed", "bit_offset": 8, "width": 8, "access": "RW", "description": "Speed value"}
]
}
TOML:
[[registers]]
name = "enable"
reg_name = "control"
addr = "0x00"
width = 1
access = "RW"
bit_offset = 0
description = "Enable bit"
[[registers]]
name = "mode"
reg_name = "control"
addr = "0x00"
width = 2
access = "RW"
bit_offset = 4
description = "Mode select"
[[registers]]
name = "speed"
reg_name = "control"
addr = "0x00"
width = 8
access = "RW"
bit_offset = 8
description = "Speed value"
This creates individual signals for each field while sharing one address.
Advanced Subregister Features
Auto-Packing
If BIT_OFFSET is not specified, Axion-HDL automatically packs fields sequentially.
signal field_a : std_logic_vector(7 downto 0); -- @axion RW REG_NAME=ctrl
signal field_b : std_logic_vector(7 downto 0); -- @axion RW REG_NAME=ctrl
-- field_a becomes bits [7:0]
-- field_b becomes bits [15:8]
Default Value Aggregation
Individual field default values are combined into the 32-bit register reset value.
signal enable : std_logic; -- @axion RW REG_NAME=cfg DEFAULT=1
signal mode : std_logic; -- @axion RW REG_NAME=cfg BIT_OFFSET=1 DEFAULT=1
-- Register reset value = 0x00000003
Strobe Aggregation
If any field in a packed register has R_STROBE or W_STROBE enabled, the entire 32-bit register will be generated with that strobe signal.
Mixed Access Modes
A packed register can contain fields with different access modes (e.g., one RO field and one RW field). In this case, the parent register is treated as RW to allow writing to the writable fields, while strict read-only behavior is enforced for RO fields at the logic level.
Overlap Handling
Overlapping bit ranges trigger a validation warning but are strictly allowed. This can be useful for aliasing fields, but ensure you understand the implications for your specific design.
Wide Signals (>32 bits)
Signals wider than 32 bits automatically span multiple addresses.
VHDL:
signal counter : std_logic_vector(63 downto 0); -- @axion RO DESC="64-bit counter"
YAML:
- name: counter
access: RO
width: 64
description: "64-bit counter"
XML:
<register name="counter" access="RO" width="64" description="64-bit counter"/>
JSON:
{
"name": "counter",
"access": "RO",
"width": 64,
"description": "64-bit counter"
}
TOML:
[[registers]]
name = "counter"
access = "RO"
width = 64
description = "64-bit counter"
This creates two consecutive 32-bit registers:
counter[31:0]at offset 0x00counter[63:32]at offset 0x04
Default Values
Set reset values for registers.
VHDL:
signal config : std_logic_vector(31 downto 0); -- @axion RW DEFAULT=0xCAFEBABE DESC="Configuration"
YAML:
- name: config
access: RW
width: 32
default: "0xCAFEBABE"
description: "Configuration"
XML:
<register name="config" access="RW" width="32" default="0xCAFEBABE" description="Configuration"/>
JSON:
{
"name": "config",
"access": "RW",
"width": 32,
"default": "0xCAFEBABE",
"description": "Configuration"
}
TOML:
[[registers]]
name = "config"
access = "RW"
width = 32
default = "0xCAFEBABE"
description = "Configuration"
Manual Address Assignment
Override automatic address allocation.
VHDL:
signal debug_reg : std_logic_vector(31 downto 0); -- @axion RW ADDR=0x100 DESC="Debug register"
Address Conflict Recovery: If two registers are assigned the same manual address, the second one is automatically reassigned to the next available address. A warning is recorded in
parsing_errorsbut neither register is dropped.
YAML:
- name: debug_reg
addr: "0x100"
access: RW
description: "Debug register"
XML:
<register name="debug_reg" addr="0x100" access="RW" description="Debug register"/>
JSON:
{
"name": "debug_reg",
"addr": "0x100",
"access": "RW",
"description": "Debug register"
}
TOML:
[[registers]]
name = "debug_reg"
addr = "0x100"
access = "RW"
description = "Debug register"
Format Comparison
Feature |
VHDL |
YAML |
TOML |
XML |
JSON |
|---|---|---|---|---|---|
Human readable |
✓ |
✓✓ |
✓✓ |
✓ |
✓ |
Version control friendly |
✓ |
✓✓ |
✓✓ |
✓ |
- |
Embedded in RTL |
✓✓ |
- |
- |
- |
- |
Automation friendly |
- |
✓ |
✓ |
✓ |
✓✓ |
Comment support |
✓ |
✓ |
✓ |
✓ |
- |
IP-XACT compatible |
- |
- |
- |
✓ |
- |
Python ecosystem standard |
- |
- |
✓ |
- |
- |
Quick Reference Card
VHDL Annotation Syntax
-- Module definition (anywhere in file)
-- @axion_def BASE_ADDR=0xNNNN [CDC_EN] [CDC_STAGE=N]
-- Register with full attributes
signal name : type; -- @axion ACCESS [ADDR=0xNN] [DESC="..."] [R_STROBE] [W_STROBE] [DEFAULT=0xNN] [REG_NAME=name] [BIT_OFFSET=N]
-- Bare annotation (all defaults: RW, auto address)
signal name : type; -- @axion
SystemVerilog Annotation Syntax
// Module definition (anywhere in file)
// @axion_def BASE_ADDR=0xNNNN [CDC_EN] [CDC_STAGE=N]
// Register with full attributes
logic [31:0] name; // @axion ACCESS [ADDR=0xNN] [DESC="..."] [R_STROBE] [W_STROBE] [DEFAULT=0xNN]
// Bare annotation (all defaults: RW, auto address)
logic [31:0] name; // @axion
YAML Structure
module: module_name
base_addr: "0x0000"
config:
cdc_en: false
cdc_stage: 2
registers:
- name: reg_name
addr: "0x00"
access: RW
width: 32
default: 0
description: "Description"
r_strobe: false
w_strobe: false
XML Structure
<?xml version="1.0" encoding="UTF-8"?>
<register_map module="module_name" base_addr="0x0000">
<config cdc_en="false" cdc_stage="2"/>
<register name="reg_name" addr="0x00" access="RW" width="32"
default="0" description="Description"
r_strobe="false" w_strobe="false"/>
</register_map>
JSON Structure
{
"module": "module_name",
"base_addr": "0x0000",
"config": {
"cdc_en": false,
"cdc_stage": 2
},
"registers": [
{
"name": "reg_name",
"addr": "0x00",
"access": "RW",
"width": 32,
"default": 0,
"description": "Description",
"r_strobe": false,
"w_strobe": false
}
]
}
TOML Structure
module = "module_name"
base_addr = "0x0000"
[config]
cdc_en = false
cdc_stage = 2
[[registers]]
name = "reg_name"
addr = "0x00"
access = "RW"
width = 32
default = 0
description = "Description"
r_strobe = false
w_strobe = false
Enumerated Values (enum_values)
Bit fields can have named states using enum_values. Each value is an integer key mapped to a name string.
VHDL Annotation
signal status_field : std_logic_vector(1 downto 0); -- @axion RW ADDR=0x00 REG_NAME=status_reg BIT_OFFSET=0 ENUM="0:IDLE,1:WAITING,3:READY"
SystemVerilog Annotation
logic [1:0] status_field; // @axion RW ADDR=0x00 ENUM="0:IDLE,1:WAITING,3:READY"
YAML
registers:
- name: status_reg
addr: "0x00"
access: RW
fields:
- name: status
bit_offset: 0
width: 2
enum_values:
0: IDLE
1: WAITING
3: READY
JSON
{
"registers": [
{
"name": "status_reg",
"addr": "0x00",
"access": "RW",
"fields": [
{
"name": "status",
"bit_offset": 0,
"width": 2,
"enum_values": {"0": "IDLE", "1": "WAITING", "3": "READY"}
}
]
}
]
}
TOML
[[registers]]
name = "status_reg"
addr = "0x00"
access = "RW"
[[registers.fields]]
name = "status"
bit_offset = 0
width = 2
[registers.fields.enum_values]
"0" = "IDLE"
"1" = "WAITING"
"3" = "READY"
XML (Simple, Nested)
<register name="status_reg" addr="0x00" access="RW">
<field name="status" bit_offset="0" width="2">
<enum_value value="0" name="IDLE"/>
<enum_value value="1" name="WAITING"/>
<enum_value value="3" name="READY"/>
</field>
</register>
XML (SPIRIT)
<spirit:field>
<spirit:name>status</spirit:name>
<spirit:bitOffset>0</spirit:bitOffset>
<spirit:bitWidth>2</spirit:bitWidth>
<spirit:enumeratedValues>
<spirit:enumeratedValue>
<spirit:name>IDLE</spirit:name>
<spirit:value>0</spirit:value>
</spirit:enumeratedValue>
<spirit:enumeratedValue>
<spirit:name>WAITING</spirit:name>
<spirit:value>1</spirit:value>
</spirit:enumeratedValue>
</spirit:enumeratedValues>
</spirit:field>
Hierarchy File Format
A hierarchy file maps module instances to base addresses, enabling centralized address management and multi-instance generation via --hier. All supported formats (YAML, TOML, JSON, XML) share the same structure and are normalized internally before processing.
Top-Level Structure
Field |
Type |
Required |
Description |
|---|---|---|---|
|
list |
Yes |
List of module instance definitions |
Instance Entry Fields
Field |
Type |
Required |
Description |
|---|---|---|---|
|
string |
Yes |
Name of the source module (must match a parsed module name) |
|
string |
Required when module appears >1× |
Unique instance identifier; used for output file naming |
|
int or hex string |
Yes |
Base address for this instance (e.g. |
YAML
instances:
- module: gtwiz_wrapper
instance: gtwiz_wrapper_0
base_addr: 0x10000
- module: gtwiz_wrapper
instance: gtwiz_wrapper_1
base_addr: 0x11000
- module: spi_master
base_addr: 0x20000
instanceis optional when a module appears only once.
TOML
[[instances]]
module = "gtwiz_wrapper"
instance = "gtwiz_wrapper_0"
base_addr = "0x10000"
[[instances]]
module = "gtwiz_wrapper"
instance = "gtwiz_wrapper_1"
base_addr = "0x11000"
[[instances]]
module = "spi_master"
base_addr = "0x20000"
JSON
{
"instances": [
{ "module": "gtwiz_wrapper", "instance": "gtwiz_wrapper_0", "base_addr": "0x10000" },
{ "module": "gtwiz_wrapper", "instance": "gtwiz_wrapper_1", "base_addr": "0x11000" },
{ "module": "spi_master", "base_addr": "0x20000" }
]
}
XML
<hierarchy>
<instance module="gtwiz_wrapper" name="gtwiz_wrapper_0" base_addr="0x10000"/>
<instance module="gtwiz_wrapper" name="gtwiz_wrapper_1" base_addr="0x11000"/>
<instance module="spi_master" base_addr="0x20000"/>
</hierarchy>
Output Naming Rules
Scenario |
Output filename |
|---|---|
Module appears once, no |
|
Module appears once, |
|
Module appears multiple times |
|