Coverage for hdl_registers/generator/vhdl/register_package.py: 96%
193 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-29 06:41 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-29 06:41 +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# --------------------------------------------------------------------------------------------------
10from pathlib import Path
11from re import compile as re_compile
12from typing import TYPE_CHECKING, Any
14from hdl_registers.constant.bit_vector_constant import UnsignedVectorConstant
15from hdl_registers.constant.boolean_constant import BooleanConstant
16from hdl_registers.constant.float_constant import FloatConstant
17from hdl_registers.constant.integer_constant import IntegerConstant
18from hdl_registers.constant.string_constant import StringConstant
19from hdl_registers.field.bit import Bit
20from hdl_registers.field.bit_vector import BitVector
21from hdl_registers.field.enumeration import Enumeration
22from hdl_registers.field.integer import Integer
23from hdl_registers.field.numerical_interpretation import (
24 Signed,
25 SignedFixedPoint,
26 Unsigned,
27 UnsignedFixedPoint,
28)
29from hdl_registers.register import Register
31from .vhdl_generator_common import VhdlGeneratorCommon
33if TYPE_CHECKING:
34 from hdl_registers.field.register_field import RegisterField
35 from hdl_registers.register_array import RegisterArray
38class VhdlRegisterPackageGenerator(VhdlGeneratorCommon):
39 """
40 Generate a base VHDL package with basic register information.
41 See the :ref:`generator_vhdl` article for usage details.
43 * For each register constant, the value as a native VHDL constant.
44 * For each register, the index within the register list.
45 * For each field in each register
47 * Register bit index range definitions.
48 * Native VHDL type corresponding to the field type.
49 * Conversion of a field value to/from SLV.
51 Also produces a constant that maps indexes to modes, suitable for use with
52 :ref:`register_file.axi_lite_register_file` or :class:`.VhdlAxiLiteWrapperGenerator`.
54 See :ref:`vhdl_dependencies` for a note on dependencies.
55 """
57 __version__ = "2.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(
69 self,
70 **kwargs: Any, # noqa: ANN401, ARG002
71 ) -> str:
72 """
73 Get a complete VHDL package with register and constant information.
74 """
75 pkg_name = f"{self.name}_regs_pkg"
77 vhdl = f"""\
78library ieee;
79use ieee.std_logic_1164.all;
80use ieee.numeric_std.all;
81use ieee.fixed_pkg.all;
83library register_file;
84use register_file.register_file_pkg.all;
87package {pkg_name} is
89"""
90 if self.register_list.constants:
91 vhdl += self._constants()
93 if self.register_list.register_objects:
94 vhdl += f"""\
95{self._register_range()}\
96{self._array_constants()}\
97{self._register_indexes()}\
98{self._register_map_head()}\
99{self._field_declarations()}\
100"""
102 vhdl += "end package;\n"
104 if self.register_list.register_objects:
105 vhdl += f"""
106package body {pkg_name} is
108{self._array_index_function_implementations()}\
109{self._register_map_body()}\
110{self._field_conversion_implementations()}\
111end package body;
112"""
114 return vhdl
116 def _constants(self) -> str:
117 """
118 A set of VHDL constants, corresponding to the provided register constants.
119 """
120 vhdl = """\
121 -- ---------------------------------------------------------------------------
122 -- Values of register constants.
123"""
124 # Match e.g. 5e60, but not 5.0e60.
125 re_float_start_with_integer = re_compile(r"^(\d+)e")
127 for constant in self.iterate_constants():
128 if isinstance(constant, BooleanConstant):
129 type_declaration = "boolean"
130 value = str(constant.value).lower()
131 elif isinstance(constant, IntegerConstant):
132 type_declaration = "integer"
133 value = str(constant.value)
134 elif isinstance(constant, FloatConstant):
135 # At least 64 bits (IEEE 1076-2008, 5.2.5.1).
136 type_declaration = "real"
137 # Note that casting a Python float to string guarantees full precision in the
138 # resulting string: https://stackoverflow.com/a/60026172
139 value = str(constant.value)
141 match = re_float_start_with_integer.match(value)
142 if match:
143 # "1e-3" is not valid VHDL, but "1.0e-3" is.
144 base = match.group(1)
145 exponent = value[match.end(1) :]
146 value = f"{base}.0{exponent}"
147 elif isinstance(constant, StringConstant):
148 type_declaration = "string"
149 value = f'"{constant.value}"'
150 elif isinstance(constant, UnsignedVectorConstant):
151 type_declaration = f"unsigned({constant.width} - 1 downto 0)"
153 if constant.is_hexadecimal_not_binary:
154 # Underscore separator is allowed in VHDL when defining a hexadecimal SLV.
155 value = f'x"{constant.value}"'
156 else:
157 # But not when defining a binary SLV.
158 value = f'"{constant.value_without_separator}"'
159 else:
160 raise TypeError(f"Got unexpected constant type. {constant}")
162 vhdl += (
163 " constant "
164 f"{self.name}_constant_{constant.name} : {type_declaration} := {value};\n"
165 )
167 vhdl += "\n"
169 return vhdl
171 @property
172 def _register_range_type_name(self) -> str:
173 """
174 Name of the type which is the legal index range of registers.
175 """
176 return f"{self.name}_register_range"
178 def _register_range(self) -> str:
179 """
180 A VHDL type that defines the legal range of register indexes.
181 Note that this method is only called if there are any registers, so
182 the indexing is safe.
183 """
184 last_index = self.register_list.register_objects[-1].index
185 index_width = 1 if last_index == 0 else last_index.bit_length()
186 address_width = index_width + 2
188 return f"""\
189 -- ---------------------------------------------------------------------------
190 -- The valid range of register indexes.
191 subtype {self._register_range_type_name} is natural range 0 to {last_index};
193 -- ---------------------------------------------------------------------------
194 -- The number of bits needed to address all {last_index + 1} registers on a register bus.
195 -- Note that this figure includes the lowest two address bits that are assumed zero, since
196 -- registers are 32-bit and unaligned accesses are not supported.
197 constant {self.name}_address_width : positive := {address_width};
199"""
201 def _array_constants(self) -> str:
202 """
203 A list of constants defining how many times each register array is repeated.
204 """
205 vhdl = ""
206 for register_array in self.iterate_register_arrays():
207 array_name = self.qualified_register_array_name(register_array=register_array)
209 vhdl += f"""\
210 -- Number of times the '{register_array.name}' register array is repeated.
211 constant {array_name}_array_length : natural := {register_array.length};
212 -- Range for indexing '{register_array.name}' register array repetitions.
213 subtype {array_name}_range is natural range 0 to {register_array.length - 1};
215"""
217 return vhdl
219 def _array_register_index_function_signature(
220 self, register: Register, register_array: "RegisterArray"
221 ) -> str:
222 """
223 Signature for the function that returns a register index for the specified index in a
224 register array.
225 """
226 array_name = self.qualified_register_array_name(register_array=register_array)
227 return f"""\
228 function {self.qualified_register_name(register, register_array)}(
229 array_index : {array_name}_range
230 ) return {self._register_range_type_name}"""
232 def _register_indexes(self) -> str:
233 """
234 A set of named constants for the register index of each register.
235 """
236 vhdl = " -- Register indexes, within the list of registers.\n"
238 for register, register_array in self.iterate_registers():
239 if register_array is None:
240 vhdl += (
241 f" constant {self.qualified_register_name(register)} : "
242 f"natural := {register.index};\n"
243 )
244 else:
245 vhdl += (
246 f"{self._array_register_index_function_signature(register, register_array)};\n"
247 )
249 vhdl += "\n"
251 return vhdl
253 def _register_map_head(self) -> str:
254 """
255 Get constants mapping the register indexes to register modes.
256 """
257 map_name = f"{self.name}_register_map"
259 return f"""\
260 -- Declare 'register_map' and 'regs_init' constants here but define them in
261 -- the package body (deferred constants).
262 -- So that functions have been elaborated when they are called.
263 -- Needed for ModelSim compilation to pass.
265 -- To be used as the 'registers' generic of 'axi_lite_register_file.vhd'.
266 constant {map_name} : register_definition_vec_t({self._register_range_type_name});
268 -- To be used for the 'regs_up' and 'regs_down' ports of 'axi_lite_register_file.vhd'.
269 subtype {self.name}_regs_t is register_vec_t({self._register_range_type_name});
270 -- To be used as the 'default_values' generic of 'axi_lite_register_file.vhd'.
271 constant {self.name}_regs_init : {self.name}_regs_t;
273 -- To be used for the 'reg_was_read' and 'reg_was_written' ports of 'axi_lite_register_file.vhd'.
274 subtype {self.name}_reg_was_accessed_t is \
275std_ulogic_vector({self._register_range_type_name});
277"""
279 def _field_declarations(self) -> str:
280 """
281 For every field in every register (plain or in array):
283 * Bit index range
284 * VHDL type
285 * width constant
286 * conversion function declarations to/from type and SLV
287 """
288 vhdl = ""
290 for register, register_array in self.iterate_registers():
291 if not register.fields:
292 continue
294 register_description = self.register_description(
295 register=register, register_array=register_array
296 )
298 vhdl += f"""\
299 -- -----------------------------------------------------------------------------
300 -- Fields in the {register_description}.
301"""
303 for field in register.fields:
304 field_name = self.qualified_field_name(
305 register=register, register_array=register_array, field=field
306 )
307 field_is_bit = isinstance(field, Bit)
309 vhdl += f" -- Range of the '{field.name}' field.\n"
310 if field_is_bit:
311 # A bit field's "range" is simply an index, so the indexing a register SLV
312 # gives a std_logic value.
313 vhdl += f" constant {field_name} : natural := {field.base_index};\n"
314 else:
315 # For other fields its an actual range.
316 vhdl += f"""\
317 subtype {field_name} is natural \
318range {field.width + field.base_index - 1} downto {field.base_index};
319 -- Width of the '{field.name}' field.
320 constant {field_name}_width : positive := {field.width};
321 -- Type for the '{field.name}' field.
322{self._field_type_declaration(field=field, field_name=field_name)}
323"""
325 vhdl += f"""\
326 -- Default value of the '{field.name}' field.
327 {self._field_init_value(field=field, field_name=field_name)}
328{self._field_conversion_function_declarations(field=field, field_name=field_name)}
329"""
331 return vhdl
333 def _field_type_declaration(self, field: "RegisterField", field_name: str) -> str:
334 """
335 Get a type declaration for the native VHDL type that corresponds to the field's type.
337 Arguments:
338 field: A field.
339 field_name: The field's qualified name.
340 """
341 if isinstance(field, BitVector):
342 if isinstance(field.numerical_interpretation, Unsigned):
343 return f" subtype {field_name}_t is u_unsigned({field.width - 1} downto 0);"
345 if isinstance(field.numerical_interpretation, Signed):
346 return f" subtype {field_name}_t is u_signed({field.width - 1} downto 0);"
348 if isinstance(field.numerical_interpretation, UnsignedFixedPoint):
349 return (
350 f" subtype {field_name}_t is ufixed("
351 f"{field.numerical_interpretation.max_bit_index} downto "
352 f"{field.numerical_interpretation.min_bit_index});"
353 )
355 if isinstance(field.numerical_interpretation, SignedFixedPoint):
356 return (
357 f" subtype {field_name}_t is sfixed("
358 f"{field.numerical_interpretation.max_bit_index} downto "
359 f"{field.numerical_interpretation.min_bit_index});"
360 )
362 raise TypeError(f'Got unexpected bit vector type for field: "{field}".')
364 if isinstance(field, Enumeration):
365 # Enum element names in VHDL are exported to the surrounding scope, causing huge
366 # risk of name clashes.
367 # At the same time, we want the elements to have somewhat concise names so they are
368 # easy to work with.
369 # Compromise by prefixing the element names with the field name.
370 element_names = [f"{field.name}_{element.name}" for element in field.elements]
371 elements = ",\n ".join(element_names)
372 return f"""\
373 type {field_name}_t is (
374 {elements}
375 );\
376"""
378 if isinstance(field, Integer):
379 return (
380 f" subtype {field_name}_t is integer range {field.min_value} to {field.max_value};"
381 )
383 raise TypeError(f'Got unexpected type for field: "{field}".')
385 def _field_init_value(self, field: "RegisterField", field_name: str) -> str:
386 """
387 Get an init value constant for the field.
388 Uses the native VHDL type that corresponds to the field's type.
390 Arguments:
391 field: A field.
392 field_name: The field's qualified name.
393 """
394 result = f"constant {field_name}_init :"
396 if isinstance(field, Bit):
397 return f"{result} std_ulogic := '{field.default_value}';"
399 if isinstance(field, BitVector):
400 return f'{result} {field_name}_t := "{field.default_value}";'
402 if isinstance(field, Enumeration):
403 return f"{result} {field_name}_t := {field.name}_{field.default_value.name};"
405 if isinstance(field, Integer):
406 return f"{result} {field_name}_t := {field.default_value};"
408 raise TypeError(f'Got unexpected type for field: "{field}".')
410 def _field_conversion_function_declarations(
411 self, field: "RegisterField", field_name: str
412 ) -> str:
413 """
414 Function declarations for functions that convert the field's native VHDL representation
415 to/from SLV.
417 Arguments:
418 field: A field.
419 field_name: The field's qualified name.
420 """
421 if isinstance(field, (Bit, BitVector)):
422 return ""
424 if isinstance(field, (Enumeration, Integer)):
425 to_slv_name = self.field_to_slv_function_name(field=field, field_name=field_name)
427 return f"""\
428 -- Type for the '{field.name}' field as an SLV.
429 subtype {field_name}_slv_t is std_ulogic_vector({field.width - 1} downto 0);
430 -- Cast a '{field.name}' field value to SLV.
431 function {to_slv_name}(data : {field_name}_t) return {field_name}_slv_t;
432 -- Get a '{field.name}' field value from a register value.
433 function to_{field_name}(data : register_t) return {field_name}_t;
434"""
436 raise TypeError(f'Got unexpected type for field: "{field}".')
438 def _array_index_function_implementations(self) -> str:
439 """
440 Implementation for the functions that return a register index for the specified index in a
441 register array.
442 """
443 vhdl = ""
444 for register_array in self.iterate_register_arrays():
445 num_registers = len(register_array.registers)
446 for register in register_array.registers:
447 vhdl += f"""\
448{self._array_register_index_function_signature(register, register_array)} is
449 begin
450 return {register_array.base_index} + array_index * {num_registers} + {register.index};
451 end function;
453"""
455 return vhdl
457 def _register_map_body(self) -> str:
458 """
459 Get the body of the register map definition constants.
460 """
461 map_name = f"{self.name}_register_map"
462 range_name = f"{self.name}_register_range"
464 register_definitions = []
465 default_values = []
466 index = 0
468 def add(register: Register, index_name: str) -> None:
469 utilized_width = self.register_utilized_width(register=register)
470 register_definitions.append(
471 f"{index} => (index => {index_name}, "
472 f"mode => {register.mode.shorthand}, "
473 f"utilized_width => {utilized_width})"
474 )
476 default_value = self.register_default_value_uint(register=register)
477 default_values.append(f'{index} => "{default_value:032b}"')
479 for register_object in self.iterate_register_objects():
480 if isinstance(register_object, Register):
481 add(
482 register=register_object,
483 index_name=self.qualified_register_name(register=register_object),
484 )
485 index += 1
486 else:
487 for array_index in range(register_object.length):
488 for register in register_object.registers:
489 register_name = self.qualified_register_name(
490 register=register, register_array=register_object
491 )
492 index_name = f"{register_name}({array_index})"
494 add(register=register, index_name=index_name)
495 index += 1
497 array_element_separator = ",\n "
498 return f"""\
499 constant {map_name} : register_definition_vec_t({range_name}) := (
500 {array_element_separator.join(register_definitions)}
501 );
503 constant {self.name}_regs_init : {self.name}_regs_t := (
504 {array_element_separator.join(default_values)}
505 );
507"""
509 def _field_conversion_implementations(self) -> str:
510 """
511 Implementation of functions that convert a register field's native VHDL representation
512 to/from SLV.
513 """
514 vhdl = ""
516 for register, register_array in self.iterate_registers():
517 for field in register.fields:
518 if isinstance(field, (Bit, BitVector)):
519 # Skip all field types that do not have any functions that need to
520 # be implemented.
521 continue
523 name = self.qualified_field_name(
524 register=register, register_array=register_array, field=field
525 )
526 to_slv_name = self.field_to_slv_function_name(field=field, field_name=name)
528 if isinstance(field, Enumeration):
529 to_slv = f"""\
530 constant data_int : natural := {name}_t'pos(data);
531 constant result : {name}_slv_t := std_ulogic_vector(
532 to_unsigned(data_int, {name}_width)
533 );
534"""
535 from_slv = f"""\
536 constant field_slv : {name}_slv_t := data({name});
537 constant field_int : natural := to_integer(unsigned(field_slv));
538 constant result : {name}_t := {name}_t'val(field_int);
539"""
540 elif isinstance(field, Integer):
541 vector_type = "signed" if field.is_signed else "unsigned"
542 to_slv = f"""\
543 constant result : {name}_slv_t := std_ulogic_vector(to_{vector_type}(data, {name}_width));
544"""
545 from_slv = f"""\
546 constant result : integer := to_integer({vector_type}(data({name})));
547"""
548 else:
549 raise TypeError(f'Got unexpected field type: "{field}".')
551 vhdl += f"""\
552 -- Cast a '{field.name}' field value to SLV.
553 function {to_slv_name}(data : {name}_t) return {name}_slv_t is
554{to_slv}\
555 begin
556 return result;
557 end function;
559 -- Get a '{field.name}' field value from a register value.
560 function to_{name}(data : register_t) return {name}_t is
561{from_slv}\
562 begin
563 return result;
564 end function;
566"""
568 return vhdl