Coverage for hdl_registers/generator/vhdl/axi_lite/wrapper.py: 100%
34 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-01 20:50 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-01 20:50 +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/reg_file/reg_file.html#axi-lite-reg-file-vhd
31 * https://github.com/hdl-modules/hdl-modules/blob/main/modules/reg_file/src/\
32axi_lite_reg_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 map.
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}_reg_file.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 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs)
71 def get_code(self, **kwargs: Any) -> str:
72 """
73 Get VHDL code for a wrapper around the generic AXi_lite register file from hdl-modules:
74 """
75 entity_name = self.output_file.stem
77 up_port = f" regs_up : in {self.name}_regs_up_t := {self.name}_regs_up_init;\n"
78 has_any_up = self.has_any_hardware_accessible_register(HardwareAccessDirection.UP)
80 down_port = (
81 f" regs_down : out {self.name}_regs_down_t " f":= {self.name}_regs_down_init;\n"
82 )
83 has_any_down = self.has_any_hardware_accessible_register(HardwareAccessDirection.DOWN)
85 was_read_port, was_written_port = self._get_was_accessed_ports()
87 # Note that either of 'reg_was_read' or 'reg_was_written' is always present, otherwise
88 # there would be no registers and we would not create this wrapper.
89 # Hence it is safe to always end the 'regs_up'/'regs_down' ports with a semicolon.
90 entity = f"""\
91entity {entity_name} is
92 port (
93 clk : in std_ulogic;
94 --# {{}}
95 --# Register control bus.
96 axi_lite_m2s : in axi_lite_m2s_t;
97 axi_lite_s2m : out axi_lite_s2m_t := axi_lite_s2m_init;
98 --# {{}}
99 -- Register values.
100{up_port if has_any_up else ""}\
101{down_port if has_any_down else ""}\
102 --# {{}}
103 -- Each bit is pulsed for one cycle when the corresponding register is read/written.
104{was_read_port}\
105{was_written_port}\
106 );
107end entity;
108"""
110 up_conversion = """
112 ------------------------------------------------------------------------------
113 -- Combinatorially convert the register record to a list of SLV values that can be handled
114 -- by the generic register file implementation.
115 assign_regs_up : process(regs_up)
116 begin
117 regs_up_slv <= to_slv(regs_up);
118 end process;
119"""
120 down_conversion = f"""
122 ------------------------------------------------------------------------------
123 -- Combinatorially convert the list of SLV values from the generic register file into the record
124 -- we want to use in our application.
125 assign_regs_down : process(regs_down_slv)
126 begin
127 regs_down <= to_{self.name}_regs_down(regs_down_slv);
128 end process;
129"""
131 was_read_conversion = f"""
133 ------------------------------------------------------------------------------
134 -- Combinatorially convert status mask to a record where only the applicable registers \
135are present.
136 assign_reg_was_read : process(reg_was_read_slv)
137 begin
138 reg_was_read <= to_{self.name}_reg_was_read(reg_was_read_slv);
139 end process;
140"""
142 was_written_conversion = f"""
144 ------------------------------------------------------------------------------
145 -- Combinatorially convert status mask to a record where only the applicable registers \
146are present.
147 assign_reg_was_written : process(reg_was_written_slv)
148 begin
149 reg_was_written <= to_{self.name}_reg_was_written(reg_was_written_slv);
150 end process;
151"""
153 vhdl = f"""\
154-- -----------------------------------------------------------------------------
155-- AXI-Lite register file for the '{self.name}' module registers.
156--
157-- Is a wrapper around the generic AXI-Lite register file from hdl-modules:
158-- * https://hdl-modules.com/modules/reg_file/reg_file.html#axi-lite-reg-file-vhd
159-- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/reg_file/src/axi_lite_reg_file.vhd
160--
161-- Sets correct generics, and performs conversion to the easy-to-use register record types.
162-- -----------------------------------------------------------------------------
163{self.header}\
164-- -----------------------------------------------------------------------------
166library ieee;
167use ieee.std_logic_1164.all;
169library axi_lite;
170use axi_lite.axi_lite_pkg.all;
172library reg_file;
173use reg_file.reg_file_pkg.all;
175use work.{self.name}_regs_pkg.all;
176use work.{self.name}_register_record_pkg.all;
179{entity}
180architecture a of {entity_name} is
182 signal regs_up_slv, regs_down_slv : {self.name}_regs_t := {self.name}_regs_init;
184 signal reg_was_read_slv, reg_was_written_slv : {self.name}_reg_was_accessed_t := (
185 others => '0'
186 );
188begin
190 ------------------------------------------------------------------------------
191 -- Instantiate the generic AXI-Lite register file from
192 -- * https://hdl-modules.com/modules/reg_file/reg_file.html#axi-lite-reg-file-vhd
193 -- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/reg_file/src/\
194axi_lite_reg_file.vhd
195 axi_lite_reg_file_inst : entity reg_file.axi_lite_reg_file
196 generic map (
197 regs => {self.name}_reg_map,
198 default_values => {self.name}_regs_init
199 )
200 port map(
201 clk => clk,
202 --
203 axi_lite_m2s => axi_lite_m2s,
204 axi_lite_s2m => axi_lite_s2m,
205 --
206 regs_up => regs_up_slv,
207 regs_down => regs_down_slv,
208 --
209 reg_was_read => reg_was_read_slv,
210 reg_was_written => reg_was_written_slv
211 );
212{up_conversion if has_any_up else ""}\
213{down_conversion if has_any_down else ""}\
214{was_read_conversion if was_read_port else ""}\
215{was_written_conversion if was_written_port else ""}\
217end architecture;
218"""
220 return vhdl
222 def _get_was_accessed_ports(self) -> tuple[str, str]:
223 has_any_read = self.has_any_software_accessible_register(
224 direction=SoftwareAccessDirection.READ
225 )
226 has_any_write = self.has_any_software_accessible_register(
227 direction=SoftwareAccessDirection.WRITE
228 )
230 # If present, is always the last port so no trailing semicolon needed.
231 was_written = (
232 (
233 f" reg_was_written : out {self.name}_reg_was_written_t := "
234 f"{self.name}_reg_was_written_init\n"
235 )
236 if has_any_write
237 else ""
238 )
240 was_read = (
241 (
242 f" reg_was_read : out {self.name}_reg_was_read_t := "
243 f"{self.name}_reg_was_read_init"
244 )
245 if has_any_read
246 else ""
247 )
249 if has_any_read:
250 was_read += ";\n" if was_written else "\n"
252 return was_read, was_written