Register arrays
A register list, i.e. the register set of one module, can contain register arrays. Meaning, a set of registers within the register list that are repeated a number of times.
This page will show you how to set up register arrays, and will showcase all the code that can be generated from it.
Usage in TOML
The TOML file below shows how to set up a register array. See comments for rules about the different properties.
1[base_addresses]
2
3# The "type" property MUST be present and set to "register_array".
4type = "register_array"
5
6# The "array_length" property MUST be present for a register array.
7# The value specified MUST be a positive integer.
8# The registers within the array will be repeated this many times.
9array_length = 3
10
11# The "description" property is OPTIONAL for a register array.
12# Will default to "" if not specified.
13# The value specified MUST be a string.
14description = "One set of base addresses for each feature."
15
16
17# ------------------------------------------------------------------------------
18# This will allocate a register "read_address" in the "base_addresses" array.
19[base_addresses.read_address]
20
21# Registers in a register array follow the exact same rules as "plain" registers.
22# The properties that MUST be set or are OPTIONAL are the same.
23# Just as with plain registers, the "type" property can be left out or explicitly set to "register".
24# Fields (bits, bit vectors, ...) can be added to array registers in the same way.
25mode = "r_w"
26
27# This will allocate a bit vector field named "address" in the "read_address" register within
28# the "base_addresses" array.
29address.type = "bit_vector"
30address.width = 28
31address.description = "Read address for a 256 MiB address space."
32
33
34# ------------------------------------------------------------------------------
35[base_addresses.write_address]
36
37mode = "r_w"
38
39address.type = "bit_vector"
40address.width = 28
41address.description = "Write address for a 256 MiB address space."
Below you will see how you can parse this TOML file and generate artifacts from it.
Usage with Python API
The Python code below shows
How to parse the TOML file listed above.
How to create an identical register list when instead using the Python API.
How to generate register artifacts.
Note that the result of the create_from_api
call is identical to that of the
parse_toml
call.
Meaning that using a TOML file or using the Python API is completely equivalent.
You choose yourself which method you want to use in your code base.
1# Standard libraries
2import sys
3from pathlib import Path
4
5# First party libraries
6from hdl_registers.generator.c.header import CHeaderGenerator
7from hdl_registers.generator.cpp.implementation import CppImplementationGenerator
8from hdl_registers.generator.cpp.interface import CppInterfaceGenerator
9from hdl_registers.generator.html.page import HtmlPageGenerator
10from hdl_registers.generator.vhdl.record_package import VhdlRecordPackageGenerator
11from hdl_registers.generator.vhdl.register_package import VhdlRegisterPackageGenerator
12from hdl_registers.parser.toml import from_toml
13from hdl_registers.register_list import RegisterList
14from hdl_registers.register_modes import REGISTER_MODES
15
16THIS_DIR = Path(__file__).parent
17
18
19def parse_toml() -> RegisterList:
20 """
21 Create the register list by parsing a TOML data file.
22 """
23 return from_toml(
24 name="caesar",
25 toml_file=THIS_DIR.parent / "toml" / "basic_feature_register_array.toml",
26 )
27
28
29def create_from_api() -> RegisterList:
30 """
31 Alternative method: Create the register list by using the Python API.
32 """
33 register_list = RegisterList(name="caesar")
34
35 register_array = register_list.append_register_array(
36 name="base_addresses", length=3, description="One set of base addresses for each feature."
37 )
38
39 register = register_array.append_register(
40 name="read_address", mode=REGISTER_MODES["r_w"], description=""
41 )
42
43 register.append_bit_vector(
44 name="address",
45 description="Read address for a 256 MiB address space.",
46 width=28,
47 default_value="0000000000000000000000000000",
48 )
49
50 register = register_array.append_register(
51 name="write_address", mode=REGISTER_MODES["r_w"], description=""
52 )
53
54 register.append_bit_vector(
55 name="address",
56 description="Write address for a 256 MiB address space.",
57 width=28,
58 default_value="0000000000000000000000000000",
59 )
60
61 return register_list
62
63
64def generate(register_list: RegisterList, output_folder: Path):
65 """
66 Generate the artifacts that we are interested in.
67 """
68 CHeaderGenerator(register_list=register_list, output_folder=output_folder).create()
69
70 CppImplementationGenerator(register_list=register_list, output_folder=output_folder).create()
71 CppInterfaceGenerator(register_list=register_list, output_folder=output_folder).create()
72
73 HtmlPageGenerator(register_list=register_list, output_folder=output_folder).create()
74
75 VhdlRegisterPackageGenerator(register_list=register_list, output_folder=output_folder).create()
76 VhdlRecordPackageGenerator(register_list=register_list, output_folder=output_folder).create()
77
78
79def main(output_folder: Path):
80 generate(register_list=parse_toml(), output_folder=output_folder / "toml")
81 generate(register_list=create_from_api(), output_folder=output_folder / "api")
82
83
84if __name__ == "__main__":
85 main(output_folder=Path(sys.argv[1]))
See RegisterList.append_register_array()
for more Python API details.
Generated code
See below for a description of the code that can be generated when using register arrays.
HTML page
See HTML file below for the human-readable documentation that is produced by the
generate()
call in the Python example above.
See HTML code generator for more details about the HTML generator and its capabilities.
VHDL package
The VHDL code below is produced by the generate()
call in the Python example above.
Click the button to expand and view the code.
See VHDL code generator for instructions on how it can be used in your VHDL project.
Base register package
Note how the register indexes are functions here, as opposed to constants as they usually are for plain registers. The argument to the function decides which array index to use. There is an assertion that the array index argument does not exceed the number of times the register array is repeated.
Record package
The record package is quite hard to understand in this example, but lets try:
The
caesar_regs_down_t
type is a record with a memberbase_addresses
, which is the name of the register array.The type of this member is a ranged array of another record with two members:
read_address
andwrite_address
, which are the names of the registers in the array.Both of these are of a record type that contain the
address
bit vector field set up in this example.
So in our VHDL code we can access a field value for example like this:
job.address <= regs_down.base_addresses[1].read_address.address;
C++
The C++ interface header and implementation code below is produced by the generate()
call in
the Python example above.
Click the button to expand and view each code block.
The class header is skipped here, since its inclusion would make this page very long. See C++ code generator for more details and an example of how the excluded file might look.
C++ interface header
Note how setters and getters for register and field values have a new argument for the array index.
C++ implementation
The C++ implementation code below is produced by the generate()
call in the Python
example above.
Click the button to expand and view the code.
Note that there is an assertion in every setter and getter that the provided array index does not exceed the number of times the register array is repeated. This will catch calculation errors during testing and at run-time.
C header
The C code below is produced by the generate()
call in the Python example above.
The index and address of each register are given by a macro where the array index is supplied as
an argument.