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

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

118 

119 up_conversion = """ 

120 

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

130 

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

139 

140 was_read_conversion = f""" 

141 

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

150 

151 was_written_conversion = f""" 

152 

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

161 

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

167 

168library ieee; 

169use ieee.std_logic_1164.all; 

170 

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; 

176 

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; 

182 

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

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

185 

186 

187{entity} 

188architecture a of {entity_name} is 

189 

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

191 

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

193 others => '0' 

194 ); 

195 

196begin 

197 

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

225 

226end architecture; 

227""" 

228 

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 ) 

236 

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 ) 

246 

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 ) 

252 

253 if has_any_read: 

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

255 

256 return was_read, was_written