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,
samd21orsame7.
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.