Skip to content

Commit e196d3a

Browse files
authored
Reverse power flows, unified battery charging port (#462)
Resolves #330. Adds a reverse voltage direction for VoltageSource/Sink. Only at most one reverse voltage source per connection (and combined with the unique forward VoltageSource requirement, means at most one bidirectional power path). Adds unit tests. Refactors the LiPo connector, Mcp73831 battery charger IC, and reverse-capable PMOS reverse voltage protector to use this new infrastructure and eliminates the dedicated charging pseudo-port. This refactors the voltage DummyDevices to the electronics_model to support unit tests. The other ones should eventually be migrated To check for reverse source uniqueness, this adds some code infrastructure: - Adds an array-element equals for RangeExpr only - Adds bool array NOT operation - Adds compiler unit tests - Regenerates protos, bumps proto version.
1 parent 5d9a140 commit e196d3a

27 files changed

Lines changed: 571 additions & 119 deletions

compiler/src/main/scala/edg/ExprBuilder.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ object ExprBuilder {
133133
)
134134
}
135135

136+
def RangeEmpty(): lit.ValueLit = {
137+
lit.ValueLit(`type` =
138+
lit.ValueLit.Type.Range(lit.RangeLit(
139+
minimum = Some(Floating(Float.NaN)),
140+
maximum = Some(Floating(Float.NaN))
141+
))
142+
)
143+
}
144+
136145
def Array(values: Seq[lit.ValueLit]): lit.ValueLit = {
137146
lit.ValueLit(`type` =
138147
lit.ValueLit.Type.Array(lit.ArrayLit(

compiler/src/main/scala/edg/compiler/Compiler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class AssignNamer() {
117117
}
118118

119119
object Compiler {
120-
final val kExpectedProtoVersion = 8
120+
final val kExpectedProtoVersion = 9
121121
}
122122

123123
/** Compiler for a particular design, with an associated library to elaborate references from.

compiler/src/main/scala/edg/compiler/ExprEvaluate.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,16 @@ object ExprEvaluate {
224224
binarySet.op match {
225225
// Note promotion rules: range takes precedence, then float, then int
226226
// TODO: can we deduplicate these cases to delegate them to evalBinary?
227+
case Op.EQ => (lhs, rhs) match {
228+
case (lhss: ArrayValue[ExprValue] @unchecked, rhs: ExprValue) =>
229+
val resultElts = lhss.values.map { arrayElt =>
230+
evalBinary(expr.BinaryExpr(op = expr.BinaryExpr.Op.EQ), arrayElt, rhs)
231+
}
232+
ArrayValue(resultElts)
233+
case _ => throw new ExprEvaluateException(
234+
s"Unknown binary set operand types in $lhs ${binarySet.op} $rhs from $binarySet"
235+
)
236+
}
227237
case Op.ADD => (lhs, rhs) match {
228238
case (ArrayValue.ExtractRange(arrayElts), rhs: RangeType) =>
229239
val resultElts = arrayElts.map { arrayElt =>
@@ -363,6 +373,8 @@ object ExprEvaluate {
363373
evalUnary(expr.UnaryExpr(op = expr.UnaryExpr.Op.NEGATE), arrayElt)
364374
}
365375
ArrayValue(resultElts)
376+
case ArrayValue.ExtractBoolean(arrayElts) =>
377+
ArrayValue(arrayElts.map { arrayElt => BooleanValue(!arrayElt) })
366378
case _ => throw new ExprEvaluateException(s"Unknown unary set operand in ${unarySet.op} $vals from $unarySet")
367379
}
368380

compiler/src/main/scala/edg/compiler/ExprToString.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class ExprToString() extends ValueExprMap[String] {
8282
import expr.BinarySetExpr.Op
8383
object InfixOp {
8484
def unapply(op: Op): Option[String] = op match {
85+
case Op.EQ => Some("==")
8586
case Op.ADD => Some("+")
8687
case Op.MULT => Some("×")
8788
case Op.CONCAT => None
@@ -91,7 +92,7 @@ class ExprToString() extends ValueExprMap[String] {
9192
object PrefixOp {
9293
def unapply(op: Op): Option[String] = op match {
9394
case Op.CONCAT => Some("concat")
94-
case Op.ADD | Op.MULT => None
95+
case Op.EQ | Op.ADD | Op.MULT => None
9596
case Op.UNDEFINED | Op.Unrecognized(_) => None
9697
}
9798
}

compiler/src/test/scala/edg/compiler/ExprEvaluateTest.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,17 @@ class ExprEvaluateTest extends AnyFlatSpec {
300300
it should "handle array unary set ops" in {
301301
import edg.ExprBuilder.Literal
302302
import edgir.expr.expr.UnarySetExpr.Op
303+
evalTest.map(
304+
ValueExpr.UnarySetOp(
305+
Op.NEGATE,
306+
ValueExpr.Literal(Seq(
307+
Literal.Boolean(false),
308+
Literal.Boolean(true),
309+
Literal.Boolean(false),
310+
))
311+
)
312+
) should equal(ArrayValue(Seq(BooleanValue(true), BooleanValue(false), BooleanValue(true))))
313+
303314
evalTest.map(
304315
ValueExpr.UnarySetOp(
305316
Op.FLATTEN,
@@ -327,6 +338,21 @@ class ExprEvaluateTest extends AnyFlatSpec {
327338
it should "handle array-value (broadcast) ops" in {
328339
import edg.ExprBuilder.Literal
329340
import edgir.expr.expr.BinarySetExpr.Op
341+
evalTest.map(
342+
ValueExpr.BinSetOp(
343+
Op.EQ,
344+
ValueExpr.Literal(Seq(Literal.Range(0, 10), Literal.Range(1, 11))),
345+
ValueExpr.Literal(0, 10)
346+
)
347+
) should equal(ArrayValue(Seq(BooleanValue(true), BooleanValue(false))))
348+
evalTest.map(
349+
ValueExpr.BinSetOp(
350+
Op.EQ,
351+
ValueExpr.Literal(Seq(Literal.Range(0, 10), Literal.RangeEmpty())),
352+
ValueExpr.LiteralRangeEmpty()
353+
)
354+
) should equal(ArrayValue(Seq(BooleanValue(false), BooleanValue(true))))
355+
330356
evalTest.map(
331357
ValueExpr.BinSetOp(
332358
Op.ADD,

edg/abstract_parts/Categories.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -384,13 +384,6 @@ class PassiveComponent(DiscreteComponent):
384384
pass
385385

386386

387-
@abstract_block
388-
class DummyDevice(InternalBlock):
389-
"""Non-physical "device" used to affect parameters."""
390-
391-
pass
392-
393-
394387
@abstract_block
395388
class IdealModel(InternalBlock):
396389
"""Ideal model device that can be used as a placeholder to get a design compiling

edg/abstract_parts/DummyDevices.py

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,6 @@ 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-
19-
class DummyVoltageSource(DummyDevice):
20-
def __init__(self, voltage_out: RangeLike = RangeExpr.ZERO, current_limits: RangeLike = RangeExpr.ALL) -> None:
21-
super().__init__()
22-
23-
self.pwr = self.Port(VoltageSource(voltage_out=voltage_out, current_limits=current_limits), [Power, InOut])
24-
25-
self.current_drawn = self.Parameter(RangeExpr(self.pwr.link().current_drawn))
26-
self.voltage_limits = self.Parameter(RangeExpr(self.pwr.link().voltage_limits))
27-
28-
29-
class DummyVoltageSink(DummyDevice):
30-
def __init__(self, voltage_limit: RangeLike = RangeExpr.ALL, current_draw: RangeLike = RangeExpr.ZERO) -> None:
31-
super().__init__()
32-
33-
self.pwr = self.Port(VoltageSink(voltage_limits=voltage_limit, current_draw=current_draw), [Power, InOut])
34-
self.voltage = self.Parameter(RangeExpr(self.pwr.link().voltage))
35-
self.current_limits = self.Parameter(RangeExpr(self.pwr.link().current_limits))
36-
37-
3813
class DummyDigitalSource(DummyDevice):
3914
def __init__(self, voltage_out: RangeLike = RangeExpr.ZERO, current_limits: RangeLike = RangeExpr.ALL) -> None:
4015
super().__init__()

edg/abstract_parts/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
PartsTableSelectorFootprint,
1212
)
1313

14-
from .Categories import DummyDevice
1514
from .Categories import DiscreteComponent, DiscreteSemiconductor, PassiveComponent
1615
from .Categories import DiscreteApplication
1716
from .Categories import Analog, OpampApplication

edg/core/ArrayExpr.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ def hull(self) -> ArrayEltType:
135135
class ArrayBoolExpr(ArrayExpr[BoolExpr, List[bool], ArrayBoolLike]):
136136
_elt_type = BoolExpr
137137

138+
def __invert__(self) -> ArrayBoolExpr:
139+
return self._new_bind(UnarySetOpBinding(self, BoolOp.op_not))
140+
138141
def any(self) -> BoolExpr:
139142
return BoolExpr()._new_bind(UnarySetOpBinding(self, BoolOp.op_or))
140143

@@ -179,6 +182,11 @@ def __rtruediv__(self, other: Union[FloatLike, RangeLike]) -> ArrayRangeExpr:
179182
self._create_unary_set_op(NumericOp.invert), RangeExpr._to_expr_type(other), NumericOp.mul
180183
)
181184

185+
def elts_equals(self, other: RangeLike) -> ArrayBoolExpr:
186+
"""Returns an ArrayBoolExpr of equality between each element of this and single-element other.
187+
TODO: generalize to equality for other array types, needs some generic version of _to_expr_type"""
188+
return ArrayBoolExpr()._new_bind(BinarySetOpBinding(self, RangeExpr._to_expr_type(other), EqOp.all_equal))
189+
182190

183191
ArrayStringLike = Union["ArrayStringExpr", Sequence[StringLike]]
184192

edg/core/Binding.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ def __init__(self, src: ConstraintExpr, op: Union[NumericOp, BoolOp, EqOp, Range
314314
NumericOp.sum: edgir.UnarySetExpr.SUM,
315315
BoolOp.op_and: edgir.UnarySetExpr.ALL_TRUE,
316316
BoolOp.op_or: edgir.UnarySetExpr.ANY_TRUE,
317+
BoolOp.op_not: edgir.UnarySetExpr.NEGATE,
317318
EqOp.all_equal: edgir.UnarySetExpr.ALL_EQ,
318319
EqOp.all_unique: edgir.UnarySetExpr.ALL_UNIQUE,
319320
RangeSetOp.min: edgir.UnarySetExpr.MINIMUM,
@@ -391,11 +392,12 @@ class BinarySetOpBinding(Binding):
391392
def __repr__(self) -> str:
392393
return f"BinaryOp({self.op}, ...)"
393394

394-
def __init__(self, lhset: ConstraintExpr, rhs: ConstraintExpr, op: NumericOp):
395+
def __init__(self, lhset: ConstraintExpr, rhs: ConstraintExpr, op: Union[NumericOp, EqOp]):
395396
self.op_map = {
396397
# Numeric
397398
NumericOp.add: edgir.BinarySetExpr.ADD,
398399
NumericOp.mul: edgir.BinarySetExpr.MULT,
400+
EqOp.all_equal: edgir.BinarySetExpr.EQ,
399401
}
400402

401403
super().__init__()

0 commit comments

Comments
 (0)