Coverage for hdl_registers/register_c_generator.py: 99%
100 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-29 22:03 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-29 22:03 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the hdl_registers project, a HDL register generator fast enough to be run
5# in real time.
6# https://hdl-registers.com
7# https://gitlab.com/hdl_registers/hdl_registers
8# --------------------------------------------------------------------------------------------------
10# Local folder libraries
11from .register import REGISTER_MODES, Register
12from .register_code_generator import RegisterCodeGenerator
15class RegisterCGenerator(RegisterCodeGenerator):
16 """
17 Generate a C code header with register information.
19 There is no unit test of this class that checks the generated code. It is instead functionally
20 tested in the file test_register_compilation.py. That test generates C code from an example
21 register set, compiles it and performs some run-time assertions in a C program.
22 That test is considered more meaningful and exhaustive than a unit test would be.
23 """
25 def __init__(self, module_name, generated_info):
26 """
27 Arguments:
28 module_name (str): The name of the register map.
29 generated_info (list(str)): Will be placed in the file headers.
30 """
31 self.module_name = module_name
32 self.generated_info = generated_info
34 def get_header(self, register_objects, constants):
35 """
36 Get a complete C header with all constants and all registers.
38 Arguments:
39 register_objects (list): Register arrays and registers to be included.
40 constants (list(Constant)): Constants to be included.
42 Returns:
43 str: C code.
44 """
45 define_name = self.module_name.upper() + "_REGS_H"
47 c_code = f"""\
48{self._file_header()}
49#ifndef {define_name}
50#define {define_name}
52{self._constants(constants)}
53{self._number_of_registers(register_objects)}
54{self._register_struct(register_objects)}
55{self._register_defines(register_objects)}\
56#endif {self._comment(define_name)}"""
58 return c_code
60 @staticmethod
61 def _comment(comment, indentation=0):
62 indent = " " * indentation
63 return f"{indent}// {comment}\n"
65 def _file_header(self):
66 return "".join([self._comment(header_line) for header_line in self.generated_info])
68 def _register_struct(self, register_objects):
69 array_structs = ""
71 register_struct_type = f"{self.module_name}_regs_t"
72 register_struct = self._comment("Type for this register map.")
73 register_struct += f"typedef struct {register_struct_type}\n"
74 register_struct += "{\n"
75 for register_object in register_objects:
76 if isinstance(register_object, Register):
77 register_struct += self._comment_block(register_object.description, indentation=2)
78 register_struct += self._comment(
79 f'Mode "{REGISTER_MODES[register_object.mode].mode_readable}".', indentation=2
80 )
81 register_struct += f" uint32_t {register_object.name};\n"
83 else:
84 array_struct_type = f"{self.module_name}_{register_object.name}_t"
86 array_structs += self._comment(
87 f'Type for the "{register_object.name}" register array.'
88 )
89 array_structs += f"typedef struct {array_struct_type}\n"
90 array_structs += "{\n"
91 for register in register_object.registers:
92 array_structs += self._comment_block(register.description, indentation=2)
93 array_structs += self._comment(
94 f'Mode "{REGISTER_MODES[register.mode].mode_readable}".', indentation=2
95 )
96 array_structs += f" uint32_t {register.name};\n"
97 array_structs += f"}} {array_struct_type};\n\n"
99 register_struct += (
100 f" {array_struct_type} {register_object.name}[{register_object.length}];\n"
101 )
102 register_struct += f"}} {register_struct_type};\n"
103 return array_structs + register_struct
105 def _number_of_registers(self, register_objects):
106 # It is possible that we have constants but no registers
107 num_regs = 0
108 if register_objects:
109 num_regs = register_objects[-1].index + 1
111 c_code = self._comment("Number of registers within this register map.")
112 c_code += f"#define {self.module_name.upper()}_NUM_REGS ({num_regs}u)\n"
114 return c_code
116 def _register_defines(self, register_objects):
117 c_code = ""
118 for register, register_array in self._iterate_registers(register_objects):
119 c_code += self._addr_define(register, register_array)
120 c_code += self._field_definitions(register, register_array)
121 c_code += "\n"
123 return c_code
125 def _addr_define(self, register, register_array):
126 name = self._register_define_name(register, register_array)
127 mode_string = f'Mode "{REGISTER_MODES[register.mode].mode_readable}".'
129 if register_array is None:
130 c_code = self._comment(f'Address of the "{register.name}" register. {mode_string}')
131 c_code += self._comment_block(register.description)
133 c_code += f"#define {name}_INDEX ({register.index}u)\n"
134 c_code += f"#define {name}_ADDR (4u * {name}_INDEX)\n"
135 else:
136 title = (
137 f'Address of the "{register.name}" register within the "{register_array.name}"'
138 f" register array (array_index < {register_array.length}). {mode_string}"
139 )
140 c_code = self._comment(title)
141 c_code += self._comment_block(register.description)
143 c_code += (
144 f"#define {name}_INDEX(array_index) ({register_array.base_index}u + "
145 f"(array_index) * {len(register_array.registers)}u + {register.index}u)\n"
146 )
147 c_code += f"#define {name}_ADDR(array_index) (4u * {name}_INDEX(array_index))\n"
149 return c_code
151 def _field_definitions(self, register, register_array):
152 register_name = self._register_define_name(register, register_array)
153 register_string = f'"{register.name}" register'
154 if register_array is not None:
155 register_string += f' within the "{register_array.name}" register array'
157 c_code = ""
158 for field in register.fields:
159 c_code += self._comment(
160 f'Mask and shift for the "{field.name}" field in the {register_string}.'
161 )
162 c_code += self._comment_block(field.description)
164 field_name = f"{register_name}_{field.name.upper()}"
165 c_code += f"#define {field_name}_SHIFT ({field.base_index}u)\n"
166 c_code += f'#define {field_name}_MASK (0b{"1" * field.width}u << {field.base_index}u)\n'
167 c_code += f"#define {field_name}_MASK_INVERSE (~{field_name}_MASK)\n"
169 return c_code
171 def _register_define_name(self, register, register_array):
172 if register_array is None:
173 name = f"{self.module_name}_{register.name}"
174 else:
175 name = f"{self.module_name}_{register_array.name}_{register.name}"
176 return name.upper()
178 def _constants(self, constants):
179 c_code = ""
180 for constant in constants:
181 if constant.is_boolean:
182 value = str(constant.value).lower()
183 elif constant.is_integer:
184 # No suffix -> "int", i.e. signed integer of at least 32 bits
185 value = str(constant.value)
186 elif constant.is_float:
187 # "f" suffix -> "float" (as opposed to "double", to match the VHDL type)
188 value = f"{constant.value}f"
189 else:
190 raise ValueError(f"Got unexpected constant type. {constant}")
192 c_code += self._comment(f'Register constant "{constant.name}".')
193 c_code += self._comment_block(constant.description)
194 c_code += f"#define {self.module_name.upper()}_{constant.name.upper()} ({value})\n"
196 return c_code