Coverage for hdl_registers/generator/vhdl/register_package.py: 96%
191 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 11:11 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 11:11 +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`.
53 """
55 __version__ = "2.0.0"
57 SHORT_DESCRIPTION = "VHDL register package"
59 @property
60 def output_file(self) -> Path:
61 """
62 Result will be placed in this file.
63 """
64 return self.output_folder / f"{self.name}_regs_pkg.vhd"
66 def get_code(
67 self,
68 **kwargs: Any, # noqa: ANN401, ARG002
69 ) -> str:
70 """
71 Get a complete VHDL package with register and constant information.
72 """
73 pkg_name = f"{self.name}_regs_pkg"
75 vhdl = f"""\
76library ieee;
77use ieee.std_logic_1164.all;
78use ieee.numeric_std.all;
79use ieee.fixed_pkg.all;
81library register_file;
82use register_file.register_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"""
122 # Match e.g. 5e60, but not 5.0e60.
123 re_float_start_with_integer = re_compile(r"^(\d+)e")
125 for constant in self.iterate_constants():
126 if isinstance(constant, BooleanConstant):
127 type_declaration = "boolean"
128 value = str(constant.value).lower()
129 elif isinstance(constant, IntegerConstant):
130 type_declaration = "integer"
131 value = str(constant.value)
132 elif isinstance(constant, FloatConstant):
133 # At least 64 bits (IEEE 1076-2008, 5.2.5.1).
134 type_declaration = "real"
135 # Note that casting a Python float to string guarantees full precision in the
136 # resulting string: https://stackoverflow.com/a/60026172
137 value = str(constant.value)
139 match = re_float_start_with_integer.match(value)
140 if match:
141 # "1e-3" is not valid VHDL, but "1.0e-3" is.
142 base = match.group(1)
143 exponent = value[match.end(1) :]
144 value = f"{base}.0{exponent}"
145 elif isinstance(constant, StringConstant):
146 type_declaration = "string"
147 value = f'"{constant.value}"'
148 elif isinstance(constant, UnsignedVectorConstant):
149 type_declaration = f"unsigned({constant.width} - 1 downto 0)"
151 if constant.is_hexadecimal_not_binary:
152 # Underscore separator is allowed in VHDL when defining a hexadecimal SLV.
153 value = f'x"{constant.value}"'
154 else:
155 # But not when defining a binary SLV.
156 value = f'"{constant.value_without_separator}"'
157 else:
158 raise TypeError(f"Got unexpected constant type. {constant}")
160 vhdl += (
161 " constant "
162 f"{self.name}_constant_{constant.name} : {type_declaration} := {value};\n"
163 )
165 vhdl += "\n"
167 return vhdl
169 @property
170 def _register_range_type_name(self) -> str:
171 """
172 Name of the type which is the legal index range of registers.
173 """
174 return f"{self.name}_register_range"
176 def _register_range(self) -> str:
177 """
178 A VHDL type that defines the legal range of register indexes.
179 Note that this method is only called if there are any registers, so
180 the indexing is safe.
181 """
182 last_index = self.register_list.register_objects[-1].index
183 index_width = 1 if last_index == 0 else last_index.bit_length()
184 address_width = index_width + 2
186 return f"""\
187 -- ---------------------------------------------------------------------------
188 -- The valid range of register indexes.
189 subtype {self._register_range_type_name} is natural range 0 to {last_index};
191 -- ---------------------------------------------------------------------------
192 -- The number of bits needed to address all {last_index + 1} registers on a register bus.
193 -- Note that this figure includes the lowest two address bits that are assumed zero, since
194 -- registers are 32-bit and unaligned accesses are not supported.
195 constant {self.name}_address_width : positive := {address_width};
197"""
199 def _array_constants(self) -> str:
200 """
201 A list of constants defining how many times each register array is repeated.
202 """
203 vhdl = ""
204 for register_array in self.iterate_register_arrays():
205 array_name = self.qualified_register_array_name(register_array=register_array)
207 vhdl += f"""\
208 -- Number of times the '{register_array.name}' register array is repeated.
209 constant {array_name}_array_length : natural := {register_array.length};
210 -- Range for indexing '{register_array.name}' register array repetitions.
211 subtype {array_name}_range is natural range 0 to {register_array.length - 1};
213"""
215 return vhdl
217 def _array_register_index_function_signature(
218 self, register: Register, register_array: "RegisterArray"
219 ) -> str:
220 """
221 Signature for the function that returns a register index for the specified index in a
222 register array.
223 """
224 array_name = self.qualified_register_array_name(register_array=register_array)
225 return f"""\
226 function {self.qualified_register_name(register, register_array)}(
227 array_index : {array_name}_range
228 ) return {self._register_range_type_name}"""
230 def _register_indexes(self) -> str:
231 """
232 A set of named constants for the register index of each register.
233 """
234 vhdl = " -- Register indexes, within the list of registers.\n"
236 for register, register_array in self.iterate_registers():
237 if register_array is None:
238 vhdl += (
239 f" constant {self.qualified_register_name(register)} : "
240 f"natural := {register.index};\n"
241 )
242 else:
243 vhdl += (
244 f"{self._array_register_index_function_signature(register, register_array)};\n"
245 )
247 vhdl += "\n"
249 return vhdl
251 def _register_map_head(self) -> str:
252 """
253 Get constants mapping the register indexes to register modes.
254 """
255 map_name = f"{self.name}_register_map"
257 return f"""\
258 -- Declare 'register_map' and 'regs_init' constants here but define them in
259 -- the package body (deferred constants).
260 -- So that functions have been elaborated when they are called.
261 -- Needed for ModelSim compilation to pass.
263 -- To be used as the 'registers' generic of 'axi_lite_register_file.vhd'.
264 constant {map_name} : register_definition_vec_t({self._register_range_type_name});
266 -- To be used for the 'regs_up' and 'regs_down' ports of 'axi_lite_register_file.vhd'.
267 subtype {self.name}_regs_t is register_vec_t({self._register_range_type_name});
268 -- To be used as the 'default_values' generic of 'axi_lite_register_file.vhd'.
269 constant {self.name}_regs_init : {self.name}_regs_t;
271 -- To be used for the 'reg_was_read' and 'reg_was_written' ports of 'axi_lite_register_file.vhd'.
272 subtype {self.name}_reg_was_accessed_t is \
273std_ulogic_vector({self._register_range_type_name});
275"""
277 def _field_declarations(self) -> str:
278 """
279 For every field in every register (plain or in array):
281 * Bit index range
282 * VHDL type
283 * width constant
284 * conversion function declarations to/from type and SLV
285 """
286 vhdl = ""
288 for register, register_array in self.iterate_registers():
289 if not register.fields:
290 continue
292 register_description = self.register_description(
293 register=register, register_array=register_array
294 )
296 vhdl += f"""\
297 -- -----------------------------------------------------------------------------
298 -- Fields in the {register_description}.
299"""
301 for field in register.fields:
302 field_name = self.qualified_field_name(
303 register=register, register_array=register_array, field=field
304 )
305 field_is_bit = isinstance(field, Bit)
307 vhdl += f" -- Range of the '{field.name}' field.\n"
308 if field_is_bit:
309 # A bit field's "range" is simply an index, so the indexing a register SLV
310 # gives a std_logic value.
311 vhdl += f" constant {field_name} : natural := {field.base_index};\n"
312 else:
313 # For other fields its an actual range.
314 vhdl += f"""\
315 subtype {field_name} is natural \
316range {field.width + field.base_index - 1} downto {field.base_index};
317 -- Width of the '{field.name}' field.
318 constant {field_name}_width : positive := {field.width};
319 -- Type for the '{field.name}' field.
320{self._field_type_declaration(field=field, field_name=field_name)}
321"""
323 vhdl += f"""\
324 -- Default value of the '{field.name}' field.
325 {self._field_init_value(field=field, field_name=field_name)}
326{self._field_conversion_function_declarations(field=field, field_name=field_name)}
327"""
329 return vhdl
331 def _field_type_declaration(self, field: "RegisterField", field_name: str) -> str:
332 """
333 Get a type declaration for the native VHDL type that corresponds to the field's type.
335 Arguments:
336 field: A field.
337 field_name: The field's qualified name.
338 """
339 if isinstance(field, BitVector):
340 if isinstance(field.numerical_interpretation, Unsigned):
341 return f" subtype {field_name}_t is u_unsigned({field.width - 1} downto 0);"
343 if isinstance(field.numerical_interpretation, Signed):
344 return f" subtype {field_name}_t is u_signed({field.width - 1} downto 0);"
346 if isinstance(field.numerical_interpretation, UnsignedFixedPoint):
347 return (
348 f" subtype {field_name}_t is ufixed("
349 f"{field.numerical_interpretation.max_bit_index} downto "
350 f"{field.numerical_interpretation.min_bit_index});"
351 )
353 if isinstance(field.numerical_interpretation, SignedFixedPoint):
354 return (
355 f" subtype {field_name}_t is sfixed("
356 f"{field.numerical_interpretation.max_bit_index} downto "
357 f"{field.numerical_interpretation.min_bit_index});"
358 )
360 raise TypeError(f'Got unexpected bit vector type for field: "{field}".')
362 if isinstance(field, Enumeration):
363 # Enum element names in VHDL are exported to the surrounding scope, causing huge
364 # risk of name clashes.
365 # At the same time, we want the elements to have somewhat concise names so they are
366 # easy to work with.
367 # Compromise by prefixing the element names with the field name.
368 element_names = [f"{field.name}_{element.name}" for element in field.elements]
369 elements = ",\n ".join(element_names)
370 return f"""\
371 type {field_name}_t is (
372 {elements}
373 );\
374"""
376 if isinstance(field, Integer):
377 return (
378 f" subtype {field_name}_t is integer range {field.min_value} to {field.max_value};"
379 )
381 raise TypeError(f'Got unexpected type for field: "{field}".')
383 def _field_init_value(self, field: "RegisterField", field_name: str) -> str:
384 """
385 Get an init value constant for the field.
386 Uses the native VHDL type that corresponds to the field's type.
388 Arguments:
389 field: A field.
390 field_name: The field's qualified name.
391 """
392 result = f"constant {field_name}_init :"
394 if isinstance(field, Bit):
395 return f"{result} std_ulogic := '{field.default_value}';"
397 if isinstance(field, BitVector):
398 return f'{result} {field_name}_t := "{field.default_value}";'
400 if isinstance(field, Enumeration):
401 return f"{result} {field_name}_t := {field.name}_{field.default_value.name};"
403 if isinstance(field, Integer):
404 return f"{result} {field_name}_t := {field.default_value};"
406 raise TypeError(f'Got unexpected type for field: "{field}".')
408 def _field_conversion_function_declarations(
409 self, field: "RegisterField", field_name: str
410 ) -> str:
411 """
412 Function declarations for functions that convert the field's native VHDL representation
413 to/from SLV.
415 Arguments:
416 field: A field.
417 field_name: The field's qualified name.
418 """
419 if isinstance(field, (Bit, BitVector)):
420 return ""
422 if isinstance(field, (Enumeration, Integer)):
423 to_slv_name = self.field_to_slv_function_name(field=field, field_name=field_name)
425 return f"""\
426 -- Type for the '{field.name}' field as an SLV.
427 subtype {field_name}_slv_t is std_ulogic_vector({field.width - 1} downto 0);
428 -- Cast a '{field.name}' field value to SLV.
429 function {to_slv_name}(data : {field_name}_t) return {field_name}_slv_t;
430 -- Get a '{field.name}' field value from a register value.
431 function to_{field_name}(data : register_t) return {field_name}_t;
432"""
434 raise TypeError(f'Got unexpected type for field: "{field}".')
436 def _array_index_function_implementations(self) -> str:
437 """
438 Implementation for the functions that return a register index for the specified index in a
439 register array.
440 """
441 vhdl = ""
442 for register_array in self.iterate_register_arrays():
443 num_registers = len(register_array.registers)
444 for register in register_array.registers:
445 vhdl += f"""\
446{self._array_register_index_function_signature(register, register_array)} is
447 begin
448 return {register_array.base_index} + array_index * {num_registers} + {register.index};
449 end function;
451"""
453 return vhdl
455 def _register_map_body(self) -> str:
456 """
457 Get the body of the register map definition constants.
458 """
459 map_name = f"{self.name}_register_map"
460 range_name = f"{self.name}_register_range"
462 register_definitions = []
463 default_values = []
464 index = 0
466 def add(register: Register, index_name: str) -> None:
467 register_definitions.append(
468 f"{index} => (index => {index_name}, "
469 f"mode => {register.mode.shorthand}, "
470 f"utilized_width => {register.utilized_width})"
471 )
472 default_values.append(f'{index} => "{register.default_value:032b}"')
474 for register_object in self.iterate_register_objects():
475 if isinstance(register_object, Register):
476 add(
477 register=register_object,
478 index_name=self.qualified_register_name(register=register_object),
479 )
480 index += 1
481 else:
482 for array_index in range(register_object.length):
483 for register in register_object.registers:
484 register_name = self.qualified_register_name(
485 register=register, register_array=register_object
486 )
487 index_name = f"{register_name}({array_index})"
489 add(register=register, index_name=index_name)
490 index += 1
492 array_element_separator = ",\n "
493 return f"""\
494 constant {map_name} : register_definition_vec_t({range_name}) := (
495 {array_element_separator.join(register_definitions)}
496 );
498 constant {self.name}_regs_init : {self.name}_regs_t := (
499 {array_element_separator.join(default_values)}
500 );
502"""
504 def _field_conversion_implementations(self) -> str:
505 """
506 Implementation of functions that convert a register field's native VHDL representation
507 to/from SLV.
508 """
509 vhdl = ""
511 for register, register_array in self.iterate_registers():
512 for field in register.fields:
513 if isinstance(field, (Bit, BitVector)):
514 # Skip all field types that do not have any functions that need to
515 # be implemented.
516 continue
518 name = self.qualified_field_name(
519 register=register, register_array=register_array, field=field
520 )
521 to_slv_name = self.field_to_slv_function_name(field=field, field_name=name)
523 if isinstance(field, Enumeration):
524 to_slv = f"""\
525 constant data_int : natural := {name}_t'pos(data);
526 constant result : {name}_slv_t := std_ulogic_vector(
527 to_unsigned(data_int, {name}_width)
528 );
529"""
530 from_slv = f"""\
531 constant field_slv : {name}_slv_t := data({name});
532 constant field_int : natural := to_integer(unsigned(field_slv));
533 constant result : {name}_t := {name}_t'val(field_int);
534"""
535 elif isinstance(field, Integer):
536 vector_type = "signed" if field.is_signed else "unsigned"
537 to_slv = f"""\
538 constant result : {name}_slv_t := std_ulogic_vector(to_{vector_type}(data, {name}_width));
539"""
540 from_slv = f"""\
541 constant result : integer := to_integer({vector_type}(data({name})));
542"""
543 else:
544 raise TypeError(f'Got unexpected field type: "{field}".')
546 vhdl += f"""\
547 -- Cast a '{field.name}' field value to SLV.
548 function {to_slv_name}(data : {name}_t) return {name}_slv_t is
549{to_slv}\
550 begin
551 return result;
552 end function;
554 -- Get a '{field.name}' field value from a register value.
555 function to_{name}(data : register_t) return {name}_t is
556{from_slv}\
557 begin
558 return result;
559 end function;
561"""
563 return vhdl