|
| 1 | +""" |
| 2 | +Read in the cmsis/devinc/stm32f405xx.h header, extract relevant constants, |
| 3 | +and create modstmconst.c. |
| 4 | +
|
| 5 | +This is not part of the automatic build process because stm32f405xx.h isn't |
| 6 | +expected to change. After generating the file, some manual intervention is |
| 7 | +needed to copy the new qstr definitions to qstrdefsport.h. |
| 8 | +""" |
| 9 | + |
| 10 | +import argparse |
| 11 | +import re |
| 12 | + |
| 13 | +# given a list of (name,regex) pairs, find the first one that matches the given line |
| 14 | +def re_match_first(regexs, line): |
| 15 | + for name, regex in regexs: |
| 16 | + match = re.match(regex, line) |
| 17 | + if match: |
| 18 | + return name, match |
| 19 | + return None, None |
| 20 | + |
| 21 | +class LexerError(Exception): |
| 22 | + def __init__(self, line): |
| 23 | + self.line = line |
| 24 | + |
| 25 | +class Lexer: |
| 26 | + re_io_reg = r'__IO uint(?P<bits>8|16|32)_t +(?P<reg>[A-Z0-9]+)' |
| 27 | + re_comment = r'(?P<comment>[A-Za-z0-9 \-/_()]+)' |
| 28 | + re_addr_offset = r'Address offset: (?P<offset>0x[0-9A-Z]{2,3})' |
| 29 | + regexs = ( |
| 30 | + ('#define hex', re.compile(r'#define +(?P<id>[A-Z0-9_]+) +\(\(uint32_t\)(?P<hex>0x[0-9A-F]+)\)($| +/\*)')), |
| 31 | + ('#define X', re.compile(r'#define +(?P<id>[A-Z0-9_]+) +(?P<id2>[A-Z0-9_]+)($| +/\*)')), |
| 32 | + ('#define X+hex', re.compile(r'#define +(?P<id>[A-Z0-9_]+) +\((?P<id2>[A-Z0-9_]+) \+ (?P<hex>0x[0-9A-F]+)\)($| +/\*)')), |
| 33 | + ('#define typedef', re.compile(r'#define +(?P<id>[A-Z0-9_]+) +\(\([A-Za-z0-9_]+_TypeDef \*\) (?P<id2>[A-Z0-9_]+)\)($| +/\*)')), |
| 34 | + ('typedef struct', re.compile(r'typedef struct$')), |
| 35 | + ('{', re.compile(r'{$')), |
| 36 | + ('}', re.compile(r'}$')), |
| 37 | + ('} TypeDef', re.compile(r'} *(?P<id>[A-Z][A-Za-z0-9_]+)_(?P<global>(Global)?)TypeDef;$')), |
| 38 | + ('IO reg', re.compile(re_io_reg + r'; +/\*!< ' + re_comment + r', +' + re_addr_offset + r' *\*/')), |
| 39 | + ('IO reg array', re.compile(re_io_reg + r'\[(?P<array>[2-8])\]; +/\*!< ' + re_comment + r', +' + re_addr_offset + r'-(0x[0-9A-Z]{2,3}) *\*/')), |
| 40 | + ) |
| 41 | + |
| 42 | + def __init__(self, filename): |
| 43 | + self.file = open(filename, 'rt') |
| 44 | + self.line_number = 0 |
| 45 | + |
| 46 | + def next_match(self, strictly_next=False): |
| 47 | + while True: |
| 48 | + line = self.file.readline() |
| 49 | + self.line_number += 1 |
| 50 | + if len(line) == 0: |
| 51 | + return ('EOF', None) |
| 52 | + match = re_match_first(Lexer.regexs, line.strip()) |
| 53 | + if strictly_next or match[0] is not None: |
10000
code> | 54 | + return match |
| 55 | + |
| 56 | + def must_match(self, kind): |
| 57 | + match = self.next_match(strictly_next=True) |
| 58 | + if match[0] != kind: |
| 59 | + raise LexerError(self.line_number) |
| 60 | + return match |
| 61 | + |
| 62 | +def parse_file(filename): |
| 63 | + lexer = Lexer(filename) |
| 64 | + |
| 65 | + reg_defs = {} |
| 66 | + consts = {} |
| 67 | + periphs = [] |
| 68 | + while True: |
| 69 | + m = lexer.next_match() |
| 70 | + if m[0] == 'EOF': |
| 71 | + break |
| 72 | + elif m[0] == '#define hex': |
| 73 | + d = m[1].groupdict() |
| 74 | + consts[d['id']] = int(d['hex'], base=16) |
| 75 | + elif m[0] == '#define X': |
| 76 | + d = m[1].groupdict() |
| 77 | + if d['id2'] in consts: |
| 78 | + consts[d['id']] = consts[d['id2']] |
| 79 | + elif m[0] == '#define X+hex': |
| 80 | + d = m[1].groupdict() |
| 81 | + if d['id2'] in consts: |
| 82 | + consts[d['id']] = consts[d['id2']] + int(d['hex'], base=16) |
| 83 | + elif m[0] == '#define typedef': |
| 84 | + d = m[1].groupdict() |
| 85 | + if d['id2'] in consts: |
| 86 | + periphs.append((d['id'], consts[d['id2']])) |
| 87 | + elif m[0] == 'typedef struct': |
| 88 | + lexer.must_match('{') |
| 89 | + m = lexer.next_match() |
| 90 | + regs = [] |
| 91 | + while m[0] in ('IO reg', 'IO reg array'): |
| 92 | + d = m[1].groupdict() |
| 93 | + reg = d['reg'] |
| 94 | + offset = int(d['offset'], base=16) |
| 95 | + bits = int(d['bits']) |
| 96 | + comment = d['commen
10000
t'] |
| 97 | + if m[0] == 'IO reg': |
| 98 | + regs.append((reg, offset, bits, comment)) |
| 99 | + else: |
| 100 | + for i in range(int(d['array'])): |
| 101 | + regs.append((reg + str(i), offset + i * bits // 8, bits, comment)) |
| 102 | + m = lexer.next_match() |
| 103 | + if m[0] == '}': |
| 104 | + pass |
| 105 | + elif m[0] == '} TypeDef': |
| 106 | + reg_defs[m[1].groupdict()['id']] = regs |
| 107 | + else: |
| 108 | + raise LexerError(lexer.line_number) |
| 109 | + |
| 110 | + return periphs, reg_defs |
| 111 | + |
| 112 | +def print_periph(periph_name, periph_val, needed_qstrs): |
| 113 | + qstr = periph_name.upper() |
| 114 | + print('{ MP_OBJ_NEW_QSTR(MP_QSTR_%s), MP_OBJ_NEW_SMALL_INT(%#x) },' % (qstr, periph_val)) |
| 115 | + needed_qstrs.add(qstr) |
| 116 | + |
| 117 | +def print_regs(reg_name, reg_defs, needed_qstrs): |
| 118 | + reg_name = reg_name.upper() |
| 119 | + for r in reg_defs: |
| 120 | + qstr = reg_name + '_' + r[0] |
| 121 | + print('{ MP_OBJ_NEW_QSTR(MP_QSTR_%s), MP_OBJ_NEW_SMALL_INT(%#x) }, // %s-bits, %s' % (qstr, r[1], r[2], r[3])) |
| 122 | + needed_qstrs.add(qstr) |
| 123 | + |
| 124 | +# This version of print regs groups registers together into submodules (eg GPIO submodule). |
| 125 | +# This makes the qstrs shorter, and makes the list of constants more manageable (since |
| 126 | +# they are not all in one big module) but it is then harder to compile the constants, and |
| 127 | +# is more cumbersome to access. |
| 128 | +# As such, we don't use this version. |
| 129 | +# And for the number of constants we have, this function seems to use about the same amount |
| 130 | +# of ROM as print_regs. |
| 131 | +def print_regs_as_submodules(reg_name, reg_defs, modules, needed_qstrs): |
| 132 | + mod_name_lower = reg_name.lower() + '_' |
| 133 | + mod_name_upper = mod_name_lower.upper() |
| 134 | + modules.append((mod_name_lower, mod_name_upper)) |
| 135 | + |
| 136 | + print(""" |
| 137 | +STATIC const mp_map_elem_t stm_%s_globals_table[] = { |
| 138 | + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_%s) }, |
| 139 | +""" % (mod_name_lower, mod_name_upper)) |
| 140 | + needed_qstrs.add(mod_name_upper) |
| 141 | + |
| 142 | + for r in reg_defs: |
| 143 | + print(' { MP_OBJ_NEW_QSTR(MP_QSTR_%s), MP_OBJ_NEW_SMALL_INT(%#x) }, // %s-bits, %s' % (r[0], r[1], r[2], r[3])) |
| 144 | + needed_qstrs.add(r[0]) |
| 145 | + |
| 146 | + print("""}; |
| 147 | +
|
| 148 | +STATIC const mp_obj_dict_t stm_%s_globals = { |
| 149 | + .base = {&mp_type_dict}, |
| 150 | + .map = { |
| 151 | + .all_keys_are_qstrs = 1, |
| 152 | + .table_is_fixed_array = 1, |
| 153 | + .used = sizeof(stm_%s_globals_table) / sizeof(mp_map_elem_t), |
| 154 | + .alloc = sizeof(stm_%s_globals_table) / sizeof(mp_map_elem_t), |
| 155 | + .table = (mp_map_elem_t*)stm_%s_globals_table, |
| 156 | + }, |
| 157 | +}; |
| 158 | +
|
| 159 | +const mp_obj_module_t stm_%s_obj = { |
| 160 | + .base = { &mp_type_module }, |
| 161 | + .name = MP_QSTR_%s, |
| 162 | + .globals = (mp_obj_dict_t*)&stm_%s_globals, |
| 163 | +}; |
| 164 | +""" % (mod_name_lower, mod_name_lower, mod_name_lower, mod_name_lower, mod_name_lower, mod_name_upper, mod_name_lower)) |
| 165 | + |
| 166 | +def main(): |
| 167 | + cmd_parser = argparse.ArgumentParser(description='Extract ST constants from a C header file.') |
| 168 | + cmd_parser.add_argument('file', nargs=1, help='input file') |
| 169 | + args = cmd_parser.parse_args() |
| 170 | + |
| 171 | + periphs, reg_defs = parse_file(args.file[0]) |
| 172 | + |
| 173 | + modules = [] |
| 174 | + needed_qstrs = set() |
| 175 | + |
| 176 | + print("// Automatically generated from %s by make-stmconst.py" % args.file[0]) |
| 177 | + print("") |
| 178 | + |
| 179 | + for periph_name, periph_val in periphs: |
| 180 | + print_periph(periph_name, periph_val, needed_qstrs) |
| 181 | + |
| 182 | + for reg in ( |
| 183 | + 'ADC',
10000
|
| 184 | + #'ADC_Common', |
| 185 | + #'CAN_TxMailBox', |
| 186 | + #'CAN_FIFOMailBox', |
| 187 | + #'CAN_FilterRegister', |
| 188 | + #'CAN', |
| 189 | + 'CRC', |
| 190 | + 'DAC', |
| 191 | + 'DBGMCU', |
| 192 | + 'DMA_Stream', |
| 193 | + 'DMA', |
| 194 | + 'EXTI', |
| 195 | + 'FLASH', |
| 196 | + 'GPIO', |
| 197 | + 'SYSCFG', |
| 198 | + 'I2C', |
| 199 | + 'IWDG', |
| 200 | + 'PWR', |
| 201 | + 'RCC', |
| 202 | + 'RTC', |
| 203 | + #'SDIO', |
| 204 | + 'SPI', |
| 205 | + 'TIM', |
| 206 | + 'USART', |
| 207 | + 'WWDG', |
| 208 | + 'RNG', |
| 209 | + ): |
| 210 | + print_regs(reg, reg_defs[reg], needed_qstrs) |
| 211 | + #print_regs_as_submodules(reg, reg_defs[reg], modules, needed_qstrs) |
| 212 | + |
| 213 | + #print("#define MOD_STM_CONST_MODULES \\") |
| 214 | + #for mod_lower, mod_upper in modules: |
| 215 | + # print(" { MP_OBJ_NEW_QSTR(MP_QSTR_%s), (mp_obj_t)&stm_%s_obj }, \\" % (mod_upper, mod_lower)) |
| 216 | + |
| 217 | + print("") |
| 218 | + |
| 219 | + for qstr in sorted(needed_qstrs): |
| 220 | + print('Q({})'.format(qstr)) |
| 221 | + |
| 222 | +if __name__ == "__main__": |
| 223 | + main() |
0 commit comments