Coverage for hdl_registers/register_c_generator.py: 99%

100 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-01-29 22:03 +0000

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

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

3# 

4# This file is part of the hdl_registers project, a HDL register generator fast enough to be run 

5# in real time. 

6# https://hdl-registers.com 

7# https://gitlab.com/hdl_registers/hdl_registers 

8# -------------------------------------------------------------------------------------------------- 

9 

10# Local folder libraries 

11from .register import REGISTER_MODES, Register 

12from .register_code_generator import RegisterCodeGenerator 

13 

14 

15class RegisterCGenerator(RegisterCodeGenerator): 

16 """ 

17 Generate a C code header with register information. 

18 

19 There is no unit test of this class that checks the generated code. It is instead functionally 

20 tested in the file test_register_compilation.py. That test generates C code from an example 

21 register set, compiles it and performs some run-time assertions in a C program. 

22 That test is considered more meaningful and exhaustive than a unit test would be. 

23 """ 

24 

25 def __init__(self, module_name, generated_info): 

26 """ 

27 Arguments: 

28 module_name (str): The name of the register map. 

29 generated_info (list(str)): Will be placed in the file headers. 

30 """ 

31 self.module_name = module_name 

32 self.generated_info = generated_info 

33 

34 def get_header(self, register_objects, constants): 

35 """ 

36 Get a complete C header with all constants and all registers. 

37 

38 Arguments: 

39 register_objects (list): Register arrays and registers to be included. 

40 constants (list(Constant)): Constants to be included. 

41 

42 Returns: 

43 str: C code. 

44 """ 

45 define_name = self.module_name.upper() + "_REGS_H" 

46 

47 c_code = f"""\ 

48{self._file_header()} 

49#ifndef {define_name} 

50#define {define_name} 

51 

52{self._constants(constants)} 

53{self._number_of_registers(register_objects)} 

54{self._register_struct(register_objects)} 

55{self._register_defines(register_objects)}\ 

56#endif {self._comment(define_name)}""" 

57 

58 return c_code 

59 

60 @staticmethod 

61 def _comment(comment, indentation=0): 

62 indent = " " * indentation 

63 return f"{indent}// {comment}\n" 

64 

65 def _file_header(self): 

66 return "".join([self._comment(header_line) for header_line in self.generated_info]) 

67 

68 def _register_struct(self, register_objects): 

69 array_structs = "" 

70 

71 register_struct_type = f"{self.module_name}_regs_t" 

72 register_struct = self._comment("Type for this register map.") 

73 register_struct += f"typedef struct {register_struct_type}\n" 

74 register_struct += "{\n" 

75 for register_object in register_objects: 

76 if isinstance(register_object, Register): 

77 register_struct += self._comment_block(register_object.description, indentation=2) 

78 register_struct += self._comment( 

79 f'Mode "{REGISTER_MODES[register_object.mode].mode_readable}".', indentation=2 

80 ) 

81 register_struct += f" uint32_t {register_object.name};\n" 

82 

83 else: 

84 array_struct_type = f"{self.module_name}_{register_object.name}_t" 

85 

86 array_structs += self._comment( 

87 f'Type for the "{register_object.name}" register array.' 

88 ) 

89 array_structs += f"typedef struct {array_struct_type}\n" 

90 array_structs += "{\n" 

91 for register in register_object.registers: 

92 array_structs += self._comment_block(register.description, indentation=2) 

93 array_structs += self._comment( 

94 f'Mode "{REGISTER_MODES[register.mode].mode_readable}".', indentation=2 

95 ) 

96 array_structs += f" uint32_t {register.name};\n" 

97 array_structs += f"}} {array_struct_type};\n\n" 

98 

99 register_struct += ( 

100 f" {array_struct_type} {register_object.name}[{register_object.length}];\n" 

101 ) 

102 register_struct += f"}} {register_struct_type};\n" 

103 return array_structs + register_struct 

104 

105 def _number_of_registers(self, register_objects): 

106 # It is possible that we have constants but no registers 

107 num_regs = 0 

108 if register_objects: 

109 num_regs = register_objects[-1].index + 1 

110 

111 c_code = self._comment("Number of registers within this register map.") 

112 c_code += f"#define {self.module_name.upper()}_NUM_REGS ({num_regs}u)\n" 

113 

114 return c_code 

115 

116 def _register_defines(self, register_objects): 

117 c_code = "" 

118 for register, register_array in self._iterate_registers(register_objects): 

119 c_code += self._addr_define(register, register_array) 

120 c_code += self._field_definitions(register, register_array) 

121 c_code += "\n" 

122 

123 return c_code 

124 

125 def _addr_define(self, register, register_array): 

126 name = self._register_define_name(register, register_array) 

127 mode_string = f'Mode "{REGISTER_MODES[register.mode].mode_readable}".' 

128 

129 if register_array is None: 

130 c_code = self._comment(f'Address of the "{register.name}" register. {mode_string}') 

131 c_code += self._comment_block(register.description) 

132 

133 c_code += f"#define {name}_INDEX ({register.index}u)\n" 

134 c_code += f"#define {name}_ADDR (4u * {name}_INDEX)\n" 

135 else: 

136 title = ( 

137 f'Address of the "{register.name}" register within the "{register_array.name}"' 

138 f" register array (array_index < {register_array.length}). {mode_string}" 

139 ) 

140 c_code = self._comment(title) 

141 c_code += self._comment_block(register.description) 

142 

143 c_code += ( 

144 f"#define {name}_INDEX(array_index) ({register_array.base_index}u + " 

145 f"(array_index) * {len(register_array.registers)}u + {register.index}u)\n" 

146 ) 

147 c_code += f"#define {name}_ADDR(array_index) (4u * {name}_INDEX(array_index))\n" 

148 

149 return c_code 

150 

151 def _field_definitions(self, register, register_array): 

152 register_name = self._register_define_name(register, register_array) 

153 register_string = f'"{register.name}" register' 

154 if register_array is not None: 

155 register_string += f' within the "{register_array.name}" register array' 

156 

157 c_code = "" 

158 for field in register.fields: 

159 c_code += self._comment( 

160 f'Mask and shift for the "{field.name}" field in the {register_string}.' 

161 ) 

162 c_code += self._comment_block(field.description) 

163 

164 field_name = f"{register_name}_{field.name.upper()}" 

165 c_code += f"#define {field_name}_SHIFT ({field.base_index}u)\n" 

166 c_code += f'#define {field_name}_MASK (0b{"1" * field.width}u << {field.base_index}u)\n' 

167 c_code += f"#define {field_name}_MASK_INVERSE (~{field_name}_MASK)\n" 

168 

169 return c_code 

170 

171 def _register_define_name(self, register, register_array): 

172 if register_array is None: 

173 name = f"{self.module_name}_{register.name}" 

174 else: 

175 name = f"{self.module_name}_{register_array.name}_{register.name}" 

176 return name.upper() 

177 

178 def _constants(self, constants): 

179 c_code = "" 

180 for constant in constants: 

181 if constant.is_boolean: 

182 value = str(constant.value).lower() 

183 elif constant.is_integer: 

184 # No suffix -> "int", i.e. signed integer of at least 32 bits 

185 value = str(constant.value) 

186 elif constant.is_float: 

187 # "f" suffix -> "float" (as opposed to "double", to match the VHDL type) 

188 value = f"{constant.value}f" 

189 else: 

190 raise ValueError(f"Got unexpected constant type. {constant}") 

191 

192 c_code += self._comment(f'Register constant "{constant.name}".') 

193 c_code += self._comment_block(constant.description) 

194 c_code += f"#define {self.module_name.upper()}_{constant.name.upper()} ({value})\n" 

195 

196 return c_code