modm_data.header2svd.stmicro.header
1# Copyright 2022, Niklas Hauser 2# SPDX-License-Identifier: MPL-2.0 3 4import re 5import logging 6import subprocess 7 8from collections import defaultdict 9 10from ..header import Header as CmsisHeader 11from ...utils import ext_path, cache_path 12import modm_data.svd as svd 13 14 15LOGGER = logging.getLogger(__file__) 16HEADER_TEMPLATE = r""" 17#include <iostream> 18#include <{{header}}> 19 20template<typename T> 21void __modm_dump_f(char const *symbol, T value) { 22 std::cout << "\"" << symbol << "\": " << uint64_t(value) << "," << std::endl; 23} 24#define __modm_dump(def) __modm_dump_f(#def, (def)) 25int main() { 26 std::cout << "cpp_defines = {";{% for define in defines %} 27#ifdef {{define}} 28 __modm_dump({{define}}); 29#endif{% endfor %} 30 std::cout << "}"; 31 return 0; 32} 33""" 34 35 36def getDefineForDevice(device_id, familyDefines): 37 # get all defines for this device name 38 devName = "STM32{}{}".format(device_id.family.upper(), device_id.name.upper()) 39 40 # Map STM32F7x8 -> STM32F7x7 41 if device_id.family == "f7" and devName[8] == "8": 42 devName = devName[:8] + "7" 43 44 deviceDefines = sorted([define for define in familyDefines if define.startswith(devName)]) 45 # if there is only one define thats the one 46 if len(deviceDefines) == 1: 47 return deviceDefines[0] 48 49 # sort with respecting variants 50 minlen = min(len(d) for d in deviceDefines) 51 deviceDefines.sort(key=lambda d: (d[:minlen], d[minlen:])) 52 53 # now we match for the size-id (and variant-id if applicable). 54 if device_id.family == "h7": 55 devNameMatch = devName + "xx" 56 else: 57 devNameMatch = devName + "x{}".format(device_id.size.upper()) 58 if device_id.family == "l1": 59 # Map STM32L1xxQC and STM32L1xxZC -> STM32L162QCxA variants 60 if device_id.pin in ["q", "z"] and device_id.size == "c": 61 devNameMatch += "A" 62 else: 63 devNameMatch += device_id.variant.upper() 64 elif device_id.family == "h7": 65 if device_id.variant: 66 devNameMatch += device_id.variant.upper() 67 for define in deviceDefines: 68 if devNameMatch <= define: 69 return define 70 71 # now we match for the pin-id. 72 devNameMatch = devName + "{}x".format(device_id.pin.upper()) 73 for define in deviceDefines: 74 if devNameMatch <= define: 75 return define 76 77 return None 78 79 80class Header(CmsisHeader): 81 HEADER_PATH = ext_path("stmicro/header") 82 CACHE_PATH = cache_path("cmsis/stm32") 83 CACHE_FAMILY = defaultdict(dict) 84 BUILTINS = { 85 "const uint32_t": 4, 86 "const uint16_t": 2, 87 "const uint8_t": 1, 88 "uint32_t": 4, 89 "uint16_t": 2, 90 "uint8_t": 1, 91 } 92 93 def __init__(self, did): 94 self.did = did 95 self.family_folder = "stm32{}xx".format(self.did.family) 96 self.cmsis_folder = Header.HEADER_PATH / self.family_folder / "Include" 97 self.family_header_file = "{}.h".format(self.family_folder) 98 99 self.family_defines = self._get_family_defines() 100 self.define = getDefineForDevice(self.did, self.family_defines) 101 assert self.define is not None 102 self.is_valid = self.define is not None 103 if not self.is_valid: return 104 105 self.header_file = "{}.h".format(self.define.lower()) 106 self.device_map = None 107 substitutions = { 108 # r"/\* +?(Legacy defines|Legacy aliases|Old .*? legacy purpose|Aliases for .*?) +?\*/.*?\n\n": "", 109 r"/\* +?Legacy (aliases|defines|registers naming) +?\*/.*?\n\n": "", 110 r"/\* +?Old .*? legacy purpose +?\*/.*?\n\n": "", 111 r"/\* +?Aliases for .*? +?\*/.*?\n\n": "", 112 # r"( 0x[0-9A-F]+)\) ": r"\1U", 113 # r"#define.*?/\*!<.*? Legacy .*?\*/\n": "", 114 } 115 super().__init__(self.cmsis_folder / self.header_file, substitutions) 116 117 def get_defines(self): 118 key = "defines" + self.did.get("core", "") 119 if key not in self._cache: 120 self._cache[key] = self._get_defines() 121 return self._cache[key] 122 123 @property 124 def _memory_map_key(self): 125 return "memmap" + self.did.get("core", "") 126 127 @property 128 def memory_map_tree(self): 129 if self._memory_map_key not in self._cache: 130 self._cache[self._memory_map_key] = self._get_memmap() 131 return self._cache[self._memory_map_key] 132 133 @property 134 def interrupt_table(self): 135 if "vectors" not in self._cache: 136 interrupt_enum = [i["values"] for i in self.header.enums if i["name"] == "IRQn_Type"][0] 137 vectors = [{"position": int(str(i["value"]).replace(" ", "")), 138 "name": i["name"][:-5]} for i in interrupt_enum] 139 self._cache["vectors"] = vectors 140 return self._cache["vectors"] 141 142 def _get_family_defines(self): 143 if self.did.family not in Header.CACHE_FAMILY: 144 defines = [] 145 match = re.findall(r"if defined\((?P<define>STM32[A-Z].....)\)", (self.cmsis_folder / self.family_header_file).read_text(encoding="utf-8", errors="replace")) 146 if match: defines = match; 147 else: LOGGER.error("Cannot find family defines for {}!".format(self.did.string)); 148 Header.CACHE_FAMILY[self.did.family]["family_defines"] = defines 149 return Header.CACHE_FAMILY[self.did.family]["family_defines"] 150 151 def _get_filtered_defines(self): 152 defines = {} 153 # get all the non-empty defines 154 for define in self.header.defines: 155 if comment := re.search(r"/\* *(.*?) *\*/", define): 156 if "legacy" in comment.group(1): continue 157 define = re.sub(r"/\*.*?\*/", "", define).strip() 158 name, *parts = define.split(" ") 159 if not len(parts): continue 160 if any(i in name for i in ["("]): continue; 161 if any(name.endswith(i) for i in ["_IRQn", "_IRQHandler", "_SUPPORT", "_TypeDef"]): continue; 162 if any(name.startswith(i) for i in ["IS_"]): continue; 163 if name in ["FLASH_SIZE", "FLASH_BANK_SIZE", "COMP1_COMMON", "COMP12_COMMON_BASE", "OPAMP12_COMMON_BASE", "BL_ID"]: continue; 164 defines[name] = "".join(parts[1:]).strip() 165 return defines 166 167 def _get_defines(self): 168 from jinja2 import Environment 169 # create the destination directory 170 destination = (Header.CACHE_PATH / self.family_folder / self.header_file).with_suffix(".cpp").absolute() 171 core = self.did.get("core", "") 172 executable = destination.with_suffix("."+core if core else "") 173 defines = self._get_filtered_defines() 174 if not executable.exists(): 175 # generate the cpp file from the template 176 LOGGER.info("Generating {} ...".format(destination.name)) 177 substitutions = {"header": self.header_file, "defines": sorted(defines)} 178 content = Environment().from_string(HEADER_TEMPLATE).render(substitutions) 179 # write the cpp file into the cache 180 destination.parent.mkdir(exist_ok=True, parents=True) 181 destination.write_text(content) 182 header_defines = [self.define] 183 if core: header_defines.append(f"CORE_C{core.upper()}=1") 184 # compile file into an executable 185 includes = [str(Header.CMSIS_PATH.absolute()), str(self.cmsis_folder.absolute())] 186 gcc_command = ["g++", "-Wno-narrowing", "-fms-extensions", 187 " ".join(f"-I{incl}" for incl in includes), 188 " ".join(f"-D{define}" for define in header_defines), 189 "-o {}".format(executable), 190 str(destination) 191 ] 192 LOGGER.info("Compiling {} ...".format(destination.name)) 193 retval = subprocess.run(" ".join(gcc_command), shell=True) 194 if retval.returncode: 195 LOGGER.error("Header compilation failed! {}".format(retval)); 196 return None 197 # execute the file 198 LOGGER.info("Running {} ...".format(executable.name)) 199 retval = subprocess.run([str(executable)], stdout=subprocess.PIPE) 200 if retval.returncode: 201 LOGGER.error("Header execution failed! {}".format(retval)); 202 return None 203 # parse the printed values 204 localv = {} 205 exec(retval.stdout, globals(), localv) 206 undefined = [d for d in defines if d not in localv["cpp_defines"]] 207 if len(undefined): 208 LOGGER.warning("Undefined macros: {}".format(undefined)) 209 return localv["cpp_defines"] 210 211 def _get_memmap(self): 212 # get the values of the definitions in this file 213 defines = self.get_defines() 214 215 # get the mapping of peripheral to its type 216 peripheral_map = {} 217 seen_defines = [] 218 for name, value in self._get_filtered_defines().items(): 219 if "*)" in value: 220 values = value.split("*)") 221 typedef = values[0].strip()[2:].strip() 222 peripheral_map[name] = (typedef, defines[name]) 223 LOGGER.debug(f"Found peripheral ({typedef} *) {name} @ 0x{defines[name]:x}") 224 225 # build the array containing the peripheral types 226 raw_types = {typedef: [(v["type"], v["name"], int(v["array_size"], 16 if v["array_size"].startswith("0x") else 10) if v["array"] else 0) 227 for v in values["properties"]["public"]] 228 for typedef, values in self.header.classes.items()} 229 230 # function to recursively flatten the types 231 def _flatten_type(typedef, result, prefix=""): 232 for (t, n, s) in raw_types[typedef]: 233 if t in Header.BUILTINS: 234 size = Header.BUILTINS[t] 235 name = None if n.upper().startswith("RESERVED") else n 236 if s == 0: 237 result.append( (size, (prefix + name) if name else name) ) 238 else: 239 if not name: 240 result.append( (size * s, name) ) 241 else: 242 result.extend([(size, f"{prefix}{name}.{ii}") for ii in range(s)]) 243 elif t in raw_types.keys(): 244 if s == 0: 245 _flatten_type(t, result, prefix) 246 else: 247 for ii in range(s): 248 _flatten_type(t, result, prefix + "{}.{}.".format(n, ii)) 249 else: 250 LOGGER.error("Unknown type: {} ({} {})".format(t, n, s)) 251 exit(1) 252 253 # flatten all types 254 flat_types = defaultdict(list) 255 for typedef in raw_types: 256 _flatten_type(typedef, flat_types[typedef]) 257 258 # match the macro definitions to the type structures 259 matched_types = defaultdict(list) 260 for typedef, pregs in flat_types.items(): 261 # print(typedef, pregs) 262 peri = "_".join([t for t in typedef.split("_") if t.isupper()]) 263 position = 0 264 for reg in pregs: 265 if reg[1] is None: 266 position += reg[0] 267 continue 268 sreg = [r for r in reg[1].split(".") if r.isupper() or r.isdigit()] 269 prefix = ["{}_{}_".format(peri, "".join([r for r in sreg if r.isupper()]))] 270 if len(sreg) > 1: 271 if sreg[0].isdigit(): 272 parts = sreg[1].split("R") 273 parts[-2] += sreg[0] 274 prefix.append("{}_{}_".format(peri, "R".join(parts))) 275 elif sreg[1].isdigit(): 276 sreg[1] = str(int(sreg[1]) + 1) 277 prefix.append("{}_{}_".format(peri, "".join([r for r in sreg if r.isupper()]) + sreg[1])) 278 prefix.append("{}_{}x_".format(peri, "".join([r for r in sreg if r.isupper()]))) 279 # A bunch of aliases 280 if "FSMC_BTCR_" in prefix: prefix.extend(["FSMC_BCRx_", "FSMC_BTRx_"]) 281 if "ADC_TR_" in prefix: prefix.append("ADC_TR1_") 282 if "DBGMCU_APB1FZ_" in prefix: prefix.append("DBGMCU_APB1_FZ_") 283 if "DBGMCU_APB2FZ_" in prefix: prefix.append("DBGMCU_APB2_FZ_") 284 # if "FLASH_KEYR_" in prefix: prefix.extend(["FLASH_KEY1_", "FLASH_KEY2_"]) 285 # if "FLASH_OPTKEYR_" in prefix: prefix.extend(["FLASH_OPTKEY1_", "FLASH_OPTKEY2_"]) 286 if "GPIO_AFR1_" in prefix: prefix.append("GPIO_AFRL_") 287 if "GPIO_AFR2_" in prefix: prefix.append("GPIO_AFRH_") 288 if "SAI_Block" in typedef: prefix = [p.replace("SAI_", "SAI_x") for p in prefix] 289 290 regmap = {} 291 for p in prefix: 292 keys = [d for d in defines.keys() if d.startswith(p)] 293 seen_defines.extend(keys) 294 regmap.update({k.replace(p, ""):defines[k] for k in keys}) 295 if not len(regmap): 296 LOGGER.info("Empty: {:30} {}->{} ({} >> {})".format(typedef, peri, prefix, reg[1], sreg)) 297 298 # convert macro names to positional arguments 299 fields = sorted(list(set([r[:-4] for r in regmap if r.endswith("_Pos")]))) 300 registers = {} 301 for field in fields: 302 regs = {k:v for k,v in regmap.items() if k == field or k.startswith(field + "_")} 303 val = regs.pop(field, None) 304 pos = regs.pop(field + "_Pos", None) 305 msk = regs.pop(field + "_Msk", None) 306 if val is None: 307 LOGGER.warning("{} not found: {}".format(field, regs)) 308 continue 309 if pos is None: 310 LOGGER.warning("{}_Pos not found: {}".format(field, regs)) 311 continue 312 if msk is None: 313 LOGGER.warning("{}_Msk not found: {}".format(field, regs)) 314 continue 315 316 rem = {k.replace(field + "_", ""):v for k,v in regs.items()} 317 mask = msk >> pos 318 width = 0 319 while(mask): 320 width += 1 321 mask >>= 1 322 registers[pos] = (field, width, msk, val, rem) 323 324 # print(registers) 325 # Store in map 326 matched_types[typedef].append( (position, reg[0], reg[1], registers) ) 327 position += reg[0] 328 329 # print the remaining 330 remaining_defines = [d for d in defines if d not in seen_defines and not d.endswith("_BASE")] 331 for typedef in matched_types: 332 peri = "_".join([t for t in typedef.split("_") if t.isupper()]) + "_" 333 rem = [d for d in remaining_defines if d.startswith(peri)] 334 if len(rem): 335 LOGGER.warning("Unassigned defines for ({} *) {}: {}".format(typedef, peri, len(rem))) 336 for d in rem: 337 LOGGER.info("{}: {}".format(d, defines[d])) 338 339 # for typedef, registers in matched_types.items(): 340 # print(typedef) 341 # for reg in registers: 342 # print(" {:03x}: {}".format(reg[0], reg[2])) 343 344 345 device = svd.Device(self.did.string) 346 for name, (typedef, address) in peripheral_map.items(): 347 svd.Peripheral(name, typedef, defines[name], parent=device) 348 349 for name, registers in matched_types.items(): 350 peripheral = svd.PeripheralType(name, parent=device) 351 for offset, width, name, bitfields in registers: 352 register = svd.Register(name, offset, width, parent=peripheral) 353 for pos, (name, width, mask, value, _) in bitfields.items(): 354 svd.BitField(name, pos, width, parent=register) 355 356 return device 357 # return (peripheral_map, matched_types)
LOGGER =
<Logger /opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/modm_data/header2svd/stmicro/header.py (WARNING)>
HEADER_TEMPLATE =
'\n#include <iostream>\n#include <{{header}}>\n\ntemplate<typename T>\nvoid __modm_dump_f(char const *symbol, T value) {\n std::cout << "\\"" << symbol << "\\": " << uint64_t(value) << "," << std::endl;\n}\n#define __modm_dump(def) __modm_dump_f(#def, (def))\nint main() {\n std::cout << "cpp_defines = {";{% for define in defines %}\n#ifdef {{define}}\n __modm_dump({{define}});\n#endif{% endfor %}\n std::cout << "}";\n return 0;\n}\n'
def
getDefineForDevice(device_id, familyDefines):
37def getDefineForDevice(device_id, familyDefines): 38 # get all defines for this device name 39 devName = "STM32{}{}".format(device_id.family.upper(), device_id.name.upper()) 40 41 # Map STM32F7x8 -> STM32F7x7 42 if device_id.family == "f7" and devName[8] == "8": 43 devName = devName[:8] + "7" 44 45 deviceDefines = sorted([define for define in familyDefines if define.startswith(devName)]) 46 # if there is only one define thats the one 47 if len(deviceDefines) == 1: 48 return deviceDefines[0] 49 50 # sort with respecting variants 51 minlen = min(len(d) for d in deviceDefines) 52 deviceDefines.sort(key=lambda d: (d[:minlen], d[minlen:])) 53 54 # now we match for the size-id (and variant-id if applicable). 55 if device_id.family == "h7": 56 devNameMatch = devName + "xx" 57 else: 58 devNameMatch = devName + "x{}".format(device_id.size.upper()) 59 if device_id.family == "l1": 60 # Map STM32L1xxQC and STM32L1xxZC -> STM32L162QCxA variants 61 if device_id.pin in ["q", "z"] and device_id.size == "c": 62 devNameMatch += "A" 63 else: 64 devNameMatch += device_id.variant.upper() 65 elif device_id.family == "h7": 66 if device_id.variant: 67 devNameMatch += device_id.variant.upper() 68 for define in deviceDefines: 69 if devNameMatch <= define: 70 return define 71 72 # now we match for the pin-id. 73 devNameMatch = devName + "{}x".format(device_id.pin.upper()) 74 for define in deviceDefines: 75 if devNameMatch <= define: 76 return define 77 78 return None
81class Header(CmsisHeader): 82 HEADER_PATH = ext_path("stmicro/header") 83 CACHE_PATH = cache_path("cmsis/stm32") 84 CACHE_FAMILY = defaultdict(dict) 85 BUILTINS = { 86 "const uint32_t": 4, 87 "const uint16_t": 2, 88 "const uint8_t": 1, 89 "uint32_t": 4, 90 "uint16_t": 2, 91 "uint8_t": 1, 92 } 93 94 def __init__(self, did): 95 self.did = did 96 self.family_folder = "stm32{}xx".format(self.did.family) 97 self.cmsis_folder = Header.HEADER_PATH / self.family_folder / "Include" 98 self.family_header_file = "{}.h".format(self.family_folder) 99 100 self.family_defines = self._get_family_defines() 101 self.define = getDefineForDevice(self.did, self.family_defines) 102 assert self.define is not None 103 self.is_valid = self.define is not None 104 if not self.is_valid: return 105 106 self.header_file = "{}.h".format(self.define.lower()) 107 self.device_map = None 108 substitutions = { 109 # r"/\* +?(Legacy defines|Legacy aliases|Old .*? legacy purpose|Aliases for .*?) +?\*/.*?\n\n": "", 110 r"/\* +?Legacy (aliases|defines|registers naming) +?\*/.*?\n\n": "", 111 r"/\* +?Old .*? legacy purpose +?\*/.*?\n\n": "", 112 r"/\* +?Aliases for .*? +?\*/.*?\n\n": "", 113 # r"( 0x[0-9A-F]+)\) ": r"\1U", 114 # r"#define.*?/\*!<.*? Legacy .*?\*/\n": "", 115 } 116 super().__init__(self.cmsis_folder / self.header_file, substitutions) 117 118 def get_defines(self): 119 key = "defines" + self.did.get("core", "") 120 if key not in self._cache: 121 self._cache[key] = self._get_defines() 122 return self._cache[key] 123 124 @property 125 def _memory_map_key(self): 126 return "memmap" + self.did.get("core", "") 127 128 @property 129 def memory_map_tree(self): 130 if self._memory_map_key not in self._cache: 131 self._cache[self._memory_map_key] = self._get_memmap() 132 return self._cache[self._memory_map_key] 133 134 @property 135 def interrupt_table(self): 136 if "vectors" not in self._cache: 137 interrupt_enum = [i["values"] for i in self.header.enums if i["name"] == "IRQn_Type"][0] 138 vectors = [{"position": int(str(i["value"]).replace(" ", "")), 139 "name": i["name"][:-5]} for i in interrupt_enum] 140 self._cache["vectors"] = vectors 141 return self._cache["vectors"] 142 143 def _get_family_defines(self): 144 if self.did.family not in Header.CACHE_FAMILY: 145 defines = [] 146 match = re.findall(r"if defined\((?P<define>STM32[A-Z].....)\)", (self.cmsis_folder / self.family_header_file).read_text(encoding="utf-8", errors="replace")) 147 if match: defines = match; 148 else: LOGGER.error("Cannot find family defines for {}!".format(self.did.string)); 149 Header.CACHE_FAMILY[self.did.family]["family_defines"] = defines 150 return Header.CACHE_FAMILY[self.did.family]["family_defines"] 151 152 def _get_filtered_defines(self): 153 defines = {} 154 # get all the non-empty defines 155 for define in self.header.defines: 156 if comment := re.search(r"/\* *(.*?) *\*/", define): 157 if "legacy" in comment.group(1): continue 158 define = re.sub(r"/\*.*?\*/", "", define).strip() 159 name, *parts = define.split(" ") 160 if not len(parts): continue 161 if any(i in name for i in ["("]): continue; 162 if any(name.endswith(i) for i in ["_IRQn", "_IRQHandler", "_SUPPORT", "_TypeDef"]): continue; 163 if any(name.startswith(i) for i in ["IS_"]): continue; 164 if name in ["FLASH_SIZE", "FLASH_BANK_SIZE", "COMP1_COMMON", "COMP12_COMMON_BASE", "OPAMP12_COMMON_BASE", "BL_ID"]: continue; 165 defines[name] = "".join(parts[1:]).strip() 166 return defines 167 168 def _get_defines(self): 169 from jinja2 import Environment 170 # create the destination directory 171 destination = (Header.CACHE_PATH / self.family_folder / self.header_file).with_suffix(".cpp").absolute() 172 core = self.did.get("core", "") 173 executable = destination.with_suffix("."+core if core else "") 174 defines = self._get_filtered_defines() 175 if not executable.exists(): 176 # generate the cpp file from the template 177 LOGGER.info("Generating {} ...".format(destination.name)) 178 substitutions = {"header": self.header_file, "defines": sorted(defines)} 179 content = Environment().from_string(HEADER_TEMPLATE).render(substitutions) 180 # write the cpp file into the cache 181 destination.parent.mkdir(exist_ok=True, parents=True) 182 destination.write_text(content) 183 header_defines = [self.define] 184 if core: header_defines.append(f"CORE_C{core.upper()}=1") 185 # compile file into an executable 186 includes = [str(Header.CMSIS_PATH.absolute()), str(self.cmsis_folder.absolute())] 187 gcc_command = ["g++", "-Wno-narrowing", "-fms-extensions", 188 " ".join(f"-I{incl}" for incl in includes), 189 " ".join(f"-D{define}" for define in header_defines), 190 "-o {}".format(executable), 191 str(destination) 192 ] 193 LOGGER.info("Compiling {} ...".format(destination.name)) 194 retval = subprocess.run(" ".join(gcc_command), shell=True) 195 if retval.returncode: 196 LOGGER.error("Header compilation failed! {}".format(retval)); 197 return None 198 # execute the file 199 LOGGER.info("Running {} ...".format(executable.name)) 200 retval = subprocess.run([str(executable)], stdout=subprocess.PIPE) 201 if retval.returncode: 202 LOGGER.error("Header execution failed! {}".format(retval)); 203 return None 204 # parse the printed values 205 localv = {} 206 exec(retval.stdout, globals(), localv) 207 undefined = [d for d in defines if d not in localv["cpp_defines"]] 208 if len(undefined): 209 LOGGER.warning("Undefined macros: {}".format(undefined)) 210 return localv["cpp_defines"] 211 212 def _get_memmap(self): 213 # get the values of the definitions in this file 214 defines = self.get_defines() 215 216 # get the mapping of peripheral to its type 217 peripheral_map = {} 218 seen_defines = [] 219 for name, value in self._get_filtered_defines().items(): 220 if "*)" in value: 221 values = value.split("*)") 222 typedef = values[0].strip()[2:].strip() 223 peripheral_map[name] = (typedef, defines[name]) 224 LOGGER.debug(f"Found peripheral ({typedef} *) {name} @ 0x{defines[name]:x}") 225 226 # build the array containing the peripheral types 227 raw_types = {typedef: [(v["type"], v["name"], int(v["array_size"], 16 if v["array_size"].startswith("0x") else 10) if v["array"] else 0) 228 for v in values["properties"]["public"]] 229 for typedef, values in self.header.classes.items()} 230 231 # function to recursively flatten the types 232 def _flatten_type(typedef, result, prefix=""): 233 for (t, n, s) in raw_types[typedef]: 234 if t in Header.BUILTINS: 235 size = Header.BUILTINS[t] 236 name = None if n.upper().startswith("RESERVED") else n 237 if s == 0: 238 result.append( (size, (prefix + name) if name else name) ) 239 else: 240 if not name: 241 result.append( (size * s, name) ) 242 else: 243 result.extend([(size, f"{prefix}{name}.{ii}") for ii in range(s)]) 244 elif t in raw_types.keys(): 245 if s == 0: 246 _flatten_type(t, result, prefix) 247 else: 248 for ii in range(s): 249 _flatten_type(t, result, prefix + "{}.{}.".format(n, ii)) 250 else: 251 LOGGER.error("Unknown type: {} ({} {})".format(t, n, s)) 252 exit(1) 253 254 # flatten all types 255 flat_types = defaultdict(list) 256 for typedef in raw_types: 257 _flatten_type(typedef, flat_types[typedef]) 258 259 # match the macro definitions to the type structures 260 matched_types = defaultdict(list) 261 for typedef, pregs in flat_types.items(): 262 # print(typedef, pregs) 263 peri = "_".join([t for t in typedef.split("_") if t.isupper()]) 264 position = 0 265 for reg in pregs: 266 if reg[1] is None: 267 position += reg[0] 268 continue 269 sreg = [r for r in reg[1].split(".") if r.isupper() or r.isdigit()] 270 prefix = ["{}_{}_".format(peri, "".join([r for r in sreg if r.isupper()]))] 271 if len(sreg) > 1: 272 if sreg[0].isdigit(): 273 parts = sreg[1].split("R") 274 parts[-2] += sreg[0] 275 prefix.append("{}_{}_".format(peri, "R".join(parts))) 276 elif sreg[1].isdigit(): 277 sreg[1] = str(int(sreg[1]) + 1) 278 prefix.append("{}_{}_".format(peri, "".join([r for r in sreg if r.isupper()]) + sreg[1])) 279 prefix.append("{}_{}x_".format(peri, "".join([r for r in sreg if r.isupper()]))) 280 # A bunch of aliases 281 if "FSMC_BTCR_" in prefix: prefix.extend(["FSMC_BCRx_", "FSMC_BTRx_"]) 282 if "ADC_TR_" in prefix: prefix.append("ADC_TR1_") 283 if "DBGMCU_APB1FZ_" in prefix: prefix.append("DBGMCU_APB1_FZ_") 284 if "DBGMCU_APB2FZ_" in prefix: prefix.append("DBGMCU_APB2_FZ_") 285 # if "FLASH_KEYR_" in prefix: prefix.extend(["FLASH_KEY1_", "FLASH_KEY2_"]) 286 # if "FLASH_OPTKEYR_" in prefix: prefix.extend(["FLASH_OPTKEY1_", "FLASH_OPTKEY2_"]) 287 if "GPIO_AFR1_" in prefix: prefix.append("GPIO_AFRL_") 288 if "GPIO_AFR2_" in prefix: prefix.append("GPIO_AFRH_") 289 if "SAI_Block" in typedef: prefix = [p.replace("SAI_", "SAI_x") for p in prefix] 290 291 regmap = {} 292 for p in prefix: 293 keys = [d for d in defines.keys() if d.startswith(p)] 294 seen_defines.extend(keys) 295 regmap.update({k.replace(p, ""):defines[k] for k in keys}) 296 if not len(regmap): 297 LOGGER.info("Empty: {:30} {}->{} ({} >> {})".format(typedef, peri, prefix, reg[1], sreg)) 298 299 # convert macro names to positional arguments 300 fields = sorted(list(set([r[:-4] for r in regmap if r.endswith("_Pos")]))) 301 registers = {} 302 for field in fields: 303 regs = {k:v for k,v in regmap.items() if k == field or k.startswith(field + "_")} 304 val = regs.pop(field, None) 305 pos = regs.pop(field + "_Pos", None) 306 msk = regs.pop(field + "_Msk", None) 307 if val is None: 308 LOGGER.warning("{} not found: {}".format(field, regs)) 309 continue 310 if pos is None: 311 LOGGER.warning("{}_Pos not found: {}".format(field, regs)) 312 continue 313 if msk is None: 314 LOGGER.warning("{}_Msk not found: {}".format(field, regs)) 315 continue 316 317 rem = {k.replace(field + "_", ""):v for k,v in regs.items()} 318 mask = msk >> pos 319 width = 0 320 while(mask): 321 width += 1 322 mask >>= 1 323 registers[pos] = (field, width, msk, val, rem) 324 325 # print(registers) 326 # Store in map 327 matched_types[typedef].append( (position, reg[0], reg[1], registers) ) 328 position += reg[0] 329 330 # print the remaining 331 remaining_defines = [d for d in defines if d not in seen_defines and not d.endswith("_BASE")] 332 for typedef in matched_types: 333 peri = "_".join([t for t in typedef.split("_") if t.isupper()]) + "_" 334 rem = [d for d in remaining_defines if d.startswith(peri)] 335 if len(rem): 336 LOGGER.warning("Unassigned defines for ({} *) {}: {}".format(typedef, peri, len(rem))) 337 for d in rem: 338 LOGGER.info("{}: {}".format(d, defines[d])) 339 340 # for typedef, registers in matched_types.items(): 341 # print(typedef) 342 # for reg in registers: 343 # print(" {:03x}: {}".format(reg[0], reg[2])) 344 345 346 device = svd.Device(self.did.string) 347 for name, (typedef, address) in peripheral_map.items(): 348 svd.Peripheral(name, typedef, defines[name], parent=device) 349 350 for name, registers in matched_types.items(): 351 peripheral = svd.PeripheralType(name, parent=device) 352 for offset, width, name, bitfields in registers: 353 register = svd.Register(name, offset, width, parent=peripheral) 354 for pos, (name, width, mask, value, _) in bitfields.items(): 355 svd.BitField(name, pos, width, parent=register) 356 357 return device 358 # return (peripheral_map, matched_types)
Header(did)
94 def __init__(self, did): 95 self.did = did 96 self.family_folder = "stm32{}xx".format(self.did.family) 97 self.cmsis_folder = Header.HEADER_PATH / self.family_folder / "Include" 98 self.family_header_file = "{}.h".format(self.family_folder) 99 100 self.family_defines = self._get_family_defines() 101 self.define = getDefineForDevice(self.did, self.family_defines) 102 assert self.define is not None 103 self.is_valid = self.define is not None 104 if not self.is_valid: return 105 106 self.header_file = "{}.h".format(self.define.lower()) 107 self.device_map = None 108 substitutions = { 109 # r"/\* +?(Legacy defines|Legacy aliases|Old .*? legacy purpose|Aliases for .*?) +?\*/.*?\n\n": "", 110 r"/\* +?Legacy (aliases|defines|registers naming) +?\*/.*?\n\n": "", 111 r"/\* +?Old .*? legacy purpose +?\*/.*?\n\n": "", 112 r"/\* +?Aliases for .*? +?\*/.*?\n\n": "", 113 # r"( 0x[0-9A-F]+)\) ": r"\1U", 114 # r"#define.*?/\*!<.*? Legacy .*?\*/\n": "", 115 } 116 super().__init__(self.cmsis_folder / self.header_file, substitutions)
HEADER_PATH =
PosixPath('/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/ext/stmicro/header')
CACHE_PATH =
PosixPath('/opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/ext/cache/cmsis/stm32')
BUILTINS =
{'const uint32_t': 4, 'const uint16_t': 2, 'const uint8_t': 1, 'uint32_t': 4, 'uint16_t': 2, 'uint8_t': 1}
interrupt_table
134 @property 135 def interrupt_table(self): 136 if "vectors" not in self._cache: 137 interrupt_enum = [i["values"] for i in self.header.enums if i["name"] == "IRQn_Type"][0] 138 vectors = [{"position": int(str(i["value"]).replace(" ", "")), 139 "name": i["name"][:-5]} for i in interrupt_enum] 140 self._cache["vectors"] = vectors 141 return self._cache["vectors"]