modm_data.header2svd.stmicro
class
Header(modm_data.header2svd.header.Header):
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 = f"stm32{self.did.family}xx" 97 self.cmsis_folder = Header._HEADER_PATH / self.family_folder / "Include" 98 self.family_header_file = f"{self.family_folder}.h" 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: 105 return 106 107 self.header_file = f"{self.define.lower()}.h" 108 self.device_map = None 109 substitutions = { 110 # r"/\* +?(Legacy defines|Legacy aliases|Old .*? legacy purpose|Aliases for .*?) +?\*/.*?\n\n": "", 111 r"/\* +?Legacy (aliases|defines|registers naming) +?\*/.*?\n\n": "", 112 r"/\* +?Old .*? legacy purpose +?\*/.*?\n\n": "", 113 r"/\* +?Aliases for .*? +?\*/.*?\n\n": "", 114 # r"( 0x[0-9A-F]+)\) ": r"\1U", 115 # r"#define.*?/\*!<.*? Legacy .*?\*/\n": "", 116 } 117 super().__init__(self.cmsis_folder / self.header_file, substitutions) 118 119 def get_defines(self): 120 key = "defines" + self.did.get("core", "") 121 if key not in self._cache: 122 self._cache[key] = self._get_defines() 123 return self._cache[key] 124 125 @property 126 def _memory_map_key(self): 127 return "memmap" + self.did.get("core", "") 128 129 @property 130 def memory_map_tree(self): 131 if self._memory_map_key not in self._cache: 132 self._cache[self._memory_map_key] = self._get_memmap() 133 return self._cache[self._memory_map_key] 134 135 @property 136 def interrupt_table(self): 137 if "vectors" not in self._cache: 138 interrupt_enum = [i["values"] for i in self.header.enums if i["name"] == "IRQn_Type"][0] 139 vectors = [ 140 {"position": int(str(i["value"]).replace(" ", "")), "name": i["name"][:-5]} for i in interrupt_enum 141 ] 142 self._cache["vectors"] = vectors 143 return self._cache["vectors"] 144 145 def _get_family_defines(self): 146 if self.did.family not in Header._CACHE_FAMILY: 147 defines = [] 148 match = re.findall( 149 r"if defined\((?P<define>STM32[A-Z].....)\)", 150 (self.cmsis_folder / self.family_header_file).read_text(encoding="utf-8", errors="replace"), 151 ) 152 if match: 153 defines = match 154 else: 155 LOGGER.error(f"Cannot find family defines for {self.did.string}!") 156 Header._CACHE_FAMILY[self.did.family]["family_defines"] = defines 157 return Header._CACHE_FAMILY[self.did.family]["family_defines"] 158 159 def _get_filtered_defines(self): 160 defines = {} 161 # get all the non-empty defines 162 for define in self.header.defines: 163 if comment := re.search(r"/\* *(.*?) *\*/", define): 164 if "legacy" in comment.group(1): 165 continue 166 define = re.sub(r"/\*.*?\*/", "", define).strip() 167 name, *parts = define.split(" ") 168 if not len(parts): 169 continue 170 if any(i in name for i in ["("]): 171 continue 172 if any(name.endswith(i) for i in ["_IRQn", "_IRQHandler", "_SUPPORT", "_TypeDef"]): 173 continue 174 if any(name.startswith(i) for i in ["IS_"]): 175 continue 176 if name in [ 177 "FLASH_SIZE", 178 "FLASH_BANK_SIZE", 179 "COMP1_COMMON", 180 "COMP12_COMMON_BASE", 181 "OPAMP12_COMMON_BASE", 182 "BL_ID", 183 ]: 184 continue 185 defines[name] = "".join(parts[1:]).strip() 186 return defines 187 188 def _get_defines(self): 189 from jinja2 import Environment 190 191 # create the destination directory 192 destination = (Header._CACHE_PATH / self.family_folder / self.header_file).with_suffix(".cpp").absolute() 193 core = self.did.get("core", "") 194 executable = destination.with_suffix("." + core if core else "") 195 defines = self._get_filtered_defines() 196 if not executable.exists(): 197 # generate the cpp file from the template 198 LOGGER.info(f"Generating {destination.name} ...") 199 substitutions = {"header": self.header_file, "defines": sorted(defines)} 200 content = Environment().from_string(HEADER_TEMPLATE).render(substitutions) 201 # write the cpp file into the cache 202 destination.parent.mkdir(exist_ok=True, parents=True) 203 destination.write_text(content) 204 header_defines = [self.define] 205 if core: 206 header_defines.append(f"CORE_C{core.upper()}=1") 207 # compile file into an executable 208 includes = [str(Header.CMSIS_PATH.absolute()), str(self.cmsis_folder.absolute())] 209 gcc_command = [ 210 "g++", 211 "-Wno-narrowing", 212 "-fms-extensions", 213 " ".join(f"-I{incl}" for incl in includes), 214 " ".join(f"-D{define}" for define in header_defines), 215 f"-o {executable}", 216 str(destination), 217 ] 218 LOGGER.info(f"Compiling {destination.name} ...") 219 retval = subprocess.run(" ".join(gcc_command), shell=True) 220 if retval.returncode: 221 LOGGER.error(f"Header compilation failed! {retval}") 222 return None 223 # execute the file 224 LOGGER.info(f"Running {executable.name} ...") 225 retval = subprocess.run([str(executable)], stdout=subprocess.PIPE) 226 if retval.returncode: 227 LOGGER.error(f"Header execution failed! {retval}") 228 return None 229 # parse the printed values 230 localv = {} 231 exec(retval.stdout, globals(), localv) 232 undefined = [d for d in defines if d not in localv["cpp_defines"]] 233 if len(undefined): 234 LOGGER.warning(f"Undefined macros: {undefined}") 235 return localv["cpp_defines"] 236 237 def _get_memmap(self): 238 # get the values of the definitions in this file 239 defines = self.get_defines() 240 241 # get the mapping of peripheral to its type 242 peripheral_map = {} 243 seen_defines = [] 244 for name, value in self._get_filtered_defines().items(): 245 if "*)" in value: 246 values = value.split("*)") 247 typedef = values[0].strip()[2:].strip() 248 peripheral_map[name] = (typedef, defines[name]) 249 LOGGER.debug(f"Found peripheral ({typedef} *) {name} @ 0x{defines[name]:x}") 250 251 # build the array containing the peripheral types 252 raw_types = { 253 typedef: [ 254 ( 255 v["type"], 256 v["name"], 257 int(v["array_size"], 16 if v["array_size"].startswith("0x") else 10) if v["array"] else 0, 258 ) 259 for v in values["properties"]["public"] 260 ] 261 for typedef, values in self.header.classes.items() 262 } 263 264 # function to recursively flatten the types 265 def _flatten_type(typedef, result, prefix=""): 266 for t, n, s in raw_types[typedef]: 267 if t in Header._BUILTINS: 268 size = Header._BUILTINS[t] 269 name = None if n.upper().startswith("RESERVED") else n 270 if s == 0: 271 result.append((size, (prefix + name) if name else name)) 272 else: 273 if not name: 274 result.append((size * s, name)) 275 else: 276 result.extend([(size, f"{prefix}{name}.{ii}") for ii in range(s)]) 277 elif t in raw_types.keys(): 278 if s == 0: 279 _flatten_type(t, result, prefix) 280 else: 281 for ii in range(s): 282 _flatten_type(t, result, prefix + f"{n}.{ii}.") 283 else: 284 LOGGER.error(f"Unknown type: {t} ({n} {s})") 285 exit(1) 286 287 # flatten all types 288 flat_types = defaultdict(list) 289 for typedef in raw_types: 290 _flatten_type(typedef, flat_types[typedef]) 291 292 # match the macro definitions to the type structures 293 matched_types = defaultdict(list) 294 for typedef, pregs in flat_types.items(): 295 # print(typedef, pregs) 296 peri = "_".join([t for t in typedef.split("_") if t.isupper()]) 297 position = 0 298 for reg in pregs: 299 if reg[1] is None: 300 position += reg[0] 301 continue 302 sreg = [r for r in reg[1].split(".") if r.isupper() or r.isdigit()] 303 prefix = ["{}_{}_".format(peri, "".join([r for r in sreg if r.isupper()]))] 304 if len(sreg) > 1: 305 if sreg[0].isdigit(): 306 parts = sreg[1].split("R") 307 parts[-2] += sreg[0] 308 prefix.append("{}_{}_".format(peri, "R".join(parts))) 309 elif sreg[1].isdigit(): 310 sreg[1] = str(int(sreg[1]) + 1) 311 prefix.append("{}_{}_".format(peri, "".join([r for r in sreg if r.isupper()]) + sreg[1])) 312 prefix.append("{}_{}x_".format(peri, "".join([r for r in sreg if r.isupper()]))) 313 # A bunch of aliases 314 if "FSMC_BTCR_" in prefix: 315 prefix.extend(["FSMC_BCRx_", "FSMC_BTRx_"]) 316 if "ADC_TR_" in prefix: 317 prefix.append("ADC_TR1_") 318 if "DBGMCU_APB1FZ_" in prefix: 319 prefix.append("DBGMCU_APB1_FZ_") 320 if "DBGMCU_APB2FZ_" in prefix: 321 prefix.append("DBGMCU_APB2_FZ_") 322 # if "FLASH_KEYR_" in prefix: prefix.extend(["FLASH_KEY1_", "FLASH_KEY2_"]) 323 # if "FLASH_OPTKEYR_" in prefix: prefix.extend(["FLASH_OPTKEY1_", "FLASH_OPTKEY2_"]) 324 if "GPIO_AFR1_" in prefix: 325 prefix.append("GPIO_AFRL_") 326 if "GPIO_AFR2_" in prefix: 327 prefix.append("GPIO_AFRH_") 328 if "SAI_Block" in typedef: 329 prefix = [p.replace("SAI_", "SAI_x") for p in prefix] 330 331 regmap = {} 332 for p in prefix: 333 keys = [d for d in defines.keys() if d.startswith(p)] 334 seen_defines.extend(keys) 335 regmap.update({k.replace(p, ""): defines[k] for k in keys}) 336 if not len(regmap): 337 LOGGER.info(f"Empty: {typedef:30} {peri}->{prefix} ({reg[1]} >> {sreg})") 338 339 # convert macro names to positional arguments 340 fields = sorted(list(set([r[:-4] for r in regmap if r.endswith("_Pos")]))) 341 registers = {} 342 for field in fields: 343 regs = {k: v for k, v in regmap.items() if k == field or k.startswith(field + "_")} 344 val = regs.pop(field, None) 345 pos = regs.pop(field + "_Pos", None) 346 msk = regs.pop(field + "_Msk", None) 347 if val is None: 348 LOGGER.warning(f"{field} not found: {regs}") 349 continue 350 if pos is None: 351 LOGGER.warning(f"{field}_Pos not found: {regs}") 352 continue 353 if msk is None: 354 LOGGER.warning(f"{field}_Msk not found: {regs}") 355 continue 356 357 rem = {k.replace(field + "_", ""): v for k, v in regs.items()} 358 mask = msk >> pos 359 width = 0 360 while mask: 361 width += 1 362 mask >>= 1 363 registers[pos] = (field, width, msk, val, rem) 364 365 # print(registers) 366 # Store in map 367 matched_types[typedef].append((position, reg[0], reg[1], registers)) 368 position += reg[0] 369 370 # print the remaining 371 remaining_defines = [d for d in defines if d not in seen_defines and not d.endswith("_BASE")] 372 for typedef in matched_types: 373 peri = "_".join([t for t in typedef.split("_") if t.isupper()]) + "_" 374 rem = [d for d in remaining_defines if d.startswith(peri)] 375 if len(rem): 376 LOGGER.warning(f"Unassigned defines for ({typedef} *) {peri}: {len(rem)}") 377 for d in rem: 378 LOGGER.info(f"{d}: {defines[d]}") 379 380 # for typedef, registers in matched_types.items(): 381 # print(typedef) 382 # for reg in registers: 383 # print(f" {reg[0]:03x}: {reg[2]}") 384 385 device = svd.Device(self.did.string) 386 for name, (typedef, address) in peripheral_map.items(): 387 svd.Peripheral(name, typedef, defines[name], parent=device) 388 389 for name, registers in matched_types.items(): 390 peripheral = svd.PeripheralType(name, parent=device) 391 for offset, width, name, bitfields in registers: 392 register = svd.Register(name, offset, width, parent=peripheral) 393 for pos, (name, width, mask, value, _) in bitfields.items(): 394 svd.BitField(name, pos, width, parent=register) 395 396 return device 397 # return (peripheral_map, matched_types)
Header(did)
94 def __init__(self, did): 95 self.did = did 96 self.family_folder = f"stm32{self.did.family}xx" 97 self.cmsis_folder = Header._HEADER_PATH / self.family_folder / "Include" 98 self.family_header_file = f"{self.family_folder}.h" 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: 105 return 106 107 self.header_file = f"{self.define.lower()}.h" 108 self.device_map = None 109 substitutions = { 110 # r"/\* +?(Legacy defines|Legacy aliases|Old .*? legacy purpose|Aliases for .*?) +?\*/.*?\n\n": "", 111 r"/\* +?Legacy (aliases|defines|registers naming) +?\*/.*?\n\n": "", 112 r"/\* +?Old .*? legacy purpose +?\*/.*?\n\n": "", 113 r"/\* +?Aliases for .*? +?\*/.*?\n\n": "", 114 # r"( 0x[0-9A-F]+)\) ": r"\1U", 115 # r"#define.*?/\*!<.*? Legacy .*?\*/\n": "", 116 } 117 super().__init__(self.cmsis_folder / self.header_file, substitutions)
interrupt_table
135 @property 136 def interrupt_table(self): 137 if "vectors" not in self._cache: 138 interrupt_enum = [i["values"] for i in self.header.enums if i["name"] == "IRQn_Type"][0] 139 vectors = [ 140 {"position": int(str(i["value"]).replace(" ", "")), "name": i["name"][:-5]} for i in interrupt_enum 141 ] 142 self._cache["vectors"] = vectors 143 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 # get all defines for this device name 39 devName = f"STM32{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 + f"x{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 + f"{device_id.pin.upper()}x" 74 for define in deviceDefines: 75 if devNameMatch <= define: 76 return define 77 78 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