Coverage for hdl_registers/generator/vhdl/axi_lite/wrapper.py: 95%
37 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 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 port (
97 clk : in std_ulogic;
98 -- Active-high synchronous reset.
99 -- The code in this entity uses initial values so an initial reset is NOT necessary.
100 -- This port can safely be left unconnected and tied to zero.
101 -- If asserted, it will reset the AXI-Lite handshaking state as well as all register values.
102 reset : in std_ulogic := '0';
103 --# { }
104 --# Register control bus.
105 axi_lite_m2s : in axi_lite_m2s_t;
106 axi_lite_s2m : out axi_lite_s2m_t := axi_lite_s2m_init;
107 --# { }
108 -- Register values.
109{up_port if has_any_up else ""}\
110{down_port if has_any_down else ""}\
111 --# { }
112 -- Each bit is pulsed for one cycle when the corresponding register is read/written.
113{was_read_port}\
114{was_written_port}\
115 );
116end entity;
117"""
119 up_conversion = """
121 ------------------------------------------------------------------------------
122 -- Combinatorially convert the register record to a list of SLV values that can be handled
123 -- by the generic register file implementation.
124 assign_regs_up : process(regs_up)
125 begin
126 regs_up_slv <= to_slv(regs_up);
127 end process;
128"""
129 down_conversion = f"""
131 ------------------------------------------------------------------------------
132 -- Combinatorially convert the list of SLV values from the generic register file into the record
133 -- we want to use in our application.
134 assign_regs_down : process(regs_down_slv)
135 begin
136 regs_down <= to_{self.name}_regs_down(regs_down_slv);
137 end process;
138"""
140 was_read_conversion = f"""
142 ------------------------------------------------------------------------------
143 -- Combinatorially convert status mask to a record where only the applicable registers \
144are present.
145 assign_reg_was_read : process(reg_was_read_slv)
146 begin
147 reg_was_read <= to_{self.name}_reg_was_read(reg_was_read_slv);
148 end process;
149"""
151 was_written_conversion = f"""
153 ------------------------------------------------------------------------------
154 -- Combinatorially convert status mask to a record where only the applicable registers \
155are present.
156 assign_reg_was_written : process(reg_was_written_slv)
157 begin
158 reg_was_written <= to_{self.name}_reg_was_written(reg_was_written_slv);
159 end process;
160"""
162 return f"""\
163-- -----------------------------------------------------------------------------
164-- AXI-Lite register file for the '{self.name}' module registers.
165-- Sets correct generics, and performs conversion to the easy-to-use register record types.
166-- -----------------------------------------------------------------------------
168library ieee;
169use ieee.std_logic_1164.all;
171-- This VHDL file is a required dependency:
172-- https://github.com/hdl-modules/hdl-modules/blob/main/modules/axi_lite/src/axi_lite_pkg.vhd
173-- See https://hdl-registers.com/rst/generator/generator_vhdl.html for dependency details.
174library axi_lite;
175use axi_lite.axi_lite_pkg.all;
177-- This VHDL file is a required dependency:
178-- https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/\
179axi_lite_register_file.vhd
180-- See https://hdl-registers.com/rst/generator/generator_vhdl.html for dependency details.
181library register_file;
183use work.{self.name}_regs_pkg.all;
184use work.{self.name}_register_record_pkg.all;
187{entity}
188architecture a of {entity_name} is
190 signal regs_up_slv, regs_down_slv : {self.name}_regs_t := {self.name}_regs_init;
192 signal reg_was_read_slv, reg_was_written_slv : {self.name}_reg_was_accessed_t := (
193 others => '0'
194 );
196begin
198 ------------------------------------------------------------------------------
199 -- Instantiate the generic register file implementation:
200 -- https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/\
201axi_lite_register_file.vhd
202 -- See https://hdl-registers.com/rst/generator/generator_vhdl.html for dependency details.
203 axi_lite_register_file_inst : entity register_file.axi_lite_register_file
204 generic map (
205 registers => {self.name}_register_map,
206 default_values => {self.name}_regs_init
207 )
208 port map(
209 clk => clk,
210 reset => reset,
211 --
212 axi_lite_m2s => axi_lite_m2s,
213 axi_lite_s2m => axi_lite_s2m,
214 --
215 regs_up => regs_up_slv,
216 regs_down => regs_down_slv,
217 --
218 reg_was_read => reg_was_read_slv,
219 reg_was_written => reg_was_written_slv
220 );
221{up_conversion if has_any_up else ""}\
222{down_conversion if has_any_down else ""}\
223{was_read_conversion if was_read_port else ""}\
224{was_written_conversion if was_written_port else ""}\
226end architecture;
227"""
229 def _get_was_accessed_ports(self) -> tuple[str, str]:
230 has_any_read = self.has_any_software_accessible_register(
231 direction=SoftwareAccessDirection.READ
232 )
233 has_any_write = self.has_any_software_accessible_register(
234 direction=SoftwareAccessDirection.WRITE
235 )
237 # If present, is always the last port so no trailing semicolon needed.
238 was_written = (
239 (
240 f" reg_was_written : out {self.name}_reg_was_written_t := "
241 f"{self.name}_reg_was_written_init\n"
242 )
243 if has_any_write
244 else ""
245 )
247 was_read = (
248 f" reg_was_read : out {self.name}_reg_was_read_t := {self.name}_reg_was_read_init"
249 if has_any_read
250 else ""
251 )
253 if has_any_read:
254 was_read += ";\n" if was_written else "\n"
256 return was_read, was_written