Coverage for hdl_registers/generator/vhdl/axi_lite/wrapper.py: 95%

37 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-12 11:11 +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 It wraps the following VHDL file: 

27 

28 * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd 

29 * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/\ 

30axi_lite_register_file.vhd 

31 

32 It also requires the generated packages from 

33 :class:`.VhdlRegisterPackageGenerator` and :class:`.VhdlRecordPackageGenerator`. 

34 

35 Note that the ``regs_up`` port is only available if there are any registers of a type where 

36 hardware gives a value to the bus. 

37 For example a "Read" register. 

38 If instead, for example, there are only "Write" registers, the ``regs_up`` port will not be 

39 available and the type for it is not available in the VHDL package. 

40 

41 Same, but vice versa, for the ``regs_down`` port. 

42 Will only be available if there are any registers of a type where the bus provides a 

43 value to the hardware, e.g. "Read, Write". 

44 

45 Similar concept for the ``reg_was_read`` and ``reg_was_written`` ports. 

46 They are only present if there are any readable/writeable registers in the register list. 

47 """ 

48 

49 __version__ = "1.0.0" 

50 

51 SHORT_DESCRIPTION = "VHDL AXI-Lite register file" 

52 

53 @property 

54 def output_file(self) -> Path: 

55 """ 

56 Result will be placed in this file. 

57 """ 

58 return self.output_folder / f"{self.name}_register_file_axi_lite.vhd" 

59 

60 def create( 

61 self, 

62 **kwargs: Any, # noqa: ANN401 

63 ) -> Path: 

64 """ 

65 See super class for API details. 

66 

67 Overloaded here because this file shall only be created if the register list 

68 actually has any registers. 

69 """ 

70 # The artifact from this generator was renamed in version 7.0.0. 

71 # An old artifact laying around might cause confusion and compilation errors. 

72 old_output_file = self.output_folder / f"{self.name}_reg_file.vhd" 

73 if old_output_file.exists(): 

74 print(f"Deleting old artifact {old_output_file}") 

75 old_output_file.unlink() 

76 

77 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs) 

78 

79 def get_code( 

80 self, 

81 **kwargs: Any, # noqa: ANN401, ARG002 

82 ) -> str: 

83 """ 

84 Get VHDL code for a wrapper around the generic AXi_lite register file from hdl-modules: 

85 """ 

86 entity_name = self.output_file.stem 

87 

88 up_port = f" regs_up : in {self.name}_regs_up_t := {self.name}_regs_up_init;\n" 

89 has_any_up = self.has_any_hardware_accessible_register(HardwareAccessDirection.UP) 

90 

91 down_port = f" regs_down : out {self.name}_regs_down_t := {self.name}_regs_down_init;\n" 

92 has_any_down = self.has_any_hardware_accessible_register(HardwareAccessDirection.DOWN) 

93 

94 was_read_port, was_written_port = self._get_was_accessed_ports() 

95 

96 # Note that either of 'reg_was_read' or 'reg_was_written' is always present, otherwise 

97 # there would be no registers and we would not create this wrapper. 

98 # Hence it is safe to always end the 'regs_up'/'regs_down' ports with a semicolon. 

99 entity = f"""\ 

100entity {entity_name} is 

101 port ( 

102 clk : in std_ulogic; 

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

166-- Is a wrapper around the generic AXI-Lite register file from hdl-modules: 

167-- * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd 

168-- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/\ 

169src/axi_lite_register_file.vhd 

170-- 

171-- Sets correct generics, and performs conversion to the easy-to-use register record types. 

172-- ----------------------------------------------------------------------------- 

173 

174library ieee; 

175use ieee.std_logic_1164.all; 

176 

177library axi_lite; 

178use axi_lite.axi_lite_pkg.all; 

179 

180library register_file; 

181use register_file.register_file_pkg.all; 

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 AXI-Lite register file from 

200 -- * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd 

201 -- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/\ 

202axi_lite_register_file.vhd 

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

211 axi_lite_m2s => axi_lite_m2s, 

212 axi_lite_s2m => axi_lite_s2m, 

213 -- 

214 regs_up => regs_up_slv, 

215 regs_down => regs_down_slv, 

216 -- 

217 reg_was_read => reg_was_read_slv, 

218 reg_was_written => reg_was_written_slv 

219 ); 

220{up_conversion if has_any_up else ""}\ 

221{down_conversion if has_any_down else ""}\ 

222{was_read_conversion if was_read_port else ""}\ 

223{was_written_conversion if was_written_port else ""}\ 

224 

225end architecture; 

226""" 

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 f" reg_was_read : out {self.name}_reg_was_read_t := {self.name}_reg_was_read_init" 

248 if has_any_read 

249 else "" 

250 ) 

251 

252 if has_any_read: 

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

254 

255 return was_read, was_written