#!/usr/bin/env python3
"""PM_K-1 MIDI block (SKiDL) -- DNP populate-option: opto-isolated IN + buffered OUT + THRU.

Run INSIDE the EDA container:
    cd hardware/eda && ./run.sh python3 ../eda/circuits/midi.py
Outputs ERC + hardware/kicad/midi.net.

USB-MIDI is the DEFAULT (firmware). This hardware DIN/TRS MIDI is a DO-NOT-POPULATE
option for laptop-free sync to standalone gear (a "stage" face fits the connector + parts).

  IN  : opto-isolated (H11L1) -- breaks the ground loop the MIDI spec requires.
  OUT : RP2350 UART TX -> 74LVC14 buffer (2 inverters = non-inverting) -> series R.
  THRU: a buffered copy of the received IN signal (re-transmit), same buffer chip.

PINOUTS
  * 74LVC14 hex Schmitt inverter: standard 14-pin (1A/1Y..6A/6Y, GND=7, VCC=14).
  * H11L1 6-pin Schmitt opto: standard 1=Anode 2=Cathode 3=NC 4=GND 5=VO 6=VCC
    (universal across makers of this part; datasheet fetch timed out -- reconfirm at layout).
CONFIRM: the series-resistor values for 3.3V MIDI per the MIDI Association spec (placeholders
below). Connector (TRS-MIDI Type-A vs DIN-5) is a FACE choice; the core exposes the loop nets.
"""
import os
from skidl import *
set_default_tool(KICAD9)
P = Pin.types
R = Part("Device","R", dest=TEMPLATE, footprint="Resistor_SMD:R_0402_1005Metric")
def C(v): return Part("Device","C", value=v, footprint="Capacitor_SMD:C_0402_1005Metric")
D = Part("Device","D", dest=TEMPLATE, footprint="Diode_SMD:D_SOD-323")

p3v3, gnd = Net("+3V3"), Net("GND")
p3v3.drive = POWER; gnd.drive = POWER
midi_tx, midi_rx = Net("MIDI_TX"), Net("MIDI_RX")          # to/from RP2350 UART (assign free GPIO at integ.)
in_a, in_b = Net("MIDI_IN_A"), Net("MIDI_IN_B")            # isolated current loop -> face connector
out_a, out_b = Net("MIDI_OUT_A"), Net("MIDI_OUT_B")
thru_a, thru_b = Net("MIDI_THRU_A"), Net("MIDI_THRU_B")

OPTO = Part(name="H11L1", tool=SKIDL, dest=TEMPLATE, ref_prefix="U",
    footprint="Package_DIP:DIP-6_W7.62mm",
    pins=[Pin(num=1,name="A",func=P.PASSIVE),Pin(num=2,name="C",func=P.PASSIVE),Pin(num=3,name="NC",func=P.NOCONNECT),
          Pin(num=4,name="GND",func=P.PWRIN),Pin(num=5,name="VO",func=P.OPENCOLL),Pin(num=6,name="VCC",func=P.PWRIN)])
BUF = Part(name="74LVC14", tool=SKIDL, dest=TEMPLATE, ref_prefix="U",
    footprint="Package_SO:TSSOP-14_4.4x5mm_P0.65mm",
    pins=[Pin(num=1,name="1A",func=P.INPUT),Pin(num=2,name="1Y",func=P.OUTPUT),Pin(num=3,name="2A",func=P.INPUT),
          Pin(num=4,name="2Y",func=P.OUTPUT),Pin(num=5,name="3A",func=P.INPUT),Pin(num=6,name="3Y",func=P.OUTPUT),
          Pin(num=7,name="GND",func=P.PWRIN),Pin(num=8,name="4Y",func=P.OUTPUT),Pin(num=9,name="4A",func=P.INPUT),
          Pin(num=10,name="5Y",func=P.OUTPUT),Pin(num=11,name="5A",func=P.INPUT),Pin(num=12,name="6Y",func=P.OUTPUT),
          Pin(num=13,name="6A",func=P.INPUT),Pin(num=14,name="VCC",func=P.PWRIN)])
opto = OPTO(ref="U8"); buf = BUF(ref="U9")

# ---- MIDI IN (opto-isolated) ----
rin = R(value="220"); dprot = D(value="1N4148WS")
in_a += rin[1]; rin[2] += opto["A"]            # current-limit into LED
opto["C"] += in_b                               # LED return (isolated side)
dprot[2] += opto["C"]; dprot[1] += opto["A"]    # reverse-protection across the LED (pin1=K,2=A)
opto["VCC"] += p3v3; opto["GND"] += gnd
rvo = R(value="10k"); opto["VO"] += midi_rx; midi_rx += rvo[1]; rvo[2] += p3v3   # pull-up; received data
copt = C("100nF"); p3v3 += copt[1]; copt[2] += gnd

# ---- MIDI OUT: TX -> two inverters -> series R ----
buf["1A"] += midi_tx; buf["1Y"] += Net("MIDI_OUT_N")    # first invert
buf["2A"] += buf["1Y"]                                   # second invert -> non-inverted
ro_b = R(value="33"); buf["2Y"] += ro_b[1]; ro_b[2] += out_b
ro_a = R(value="33"); p3v3 += ro_a[1]; ro_a[2] += out_a  # current-source leg

# ---- MIDI THRU: re-buffer the received signal ----
buf["3A"] += midi_rx; buf["3Y"] += Net("MIDI_THRU_N")
buf["4A"] += buf["3Y"]
rt_b = R(value="33"); buf["4Y"] += rt_b[1]; rt_b[2] += thru_b
rt_a = R(value="33"); p3v3 += rt_a[1]; rt_a[2] += thru_a

# unused inverters parked, supply
buf["5A"] += gnd; buf["6A"] += gnd
buf["VCC"] += p3v3; buf["GND"] += gnd
cbuf = C("100nF"); p3v3 += cbuf[1]; cbuf[2] += gnd

ERC()
out = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "kicad", "midi.net"))
generate_netlist(file_=out)
print("MIDI netlist ->", out)
