Block O (modem): U19 A7670SA-LASC (C18548276, LGA-124) - 19 signal nets + 39 GND + 66 NC, all 124 pads accounted for. UART via 1.8V-side nets (_M suffix); USB joins J9 directly (VBUS A4/A9/B4/B9 + DP/DM); USB_BOOT -> TP8; VDD_EXT -> 1V8_MODEM (shifter reference only); NETLIGHT (1.8V) -> R109 4.7k -> Q22 MMBT3904 low-side, reusing the existing Rmled1+D4G1 LED string re-driven: MODEM_LED label -> 3V3, D4G1 cathode GND label -> MODEM_LED_K. Block P (3.8V rail): 24V -> D20 SS34 || MAIN5V -> D21 PMEG3050EP (C50603) -> VIN_MODEM -> U20 AP64350SP-13 (C2071691, 40V/3.5A) -> L20 4.7uH -> VBAT_MODEM. Values from datasheet DS41976: FB 0.8V (R110 75k / R111 20k = 3.8V), RT R117 200k = 500kHz (pin must not float), COMP R116 14k + C141 3.3nF + C142 47pF, BST C129 100nF, EP -> GND. EN = MODEM_PWR_EN (PC10) with R113 100k pull-down: rail DEFAULTS OFF (invariant #1); EN low >= 2s = HARD_CYCLE (invariant #3). Bulk: 2x 220uF/10V (C2827940) + 22uF/1uF/100nF/33pF/10pF + R112 1k bleed + D22 SMAJ5.0A TVS (C113952). VERIFY at layout: D22 pad-1-is-cathode against the fp marker. Block Q (SIM): U22 MFF2_SIM symbol (new in Drogheda-sym; FOOTPRINT TBD "Drogheda:MFF2_5x6_TBD" - build from the chosen carrier chip datasheet; PCB sync will flag it, expected). SIM_VDD 100nF + PESD5V0S1BA on DATA/CLK/RST (D23-25, C2827694, orientation cloned from D14). Block R (level shift): 2x 74LVC2T45DC (C730237, direction-fixed). U23 DIR=GND: 3V3 DIN_4G -> 1.8V DIN_4G_M. U24 DIR=1V8: DOUT_4G_M -> DOUT_4G (also feeds U18 RXD2) and MODEM_STATUS_M -> MODEM_STATUS (PC9). R106 100R taps DOUT_4G -> USART6_RX (PC7). DNP 0R bypasses R107/R108 across the RTCM path. Q20/Q21 2N7002 open-drain on PWRKEY/RESET with 100k pull-downs - MCU absent leaves the modem free (invariant #1). PWRKEY timing: SIMCom >=50ms on / >=2.5s off. CORRECTIONS this pass: (1) the schematic has 382 WIRE SEGMENTS - the earlier "zero wires" finding was a regex artifact (multi-line xy); label-on-pin work was unaffected (all verified against the PCB), but the LED relabel needed a wire-graph trace, now in the script. (2) 9 stale no_connect markers removed from MCU pins that now carry labels (PC1-PC3, PA7, PC8-PC12). (3) A7670SA is a 124-pad LGA, not the 88 claimed in the modem survey. NEXT: Leo runs ERC + Update PCB from Schematic (U22 will complain - footprint TBD, skip it), then T5 = LTC4040 input-limit errata fix + U7/L7 disposition, T6 = RF Block S + netclasses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJ4eAB6zjQT49RVwpgfKdt
408 lines
21 KiB
Python
408 lines
21 KiB
Python
#!/usr/bin/env python3
|
|
"""v6 T4 - Blocks O+P+Q+R: A7670SA modem, 3.8V OR-input rail, MFF2 SIM, level shifters.
|
|
|
|
Sources of record: A7670SA pin map = EasyEDA/LCSC C18548276 symbol (VBAT 55/56/57, RF_ANT 60
|
|
match the SIMCom HW design guide). AP64350 values = datasheet DS41976: Vref 0.8V, RT 200k
|
|
= 500kHz (must not float), COMP 14k+3.3nF(+47pF), BST 100nF, L 4.7uH, EP->GND.
|
|
|
|
Usage: python3 2026-08-25_v6_T4_lte_island.py [--apply]
|
|
"""
|
|
import os, sys, shutil, uuid, re
|
|
|
|
KI = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '03-Architecture-Hardware', 'kicad')
|
|
SCH = os.path.join(KI, 'hbox-watchdog-v6.kicad_sch')
|
|
DSYM= os.path.join(KI, 'Drogheda-sym.kicad_sym')
|
|
VSYM= os.path.join(KI, 'Drogheda-v6.kicad_sym')
|
|
PCB = os.path.join(KI, 'hbox-watchdog-v6.kicad_pcb')
|
|
APPLY = '--apply' in sys.argv
|
|
ROOT = '/561403b3-895e-4153-a7be-3876ae930d43'
|
|
NC = '<NC>'
|
|
|
|
def f(v):
|
|
v = 0.0 if abs(v) < 1e-9 else v
|
|
return '%.10g' % v
|
|
|
|
def bal(t):
|
|
d=0; instr=False; esc=False
|
|
for ch in t:
|
|
if esc: esc=False; continue
|
|
if ch=='\\': esc=True; continue
|
|
if ch=='"': instr = not instr; continue
|
|
if instr: continue
|
|
if ch=='(': d+=1
|
|
elif ch==')': d-=1
|
|
return d
|
|
|
|
def span_from(text, i):
|
|
d=0; instr=False; esc=False; j=i
|
|
while j < len(text):
|
|
ch=text[j]
|
|
if esc: esc=False
|
|
elif ch=='\\': esc=True
|
|
elif ch=='"': instr = not instr
|
|
elif not instr:
|
|
if ch=='(': d+=1
|
|
elif ch==')':
|
|
d-=1
|
|
if d==0: return j+1
|
|
j+=1
|
|
raise ValueError('unbalanced')
|
|
|
|
PIN_RE = re.compile(r'\(pin \w+ \w+\s*\(?at ([-\d.]+) ([-\d.]+) (\d+)\)(?:(?!\(pin ).)*?\(number "([^"]+)"', re.S)
|
|
PIN_RE2= re.compile(r'\(pin \w+ \w+\s*\n\s*\(at ([-\d.]+) ([-\d.]+) (\d+)\)(?:(?!\(pin ).)*?\(number "([^"]+)"', re.S)
|
|
|
|
def libpins(libsrc, name):
|
|
i = libsrc.find('(symbol "%s"' % name)
|
|
if i < 0: return None
|
|
j = libsrc.find('\n (symbol "', i+10)
|
|
if j < 0: j = libsrc.find('\n\t\t(symbol "', i+10)
|
|
body = libsrc[i: j if j>0 else len(libsrc)]
|
|
pins = PIN_RE2.findall(body) or PIN_RE.findall(body)
|
|
return [(num, float(x), float(y)) for x,y,rot,num in pins]
|
|
|
|
sch = open(SCH, encoding='utf-8').read()
|
|
dsym = open(DSYM, encoding='utf-8').read()
|
|
vsym = open(VSYM, encoding='utf-8').read()
|
|
|
|
# ---------- 0. MFF2 symbol into Drogheda-sym (if absent) ----------
|
|
MFF2 = ''' (symbol "MFF2_SIM"
|
|
(exclude_from_sim no)
|
|
(in_bom yes)
|
|
(on_board yes)
|
|
(property "Reference" "U"
|
|
(at 0 12.7 0)
|
|
(effects (font (size 1.27 1.27)))
|
|
)
|
|
(property "Value" "MFF2_SIM"
|
|
(at 0 10.16 0)
|
|
(effects (font (size 1.27 1.27)))
|
|
)
|
|
(property "Footprint" "Drogheda:MFF2_5x6_TBD"
|
|
(at 0 -5.08 0)
|
|
(effects (font (size 1.27 1.27)))
|
|
(hide yes)
|
|
)
|
|
(property "Datasheet" "~"
|
|
(at 0 -7.62 0)
|
|
(effects (font (size 1.27 1.27)))
|
|
(hide yes)
|
|
)
|
|
(property "Description" "MFF2 / DFN-8 5x6 mm soldered M2M SIM (ETSI TS 102 671). Pads: 1=C1 VCC, 2=C2 RST, 3=C3 CLK, 4=C4 NC, 5=C5 GND, 6=C6 VPP (NC), 7=C7 I/O, 8=C8 NC. FOOTPRINT TBD - build from the chosen carrier chip datasheet before layout. Carrier-supplied part, not on LCSC."
|
|
(at 0 -10.16 0)
|
|
(effects (font (size 1.27 1.27)))
|
|
(hide yes)
|
|
)
|
|
(symbol "MFF2_SIM_0_1"
|
|
(rectangle
|
|
(start -7.62 8.89)
|
|
(end 7.62 -8.89)
|
|
(stroke (width 0.254) (type default))
|
|
(fill (type background))
|
|
)
|
|
)
|
|
(symbol "MFF2_SIM_1_1"
|
|
'''
|
|
MFF2_PINS=[('1','VCC','power_in',-12.7,5.08,0),('2','RST','input',-12.7,2.54,0),('3','CLK','input',-12.7,0,0),
|
|
('4','NC4','passive',-12.7,-2.54,0),('5','GND','power_in',0,-13.97,90),('6','VPP','passive',12.7,-2.54,180),
|
|
('7','IO','bidirectional',12.7,2.54,180),('8','NC8','passive',12.7,0,180)]
|
|
for num,nm,tp,px,py,rot in MFF2_PINS:
|
|
MFF2 += (' (pin %s line\n (at %s %s %d)\n (length 5.08)\n'
|
|
' (name "%s"\n (effects (font (size 1.27 1.27)))\n )\n'
|
|
' (number "%s"\n (effects (font (size 1.27 1.27)))\n )\n )\n') % (tp,f(px),f(py),rot,nm,num)
|
|
MFF2 += ' )\n )\n'
|
|
mff2_added = False
|
|
if '(symbol "MFF2_SIM"' not in dsym:
|
|
end = dsym.rstrip(); assert end.endswith(')')
|
|
dsym = end[:-1].rstrip('\n') + '\n' + MFF2 + ')\n'
|
|
mff2_added = True
|
|
|
|
# ---------- 1. locate donors / relabel targets ----------
|
|
lib_cached = sch.split('(lib_symbols',1)[1]
|
|
def cachedpins(name): return libpins(lib_cached, name)
|
|
|
|
inst = {}
|
|
for b in re.finditer(r'\n\t\(symbol\n(?:(?!\n\t\(symbol\n).)*', sch, re.S):
|
|
blk=b.group(0)
|
|
r=re.search(r'\(property "Reference" "([^"]+)"',blk); l=re.search(r'\(lib_id "([^"]+)"',blk); a=re.search(r'\(at ([-\d.]+) ([-\d.]+) (\d+)\)',blk)
|
|
if r and l and a: inst[r.group(1)]=(l.group(1),float(a.group(1)),float(a.group(2)))
|
|
|
|
def find_labels(text):
|
|
d={}
|
|
for m in re.finditer(r'\n\t\((label|global_label) "([^"]+)"\s*\n\s*\(at ([-\d.]+) ([-\d.]+) (\d+)\)', text):
|
|
d[(round(float(m.group(3)),3), round(float(m.group(4)),3))] = (m.group(2), m.start()+1)
|
|
return d
|
|
labs = find_labels(sch)
|
|
|
|
# D4G1 cathode relabel target: the GND label at (74.93, 265.43), PROVEN wire-connected to
|
|
# D4G1 pin1 by graph trace on 2026-08-25 (the sch has 382 wire segments - the earlier
|
|
# "zero wires" note was a regex artifact; labels here sit on WIRES, not on pins).
|
|
kx,ky = 74.93, 265.43
|
|
if (kx,ky) not in labs or labs[(kx,ky)][0] != 'GND':
|
|
sys.exit('expected the D4G1-cathode GND label at %s,%s; found %s' % (kx,ky,labs.get((kx,ky))))
|
|
if (55.88,260.35) not in labs or labs[(55.88,260.35)][0] != 'MODEM_LED':
|
|
sys.exit('orphan MODEM_LED label not found at (55.88,260.35)')
|
|
|
|
# wire vertices + all existing pin coords, for the collision guard
|
|
wire_vts = set()
|
|
for m in re.finditer(r'\(wire\s*\n\s*\(pts\s*\n((?:\s*\(xy [-\d.]+ [-\d.]+\)\s*\n?)+)', sch):
|
|
for a,b in re.findall(r'\(xy ([-\d.]+) ([-\d.]+)\)', m.group(1)):
|
|
wire_vts.add((round(float(a),3), round(float(b),3)))
|
|
pin_coords = set()
|
|
for ref,(lb_,X_,Y_) in inst.items():
|
|
pp = cachedpins(lb_) or []
|
|
for num,px,py in pp:
|
|
pin_coords.add((round(X_+px,3), round(Y_-py,3)))
|
|
|
|
# D14 orientation donor (PESD): read its pad nets from the PCB
|
|
pcb = open(PCB, encoding='utf-8').read()
|
|
d14 = re.search(r'\n\t\(footprint (?:(?!\n\t\(footprint ).)*?\(property "Reference" "D14"(?:(?!\n\t\(footprint ).)*', pcb, re.S).group(0)
|
|
d14pads = dict(re.findall(r'\(pad "(\d)"(?:(?!\(pad ").)*?\(net "([^"]*)"\)', d14, re.S))
|
|
sig_pin = '1' if d14pads.get('2')=='GND' else '2'
|
|
d14lib = inst['D14'][0]
|
|
|
|
# ---------- 2. cache the vendor symbols + MFF2 into the schematic ----------
|
|
def grab(libsrc, name, prefix):
|
|
i = libsrc.find(' (symbol "%s"\n' % name)
|
|
if i < 0: sys.exit('symbol %s not found in source lib' % name)
|
|
j = libsrc.index('(', i)
|
|
k = span_from(libsrc, j)
|
|
return libsrc[j:k].replace('(symbol "%s"' % name, '(symbol "%s:%s"' % (prefix,name), 1)
|
|
|
|
cache_add = ''
|
|
for nm in ('A7670SA-LASC','AP64350SP-13','74LVC2T45DC,125','PMEG3050EP,115','SMAJ5.0A_C113952'):
|
|
if '(symbol "Drogheda-v6:%s"' % nm not in sch:
|
|
cache_add += '\n\t\t' + grab(vsym, nm, 'Drogheda-v6')
|
|
if '(symbol "Drogheda-sym:MFF2_SIM"' not in sch:
|
|
cache_add += '\n\t\t' + grab(dsym, 'MFF2_SIM', 'Drogheda-sym')
|
|
|
|
m = re.search(r'\n\t\(lib_symbols\n', sch)
|
|
k = span_from(sch, sch.index('(', m.start()))
|
|
sch = sch[:k-1] + cache_add + '\n\t' + sch[k-1:]
|
|
|
|
def newpins(name, src):
|
|
pp = libpins(src, name)
|
|
if not pp: sys.exit('no pins found for %s' % name)
|
|
return pp
|
|
|
|
# ---------- 3. build additions ----------
|
|
add = ''
|
|
nlab = nnc = 0
|
|
newlabels = []
|
|
def add_lab(net,x,y,rot,just):
|
|
global add,nlab
|
|
add += ('\n\t(label "%s"\n\t\t(at %s %s %d)\n\t\t(effects\n\t\t\t(font\n\t\t\t\t(size 1.27 1.27)\n'
|
|
'\t\t\t)\n\t\t\t(justify %s)\n\t\t)\n\t\t(uuid "%s")\n\t)') % (net,f(x),f(y),rot,just,uuid.uuid4())
|
|
nlab += 1; newlabels.append((net,round(x,3),round(y,3)))
|
|
def add_nc(x,y):
|
|
global add,nnc
|
|
add += '\n\t(no_connect\n\t\t(at %s %s)\n\t\t(uuid "%s")\n\t)' % (f(x),f(y),uuid.uuid4())
|
|
nnc += 1
|
|
def put(net,x,y,rot,just):
|
|
if net == NC: add_nc(x,y)
|
|
else: add_lab(net,x,y,rot,just)
|
|
|
|
def mksym(lib_id, ref, val, x, y, fp, npins, lcsc='', dnp=False):
|
|
props=[('Reference',ref,x-3,y-3,''),('Value',val,x+3,y-3,''),('Footprint',fp,x,y,'\n\t\t\t(hide yes)'),
|
|
('Datasheet','~',x,y,'\n\t\t\t(hide yes)'),('Description','',x,y,'\n\t\t\t(hide yes)')]
|
|
if lcsc: props.append(('LCSC',lcsc,x,y,'\n\t\t\t(hide yes)'))
|
|
p=''
|
|
for nm,v,px,py,hide in props:
|
|
p += ('\t\t(property "%s" "%s"\n\t\t\t(at %s %s 0)%s\n\t\t\t(show_name no)\n\t\t\t(do_not_autoplace no)\n'
|
|
'\t\t\t(effects\n\t\t\t\t(font\n\t\t\t\t\t(size 1.27 1.27)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n') % (nm,v,f(px),f(py),hide)
|
|
pn=''.join('\t\t(pin "%s"\n\t\t\t(uuid "%s")\n\t\t)\n' % (n,uuid.uuid4()) for n in npins)
|
|
return ('\n\t(symbol\n\t\t(lib_id "%s")\n\t\t(at %s %s 0)\n\t\t(unit 1)\n\t\t(body_style 1)\n'
|
|
'\t\t(exclude_from_sim no)\n\t\t(in_bom yes)\n\t\t(on_board yes)\n\t\t(in_pos_files yes)\n'
|
|
'\t\t(dnp %s)\n\t\t(uuid "%s")\n%s%s\t\t(instances\n\t\t\t(project "hbox-watchdog-v6"\n'
|
|
'\t\t\t\t(path "%s"\n\t\t\t\t\t(reference "%s")\n\t\t\t\t\t(unit 1)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t)'
|
|
) % (lib_id,f(x),f(y),'yes' if dnp else 'no',uuid.uuid4(),p,pn,ROOT,ref)
|
|
|
|
def orient(px,py):
|
|
if abs(px) < 0.01: return 90,'left bottom'
|
|
return (0,'left bottom') if px>0 else (180,'right bottom')
|
|
|
|
# ----- Block O: U19 A7670SA -----
|
|
U19X,U19Y = 330.0, 330.0
|
|
NET19 = {55:'VBAT_MODEM',56:'VBAT_MODEM',57:'VBAT_MODEM',
|
|
9:'DOUT_4G_M',10:'DIN_4G_M',1:'MODEM_PWRKEY_M',16:'MODEM_RST_M',66:'MODEM_STATUS_M',
|
|
52:'NETLIGHT_1V8',24:'USB_VBUS',27:'USB_DP',28:'USB_DM',6:'MODEM_BOOT',15:'1V8_MODEM',
|
|
30:'SIM_VDD',31:'SIM_DATA',32:'SIM_CLK',33:'SIM_RST',60:'RF_LTE'}
|
|
GND19 = {2,17,18,29,39,45,46,54,58,59,61,62,63,64,65,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,91,92,94}
|
|
a7pins = newpins('A7670SA-LASC', vsym)
|
|
if len(a7pins) != 124: sys.exit('A7670SA pin extract failed: %d' % len(a7pins))
|
|
add += mksym('Drogheda-v6:A7670SA-LASC','U19','A7670SA-LASC',U19X,U19Y,
|
|
'Drogheda-v6:LGA-124_L24.0-W24.0-P1.0-A7670C-LASS',[num for num,_,_ in a7pins],'C18548276')
|
|
u19 = {'lab':0,'gnd':0,'nc':0}
|
|
for num,px,py in a7pins:
|
|
n=int(num); x,y = U19X+px, U19Y-py
|
|
rot,just = orient(px,py)
|
|
if n in NET19: add_lab(NET19[n],x,y,rot,just); u19['lab']+=1
|
|
elif n in GND19: add_lab('GND',x,y,rot,just); u19['gnd']+=1
|
|
else: add_nc(x,y); u19['nc']+=1
|
|
|
|
# ----- Block P -----
|
|
U20X,U20Y = 262.0, 300.0
|
|
bpins = newpins('AP64350SP-13', vsym)
|
|
add += mksym('Drogheda-v6:AP64350SP-13','U20','AP64350SP-13',U20X,U20Y,
|
|
'Drogheda-v6:SO-8_L4.9-W3.9-P1.27-LS6.0-BL-EP',[num for num,_,_ in bpins],'C2071691')
|
|
NET20 = {'1':'BST_MODEM','2':'VIN_MODEM','3':'MODEM_PWR_EN','4':'RT_MODEM','5':'FB_MODEM',
|
|
'6':'COMP_MODEM','7':'GND','8':'SW_MODEM','9':'GND'}
|
|
for num,px,py in bpins:
|
|
rot,just = orient(px,py)
|
|
add_lab(NET20[num],U20X+px,U20Y-py,rot,just)
|
|
|
|
# diodes: D_Schottky and PMEG symbol both 1=K 2=A; SMAJ pad1 assumed K (VERIFY at layout)
|
|
DIODES=[('D20','SS34','Device:D_Schottky','Diode_SMD:D_SMA','',240.0,286.0,'VIN_MODEM','24V'),
|
|
('D21','PMEG3050EP','Drogheda-v6:PMEG3050EP,115','Drogheda-v6:SOD-128_L3.7-W2.5-LS4.7-RD','C50603',240.0,293.0,'VIN_MODEM','MAIN5V'),
|
|
('D22','SMAJ5.0A','Drogheda-v6:SMAJ5.0A_C113952','Drogheda-v6:SMA_L4.4-W2.6-LS5.0-RD','C113952',300.0,370.0,'VBAT_MODEM','GND')]
|
|
for ref,val,lib,fp,lcsc,x,y,knet,anet in DIODES:
|
|
nm = lib.split(':')[1]
|
|
pp = newpins(nm, vsym) if lib.startswith('Drogheda-v6') else cachedpins(lib)
|
|
add += mksym(lib,ref,val,x,y,fp,[num for num,_,_ in pp],lcsc)
|
|
for num,px,py in pp:
|
|
rot,just = orient(px,py)
|
|
add_lab(knet if num=='1' else anet, x+px,y-py,rot,just)
|
|
|
|
L20X,L20Y=300.0,300.0
|
|
lp={num:(px,py) for num,px,py in cachedpins('Device:L')}
|
|
add += mksym('Device:L','L20','4.7uH/5A',L20X,L20Y,'Inductor_SMD:L_Bourns-SRU8028_8.0x8.0mm',['1','2'])
|
|
add_lab('SW_MODEM', L20X+lp['1'][0],L20Y-lp['1'][1],90,'left bottom')
|
|
add_lab('VBAT_MODEM',L20X+lp['2'][0],L20Y-lp['2'][1],90,'left bottom')
|
|
|
|
PASS=[('C127','10uF','Device:C','Capacitor_SMD:C_0805_2012Metric','',252.0,286.0,'VIN_MODEM','GND',False),
|
|
('C128','1uF','Device:C','Capacitor_SMD:C_0603_1608Metric','',258.0,286.0,'VIN_MODEM','GND',False),
|
|
('C129','100nF','Device:C','Capacitor_SMD:C_0402_1005Metric','',276.0,286.0,'BST_MODEM','SW_MODEM',False),
|
|
('R117','200k/1%','Device:R','Resistor_SMD:R_0603_1608Metric','',246.0,310.0,'RT_MODEM','GND',False),
|
|
('R116','14k','Device:R','Resistor_SMD:R_0603_1608Metric','',252.0,310.0,'COMP_MODEM','COMP_RC',False),
|
|
('C141','3.3nF','Device:C','Capacitor_SMD:C_0402_1005Metric','',258.0,310.0,'COMP_RC','GND',False),
|
|
('C142','47pF','Device:C','Capacitor_SMD:C_0402_1005Metric','',264.0,310.0,'COMP_MODEM','GND',False),
|
|
('R110','75k/1%','Device:R','Resistor_SMD:R_0603_1608Metric','',282.0,310.0,'VBAT_MODEM','FB_MODEM',False),
|
|
('R111','20k/1%','Device:R','Resistor_SMD:R_0603_1608Metric','',288.0,310.0,'FB_MODEM','GND',False),
|
|
('R113','100k','Device:R','Resistor_SMD:R_0603_1608Metric','',270.0,310.0,'MODEM_PWR_EN','GND',False),
|
|
('C130','220uF/10V','Device:C_Polarized','Capacitor_SMD:CP_Elec_6.3x7.7','C2827940',310.0,370.0,'VBAT_MODEM','GND',False),
|
|
('C131','220uF/10V','Device:C_Polarized','Capacitor_SMD:CP_Elec_6.3x7.7','C2827940',318.0,370.0,'VBAT_MODEM','GND',False),
|
|
('C132','22uF','Device:C','Capacitor_SMD:C_0805_2012Metric','C86817',326.0,370.0,'VBAT_MODEM','GND',False),
|
|
('C133','1uF','Device:C','Capacitor_SMD:C_0603_1608Metric','',332.0,370.0,'VBAT_MODEM','GND',False),
|
|
('C134','100nF','Device:C','Capacitor_SMD:C_0402_1005Metric','',338.0,370.0,'VBAT_MODEM','GND',False),
|
|
('C135','33pF','Device:C','Capacitor_SMD:C_0402_1005Metric','',344.0,370.0,'VBAT_MODEM','GND',False),
|
|
('C136','10pF','Device:C','Capacitor_SMD:C_0402_1005Metric','',350.0,370.0,'VBAT_MODEM','GND',False),
|
|
('R112','1k','Device:R','Resistor_SMD:R_0603_1608Metric','',356.0,370.0,'VBAT_MODEM','GND',False),
|
|
('C125','2.2uF','Device:C','Capacitor_SMD:C_0603_1608Metric','',380.0,300.0,'1V8_MODEM','GND',False),
|
|
('C126','100nF','Device:C','Capacitor_SMD:C_0402_1005Metric','',400.0,352.0,'SIM_VDD','GND',False),
|
|
('R106','100R','Device:R','Resistor_SMD:R_0603_1608Metric','',390.0,255.0,'DOUT_4G','USART6_RX',False),
|
|
('R109','4.7k','Device:R','Resistor_SMD:R_0603_1608Metric','',380.0,286.0,'NETLIGHT_1V8','Q22_BASE',False),
|
|
('R114','100k','Device:R','Resistor_SMD:R_0603_1608Metric','',252.0,340.0,'MODEM_PWRKEY','GND',False),
|
|
('R115','100k','Device:R','Resistor_SMD:R_0603_1608Metric','',276.0,340.0,'MODEM_RST','GND',False),
|
|
('R107','0R-DNP','Device:R','Resistor_SMD:R_0603_1608Metric','',264.0,255.0,'DOUT_4G_M','DOUT_4G',True),
|
|
('R108','0R-DNP','Device:R','Resistor_SMD:R_0603_1608Metric','',270.0,255.0,'DIN_4G','DIN_4G_M',True),
|
|
('C137','100nF','Device:C','Capacitor_SMD:C_0402_1005Metric','',248.0,395.0,'1V8_MODEM','GND',False),
|
|
('C138','100nF','Device:C','Capacitor_SMD:C_0402_1005Metric','',288.0,395.0,'3V3','GND',False),
|
|
('C139','100nF','Device:C','Capacitor_SMD:C_0402_1005Metric','',248.0,418.0,'1V8_MODEM','GND',False),
|
|
('C140','100nF','Device:C','Capacitor_SMD:C_0402_1005Metric','',288.0,418.0,'3V3','GND',False)]
|
|
for ref,val,lib,fp,lcsc,x,y,n1,n2,dnp in PASS:
|
|
pp={num:(px,py) for num,px,py in cachedpins(lib)}
|
|
add += mksym(lib,ref,val,x,y,fp,['1','2'],lcsc,dnp=dnp)
|
|
add_lab(n1,x+pp['1'][0],y-pp['1'][1],90,'left bottom')
|
|
add_lab(n2,x+pp['2'][0],y-pp['2'][1],90,'left bottom')
|
|
|
|
# ----- Block Q: MFF2 + SIM ESD -----
|
|
U22X,U22Y=400.0,340.0
|
|
mp = newpins('MFF2_SIM', dsym)
|
|
add += mksym('Drogheda-sym:MFF2_SIM','U22','MFF2-SIM-carrier',U22X,U22Y,'Drogheda:MFF2_5x6_TBD',[num for num,_,_ in mp])
|
|
MQ={'1':'SIM_VDD','2':'SIM_RST','3':'SIM_CLK','4':NC,'5':'GND','6':NC,'7':'SIM_DATA','8':NC}
|
|
for num,px,py in mp:
|
|
rot,just = orient(px,py)
|
|
put(MQ[num],U22X+px,U22Y-py,rot,just)
|
|
dp={num:(px,py) for num,px,py in cachedpins(d14lib)}
|
|
for ref,sig,x,y in [('D23','SIM_DATA',420.0,352.0),('D24','SIM_CLK',426.0,352.0),('D25','SIM_RST',432.0,352.0)]:
|
|
add += mksym(d14lib,ref,'PESD5V0S1BA',x,y,'Diode_SMD:D_SOD-323',['1','2'],'C2827694')
|
|
add_lab(sig if sig_pin=='1' else 'GND', x+dp['1'][0], y-dp['1'][1],90,'left bottom')
|
|
add_lab('GND' if sig_pin=='1' else sig, x+dp['2'][0], y-dp['2'][1],90,'left bottom')
|
|
|
|
# ----- Block R -----
|
|
spins = newpins('74LVC2T45DC,125', vsym)
|
|
for ref,x,y,nets in [('U23',255.0,385.0,{'1':'1V8_MODEM','2':'DIN_4G_M','3':NC,'4':'GND','5':'GND','6':'GND','7':'DIN_4G','8':'3V3'}),
|
|
('U24',255.0,408.0,{'1':'1V8_MODEM','2':'DOUT_4G_M','3':'MODEM_STATUS_M','4':'GND','5':'1V8_MODEM','6':'MODEM_STATUS','7':'DOUT_4G','8':'3V3'})]:
|
|
add += mksym('Drogheda-v6:74LVC2T45DC,125',ref,'74LVC2T45',x,y,'Drogheda-v6:VSSOP-8_L2.1-W2.4-P0.50-LS3.2-BL',[num for num,_,_ in spins],'C730237')
|
|
for num,px,py in spins:
|
|
rot,just = orient(px,py)
|
|
put(nets[num],x+px,y-py,rot,just)
|
|
|
|
qn = cachedpins('Device:Q_NMOS_GSD')
|
|
for ref,gate,drain,x,y in [('Q20','MODEM_PWRKEY','MODEM_PWRKEY_M',246.0,330.0),('Q21','MODEM_RST','MODEM_RST_M',270.0,330.0)]:
|
|
add += mksym('Device:Q_NMOS_GSD',ref,'2N7002',x,y,'Package_TO_SOT_SMD:SOT-23',[num for num,_,_ in qn])
|
|
NQ={'1':gate,'2':'GND','3':drain}
|
|
for num,px,py in qn:
|
|
rot,just = orient(px,py)
|
|
add_lab(NQ[num],x+px,y-py,rot,just)
|
|
qb = cachedpins('Transistor_BJT:Q_NPN_BEC')
|
|
add += mksym('Transistor_BJT:Q_NPN_BEC','Q22','MMBT3904',390.0,290.0,'Package_TO_SOT_SMD:SOT-23',[num for num,_,_ in qb])
|
|
NB={'1':'Q22_BASE','2':'GND','3':'MODEM_LED_K'}
|
|
for num,px,py in qb:
|
|
rot,just = orient(px,py)
|
|
add_lab(NB[num],390.0+px,290.0-py,rot,just)
|
|
tp = cachedpins('Connector:TestPoint')
|
|
add += mksym('Connector:TestPoint','TP8','USB_BOOT',420.0,300.0,'TestPoint:TestPoint_Pad_D1.5mm',[num for num,_,_ in tp])
|
|
add_lab('MODEM_BOOT',420.0+tp[0][1],300.0-tp[0][2],90,'left bottom')
|
|
|
|
pf = cachedpins('power:PWR_FLAG')
|
|
for ref,net,x in [('PF15','VIN_MODEM',300.0),('PF16','VBAT_MODEM',310.0),('PF17','1V8_MODEM',320.0)]:
|
|
add += mksym('power:PWR_FLAG',ref,'PWR_FLAG',x,255.0,'',[num for num,_,_ in pf])
|
|
add_lab(net,x+pf[0][1],255.0-pf[0][2],90,'left bottom')
|
|
|
|
MCU_LBL=[('MODEM_PWRKEY',482.6,436.88),('MODEM_STATUS',482.6,439.42),('MODEM_PWR_EN',482.6,441.96),('MODEM_RST',482.6,444.5)]
|
|
for net,x,y in MCU_LBL:
|
|
add_lab(net,x,y,180,'right bottom')
|
|
INTENTIONAL = {(round(x,3),round(y,3)) for _,x,y in MCU_LBL}
|
|
|
|
# remove stale no_connect markers on MCU pins that now carry labels (PC8-PC11 from this
|
|
# run, and PC1/PC2/PC3/PA7/PC12 which got labels in T3b while their NCs remained)
|
|
STALE_NC = INTENTIONAL | {(482.6,419.1),(482.6,421.64),(482.6,424.18),(518.16,411.48),(482.6,447.04)}
|
|
removed_nc = 0
|
|
def rm_stale_nc(text):
|
|
global removed_nc
|
|
out=[]; last=0
|
|
for m in re.finditer(r'\n\t\(no_connect\s*\n\s*\(at ([-\d.]+) ([-\d.]+)\)(?:(?!\n\t\().)*?\n\t\)', text, re.S):
|
|
c=(round(float(m.group(1)),3),round(float(m.group(2)),3))
|
|
if c in STALE_NC:
|
|
out.append(text[last:m.start()]); last=m.end(); removed_nc+=1
|
|
out.append(text[last:])
|
|
return ''.join(out)
|
|
sch = rm_stale_nc(sch)
|
|
|
|
# ---------- 4. relabels (positions re-found on the current sch) ----------
|
|
labs = find_labels(sch)
|
|
def relabel(coord, old, new):
|
|
global sch
|
|
name,start = labs[coord]
|
|
assert name==old, (coord,name,old)
|
|
end = span_from(sch, sch.index('(', start))
|
|
seg = sch[start:end].replace('"%s"'%old, '"%s"'%new, 1)
|
|
sch = sch[:start] + seg + sch[end:]
|
|
todo = [((55.88,260.35),'MODEM_LED','3V3'),((kx,ky),'GND','MODEM_LED_K')]
|
|
for coord,old,new in sorted(todo, key=lambda t:-labs[t[0]][1]):
|
|
relabel(coord,old,new)
|
|
|
|
# ---------- 5. collision check + write ----------
|
|
existing = (set(find_labels(sch).keys()) | wire_vts | pin_coords) - INTENTIONAL
|
|
coll = [(n,x,y) for n,x,y in newlabels if (x,y) in existing]
|
|
if coll: sys.exit('LABEL COLLISION with existing labels/wires/pins: %s' % coll[:5])
|
|
|
|
end = sch.rstrip(); assert end.endswith(')')
|
|
sch = end[:-1].rstrip('\n') + add + '\n)\n'
|
|
|
|
print('U19: %d net labels + %d GND + %d NC (sum %d, expect 124)' % (u19['lab'],u19['gnd'],u19['nc'],sum(u19.values())))
|
|
print('symbols: U19 U20 U22 U23 U24 D20-D25 Q20-Q22 L20 TP8 PF15-17 + %d passives (2 DNP)' % len(PASS))
|
|
print('labels added: %d no_connects added: %d' % (nlab,nnc))
|
|
print('relabels: MODEM_LED@(55.88,260.35)->3V3 ; GND@D4G1.K(%s,%s)->MODEM_LED_K' % (kx,ky))
|
|
print('PESD orientation cloned from D14: signal pin = %s' % sig_pin)
|
|
print('stale no_connects removed from labeled MCU pins: %d (expect 9)' % removed_nc)
|
|
print('MFF2 symbol added to Drogheda-sym: %s' % mff2_added)
|
|
print('paren: sch=%d dsym=%d' % (bal(sch),bal(dsym)))
|
|
if not APPLY:
|
|
print('\nDRY RUN - nothing written. Re-run with --apply.'); sys.exit(0)
|
|
if bal(sch)!=0 or bal(dsym)!=0: sys.exit('REFUSING: paren imbalance')
|
|
shutil.copy2(SCH,SCH+'.T4bak'); shutil.copy2(DSYM,DSYM+'.T4bak')
|
|
open(SCH,'w',encoding='utf-8').write(sch)
|
|
open(DSYM,'w',encoding='utf-8').write(dsym)
|
|
print('\nAPPLIED. Backups: *.T4bak')
|