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