Coverage for hdl_registers/generator/vhdl/register_package.py: 96%
188 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
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.bit import Bit
21from hdl_registers.field.bit_vector import BitVector
22from hdl_registers.field.enumeration import Enumeration
23from hdl_registers.field.integer import Integer
24from hdl_registers.field.numerical_interpretation import (
25 Signed,
26 SignedFixedPoint,
27 Unsigned,
28 UnsignedFixedPoint,
29)
30from hdl_registers.register import Register
32if TYPE_CHECKING:
33 from hdl_registers.field.register_field import RegisterField
34 from hdl_registers.register_array import RegisterArray
36# Local folder libraries
37from .vhdl_generator_common import VhdlGeneratorCommon
40class VhdlRegisterPackageGenerator(VhdlGeneratorCommon):
41 """
42 Generate a base VHDL package with basic register information.
43 See the :ref:`generator_vhdl` article for usage details.
45 * For each register constant, the value as a native VHDL constant.
46 * For each register, the index within the register map.
47 * For each field in each register
49 * Register bit index range definitions.
50 * Native VHDL type corresponding to the field type.
51 * Conversion of a field value to/from SLV.
53 Also produces a register map constant, mapping indexes to modes, suitable for use with
54 :ref:`reg_file.axi_lite_reg_file` or :class:`.VhdlAxiLiteWrapperGenerator`.
55 """
57 __version__ = "1.0.0"
59 SHORT_DESCRIPTION = "VHDL register package"
61 @property
62 def output_file(self) -> Path:
63 """
64 Result will be placed in this file.
65 """
66 return self.output_folder / f"{self.name}_regs_pkg.vhd"
68 def get_code(self, **kwargs: Any) -> str:
69 """
70 Get a complete VHDL package with register and constant information.
71 """
72 pkg_name = f"{self.name}_regs_pkg"
74 vhdl = f"""\
75{self.header}
76library ieee;
77use ieee.std_logic_1164.all;
78use ieee.numeric_std.all;
79use ieee.fixed_pkg.all;
81library reg_file;
82use reg_file.reg_file_pkg.all;
85package {pkg_name} is
87"""
88 if self.register_list.constants:
89 vhdl += self._constants()
91 if self.register_list.register_objects:
92 vhdl += f"""\
93{self._register_range()}\
94{self._array_constants()}\
95{self._register_indexes()}\
96{self._register_map_head()}\
97{self._field_declarations()}\
98"""
100 vhdl += "end package;\n"
102 if self.register_list.register_objects:
103 vhdl += f"""
104package body {pkg_name} is
106{self._array_index_function_implementations()}\
107{self._register_map_body()}\
108{self._field_conversion_implementations()}\
109end package body;
110"""
112 return vhdl
114 def _constants(self) -> str:
115 """
116 A set of VHDL constants, corresponding to the provided register constants.
117 """
118 vhdl = """\
119 -- ---------------------------------------------------------------------------
120 -- Values of register constants.
121"""
123 for constant in self.iterate_constants():
124 if isinstance(constant, BooleanConstant):
125 type_declaration = "boolean"
126 value = str(constant.value).lower()
127 elif isinstance(constant, IntegerConstant):
128 type_declaration = "integer"
129 value = str(constant.value)
130 elif isinstance(constant, FloatConstant):
131 # At least 64 bits (IEEE 1076-2008, 5.2.5.1).
132 type_declaration = "real"
133 # Note that casting a Python float to string guarantees full precision in the
134 # resulting string: https://stackoverflow.com/a/60026172
135 value = str(constant.value)
136 elif isinstance(constant, StringConstant):
137 type_declaration = "string"
138 value = f'"{constant.value}"'
139 elif isinstance(constant, UnsignedVectorConstant):
140 type_declaration = f"unsigned({constant.width} - 1 downto 0)"
142 if constant.is_hexadecimal_not_binary:
143 # Underscore separator is allowed in VHDL when defining a hexadecimal SLV.
144 value = f'x"{constant.value}"'
145 else:
146 # But not when defining a binary SLV.
147 value = f'"{constant.value_without_separator}"'
148 else:
149 raise ValueError(f"Got unexpected constant type. {constant}")
151 vhdl += (
152 " constant "
153 f"{self.name}_constant_{constant.name} : {type_declaration} := {value};\n"
154 )
156 vhdl += "\n"
158 return vhdl
160 @property
161 def _register_range_type_name(self) -> str:
162 """
163 Name of the type which is the legal index range of registers.
164 """
165 return f"{self.name}_reg_range"
167 def _register_range(self) -> str:
168 """
169 A VHDL type that defines the legal range of register indexes.
170 """
171 last_index = self.register_list.register_objects[-1].index
172 vhdl = f"""\
173 -- ---------------------------------------------------------------------------
174 -- The valid range of register indexes.
175 subtype {self._register_range_type_name} is natural range 0 to {last_index};
177"""
178 return vhdl
180 def _array_constants(self) -> str:
181 """
182 A list of constants defining how many times each register array is repeated.
183 """
184 vhdl = ""
185 for register_array in self.iterate_register_arrays():
186 array_name = self.qualified_register_array_name(register_array=register_array)
188 vhdl += f"""\
189 -- Number of times the '{register_array.name}' register array is repeated.
190 constant {array_name}_array_length : natural := {register_array.length};
191 -- Range for indexing '{register_array.name}' register array repetitions.
192 subtype {array_name}_range is natural range 0 to {register_array.length - 1};
194"""
196 return vhdl
198 def _array_register_index_function_signature(
199 self, register: Register, register_array: "RegisterArray"
200 ) -> str:
201 """
202 Signature for the function that returns a register index for the specified index in a
203 register array.
204 """
205 array_name = self.qualified_register_array_name(register_array=register_array)
206 vhdl = f"""\
207 function {self.qualified_register_name(register, register_array)}(
208 array_index : {array_name}_range
209 ) return {self._register_range_type_name}"""
210 return vhdl
212 def _register_indexes(self) -> str:
213 """
214 A set of named constants for the register index of each register.
215 """
216 vhdl = " -- Register indexes, within the list of registers.\n"
218 for register, register_array in self.iterate_registers():
219 if register_array is None:
220 vhdl += (
221 f" constant {self.qualified_register_name(register)} : "
222 f"natural := {register.index};\n"
223 )
224 else:
225 vhdl += (
226 f"{self._array_register_index_function_signature(register, register_array)};\n"
227 )
229 vhdl += "\n"
231 return vhdl
233 def _register_map_head(self) -> str:
234 """
235 Get constants mapping the register indexes to register modes.
236 """
237 map_name = f"{self.name}_reg_map"
239 vhdl = f"""\
240 -- Declare 'reg_map' and 'regs_init' constants here but define them in body (deferred constants).
241 -- So that functions have been elaborated when they are called.
242 -- Needed for ModelSim compilation to pass.
244 -- To be used as the 'regs' generic of 'axi_lite_reg_file.vhd'.
245 constant {map_name} : reg_definition_vec_t({self._register_range_type_name});
247 -- To be used for the 'regs_up' and 'regs_down' ports of 'axi_lite_reg_file.vhd'.
248 subtype {self.name}_regs_t is reg_vec_t({self._register_range_type_name});
249 -- To be used as the 'default_values' generic of 'axi_lite_reg_file.vhd'.
250 constant {self.name}_regs_init : {self.name}_regs_t;
252 -- To be used for the 'reg_was_read' and 'reg_was_written' ports of 'axi_lite_reg_file.vhd'.
253 subtype {self.name}_reg_was_accessed_t is \
254std_ulogic_vector({self._register_range_type_name});
256"""
258 return vhdl
260 def _field_declarations(self) -> str:
261 """
262 For every field in every register (plain or in array):
264 * Bit index range
265 * VHDL type
266 * width constant
267 * conversion function declarations to/from type and SLV
268 """
269 vhdl = ""
271 for register, register_array in self.iterate_registers():
272 if not register.fields:
273 continue
275 register_description = self.register_description(
276 register=register, register_array=register_array
277 )
279 vhdl += f"""\
280 -- -----------------------------------------------------------------------------
281 -- Fields in the {register_description}.
282"""
284 for field in register.fields:
285 field_name = self.qualified_field_name(
286 register=register, register_array=register_array, field=field
287 )
288 field_is_bit = isinstance(field, Bit)
290 vhdl += f" -- Range of the '{field.name}' field.\n"
291 if field_is_bit:
292 # A bit field's "range" is simply an index, so the indexing a register SLV
293 # gives a std_logic value.
294 vhdl += f" constant {field_name} : natural := {field.base_index};\n"
295 else:
296 # For other fields its an actual range.
297 vhdl += f"""\
298 subtype {field_name} is natural \
299range {field.width + field.base_index - 1} downto {field.base_index};
300 -- Width of the '{field.name}' field.
301 constant {field_name}_width : positive := {field.width};
302 -- Type for the '{field.name}' field.
303{self._field_type_declaration(field=field, field_name=field_name)}
304"""
306 vhdl += f"""\
307 -- Default value of the '{field.name}' field.
308 {self._field_init_value(field=field, field_name=field_name)}
309{self._field_conversion_function_declarations(field=field, field_name=field_name)}
310"""
312 return vhdl
314 def _field_type_declaration(self, field: "RegisterField", field_name: str) -> str:
315 """
316 Get a type declaration for the native VHDL type that corresponds to the field's type.
318 Arguments:
319 field: A field.
320 field_name: The field's qualified name.
321 """
322 if isinstance(field, BitVector):
323 if isinstance(field.numerical_interpretation, Unsigned):
324 return f" subtype {field_name}_t is u_unsigned({field.width - 1} downto 0);"
326 if isinstance(field.numerical_interpretation, Signed):
327 return f" subtype {field_name}_t is u_signed({field.width - 1} downto 0);"
329 if isinstance(field.numerical_interpretation, UnsignedFixedPoint):
330 return (
331 f" subtype {field_name}_t is ufixed("
332 f"{field.numerical_interpretation.max_bit_index} downto "
333 f"{field.numerical_interpretation.min_bit_index});"
334 )
336 if isinstance(field.numerical_interpretation, SignedFixedPoint):
337 return (
338 f" subtype {field_name}_t is sfixed("
339 f"{field.numerical_interpretation.max_bit_index} downto "
340 f"{field.numerical_interpretation.min_bit_index});"
341 )
343 raise TypeError(f'Got unexpected bit vector type for field: "{field}".')
345 if isinstance(field, Enumeration):
346 # Enum element names in VHDL are exported to the surrounding scope, causing huge
347 # risk of name clashes.
348 # At the same time, we want the elements to have somewhat concise names so they are
349 # easy to work with.
350 # Compromise by prefixing the element names with the field name.
351 element_names = [f"{field.name}_{element.name}" for element in field.elements]
352 elements = ",\n ".join(element_names)
353 return f"""\
354 type {field_name}_t is (
355 {elements}
356 );\
357"""
359 if isinstance(field, Integer):
360 return (
361 f" subtype {field_name}_t is integer "
362 f"range {field.min_value} to {field.max_value};"
363 )
365 raise TypeError(f'Got unexpected type for field: "{field}".')
367 def _field_init_value(self, field: "RegisterField", field_name: str) -> str:
368 """
369 Get an init value constant for the field.
370 Uses the native VHDL type that corresponds to the field's type.
372 Arguments:
373 field: A field.
374 field_name: The field's qualified name.
375 """
376 result = f"constant {field_name}_init :"
378 if isinstance(field, Bit):
379 return f"{result} std_ulogic := '{field.default_value}';"
381 if isinstance(field, BitVector):
382 return f'{result} {field_name}_t := "{field.default_value}";'
384 if isinstance(field, Enumeration):
385 return f"{result} {field_name}_t := {field.name}_{field.default_value.name};"
387 if isinstance(field, Integer):
388 return f"{result} {field_name}_t := {field.default_value};"
390 raise TypeError(f'Got unexpected type for field: "{field}".')
392 def _field_conversion_function_declarations(
393 self, field: "RegisterField", field_name: str
394 ) -> str:
395 """
396 Function declarations for functions that convert the field's native VHDL representation
397 to/from SLV.
399 Arguments:
400 field: A field.
401 field_name: The field's qualified name.
402 """
403 if isinstance(field, (Bit, BitVector)):
404 return ""
406 if isinstance(field, (Enumeration, Integer)):
407 to_slv_name = self.field_to_slv_function_name(field=field, field_name=field_name)
409 return f"""\
410 -- Type for the '{field.name}' field as an SLV.
411 subtype {field_name}_slv_t is std_ulogic_vector({field.width - 1} downto 0);
412 -- Cast a '{field.name}' field value to SLV.
413 function {to_slv_name}(data : {field_name}_t) return {field_name}_slv_t;
414 -- Get a '{field.name}' field value from a register value.
415 function to_{field_name}(data : reg_t) return {field_name}_t;
416"""
418 raise TypeError(f'Got unexpected type for field: "{field}".')
420 def _array_index_function_implementations(self) -> str:
421 """
422 Implementation for the functions that return a register index for the specified index in a
423 register array.
424 """
425 vhdl = ""
426 for register_array in self.iterate_register_arrays():
427 num_registers = len(register_array.registers)
428 for register in register_array.registers:
429 vhdl += f"""\
430{self._array_register_index_function_signature(register, register_array)} is
431 begin
432 return {register_array.base_index} + array_index * {num_registers} + {register.index};
433 end function;
435"""
437 return vhdl
439 def _register_map_body(self) -> str:
440 """
441 Get the body of the register map definition constants.
442 """
443 map_name = f"{self.name}_reg_map"
444 range_name = f"{self.name}_reg_range"
446 register_definitions = []
447 default_values = []
448 vhdl_array_index = 0
449 for register_object in self.iterate_register_objects():
450 if isinstance(register_object, Register):
451 idx = self.qualified_register_name(register_object)
452 opening = f"{vhdl_array_index} => "
454 register_definitions.append(
455 f"{opening}(idx => {idx}, reg_type => {register_object.mode.shorthand})"
456 )
457 default_values.append(f'{opening}"{register_object.default_value:032b}"')
459 vhdl_array_index = vhdl_array_index + 1
461 else:
462 for array_index in range(register_object.length):
463 for register in register_object.registers:
464 regiser_name = self.qualified_register_name(register, register_object)
465 idx = f"{regiser_name}({array_index})"
466 opening = f"{vhdl_array_index} => "
468 register_definitions.append(
469 f"{opening}(idx => {idx}, reg_type => {register.mode.shorthand})"
470 )
471 default_values.append(f'{opening}"{register.default_value:032b}"')
473 vhdl_array_index = vhdl_array_index + 1
475 array_element_separator = ",\n "
476 vhdl = f"""\
477 constant {map_name} : reg_definition_vec_t({range_name}) := (
478 {array_element_separator.join(register_definitions)}
479 );
481 constant {self.name}_regs_init : {self.name}_regs_t := (
482 {array_element_separator.join(default_values)}
483 );
485"""
487 return vhdl
489 def _field_conversion_implementations(self) -> str:
490 """
491 Implementation of functions that convert a register field's native VHDL representation
492 to/from SLV.
493 """
494 vhdl = ""
496 for register, register_array in self.iterate_registers():
497 for field in register.fields:
498 if isinstance(field, (Bit, BitVector)):
499 # Skip all field types that do not have any functions that need to
500 # be implemented.
501 continue
503 name = self.qualified_field_name(
504 register=register, register_array=register_array, field=field
505 )
506 to_slv_name = self.field_to_slv_function_name(field=field, field_name=name)
508 if isinstance(field, Enumeration):
509 to_slv = f"""\
510 constant data_int : natural := {name}_t'pos(data);
511 constant result : {name}_slv_t := std_ulogic_vector(
512 to_unsigned(data_int, {name}_width)
513 );
514"""
515 from_slv = f"""\
516 constant field_slv : {name}_slv_t := data({name});
517 constant field_int : natural := to_integer(unsigned(field_slv));
518 constant result : {name}_t := {name}_t'val(field_int);
519"""
520 elif isinstance(field, Integer):
521 vector_type = "signed" if field.is_signed else "unsigned"
522 to_slv = f"""\
523 constant result : {name}_slv_t := std_ulogic_vector(to_{vector_type}(data, {name}_width));
524"""
525 from_slv = f"""\
526 constant result : integer := to_integer({vector_type}(data({name})));
527"""
528 else:
529 raise TypeError(f'Got unexpected field type: "{field}".')
531 vhdl += f"""\
532 -- Cast a '{field.name}' field value to SLV.
533 function {to_slv_name}(data : {name}_t) return {name}_slv_t is
534{to_slv}\
535 begin
536 return result;
537 end function;
539 -- Get a '{field.name}' field value from a register value.
540 function to_{name}(data : reg_t) return {name}_t is
541{from_slv}\
542 begin
543 return result;
544 end function;
546"""
548 return vhdl