Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
381505a
wip analog compositional passive
ducky64 Mar 12, 2026
7330b19
fix netlist differences
ducky64 Mar 13, 2026
5d427b7
Merge branch 'master' into compositional-passive
ducky64 Mar 15, 2026
67fddf3
wip
ducky64 Mar 15, 2026
e21c223
Merge branch 'master' into compositional-passive
ducky64 Mar 15, 2026
3fa0530
wip
ducky64 Mar 15, 2026
86c8d08
cleaning
ducky64 Mar 15, 2026
0b100aa
preserve original behavior
ducky64 Mar 15, 2026
5719562
merge
ducky64 Mar 16, 2026
4ab3e56
Merge branch 'master' into compositional-passive
ducky64 Mar 16, 2026
6ada796
wip?
ducky64 Mar 16, 2026
0d7be09
baseline
ducky64 Mar 16, 2026
3659d2d
Update NetlistGenerator.py
ducky64 Mar 16, 2026
44e29e7
merge
ducky64 Mar 16, 2026
8fef5ce
Merge branch 'master' into compositional-passive
ducky64 Mar 17, 2026
1866e4b
Merge branch 'master' into compositional-passive
ducky64 Mar 30, 2026
1f065e9
create HasPassivePort, remove deprecations
ducky64 Mar 31, 2026
03b9d5d
refactor some analog adapters
ducky64 Mar 31, 2026
9ef2d90
some cleanup, some remaining failures
ducky64 Apr 5, 2026
b8719ff
moles being whacked
ducky64 Apr 5, 2026
af5df64
fix test
ducky64 Apr 5, 2026
7956871
cleaning
ducky64 Apr 5, 2026
afb5d2f
blacken
ducky64 Apr 5, 2026
7cf31a9
Fix types
ducky64 Apr 5, 2026
2a60f63
Make adapters kicad-importable
ducky64 Apr 5, 2026
f08175a
fix for mypy upgrade
ducky64 Apr 5, 2026
a6fcb20
clean up ordering
ducky64 Apr 5, 2026
7b1c3cc
Revert "clean up ordering"
ducky64 Apr 5, 2026
7fba83a
first batch of changes
ducky64 Apr 5, 2026
9b8bc54
cleaning adapt_to
ducky64 Apr 5, 2026
cce9138
cleanup
ducky64 Apr 5, 2026
5f2ce09
refactoring wip
ducky64 Apr 5, 2026
bda0ad8
cleanup up diode
ducky64 Apr 5, 2026
7300233
cleanup
ducky64 Apr 5, 2026
6a68363
cleaning
ducky64 Apr 5, 2026
844bb76
Update ResistiveSensor.py
ducky64 Apr 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 32 additions & 32 deletions edg/abstract_parts/AbstractAnalogSwitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,12 @@ def __init__(self) -> None:
self.control = self.Export(self.device.control)

self.inputs = self.Port(Vector(AnalogSink.empty()))
self.out = self.Export(
self.device.com.adapt_to(
AnalogSource(
voltage_out=self.inputs.hull(lambda x: x.link().voltage),
signal_out=self.inputs.hull(lambda x: x.link().signal),
current_limits=self.device.analog_current_limits, # this device only, current draw propagated
impedance=self.device.analog_on_resistance + self.inputs.hull(lambda x: x.link().source_impedance),
)
self.out = self.Port(
AnalogSource(
voltage_out=self.inputs.hull(lambda x: x.link().voltage),
signal_out=self.inputs.hull(lambda x: x.link().signal),
current_limits=self.device.analog_current_limits, # this device only, current draw propagated
impedance=self.device.analog_on_resistance + self.inputs.hull(lambda x: x.link().source_impedance),
)
)

Expand All @@ -145,18 +143,20 @@ def __init__(self) -> None:
@override
def generate(self) -> None:
super().generate()

self.connect(self.out.net, self.device.com)

self.inputs.defined()
for elt in self.get(self.inputs.requested()):
self.connect(
self.inputs.append_elt(AnalogSink.empty(), elt),
self.device.inputs.request(elt).adapt_to(
AnalogSink(
voltage_limits=self.device.analog_voltage_limits, # this device only, voltages propagated
current_draw=self.out.link().current_drawn,
impedance=self.out.link().sink_impedance + self.device.analog_on_resistance,
)
input = self.inputs.append_elt(
AnalogSink(
voltage_limits=self.device.analog_voltage_limits, # this device only, voltages propagated
current_draw=self.out.link().current_drawn,
impedance=self.out.link().sink_impedance + self.device.analog_on_resistance,
),
elt,
)
self.connect(input.net, self.device.inputs.request(elt))
if self.get(self.control_gnd.is_connected()):
self.connect(self.control_gnd, self.device.control_gnd)

Expand All @@ -182,13 +182,11 @@ def __init__(self) -> None:
self.control = self.Export(self.device.control)

self.outputs = self.Port(Vector(AnalogSource.empty()))
self.input = self.Export(
self.device.com.adapt_to(
AnalogSink(
voltage_limits=self.device.analog_voltage_limits, # this device only, voltages propagated
current_draw=self.outputs.hull(lambda x: x.link().current_drawn),
impedance=self.device.analog_on_resistance + self.outputs.hull(lambda x: x.link().sink_impedance),
)
self.input = self.Port(
AnalogSink(
voltage_limits=self.device.analog_voltage_limits, # this device only, voltages propagated
current_draw=self.outputs.hull(lambda x: x.link().current_drawn),
impedance=self.device.analog_on_resistance + self.outputs.hull(lambda x: x.link().sink_impedance),
)
)

Expand All @@ -197,19 +195,21 @@ def __init__(self) -> None:
@override
def generate(self) -> None:
super().generate()

self.connect(self.input.net, self.device.com)

self.outputs.defined()
for elt in self.get(self.outputs.requested()):
self.connect(
self.outputs.append_elt(AnalogSource.empty(), elt),
self.device.inputs.request(elt).adapt_to(
AnalogSource(
voltage_out=self.input.link().voltage,
signal_out=self.input.link().signal,
current_limits=self.device.analog_current_limits, # this device only, voltages propagated
impedance=self.input.link().source_impedance + self.device.analog_on_resistance,
)
output = self.outputs.append_elt(
AnalogSource(
voltage_out=self.input.link().voltage,
signal_out=self.input.link().signal,
current_limits=self.device.analog_current_limits, # this device only, voltages propagated
impedance=self.input.link().source_impedance + self.device.analog_on_resistance,
),
elt,
)
self.connect(output.net, self.device.inputs.request(elt))

def demux_to(
self, input: Optional[Port[AnalogLink]] = None, outputs: Optional[List[Port[AnalogLink]]] = None
Expand Down
6 changes: 4 additions & 2 deletions edg/abstract_parts/AbstractCapacitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,10 +403,12 @@ def __init__(self, capacitance: RangeLike, *, exact_capacitance: BoolLike = Fals
super().__init__()

self.cap = self.Block(Capacitor(capacitance, voltage=RangeExpr(), exact_capacitance=exact_capacitance))
self.gnd = self.Export(self.cap.neg.adapt_to(Ground()), [Common])
self.io = self.Export(self.cap.pos.adapt_to(AnalogSink()), [InOut]) # ideal open port
self.gnd = self.Port(Ground.empty(), [Common])
self.io = self.Port(AnalogSink(), [InOut]) # ideal open port

self.assign(self.cap.voltage, self.io.link().voltage - self.gnd.link().voltage)
self.connect(self.cap.neg.adapt_to(Ground()), self.gnd) # TODO refactor #114
self.connect(self.io.net, self.cap.pos)

def connected(
self, gnd: Optional[Port[GroundLink]] = None, io: Optional[Port[AnalogLink]] = None
Expand Down
35 changes: 14 additions & 21 deletions edg/abstract_parts/AbstractDiodes.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from typing import Dict, Any
from typing import Dict
from deprecated import deprecated
from typing_extensions import override

from ..electronics_model import *
from .DummyDevices import ForcedAnalogVoltage
from .Categories import *
from .PartsTable import PartsTableColumn, PartsTableRow
from .PartsTablePart import PartsTableSelector
Expand Down Expand Up @@ -222,27 +220,22 @@ class AnalogClampZenerDiode(Protection, KiCadImportableBlock):
def __init__(self, voltage: RangeLike):
super().__init__()

self.signal_in = self.Port(AnalogSink.empty(), [Input])
self.signal_out = self.Port(AnalogSource.empty(), [Output])
self.gnd = self.Port(Ground.empty(), [Common])

self.voltage = self.ArgParameter(voltage)

@override
def contents(self) -> None:
super().contents()
self.diode = self.Block(ZenerDiode(zener_voltage=voltage))

self.diode = self.Block(ZenerDiode(zener_voltage=self.voltage))

self.forced = self.Block(
ForcedAnalogVoltage(
forced_voltage=self.signal_in.link().voltage.intersect(
self.gnd = self.Port(Ground.empty(), [Common])
self.signal_in = self.Port(AnalogSink(), [Input])
self.signal_out = self.Port(
AnalogSource(
voltage_out=self.signal_in.link().voltage.intersect(
self.gnd.link().voltage + (0, self.diode.actual_zener_voltage.upper())
)
)
),
signal_out=self.signal_in.link().signal,
),
[Output],
)
self.connect(self.signal_in, self.forced.signal_in)
self.connect(self.signal_out, self.forced.signal_out, self.diode.cathode.adapt_to(AnalogSink()))
self.assign(self.signal_in.current_draw, self.signal_out.link().current_drawn)

self.connect(self.signal_in.net, self.signal_out.net, self.diode.cathode)
self.connect(self.diode.anode.adapt_to(Ground()), self.gnd)

@override
Expand Down
27 changes: 13 additions & 14 deletions edg/abstract_parts/AbstractResistor.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,14 +392,21 @@ def __init__(
):
super().__init__()

self.signal_in = self.Port(AnalogSink.empty(), [Input])
self.signal_out = self.Port(AnalogSource.empty(), [Output])

self.clamp_target = self.ArgParameter(clamp_target)
self.clamp_current = self.ArgParameter(clamp_current)
self.protection_voltage = self.ArgParameter(protection_voltage)
self.zero_out = self.ArgParameter(zero_out)

self.signal_in = self.Port(AnalogSink(), [Input])
self.signal_out = self.Port(
AnalogSource(
voltage_out=self.signal_in.link().voltage.intersect(self.clamp_target),
signal_out=self.signal_in.link().signal,
impedance=RangeExpr(),
),
[Output],
)

@override
def contents(self) -> None:
super().contents()
Expand All @@ -415,17 +422,9 @@ def contents(self) -> None:
)
)
)
self.connect(self.res.a.adapt_to(AnalogSink()), self.signal_in)
self.connect(
self.res.b.adapt_to(
AnalogSource(
voltage_out=self.signal_in.link().voltage.intersect(self.clamp_target),
signal_out=self.signal_in.link().signal,
impedance=self.signal_in.link().source_impedance + self.res.actual_resistance,
)
),
self.signal_out,
)
self.connect(self.res.a, self.signal_in.net)
self.connect(self.res.b, self.signal_out.net)
self.assign(self.signal_out.impedance, self.signal_in.link().source_impedance + self.res.actual_resistance)

@override
def symbol_pinning(self, symbol_name: str) -> Dict[str, Port]:
Expand Down
22 changes: 11 additions & 11 deletions edg/abstract_parts/AbstractSolidStateRelay.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,22 @@ def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]:
def __init__(self) -> None:
super().__init__()

self.ic = self.Block(SolidStateRelay())

self.signal = self.Port(DigitalSink.empty())
self.gnd = self.Port(Ground.empty(), [Common])

self.apull = self.Port(AnalogSink.empty())
self.ain = self.Port(AnalogSink.empty())
self.ain = self.Port(
AnalogSink(
voltage_limits=RangeExpr(),
impedance=RangeExpr(),
)
)
self.aout = self.Port(AnalogSource.empty())
self.assign(self.ain.voltage_limits, self.apull.link().voltage + self.ic.load_voltage_limit)
self.assign(self.ain.impedance, self.aout.link().sink_impedance + self.ic.load_resistance)

self.ic = self.Block(SolidStateRelay())
self.res = self.Block(
Resistor(
resistance=(
Expand All @@ -134,15 +142,7 @@ def __init__(self) -> None:
self.connect(self.res.a, self.ic.ledk)
self.connect(self.res.b.adapt_to(Ground()), self.gnd)

self.connect(
self.ain,
self.ic.feta.adapt_to(
AnalogSink(
voltage_limits=self.apull.link().voltage + self.ic.load_voltage_limit,
impedance=self.aout.link().sink_impedance + self.ic.load_resistance,
)
),
)
self.connect(self.ain.net, self.ic.feta)
self.pull_merge = self.Block(MergedAnalogSource()).connected_from(
self.apull,
self.ic.fetb.adapt_to(
Expand Down
7 changes: 4 additions & 3 deletions edg/abstract_parts/AbstractTestPoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ class AnalogTestPoint(BaseTypedTestPoint, Block):

def __init__(self, *args: Any) -> None:
super().__init__(*args)
self.io = self.Port(AnalogSink.empty(), [InOut])
self.connect(self.io, self.tp.io.adapt_to(AnalogSink()))
self.io: AnalogSink = self.Port(AnalogSink(), [InOut])
self.connect(self.io.net, self.tp.io)

def connected(self, io: Port[AnalogLink]) -> "AnalogTestPoint":
cast(Block, builder.get_enclosing_block()).connect(io, self.io)
Expand All @@ -135,7 +135,8 @@ class AnalogCoaxTestPoint(BaseRfTestPoint, Block):

def __init__(self, *args: Any) -> None:
super().__init__(*args)
self.io = self.Export(self.conn.sig.adapt_to(AnalogSink()), [InOut])
self.io: AnalogSink = self.Port(AnalogSink(), [InOut])
self.connect(self.io.net, self.conn.sig)

def connected(self, io: Port[AnalogLink]) -> "AnalogCoaxTestPoint":
cast(Block, builder.get_enclosing_block()).connect(io, self.io)
Expand Down
17 changes: 2 additions & 15 deletions edg/abstract_parts/DummyDevices.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,25 +113,11 @@ def __init__(self, forced_voltage: RangeLike, forced_current: RangeLike) -> None
self.pwr_out = self.Port(VoltageSource(voltage_out=forced_voltage), [Output])


class ForcedAnalogVoltage(DummyDevice, NetBlock):
def __init__(self, forced_voltage: RangeLike = RangeExpr()) -> None:
super().__init__()

self.signal_in = self.Port(AnalogSink(current_draw=RangeExpr()), [Input])

self.signal_out = self.Port(
AnalogSource(voltage_out=forced_voltage, signal_out=self.signal_in.link().signal), [Output]
)

self.assign(self.signal_in.current_draw, self.signal_out.link().current_drawn)


class ForcedAnalogSignal(KiCadImportableBlock, DummyDevice, NetBlock):
class ForcedAnalogSignal(KiCadImportableBlock, DummyDevice):
def __init__(self, forced_signal: RangeLike = RangeExpr()) -> None:
super().__init__()

self.signal_in = self.Port(AnalogSink(current_draw=RangeExpr()), [Input])

self.signal_out = self.Port(
AnalogSource(
voltage_out=self.signal_in.link().voltage,
Expand All @@ -142,6 +128,7 @@ def __init__(self, forced_signal: RangeLike = RangeExpr()) -> None:
)

self.assign(self.signal_in.current_draw, self.signal_out.link().current_drawn)
self.connect(self.signal_in.net, self.signal_out.net)

@override
def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]:
Expand Down
15 changes: 8 additions & 7 deletions edg/abstract_parts/IoController.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from itertools import chain
from typing import List, Dict, Tuple, Type, Optional, Any
from typing import List, Dict, Tuple, Type, Optional, Any, Union

from deprecated import deprecated
from typing_extensions import override

from ..electronics_model import *
from .PinMappable import AllocatedResource, PinMappable, PinMapUtil
from .Categories import ProgrammableController
from ..electronics_model.PassivePort import HasPassivePort


@non_library
Expand Down Expand Up @@ -97,12 +98,12 @@ def _export_ios_from(self, inner: "BaseIoController", excludes: List[BasePort] =
@staticmethod
def _instantiate_from(
ios: List[BasePort], allocations: List[AllocatedResource]
) -> Tuple[Dict[str, CircuitPort], RangeExpr]:
) -> Tuple[Dict[str, Union[CircuitPort, HasPassivePort]], RangeExpr]:
"""Given a mapping of port types to IO ports and allocated resources from PinMapUtil,
instantiate vector elements (if a vector) or init the port model (if a port)
for the allocated resources using their data model and return the pin mapping."""
ios_by_type = {io.elt_type() if isinstance(io, Vector) else type(io): io for io in ios}
pinmap: Dict[str, CircuitPort] = {}
pinmap: Dict[str, Union[CircuitPort, HasPassivePort]] = {}

ports_assigned = IdentitySet[Port]()
io_current_draw_builder = RangeExpr._to_expr_type(RangeExpr.ZERO)
Expand Down Expand Up @@ -141,14 +142,14 @@ def _instantiate_from(
# TODO: recurse into bundles, really needs a more unified way of handling current draw

if isinstance(allocation.pin, str):
assert isinstance(io_port, CircuitPort)
assert isinstance(io_port, (CircuitPort, HasPassivePort))
pinmap[allocation.pin] = io_port
elif allocation.pin is None:
assert isinstance(io_port, CircuitPort) # otherwise discarded
assert isinstance(io_port, (CircuitPort, HasPassivePort)) # otherwise discarded
elif isinstance(allocation.pin, dict):
for subport_name, (pin_name, pin_resource) in allocation.pin.items():
subport = getattr(io_port, subport_name)
assert isinstance(subport, CircuitPort), f"bad sub-port {pin_name} {subport}"
assert isinstance(subport, (CircuitPort, HasPassivePort)), f"bad sub-port {pin_name} {subport}"
pinmap[pin_name] = subport
else:
raise NotImplementedError(f"unknown allocation pin type {allocation.pin}")
Expand Down Expand Up @@ -183,7 +184,7 @@ def _io_pinmap(self) -> PinMapUtil:
"""Implement me. Defines the assignable IO pinmaps."""
raise NotImplementedError

def _make_pinning(self) -> Dict[str, CircuitPort]:
def _make_pinning(self) -> Dict[str, Union[CircuitPort, HasPassivePort]]:
allocation_list = []
for io_port in self._io_ports:
if isinstance(io_port, Vector): # derive Vector connections from requested
Expand Down
5 changes: 3 additions & 2 deletions edg/abstract_parts/MergedBlocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def connected_from(self, *ins: Port[DigitalLink]) -> "MergedDigitalSource":
return self


class MergedAnalogSource(KiCadImportableBlock, DummyDevice, NetBlock, GeneratorBlock):
class MergedAnalogSource(KiCadImportableBlock, DummyDevice, GeneratorBlock):
@override
def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]:
assert symbol_name.startswith("edg_importable:Merge") # can be any merge
Expand All @@ -89,10 +89,11 @@ def generate(self) -> None:
super().generate()
self.inputs.defined()
for in_request in self.get(self.inputs.requested()):
self.inputs.append_elt(
elt_port = self.inputs.append_elt(
AnalogSink(current_draw=self.output.link().current_drawn, impedance=self.output.link().sink_impedance),
in_request,
)
self.connect(self.output.net, elt_port.net)

self.assign(self.output.voltage_out, self.inputs.hull(lambda x: x.link().voltage))
self.assign(self.output.signal_out, self.inputs.hull(lambda x: x.link().signal))
Expand Down
Loading
Loading