diff --git a/edg/electronics_interfaces/CanPort.py b/edg/electronics_interfaces/CanPort.py index ac4232a45..aa1d7feb2 100644 --- a/edg/electronics_interfaces/CanPort.py +++ b/edg/electronics_interfaces/CanPort.py @@ -35,12 +35,15 @@ def contents(self) -> None: class CanControllerPort(Port[CanLogicLink]): link_type = CanLogicLink - def __init__(self, model: Optional[DigitalBidir] = None) -> None: + def __init__(self, model: Optional[DigitalBidir] = None, *, bitrate_limit: RangeLike = RangeExpr.ALL) -> None: + # 0-1 Mbit/s are standard CANbus + # 1-8 Mbit/s imply CAN-FD super().__init__() if model is None: # ideal by default model = DigitalBidir() self.txd = self.Port(DigitalSource.from_bidir(model)) self.rxd = self.Port(DigitalSink.from_bidir(model)) + self.bitrate_limit = self.Parameter(RangeExpr(bitrate_limit)) class CanTransceiverPort(Port[CanLogicLink]): diff --git a/edg/electronics_interfaces/I2cPort.py b/edg/electronics_interfaces/I2cPort.py index 30c939749..20a496ff5 100644 --- a/edg/electronics_interfaces/I2cPort.py +++ b/edg/electronics_interfaces/I2cPort.py @@ -3,6 +3,7 @@ from ..electronics_model import * from .DigitalPorts import DigitalSink, DigitalSource, DigitalBidir, DigitalBidirBridge, DigitalSinkBridge +from ..util import deprecated_param_remap class I2cLink(Link): @@ -24,6 +25,7 @@ def __init__(self) -> None: self.addresses = self.Parameter( ArrayIntExpr(self.targets.flatten(lambda x: x.addresses).concat(self.controller.addresses)) ) + self.frequency = self.Parameter(RangeExpr()) self.has_pull = self.Parameter(BoolExpr(self.pull.any_connected())) @@ -35,6 +37,11 @@ def contents(self) -> None: self.require(self.controller.has_pullup.implies(self.pull.length() == 0), "redundant pullup with controller") self.require(self.addresses.all_unique(), "conflicting addresses on I2C bus") + self.assign( + self.frequency, + self.controller.frequency_limit.intersect(self.targets.intersection(lambda x: x.frequency_limit)), + ) + self.scl = self.connect( self.pull.map_extract(lambda device: device.scl), self.controller.scl, @@ -92,7 +99,12 @@ class I2cController(Port[I2cLink]): bridge_type = I2cControllerBridge def __init__( - self, model: Optional[DigitalBidir] = None, *, has_pullup: BoolLike = False, addresses: ArrayIntLike = [] + self, + model: Optional[DigitalBidir] = None, + *, + has_pullup: BoolLike = False, + addresses: ArrayIntLike = [], + frequency_limit: RangeLike = RangeExpr.ALL, ) -> None: super().__init__() if model is None: @@ -101,7 +113,7 @@ def __init__( self.sda = self.Port(model) self.addresses = self.Parameter(ArrayIntExpr(addresses)) - self.frequency = self.Parameter(RangeExpr(RangeExpr.ZERO)) + self.frequency_limit = self.Parameter(RangeExpr(frequency_limit)) self.has_pullup = self.Parameter(BoolExpr(has_pullup)) @@ -116,7 +128,7 @@ def __init__(self) -> None: def contents(self) -> None: super().contents() - self.outer_port.init_from(I2cTarget(DigitalBidir.empty(), self.inner_link.link().addresses)) + self.outer_port.init_from(I2cTarget(DigitalBidir.empty(), addresses=self.inner_link.link().addresses)) self.scl_bridge = self.Block(DigitalSinkBridge()) self.connect(self.outer_port.scl, self.scl_bridge.outer_port) @@ -131,15 +143,22 @@ class I2cTarget(Port[I2cLink]): link_type = I2cLink bridge_type = I2cTargetBridge - def __init__(self, model: Optional[DigitalBidir] = None, addresses: ArrayIntLike = []) -> None: - """Addresses specified excluding the R/W bit (as a 7-bit number, as directly used by Arduino)""" + @deprecated_param_remap((2, "addresses")) + def __init__( + self, + model: Optional[DigitalBidir] = None, + *, + addresses: ArrayIntLike = [], + frequency_limit: RangeLike = RangeExpr.ALL, + ) -> None: + """Addresses specified excluding the R/W bit (as a 7-bit number, as directly used by Arduino and Rust embedded-hal)""" super().__init__() if model is None: model = DigitalBidir() # ideal by default self.scl = self.Port(DigitalSink.from_bidir(model)) self.sda = self.Port(model) - self.frequency_limit = self.Parameter(RangeExpr(RangeExpr.ALL)) # range of acceptable frequencies + self.frequency_limit = self.Parameter(RangeExpr(frequency_limit)) # range of acceptable frequencies self.addresses = self.Parameter(ArrayIntExpr(addresses)) diff --git a/edg/electronics_interfaces/I2sPort.py b/edg/electronics_interfaces/I2sPort.py index b4022425a..6d3ff6221 100644 --- a/edg/electronics_interfaces/I2sPort.py +++ b/edg/electronics_interfaces/I2sPort.py @@ -26,7 +26,7 @@ class I2sController(Port[I2sLink]): link_type = I2sLink - def __init__(self, model: Optional[DigitalBidir] = None) -> None: + def __init__(self, model: Optional[DigitalBidir] = None, *, bitrate_limit: RangeLike = RangeExpr.ALL) -> None: super().__init__() if model is None: model = DigitalBidir() # ideal by default @@ -34,6 +34,8 @@ def __init__(self, model: Optional[DigitalBidir] = None) -> None: self.ws = self.Port(DigitalSource.from_bidir(model)) self.sd = self.Port(model) # bidirectional + self.bitrate_limit = self.Parameter(RangeExpr(bitrate_limit)) # bitrate + class I2sTargetReceiver(Port[I2sLink]): """Target means SCK and WS are inputs, receiver means SD is input""" diff --git a/edg/electronics_interfaces/SpiPort.py b/edg/electronics_interfaces/SpiPort.py index fdf5e63a2..58017cf34 100644 --- a/edg/electronics_interfaces/SpiPort.py +++ b/edg/electronics_interfaces/SpiPort.py @@ -3,6 +3,7 @@ from ..electronics_model import * from .DigitalPorts import DigitalSink, DigitalSource, DigitalBidir +from ..util import deprecated_param_remap class SpiLink(Link): @@ -37,7 +38,8 @@ def contents(self) -> None: class SpiController(Port[SpiLink]): link_type = SpiLink - def __init__(self, model: Optional[DigitalBidir] = None, frequency: RangeLike = RangeExpr.ZERO) -> None: + @deprecated_param_remap(("frequency", "frequency_limit"), (2, "frequency_limit")) + def __init__(self, model: Optional[DigitalBidir] = None, *, frequency_limit: RangeLike = RangeExpr.ALL) -> None: super().__init__() if model is None: model = DigitalBidir() # ideal by default @@ -45,14 +47,15 @@ def __init__(self, model: Optional[DigitalBidir] = None, frequency: RangeLike = self.mosi = self.Port(DigitalSource.from_bidir(model)) self.miso = self.Port(DigitalSink.from_bidir(model)) - self.frequency = self.Parameter(RangeExpr(frequency)) + self.frequency_limit = self.Parameter(RangeExpr(frequency_limit)) self.mode = self.Parameter(RangeExpr()) # modes supported, in [0, 3] TODO: what about sparse modes? class SpiPeripheral(Port[SpiLink]): link_type = SpiLink - def __init__(self, model: Optional[DigitalBidir] = None, frequency_limit: RangeLike = RangeExpr.ALL) -> None: + @deprecated_param_remap((2, "frequency_limit")) + def __init__(self, model: Optional[DigitalBidir] = None, *, frequency_limit: RangeLike = RangeExpr.ALL) -> None: super().__init__() if model is None: model = DigitalBidir() # ideal by default diff --git a/edg/electronics_interfaces/UartPort.py b/edg/electronics_interfaces/UartPort.py index 3f381db31..826979fda 100644 --- a/edg/electronics_interfaces/UartPort.py +++ b/edg/electronics_interfaces/UartPort.py @@ -22,12 +22,11 @@ def contents(self) -> None: class UartPort(Port[UartLink]): link_type = UartLink - def __init__(self, model: Optional[DigitalBidir] = None) -> None: + def __init__(self, model: Optional[DigitalBidir] = None, *, baud_limit: RangeLike = RangeExpr.ALL) -> None: super().__init__() if model is None: model = DigitalBidir() # ideal by default self.tx = self.Port(DigitalSource.from_bidir(model)) self.rx = self.Port(DigitalSink.from_bidir(model)) - self.baud = self.Parameter(RangeExpr(RangeExpr.ZERO)) - self.baud_limit = self.Parameter(RangeExpr(RangeExpr.INF)) + self.baud_limit = self.Parameter(RangeExpr(baud_limit)) diff --git a/edg/electronics_interfaces/test_i2c_link.py b/edg/electronics_interfaces/test_i2c_link.py index 4e61c9926..cf30e60bb 100644 --- a/edg/electronics_interfaces/test_i2c_link.py +++ b/edg/electronics_interfaces/test_i2c_link.py @@ -20,7 +20,7 @@ def __init__(self) -> None: class I2cTargetBlock(Block): def __init__(self, address: IntLike): super().__init__() - self.port = self.Port(I2cTarget(DigitalBidir(), [address])) + self.port = self.Port(I2cTarget(DigitalBidir(), addresses=[address])) class I2cTest(DesignTop): diff --git a/edg/parts/analog/dac/Mcp4728.py b/edg/parts/analog/dac/Mcp4728.py index 420c0ba60..c570c4cb8 100644 --- a/edg/parts/analog/dac/Mcp4728.py +++ b/edg/parts/analog/dac/Mcp4728.py @@ -29,7 +29,9 @@ def __init__(self) -> None: voltage_limit_tolerance=(-0.3, 0.3) * Volt, input_threshold_factor=(0.3, 0.7), # for Vdd >= 2.7v ) - self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x60])) # TODO 3LSBs EEPROM programmable + self.i2c = self.Port( + I2cTarget(dio_model, addresses=[0x60], frequency_limit=(100, 3400) * kHertz) + ) # TODO 3LSBs EEPROM programmable self.ldac = self.Port(DigitalSink.from_bidir(dio_model)) self.rdy = self.Port(DigitalSource.low_from_supply(self.vss), optional=True) diff --git a/edg/parts/analog/dac/Mcp47f.py b/edg/parts/analog/dac/Mcp47f.py deleted file mode 100644 index ef20be8f2..000000000 --- a/edg/parts/analog/dac/Mcp47f.py +++ /dev/null @@ -1,117 +0,0 @@ -from typing_extensions import override - -from ....circuits import * - - -class Mcp47f_Device(InternalSubcircuit, FootprintBlock, GeneratorBlock): - def __init__(self, addr_lsb: IntLike) -> None: - super().__init__() - self.vss = self.Port(Ground()) - self.vdd = self.Port( - VoltageSink( - voltage_limits=(2.7, 5.5) * Volt, # technically down to 1.8 w/ reduced performance - current_draw=(0.00085, 2.5) * mAmp, - ) - ) # quad DAC, serial inactive to EE write - - ref_model = VoltageSink(voltage_limits=self.vss.link().voltage.hull(self.vdd.link().voltage)) - self.vref0 = self.Port(ref_model) - self.vref1 = self.Port(ref_model) - - out_ref0_model = AnalogSource.from_supply( - self.vss, - self.vref0, - signal_bound=(0.01 * Volt, -0.016 * Volt), # output amp min / max voltages - current_limits=(-3, 3) * mAmp, # short circuit current, typ - impedance=(122, 900) * Ohm, # derived from assumed Vout=Vdd=2.7v, Isc=3-22mA - ) - out_ref1_model = AnalogSource.from_supply( - self.vss, - self.vref1, - signal_bound=(0.01 * Volt, -0.016 * Volt), # output amp min / max voltages - current_limits=(-3, 3) * mAmp, # short circuit current, typ - impedance=(122, 900) * Ohm, # derived from assumed Vout=Vdd=2.7v, Isc=3-22mA - ) - self.vout0 = self.Port(out_ref0_model, optional=True) - self.vout1 = self.Port(out_ref1_model, optional=True) - self.vout2 = self.Port(out_ref0_model, optional=True) - self.vout3 = self.Port(out_ref1_model, optional=True) - - dio_model = DigitalBidir.from_supply( # LAT0/1/HVC, same input thresholds for I2C - self.vss, - self.vdd, - voltage_limit_tolerance=(-0.6, 0.3) * Volt, - current_limits=(-2, 2) * mAmp, - input_threshold_factor=(0.3, 0.7), - ) - self.lat0 = self.Port(dio_model) - self.lat1 = self.Port(dio_model) - self.i2c = self.Port(I2cTarget(dio_model, addresses=ArrayIntExpr())) - - self.addr_lsb = self.ArgParameter(addr_lsb) - self.generator_param(self.addr_lsb) - - @override - def generate(self) -> None: - super().generate() - - addr_lsb = self.get(self.addr_lsb) - self.require((addr_lsb < 4) & (addr_lsb >= 0), f"addr_lsb={addr_lsb} must be within [0, 4)") - self.assign(self.i2c.addresses, [0x60 | addr_lsb]) # fixed for volatile devices - - self.footprint( - "U", - "Package_SO:TSSOP-20_4.4x6.5mm_P0.65mm", - { - "1": self.lat1, - "2": self.vdd, - "3": self.vdd if addr_lsb & 1 else self.vss, # A0 - "4": self.vref0, - "5": self.vout0, - "6": self.vout2, - # '7', '8': nc - "9": self.vss, - # '10', '11', '12', '13': nc - "14": self.vout3, - "15": self.vout1, - "16": self.vref1, - "17": self.vdd if addr_lsb & 2 else self.vss, # A1 - "18": self.i2c.scl, - "19": self.i2c.sda, - "20": self.lat0, - }, - mfr="Microchip Technology", - part="MCP47FVB24T-20E/ST", - datasheet="https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf", - ) - - -class Mcp47f(DigitalToAnalog, Block): - """MCP47FxBx4/8 quad / octal 8/10/12-bit I2C DAC, with selectable internal or external Vref""" - - def __init__(self, addr_lsb: IntLike = 0) -> None: - super().__init__() - self.ic = self.Block(Mcp47f_Device(addr_lsb=addr_lsb)) - self.pwr = self.Export(self.ic.vdd, [Power]) - self.gnd = self.Export(self.ic.vss, [Common]) - - self.ref0 = self.Export(self.ic.vref0) - self.ref1 = self.Export(self.ic.vref1) - - self.out0 = self.Export(self.ic.vout0, optional=True) - self.out1 = self.Export(self.ic.vout1, optional=True) - self.out2 = self.Export(self.ic.vout2, optional=True) - self.out3 = self.Export(self.ic.vout3, optional=True) - - self.i2c = self.Export(self.ic.i2c) - self.lat0 = self.Export(self.ic.lat0) - self.lat1 = self.Export(self.ic.lat1) - - @override - def contents(self) -> None: - super().contents() - - # Datasheet section 6.2, example uses two bypass capacitors - self.vdd_cap = ElementDict[DecouplingCapacitor]() - self.vdd_cap[0] = self.Block(DecouplingCapacitor(0.1 * uFarad(tol=0.2))).connected(self.gnd, self.pwr) - self.vdd_cap[1] = self.Block(DecouplingCapacitor(10 * uFarad(tol=0.2))).connected(self.gnd, self.pwr) diff --git a/edg/parts/analog/dac/__init__.py b/edg/parts/analog/dac/__init__.py index 6c10a24d9..d1aec5e95 100644 --- a/edg/parts/analog/dac/__init__.py +++ b/edg/parts/analog/dac/__init__.py @@ -1,3 +1,2 @@ from .Mcp4901 import Mcp4921 -from .Mcp47f import Mcp47f from .Mcp4728 import Mcp4728 diff --git a/edg/parts/analog/opamp/Ina219.py b/edg/parts/analog/opamp/Ina219.py index 86fa244ed..b8a693f58 100644 --- a/edg/parts/analog/opamp/Ina219.py +++ b/edg/parts/analog/opamp/Ina219.py @@ -14,7 +14,9 @@ def __init__(self, addr_lsb: IntLike): self.vs = self.Port(VoltageSink(voltage_limits=(3, 5.5) * Volt, current_draw=(6 * uAmp, 1 * mAmp))) self.gnd = self.Port(Ground()) - self.i2c = self.Port(I2cTarget(DigitalBidir.empty(), addresses=[0x40 + self.addr_lsb])) + self.i2c = self.Port( + I2cTarget(DigitalBidir.empty(), addresses=[0x40 + self.addr_lsb], frequency_limit=(1, 2560) * kHertz) + ) self.i2c.sda.init_from( DigitalBidir.from_supply( self.gnd, self.vs, voltage_limit_abs=(-0.3, 6.0) * Volt, input_threshold_factor=(0.3, 0.7) diff --git a/edg/parts/display/EInk_Er_Epd027_2.py b/edg/parts/display/EInk_Er_Epd027_2.py index 7a63656af..e787688cf 100644 --- a/edg/parts/display/EInk_Er_Epd027_2.py +++ b/edg/parts/display/EInk_Er_Epd027_2.py @@ -84,7 +84,7 @@ def __init__(self) -> None: self.dc = self.Port(din_model, optional=True) self.csb = self.Port(din_model) - self.spi = self.Port(SpiPeripheral(dio_model)) + self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 20) * MHertz)) # 2.5MHz read mode self.conn = self.Block(Fpc050Bottom(length=24)).connected( { diff --git a/edg/parts/display/lcd/Ch280qv10_Ct.py b/edg/parts/display/lcd/Ch280qv10_Ct.py index a864df7c4..d9d156a82 100644 --- a/edg/parts/display/lcd/Ch280qv10_Ct.py +++ b/edg/parts/display/lcd/Ch280qv10_Ct.py @@ -71,8 +71,11 @@ def __init__(self) -> None: self.cs = self.Port(din_model) # pin 39 TE out unused + # CST026-A, https://cdn-shop.adafruit.com/product-files/1947/CST0x6_DS.pdf ctp_dio_model = DigitalBidir.from_supply(self.gnd, self.iovcc, input_threshold_abs=(1.0, 1.9) * Volt) - self.ctp_i2c = self.Port(I2cTarget(ctp_dio_model, [0x38]), optional=True) + self.ctp_i2c = self.Port( + I2cTarget(ctp_dio_model, addresses=[0x38], frequency_limit=(0, 400) * kHertz), optional=True + ) # pin 46 is CTQ IRQ, unused (semantics not defined) self.ctp_res = self.Port(DigitalSink.from_bidir(ctp_dio_model)) self.require(self.ctp_i2c.is_connected().implies(self.ctp_res.is_connected())) @@ -123,7 +126,8 @@ def __init__(self) -> None: self.device = self.Block(Ch280qv10_Ct_Device()) self.gnd = self.Export(self.device.gnd, [Common]) self.pwr = self.Export(self.device.iovcc, [Power]) - self.spi = self.Port(SpiPeripheral.empty()) + # SPI capable of 10 MHz in write mode, slower (as modeled) in read mode + self.spi = self.Port(SpiPeripheral(DigitalBidir.empty(), frequency_limit=(0, 6.6) * MHertz)) self.cs = self.Export(self.device.cs) self.dc = self.Export(self.device.wr_rs) diff --git a/edg/parts/display/lcd/Er_Tft1_28_3.py b/edg/parts/display/lcd/Er_Tft1_28_3.py index 1b8026d99..7299f8412 100644 --- a/edg/parts/display/lcd/Er_Tft1_28_3.py +++ b/edg/parts/display/lcd/Er_Tft1_28_3.py @@ -47,13 +47,15 @@ def __init__(self) -> None: self.rs = self.Port(din_model) # Control pins - self.spi = self.Port(SpiPeripheral(dio_model)) + # SPI up to 100 MHz in write mode, slower (as modeled) in read mode + self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 6.6) * MHertz)) self.cs = self.Port(din_model) self.rst = self.Port(din_model) # Capacitive Touch Panel (CTP) + # CST816S, https://www.buydisplay.com/download/ic/DS-CST816S_DS_V1.3.pdf self.ctp_vdd = self.Port(VoltageSink(voltage_limits=(2.7, 3.6) * Volt, current_draw=(5 * uAmp, 2.5 * mAmp))) - self.ctp_i2c = self.Port(I2cTarget(dio_model, addresses=[0x15])) + self.ctp_i2c = self.Port(I2cTarget(dio_model, addresses=[0x15], frequency_limit=(10, 1000) * kHertz)) self.ctp_rst = self.Port(din_model) self.ctp_int = self.Port(din_model) diff --git a/edg/parts/display/lcd/Qt096t_if09.py b/edg/parts/display/lcd/Qt096t_if09.py index 5baa5ef43..8121d6fb9 100644 --- a/edg/parts/display/lcd/Qt096t_if09.py +++ b/edg/parts/display/lcd/Qt096t_if09.py @@ -29,7 +29,8 @@ def __init__(self) -> None: self.rs = self.Port(din_model) # data / command selection pin self.cs = self.Port(din_model) - self.spi = self.Port(SpiPeripheral(dio_model)) + # up to 15 MHz in write mode, slower (as modeled) in read mode + self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 6.6) * MHertz)) self.leda = self.Port(Passive()) self.conn = self.Block(Fpc050Bottom(length=8)).connected( @@ -47,7 +48,7 @@ def __init__(self) -> None: class Qt096t_if09(Lcd, Resettable, Block): - """ST7735S-based LCD module with a 8-pin 0.5mm-pitch FPC connector""" + """ST7735S-based LCD module with an 8-pin 0.5mm-pitch FPC connector, like ER-TFT0.96-4.""" def __init__(self) -> None: super().__init__() diff --git a/edg/parts/display/oled/Er_Oled_022.py b/edg/parts/display/oled/Er_Oled_022.py index c65d0c525..4bcf4cf2a 100644 --- a/edg/parts/display/oled/Er_Oled_022.py +++ b/edg/parts/display/oled/Er_Oled_022.py @@ -99,10 +99,12 @@ def __init__(self) -> None: self.gnd = self.Export(self.device.vss, [Common]) self.vcc = self.Export(self.device.vcc) # device power self.pwr = self.Export(self.device.vdd) # logic power - self.spi = self.Port(SpiPeripheral.empty(), optional=True) + self.spi = self.Port(SpiPeripheral(DigitalBidir.empty(), frequency_limit=(0, 4) * MHertz), optional=True) self.cs = self.Port(DigitalSink.empty(), optional=True) self.dc = self.Port(DigitalSink.empty(), optional=True) - self.i2c = self.Port(I2cTarget.empty(), optional=True) + self.i2c = self.Port( + I2cTarget(DigitalBidir.empty(), addresses=[0x3C], frequency_limit=(0, 400) * kHertz), optional=True + ) self.generator_param(self.spi.is_connected(), self.i2c.is_connected()) @override @@ -150,7 +152,6 @@ def generate(self) -> None: self.connect(self.device.dc, gnd_digital) # addr, TODO support I2C addr self.connect(self.device.cs, gnd_digital) self.require(~self.spi.is_connected() & ~self.cs.is_connected() & ~self.dc.is_connected()) - self.assign(self.i2c.addresses, [0x3C]) elif self.get(self.spi.is_connected()): self.connect(self.device.bs1, gnd_digital) self.connect(self.device.bs2, gnd_digital) diff --git a/edg/parts/display/oled/Er_Oled_028.py b/edg/parts/display/oled/Er_Oled_028.py index 0a550afd0..36807c344 100644 --- a/edg/parts/display/oled/Er_Oled_028.py +++ b/edg/parts/display/oled/Er_Oled_028.py @@ -59,7 +59,7 @@ def __init__(self) -> None: self.bs0 = self.Port(din_model) # 3-wire (1) / 4-wire (0) serial - self.spi = self.Port(SpiPeripheral(dio_model)) + self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 3.3) * MHertz)) self.dc = self.Port(din_model) # ground if unused self.cs = self.Port(din_model) diff --git a/edg/parts/display/oled/Er_Oled_091_3.py b/edg/parts/display/oled/Er_Oled_091_3.py index c261c77c1..0afa3ca02 100644 --- a/edg/parts/display/oled/Er_Oled_091_3.py +++ b/edg/parts/display/oled/Er_Oled_091_3.py @@ -66,7 +66,7 @@ def __init__(self) -> None: ) din_model = DigitalSink.from_bidir(dio_model) - self.spi = self.Port(SpiPeripheral(dio_model)) + self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 10) * MHertz)) self.dc = self.Port(din_model) self.res = self.Port(din_model) self.cs = self.Port(din_model) diff --git a/edg/parts/display/oled/Er_Oled_096_1_1.py b/edg/parts/display/oled/Er_Oled_096_1_1.py index 2bf39ed91..c9e6e96b7 100644 --- a/edg/parts/display/oled/Er_Oled_096_1_1.py +++ b/edg/parts/display/oled/Er_Oled_096_1_1.py @@ -120,10 +120,12 @@ def __init__(self) -> None: self.device = self.Block(Er_Oled_096_1_1_Device()) self.gnd = self.Export(self.device.vss, [Common]) self.pwr = self.Export(self.device.vdd, [Power]) - self.spi = self.Port(SpiPeripheral.empty(), optional=True) + self.spi = self.Port(SpiPeripheral(DigitalBidir.empty(), frequency_limit=(0, 10) * MHertz), optional=True) self.cs = self.Port(DigitalSink.empty(), optional=True) self.dc = self.Port(DigitalSink.empty(), optional=True) - self.i2c = self.Port(I2cTarget.empty(), optional=True) + self.i2c = self.Port( + I2cTarget(DigitalBidir.empty(), addresses=[0x3C], frequency_limit=(0, 400) * kHertz), optional=True + ) self.generator_param(self.spi.is_connected(), self.dc.is_connected(), self.i2c.is_connected()) @override @@ -174,7 +176,6 @@ def generate(self) -> None: self.connect(self.device.dc, gnd_digital) # addr, TODO support I2C addr self.connect(self.device.cs, gnd_digital) self.require(~self.spi.is_connected() & ~self.cs.is_connected() & ~self.dc.is_connected()) - self.assign(self.i2c.addresses, [0x3C]) elif self.get(self.spi.is_connected()): self.connect(self.device.bs1, gnd_digital) self.connect(self.spi.sck, self.device.d0) diff --git a/edg/parts/interface/Ethernet_W5500.py b/edg/parts/interface/Ethernet_W5500.py index bc78380e2..d673d5b4f 100644 --- a/edg/parts/interface/Ethernet_W5500.py +++ b/edg/parts/interface/Ethernet_W5500.py @@ -45,7 +45,8 @@ def __init__(self) -> None: pullup_capable=True, ) - self.spi = self.Port(SpiPeripheral(dio_model)) + # "theoretical design speed" of 80 MHz, minimum guaranteed speed of 33.3 MHz + self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 33.3) * MHertz)) self.scsn = self.Port(dio_pu_model) # according to some internet forum posts, a reset pulse is not needed self.rstn = self.Port(dio_pu_model, optional=True) diff --git a/edg/parts/interface/IoExpander_Pca9554.py b/edg/parts/interface/IoExpander_Pca9554.py index 4c427990b..9e8be919e 100644 --- a/edg/parts/interface/IoExpander_Pca9554.py +++ b/edg/parts/interface/IoExpander_Pca9554.py @@ -25,7 +25,7 @@ def __init__(self, addr_lsb: IntLike, **kwargs: Any) -> None: voltage_limit_tolerance=(-0.5, 0.5) * Volt, input_threshold_factor=(0.3, 0.7), ) - self.i2c = self.Port(I2cTarget(i2c_model, addresses=ArrayIntExpr())) + self.i2c = self.Port(I2cTarget(i2c_model, addresses=ArrayIntExpr(), frequency_limit=(0, 400) * kHertz)) self.io = self.Port(Vector(DigitalBidir.empty()), optional=True) diff --git a/edg/parts/interface/IoExpander_Pcf8574.py b/edg/parts/interface/IoExpander_Pcf8574.py index ac17a5f80..a23e9ef40 100644 --- a/edg/parts/interface/IoExpander_Pcf8574.py +++ b/edg/parts/interface/IoExpander_Pcf8574.py @@ -24,7 +24,7 @@ def __init__(self, addr_lsb: IntLike, **kwargs: Any) -> None: voltage_limit_tolerance=(-0.5, 0.5) * Volt, input_threshold_factor=(0.3, 0.7), ) - self.i2c = self.Port(I2cTarget(i2c_model, addresses=ArrayIntExpr())) + self.i2c = self.Port(I2cTarget(i2c_model, addresses=ArrayIntExpr(), frequency_limit=(0, 100) * kHertz)) self.io = self.Port(Vector(DigitalBidir.empty()), optional=True) diff --git a/edg/parts/interface/Rf_Pn7160.py b/edg/parts/interface/Rf_Pn7160.py index 7417ed2d8..751cbc6fb 100644 --- a/edg/parts/interface/Rf_Pn7160.py +++ b/edg/parts/interface/Rf_Pn7160.py @@ -295,7 +295,9 @@ def __init__(self) -> None: # digital interfaces self.i2c = self.Port( I2cTarget( - DigitalBidir.from_supply(self.vss, self.vddpad, input_threshold_factor=(0.3, 0.7)), addresses=[0x28] + DigitalBidir.from_supply(self.vss, self.vddpad, input_threshold_factor=(0.3, 0.7)), + addresses=[0x28], + frequency_limit=(0, 3400) * kHertz, ) ) # in ADR = (0, 0) diff --git a/edg/parts/interface/UsbInterface_Ft232h.py b/edg/parts/interface/UsbInterface_Ft232h.py index e5e262b2a..19e467df3 100644 --- a/edg/parts/interface/UsbInterface_Ft232h.py +++ b/edg/parts/interface/UsbInterface_Ft232h.py @@ -144,7 +144,8 @@ def __init__(self) -> None: self.pwr = self.Port(VoltageSink.empty()) self.eeclk = self.Port(DigitalSink.empty()) self.eedata = self.Port(DigitalBidir.empty()) - self.spi = self.Port(SpiController.empty()) + # datasheet: 93LC56B or equivalent capable of 1 Mbit/s clock rate, assume driver at exactly 1 Mbit/s + self.spi = self.Port(SpiController(DigitalBidir.empty(), frequency_limit=(1, 1) * MHertz)) @override def contents(self) -> None: @@ -168,8 +169,8 @@ def __init__(self) -> None: self.usb = self.Export(self.ic.usb) # connect one of UART, MPSSE, or ADBUS pins - self.uart = self.Port(UartPort.empty(), optional=True) - self.mpsse = self.Port(SpiController.empty(), optional=True) + self.uart = self.Port(UartPort(DigitalBidir.empty(), baud_limit=(183, 12000000) * Hertz), optional=True) + self.mpsse = self.Port(SpiController(DigitalBidir.empty(), frequency_limit=(0, 30) * MHertz), optional=True) self.mpsse_cs = self.Port(DigitalSource.empty(), optional=True) self.adbus = self.Port(Vector(DigitalBidir.empty())) diff --git a/edg/parts/interface/UsbPd_Fusb302b.py b/edg/parts/interface/UsbPd_Fusb302b.py index 2f7403e46..4a28647f7 100644 --- a/edg/parts/interface/UsbPd_Fusb302b.py +++ b/edg/parts/interface/UsbPd_Fusb302b.py @@ -23,7 +23,7 @@ def __init__(self) -> None: input_thresholds=(0.51, 1.32) * Volt, output_thresholds=(0.35, float("inf")) * Volt, ) - self.i2c = self.Port(I2cTarget(i2c_model, [0x22])) + self.i2c = self.Port(I2cTarget(i2c_model, addresses=[0x22], frequency_limit=(0, 1000) * kHertz)) self.int_n = self.Port(DigitalSource.low_from_supply(self.gnd), optional=True) @override diff --git a/edg/parts/logic/SpiMemory_93Lc.py b/edg/parts/logic/SpiMemory_93Lc.py index cbe8e200b..74be4d8ed 100644 --- a/edg/parts/logic/SpiMemory_93Lc.py +++ b/edg/parts/logic/SpiMemory_93Lc.py @@ -43,7 +43,7 @@ def __init__(self, size: RangeLike): voltage_limit_tolerance=(-0.6, 1), input_threshold_abs=(0.8, 2.0), # Table 1-1, for Vcc > 2.7 ) - self.spi = self.Port(SpiPeripheral(dio_model, (0, 2) * MHertz)) # for Vcc >= 2.5 + self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 2) * MHertz)) # for Vcc >= 2.5 self.cs = self.Port(dio_model) self.actual_size = self.Parameter(IntExpr()) diff --git a/edg/parts/logic/SpiMemory_W25q.py b/edg/parts/logic/SpiMemory_W25q.py index 8a494cefe..fbbeea182 100644 --- a/edg/parts/logic/SpiMemory_W25q.py +++ b/edg/parts/logic/SpiMemory_W25q.py @@ -55,7 +55,7 @@ def __init__(self, size: RangeLike): dio_model = DigitalBidir.from_supply( self.gnd, self.vcc, voltage_limit_tolerance=(-0.5, 0.4), input_threshold_factor=(0.3, 0.7) ) - self.spi = self.Port(SpiPeripheral(dio_model, (0, 104) * MHertz)) + self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 104) * MHertz)) self.cs = self.Port(dio_model) self.wp = self.Port(dio_model) self.hold = self.Port(dio_model) diff --git a/edg/parts/microcontroller/Ch32v003.py b/edg/parts/microcontroller/Ch32v003.py index 34c5f8b4c..5608db61d 100644 --- a/edg/parts/microcontroller/Ch32v003.py +++ b/edg/parts/microcontroller/Ch32v003.py @@ -162,10 +162,10 @@ def _io_pinmap(self) -> PinMapUtil: ) uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty()) + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 24) * MHertz) # TODO SPI peripherals, which have fixed-pin CS lines - i2c_model = I2cController(DigitalBidir.empty()) - i2c_target_model = I2cTarget(DigitalBidir.empty()) + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(0, 400) * kHertz) + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(0, 400) * kHertz) return PinMapUtil( [ # table 2-1 diff --git a/edg/parts/microcontroller/Ch32v203.py b/edg/parts/microcontroller/Ch32v203.py index de71ac09e..5376ef620 100644 --- a/edg/parts/microcontroller/Ch32v203.py +++ b/edg/parts/microcontroller/Ch32v203.py @@ -183,10 +183,10 @@ def _io_pinmap(self) -> PinMapUtil: ) uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty()) + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 36) * MHertz) # TODO SPI peripherals, which have fixed-pin CS lines - i2c_model = I2cController(DigitalBidir.empty()) - i2c_target_model = I2cTarget(DigitalBidir.empty()) + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(0, 400) * kHertz) + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(0, 400) * kHertz) return PinMapUtil( [ # table 2-1 diff --git a/edg/parts/microcontroller/Esp32.py b/edg/parts/microcontroller/Esp32.py index 7c69a8dc5..a3a7eddad 100644 --- a/edg/parts/microcontroller/Esp32.py +++ b/edg/parts/microcontroller/Esp32.py @@ -148,13 +148,13 @@ def _io_pinmap(self) -> PinMapUtil: dac_model = AnalogSource.from_supply(self.gnd, self.pwr) # TODO: no specs in datasheet?! - uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty(), (0, 80) * MHertz) # section 4.1.17 - spi_peripheral_model = SpiPeripheral(DigitalBidir.empty(), (0, 80) * MHertz) - i2c_model = I2cController(DigitalBidir.empty()) # section 4.1.11, 100/400kHz and up to 5MHz - i2c_target_model = I2cTarget(DigitalBidir.empty()) + uart_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 5) * MHertz) + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 80) * MHertz) # section 4.1.17 + spi_peripheral_model = SpiPeripheral(DigitalBidir.empty(), frequency_limit=(0, 80) * MHertz) + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(100, 5000) * kHertz) # section 4.1.11 + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(100, 5000) * kHertz) touch_model = TouchDriver() - can_model = CanControllerPort(DigitalBidir.empty()) # aka TWAI + can_model = CanControllerPort(DigitalBidir.empty(), bitrate_limit=(12.5, 1000) * kHertz) # aka TWAI i2s_model = I2sController(DigitalBidir.empty()) dvp8_model = Dvp8Host(DigitalBidir.empty()) diff --git a/edg/parts/microcontroller/Esp32c3.py b/edg/parts/microcontroller/Esp32c3.py index 945d47347..17df39fee 100644 --- a/edg/parts/microcontroller/Esp32c3.py +++ b/edg/parts/microcontroller/Esp32c3.py @@ -150,13 +150,15 @@ def _io_pinmap(self) -> PinMapUtil: # TODO: impedance / leakage - not specified by datasheet ) - uart_model = UartPort(DigitalBidir.empty()) + uart_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 5) * MHertz) spi_model = SpiController( - DigitalBidir.empty(), (0, 60) * MHertz + DigitalBidir.empty(), frequency_limit=(0, 60) * MHertz ) # section 3.4.2, max block in GP controller mode - spi_peripheral_model = SpiPeripheral(DigitalBidir.empty(), (0, 60) * MHertz) - i2c_model = I2cController(DigitalBidir.empty()) # section 3.4.4, supporting 100/400 and up to 800 kbit/s - i2c_target_model = I2cTarget(DigitalBidir.empty()) + spi_peripheral_model = SpiPeripheral(DigitalBidir.empty(), frequency_limit=(0, 60) * MHertz) + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(100, 800) * kHertz) # section 3.4.4 + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(100, 800) * kHertz) + i2s_model = I2sController(DigitalBidir.empty(), bitrate_limit=(0.01, 40) * MHertz) + can_model = CanControllerPort(DigitalBidir.empty(), bitrate_limit=(1, 1000) * kHertz) # aka TWAI return ( PinMapUtil( @@ -188,8 +190,8 @@ def _io_pinmap(self) -> PinMapUtil: PeripheralAnyResource("I2C_T", i2c_target_model), # TODO shared resource w/ I2C controller PeripheralAnyResource("SPI2", spi_model), PeripheralAnyResource("SPI2_P", spi_peripheral_model), # TODO shared resource w/ SPI controller - PeripheralAnyResource("I2S", I2sController.empty()), - PeripheralAnyResource("TWAI", CanControllerPort.empty()), + PeripheralAnyResource("I2S", i2s_model), + PeripheralAnyResource("TWAI", can_model), ] ) .remap_pins(self.RESOURCE_PIN_REMAP) diff --git a/edg/parts/microcontroller/Esp32s3.py b/edg/parts/microcontroller/Esp32s3.py index 4d7c6bba0..a8fa2b974 100644 --- a/edg/parts/microcontroller/Esp32s3.py +++ b/edg/parts/microcontroller/Esp32s3.py @@ -132,16 +132,14 @@ def _io_pinmap(self) -> PinMapUtil: # TODO: impedance / leakage - not specified by datasheet ) - uart_model = UartPort(DigitalBidir.empty()) # section 3.5.5, up to 5Mbps - spi_model = SpiController( - DigitalBidir.empty(), (0, 80) * MHertz - ) # section 3.5.2, 80MHz in controller, 60MHz in peripheral - spi_peripheral_model = SpiPeripheral(DigitalBidir.empty(), (0, 80) * MHertz) - i2c_model = I2cController(DigitalBidir.empty()) # section 3.5.6, 100/400kHz and up to 800kbit/s - i2c_target_model = I2cController(DigitalBidir.empty()) + uart_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 5) * MHertz) # section 3.5.5 + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 80) * MHertz) # section 3.5.2 + spi_peripheral_model = SpiPeripheral(DigitalBidir.empty(), frequency_limit=(0, 60) * MHertz) + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(100, 800) * kHertz) # section 3.5.6 + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(100, 800) * kHertz) touch_model = TouchDriver() - can_model = CanControllerPort(DigitalBidir.empty()) # aka TWAI, up to 1Mbit/s - i2s_model = I2sController(DigitalBidir.empty()) + can_model = CanControllerPort(DigitalBidir.empty(), bitrate_limit=(1, 1000) * kHertz) + i2s_model = I2sController(DigitalBidir.empty(), bitrate_limit=(0.01, 40) * MHertz) dvp8_model = Dvp8Host(DigitalBidir.empty()) return ( diff --git a/edg/parts/microcontroller/Ice40up.py b/edg/parts/microcontroller/Ice40up.py index 2a8d2fe9c..649397c61 100644 --- a/edg/parts/microcontroller/Ice40up.py +++ b/edg/parts/microcontroller/Ice40up.py @@ -168,7 +168,7 @@ def _io_pinmap(self) -> PinMapUtil: # hard macros, not tied to any particular pin i2c_model = I2cController(DigitalBidir.empty()) # user I2C, table 4.7 - spi_model = SpiController(DigitalBidir.empty(), (0, 45) * MHertz) # user SPI, table 4.10 + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 45) * MHertz) # user SPI, table 4.10 return PinMapUtil( [ # names consistent with pinout spreadsheet diff --git a/edg/parts/microcontroller/Lpc1549.py b/edg/parts/microcontroller/Lpc1549.py index e5c243e7d..ee79b6342 100644 --- a/edg/parts/microcontroller/Lpc1549.py +++ b/edg/parts/microcontroller/Lpc1549.py @@ -129,9 +129,11 @@ def _io_pinmap(self) -> PinMapUtil: ) self.reset.init_from(DigitalSink.from_bidir(dio_5v_model)) - uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty()) - spi_peripheral_model = SpiPeripheral(DigitalBidir.empty()) # MISO driven when CS asserted + uart_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 15) * MHertz) + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 17) * MHertz) + spi_peripheral_model = SpiPeripheral( + DigitalBidir.empty(), frequency_limit=(0, 17) * MHertz + ) # MISO driven when CS asserted return PinMapUtil( [ # partial table for 48- and 64-pin only @@ -186,13 +188,15 @@ def _io_pinmap(self) -> PinMapUtil: PeripheralAnyResource("SPI1", spi_model), PeripheralAnyResource("SPI0_P", spi_peripheral_model), # TODO shared resource w/ SPI controller PeripheralAnyResource("SPI1_P", spi_peripheral_model), # TODO shared resource w/ SPI controller - PeripheralAnyResource("CAN0", CanControllerPort(DigitalBidir.empty())), + PeripheralAnyResource("CAN0", CanControllerPort(DigitalBidir.empty(), bitrate_limit=(0, 1) * MHertz)), PeripheralFixedResource( - "I2C0", I2cController(DigitalBidir.empty()), {"scl": ["PIO0_22"], "sda": ["PIO0_23"]} + "I2C0", + I2cController(DigitalBidir.empty(), frequency_limit=(0, 1000) * kHertz), + {"scl": ["PIO0_22"], "sda": ["PIO0_23"]}, ), PeripheralFixedResource( "I2C0_T", - I2cTarget(DigitalBidir.empty()), + I2cTarget(DigitalBidir.empty(), frequency_limit=(0, 1000) * kHertz), {"scl": ["PIO0_22"], "sda": ["PIO0_23"]}, # TODO shared resource w/ I2C controller ), PeripheralFixedPin( diff --git a/edg/parts/microcontroller/Rp2040.py b/edg/parts/microcontroller/Rp2040.py index c0b7c11c0..be2ddf46b 100644 --- a/edg/parts/microcontroller/Rp2040.py +++ b/edg/parts/microcontroller/Rp2040.py @@ -182,10 +182,10 @@ def _io_pinmap(self) -> PinMapUtil: impedance=(100, float("inf")) * kOhm, ) - uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty()) - i2c_model = I2cController(DigitalBidir.empty()) - i2c_target_model = I2cTarget(DigitalBidir.empty()) + uart_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 921600) * Hertz) + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 62.5) * MHertz) + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(0, 1000) * kHertz) + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(0, 1000) * kHertz) return ( PinMapUtil( diff --git a/edg/parts/microcontroller/Stm32f103.py b/edg/parts/microcontroller/Stm32f103.py index a69ddad63..7bc96bce3 100644 --- a/edg/parts/microcontroller/Stm32f103.py +++ b/edg/parts/microcontroller/Stm32f103.py @@ -145,11 +145,12 @@ def _io_pinmap(self) -> PinMapUtil: impedance=(100, float("inf")) * kOhm, ) - uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty()) + uart1_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 4.5) * MHertz) # faster APB2 clock + uart23_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 2.25) * MHertz) # slower APB1 clock + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 18) * MHertz) # TODO SPI peripherals, which have fixed-pin CS lines - i2c_model = I2cController(DigitalBidir.empty()) - i2c_target_model = I2cTarget(DigitalBidir.empty()) + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(0, 400) * kHertz) + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(0, 400) * kHertz) return PinMapUtil( [ # Table 5, partial table for 48-pin only @@ -190,12 +191,12 @@ def _io_pinmap(self) -> PinMapUtil: PinResource("PC13", {"PC13": dio_pc_13_14_15_model}), PinResource("PC14", {"PC14": dio_pc_13_14_15_model, "OSC32_IN": Passive()}), PinResource("PC15", {"PC15": dio_pc_13_14_15_model, "OSC32_OUT": Passive()}), - PeripheralFixedResource("USART2", uart_model, {"tx": ["PA2", "PD5"], "rx": ["PA3", "PD6"]}), + PeripheralFixedResource("USART2", uart23_model, {"tx": ["PA2", "PD5"], "rx": ["PA3", "PD6"]}), PeripheralFixedResource( "SPI1", spi_model, {"sck": ["PA5", "PB3"], "miso": ["PA6", "PB4"], "mosi": ["PA7", "PB5"]} ), PeripheralFixedResource( - "USART3", uart_model, {"tx": ["PB10", "PD8", "PC10"], "rx": ["PB11", "PD9", "PC11"]} + "USART3", uart23_model, {"tx": ["PB10", "PD8", "PC10"], "rx": ["PB11", "PD9", "PC11"]} ), PeripheralFixedResource("I2C2", i2c_model, {"scl": ["PB10"], "sda": ["PB11"]}), PeripheralFixedResource( @@ -204,10 +205,10 @@ def _io_pinmap(self) -> PinMapUtil: {"scl": ["PB10"], "sda": ["PB11"]}, # TODO shared resource w/ I2C controller ), PeripheralFixedResource("SPI2", spi_model, {"sck": ["PB13"], "miso": ["PB14"], "mosi": ["PB15"]}), - PeripheralFixedResource("USART1", uart_model, {"tx": ["PA9", "PB6"], "rx": ["PA10", "PB7"]}), + PeripheralFixedResource("USART1", uart1_model, {"tx": ["PA9", "PB6"], "rx": ["PA10", "PB7"]}), PeripheralFixedResource( "CAN", - CanControllerPort(DigitalBidir.empty()), + CanControllerPort(DigitalBidir.empty(), bitrate_limit=(0, 1) * MHertz), {"txd": ["PA12", "PD1", "PB9"], "rxd": ["PA11", "PD0", "PB8"]}, ), PeripheralFixedResource( diff --git a/edg/parts/microcontroller/Stm32f303.py b/edg/parts/microcontroller/Stm32f303.py index 9321d3a29..4dbddc19e 100644 --- a/edg/parts/microcontroller/Stm32f303.py +++ b/edg/parts/microcontroller/Stm32f303.py @@ -178,11 +178,12 @@ def _io_pinmap(self) -> PinMapUtil: self.gnd, vdd, signal_bound=(0.2 * Volt, -0.2 * Volt), impedance=15 * kOhm(tol=0) # assumes buffer off ) - uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty()) + uart_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 9) * MHertz) + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 18) * MHertz) # TODO SPI peripherals, which have fixed-pin CS lines - i2c_model = I2cController(DigitalBidir.empty()) - i2c_target_model = I2cTarget(DigitalBidir.empty()) + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(0, 1000) * kHertz) + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(0, 1000) * kHertz) + can_model = CanControllerPort(DigitalBidir.empty(), bitrate_limit=(0, 1) * MHertz) return PinMapUtil( [ # Table 13, partial table for 48-pin only @@ -233,9 +234,7 @@ def _io_pinmap(self) -> PinMapUtil: "USART3", uart_model, {"tx": ["PB10", "PC10", "PB9"], "rx": ["PB11", "PC11", "PB8"]} # 1/3 check ), PeripheralFixedResource("USART1", uart_model, {"tx": ["PA9", "PB6"], "rx": ["PA10", "PB7"]}), - PeripheralFixedResource( - "CAN", CanControllerPort(DigitalBidir.empty()), {"tx": ["PA12", "PB9"], "rx": ["PA11", "PB8"]} - ), + PeripheralFixedResource("CAN", can_model, {"tx": ["PA12", "PB9"], "rx": ["PA11", "PB8"]}), PeripheralFixedResource( "I2C1", i2c_model, {"scl": ["PA15", "PB6", "PB8"], "sda": ["PA14", "PB7", "PB9"]} ), diff --git a/edg/parts/microcontroller/Stm32g031.py b/edg/parts/microcontroller/Stm32g031.py index fddb95b0b..bb3909563 100644 --- a/edg/parts/microcontroller/Stm32g031.py +++ b/edg/parts/microcontroller/Stm32g031.py @@ -104,11 +104,11 @@ def _io_pinmap(self) -> PinMapUtil: impedance=(50, float("inf")) * kOhm, # max external impedance, at lowest listed sampling rate ) - uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty()) + uart_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 8) * MHertz) + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 32) * MHertz) # TODO SPI peripherals, which have fixed-pin CS lines - i2c_model = I2cController(DigitalBidir.empty()) - i2c_target_model = I2cTarget(DigitalBidir.empty()) + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(0, 1) * MHertz) + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(0, 1) * MHertz) return PinMapUtil( [ # Table 12, partial table for up to 32-pin only diff --git a/edg/parts/microcontroller/Stm32g431.py b/edg/parts/microcontroller/Stm32g431.py index 67f068a86..7739c6ad9 100644 --- a/edg/parts/microcontroller/Stm32g431.py +++ b/edg/parts/microcontroller/Stm32g431.py @@ -134,10 +134,12 @@ def _io_pinmap(self) -> PinMapUtil: ) ) - uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty()) - i2c_model = I2cController(DigitalBidir.empty()) - i2c_target_model = I2cTarget(DigitalBidir.empty()) + uart_model = UartPort(DigitalBidir.empty()) # baud limit not directly specified + lpuart_model = UartPort(DigitalBidir.empty()) # baud limit not directly specified + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 75) * MHertz) # 41 Mbps in peripheral mode + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(0, 1) * MHertz) + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(0, 1) * MHertz) + fdcan_model = CanControllerPort(DigitalBidir.empty(), bitrate_limit=(0, 8) * MHertz) return PinMapUtil( [ # for 32 pins only for now @@ -195,7 +197,7 @@ def _io_pinmap(self) -> PinMapUtil: ], }, ), - PeripheralFixedResource("LPUART1", uart_model, {"tx": ["PA2", "PB11"], "rx": ["PA3", "PB10"]}), + PeripheralFixedResource("LPUART1", lpuart_model, {"tx": ["PA2", "PB11"], "rx": ["PA3", "PB10"]}), PeripheralFixedResource( "I2C1", i2c_model, {"scl": ["PA13", "PA15", "PB8"], "sda": ["PA14", "PB7", "PB9"]} ), @@ -205,9 +207,7 @@ def _io_pinmap(self) -> PinMapUtil: PeripheralFixedResource("I2C2", i2c_model, {"scl": ["PA9"], "sda": ["PA8", "PF0"]}), PeripheralFixedResource("I2C2_T", i2c_target_model, {"scl": ["PA9"], "sda": ["PA8", "PF0"]}), PeripheralFixedResource("I2C3", i2c_model, {"scl": ["PA8"], "sda": ["PB5"]}), - PeripheralFixedResource( - "FDCAN", CanControllerPort(DigitalBidir.empty()), {"tx": ["PA12", "PB9"], "rx": ["PA11", "PB8"]} - ), + PeripheralFixedResource("FDCAN", fdcan_model, {"tx": ["PA12", "PB9"], "rx": ["PA11", "PB8"]}), PeripheralFixedResource( "SWD", SwdTargetPort(DigitalBidir.empty()), diff --git a/edg/parts/microcontroller/Stm32l432.py b/edg/parts/microcontroller/Stm32l432.py index ccf5ced57..be74887fb 100644 --- a/edg/parts/microcontroller/Stm32l432.py +++ b/edg/parts/microcontroller/Stm32l432.py @@ -122,11 +122,13 @@ def _io_pinmap(self) -> PinMapUtil: impedance=(9.6, 13.8) * kOhm, # DAC buffer off ) - uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty()) + uart_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 10) * MHertz) + lpuart_model = UartPort(DigitalBidir.empty()) # different clock domain, baud limit not directly specified + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 40) * MHertz) # 24 Mbps in peripheral mode # TODO SPI peripherals, which have fixed-pin CS lines - i2c_model = I2cController(DigitalBidir.empty()) - i2c_target_model = I2cTarget(DigitalBidir.empty()) + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(0, 1) * MHertz) + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(0, 1) * MHertz) + can_model = CanControllerPort(DigitalBidir.empty(), bitrate_limit=(0, 1) * MHertz) return PinMapUtil( [ # Table 12, partial table for up to 32-pin only @@ -162,7 +164,7 @@ def _io_pinmap(self) -> PinMapUtil: {"sck": ["PA1", "PA5", "PB3"], "miso": ["PA6", "PA11", "PB4"], "mosi": ["PA7", "PA12", "PB5"]}, ), PeripheralFixedResource("USART2", uart_model, {"tx": ["PA2"], "rx": ["PA3", "PA15"]}), - PeripheralFixedResource("LPUART1", uart_model, {"tx": ["PA2"], "rx": ["PA3"]}), + PeripheralFixedResource("LPUART1", lpuart_model, {"tx": ["PA2"], "rx": ["PA3"]}), PeripheralFixedResource("I2C3", i2c_model, {"scl": ["PA7"], "sda": ["PB4"]}), PeripheralFixedResource( "I2C3_T", @@ -176,9 +178,7 @@ def _io_pinmap(self) -> PinMapUtil: {"scl": ["PA9", "PB6"], "sda": ["PA10", "PB7"]}, # TODO shared resource w/ I2C controller ), PeripheralFixedResource("USART2", uart_model, {"tx": ["PA9"], "rx": ["PA10"]}), - PeripheralFixedResource( - "CAN", CanControllerPort(DigitalBidir.empty()), {"tx": ["PA12"], "rx": ["PA11"]} - ), + PeripheralFixedResource("CAN", can_model, {"tx": ["PA12"], "rx": ["PA11"]}), PeripheralFixedResource( "USB", UsbDevicePort(speed=UsbLink.UsbFullSpeedOnly), {"dp": ["PA12"], "dm": ["PA11"]} ), diff --git a/edg/parts/microcontroller/nRF52840.py b/edg/parts/microcontroller/nRF52840.py index dc5b3e8e6..cb5b74cd4 100644 --- a/edg/parts/microcontroller/nRF52840.py +++ b/edg/parts/microcontroller/nRF52840.py @@ -154,12 +154,14 @@ def _io_pinmap(self) -> PinMapUtil: impedance=Range.from_lower(1) * MOhm, ) - uart_model = UartPort(DigitalBidir.empty()) - spi_model = SpiController(DigitalBidir.empty(), (125, 32000) * kHertz) - spi_peripheral_model = SpiPeripheral(DigitalBidir.empty(), (125, 32000) * kHertz) # tristated by CS pin - i2c_model = I2cController(DigitalBidir.empty()) - i2c_target_model = I2cTarget(DigitalBidir.empty()) - i2s_model = I2sController(DigitalBidir.empty()) + uart_model = UartPort(DigitalBidir.empty(), baud_limit=(0, 1000) * kHertz) + spi_model = SpiController(DigitalBidir.empty(), frequency_limit=(0, 32) * MHertz) + spi_peripheral_model = SpiPeripheral( + DigitalBidir.empty(), frequency_limit=(0, 8) * MHertz + ) # tristated by CS pin + i2c_model = I2cController(DigitalBidir.empty(), frequency_limit=(100, 400) * kHertz) + i2c_target_model = I2cTarget(DigitalBidir.empty(), frequency_limit=(100, 400) * kHertz) + i2s_model = I2sController(DigitalBidir.empty(), bitrate_limit=(0, 2000) * kHertz) hf_io_pins = [ "P0.00", diff --git a/edg/parts/power/FuelGauge_Max17048.py b/edg/parts/power/FuelGauge_Max17048.py index 3d2225d08..ecbdb8803 100644 --- a/edg/parts/power/FuelGauge_Max17048.py +++ b/edg/parts/power/FuelGauge_Max17048.py @@ -20,7 +20,7 @@ def __init__(self) -> None: dio_model = DigitalBidir.from_supply( self.gnd, self.pwr, voltage_limit_abs=(-0.3, 5.5) * Volt, input_threshold_abs=(0.5, 1.4) * Volt ) - self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x36])) + self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x36], frequency_limit=(0, 400) * kHertz)) self.alrt = self.Port( DigitalSource.low_from_supply(self.gnd), diff --git a/edg/parts/power/converter/Mp2722.py b/edg/parts/power/converter/Mp2722.py index 8a64cf852..b10456f1d 100644 --- a/edg/parts/power/converter/Mp2722.py +++ b/edg/parts/power/converter/Mp2722.py @@ -71,7 +71,7 @@ def __init__(self, charging_current: RangeLike): self.pg = self.Port(DigitalSource.low_from_supply(self.gnd), optional=True) # requires 10k pullup # i2C up to 5v tolerant - self.i2c = self.Port(I2cTarget(dio_model)) + self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x3F], frequency_limit=(0, 400) * kHertz)) self.cc = self.Port(UsbCcPort(), optional=True) self.usb = self.Port( UsbDevicePort(speed=Range.exact(UsbLink.UsbLowSpeed)), optional=True diff --git a/edg/parts/sensor/Camera_Ov2640_Fpc24.py b/edg/parts/sensor/Camera_Ov2640_Fpc24.py index e88e4ca84..96911206c 100644 --- a/edg/parts/sensor/Camera_Ov2640_Fpc24.py +++ b/edg/parts/sensor/Camera_Ov2640_Fpc24.py @@ -54,7 +54,7 @@ def __init__(self) -> None: # formally this is SCCB (serial camera control bus), but is I2C compatible # https://e2e.ti.com/support/processors-group/processors/f/processors-forum/6092/sccb-vs-i2c # 0x60 for write, 0x61 for read, translated to the 7-bit address - self.sio = self.Port(I2cTarget(dio_model, [0x30])) + self.sio = self.Port(I2cTarget(dio_model, addresses=[0x30], frequency_limit=(0, 400) * kHertz)) self.conn = self.Block(Fpc050Bottom(length=24)).connected( { diff --git a/edg/parts/sensor/DistanceArray_Vl53l5cx.py b/edg/parts/sensor/DistanceArray_Vl53l5cx.py index b963f9649..b7a897932 100644 --- a/edg/parts/sensor/DistanceArray_Vl53l5cx.py +++ b/edg/parts/sensor/DistanceArray_Vl53l5cx.py @@ -47,6 +47,7 @@ def __init__(self) -> None: ), ), addresses=[0x52], # TODO software remappable + frequency_limit=(0, 1000) * kHertz, ), [Output], ) diff --git a/edg/parts/sensor/Distance_Vl53l0x.py b/edg/parts/sensor/Distance_Vl53l0x.py index 2e80e900b..625c0a4b9 100644 --- a/edg/parts/sensor/Distance_Vl53l0x.py +++ b/edg/parts/sensor/Distance_Vl53l0x.py @@ -36,7 +36,8 @@ def __init__(self) -> None: self.vdd, voltage_limit_abs=(-0.5, 3.6), # not referenced to Vdd! input_threshold_abs=(0.6, 1.12), - ) + ), + frequency_limit=(0, 400) * kHertz, ), [Output], ) diff --git a/edg/parts/sensor/EnvironmentalSensor_Bme680.py b/edg/parts/sensor/EnvironmentalSensor_Bme680.py index d536e3fbe..0bda949b4 100644 --- a/edg/parts/sensor/EnvironmentalSensor_Bme680.py +++ b/edg/parts/sensor/EnvironmentalSensor_Bme680.py @@ -22,7 +22,8 @@ def __init__(self) -> None: voltage_limit_abs=(-0.3 * Volt, self.vddio.voltage_limits.upper() + 0.3), input_threshold_factor=(0.2, 0.8), ) - self.i2c = self.Port(I2cTarget(dio_model, [0x76])) + # note, frequency limits for 1.2 VddIO + self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x76], frequency_limit=(0, 2380) * kHertz)) @override def contents(self) -> None: diff --git a/edg/parts/sensor/FlirLepton.py b/edg/parts/sensor/FlirLepton.py index 169b92b38..6063fe094 100644 --- a/edg/parts/sensor/FlirLepton.py +++ b/edg/parts/sensor/FlirLepton.py @@ -32,9 +32,9 @@ def __init__(self) -> None: dio_model = DigitalBidir.from_supply(self.gnd, self.vddio, voltage_limit_tolerance=(0, 0.6) * Volt) self.master_clk = self.Port(DigitalSink.from_bidir(dio_model)) # 25MHz clock - self.spi = self.Port(SpiPeripheral(dio_model, (0, 20) * MHertz)) + self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 20) * MHertz)) self.cs = self.Port(DigitalSink.from_bidir(dio_model)) - self.cci = self.Port(I2cTarget(dio_model, [0x2A])) # frequency up to 1MHz + self.cci = self.Port(I2cTarget(dio_model, addresses=[0x2A], frequency_limit=(0, 1000) * kHertz)) self.reset_l = self.Port(DigitalSink.from_bidir(dio_model)) self.pwr_dwn_l = self.Port(DigitalSink.from_bidir(dio_model)) diff --git a/edg/parts/sensor/Imu_Lsm6ds3trc.py b/edg/parts/sensor/Imu_Lsm6ds3trc.py index ed4d9dcd8..b1efdf31a 100644 --- a/edg/parts/sensor/Imu_Lsm6ds3trc.py +++ b/edg/parts/sensor/Imu_Lsm6ds3trc.py @@ -24,7 +24,9 @@ def __init__(self) -> None: current_limits=(-4, 4) * mAmp, input_threshold_factor=(0.3, 0.7), ) - self.i2c = self.Port(I2cTarget(dio_model)) + self.i2c = self.Port( + I2cTarget(dio_model, addresses=[0x6A], frequency_limit=(100, 400) * kHertz) + ) # 0x6B for SA0=1 dout_model = DigitalSource.low_from_supply(self.gnd) self.int1 = self.Port(dout_model, optional=True) diff --git a/edg/parts/sensor/Imu_Lsm6dsv16x.py b/edg/parts/sensor/Imu_Lsm6dsv16x.py index a29dd9760..0c5e5bf3f 100644 --- a/edg/parts/sensor/Imu_Lsm6dsv16x.py +++ b/edg/parts/sensor/Imu_Lsm6dsv16x.py @@ -24,7 +24,7 @@ def __init__(self) -> None: current_limits=(-4, 4) * mAmp, input_threshold_factor=(0.3, 0.7), ) - self.i2c = self.Port(I2cTarget(dio_model)) + self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x6A], frequency_limit=(0, 1000) * kHertz)) # for SA=0 dout_model = DigitalSource.low_from_supply(self.gnd) self.int1 = self.Port(dout_model, optional=True) diff --git a/edg/parts/sensor/LightSensor_As7341.py b/edg/parts/sensor/LightSensor_As7341.py index c9b89952d..a0d5b1fce 100644 --- a/edg/parts/sensor/LightSensor_As7341.py +++ b/edg/parts/sensor/LightSensor_As7341.py @@ -19,7 +19,7 @@ def __init__(self) -> None: input_threshold_abs=(0.54, 1.26) * Volt, output_threshold_abs=(0, float("inf")) * Volt, # reflects pulldown ) - self.i2c = self.Port(I2cTarget(dio_model, [0x39])) + self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x39], frequency_limit=(0, 400) * kHertz)) @override def contents(self) -> None: diff --git a/edg/parts/sensor/LightSensor_Bh1750.py b/edg/parts/sensor/LightSensor_Bh1750.py index 4cd4ff7ac..07ee550ef 100644 --- a/edg/parts/sensor/LightSensor_Bh1750.py +++ b/edg/parts/sensor/LightSensor_Bh1750.py @@ -19,7 +19,7 @@ def __init__(self) -> None: ) # reset pin dio_model = DigitalBidir.from_supply(self.gnd, self.vcc, input_threshold_factor=(0.3, 0.7)) - self.i2c = self.Port(I2cTarget(dio_model, [0x23])) + self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x23], frequency_limit=(0, 400) * kHertz)) @override def contents(self) -> None: diff --git a/edg/parts/sensor/Mag_Qmc5883.py b/edg/parts/sensor/Mag_Qmc5883.py index b898c84e9..caa48af76 100644 --- a/edg/parts/sensor/Mag_Qmc5883.py +++ b/edg/parts/sensor/Mag_Qmc5883.py @@ -22,7 +22,7 @@ def __init__(self) -> None: voltage_limit_abs=(-0.3 * Volt, self.vddio.voltage_limits.upper() + 0.3), input_threshold_factor=(0.3, 0.7), ) - self.i2c = self.Port(I2cTarget(dio_model)) + self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x0D], frequency_limit=(0, 400) * kHertz)) self.drdy = self.Port(DigitalSource.from_bidir(dio_model), optional=True) self.setp = self.Port(Passive()) @@ -106,7 +106,7 @@ def __init__(self) -> None: voltage_limit_tolerance=(-0.3, 0.3) * Volt, # assumed form Vdd absolute maximum rating input_threshold_factor=(0.3, 0.7), ) - self.i2c = self.Port(I2cTarget(dio_model)) + self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x2C], frequency_limit=(0, 400) * kHertz)) self.c1 = self.Port(VoltageSource(voltage=self.vdd.link().voltage, current_limits=(0, 0) * Amp)) # assumed @override diff --git a/edg/parts/sensor/Rtc_Pcf2129.py b/edg/parts/sensor/Rtc_Pcf2129.py index 54128a397..a1c609b34 100644 --- a/edg/parts/sensor/Rtc_Pcf2129.py +++ b/edg/parts/sensor/Rtc_Pcf2129.py @@ -21,7 +21,7 @@ def __init__(self) -> None: output_thresholds=(0, self.pwr.link().voltage.upper()), ) - self.spi = self.Port(SpiPeripheral(dio_model), [Output]) + self.spi = self.Port(SpiPeripheral(dio_model, frequency_limit=(0, 6.5) * MHertz), [Output]) self.cs = self.Port(DigitalSink.from_bidir(dio_model)) opendrain_model = DigitalSource.low_from_supply(self.gnd, current_limits=(-1, 0) * mAmp) diff --git a/edg/parts/sensor/Temp_Shtc3.py b/edg/parts/sensor/Temp_Shtc3.py index 043e8e02b..05647f1e8 100644 --- a/edg/parts/sensor/Temp_Shtc3.py +++ b/edg/parts/sensor/Temp_Shtc3.py @@ -13,7 +13,7 @@ def __init__(self) -> None: self.vss = self.Port(Ground()) dio_model = DigitalBidir.from_supply(self.vss, self.vdd, input_threshold_factor=(0.42, 0.7)) - self.i2c = self.Port(I2cTarget(dio_model, [0x70])) + self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x70], frequency_limit=(0, 1000) * kHertz)) @override def contents(self) -> None: diff --git a/edg/parts/sensor/Temp_TexasInstruments.py b/edg/parts/sensor/Temp_TexasInstruments.py index 62efbdedd..22e6814d4 100644 --- a/edg/parts/sensor/Temp_TexasInstruments.py +++ b/edg/parts/sensor/Temp_TexasInstruments.py @@ -13,7 +13,7 @@ def __init__(self) -> None: ) dio_model = DigitalBidir.from_supply(self.gnd, self.vdd, input_threshold_factor=(0.3, 0.7)) - self.i2c = self.Port(I2cTarget(dio_model, [0x40])) + self.i2c = self.Port(I2cTarget(dio_model, addresses=[0x40], frequency_limit=(10, 400) * kHertz)) @override def contents(self) -> None: @@ -63,7 +63,7 @@ def __init__(self, addr_lsb: IntLike) -> None: ) dio_model = DigitalBidir.from_supply(self.gnd, self.vdd, input_threshold_factor=(0.3, 0.7)) - self.i2c = self.Port(I2cTarget(dio_model, addresses=ArrayIntExpr())) + self.i2c = self.Port(I2cTarget(dio_model, addresses=ArrayIntExpr(), frequency_limit=(1, 2560) * kHertz)) self.alert = self.Port(DigitalSource.low_from_supply(self.gnd), optional=True) self.addr_lsb = self.ArgParameter(addr_lsb) diff --git a/edg/util/__init__.py b/edg/util/__init__.py index f2753b1c4..50da1a08c 100644 --- a/edg/util/__init__.py +++ b/edg/util/__init__.py @@ -1,22 +1,37 @@ import warnings from functools import wraps -from typing import Tuple, Callable, TypeVar, Any +from typing import Tuple, Callable, TypeVar, Any, Union CallableType = TypeVar("CallableType", bound=Callable[..., Any]) -def deprecated_param_remap(*params: Tuple[str, str]) -> Callable[[CallableType], CallableType]: - """Decorator to remap deprecated parameter names to new names. +def deprecated_param_remap(*params: Tuple[Union[int, str], str]) -> Callable[[CallableType], CallableType]: + """Decorator to remap deprecated parameter positional arg or kwarg names to new kwarg names. Args: - *params: A list of tuples where each tuple contains the old parameter name and the new parameter name. + *params: A list of tuples where each tuple contains the old positional index (int) or kwarg name (str) + and the new kwarg name. """ def decorator(func: CallableType) -> CallableType: @wraps(func) def wrapper(*args: Any, **kwargs: Any) -> Any: - for old_param, new_param in params: - if old_param in kwargs: + # last arg-params first to avoid shifting issues + sorted_params = sorted(params, key=lambda x: -x[0] if isinstance(x[0], int) else float("inf")) + for old_param, new_param in sorted_params: + if isinstance(old_param, int) and old_param < len(args): + warnings.warn( + f"Positional argument {old_param} (0-based, including self) is deprecated and replaced with {new_param}", + DeprecationWarning, + stacklevel=2, + ) + if new_param in kwargs: + raise ValueError( + f"both old positional argument {old_param} and new {new_param} parameter specified" + ) + kwargs[new_param] = args[old_param] + args = args[:old_param] + args[old_param + 1 :] + elif isinstance(old_param, str) and old_param in kwargs: warnings.warn( f"{old_param} is deprecated and replaced with {new_param}", DeprecationWarning,