Files
pcb/scripts/design_data.py
T

795 lines
47 KiB
Python

#!/usr/bin/env python3
"""Single-source component and connectivity data for the GNSS/IMU FPGA PCB.
Generated KiCad, BOM, pinout, and assembly data import this module. Ti375 ball
data comes from the Efinity 2025.2 C529 resource DB, never a hand transcription.
"""
from __future__ import annotations
import csv
from dataclasses import dataclass, field
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
@dataclass
class Pin:
number: str
name: str
net: str | None
etype: str = "passive"
@dataclass
class Component:
ref: str
value: str
mpn: str
manufacturer: str
footprint: str
pins: list[Pin]
description: str = ""
assembly: str = "SMT"
dnp: bool = False
fields: dict[str, str] = field(default_factory=dict)
def p(number: str | int, name: str, net: str | None, etype: str = "passive") -> Pin:
return Pin(str(number), name, net, etype)
components: list[Component] = []
_refs = {"R": 0, "C": 0, "L": 0, "FB": 0, "TP": 0}
def add(component: Component) -> Component:
assert not any(c.ref == component.ref for c in components), component.ref
components.append(component)
return component
def two_pin(prefix: str, value: str, mpn: str, manufacturer: str,
footprint: str, net1: str, net2: str, description: str,
*, dnp: bool = False, ref: str | None = None) -> Component:
if ref is None:
_refs[prefix] += 1
ref = f"{prefix}{_refs[prefix]}"
return add(Component(ref, value, mpn, manufacturer, footprint,
[p(1, "1", net1), p(2, "2", net2)],
description, dnp=dnp))
def r(value: str, net1: str, net2: str, *, mpn: str = "",
description: str = "Resistor", dnp: bool = False) -> Component:
values = {
"0R": "RC0402JR-070RL", "22R": "RC0402FR-0722RL",
"27R": "RC0402FR-0727RL", "33R": "RC0402FR-0733RL",
"825R": "RC0402FR-07825RL", "1K": "RC0402FR-071KL", "1.24K": "RC0402FR-071K24L",
"2.1K": "RC0402FR-072K1L", "2.2K": "RC0402FR-072K2L",
"4.7K": "RC0402FR-074K7L", "10K": "RC0402FR-0710KL",
"12K": "RC0402FR-0712KL", "15K": "RC0402FR-0715KL",
"22.1K": "RC0402FR-0722K1L", "100K": "RC0402FR-07100KL",
"133K": "RC0402FR-07133KL", "205K": "RC0402FR-07205KL",
"232K": "RC0402FR-07232KL", "383K": "RC0402FR-07383KL",
"442K": "RC0402FR-07442KL", "900K": "RC0402FR-07909KL",
"1M": "RC0402FR-071ML",
}
return two_pin("R", value, mpn or values.get(value, ""), "Yageo",
"Resistor_SMD:R_0402_1005Metric", net1, net2, description,
dnp=dnp)
def c(value: str, net1: str, net2: str, *, mpn: str = "",
footprint: str = "Capacitor_SMD:C_0402_1005Metric",
description: str = "MLCC", dnp: bool = False) -> Component:
values = {
"10pF": "GRM1555C1H100JA01D", "15pF": "GRM1555C1H150JA01D",
"27pF": "600L270JT200T", "750pF": "GRM1555C1H751JA01D",
"1.8nF": "GRM155R71H182KA01D", "4.7nF": "GRM155R71H472KA01D",
"10nF": "GRM155R71H103KA88D", "100nF": "GRM155R71A104KA01D",
"470nF": "GCM155C71A474KE36D", "1uF": "GRM155R60J105KE19D",
"10uF": "GRM188R60J106ME47D", "22uF": "GRM21BZ70J226ME44L",
"47uF": "GRM32ER70J476ME19L", "100uF": "GRT32EC70J107ME13L",
}
return two_pin("C", value, mpn or values.get(value, ""), "Murata",
footprint, net1, net2, description, dnp=dnp)
def tp(net: str, label: str) -> Component:
_refs["TP"] += 1
return add(Component(f"TP{_refs['TP']}", label, "", "",
"TestPoint:TestPoint_Pad_D1.0mm",
[p(1, label, net)], "Local labelled SMT bring-up test pad", "SMT"))
def add_ic(ref: str, value: str, mpn: str, manufacturer: str, footprint: str,
pins: list[Pin], description: str) -> Component:
return add(Component(ref, value, mpn, manufacturer, footprint, pins, description))
# Ti375 C529: represent every package ball exactly once.
pinout = {row["ball"]: row for row in csv.DictReader(
(ROOT / "docs" / "ti375_pinout.csv").open(newline=""))}
package = list(csv.DictReader((ROOT / "docs" / "ti375_c529_package.csv").open(newline="")))
def fpga_net(row: dict[str, str]) -> str | None:
ball = row["ball"]
if ball in pinout:
return pinout[ball]["signal"]
name = row["official_pad_name"]
if name in ("GND", "VQPS"):
return "GND"
if name == "VCC" or name.startswith("VCCA_") or name == "VDD_SOC":
return "VCC0V95"
if name == "VCCAUX":
return "VCC1V8"
vccio = {
"VCCIO4C": "VCC1V8", "VCCIO4A_4B": "VCC1V8", "VCCIO4D": "VCC1V8",
"VCCIO2A": "VCC1V8", "VCCIO2B": "VCC1V8", "VCCIO2C": "VCC1V8",
"VCCIO2D": "VCC1V8", "VCCIO2E": "VCC1V8",
"VCCIO33_BR0": "VCC3V0_DIG", "VCCIO33_TR0": "VCC3V0_DIG",
"VCCIO33_TR1": "VCC3V3", "VCCIO33_TR2": "VCC3V3",
"VCCIO33_BR3_BR4": "VCC3V3", "VCCIO33_TL1_TL5": "VCC3V3",
"VCCIO33_BL2_BL3": "VCC3V3",
}
if name in vccio:
return vccio[name]
if name.startswith("REF_RES_"):
return name
# JTAG_VCCIO_SEL floats for 3.3-V BR3/BR4. Official NC, unused DDR
# signals, and unused DDR rails remain open per Efinix's unused table.
return None
fpga_pins = []
for row in package:
etype = {"power": "power_in", "input": "input", "output": "output"}.get(
row["direction"], "bidirectional")
fpga_pins.append(p(row["ball"], row["official_pad_name"], fpga_net(row), etype))
add_ic("U1", "Ti375C529C3", "Ti375C529C3", "Efinix",
"Package_BGA:BGA-529_19x19mm_Layout23x23_P0.8mm", fpga_pins,
"Titanium Ti375 FPGA, commercial standard-power 0.95-V option, C529")
for name in ("REF_RES_4A", "REF_RES_4B", "REF_RES_4D", "REF_RES_2E", "REF_RES_2A"):
r("10K", name, "GND", description="Efinix mandatory 10-kohm 1% reference resistor")
# USB-C and sink role detection.
add_ic("J1", "USB-C USB2 sink", "USB4105-GF-A-120", "GCT",
"Connector_USB:USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal", [
p("A1", "GND", "GND"), p("A4", "VBUS", "VBUS_RAW"), p("A5", "CC1", "USB_CC1"),
p("A6", "D+", "USB_D_P"), p("A7", "D-", "USB_D_N"), p("A8", "SBU1", None),
p("A9", "VBUS", "VBUS_RAW"), p("A12", "GND", "GND"),
p("B1", "GND", "GND"), p("B4", "VBUS", "VBUS_RAW"), p("B5", "CC2", "USB_CC2"),
p("B6", "D+", "USB_D_P"), p("B7", "D-", "USB_D_N"), p("B8", "SBU2", None),
p("B9", "VBUS", "VBUS_RAW"), p("B12", "GND", "GND"), p("SH", "SHIELD", "USB_SHIELD")],
"USB 2.0 Type-C receptacle, UFP/sink")
add_ic("U5", "TUSB320LAI", "TUSB320LAIRSW(R)", "Texas Instruments",
"Package_DFN_QFN:Texas_X2QFN-12_1.6x1.6mm_P0.4mm", [
p(1, "CC1", "USB_CC1", "bidirectional"), p(2, "CC2", "USB_CC2", "bidirectional"),
p(3, "PORT", "GND", "input"), p(4, "VBUS_DET", "USB_VBUS_DET", "input"),
p(5, "ADDR", None), p(6, "OUT3", None, "output"),
p(7, "OUT1", "TUSB_OUT1", "output"), p(8, "OUT2", "TUSB_OUT2", "output"),
p(9, "ID", None, "output"), p(10, "GND", "GND", "power_in"),
p(11, "EN_N", "GND", "input"), p(12, "VDD", "VCC3V3_AUX", "power_in")],
"USB Type-C port controller in UFP GPIO mode; integrated Rd on CC1/CC2")
r("900K", "VBUS_RAW", "USB_VBUS_DET", description="TUSB320 VBUS_DET current limiter")
r("10K", "VCC3V3_AUX", "TUSB_OUT1", description="TUSB320 open-drain OUT1 pull-up")
r("10K", "VCC3V3_AUX", "TUSB_OUT2", description="TUSB320 open-drain OUT2 pull-up")
c("100nF", "VCC3V3_AUX", "GND", description="TUSB320 local bypass")
add_ic("U6", "TPD2EUSB30A", "TPD2EUSB30ADRTR", "Texas Instruments",
"Package_TO_SOT_SMD:Texas_DRT-3", [p(1, "IO1", "USB_D_P"), p(2, "GND", "GND"), p(3, "IO2", "USB_D_N")],
"Low-capacitance USB 2.0 ESD protection")
c("4.7nF", "USB_SHIELD", "GND", description="USB shield HF coupling")
r("1M", "USB_SHIELD", "GND", description="USB shield bleed")
r("0R", "USB_SHIELD", "GND", description="Optional direct shield bond", dnp=True)
add_ic("J3", "EXT_5V", "1935161", "Phoenix Contact",
"TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2_1x02_P5.00mm_Horizontal", [
p(1, "EXT_5V", "EXT5_RAW", "power_in"), p(2, "GND", "GND", "power_in")],
"Clearly labelled external regulated 5-V input")
components[-1].assembly = "THT"
def add_efuse(ref: str, input_net: str, output_net: str, ilim: str) -> None:
add_ic(ref, "TPS259470A", "TPS259470ARPWR", "Texas Instruments",
"board:Texas_RPW0010A_QFN10", [
p(1, "EN/UVLO", f"{ref}_ENUV", "input"), p(2, "OVLO", f"{ref}_OV", "input"),
p(3, "AUXOFF", None, "open_collector"), p(4, "FLT", f"{ref}_FLT", "open_collector"),
p(5, "IN", input_net, "power_in"), p(6, "OUT", output_net, "power_out"),
p(7, "DVDT", f"{ref}_DVDT", "output"), p(8, "GND", "GND", "power_in"),
p(9, "ILM", f"{ref}_ILM", "output"), p(10, "ITIMER", None)],
"True-reverse-blocking eFuse; UV/OV/current/inrush protection")
r("232K", input_net, f"{ref}_ENUV", description="4.0-V nominal eFuse UV divider top")
r("100K", f"{ref}_ENUV", "GND", description="eFuse UV divider bottom")
r("383K", input_net, f"{ref}_OV", description="5.8-V nominal eFuse OV divider top")
r("100K", f"{ref}_OV", "GND", description="eFuse OV divider bottom")
r(ilim, f"{ref}_ILM", "GND", description="eFuse RILM=3334/ILIM")
c("4.7nF", f"{ref}_DVDT", "GND", description="eFuse output slew control")
c("1uF", input_net, "GND", description="eFuse local input bypass")
c("10uF", output_net, "GND", description="eFuse local output bypass")
tp(f"{ref}_FLT", f"{ref} FLT")
add_efuse("U7", "VBUS_RAW", "USB5_PROT", "1.24K") # 2.69 A typical, <3 A max.
add_efuse("U8", "EXT5_RAW", "EXT5_PROT", "825R") # 4.04 A typical.
add_ic("U9", "TPS2121", "TPS2121RUXR", "Texas Instruments", "board:Texas_RUX0012A_VQFN12", [
p(1, "OUT", "SYS5", "power_out"), p(2, "IN2", "EXT5_PROT", "power_in"), p(3, "CP2", "VCC3V3_AUX", "input"),
p(4, "OV2", "MUX_OV2", "input"), p(5, "OV1", "MUX_OV1", "input"), p(6, "PR1", "GND", "input"),
p(7, "IN1", "USB5_PROT", "power_in"), p(8, "OUT", "SYS5", "power_out"), p(9, "ST", "MUX_ST", "output"),
p(10, "ILM", "MUX_ILM", "output"), p(11, "SS", "MUX_SS", "output"), p(12, "GND", "GND", "power_in")],
"4.5-A power mux; external input wins near-equal valid 5-V inputs")
r("442K", "USB5_PROT", "MUX_OV1", description="TPS2121 IN1 5.75-V OV divider top")
r("100K", "MUX_OV1", "GND", description="TPS2121 IN1 OV divider bottom")
r("442K", "EXT5_PROT", "MUX_OV2", description="TPS2121 IN2 5.75-V OV divider top")
r("100K", "MUX_OV2", "GND", description="TPS2121 IN2 OV divider bottom")
r("22.1K", "MUX_ILM", "GND", description="TPS2121 4.5-A typical output limit")
c("10nF", "MUX_SS", "GND", description="TPS2121 soft-start")
for _ in range(2):
c("47uF", "SYS5", "GND", footprint="Capacitor_SMD:C_1210_3225Metric", description="5-V system bulk")
add_ic("U10", "SN74LVC2G02", "SN74LVC2G02DCUR", "Texas Instruments",
"Package_SO:VSSOP-8_2.3x2mm_P0.5mm", [
p(1, "1Y", "USB_3A_OK", "output"), p(2, "1A", "TUSB_OUT1", "input"), p(3, "1B", "TUSB_OUT2", "input"),
p(4, "GND", "GND", "power_in"), p(5, "2A", "MUX_ST", "input"), p(6, "2B", "MUX_ST", "input"),
p(7, "2Y", "EXT_SELECTED", "output"), p(8, "VCC", "VCC3V3_AUX", "power_in")],
"USB 3-A advertisement NOR decode and mux-status inversion")
add_ic("U11", "SN74LVC1G32", "SN74LVC1G32DBVR", "Texas Instruments", "Package_TO_SOT_SMD:SOT-23-5", [
p(1, "A", "USB_3A_OK", "input"), p(2, "B", "EXT_SELECTED", "input"), p(3, "GND", "GND", "power_in"),
p(4, "Y", "MAIN_EN", "output"), p(5, "VCC", "VCC3V3_AUX", "power_in")],
"Main-rail enable OR: qualified 3-A USB or selected external 5 V")
c("100nF", "VCC3V3_AUX", "GND", description="Power-qualification logic local bypass")
c("100nF", "VCC3V3_AUX", "GND", description="Power qualification logic bypass")
# Rail generation and Efinix sequencing.
def add_ldo(ref: str, value: str, mpn: str, vin: str, vout: str, enable: str, desc: str) -> None:
add_ic(ref, value, mpn, "Texas Instruments", "Package_TO_SOT_SMD:SOT-23-5", [
p(1, "IN", vin, "power_in"), p(2, "GND", "GND", "power_in"), p(3, "EN", enable, "input"),
p(4, "NC", None), p(5, "OUT", vout, "power_out")], desc)
c("1uF", vin, "GND", description=f"{ref} local input capacitor")
c("1uF", vout, "GND", description=f"{ref} low-noise LDO output capacitor")
add_ldo("U12", "TPS7A2033", "TPS7A2033PDBVR", "SYS5", "VCC3V3_AUX", "SYS5",
"Always-on 300-mA auxiliary 3.3-V LDO")
add_ic("U13", "TPS6287B15HA0", "TPS6287B15HA0WRZVR", "Texas Instruments",
"board:Texas_RZV0024A_WQFN24", [
p(1, "VOSNS", "CORE_SENSE_P", "input"), p(2, "EN", "CORE_EN_R", "input"),
p(3, "VSET1", "GND", "input"), p(4, "VSET2", "SYS5", "input"),
p(5, "VIN", "SYS5", "power_in"), p(6, "VIN", "SYS5", "power_in"),
p(7, "GND", "GND", "power_in"), p(8, "GND", "GND", "power_in"),
p(9, "SW", "CORE_SW", "power_out"), p(10, "SW", "CORE_SW", "power_out"),
p(11, "SW", "CORE_SW", "power_out"), p(12, "SW", "CORE_SW", "power_out"),
p(13, "GND", "GND", "power_in"), p(14, "GND", "GND", "power_in"),
p(15, "VIN", "SYS5", "power_in"), p(16, "VIN", "SYS5", "power_in"),
p(17, "SYNCOUT", None, "output"), p(18, "MODE/SYNC", "SYS5", "input"),
p(19, "SDA", "GND", "bidirectional"), p(20, "SCL", "GND", "bidirectional"),
p(21, "PG", "CORE_PG", "open_collector"), p(22, "AGND", "GND", "power_in"),
p(23, "COMP", "CORE_COMP", "input"), p(24, "GOSNS", "CORE_SENSE_N", "input"),
p(25, "THERMAL_PADS", "GND", "power_in")],
"15-A synchronous 0.95-V FPGA core/VCCA/VDD_SOC buck, fixed PWM")
r("15K", "MAIN_EN", "CORE_EN_R", description="TPS6287B mandatory >=15-kohm EN series resistor")
r("0R", "VCC0V95", "CORE_SENSE_P", description="Remote positive sense link at FPGA PDN")
r("0R", "GND", "CORE_SENSE_N", description="Remote ground sense link at FPGA PDN")
r("2.1K", "CORE_COMP", "CORE_COMP_RC", description="TPS6287B calculated compensation RZ")
c("1.8nF", "CORE_COMP_RC", "GND", description="TPS6287B calculated compensation CC")
c("10pF", "CORE_COMP", "GND", description="TPS6287B HF compensation bypass CC2")
two_pin("L", "100nH", "XEL4030-101MEC", "Coilcraft", "board:Coilcraft_XEL4030",
"CORE_SW", "VCC0V95", "Core inductor: 30-A saturation, 1.5-mohm DCR")
for _ in range(4):
c("22uF", "SYS5", "GND", mpn="GCM31CR71A226KE02L",
footprint="Capacitor_SMD:C_1206_3216Metric", description="Core buck butterfly input capacitor")
for _ in range(4):
c("100uF", "VCC0V95", "GND", footprint="Capacitor_SMD:C_1210_3225Metric",
description="Core output; 400-uF nominal, >=195-uF effective target")
r("10K", "VCC3V3_AUX", "CORE_PG", description="Core buck PG pull-up")
def add_tps62867(ref: str, output: str, sw: str, en: str, pg: str, vset: str, value: str) -> None:
add_ic(ref, f"TPS62867 {value}", "TPS62867RQYR", "Texas Instruments",
"board:Texas_RQY0009A_VQFN9", [
p(1, "AGND", "GND", "power_in"), p(2, "PGND", "GND", "power_in"),
p(3, "VIN", "SYS5", "power_in"), p(4, "EN", en, "input"),
p(5, "PG", pg, "open_collector"), p(6, "VSET/MODE", f"{ref}_VSET", "input"),
p(7, "SW", sw, "power_out"), p(8, "VOS", output, "input"), p(9, "FB", output, "input")],
f"6-A synchronous {value} FPGA auxiliary/I/O buck")
r(vset, f"{ref}_VSET", "GND", description=f"TPS62867 fixed {value} VSET resistor")
two_pin("L", "240nH", "DFE201612E-R24M=P2", "Murata", "board:Murata_DFE201612E",
sw, output, "TPS62867 manufacturer-recommended 6.6-A inductor")
for _ in range(2):
c("10uF", "SYS5", "GND", description=f"{ref} input capacitor")
for _ in range(2):
c("22uF", output, "GND", footprint="Capacitor_SMD:C_0805_2012Metric",
description=f"{ref} output capacitor")
add_tps62867("U14", "VCC1V8", "BUCK1V8_SW", "SEQ_FLAG1", "PG_1V8", "133K", "1.8 V")
add_tps62867("U15", "VCC3V3", "BUCK3V3_SW", "SEQ_FLAG2", "PG_3V3", "205K", "3.3 V")
add_ic("U16", "LM3880-1AA", "LM3880MF-1AA/NOPB", "Texas Instruments",
"Package_TO_SOT_SMD:SOT-23-6", [
p(1, "VCC", "VCC3V3_AUX", "power_in"), p(2, "GND", "GND", "power_in"),
p(3, "EN", "CORE_PG", "input"), p(4, "FLAG3", "SEQ_FLAG3", "open_collector"),
p(5, "FLAG2", "SEQ_FLAG2", "open_collector"), p(6, "FLAG1", "SEQ_FLAG1", "open_collector")],
"Three-rail sequencer, 10-ms delay per rail")
for net in ("SEQ_FLAG1", "SEQ_FLAG2"):
r("10K", "VCC3V3_AUX", net, description="LM3880 open-drain flag pull-up")
c("100nF", "VCC3V3_AUX", "GND", description="LM3880 bypass")
# Keep the four independent rail-valid indications separate from CRESET_N.
# They are combined below by always-on logic; this prevents an intentional
# configuration reset from corrupting the power-good state used to isolate the
# FTDI drivers.
r("10K", "VCC1V8", "FPGA_CRESET_N", description="FPGA CRESET_N configuration-rail pull-up")
r("0R", "SEQ_FLAG3", "PWR_SEQ_OK", description="Sequencer final-flag sense link")
r("0R", "PG_1V8", "PWR_1V8_OK", description="1.8-V buck power-good sense link")
r("0R", "PG_3V3", "PWR_3V3_OK", description="3.3-V buck power-good sense link")
add_ldo("U17", "TPS7A2030", "TPS7A2030PDBVR", "VCC3V3", "VCC3V0_RF", "VCC3V3",
"Dedicated low-noise 3.0-V MAX2771 analog LDO")
add_ldo("U18", "TPS7A2030", "TPS7A2030PDBVR", "VCC3V3", "VCC3V0_DIG", "VCC3V3",
"Dedicated low-noise 3.0-V MAX2771 digital and FPGA-bank LDO")
add_ldo("U19", "TPS7A2030", "TPS7A2030PDBVR", "VCC3V3", "VCC3V0_CLK", "VCC3V3",
"Dedicated low-noise 3.0-V oscillator LDO")
add_ldo("U20", "TPS7A2033", "TPS7A2033PDBVR", "SYS5", "VCC3V3_ANT", "MAIN_EN",
"Clean 3.3-V active-antenna bias LDO")
# The supervisor is powered from the always-on auxiliary rail, so its open-drain
# output remains authoritative even while the monitored 3.0-V FPGA I/O rail is
# absent. Its 20-ms (CT open) timeout also guarantees the configuration delay
# after the last FPGA supply becomes valid. Power-good is kept separate from
# user/FTDI reset so the JTAG buffers stay enabled during deliberate resets.
add_ic("U28", "TPS3808G30", "TPS3808G30DBVR", "Texas Instruments",
"Package_TO_SOT_SMD:SOT-23-6", [
p(1, "RESET_N", "FPGA_POWER_OK_N", "open_collector"), p(2, "GND", "GND", "power_in"),
p(3, "MR_N", "VCC3V3_AUX", "input"), p(4, "CT", None, "input"),
p(5, "SENSE", "VCC3V0_DIG", "input"), p(6, "VDD", "VCC3V3_AUX", "power_in")],
"3.0-V FPGA-bank supervisor; open-drain reset with 20-ms release delay")
# Always-on AND tree creates a true all-rail-valid signal. The fourth gate
# includes LM3880 FLAG3, so both the programmed power-up interval and every
# converter/supervisor indication must be valid before FPGA-facing FTDI
# drivers are enabled or CRESET_N is released.
add_ic("U34", "SN74LVC08A", "SN74LVC08APWR", "Texas Instruments",
"Package_SO:TSSOP-14_4.4x5mm_P0.65mm", [
p(1, "1A", "CORE_PG", "input"), p(2, "1B", "PWR_1V8_OK", "input"),
p(3, "1Y", "PWR_OK_A", "output"),
p(4, "2A", "PWR_3V3_OK", "input"), p(5, "2B", "FPGA_POWER_OK_N", "input"),
p(6, "2Y", "PWR_OK_B", "output"), p(7, "GND", "GND", "power_in"),
p(8, "3Y", "PWR_OK_C", "output"), p(9, "3A", "PWR_OK_A", "input"),
p(10, "3B", "PWR_OK_B", "input"),
p(11, "4Y", "ALL_FPGA_POWER_OK", "output"),
p(12, "4A", "PWR_OK_C", "input"), p(13, "4B", "PWR_SEQ_OK", "input"),
p(14, "VCC", "VCC3V3_AUX", "power_in")],
"Always-on all-rail power-valid logic for reset release and debug isolation")
# Dense reference-design-style bypassing plus one bulk group per rail.
for _ in range(18):
c("100nF", "VCC0V95", "GND", description="Ti375 core/VCCA/VDD_SOC HF bypass")
for _ in range(4):
c("10uF", "VCC0V95", "GND", description="Ti375 core local bulk")
for _ in range(6):
c("100nF", "VCC1V8", "GND", description="Ti375 VCCAUX/HSIO/config bypass")
for _ in range(2):
c("10uF", "VCC1V8", "GND", description="Ti375 1.8-V local bulk")
for rail in ("VCC3V3", "VCC3V0_DIG"):
for _ in range(4):
c("100nF", rail, "GND", description=f"Ti375 HVIO bank bypass on {rail}")
c("10uF", rail, "GND", description=f"Ti375 I/O-rail bulk on {rail}")
# MAX2771 direct MIXIN_HI path and OFF/3.3-V/5-V active-antenna bias.
add_ic("J2", "GPS L1 SMA", "132289", "Amphenol RF",
"Connector_Coaxial:SMA_Amphenol_132289_EdgeMount",
[p(1, "RF", "RF_SMA"), p(2, "GND", "GND")],
"Edge-launch 50-ohm active GNSS antenna input")
components[-1].assembly = "THT"
add_ic("D1", "ESD501", "ESD501DPYR", "Texas Instruments", "board:Texas_DPY0002A_X1SON",
[p(1, "RF", "RF_SMA"), p(2, "GND", "GND")],
"0.3-pF bidirectional 12-V RF ESD protector")
c("27pF", "RF_SMA", "RF_PRE_SAW", mpn="600L270JT200T", footprint="board:Kyocera_600L_0402",
description="High-Q 200-V C0G GNSS RF DC block")
add_ic("FL1", "GPS L1/L1C SAW", "SF14-1582M5UUS2", "Kyocera", "board:Kyocera_SF14_5pad", [
p(1, "IN", "RF_PRE_SAW"), p(2, "GND", "GND"), p(3, "GND", "GND"),
p(4, "OUT", "RF_POST_SAW"), p(5, "GND", "GND")],
"1565.19-1585.65-MHz SAW, 1.2-dB typical insertion loss")
c("DNP", "RF_POST_SAW", "GND", mpn="", description="RF tuning shunt C1", dnp=True)
r("0R", "RF_POST_SAW", "MAX_MIXIN_HI", description="RF tuning series link")
c("DNP", "MAX_MIXIN_HI", "GND", mpn="", description="RF tuning shunt C2", dnp=True)
add_ic("J5", "ANT BIAS OFF/3V3/5V", "61300311121", "Wuerth Elektronik",
"Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical", [
p(1, "3V3", "VCC3V3_ANT", "power_in"), p(2, "SELECT", "ANT_BIAS_SEL", "power_in"),
p(3, "5V", "SYS5", "power_in")],
"One-shunt hardware antenna bias selection; no shunt is OFF")
components[-1].assembly = "THT"
add_ic("U27", "TPS22944", "TPS22944DCKR", "Texas Instruments",
"Package_TO_SOT_SMD:SOT-353_SC-70-5", [
p(1, "VOUT", "ANT_BIAS_SW", "power_out"), p(2, "GND", "GND", "power_in"),
p(3, "OC", "ANT_BIAS_OC", "open_collector"), p(4, "ON", "ANT_BIAS_ON", "input"),
p(5, "VIN", "ANT_BIAS_SEL", "power_in")],
"Fixed 100-mA current-limited active-antenna load switch")
r("100K", "ANT_BIAS_SEL", "ANT_BIAS_ON", description="Bias switch follows selected source")
r("100K", "ANT_BIAS_ON", "GND", description="No-jumper default OFF")
r("10K", "VCC3V3_AUX", "ANT_BIAS_OC", description="Antenna overcurrent pull-up")
c("1uF", "ANT_BIAS_SEL", "GND", description="Bias-switch input bypass")
c("1uF", "ANT_BIAS_SW", "GND", description="Bias-switch output bypass before choke")
two_pin("L", "56nH", "0402HPH-56NXJRW", "Coilcraft", "Inductor_SMD:L_0402_1005Metric",
"ANT_BIAS_SW", "RF_SMA", "L1 bias-tee choke: SRF 2.1 GHz, Q 64, 330 mA")
tp("ANT_BIAS_OC", "ANT OC")
add_ic("U21", "MAX2771", "MAX2771ETI+", "Analog Devices",
"Package_DFN_QFN:QFN-28-1EP_5x5mm_P0.5mm_EP3.25x3.25mm", [
p(1, "CPOUT", "MAX_CPOUT", "output"), p(2, "VCCCP", "VCC3V0_RF", "power_in"),
p(3, "XTAL", "MAX_XTAL", "input"), p(4, "CLKOUT", "MAX_CLKOUT_SRC", "output"),
p(5, "ADC_CLKIN", "MAX_ADC_CLKIN", "input"), p(6, "VCCD", "VCC3V0_DIG", "power_in"),
p(7, "SDATA", "MAX_SDATA_DEV", "bidirectional"), p(8, "SCLK", "MAX_SCLK_DEV", "input"),
p(9, "CSN", "MAX_CSN_DEV", "input"), p(10, "Q1", "MAX_Q1_DEV", "output"),
p(11, "Q0", "MAX_Q0_DEV", "output"), p(12, "I0", "MAX_I0_DEV", "output"),
p(13, "I1", "MAX_I1_DEV", "output"), p(14, "VCCADC", "VCC3V0_DIG", "power_in"),
p(15, "ANAIPOUT", None, "output"), p(16, "ANAINOUT", None, "output"),
p(17, "LD", "MAX_LD_DEV", "output"), p(18, "SHDN", "MAX_SHDN_DEV", "input"),
p(19, "LNAIN_HI", None, "input"), p(20, "LNAIN_LO", None, "input"), p(21, "NC", None),
p(22, "VCCRF", "VCC3V0_RF", "power_in"), p(23, "LNAOUT_LO", None, "output"),
p(24, "LNAOUT_HI", None, "output"), p(25, "MIXIN_HI", "MAX_MIXIN_HI", "input"),
p(26, "MIXIN_LO", None, "input"), p(27, "VCCIF", "VCC3V0_RF", "power_in"),
p(28, "VCCVCO", "VCC3V0_RF", "power_in"), p(29, "EP", "GND", "power_in")],
"GNSS receiver; both LNAs disabled; direct high-band mixer input")
# Official typical topology: CPOUT has a small direct capacitor and an R-C
# series integrating branch to ground.
c("15pF", "MAX_CPOUT", "GND", description="MAX2771 PLL loop-filter shunt capacitor")
r("15K", "MAX_CPOUT", "MAX_LOOP_RC", description="MAX2771 PLL loop-filter resistor")
c("750pF", "MAX_LOOP_RC", "GND", description="MAX2771 PLL loop integrating capacitor")
c("10nF", "TCXO16_OUT", "MAX_XTAL", description="MAX2771 external reference AC coupling")
r("33R", "OSC40_OUT", "MAX_ADC_CLKIN", description="40-MHz ADC-clock source termination")
for signal in ("MAX_CLKOUT", "MAX_I0", "MAX_I1", "MAX_Q0", "MAX_Q1", "MAX_LD"):
source = f"{signal}_SRC" if signal == "MAX_CLKOUT" else f"{signal}_DEV"
r("22R", source, signal, description="MAX2771 source-series digital damping")
for signal in ("MAX_SCLK", "MAX_CSN", "MAX_SDATA", "MAX_SHDN"):
r("22R", signal, f"{signal}_DEV", description="MAX2771 serial/control source damping")
r("100K", "MAX_SHDN_DEV", "GND", description="MAX2771 remains shut down before FPGA configuration")
for rail, count in (("VCC3V0_RF", 5), ("VCC3V0_DIG", 2)):
for _ in range(count):
c("100nF", rail, "GND", description=f"MAX2771 local {rail} bypass")
c("1uF", rail, "GND", description=f"MAX2771 local {rail} bulk")
add_ic("U23", "16.368 MHz TCXO", "X1G005441030125", "Epson", "board:Epson_TG2016SMN", [
p(1, "NC", None), p(2, "GND", "GND", "power_in"), p(3, "OUT", "TCXO16_OUT", "output"),
p(4, "VCC", "VCC3V0_CLK", "power_in")],
"TG2016SMN clipped-sine ±0.5-ppm MAX2771 reference")
c("100nF", "VCC3V0_CLK", "GND", description="16.368-MHz TCXO bypass")
c("1uF", "VCC3V0_CLK", "GND", description="Clock-rail local bulk")
add_ic("U24", "40.000 MHz XO", "X1G004171041100", "Epson",
"Oscillator:Oscillator_SMD_SeikoEpson_SG210-4Pin_2.5x2.0mm", [
p(1, "ST", "VCC3V0_CLK", "input"), p(2, "GND", "GND", "power_in"),
p(3, "OUT", "OSC40_OUT", "output"), p(4, "VCC", "VCC3V0_CLK", "power_in")],
"SG-210STF ±25-ppm CMOS FPGA/system ADC oscillator")
r("33R", "OSC40_OUT", "CLK_40M_FPGA", description="40-MHz FPGA-clock source termination")
c("100nF", "VCC3V0_CLK", "GND", description="40-MHz oscillator bypass")
# Exact Murata SCH16T-K01-1 SafeSPI pinout and carrier-board capacitors.
add_ic("U22", "SCH16T-K01-1", "SCH16T-K01-1", "Murata", "board:Murata_SCH16T_K01", [
p(1, "HS_GND", "GND", "power_in"), p(2, "RESERVED", None),
p(3, "TA9_GND", "GND", "power_in"), p(4, "TA8_GND", "GND", "power_in"),
p(5, "RESERVED_GND", "GND", "power_in"), p(6, "EXTRESN", "IMU_EXTRESN", "input"),
p(7, "RESERVED_GND", "GND", "power_in"), p(8, "V3P3", "IMU_3V3", "power_in"),
p(9, "GND", "GND", "power_in"), p(10, "VREGA2", "IMU_VREGA2", "power_out"),
p(11, "VREGA", "IMU_VREGA2", "power_out"), p(12, "GND", "GND", "power_in"),
p(13, "GND", "GND", "power_in"), p(14, "VREGD2", "IMU_VREGD", "power_out"),
p(15, "VREGD", "IMU_VREGD", "power_out"), p(16, "GND", "GND", "power_in"),
p(17, "V3P3", "IMU_3V3", "power_in"), p(18, "VDDIO", "VCC3V3", "power_in"),
p(19, "MISO", "IMU_MISO_DEV", "output"), p(20, "DRY_SYNC", "IMU_DRY_SYNC", "bidirectional"),
p(21, "SCK", "IMU_SCK_DEV", "input"), p(22, "CS", "IMU_CS_DEV", "input"),
p(23, "MOSI", "IMU_MOSI_DEV", "input"), p(24, "HS_GND", "GND", "power_in")],
"Six-DoF SafeSPI IMU, exact K01-1 variant")
two_pin("FB", "600R@100MHz", "BLM15AG601SN1D", "Murata", "Inductor_SMD:L_0402_1005Metric",
"VCC3V3", "IMU_3V3", "IMU supply ferrite isolation")
for signal in ("IMU_SCK", "IMU_MOSI", "IMU_CS"):
r("22R", signal, f"{signal}_DEV", description="SafeSPI source-series damping")
r("22R", "IMU_MISO_DEV", "IMU_MISO", description="SafeSPI MISO source damping at IMU")
r("10K", "IMU_EXTRESN", "GND", description="Hold IMU reset until FPGA releases it")
c("100nF", "IMU_3V3", "GND", description="Murata V3P3 HF bypass")
c("1uF", "IMU_3V3", "GND", description="Murata V3P3 local capacitor")
c("10uF", "IMU_VREGA2", "GND", description="Murata required VREGA2 capacitor")
c("1uF", "IMU_VREGD", "GND", description="Murata required VREGD capacitor")
c("100nF", "VCC3V3", "GND", description="Murata VDDIO bypass")
# FT4232H follows the Efinix development-kit interface order:
# A/interface 0 = RISC-V JTAG, B/interface 1 = FPGA hard JTAG.
ftdi_pins = [
p(1, "EECS", "FTDI_EECS", "output"), p(2, "VCORE", "FTDI_1V8", "power_in"),
p(3, "OSCI", "FTDI_OSCI", "input"), p(4, "OSCO", None, "output"),
p(5, "VPHY", "FTDI_VPHY", "power_in"), p(6, "REF", "FTDI_REF", "input"),
p(7, "DM", "FTDI_D_N", "bidirectional"), p(8, "DP", "FTDI_D_P", "bidirectional"),
p(9, "VPLL", "FTDI_VPLL", "power_in"), p(10, "TEST", "GND", "input"),
p(11, "RESET_N", "FTDI_RESET_N", "input"),
p(12, "ADBUS0/TCK", "FTDI_A_TCK_SRC", "bidirectional"),
p(13, "ADBUS1/TDI", "FTDI_A_TDI_SRC", "bidirectional"),
p(14, "ADBUS2/TDO", "FTDI_A_TDO_SRC", "bidirectional"),
p(15, "ADBUS3/TMS", "FTDI_A_TMS_SRC", "bidirectional"),
p(16, "VCCIO", "VCC3V3_AUX", "power_in"),
p(17, "ADBUS4", None, "bidirectional"), p(18, "ADBUS5", None, "bidirectional"),
p(19, "ADBUS6", None, "bidirectional"), p(20, "ADBUS7", None, "bidirectional"),
p(21, "GND", "GND", "power_in"),
p(22, "BDBUS0/TCK", "FTDI_B_TCK_SRC", "bidirectional"),
p(23, "BDBUS1/TDI", "FTDI_B_TDI_SRC", "bidirectional"),
p(24, "BDBUS2/TDO", "FTDI_B_TDO_SRC", "bidirectional"),
p(25, "BDBUS3/TMS", "FTDI_B_TMS_SRC", "bidirectional"),
p(26, "BDBUS4", "FTDI_B_RESET_N_SRC", "bidirectional"), p(27, "BDBUS5", None, "bidirectional"),
p(28, "BDBUS6", None, "bidirectional"), p(29, "BDBUS7", None, "bidirectional"),
p(30, "SUSPEND_N", None, "output"), p(31, "VCORE", "FTDI_1V8", "power_in"),
p(32, "CDBUS0/TXD", "FTDI_C_TXD_SRC", "bidirectional"),
p(33, "CDBUS1/RXD", "FTDI_C_RXD_SRC", "bidirectional"),
p(34, "CDBUS2", None, "bidirectional"), p(35, "CDBUS3", None, "bidirectional"),
p(36, "VCCIO", "VCC3V3_AUX", "power_in"),
p(37, "CDBUS4", None, "bidirectional"), p(38, "CDBUS5", None, "bidirectional"),
p(39, "CDBUS6", None, "bidirectional"), p(40, "CDBUS7", None, "bidirectional"),
p(41, "GND", "GND", "power_in"),
p(42, "DDBUS0/TXD", "FTDI_D_TXD_SRC", "bidirectional"),
p(43, "VREGOUT", "FTDI_1V8", "power_out"), p(44, "VREGIN", "VCC3V3_AUX", "power_in"),
p(45, "GND", "GND", "power_in"),
p(46, "DDBUS1/RXD", "FTDI_D_RXD_SRC", "bidirectional"),
p(47, "DDBUS2/RTS", "FTDI_D_DBG_SRC", "bidirectional"),
p(48, "DDBUS3", None, "bidirectional"), p(49, "DDBUS4", None, "bidirectional"),
p(50, "VCCIO", "VCC3V3_AUX", "power_in"),
p(51, "DDBUS5", None, "bidirectional"), p(52, "DDBUS6", None, "bidirectional"),
p(53, "DDBUS7", None, "bidirectional"), p(54, "PWREN_N", None, "output"),
p(55, "EEDATA", "FTDI_EEDATA", "bidirectional"), p(56, "EECLK", "FTDI_EECLK", "output"),
p(57, "EP", "GND", "power_in")]
add_ic("U2", "FT4232H-56Q", "FT4232H-56Q-REEL", "FTDI", "board:FTDI_VQFN56_EP5p9",
ftdi_pins, "Quad high-speed USB bridge; all four channels assigned")
r("27R", "USB_D_P", "FTDI_D_P", description="FTDI USB D+ series termination")
r("27R", "USB_D_N", "FTDI_D_N", description="FTDI USB D- series termination")
r("12K", "FTDI_REF", "GND", description="FT4232H USB PHY reference resistor, 1%")
r("4.7K", "VBUS_RAW", "FTDI_RESET_N", description="Self-powered VBUS-present reset divider top")
r("10K", "FTDI_RESET_N", "GND", description="Self-powered VBUS-present reset divider bottom")
for _ in range(2):
c("100nF", "VCC3V3_AUX", "GND", description="FT4232H VCCIO/VREGIN bypass")
for _ in range(2):
c("100nF", "FTDI_1V8", "GND", description="FT4232H VCORE bypass")
c("1uF", "FTDI_1V8", "GND", description="FT4232H regulator output capacitor")
two_pin("FB", "600R@100MHz", "BLM15AG601SN1D", "Murata", "Inductor_SMD:L_0402_1005Metric",
"VCC3V3_AUX", "FTDI_VPHY", "FTDI USB-PHY supply ferrite")
two_pin("FB", "600R@100MHz", "BLM15AG601SN1D", "Murata", "Inductor_SMD:L_0402_1005Metric",
"VCC3V3_AUX", "FTDI_VPLL", "FTDI PLL supply ferrite")
c("100nF", "FTDI_VPHY", "GND", description="FTDI VPHY bypass")
c("100nF", "FTDI_VPLL", "GND", description="FTDI VPLL bypass")
add_ic("U25", "12.000 MHz XO", "ECS-2520MV-120-BN-TR", "ECS",
"Oscillator:Oscillator_SMD_ECS_2520MV-xxx-xx-4Pin_2.5x2.0mm", [
p(1, "OE", "VCC3V3_AUX", "input"), p(2, "GND", "GND", "power_in"),
p(3, "OUT", "FTDI_OSC12", "output"), p(4, "VDD", "VCC3V3_AUX", "power_in")],
"Current-production 12-MHz CMOS FT4232H clock")
r("33R", "FTDI_OSC12", "FTDI_OSCI", description="FTDI oscillator source termination")
c("100nF", "VCC3V3_AUX", "GND", description="FTDI oscillator local bypass")
add_ic("U3", "93LC56B x16", "93LC56BT-I/OT", "Microchip", "Package_TO_SOT_SMD:SOT-23-6", [
p(1, "CS", "FTDI_EECS", "input"), p(2, "CLK", "FTDI_EECLK", "input"),
p(3, "DI", "FTDI_EEDATA", "input"), p(4, "DO", "FTDI_EEDO", "output"),
p(5, "VSS", "GND", "power_in"), p(6, "VCC", "VCC3V3_AUX", "power_in")],
"FTDI descriptor/channel-mode EEPROM, fixed 16-bit organization")
r("2.2K", "FTDI_EEDO", "FTDI_EEDATA", description="EEPROM shared-data isolation")
r("10K", "FTDI_EEDO", "VCC3V3_AUX", description="EEPROM DO pull-up")
c("100nF", "VCC3V3_AUX", "GND", description="FTDI EEPROM bypass")
# FTDI is intentionally powered from the always-on debug rail. All signals
# that can drive the FPGA therefore pass through Ioff-capable buffers powered
# by the FPGA's 3.3-V I/O rail. When the FPGA rail is absent, the buffer
# outputs are high impedance and no unpowered FPGA pin can be back-driven.
add_ic("U29", "SN74LVC244A", "SN74LVC244APWR", "Texas Instruments",
"Package_SO:TSSOP-20_4.4x6.5mm_P0.65mm", [
p(1, "1OE_N", "FTDI_FPGA_BUF_OE_N", "input"),
p(2, "1A1", "FTDI_A_TCK_SRC", "input"), p(18, "1Y1", "BUF_A_TCK", "output"),
p(4, "1A2", "FTDI_A_TDI_SRC", "input"), p(16, "1Y2", "BUF_A_TDI", "output"),
p(6, "1A3", "FTDI_A_TMS_SRC", "input"), p(14, "1Y3", "BUF_A_TMS", "output"),
p(8, "1A4", "FTDI_B_TCK_SRC", "input"), p(12, "1Y4", "BUF_B_TCK", "output"),
p(19, "2OE_N", "FTDI_FPGA_BUF_OE_N", "input"),
p(11, "2A1", "FTDI_B_TDI_SRC", "input"), p(9, "2Y1", "BUF_B_TDI", "output"),
p(13, "2A2", "FTDI_B_TMS_SRC", "input"), p(7, "2Y2", "BUF_B_TMS", "output"),
p(15, "2A3", "FTDI_C_TXD_SRC", "input"), p(5, "2Y3", "BUF_C_TXD", "output"),
p(17, "2A4", "FTDI_D_TXD_SRC", "input"), p(3, "2Y4", "BUF_D_TXD", "output"),
p(10, "GND", "GND", "power_in"), p(20, "VCC", "VCC3V3", "power_in")],
"Ioff-protected FPGA-input buffer; automatically isolates when FPGA 3.3 V is off")
add_ic("U30", "SN74LVC1G125", "SN74LVC1G125DBVR", "Texas Instruments",
"Package_TO_SOT_SMD:SOT-23-5", [p(1, "OE_N", "FTDI_FPGA_BUF_OE_N", "input"),
p(2, "A", "FTDI_D_DBG_SRC", "input"),
p(3, "GND", "GND", "power_in"), p(4, "Y", "BUF_D_DBG", "output"),
p(5, "VCC", "VCC3V3", "power_in")],
"Ioff-protected tri-state debug-GPIO buffer powered from FPGA 3.3 V")
add_ic("U31", "SN74LVC1G07", "SN74LVC1G07DBVR", "Texas Instruments",
"Package_TO_SOT_SMD:SOT-23-5", [p(1, "NC", None), p(2, "A", "FTDI_B_RESET_N_SRC", "input"),
p(3, "GND", "GND", "power_in"), p(4, "Y_OD", "FPGA_CRESET_N", "open_collector"),
p(5, "VCC", "VCC3V3_AUX", "power_in")],
"Open-drain FTDI reset request into FPGA CRESET_N")
add_ic("U32", "SN74LVC1G07", "SN74LVC1G07DBVR", "Texas Instruments",
"Package_TO_SOT_SMD:SOT-23-5", [p(1, "NC", None), p(2, "A", "ALL_FPGA_POWER_OK", "input"),
p(3, "GND", "GND", "power_in"), p(4, "Y_OD", "FPGA_CRESET_N", "open_collector"),
p(5, "VCC", "VCC3V3_AUX", "power_in")],
"Open-drain all-rail-valid translation into FPGA CRESET_N")
add_ic("U33", "SN74LVC1G07", "SN74LVC1G07DBVR", "Texas Instruments",
"Package_TO_SOT_SMD:SOT-23-5", [p(1, "NC", None), p(2, "A", "EXT_RESET_FILTER_N", "input"),
p(3, "GND", "GND", "power_in"), p(4, "Y_OD", "FPGA_CRESET_N", "open_collector"),
p(5, "VCC", "VCC1V8", "power_in")],
"Open-drain external/manual reset isolation into FPGA CRESET_N")
add_ic("Q1", "MMBT3904", "MMBT3904LT1G", "onsemi", "Package_TO_SOT_SMD:SOT-23", [
p(1, "B", "FTDI_BUF_OE_BASE", "input"), p(2, "E", "GND", "power_in"),
p(3, "C", "FTDI_FPGA_BUF_OE_N", "open_collector")],
"Power-good inverter: enables FTDI-to-FPGA buffers only after all rails are valid")
for src, dst in (("BUF_B_TCK", "HARD_TCK"), ("BUF_B_TMS", "HARD_TMS"),
("BUF_B_TDI", "HARD_TDI"), ("HARD_TDO", "FTDI_B_TDO_SRC")):
r("33R", src, dst, description="FTDI interface-1 hard-JTAG source damping")
r("10K", "VCC3V3", "HARD_TMS", description="Efinix-required hard-JTAG weak pull-up")
c("100nF", "VCC3V3_AUX", "GND", description="Additional FT4232H channel-A VCCIO bypass")
for src, dst in (("BUF_A_TCK", "RISCV_TCK"), ("BUF_A_TDI", "RISCV_TDI"),
("BUF_A_TMS", "RISCV_TMS"), ("RISCV_TDO", "FTDI_A_TDO_SRC"),
("BUF_C_TXD", "UART0_RX"), ("UART0_TX", "FTDI_C_RXD_SRC"),
("BUF_D_TXD", "UART1_RX"), ("UART1_TX", "FTDI_D_RXD_SRC"),
("BUF_D_DBG", "FTDI_DBG_GPIO")):
r("33R", src, dst, description="FT4232H channel signal damping")
# Active-serial flash and four compact Digilent-format PMOD connectors.
add_ic("U26", "512-Mbit QSPI flash", "IS25WP512M-JLLE", "ISSI",
"Package_SON:WSON-8-1EP_6x5mm_P1.27mm_EP3.4x4.3mm", [
p(1, "CS_N", "QSPI_CS_N", "input"), p(2, "SO/IO1", "QSPI_IO1", "bidirectional"),
p(3, "WP_N/IO2", "QSPI_IO2", "bidirectional"), p(4, "GND", "GND", "power_in"),
p(5, "SI/IO0", "QSPI_IO0", "bidirectional"), p(6, "SCLK", "QSPI_SCLK", "input"),
p(7, "HOLD_N/IO3", "QSPI_IO3", "bidirectional"), p(8, "VCC", "VCC1V8", "power_in"),
p(9, "EP", "GND", "power_in")],
"1.8-V 512-Mbit FPGA active-serial configuration flash")
r("10K", "VCC1V8", "QSPI_CS_N", description="Configuration flash CS pull-up")
r("10K", "VCC1V8", "FPGA_TEST_N", description="Efinix mandatory TEST_N weak pull-up")
r("10K", "VCC1V8", "FPGA_CSI", description="Efinix mandatory CSI weak pull-up")
for net in ("QSPI_IO2", "QSPI_IO3"):
r("10K", "VCC1V8", net, description="Configuration flash hold/write-protect pull-up")
c("100nF", "VCC1V8", "GND", description="Configuration flash bypass")
c("1uF", "VCC1V8", "GND", description="Configuration flash local bulk")
def add_pmod(ref: str, prefix: str, rail: str, voltage: str) -> None:
pins = [p(i, f"IO{i}", f"{prefix}_{i}") for i in (1, 2, 3, 4)]
pins += [p(5, "GND", "GND"), p(6, voltage, rail)]
pins += [p(i, f"IO{i}", f"{prefix}_{i}") for i in (7, 8, 9, 10)]
pins += [p(11, "GND", "GND"), p(12, voltage, rail)]
add_ic(ref, f"PMOD {voltage}", "67997-412HLF", "Amphenol ICC",
"board:Digilent_PMOD_2x6_P2.54mm", pins,
f"Digilent 2x6 PMOD: eight {voltage} GPIO, two GND, two {voltage}")
components[-1].assembly = "THT"
add_pmod("JPMOD1", "PMOD1", "VCC3V3", "3.3 V")
add_pmod("JPMOD2", "PMOD2", "VCC3V3", "3.3 V")
add_pmod("JPMOD3", "PMOD3", "VCC1V8", "1.8 V")
add_pmod("JPMOD4", "PMOD4", "VCC1V8", "1.8 V")
for net, label in (("VBUS_RAW", "USB VBUS"), ("EXT5_RAW", "EXT 5V"), ("SYS5", "SYS 5V"),
("VCC3V3_AUX", "AUX 3V3"), ("VCC0V95", "CORE 0V95"),
("VCC1V8", "1V8"), ("VCC3V3", "3V3"), ("VCC3V0_RF", "RF 3V0"),
("VCC3V0_DIG", "DIG 3V0"), ("FPGA_CRESET_N", "CRESET_N"),
("FPGA_CDONE", "CDONE"), ("GND", "GND")):
tp(net, label)
# Configuration/JTAG pulls are located on the FPGA side of the isolation
# buffers and use the voltage of their respective I/O bank.
r("10K", "VCC1V8", "FPGA_CDONE", description="Efinix-required CDONE open-drain pull-up")
for net in ("HARD_TCK", "HARD_TDI", "HARD_TDO"):
r("10K", "VCC3V3", net, description="Efinix-required hard-JTAG weak pull-up")
for net in ("RISCV_TCK", "RISCV_TMS", "RISCV_TDI", "RISCV_TDO"):
r("10K", "VCC3V3", net, description="RISC-V JTAG idle-state pull-up")
# Supervisor support and reset-request fan-in. FTDI BDBUS4 uses the
# Efinix-compatible active-low convention. Independent open-drain paths avoid
# output contention and allow JTAG to remain live while CRESET_N is asserted.
r("10K", "VCC3V3_AUX", "FTDI_B_RESET_N_SRC", description="FTDI FPGA-reset request default release")
r("10K", "VCC3V3_AUX", "FPGA_POWER_OK_N", description="TPS3808 open-drain power-good pull-up")
r("10K", "VCC3V3", "FTDI_FPGA_BUF_OE_N", description="Keep FTDI-to-FPGA buffers disabled until power-good")
r("10K", "ALL_FPGA_POWER_OK", "FTDI_BUF_OE_BASE", description="All-rail-good inverter base-current limiter")
r("100K", "FTDI_BUF_OE_BASE", "GND", description="Power-good inverter default-off base pull-down")
r("10K", "VCC1V8", "EXT_RESET_FILTER_N", description="External/manual reset input pull-up")
r("1K", "EXT_RESET_N", "EXT_RESET_FILTER_N", description="External-reset input current and ESD limiting")
r("10K", "VCC3V3_AUX", "PWR_SEQ_OK", description="LM3880 final-flag pull-up for all-power logic")
r("10K", "VCC3V3_AUX", "PWR_1V8_OK", description="1.8-V power-good pull-up for all-power logic")
r("10K", "VCC3V3_AUX", "PWR_3V3_OK", description="3.3-V power-good pull-up for all-power logic")
c("4.7nF", "VCC3V0_DIG", "GND", description="TPS3808 SENSE transient filter")
c("4.7nF", "EXT_RESET_FILTER_N", "GND", description="Manual/external reset cable-noise filter")
c("100nF", "VCC3V3_AUX", "GND", description="TPS3808 local bypass")
c("100nF", "VCC3V3", "GND", description="SN74LVC244A local bypass")
c("100nF", "VCC3V3", "GND", description="SN74LVC1G125 local bypass")
c("100nF", "VCC3V3_AUX", "GND", description="SN74LVC1G07 local bypass")
c("100nF", "VCC3V3_AUX", "GND", description="Power-good open-drain buffer local bypass")
c("100nF", "VCC1V8", "GND", description="External-reset open-drain buffer local bypass")
c("100nF", "VCC3V3_AUX", "GND", description="All-rail-valid logic local bypass")
add_ic("J4", "EXT RESET_N", "SM02B-SRSS-TB(LF)(SN)", "JST",
"Connector_JST:JST_SH_SM02B-SRSS-TB_1x02-1MP_P1.00mm_Horizontal", [
p(1, "EXT_RESET_N", "EXT_RESET_N", "input"), p(2, "GND", "GND", "power_in")],
"External active-low open-drain reset input; JST-SH 1.0-mm")
add_ic("SW1", "FPGA RESET", "B3U-1000P", "Omron",
"Button_Switch_SMD:SW_SPST_B3U-1000P", [
p(1, "RESET", "EXT_RESET_FILTER_N", "passive"), p(2, "GND", "GND", "passive")],
"User-accessible momentary FPGA configuration reset")
add_ic("D2", "RESET ESD", "ESD5Z3.3T1G", "onsemi", "Diode_SMD:D_SOD-523", [
p(1, "K", "EXT_RESET_N", "passive"), p(2, "A", "GND", "passive")],
"External-reset connector ESD clamp")
def validate() -> None:
refs = [x.ref for x in components]
assert len(refs) == len(set(refs))
u1 = next(x for x in components if x.ref == "U1")
assert len(u1.pins) == 529 and len({x.number for x in u1.pins}) == 529
assert next(x for x in u1.pins if x.number == "G10").net == "HARD_TCK"
assert {pin.number: pin.net for pin in u1.pins if pin.number in {
"G10", "G12", "G9", "G13", "F12", "V19", "P18", "T18"
}} == {
"G10": "HARD_TCK", "G12": "HARD_TMS", "G9": "HARD_TDI", "G13": "HARD_TDO",
"F12": "RISCV_TCK", "V19": "RISCV_TDI", "P18": "RISCV_TMS", "T18": "RISCV_TDO",
}
assert next(x for x in u1.pins if x.number == "B11").net == "MAX_CLKOUT"
maxic = next(x for x in components if x.ref == "U21")
assert next(x for x in maxic.pins if x.number == "25").net == "MAX_MIXIN_HI"
assert all(next(x for x in maxic.pins if x.number == n).net is None
for n in ("19", "20", "23", "24", "26"))
pmods = [next(x for x in components if x.ref == f"JPMOD{i}") for i in range(1, 5)]
assert all(len(x.pins) == 12 for x in pmods)
assert all(sum(pin.net == "GND" for pin in x.pins) == 2 for x in pmods)
assert all(sum((pin.net or "").startswith("PMOD") for pin in x.pins) == 8 for x in pmods)
assert all(sum(pin.net == "VCC3V3" for pin in x.pins) == 2 for x in pmods[:2])
assert all(sum(pin.net == "VCC1V8" for pin in x.pins) == 2 for x in pmods[2:])
assert not any((pin.net or "").startswith("EXP_") for pin in u1.pins)
u2 = next(x for x in components if x.ref == "U2")
assert {pin.number: pin.net for pin in u2.pins if pin.number in {
"12", "13", "14", "15", "22", "23", "24", "25"
}} == {
"12": "FTDI_A_TCK_SRC", "13": "FTDI_A_TDI_SRC",
"14": "FTDI_A_TDO_SRC", "15": "FTDI_A_TMS_SRC",
"22": "FTDI_B_TCK_SRC", "23": "FTDI_B_TDI_SRC",
"24": "FTDI_B_TDO_SRC", "25": "FTDI_B_TMS_SRC",
}
assert not any(c.ref in {"U4", "J6"} for c in components)
assert not any((pin.net or "").startswith("FTDI_HARD_") or pin.net == "JTAG_SW_OE_N"
for c in components for pin in c.pins)
def has_link(net_a: str, net_b: str, value: str = "33R") -> bool:
return any(c.value == value and len(c.pins) == 2 and
{c.pins[0].net, c.pins[1].net} == {net_a, net_b}
for c in components)
for src, dst in (
("BUF_A_TCK", "RISCV_TCK"), ("BUF_A_TMS", "RISCV_TMS"),
("BUF_A_TDI", "RISCV_TDI"), ("FTDI_A_TDO_SRC", "RISCV_TDO"),
("BUF_B_TCK", "HARD_TCK"), ("BUF_B_TMS", "HARD_TMS"),
("BUF_B_TDI", "HARD_TDI"), ("FTDI_B_TDO_SRC", "HARD_TDO"),
):
assert has_link(src, dst), (src, dst)
assert next(pin for pin in u2.pins if pin.number == "26").net == "FTDI_B_RESET_N_SRC"
u28 = next(x for x in components if x.ref == "U28")
assert {pin.name: pin.net for pin in u28.pins} == {
"RESET_N": "FPGA_POWER_OK_N", "GND": "GND", "MR_N": "VCC3V3_AUX",
"CT": None, "SENSE": "VCC3V0_DIG", "VDD": "VCC3V3_AUX"}
u34 = next(x for x in components if x.ref == "U34")
assert {pin.number: pin.net for pin in u34.pins if pin.number in {"1", "2", "4", "5", "12", "13"}} == {
"1": "CORE_PG", "2": "PWR_1V8_OK", "4": "PWR_3V3_OK",
"5": "FPGA_POWER_OK_N", "12": "PWR_OK_C", "13": "PWR_SEQ_OK"}
assert next(pin for pin in next(x for x in components if x.ref == "U32").pins
if pin.number == "2").net == "ALL_FPGA_POWER_OK"
assert not any(c.value == "0R" and "FPGA_CRESET_N" in {pin.net for pin in c.pins}
for c in components)
for net, rail in (("FPGA_CDONE", "VCC1V8"), ("HARD_TCK", "VCC3V3"),
("HARD_TMS", "VCC3V3"), ("HARD_TDI", "VCC3V3"),
("HARD_TDO", "VCC3V3")):
assert has_link(net, rail, "10K"), (net, rail)
assert {pin.net for pin in next(x for x in components if x.ref == "J4").pins} == {
"EXT_RESET_N", "GND"}
assert [x.ref for x in components if x.ref.startswith("TP")] == [f"TP{i}" for i in range(1, 16)]
j5 = next(x for x in components if x.ref == "J5")
assert {x.net for x in j5.pins} == {"VCC3V3_ANT", "ANT_BIAS_SEL", "SYS5"}
validate()