modm_data.header2svd.stmicro
class
Header(modm_data.header2svd.header.Header):
68class Header(CmsisHeader): 69 _HEADER_PATH = ext_path("stmicro/header") 70 _CACHE_PATH = cache_path("cmsis/stm32") 71 _CACHE_FAMILY = defaultdict(dict) 72 _BUILTINS = { 73 "const uint32_t": 4, 74 "const uint16_t": 2, 75 "const uint8_t": 1, 76 "uint32_t": 4, 77 "uint16_t": 2, 78 "uint8_t": 1, 79 } 80 81 def __init__(self, did): 82 self.did = did 83 self.family_folder = "stm32{}xx".format(self.did.family) 84 if self.did.string[5:8] in ["h7r", "h7s"]: 85 self.family_folder = "stm32h7rsxx" 86 elif self.did.string[5:8] == "wb0": 87 self.family_folder = "stm32wb0xx" 88 elif self.did.string[5:8] == "wba": 89 self.family_folder = "stm32wbaxx" 90 elif self.did.string[5:8] == "wl3": 91 self.family_folder = "stm32wl3xx" 92 self.cmsis_folder = Header._HEADER_PATH / self.family_folder / "Include" 93 self.family_header_file = f"{self.family_folder}.h" 94 if self.did.string[5:8] == "wb0": 95 self.family_header_file = "stm32wb0x.h" 96 elif self.did.string[5:8] == "wl3": 97 self.family_header_file = "stm32wl3x.h" 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: 104 return 105 106 self.header_file = f"{self.define.lower()}.h" 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 = [ 139 {"position": int(str(i["value"]).replace(" ", "")), "name": i["name"][:-5]} for i in interrupt_enum 140 ] 141 self._cache["vectors"] = vectors 142 return self._cache["vectors"] 143 144 def _get_family_defines(self): 145 if self.did.family not in Header._CACHE_FAMILY: 146 defines = [] 147 match = re.findall( 148 r"if defined\((?P<define>STM32[A-Z].....)\)", 149 (self.cmsis_folder / self.family_header_file).read_text(encoding="utf-8", errors="replace"), 150 ) 151 if match: 152 defines = match 153 else: 154 LOGGER.error(f"Cannot find family defines for {self.did.string}!") 155 Header._CACHE_FAMILY[self.did.family]["family_defines"] = defines 156 return Header._CACHE_FAMILY[self.did.family]["family_defines"] 157 158 def _get_filtered_defines(self): 159 defines = {} 160 # get all the non-empty defines 161 for define in self.header.defines: 162 if comment := re.search(r"/\* *(.*?) *\*/", define): 163 if "legacy" in comment.group(1): 164 continue 165 define = re.sub(r"/\*.*?\*/", "", define).strip() 166 name, *parts = define.split(" ") 167 if not len(parts): 168 continue 169 if any(i in name for i in ["("]): 170 continue 171 if any(name.endswith(i) for i in ["_IRQn", "_IRQHandler", "_SUPPORT", "_TypeDef"]): 172 continue 173 if any(name.startswith(i) for i in ["IS_"]): 174 continue 175 if name in [ 176 "FLASH_SIZE", 177 "FLASH_BANK_SIZE", 178 "COMP1_COMMON", 179 "COMP12_COMMON_BASE", 180 "OPAMP12_COMMON_BASE", 181 "BL_ID", 182 ]: 183 continue 184 defines[name] = "".join(parts[1:]).strip() 185 return defines 186 187 def _get_defines(self): 188 from jinja2 import Environment 189 190 # create the destination directory 191 destination = (Header._CACHE_PATH / self.family_folder / self.header_file).with_suffix(".cpp").absolute() 192 core = self.did.get("core", "") 193 executable = destination.with_suffix("." + core if core else "") 194 defines = self._get_filtered_defines() 195 if not executable.exists(): 196 # generate the cpp file from the template 197 LOGGER.info(f"Generating {destination.name} ...") 198 substitutions = {"header": self.header_file, "defines": sorted(defines)} 199 content = Environment().from_string(HEADER_TEMPLATE).render(substitutions) 200 # write the cpp file into the cache 201 destination.parent.mkdir(exist_ok=True, parents=True) 202 destination.write_text(content) 203 header_defines = [self.define] 204 if core: 205 header_defines.append(f"CORE_C{core.upper()}=1") 206 # compile file into an executable 207 includes = [str(Header.CMSIS_PATH.absolute()), str(self.cmsis_folder.absolute())] 208 gcc_command = [ 209 "g++", 210 "-Wno-narrowing", 211 "-fms-extensions", 212 " ".join(f"-I{incl}" for incl in includes), 213 " ".join(f"-D{define}" for define in header_defines), 214 f"-o {executable}", 215 str(destination), 216 ] 217 LOGGER.info(f"Compiling {destination.name} ...") 218 retval = subprocess.run(" ".join(gcc_command), shell=True) 219 if retval.returncode: 220 LOGGER.error(f"Header compilation failed! {retval}") 221 return None 222 # execute the file 223 LOGGER.info(f"Running {executable.name} ...") 224 retval = subprocess.run([str(executable)], stdout=subprocess.PIPE) 225 if retval.returncode: 226 LOGGER.error(f"Header execution failed! {retval}") 227 return None 228 # parse the printed values 229 localv = {} 230 exec(retval.stdout, globals(), localv) 231 undefined = [d for d in defines if d not in localv["cpp_defines"]] 232 if len(undefined): 233 LOGGER.warning(f"Undefined macros: {undefined}") 234 return localv["cpp_defines"] 235 236 def _get_memmap(self): 237 # get the values of the definitions in this file 238 defines = self.get_defines() 239 240 # get the mapping of peripheral to its type 241 peripheral_map = {} 242 seen_defines = [] 243 for name, value in self._get_filtered_defines().items(): 244 if "*)" in value: 245 values = value.split("*)") 246 typedef = values[0].strip()[2:].strip() 247 peripheral_map[name] = (typedef, defines[name]) 248 LOGGER.debug(f"Found peripheral ({typedef} *) {name} @ 0x{defines[name]:x}") 249 250 # build the array containing the peripheral types 251 raw_types = { 252 typedef: [ 253 ( 254 v["type"], 255 v["name"], 256 int(v["array_size"], 16 if v["array_size"].startswith("0x") else 10) if v["array"] else 0, 257 ) 258 for v in values["properties"]["public"] 259 ] 260 for typedef, values in self.header.classes.items() 261 } 262 263 # function to recursively flatten the types 264 def _flatten_type(typedef, result, prefix=""): 265 for t, n, s in raw_types[typedef]: 266 if t in Header._BUILTINS: 267 size = Header._BUILTINS[t] 268 name = None if n.upper().startswith("RESERVED") else n 269 if s == 0: 270 result.append((size, (prefix + name) if name else name)) 271 else: 272 if not name: 273 result.append((size * s, name)) 274 else: 275 result.extend([(size, f"{prefix}{name}.{ii}") for ii in range(s)]) 276 elif t in raw_types.keys(): 277 if s == 0: 278 _flatten_type(t, result, prefix) 279 else: 280 for ii in range(s): 281 _flatten_type(t, result, prefix + f"{n}.{ii}.") 282 else: 283 LOGGER.error(f"Unknown type: {t} ({n} {s})") 284 exit(1) 285 286 # flatten all types 287 flat_types = defaultdict(list) 288 for typedef in raw_types: 289 _flatten_type(typedef, flat_types[typedef]) 290 291 # match the macro definitions to the type structures 292 matched_types = defaultdict(list) 293 for typedef, pregs in flat_types.items(): 294 # print(typedef, pregs) 295 peri = "_".join([t for t in typedef.split("_") if t.isupper()]) 296 position = 0 297 for reg in pregs: 298 if reg[1] is None: 299 position += reg[0] 300 continue 301 sreg = [r for r in reg[1].split(".") if r.isupper() or r.isdigit()] 302 prefix = ["{}_{}_".format(peri, "".join([r for r in sreg if r.isupper()]))] 303 if len(sreg) > 1: 304 if sreg[0].isdigit(): 305 parts = sreg[1].split("R") 306 parts[-2] += sreg[0] 307 prefix.append("{}_{}_".format(peri, "R".join(parts))) 308 elif sreg[1].isdigit(): 309 sreg[1] = str(int(sreg[1]) + 1) 310 prefix.append("{}_{}_".format(peri, "".join([r for r in sreg if r.isupper()]) + sreg[1])) 311 prefix.append("{}_{}x_".format(peri, "".join([r for r in sreg if r.isupper()]))) 312 # A bunch of aliases 313 if "FSMC_BTCR_" in prefix: 314 prefix.extend(["FSMC_BCRx_", "FSMC_BTRx_"]) 315 if "ADC_TR_" in prefix: 316 prefix.append("ADC_TR1_") 317 if "DBGMCU_APB1FZ_" in prefix: 318 prefix.append("DBGMCU_APB1_FZ_") 319 if "DBGMCU_APB2FZ_" in prefix: 320 prefix.append("DBGMCU_APB2_FZ_") 321 # if "FLASH_KEYR_" in prefix: prefix.extend(["FLASH_KEY1_", "FLASH_KEY2_"]) 322 # if "FLASH_OPTKEYR_" in prefix: prefix.extend(["FLASH_OPTKEY1_", "FLASH_OPTKEY2_"]) 323 if "GPIO_AFR1_" in prefix: 324 prefix.append("GPIO_AFRL_") 325 if "GPIO_AFR2_" in prefix: 326 prefix.append("GPIO_AFRH_") 327 if "SAI_Block" in typedef: 328 prefix = [p.replace("SAI_", "SAI_x") for p in prefix] 329 330 regmap = {} 331 for p in prefix: 332 keys = [d for d in defines.keys() if d.startswith(p)] 333 seen_defines.extend(keys) 334 regmap.update({k.replace(p, ""): defines[k] for k in keys}) 335 if not len(regmap): 336 LOGGER.info(f"Empty: {typedef:30} {peri}->{prefix} ({reg[1]} >> {sreg})") 337 338 # convert macro names to positional arguments 339 fields = sorted(list(set([r[:-4] for r in regmap if r.endswith("_Pos")]))) 340 registers = {} 341 for field in fields: 342 regs = {k: v for k, v in regmap.items() if k == field or k.startswith(field + "_")} 343 val = regs.pop(field, None) 344 pos = regs.pop(field + "_Pos", None) 345 msk = regs.pop(field + "_Msk", None) 346 if val is None: 347 LOGGER.warning(f"{field} not found: {regs}") 348 continue 349 if pos is None: 350 LOGGER.warning(f"{field}_Pos not found: {regs}") 351 continue 352 if msk is None: 353 LOGGER.warning(f"{field}_Msk not found: {regs}") 354 continue 355 356 rem = {k.replace(field + "_", ""): v for k, v in regs.items()} 357 mask = msk >> pos 358 width = 0 359 while mask: 360 width += 1 361 mask >>= 1 362 registers[pos] = (field, width, msk, val, rem) 363 364 # print(registers) 365 # Store in map 366 matched_types[typedef].append((position, reg[0], reg[1], registers)) 367 position += reg[0] 368 369 # print the remaining 370 remaining_defines = [d for d in defines if d not in seen_defines and not d.endswith("_BASE")] 371 for typedef in matched_types: 372 peri = "_".join([t for t in typedef.split("_") if t.isupper()]) + "_" 373 rem = [d for d in remaining_defines if d.startswith(peri)] 374 if len(rem): 375 LOGGER.warning(f"Unassigned defines for ({typedef} *) {peri}: {len(rem)}") 376 for d in rem: 377 LOGGER.info(f"{d}: {defines[d]}") 378 379 # for typedef, registers in matched_types.items(): 380 # print(typedef) 381 # for reg in registers: 382 # print(f" {reg[0]:03x}: {reg[2]}") 383 384 device = svd.Device(self.did.string) 385 for name, (typedef, address) in peripheral_map.items(): 386 svd.Peripheral(name, typedef, defines[name], parent=device) 387 388 for name, registers in matched_types.items(): 389 peripheral = svd.PeripheralType(name, parent=device) 390 for offset, width, name, bitfields in registers: 391 register = svd.Register(name, offset, width, parent=peripheral) 392 for pos, (name, width, mask, value, _) in bitfields.items(): 393 svd.BitField(name, pos, width, parent=register) 394 395 return device 396 # return (peripheral_map, matched_types)
Header(did)
81 def __init__(self, did): 82 self.did = did 83 self.family_folder = "stm32{}xx".format(self.did.family) 84 if self.did.string[5:8] in ["h7r", "h7s"]: 85 self.family_folder = "stm32h7rsxx" 86 elif self.did.string[5:8] == "wb0": 87 self.family_folder = "stm32wb0xx" 88 elif self.did.string[5:8] == "wba": 89 self.family_folder = "stm32wbaxx" 90 elif self.did.string[5:8] == "wl3": 91 self.family_folder = "stm32wl3xx" 92 self.cmsis_folder = Header._HEADER_PATH / self.family_folder / "Include" 93 self.family_header_file = f"{self.family_folder}.h" 94 if self.did.string[5:8] == "wb0": 95 self.family_header_file = "stm32wb0x.h" 96 elif self.did.string[5:8] == "wl3": 97 self.family_header_file = "stm32wl3x.h" 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: 104 return 105 106 self.header_file = f"{self.define.lower()}.h" 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)
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 = [ 139 {"position": int(str(i["value"]).replace(" ", "")), "name": i["name"][:-5]} for i in interrupt_enum 140 ] 141 self._cache["vectors"] = vectors 142 return self._cache["vectors"]
Inherited Members
- modm_data.header2svd.header.Header
- CMSIS_PATH
- filename
- substitutions
- header
def
getDefineForDevice(device_id, familyDefines):
37def getDefineForDevice(device_id, familyDefines): 38 if len(familyDefines) == 1: 39 return familyDefines[0] 40 41 # get all defines for this device name 42 devName = "STM32{}{}".format(device_id.family.upper(), device_id.name.upper()) 43 44 # Map STM32F7x8 -> STM32F7x7 45 if device_id.family == "f7" and devName[8] == "8": 46 devName = devName[:8] + "7" 47 48 deviceDefines = sorted([define for define in familyDefines if define.startswith(devName)]) 49 # if there is only one define thats the one 50 if len(deviceDefines) == 1: 51 return deviceDefines[0] 52 53 # now we match for the size-id. 54 devNameMatch = devName + "x{}".format(device_id.size.upper()) 55 for define in deviceDefines: 56 if devNameMatch <= define: 57 return define 58 59 # now we match for the pin-id. 60 devNameMatch = devName + "{}x".format(device_id.pin.upper()) 61 for define in deviceDefines: 62 if devNameMatch <= define: 63 return define 64 65 return None
def
normalize_memory_map(memtree):
164def normalize_memory_map(memtree): 165 # print(RenderTree(memtree, maxlevel=2)) 166 memtree = _normalize_subtypes(memtree, "DMA_TypeDef", "DMA_Channel_TypeDef", "DMA_Stream_TypeDef") 167 memtree = _normalize_subtypes(memtree, "MDMA_TypeDef", "MDMA_Channel_TypeDef") 168 memtree = _normalize_subtypes(memtree, "BDMA_TypeDef", "BDMA_Channel_TypeDef") 169 memtree = _normalize_subtypes(memtree, "LTDC_TypeDef", "LTDC_Layer_TypeDef") 170 memtree = _normalize_subtypes(memtree, "SAI_TypeDef", "SAI_Block_TypeDef") 171 memtree = _normalize_subtypes(memtree, "RAMECC_TypeDef", "RAMECC_MonitorTypeDef") 172 173 memtree = _normalize_dfsdm(memtree) 174 memtree = _normalize_dmamux(memtree) 175 memtree = _normalize_adc_common(memtree) 176 177 memtree = _normalize_duplicates(memtree, lambda n: "_COMMON" in n.name, lambda n: "_COMMON" not in n.name) 178 memtree = _normalize_duplicates(memtree, lambda n: "OPAMP" == n.name, lambda n: re.match(r"OPAMP\d$", n.name)) 179 180 memtree = _normalize_instances(memtree) 181 memtree = _normalize_order(memtree) 182 return memtree