metronome/hardware/eda/circuits/power_tree.py
Me Here cd619cfeb2 PM_K-1 hardware: power tree (USB 5V -> +/-18V switcher -> clean +/-15V LDOs + 3V3)
circuits/power_tree.py captures the full supply, topology + values verified from the
TPS65131 datasheet (SLVS493E: pinout p3, Typical Application Fig 8-1 p11, BOM Table 8-2
p12, FB equations p13 Vref=1.213V) and the TPS7A49/TPS7A30 LDO datasheets:
- TPS65131 dual boost/inverter: L1/L2=4.7uH, D1/D2=MBRM120, FB dividers set ~+/-18.2V
  (R1=1.4M/R2=100k, R3=1.5M/R4=100k), comp C7/C6 on CP/CN, VREF 220nF, no Q1 (USB),
  PSP/PSN=GND forced-PWM for low audio-band noise.
- TPS7A4901/TPS7A3001 post-regulate to clean +/-15V (shared 8-pin pinout, EN tied to IN).
- AP2112K-3.3 -> digital 3V3.
ERC 0 errors; netlist 0 errors.

CONFIRM before fab: exact LDO Vfb (used ~1.194V/-1.18V for the dividers) and the
AP2112K SOT-23-5 pinout. Switcher validated vs TI reference design; no SPICE (behavioral
models don't validate a switcher) -- layout per the TI EVM.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 20:53:44 -05:00

141 lines
8.1 KiB
Python

#!/usr/bin/env python3
"""PM_K-1 power tree (SKiDL): USB 5V -> +/-18V switcher -> ultra-low-noise +/-15V LDOs,
plus the digital 3V3 rail.
Run INSIDE the EDA container:
cd hardware/eda && ./run.sh python3 ../eda/circuits/power_tree.py
Outputs ERC + hardware/kicad/power_tree.net.
WHY THIS SHAPE
A switching converter is noisy; audio op-amps need quiet rails. So the TPS65131 makes
RAW +/-18V (efficiently, from USB 5V), then TPS7A4901/TPS7A3001 ultra-low-noise LDOs
drop that to CLEAN +/-15V for the analog section. 3V3 (digital) comes from a simple LDO.
VERIFIED FROM DATASHEETS (topology + values, not guessed)
* TPS65131 (TI SLVS493E): pinout p.3; Typical Application Fig 8-1 (p.11) gives the
L/D/C topology; Table 8-2 (p.12) the BOM; FB equations p.13 (Vref=1.213V):
Vpos=Vref(1+R1/R2); Vneg=-Vref*R3/R4. Inductors 4.7uH; D1/D2=MBRM120 Schottky.
We target ~+/-18V (LDO headroom): R1=1.4M/R2=100k -> +18.2V ; R3=1.5M/R4=100k -> -18.2V.
No Q1 battery switch (USB powered) -> BSW left open. PSP/PSN tied LOW = forced PWM
(constant-frequency, cleaner in the audio band) -- a deliberate change from the
battery-oriented example.
* TPS7A4901 / TPS7A3001 (8-pin, identical pinout 1=OUT 2=FB 3=NC 4=GND 5=EN 6=NR/SS
7=DNC 8=IN + PowerPAD): Vout=Vfb(1+Rt/Rb). Vfb approx +1.194V / -1.18V -- CONFIRM the
exact Vfb in each elec-char table before finalizing the divider.
* AP2112K-3.3 SOT-23-5 (1=VIN 2=GND 3=EN 4=NC 5=VOUT) -- standard pinout, confirm.
No SPICE here: a switcher is validated against TI's reference design + bench measurement,
not a behavioral op-amp model. Layout (switch nodes, ground return) is critical -- follow
the TI EVM/layout guidance.
"""
import os
from skidl import *
set_default_tool(KICAD9)
P = Pin.types
R = Part("Device","R", dest=TEMPLATE, footprint="Resistor_SMD:R_0805_2012Metric")
def Cp(v, fp="Capacitor_SMD:C_0805_2012Metric"):
return Part("Device","C", value=v, footprint=fp)
L = Part("Device","L", dest=TEMPLATE, footprint="Inductor_SMD:L_Wuerth_WE-PD_Typ4_M")
DS = Part("Device","D_Schottky", dest=TEMPLATE, footprint="Diode_SMD:D_SMA")
FB = Part("Device","L", dest=TEMPLATE, footprint="Inductor_SMD:L_0805_2012Metric") # ferrite bead
def mk(name, pins, fp, ref="U"):
return Part(name=name, tool=SKIDL, dest=TEMPLATE, ref_prefix=ref, footprint=fp, pins=pins)
# ---- nets ----
vbus, p5, p18, n18, p15, n15, p3v3, gnd, vref = (Net("VBUS_5V"), Net("+5V"), Net("+18V"),
Net("-18V"), Net("+15V"), Net("-15V"), Net("+3V3"), Net("GND"), Net("VREF"))
for n in (vbus, p5, p18, n18, p15, n15, p3v3): n.drive = POWER
gnd.drive = POWER
sw_boost, sw_inv, fbp, fbn = Net("SW_BOOST"), Net("SW_INV"), Net("FBP"), Net("FBN")
innf = Net("INN_F") # RC-filtered +5V to the inverter input
# ---- parts ----
TPS65131 = mk("TPS65131",
[Pin(num=1,name="INP",func=P.PASSIVE),Pin(num=24,name="INP2",func=P.PASSIVE),
Pin(num=2,name="PGND",func=P.PWRIN),Pin(num=3,name="PGND2",func=P.PWRIN),
Pin(num=4,name="VIN",func=P.PWRIN),Pin(num=5,name="INN",func=P.PASSIVE),Pin(num=6,name="INN2",func=P.PASSIVE),
Pin(num=7,name="BSW",func=P.OUTPUT),Pin(num=8,name="ENP",func=P.INPUT),Pin(num=9,name="PSP",func=P.INPUT),
Pin(num=10,name="ENN",func=P.INPUT),Pin(num=11,name="PSN",func=P.INPUT),
Pin(num=12,name="NC12",func=P.NOCONNECT),Pin(num=20,name="NC20",func=P.NOCONNECT),
Pin(num=13,name="OUTN",func=P.PASSIVE),Pin(num=14,name="OUTN2",func=P.PASSIVE),
Pin(num=15,name="VNEG",func=P.INPUT),Pin(num=16,name="FBN",func=P.INPUT),Pin(num=17,name="VREF",func=P.PWROUT),
Pin(num=18,name="CN",func=P.PASSIVE),Pin(num=19,name="AGND",func=P.PWRIN),Pin(num=21,name="CP",func=P.PASSIVE),
Pin(num=22,name="FBP",func=P.INPUT),Pin(num=23,name="VPOS",func=P.INPUT),
Pin(num=25,name="EP",func=P.PWRIN)],
"Package_DFN_QFN:QFN-24-1EP_4x4mm_P0.5mm")
# LDOs share one pinout (HVSSOP-8 PowerPAD)
def ldo(name):
return mk(name, [Pin(num=1,name="OUT",func=P.PWROUT),Pin(num=2,name="FB",func=P.INPUT),
Pin(num=3,name="NC",func=P.NOCONNECT),Pin(num=4,name="GND",func=P.PWRIN),Pin(num=5,name="EN",func=P.INPUT),
Pin(num=6,name="NR",func=P.PASSIVE),Pin(num=7,name="DNC",func=P.NOCONNECT),Pin(num=8,name="IN",func=P.PWRIN),
Pin(num=9,name="EP",func=P.PWRIN)], "Package_SO:HVSSOP-8-1EP_3x3mm_P0.65mm")
AP2112 = mk("AP2112K-3.3", [Pin(num=1,name="VIN",func=P.PWRIN),Pin(num=2,name="GND",func=P.PWRIN),
Pin(num=3,name="EN",func=P.INPUT),Pin(num=4,name="NC",func=P.NOCONNECT),Pin(num=5,name="VOUT",func=P.PWROUT)],
"Package_TO_SOT_SMD:SOT-23-5")
u7 = TPS65131(ref="U7"); u8 = ldo("TPS7A4901")(ref="U8"); u9 = ldo("TPS7A3001")(ref="U9"); u10 = AP2112(ref="U10")
l1, l2 = L(value="4.7uH"), L(value="4.7uH")
d1, d2 = DS(value="MBRM120"), DS(value="MBRM120")
# ---- USB input: ferrite + bulk ----
fb1 = FB(value="600R@100MHz"); vbus += fb1[1]; fb1[2] += p5
cbulk = Cp("10uF","Capacitor_SMD:C_1206_3216Metric"); p5 += cbulk[1]; cbulk[2] += gnd
# ---- TPS65131: control supply + grounds + enables ----
u7["VIN"] += p5; c2 = Cp("4.7uF"); p5 += c2[1]; c2[2] += gnd
u7["PGND"] += gnd; u7["PGND2"] += gnd; u7["AGND"] += gnd; u7["EP"] += gnd
u7["ENP"] += p5; u7["ENN"] += p5 # enabled
u7["PSP"] += gnd; u7["PSN"] += gnd # forced PWM (low audio-band noise)
# BSW, NC left unconnected (no battery switch)
# boost: +5V -> L1 -> INP(switch); C1 at inductor input; D1 INP->+18V; C4 on +18V
c1 = Cp("4.7uF"); p5 += c1[1]; c1[2] += gnd
p5 += l1[1]; l1[2] += sw_boost; u7["INP"] += sw_boost; u7["INP2"] += sw_boost
d1[2] += sw_boost; d1[1] += p18 # Device D_Schottky: pin1=K, pin2=A -> anode at SW, cathode at +18V
c4 = Cp("22uF","Capacitor_SMD:C_1206_3216Metric"); p18 += c4[1]; c4[2] += gnd
u7["VPOS"] += p18
# positive feedback divider (+18.2V): R1 +18->FBP (C9 feed-forward), R2 FBP->gnd
r1 = R(value="1.4M"); r2 = R(value="100k"); c9 = Cp("6.8pF","Capacitor_SMD:C_0603_1608Metric")
p18 += r1[1]; r1[2] += fbp; c9[1] += p18; c9[2] += fbp
r2[1] += fbp; r2[2] += gnd; u7["FBP"] += fbp
# inverter input: +5V -> R7 -> INN ; C3 filter
r7 = R(value="100"); c3 = Cp("100nF"); p5 += r7[1]; r7[2] += innf; c3[1] += innf; c3[2] += gnd
u7["INN"] += innf; u7["INN2"] += innf
# inverter: OUTN -> L2 -> gnd ; D2 -18V->OUTN ; C5 on -18V
u7["OUTN"] += sw_inv; u7["OUTN2"] += sw_inv; l2[1] += sw_inv; l2[2] += gnd
d2[1] += sw_inv; d2[2] += n18 # anode at -18V, cathode at SW_INV
c5 = Cp("22uF","Capacitor_SMD:C_1206_3216Metric"); n18 += c5[1]; c5[2] += gnd
u7["VNEG"] += n18
# negative feedback: R3 -18->FBN (C10 ff), R4 VREF->FBN
r3 = R(value="1.5M"); r4 = R(value="100k"); c10 = Cp("7.5pF","Capacitor_SMD:C_0603_1608Metric")
n18 += r3[1]; r3[2] += fbn; c10[1] += n18; c10[2] += fbn
r4[1] += vref; r4[2] += fbn; u7["FBN"] += fbn
# VREF bypass + compensation
u7["VREF"] += vref; c8 = Cp("220nF"); vref += c8[1]; c8[2] += gnd
c7 = Cp("4.7nF","Capacitor_SMD:C_0603_1608Metric"); u7["CP"] += c7[1]; c7[2] += gnd # boost comp
c6 = Cp("10nF"); u7["CN"] += c6[1]; c6[2] += gnd # inverter comp
# ---- +15V LDO (TPS7A4901): IN<-+18, OUT->+15, divider, NR cap ----
u8["IN"] += p18; ci8 = Cp("1uF"); p18 += ci8[1]; ci8[2] += gnd
u8["OUT"] += p15; co8 = Cp("2.2uF"); p15 += co8[1]; co8[2] += gnd
u8["GND"] += gnd; u8["EP"] += gnd; u8["EN"] += p18
rt8 = R(value="116k"); rb8 = R(value="10k"); p15 += rt8[1]; rt8[2] += u8["FB"]; rb8[1] += u8["FB"]; rb8[2] += gnd
cnr8 = Cp("10nF"); u8["NR"] += cnr8[1]; cnr8[2] += gnd
# ---- -15V LDO (TPS7A3001): IN<--18, OUT->-15, divider, NR cap ----
u9["IN"] += n18; ci9 = Cp("1uF"); n18 += ci9[1]; ci9[2] += gnd
u9["OUT"] += n15; co9 = Cp("2.2uF"); n15 += co9[1]; co9[2] += gnd
u9["GND"] += gnd; u9["EP"] += gnd; u9["EN"] += n18
rt9 = R(value="117k"); rb9 = R(value="10k"); n15 += rt9[1]; rt9[2] += u9["FB"]; rb9[1] += u9["FB"]; rb9[2] += gnd
cnr9 = Cp("10nF"); u9["NR"] += cnr9[1]; cnr9[2] += gnd
# ---- 3V3 LDO (AP2112K): +5V -> +3V3 ----
u10["VIN"] += p5; u10["EN"] += p5; u10["GND"] += gnd; u10["VOUT"] += p3v3
ci10 = Cp("1uF"); p5 += ci10[1]; ci10[2] += gnd
co10 = Cp("1uF"); p3v3 += co10[1]; co10[2] += gnd
ERC()
out = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "kicad", "power_tree.net"))
generate_netlist(file_=out)
print("Power tree netlist ->", out)