Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
260 commits
Select commit Hold shift + click to select a range
2679f60
Update test_usb_source_measure.py
ducky64 Feb 12, 2025
89c60a9
wip additional device symbols
ducky64 Feb 12, 2025
4c59e7c
test skeleton
ducky64 Feb 17, 2025
3997cdf
basic equals test
ducky64 Feb 17, 2025
64ef223
fix ratio corners
ducky64 Feb 17, 2025
947cb86
rebaseline netlist
ducky64 Feb 18, 2025
6d8108d
gated summing amp + fine voltage control
ducky64 Feb 18, 2025
d6e3e65
nano2 fuseholder
ducky64 Feb 18, 2025
ac9e848
ilim sense and high speed mclk
ducky64 Feb 19, 2025
fb62d76
ffffffuses
ducky64 Feb 19, 2025
3bcad3a
rebaseline netlists
ducky64 Feb 19, 2025
b3b72ae
INA219 not working bus conflict + some bugfixes
ducky64 Feb 19, 2025
2490e12
ina219 addr
ducky64 Feb 20, 2025
22ebb4b
Create JfetProtect.asc
ducky64 Feb 20, 2025
06c0371
divider + diff-rc importables
ducky64 Feb 22, 2025
9111c4e
Merge branch 'master' into smuv3.2
ducky64 Feb 22, 2025
d722922
new baseline netlist
ducky64 Feb 24, 2025
da1804e
ina826
ducky64 Feb 24, 2025
95ce681
ina826 w/ tolerancing
ducky64 Feb 24, 2025
ba70104
use stock divider symbol
ducky64 Feb 28, 2025
f1d2da0
updated control schematic w/o netlist diff
ducky64 Mar 1, 2025
21842c2
wip control stage
ducky64 Mar 3, 2025
db8ec6f
WIP smu control stage
ducky64 Mar 4, 2025
b95d939
cleaning
ducky64 Mar 4, 2025
31a72d9
chipping away at errors
ducky64 Mar 5, 2025
8c3c970
update netlist
ducky64 Mar 5, 2025
02d3f6e
Update UsbSourceMeasure.kicad_pro
ducky64 Mar 5, 2025
7a5976d
use ratio type
ducky64 Mar 5, 2025
19f9397
architecturally almost there!
ducky64 Mar 5, 2025
a0b7b18
proper negative rails
ducky64 Mar 5, 2025
43258ce
3 bugs left, 1 real
ducky64 Mar 5, 2025
29f7625
lol?
ducky64 Mar 5, 2025
a9c22ce
Update SourceMeasureControl.kicad_sch
ducky64 Mar 5, 2025
47bf22e
Update SourceMeasureControl.kicad_sch
ducky64 Mar 5, 2025
82ce5aa
netlisting
ducky64 Mar 6, 2025
93e7328
rebaseline schematic
ducky64 Mar 6, 2025
533a707
Update SourceMeasureControl.kicad_sch
ducky64 Mar 6, 2025
69cf43a
basically erc clean
ducky64 Mar 6, 2025
88477ff
add i2s speaker driver and repin
ducky64 Mar 6, 2025
7395bdd
latest
ducky64 Mar 7, 2025
00b66db
cleaning
ducky64 Mar 10, 2025
6b67558
cap spam
ducky64 Mar 10, 2025
ed90c44
model clamp pdis
ducky64 Mar 10, 2025
1b7ff22
add voltage comparator utility block
ducky64 Mar 14, 2025
96e34c7
low range cheaper SSR
ducky64 Mar 14, 2025
300f9b8
Update test_usb_source_measure.py
ducky64 Mar 15, 2025
137acd8
Update test_usb_source_measure.py
ducky64 Mar 15, 2025
50565f0
Merge branch 'master' into smuv3.2
ducky64 Mar 16, 2025
7b7a233
sr latch with set priority
ducky64 Mar 17, 2025
8a7af80
Update test_usb_source_measure.py
ducky64 Mar 17, 2025
bd59774
series resistor wip
ducky64 Mar 17, 2025
bca8bbe
cleaning
ducky64 Mar 17, 2025
58fe807
Improved int support
ducky64 Mar 17, 2025
025cb41
Merge branch 'master' into smuv3.2
ducky64 Mar 17, 2025
0405ce5
tolerance stackup on series resistors
ducky64 Mar 17, 2025
7246482
cleaning
ducky64 Mar 22, 2025
e83c164
Merge branch 'master' into smuv3.2
ducky64 Mar 23, 2025
e56a754
Update Logic_74Lvc.py
ducky64 Mar 23, 2025
630f8ee
fix voltage comp port in type
ducky64 Mar 23, 2025
651c1f9
Add RC snubber
ducky64 Mar 23, 2025
7c868d3
add RC and TVS, resolve all ERCs
ducky64 Mar 23, 2025
788c655
regenerate netlists
ducky64 Mar 23, 2025
23c58ce
layout beginnings
ducky64 Mar 23, 2025
39019a3
Update UsbSourceMeasure.kicad_pro
ducky64 Mar 23, 2025
be179d1
Merge branch 'master' into smuv3.2
ducky64 Apr 13, 2025
4670db4
merge
ducky64 Jun 25, 2025
e7f5748
Update AbstractResistor.py
ducky64 Jun 25, 2025
30c657f
Update UsbSourceMeasure.net
ducky64 Jun 25, 2025
b4e9b44
merge
ducky64 Jul 14, 2025
be37f18
cleaning an rebaseline
ducky64 Jul 15, 2025
cba68b3
optional control ground for analog switch
ducky64 Jul 25, 2025
05ccc57
ripup
ducky64 Jul 25, 2025
103d31a
Update UsbSourceMeasure.kicad_pcb
ducky64 Jul 25, 2025
0558d53
analog stage pnr
ducky64 Jul 26, 2025
fd8cebe
Update UsbSourceMeasure.kicad_pcb
ducky64 Jul 30, 2025
1501b6e
Update UsbSourceMeasure.kicad_pcb
ducky64 Jul 30, 2025
14699e0
Update UsbSourceMeasure.kicad_pcb
ducky64 Jul 30, 2025
07e6f9b
Update UsbSourceMeasure.kicad_pcb
ducky64 Jul 30, 2025
32db99e
Update UsbSourceMeasure.kicad_pcb
ducky64 Jul 31, 2025
c517eb5
Update UsbSourceMeasure.kicad_pcb
ducky64 Jul 31, 2025
59dcc6b
Update UsbSourceMeasure.kicad_pcb
ducky64 Jul 31, 2025
9a93138
prev-conv-rework
ducky64 Aug 2, 2025
afdc988
not great concept
ducky64 Aug 2, 2025
67ecc80
gate driver concepts
ducky64 Aug 2, 2025
8f66328
cleaner switcher?
ducky64 Aug 2, 2025
a92dfa4
sw concept 2
ducky64 Aug 2, 2025
8907327
one trace short of a happy meal
ducky64 Aug 2, 2025
871c13f
compactify
ducky64 Aug 2, 2025
670bf7e
better net naming
ducky64 Aug 2, 2025
c88c4eb
new buck boost layout
ducky64 Aug 2, 2025
3cb4174
wip
ducky64 Aug 2, 2025
40de282
wip
ducky64 Aug 4, 2025
93d67d2
analog stage layout wip
ducky64 Aug 4, 2025
25b685d
analog block continuation
ducky64 Aug 4, 2025
b5f53bb
remove matching resistors + mypy clean
ducky64 Aug 7, 2025
18b1a02
simplify dmeas amp
ducky64 Aug 10, 2025
0cd76cb
wip new layout
ducky64 Aug 10, 2025
057c799
analog routing
ducky64 Aug 11, 2025
66083c6
wip layout
ducky64 Aug 12, 2025
9c1b93b
Update UsbSourceMeasure.kicad_pcb
ducky64 Aug 13, 2025
d90c2fc
wip analog control block
ducky64 Aug 16, 2025
47026dd
analog stage mostly routed
ducky64 Aug 16, 2025
1e5089b
cohesive analog stage
ducky64 Aug 17, 2025
96374f6
support TO-220, and now the layout is messed up
ducky64 Aug 17, 2025
3ea1c32
to-220 concept
ducky64 Aug 17, 2025
b890721
layout wip
ducky64 Aug 17, 2025
3486202
layout wip
ducky64 Aug 18, 2025
44a19d2
Update UsbSourceMeasure.kicad_pcb
ducky64 Aug 19, 2025
9695498
layout wip
ducky64 Aug 24, 2025
6734a99
Update UsbSourceMeasure.kicad_pcb
ducky64 Aug 24, 2025
31d0fae
wip
ducky64 Aug 30, 2025
cd83857
buck converter blocks
ducky64 Aug 30, 2025
2aeb10f
Update UsbSourceMeasure.kicad_pcb
ducky64 Aug 30, 2025
3bd50bf
wip
ducky64 Aug 30, 2025
1912875
Update UsbSourceMeasure.kicad_pcb
ducky64 Aug 30, 2025
8f550c9
Update UsbSourceMeasure.kicad_pcb
ducky64 Aug 30, 2025
7970bce
floorplanned
ducky64 Aug 30, 2025
7b068bc
Update UsbSourceMeasure.kicad_pcb
ducky64 Aug 30, 2025
3e23023
wip
ducky64 Sep 2, 2025
7ec5ea4
update
ducky64 Sep 3, 2025
d967fdb
updates to address review comments + use PowerSO8
ducky64 Sep 4, 2025
caebc07
layout wip
ducky64 Sep 4, 2025
787bfad
wip
ducky64 Sep 4, 2025
9426252
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 4, 2025
b3d5647
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 4, 2025
06549c2
wip
ducky64 Sep 6, 2025
15966b4
Candidate power layout example
DC37 Sep 13, 2025
a9ee384
kicad8 compatibility
ducky64 Sep 13, 2025
f373efb
wip larger power traces
ducky64 Sep 13, 2025
53ce0f3
wip
ducky64 Sep 13, 2025
0cc4ec1
wip
ducky64 Sep 14, 2025
2f6403f
wip2
ducky64 Sep 14, 2025
2d770d8
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 14, 2025
b030dc8
wip
ducky64 Sep 14, 2025
2b44909
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 14, 2025
bd74f4c
cleaning
ducky64 Sep 14, 2025
b6af3e5
optimization
ducky64 Sep 14, 2025
f449590
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 14, 2025
0c709a2
layout
ducky64 Sep 15, 2025
7e33d39
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 15, 2025
409ec9c
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 15, 2025
05e37e3
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 15, 2025
41138c6
concept
ducky64 Sep 17, 2025
a7cf4d5
new layout concept - not a complete fan
ducky64 Sep 20, 2025
8c0c28b
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 20, 2025
b18d6db
even tigheter
ducky64 Sep 20, 2025
8a038d0
wip
ducky64 Sep 21, 2025
0997571
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 21, 2025
b827752
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 21, 2025
9cb5d39
swap pin for ssrs
ducky64 Sep 21, 2025
390308c
layout concept
ducky64 Sep 21, 2025
8bf5ae4
Revert "swap pin for ssrs"
ducky64 Sep 21, 2025
9f1417a
output block layout
ducky64 Sep 21, 2025
62524b4
beginnings of global routing
ducky64 Sep 21, 2025
eda7e8d
wip
ducky64 Sep 21, 2025
c4d8b9e
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 21, 2025
f7803b1
concept
ducky64 Sep 21, 2025
4d74d08
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 22, 2025
e5629f8
cleaner
ducky64 Sep 22, 2025
686edf7
continued rearrangement
ducky64 Sep 22, 2025
388b1a7
most things placed?
ducky64 Sep 22, 2025
9874dad
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 22, 2025
d8d7eb2
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 22, 2025
91d757c
routing continued
ducky64 Sep 22, 2025
b830c7c
wip global routing and pinning
ducky64 Sep 23, 2025
38c8e2b
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 23, 2025
68dc849
bad power routing
ducky64 Sep 24, 2025
ae49a95
power
ducky64 Sep 24, 2025
e4a8f73
routing optimizaiton
ducky64 Sep 25, 2025
644fbfa
global routing
ducky64 Sep 26, 2025
edab5ea
routing continued
ducky64 Sep 27, 2025
a0ca77b
simplify ovp routing
ducky64 Sep 27, 2025
808887f
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 27, 2025
d2ed36f
power arch cleanup
ducky64 Sep 28, 2025
d687623
gbr
ducky64 Sep 28, 2025
5fbbb9f
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 28, 2025
dd28d82
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 28, 2025
f7f41c7
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 28, 2025
847bb2f
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 28, 2025
708fb33
mcu pinning
ducky64 Sep 28, 2025
98199a4
checkpoint
ducky64 Sep 28, 2025
4458c5b
digital pinning
ducky64 Sep 28, 2025
c28deaa
almost there?
ducky64 Sep 28, 2025
85b117a
lots of progress
ducky64 Sep 28, 2025
76baedf
ratliens done?
ducky64 Sep 29, 2025
80a2580
rc0
ducky64 Sep 29, 2025
21069ff
cleaned up
ducky64 Sep 29, 2025
8a315ab
last changes
ducky64 Sep 29, 2025
be24bd8
wip
ducky64 Sep 29, 2025
3ff5a34
cleaned final 1
ducky64 Sep 29, 2025
3d6c8eb
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 29, 2025
eaddee0
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 29, 2025
c0fb8fa
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 29, 2025
0aec02c
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 29, 2025
c05d025
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 29, 2025
08d14cc
Update UsbSourceMeasure.kicad_pcb
ducky64 Sep 29, 2025
635044e
wip
ducky64 Oct 15, 2025
73c281a
wip
ducky64 Oct 19, 2025
58911db
merge
ducky64 Oct 19, 2025
4b79c78
merge
ducky64 Oct 19, 2025
633569d
cleanup
ducky64 Oct 19, 2025
97a93ce
all hdl changes
ducky64 Oct 19, 2025
f79c137
new hdl baseline
ducky64 Oct 19, 2025
59e954a
tuning pours
ducky64 Oct 20, 2025
13c65cc
soldermaskdefined testpads
ducky64 Oct 21, 2025
acfc0be
routing cleanup
ducky64 Oct 21, 2025
75a0413
Update UsbSourceMeasure.kicad_pcb
ducky64 Oct 21, 2025
b5d7c97
concept wip
ducky64 Oct 21, 2025
d0da917
Update UsbSourceMeasure.kicad_pcb
ducky64 Oct 22, 2025
c1f701b
tighter encoder crtyd
ducky64 Oct 23, 2025
7429458
barely clears
ducky64 Oct 23, 2025
601aaa6
wip densification
ducky64 Oct 23, 2025
017a37e
almost there - maybe?
ducky64 Oct 24, 2025
ae609a9
slow progress
ducky64 Oct 24, 2025
b29218e
cleaning
ducky64 Oct 24, 2025
9135f73
fix cap parsing
ducky64 Oct 24, 2025
fa1439d
everything but the silk!
ducky64 Oct 24, 2025
e62be92
RC1 DONE AT LAST
ducky64 Oct 24, 2025
9ca3e8c
rc1, really now
ducky64 Oct 24, 2025
5a782fe
Update UsbSourceMeasure.kicad_pcb
ducky64 Oct 24, 2025
c4c8d56
Update JlcElectrolyticCapacitor.py
ducky64 Oct 25, 2025
4d12223
fabrication optimization
ducky64 Oct 25, 2025
755c125
move importable schematics
ducky64 Oct 25, 2025
f8f16d6
re-routing
ducky64 Oct 25, 2025
159ef46
wip less expensive neopixels
ducky64 Oct 25, 2025
e8fdbf7
wip the last darn led
ducky64 Oct 25, 2025
c912afe
compactify
ducky64 Oct 25, 2025
a984e4e
roar
ducky64 Oct 25, 2025
f25dc46
Update UsbSourceMeasure.kicad_pcb
ducky64 Oct 25, 2025
2e844f9
progress
ducky64 Oct 25, 2025
58c962e
monster left
ducky64 Oct 25, 2025
203f0ed
fix caps
ducky64 Oct 25, 2025
ceb5568
wip
ducky64 Oct 25, 2025
fcfb4cc
rc2
ducky64 Oct 25, 2025
f8dbc0e
Delete worksheet.ods
ducky64 Oct 25, 2025
0c1c0e3
rc3
ducky64 Oct 25, 2025
138240f
wip line item reduction
ducky64 Oct 25, 2025
2d05a1b
optimization - zeners not behaving
ducky64 Oct 26, 2025
afe3c02
fix diodes parsing, fix mcp4728p/n
ducky64 Oct 29, 2025
2a39743
wip
ducky64 Oct 30, 2025
a7711c4
wip
ducky64 Oct 30, 2025
c75f1ab
cleaning
ducky64 Oct 30, 2025
6a5e033
Update test_usb_source_measure.py
ducky64 Oct 30, 2025
ba663fc
Update UsbSourceMeasure.kicad_pro
ducky64 Oct 31, 2025
ecb3cb0
merge
ducky64 Nov 9, 2025
2910ebc
rebaseline
ducky64 Nov 9, 2025
0f497a5
merge
ducky64 Nov 18, 2025
c24cfff
cleaning
ducky64 Nov 18, 2025
f77dd8e
correct part rotation
ducky64 Nov 19, 2025
305b39c
Fix for NOR gate
ducky64 Nov 26, 2025
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
11 changes: 9 additions & 2 deletions edg/abstract_parts/AbstractAnalogSwitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def __init__(self) -> None:
self.pwr = self.Port(VoltageSink.empty(), [Power])
self.gnd = self.Port(Ground.empty(), [Common])

self.control_gnd = self.Port(Ground.empty(), optional=True) # optional separate ground for control signal
self.control = self.Port(Vector(DigitalSink.empty())) # length source

self.com = self.Port(Passive.empty())
Expand All @@ -40,7 +41,7 @@ class AnalogSwitchTree(AnalogSwitch, GeneratorBlock):
def __init__(self, switch_size: IntLike = 0):
super().__init__()
self.switch_size = self.ArgParameter(switch_size)
self.generator_param(self.switch_size, self.inputs.requested())
self.generator_param(self.switch_size, self.inputs.requested(), self.control_gnd.is_connected())

def generate(self):
import math
Expand Down Expand Up @@ -72,6 +73,8 @@ def generate(self):
all_switches.append(sw)
self.connect(sw.pwr, self.pwr)
self.connect(sw.gnd, self.gnd)
if self.get(self.control_gnd.is_connected()):
self.connect(sw.control_gnd, self.control_gnd)

for sw_port_i in range(switch_size):
port_i = sw_i * switch_size + sw_port_i
Expand Down Expand Up @@ -115,6 +118,8 @@ def __init__(self) -> None:
self.device = self.Block(AnalogSwitch())
self.pwr = self.Export(self.device.pwr, [Power])
self.gnd = self.Export(self.device.gnd, [Common])

self.control_gnd = self.Port(Ground.empty(), optional=True) # optional separate ground for control signal
self.control = self.Export(self.device.control)

self.inputs = self.Port(Vector(AnalogSink.empty()))
Expand All @@ -125,7 +130,7 @@ def __init__(self) -> None:
impedance=self.device.analog_on_resistance + self.inputs.hull(lambda x: x.link().source_impedance)
)))

self.generator_param(self.inputs.requested())
self.generator_param(self.inputs.requested(), self.control_gnd.is_connected())

def generate(self):
super().generate()
Expand All @@ -138,6 +143,8 @@ def generate(self):
current_draw=self.out.link().current_drawn,
impedance=self.out.link().sink_impedance + self.device.analog_on_resistance
)))
if self.get(self.control_gnd.is_connected()):
self.connect(self.control_gnd, self.device.control_gnd)

def mux_to(self, inputs: Optional[List[Port[AnalogLink]]] = None,
output: Optional[Port[AnalogLink]] = None) -> 'AnalogMuxer':
Expand Down
28 changes: 27 additions & 1 deletion edg/abstract_parts/AbstractCapacitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,33 @@ def connected(self, gnd: Optional[Port[GroundLink]] = None, pwr: Optional[Port[V
return self


class CombinedCapacitorElement(Capacitor):
class AnalogCapacitor(DiscreteApplication, KiCadImportableBlock):
"""Capacitor attached to an analog line, that presents as an open model-wise.
"""
def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]:
assert symbol_name in ('Device:C', 'Device:C_Small', 'Device:C_Polarized', 'Device:C_Polarized_Small')
return {'1': self.io, '2': self.gnd}

def __init__(self, capacitance: RangeLike, *, exact_capacitance: BoolLike = False) -> None:
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.assign(self.cap.voltage, self.io.link().voltage - self.gnd.link().voltage)

def connected(self, gnd: Optional[Port[GroundLink]] = None, io: Optional[Port[AnalogLink]] = None) -> \
'AnalogCapacitor':
"""Convenience function to connect both ports, returning this object so it can still be given a name."""
if gnd is not None:
cast(Block, builder.get_enclosing_block()).connect(gnd, self.gnd)
if io is not None:
cast(Block, builder.get_enclosing_block()).connect(io, self.io)
return self


class CombinedCapacitorElement(Capacitor): # to avoid an abstract part error
def contents(self):
super().contents()
self.assign(self.actual_capacitance, self.capacitance) # fake it, since a combined capacitance is handwavey
Expand Down
63 changes: 63 additions & 0 deletions edg/abstract_parts/AbstractComparator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Mapping

from .ResistiveDivider import FeedbackVoltageDivider, VoltageDivider
from ..abstract_parts import Analog
from ..electronics_model import *

Expand All @@ -22,3 +23,65 @@ def __init__(self) -> None:
self.inn = self.Port(AnalogSink.empty())
self.inp = self.Port(AnalogSink.empty())
self.out = self.Port(DigitalSource.empty())


class VoltageComparator(GeneratorBlock):
"""A comparator subcircuit that compares an input voltage rail against some reference, either
internally generated from the power lines or an external analog signals.
Accounts for tolerance stackup on the reference input - so make sure the trip
tolerance is specified wide enough.
The output is logic high when the input exceeds the trip voltage by default,
this can be inverted with the invert parameter.
Optionally this can take a reference voltage input, otherwise this generates a divider.

TODO: maybe a version that takes an input analog signal?
"""
def __init__(self, trip_voltage: RangeLike, *, invert: BoolLike = False,
input_impedance: RangeLike=(4.7, 47)*kOhm,
trip_ref: RangeLike=1.65*Volt(tol=0.10)):
super().__init__()
self.comp = self.Block(Comparator())
self.gnd = self.Export(self.comp.gnd, [Common])
self.pwr = self.Export(self.comp.pwr, [Power])
self.input = self.Port(AnalogSink.empty(), [Input])
self.output = self.Export(self.comp.out, [Output])
self.ref = self.Port(AnalogSink.empty(), optional=True)

self.trip_voltage = self.ArgParameter(trip_voltage)
self.invert = self.ArgParameter(invert)
self.trip_ref = self.ArgParameter(trip_ref) # only used if self.ref disconnected
self.input_impedance = self.ArgParameter(input_impedance)
self.generator_param(self.ref.is_connected(), self.invert)

self.actual_trip_voltage = self.Parameter(RangeExpr())

def generate(self):
super().generate()

if self.get(self.ref.is_connected()):
ref_pin: Port[AnalogLink] = self.ref
ref_voltage = self.ref.link().signal
else:
self.ref_div = self.Block(VoltageDivider(
output_voltage=self.trip_ref,
impedance=self.input_impedance,
))
self.connect(self.ref_div.input, self.pwr)
self.connect(self.ref_div.gnd, self.gnd)
ref_pin = self.ref_div.output
ref_voltage = self.ref_div.output.link().signal

self.comp_div = self.Block(FeedbackVoltageDivider(
impedance=self.input_impedance,
output_voltage=ref_voltage,
assumed_input_voltage=self.trip_voltage
))
self.assign(self.actual_trip_voltage, self.comp_div.actual_input_voltage)
self.connect(self.comp_div.input, self.input.as_voltage_source())
self.connect(self.comp_div.gnd, self.gnd)
if not self.get(self.invert): # positive connection
self.connect(self.comp.inp, self.comp_div.output)
self.connect(self.comp.inn, ref_pin)
else: # negative connection
self.connect(self.comp.inn, self.comp_div.output)
self.connect(self.comp.inp, ref_pin)
9 changes: 9 additions & 0 deletions edg/abstract_parts/AbstractFets.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ class FetStandardFootprint(StandardFootprint['Fet']):
'4': block.gate,
'5': block.drain,
},
(
'Package_TO_SOT_THT:TO-220-3_Horizontal_TabUp',
'Package_TO_SOT_THT:TO-220-3_Horizontal_TabDown',
'Package_TO_SOT_THT:TO-220-3_Vertical',
): lambda block: {
'1': block.gate,
'2': block.drain,
'3': block.source,
},
}


Expand Down
94 changes: 93 additions & 1 deletion edg/abstract_parts/AbstractResistor.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,53 @@ def _row_sort_by(cls, row: PartsTableRow) -> Any:
super()._row_sort_by(row))


class SeriesResistor(Resistor, GeneratorBlock):
"""Splits a resistor into equal resistors in series. Improves power and voltage ratings
by distributing the load across multiple devices.

Generally used as a refinement to break up a single (logical) resistor that is dissipating too much power
or has an excessive voltage across it. Accounts for tolerance stackup for power and voltage distribution
using specified (not actual) resistor tolerance - is a pessimistic calculation."""
def __init__(self, *args, count: IntLike = 2, **kwargs):
super().__init__(*args, **kwargs)
self.count = self.ArgParameter(count)
self.generator_param(self.count, self.resistance)

def generate(self):
super().generate()
count = self.get(self.count)
last_port = self.a
cumu_resistance: RangeLike = Range.exact(0)
cumu_power_rating: RangeLike = Range.exact(0)
cumu_voltage_rating: RangeLike = Range.exact(0)
self.res = ElementDict[Resistor]()

# calculate tolerance stackup effects on R for worst-case power and voltage
resistance_range = self.get(self.resistance)
resistance_tol = (resistance_range.upper - resistance_range.lower) / 2 / resistance_range.center()
resistance_tol = min(0.05, resistance_tol) # in practice there should be no >5% resistors
resistance_ratio_range = Range((1 - resistance_tol) / (count + resistance_tol * (count - 2)),
(1 + resistance_tol) / (count - resistance_tol * (count - 2)))

elt_resistance = resistance_range / count
elt_power = self.power * resistance_ratio_range
elt_voltage = self.voltage * resistance_ratio_range

for i in range(count):
self.res[i] = res = self.Block(Resistor(resistance=elt_resistance,
power=elt_power,
voltage=elt_voltage))
self.connect(last_port, res.a)
cumu_resistance = cumu_resistance + res.actual_resistance
cumu_power_rating = cumu_power_rating + res.actual_power_rating
cumu_voltage_rating = cumu_voltage_rating + res.actual_voltage_rating
last_port = res.b
self.connect(last_port, self.b)
self.assign(self.actual_resistance, cumu_resistance)
self.assign(self.actual_power_rating, cumu_power_rating)
self.assign(self.actual_voltage_rating, cumu_voltage_rating)


class PullupResistor(DiscreteApplication):
"""Pull-up resistor with an VoltageSink for automatic implicit connect to a Power line."""
def __init__(self, resistance: RangeLike) -> None:
Expand Down Expand Up @@ -205,8 +252,12 @@ def generate(self):
self.connect(self.io.append_elt(DigitalSource.empty(), requested), res.io)


class SeriesPowerResistor(DiscreteApplication):
class SeriesPowerResistor(DiscreteApplication, KiCadImportableBlock):
"""Series resistor for power applications"""
def symbol_pinning(self, symbol_name: str) -> Mapping[str, BasePort]:
assert symbol_name in ('Device:R', 'Device:R_Small')
return {'1': self.pwr_in, '2': self.pwr_out}

def __init__(self, resistance: RangeLike) -> None:
super().__init__()

Expand Down Expand Up @@ -324,3 +375,44 @@ def contents(self):
def symbol_pinning(self, symbol_name: str) -> Dict[str, Port]:
assert symbol_name == 'Device:R'
return {'1': self.signal_in, '2': self.signal_out}


class DigitalClampResistor(Protection, KiCadImportableBlock):
"""Inline resistor that limits the current (to a parameterized amount) which works in concert
with ESD diodes in the downstream device to clamp the signal voltage to allowable levels.

The protection voltage can be extended beyond the modeled range from the input signal,
and can also be specified to allow zero output voltage (for when the downstream device
is powered down)

TODO: clamp_target should be inferred from the target voltage_limits,
but voltage_limits doesn't always get propagated."""
def __init__(self, clamp_target: RangeLike = (0, 3)*Volt, clamp_current: RangeLike = (1.0, 10)*mAmp,
protection_voltage: RangeLike = (0, 0)*Volt, zero_out: BoolLike = False):
super().__init__()

self.signal_in = self.Port(DigitalSink.empty(), [Input])
self.signal_out = self.Port(DigitalSource.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)

def contents(self):
super().contents()

# TODO bidirectional clamping calcs?
self.res = self.Block(Resistor(resistance=1/self.clamp_current * self.zero_out.then_else(
self.signal_in.link().voltage.hull(self.protection_voltage).upper(),
self.signal_in.link().voltage.hull(self.protection_voltage).upper() - self.clamp_target.upper(),
)))
self.connect(self.res.a.adapt_to(DigitalSink(current_draw=self.signal_out.link().current_drawn)), self.signal_in)
self.connect(self.res.b.adapt_to(DigitalSource(
voltage_out=self.signal_in.link().voltage.intersect(self.clamp_target),
output_thresholds=self.signal_in.link().output_thresholds
)), self.signal_out)

def symbol_pinning(self, symbol_name: str) -> Dict[str, Port]:
assert symbol_name == 'Device:R'
return {'1': self.signal_in, '2': self.signal_out}
12 changes: 6 additions & 6 deletions edg/abstract_parts/AbstractTestPoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,15 @@ def connected(self, io: Port[AnalogLink]) -> 'AnalogTestPoint':
return self


class AnalogRfTestPoint(BaseRfTestPoint, Block):
"""Test point with a AnalogSink port and 50-ohm matching resistor."""
class AnalogCoaxTestPoint(BaseRfTestPoint, Block):
"""Test point with a AnalogSink port and using a coax connector with shielding connected to gnd.
No impedance matching, this is intended for lower frequency signals where the wavelength would be
much longer than the test lead length"""
def __init__(self, *args):
super().__init__(*args)
self.res = self.Block(Resistor(50*Ohm(tol=0.05)))
self.io = self.Export(self.res.a.adapt_to(AnalogSink()), [InOut])
self.connect(self.res.b, self.conn.sig)
self.io = self.Export(self.conn.sig.adapt_to(AnalogSink()), [InOut])

def connected(self, io: Port[AnalogLink]) -> 'AnalogRfTestPoint':
def connected(self, io: Port[AnalogLink]) -> 'AnalogCoaxTestPoint':
cast(Block, builder.get_enclosing_block()).connect(io, self.io)
return self

Expand Down
6 changes: 3 additions & 3 deletions edg/abstract_parts/DigitalAmplifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ class HighSideSwitch(PowerSwitch, KiCadSchematicBlock, GeneratorBlock):

TODO: clamp_voltage should be compared against the actual voltage so the clamp is automatically generated,
but generators don't support link terms (yet?)"""
def __init__(self, pull_resistance: RangeLike = 10000*Ohm(tol=0.05), max_rds: FloatLike = 1*Ohm,
def __init__(self, pull_resistance: RangeLike = 10*kOhm(tol=0.05), max_rds: FloatLike = 1*Ohm,
frequency: RangeLike = RangeExpr.ZERO, *,
clamp_voltage: RangeLike = RangeExpr.ZERO, clamp_resistance_ratio: FloatLike = 10) -> None:
super().__init__()

self.pwr = self.Port(VoltageSink.empty(), [Power]) # amplifier voltage
self.gnd = self.Port(Ground.empty(), [Common])

self.control = self.Port(DigitalSink.empty(), [Input])
self.output = self.Port(VoltageSource.empty(), [Output])
self.control = self.Port(DigitalSink.empty())
self.output = self.Port(VoltageSource.empty())

self.pull_resistance = self.ArgParameter(pull_resistance)
self.max_rds = self.ArgParameter(max_rds)
Expand Down
23 changes: 23 additions & 0 deletions edg/abstract_parts/OpampCircuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,26 @@ def contents(self) -> None:
})

self.assign(self.actual_factor, 1 / self.r.actual_resistance / self.c.actual_capacitance)


class SummingAmplifier(OpampApplication):
@classmethod
def calculate_ratio(cls, resistances: List[Range]) -> List[Range]:
"""Calculates each input's contribution to the output, for 1x gain.
Non-inverting summing amplifier topology.
Based on https://www.electronicshub.org/summing-amplifier/, which calculates the voltage of each input
and uses superposition to combine them."""
output = []
for i, resistance in enumerate(resistances):
# compute the two tolerance corners
others = resistances[:i] + resistances[i+1:]
other_lowest_parallel = 1 / sum([1 / other.lower for other in others])
other_highest_parallel = 1 / sum([1 / other.upper for other in others])
# ratio is lowest when this resistance is highest and other is lowest
ratio_lowest = other_lowest_parallel / (resistance.upper + other_lowest_parallel)
# ratio is highest when this resistance is lowest and other is highest
ratio_highest = other_highest_parallel / (resistance.lower + other_highest_parallel)

output.append(Range(ratio_lowest, ratio_highest))

return output
Loading