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

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 

10# Standard libraries 

11from pathlib import Path 

12from typing import Any 

13 

14# First party libraries 

15from hdl_registers.generator.vhdl.vhdl_generator_common import VhdlGeneratorCommon 

16from hdl_registers.register_mode import HardwareAccessDirection, SoftwareAccessDirection 

17 

18 

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. 

23 

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. 

27 

28 It wraps the following VHDL file: 

29 

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 

33 

34 It also requires the generated packages from 

35 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`. 

36 

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. 

42 

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

46 

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

50 

51 __version__ = "1.0.0" 

52 

53 SHORT_DESCRIPTION = "VHDL AXI-Lite register file" 

54 

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" 

61 

62 def create(self, **kwargs: Any) -> Path: 

63 """ 

64 See super class for API details. 

65 

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

75 

76 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs) 

77 

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 

83 

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) 

86 

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) 

91 

92 was_read_port, was_written_port = self._get_was_accessed_ports() 

93 

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

116 

117 up_conversion = """ 

118 

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

128 

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

137 

138 was_read_conversion = f""" 

139 

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

148 

149 was_written_conversion = f""" 

150 

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

159 

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

171 

172library ieee; 

173use ieee.std_logic_1164.all; 

174 

175library axi_lite; 

176use axi_lite.axi_lite_pkg.all; 

177 

178library register_file; 

179use register_file.register_file_pkg.all; 

180 

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

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

183 

184 

185{entity} 

186architecture a of {entity_name} is 

187 

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

189 

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

191 others => '0' 

192 ); 

193 

194begin 

195 

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

222 

223end architecture; 

224""" 

225 

226 return vhdl 

227 

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 ) 

235 

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 ) 

245 

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 ) 

254 

255 if has_any_read: 

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

257 

258 return was_read, was_written