Working with data files

The register parser reads a data file and constructs a RegisterList object. It is important that the data file is formatted correctly and has the necessary properties. The register parser will warn if there are any errors in the data, such as missing properties, unknown properties, wrong data type for properties, etc.

The register parser is implemented in the RegisterParser class. It can be called with the from_toml(), from_json() and from_yaml() functions.

Why TOML?

The TOML data file format is highly recommended for specifying your registers. All the examples on this website use TOML.

It is a configuration file format that is easy to both read and write. Compared to XML, YAML and JSON, which would be the most obvious alternatives, it has the following advantages that are relevant when handling FPGA registers:

  • Supports comments in file.

  • Supports hexadecimal and binary integer values, with optional underscore separator.

  • Clear and readable handling of multiline strings.

  • Duplicate key is an error.

  • Fewer control characters compared to XML and JSON.

  • Very fast Python parser available.

Furthermore, while readability can be considered subjective, the TOML format is considered quite obvious and easy to read.

TOML Format

Below is an example of a typical TOML file, which can be parsed with a call to from_toml(). It sets up:

  1. Two registers with different modes.

    1. One of which contains a bit field and an enumeration field.

  2. A register array with two registers and fields.

  3. An integer constant and a float constant.

See the menu sidebar for details about how to set up the different fields, constants, etc.

Also, see the “generator” articles for insight into the code that can be generated from this definition file. For example, the human-readable documentation from the data below can be seen in the HTML code generator article.

Register TOML format example.
 1################################################################################
 2[config]
 3
 4# The "type" property is OPTIONAL for a register (but required for register arrays and constants).
 5# It is commonly left out for registers to keep line count down.
 6# Will default to "register" if not specified.
 7# The value specified MUST be "register".
 8type = "register"
 9
10# The "mode" property MUST be present for a register.
11# The value specified MUST be a valid shorthand mode name. Either of:
12# * "r" for Read.
13# * "w" for Write.
14# * "r_w" for Read, Write.
15# * "wpulse" for Write-pulse.
16# * "r_wpulse" for Read, Write-pulse.
17# See https://hdl-registers.com/rst/basic_feature/basic_feature_register_modes.html for details.
18mode = "r_w"
19
20# The "description" property is OPTIONAL for a register.
21# Will default to "" if not specified.
22# The value specified MUST be a string.
23description = """
24This is the description of my register.
25
26Rudimentary RST formatting can be used, such as **boldface** and *italics*.
27"""
28
29[config.enable]
30
31type = "bit"
32description = "Enable operation."
33default_value = "1"
34
35[config.direction]
36
37type = "enumeration"
38description = "Set the data direction."
39default_value = "high_z"
40
41element.data_in = "Receive data from outside the FPGA."
42element.high_z = """
43Set pins to high impedance.
44
45Will not process any incoming data, nor send anything out.
46"""
47element.data_out = "Send data from FPGA."
48
49
50################################################################################
51[status]
52
53mode = "r"
54
55
56################################################################################
57[channels]
58
59type = "register_array"
60array_length = 4
61description = "Configuration for each channel."
62
63
64# ------------------------------------------------------------------------------
65[channels.read_address]
66
67mode = "r_w"
68description = "Read address for DMA data."
69
70
71# ------------------------------------------------------------------------------
72[channels.config]
73
74mode = "w"
75description = "Configuration of channel settings."
76
77enable.type = "bit"
78enable.description = "Enable this channel."
79
80tuser.type = "bit_vector"
81tuser.width = 8
82tuser.description = "**TUSER** value for this channel."
83
84
85################################################################################
86[axi_data_width]
87
88type = "constant"
89value = 64
90description = "Data width of the AXI port used by this module."
91
92
93################################################################################
94[clock_rate_hz]
95
96type = "constant"
97value = 156.25e6
98description = "The clock rate used in the system, given in Hertz."

Using JSON data file

The TOML format is highly recommended due to the benefits it offers, listed above. Also all the examples on this website use TOML. However, the tool also supports using JSON data files if that is desired.

In this case you need to construct your JSON data on the exact format as the TOML format above and then parse it with a call to from_json().

Below is an example JSON snippet that sets up some register data:

Register JSON format example.
 1{
 2    "configuration": {
 3        "mode": "r_w",
 4        "description": "Configuration register.",
 5        "enable": {
 6            "type": "bit",
 7            "description": "Enable data passthrough.",
 8            "default_value": "1"
 9        }
10    },
11    "status": {
12        "mode": "r",
13        "description": "General status register."
14    },
15    "data_width": {
16        "type": "constant",
17        "value": 64,
18        "description": "The width of the AXI port used by the module."
19    },
20    "clk_frequency_hz": {
21        "type": "constant",
22        "value": 156250000.0,
23        "description": "The system clock frequency in Hertz."
24    }
25}

Using YAML data file

The TOML format is highly recommended due to the benefits it offers, listed above. Also all the examples on this website use TOML. However, the tool also supports using YAML data files if that is desired.

In this case you need to construct your YAML data on the exact format as the TOML format above and then parse it with a call to from_yaml().

Below is an example YAML snippet that sets up some register data:

Register YAML format example.
 1configuration:
 2  mode: r_w
 3  description: Configuration register.
 4  enable:
 5    type: bit
 6    default_value: '1'
 7    description: Enable data passthrough.
 8
 9status:
10  mode: r
11  description: General status register.
12
13clk_frequency_hz:
14  type: constant
15  value: 156250000.0
16  description: The system clock frequency in Hertz.
17
18data_width:
19  type: constant
20  value: 64
21  description: The width of the AXI port used by the module.

Other data file formats

The TOML format is highly recommended due to the benefits it offers, listed above. We also officially support parsing JSON and YAML, see above. However, if you want to parse some other exotic data file format then that is also possible.

The parser functions, e.g. from_toml(), are just very thin wrappers around the RegisterParser class. The RegisterParser class takes register data as a Python dictionary and returns the high-level object RegisterList. So in order to implement your own parser, simply read or construct the data as Python dictionary in the exact format as described above and pass it to RegisterParser.

If you write a parser that you think others might have use for, please consider contributing to this project by creating an issue or a pull request.