Skip to content

Commit becfb8f

Browse files
authored
Nondirectioned Ground refactor (#364)
This introduces a dedicated non-directioned Ground type, with an optional GroundReference which can specify the voltage level. Ground can optionally specify a voltage tolerance. Prior, Ground was actually just a VoltageSink in disguise, which had a few problems stemming from the VoltageSink/VoltageSource directionality that this fixes: - Avoids needing explicit VoltageSource merges when connecting the ground of multiple power supplies, eg USB + battery - Avoids a confusing gnd and gnd_src for devices that can source or sink power - Eliminates unused current_draw parameter This refactors a lot of internal libraries. Notable structural changes: - BLDC driver current-sense resistors moved into the block, generating if the sense pins are used. - Ditto with the soldering iron connector - Gate driver switch node is now a Ground - Unify gnd and gnd_src for blocks that have both - DecouplingCapacitor defined in terms of gnd/voltage instead of pos/neg This change is breaking and requires some changes to top-level designs: - Voltage-source merges for grounds for multiple power sources must be removed - GroundSource should be replaced with Ground (remapped with a deprecation) - VoltageTestPoint must be replaced with GroundTestPoint, now that the types are different - In general, ground-side current sense resistors are more difficult now, the move so far is to move them into the associated block Library cleanup to support offset grounds is needed in the future, to make sure all devices use VoltageSink.from_gnd to use a ground-relative voltage instead of assuming ground is 0v. Also some internal cleanup. Notes for the future: - Some things that might need further thought - Is there a clean unified way to get the voltage of a port accounting for connected-ness and directionality? Currently, we have `GroundLink._voltage_range` and `VoltageLink,_voltage_range`, though code that doesn't need to be as robust uses port.link().voltage
1 parent 8390382 commit becfb8f

File tree

96 files changed

+1113
-1012
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+1113
-1012
lines changed

edg/abstract_parts/AbstractCapacitor.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -328,17 +328,18 @@ def __init__(self, capacitance: RangeLike, *, exact_capacitance: BoolLike = Fals
328328
super().__init__()
329329

330330
self.cap = self.Block(Capacitor(capacitance, voltage=RangeExpr(), exact_capacitance=exact_capacitance))
331-
self.gnd = self.Export(self.cap.neg.adapt_to(VoltageSink()), [Common])
332-
self.pwr = self.Export(self.cap.pos.adapt_to(VoltageSink(
333-
voltage_limits=(self.cap.actual_voltage_rating + self.gnd.link().voltage).hull(self.gnd.link().voltage),
331+
self.gnd = self.Export(self.cap.neg.adapt_to(Ground()), [Common])
332+
self.pwr = self.Export(self.cap.pos.adapt_to(VoltageSink.from_gnd(
333+
self.gnd,
334+
voltage_limits=self.cap.actual_voltage_rating,
334335
current_draw=0*Amp(tol=0)
335336
)), [Power, InOut])
336337

337338
self.assign(self.cap.voltage, self.pwr.link().voltage - self.gnd.link().voltage)
338339

339340
# TODO there should be a way to forward the description string of the inner element
340341

341-
def connected(self, gnd: Optional[Port[VoltageLink]] = None, pwr: Optional[Port[VoltageLink]] = None) -> \
342+
def connected(self, gnd: Optional[Port[GroundLink]] = None, pwr: Optional[Port[VoltageLink]] = None) -> \
342343
'DecouplingCapacitor':
343344
"""Convenience function to connect both ports, returning this object so it can still be given a name."""
344345
if gnd is not None:

edg/abstract_parts/AbstractDevices.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def __init__(self, voltage: RangeLike,
1111
super().__init__()
1212

1313
self.pwr = self.Port(VoltageSource.empty()) # set by subclasses
14-
self.gnd = self.Port(GroundSource.empty())
14+
self.gnd = self.Port(Ground.empty())
1515

1616
self.voltage = self.ArgParameter(voltage)
1717
self.capacity = self.ArgParameter(capacity)

edg/abstract_parts/AbstractLed.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def __init__(self, count: IntLike, color: LedColorLike = Led.Any, *,
111111
current_draw: RangeLike = (1, 10) * mAmp):
112112
super().__init__()
113113
self.signals = self.Port(Vector(DigitalSink.empty()), [InOut])
114-
self.gnd = self.Port(VoltageSink.empty(), [Common])
114+
self.gnd = self.Port(Ground.empty(), [Common])
115115

116116
self.color = self.ArgParameter(color)
117117
self.current_draw = self.ArgParameter(current_draw)

edg/abstract_parts/AbstractOpamp.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def __init__(self):
5050
class MultipackOpampGenerator(MultipackOpamp, GeneratorBlock):
5151
"""Skeleton base class that provides scaffolding for common packed opamp definitions"""
5252
class OpampPorts(NamedTuple):
53-
gnd: VoltageSink
53+
gnd: Ground
5454
pwr: VoltageSink
5555
amps: List[Tuple[AnalogSink, AnalogSink, AnalogSource]] # amp-, amp+, out
5656

@@ -68,17 +68,17 @@ def generate(self):
6868
super().generate()
6969
amp_ports = self._make_multipack_opamp()
7070

71-
self.gnd_merge = self.Block(PackedVoltageSource())
71+
self.gnd_merge = self.Block(PackedGround())
7272
self.pwr_merge = self.Block(PackedVoltageSource())
73-
self.connect(self.gnd_merge.pwr_out, amp_ports.gnd)
73+
self.connect(self.gnd_merge.gnd_out, amp_ports.gnd)
7474
self.connect(self.pwr_merge.pwr_out, amp_ports.pwr)
7575

7676
requested = self.get(self.pwr.requested())
7777
assert self.get(self.gnd.requested()) == self.get(self.inp.requested()) == \
7878
self.get(self.inn.requested()) == self.get(self.out.requested()) == requested
7979
for i, (amp_neg, amp_pos, amp_out) in zip(requested, amp_ports.amps):
8080
self.connect(self.pwr.append_elt(VoltageSink.empty(), i), self.pwr_merge.pwr_ins.request(i))
81-
self.connect(self.gnd.append_elt(VoltageSink.empty(), i), self.gnd_merge.pwr_ins.request(i))
81+
self.connect(self.gnd.append_elt(Ground.empty(), i), self.gnd_merge.gnd_ins.request(i))
8282
self.connect(self.inn.append_elt(AnalogSink.empty(), i), amp_neg)
8383
self.connect(self.inp.append_elt(AnalogSink.empty(), i), amp_pos)
8484
self.connect(self.out.append_elt(AnalogSource.empty(), i), amp_out)

edg/abstract_parts/AbstractResistor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ class PulldownResistorArray(TypedTestPoint, GeneratorBlock):
204204
@init_in_parent
205205
def __init__(self, resistance: RangeLike):
206206
super().__init__()
207-
self.gnd = self.Port(VoltageSink.empty(), [Common])
207+
self.gnd = self.Port(Ground.empty(), [Common])
208208
self.io = self.Port(Vector(DigitalSingleSource.empty()), [InOut])
209209
self.generator_param(self.io.requested())
210210
self.resistance = self.ArgParameter(resistance)

edg/abstract_parts/AbstractTestPoint.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ def contents(self):
5151
self.assign(conn_tp.tp_name, (self.tp_name == "").then_else(self.io.link().name(), self.tp_name))
5252

5353

54+
class GroundTestPoint(BaseTypedTestPoint, Block):
55+
"""Test point with a VoltageSink port."""
56+
def __init__(self, *args):
57+
super().__init__(*args)
58+
self.io = self.Port(Ground.empty(), [InOut])
59+
self.connect(self.io, self.tp.io.adapt_to(Ground()))
60+
61+
def connected(self, io: Port[GroundLink]) -> 'GroundTestPoint':
62+
cast(Block, builder.get_enclosing_block()).connect(io, self.io)
63+
return self
64+
65+
5466
class VoltageTestPoint(BaseTypedTestPoint, Block):
5567
"""Test point with a VoltageSink port."""
5668
def __init__(self, *args):

edg/abstract_parts/DummyDevices.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ def __init__(self) -> None:
1010
self.io = self.Port(Passive(), [InOut])
1111

1212

13+
class DummyGround(DummyDevice):
14+
def __init__(self) -> None:
15+
super().__init__()
16+
self.gnd = self.Port(Ground(), [Common, InOut])
17+
18+
1319
class DummyVoltageSource(DummyDevice):
1420
@init_in_parent
1521
def __init__(self, voltage_out: RangeLike = RangeExpr.ZERO,

edg/abstract_parts/GateDrivers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __init__(self, has_boot_diode: BoolLike):
2929
self.low_out = self.Port(DigitalSource.empty()) # referenced to main gnd
3030

3131
self.high_pwr = self.Port(VoltageSink.empty(), optional=True) # not used with internal boot diode
32-
self.high_gnd = self.Port(VoltageSink.empty()) # this encodes the voltage limit from gnd
32+
self.high_gnd = self.Port(Ground.empty()) # a separate constraint needs to encode voltage limits
3333
self.high_out = self.Port(DigitalSource.empty()) # referenced to high_pwr and high_gnd
3434

3535

edg/abstract_parts/IoControllerInterfaceMixins.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -86,26 +86,15 @@ class IoControllerBle(BlockInterfaceMixin[BaseIoController]):
8686
"""Mixin indicating this IoController has programmable Bluetooth LE. Does not expose any ports."""
8787

8888

89-
@non_library
90-
class IoControllerGroundOut(BlockInterfaceMixin[IoController]):
91-
"""Base mixin for an IoController that can act as a power output (e.g. dev boards),
92-
this only provides the ground source pin. Subclasses can define output power pins.
93-
Multiple power pin mixins can be used on the same class, but only one gnd_out can be connected."""
94-
def __init__(self, *args, **kwargs) -> None:
95-
super().__init__(*args, **kwargs)
96-
self.gnd_out = self.Port(GroundSource.empty(), optional=True,
97-
doc="Ground for power output ports, when the device is acting as a power source")
98-
99-
100-
class IoControllerPowerOut(IoControllerGroundOut, BlockInterfaceMixin[IoController]):
89+
class IoControllerPowerOut(BlockInterfaceMixin[IoController]):
10190
"""IO controller mixin that provides an output of the IO controller's VddIO rail, commonly 3.3v."""
10291
def __init__(self, *args, **kwargs) -> None:
10392
super().__init__(*args, **kwargs)
10493
self.pwr_out = self.Port(VoltageSource.empty(), optional=True,
10594
doc="Power output port, typically of the device's Vdd or VddIO rail; must be used with gnd_out")
10695

10796

108-
class IoControllerUsbOut(IoControllerGroundOut, BlockInterfaceMixin[IoController]):
97+
class IoControllerUsbOut(BlockInterfaceMixin[IoController]):
10998
"""IO controller mixin that provides an output of the IO controller's USB Vbus."""
11099
def __init__(self, *args, **kwargs) -> None:
111100
super().__init__(*args, **kwargs)

edg/abstract_parts/PowerCircuits.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,11 @@ def contents(self):
8888
self.connect(self.high_gate_res.b, self.high_fet.gate)
8989

9090
# to avoid tolerance stackup, model the switch node as a static voltage
91+
self.connect(self.low_fet.drain, self.high_fet.source)
9192
self.connect(self.low_fet.drain.adapt_to(VoltageSource(
92-
voltage_out=self.pwr.link().voltage)),
93-
self.high_fet.source.adapt_to(VoltageSink()),
94-
self.driver.high_gnd,
93+
voltage_out=self.pwr.link().voltage)),
9594
self.out)
95+
self.connect(self.out.as_ground((0, 0)*Amp), self.driver.high_gnd) # TODO model driver current
9696

9797

9898
class FetHalfBridgeIndependent(FetHalfBridge, HalfBridgeIndependent):

0 commit comments

Comments
 (0)