A simplified EDG EDSL guide, primarily as a reference for those who have been through the getting started guide.
Some documentation may be out of date.
BoolExpr
: boolean (true / false) variable- Supports standard boolean operations
FloatExpr
: numeric variable- Suppers standard numeric operations
RangeExpr
: range (interval) variable- Supports standard numeric operations (multiplication and division are undefined), some set operations (
.within(other)
,.contains(other)
,.intersect(other)
), and get operations (.lower()
,.upper()
)
- Supports standard numeric operations (multiplication and division are undefined), some set operations (
StringExpr
: string (text) variable- Supports equality operations only
Blocks represent a subcircuit (or hierarchical schematic sheet), and consist of boundary ports and internal subblocks and connections (links) between ports on those subblocks.
Skeleton structure:
class MyBlock(Block):
def __init__(self) -> None:
super().__init__() # essential to call the superclass method beforehand to initialize state
# declare ports here, and subblocks whose ports are exported
def contents(self) -> None:
super().contents() # essential to call the superclass method beforehand to initialize state
# declare subblocks and connections here
These are properties of Blocks:
- The Block type hierarchy defines allowed refinements
@abstract_block
decorates a block as abstract, which will error on netlisting
These can be called inside __init__(...)
:
self.Parameter(ParameterType(optional_value))
: declare parameters, optionally with a valueself.Port(PortType(...))
: declare ports- Optional arguments:
tags=[ImplicitTags], optional=False
self.Export(self.subblock.port)
: export a subblock's port
- Optional arguments:
@init_in_parent
decorator is needed if__init__(...)
takes Parameter arguments and must generate constraints in the parent Block
These can be called inside contents()
:
self.Block(BlockType(...))
: declare sub-blocks- Also allowed inside
__init__(...)
, to allow the use ofExport
s
- Also allowed inside
self.constrain(...)
: constrain someBoolExpr
(which can be an inline expression, egself.param1 == self.param2
) to be trueself.connect(self.subblock1.port, self.subblock2.port, ...)
: connect own ports and/or subblock's ports- Naming is optional.
with self.implicit_connect(...) as imp:
: open an implicit connection scope- Implicit connection arguments of the form
ImplicitConnect(connect_to_port, [MatchingTags])
imp.Block(BlockType(...))
: similar toself.Block(...)
but implicitly connects ports with matching tags
- Implicit connection arguments of the form
self.chain(self.Block(BlockType(...)), self.Block(BlockType(...)), ...)
: chain-connect between blocks, incontents
- Return of
self.chain
can be unpacked into a tuple of chained blocks, and the chain object (itself). Naming of the chain object is optional. - Elements are chained from left (outputs) to right (inputs)
- The first argument to chain may be a port or block with an
InOut
orOutput
-tagged port. - Middle elements must be a block with an
InOut
-tagged port, orInput
- andOutput
-tagged ports. - The last argument to chain may be a port or block with an
InOut
orInput
-tageed port.
- Return of
- Assign names to components by assigning the object to a
self
variable:self.subblock_name = self.Block(BlockType(...))
(self.subblock_name1, self.subblock_name2, ...), self.chain_name = self.chain(...)
Generators allow some Python code to run that has access to the solved values of some parameters. TODO - write this section pending generator refactoring, in the meantime see the port array section of the getting started tutorial.
FootprintBlock is a block that is associated with a PCB footprint.
All primitives that can be called inside Block
's __init__(...)
can be called inside __init__(...)
These can be called inside contents()
:
self.footprint(refdes='R', footprint='Resistor_SMD:R_0603_1608Metric', pinning={...})
: associates a footprint with this block- Pinning argument format:
{'pin_name': self.port, ...}
: associates footprint pins with ports or subports - Optional arguments:
mfr='Manufacturer A', part='Part Number', value='1kOhm', datasheet='www.example.net'
- Pinning argument format:
VoltageLink
: voltage railVoltageSource(voltage_out, current_limits)
: voltage sourceVoltageSink(voltage_limits, current_draw)
: voltage sink (power input)
DigitalLink
: low-speed (up to ~100 MHz) digital signals, modeling voltage limits and input / output thresholdsDigitalSource(voltage_out, current_limits, output_thresholds)
: digital output-only pinoutput_thresholds
is the range of (maximum low output, minimum high output)
DigitalSink(voltage_limits, current_draw, input_thresholds)
: digital input-only pininput_thresholds
is the range of (maximum low input, minimum high input)
DigitalBidir(...)
: digital bidirectional (eg, GPIO) pin- Has all arguments of
DigitalSource
andDigitalSink
- Has all arguments of
AnalogLink
: analog signal that models input and output impedanceAnalogSource(voltage_out, current_limits, impedance)
: analog outputAnalogSink(voltage_limits, current_draw, impedance)
: analog signal input
PassiveLink
: connected copper that contains no additional dataPassive
: single wire port that contains no additional data, but can be adapted to other types (eg, with.as_voltage_source(...)
). Useful for low-level elements (eg, resistors) that can be used in several ways in higher-level constructs (eg, pull-up resistor for digital applications).
I2cLink
: I2C net, consisting of.scl
and.sda
Digital sub-ports.I2CMaster(model)
: I2C master portmodel
: DigitalBidir model for both SCL and SDA sub-ports
I2CSlave(model)
: I2C slave port
SpiLink
: SPI net, consisting of.sck
,.miso
, and.mosi
Digital sub-ports. CS must be handled separately.SpiMaster(model, frequency)
: SPI master portfrequency
: range of allowable frequencies, generally including zero
SpiSlave(model, frequency)
: SPI slave port
UartLink
: UART net, consisting of.tx
and.rx
Digital sub-ports in a crossover point-to-point connection.UartPort(model)
: UART port
UsbLink
: USB data net, consisting of.dp
(D+) and.dm
(D-) Digital sub-ports in a point-to-point connection.UsbHostPort
: USB hostUsbDevicePort
: USB deviceUsbPassivePort
: other components (eg, TVS diode) on the USB line
CanLogicLink
: CAN logic-level (TXD/RXD) net, consisting of.txd
and.rxd
Digital sub-ports in a point-to-point connection.CanControllerPort(model)
: CAN controller-side portCanTransceiverPort(model)
: CAN transceiver-side port
CanDiffLink
: CAN differential (CANH/CANL) net, consisting of.canh
and.canl
Digital sub-ports in a bus connection.CanDiffPort(model)
: CAN node port
SwdLink
: SWD programming net, consisting of.swdio
,.swclk
,.swo
, and.reset
Digital sub-ports in a point-to-point connection.SwdHostPort(model)
: SWD host-side (programmer) portSwdTargetPort(model)
: SWD target-side (DUT) port
CrystalLink
: crystal net, consisting of.xi
and.xo
sub-ports in a point-to-point connection.CrystalDriver(frequency_limits, voltage_out)
: driver-side portCrystalPort(frequency)
: crystal-side port, indicating the frequency of the crystal
The IDE's library tab provides a categorized list of available library blocks. Many blocks also have a short descriptive docstring.
These are core primitives needed to model new ports and links
Ports can have parameters and may be connected to each other via a Link.
Skeleton structure:
class MyPort(Port[MyPortLinkType]):
link_type = MyPortLinkType # required
bridge_type = MyPortBridgeType # optional, if a bridge is needed
def __init__(self) -> None:
super().__init__() # essential to call the superclass method beforehand to initialize state
# declare elements like parameters here
These are properties of Ports:
- The Port type hierarchy currently is not used. However, inheritance may still be useful for code re-use / de-duplication.
These can be called inside __init__(...)
:
__init__(...)
may take arguments, and no decorators (as with Block) are neededself.Parameter(ParameterType(optional_value))
: declare parameters, optionally with a value
Bundles are a special type of Port that is made up of constituent sub-Ports.
Skeleton structure:
class MyBundle(Bundle):
link_type = MyBundleLinkType
bridge_type = MyBundleBridgeType # optional, if a bridge is needed
def __init__(self) -> None:
super().__init__() # essential to call the superclass method beforehand to initialize state
# declare elements like parameters here
In addition to the primitives that can be called inside Port
's __init__(...)
, these can be called inside Bundle
's __init__(...)
:
self.Port(PortType(...))
: declare bundle sub-port
Links define propagation rules for connected Ports.
Links are structured the same as Blocks, with similar primitives (see the Block documentation for details):
class MyPortLink(Link):
def __init__(self) -> None:
super().__init__() # essential to call the superclass method beforehand to initialize state
# declare ports here
def contents(self) -> None:
super().contents() # essential to call the superclass method beforehand to initialize state
# declare constraints and internal connections here
These are properties of Links:
- Each Port can have one type of associated link.
- The Link type hierarchy currently is not used. However, inheritance may still be useful for code re-use / de-duplication.
- Extend
CircuitLink
instead ofLink
to copper-connect all ports.
These can be called inside __init__()
:
__init__()
cannot have arguments, since links are always inferredself.Parameter(ParameterType(optional_value))
self.Port(PortType(...))
(without tags, but can have optional)
These can be called inside contents()
:
self.connect(self.port1.subport, self.port2.subport, ...)
: for Bundle ports, connect sub-ports and infer inner link- Naming is optional.
self.constrain(...)
Bridges are a special type of Block
that adapts from a Link-facing (inner, self.inner_link
) port to a Block edge (outer, self.outer_port
) port.
Extend CircuitPortBridge
instead of PortBridge
to copper-connect all ports.
In __init__()
(may not take arguments), declare these two required ports.
In contents()
, add constraints as needed.
PortAdapters are a special type of Block
that adapts / converts from a source (self.src
) port to a destination (self.dst
) port.
Extend CircuitPortAdapter
instead of PortAdapter
to copper-connect all ports.
In __init__(...)
(may take arguments, must decorate with @init_in_parent
if arguments), declare these two required ports.
In contents()
, add constraints as needed.