Coverage for hdl_registers/generator/vhdl/axi_lite/wrapper.py: 95%
37 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-20 22:43 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-20 22:43 +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 typing import Any
13from hdl_registers.generator.vhdl.vhdl_generator_common import VhdlGeneratorCommon
14from hdl_registers.register_mode import HardwareAccessDirection, SoftwareAccessDirection
17class VhdlAxiLiteWrapperGenerator(VhdlGeneratorCommon):
18 """
19 Generate a VHDL wrapper around a generic AXI-Lite register file with correct generics and ports.
20 See the :ref:`generator_vhdl` article for usage details.
22 The wrapper will set the correct generics and will use record types for ``regs_up`` and
23 ``regs_down``.
24 This makes it very easy-to-use and saves a lot of manual conversion.
26 The file is dependent on the packages from
27 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`.
28 See also :ref:`vhdl_dependencies` for further dependencies.
30 Note that the ``regs_up`` port is only available if there are any registers of a type where
31 hardware gives a value to the bus.
32 For example a "Read" register.
33 If instead, for example, there are only "Write" registers, the ``regs_up`` port will not be
34 available and the type for it is not available in the VHDL package.
36 Same, but vice versa, for the ``regs_down`` port.
37 Will only be available if there are any registers of a type where the bus provides a
38 value to the hardware, e.g. "Read, Write".
40 Similar concept for the ``reg_was_read`` and ``reg_was_written`` ports.
41 They are only present if there are any readable/writeable registers in the register list.
42 """
44 __version__ = "1.0.3"
46 SHORT_DESCRIPTION = "VHDL AXI-Lite register file"
48 @property
49 def output_file(self) -> Path:
50 """
51 Result will be placed in this file.
52 """
53 return self.output_folder / f"{self.name}_register_file_axi_lite.vhd"
55 def create(
56 self,
57 **kwargs: Any, # noqa: ANN401
58 ) -> Path:
59 """
60 See super class for API details.
62 Overloaded here because this file shall only be created if the register list
63 actually has any registers.
64 """
65 # The artifact from this generator was renamed in version 7.0.0.
66 # An old artifact laying around might cause confusion and compilation errors.
67 old_output_file = self.output_folder / f"{self.name}_reg_file.vhd"
68 if old_output_file.exists():
69 print(f"Deleting old artifact {old_output_file}")
70 old_output_file.unlink()
72 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs)
74 def get_code(
75 self,
76 **kwargs: Any, # noqa: ANN401, ARG002
77 ) -> str:
78 """
79 Get VHDL code for a wrapper around the generic AXi_lite register file from hdl-modules:
80 """
81 entity_name = self.output_file.stem
83 up_port = f" regs_up : in {self.name}_regs_up_t := {self.name}_regs_up_init;\n"
84 has_any_up = self.has_any_hardware_accessible_register(HardwareAccessDirection.UP)
86 down_port = f" regs_down : out {self.name}_regs_down_t := {self.name}_regs_down_init;\n"
87 has_any_down = self.has_any_hardware_accessible_register(HardwareAccessDirection.DOWN)
89 was_read_port, was_written_port = self._get_was_accessed_ports()
91 # Note that either of 'reg_was_read' or 'reg_was_written' is always present, otherwise
92 # there would be no registers and we would not create this wrapper.
93 # Hence it is safe to always end the 'regs_up'/'regs_down' ports with a semicolon.
94 entity = f"""\
95entity {entity_name} is
96 generic (
97 default_values : {self.name}_registers_t := {self.name}_registers_init
98 );
99 port (
100 clk : in std_ulogic;
101 -- Active-high synchronous reset.
102 -- The code in this entity uses initial values so an initial reset is NOT necessary.
103 -- This port can safely be left unconnected and tied to zero.
104 -- If asserted, it will reset the AXI-Lite handshaking state as well as all register values.
105 reset : in std_ulogic := '0';
106 --# {{}}
107 --# Register control bus.
108 axi_lite_m2s : in axi_lite_m2s_t;
109 axi_lite_s2m : out axi_lite_s2m_t := axi_lite_s2m_init;
110 --# {{}}
111 -- Register values.
112{up_port if has_any_up else ""}\
113{down_port if has_any_down else ""}\
114 --# {{}}
115 -- Each bit is pulsed for one cycle when the corresponding register is read/written.
116{was_read_port}\
117{was_written_port}\
118 );
119end entity;
120"""
122 up_conversion = """
124 ------------------------------------------------------------------------------
125 -- Combinatorially convert the register record to a list of SLV values that can be handled
126 -- by the generic register file implementation.
127 assign_regs_up : process(regs_up)
128 begin
129 regs_up_slv <= to_slv(regs_up);
130 end process;
131"""
132 down_conversion = f"""
134 ------------------------------------------------------------------------------
135 -- Combinatorially convert the list of SLV values from the generic register file into the record
136 -- we want to use in our application.
137 assign_regs_down : process(regs_down_slv)
138 begin
139 regs_down <= to_{self.name}_regs_down(regs_down_slv);
140 end process;
141"""
143 was_read_conversion = f"""
145 ------------------------------------------------------------------------------
146 -- Combinatorially convert status mask to a record where only the applicable registers \
147are present.
148 assign_reg_was_read : process(reg_was_read_slv)
149 begin
150 reg_was_read <= to_{self.name}_reg_was_read(reg_was_read_slv);
151 end process;
152"""
154 was_written_conversion = f"""
156 ------------------------------------------------------------------------------
157 -- Combinatorially convert status mask to a record where only the applicable registers \
158are present.
159 assign_reg_was_written : process(reg_was_written_slv)
160 begin
161 reg_was_written <= to_{self.name}_reg_was_written(reg_was_written_slv);
162 end process;
163"""
165 return f"""\
166-- -----------------------------------------------------------------------------
167-- AXI-Lite register file for the '{self.name}' module registers.
168-- Sets correct generics, and performs conversion to the easy-to-use register record types.
169-- -----------------------------------------------------------------------------
171library ieee;
172use ieee.std_logic_1164.all;
174-- This VHDL file is a required dependency:
175-- https://github.com/hdl-modules/hdl-modules/blob/main/modules/axi_lite/src/axi_lite_pkg.vhd
176-- See https://hdl-registers.com/rst/generator/generator_vhdl.html for dependency details.
177library axi_lite;
178use axi_lite.axi_lite_pkg.all;
180-- This VHDL file is a required dependency:
181-- https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/\
182axi_lite_register_file.vhd
183-- See https://hdl-registers.com/rst/generator/generator_vhdl.html for dependency details.
184library register_file;
186use work.{self.name}_regs_pkg.all;
187use work.{self.name}_register_record_pkg.all;
190{entity}
191architecture a of {entity_name} is
193 constant default_values_slv : {self.name}_regs_t := to_slv(default_values);
195 signal regs_up_slv, regs_down_slv : {self.name}_regs_t := {self.name}_regs_init;
197 signal reg_was_read_slv, reg_was_written_slv : {self.name}_reg_was_accessed_t := (
198 others => '0'
199 );
201begin
203 ------------------------------------------------------------------------------
204 -- Instantiate the generic register file implementation:
205 -- https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/\
206axi_lite_register_file.vhd
207 -- See https://hdl-registers.com/rst/generator/generator_vhdl.html for dependency details.
208 axi_lite_register_file_inst : entity register_file.axi_lite_register_file
209 generic map (
210 registers => {self.name}_register_map,
211 default_values => default_values_slv
212 )
213 port map(
214 clk => clk,
215 reset => reset,
216 --
217 axi_lite_m2s => axi_lite_m2s,
218 axi_lite_s2m => axi_lite_s2m,
219 --
220 regs_up => regs_up_slv,
221 regs_down => regs_down_slv,
222 --
223 reg_was_read => reg_was_read_slv,
224 reg_was_written => reg_was_written_slv
225 );
226{up_conversion if has_any_up else ""}\
227{down_conversion if has_any_down else ""}\
228{was_read_conversion if was_read_port else ""}\
229{was_written_conversion if was_written_port else ""}\
231end architecture;
232"""
234 def _get_was_accessed_ports(self) -> tuple[str, str]:
235 has_any_read = self.has_any_software_accessible_register(
236 direction=SoftwareAccessDirection.READ
237 )
238 has_any_write = self.has_any_software_accessible_register(
239 direction=SoftwareAccessDirection.WRITE
240 )
242 # If present, is always the last port so no trailing semicolon needed.
243 was_written = (
244 (
245 f" reg_was_written : out {self.name}_reg_was_written_t := "
246 f"{self.name}_reg_was_written_init\n"
247 )
248 if has_any_write
249 else ""
250 )
252 was_read = (
253 f" reg_was_read : out {self.name}_reg_was_read_t := {self.name}_reg_was_read_init"
254 if has_any_read
255 else ""
256 )
258 if has_any_read:
259 was_read += ";\n" if was_written else "\n"
261 return was_read, was_written