Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -------------------------------------------------------------------------------------------------- 

2# Copyright (c) Lukas Vik. All rights reserved. 

3# 

4# This file is part of the tsfpga project. 

5# https://tsfpga.com 

6# https://gitlab.com/tsfpga/tsfpga 

7# -------------------------------------------------------------------------------------------------- 

8 

9import copy 

10import tomlkit 

11 

12from tsfpga.system_utils import read_file 

13from .register_list import RegisterList 

14from .constant import Constant 

15 

16 

17def load_toml_file(toml_file): 

18 if not toml_file.exists(): 

19 raise FileNotFoundError(f"Requested TOML file does not exist: {toml_file}") 

20 

21 raw_toml = read_file(toml_file) 

22 try: 

23 return tomlkit.loads(raw_toml) 

24 except Exception as exception_info: 

25 message = f"Error while parsing TOML file {toml_file}:\n{exception_info}" 

26 raise ValueError(message) from exception_info 

27 

28 

29def from_toml(module_name, toml_file, default_registers=None): 

30 """ 

31 Parse a register TOML file. 

32 

33 Arguments: 

34 module_name (str): The name of the module that these registers belong to. 

35 toml_file (`pathlib.Path`): The TOML file path. 

36 default_registers (list(:class:`.Register`)): List of default registers. 

37 

38 Returns: 

39 :class:`.RegisterList`: The resulting register list. 

40 """ 

41 parser = RegisterParser( 

42 module_name=module_name, 

43 source_definition_file=toml_file, 

44 default_registers=default_registers, 

45 ) 

46 toml_data = load_toml_file(toml_file) 

47 

48 return parser.parse(toml_data) 

49 

50 

51class RegisterParser: 

52 

53 recognized_constant_items = {"value", "description"} 

54 recognized_register_items = {"mode", "description", "bit", "bit_vector"} 

55 recognized_register_array_items = {"array_length", "description", "register"} 

56 recognized_bit_items = {"description", "default_value"} 

57 recognized_bit_vector_items = {"description", "width", "default_value"} 

58 

59 def __init__(self, module_name, source_definition_file, default_registers): 

60 self._register_list = RegisterList( 

61 name=module_name, source_definition_file=source_definition_file 

62 ) 

63 self._source_definition_file = source_definition_file 

64 

65 self._default_register_names = [] 

66 if default_registers is not None: 

67 # Perform deep copy of the mutable register objects 

68 self._register_list.register_objects = copy.deepcopy(default_registers) 

69 for register in default_registers: 

70 self._default_register_names.append(register.name) 

71 

72 self._names_taken = set() 

73 

74 def parse(self, register_data): 

75 """ 

76 Parse the TOML data. 

77 

78 Arguments: 

79 register_data (str): TOML register data. 

80 

81 Returns: 

82 :class:`.RegisterList`: The resulting register list. 

83 """ 

84 if "constant" in register_data: 

85 for name, items in register_data["constant"].items(): 

86 self._parse_constant(name, items) 

87 

88 if "register" in register_data: 

89 for name, items in register_data["register"].items(): 

90 self._parse_plain_register(name, items) 

91 

92 if "register_array" in register_data: 

93 for name, items in register_data["register_array"].items(): 

94 self._parse_register_array(name, items) 

95 

96 return self._register_list 

97 

98 def _parse_constant(self, name, items): 

99 constant = Constant(name=name, value=items["value"]) 

100 

101 for item_name, item_value in items.items(): 

102 if item_name not in self.recognized_constant_items: 

103 message = ( 

104 f'Error while parsing constant "{name}" in {self._source_definition_file}: ' 

105 f'Unknown key "{item_name}"' 

106 ) 

107 raise ValueError(message) 

108 

109 if item_name == "description": 

110 constant.description = item_value 

111 

112 self._register_list.constants.append(constant) 

113 

114 def _parse_plain_register(self, name, items): 

115 for item_name in items.keys(): 

116 if item_name not in self.recognized_register_items: 

117 message = ( 

118 f'Error while parsing register "{name}" in {self._source_definition_file}: ' 

119 f'Unknown key "{item_name}"' 

120 ) 

121 raise ValueError(message) 

122 

123 description = items.get("description", "") 

124 

125 if name in self._default_register_names: 

126 # Default registers can be "updated" in the sense that the user can use a custom 

127 # description and add whatever bits they use in the current module. They can not however 

128 # change the mode. 

129 if "mode" in items: 

130 message = ( 

131 f'Overloading register "{name}" in {self._source_definition_file}, ' 

132 'one can not change "mode" from default' 

133 ) 

134 raise ValueError(message) 

135 

136 register = self._register_list.get_register(name) 

137 register.description = description 

138 

139 else: 

140 # If it is a new register however the mode has to be specified. 

141 if "mode" not in items: 

142 raise ValueError( 

143 f'Register "{name}" in {self._source_definition_file} does not have ' 

144 '"mode" field' 

145 ) 

146 mode = items["mode"] 

147 register = self._register_list.append_register( 

148 name=name, mode=mode, description=description 

149 ) 

150 

151 self._names_taken.add(name) 

152 

153 if "bit" in items: 

154 self._parse_bits(register, items["bit"]) 

155 

156 if "bit_vector" in items: 

157 self._parse_bit_vectors(register, items["bit_vector"]) 

158 

159 def _parse_register_array(self, name, items): 

160 if name in self._names_taken: 

161 message = f'Duplicate name "{name}" in {self._source_definition_file}' 

162 raise ValueError(message) 

163 if "array_length" not in items: 

164 message = ( 

165 f'Register array "{name}" in {self._source_definition_file} does not have ' 

166 '"array_length" attribute' 

167 ) 

168 raise ValueError(message) 

169 

170 for item_name in items: 

171 if item_name not in self.recognized_register_array_items: 

172 message = ( 

173 f'Error while parsing register array "{name}" in ' 

174 f'{self._source_definition_file}: Unknown key "{item_name}"' 

175 ) 

176 raise ValueError(message) 

177 

178 length = items["array_length"] 

179 description = items.get("description", "") 

180 register_array = self._register_list.append_register_array( 

181 name=name, length=length, description=description 

182 ) 

183 

184 for register_name, register_items in items["register"].items(): 

185 # The only required field 

186 if "mode" not in register_items: 

187 message = ( 

188 f'Register "{register_name}" within array "{name}" in ' 

189 f'{self._source_definition_file} does not have "mode" field' 

190 ) 

191 raise ValueError(message) 

192 

193 for register_item_name in register_items.keys(): 

194 if register_item_name not in self.recognized_register_items: 

195 message = ( 

196 f'Error while parsing register "{register_name}" in array "{name}" in ' 

197 f'{self._source_definition_file}: Unknown key "{register_item_name}"' 

198 ) 

199 raise ValueError(message) 

200 

201 mode = register_items["mode"] 

202 description = register_items.get("description", "") 

203 

204 register = register_array.append_register( 

205 name=register_name, mode=mode, description=description 

206 ) 

207 

208 if "bit" in register_items: 

209 self._parse_bits(register, register_items["bit"]) 

210 

211 if "bit_vector" in register_items: 

212 self._parse_bit_vectors(register, register_items["bit_vector"]) 

213 

214 def _parse_bits(self, register, bit_configurations): 

215 for bit_name, bit_configuration in bit_configurations.items(): 

216 for item_name in bit_configuration.keys(): 

217 if item_name not in self.recognized_bit_items: 

218 message = ( 

219 f'Error while parsing bit "{bit_name}" in register "{register.name}" in ' 

220 f'{self._source_definition_file}: Unknown key "{item_name}"' 

221 ) 

222 raise ValueError(message) 

223 

224 description = bit_configuration.get("description", "") 

225 default_value = bit_configuration.get("default_value", "0") 

226 

227 register.append_bit(name=bit_name, description=description, default_value=default_value) 

228 

229 def _parse_bit_vectors(self, register, bit_vector_configurations): 

230 for vector_name, vector_configuration in bit_vector_configurations.items(): 

231 # The only required field 

232 if "width" not in vector_configuration: 

233 message = ( 

234 f'Bit vector "{vector_name}" in register "{register.name}" in file ' 

235 f'{self._source_definition_file} does not have a "width" property' 

236 ) 

237 raise ValueError(message) 

238 

239 for item_name in vector_configuration.keys(): 

240 if item_name not in self.recognized_bit_vector_items: 

241 message = ( 

242 f'Error while parsing bit vector "{vector_name}" in register ' 

243 f'"{register.name}" in {self._source_definition_file}: ' 

244 f'Unknown key "{item_name}"' 

245 ) 

246 raise ValueError(message) 

247 

248 width = vector_configuration["width"] 

249 

250 description = vector_configuration.get("description", "") 

251 default_value = vector_configuration.get("default_value", "0" * width) 

252 

253 register.append_bit_vector(vector_name, description, width, default_value)