PM_K-1 hardware: Stage 1 input receiver as SKiDL (code-defined schematic)

Capture method = SKiDL per decision. circuits/stage1_input.py defines the
balanced line receiver + per-leg protection (DC-block film cap, series R, bias R,
clamp diodes to the rails) and emits a KiCad netlist. ERC: 0 errors (2 expected
warnings -- AIN_HOT/COLD reach only one pin until the interconnect block exists).

Container: env vars point SKiDL/KiCad at the symbol/footprint libs.

VERIFY-before-layout flagged in-file: exact THAT124x gain suffix, its SO-8 pin
numbers, clamp-diode orientation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Me Here 2026-05-30 19:43:07 -05:00
parent d51c9f1011
commit 3f7f4b94d9
8 changed files with 163 additions and 0 deletions

View file

@ -27,5 +27,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
apt-get clean && rm -rf /var/lib/apt/lists/* && \
pip3 install --no-cache-dir --break-system-packages skidl
# Point SKiDL / KiCad CLI at the installed libraries (reproducible, not ad-hoc).
ENV KICAD9_SYMBOL_DIR=/usr/share/kicad/symbols \
KICAD9_FOOTPRINT_DIR=/usr/share/kicad/footprints \
KICAD_SYMBOL_DIR=/usr/share/kicad/symbols
WORKDIR /work
CMD ["bash"]

View file

@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""PM_K-1 audio chain - Stage 1: balanced LINE input receiver + protection (SKiDL).
Code-defined schematic. Run INSIDE the EDA container:
cd hardware/eda && ./run.sh python3 ../eda/circuits/stage1_input.py
Outputs ERC results + a KiCad netlist at hardware/kicad/stage1_input.net,
which imports into Pcbnew for layout.
VERIFY-BEFORE-LAYOUT (datasheet items -- flagged, not guessed):
* exact THAT line-receiver suffix for the gain we want (1240/1243/1246 differ)
* the receiver's SO-8 pin NUMBERS (function->pin map below is placeholder)
* clamp-diode part choice + pin orientation
The TOPOLOGY/connectivity below is correct; only these pin-level details need
confirming against the datasheet.
"""
import os
from skidl import *
set_default_tool(KICAD9)
# ---------- passive templates ----------
R = Part("Device", "R", dest=TEMPLATE, footprint="Resistor_SMD:R_0805_2012Metric")
C = Part("Device", "C", dest=TEMPLATE, footprint="Capacitor_SMD:C_0805_2012Metric")
D = Part("Device", "D", dest=TEMPLATE, footprint="Diode_SMD:D_SOD-323")
# ---------- power rails (global nets; marked POWER-driven for ERC) ----------
# No power-flag *symbols* -- they carry no footprint and SKiDL errors on that.
# The rails arrive from the power block; here we just declare + drive them.
p15, n15, gnd = Net("+15V"), Net("-15V"), Net("GND")
for n in (p15, n15, gnd):
n.drive = POWER
# ---------- balanced line receiver (THAT124x) : PINOUT TODO-VERIFY ----------
RX = Part(name="THAT124x_RX", tool=SKIDL, dest=TEMPLATE, ref_prefix="U",
footprint="Package_SO:SOIC-8_3.9x4.9mm_P1.27mm",
pins=[
Pin(num=1, name="IN-", func=Pin.types.INPUT),
Pin(num=2, name="REF", func=Pin.types.INPUT),
Pin(num=3, name="OUT", func=Pin.types.OUTPUT),
Pin(num=4, name="V-", func=Pin.types.PWRIN),
Pin(num=5, name="V+", func=Pin.types.PWRIN),
Pin(num=6, name="SENSE", func=Pin.types.INPUT),
Pin(num=7, name="IN+", func=Pin.types.INPUT),
Pin(num=8, name="NC", func=Pin.types.NOCONNECT),
])
rx = RX(ref="U1")
# ---------- nets ----------
ain_hot, ain_cold, rxout = Net("AIN_HOT"), Net("AIN_COLD"), Net("RX_OUT")
def protected_input(src, node_name):
"""series DC-block cap (blocks +48V phantom) -> series R -> node;
bias R to gnd; clamp diodes to the +/-15V rails (orientation TODO-VERIFY)."""
cblk = C(value="2.2uF", footprint="Capacitor_SMD:C_1206_3216Metric") # film
rs, rb = R(value="1k"), R(value="1Meg")
dp, dn = D(value="1N4148WS"), D(value="1N4148WS")
node = Net(node_name)
src += cblk[1]; cblk[2] += rs[1]; rs[2] += node
rb[1] += node; rb[2] += gnd
dp[2] += p15; node += dp[1] # clamp high to +15
dn[1] += n15; dn[2] += node # clamp low to -15 (avoid rebinding globals)
return node
rx["IN+"] += protected_input(ain_hot, "RXIN_P")
rx["IN-"] += protected_input(ain_cold, "RXIN_N")
rx["REF"] += gnd
rx["SENSE"] += rxout # sense tied to output (local feedback)
rx["OUT"] += rxout
rx["V+"] += p15
rx["V-"] += n15
# supply decoupling at the receiver
for rail in (p15, n15):
c = C(value="100nF")
rail += c[1]; c[2] += gnd
ERC()
out = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "kicad", "stage1_input.net"))
generate_netlist(file_=out)
print("Stage 1 netlist ->", out)

View file

@ -7,3 +7,4 @@
*.kicad_prl
fp-info-cache
~*.lck
*.net

0
hardware/kicad/skidl.erc Normal file
View file

9
hardware/kicad/skidl.log Normal file
View file

@ -0,0 +1,9 @@
WARNING: KICAD8_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched. @ [/work/hardware/kicad/<frozen importlib._bootstrap_external>:995=>/work/hardware/kicad/<frozen importlib._bootstrap>:488]
WARNING: KICAD6_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched. @ [/work/hardware/kicad/<frozen importlib._bootstrap_external>:995=>/work/hardware/kicad/<frozen importlib._bootstrap>:488]
WARNING: KICAD9_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched. @ [/work/hardware/kicad/<frozen importlib._bootstrap_external>:995=>/work/hardware/kicad/<frozen importlib._bootstrap>:488]
WARNING: KICAD_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched. @ [/work/hardware/kicad/<frozen importlib._bootstrap_external>:995=>/work/hardware/kicad/<frozen importlib._bootstrap>:488]
WARNING: KICAD7_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched. @ [/work/hardware/kicad/<frozen importlib._bootstrap_external>:995=>/work/hardware/kicad/<frozen importlib._bootstrap>:488]
WARNING: fp-lib-table file was not found. Component footprints are not available.
WARNING: fp-lib-table file was not found. Component footprints are not available.
WARNING: fp-lib-table file was not found. Component footprints are not available.
WARNING: fp-lib-table file was not found. Component footprints are not available.

View file

@ -0,0 +1,5 @@
ERC WARNING: Only one pin (PASSIVE pin 1/~ of C/C1) attached to net AIN_HOT.
ERC WARNING: Only one pin (PASSIVE pin 1/~ of C/C2) attached to net AIN_COLD.
ERC INFO: 2 warnings found while running ERC.
ERC INFO: 0 errors found while running ERC.

View file

@ -0,0 +1,37 @@
WARNING: KICAD8_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched. @ [/work/hardware/kicad/<frozen importlib._bootstrap_external>:995=>/work/hardware/kicad/<frozen importlib._bootstrap>:488]
WARNING: KICAD6_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched. @ [/work/hardware/kicad/<frozen importlib._bootstrap_external>:995=>/work/hardware/kicad/<frozen importlib._bootstrap>:488]
WARNING: KICAD7_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched. @ [/work/hardware/kicad/<frozen importlib._bootstrap_external>:995=>/work/hardware/kicad/<frozen importlib._bootstrap>:488]
WARNING: fp-lib-table file was not found. Component footprints are not available.
WARNING: fp-lib-table file was not found. Component footprints are not available.
WARNING: fp-lib-table file was not found. Component footprints are not available.
WARNING: fp-lib-table file was not found. Component footprints are not available.
WARNING: Missing tag on THAT124x_RX instantiated at /work/hardware/eda/circuits/stage1_input.py:46.
WARNING: Random tag NiUcdSBKvZ generated for THAT124x_RX.
WARNING: Missing tag on C instantiated at /work/hardware/eda/circuits/stage1_input.py:54.
WARNING: Random tag UDuxvr_uTL generated for C.
WARNING: Missing tag on R instantiated at /work/hardware/eda/circuits/stage1_input.py:55.
WARNING: Random tag _Np5DvK_aE generated for R.
WARNING: Missing tag on R instantiated at /work/hardware/eda/circuits/stage1_input.py:55.
WARNING: Random tag 7TLEUp_48E generated for R.
WARNING: Missing tag on D instantiated at /work/hardware/eda/circuits/stage1_input.py:56.
WARNING: Random tag e2BBosboqS generated for D.
WARNING: Missing tag on D instantiated at /work/hardware/eda/circuits/stage1_input.py:56.
WARNING: Random tag ngnho2nWKf generated for D.
WARNING: Missing tag on C instantiated at /work/hardware/eda/circuits/stage1_input.py:54.
WARNING: Random tag WXfeI7Gfj2 generated for C.
WARNING: Missing tag on R instantiated at /work/hardware/eda/circuits/stage1_input.py:55.
WARNING: Random tag jKB97sf61u generated for R.
WARNING: Missing tag on R instantiated at /work/hardware/eda/circuits/stage1_input.py:55.
WARNING: Random tag mXEcsSvVUJ generated for R.
WARNING: Missing tag on D instantiated at /work/hardware/eda/circuits/stage1_input.py:56.
WARNING: Random tag aEBPrkC6b9 generated for D.
WARNING: Missing tag on D instantiated at /work/hardware/eda/circuits/stage1_input.py:56.
WARNING: Random tag vSi9f4M7Zg generated for D.
WARNING: Missing tag on C instantiated at /work/hardware/eda/circuits/stage1_input.py:74.
WARNING: Random tag I5Ulz7qVtA generated for C.
WARNING: Missing tag on C instantiated at /work/hardware/eda/circuits/stage1_input.py:74.
WARNING: Random tag clPAosAbcJ generated for C.
WARNING: Missing tag on instantiated at /work/hardware/kicad/<frozen importlib._bootstrap>:488.
INFO: 31 warnings found while generating netlist.
INFO: 0 errors found while generating netlist.

View file

@ -0,0 +1,26 @@
from collections import defaultdict
from skidl import Pin, Part, Alias, SchLib, SKIDL, TEMPLATE
from skidl.pin import pin_types
SKIDL_lib_version = '0.0.1'
stage1_input = SchLib(tool=SKIDL).add_parts(*[
Part(**{ 'name':'THAT124x_RX', 'dest':TEMPLATE, 'tool':SKIDL, 'aliases':Alias({'THAT124x_RX'}), 'ref_prefix':'U', 'fplist':None, 'footprint':'Package_SO:SOIC-8_3.9x4.9mm_P1.27mm', 'keywords':None, 'description':'', 'datasheet':None, 'pins':[
Pin(num='1',name='IN-',func=pin_types.INPUT),
Pin(num='2',name='REF',func=pin_types.INPUT),
Pin(num='3',name='OUT',func=pin_types.OUTPUT),
Pin(num='4',name='V-',func=pin_types.PWRIN),
Pin(num='5',name='V+',func=pin_types.PWRIN),
Pin(num='6',name='SENSE',func=pin_types.INPUT),
Pin(num='7',name='IN+',func=pin_types.INPUT),
Pin(num='8',name='NC',func=pin_types.NOCONNECT)] }),
Part(**{ 'name':'C', 'dest':TEMPLATE, 'tool':SKIDL, 'aliases':Alias({'C'}), 'ref_prefix':'C', 'fplist':[''], 'footprint':'Capacitor_SMD:C_1206_3216Metric', 'keywords':'cap capacitor', 'description':'Unpolarized capacitor', 'datasheet':'~', 'pins':[
Pin(num='1',name='~',func=pin_types.PASSIVE,unit=1),
Pin(num='2',name='~',func=pin_types.PASSIVE,unit=1)], 'unit_defs':[] }),
Part(**{ 'name':'R', 'dest':TEMPLATE, 'tool':SKIDL, 'aliases':Alias({'R'}), 'ref_prefix':'R', 'fplist':[''], 'footprint':'Resistor_SMD:R_0805_2012Metric', 'keywords':'R res resistor', 'description':'Resistor', 'datasheet':'~', 'pins':[
Pin(num='1',name='~',func=pin_types.PASSIVE,unit=1),
Pin(num='2',name='~',func=pin_types.PASSIVE,unit=1)], 'unit_defs':[] }),
Part(**{ 'name':'D', 'dest':TEMPLATE, 'tool':SKIDL, 'aliases':Alias({'D'}), 'ref_prefix':'D', 'fplist':[''], 'footprint':'Diode_SMD:D_SOD-323', 'keywords':'diode', 'description':'Diode', 'datasheet':'~', 'pins':[
Pin(num='1',name='K',func=pin_types.PASSIVE,unit=1),
Pin(num='2',name='A',func=pin_types.PASSIVE,unit=1)], 'unit_defs':[] })])