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

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# -------------------------------------------------------------------------------------------------- 

9 

10from pathlib import Path 

11from typing import Any 

12 

13from hdl_registers.generator.vhdl.vhdl_generator_common import VhdlGeneratorCommon 

14from hdl_registers.register_mode import HardwareAccessDirection, SoftwareAccessDirection 

15 

16 

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. 

21 

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. 

25 

26 The file is dependent on the packages from 

27 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`. 

28 See also :ref:`vhdl_dependencies` for further dependencies. 

29 

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. 

35 

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". 

39 

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 """ 

43 

44 __version__ = "1.0.3" 

45 

46 SHORT_DESCRIPTION = "VHDL AXI-Lite register file" 

47 

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" 

54 

55 def create( 

56 self, 

57 **kwargs: Any, # noqa: ANN401 

58 ) -> Path: 

59 """ 

60 See super class for API details. 

61 

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() 

71 

72 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs) 

73 

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 

82 

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) 

85 

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) 

88 

89 was_read_port, was_written_port = self._get_was_accessed_ports() 

90 

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""" 

121 

122 up_conversion = """ 

123 

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""" 

133 

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""" 

142 

143 was_read_conversion = f""" 

144 

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""" 

153 

154 was_written_conversion = f""" 

155 

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""" 

164 

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-- ----------------------------------------------------------------------------- 

170 

171library ieee; 

172use ieee.std_logic_1164.all; 

173 

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; 

179 

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; 

185 

186use work.{self.name}_regs_pkg.all; 

187use work.{self.name}_register_record_pkg.all; 

188 

189 

190{entity} 

191architecture a of {entity_name} is 

192 

193 constant default_values_slv : {self.name}_regs_t := to_slv(default_values); 

194 

195 signal regs_up_slv, regs_down_slv : {self.name}_regs_t := {self.name}_regs_init; 

196 

197 signal reg_was_read_slv, reg_was_written_slv : {self.name}_reg_was_accessed_t := ( 

198 others => '0' 

199 ); 

200 

201begin 

202 

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 ""}\ 

230 

231end architecture; 

232""" 

233 

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 ) 

241 

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 ) 

251 

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 ) 

257 

258 if has_any_read: 

259 was_read += ";\n" if was_written else "\n" 

260 

261 return was_read, was_written