# 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** | `.vhd`, `.vhdl` | Embedded in existing RTL code |
| **SystemVerilog** | `.sv`, `.svh` | Embedded in existing RTL code |
| **YAML** | `.yaml`, `.yml` | Human-readable, version control friendly |
| **XML** | `.xml` | IP-XACT compatible, tool integration |
| **JSON** | `.json` | Automation and scripting |
| **TOML** | `.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:
```vhdl
-- @axion_def BASE_ADDR=0x1000 CDC_EN CDC_STAGE=3
```
### SystemVerilog (`@axion_def`)
Use `//` comments for module configuration:
```systemverilog
// @axion_def BASE_ADDR=0x1000 CDC_EN CDC_STAGE=3
```
### YAML/TOML/XML/JSON
| Attribute | YAML | TOML | XML | JSON | Description | Default |
|-----------|------|------|-----|------|-------------|---------|
| Module Name | `module:` | `module =` | `module=""` | `"module":` | Module/entity name | Required |
| Base Address | `base_addr:` | `base_addr =` | `base_addr=""` | `"base_addr":` | Starting address (hex string) | `0x0000` |
| CDC Enable | `config.cdc_en:` | `[config]`
`cdc_en =` | `` | `"config":{"cdc_en":}` | Enable clock domain crossing | `false` |
| CDC Stages | `config.cdc_stage:` | `[config]`
`cdc_stage =` | `` | `"config":{"cdc_stage":}` | Synchronizer stages (2-5) | `2` |
### VHDL Module Attributes Table
| Attribute | Syntax | Description | Default |
|-----------|--------|-------------|---------|
| `BASE_ADDR` | `BASE_ADDR=0xNNNN` | Module base address | `0x0000` |
| `CDC_EN` | `CDC_EN` or `CDC_EN=true` | Enable CDC synchronizers | `false` |
| `CDC_STAGE` | `CDC_STAGE=N` | Number of sync stages (2-5) | `2` |
---
## Register-Level Attributes
### Access Modes
| Mode | Software | Hardware | Description |
|------|----------|----------|-------------|
| `RO` | Read | Write | Hardware writes, software reads |
| `WO` | Write | Read | Software writes, hardware reads |
| `RW` | Read/Write | Read/Write | Both can read and write |
### Complete Attribute Reference
#### VHDL Register Attributes (`@axion`)
```vhdl
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:
```vhdl
signal my_reg : std_logic_vector(31 downto 0); -- @axion
-- Defaults to: RW access, auto-assigned address, no strobes
```
#### SystemVerilog Register Attributes (`@axion`)
```systemverilog
logic [31:0] reg_name; // @axion ACCESS [OPTIONS]
```
Attributes are optional — a bare `// @axion` annotation is valid and uses all defaults:
```systemverilog
logic [31:0] my_reg; // @axion
// Defaults to: RW access, auto-assigned address, no strobes
```
| Attribute | Syntax | Description | Default |
|-----------|--------|-------------|---------|
| Access Mode | `RO`, `WO`, `RW` | Register access type | `RW` |
| Address | `ADDR=0xNN` | Manual address assignment | Auto-assigned |
| Description | `DESC="text"` | Register description | Empty |
| Read Strobe | `R_STROBE` | Generate read strobe signal | `false` |
| Write Strobe | `W_STROBE` | Generate write strobe signal | `false` |
| Default Value | `DEFAULT=0xNN` | Reset value | `0x0` |
| Register Name | `REG_NAME=name` | Group into packed register | Signal name |
| Bit Offset | `BIT_OFFSET=N` | Bit position in packed reg | `0` |
#### YAML Register Attributes
```yaml
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 |
|-----------|------|-------------|---------|
| `name` | string | Register/signal name | Required |
| `addr` | string | Address in hex (e.g., "0x04") | Auto-assigned |
| `access` | string | `RO`, `WO`, or `RW` | Required |
| `width` | integer | Bit width (1-1024) | `32` |
| `default` | string/int | Reset value | `0` |
| `description` | string | Documentation text | Empty |
| `r_strobe` | boolean | Generate read strobe | `false` |
| `w_strobe` | boolean | Generate write strobe | `false` |
| `fields` | array | Subregister field definitions | None |
#### XML Register Attributes
```xml
```
| Attribute | Type | Description | Default |
|-----------|------|-------------|---------|
| `name` | string | Register name | Required |
| `addr` | string | Address in hex | Auto-assigned |
| `access` | string | `RO`, `WO`, or `RW` | Required |
| `width` | integer | Bit width | `32` |
| `default` | string | Reset value | `0` |
| `description` | string | Documentation | Empty |
| `r_strobe` | string | `"true"` or `"false"` | `"false"` |
| `w_strobe` | string | `"true"` or `"false"` | `"false"` |
| `reg_name` | string | Packed register name | None |
| `bit_offset` | integer | Bit position for packed | `0` |
#### JSON Register Attributes
```json
{
"name": "reg_name",
"addr": "0x00",
"access": "RW",
"width": 32,
"default": "0x00",
"description": "Description text",
"r_strobe": true,
"w_strobe": true
}
```
| Attribute | Type | Description | Default |
|-----------|------|-------------|---------|
| `name` | string | Register name | Required |
| `addr` | string | Address in hex | Auto-assigned |
| `access` | string | `RO`, `WO`, or `RW` | Required |
| `width` | integer | Bit width | `32` |
| `default` | string/int | Reset value | `0` |
| `description` | string | Documentation | Empty |
| `r_strobe` | boolean | Generate read strobe | `false` |
| `w_strobe` | boolean | Generate write strobe | `false` |
| `fields` | array | Subregister fields | None |
#### TOML Register Attributes
```toml
[[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 |
|-----------|------|-------------|---------|
| `name` | string | Register/signal name | Required |
| `addr` | string | Address in hex (e.g., "0x04") | Auto-assigned |
| `access` | string | `"RO"`, `"WO"`, or `"RW"` | Required |
| `width` | integer | Bit width (1-1024) | `32` |
| `default` | string/int | Reset value | `0` |
| `description` | string | Documentation text | Empty |
| `r_strobe` | boolean | Generate read strobe | `false` |
| `w_strobe` | boolean | Generate write strobe | `false` |
| `reg_name` | string | Packed register name | None |
| `bit_offset` | integer | Bit position for packed | `0` |
---
## Advanced Features
### Clock Domain Crossing (CDC)
Enable CDC synchronizers when your AXI bus and register logic use different clocks.
**VHDL:**
```vhdl
-- @axion_def BASE_ADDR=0x1000 CDC_EN CDC_STAGE=3
```
**YAML:**
```yaml
module: my_module
base_addr: "0x1000"
config:
cdc_en: true
cdc_stage: 3
```
**XML:**
```xml
```
**JSON:**
```json
{
"module": "my_module",
"base_addr": "0x1000",
"config": {
"cdc_en": true,
"cdc_stage": 3
}
}
```
**TOML:**
```toml
module = "my_module"
base_addr = "0x1000"
[config]
cdc_en = true
cdc_stage = 3
```
| Parameter | Description | Default |
|-----------|-------------|---------|
| `CDC_EN` / `cdc_en` | Enable CDC synchronizers | `false` |
| `CDC_STAGE` / `cdc_stage` | Number of synchronizer stages | `2` |
---
### Strobe Signals
Strobe signals provide single-cycle pulses on read/write operations.
**VHDL:**
```vhdl
signal irq_status : std_logic_vector(31 downto 0); -- @axion RW R_STROBE W_STROBE DESC="Interrupt status"
```
**YAML:**
```yaml
- name: irq_status
access: RW
r_strobe: true
w_strobe: true
description: "Interrupt status"
```
**XML:**
```xml
```
**JSON:**
```json
{
"name": "irq_status",
"access": "RW",
"r_strobe": true,
"w_strobe": true,
"description": "Interrupt status"
}
```
**TOML:**
```toml
[[registers]]
name = "irq_status"
access = "RW"
r_strobe = true
w_strobe = true
description = "Interrupt status"
```
**Use Cases:**
- `R_STROBE`: Clear-on-read status registers
- `W_STROBE`: Trigger actions on write (start transfer, clear flags)
---
### Subregisters (Packed Fields)
Pack multiple fields into a single 32-bit register address.
**VHDL:**
```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:**
```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:**
```xml
```
**JSON:**
```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:**
```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.
```vhdl
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.
```vhdl
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:**
```vhdl
signal counter : std_logic_vector(63 downto 0); -- @axion RO DESC="64-bit counter"
```
**YAML:**
```yaml
- name: counter
access: RO
width: 64
description: "64-bit counter"
```
**XML:**
```xml
```
**JSON:**
```json
{
"name": "counter",
"access": "RO",
"width": 64,
"description": "64-bit counter"
}
```
**TOML:**
```toml
[[registers]]
name = "counter"
access = "RO"
width = 64
description = "64-bit counter"
```
This creates two consecutive 32-bit registers:
- `counter[31:0]` at offset 0x00
- `counter[63:32]` at offset 0x04
---
### Default Values
Set reset values for registers.
**VHDL:**
```vhdl
signal config : std_logic_vector(31 downto 0); -- @axion RW DEFAULT=0xCAFEBABE DESC="Configuration"
```
**YAML:**
```yaml
- name: config
access: RW
width: 32
default: "0xCAFEBABE"
description: "Configuration"
```
**XML:**
```xml
```
**JSON:**
```json
{
"name": "config",
"access": "RW",
"width": 32,
"default": "0xCAFEBABE",
"description": "Configuration"
}
```
**TOML:**
```toml
[[registers]]
name = "config"
access = "RW"
width = 32
default = "0xCAFEBABE"
description = "Configuration"
```
---
### Manual Address Assignment
Override automatic address allocation.
**VHDL:**
```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_errors` but neither register is dropped.
**YAML:**
```yaml
- name: debug_reg
addr: "0x100"
access: RW
description: "Debug register"
```
**XML:**
```xml
```
**JSON:**
```json
{
"name": "debug_reg",
"addr": "0x100",
"access": "RW",
"description": "Debug register"
}
```
**TOML:**
```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
```vhdl
-- 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
```systemverilog
// 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
```yaml
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
```
### JSON Structure
```json
{
"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
```toml
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
```vhdl
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
```systemverilog
logic [1:0] status_field; // @axion RW ADDR=0x00 ENUM="0:IDLE,1:WAITING,3:READY"
```
### YAML
```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
```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
```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)
```xml
```
### XML (SPIRIT)
```xml
status
0
2
IDLE
0
WAITING
1
```
---
## 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 |
|-------|------|----------|-------------|
| `instances` | list | Yes | List of module instance definitions |
### Instance Entry Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `module` | string | Yes | Name of the source module (must match a parsed module name) |
| `instance` | string | Required when module appears >1× | Unique instance identifier; used for output file naming |
| `base_addr` | int or hex string | Yes | Base address for this instance (e.g. `0x10000` or `65536`) |
### YAML
```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
```
> `instance` is optional when a module appears only once.
### TOML
```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
```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
```xml
```
### Output Naming Rules
| Scenario | Output filename |
|----------|----------------|
| Module appears once, no `instance` | `_axion_reg.vhd` (unchanged) |
| Module appears once, `instance` given | `_axion_reg.vhd` |
| Module appears multiple times | `_axion_reg.vhd` per entry |