Coverage for hdl_registers/generator/vhdl/register_package.py: 96%
193 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-28 20:51 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-28 20:51 +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 re import compile as re_compile
13from typing import TYPE_CHECKING, Any
15# First party libraries
16from hdl_registers.constant.bit_vector_constant import UnsignedVectorConstant
17from hdl_registers.constant.boolean_constant import BooleanConstant
18from hdl_registers.constant.float_constant import FloatConstant
19from hdl_registers.constant.integer_constant import IntegerConstant
20from hdl_registers.constant.string_constant import StringConstant
21from hdl_registers.field.bit import Bit
22from hdl_registers.field.bit_vector import BitVector
23from hdl_registers.field.enumeration import Enumeration
24from hdl_registers.field.integer import Integer
25from hdl_registers.field.numerical_interpretation import (
26 Signed,
27 SignedFixedPoint,
28 Unsigned,
29 UnsignedFixedPoint,
30)
31from hdl_registers.register import Register
33if TYPE_CHECKING:
34 from hdl_registers.field.register_field import RegisterField
35 from hdl_registers.register_array import RegisterArray
37# Local folder libraries
38from .vhdl_generator_common import VhdlGeneratorCommon
41class VhdlRegisterPackageGenerator(VhdlGeneratorCommon):
42 """
43 Generate a base VHDL package with basic register information.
44 See the :ref:`generator_vhdl` article for usage details.
46 * For each register constant, the value as a native VHDL constant.
47 * For each register, the index within the register list.
48 * For each field in each register
50 * Register bit index range definitions.
51 * Native VHDL type corresponding to the field type.
52 * Conversion of a field value to/from SLV.
54 Also produces a constant that maps indexes to modes, suitable for use with
55 :ref:`register_file.axi_lite_register_file` or :class:`.VhdlAxiLiteWrapperGenerator`.
56 """
58 __version__ = "2.0.0"
60 SHORT_DESCRIPTION = "VHDL register package"
62 @property
63 def output_file(self) -> Path:
64 """
65 Result will be placed in this file.
66 """
67 return self.output_folder / f"{self.name}_regs_pkg.vhd"
69 def get_code(self, **kwargs: Any) -> 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 re_float_start_with_integer = re_compile(r"^(\d+)e")
124 for constant in self.iterate_constants():
125 if isinstance(constant, BooleanConstant):
126 type_declaration = "boolean"
127 value = str(constant.value).lower()
128 elif isinstance(constant, IntegerConstant):
129 type_declaration = "integer"
130 value = str(constant.value)
131 elif isinstance(constant, FloatConstant):
132 # At least 64 bits (IEEE 1076-2008, 5.2.5.1).
133 type_declaration = "real"
134 # Note that casting a Python float to string guarantees full precision in the
135 # resulting string: https://stackoverflow.com/a/60026172
136 value = str(constant.value)
137 match = re_float_start_with_integer.match(value)
138 if match:
139 # "1e-3" is not valid VHDL, but "1.0e-3" is.
140 value = f"{match.group(1)}.0{value[match.end(1):]}"
141 elif isinstance(constant, StringConstant):
142 type_declaration = "string"
143 value = f'"{constant.value}"'
144 elif isinstance(constant, UnsignedVectorConstant):
145 type_declaration = f"unsigned({constant.width} - 1 downto 0)"
147 if constant.is_hexadecimal_not_binary:
148 # Underscore separator is allowed in VHDL when defining a hexadecimal SLV.
149 value = f'x"{constant.value}"'
150 else:
151 # But not when defining a binary SLV.
152 value = f'"{constant.value_without_separator}"'
153 else:
154 raise ValueError(f"Got unexpected constant type. {constant}")
156 vhdl += (
157 " constant "
158 f"{self.name}_constant_{constant.name} : {type_declaration} := {value};\n"
159 )
161 vhdl += "\n"
163 return vhdl
165 @property
166 def _register_range_type_name(self) -> str:
167 """
168 Name of the type which is the legal index range of registers.
169 """
170 return f"{self.name}_register_range"
172 def _register_range(self) -> str:
173 """
174 A VHDL type that defines the legal range of register indexes.
175 Note that this method is only called if there are any registers, so
176 the indexing is safe.
177 """
178 last_index = self.register_list.register_objects[-1].index
179 index_width = 1 if last_index == 0 else last_index.bit_length()
180 address_width = index_width + 2
182 vhdl = f"""\
183 -- ---------------------------------------------------------------------------
184 -- The valid range of register indexes.
185 subtype {self._register_range_type_name} is natural range 0 to {last_index};
187 -- ---------------------------------------------------------------------------
188 -- The number of bits needed to address all {last_index + 1} registers on a register bus.
189 -- Note that this figure includes the lowest two address bits that are assumed zero, since
190 -- registers are 32-bit and unaligned accesses are not supported.
191 constant {self.name}_address_width : positive := {address_width};
193"""
194 return vhdl
196 def _array_constants(self) -> str:
197 """
198 A list of constants defining how many times each register array is repeated.
199 """
200 vhdl = ""
201 for register_array in self.iterate_register_arrays():
202 array_name = self.qualified_register_array_name(register_array=register_array)
204 vhdl += f"""\
205 -- Number of times the '{register_array.name}' register array is repeated.
206 constant {array_name}_array_length : natural := {register_array.length};
207 -- Range for indexing '{register_array.name}' register array repetitions.
208 subtype {array_name}_range is natural range 0 to {register_array.length - 1};
210"""
212 return vhdl
214 def _array_register_index_function_signature(
215 self, register: Register, register_array: "RegisterArray"
216 ) -> str:
217 """
218 Signature for the function that returns a register index for the specified index in a
219 register array.
220 """
221 array_name = self.qualified_register_array_name(register_array=register_array)
222 vhdl = f"""\
223 function {self.qualified_register_name(register, register_array)}(
224 array_index : {array_name}_range
225 ) return {self._register_range_type_name}"""
226 return vhdl
228 def _register_indexes(self) -> str:
229 """
230 A set of named constants for the register index of each register.
231 """
232 vhdl = " -- Register indexes, within the list of registers.\n"
234 for register, register_array in self.iterate_registers():
235 if register_array is None:
236 vhdl += (
237 f" constant {self.qualified_register_name(register)} : "
238 f"natural := {register.index};\n"
239 )
240 else:
241 vhdl += (
242 f"{self._array_register_index_function_signature(register, register_array)};\n"
243 )
245 vhdl += "\n"
247 return vhdl
249 def _register_map_head(self) -> str:
250 """
251 Get constants mapping the register indexes to register modes.
252 """
253 map_name = f"{self.name}_register_map"
255 vhdl = f"""\
256 -- Declare 'register_map' and 'regs_init' constants here but define them in
257 -- the package body (deferred constants).
258 -- So that functions have been elaborated when they are called.
259 -- Needed for ModelSim compilation to pass.
261 -- To be used as the 'registers' generic of 'axi_lite_register_file.vhd'.
262 constant {map_name} : register_definition_vec_t({self._register_range_type_name});
264 -- To be used for the 'regs_up' and 'regs_down' ports of 'axi_lite_register_file.vhd'.
265 subtype {self.name}_regs_t is register_vec_t({self._register_range_type_name});
266 -- To be used as the 'default_values' generic of 'axi_lite_register_file.vhd'.
267 constant {self.name}_regs_init : {self.name}_regs_t;
269 -- To be used for the 'reg_was_read' and 'reg_was_written' ports of 'axi_lite_register_file.vhd'.
270 subtype {self.name}_reg_was_accessed_t is \
271std_ulogic_vector({self._register_range_type_name});
273"""
275 return vhdl
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 "
379 f"range {field.min_value} to {field.max_value};"
380 )
382 raise TypeError(f'Got unexpected type for field: "{field}".')
384 def _field_init_value(self, field: "RegisterField", field_name: str) -> str:
385 """
386 Get an init value constant for the field.
387 Uses the native VHDL type that corresponds to the field's type.
389 Arguments:
390 field: A field.
391 field_name: The field's qualified name.
392 """
393 result = f"constant {field_name}_init :"
395 if isinstance(field, Bit):
396 return f"{result} std_ulogic := '{field.default_value}';"
398 if isinstance(field, BitVector):
399 return f'{result} {field_name}_t := "{field.default_value}";'
401 if isinstance(field, Enumeration):
402 return f"{result} {field_name}_t := {field.name}_{field.default_value.name};"
404 if isinstance(field, Integer):
405 return f"{result} {field_name}_t := {field.default_value};"
407 raise TypeError(f'Got unexpected type for field: "{field}".')
409 def _field_conversion_function_declarations(
410 self, field: "RegisterField", field_name: str
411 ) -> str:
412 """
413 Function declarations for functions that convert the field's native VHDL representation
414 to/from SLV.
416 Arguments:
417 field: A field.
418 field_name: The field's qualified name.
419 """
420 if isinstance(field, (Bit, BitVector)):
421 return ""
423 if isinstance(field, (Enumeration, Integer)):
424 to_slv_name = self.field_to_slv_function_name(field=field, field_name=field_name)
426 return f"""\
427 -- Type for the '{field.name}' field as an SLV.
428 subtype {field_name}_slv_t is std_ulogic_vector({field.width - 1} downto 0);
429 -- Cast a '{field.name}' field value to SLV.
430 function {to_slv_name}(data : {field_name}_t) return {field_name}_slv_t;
431 -- Get a '{field.name}' field value from a register value.
432 function to_{field_name}(data : register_t) return {field_name}_t;
433"""
435 raise TypeError(f'Got unexpected type for field: "{field}".')
437 def _array_index_function_implementations(self) -> str:
438 """
439 Implementation for the functions that return a register index for the specified index in a
440 register array.
441 """
442 vhdl = ""
443 for register_array in self.iterate_register_arrays():
444 num_registers = len(register_array.registers)
445 for register in register_array.registers:
446 vhdl += f"""\
447{self._array_register_index_function_signature(register, register_array)} is
448 begin
449 return {register_array.base_index} + array_index * {num_registers} + {register.index};
450 end function;
452"""
454 return vhdl
456 def _register_map_body(self) -> str:
457 """
458 Get the body of the register map definition constants.
459 """
460 map_name = f"{self.name}_register_map"
461 range_name = f"{self.name}_register_range"
463 register_definitions = []
464 default_values = []
465 index = 0
467 def add(register: Register, index_name: str) -> None:
468 register_definitions.append(
469 f"{index} => (index => {index_name}, "
470 f"mode => {register.mode.shorthand}, "
471 f"utilized_width => {register.utilized_width})"
472 )
473 default_values.append(f'{index} => "{register.default_value:032b}"')
475 for register_object in self.iterate_register_objects():
476 if isinstance(register_object, Register):
477 add(
478 register=register_object,
479 index_name=self.qualified_register_name(register=register_object),
480 )
481 index += 1
482 else:
483 for array_index in range(register_object.length):
484 for register in register_object.registers:
485 register_name = self.qualified_register_name(
486 register=register, register_array=register_object
487 )
488 index_name = f"{register_name}({array_index})"
490 add(register=register, index_name=index_name)
491 index += 1
493 array_element_separator = ",\n "
494 vhdl = f"""\
495 constant {map_name} : register_definition_vec_t({range_name}) := (
496 {array_element_separator.join(register_definitions)}
497 );
499 constant {self.name}_regs_init : {self.name}_regs_t := (
500 {array_element_separator.join(default_values)}
501 );
503"""
505 return vhdl
507 def _field_conversion_implementations(self) -> str:
508 """
509 Implementation of functions that convert a register field's native VHDL representation
510 to/from SLV.
511 """
512 vhdl = ""
514 for register, register_array in self.iterate_registers():
515 for field in register.fields:
516 if isinstance(field, (Bit, BitVector)):
517 # Skip all field types that do not have any functions that need to
518 # be implemented.
519 continue
521 name = self.qualified_field_name(
522 register=register, register_array=register_array, field=field
523 )
524 to_slv_name = self.field_to_slv_function_name(field=field, field_name=name)
526 if isinstance(field, Enumeration):
527 to_slv = f"""\
528 constant data_int : natural := {name}_t'pos(data);
529 constant result : {name}_slv_t := std_ulogic_vector(
530 to_unsigned(data_int, {name}_width)
531 );
532"""
533 from_slv = f"""\
534 constant field_slv : {name}_slv_t := data({name});
535 constant field_int : natural := to_integer(unsigned(field_slv));
536 constant result : {name}_t := {name}_t'val(field_int);
537"""
538 elif isinstance(field, Integer):
539 vector_type = "signed" if field.is_signed else "unsigned"
540 to_slv = f"""\
541 constant result : {name}_slv_t := std_ulogic_vector(to_{vector_type}(data, {name}_width));
542"""
543 from_slv = f"""\
544 constant result : integer := to_integer({vector_type}(data({name})));
545"""
546 else:
547 raise TypeError(f'Got unexpected field type: "{field}".')
549 vhdl += f"""\
550 -- Cast a '{field.name}' field value to SLV.
551 function {to_slv_name}(data : {name}_t) return {name}_slv_t is
552{to_slv}\
553 begin
554 return result;
555 end function;
557 -- Get a '{field.name}' field value from a register value.
558 function to_{name}(data : register_t) return {name}_t is
559{from_slv}\
560 begin
561 return result;
562 end function;
564"""
566 return vhdl