Enumeration fields
Register fields can be of the type enumeration. Meaning, a field that can only take on a limited, pre-defined, and named, set of values. This is a very common and highly useful programming concept. See Wikipedia if you are unfamiliar with this: https://en.wikipedia.org/wiki/Enumerated_type
This page will show you how the set up enumeration fields in a register, 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 with two enumeration fields. See comments for rules about the different properties.
1[config]
2
3mode = "r_w"
4description = "Configuration register."
5
6# This will allocate an enumeration field named "severity_level" in the "config" register.
7[config.severity_level]
8
9# The "type" property MUST be present and set to "enumeration".
10type = "enumeration"
11
12# The "description" property is OPTIONAL for an enumeration field.
13# Will default to "" if not specified.
14# The value specified MUST be a string.
15description = "Run-time configuration of severity."
16
17# The "default_value" property is OPTIONAL for an enumeration field.
18# Will default to the first enumeration element declared below, if not specified.
19# The value specified MUST be a string that matches one of the enumeration element names
20# specified below.
21default_value = "warning"
22
23# For an enumeration field there MUST be at least one enumeration element declared.
24# The name of each element is mapped below to the description of that element.
25element.info = "Informational message. Is not considered an error."
26element.warning = "Warning message. Is not considered an error."
27element.error = "Error message. Is considered an error."
28element.failure = "Failure message. Is considered an error."
29
30
31[config.packet_source]
32
33type = "enumeration"
34description = "Set input mux."
35
36element.streaming = "Process incoming streaming data."
37element.dma = "Read packets from DMA."
38element.none = "Don't send anything."
Note that the second field does not have any default value, meaning that it will automatically
default to the first element (streaming
).
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(name="caesar", toml_file=THIS_DIR.parent / "toml" / "field_enumeration.toml")
24
25
26def create_from_api() -> RegisterList:
27 """
28 Alternative method: Create the register list by using the Python API.
29 """
30 register_list = RegisterList(name="caesar")
31
32 register = register_list.append_register(
33 name="config", mode=REGISTER_MODES["r_w"], description="Configuration register."
34 )
35
36 register.append_enumeration(
37 name="severity_level",
38 description="Run-time configuration of severity.",
39 elements={
40 "info": "Informational message. Is not considered an error.",
41 "warning": "Warning message. Is not considered an error.",
42 "error": "Error message. Is considered an error.",
43 "failure": "Failure message. Is considered an error.",
44 },
45 default_value="warning",
46 )
47
48 register.append_enumeration(
49 name="packet_source",
50 description="Set input mux.",
51 elements={
52 "streaming": "Process incoming streaming data.",
53 "dma": "Read packets from DMA.",
54 "none": "Don't send anything.",
55 },
56 default_value="streaming",
57 )
58
59 return register_list
60
61
62def generate(register_list: RegisterList, output_folder: Path):
63 """
64 Generate the artifacts that we are interested in.
65 """
66 CHeaderGenerator(register_list=register_list, output_folder=output_folder).create()
67
68 CppImplementationGenerator(register_list=register_list, output_folder=output_folder).create()
69 CppInterfaceGenerator(register_list=register_list, output_folder=output_folder).create()
70
71 HtmlPageGenerator(register_list=register_list, output_folder=output_folder).create()
72
73 VhdlRegisterPackageGenerator(register_list=register_list, output_folder=output_folder).create()
74 VhdlRecordPackageGenerator(register_list=register_list, output_folder=output_folder).create()
75
76
77def main(output_folder: Path):
78 generate(register_list=parse_toml(), output_folder=output_folder / "toml")
79 generate(register_list=create_from_api(), output_folder=output_folder / "api")
80
81
82if __name__ == "__main__":
83 main(output_folder=Path(sys.argv[1]))
See Register.append_enumeration()
for more Python API details.
Generated code
See below for a description of the code that can be generated when using enumeration fields.
HTML page
See HTML file below for the human-readable documentation that is produced by the
generate()
call in the Python example above.
Each enumeration field is documented and the description of each element is included.
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
Some interesting things to notice:
There is only one register, at index 0.
The first field is two bits wide, occupying bits 1 and 0, while the second one is only one bit wide, occupying but 3.
VHDL supports enumeration types natively. The elements of the enumeration are exposed to the scope of the package, and all files that include it.
For each enumeration field, there are conversion functions for
Converting from the enumeration type to
std_logic_vector
.Slicing a register value at the correct range and converting from
std_logic_vector
to enumeration.
Record package
The caesar_regs_down_t
type is a record with a member config
, the only register in
this example.
The type of the config
member is another record with the two enumerations set up in
our example: severity_level
and packet_source
.
These are of enumeration types defined in the base register package above.
In our VHDL code we can access a field value for example like this:
if regs_down.config.packet_source = packet_source_streaming then
result_valid <= input_valid;
elsif regs_down.config.packet_source = packet_source_dma then
result_valid <= job_valid;
else
-- packet_source_none
result_valid <= '0';
end if;
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
Some interesting things to notice in the interface header:
The valid enumeration values are defined using a C++
enum
declaration in the namespace of each field.The setters and getters for each field value use the enumeration type as argument or return value.
C++ implementation
Note that each getter casts a uint32_t
value read from the register bus to the enumeration type.
C header
The C code below is produced by the generate()
call in the Python example above.
The range and mask of the each field are available as constants.
Note how the valid enumeration values are defined using a C enum
declaration.