#!/usr/bin/env python3 """Export the authoritative Ti375 C529 package map from Efinity. Run with Efinity's bundled Python because the resource pickle contains Efinity classes. No pin name or ball assignment is embedded in this script. """ from __future__ import annotations import argparse import csv import gzip import hashlib import json import pickle import re from pathlib import Path HSIO_BANKS = {"2A", "2B", "2C", "2D", "2E", "4A_4B", "4C", "4D"} HVIO_BANKS = {"BL2_BL3", "BR0", "BR3_BR4", "TL1_TL5", "TR0", "TR1", "TR2"} def natural_ball(ball: str) -> tuple[str, int]: match = re.fullmatch(r"([A-Z]+)([0-9]+)", ball) if not match: return ball, 0 return match.group(1), int(match.group(2)) def main() -> None: parser = argparse.ArgumentParser() parser.add_argument( "--db", type=Path, default=Path("/tools/efinix/efinity/2025.2/pt/db/resource/Ti375C529.db"), ) parser.add_argument("--csv", type=Path, default=Path("docs/ti375_c529_package.csv")) parser.add_argument("--meta", type=Path, default=Path("docs/ti375_c529_package.meta.json")) args = parser.parse_args() with gzip.open(args.db, "rb") as stream: device = pickle.load(stream) package = device.get_package() io_db = device._PeripheryDevice__io_pad io_by_pad = { vars(item)["_IOPadInfo__pad_name"]: vars(item) for item in io_db.get_all_io_pads() } rows: list[dict[str, str]] = [] for ball, mapping in sorted(package.get_package_pad_map().items(), key=lambda pair: natural_ball(pair[0])): pad = mapping.get_pad_name() info = io_by_pad.get(pad, {}) die_bank = info.get("_IOPadInfo__bank_name", "") package_bank = info.get("_IOPadInfo__pkg_bank_name", "") direction_obj = info.get("_IOPadInfo__direction") direction = getattr(direction_obj, "name", "") instances = info.get("_IOPadInfo__instances", []) if package_bank in HSIO_BANKS: bank_class = "HSIO" allowed_vccio = "1.2;1.35;1.5;1.8" elif package_bank in HVIO_BANKS: bank_class = "HVIO" allowed_vccio = "1.8;2.5;3.0;3.3" else: bank_class = "HARD/POWER" allowed_vccio = "" special: list[str] = [] upper_pad = pad.upper() if "CLK" in upper_pad: special.append("CLOCK_CAPABLE") if "PLLIN" in upper_pad: special.append("PLL_INPUT") if "EXTFB" in upper_pad: special.append("PLL_EXTFB") if "CDI" in upper_pad: special.append("CORE_DATA_INPUT") if "EXTSPI" in upper_pad: special.append("EXT_SPI") if "CBSEL" in upper_pad: special.append("CONFIG_BOOT_SELECT") if re.search(r"_(P|N)(?:_|$)", upper_pad): special.append("DIFFERENTIAL_MEMBER") is_fabric_gpio = pad.startswith(("GPIOB_", "GPIOT_", "GPIOL_", "GPIOR_")) rows.append( { "ball": ball, "official_pad_name": pad, "die_bank": die_bank, "package_bank": package_bank, "bank_class": bank_class, "allowed_vccio_v": allowed_vccio, "direction": direction, "function": info.get("_IOPadInfo__function", ""), "periphery_instances": ";".join(instances), "instance_pin": info.get("_IOPadInfo__pin", "") or "", "special_capability": ";".join(special), "fabric_gpio": "yes" if is_fabric_gpio else "no", } ) args.csv.parent.mkdir(parents=True, exist_ok=True) with args.csv.open("w", newline="", encoding="utf-8") as stream: writer = csv.DictWriter(stream, fieldnames=list(rows[0])) writer.writeheader() writer.writerows(rows) digest = hashlib.sha256(args.db.read_bytes()).hexdigest() metadata = { "device": device.get_device_name(), "mapped_device": device.get_device_mapped_name(), "package": package.get_package_name(), "package_status": package.get_status_str(), "source_database": str(args.db), "source_sha256": digest, "row_count": len(rows), "fabric_gpio_count": sum(row["fabric_gpio"] == "yes" for row in rows), "package_banks": package.get_package_bank_names(), } args.meta.write_text(json.dumps(metadata, indent=2, sort_keys=True) + "\n", encoding="utf-8") if __name__ == "__main__": main()