#!/usr/bin/env python3
"""build_power.py — generate the CM5-carrier Power sheet, stage by stage.

Same connectivity model as build_ekey.py: every component pin gets a named label
(or a power:GND symbol) coincident with its connection point, so nets form by name
with no wire routing. Coords are grid-snapped to 1.27mm so labels bind to pins.

STAGE 1 (this file): input protection —
  J_PWR(15V) -> D_REV(LM74700 ideal-diode + Q_REV N-FET) -> VIN_PROT, with input TVS
  + bulk. Reverse-polarity + reverse-current via the ideal-diode controller.
Later stages (bucks) append to PLACEMENTS and re-run.

Datasheet items to CONFIRM against LM74700-Q1 (values, not topology):
  - C_VCAP value/reference (charge-pump reservoir; ~1uF typ)
  - EN handling (tied to +15V_IN for always-on; EN abs-max covers 15V)
  - Q_REV FET pick (Vds>=30V, low Rds(on), SOA for inrush) + optional gate resistor
  - TVS standoff (SMBJ18A: 18V standoff / ~29V clamp < buck Vin rating)
"""
import re, sys, os, uuid as uuidlib

ROOT_UUID = "e63e39d7-6ac0-4ffd-8aa3-1841a4541b55"
PROJECT = "CM5IO"
KSS = "/Applications/KiCad/KiCad.app/Contents/SharedSupport/symbols"
HERE = os.path.dirname(os.path.abspath(__file__))
CARRIER = os.path.join(HERE, "..", "carrier")

def U(label): return str(uuidlib.uuid5(uuidlib.NAMESPACE_DNS, "cm5carrier-power-" + label))
SHEET_SYMBOL_UUID = U("sheet-symbol")
SHEET_FILE_UUID = U("sheet-file")

def g(v): return round(round(v / 1.27) * 1.27, 2)

def paren_end(t, s):
    d = 0
    for i in range(s, len(t)):
        if t[i] == '(': d += 1
        elif t[i] == ')':
            d -= 1
            if d == 0: return i + 1
    raise ValueError("unbalanced")

def get_symbol(text, name):
    m = re.search(r'\(symbol\s+"' + re.escape(name) + r'"', text)
    return text[m.start():paren_end(text, m.start())] if m else None

def lib_pins(seg):
    out = {}
    for pm in re.finditer(r'\(pin\b', seg):
        s = pm.start(); ps = seg[s:paren_end(seg, s)]
        at = re.search(r'\(at\s+([\d.-]+)\s+([\d.-]+)\s+([\d.-]+)\)', ps)
        nu = re.search(r'\(number\s+"([^"]+)"', ps)
        if at and nu:
            out.setdefault(nu.group(1), (float(at.group(1)), float(at.group(2)), float(at.group(3))))
    return out

# ---- lib_symbol sources -----------------------------------------------------------
carrier_hs = open(os.path.join(CARRIER, "CM5_HighSpeed.kicad_sch")).read()
LIBS = {}  # lib_id -> (embed_block, {pin:(lx,ly,ang)})
def add_from(lib_id, srctext, srcname=None):
    srcname = srcname or lib_id.split(':')[-1]
    blk = get_symbol(srctext, lib_id) or get_symbol(srctext, srcname)
    if not blk: sys.exit("missing lib_symbol " + lib_id)
    if ('"' + lib_id + '"') not in blk[:60]:
        blk = blk.replace('(symbol "' + srcname + '"', '(symbol "' + lib_id + '"', 1)
    LIBS[lib_id] = (blk, lib_pins(blk))

# reuse R/C/C_Polarized/GND verbatim from the carrier (guaranteed compatible)
for lid in ["Device:R", "Device:C", "Device:C_Polarized", "power:GND"]:
    add_from(lid, carrier_hs)
add_from("Device:L", open(f"{KSS}/Device.kicad_sym").read(), "L")

def register_lm61460():
    """Hand-authored lib_symbol for LM61460-Q1 (14-pin HTSSOP-1EP + EP=15),
    built from the datasheet pin table (SNVSB70F). VREF(FB)=1.0V, VIN abs-max 42V.
    Pin types picked for cross-sheet ERC: VIN/PGND/AGND/BIAS/EP power_in, VCC power_out,
    SW output, FB/EN input, RT/CBOOT/RBOOT passive, PGOOD open_collector."""
    P = [('8','VIN1','power_in',-16.51,15.24,0), ('12','VIN2','power_in',-16.51,12.7,0),
         ('7','EN/SYNC','input',-16.51,5.08,0), ('1','BIAS','power_in',-16.51,0,0),
         ('6','RT','passive',-16.51,-7.62,0),
         ('10','SW','output',16.51,15.24,180), ('14','CBOOT','passive',16.51,12.7,180),
         ('13','RBOOT','passive',16.51,10.16,180), ('2','VCC','power_out',16.51,5.08,180),
         ('4','FB','passive',16.51,-2.54,180), ('5','PGOOD','open_collector',16.51,-7.62,180),
         ('9','PGND1','power_in',-7.62,-21.59,90), ('11','PGND2','power_in',-2.54,-21.59,90),
         ('3','AGND','power_in',2.54,-21.59,90), ('15','EP','power_in',7.62,-21.59,90)]
    pt = ""
    for num, name, ty, lx, ly, ang in P:
        pt += (f'\t\t\t(pin {ty} line (at {lx} {ly} {ang}) (length 3.81)\n'
               f'\t\t\t\t(name "{name}" (effects (font (size 1.27 1.27))))\n'
               f'\t\t\t\t(number "{num}" (effects (font (size 1.27 1.27))))\n\t\t\t)\n')
    blk = ('\t(symbol "CM5IO:LM61460-Q1"\n\t\t(pin_names (offset 1.016))\n'
           '\t\t(exclude_from_sim no)\n\t\t(in_bom yes)\n\t\t(on_board yes)\n'
           '\t\t(property "Reference" "U" (at 0 22.86 0) (effects (font (size 1.27 1.27))))\n'
           '\t\t(property "Value" "LM61460-Q1" (at 0 -26.67 0) (effects (font (size 1.27 1.27))))\n'
           '\t\t(property "Footprint" "Package_SO:Texas_HTSSOP-14-1EP_4.4x5mm_P0.65mm_EP3.4x5mm_Mask3.155x3.255mm_ThermalVias" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
           '\t\t(property "Datasheet" "https://www.ti.com/lit/ds/symlink/lm61460-q1.pdf" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
           '\t\t(symbol "LM61460-Q1_0_1"\n'
           '\t\t\t(rectangle (start -12.7 20.32) (end 12.7 -20.32) (stroke (width 0.254) (type default)) (fill (type background)))\n\t\t)\n'
           f'\t\t(symbol "LM61460-Q1_1_1"\n{pt}\t\t)\n\t)')
    LIBS["CM5IO:LM61460-Q1"] = (blk, lib_pins(blk))
register_lm61460()

def register_tps54202():
    """Hand-authored lib_symbol for TPS54202 (SOT-23-6), from the datasheet pin table
    (SLVSD26C): GND=1, SW=2, VIN=3, FB=4, EN=5, BOOT=6. VFB=0.596V, VIN abs-max 30V,
    fixed 500kHz. The KiCad lib symbol extends TPS54302 (no own pins) so it can't be
    embedded standalone. FB/EN typed passive: both are high-Z sense pins on
    resistor-divider-only nets (else ERC/analyzer flag no_driver)."""
    P = [('3','VIN','power_in',-13.97,5.08,0), ('5','EN','passive',-13.97,0,0),
         ('6','BOOT','passive',13.97,5.08,180), ('2','SW','output',13.97,2.54,180),
         ('4','FB','passive',13.97,-2.54,180), ('1','GND','power_in',0,-13.97,90)]
    pt = ""
    for num, name, ty, lx, ly, ang in P:
        pt += (f'\t\t\t(pin {ty} line (at {lx} {ly} {ang}) (length 3.81)\n'
               f'\t\t\t\t(name "{name}" (effects (font (size 1.27 1.27))))\n'
               f'\t\t\t\t(number "{num}" (effects (font (size 1.27 1.27))))\n\t\t\t)\n')
    blk = ('\t(symbol "CM5IO:TPS54202"\n\t\t(pin_names (offset 1.016))\n'
           '\t\t(exclude_from_sim no)\n\t\t(in_bom yes)\n\t\t(on_board yes)\n'
           '\t\t(property "Reference" "U" (at 0 12.7 0) (effects (font (size 1.27 1.27))))\n'
           '\t\t(property "Value" "TPS54202" (at 0 -16.51 0) (effects (font (size 1.27 1.27))))\n'
           '\t\t(property "Footprint" "Package_TO_SOT_SMD:SOT-23-6" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
           '\t\t(property "Datasheet" "https://www.ti.com/lit/ds/symlink/tps54202.pdf" (at 0 0 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
           '\t\t(symbol "TPS54202_0_1"\n'
           '\t\t\t(rectangle (start -10.16 10.16) (end 10.16 -10.16) (stroke (width 0.254) (type default)) (fill (type background)))\n\t\t)\n'
           f'\t\t(symbol "TPS54202_1_1"\n{pt}\t\t)\n\t)')
    LIBS["CM5IO:TPS54202"] = (blk, lib_pins(blk))
register_tps54202()
# +5v pre-exists in the carrier as a HIERARCHICAL net (local + hierarchical labels on the CM5
# sheets) -> expose it hierarchically here (a global label collides: same_local_global_label).
# +3V3_RF/+3V3_AUX are new nets introduced by this port -> global is fine.
GLOBAL_NETS = {"+3V3_RF", "+3V3_AUX", "PCIE_PWR_EN"}
HIER_NETS = {"+5v"}
# from KiCad libs (renamed to full lib_id)
add_from("power:PWR_FLAG", open(f"{KSS}/power.kicad_sym").read(), "PWR_FLAG")
add_from("Power_Management:LM74700", open(f"{KSS}/Power_Management.kicad_sym").read(), "LM74700")
add_from("Transistor_FET:Q_NMOS_GDS", open(f"{KSS}/Transistor_FET.kicad_sym").read(), "Q_NMOS_GDS")
add_from("Device:D_TVS", open(f"{KSS}/Device.kicad_sym").read(), "D_TVS")
add_from("Connector:Screw_Terminal_01x02", open(f"{KSS}/Connector.kicad_sym").read(), "Screw_Terminal_01x02")

# ---- placement engine -------------------------------------------------------------
body = []
def emit_label(net, x, y, ang=0):
    body.append(f'\t(label "{net}"\n\t\t(at {x} {y} {ang})\n'
                f'\t\t(effects (font (size 1.27 1.27)) (justify left))\n\t\t(uuid "{U("l-"+net+f"-{x}-{y}")}")\n\t)\n')
def emit_glabel(net, x, y, ang=0):
    body.append(f'\t(global_label "{net}"\n\t\t(shape input)\n\t\t(at {x} {y} {ang})\n'
                f'\t\t(effects (font (size 1.27 1.27)) (justify left))\n\t\t(uuid "{U("gl-"+net+f"-{x}-{y}")}")\n\t)\n')
def emit_hlabel(net, x, y, ang=0):
    body.append(f'\t(hierarchical_label "{net}"\n\t\t(shape passive)\n\t\t(at {x} {y} {ang})\n'
                f'\t\t(effects (font (size 1.27 1.27)) (justify left))\n\t\t(uuid "{U("hl-"+net+f"-{x}-{y}")}")\n\t)\n')
def emit_gnd(x, y, tag):
    body.append(f'\t(symbol\n\t\t(lib_id "power:GND")\n\t\t(at {x} {y} 0)\n\t\t(unit 1)\n'
                f'\t\t(exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no)\n\t\t(uuid "{U("gnd-"+tag)}")\n'
                f'\t\t(property "Reference" "#PWR{tag}" (at {x} {y+3} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
                f'\t\t(property "Value" "GND" (at {x} {y+2} 0) (effects (font (size 1.27 1.27))))\n'
                f'\t\t(property "Footprint" "" (at {x} {y} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
                f'\t\t(property "Datasheet" "" (at {x} {y} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
                f'\t\t(pin "1" (uuid "{U("gp-"+tag)}"))\n'
                f'\t\t(instances (project "{PROJECT}" (path "/{ROOT_UUID}/{SHEET_SYMBOL_UUID}" (reference "#PWR{tag}") (unit 1))))\n\t)\n')
def emit_flag(x, y, tag):
    body.append(f'\t(symbol\n\t\t(lib_id "power:PWR_FLAG")\n\t\t(at {x} {y} 0)\n\t\t(unit 1)\n'
                f'\t\t(exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no)\n\t\t(uuid "{U("flg-"+tag)}")\n'
                f'\t\t(property "Reference" "#FLG{tag}" (at {x} {y-3} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
                f'\t\t(property "Value" "PWR_FLAG" (at {x} {y-2} 0) (effects (font (size 1.27 1.27))))\n'
                f'\t\t(property "Footprint" "" (at {x} {y} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
                f'\t\t(property "Datasheet" "" (at {x} {y} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
                f'\t\t(pin "1" (uuid "{U("fp-"+tag)}"))\n'
                f'\t\t(instances (project "{PROJECT}" (path "/{ROOT_UUID}/{SHEET_SYMBOL_UUID}" (reference "#FLG{tag}") (unit 1))))\n\t)\n')

def place(lib_id, ref, value, footprint, ox, oy, pin_nets):
    """Place a symbol at grid-snapped (ox,oy); attach each pin's net by name.
    pin_nets: {pinnum: 'NET' | ('gnd',) }. GND pins use a power:GND symbol."""
    ox, oy = g(ox), g(oy)
    _, pins = LIBS[lib_id]
    extra = ('\t\t(property "Description" "" (at %g %g 0) (effects (font (size 1.27 1.27)) (hide yes)))\n' % (ox, oy))
    sym = (f'\t(symbol\n\t\t(lib_id "{lib_id}")\n\t\t(at {ox} {oy} 0)\n\t\t(unit 1)\n'
           f'\t\t(exclude_from_sim no) (in_bom yes) (on_board yes) (dnp no)\n\t\t(uuid "{U("sym-"+ref)}")\n'
           f'\t\t(property "Reference" "{ref}" (at {ox+2.54} {oy-1} 0) (effects (font (size 1.27 1.27)) (justify left)))\n'
           f'\t\t(property "Value" "{value}" (at {ox+2.54} {oy+1} 0) (effects (font (size 1.27 1.27)) (justify left)))\n'
           f'\t\t(property "Footprint" "{footprint}" (at {ox} {oy} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n'
           f'\t\t(property "Datasheet" "" (at {ox} {oy} 0) (effects (font (size 1.27 1.27)) (hide yes)))\n' + extra)
    for num in sorted(pins, key=lambda n: int(re.sub(r'\D', '', n) or 0)):
        sym += f'\t\t(pin "{num}" (uuid "{U("p-"+ref+"-"+num)}"))\n'
    sym += f'\t\t(instances (project "{PROJECT}" (path "/{ROOT_UUID}/{SHEET_SYMBOL_UUID}" (reference "{ref}") (unit 1))))\n\t)\n'
    body.append(sym)
    for num, net in pin_nets.items():
        lx, ly, _ = pins[num]; px, py = round(ox + lx, 2), round(oy - ly, 2)
        if net == ('gnd',): emit_gnd(px, py, ref + num)
        elif isinstance(net, tuple) and net[0] == 'global': emit_glabel(net[1], px, py)
        elif net in GLOBAL_NETS: emit_glabel(net, px, py)
        elif net in HIER_NETS: emit_hlabel(net, px, py)
        else: emit_label(net, px, py)
    return ox, oy

# ---- STAGE 1: input protection ----------------------------------------------------
GND = ('gnd',)
place("Connector:Screw_Terminal_01x02", "J_PWR", "15V_DC_IN", "TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2_1x02_P5.00mm_Horizontal",
      80, 80, {'1': '+15V_IN', '2': GND})
drev = place("Power_Management:LM74700", "D_REV", "LM74700-Q1", "Package_TO_SOT_SMD:SOT-23-6",
      120, 80, {'6': '+15V_IN', '3': '+15V_IN', '4': 'VIN_PROT', '5': 'GATE_REV', '1': 'VCAP_REV', '2': GND})
place("Transistor_FET:Q_NMOS_GDS", "Q_REV", "CSD18540 (Vds>=30V)", "Package_TO_SOT_SMD:TO-263-2",
      140, 90, {'1': 'GATE_REV', '2': 'VIN_PROT', '3': '+15V_IN'})
place("Device:C", "C_VCAP", "1u", "Capacitor_SMD:C_0402_1005Metric", 120, 100, {'1': 'VCAP_REV', '2': GND})
tvs = place("Device:D_TVS", "TVS_IN", "SMBJ18A", "Diode_SMD:D_SMB", 160, 80, {'1': 'VIN_PROT', '2': GND})
place("Device:C_Polarized", "C_BULK1", "100u/25V", "Capacitor_SMD:CP_Elec_8x10", 170, 80, {'1': 'VIN_PROT', '2': GND})
place("Device:C", "C_BULK2", "10u/50V", "Capacitor_SMD:C_1210_3225Metric", 180, 80, {'1': 'VIN_PROT', '2': GND})
# PWR_FLAGs co-located exactly on real pins so they bind: +15V_IN @ D_REV.6 (ANODE),
# VIN_PROT @ TVS.1 (a power_in driver for VIN_PROT, needed once bucks consume it).
def pin_abs(origin, lib_id, num):
    lx, ly, _ = LIBS[lib_id][1][num]
    return round(origin[0] + lx, 2), round(origin[1] - ly, 2)
fx, fy = pin_abs(drev, "Power_Management:LM74700", "6"); emit_flag(fx, fy, "15IN")
vx, vy = pin_abs(tvs, "Device:D_TVS", "1"); emit_flag(vx, vy, "VINP")

# ---- STAGE 2: U_BUCK5  15V -> +5v  5A (LM61460-Q1, sync, 42V absmax) --------------
# FB divider: Vout = VREF*(1+Rfbt/Rfbb), VREF=1.0V -> Rfbt/Rfbb=4 -> 40.2k/10.0k = 5.02V.
# fsw ~= 400kHz (RT 31.6k); L=4.7uH; Cout 2x47u/16V; Cin 2x10u/50V; Cboot 100n; BIAS->+5v.
place("CM5IO:LM61460-Q1", "U_BUCK5", "LM61460-Q1", "Package_SO:Texas_HTSSOP-14-1EP_4.4x5mm_P0.65mm_EP3.4x5mm_Mask3.155x3.255mm_ThermalVias",
      110, 150, {'8': 'VIN_PROT', '12': 'VIN_PROT', '7': 'VIN_PROT', '1': '+5v', '2': 'VCC5',
                 '6': 'RT5', '5': 'PG5', '4': 'FB5', '10': 'SW5', '14': 'BOOT5', '13': 'RBOOT5',
                 '9': GND, '11': GND, '3': GND, '15': GND})
l5 = place("Device:L", "L5", "4.7uH/7A", "Inductor_SMD:L_Bourns_SRP1245A", 150, 138, {'1': 'SW5', '2': '+5v'})
place("Device:C", "C_BOOT5", "100n", "Capacitor_SMD:C_0402_1005Metric", 140, 132, {'1': 'SW5', '2': 'BOOT5'})
place("Device:R", "R_BOOT5", "4.7", "Resistor_SMD:R_0402_1005Metric", 145, 128, {'1': 'RBOOT5', '2': 'BOOT5'})
place("Device:R", "R_RT5", "31.6k", "Resistor_SMD:R_0402_1005Metric", 90, 158, {'1': 'RT5', '2': GND})
place("Device:R", "R_PG5", "100k", "Resistor_SMD:R_0402_1005Metric", 135, 143, {'1': 'PG5', '2': '+5v'})
place("Device:R", "R_FBT5", "40.2k", "Resistor_SMD:R_0402_1005Metric", 165, 150, {'1': '+5v', '2': 'FB5'})
place("Device:R", "R_FBB5", "10.0k", "Resistor_SMD:R_0402_1005Metric", 165, 158, {'1': 'FB5', '2': GND})
place("Device:C", "C_VCC5", "1u", "Capacitor_SMD:C_0402_1005Metric", 100, 158, {'1': 'VCC5', '2': GND})
place("Device:C", "C_IN5a", "10u/50V", "Capacitor_SMD:C_1210_3225Metric", 95, 145, {'1': 'VIN_PROT', '2': GND})
place("Device:C", "C_IN5b", "10u/50V", "Capacitor_SMD:C_1210_3225Metric", 100, 145, {'1': 'VIN_PROT', '2': GND})
place("Device:C", "C_OUT5a", "47u/16V", "Capacitor_SMD:C_1210_3225Metric", 175, 138, {'1': '+5v', '2': GND})
place("Device:C", "C_OUT5b", "47u/16V", "Capacitor_SMD:C_1210_3225Metric", 180, 138, {'1': '+5v', '2': GND})
# +5v is now sourced by this buck -> PWR_FLAG co-located on L5 pin2 (+5v)
p5x, p5y = pin_abs(l5, "Device:L", "2"); emit_flag(p5x, p5y, "P5V")

# ---- STAGE 3: U_BUCK33R  15V -> +3V3_RF  4A (LM61460-Q1, sync) --------------------
# FB divider: VREF=1.0V -> Rfbt/Rfbb=2.32 -> 23.2k/10.0k = 3.32V.
# Same support set as stage 2 (RT 31.6k ~420kHz, L 4.7uH/7A: ripple ~1.3A = 33% of 4A).
# BIAS -> +3V3_RF (datasheet: BIAS>=3.1V uses output LDO path; falls back to VIN below).
# EN <- PCIE_PWR_EN (CM5 Module1 pin 106, global net) + 100k pull-down (= reference R14)
# so the Wi-Fi rail stays OFF until the CM5 asserts it.
place("CM5IO:LM61460-Q1", "U_BUCK33R", "LM61460-Q1", "Package_SO:Texas_HTSSOP-14-1EP_4.4x5mm_P0.65mm_EP3.4x5mm_Mask3.155x3.255mm_ThermalVias",
      230, 150, {'8': 'VIN_PROT', '12': 'VIN_PROT', '7': 'PCIE_PWR_EN', '1': '+3V3_RF', '2': 'VCC33',
                 '6': 'RT33', '5': 'PG33', '4': 'FB33', '10': 'SW33', '14': 'BOOT33', '13': 'RBOOT33',
                 '9': GND, '11': GND, '3': GND, '15': GND})
l33 = place("Device:L", "L33", "4.7uH/7A", "Inductor_SMD:L_Bourns_SRP1245A", 270, 138, {'1': 'SW33', '2': '+3V3_RF'})
place("Device:C", "C_BOOT33", "100n", "Capacitor_SMD:C_0402_1005Metric", 260, 132, {'1': 'SW33', '2': 'BOOT33'})
place("Device:R", "R_BOOT33", "4.7", "Resistor_SMD:R_0402_1005Metric", 265, 128, {'1': 'RBOOT33', '2': 'BOOT33'})
place("Device:R", "R_RT33", "31.6k", "Resistor_SMD:R_0402_1005Metric", 210, 158, {'1': 'RT33', '2': GND})
place("Device:R", "R_PG33", "100k", "Resistor_SMD:R_0402_1005Metric", 255, 143, {'1': 'PG33', '2': '+3V3_RF'})
place("Device:R", "R_FBT33", "23.2k", "Resistor_SMD:R_0402_1005Metric", 285, 150, {'1': '+3V3_RF', '2': 'FB33'})
place("Device:R", "R_FBB33", "10.0k", "Resistor_SMD:R_0402_1005Metric", 285, 158, {'1': 'FB33', '2': GND})
place("Device:R", "R_EN33", "100k", "Resistor_SMD:R_0402_1005Metric", 200, 165, {'1': 'PCIE_PWR_EN', '2': GND})
place("Device:C", "C_VCC33", "1u", "Capacitor_SMD:C_0402_1005Metric", 220, 158, {'1': 'VCC33', '2': GND})
place("Device:C", "C_IN33a", "10u/50V", "Capacitor_SMD:C_1210_3225Metric", 215, 145, {'1': 'VIN_PROT', '2': GND})
place("Device:C", "C_IN33b", "10u/50V", "Capacitor_SMD:C_1210_3225Metric", 220, 145, {'1': 'VIN_PROT', '2': GND})
place("Device:C", "C_OUT33a", "47u/16V", "Capacitor_SMD:C_1210_3225Metric", 240, 165, {'1': '+3V3_RF', '2': GND})
place("Device:C", "C_OUT33b", "47u/16V", "Capacitor_SMD:C_1210_3225Metric", 245, 165, {'1': '+3V3_RF', '2': GND})
# +3V3_RF is now sourced by this buck -> PWR_FLAG on L33 pin2 (replaces the E-key temp flag)
p33x, p33y = pin_abs(l33, "Device:L", "2"); emit_flag(p33x, p33y, "P33R")

# ---- STAGE 4: U_BUCK33A  15V -> +3V3_AUX  ~1A (TPS54202, sync, 500kHz fixed) ------
# Quiet rail: GPS + PPS Schmitt buffer + IO pull-ups + RTC domain (design doc ~1A).
# Abs-max gate PASSED (thin): VIN abs-max 30V vs SMBJ18A clamp <=29.2V at full rated
# 600W surge (0.8V margin, transient-only; 28V operating max is not the surge number).
# FB per TI's 3.3V table row: R2=100k/R3=22.1k + Cff 56pF -> 0.596*(1+100/22.1)=3.29V.
# L=10uH (dI~0.52A, Ipk~1.26A), Cout 2x22uF. EN abs-max is 7V -> NO direct VIN tie;
# UVLO divider 866k/110k (Ip=0.7uA/Ih=1.55uA, Ven 1.21/1.19V) -> start 10.1V/stop 8.6V,
# EN sits ~1.9V at 15V in (~3.5V at 29V surge clamp, still < 7V abs-max).
place("CM5IO:TPS54202", "U_BUCK33A", "TPS54202", "Package_TO_SOT_SMD:SOT-23-6",
      230, 95, {'3': 'VIN_PROT', '5': 'EN33A', '6': 'BOOT33A', '2': 'SW33A',
                '4': 'FB33A', '1': GND})
l33a = place("Device:L", "L33A", "10uH/2.5A", "Inductor_SMD:L_1210_3225Metric", 260, 85, {'1': 'SW33A', '2': '+3V3_AUX'})
place("Device:C", "C_BOOT33A", "100n", "Capacitor_SMD:C_0402_1005Metric", 250, 78, {'1': 'SW33A', '2': 'BOOT33A'})
place("Device:R", "R_UVT33A", "866k", "Resistor_SMD:R_0402_1005Metric", 205, 85, {'1': 'VIN_PROT', '2': 'EN33A'})
place("Device:R", "R_UVB33A", "110k", "Resistor_SMD:R_0402_1005Metric", 205, 95, {'1': 'EN33A', '2': GND})
place("Device:R", "R_FBT33A", "100k", "Resistor_SMD:R_0402_1005Metric", 280, 95, {'1': '+3V3_AUX', '2': 'FB33A'})
place("Device:C", "C_FF33A", "56p", "Capacitor_SMD:C_0402_1005Metric", 288, 95, {'1': '+3V3_AUX', '2': 'FB33A'})
place("Device:R", "R_FBB33A", "22.1k", "Resistor_SMD:R_0402_1005Metric", 280, 105, {'1': 'FB33A', '2': GND})
# NB refdes must not differ from stage-3's C_IN33a by case alone — SPICE names are
# case-insensitive and the simulate_subcircuits testbench collides ("device already exists").
place("Device:C", "C_IN33Aa", "10u/50V", "Capacitor_SMD:C_1210_3225Metric", 215, 105, {'1': 'VIN_PROT', '2': GND})
place("Device:C", "C_INHF33A", "100n", "Capacitor_SMD:C_0402_1005Metric", 220, 105, {'1': 'VIN_PROT', '2': GND})
place("Device:C", "C_OUT33Aa", "22u/16V", "Capacitor_SMD:C_1210_3225Metric", 265, 105, {'1': '+3V3_AUX', '2': GND})
place("Device:C", "C_OUT33Ab", "22u/16V", "Capacitor_SMD:C_1210_3225Metric", 270, 105, {'1': '+3V3_AUX', '2': GND})
# +3V3_AUX sourced here -> PWR_FLAG on L33A pin2
p33ax, p33ay = pin_abs(l33a, "Device:L", "2"); emit_flag(p33ax, p33ay, "P33A")

# ---- file scaffold ----------------------------------------------------------------
libsyms = "\n".join(blk for blk, _ in LIBS.values())
sheet = ('(kicad_sch\n\t(version 20231120)\n\t(generator "eeschema")\n\t(generator_version "8.0")\n'
         f'\t(uuid "{SHEET_FILE_UUID}")\n\t(paper "A4")\n'
         '\t(title_block\n\t\t(title "CM5 Carrier - Power")\n\t\t(rev "A")\n'
         '\t\t(comment 1 "15V inlet -> ideal-diode -> VIN_PROT -> 3 bucks (+5V, +3V3_RF, +3V3_AUX)")\n\t)\n'
         '\t(lib_symbols\n' + libsyms + '\n\t)\n' + "".join(body) +
         '\t(sheet_instances\n\t\t(path "/"\n\t\t\t(page "5")\n\t\t)\n\t)\n)\n')
assert sheet.count('(') == sheet.count(')'), f"UNBALANCED {sheet.count('(')} vs {sheet.count(')')}"
print("sheet-symbol uuid:", SHEET_SYMBOL_UUID)
print("parens balanced:", sheet.count('('))
out = os.path.join(CARRIER, "Power.kicad_sch")
if '--apply' in sys.argv: open(out, 'w').write(sheet); print("WROTE " + out)
else: print("DRY RUN (--apply to write)")
