Coverage for hdl_registers/generator/c/header.py: 98%
108 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-01 20:50 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-01 20:50 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the hdl-registers project, an HDL register generator fast enough to run
5# in real time.
6# https://hdl-registers.com
7# https://github.com/hdl-registers/hdl-registers
8# --------------------------------------------------------------------------------------------------
10# Standard libraries
11from pathlib import Path
12from typing import TYPE_CHECKING, Any, Optional
14# First party libraries
15from hdl_registers.constant.bit_vector_constant import UnsignedVectorConstant
16from hdl_registers.constant.boolean_constant import BooleanConstant
17from hdl_registers.constant.float_constant import FloatConstant
18from hdl_registers.constant.integer_constant import IntegerConstant
19from hdl_registers.constant.string_constant import StringConstant
20from hdl_registers.field.enumeration import Enumeration
21from hdl_registers.generator.register_code_generator import RegisterCodeGenerator
22from hdl_registers.register import Register
23from hdl_registers.register_list import RegisterList
25if TYPE_CHECKING:
26 # First party libraries
27 from hdl_registers.register_array import RegisterArray
29# There is no unit test of this class that checks the generated code. It is instead functionally
30# tested in the file 'test_compiled_c_code.py'. That test generates C code from an example
31# register set, compiles it and performs some run-time assertions in a C program.
32# That test is considered more meaningful and exhaustive than a unit test would be.
35class CHeaderGenerator(RegisterCodeGenerator):
36 """
37 Generate a C header.
38 See the :ref:`generator_c` article for usage details.
40 The header will contain:
42 * Constant values for all :ref:`register constants <constant_overview>`.
44 * Enumeration types for all :ref:`field_enumeration`.
46 * A ``struct`` type with all registers as members, which can be memory mapped directly.
48 * For each register, ``#define`` constants with the index and address of the register.
50 * For each field in each register, ``#define`` constants with the bit shift, bit mask and
51 inverse bit mask of the field.
52 """
54 __version__ = "1.0.0"
56 SHORT_DESCRIPTION = "C header"
58 COMMENT_START = "//"
60 # The most commonly used indentation.
61 # For code at the top level.
62 DEFAULT_INDENTATION_LEVEL = 0
64 @property
65 def output_file(self) -> Path:
66 """
67 Result will be placed in this file.
68 """
69 return self.output_folder / self._file_name
71 def __init__(
72 self, register_list: RegisterList, output_folder: Path, file_name: Optional[str] = None
73 ):
74 """
75 For argument description, please see the super class.
77 Arguments:
78 file_name: Optionally specify an explicit result file name.
79 If not specified, the name will be derived from the name of the register list.
80 """
81 super().__init__(register_list=register_list, output_folder=output_folder)
83 self._file_name = f"{self.name}_regs.h" if file_name is None else file_name
85 def get_code(self, **kwargs: Any) -> str:
86 """
87 Get a complete C header with all constants and all registers.
88 """
89 define_name = f"{self.name.upper()}_REGS_H"
91 c_code = f"""\
92{self.header}
93#ifndef {define_name}
94#define {define_name}
96{self._constants()}
97{self._number_of_registers()}
98{self._register_struct()}
99{self._register_defines()}\
100#endif {self.comment(define_name)}"""
102 return c_code
104 def _register_struct(self) -> str:
105 array_structs = ""
107 register_struct_type = f"{self.name}_regs_t"
109 register_struct = self.comment("Type for this register map.")
110 register_struct += f"typedef struct {register_struct_type}\n"
111 register_struct += "{\n"
113 for register_object in self.iterate_register_objects():
114 if isinstance(register_object, Register):
115 register_struct += self.comment(f'Mode "{register_object.mode.name}".', indent=2)
116 register_struct += f" uint32_t {register_object.name};\n"
118 else:
119 array_struct_type = f"{self.name}_{register_object.name}_t"
121 array_structs += self.comment(
122 f"Type for the '{register_object.name}' register array."
123 )
124 array_structs += f"typedef struct {array_struct_type}\n"
125 array_structs += "{\n"
126 for register in register_object.registers:
127 array_structs += self.comment(f"Mode '{register.mode.name}'.", indent=2)
128 array_structs += f" uint32_t {register.name};\n"
129 array_structs += f"}} {array_struct_type};\n\n"
131 register_struct += (
132 f" {array_struct_type} {register_object.name}[{register_object.length}];\n"
133 )
135 register_struct += f"}} {register_struct_type};\n"
137 return array_structs + register_struct
139 def _number_of_registers(self) -> str:
140 # It is possible that we have constants but no registers
141 num_regs = 0
142 if self.register_list.register_objects:
143 num_regs = self.register_list.register_objects[-1].index + 1
145 c_code = self.comment("Number of registers within this register map.")
146 c_code += f"#define {self.name.upper()}_NUM_REGS ({num_regs}u)\n"
148 return c_code
150 def _register_defines(self) -> str:
151 c_code = ""
152 for register, register_array in self.iterate_registers():
153 c_code += self._addr_define(register, register_array)
154 c_code += self._field_definitions(register, register_array)
155 c_code += "\n"
157 return c_code
159 def _addr_define(self, register: Register, register_array: Optional["RegisterArray"]) -> str:
160 name = self.qualified_register_name(
161 register=register, register_array=register_array
162 ).upper()
164 register_description = self.register_description(
165 register=register, register_array=register_array
166 )
168 register_array_comment = (
169 f" (array_index < {register_array.length})" if register_array else ""
170 )
171 comment = [
172 f"Address of the {register_description}{register_array_comment}.",
173 f"Mode '{register.mode.name}'.",
174 ]
175 c_code = self.comment_block(comment)
177 if register_array:
178 c_code += (
179 f"#define {name}_INDEX(array_index) ({register_array.base_index}u + "
180 f"(array_index) * {len(register_array.registers)}u + {register.index}u)\n"
181 )
182 c_code += f"#define {name}_ADDR(array_index) (4u * {name}_INDEX(array_index))\n"
183 else:
184 c_code += f"#define {name}_INDEX ({register.index}u)\n"
185 c_code += f"#define {name}_ADDR (4u * {name}_INDEX)\n"
187 return c_code
189 def _field_definitions(
190 self, register: Register, register_array: Optional["RegisterArray"]
191 ) -> str:
192 c_code = ""
193 for field in register.fields:
194 field_description = self.field_description(
195 register=register, register_array=register_array, field=field
196 )
197 c_code += self.comment(f"Attributes for the {field_description}.")
199 name = self.qualified_field_name(
200 register=register, field=field, register_array=register_array
201 ).upper()
202 c_code += f"#define {name}_SHIFT ({field.base_index}u)\n"
203 c_code += f'#define {name}_MASK (0b{"1" * field.width}u << {field.base_index}u)\n'
204 c_code += f"#define {name}_MASK_INVERSE (~{name}_MASK)\n"
206 if isinstance(field, Enumeration):
207 # Enums in C export their enumerators to the surrounding scope, causing huge risk of
208 # name clashes.
209 # Hence we give the enumerators long names qualified with the register name, etc.
210 name_value_pairs = [
211 f"{name}_{element.name.upper()} = {element.value},"
212 for element in field.elements
213 ]
214 separator = "\n "
216 c_code += f"""\
217enum {self.to_pascal_case(name)}
218{{
219 {separator.join(name_value_pairs)}
220}};
221"""
223 return c_code
225 def _constants(self) -> str:
226 c_code = ""
228 def define(name: str, value: str) -> str:
229 return f"#define {name} ({value})\n"
231 for constant in self.iterate_constants():
232 c_code += self.comment(f"Value of register constant '{constant.name}'.")
234 constant_name = f"{self.name.upper()}_{constant.name.upper()}"
236 if isinstance(constant, BooleanConstant):
237 c_code += define(constant_name, str(constant.value).lower())
238 elif isinstance(constant, IntegerConstant):
239 # No suffix -> "int", i.e. signed integer of at least 32 bits.
240 c_code += define(constant_name, str(constant.value))
241 elif isinstance(constant, FloatConstant):
242 # No suffix -> "double" (https://stackoverflow.com/questions/13276862).
243 # Matches the VHDL type which is at least 64 bits (IEEE 1076-2008, 5.2.5.1).
244 # Note that casting a Python float to string guarantees full precision in the
245 # resulting string: https://stackoverflow.com/a/60026172
246 c_code += define(constant_name, str(constant.value))
247 elif isinstance(constant, StringConstant):
248 # C string literal: Raw value enclosed in double quotation marks.
249 # Without "static", we get a linker warning about multiple definitions
250 # despite the multiple-include guard.
251 # See here https://stackoverflow.com/a/9196883 for some other information.
252 c_code += f'static char *{constant_name} = "{constant.value}";\n'
253 elif isinstance(constant, UnsignedVectorConstant):
254 # "unsigned" and "long" as suffix.
255 # Makes it possible to use large numbers for e.g. base addresses.
256 c_code += define(
257 constant_name, f"{constant.prefix}{constant.value_without_separator}UL"
258 )
259 else:
260 raise ValueError(f"Got unexpected constant type. {constant}")
262 return c_code