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

34 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-19 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/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 

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

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}_reg_file.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 return self._create_if_there_are_registers_otherwise_delete_file(**kwargs) 

70 

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 

76 

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) 

79 

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) 

84 

85 was_read_port, was_written_port = self._get_was_accessed_ports() 

86 

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

109 

110 up_conversion = """ 

111 

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

121 

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

130 

131 was_read_conversion = f""" 

132 

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

141 

142 was_written_conversion = f""" 

143 

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

152 

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

165 

166library ieee; 

167use ieee.std_logic_1164.all; 

168 

169library axi_lite; 

170use axi_lite.axi_lite_pkg.all; 

171 

172library reg_file; 

173use reg_file.reg_file_pkg.all; 

174 

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

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

177 

178 

179{entity} 

180architecture a of {entity_name} is 

181 

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

183 

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

185 others => '0' 

186 ); 

187 

188begin 

189 

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

216 

217end architecture; 

218""" 

219 

220 return vhdl 

221 

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 ) 

229 

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 ) 

239 

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 ) 

248 

249 if has_any_read: 

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

251 

252 return was_read, was_written