main
John Lauer R0 engineering checkpoint: checked schematic, sourcing, servo evidence and safety-policy code; PCB and qualification incomplete 10054d3 1h ago
#!/usr/bin/env python3
"""Independent R0 connectivity source; preliminary until ERC, review and simulation pass."""
import json
from pathlib import Path
P=Path(__file__).parents[1]; C=[]; N={}
BASIC=json.loads((P/'design/basic-mpn-map.json').read_text())
def part(ref,value,fp,pins,block,mpn=None,sym=None):
 c=dict(ref=ref,value=value,mpn=mpn or value,fp=fp,block=block,pins={str(k):v for k,v in pins.items()},sym=sym or 'Load:'+ref)
 C.append(c)
 for k,v in pins.items():
  if v is not None:N.setdefault(v,[]).append(f'{ref}.{k}')
 return c
def r(ref,value,a,b,block='control',mpn=None):return part(ref,value,'Resistor_SMD:R_0402_1005Metric',{1:a,2:b},block,mpn or BASIC['resistors'][value],sym='Device:R')
def cap(ref,value,a,b,block='control'):return part(ref,value,('Capacitor_SMD:C_0805_2012Metric' if value=='4.7u 25V' else 'Capacitor_SMD:C_0603_1608Metric'),{1:a,2:b},block,BASIC['capacitors'][value],sym='Device:C')
# MCU pins from STM32G0B1KCTx symbol, cross-check DS13560 Rev6 before freeze.
part('U1','STM32G0B1KCT6','Package_QFP:LQFP-32_7x7mm_P0.8mm',{1:None,2:None,3:None,4:'3V3',5:'GND',6:'NRST',7:'TEMP_SENSE',8:'DUT_ADC',9:'UART_TX',10:'UART_RX',11:'ARM_CLK',12:'MCU_ENABLE',13:'WDI',14:'FAN_TACH',15:'USB_DETECT',16:None,17:'HW_OK',18:None,19:None,20:'ALERT',21:None,22:'USB_DM',23:'USB_DP',24:'SWDIO',25:'SWCLK',26:'LATCH_Q',27:'FAULT_CLEAR',28:None,29:None,30:'I2C_SCL',31:'I2C_SDA',32:None},'digital',sym='MCU_ST_STM32G0:STM32G0B1KCTx')
part('U2','DAC60501ZDGSR','Load:TI_DGS0010A',{1:'3V3',2:'DAC_OUT',3:None,4:'GND',5:'3V3',6:'I2C_SCL',7:'GND',8:'I2C_SDA',9:None,10:'VREF_2V5'},'servo')
part('U3','OPA189IDBVR','Package_TO_SOT_SMD:SOT-23-5',{1:'SERVO_OUT',2:'GND',3:'I_SET',4:'SERVO_FB',5:'V_GATE'},'servo')
part('U4','INA226AIDGST','Load:TI_DGS0010A',{1:'GND',2:'GND',3:'ALERT',4:'I2C_SDA',5:'I2C_SCL',6:'3V3',7:'GND',8:'LOAD_POS',9:'SHUNT_N_SENSE',10:'SHUNT_P_SENSE'},'telemetry')
part('U5','TPS61040DBVR','Package_TO_SOT_SMD:SOT-23-5',{1:'BOOST_SW',2:'GND',3:'BOOST_FB',4:'5V_AUX',5:'5V_AUX'},'auxiliary')
# STWD100 pin mapping must be verified from exact manufacturer package before freezing.
part('U6','STWD100NYWY3F','Package_TO_SOT_SMD:SOT-23-5',{1:'HW_OK',2:'GND',3:'GND',4:'WDI',5:'3V3'},'protection')
# LM339B outputs wired-OR: hot/short thermistor, open thermistor, overcurrent, overvoltage.
part('U7','LM339BIDR','Package_SO:SOIC-14_3.9x8.7mm_P1.27mm',{1:'HW_OK',2:'HW_OK',3:'V_GATE',4:'TEMP_HOT_LIMIT',5:'TEMP_SENSE',6:'TEMP_SENSE',7:'TEMP_OPEN_LIMIT',8:'SHUNT_P_SENSE',9:'I_HARD_LIMIT',10:'DUT_ADC',11:'DUT_OV_LIMIT',12:'GND',13:'HW_OK',14:'HW_OK'},'protection')
part('U8','SN74LVC1G175DBVR','Package_TO_SOT_SMD:SOT-23-6',{1:'ARM_CLK',2:'GND',3:'3V3',4:'LATCH_Q',5:'3V3',6:'HW_OK'},'protection',sym='74xGxx:74LVC1G175')
part('U9','TLV75533PDBVR','Package_TO_SOT_SMD:SOT-23-5',{1:'5V_AUX',2:'GND',3:'5V_AUX',4:None,5:'3V3'},'auxiliary')
# Power sink, low-side four-terminal shunt; each Kelvin terminal is its own net.
part('Q1','IXTP15N50L2','Package_TO_SOT_THT:TO-220-3_Horizontal_TabUp',{1:'GATE',2:'LOAD_POS',3:'SHUNT_P'},'power')
part('RSH1','0.020 Ohm 0.5% Kelvin','Load:LVK12',{1:'SHUNT_P',2:'SHUNT_P_SENSE',3:'SHUNT_N_SENSE',4:'GND'},'power','LVK12R020DER')
part('D1','B540C-13-F','Diode_SMD:D_SMC',{1:'LOAD_POS',2:'DUT_FUSED'},'power')
part('F1','5 A 32 V fast fuse','Fuse:Fuse_1206_3216Metric',{1:'DUT_POS',2:'DUT_FUSED'},'power','0466005.NR')
# Gate defaults clamped from auxiliary gate rail; both release transistors must be on.
part('Q2','MMBT3904','Package_TO_SOT_SMD:SOT-23',{1:'CLAMP_BASE',2:'GND',3:'GATE'},'protection','MMBT3904-7-F')
part('Q3','2N7002','Package_TO_SOT_SMD:SOT-23',{1:'LATCH_Q',2:'RELEASE_MID',3:'CLAMP_BASE'},'protection','2N7002-7-F')
part('Q4','2N7002','Package_TO_SOT_SMD:SOT-23',{1:'MCU_ENABLE',2:'GND',3:'RELEASE_MID'},'protection','2N7002-7-F')
# MCU open-drain FAULT_CLEAR is additional asynchronous clear; pull high never allowed.
r('R1','10k','3V3','HW_OK','protection');r('R2','100k','MCU_ENABLE','GND','protection');r('R3','100k','ARM_CLK','GND','protection')
r('R4','10k','V_GATE','CLAMP_BASE','protection');r('R5','100k','CLAMP_BASE','GND','protection');r('R6','100k','GATE','GND','power');r('R7','1k','SERVO_OUT','GATE','servo')
r('R8','33k','DAC_OUT','SET_DIV','servo',mpn='ERA-2AEB333X');r('R9','5.6k','SET_DIV','I_SET','servo',mpn='TNPW04025K60BEED');r('R10','1k','I_SET','GND','servo',mpn='TNPW04021K00BEED');cap('C1','100n','I_SET','GND','servo')
r('R11','10k','SHUNT_P_SENSE','SERVO_FB','servo');cap('C2','1n','SERVO_OUT','SERVO_FB','servo')
# Differential sense capacitor returns to Kelvin minus; optional series filter pending noise review.
cap('C3','100n','SHUNT_P_SENSE','SHUNT_N_SENSE','telemetry')
r('R13','4.7k','3V3','I2C_SDA','digital');r('R14','4.7k','3V3','I2C_SCL','digital');r('R15','10k','3V3','ALERT','digital');r('R16','10k','3V3','NRST','digital');cap('C4','100n','NRST','GND','digital')
# Fault clear via diode so MCU high cannot override wired-OR faults.
part('D2','BAT54WS-7-F','Diode_SMD:D_SOD-323',{1:'FAULT_CLEAR',2:'HW_OK'},'protection')
# Conservative analogue limits. Values selected from Adom stock, to be tolerance analysed.
r('R17','10k','3V3','TEMP_SENSE','protection');r('R18','10k','VREF_2V5','TEMP_HOT_LIMIT','protection');r('R19','3.3k','TEMP_HOT_LIMIT','GND','protection')
r('R20','1k','3V3','TEMP_OPEN_LIMIT','protection');r('R21','10k','TEMP_OPEN_LIMIT','GND','protection')
r('R22','33k','VREF_2V5','I_HARD_LIMIT','protection');r('R23','1k','I_HARD_LIMIT','GND','protection')
r('R24','100k','DUT_POS','DUT_DIV','telemetry');r('R25','10k','DUT_DIV','GND','telemetry');r('R26','10k','DUT_DIV','DUT_ADC','telemetry');cap('C5','10n','DUT_ADC','GND','telemetry')
# ADC input clamps; BAV199 dual series diode pin 3 midpoint.
part('D3','BAV199','Package_TO_SOT_SMD:SOT-23',{1:'GND',2:'3V3',3:'DUT_ADC'},'telemetry','BAV199-7-F')
r('R27','1k','VREF_2V5','DUT_OV_LIMIT','protection');r('R28','10k','DUT_OV_LIMIT','GND','protection')
# Always-on 5V auxiliary power; USB is data-only and cannot backfeed PS2.
part('J8','USB4105-GF-A','Connector_USB:USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal',{ 'A1':'GND','A4':'USB_VBUS','A5':'CC1','A6':'USB_DP_CONN','A7':'USB_DM_CONN','A8':None,'A9':'USB_VBUS','A12':'GND','B1':'GND','B4':'USB_VBUS','B5':'CC2','B6':'USB_DP_CONN','B7':'USB_DM_CONN','B8':None,'B9':'USB_VBUS','B12':'GND','SH':'GND'},'digital')
r('R29','4.7k','CC1','GND','digital');r('R30','4.7k','CC2','GND','digital');r('R31','22','USB_DP_CONN','USB_DP','digital');r('R32','22','USB_DM_CONN','USB_DM','digital')
r('R33','100k','USB_VBUS','USB_DETECT','digital');r('R34','100k','USB_DETECT','GND','digital')
part('U10','USBLC6-2SC6','Package_TO_SOT_SMD:SOT-23-6',{1:'USB_DP_CONN',2:'GND',3:'USB_DM_CONN',4:'USB_DM_CONN',5:'USB_VBUS',6:'USB_DP_CONN'},'digital')
# Gate amplifier rail: preliminary 12.33V from 1.233V reference using 680k+220k / 100k.
part('L1','10uH','Load:L_Abracon_AMPLH5030S',{1:'5V_AUX',2:'BOOST_SW'},'auxiliary','AMPLH5030S-100MT')
part('D4','B140-13-F','Diode_SMD:D_SMA',{1:'V_GATE',2:'BOOST_SW'},'auxiliary')
r('R35','680k','V_GATE','BOOST_DIV','auxiliary');r('R36','220k','BOOST_DIV','BOOST_FB','auxiliary');r('R37','100k','BOOST_FB','GND','auxiliary')
cap('C6','4.7u 25V','V_GATE','GND','auxiliary');cap('C7','4.7u 10V','5V_AUX','GND','auxiliary');cap('C8','1u','3V3','GND','auxiliary');cap('C9','1u','5V_AUX','GND','auxiliary');cap('C10','1u','VREF_2V5','GND','servo')
for idx,rail in enumerate(['3V3','3V3','V_GATE','3V3','5V_AUX','3V3','V_GATE','3V3'],11):cap('C'+str(idx),'100n',rail,'GND')
# Fan supplied continuously by auxiliary rail: fail-safe full-speed cooling, tach monitored.
part('J9','Fan connector','Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical',{1:'5V_AUX',2:'GND',3:'FAN_TACH'},'mechanical','M20-9990346')
r('R38','10k','3V3','FAN_TACH','digital')
part('J10','Case sensor connector','Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical',{1:'TEMP_SENSE',2:'GND'},'mechanical','M20-9990246')
part('J11','SWD pads','Connector_PinHeader_1.27mm:PinHeader_1x05_P1.27mm_Vertical',{1:'3V3',2:'SWDIO',3:'SWCLK',4:'NRST',5:'GND'},'digital','FTS-105-01-L-S')
# Quiet analog return: no external short from the Kelvin minus terminal to the power ground.
# The only conductive relationship is inside RSH1. Do not add an AGND-to-GND jumper or plane stitch.
analog_returns={'U2':['4'],'U3':['2'],'U4':['7'],'U7':['12'], 'R10':['2'],'C1':['2'],'C10':['2'],'C12':['2'],'C13':['2'],'C14':['2'],'C17':['2'],'R19':['2'],'R23':['2'],'R28':['2']}
for c in C:
 for pin in analog_returns.get(c['ref'],[]):
  assert c['pins'][pin]=='GND', (c['ref'],pin)
  c['pins'][pin]='SHUNT_N_SENSE'
# Rebuild connectivity after return reassignment.
N.clear()
for c in C:
 for pin,name in c['pins'].items():
  if name is not None:N.setdefault(name,[]).append(c['ref']+'.'+pin)
# Machine pins and contacts use the qualified project-local Adom library.
for ref,x,y in [('MP1',0,0),('MP2',60,0),('MP3',0,68),('MP4',60,68)]:
 c=part(ref,'MachinePinMediumShort','Molecule:MachinePinMediumShort',{1:'GND'},'molecule','MachinePinMediumShort');c['fixed']={'x':x,'y':y}
for i,(name,x,y) in enumerate([('DUT_POS',8,0),('GND',12,0),('5V_AUX',20,0),('GND',24,0),('UART_TX',36,0),('UART_RX',40,0),('GND',44,0)],1):
 c=part('J'+str(i),'MachineContactMedium','Molecule:MachineContactMedium',{1:name},'molecule','MachineContactMedium');c['fixed']={'x':x,'y':y}
# Native probe landings, explicitly available away from the sink.
for i,n in enumerate(['DUT_POS','LOAD_POS','SHUNT_P_SENSE','I_SET','GATE','V_GATE','3V3','TEMP_SENSE','HW_OK','LATCH_Q','GND'],1):part('TP'+str(i),n,'TestPoint:TestPoint_Pad_D1.0mm',{1:n},'probe','no part')
out={'design':'adom-custom-electronic-load','revision':'R0 draft','components':C,'nets':N,'molecule':{'pin':'MachinePinMediumShort','grid_mm':2,'pin_span_mm':[60,68],'board_mm':[64,72],'edge_margin_mm':2,'corner_pins_net':'GND'},'review_required':['All custom pin maps against current primary datasheets','Shunt pin numbering and exact land pattern','Auxiliary fault response, DAC low-end linearity','USB pin remapping and pin multiplexing','Physical power contacts current rating','Heatsink and clip interface']}
(P/'design/netlist.json').write_text(json.dumps(out,indent=2));print(len(C),'components',len(N),'nets')