Coverage for hdl_registers/generator/vhdl/axi_lite/wrapper.py: 95%
38 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 typing import Any
14# First party libraries
15from hdl_registers.generator.vhdl.vhdl_generator_common import VhdlGeneratorCommon
16from hdl_registers.register_mode import HardwareAccessDirection, SoftwareAccessDirection
19class VhdlAxiLiteWrapperGenerator(VhdlGeneratorCommon):
20 """
21 Generate a VHDL wrapper around a generic AXI-Lite register file with correct generics and ports.
22 See the :ref:`generator_vhdl` article for usage details.
24 The wrapper will set the correct generics and will use record types for ``regs_up`` and
25 ``regs_down``.
26 This makes it very easy-to-use and saves a lot of manual conversion.
28 It wraps the following VHDL file:
30 * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd
31 * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/\
32axi_lite_register_file.vhd
34 It also requires the generated packages from
35 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`.
37 Note that the ``regs_up`` port is only available if there are any registers of a type where
38 hardware gives a value to the bus.
39 For example a "Read" register.
40 If instead, for example, there are only "Write" registers, the ``regs_up`` port will not be
41 available and the type for it is not available in the VHDL package.
43 Same, but vice versa, for the ``regs_down`` port.
44 Will only be available if there are any registers of a type where the bus provides a
45 value to the hardware, e.g. "Read, Write".
47 Similar concept for the ``reg_was_read`` and ``reg_was_written`` ports.
48 They are only present if there are any readable/writeable registers in the register list.
49 """
51 __version__ = "1.0.0"
53 SHORT_DESCRIPTION = "VHDL AXI-Lite register file"
55 @property
56 def output_file(self) -> Path:
57 """
58 Result will be placed in this file.
59 """
60 return self.output_folder / f"{self.name}_register_file_axi_lite.vhd"
62 def create(self, **kwargs: Any) -> Path:
63 """
64 See super class for API details.
66 Overloaded here because this file shall only be created if the register list
67 actually has any registers.
68 """
69 # The artifact from this generator was renamed in version 7.0.0.
70 # An old artifact laying around might cause confusion and compilation errors.
71 old_output_file = self.output_folder / f"{self.name}_reg_file.vhd"
72 if old_output_file.exists():
73 print(f"Deleting old artifact {old_output_file}")
74 old_output_file.unlink()
76 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs)
78 def get_code(self, **kwargs: Any) -> str:
79 """
80 Get VHDL code for a wrapper around the generic AXi_lite register file from hdl-modules:
81 """
82 entity_name = self.output_file.stem
84 up_port = f" regs_up : in {self.name}_regs_up_t := {self.name}_regs_up_init;\n"
85 has_any_up = self.has_any_hardware_accessible_register(HardwareAccessDirection.UP)
87 down_port = (
88 f" regs_down : out {self.name}_regs_down_t " f":= {self.name}_regs_down_init;\n"
89 )
90 has_any_down = self.has_any_hardware_accessible_register(HardwareAccessDirection.DOWN)
92 was_read_port, was_written_port = self._get_was_accessed_ports()
94 # Note that either of 'reg_was_read' or 'reg_was_written' is always present, otherwise
95 # there would be no registers and we would not create this wrapper.
96 # Hence it is safe to always end the 'regs_up'/'regs_down' ports with a semicolon.
97 entity = f"""\
98entity {entity_name} is
99 port (
100 clk : in std_ulogic;
101 --# { }
102 --# Register control bus.
103 axi_lite_m2s : in axi_lite_m2s_t;
104 axi_lite_s2m : out axi_lite_s2m_t := axi_lite_s2m_init;
105 --# { }
106 -- Register values.
107{up_port if has_any_up else ""}\
108{down_port if has_any_down else ""}\
109 --# { }
110 -- Each bit is pulsed for one cycle when the corresponding register is read/written.
111{was_read_port}\
112{was_written_port}\
113 );
114end entity;
115"""
117 up_conversion = """
119 ------------------------------------------------------------------------------
120 -- Combinatorially convert the register record to a list of SLV values that can be handled
121 -- by the generic register file implementation.
122 assign_regs_up : process(regs_up)
123 begin
124 regs_up_slv <= to_slv(regs_up);
125 end process;
126"""
127 down_conversion = f"""
129 ------------------------------------------------------------------------------
130 -- Combinatorially convert the list of SLV values from the generic register file into the record
131 -- we want to use in our application.
132 assign_regs_down : process(regs_down_slv)
133 begin
134 regs_down <= to_{self.name}_regs_down(regs_down_slv);
135 end process;
136"""
138 was_read_conversion = f"""
140 ------------------------------------------------------------------------------
141 -- Combinatorially convert status mask to a record where only the applicable registers \
142are present.
143 assign_reg_was_read : process(reg_was_read_slv)
144 begin
145 reg_was_read <= to_{self.name}_reg_was_read(reg_was_read_slv);
146 end process;
147"""
149 was_written_conversion = f"""
151 ------------------------------------------------------------------------------
152 -- Combinatorially convert status mask to a record where only the applicable registers \
153are present.
154 assign_reg_was_written : process(reg_was_written_slv)
155 begin
156 reg_was_written <= to_{self.name}_reg_was_written(reg_was_written_slv);
157 end process;
158"""
160 vhdl = f"""\
161-- -----------------------------------------------------------------------------
162-- AXI-Lite register file for the '{self.name}' module registers.
163--
164-- Is a wrapper around the generic AXI-Lite register file from hdl-modules:
165-- * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd
166-- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/\
167src/axi_lite_register_file.vhd
168--
169-- Sets correct generics, and performs conversion to the easy-to-use register record types.
170-- -----------------------------------------------------------------------------
172library ieee;
173use ieee.std_logic_1164.all;
175library axi_lite;
176use axi_lite.axi_lite_pkg.all;
178library register_file;
179use register_file.register_file_pkg.all;
181use work.{self.name}_regs_pkg.all;
182use work.{self.name}_register_record_pkg.all;
185{entity}
186architecture a of {entity_name} is
188 signal regs_up_slv, regs_down_slv : {self.name}_regs_t := {self.name}_regs_init;
190 signal reg_was_read_slv, reg_was_written_slv : {self.name}_reg_was_accessed_t := (
191 others => '0'
192 );
194begin
196 ------------------------------------------------------------------------------
197 -- Instantiate the generic AXI-Lite register file from
198 -- * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd
199 -- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/\
200axi_lite_register_file.vhd
201 axi_lite_register_file_inst : entity register_file.axi_lite_register_file
202 generic map (
203 registers => {self.name}_register_map,
204 default_values => {self.name}_regs_init
205 )
206 port map(
207 clk => clk,
208 --
209 axi_lite_m2s => axi_lite_m2s,
210 axi_lite_s2m => axi_lite_s2m,
211 --
212 regs_up => regs_up_slv,
213 regs_down => regs_down_slv,
214 --
215 reg_was_read => reg_was_read_slv,
216 reg_was_written => reg_was_written_slv
217 );
218{up_conversion if has_any_up else ""}\
219{down_conversion if has_any_down else ""}\
220{was_read_conversion if was_read_port else ""}\
221{was_written_conversion if was_written_port else ""}\
223end architecture;
224"""
226 return vhdl
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 (
248 f" reg_was_read : out {self.name}_reg_was_read_t := "
249 f"{self.name}_reg_was_read_init"
250 )
251 if has_any_read
252 else ""
253 )
255 if has_any_read:
256 was_read += ";\n" if was_written else "\n"
258 return was_read, was_written