modm_data.atdf.sam

  1# Copyright 2013, Niklas Hauser
  2# Copyright 2016, Fabian Greif
  3# Copyright 2022, Christopher Durand
  4# SPDX-License-Identifier: MPL-2.0
  5
  6import logging
  7from pathlib import Path
  8from collections import defaultdict
  9
 10from ..utils import ext_path, XmlReader
 11from .identifier import sam_did_from_string
 12
 13LOGGER = logging.getLogger(__name__)
 14_ATDF_PATH = ext_path("microchip/sam")
 15
 16
 17def device_files(prefix: str) -> list[Path]:
 18    """
 19    :param prefix: A SAM device prefix, for example, `samd21` or `same7`.
 20    :return: A sorted list of ATDF files matching the prefix.
 21    """
 22    return sorted(_ATDF_PATH.glob(f"*/AT{prefix.upper()}*"))
 23
 24
 25def devices_from_file(path: Path) -> list[str]:
 26    """:return: A sorted list of device order codes described in the ATDF file."""
 27    device_file = XmlReader(path)
 28    return sorted(set(d for d in device_file.query("//variants/variant/@ordercode") if d != "standard"))
 29
 30
 31def device_from_ordercode(path: Path, ordercode: str) -> dict:
 32    """
 33    Extracts the device data of one order code from a SAM ATDF file.
 34
 35    :param path: Path to the ATDF file.
 36    :param ordercode: The device order code, for example, `ATSAMD21G18A-AU`.
 37    :return: A dictionary of device properties.
 38    """
 39    p = {}
 40
 41    device_file = XmlReader(path)
 42    variant = device_file.query(f'//variants/variant[@ordercode="{ordercode}"]')[0]
 43    p["id"] = did = sam_did_from_string(ordercode.lower())
 44    LOGGER.info("Parsing '%s'", did.string)
 45
 46    # Package information
 47    p["package"] = variant.get("package")
 48    p["pinout"] = variant.get("pinout")
 49    p["pinout_pins"] = {
 50        pin.get("position"): pin.get("pad") for pin in device_file.query(f'//pinouts/pinout[@name="{p["pinout"]}"]/pin')
 51    }
 52
 53    # information about the core and architecture
 54    p["core"] = core = device_file.query("//device")[0].get("architecture").lower()
 55    fpu, dp = False, False
 56    for param in device_file.query("//device/parameters")[0]:
 57        name, value = param.get("name"), param.get("value")
 58        if name == "__FPU_PRESENT" and value == "1":
 59            fpu = True
 60        if name == "__FPU_DP" and value == "1":
 61            dp = True
 62        if name.startswith("__CM") and name.endswith("_REV"):
 63            rev = int(value, 0)
 64            p["revision"] = f"r{rev >> 8}p{rev & 0xFF}"
 65    if fpu:
 66        p["fpu"] = "fpv4-sp-d16" if "m4" in core else ("fpv5-d16" if dp else "fpv5-sp-d16")
 67
 68    # find the values for flash, ram and (optional) eeprom
 69    memories = []
 70    for memory_segment in device_file.query("//memory-segment"):
 71        name = memory_segment.get("name")
 72        start = memory_segment.get("start")
 73        size = int(memory_segment.get("size"), 16)
 74        access = memory_segment.get("rw", "r").lower()
 75        if memory_segment.get("exec") == "true":
 76            access += "x"
 77        if name in ["FLASH", "IFLASH"]:
 78            memories.append({"name": "flash", "access": "rx", "size": str(size), "start": start})
 79        elif name in ["HMCRAMC0", "HMCRAM0", "HSRAM", "IRAM"]:
 80            memories.append({"name": "ram", "access": access, "size": str(size), "start": start})
 81        elif name in ["LPRAM", "BKUPRAM"]:
 82            memories.append({"name": "lpram", "access": access, "size": str(size), "start": start})
 83        elif name in ["SEEPROM", "RWW"]:
 84            memories.append({"name": "eeprom", "access": "r", "size": str(size), "start": start})
 85        else:
 86            LOGGER.debug("Memory segment '%s' not used", name)
 87    p["memories"] = memories
 88
 89    modules = []
 90    p["gclk_data"] = {"clocks": defaultdict(list)}
 91    p["dma_requests"] = defaultdict(list)
 92    for m in device_file.query("//peripherals/module/instance"):
 93        module_name = m.getparent().get("name").lower()
 94        instance = m.get("name").lower()
 95        for param in m.xpath("parameters/param"):
 96            name = param.get("name")
 97            if name.startswith("GCLK_ID"):
 98                clock_name = name[8:].lower()
 99                p["gclk_data"]["clocks"][instance].append(
100                    (clock_name if clock_name != "" else None, param.get("value"))
101                )
102            if name.startswith("DMAC_ID_"):
103                signal = "_".join(name.lower().split("_")[2:])
104                p["dma_requests"][instance].append((signal, int(param.get("value"))))
105
106        if module_name == "gclk":
107            p["gclk_data"]["generator_count"] = int(m.xpath('parameters/param[@name="GEN_NUM"]')[0].attrib["value"])
108        if module_name != "port":
109            modules.append((module_name, instance))
110    p["modules"] = sorted(list(set(modules)))
111
112    # parse GCLK sources from register section
113    generators = device_file.query('//modules/module[@name="GCLK"]/value-group[@name="GCLK_GENCTRL__SRC"]/value')
114    p["gclk_data"]["sources"] = dict([(g.get("name").capitalize(), g.get("value")) for g in generators])
115
116    signals = []
117    gpios = []
118    for s in device_file.query("//peripherals/module/instance/signals/signal"):
119        tmp = {
120            "module": s.getparent().getparent().getparent().get("name").lower(),
121            "instance": s.getparent().getparent().get("name").lower(),
122        }
123        tmp.update({k: v.lower() for k, v in s.items()})
124        if "group" in tmp:
125            tmp["group"] = tmp["group"].replace(f"{tmp['instance']}_", "")
126
127        # Fix duplicate GPIO data for SAMx7x revision A devices
128        # FIXME: The family is lower case, so this fix is never applied!
129        if did.family == "E7x/S7x/V7x" and did.variant == "a":
130            if tmp["module"] in ("sdramc", "smc"):
131                continue
132
133        if tmp["group"] in ["p", "pin"] or tmp["group"].startswith("port"):
134            gpios.append(tmp)
135        else:
136            signals.append(tmp)
137    gpios = sorted([(g["pad"][1], g["pad"][2:]) for g in gpios])
138
139    p["signals"] = signals
140    # Filter gpios by pinout
141    p["gpios"] = [pin for pin in gpios if f"P{pin[0].upper()}{pin[1]}" in p["pinout_pins"].values()]
142    p["interrupts"] = [
143        {"position": i.get("index"), "name": i.get("name")} for i in device_file.query("//interrupts/interrupt")
144    ]
145
146    # Events pass data from source to sink without waking the processor
147    p["event_sources"] = [
148        {"index": i.get("index"), "name": i.get("name"), "instance": i.get("module-instance")}
149        for i in device_file.query("//events/generators/generator")
150    ]
151    p["event_users"] = [
152        {"index": i.get("index"), "name": i.get("name"), "instance": i.get("module-instance")}
153        for i in device_file.query("//events/users/user")
154    ]
155    return p
LOGGER = <Logger modm_data.atdf.sam (WARNING)>
def device_files(prefix: str) -> list[pathlib.Path]:
18def device_files(prefix: str) -> list[Path]:
19    """
20    :param prefix: A SAM device prefix, for example, `samd21` or `same7`.
21    :return: A sorted list of ATDF files matching the prefix.
22    """
23    return sorted(_ATDF_PATH.glob(f"*/AT{prefix.upper()}*"))
Parameters
  • prefix: A SAM device prefix, for example, samd21 or same7.
Returns

A sorted list of ATDF files matching the prefix.

def devices_from_file(path: pathlib.Path) -> list[str]:
26def devices_from_file(path: Path) -> list[str]:
27    """:return: A sorted list of device order codes described in the ATDF file."""
28    device_file = XmlReader(path)
29    return sorted(set(d for d in device_file.query("//variants/variant/@ordercode") if d != "standard"))
Returns

A sorted list of device order codes described in the ATDF file.

def device_from_ordercode(path: pathlib.Path, ordercode: str) -> dict:
 32def device_from_ordercode(path: Path, ordercode: str) -> dict:
 33    """
 34    Extracts the device data of one order code from a SAM ATDF file.
 35
 36    :param path: Path to the ATDF file.
 37    :param ordercode: The device order code, for example, `ATSAMD21G18A-AU`.
 38    :return: A dictionary of device properties.
 39    """
 40    p = {}
 41
 42    device_file = XmlReader(path)
 43    variant = device_file.query(f'//variants/variant[@ordercode="{ordercode}"]')[0]
 44    p["id"] = did = sam_did_from_string(ordercode.lower())
 45    LOGGER.info("Parsing '%s'", did.string)
 46
 47    # Package information
 48    p["package"] = variant.get("package")
 49    p["pinout"] = variant.get("pinout")
 50    p["pinout_pins"] = {
 51        pin.get("position"): pin.get("pad") for pin in device_file.query(f'//pinouts/pinout[@name="{p["pinout"]}"]/pin')
 52    }
 53
 54    # information about the core and architecture
 55    p["core"] = core = device_file.query("//device")[0].get("architecture").lower()
 56    fpu, dp = False, False
 57    for param in device_file.query("//device/parameters")[0]:
 58        name, value = param.get("name"), param.get("value")
 59        if name == "__FPU_PRESENT" and value == "1":
 60            fpu = True
 61        if name == "__FPU_DP" and value == "1":
 62            dp = True
 63        if name.startswith("__CM") and name.endswith("_REV"):
 64            rev = int(value, 0)
 65            p["revision"] = f"r{rev >> 8}p{rev & 0xFF}"
 66    if fpu:
 67        p["fpu"] = "fpv4-sp-d16" if "m4" in core else ("fpv5-d16" if dp else "fpv5-sp-d16")
 68
 69    # find the values for flash, ram and (optional) eeprom
 70    memories = []
 71    for memory_segment in device_file.query("//memory-segment"):
 72        name = memory_segment.get("name")
 73        start = memory_segment.get("start")
 74        size = int(memory_segment.get("size"), 16)
 75        access = memory_segment.get("rw", "r").lower()
 76        if memory_segment.get("exec") == "true":
 77            access += "x"
 78        if name in ["FLASH", "IFLASH"]:
 79            memories.append({"name": "flash", "access": "rx", "size": str(size), "start": start})
 80        elif name in ["HMCRAMC0", "HMCRAM0", "HSRAM", "IRAM"]:
 81            memories.append({"name": "ram", "access": access, "size": str(size), "start": start})
 82        elif name in ["LPRAM", "BKUPRAM"]:
 83            memories.append({"name": "lpram", "access": access, "size": str(size), "start": start})
 84        elif name in ["SEEPROM", "RWW"]:
 85            memories.append({"name": "eeprom", "access": "r", "size": str(size), "start": start})
 86        else:
 87            LOGGER.debug("Memory segment '%s' not used", name)
 88    p["memories"] = memories
 89
 90    modules = []
 91    p["gclk_data"] = {"clocks": defaultdict(list)}
 92    p["dma_requests"] = defaultdict(list)
 93    for m in device_file.query("//peripherals/module/instance"):
 94        module_name = m.getparent().get("name").lower()
 95        instance = m.get("name").lower()
 96        for param in m.xpath("parameters/param"):
 97            name = param.get("name")
 98            if name.startswith("GCLK_ID"):
 99                clock_name = name[8:].lower()
100                p["gclk_data"]["clocks"][instance].append(
101                    (clock_name if clock_name != "" else None, param.get("value"))
102                )
103            if name.startswith("DMAC_ID_"):
104                signal = "_".join(name.lower().split("_")[2:])
105                p["dma_requests"][instance].append((signal, int(param.get("value"))))
106
107        if module_name == "gclk":
108            p["gclk_data"]["generator_count"] = int(m.xpath('parameters/param[@name="GEN_NUM"]')[0].attrib["value"])
109        if module_name != "port":
110            modules.append((module_name, instance))
111    p["modules"] = sorted(list(set(modules)))
112
113    # parse GCLK sources from register section
114    generators = device_file.query('//modules/module[@name="GCLK"]/value-group[@name="GCLK_GENCTRL__SRC"]/value')
115    p["gclk_data"]["sources"] = dict([(g.get("name").capitalize(), g.get("value")) for g in generators])
116
117    signals = []
118    gpios = []
119    for s in device_file.query("//peripherals/module/instance/signals/signal"):
120        tmp = {
121            "module": s.getparent().getparent().getparent().get("name").lower(),
122            "instance": s.getparent().getparent().get("name").lower(),
123        }
124        tmp.update({k: v.lower() for k, v in s.items()})
125        if "group" in tmp:
126            tmp["group"] = tmp["group"].replace(f"{tmp['instance']}_", "")
127
128        # Fix duplicate GPIO data for SAMx7x revision A devices
129        # FIXME: The family is lower case, so this fix is never applied!
130        if did.family == "E7x/S7x/V7x" and did.variant == "a":
131            if tmp["module"] in ("sdramc", "smc"):
132                continue
133
134        if tmp["group"] in ["p", "pin"] or tmp["group"].startswith("port"):
135            gpios.append(tmp)
136        else:
137            signals.append(tmp)
138    gpios = sorted([(g["pad"][1], g["pad"][2:]) for g in gpios])
139
140    p["signals"] = signals
141    # Filter gpios by pinout
142    p["gpios"] = [pin for pin in gpios if f"P{pin[0].upper()}{pin[1]}" in p["pinout_pins"].values()]
143    p["interrupts"] = [
144        {"position": i.get("index"), "name": i.get("name")} for i in device_file.query("//interrupts/interrupt")
145    ]
146
147    # Events pass data from source to sink without waking the processor
148    p["event_sources"] = [
149        {"index": i.get("index"), "name": i.get("name"), "instance": i.get("module-instance")}
150        for i in device_file.query("//events/generators/generator")
151    ]
152    p["event_users"] = [
153        {"index": i.get("index"), "name": i.get("name"), "instance": i.get("module-instance")}
154        for i in device_file.query("//events/users/user")
155    ]
156    return p

Extracts the device data of one order code from a SAM ATDF file.

Parameters
  • path: Path to the ATDF file.
  • ordercode: The device order code, for example, ATSAMD21G18A-AU.
Returns

A dictionary of device properties.