Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .github/workflows/pr-scala.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ jobs:
- uses: actions/setup-python@v1
with:
python-version: '3.10'
- name: Setup sbt launcher
uses: sbt/setup-sbt@v1
- name: install dependencies
run: pip install -r requirements.txt
- name: sbt test
Expand Down
27 changes: 17 additions & 10 deletions compiler/src/main/scala/edg/compiler/ExprEvaluate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,16 @@ object ExprEvaluate {
}

case Op.RANGE => (lhs, rhs) match {
case (FloatPromotable(lhs), FloatPromotable(rhs)) => RangeValue(math.min(lhs, rhs), math.max(lhs, rhs))
case (FloatPromotable(lhs), FloatPromotable(rhs)) =>
if (lhs.isNaN && rhs.isNaN) { // here, NaN is treated as empty and dispreferred (instead of NaN prop)
RangeEmpty
} else if (lhs.isNaN) {
RangeValue(rhs, rhs)
} else if (rhs.isNaN) {
RangeValue(lhs, lhs)
} else {
RangeValue(math.min(lhs, rhs), math.max(lhs, rhs))
}
case _ =>
throw new ExprEvaluateException(s"Unknown binary operands types in $lhs ${binary.op} $rhs from $binary")
}
Expand Down Expand Up @@ -259,6 +268,12 @@ object ExprEvaluate {

case (Op.MIN, RangeValue(valMin, _)) => FloatValue(valMin)
case (Op.MAX, RangeValue(_, valMax)) => FloatValue(valMax)

// TODO can we have stricter semantics to avoid min(RangeEmpty) and max(RangeEmpty)?
// This just NaNs out so at least it propagates
case (Op.MAX, RangeEmpty) => FloatValue(Float.NaN)
case (Op.MIN, RangeEmpty) => FloatValue(Float.NaN)

case (Op.CENTER, RangeValue(valMin, valMax)) => FloatValue((valMin + valMax) / 2)
case (Op.WIDTH, RangeValue(valMin, valMax)) => FloatValue(math.abs(valMax - valMin))

Expand All @@ -273,6 +288,7 @@ object ExprEvaluate {
case (Op.SUM, ArrayValue.Empty(_)) => FloatValue(0) // TODO type needs to be dynamic
case (Op.SUM, ArrayValue.ExtractFloat(vals)) => FloatValue(vals.sum)
case (Op.SUM, ArrayValue.ExtractInt(vals)) => IntValue(vals.sum)
case (Op.SUM, ArrayValue.ExtractBoolean(vals)) => IntValue(vals.count(_ == true))
case (Op.SUM, ArrayValue.UnpackRange(extracted)) => extracted match {
case ArrayValue.UnpackRange.FullRange(valMins, valMaxs) => RangeValue(valMins.sum, valMaxs.sum)
case _ => RangeEmpty // TODO how should sum behave on empty ranges?
Expand All @@ -294,15 +310,6 @@ object ExprEvaluate {
case (Op.MINIMUM, ArrayValue.ExtractFloat(vals)) => FloatValue(vals.min)
case (Op.MINIMUM, ArrayValue.ExtractInt(vals)) => IntValue(vals.min)

// TODO this is definitely a hack in the absence of a proper range extractor
case (Op.MAXIMUM, RangeValue(lower, upper)) => FloatValue(upper)
case (Op.MINIMUM, RangeValue(lower, upper)) => FloatValue(lower)

// TODO can we have stricter semantics to avoid min(RangeEmpty) and max(RangeEmpty)?
// This just NaNs out so at least it propagates
case (Op.MAXIMUM, RangeEmpty) => FloatValue(Float.NaN)
case (Op.MINIMUM, RangeEmpty) => FloatValue(Float.NaN)

// TODO this should be a user-level assertion instead of a compiler error
case (Op.SET_EXTRACT, ArrayValue.Empty(_)) =>
throw new ExprEvaluateException(s"SetExtract with empty values from $unarySet")
Expand Down
2 changes: 1 addition & 1 deletion developing.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ python -m unittest discover

Or, to run tests for a specific package (eg, `edg_core` in this command):
```
python -m unittest discover -s edg_core -t .
python -m unittest discover -s edg.core -t .
```

Or, to run one specific test:
Expand Down
1 change: 1 addition & 0 deletions edg/BoardTop.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ def refinements(self) -> Refinements:
(Fpc050Top, Afc07Top),
(Fpc030Bottom, HiroseFh35cshw),
(UsbEsdDiode, Pesd5v0x1bt),
(Comparator, Lmv331),
(Opamp, Lmv321),
(SpiMemory, W25q), # 128M version is a basic part
(TestPoint, Keystone5015), # this is larger, but is part of JLC's parts inventory
Expand Down
24 changes: 24 additions & 0 deletions edg/abstract_parts/AbstractComparator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import Mapping

from ..electronics_model import *


class Comparator(KiCadInstantiableBlock, Block):
"""Abstract comparator interface, output goes high when inp > inn."""
def symbol_pinning(self, symbol_name: str) -> Mapping[str, BasePort]:
assert symbol_name in ('Simulation_SPICE:OPAMP', 'edg_importable:Opamp')
return {'+': self.inp, '-': self.inn, '3': self.out, 'V+': self.pwr, 'V-': self.gnd}

@classmethod
def block_from_symbol(cls, symbol_name: str, properties: Mapping[str, str]) -> 'Comparator':
return Comparator()

@init_in_parent
def __init__(self) -> None:
super().__init__()

self.pwr = self.Port(VoltageSink.empty(), [Power])
self.gnd = self.Port(Ground.empty(), [Common])
self.inn = self.Port(AnalogSink.empty())
self.inp = self.Port(AnalogSink.empty())
self.out = self.Port(DigitalSource.empty())
2 changes: 1 addition & 1 deletion edg/abstract_parts/AbstractDebugHeaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class SwdCortexTargetConnectorReset(BlockInterfaceMixin[SwdCortexTargetConnector
"""Mixin for SWD connectors with adding the optional reset pin"""
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.reset = self.Port(DigitalBidir.empty(), optional=True) # can tri-state when not asserted
self.reset = self.Port(DigitalSource.empty(), optional=True) # as open-drain


class SwdCortexTargetConnectorSwo(BlockInterfaceMixin[SwdCortexTargetConnector]):
Expand Down
12 changes: 6 additions & 6 deletions edg/abstract_parts/AbstractResistor.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def __init__(self, resistance: RangeLike) -> None:

self.pwr = self.Export(self.res.a.adapt_to(VoltageSink()), [Power])
self.io = self.Export(self.res.b.adapt_to(
DigitalSingleSource.high_from_supply(self.pwr, is_pullup=True)
DigitalSource.pullup_from_supply(self.pwr)
), [InOut])

def connected(self, pwr: Optional[Port[VoltageLink]] = None, io: Optional[Port[DigitalLink]] = None) -> \
Expand All @@ -154,7 +154,7 @@ def __init__(self, resistance: RangeLike) -> None:

self.gnd = self.Export(self.res.a.adapt_to(Ground()), [Common])
self.io = self.Export(self.res.b.adapt_to(
DigitalSingleSource.low_from_supply(self.gnd, is_pulldown=True)
DigitalSource.pulldown_from_supply(self.gnd)
), [InOut])

def connected(self, gnd: Optional[Port[VoltageLink]] = None, io: Optional[Port[DigitalLink]] = None) -> \
Expand All @@ -173,7 +173,7 @@ class PullupResistorArray(TypedTestPoint, GeneratorBlock):
def __init__(self, resistance: RangeLike):
super().__init__()
self.pwr = self.Port(VoltageSink.empty(), [Power])
self.io = self.Port(Vector(DigitalSingleSource.empty()), [InOut])
self.io = self.Port(Vector(DigitalSource.empty()), [InOut])
self.generator_param(self.io.requested())
self.resistance = self.ArgParameter(resistance)

Expand All @@ -183,7 +183,7 @@ def generate(self):
for requested in self.get(self.io.requested()):
res = self.res[requested] = self.Block(PullupResistor(self.resistance))
self.connect(self.pwr, res.pwr)
self.connect(self.io.append_elt(DigitalSingleSource.empty(), requested), res.io)
self.connect(self.io.append_elt(DigitalSource.empty(), requested), res.io)


class PulldownResistorArray(TypedTestPoint, GeneratorBlock):
Expand All @@ -192,7 +192,7 @@ class PulldownResistorArray(TypedTestPoint, GeneratorBlock):
def __init__(self, resistance: RangeLike):
super().__init__()
self.gnd = self.Port(Ground.empty(), [Common])
self.io = self.Port(Vector(DigitalSingleSource.empty()), [InOut])
self.io = self.Port(Vector(DigitalSource.empty()), [InOut])
self.generator_param(self.io.requested())
self.resistance = self.ArgParameter(resistance)

Expand All @@ -202,7 +202,7 @@ def generate(self):
for requested in self.get(self.io.requested()):
res = self.res[requested] = self.Block(PulldownResistor(self.resistance))
self.connect(self.gnd, res.gnd)
self.connect(self.io.append_elt(DigitalSingleSource.empty(), requested), res.io)
self.connect(self.io.append_elt(DigitalSource.empty(), requested), res.io)


class SeriesPowerResistor(DiscreteApplication):
Expand Down
28 changes: 14 additions & 14 deletions edg/abstract_parts/AbstractSwitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,14 @@ def __init__(self) -> None:
super().__init__()

self.gnd = self.Port(Ground.empty(), [Common])
self.out = self.Port(DigitalSingleSource.empty(), [Output])
self.out = self.Port(DigitalSource.empty(), [Output])

def contents(self):
super().contents()
self.package = self.Block(Switch(current=self.out.link().current_drawn,
voltage=self.out.link().voltage))

self.connect(self.out, self.package.sw.adapt_to(DigitalSingleSource.low_from_supply(self.gnd)))
self.connect(self.out, self.package.sw.adapt_to(DigitalSource.low_from_supply(self.gnd)))
self.connect(self.gnd, self.package.com.adapt_to(Ground()))


Expand All @@ -107,8 +107,8 @@ def __init__(self) -> None:
super().__init__()

self.gnd = self.Port(Ground.empty(), [Common])
self.a = self.Port(DigitalSingleSource.empty())
self.b = self.Port(DigitalSingleSource.empty())
self.a = self.Port(DigitalSource.empty())
self.b = self.Port(DigitalSource.empty())


class DigitalWrapperRotaryEncoder(DigitalRotaryEncoder):
Expand All @@ -118,7 +118,7 @@ def contents(self):
self.package = self.Block(RotaryEncoder(current=self.a.link().current_drawn.hull(self.b.link().current_drawn),
voltage=self.a.link().voltage.hull(self.b.link().voltage)))

dio_model = DigitalSingleSource.low_from_supply(self.gnd)
dio_model = DigitalSource.low_from_supply(self.gnd)
self.connect(self.a, self.package.a.adapt_to(dio_model))
self.connect(self.b, self.package.b.adapt_to(dio_model))
self.connect(self.gnd, self.package.com.adapt_to(Ground()))
Expand All @@ -130,7 +130,7 @@ class DigitalRotaryEncoderSwitch(BlockInterfaceMixin[DigitalRotaryEncoder]):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)

self.sw = self.Port(DigitalSingleSource.empty(), optional=True)
self.sw = self.Port(DigitalSource.empty(), optional=True)


class DigitalWrapperRotaryEncoderWithSwitch(DigitalRotaryEncoderSwitch, DigitalWrapperRotaryEncoder, GeneratorBlock):
Expand All @@ -142,7 +142,7 @@ def generate(self):
super().generate()
if self.get(self.sw.is_connected()):
package_sw = self.package.with_mixin(RotaryEncoderSwitch())
dio_model = DigitalSingleSource.low_from_supply(self.gnd)
dio_model = DigitalSource.low_from_supply(self.gnd)
self.connect(self.sw, package_sw.sw.adapt_to(dio_model))


Expand All @@ -153,10 +153,10 @@ def __init__(self) -> None:
super().__init__()

self.gnd = self.Port(Ground.empty(), [Common])
self.a = self.Port(DigitalSingleSource.empty())
self.b = self.Port(DigitalSingleSource.empty())
self.c = self.Port(DigitalSingleSource.empty())
self.d = self.Port(DigitalSingleSource.empty())
self.a = self.Port(DigitalSource.empty())
self.b = self.Port(DigitalSource.empty())
self.c = self.Port(DigitalSource.empty())
self.d = self.Port(DigitalSource.empty())


class DigitalWrapperDirectionSwitch(DigitalDirectionSwitch):
Expand All @@ -166,7 +166,7 @@ def contents(self):
self.package = self.Block(DirectionSwitch(current=self.a.link().current_drawn.hull(self.b.link().current_drawn),
voltage=self.a.link().voltage.hull(self.b.link().voltage)))

dio_model = DigitalSingleSource.low_from_supply(self.gnd)
dio_model = DigitalSource.low_from_supply(self.gnd)
self.connect(self.a, self.package.a.adapt_to(dio_model))
self.connect(self.b, self.package.b.adapt_to(dio_model))
self.connect(self.c, self.package.c.adapt_to(dio_model))
Expand All @@ -180,7 +180,7 @@ class DigitalDirectionSwitchCenter(BlockInterfaceMixin[DigitalDirectionSwitch]):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)

self.center = self.Port(DigitalSingleSource.empty(), optional=True)
self.center = self.Port(DigitalSource.empty(), optional=True)


class DigitalWrapperDirectionSwitchWithCenter(DigitalDirectionSwitchCenter, DigitalWrapperDirectionSwitch,
Expand All @@ -193,5 +193,5 @@ def generate(self):
super().generate()
if self.get(self.center.is_connected()):
package_sw = self.package.with_mixin(DirectionSwitchCenter())
dio_model = DigitalSingleSource.low_from_supply(self.gnd)
dio_model = DigitalSource.low_from_supply(self.gnd)
self.connect(self.center, package_sw.center.adapt_to(dio_model))
4 changes: 2 additions & 2 deletions edg/abstract_parts/DigitalAmplifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def __init__(self, max_rds: FloatLike = 1*Ohm, frequency: RangeLike = RangeExpr.

self.gnd = self.Port(Ground.empty(), [Common])
self.control = self.Port(DigitalSink.empty(), [Input])
self.output = self.Port(DigitalSingleSource.empty(), [Output])
self.output = self.Port(DigitalSource.empty(), [Output])

self.max_rds = self.ArgParameter(max_rds)
self.frequency = self.ArgParameter(frequency)
Expand All @@ -131,7 +131,7 @@ def contents(self):
frequency=self.frequency,
drive_current=self.control.link().current_limits
))
self.connect(self.drv.drain.adapt_to(DigitalSingleSource.low_from_supply(self.gnd
self.connect(self.drv.drain.adapt_to(DigitalSource.low_from_supply(self.gnd
)), self.output)
self.connect(self.drv.source.adapt_to(Ground()), self.gnd)
self.connect(self.drv.gate.adapt_to(DigitalSink()),
Expand Down
2 changes: 1 addition & 1 deletion edg/abstract_parts/PassiveFilters.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __init__(self, impedance: RangeLike, time_constant: RangeLike):
voltage=self.pwr.link().voltage))

self.connect(self.pwr, self.rc.input.adapt_to(VoltageSink()))
self.io = self.Export(self.rc.output.adapt_to(DigitalSingleSource.high_from_supply(self.pwr)), [Output])
self.io = self.Export(self.rc.output.adapt_to(DigitalSource.pullup_from_supply(self.pwr)), [Output])
self.gnd = self.Export(self.rc.gnd.adapt_to(Ground()), [Common])

def connected(self, *, gnd: Optional[Port[VoltageLink]] = None, pwr: Optional[Port[VoltageLink]] = None,
Expand Down
1 change: 1 addition & 0 deletions edg/abstract_parts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
from .AbstractSwitch import Switch, TactileSwitch, MechanicalKeyswitch, DigitalSwitch
from .AbstractSwitch import RotaryEncoder, RotaryEncoderSwitch, DigitalRotaryEncoder, DigitalRotaryEncoderSwitch
from .AbstractSwitch import DirectionSwitch, DirectionSwitchCenter, DigitalDirectionSwitch, DigitalDirectionSwitchCenter
from .AbstractComparator import Comparator
from .AbstractOpamp import Opamp, OpampElement, MultipackOpamp, MultipackOpampGenerator
from .OpampCircuits import OpampFollower, Amplifier, DifferentialAmplifier, IntegratorInverting
from .AbstractSpiMemory import SpiMemory, SpiMemoryQspi
Expand Down
4 changes: 4 additions & 0 deletions edg/core/Array.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,10 @@ def all(self, selector: Callable[[VectorType], BoolExpr]) -> BoolExpr:
param = self.validate_selector(BoolExpr, selector(self._elt_sample))
return ArrayBoolExpr()._bind(MapExtractBinding(self, param)).all()

def count(self, selector: Callable[[VectorType], BoolExpr]) -> IntExpr:
param = self.validate_selector(BoolExpr, selector(self._elt_sample))
return ArrayBoolExpr()._bind(MapExtractBinding(self, param)).count()

@overload
def sum(self, selector: Callable[[VectorType], RangeExpr]) -> RangeExpr: ...
@overload
Expand Down
3 changes: 3 additions & 0 deletions edg/core/ArrayExpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ def any(self) -> BoolExpr:
def all(self) -> BoolExpr:
return BoolExpr()._new_bind(UnarySetOpBinding(self, BoolOp.op_and))

def count(self) -> IntExpr:
return IntExpr()._new_bind(UnarySetOpBinding(self, NumericOp.sum))


ArrayIntLike = Union['ArrayIntExpr', Sequence[IntLike]]
class ArrayIntExpr(ArrayExpr[IntExpr, List[int], ArrayIntLike]):
Expand Down
6 changes: 4 additions & 2 deletions edg/core/Blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ def add_ports(self, ports: Iterable[BasePort]):
is_export = self._is_export()
if is_export:
(ext_port, int_port) = is_export
if ext_port._get_initializers([]):
raise UnconnectableError(f"Connected boundary port {ext_port._name_from(self.parent, allow_unknown=True)} may not have initializers")
initializers = ext_port._get_initializers([])
if initializers:
raise UnconnectableError(f"Connected boundary port {ext_port._name_from(self.parent, allow_unknown=True)} may not have initializers, "
f"got {', '.join(['.'.join(path) + '=' + str(value) for _, path, value in initializers])}")
return # is an export, not a connection

# otherwise, is a link-mediated connection
Expand Down
Binary file modified edg/core/resources/edg-compiler-precompiled.jar
Binary file not shown.
6 changes: 3 additions & 3 deletions edg/electronics_model/DebugPorts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import *

from ..core import *
from .DigitalPorts import DigitalSink, DigitalSource, DigitalBidir, DigitalSingleSource
from .DigitalPorts import DigitalSink, DigitalSource, DigitalBidir


class SwdLink(Link):
Expand Down Expand Up @@ -46,9 +46,9 @@ def __init__(self, model: Optional[DigitalBidir] = None) -> None:
class SwdPullPort(Bundle[SwdLink]):
link_type = SwdLink

def __init__(self, model: Optional[DigitalSingleSource] = None) -> None:
def __init__(self, model: Optional[DigitalSource] = None) -> None:
super().__init__()
if model is None:
model = DigitalSingleSource() # ideal by default
model = DigitalSource() # ideal by default
self.swdio = self.Port(model)
self.swclk = self.Port(model)
Loading