modm_data.cubemx.device_data

  1# Copyright 2016, Fabian Greif
  2# Copyright 2016, Niklas Hauser
  3# SPDX-License-Identifier: MPL-2.0
  4
  5import os
  6import re
  7import logging
  8from collections import defaultdict
  9
 10from ..owl.stmicro import did_from_string
 11from ..utils import ext_path, XmlReader
 12from . import stm32_data
 13from ..cubehal import read_request_map as dmamux_request_map
 14from . import peripherals
 15
 16LOGGER = logging.getLogger(__file__)
 17
 18
 19_MCU_PATH = ext_path("stmicro/cubemx/mcu")
 20_FAMILY_FILE = None
 21
 22def _family_file() -> XmlReader:
 23    global _FAMILY_FILE
 24    if _FAMILY_FILE is None:
 25        _FAMILY_FILE = XmlReader(_MCU_PATH / "families.xml")
 26    return _FAMILY_FILE
 27
 28
 29# ============================= MULTIPLE DEVICES ==============================
 30def _format_raw_devices(rawDevices):
 31    TemperatureMap = {0: "6", 105: "7", 125: "3"}
 32    devices = set()
 33    for dev in rawDevices:
 34        temp_max = dev.find("Temperature")
 35        temp_max = "" if temp_max is None else temp_max.get("Max")
 36        name = dev.get("RefName")
 37        temp_max = int(float(temp_max)) if len(temp_max) else min(TemperatureMap)
 38        for temp, value in TemperatureMap.items():
 39            if temp_max >= temp:
 40                devices.add(name[:12] + value + name[13:])
 41    return sorted(list(devices))
 42
 43
 44def devices_from_family(family: str) -> list[str]:
 45    """
 46    :param family: A STM32 family name, for example, F0, H7, G4.
 47    :return: A list of device names belonging to a STM32 family.
 48    """
 49    devices = _family_file().query(f'//Family[@Name="{family.upper()}"]/SubFamily/Mcu/@RefName')
 50    devices = _format_raw_devices(devices)
 51    LOGGER.info("Found devices of family '{}': {}".format(family, ", ".join(devices)))
 52    return devices
 53
 54
 55def devices_from_prefix(prefix: str) -> list[str]:
 56    """
 57    :param prefix: A STM32 device prefix, for example, STM32, STM32H7, STM32G432.
 58    :return: A list of device names starting with the prefix.
 59    """
 60    devices = _family_file().query(f'//Family/SubFamily/Mcu[starts-with(@RefName,"{prefix.upper()}")]')
 61    devices = _format_raw_devices(devices)
 62    devices = [d for d in devices if not stm32_data.ignoreDevice(d)]
 63    LOGGER.info("Found devices for prefix '{}': {}".format(prefix, ", ".join(devices)))
 64    return list(sorted(devices))
 65
 66def cubemx_device_list() -> list["modm_data.owl.DeviceIdentifier"]:
 67    """
 68    :return: A list of all STM32 device identifiers.
 69    """
 70    return [did_from_string(d) for d in devices_from_prefix("STM32")]
 71
 72
 73# ============================= INDIVIDUAL DEVICE =============================
 74def devices_from_partname(partname: str) -> list[dict[str]]:
 75    """
 76    Find the STM32 device name in the STM32CubeMX database and convert the data
 77    into a device specific key-value mapping describing the hardware.
 78
 79    .. note::
 80       One partname may contain multiple devices, for example, multiple
 81       asymmetric CPU cores like in the STM32H745 device.
 82
 83    :param partname: A full STM32 device name.
 84    :return: a list of dictionaries containing a device specific data structure.
 85    """
 86    deviceNames = _family_file().query('//Family/SubFamily/Mcu[starts-with(@RefName,"{}")]'
 87                                                 .format(partname[:12] + "x" + partname[13:]))
 88    comboDeviceName = sorted([d.get("Name") for d in deviceNames])[0]
 89    device_file = XmlReader(os.path.join(_MCU_PATH, comboDeviceName + ".xml"))
 90    did = did_from_string(partname.lower())
 91    LOGGER.info("Parsing '{}'".format(did.string))
 92
 93    # information about the core and architecture
 94    cores = [c.text.lower().replace("arm ", "") for c in device_file.query('//Core')]
 95    if len(cores) > 1: did.naming_schema += "@{core}"
 96    devices = [_properties_from_id(comboDeviceName, device_file, did.copy(), c) for c in cores]
 97    return [d for d in devices if d is not None]
 98
 99
100def _properties_from_id(comboDeviceName, device_file, did, core):
101    if core.endswith("m4") or core.endswith("m7") or core.endswith("m33"):
102        core += "f"
103    if did.family in ["h7"] or (did.family in ["f7"] and did.name not in ["45", "46", "56"]):
104        core += "d"
105    if "@" in did.naming_schema:
106        did.set("core", core[7:9])
107    p = {"id": did, "core": core}
108
109    # Maximum operating frequency
110    max_frequency = float(device_file.query('//Frequency')[0].text)
111    # H7 dual-core devices run the M4 core at half the frequency as the M7 core
112    if did.get("core", "") == "m4": max_frequency /= 2.0;
113    p["max_frequency"] = int(max_frequency * 1e6)
114
115    # Information from the CMSIS headers
116    # stm_header = Header(did)
117    # if not stm_header.is_valid:
118    #     LOGGER.error("CMSIS Header invalid for %s", did.string)
119    #     return None
120
121    # flash and ram sizes
122    # The <ram> and <flash> can occur multiple times.
123    # they are "ordered" in the same way as the `(S-I-Z-E)` ids in the device combo name
124    # we must first find out which index the current did.size has inside `(S-I-Z-E)`
125    sizeIndexFlash = 0
126    sizeIndexRam = 0
127
128    match = re.search(r"\(.(-.)*\)", comboDeviceName)
129    if match:
130        sizeArray = match.group(0)[1:-1].lower().split("-")
131        sizeIndexFlash = sizeArray.index(did.size)
132        sizeIndexRam = sizeIndexFlash
133
134    rams = sorted([int(r.text) for r in device_file.query('//Ram')])
135    if sizeIndexRam >= len(rams):
136        sizeIndexRam = len(rams) - 1
137
138    flashs = sorted([int(f.text) for f in device_file.query('//Flash')])
139    if sizeIndexFlash >= len(flashs):
140        sizeIndexFlash = len(flashs) - 1
141
142
143    p["ram"] = rams[sizeIndexRam] * 1024
144    p["flash"] = flashs[sizeIndexFlash] * 1024
145
146    memories = []
147    for (mem_name, mem_start, mem_size) in stm32_data.getMemoryForDevice(did, p["flash"], p["ram"]):
148        access = "rwx"
149        if did.family == "f4" and mem_name == "ccm": access = "rw";
150        if "flash" in mem_name: access = "rx";
151        memories.append({"name": mem_name, "access": access, "size": str(mem_size),
152                         "start": "0x{:02X}".format(mem_start)})
153
154    p["memories"] = memories
155
156    # packaging
157    package = device_file.query('//@Package')[0]
158    p["pin-count"] = re.findall(r"[0-9]+", package)[0]
159    p["package"] = re.findall(r"[A-Za-z\.]+", package)[0]
160
161    def clean_up_version(version):
162        match = re.search("v[1-9]_[0-9x]", version.replace(".", "_"))
163        if match:
164            version = match.group(0).replace("_", ".")
165        else:
166            pass
167            # print(version)
168        return version
169
170    modules = []
171    dmaFile = None
172    for ip in device_file.query('//IP'):
173        # These IPs are all software modules, NOT hardware modules. Their version string is weird too.
174        software_ips = {"GFXSIMULATOR", "GRAPHICS", "FATFS", "TOUCHSENSING", "PDM2PCM",
175                        "MBEDTLS", "FREERTOS", "CORTEX_M", "NVIC", "USB_DEVICE",
176                        "USB_HOST", "LWIP", "LIBJPEG", "GUI_INTERFACE", "TRACER",
177                        "FILEX", "LEVELX", "THREADX", "USBX", "LINKEDLIST", "NETXDUO"}
178        if any(ip.get("Name").upper().startswith(p) for p in software_ips):
179            continue
180
181        rversion = ip.get("Version")
182        module = (ip.get("Name"), ip.get("InstanceName"), clean_up_version(rversion))
183
184        if module[0] == "DMA":
185            # lets load additional information about the DMA
186            dmaFile = XmlReader(os.path.join(_MCU_PATH, "IP", "DMA-" + rversion + "_Modes.xml"))
187            for rdma in dmaFile.query('//IP/ModeLogicOperator/Mode[starts-with(@Name,"DMA")]/@Name'):
188                for dma in rdma.split(","):
189                    modules.append((module[0].lower(), dma.strip().lower(), module[2].lower()))
190            continue
191        if module[0].startswith("TIM"):
192            module = ("TIM",) + module[1:]
193
194        modules.append(tuple([m.lower() for m in module]))
195
196    modules.append( ("flash", "flash", "v1.0"))
197    modules = [m + peripherals.getPeripheralData(did, m) for m in modules]
198
199    p["modules"] = modules
200    LOGGER.debug("Available Modules are:\n" + _modulesToString(modules))
201    instances = [m[1] for m in modules]
202    # print("\n".join(str(m) for m in modules))
203
204    # p["stm_header"] = stm_header
205    # p["interrupts"] = stm_header.interrupt_table
206    # Flash latency table
207    # p["flash_latency"] = stm32_data.getFlashLatencyForDevice(did)
208
209    # lets load additional information about the GPIO IP
210    ip_file = device_file.query('//IP[@Name="GPIO"]')[0].get("Version")
211    ip_file = os.path.join(_MCU_PATH, "IP", "GPIO-" + ip_file + "_Modes.xml")
212    gpioFile = XmlReader(ip_file)
213
214    pins = device_file.query('//Pin[@Type="I/O"][starts-with(@Name,"P")]')
215    def raw_pin_sort(p):
216        port = p.get("Name")[1:2]
217        pin = p.get("Name")[:4]
218        if len(pin) > 3 and not pin[3].isdigit():
219            pin = pin[:3]
220        return (port, int(pin[2:]))
221    pins = sorted(pins, key=raw_pin_sort)
222    # Remove package remaps from GPIO data (but not from package)
223    pins.sort(key=lambda p: "PINREMAP" not in p.get("Variant", ""))
224
225    gpios = []
226
227    def pin_name(name):
228        name = name[:4]
229        if len(name) > 3 and not name[3].isdigit():
230            name = name[:3]
231        return (name[1:2].lower(), name[2:].lower())
232
233    # Find the remap pin pairs, if they exist
234    double_pinouts = defaultdict(list)
235    for pin in device_file.query('//Pin'):
236        double_pinouts[pin.get("Position")].append((pin.get("Name"), pin.get("Variant", "DEFAULT")))
237    double_pinouts = {pos: {pin:variant for (pin, variant) in pins}
238                            for pos, pins in double_pinouts.items()
239                                if len(pins) > 1 and any("PINREMAP" in pin[1] for pin in pins)}
240
241    # Get the pinout for this package with correct remap variants
242    pinout = []
243    for pin in device_file.query("//Pin"):
244        name = pin.get("Name")
245        pos = pin.get("Position")
246        pinv = {
247            "name": name,
248            "position": pos,
249            "type": pin.get("Type"),
250        }
251        variant = double_pinouts.get(pos, {}).get(name)
252        if (variant is not None and (pin.get("Type") != "I/O" or (
253            pin_name(name)[0] in ['a'] and
254            pin_name(name)[1] in ['9', '10', '11', '12']))):
255            pinv["variant"] = "remap" if "PINREMAP" in variant else "remap-default"
256        pinout.append(pinv)
257
258    p["pinout"] = pinout
259    p["package"] = device_file.query("/Mcu/@Package")[0]
260
261    def split_af(af):
262        # entry 0 contains names without instance
263        # entry 1 contains names with instance
264        mdriv = [m for m in modules if af.startswith(m[0] + "_")]
265        minst = [m for m in modules if af.startswith(m[1] + "_")]
266        # print(af, mdriv, minst)
267        if len(minst) > 1:
268            LOGGER.warning("Ambiguos driver: {} {}".format(af, minst))
269            exit(1)
270
271        minst = minst[0] if len(minst) else None
272        mdriv = mdriv[0] if len(mdriv) else None
273
274        driver = minst[0] if minst else (mdriv[0] if mdriv else None)
275        if not driver:
276            LOGGER.debug("Unknown driver: {}".format(af))
277        instance = None
278        if minst and driver:
279            pinst = minst[1].replace(driver, "")
280            if len(pinst): instance = pinst;
281        if minst or mdriv:
282            name = af.replace((minst[1] if minst else mdriv[0]) + "_", "")
283            if not len(name):
284                LOGGER.error("Unknown name: {} {}".format(af, minst, mdriv))
285                exit(1)
286        else:
287            name = af
288
289        return (driver, instance, name)
290
291    def split_multi_af(af):
292        af = af.replace("ir_", "irtim_") \
293               .replace("crs_", "rcc_crs_") \
294               .replace("timx_", "tim_")
295        if af == "cec": af = "hdmi_cec_cec";
296
297        driver, instance, names = split_af(af)
298        rafs = []
299        for name in names.split("-"):
300            rafs.append( (driver, instance, name) )
301        return rafs
302
303    if dmaFile is not None:
304        dma_dumped = []
305        dma_streams = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
306        dma_request_map = None
307        for sig in dmaFile.query('//ModeLogicOperator[@Name="XOR"]/Mode'):
308            name = rname = sig.get("Name")
309            if did.family == "wl" and rname in ["SAI1_A", "SAI1_B", "QUADSPI"]:
310                continue  # CubeMX data is wrong, WL devices don't have these peripherals
311
312            parent = sig.getparent().getparent().get("Name")
313            instance = parent.split("_")[0][3:]
314            parent = parent.split("_")[1]
315
316            request = dmaFile.query('//RefMode[@Name="{}"]'.format(name))[0]
317            def rv(param, default=[]):
318                vls = request.xpath('./Parameter[@Name="{}"]/PossibleValue/text()'.format(param))
319                if not len(vls): vls = default;
320                return vls
321
322            name = name.lower().split(":")[0]
323            if name == "memtomem":
324                continue
325
326            # Several corrections
327            name = name.replace("spdif_rx", "spdifrx")
328            if name.startswith("dac") and "_" not in name: name = "dac_{}".format(name);
329            if any(name == n for n in ["sdio", "sdmmc2", "sdmmc1"]): continue
330            if len(name.split("_")) < 2: name = "{}_default".format(name);
331            driver, inst, name = split_af(name)
332
333            if "[" in parent:
334                if dma_request_map is None:
335                    dma_request_map = dmamux_request_map(did)
336                channel = dma_request_map[rname]
337                stream = instance = 0
338                p["dma_naming"] = (None, "request", "signal")
339            elif "Stream" in parent:
340                channel = rv("Channel", ["software"])[0].replace("DMA_CHANNEL_", "")
341                stream = parent.replace("Stream", "")
342                p["dma_naming"] = ("stream", "channel", "signal")
343            elif rv("Request"):
344                channel = rv("Request", ["software"])[0].replace("DMA_REQUEST_", "")
345                stream = parent.replace("Channel", "")
346                p["dma_naming"] = ("channel", "request", "signal")
347            else:
348                channel = parent.replace("Channel", "")
349                stream = channel
350                p["dma_naming"] = (None, "channel", "signal")
351
352            if driver is None: # peripheral is not part of this device
353                dma_dumped.append( (instance, stream, name) )
354                continue
355            mode = [v[4:].lower() for v in rv("Mode")]
356            for sname in ([None] if name == "default" else name.split("/")):
357                signal = {
358                    "driver": driver,
359                    "name": sname,
360                    "direction": [v[4:].replace("PERIPH", "p").replace("MEMORY", "m").replace("_TO_", "2") for v in rv("Direction")],
361                    "mode": mode,
362                    "increase": "ENABLE" in rv("PeriphInc", ["DMA_PINC_ENABLE"])[0],
363                }
364                if inst: signal["instance"] = inst;
365                remaps = stm32_data.getDmaRemap(did, instance, channel, driver, inst, sname)
366                if remaps: signal["remap"] = remaps;
367                dma_streams[instance][stream][channel].append(signal)
368                # print(instance, stream, channel)
369                # print(signal)
370
371        # Manually handle condition expressions from XML for
372        # (STM32F030CCTx|STM32F030RCTx) and (STM32F070CBTx|STM32F070RBTx)
373        if did.family in ['f0']:
374            if (did.name == '30' and did.size == 'c'):
375                dma_streams['1'].pop('6')
376                dma_streams['1'].pop('7')
377                dma_streams.pop('2')
378            if (did.name == '70' and did.size == 'b'):
379                dma_streams['1'].pop('6')
380                dma_streams['1'].pop('7')
381
382        # De-duplicate DMA signal entries
383        def deduplicate_list(l):
384            return [i for n, i in enumerate(l) if i not in l[n + 1:]]
385        for stream in dma_streams:
386            for channel in dma_streams[stream]:
387                for signal in dma_streams[stream][channel]:
388                    dma_streams[stream][channel][signal] = deduplicate_list(
389                        dma_streams[stream][channel][signal])
390
391        # if p["dma_naming"][1] == "request":
392        #     print(did, dmaFile.filename)
393        p["dma"] = dma_streams
394        if len(dma_dumped):
395            for instance, stream, name in sorted(dma_dumped):
396                LOGGER.debug("DMA{}#{}: dumping {}".format(instance, stream, name))
397
398        # If DMAMUX is used, add DMAMUX to DMA peripheral channel mappings
399        if p["dma_naming"] == (None, "request", "signal"):
400            # There can be multiple "//RefParameter[@Name="Instance"]" nodes constrained by
401            # a <Condition> child node filtering by the STM32 die id
402            # Try to match a node with condition first, if nothing matches choose the default one
403            die_id = device_file.query('//Die')[0].text
404            q = '//RefParameter[@Name="Instance"]/Condition[@Expression="%s"]/../PossibleValue/@Value' % die_id
405            channels = dmaFile.query(q)
406            if len(channels) == 0:
407                # match channels from node without <Condition> child node
408                channels = dmaFile.query('//RefParameter[@Name="Instance" and not(Condition)]/PossibleValue/@Value')
409
410            mux_channels = []
411            # H7 has "Stream" instead of "Channel" for DMAMUX1
412            mux_channel_regex = re.compile(r"DMA(?P<instance>([0-9]))_(Channel|Stream)(?P<channel>([0-9]+))")
413            for mux_ch_position, channel in enumerate(channels):
414                m = mux_channel_regex.match(channel)
415                assert m is not None
416                mux_channels.append({'position'     : mux_ch_position,
417                                     'dma-instance' : int(m.group("instance")),
418                                     'dma-channel'  : int(m.group("channel"))})
419            p["dma_mux_channels"] = mux_channels
420
421    if did.family == "f1":
422        grouped_f1_signals = gpioFile.compactQuery('//GPIO_Pin/PinSignal/@Name')
423
424    _seen_gpio = set()
425    for pin in pins:
426        rname = pin.get("Name")
427        name = pin_name(rname)
428
429        # the analog channels are only available in the Mcu file, not the GPIO file
430        localSignals = device_file.compactQuery('//Pin[@Name="{}"]/Signal[not(@Name="GPIO")]/@Name'.format(rname))
431        # print(name, localSignals)
432        altFunctions = []
433
434        if did.family == "f1":
435            altFunctions = [ (s.lower(), "-1") for s in localSignals if s not in grouped_f1_signals]
436        else:
437            allSignals = gpioFile.compactQuery('//GPIO_Pin[@Name="{}"]/PinSignal/SpecificParameter[@Name="GPIO_AF"]/..'.format(rname))
438            signalMap = { a.get("Name"): a[0][0].text.lower().replace("gpio_af", "")[:2].replace("_", "") for a in allSignals }
439            altFunctions = [ (s.lower(), (signalMap[s] if s in signalMap else "-1")) for s in localSignals ]
440
441        afs = []
442        for af in altFunctions:
443            for raf in split_multi_af(af[0]):
444                naf = {}
445                naf["driver"], naf["instance"], naf["name"] = raf
446                naf["af"] = af[1] if int(af[1]) >= 0 else None
447                if "exti" in naf["name"]: continue;
448                afs.append(naf)
449
450        gpio = (name[0], name[1], afs)
451        if name not in _seen_gpio:
452            gpios.append(gpio)
453            _seen_gpio.add(name)
454        # print(gpio[0].upper(), gpio[1], afs)
455        # LOGGER.debug("{}{}: {} ->".format(gpio[0].upper(), gpio[1]))
456
457    remaps = {}
458    if did.family == "f1":
459        for remap in gpioFile.compactQuery('//GPIO_Pin/PinSignal/RemapBlock/@Name'):
460            module = remap.split("_")[0].lower()
461            config = remap.split("_")[1].replace("REMAP", "").replace("IREMAP", "")
462            mapping = stm32_data.getGpioRemapForModuleConfig(module, config)
463
464            mpins = []
465            for pin in gpioFile.compactQuery('//GPIO_Pin/PinSignal/RemapBlock[@Name="{}"]/..'.format(remap)):
466                name = pin.getparent().get("Name")[:4].split("-")[0].split("/")[0].strip().lower()
467                pport, ppin = name[1:2], name[2:]
468                if not any([pp[0] == pport and pp[1] == ppin for pp in gpios]):
469                    continue
470                mmm = {"port": pport, "pin": ppin}
471                driver, _, name = split_af(pin.get("Name").lower())
472                if driver is None: continue;
473                mmm["name"] = name;
474                mpins.append(mmm)
475
476            if module not in remaps:
477                driver, instance, _ = split_af(module + "_lol")
478                if not driver: continue;
479                remaps[module] = {
480                    "mask": mapping["mask"],
481                    "position": mapping["position"],
482                    "groups": {},
483                    "driver": driver,
484                    "instance": instance,
485                }
486            if len(mpins) > 0:
487                remaps[module]["groups"][mapping["mapping"]] = mpins
488                LOGGER.debug("{:<20}{}".format(module + "_" + config, ["{}{}:{}".format(b["port"], b["pin"], b["name"]) for b in mpins]))
489
490        # import json
491        # print(json.dumps(remaps, indent=4))
492
493    p["remaps"] = remaps
494    p["gpios"] = gpios
495
496    return p
497
498
499def _modulesToString(modules):
500    string = ""
501    mods = sorted(modules)
502    char = mods[0][0][0:1]
503    for _, instance, _, _, _, _ in mods:
504        if not instance.startswith(char):
505            string += "\n"
506        string += instance + " \t"
507        char = instance[0][0:1]
508    return string
LOGGER = <Logger /opt/hostedtoolcache/Python/3.12.2/x64/lib/python3.12/site-packages/modm_data/cubemx/device_data.py (WARNING)>
def devices_from_family(family: str) -> list[str]:
45def devices_from_family(family: str) -> list[str]:
46    """
47    :param family: A STM32 family name, for example, F0, H7, G4.
48    :return: A list of device names belonging to a STM32 family.
49    """
50    devices = _family_file().query(f'//Family[@Name="{family.upper()}"]/SubFamily/Mcu/@RefName')
51    devices = _format_raw_devices(devices)
52    LOGGER.info("Found devices of family '{}': {}".format(family, ", ".join(devices)))
53    return devices
Parameters
  • family: A STM32 family name, for example, F0, H7, G4.
Returns

A list of device names belonging to a STM32 family.

def devices_from_prefix(prefix: str) -> list[str]:
56def devices_from_prefix(prefix: str) -> list[str]:
57    """
58    :param prefix: A STM32 device prefix, for example, STM32, STM32H7, STM32G432.
59    :return: A list of device names starting with the prefix.
60    """
61    devices = _family_file().query(f'//Family/SubFamily/Mcu[starts-with(@RefName,"{prefix.upper()}")]')
62    devices = _format_raw_devices(devices)
63    devices = [d for d in devices if not stm32_data.ignoreDevice(d)]
64    LOGGER.info("Found devices for prefix '{}': {}".format(prefix, ", ".join(devices)))
65    return list(sorted(devices))
Parameters
  • prefix: A STM32 device prefix, for example, STM32, STM32H7, STM32G432.
Returns

A list of device names starting with the prefix.

def cubemx_device_list() -> list[modm_data.owl.identifier.DeviceIdentifier]:
67def cubemx_device_list() -> list["modm_data.owl.DeviceIdentifier"]:
68    """
69    :return: A list of all STM32 device identifiers.
70    """
71    return [did_from_string(d) for d in devices_from_prefix("STM32")]
Returns

A list of all STM32 device identifiers.

def devices_from_partname(partname: str) -> list[dict[str]]:
75def devices_from_partname(partname: str) -> list[dict[str]]:
76    """
77    Find the STM32 device name in the STM32CubeMX database and convert the data
78    into a device specific key-value mapping describing the hardware.
79
80    .. note::
81       One partname may contain multiple devices, for example, multiple
82       asymmetric CPU cores like in the STM32H745 device.
83
84    :param partname: A full STM32 device name.
85    :return: a list of dictionaries containing a device specific data structure.
86    """
87    deviceNames = _family_file().query('//Family/SubFamily/Mcu[starts-with(@RefName,"{}")]'
88                                                 .format(partname[:12] + "x" + partname[13:]))
89    comboDeviceName = sorted([d.get("Name") for d in deviceNames])[0]
90    device_file = XmlReader(os.path.join(_MCU_PATH, comboDeviceName + ".xml"))
91    did = did_from_string(partname.lower())
92    LOGGER.info("Parsing '{}'".format(did.string))
93
94    # information about the core and architecture
95    cores = [c.text.lower().replace("arm ", "") for c in device_file.query('//Core')]
96    if len(cores) > 1: did.naming_schema += "@{core}"
97    devices = [_properties_from_id(comboDeviceName, device_file, did.copy(), c) for c in cores]
98    return [d for d in devices if d is not None]

Find the STM32 device name in the STM32CubeMX database and convert the data into a device specific key-value mapping describing the hardware.

One partname may contain multiple devices, for example, multiple asymmetric CPU cores like in the STM32H745 device.

Parameters
  • partname: A full STM32 device name.
Returns

a list of dictionaries containing a device specific data structure.