modm_data.picosdk

Raspberry Pi Pico SDK

Parses the CMSIS-SVD files of the RP2040 and RP2350 devices from the Pico SDK.

 1# Copyright 2026, Niklas Hauser
 2# SPDX-License-Identifier: MPL-2.0
 3
 4"""
 5# Raspberry Pi Pico SDK
 6
 7Parses the CMSIS-SVD files of the RP2040 and RP2350 devices from the Pico SDK.
 8"""
 9
10from .device_data import device_files, device_from_file, did_from_string
11
12__all__ = [
13    "device_files",
14    "device_from_file",
15    "did_from_string",
16]
def device_files(prefix: str) -> list[pathlib.Path]:
34def device_files(prefix: str) -> list[Path]:
35    """
36    :param prefix: A device prefix, for example, `rp2040`.
37    :return: A sorted list of SVD files matching the prefix.
38    """
39    return sorted(_SVD_PATH.glob(f"{prefix.lower()}*.svd"))
Parameters
  • prefix: A device prefix, for example, rp2040.
Returns

A sorted list of SVD files matching the prefix.

def device_from_file(path: pathlib.Path) -> dict:
 84def device_from_file(path: Path) -> dict:
 85    """
 86    Extracts the device data from an RP SVD file.
 87
 88    :param path: Path to the SVD file.
 89    :return: A dictionary of device properties.
 90    """
 91    device_file = XmlReader(path)
 92    p = {"id": (did := did_from_string(Path(path).stem))}
 93    LOGGER.info("Parsing '%s'", did.string)
 94
 95    # information about the core and architecture
 96    core = device_file.query("//device/cpu/name")[0].text.lower().replace("cm", "cortex-m")
 97    if device_file.query("//device/cpu/fpuPresent")[0].text in ("1", "true"):
 98        p["fpu"] = "fpv5-sp-d16"
 99    p["core"] = core
100    p["revision"] = device_file.query("//device/cpu/revision")[0].text
101
102    # TODO: Memories are not described in the SVD file
103    p["memories"] = [
104        {"name": "ram", "access": "rwx", "size": str(0x40000), "start": "0x20000000"},
105        {"name": "core1", "access": "rwx", "size": str(0x1000), "start": "0x20040000"},
106        {"name": "core0", "access": "rwx", "size": str(0x1000), "start": "0x20041000"},
107    ]
108
109    modules = [
110        {"module": "jtag", "instance": "jtag"},
111        {"module": "usb", "instance": "usb"},
112        {"module": "xip", "instance": "xip"},
113    ]
114    gpios = []
115    adc_channels = []
116    dma_channels = []
117    clocks = []
118    for m in device_file.query("//peripherals/peripheral"):
119        modulename = m.find("name").text
120        if modulename == "IO_BANK0":
121            for r in m.findall("./registers/register"):
122                if r.find("description") is not None and r.find("description").text == "GPIO status":
123                    name = re.search(r"^GPIO(?P<name>.*)_STATUS$", r.find("name").text).group("name").lower()
124                    ctrl = m.find("./registers/register[name='GPIO" + name + "_CTRL']")
125                    gpios.append({"name": name, "bank": "bank0", "idx": int(name), "funcs": _gpio_funcs(ctrl)})
126        elif modulename == "IO_QSPI":
127            for r in m.findall("./registers/register"):
128                if r.find("description") is not None and r.find("description").text == "GPIO status":
129                    name = re.search(r"^GPIO_QSPI_(?P<name>.*)_STATUS$", r.find("name").text).group("name").lower()
130                    ctrl = m.find("./registers/register[name='GPIO_QSPI_" + name.upper() + "_CTRL']")
131                    idx = int(int(r.find("addressOffset").text[2:], 16) / 8)
132                    gpios.append({"name": name, "idx": idx, "bank": "qspi", "funcs": _gpio_funcs(ctrl)})
133        elif modulename == "ADC":
134            # TODO: Find way to get ADC channels info
135            adc_channels = [
136                {"id": 0, "name": "Ch0"},
137                {"id": 1, "name": "Ch1"},
138                {"id": 2, "name": "Ch2"},
139                {"id": 3, "name": "Ch3"},
140                {"id": 4, "name": "Temperature"},
141            ]
142            modules.append({"module": "adc", "instance": "adc"})
143        elif modulename == "DMA":
144            for r in m.findall("./registers/register"):
145                if match := re.search(r"^CH(?P<name>\d+)_READ_ADDR$", r.find("name").text):
146                    dma_channels.append({"name": match.group("name").lower()})
147        elif modulename == "CLOCKS":
148            for r in m.findall("./registers/register"):
149                if match := re.search(r"^CLK_(?P<name>.+)_CTRL$", r.find("name").text):
150                    name = match.group("name").lower()
151                    aux_fld = r.find("./fields/field[name='AUXSRC']")
152                    if aux_fld is None:
153                        continue
154                    idx = int(int(r.find("addressOffset").text, 16) / 12)
155                    sources = []
156                    aux_sel = 0
157                    src_fld = r.find("./fields/field[name='SRC']")
158                    if src_fld is not None:
159                        for f in src_fld.findall("./enumeratedValues/enumeratedValue"):
160                            src_name = f.find("name").text
161                            if src_name == "clksrc_clk_" + name + "_aux":
162                                aux_sel = f.find("value").text
163                            else:
164                                sources.append(
165                                    {"name": _clock_source_name(src_name), "src": f.find("value").text, "aux": 0}
166                                )
167                    for f in aux_fld.findall("./enumeratedValues/enumeratedValue"):
168                        sources.append(
169                            {
170                                "name": _clock_source_name(f.find("name").text),
171                                "src": aux_sel,
172                                "aux": f.find("value").text,
173                            }
174                        )
175                    clocks.append({"name": name, "sources": sources, "glitchless": src_fld is not None, "idx": idx})
176        else:
177            match = re.search(r"(?P<module>.*\D)(?P<instance>\d*$)", modulename)
178            modules.append({"module": match.group("module").lower(), "instance": modulename.lower()})
179
180    p["modules"] = sorted(list(set([(m["module"], m["instance"]) for m in modules])))
181    p["gpios"] = gpios
182    p["adc_channels"] = adc_channels
183    p["dma_channels"] = dma_channels
184    p["clocks"] = clocks
185
186    modules_map = {"clocks": {"module": "clocks", "instance": "clocks"}}
187    for m in modules:
188        modules_map[m["instance"]] = m
189
190    # Manually patch this here instead of the SVD file
191    adc_map = {
192        "bank026": {"driver": "adc", "name": "in0", "af": "-1"},
193        "bank027": {"driver": "adc", "name": "in1", "af": "-1"},
194        "bank028": {"driver": "adc", "name": "in2", "af": "-1"},
195        "bank029": {"driver": "adc", "name": "in3", "af": "-1"},
196    }
197    for gpio in gpios:
198        gpio["signals"] = [_func_to_signal(modules_map, func) for func in gpio["funcs"]]
199        if adcsig := adc_map.get(gpio["bank"] + gpio["name"]):
200            gpio["signals"].append(adcsig)
201
202    # Unique interrupts
203    p["interrupts"] = []
204    for i in device_file.query("//peripherals/peripheral/interrupt"):
205        interrupt = {"position": i.find("value").text, "name": i.find("name").text}
206        if interrupt not in p["interrupts"]:
207            p["interrupts"].append(interrupt)
208    return p

Extracts the device data from an RP SVD file.

Parameters
  • path: Path to the SVD file.
Returns

A dictionary of device properties.

def did_from_string(string: str) -> modm_data.kg.DeviceIdentifier:
16def did_from_string(string: str) -> DeviceIdentifier:
17    """
18    Parses RP device strings, for example, `rp2040`, organized as
19    `{platform}{cores}{type}{ram}{flash}`.
20    """
21    string = string.lower()
22    if not string.startswith("rp"):
23        raise ValueError(f"Unknown identifier '{string}'!")
24    i = DeviceIdentifier("{platform}{cores}{type}{ram}{flash}")
25    i.set("platform", "rp")
26    i.set("cores", string[2])
27    i.set("type", string[3])
28    i.set("ram", string[4])
29    i.set("flash", string[5])
30    i.set("family", string[2:4])
31    return i

Parses RP device strings, for example, rp2040, organized as {platform}{cores}{type}{ram}{flash}.