Coverage for hdl_registers/register_c_generator.py: 100%

92 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-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/tsfpga/hdl_registers 

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

9 

10from .register import Register, REGISTER_MODES 

11from .register_code_generator import RegisterCodeGenerator 

12 

13 

14class RegisterCGenerator(RegisterCodeGenerator): 

15 """ 

16 Generate a C code header with register information. 

17 

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

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

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

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

22 """ 

23 

24 def __init__(self, module_name, generated_info): 

25 """ 

26 Arguments: 

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

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

29 """ 

30 self.module_name = module_name 

31 self.generated_info = generated_info 

32 

33 def get_header(self, register_objects, constants): 

34 """ 

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

36 

37 Arguments: 

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

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

40 

41 Returns: 

42 str: C code. 

43 """ 

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

45 

46 c_code = f"""\ 

47{self._file_header()} 

48#ifndef {define_name} 

49#define {define_name} 

50 

51{self._constants(constants)} 

52{self._number_of_registers(register_objects)} 

53{self._register_struct(register_objects)} 

54{self._register_defines(register_objects)}\ 

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

56 

57 return c_code 

58 

59 @staticmethod 

60 def _comment(comment, indentation=0): 

61 indent = " " * indentation 

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

63 

64 def _file_header(self): 

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

66 

67 def _register_struct(self, register_objects): 

68 array_structs = "" 

69 

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

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

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

73 register_struct += "{\n" 

74 for register_object in register_objects: 

75 if isinstance(register_object, Register): 

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

77 register_struct += self._comment( 

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

79 ) 

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

81 

82 else: 

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

84 

85 array_structs += self._comment( 

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

87 ) 

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

89 array_structs += "{\n" 

90 for register in register_object.registers: 

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

92 array_structs += self._comment( 

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

94 ) 

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

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

97 

98 register_struct += ( 

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

100 ) 

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

102 return array_structs + register_struct 

103 

104 def _number_of_registers(self, register_objects): 

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

106 num_regs = 0 

107 if register_objects: 

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

109 

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

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

112 

113 return c_code 

114 

115 def _register_defines(self, register_objects): 

116 c_code = "" 

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

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

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

120 c_code += "\n" 

121 

122 return c_code 

123 

124 def _addr_define(self, register, register_array): 

125 name = self._register_define_name(register, register_array) 

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

127 

128 if register_array is None: 

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

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

131 

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

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

134 else: 

135 title = ( 

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

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

138 ) 

139 c_code = self._comment(title) 

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

141 

142 c_code += ( 

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

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

145 ) 

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

147 

148 return c_code 

149 

150 def _field_definitions(self, register, register_array): 

151 register_name = self._register_define_name(register, register_array) 

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

153 if register_array is not None: 

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

155 

156 c_code = "" 

157 for field in register.fields: 

158 c_code += self._comment( 

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

160 ) 

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

162 

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

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

165 c_code += ( 

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

167 ) 

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 c_code += self._comment(f'Register constant "{constant.name}".') 

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

183 c_code += ( 

184 f"#define {self.module_name.upper()}_{constant.name.upper()} ({constant.value})\n" 

185 ) 

186 return c_code