Initial Ti375 GNSS IMU board design and documentation
This commit is contained in:
Executable
+258
@@ -0,0 +1,258 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the authoritative Ti375 pin tables and MAX2771 register image.
|
||||
|
||||
The package source is exported directly from Efinity 2025.2's Ti375C529
|
||||
resource database by export_ti375_package.py. This script deliberately fails
|
||||
if a named ball, bank, voltage, or clock capability does not match that source.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
PACKAGE_CSV = ROOT / "docs" / "ti375_c529_package.csv"
|
||||
|
||||
|
||||
# signal, ball, direction, subsystem, selected I/O standard, design note
|
||||
ASSIGNMENTS = [
|
||||
("HARD_TCK", "G10", "input", "hard_jtag", "3.3 V LVCMOS", "Dedicated hard TAP TCK; FT4232H interface 1 BDBUS0 through power-domain isolation"),
|
||||
("HARD_TMS", "G12", "input", "hard_jtag", "3.3 V LVCMOS", "Dedicated hard TAP TMS; FT4232H interface 1 BDBUS3 through power-domain isolation"),
|
||||
("HARD_TDI", "G9", "input", "hard_jtag", "3.3 V LVCMOS", "Dedicated hard TAP TDI; FT4232H interface 1 BDBUS1 through power-domain isolation"),
|
||||
("HARD_TDO", "G13", "output", "hard_jtag", "3.3 V LVCMOS", "Dedicated hard TAP TDO; FT4232H interface 1 BDBUS2 through power-domain isolation"),
|
||||
("FPGA_CRESET_N", "F9", "input", "configuration", "1.8 V LVCMOS", "Open-drain fan-in of all-rail-valid, FTDI, and filtered external/manual reset requests"),
|
||||
("FPGA_CDONE", "F8", "output", "configuration", "1.8 V LVCMOS", "Configuration status test point"),
|
||||
("QSPI_CS_N", "A7", "output", "configuration", "1.8 V LVCMOS", "SPI-active SSL_N"),
|
||||
("QSPI_SCLK", "G6", "output", "configuration", "1.8 V LVCMOS", "SPI-active CCK; official CCK_PLLIN0 pin"),
|
||||
("QSPI_IO0", "H4", "bidirectional", "configuration", "1.8 V LVCMOS", "SPI-active CDI0"),
|
||||
("QSPI_IO1", "H5", "bidirectional", "configuration", "1.8 V LVCMOS", "SPI-active CDI1"),
|
||||
("QSPI_IO2", "G5", "bidirectional", "configuration", "1.8 V LVCMOS", "SPI-active CDI2"),
|
||||
("QSPI_IO3", "B7", "bidirectional", "configuration", "1.8 V LVCMOS", "SPI-active CDI3"),
|
||||
("FPGA_TEST_N", "J2", "input", "configuration", "1.8 V LVCMOS", "Mandatory external weak pull-up disables test mode during configuration"),
|
||||
("FPGA_CSI", "J5", "input", "configuration", "1.8 V LVCMOS", "Mandatory external weak pull-up selects this FPGA throughout configuration"),
|
||||
("RISCV_TCK", "F12", "input", "secondary_jtag", "3.3 V LVCMOS", "Fabric RISC-V JTAG clock moved off the plain-GPIO PMOD allocation"),
|
||||
("RISCV_TDI", "V19", "input", "secondary_jtag", "3.3 V LVCMOS", "Official development-kit hardened RISC-V JTAG assignment"),
|
||||
("RISCV_TMS", "P18", "input", "secondary_jtag", "3.3 V LVCMOS", "Clock-capable fabric pin; official development-kit assignment"),
|
||||
("RISCV_TDO", "T18", "output", "secondary_jtag", "3.3 V LVCMOS", "Official development-kit hardened RISC-V JTAG assignment"),
|
||||
("MAX_CLKOUT", "B11", "input", "max2771", "3.0 V LVCMOS", "Source-synchronous ADC capture clock on dedicated clock-capable input"),
|
||||
("MAX_I0", "D10", "input", "max2771", "3.0 V LVCMOS", "ADC I bit 0"),
|
||||
("MAX_I1", "D11", "input", "max2771", "3.0 V LVCMOS", "ADC I bit 1; clock-capable pad retained in same BR0 bank"),
|
||||
("MAX_Q0", "E10", "input", "max2771", "3.0 V LVCMOS", "ADC Q bit 0; PLL-capable pad in same BR0 bank"),
|
||||
("MAX_Q1", "E11", "input", "max2771", "3.0 V LVCMOS", "ADC Q bit 1; PLL-capable pad in same BR0 bank"),
|
||||
("MAX_LD", "F11", "input", "max2771", "3.0 V LVCMOS", "PLL lock status"),
|
||||
("MAX_SCLK", "F16", "output", "max2771", "3.0 V LVCMOS", "Three-wire serial clock"),
|
||||
("MAX_CSN", "F17", "output", "max2771", "3.0 V LVCMOS", "Three-wire active-low chip select"),
|
||||
("MAX_SDATA", "G16", "bidirectional", "max2771", "3.0 V LVCMOS", "Single bidirectional three-wire serial data; not split MOSI/MISO"),
|
||||
("MAX_SHDN", "G17", "output", "max2771", "3.0 V LVCMOS", "MAX2771 hardware shutdown control"),
|
||||
("IMU_SCK", "A8", "output", "sch16t", "3.3 V LVCMOS", "SafeSPI mode-0 clock"),
|
||||
("IMU_MOSI", "A9", "output", "sch16t", "3.3 V LVCMOS", "SafeSPI controller-to-sensor data"),
|
||||
("IMU_MISO", "B9", "input", "sch16t", "3.3 V LVCMOS", "SafeSPI sensor-to-controller data"),
|
||||
("IMU_CS", "B10", "output", "sch16t", "3.3 V LVCMOS", "SafeSPI active-low chip select"),
|
||||
("IMU_DRY_SYNC", "A11", "bidirectional", "sch16t", "3.3 V LVCMOS", "Murata multiplexed Data Ready output / SYNC input"),
|
||||
("IMU_EXTRESN", "C10", "output", "sch16t", "3.3 V LVCMOS", "External active-low reset"),
|
||||
("CLK_40M_FPGA", "U17", "input", "clock", "3.3 V LVCMOS", "40 MHz oscillator branch on clock-capable pin"),
|
||||
("UART0_RX", "P17", "input", "ft4232", "3.3 V LVCMOS", "FT4232H channel C TXD to FPGA RX"),
|
||||
("UART0_TX", "R17", "output", "ft4232", "3.3 V LVCMOS", "FPGA TX to FT4232H channel C RXD"),
|
||||
("UART1_RX", "R18", "input", "ft4232", "3.3 V LVCMOS", "FT4232H channel D TXD to FPGA RX"),
|
||||
("UART1_TX", "T19", "output", "ft4232", "3.3 V LVCMOS", "FPGA TX to FT4232H channel D RXD"),
|
||||
("FTDI_DBG_GPIO", "U19", "bidirectional", "ft4232", "3.3 V LVCMOS", "Channel D debug GPIO/test point; PLL-capable"),
|
||||
("PMOD1_1", "U6", "bidirectional", "pmod_3v3", "3.3 V LVCMOS", "PMOD 1 pin 1; plain HVIO GPIO"),
|
||||
("PMOD1_2", "V4", "bidirectional", "pmod_3v3", "3.3 V LVCMOS", "PMOD 1 pin 2; plain HVIO GPIO"),
|
||||
("PMOD1_3", "A13", "bidirectional", "pmod_tr1", "3.3 V LVCMOS", "PMOD 1 pin 3"),
|
||||
("PMOD1_4", "C9", "bidirectional", "pmod_3v3", "3.3 V LVCMOS", "PMOD 1 pin 4; plain HVIO GPIO"),
|
||||
("PMOD1_7", "C12", "bidirectional", "pmod_tr1", "3.3 V LVCMOS", "PMOD 1 pin 7"),
|
||||
("PMOD1_8", "C13", "bidirectional", "pmod_tr1", "3.3 V LVCMOS", "PMOD 1 pin 8"),
|
||||
("PMOD1_9", "C14", "bidirectional", "pmod_tr1", "3.3 V LVCMOS", "PMOD 1 pin 9"),
|
||||
("PMOD1_10", "D12", "bidirectional", "pmod_tr1", "3.3 V LVCMOS", "PMOD 1 pin 10"),
|
||||
("PMOD2_1", "V20", "bidirectional", "pmod_3v3", "3.3 V LVCMOS", "PMOD 2 pin 1; plain HVIO GPIO"),
|
||||
("PMOD2_2", "D13", "bidirectional", "pmod_tr2", "3.3 V LVCMOS", "PMOD 2 pin 2"),
|
||||
("PMOD2_3", "E13", "bidirectional", "pmod_tr2", "3.3 V LVCMOS", "PMOD 2 pin 3"),
|
||||
("PMOD2_4", "E14", "bidirectional", "pmod_tr2", "3.3 V LVCMOS", "PMOD 2 pin 4"),
|
||||
("PMOD2_7", "E16", "bidirectional", "pmod_tr2", "3.3 V LVCMOS", "PMOD 2 pin 7"),
|
||||
("PMOD2_8", "F13", "bidirectional", "pmod_tr2", "3.3 V LVCMOS", "PMOD 2 pin 8"),
|
||||
("PMOD2_9", "E15", "bidirectional", "pmod_tr2", "3.3 V LVCMOS", "PMOD 2 pin 9"),
|
||||
("PMOD2_10", "F14", "bidirectional", "pmod_tr2", "3.3 V LVCMOS", "PMOD 2 pin 10"),
|
||||
# The two 1.8-V PMODs use plain HSIO pairs only. None of these pads has
|
||||
# a configuration, clock, PLL, or external-feedback alternate function.
|
||||
("PMOD3_1", "C20", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 3 pin 1; HSIO pair 51 P"),
|
||||
("PMOD3_2", "B20", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 3 pin 2; HSIO pair 51 N"),
|
||||
("PMOD3_3", "C17", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 3 pin 3; HSIO pair 52 P"),
|
||||
("PMOD3_4", "B17", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 3 pin 4; HSIO pair 52 N"),
|
||||
("PMOD3_7", "A16", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 3 pin 7; HSIO pair 53 P"),
|
||||
("PMOD3_8", "A17", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 3 pin 8; HSIO pair 53 N"),
|
||||
("PMOD3_9", "C19", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 3 pin 9; HSIO pair 54 P"),
|
||||
("PMOD3_10", "B19", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 3 pin 10; HSIO pair 54 N"),
|
||||
("PMOD4_1", "B14", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 4 pin 1; HSIO pair 55 P"),
|
||||
("PMOD4_2", "A14", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 4 pin 2; HSIO pair 55 N"),
|
||||
("PMOD4_3", "J18", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 4 pin 3; HSIO pair 56 P"),
|
||||
("PMOD4_4", "H18", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 4 pin 4; HSIO pair 56 N"),
|
||||
("PMOD4_7", "A18", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 4 pin 7; HSIO pair 57 P"),
|
||||
("PMOD4_8", "A19", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 4 pin 8; HSIO pair 57 N"),
|
||||
("PMOD4_9", "D16", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 4 pin 9; HSIO pair 58 P"),
|
||||
("PMOD4_10", "D17", "bidirectional", "pmod_2e", "1.8 V LVCMOS", "PMOD 4 pin 10; HSIO pair 58 N"),
|
||||
]
|
||||
|
||||
|
||||
EXPECTED_HARD_NAMES = {
|
||||
"G10": "TCK", "G12": "TMS", "G9": "TDI", "G13": "TDO",
|
||||
"F9": "CRESET_N", "F8": "CDONE", "C8": "JTAG_VCCIO_SEL",
|
||||
}
|
||||
|
||||
|
||||
def load_package() -> tuple[list[dict[str, str]], dict[str, dict[str, str]]]:
|
||||
with PACKAGE_CSV.open(newline="") as f:
|
||||
rows = list(csv.DictReader(f))
|
||||
assert len(rows) == 529, f"C529 package source has {len(rows)} rows, expected 529"
|
||||
by_ball = {r["ball"]: r for r in rows}
|
||||
assert len(by_ball) == 529
|
||||
for ball, name in EXPECTED_HARD_NAMES.items():
|
||||
assert by_ball[ball]["official_pad_name"] == name, (ball, by_ball[ball], name)
|
||||
return rows, by_ball
|
||||
|
||||
|
||||
def output_pin_tables(rows: list[dict[str, str]], by_ball: dict[str, dict[str, str]]) -> None:
|
||||
assigned: dict[str, dict[str, str]] = {}
|
||||
out_rows: list[dict[str, str]] = []
|
||||
for signal, ball, direction, subsystem, standard, note in ASSIGNMENTS:
|
||||
assert ball not in assigned, f"duplicate assignment at {ball}"
|
||||
pkg = by_ball[ball]
|
||||
if "clock" in note.lower() and "clock-capable" in note.lower():
|
||||
assert "CLOCK_CAPABLE" in pkg["special_capability"], (signal, ball, pkg)
|
||||
if standard.startswith("3.3"):
|
||||
assert "3.3" in pkg["allowed_vccio_v"], (signal, ball, pkg)
|
||||
if standard.startswith("3.0"):
|
||||
assert "3.0" in pkg["allowed_vccio_v"], (signal, ball, pkg)
|
||||
if standard.startswith("1.8"):
|
||||
assert "1.8" in pkg["allowed_vccio_v"], (signal, ball, pkg)
|
||||
item = {
|
||||
"signal": signal,
|
||||
"ball": ball,
|
||||
"official_pin_name": pkg["official_pad_name"],
|
||||
"die_bank": pkg["die_bank"],
|
||||
"package_bank": pkg["package_bank"],
|
||||
"vccio": standard.split()[0] + " V",
|
||||
"io_standard": standard,
|
||||
"direction": direction,
|
||||
"clock_capability": "yes" if "CLOCK_CAPABLE" in pkg["special_capability"] else "no",
|
||||
"special_function": pkg["special_capability"],
|
||||
"subsystem": subsystem,
|
||||
"connector": "on-board",
|
||||
"connector_pin": "",
|
||||
"note": note,
|
||||
}
|
||||
if subsystem.startswith("pmod_"):
|
||||
pmod_number = signal.removeprefix("PMOD").split("_", 1)[0]
|
||||
item["connector"] = f"JPMOD{pmod_number}"
|
||||
item["connector_pin"] = signal.rsplit("_", 1)[1]
|
||||
assigned[ball] = item
|
||||
out_rows.append(item)
|
||||
|
||||
# Only the four PMODs are exposed. All other fabric GPIOs remain physically
|
||||
# unconnected, reducing connector count, board area, and escape-routing load.
|
||||
inventory: list[dict[str, str]] = []
|
||||
for pkg in rows:
|
||||
if pkg["fabric_gpio"] != "yes":
|
||||
continue
|
||||
ball = pkg["ball"]
|
||||
if ball in assigned:
|
||||
a = assigned[ball]
|
||||
inventory.append({
|
||||
"ball": ball, "official_pin_name": pkg["official_pad_name"],
|
||||
"package_bank": pkg["package_bank"], "bank_class": pkg["bank_class"],
|
||||
"selected_vccio": a["vccio"], "io_standard": a["io_standard"],
|
||||
"clock_capability": a["clock_capability"], "special_function": pkg["special_capability"],
|
||||
"status": "used", "signal": a["signal"], "connector": a["connector"],
|
||||
"connector_pin": a["connector_pin"], "note": a["note"],
|
||||
})
|
||||
continue
|
||||
voltage = "3.3 V" if pkg["bank_class"] == "HVIO" else "1.8 V"
|
||||
standard = "3.3 V LVCMOS" if pkg["bank_class"] == "HVIO" else "1.8 V LVCMOS"
|
||||
inventory.append({
|
||||
"ball": ball, "official_pin_name": pkg["official_pad_name"],
|
||||
"package_bank": pkg["package_bank"], "bank_class": pkg["bank_class"],
|
||||
"selected_vccio": voltage, "io_standard": standard,
|
||||
"clock_capability": "yes" if "CLOCK_CAPABLE" in pkg["special_capability"] else "no",
|
||||
"special_function": pkg["special_capability"],
|
||||
"status": "unused", "signal": "", "connector": "",
|
||||
"connector_pin": "", "note": "Unused fabric GPIO; left unconnected at the BGA",
|
||||
})
|
||||
|
||||
all_gpio = {r["ball"] for r in rows if r["fabric_gpio"] == "yes"}
|
||||
assert len(all_gpio) == 227
|
||||
used_gpio = set(assigned) & all_gpio
|
||||
# Two former stand-alone auxiliary probes were deliberately released when
|
||||
# the connector-like test-point bank was removed.
|
||||
assert len(used_gpio) == 66
|
||||
assert len(all_gpio - used_gpio) == 161
|
||||
|
||||
fieldnames = list(out_rows[0])
|
||||
with (ROOT / "docs" / "ti375_pinout.csv").open("w", newline="") as f:
|
||||
w = csv.DictWriter(f, fieldnames=fieldnames)
|
||||
w.writeheader()
|
||||
w.writerows(sorted(out_rows, key=lambda r: r["ball"]))
|
||||
with (ROOT / "docs" / "unused_gpio_inventory.csv").open("w", newline="") as f:
|
||||
w = csv.DictWriter(f, fieldnames=list(inventory[0]))
|
||||
w.writeheader()
|
||||
w.writerows(sorted(inventory, key=lambda r: r["ball"]))
|
||||
|
||||
with (ROOT / "docs" / "ti375_pin_constraints.isf").open("w") as f:
|
||||
f.write("# Efinity interface constraints generated from the official Ti375C529 database.\n")
|
||||
f.write("# Verify port names against the final RTL top level before importing.\n")
|
||||
for a in sorted(out_rows, key=lambda r: r["signal"]):
|
||||
f.write(f'set_pin_assignment -pin_name {a["ball"]} -port_name {a["signal"]}\n')
|
||||
|
||||
|
||||
def output_max2771_registers() -> None:
|
||||
defaults = {
|
||||
0x0: 0xBEA41603, 0x1: 0x20550288, 0x2: 0x0EAFA1DC,
|
||||
0x3: 0x698C0008, 0x4: 0x00C00080, 0x5: 0x08000070,
|
||||
0x6: 0x08000000, 0x7: 0x010061B2, 0x8: 0x01E0F401,
|
||||
0x9: 0x00C00002, 0xA: 0x010061B0,
|
||||
}
|
||||
regs = dict(defaults)
|
||||
# GPS L1/L1C wideband example from MAX2771 Rev 2 Applications Information.
|
||||
regs[0x0] = (regs[0x0] & ~(1 << 17)) | (1 << 17) # MIXPOLE=36 MHz
|
||||
regs[0x0] = (regs[0x0] & ~(0x3 << 15)) | (0x2 << 15) # both LNAs disabled
|
||||
regs[0x0] = (regs[0x0] & ~(0x3 << 13)) | (0x0 << 13) # high-band mixer
|
||||
regs[0x0] = (regs[0x0] & ~(0x7 << 3)) | (0x3 << 3) # 23.4 MHz LPF
|
||||
regs[0x0] &= ~(1 << 2) # 5th order
|
||||
regs[0x0] &= ~(1 << 1) # low-pass, not polyphase
|
||||
regs[0x3] |= 1 << 3 # integer-N PLL
|
||||
regs[0x4] = (regs[0x4] & ~((0x7FFF << 13) | (0x3FF << 3))) | (1540 << 13) | (16 << 3)
|
||||
regs[0x7] |= 1 << 28 # external ADC clock on ADC_CLKIN
|
||||
regs[0xA] |= 1 << 2 # CLKOUT outputs ADC sampling clock
|
||||
assert (regs[0] >> 22) & 0xFF == 0xFA # required reserved field
|
||||
assert (regs[9] >> 22) & 0x7 == 0x3 # required reserved field
|
||||
assert regs[4] == 0x00C08080
|
||||
assert abs(16.368 / 16 * 1540 - 1575.42) < 1e-9
|
||||
|
||||
with (ROOT / "docs" / "max2771_registers.csv").open("w", newline="") as f:
|
||||
fieldnames = ["address", "value_hex", "default_hex", "changed", "purpose"]
|
||||
w = csv.DictWriter(f, fieldnames=fieldnames)
|
||||
w.writeheader()
|
||||
purposes = {
|
||||
0: "MIXIN_HI, both LNAs disabled, 23.4 MHz 5th-order LPF, 36 MHz mixer pole",
|
||||
3: "Integer-N L1 PLL",
|
||||
4: "NDIV=1540, RDIV=16: 16.368 MHz / 16 * 1540 = 1575.42 MHz",
|
||||
7: "External 40 MHz ADC sampling clock on ADC_CLKIN",
|
||||
10: "Route ADC sampling clock to CLKOUT for source-synchronous FPGA capture",
|
||||
}
|
||||
for addr in range(11):
|
||||
w.writerow({
|
||||
"address": f"0x{addr:X}", "value_hex": f"0x{regs[addr]:08X}",
|
||||
"default_hex": f"0x{defaults[addr]:08X}",
|
||||
"changed": "yes" if regs[addr] != defaults[addr] else "no",
|
||||
"purpose": purposes.get(addr, "Preserve MAX2771 documented reset/default value"),
|
||||
})
|
||||
|
||||
|
||||
def main() -> None:
|
||||
rows, by_ball = load_package()
|
||||
output_pin_tables(rows, by_ball)
|
||||
output_max2771_registers()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user