circuitpython/ports/mimxrt10xx/tools/gen_peripherals_data.py
2023-08-17 11:39:14 -07:00

377 lines
15 KiB
Python

import sys
import pathlib
import xml.etree.ElementTree as ET
SIGNALS = {
"LPI2C": ["SDA", "SCL"],
"LPSPI": ["SCK", "SDO", "SDI"],
"LPUART": ["RX", "TX", "RTS", "CTS"],
"I2S": ["RX_DATA0", "RX_SYNC", "TX_BCLK", "TX_DATA0", "TX_SYNC", "MCLK"],
"MQS": ["LEFT", "RIGHT"],
}
SIGNAL_RENAME = {
"CTS_B": "CTS",
"RTS_B": "RTS",
"RXD": "RX",
"TXD": "TX",
"TX_DATA00": "TX_DATA0",
"RX_DATA00": "RX_DATA0",
"TX_DATA": "TX_DATA0",
"RX_DATA": "RX_DATA0",
}
SKIP_LPSR = True
svd_folder = pathlib.Path(sys.argv[1])
# Download and extract config tools data from https://mcuxpresso.nxp.com/en/select_config_tools_data
config_data_folder = pathlib.Path(sys.argv[2])
devices = sys.argv[3:]
peripherals_dir = pathlib.Path("peripherals/mimxrt10xx")
copyright = """/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Lucian Copeland for Adafruit Industries
* Copyright (c) 2019 Artur Pacholec
* Copyright (c) 2023 Scott Shawcroft for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
"""
autogen_warning_template = """/*
* This file is autogenerated! Do NOT hand edit it. Instead, edit tools/gen_peripherals_data.py and
* then rerun the script. You'll need to 1) clone https://github.com/nxp-mcuxpresso/mcux-soc-svd/
* and 2) download and extract config tools data from https://mcuxpresso.nxp.com/en/select_config_tools_data.
*
* Run `python tools/gen_peripherals_data.py <svd dir> <config dir> {}` to update this file.
*/
"""
for device in devices:
print(device)
autogen_warning = autogen_warning_template.format(device)
svd_fn = svd_folder / device / (device + ".xml")
if not svd_fn.exists():
svd_fn = svd_folder / device / (device + "_cm7.xml")
pin_to_analog = {}
out_dir = peripherals_dir / device
largest_signals = None
for signal_file in config_data_folder.glob(
f"{device}*ConfigTools_data*/**/signal_configuration.xml"
):
if largest_signals is None or signal_file.stat().st_size > largest_signals.stat().st_size:
largest_signals = signal_file
# Use the signal file to find analog connections
signal_tree = ET.parse(largest_signals)
signal_root = signal_tree.getroot()
for connection in signal_root.iter("connections"):
if "name_part" not in connection.attrib or not connection.attrib["name_part"].startswith(
"ADC"
):
continue
name_part = connection.attrib["name_part"]
try:
assign = next(connection.iter("assign"))
except StopIteration:
continue
split_pin = assign.attrib["register"].split("_")
pin_name = "_".join(split_pin[split_pin.index("GPIO") :])
adc_instance, adc_channel = name_part.split("_")
try:
adc_channel = int(adc_channel[2:])
except ValueError:
continue
pin_to_analog[pin_name] = (adc_instance, adc_channel)
# Find USB pins
usb_pins = []
all_pins = set()
for pin in signal_root.iter("pin"):
pin_name = pin.get("name")
if SKIP_LPSR and "LPSR" in pin_name:
continue
all_pins.add(pin_name)
if not pin_name.startswith("USB_OTG"):
continue
if not pin_name.endswith(("DN", "DP")):
continue
usb_pins.append(pin_name)
# Find peripherals
all_peripherals = {}
for peripheral in signal_root.iter("peripheral"):
ptype = peripheral.get("peripheral_type")
if ptype not in all_peripherals:
all_peripherals[ptype] = []
all_peripherals[ptype].append(peripheral.get("id"))
print(svd_fn)
tree = ET.parse(svd_fn)
root = tree.getroot()
pin_number = 0
last_gpio_base = None
mux_register_base = None
pins_h = [
copyright,
autogen_warning,
"#pragma once",
"",
"#define FORMAT_PIN(pin_name) extern const mcu_pin_obj_t pin_##pin_name;",
'#include "pin_names.h"',
"#undef FORMAT_PIN",
]
pins_c = [
copyright,
autogen_warning,
'#include "py/obj.h"',
'#include "py/mphal.h"',
'#include "mimxrt10xx/pins.h"',
"",
]
pin_names_h = [copyright, "", "// define FORMAT_PIN(pin_name) and then include this file."]
mux_registers_by_pin = {}
peripheral_inputs = {}
pwm_outputs = []
for register in root.iter("register"):
name = register.find("name").text
if name.endswith("SELECT_INPUT"):
name_split = name.split("_")
instance = name_split[0]
signal = "_".join(name_split[1:-2])
signal = SIGNAL_RENAME.get(signal, signal)
if instance not in peripheral_inputs:
peripheral_inputs[instance] = {}
if signal not in peripheral_inputs[instance]:
peripheral_inputs[instance][signal] = {}
for evalue in register.iter("enumeratedValue"):
ename = evalue.find("name").text.strip("_")
if "_ALT" in ename:
pin_name, alt = ename.rsplit("_", maxsplit=1)
else:
pin_name = ename
alt = evalue.find("description").text.rsplit(maxsplit=1)[1]
if SKIP_LPSR and "LPSR" in pin_name:
continue
alt = int(alt[3:])
value = int(evalue.find("value").text, 0)
peripheral_inputs[instance][signal][pin_name] = [alt, name, value]
# Mux registers come before PAD registers.
elif name.startswith("SW_MUX_CTL_PAD_GPIO"):
address_offset = int(register.find("addressOffset").text, 16)
if mux_register_base is None:
mux_register_base = address_offset
split_pin = name.split("_")
pin_name = "_".join(split_pin[4:])
if pin_name not in all_pins:
print("skip", pin_name)
continue
gpio_base = "_".join(split_pin[4:-1])
mux_registers_by_pin[pin_name] = register
if last_gpio_base != gpio_base:
pin_names_h.append("")
last_gpio_base = gpio_base
pin_number += 1
pin_names_h.append(f"FORMAT_PIN({pin_name})")
elif name.startswith("SW_PAD_CTL_PAD_GPIO"):
split_pin = name.split("_")
pin_name = "_".join(split_pin[4:])
if pin_name not in all_pins:
continue
mux_register = mux_registers_by_pin[pin_name]
mux_reset = int(mux_register.find("resetValue").text, 16)
pad_reset = int(register.find("resetValue").text, 16)
# Look through alt modes to find GPIO.
mux_field = mux_register.find("fields").find("field")
assert mux_field.find("name").text == "MUX_MODE"
for alt in mux_field.iter("enumeratedValue"):
desc = alt.find("description").text
if "FLEXPWM" in desc:
desc_split = desc.split()
alt = desc_split[3]
connection = desc_split[6]
pwm_instance = int(connection[7:8])
if connection.count("_") == 1:
# Form: FLEXPWM#_PWMC##
channel = connection[-3:-2]
module = int(connection[-2:])
else: # two _
# Form: FLEXPWM#_PWM#_C
channel = connection[-1:]
module = int(connection[-3:-2])
pwm_outputs.append((pwm_instance, module, channel, connection, pin_name))
elif "GPIO" in desc:
alt_name = desc.split()[-4]
# The 117x has a GPIO mux between GPIOn and CM7_GPIOn. For now,
# we use the the slow, default GPIOn.
if alt_name.startswith("GPIO_MUX"):
alt_name = alt_name.replace("GPIO_MUX", "GPIO")
gpio_instance, gpio_number = alt_name.split("_")
if gpio_instance == "GPIOMUX":
gpio_instance = "GPIO1"
gpio_number = int(gpio_number[2:])
else:
desc_split = desc.split()
alt = desc_split[3]
connection = desc_split[6]
alt = int(alt[3:])
if "_" not in connection:
print("skipping", pin_name, connection)
continue
instance, signal = connection.split("_", maxsplit=1)
signal = SIGNAL_RENAME.get(signal, signal)
if instance not in peripheral_inputs:
peripheral_inputs[instance] = {}
if signal not in peripheral_inputs[instance]:
peripheral_inputs[instance][signal] = {}
peripheral_inputs[instance][signal][pin_name] = [alt, None, 0]
if pin_name in pin_to_analog:
adc_instance, adc_channel = pin_to_analog[pin_name]
else:
adc_instance = "NO_ADC"
adc_channel = 0
pins_c.append(
f"const mcu_pin_obj_t pin_{pin_name} = PIN({gpio_instance}, {gpio_number}, {pin_name}, {adc_instance}, {adc_channel}, 0x{mux_reset:08X}, 0x{pad_reset:08X});"
)
if usb_pins:
pins_c.append("")
for pin_name in sorted(usb_pins):
pin_names_h.append(f"FORMAT_PIN({pin_name})")
pins_c.append(f"const mcu_pin_obj_t pin_{pin_name} = {{ {{ &mcu_pin_type }}, }};")
pin_names_h.append("")
pins_c.append("")
pins_h.append("")
pins_h.append("// Pads can be reset. Other pins like USB cannot be.")
pins_h.append(f"#define PAD_COUNT ({pin_number})")
pins_h.append(f"#define PIN_COUNT (PAD_COUNT + {len(usb_pins)})")
pins_h.append(f"extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];")
pins_h.append("")
out_dir.mkdir(exist_ok=True)
(out_dir / "pin_names.h").write_text("\n".join(pin_names_h))
(out_dir / "pins.h").write_text("\n".join(pins_h))
(out_dir / "pins.c").write_text("\n".join(pins_c))
periph_h = [copyright, autogen_warning, "#pragma once"]
periph_c = [
copyright,
autogen_warning,
'#include "py/obj.h"',
'#include "py/mphal.h"',
'#include "mimxrt10xx/periph.h"',
"",
]
for ptype in SIGNALS:
instances = all_peripherals[ptype]
short_name = ptype.lower()
if short_name.startswith("lp"):
short_name = short_name[2:]
# Only one MQS exists and it is related to SAI3
if ptype != "MQS":
periph_h.append(
f"extern {ptype}_Type *const mcu_{short_name}_banks[{len(instances)}];"
)
joined_instances = ", ".join(instances)
periph_c.append(
f"{ptype}_Type *const mcu_{short_name}_banks[{len(instances)}] = {{ {joined_instances} }};"
)
periph_c.append("")
for signal in SIGNALS[ptype]:
pin_count = 0
for instance in instances:
if instance not in peripheral_inputs or signal not in peripheral_inputs[instance]:
continue
pin_count += len(peripheral_inputs[instance][signal])
periph_h.append(
f"extern const mcu_periph_obj_t mcu_{short_name}_{signal.lower()}_list[{pin_count}];"
)
periph_c.append(
f"const mcu_periph_obj_t mcu_{short_name}_{signal.lower()}_list[{pin_count}] = {{"
)
for instance in instances:
if instance not in peripheral_inputs or signal not in peripheral_inputs[instance]:
continue
# MQS is tied to SAI3
if instance == "MQS":
instance_number = 3
else:
instance_number = int(instance[len(ptype) :])
if instance_number > 1:
periph_c.append("")
pins = peripheral_inputs[instance][signal]
pin_names = list(pins.keys())
pin_names.sort(key=lambda x: pins[x][-1])
for pin_name in pin_names:
alt, select_input, input_value = pins[pin_name]
if select_input:
select_input = f"kIOMUXC_{select_input}"
else:
select_input = "0"
periph_c.append(
f" PERIPH_PIN({instance_number}, {alt}, {select_input}, {input_value}, &pin_{pin_name}),"
)
periph_c.append(f"}};")
periph_c.append(f"")
periph_h.append("")
pwm_outputs.sort(key=lambda x: x[:3])
periph_c.append(f"const mcu_pwm_obj_t mcu_pwm_list[{len(pwm_outputs)}] = {{")
periph_h.append(f"extern const mcu_pwm_obj_t mcu_pwm_list[{len(pwm_outputs)}];")
last_channel = None
for pwm_instance, module, channel, connection, pin_name in pwm_outputs:
this_channel = (pwm_instance, module, channel)
if last_channel is not None and last_channel != this_channel:
periph_c.append("")
last_channel = this_channel
periph_c.append(
f" PWM_PIN(PWM{pwm_instance}, kPWM_Module_{module}, kPWM_Pwm{channel}, IOMUXC_{pin_name}_{connection}, &pin_{pin_name}),"
)
periph_c.append(f"}};")
periph_c.append("")
periph_h.append("")
(out_dir / "periph.h").write_text("\n".join(periph_h))
(out_dir / "periph.c").write_text("\n".join(periph_c))