stm32f1-hal 0.12.3

HAL for the STM32F1 family
Documentation
import csv
import os
import pprint
import subprocess

from base import Write

DRY_RUN = False
SCRIPT = os.path.relpath(__file__, os.getcwd()).replace("\\", "/")
REMAP_MODES = {
    "DEFAULT": "RemapDefault",
    "PARTIAL_REMAP": "RemapPartial1",
    "PARTIAL_REMAP1": "RemapPartial1",
    "PARTIAL_REMAP2": "RemapPartial2",
    "FULL_REMAP": "RemapFull",
    "REMAP": "RemapFull",
}

CFG_TABLE = {
    "TIM1": '#[cfg(any(feature = "f100", feature = "f103", feature = "connectivity"))]',
    "TIM4": '#[cfg(feature = "medium")]',
    "TIM5": '#[cfg(any(feature = "high", feature = "connectivity"))]',
    "TIM6": '#[cfg(any(feature = "f100", feature = "high", feature = "connectivity"))]',
    "TIM7": '#[cfg(any(all(feature = "high", any(feature = "f101", feature = "f103")),any(feature = "f100", feature = "connectivity")))]',
    "TIM8": '#[cfg(all(feature = "f103", feature = "high"))]',
    "TIM9": '#[cfg(feature = "xl")]',
    "TIM10": '#[cfg(feature = "xl")]',
    "TIM11": '#[cfg(feature = "xl")]',
    "TIM12": '#[cfg(any(feature = "xl", all(feature = "f100", feature = "high",)))]',
    "TIM13": '#[cfg(any(feature = "xl", all(feature = "f100", feature = "high",)))]',
    "TIM14": '#[cfg(any(feature = "xl", all(feature = "f100", feature = "high",)))]',
    "TIM15": '#[cfg(feature = "f100")]',
    "TIM16": '#[cfg(feature = "f100")]',
    "TIM17": '#[cfg(feature = "f100")]',
    "SPI3": '#[cfg(feature = "connectivity")]',
}


def match_filter(filter: str, name: str) -> bool:
    if filter == "UART":
        return name.startswith("UART") or name.startswith("USART")
    return name.startswith(filter)


def func_pin_name(filter: str, func: str) -> str:
    return filter[0] + filter[1:].lower() + func[0] + func[1:].lower() + "Pin"


REG_OP_TEMPLATE = """impl RemapMode<{peri}> for {mode}<{peri}> {{
    fn remap(afio: &mut Afio) {{
        {op}
    }}
}}
"""


def write_reg_operation(d: dict, filter: str, w: Write) -> None:
    w.write("\n// Register operations ------------\n\n")
    for peri, remap_modes in sorted(d.items()):
        if match_filter(filter, peri):
            for mode_name, mode_info in sorted(remap_modes.items()):
                mode = REMAP_MODES[mode_name]
                reg = mode_info["reg"]
                bits: str = mode_info["bits"]
                if reg == "none":
                    op = ""
                elif peri == "TIM5":
                    b = "set_bit" if bits[2] == "1" else "clear_bit"
                    op = f"afio.{reg}.modify_mapr(|_, w| w.{peri.lower()}ch4_iremap().{b}());"
                elif len(bits) == 3:
                    b = "set_bit" if bits[2] == "1" else "clear_bit"
                    op = f"afio.{reg}.modify_mapr(|_, w| w.{peri.lower()}_remap().{b}());"
                elif len(bits) == 4:
                    b = f"unsafe {{|_, w| w.{peri.lower()}_remap().bits({bits})}}"
                    op = f"afio.{reg}.modify_mapr({b});"
                else:
                    continue
                w.write(CFG_TABLE.get(peri, ""))
                w.write(REG_OP_TEMPLATE.format(mode=mode, peri=peri, op=op))


BIND_ALT_TYPE = """pub trait {func}<REMAP> {{
    type P;
    fn into_alternate(self) -> Self::P;
"""

BIND_IS_PIN = """ fn is_pin(&self) -> bool {
        true
    }
}
"""


def write_binder_type(d: dict, filter: str, w: Write) -> None:
    w.write("\n// Binder types ------------------\n\n")
    func_list: list[str] = []
    for peri, remap_modes in d.items():
        if match_filter(filter, peri):
            for mode_info in remap_modes.values():
                for pin_func in mode_info["pins"].keys():
                    func_list.append(pin_func)

    func_list = sorted(list(set(func_list)))
    for func in func_list:
        name = func_pin_name(filter, func)
        if filter == "I2C" or name in ["SpiSckPin", "SpiNssPin"]:
            w.write(BIND_ALT_TYPE.format(func=name) + "}")
        elif name.startswith("TimCh") or name in ["UartTxPin", "UartCkPin", "SpiMosiPin"]:
            w.write(BIND_ALT_TYPE.format(func=name) + BIND_IS_PIN)
            w.write(f"impl_for_none_pin_into!({name});")
        elif filter in ["UART", "TIM"]:
            w.write("pub trait {}<REMAP> {{".format(name) + BIND_IS_PIN)
            w.write(f"impl_for_none_pin!({name});")
        else:
            w.write(f"pub trait {name}<REMAP>{{}}")

    w.write("\n")


BIND_PIN = """impl {func}<{remap}<{peri}>> for {pin}<Input> {{
    type P = {pin}<Alternate<{alt}>>;
    fn into_alternate(self) -> Self::P {{
        self.into_mode(&mut Cr)
    }}
}}
"""

IMPL_TEMPLATE_LIST = [
    (
        ["UartRxPin", "SpiMisoPin"],
        "impl<UP: UpMode> {func}<{remap}<{peri}>> for {pin}<Input<UP>>{{}}",
        "",
    ),
    (
        [
            "UartTxPin",
            "UartCkPin",
            "TimCh1Pin",
            "TimCh2Pin",
            "TimCh3Pin",
            "TimCh4Pin",
            "SpiSckPin",
            "SpiMosiPin",
            "SpiNssPin",
        ],
        BIND_PIN,
        "PushPull",
    ),
    (["I2cSclPin", "I2cSdaPin"], BIND_PIN, "OpenDrain"),
]


def get_impl_template(func: str) -> tuple[str, str]:
    for item in IMPL_TEMPLATE_LIST:
        if func in item[0]:
            return (item[1], item[2])
    return ("", "")


def write_item(filter: str, peri: str, remap: str, pins: dict[str, str], w: Write) -> None:
    for pin_func, pin in sorted(pins.items()):
        func = func_pin_name(filter, pin_func)
        (impl, alt) = get_impl_template(func)
        if impl:
            cfg = CFG_TABLE.get(peri, "")
            if cfg:
                w.write(cfg)

            w.write(impl.format(func=func, remap=remap, peri=peri, pin=pin, alt=alt))


def write_table(d: dict, filter: str, csv_file: str, target_file: str) -> None:
    with open(target_file, "r", encoding="utf-8") as f:
        code = f.read()
        i = code.find("// table") + len("// table")
        before = code[:i]
        code = code[i:]

    w = Write(target_file, DRY_RUN)
    w.write(before)
    w.write("\n// Do NOT manually modify the code.\n")
    w.write(
        f"// It's generated by {SCRIPT} from {csv_file}\n",
    )
    write_binder_type(d, filter, w)
    w.write("\n// Bind pins ---------------------\n\n")
    for peri, remap_modes in sorted(d.items()):
        if match_filter(filter, peri):
            for mode_name, mode_info in sorted(remap_modes.items()):
                remap = REMAP_MODES[mode_name]
                write_item(filter, peri, remap, mode_info["pins"], w)
    w.write("\n")
    write_reg_operation(d, filter, w)
    w.close()
    subprocess.run(["rustfmt", target_file])


def parse_remap_info(row: list[str], ret_d: dict) -> None:
    peripheral = row[0]
    reg = row[1]
    remap_mode = row[2]
    reg_bits = row[3]
    pins: dict[str, str] = {}
    for pin in row[4:]:
        if pin:
            (func, pin) = pin.split(":")
            if "/" in func:
                (f1, f2) = func.split("/")
                pins[f1] = pin
                pins[f2] = pin
            else:
                pins[func] = pin

    p = ret_d.setdefault(peripheral, {})
    p[remap_mode] = {
        "reg": reg,
        "bits": reg_bits,
        "pins": pins,
    }


def csv_to_code(csv_file: str, show: bool = False) -> None:
    print(csv_file)
    d: dict = {}
    with open(csv_file, newline="", encoding="utf-8") as f:
        reader = csv.reader(f, delimiter=",", quotechar='"')
        for row in reader:
            if row[0]:
                parse_remap_info(row, d)

    if show:
        pprint.pprint(d)

    write_table(d, "UART", csv_file, "src/afio/uart_remap.rs")
    write_table(d, "TIM", csv_file, "src/afio/timer_remap.rs")
    write_table(d, "I2C", csv_file, "src/afio/i2c_remap.rs")
    write_table(d, "SPI", csv_file, "src/afio/spi_remap.rs")


if __name__ == "__main__":
    csv_to_code("scripts/table/stm32f1_remap_peripheral.csv")