Coverage for hdl_registers/generator/vhdl/axi_lite/wrapper.py: 95%
37 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 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 It wraps the following VHDL file:
28 * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd
29 * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/\
30axi_lite_register_file.vhd
32 It also requires the generated packages from
33 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`.
35 Note that the ``regs_up`` port is only available if there are any registers of a type where
36 hardware gives a value to the bus.
37 For example a "Read" register.
38 If instead, for example, there are only "Write" registers, the ``regs_up`` port will not be
39 available and the type for it is not available in the VHDL package.
41 Same, but vice versa, for the ``regs_down`` port.
42 Will only be available if there are any registers of a type where the bus provides a
43 value to the hardware, e.g. "Read, Write".
45 Similar concept for the ``reg_was_read`` and ``reg_was_written`` ports.
46 They are only present if there are any readable/writeable registers in the register list.
47 """
49 __version__ = "1.0.0"
51 SHORT_DESCRIPTION = "VHDL AXI-Lite register file"
53 @property
54 def output_file(self) -> Path:
55 """
56 Result will be placed in this file.
57 """
58 return self.output_folder / f"{self.name}_register_file_axi_lite.vhd"
60 def create(
61 self,
62 **kwargs: Any, # noqa: ANN401
63 ) -> Path:
64 """
65 See super class for API details.
67 Overloaded here because this file shall only be created if the register list
68 actually has any registers.
69 """
70 # The artifact from this generator was renamed in version 7.0.0.
71 # An old artifact laying around might cause confusion and compilation errors.
72 old_output_file = self.output_folder / f"{self.name}_reg_file.vhd"
73 if old_output_file.exists():
74 print(f"Deleting old artifact {old_output_file}")
75 old_output_file.unlink()
77 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs)
79 def get_code(
80 self,
81 **kwargs: Any, # noqa: ANN401, ARG002
82 ) -> str:
83 """
84 Get VHDL code for a wrapper around the generic AXi_lite register file from hdl-modules:
85 """
86 entity_name = self.output_file.stem
88 up_port = f" regs_up : in {self.name}_regs_up_t := {self.name}_regs_up_init;\n"
89 has_any_up = self.has_any_hardware_accessible_register(HardwareAccessDirection.UP)
91 down_port = f" regs_down : out {self.name}_regs_down_t := {self.name}_regs_down_init;\n"
92 has_any_down = self.has_any_hardware_accessible_register(HardwareAccessDirection.DOWN)
94 was_read_port, was_written_port = self._get_was_accessed_ports()
96 # Note that either of 'reg_was_read' or 'reg_was_written' is always present, otherwise
97 # there would be no registers and we would not create this wrapper.
98 # Hence it is safe to always end the 'regs_up'/'regs_down' ports with a semicolon.
99 entity = f"""\
100entity {entity_name} is
101 port (
102 clk : in std_ulogic;
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--
166-- Is a wrapper around the generic AXI-Lite register file from hdl-modules:
167-- * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd
168-- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/\
169src/axi_lite_register_file.vhd
170--
171-- Sets correct generics, and performs conversion to the easy-to-use register record types.
172-- -----------------------------------------------------------------------------
174library ieee;
175use ieee.std_logic_1164.all;
177library axi_lite;
178use axi_lite.axi_lite_pkg.all;
180library register_file;
181use register_file.register_file_pkg.all;
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 AXI-Lite register file from
200 -- * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd
201 -- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/\
202axi_lite_register_file.vhd
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 --
211 axi_lite_m2s => axi_lite_m2s,
212 axi_lite_s2m => axi_lite_s2m,
213 --
214 regs_up => regs_up_slv,
215 regs_down => regs_down_slv,
216 --
217 reg_was_read => reg_was_read_slv,
218 reg_was_written => reg_was_written_slv
219 );
220{up_conversion if has_any_up else ""}\
221{down_conversion if has_any_down else ""}\
222{was_read_conversion if was_read_port else ""}\
223{was_written_conversion if was_written_port else ""}\
225end architecture;
226"""
228 def _get_was_accessed_ports(self) -> tuple[str, str]:
229 has_any_read = self.has_any_software_accessible_register(
230 direction=SoftwareAccessDirection.READ
231 )
232 has_any_write = self.has_any_software_accessible_register(
233 direction=SoftwareAccessDirection.WRITE
234 )
236 # If present, is always the last port so no trailing semicolon needed.
237 was_written = (
238 (
239 f" reg_was_written : out {self.name}_reg_was_written_t := "
240 f"{self.name}_reg_was_written_init\n"
241 )
242 if has_any_write
243 else ""
244 )
246 was_read = (
247 f" reg_was_read : out {self.name}_reg_was_read_t := {self.name}_reg_was_read_init"
248 if has_any_read
249 else ""
250 )
252 if has_any_read:
253 was_read += ";\n" if was_written else "\n"
255 return was_read, was_written