e1a116d0f5
Custom ESP32-WROOM-32E + Si3210 SLIC board replacing the ESP32-A1S dev kit (final PLIP target, RJ9 handset). Generated via tools/ gen_si3210_lib.py (symbol + QFN-38 footprint), gen_schematic.py (parses standard KiCad libs, 45 instances, 157 global labels on pin ends) and gen_pcb.py (58x38mm outline, 41 footprints placed per the plan, nets assigned, unrouted). ERC: 0 violations. Schematic follows the spec GPIO map and the plan's passive network. PCB placed not routed (Freerouting + DRC + gerbers remain — see README). BOM in JLCPCB format; CP2102N/transformer/RJ9 LCSC parts marked TBD. WARNINGS (README): the Si3210 -72V DC-DC stage is simplified and must be checked against the Skyworks datasheet/AN35 before fab; line protection is minimal; human review required before ordering.
159 lines
6.5 KiB
Python
159 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate the custom Si3210 KiCad library: symbol + QFN-38 footprint.
|
|
|
|
Pin numbers and groups come from the hardware plan
|
|
(docs/superpowers/plans/2026-04-08-plip-telephone-hardware.md, Task 2/3).
|
|
Run from anywhere: python3 gen_si3210_lib.py
|
|
Outputs land in ../libs/.
|
|
"""
|
|
from pathlib import Path
|
|
|
|
LIBS = Path(__file__).resolve().parent.parent / "libs"
|
|
|
|
# (name, number, electrical_type) — order top→bottom per side.
|
|
LEFT = [
|
|
("SCLK", "34", "input"), ("SDI", "33", "input"), ("SDO", "32", "output"),
|
|
("~{CS}", "35", "input"), None,
|
|
("PCLK", "37", "input"), ("FSYNC", "2", "input"), ("DTX", "1", "output"),
|
|
("DRX", "38", "input"), None,
|
|
("~{RESET}", "3", "input"), ("~{INT}", "36", "open_collector"),
|
|
("TEST", "28", "input"), None,
|
|
("SDCH", "4", "passive"), ("SDCL", "5", "passive"),
|
|
("SDITHRU", "31", "passive"),
|
|
]
|
|
RIGHT = [
|
|
("STIPAC", "16", "passive"), ("STIPDC", "11", "passive"),
|
|
("STIPE", "13", "passive"), ("ITIPP", "24", "passive"),
|
|
("ITIPN", "25", "passive"), None,
|
|
("SRINGAC", "17", "passive"), ("SRINGDC", "12", "passive"),
|
|
("SRINGE", "15", "passive"), ("IRINGP", "22", "passive"),
|
|
("IRINGN", "21", "passive"), None,
|
|
("DCDRV", "30", "output"), ("DCFF", "29", "input"),
|
|
("SVBAT", "14", "passive"), None,
|
|
("IREF", "7", "passive"), ("CAPP", "8", "passive"),
|
|
("CAPM", "10", "passive"), ("IGMP", "20", "passive"),
|
|
("IGMN", "18", "passive"),
|
|
]
|
|
TOP = [("VDDA1", "6"), ("VDDA2", "23"), ("VDDD", "26")] # power_in
|
|
BOTTOM = [("GNDA", "19"), ("GNDD", "27"), ("QGND", "9"), ("EP", "39")] # power_in
|
|
|
|
HALF_W = 15.24
|
|
ROWS = max(len(LEFT), len(RIGHT))
|
|
TOP_Y = (ROWS - 1) * 2.54 / 2.0
|
|
HALF_H = TOP_Y + 2.54 # one row of margin above/below
|
|
PIN_LEN = 2.54
|
|
|
|
|
|
def pin(name, num, etype, x, y, angle):
|
|
return f""" (pin {etype} line (at {x:g} {y:g} {angle}) (length {PIN_LEN:g})
|
|
(name "{name}" (effects (font (size 1.27 1.27))))
|
|
(number "{num}" (effects (font (size 1.27 1.27))))
|
|
)"""
|
|
|
|
|
|
def gen_symbol() -> str:
|
|
pins = []
|
|
y = TOP_Y
|
|
for entry in LEFT:
|
|
if entry:
|
|
n, num, t = entry
|
|
pins.append(pin(n, num, t, -(HALF_W + PIN_LEN), y, 0))
|
|
y -= 2.54
|
|
y = TOP_Y
|
|
for entry in RIGHT:
|
|
if entry:
|
|
n, num, t = entry
|
|
pins.append(pin(n, num, t, HALF_W + PIN_LEN, y, 180))
|
|
y -= 2.54
|
|
xs = [-(len(TOP) - 1) * 2.54 / 2 + i * 2.54 for i in range(len(TOP))]
|
|
for (n, num), x in zip(TOP, xs):
|
|
pins.append(pin(n, num, "power_in", x, HALF_H + PIN_LEN, 270))
|
|
xs = [-(len(BOTTOM) - 1) * 2.54 / 2 + i * 2.54 for i in range(len(BOTTOM))]
|
|
for (n, num), x in zip(BOTTOM, xs):
|
|
pins.append(pin(n, num, "power_in", x, -(HALF_H + PIN_LEN), 90))
|
|
|
|
body = "\n".join(pins)
|
|
return f"""(kicad_symbol_lib (version 20231120) (generator gen_si3210_lib)
|
|
(symbol "Si3210" (in_bom yes) (on_board yes)
|
|
(property "Reference" "U" (at {-HALF_W:g} {HALF_H + 1.27:g} 0)
|
|
(effects (font (size 1.27 1.27)) (justify left)))
|
|
(property "Value" "Si3210" (at {HALF_W:g} {HALF_H + 1.27:g} 0)
|
|
(effects (font (size 1.27 1.27)) (justify right)))
|
|
(property "Footprint" "Si3210:Si3210_QFN38" (at 0 0 0)
|
|
(effects (font (size 1.27 1.27)) hide))
|
|
(property "Datasheet" "https://www.skyworksinc.com/-/media/SkyWorks/SL/documents/public/data-sheets/Si3210.pdf" (at 0 0 0)
|
|
(effects (font (size 1.27 1.27)) hide))
|
|
(property "Description" "ProSLIC subscriber line interface circuit, integrated codec + ring generator, QFN-38" (at 0 0 0)
|
|
(effects (font (size 1.27 1.27)) hide))
|
|
(symbol "Si3210_0_1"
|
|
(rectangle (start {-HALF_W:g} {HALF_H:g}) (end {HALF_W:g} {-HALF_H:g})
|
|
(stroke (width 0.254) (type default)) (fill (type background)))
|
|
)
|
|
(symbol "Si3210_1_1"
|
|
{body}
|
|
)
|
|
)
|
|
)
|
|
"""
|
|
|
|
|
|
def gen_footprint() -> str:
|
|
# QFN-38 5x7 mm, 0.5 mm pitch, pads 0.25x0.75, EP 3.4x5.4 (plan Task 3).
|
|
# 9 pads on the 7 mm (left/right) edges, 10 on the 5 mm (top/bottom).
|
|
# CCW numbering from pin 1 at top of the left edge.
|
|
pads = []
|
|
pitch = 0.5
|
|
pad_l, pad_w = 0.75, 0.25
|
|
half_body_x, half_body_y = 2.5, 3.5 # body 5 x 7
|
|
px = half_body_x - pad_l / 2 + 0.05 # pad centre just inside the edge
|
|
py = half_body_y - pad_l / 2 + 0.05
|
|
|
|
def smd(num, x, y, w, h):
|
|
pads.append(
|
|
f' (pad "{num}" smd roundrect (at {x:.3f} {y:.3f}) '
|
|
f"(size {w:g} {h:g}) (layers \"F.Cu\" \"F.Paste\" \"F.Mask\") "
|
|
f"(roundrect_rratio 0.25))"
|
|
)
|
|
|
|
n = 1
|
|
ys = [-(9 - 1) * pitch / 2 + i * pitch for i in range(9)]
|
|
for y in ys: # left edge, top→bottom
|
|
smd(n, -px, y, pad_l, pad_w); n += 1
|
|
xs = [-(10 - 1) * pitch / 2 + i * pitch for i in range(10)]
|
|
for x in xs: # bottom edge, left→right
|
|
smd(n, x, py, pad_w, pad_l); n += 1
|
|
for y in reversed(ys): # right edge, bottom→top
|
|
smd(n, px, y, pad_l, pad_w); n += 1
|
|
for x in reversed(xs): # top edge, right→left
|
|
smd(n, x, -py, pad_w, pad_l); n += 1
|
|
assert n == 39, n
|
|
pads.append(' (pad "39" smd rect (at 0 0) (size 3.4 5.4) '
|
|
'(layers "F.Cu" "F.Paste" "F.Mask"))')
|
|
|
|
return f"""(footprint "Si3210_QFN38" (version 20231120) (generator gen_si3210_lib)
|
|
(layer "F.Cu")
|
|
(descr "QFN-38 5x7mm pitch 0.5mm, exposed pad 3.4x5.4mm (Si3210 ProSLIC)")
|
|
(attr smd)
|
|
(fp_text reference "REF**" (at 0 -4.6) (layer "F.SilkS")
|
|
(effects (font (size 1 1) (thickness 0.15))))
|
|
(fp_text value "Si3210_QFN38" (at 0 4.6) (layer "F.Fab")
|
|
(effects (font (size 1 1) (thickness 0.15))))
|
|
(fp_line (start -2.5 -3.5) (end 2.5 -3.5) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
|
(fp_line (start 2.5 -3.5) (end 2.5 3.5) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
|
(fp_line (start 2.5 3.5) (end -2.5 3.5) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
|
(fp_line (start -2.5 3.5) (end -2.5 -3.5) (stroke (width 0.1) (type solid)) (layer "F.Fab"))
|
|
(fp_circle (center -3.2 -2.6) (end -3.0 -2.6) (stroke (width 0.2) (type solid)) (fill solid) (layer "F.SilkS"))
|
|
{chr(10).join(pads)}
|
|
)
|
|
"""
|
|
|
|
|
|
if __name__ == "__main__":
|
|
LIBS.mkdir(parents=True, exist_ok=True)
|
|
(LIBS / "Si3210.kicad_sym").write_text(gen_symbol(), encoding="utf-8")
|
|
fp_dir = LIBS / "Si3210.pretty"
|
|
fp_dir.mkdir(exist_ok=True)
|
|
(fp_dir / "Si3210_QFN38.kicad_mod").write_text(gen_footprint(), encoding="utf-8")
|
|
print("wrote", LIBS / "Si3210.kicad_sym")
|
|
print("wrote", fp_dir / "Si3210_QFN38.kicad_mod")
|