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
4 changes: 0 additions & 4 deletions edg/abstract_parts/DigitalAmplifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ def generate(self):
drain_current=(0, pull_current_max),
gate_voltage=(self.control.link().output_thresholds.upper(), self.control.link().voltage.upper()),
rds_on=(0, low_amp_rds_max), # TODO size on turnon time
gate_charge=(0, float('inf')), # TODO size on turnon time
power=(0, 0) * Watt,
frequency=self.frequency,
drive_current=self.control.link().current_limits # TODO this is kind of a max drive current
))
Expand All @@ -71,8 +69,6 @@ def generate(self):
drain_current=self.output.link().current_drawn,
gate_voltage=pass_gate_voltage,
rds_on=(0, self.max_rds),
gate_charge=(0, float('inf')), # TODO size on turnon time
power=(0, 0) * Watt,
frequency=self.frequency,
drive_current=(-1 * pwr_voltage.lower() / pull_resistance.upper(),
pwr_voltage.lower() / low_amp_rds_max) # TODO simultaneously solve both FETs
Expand Down
5 changes: 4 additions & 1 deletion edg/abstract_parts/PartsTable.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ def filter(self, fn: Callable[[PartsTableRow], bool]) -> PartsTable:

def map_new_columns(self, fn: Callable[[PartsTableRow], Optional[Dict[PartsTableColumn[Any], Any]]],
overwrite: bool = False) -> PartsTable:
"""Creates a new table (deep copy) with additional rows."""
"""Creates a new table (deep copy) with additional rows.
All entries must have the same keys.
Specify overwrite=True to overwrite an existing row. To preserve the existing value (no-op), return it.
Return None to drop the row."""
new_rows: List[PartsTableRow] = []
first_keys: Optional[KeysView] = None
for row in self.rows:
Expand Down
8 changes: 3 additions & 5 deletions edg/abstract_parts/PartsTablePart.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,9 @@ def generate(self):
postprocessed_table = self._table_postprocess(matching_table)
postprocessed_table = postprocessed_table.sort_by(self._row_sort_by)
self.assign(self.matching_parts, postprocessed_table.map(lambda row: row[self.PART_NUMBER_COL]))
if len(postprocessed_table) > 0:
selected_row = postprocessed_table.first()
self._row_generate(selected_row)
else: # if no matching part, generate a parameter error instead of crashing
self.require(False, "no matching part")
assert len(postprocessed_table) > 0, "no matching part" # crash to make generator failures more obvious
Copy link

Copilot AI Oct 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message 'no matching part' is too generic. It should include context about which component type or requirements failed to match to aid debugging.

Suggested change
assert len(postprocessed_table) > 0, "no matching part" # crash to make generator failures more obvious
assert len(postprocessed_table) > 0, (
f"no matching part for {self.__class__.__name__} with part='{self.get(self.part)}'"
) # crash to make generator failures more obvious

Copilot uses AI. Check for mistakes.
selected_row = postprocessed_table.first()
self._row_generate(selected_row)


@abstract_block
Expand Down
2 changes: 0 additions & 2 deletions edg/abstract_parts/test_resistor_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ def __init__(self):
super().__init__()
self.dut = self.Block(GenericChipResistor(
resistance=1 * kOhm(tol=0.1),
power=(0, 0)*Watt
))
(self.dummya, ), _ = self.chain(self.dut.a, self.Block(DummyPassive()))
(self.dummyb, ), _ = self.chain(self.dut.b, self.Block(DummyPassive()))
Expand All @@ -30,7 +29,6 @@ def __init__(self):
super().__init__()
self.dut = self.Block(GenericChipResistor(
resistance=8.06 * kOhm(tol=0.01),
power=(0, 0)*Watt
))
(self.dummya, ), _ = self.chain(self.dut.a, self.Block(DummyPassive()))
(self.dummyb, ), _ = self.chain(self.dut.b, self.Block(DummyPassive()))
Expand Down
4 changes: 2 additions & 2 deletions edg/electronics_model/DigitalPorts.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,8 @@ def __init__(self):
current_draw=RangeExpr()
))
self.dst = self.Port(VoltageSource(
voltage_out=(self.src.link().output_thresholds.upper(), self.src.link().voltage.upper()),
current_limits=(-float('inf'), float('inf'))))
voltage_out=(self.src.link().output_thresholds.upper(), self.src.link().voltage.upper())
))
self.assign(self.src.current_draw, self.dst.link().current_drawn)


Expand Down
1 change: 0 additions & 1 deletion edg/electronics_model/GroundPort.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ def __init__(self):
self.dst = self.Port(AnalogSource(
voltage_out=self.src.link().voltage,
signal_out=self.src.link().voltage,
impedance=(0, 0)*Ohm,
))


Expand Down
1 change: 0 additions & 1 deletion edg/electronics_model/VoltagePorts.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ def __init__(self):

super().__init__()
self.src = self.Port(VoltageSink(
voltage_limits=(-float('inf'), float('inf'))*Volt,
current_draw=RangeExpr()
))
self.dst = self.Port(AnalogSource(
Expand Down
31 changes: 11 additions & 20 deletions edg/jlcparts/JlcPartsFet.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ class JlcPartsBaseFet(JlcPartsBase, BaseTableFet):
_JLC_PARTS_FILE_NAMES = ["TransistorsMOSFETs"]
_CHANNEL_MAP = {
'N Channel': 'N',
'1 N-channel': 'N',
'1 N-Channel': 'N',
'P Channel': 'P',
'1 Piece P-Channel': 'P',
}

@classmethod
Expand Down Expand Up @@ -39,14 +42,18 @@ def _entry_to_table_row(cls, row_dict: Dict[PartsTableColumn, Any], filename: st
input_capacitance: Optional[float] = attributes.get("Input capacitance (ciss@vds)", float, sub='capacity')
except (KeyError, TypeError):
input_capacitance = None

try: # not specified for most parts apparently
gate_charge = attributes.get("Total gate charge (qg@vgs)", float, sub='charge')
gate_charge: Optional[float] = attributes.get("Total gate charge (qg@vgs)", float, sub='charge')
except (KeyError, TypeError):
if input_capacitance is not None: # not strictly correct but kind of a guesstimate
gate_charge = input_capacitance * vgs_for_ids
else:
gate_charge = 3000e-9 # very pessimistic upper bound
row_dict[cls.GATE_CHARGE] = Range.exact(gate_charge)
gate_charge = None
if gate_charge is not None:
row_dict[cls.GATE_CHARGE] = Range.exact(gate_charge)
else:
row_dict[cls.GATE_CHARGE] = Range.all()

return row_dict
except (KeyError, TypeError, PartParserUtil.ParseError):
Expand All @@ -58,23 +65,7 @@ class JlcPartsFet(PartsTableSelectorFootprint, JlcPartsBaseFet, TableFet):


class JlcPartsSwitchFet(PartsTableSelectorFootprint, JlcPartsBaseFet, TableSwitchFet):
@init_in_parent
def __init__(self, *args, manual_gate_charge: RangeLike = RangeExpr.ZERO, **kwargs):
super().__init__(*args, **kwargs)
# allow the user to specify a gate charge
self.manual_gate_charge = self.ArgParameter(manual_gate_charge)
self.generator_param(self.manual_gate_charge)

def _table_postprocess(self, table: PartsTable) -> PartsTable:
manual_gate_charge = self.get(self.manual_gate_charge)
def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]:
return {self.GATE_CHARGE: manual_gate_charge}

# must run before TableFet power calculations
if not manual_gate_charge == Range.exact(0):
table = table.map_new_columns(process_row, overwrite=True)

return super()._table_postprocess(table)
pass


lambda: JlcPartsFet(), JlcPartsSwitchFet() # ensure class is instantiable (non-abstract)
1 change: 0 additions & 1 deletion edg/parts/AdcSpi_Mcp3201.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ def __init__(self) -> None:
dio_model = DigitalBidir.from_supply(
self.vss, self.vdd,
voltage_limit_tolerance=(-0.6, 0.6)*Volt,
current_draw=(0, 0), # leakage current not modeled
input_threshold_factor=(0.3, 0.7)
)
# Datasheet section 6.2, minimum clock speed
Expand Down
4 changes: 0 additions & 4 deletions edg/parts/BatteryProtector_S8261A.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,13 @@ def __init__(self) -> None:

self.do_fet = self.Block(Fet.NFet(
drain_current=self.pwr_in.link().current_drawn,
power=RangeExpr.ZERO,
gate_voltage=self.pwr_in.link().voltage,
gate_charge=RangeExpr.ALL,
rds_on=(0, 0.1)*Ohm,
drain_voltage=self.pwr_in.link().voltage
))
self.co_fet = self.Block(Fet.NFet(
drain_current=self.pwr_in.link().current_drawn,
power=RangeExpr.ZERO,
gate_voltage=self.pwr_in.link().voltage,
gate_charge=RangeExpr.ALL,
rds_on=(0, 0.1)*Ohm,
drain_voltage=self.pwr_in.link().voltage
))
Expand Down
1 change: 0 additions & 1 deletion edg/parts/CanTransceiver_Iso1050.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ def __init__(self):
self.controller = self.Port(CanTransceiverPort(DigitalBidir(
voltage_limits=(-0.5 * Volt, self.vcc1.link().voltage.lower() + 0.5 * Volt),
voltage_out=(0 * Volt, self.vcc1.link().voltage.lower()),
current_draw=(0, 0), # TODO actually an unspecified default
current_limits=(-5, 5) * uAmp,
input_thresholds=(0.8, 2) * Volt,
output_thresholds=(0 * Volt, self.vcc1.link().voltage.lower())
Expand Down
1 change: 0 additions & 1 deletion edg/parts/CanTransceiver_Sn65hvd230.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ def __init__(self):
self.controller = self.Port(CanTransceiverPort(DigitalBidir(
voltage_limits=(-0.5 * Volt, self.vcc.link().voltage.lower() + 0.5 * Volt),
voltage_out=(0 * Volt, self.vcc.link().voltage.lower()),
current_draw=(0, 0),
current_limits=(-8, 8) * mAmp, # driver pin actually -40-48mA
input_thresholds=(0.8, 2) * Volt,
output_thresholds=(0 * Volt, self.vcc.link().voltage.lower())
Expand Down
2 changes: 0 additions & 2 deletions edg/parts/DacSpi_Mcp4901.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ def __init__(self) -> None:

self.vref = self.Port(VoltageSink(
voltage_limits=(0.04*Volt, self.vdd.link().voltage.lower() - 0.04),
current_draw=(0, 0) # input current not specified
))
self.vout = self.Port(AnalogSource.from_supply(
self.vss, self.vref,
Expand All @@ -23,7 +22,6 @@ def __init__(self) -> None:
dio_model = DigitalBidir.from_supply(
self.vss, self.vdd,
voltage_limit_tolerance=(-0.3, 0.3)*Volt,
current_draw=(0, 0), # leakage current not modeled
current_limits=(-25, 25)*mAmp,
input_threshold_factor=(0.2, 0.7)
)
Expand Down
2 changes: 1 addition & 1 deletion edg/parts/EInk_E2154fs091.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def contents(self) -> None:
drain_voltage=(0, 30)*Volt,
drain_current=(0, 0.8)*Amp, # assumed, from inductor rating
gate_voltage=(3, 16)*Volt, # assumed, from capacitor ratings # TODO use pwr voltage instead of hardcoding
rds_on=(0, 85)*mOhm, gate_charge=(0, float('inf')),
rds_on=(0, 85)*mOhm,
power=(0, 0.2)*Watt # about 4x resistive loss @ 0.8A, 85mOhm; we don't know the switch frequency or drive current
))
self.boost_ind = self.Block(Inductor(
Expand Down
1 change: 0 additions & 1 deletion edg/parts/Fpga_Ice40up.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ def make_dio_model(gnd: Ground, vccio: VoltageSink):
gnd, vccio,
voltage_limit_tolerance=(-0.3, 0.2) * Volt, # table 4.13
current_limits=(-8, 8)*mAmp, # for LVCMOS 3.3 (least restrictive)
current_draw=(0, 0)*Amp,
input_threshold_abs=(0.8, 2.0), # most restrictive, for LVCMOS 3.3
pullup_capable=True, pulldown_capable=False,
)
Expand Down
1 change: 0 additions & 1 deletion edg/parts/Fusb302b.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ def __init__(self) -> None:

self.cc = self.Port(UsbCcPort()) # TODO pass in port models?
i2c_model = DigitalBidir( # interestingly, IO maximum voltages are not specified
current_draw=(0, 0), # leakage current (Table 13) not modeled
voltage_out=(0, 0.35)*Volt, # low-level output voltage
current_limits=(-20, 0)*mAmp, # low-level output current limits
input_thresholds=(0.51, 1.32)*Volt,
Expand Down
3 changes: 1 addition & 2 deletions edg/parts/Jacdac.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,7 @@ def contents(self):
signal_level = self.signal.link().voltage
self.rc = self.Block(LowPassRc(impedance=220*Ohm(tol=0.05), cutoff_freq=22*MHertz(tol=0.12),
voltage=signal_level))
clamp_diode_model = Diode(reverse_voltage=(0, signal_level.upper()),
current=(0, 0))
clamp_diode_model = Diode(reverse_voltage=(0, signal_level.upper()), current=(0, 0)*Amp)
self.clamp_hi = self.Block(clamp_diode_model)
self.clamp_lo = self.Block(clamp_diode_model)

Expand Down
Loading