From e23d3ccc1a69e15a81c527768cc13ebdb359523a Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 00:40:41 -0700 Subject: [PATCH 01/14] wip --- edg/abstract_parts/PartsTablePart.py | 8 +++----- edg/jlcparts/JlcPartsFet.py | 3 +++ edg/parts/JlcFet.py | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/edg/abstract_parts/PartsTablePart.py b/edg/abstract_parts/PartsTablePart.py index d5c40f651..03a81ddbd 100644 --- a/edg/abstract_parts/PartsTablePart.py +++ b/edg/abstract_parts/PartsTablePart.py @@ -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 + selected_row = postprocessed_table.first() + self._row_generate(selected_row) @abstract_block diff --git a/edg/jlcparts/JlcPartsFet.py b/edg/jlcparts/JlcPartsFet.py index 5b666b4cf..44dc637ca 100644 --- a/edg/jlcparts/JlcPartsFet.py +++ b/edg/jlcparts/JlcPartsFet.py @@ -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 diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index d8fceb7cd..74a5849a6 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -7,22 +7,29 @@ class JlcBaseFet(JlcTableSelector): PACKAGE_FOOTPRINT_MAP = { + 'SOT-23': 'Package_TO_SOT_SMD:SOT-23', 'SOT23-3': 'Package_TO_SOT_SMD:SOT-23', 'SOT-23-3': 'Package_TO_SOT_SMD:SOT-23', 'SOT-23-3L': 'Package_TO_SOT_SMD:SOT-23', 'SOT-323': 'Package_TO_SOT_SMD:SOT-323_SC-70', + 'SOT-323(SC-80)': 'Package_TO_SOT_SMD:SOT-323_SC-70', 'SOT-323-3': 'Package_TO_SOT_SMD:SOT-323_SC-70', 'SC-70-3': 'Package_TO_SOT_SMD:SOT-323_SC-70', 'TO-252': 'Package_TO_SOT_SMD:TO-252-2', # aka DPak 'TO-252-2': 'Package_TO_SOT_SMD:TO-252-2', + 'TO-252(DPAK)': 'Package_TO_SOT_SMD:TO-252-2', + 'DPAK': 'Package_TO_SOT_SMD:TO-252-2', + 'TO-252-2(DPAK)': 'Package_TO_SOT_SMD:TO-252-2', 'TO-263-2': 'Package_TO_SOT_SMD:TO-263-2', # aka D2Pak + 'D2PAK': 'Package_TO_SOT_SMD:TO-263-2', 'SOT-223': 'Package_TO_SOT_SMD:SOT-223-3_TabPin2', 'SOT-223-3': 'Package_TO_SOT_SMD:SOT-223-3_TabPin2', 'SO-8': 'Package_SO:SOIC-8_3.9x4.9mm_P1.27mm', + 'SOIC-8': 'Package_SO:SOIC-8_3.9x4.9mm_P1.27mm', 'SOIC-8_3.9x4.9x1.27P': 'Package_SO:SOIC-8_3.9x4.9mm_P1.27mm', 'SOP-8': 'Package_SO:SOIC-8_3.9x4.9mm_P1.27mm', 'SOP-8_3.9x4.9x1.27P': 'Package_SO:SOIC-8_3.9x4.9mm_P1.27mm', @@ -47,6 +54,13 @@ class JlcBaseFet(JlcTableSelector): 'PQFN-8(5x6)': 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', 'PRPAK5x6': 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', 'PRPAK5x6-8L': 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', + 'PG-TDSON-8_EP_5.2x6.2x1.27P': 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', + + 'TDSON-8-EP(5x6)': 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', + 'PDFN-8(5x6)': 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', + 'DFN-8-EP(6.1x5.2)': 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', + 'DFN-8(4.9x5.8)': 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', + 'PDFN-8(5.8x4.9)': 'Package_DFN_QFN:PQFN-8-EP_6x5mm_P1.27mm_Generic', } DESCRIPTION_PARSERS: List[DescriptionParser] = [ From 46023c5daf7c21cd0e7abb73b041b37562295506 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 11:57:02 -0700 Subject: [PATCH 02/14] wip fet parser --- edg/abstract_parts/AbstractFets.py | 17 +++++++++++-- edg/jlcparts/JlcPartsFet.py | 34 ++++++++++++++----------- edg/parts/JlcFet.py | 41 +++++++++++++++++------------- 3 files changed, 57 insertions(+), 35 deletions(-) diff --git a/edg/abstract_parts/AbstractFets.py b/edg/abstract_parts/AbstractFets.py index b1043b76d..99575c727 100644 --- a/edg/abstract_parts/AbstractFets.py +++ b/edg/abstract_parts/AbstractFets.py @@ -1,7 +1,7 @@ from typing import Optional, Any, Dict from ..electronics_model import * -from .PartsTable import PartsTableColumn, PartsTableRow, PartsTable +from .PartsTable import PartsTableColumn, PartsTableRow, PartsTable, ExperimentalUserFnPartsTable from .PartsTablePart import PartsTableSelector from .Categories import * from .StandardFootprint import StandardFootprint, HasStandardFootprint @@ -88,12 +88,22 @@ def NFet(*args, **kwargs) -> 'Fet': def PFet(*args, **kwargs) -> 'Fet': return Fet(*args, **kwargs, channel='P') + @staticmethod + @ExperimentalUserFnPartsTable.user_fn([]) + def _sort_qgrdson(): + """Sort function that sorts by a common rule-of-thumb (but not always useful) MOSFET figure-of-merit, + Qg * Rds.""" + def sort_fn(row: PartsTableRow) -> float: + pass + return sort_fn + @init_in_parent def __init__(self, drain_voltage: RangeLike, drain_current: RangeLike, *, gate_voltage: RangeLike = (0, 0), gate_threshold_voltage: RangeLike = Range.all(), rds_on: RangeLike = Range.all(), gate_charge: RangeLike = Range.all(), power: RangeLike = Range.exact(0), - channel: StringLike = StringExpr()) -> None: + channel: StringLike = StringExpr(), + experimental_sort_fn: StringLike = "") -> None: super().__init__() self.source = self.Port(Passive.empty()) @@ -108,6 +118,7 @@ def __init__(self, drain_voltage: RangeLike, drain_current: RangeLike, *, self.gate_charge = self.ArgParameter(gate_charge) self.power = self.ArgParameter(power) self.channel = self.ArgParameter(channel) + self.experimental_sort_fn = self.ArgParameter(experimental_sort_fn) self.actual_drain_voltage_rating = self.Parameter(RangeExpr()) self.actual_drain_current_rating = self.Parameter(RangeExpr()) @@ -251,6 +262,8 @@ def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: new_cols[self.TOTAL_POWER] = new_cols[self.STATIC_POWER] + new_cols[self.SWITCHING_POWER] + return new_cols + if new_cols[self.TOTAL_POWER].fuzzy_in(row[self.POWER_RATING]): return new_cols else: diff --git a/edg/jlcparts/JlcPartsFet.py b/edg/jlcparts/JlcPartsFet.py index 44dc637ca..73ac75c36 100644 --- a/edg/jlcparts/JlcPartsFet.py +++ b/edg/jlcparts/JlcPartsFet.py @@ -1,3 +1,4 @@ +import math from typing import Any, Optional, Dict from ..abstract_parts import * from ..parts.JlcFet import JlcFet @@ -42,42 +43,45 @@ 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') 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 + gate_charge = float('nan') # placeholder for unspecified row_dict[cls.GATE_CHARGE] = Range.exact(gate_charge) return row_dict except (KeyError, TypeError, PartParserUtil.ParseError): return None - -class JlcPartsFet(PartsTableSelectorFootprint, JlcPartsBaseFet, TableFet): - pass - - -class JlcPartsSwitchFet(PartsTableSelectorFootprint, JlcPartsBaseFet, TableSwitchFet): @init_in_parent - def __init__(self, *args, manual_gate_charge: RangeLike = RangeExpr.ZERO, **kwargs): + def __init__(self, *args, fallback_gate_charge: RangeLike = Range.from_tolerance(3000e-9, 0), **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) + self.fallback_gate_charge = self.ArgParameter(fallback_gate_charge) + self.generator_param(self.fallback_gate_charge) def _table_postprocess(self, table: PartsTable) -> PartsTable: - manual_gate_charge = self.get(self.manual_gate_charge) + fallback_gate_charge = self.get(self.fallback_gate_charge) def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: - return {self.GATE_CHARGE: manual_gate_charge} + if math.isnan(row[self.GATE_CHARGE].lower): + return {self.GATE_CHARGE: fallback_gate_charge} + else: + return None # 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.map_new_columns(process_row, overwrite=True)) - return super()._table_postprocess(table) + +class JlcPartsFet(PartsTableSelectorFootprint, JlcPartsBaseFet, TableFet): + pass + + +class JlcPartsSwitchFet(PartsTableSelectorFootprint, JlcPartsBaseFet, TableSwitchFet): + pass lambda: JlcPartsFet(), JlcPartsSwitchFet() # ensure class is instantiable (non-abstract) diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index 74a5849a6..5a9af007f 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -64,7 +64,7 @@ class JlcBaseFet(JlcTableSelector): } DESCRIPTION_PARSERS: List[DescriptionParser] = [ - (re.compile("(\S+V) (\S+A) (\S+W) (\S+Ω)@(\S+V),\S+A (\S+V)@\S+A ([PN]) Channel .* MOSFETs.*"), + (re.compile("(\S+V) (\S+A) (\S+W) (\S+Ω)@(\S+V),\S+A (\S+V)@\S+A.* ([PN]) Channel.* MOSFETs.*"), lambda match: { TableFet.CHANNEL: match.group(7), TableFet.VDS_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(1), 'V')), @@ -76,10 +76,10 @@ class JlcBaseFet(JlcTableSelector): PartParserUtil.parse_value(match.group(5), 'V')), TableFet.RDS_ON: Range.exact(PartParserUtil.parse_value(match.group(4), 'Ω')), TableFet.POWER_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(3), 'W')), - TableFet.GATE_CHARGE: Range.zero_to_upper(3000e-9), # not specified, pessimistic upper bound + TableFet.GATE_CHARGE: Range.all(), # placeholder for unspecified }), # Some of them have the power entry later, for whatever reason - (re.compile("(\S+V) (\S+A) (\S+Ω)@(\S+V),\S+A (\S+W) (\S+V)@\S+A ([PN]) Channel .* MOSFETs.*"), + (re.compile("(\S+V) (\S+A) (\S+Ω)@(\S+V),\S+A (\S+W) (\S+V)@\S+A.* ([PN]) Channel.* (\S+C)@\S+V.* MOSFETs.*"), lambda match: { TableFet.CHANNEL: match.group(7), TableFet.VDS_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(1), 'V')), @@ -91,8 +91,9 @@ class JlcBaseFet(JlcTableSelector): PartParserUtil.parse_value(match.group(4), 'V')), TableFet.RDS_ON: Range.exact(PartParserUtil.parse_value(match.group(3), 'Ω')), TableFet.POWER_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(5), 'W')), - TableFet.GATE_CHARGE: Range.zero_to_upper(3000e-9), # not specified, pessimistic upper bound + TableFet.GATE_CHARGE: Range.exact(PartParserUtil.parse_value(match.group(8), 'C')), }), + ] @classmethod @@ -114,29 +115,33 @@ def parse_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: return cls._jlc_table().map_new_columns(parse_row) - -class JlcFet(PartsTableSelectorFootprint, JlcBaseFet, TableFet): - pass - - -class JlcSwitchFet(PartsTableSelectorFootprint, JlcBaseFet, TableSwitchFet): @init_in_parent - def __init__(self, *args, manual_gate_charge: RangeLike = RangeExpr.ZERO, **kwargs): + def __init__(self, *args, fallback_gate_charge: RangeLike = Range.from_tolerance(3000e-9, 0), **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) + self.fallback_gate_charge = self.ArgParameter(fallback_gate_charge) + self.generator_param(self.fallback_gate_charge) def _table_postprocess(self, table: PartsTable) -> PartsTable: - manual_gate_charge = self.get(self.manual_gate_charge) + fallback_gate_charge = self.get(self.fallback_gate_charge) def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: - return {self.GATE_CHARGE: manual_gate_charge} + if row[self.GATE_CHARGE] == Range.all(): + print("fallback gate charge for part", row[self.PART_NUMBER_COL]) + return {self.GATE_CHARGE: fallback_gate_charge} + else: + print(f"ok Qg part {row[self.PART_NUMBER_COL]} = {row[self.GATE_CHARGE]}") + return None # 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.map_new_columns(process_row, overwrite=True)) - return super()._table_postprocess(table) + +class JlcFet(PartsTableSelectorFootprint, JlcBaseFet, TableFet): + pass + + +class JlcSwitchFet(PartsTableSelectorFootprint, JlcBaseFet, TableSwitchFet): + pass lambda: JlcFet, JlcSwitchFet() # ensure class is instantiable (non-abstract) From 116135a0a74e242a80b926ccbf0caad33280ed7c Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 13:08:10 -0700 Subject: [PATCH 03/14] wip --- edg/abstract_parts/PartsTable.py | 5 ++++- edg/jlcparts/JlcPartsFet.py | 2 +- edg/parts/JlcFet.py | 24 ++++++++++++++++++------ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/edg/abstract_parts/PartsTable.py b/edg/abstract_parts/PartsTable.py index 8fecfac3d..211097c86 100644 --- a/edg/abstract_parts/PartsTable.py +++ b/edg/abstract_parts/PartsTable.py @@ -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: diff --git a/edg/jlcparts/JlcPartsFet.py b/edg/jlcparts/JlcPartsFet.py index 73ac75c36..b1e597778 100644 --- a/edg/jlcparts/JlcPartsFet.py +++ b/edg/jlcparts/JlcPartsFet.py @@ -70,7 +70,7 @@ def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: if math.isnan(row[self.GATE_CHARGE].lower): return {self.GATE_CHARGE: fallback_gate_charge} else: - return None + return {self.GATE_CHARGE: row[self.GATE_CHARGE]} # must run before TableFet power calculations return super()._table_postprocess(table.map_new_columns(process_row, overwrite=True)) diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index 5a9af007f..8709fc48e 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -78,7 +78,7 @@ class JlcBaseFet(JlcTableSelector): TableFet.POWER_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(3), 'W')), TableFet.GATE_CHARGE: Range.all(), # placeholder for unspecified }), - # Some of them have the power entry later, for whatever reason + # some are more detailed (re.compile("(\S+V) (\S+A) (\S+Ω)@(\S+V),\S+A (\S+W) (\S+V)@\S+A.* ([PN]) Channel.* (\S+C)@\S+V.* MOSFETs.*"), lambda match: { TableFet.CHANNEL: match.group(7), @@ -91,9 +91,23 @@ class JlcBaseFet(JlcTableSelector): PartParserUtil.parse_value(match.group(4), 'V')), TableFet.RDS_ON: Range.exact(PartParserUtil.parse_value(match.group(3), 'Ω')), TableFet.POWER_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(5), 'W')), - TableFet.GATE_CHARGE: Range.exact(PartParserUtil.parse_value(match.group(8), 'C')), + TableFet.GATE_CHARGE: Range.exact(PartParserUtil.parse_value(match.group(8), 'C')) + }), + # many still don't have the gate charge + (re.compile("(\S+V) (\S+A) (\S+Ω)@(\S+V),\S+A (\S+W) (\S+V)@\S+A.* ([PN]) Channel.* MOSFETs.*"), + lambda match: { + TableFet.CHANNEL: match.group(7), + TableFet.VDS_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(1), 'V')), + TableFet.IDS_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(2), 'A')), + # Vgs isn't specified, so the Ron@Vgs is used as a lower bound; assumed symmetric + TableFet.VGS_RATING: Range.from_abs_tolerance(0, + PartParserUtil.parse_value(match.group(4), 'V')), + TableFet.VGS_DRIVE: Range(PartParserUtil.parse_value(match.group(6), 'V'), + PartParserUtil.parse_value(match.group(4), 'V')), + TableFet.RDS_ON: Range.exact(PartParserUtil.parse_value(match.group(3), 'Ω')), + TableFet.POWER_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(5), 'W')), + TableFet.GATE_CHARGE: Range.all(), # placeholder for unspecified }), - ] @classmethod @@ -126,11 +140,9 @@ def _table_postprocess(self, table: PartsTable) -> PartsTable: fallback_gate_charge = self.get(self.fallback_gate_charge) def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: if row[self.GATE_CHARGE] == Range.all(): - print("fallback gate charge for part", row[self.PART_NUMBER_COL]) return {self.GATE_CHARGE: fallback_gate_charge} else: - print(f"ok Qg part {row[self.PART_NUMBER_COL]} = {row[self.GATE_CHARGE]}") - return None + return {self.GATE_CHARGE: row[self.GATE_CHARGE]} # must run before TableFet power calculations return super()._table_postprocess(table.map_new_columns(process_row, overwrite=True)) From 02e5b8f604b306a80e65711174ed0976df7fb393 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 13:12:12 -0700 Subject: [PATCH 04/14] Update AbstractFets.py --- edg/abstract_parts/AbstractFets.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/edg/abstract_parts/AbstractFets.py b/edg/abstract_parts/AbstractFets.py index 99575c727..6e99907c1 100644 --- a/edg/abstract_parts/AbstractFets.py +++ b/edg/abstract_parts/AbstractFets.py @@ -262,8 +262,6 @@ def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: new_cols[self.TOTAL_POWER] = new_cols[self.STATIC_POWER] + new_cols[self.SWITCHING_POWER] - return new_cols - if new_cols[self.TOTAL_POWER].fuzzy_in(row[self.POWER_RATING]): return new_cols else: From 092ce9c8bfe5f31705a2389a022b1117e6317254 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 13:26:08 -0700 Subject: [PATCH 05/14] supplemental Qc --- edg/parts/JlcFet.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index 8709fc48e..b637b5679 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -110,6 +110,17 @@ class JlcBaseFet(JlcTableSelector): }), ] + SUPPLEMENTAL_QC = { # mfr part number to typ Qc @ max Vgs (if multiple specified) + 'IRFH7440TRPBF': 92e-9, # @ Vgs=10 + 'BSC028N06NSATMA1': 37e-9, # @ Vgs=0...10V + 'BSC057N08NS3G': 42-9, # @ Vgs=0...10V + 'BSC093N04LSG': 18e-9, # @ Vgs=0...10V + 'BSC160N10NS3G': 19e-9, # @ Vgs=0...10V + 'SIR876ADP-T1-GE3': 32.8e-9, # @ Vgs=10 + 'SI7336ADP-T1-E3': 36e-9, # @ Vgs=4.5 + 'SIR470DP-T1-GE3': 102e-9, # @ Vgs=10 + } + @classmethod def _make_table(cls) -> PartsTable: def parse_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: @@ -123,6 +134,9 @@ def parse_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: if new_cols is None: return None + if new_cols[cls.GATE_CHARGE] == Range.all() and row[cls.PART_NUMBER_COL] in cls.SUPPLEMENTAL_QC: + new_cols[cls.GATE_CHARGE] = Range.exact(cls.SUPPLEMENTAL_QC[row[cls.PART_NUMBER_COL]]) + new_cols[cls.KICAD_FOOTPRINT] = footprint new_cols.update(cls._parse_jlcpcb_common(row)) return new_cols From c09942a1f8d635d294611b7a360898a66d854cd6 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 13:35:29 -0700 Subject: [PATCH 06/14] Update JlcFet.py --- edg/parts/JlcFet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index b637b5679..c999406fd 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -113,7 +113,7 @@ class JlcBaseFet(JlcTableSelector): SUPPLEMENTAL_QC = { # mfr part number to typ Qc @ max Vgs (if multiple specified) 'IRFH7440TRPBF': 92e-9, # @ Vgs=10 'BSC028N06NSATMA1': 37e-9, # @ Vgs=0...10V - 'BSC057N08NS3G': 42-9, # @ Vgs=0...10V + 'BSC057N08NS3G': 42e-9, # @ Vgs=0...10V 'BSC093N04LSG': 18e-9, # @ Vgs=0...10V 'BSC160N10NS3G': 19e-9, # @ Vgs=0...10V 'SIR876ADP-T1-GE3': 32.8e-9, # @ Vgs=10 From 5f3ac6bf4a348dcedf7eda4f2450cbc3d7195b99 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 13:38:48 -0700 Subject: [PATCH 07/14] Update AbstractFets.py --- edg/abstract_parts/AbstractFets.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/edg/abstract_parts/AbstractFets.py b/edg/abstract_parts/AbstractFets.py index 6e99907c1..b1043b76d 100644 --- a/edg/abstract_parts/AbstractFets.py +++ b/edg/abstract_parts/AbstractFets.py @@ -1,7 +1,7 @@ from typing import Optional, Any, Dict from ..electronics_model import * -from .PartsTable import PartsTableColumn, PartsTableRow, PartsTable, ExperimentalUserFnPartsTable +from .PartsTable import PartsTableColumn, PartsTableRow, PartsTable from .PartsTablePart import PartsTableSelector from .Categories import * from .StandardFootprint import StandardFootprint, HasStandardFootprint @@ -88,22 +88,12 @@ def NFet(*args, **kwargs) -> 'Fet': def PFet(*args, **kwargs) -> 'Fet': return Fet(*args, **kwargs, channel='P') - @staticmethod - @ExperimentalUserFnPartsTable.user_fn([]) - def _sort_qgrdson(): - """Sort function that sorts by a common rule-of-thumb (but not always useful) MOSFET figure-of-merit, - Qg * Rds.""" - def sort_fn(row: PartsTableRow) -> float: - pass - return sort_fn - @init_in_parent def __init__(self, drain_voltage: RangeLike, drain_current: RangeLike, *, gate_voltage: RangeLike = (0, 0), gate_threshold_voltage: RangeLike = Range.all(), rds_on: RangeLike = Range.all(), gate_charge: RangeLike = Range.all(), power: RangeLike = Range.exact(0), - channel: StringLike = StringExpr(), - experimental_sort_fn: StringLike = "") -> None: + channel: StringLike = StringExpr()) -> None: super().__init__() self.source = self.Port(Passive.empty()) @@ -118,7 +108,6 @@ def __init__(self, drain_voltage: RangeLike, drain_current: RangeLike, *, self.gate_charge = self.ArgParameter(gate_charge) self.power = self.ArgParameter(power) self.channel = self.ArgParameter(channel) - self.experimental_sort_fn = self.ArgParameter(experimental_sort_fn) self.actual_drain_voltage_rating = self.Parameter(RangeExpr()) self.actual_drain_current_rating = self.Parameter(RangeExpr()) From 9380a8ebd369639933a06887fa870143aee42794 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 13:39:57 -0700 Subject: [PATCH 08/14] fix types --- edg/parts/JlcFet.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index c999406fd..aa6c4de92 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -134,8 +134,8 @@ def parse_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: if new_cols is None: return None - if new_cols[cls.GATE_CHARGE] == Range.all() and row[cls.PART_NUMBER_COL] in cls.SUPPLEMENTAL_QC: - new_cols[cls.GATE_CHARGE] = Range.exact(cls.SUPPLEMENTAL_QC[row[cls.PART_NUMBER_COL]]) + if new_cols[TableFet.GATE_CHARGE] == Range.all() and row[cls.PART_NUMBER_COL] in cls.SUPPLEMENTAL_QC: + new_cols[TableFet.GATE_CHARGE] = Range.exact(cls.SUPPLEMENTAL_QC[row[cls.PART_NUMBER_COL]]) new_cols[cls.KICAD_FOOTPRINT] = footprint new_cols.update(cls._parse_jlcpcb_common(row)) @@ -153,10 +153,10 @@ def __init__(self, *args, fallback_gate_charge: RangeLike = Range.from_tolerance def _table_postprocess(self, table: PartsTable) -> PartsTable: fallback_gate_charge = self.get(self.fallback_gate_charge) def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: - if row[self.GATE_CHARGE] == Range.all(): - return {self.GATE_CHARGE: fallback_gate_charge} + if row[TableFet.GATE_CHARGE] == Range.all(): + return {TableFet.GATE_CHARGE: fallback_gate_charge} else: - return {self.GATE_CHARGE: row[self.GATE_CHARGE]} + return {TableFet.GATE_CHARGE: row[TableFet.GATE_CHARGE]} # must run before TableFet power calculations return super()._table_postprocess(table.map_new_columns(process_row, overwrite=True)) From ed4fff2a98dd07d0f6995e2ef383802358684138 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 14:26:57 -0700 Subject: [PATCH 09/14] cleanup gate charge handling --- edg/jlcparts/JlcPartsFet.py | 28 +++++----------------- edg/parts/JlcFet.py | 46 +++++++++++++++++++++---------------- 2 files changed, 32 insertions(+), 42 deletions(-) diff --git a/edg/jlcparts/JlcPartsFet.py b/edg/jlcparts/JlcPartsFet.py index b1e597778..e3284c90d 100644 --- a/edg/jlcparts/JlcPartsFet.py +++ b/edg/jlcparts/JlcPartsFet.py @@ -1,4 +1,3 @@ -import math from typing import Any, Optional, Dict from ..abstract_parts import * from ..parts.JlcFet import JlcFet @@ -45,36 +44,21 @@ def _entry_to_table_row(cls, row_dict: Dict[PartsTableColumn, Any], filename: st 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 = float('nan') # placeholder for unspecified - 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): return None - @init_in_parent - def __init__(self, *args, fallback_gate_charge: RangeLike = Range.from_tolerance(3000e-9, 0), **kwargs): - super().__init__(*args, **kwargs) - # allow the user to specify a gate charge - self.fallback_gate_charge = self.ArgParameter(fallback_gate_charge) - self.generator_param(self.fallback_gate_charge) - - def _table_postprocess(self, table: PartsTable) -> PartsTable: - fallback_gate_charge = self.get(self.fallback_gate_charge) - def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: - if math.isnan(row[self.GATE_CHARGE].lower): - return {self.GATE_CHARGE: fallback_gate_charge} - else: - return {self.GATE_CHARGE: row[self.GATE_CHARGE]} - - # must run before TableFet power calculations - return super()._table_postprocess(table.map_new_columns(process_row, overwrite=True)) - class JlcPartsFet(PartsTableSelectorFootprint, JlcPartsBaseFet, TableFet): pass diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index aa6c4de92..fc8dc46ce 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -5,6 +5,30 @@ from .JlcPart import DescriptionParser, JlcTableSelector +@non_library +class FetFallbackGateCharge(BaseTableFet): + """A TableFet that allows a fallback gate charge if not specified in the table. + Unspecified entries must be parsed as Range.all(), which will be substituted with the fallback + value in per-Block post-processing.""" + @init_in_parent + def __init__(self, *args, fallback_gate_charge: RangeLike = Range.from_tolerance(3000e-9, 0), **kwargs): + super().__init__(*args, **kwargs) + # allow the user to specify a gate charge + self.fallback_gate_charge = self.ArgParameter(fallback_gate_charge) + self.generator_param(self.fallback_gate_charge) + + def _table_postprocess(self, table: PartsTable) -> PartsTable: + fallback_gate_charge = self.get(self.fallback_gate_charge) + def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: + if row[TableFet.GATE_CHARGE] == Range.all(): + return {TableFet.GATE_CHARGE: fallback_gate_charge} + else: + return {TableFet.GATE_CHARGE: row[TableFet.GATE_CHARGE]} + + # must run before TableFet power calculations + return super()._table_postprocess(table.map_new_columns(process_row, overwrite=True)) + + class JlcBaseFet(JlcTableSelector): PACKAGE_FOOTPRINT_MAP = { 'SOT-23': 'Package_TO_SOT_SMD:SOT-23', @@ -143,30 +167,12 @@ def parse_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: return cls._jlc_table().map_new_columns(parse_row) - @init_in_parent - def __init__(self, *args, fallback_gate_charge: RangeLike = Range.from_tolerance(3000e-9, 0), **kwargs): - super().__init__(*args, **kwargs) - # allow the user to specify a gate charge - self.fallback_gate_charge = self.ArgParameter(fallback_gate_charge) - self.generator_param(self.fallback_gate_charge) - - def _table_postprocess(self, table: PartsTable) -> PartsTable: - fallback_gate_charge = self.get(self.fallback_gate_charge) - def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]: - if row[TableFet.GATE_CHARGE] == Range.all(): - return {TableFet.GATE_CHARGE: fallback_gate_charge} - else: - return {TableFet.GATE_CHARGE: row[TableFet.GATE_CHARGE]} - - # must run before TableFet power calculations - return super()._table_postprocess(table.map_new_columns(process_row, overwrite=True)) - -class JlcFet(PartsTableSelectorFootprint, JlcBaseFet, TableFet): +class JlcFet(PartsTableSelectorFootprint, JlcBaseFet, FetFallbackGateCharge, TableFet): pass -class JlcSwitchFet(PartsTableSelectorFootprint, JlcBaseFet, TableSwitchFet): +class JlcSwitchFet(PartsTableSelectorFootprint, JlcBaseFet, FetFallbackGateCharge, TableSwitchFet): pass From 5cc925c739d0fe1a2f555f1c187efbad75abb2e5 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 14:36:07 -0700 Subject: [PATCH 10/14] wip tables --- edg/parts/JlcFet.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index fc8dc46ce..6d8c1d496 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -135,6 +135,7 @@ class JlcBaseFet(JlcTableSelector): ] SUPPLEMENTAL_QC = { # mfr part number to typ Qc @ max Vgs (if multiple specified) + # QFN-8 devices 'IRFH7440TRPBF': 92e-9, # @ Vgs=10 'BSC028N06NSATMA1': 37e-9, # @ Vgs=0...10V 'BSC057N08NS3G': 42e-9, # @ Vgs=0...10V @@ -143,6 +144,13 @@ class JlcBaseFet(JlcTableSelector): 'SIR876ADP-T1-GE3': 32.8e-9, # @ Vgs=10 'SI7336ADP-T1-E3': 36e-9, # @ Vgs=4.5 'SIR470DP-T1-GE3': 102e-9, # @ Vgs=10 + + # SOIC-8 devices, top 5 stock in the static parts table + 'AO4406A': 14e-9, # @ Vgs=10 + 'IRF8313TRPBF': 6.0e-9, # @ Vgs=4.5 + 'AO4435': 18e-9, # @ Vgs=-10 + 'AO4419': 19e-9, # @ Vgs=4.5 + 'AO4264E': 14.5e-9, # @ Vgs=10 } @classmethod From a9d428fe299a7652ce5b81910d2ba1715f3199d1 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 15:23:35 -0700 Subject: [PATCH 11/14] Clear unmodeled parameters --- edg/abstract_parts/DigitalAmplifiers.py | 4 ---- edg/abstract_parts/test_resistor_generic.py | 2 -- edg/electronics_model/DigitalPorts.py | 4 ++-- edg/electronics_model/GroundPort.py | 1 - edg/electronics_model/VoltagePorts.py | 1 - edg/parts/AdcSpi_Mcp3201.py | 1 - edg/parts/BatteryProtector_S8261A.py | 4 ---- edg/parts/CanTransceiver_Iso1050.py | 1 - edg/parts/CanTransceiver_Sn65hvd230.py | 1 - edg/parts/DacSpi_Mcp4901.py | 2 -- edg/parts/EInk_E2154fs091.py | 2 +- edg/parts/Fpga_Ice40up.py | 1 - edg/parts/Fusb302b.py | 1 - edg/parts/Jacdac.py | 3 +-- edg/parts/JlcFet.py | 11 +++++++---- edg/parts/Microcontroller_Esp32.py | 2 -- edg/parts/Microcontroller_Esp32c3.py | 1 - edg/parts/Microcontroller_Lpc1549.py | 3 --- edg/parts/Microcontroller_Stm32f103.py | 7 +++---- edg/parts/Microcontroller_Stm32f303.py | 12 ++++++------ edg/parts/Microcontroller_Stm32g431.py | 2 +- edg/parts/Microcontroller_nRF52840.py | 1 - edg/parts/MotorDriver_Drv8870.py | 1 - edg/parts/Opamp_Lmv321.py | 1 - edg/parts/Opamp_Mcp6001.py | 1 - edg/parts/PowerConditioning.py | 13 +++---------- edg/parts/RfModules.py | 3 --- edg/parts/Rtc_Pcf2129.py | 1 - 28 files changed, 24 insertions(+), 63 deletions(-) diff --git a/edg/abstract_parts/DigitalAmplifiers.py b/edg/abstract_parts/DigitalAmplifiers.py index 4220b9837..bf5617002 100644 --- a/edg/abstract_parts/DigitalAmplifiers.py +++ b/edg/abstract_parts/DigitalAmplifiers.py @@ -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 )) @@ -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 diff --git a/edg/abstract_parts/test_resistor_generic.py b/edg/abstract_parts/test_resistor_generic.py index 5a75c0c74..6e372ed16 100644 --- a/edg/abstract_parts/test_resistor_generic.py +++ b/edg/abstract_parts/test_resistor_generic.py @@ -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())) @@ -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())) diff --git a/edg/electronics_model/DigitalPorts.py b/edg/electronics_model/DigitalPorts.py index fa95a9b2f..4b9aca5de 100644 --- a/edg/electronics_model/DigitalPorts.py +++ b/edg/electronics_model/DigitalPorts.py @@ -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) diff --git a/edg/electronics_model/GroundPort.py b/edg/electronics_model/GroundPort.py index 1d35640d0..114750333 100644 --- a/edg/electronics_model/GroundPort.py +++ b/edg/electronics_model/GroundPort.py @@ -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, )) diff --git a/edg/electronics_model/VoltagePorts.py b/edg/electronics_model/VoltagePorts.py index 48b921886..175c197dc 100644 --- a/edg/electronics_model/VoltagePorts.py +++ b/edg/electronics_model/VoltagePorts.py @@ -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( diff --git a/edg/parts/AdcSpi_Mcp3201.py b/edg/parts/AdcSpi_Mcp3201.py index 9ac05ccf4..3c35373b1 100644 --- a/edg/parts/AdcSpi_Mcp3201.py +++ b/edg/parts/AdcSpi_Mcp3201.py @@ -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 diff --git a/edg/parts/BatteryProtector_S8261A.py b/edg/parts/BatteryProtector_S8261A.py index ba6b2368d..974f74c0b 100644 --- a/edg/parts/BatteryProtector_S8261A.py +++ b/edg/parts/BatteryProtector_S8261A.py @@ -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 )) diff --git a/edg/parts/CanTransceiver_Iso1050.py b/edg/parts/CanTransceiver_Iso1050.py index ccabc78fb..5f1d0626b 100644 --- a/edg/parts/CanTransceiver_Iso1050.py +++ b/edg/parts/CanTransceiver_Iso1050.py @@ -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()) diff --git a/edg/parts/CanTransceiver_Sn65hvd230.py b/edg/parts/CanTransceiver_Sn65hvd230.py index 286ea0ea5..697598aec 100644 --- a/edg/parts/CanTransceiver_Sn65hvd230.py +++ b/edg/parts/CanTransceiver_Sn65hvd230.py @@ -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()) diff --git a/edg/parts/DacSpi_Mcp4901.py b/edg/parts/DacSpi_Mcp4901.py index 18a578a68..2ba2c9af0 100644 --- a/edg/parts/DacSpi_Mcp4901.py +++ b/edg/parts/DacSpi_Mcp4901.py @@ -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, @@ -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) ) diff --git a/edg/parts/EInk_E2154fs091.py b/edg/parts/EInk_E2154fs091.py index a2a38f1bc..98c8e559d 100644 --- a/edg/parts/EInk_E2154fs091.py +++ b/edg/parts/EInk_E2154fs091.py @@ -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( diff --git a/edg/parts/Fpga_Ice40up.py b/edg/parts/Fpga_Ice40up.py index 61a655bf0..f036d87d4 100644 --- a/edg/parts/Fpga_Ice40up.py +++ b/edg/parts/Fpga_Ice40up.py @@ -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, ) diff --git a/edg/parts/Fusb302b.py b/edg/parts/Fusb302b.py index a771df769..d337d4ce0 100644 --- a/edg/parts/Fusb302b.py +++ b/edg/parts/Fusb302b.py @@ -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, diff --git a/edg/parts/Jacdac.py b/edg/parts/Jacdac.py index e01c475de..b3e2dc230 100644 --- a/edg/parts/Jacdac.py +++ b/edg/parts/Jacdac.py @@ -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) diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index 6d8c1d496..3e737f212 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -6,9 +6,9 @@ @non_library -class FetFallbackGateCharge(BaseTableFet): +class FetFallbackGateCharge(PartsTableSelector, BaseTableFet): """A TableFet that allows a fallback gate charge if not specified in the table. - Unspecified entries must be parsed as Range.all(), which will be substituted with the fallback + Unspecified entries must be Range.all(), which will be substituted with the fallback value in per-Block post-processing.""" @init_in_parent def __init__(self, *args, fallback_gate_charge: RangeLike = Range.from_tolerance(3000e-9, 0), **kwargs): @@ -100,7 +100,7 @@ class JlcBaseFet(JlcTableSelector): PartParserUtil.parse_value(match.group(5), 'V')), TableFet.RDS_ON: Range.exact(PartParserUtil.parse_value(match.group(4), 'Ω')), TableFet.POWER_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(3), 'W')), - TableFet.GATE_CHARGE: Range.all(), # placeholder for unspecified + TableFet.GATE_CHARGE: Range.all(), # unspecified }), # some are more detailed (re.compile("(\S+V) (\S+A) (\S+Ω)@(\S+V),\S+A (\S+W) (\S+V)@\S+A.* ([PN]) Channel.* (\S+C)@\S+V.* MOSFETs.*"), @@ -130,7 +130,7 @@ class JlcBaseFet(JlcTableSelector): PartParserUtil.parse_value(match.group(4), 'V')), TableFet.RDS_ON: Range.exact(PartParserUtil.parse_value(match.group(3), 'Ω')), TableFet.POWER_RATING: Range.zero_to_upper(PartParserUtil.parse_value(match.group(5), 'W')), - TableFet.GATE_CHARGE: Range.all(), # placeholder for unspecified + TableFet.GATE_CHARGE: Range.all(), # unspecified }), ] @@ -151,6 +151,9 @@ class JlcBaseFet(JlcTableSelector): 'AO4435': 18e-9, # @ Vgs=-10 'AO4419': 19e-9, # @ Vgs=4.5 'AO4264E': 14.5e-9, # @ Vgs=10 + 'AO4485': 42e-9, # @ Vgs=10 + 'AO4459': 9.2e-9, # @ Vgs=10 + 'AO4468': 15e-9, # @ Vgs=10 } @classmethod diff --git a/edg/parts/Microcontroller_Esp32.py b/edg/parts/Microcontroller_Esp32.py index ef665d6e7..3cb1b88a7 100644 --- a/edg/parts/Microcontroller_Esp32.py +++ b/edg/parts/Microcontroller_Esp32.py @@ -33,7 +33,6 @@ def _dio_model(self, pwr: Port[VoltageLink]) -> DigitalBidir: self.gnd, pwr, voltage_limit_tolerance=(-0.3, 0.3) * Volt, current_limits=(-28, 40)*mAmp, - current_draw=(0, 0)*Amp, input_threshold_factor=(0.25, 0.75), pullup_capable=True, pulldown_capable=True, ) @@ -45,7 +44,6 @@ def _io_pinmap(self) -> PinMapUtil: self.gnd, pwr, voltage_limit_tolerance=(-0.3, 0.3) * Volt, current_limits=(-28, 20)*mAmp, # reduced sourcing capability - current_draw=(0, 0)*Amp, input_threshold_factor=(0.25, 0.75), pullup_capable=True, pulldown_capable=True, ) diff --git a/edg/parts/Microcontroller_Esp32c3.py b/edg/parts/Microcontroller_Esp32c3.py index df17f9542..11a6319a1 100644 --- a/edg/parts/Microcontroller_Esp32c3.py +++ b/edg/parts/Microcontroller_Esp32c3.py @@ -33,7 +33,6 @@ def _dio_model(self, pwr: Port[VoltageLink]) -> DigitalBidir: self.gnd, pwr, voltage_limit_tolerance=(-0.3, 0.3)*Volt, current_limits=(-28, 40)*mAmp, - current_draw=(0, 0)*Amp, input_threshold_factor=(0.25, 0.75), pullup_capable=True, pulldown_capable=True, ) diff --git a/edg/parts/Microcontroller_Lpc1549.py b/edg/parts/Microcontroller_Lpc1549.py index 12212bf67..fad6c4473 100644 --- a/edg/parts/Microcontroller_Lpc1549.py +++ b/edg/parts/Microcontroller_Lpc1549.py @@ -61,7 +61,6 @@ def _io_pinmap(self) -> PinMapUtil: dio_5v_model = DigitalBidir.from_supply( self.gnd, self.pwr, voltage_limit_abs=(0, 5) * Volt, - current_draw=(0, 0) * Amp, current_limits=(-50, 45) * mAmp, input_threshold_factor=(0.3, 0.7), pullup_capable=True, pulldown_capable=True @@ -69,7 +68,6 @@ def _io_pinmap(self) -> PinMapUtil: dio_non5v_model = DigitalBidir.from_supply( # only used when overlapped w/ DAC PIO0_12 self.gnd, self.pwr, # up to VddA voltage_limit_tolerance=(0, 0) * Volt, - current_draw=(0, 0) * Amp, current_limits=(-50, 45) * mAmp, input_threshold_factor=(0.3, 0.7), pullup_capable=True, pulldown_capable=True @@ -77,7 +75,6 @@ def _io_pinmap(self) -> PinMapUtil: dio_highcurrrent_model = DigitalBidir.from_supply( # only used for PIO0_24 self.gnd, self.pwr, voltage_limit_abs=(0, 5) * Volt, - current_draw=(0, 0) * Amp, current_limits=(-50, 20) * mAmp, # TODO: 12mA when Vdd < 2.7V input_threshold_factor=(0.3, 0.7), pullup_capable=True, pulldown_capable=True diff --git a/edg/parts/Microcontroller_Stm32f103.py b/edg/parts/Microcontroller_Stm32f103.py index 74deddc53..24874906a 100644 --- a/edg/parts/Microcontroller_Stm32f103.py +++ b/edg/parts/Microcontroller_Stm32f103.py @@ -28,7 +28,6 @@ def __init__(self, **kwargs) -> None: self.nrst = self.Port(DigitalSink.from_supply( self.gnd, self.pwr, voltage_limit_tolerance=(-0.3, 0.3)*Volt, # Table 5.3.1, general operating conditions TODO: FT IO, BOOT0 IO - current_draw=(0, 0)*Amp, input_threshold_abs=(0.8, 2)*Volt, pullup_capable=True ), optional=True) # note, internal pull-up resistor, 30-50 kOhm by Table 35 @@ -62,21 +61,21 @@ def _io_pinmap(self) -> PinMapUtil: dio_ft_model = DigitalBidir.from_supply( self.gnd, self.pwr, voltage_limit_abs=(-0.3, 5.2) * Volt, # Table 5.3.1, general operating conditions, TODO relaxed for Vdd>2v - current_draw=(0, 0)*Amp, current_limits=(-20, 20)*mAmp, # Section 5.3.13 Output driving current, TODO loose with relaxed VOL/VOH + current_limits=(-20, 20)*mAmp, # Section 5.3.13 Output driving current, TODO loose with relaxed VOL/VOH input_threshold_factor=(0.35, 0.65), # TODO relaxed (but more complex) bounds available pullup_capable=True, pulldown_capable=True ) dio_std_model = DigitalBidir.from_supply( self.gnd, self.pwr, voltage_limit_tolerance=(-0.3, 0.3)*Volt, # Table 5.3.1, general operating conditions - current_draw=(0, 0)*Amp, current_limits=(-20, 20)*mAmp, # Section 5.3.13 Output driving current, TODO loose with relaxed VOL/VOH + current_limits=(-20, 20)*mAmp, # Section 5.3.13 Output driving current, TODO loose with relaxed VOL/VOH input_threshold_factor=(0.35, 0.65), # TODO relaxed (but more complex) bounds available pullup_capable=True, pulldown_capable=True, ) dio_pc_13_14_15_model = DigitalBidir.from_supply( self.gnd, self.pwr, voltage_limit_tolerance=(-0.3, 0.3)*Volt, # Table 5.3.1, general operating conditions - current_draw=(0, 0)*Amp, current_limits=(-3, 3)*mAmp, # Section 5.3.13 Output driving current + current_limits=(-3, 3)*mAmp, # Section 5.3.13 Output driving current input_threshold_factor=(0.35, 0.65), # TODO relaxed (but more complex) bounds available pullup_capable=True, pulldown_capable=True, ) diff --git a/edg/parts/Microcontroller_Stm32f303.py b/edg/parts/Microcontroller_Stm32f303.py index a5846ff14..0e78f7974 100644 --- a/edg/parts/Microcontroller_Stm32f303.py +++ b/edg/parts/Microcontroller_Stm32f303.py @@ -33,35 +33,35 @@ def _io_pinmap(self) -> PinMapUtil: dio_tc_model = DigitalBidir.from_supply( self.gnd, vdd, voltage_limit_abs=(-0.3, 0.3) * Volt, # Table 19 - current_draw=(0, 0)*Amp, current_limits=current_limits, + current_limits=current_limits, input_threshold_factor=input_threshold_factor, pullup_capable=True, pulldown_capable=True ) dio_tc_switch_model = DigitalBidir.from_supply( self.gnd, vdd, voltage_limit_abs=(-0.3, 0.3) * Volt, # Table 19 - current_draw=(0, 0)*Amp, current_limits=(-3, 0), # Table 13, note 1, can sink 3 mA and should not source current + current_limits=(-3, 0), # Table 13, note 1, can sink 3 mA and should not source current input_threshold_factor=input_threshold_factor, pullup_capable=True, pulldown_capable=True ) dio_tt_model = DigitalBidir.from_supply( self.gnd, vdd, voltage_limit_abs=(-0.3, 3.6) * Volt, # Table 19 - current_draw=(0, 0)*Amp, current_limits=current_limits, + current_limits=current_limits, input_threshold_factor=input_threshold_factor, pullup_capable=True, pulldown_capable=True ) dio_tta_model = DigitalBidir.from_supply( self.gnd, vdd, voltage_limit_abs=(-0.3 * Volt, vdda.link().voltage.lower() + 0.3 * Volt), # Table 19 - current_draw=(0, 0)*Amp, current_limits=current_limits, + current_limits=current_limits, input_threshold_factor=input_threshold_factor, pullup_capable=True, pulldown_capable=True ) dio_ft_model = DigitalBidir.from_supply( self.gnd, vdd, voltage_limit_abs=(-0.3, 5.5) * Volt, # Table 19 - current_draw=(0, 0)*Amp, current_limits=current_limits, + current_limits=current_limits, input_threshold_factor=input_threshold_factor, pullup_capable=True, pulldown_capable=True ) @@ -69,7 +69,7 @@ def _io_pinmap(self) -> PinMapUtil: dio_boot0_model = DigitalBidir.from_supply( self.gnd, vdd, voltage_limit_abs=(-0.3, 5.5) * Volt, # Table 19 - current_draw=(0, 0)*Amp, current_limits=current_limits, + current_limits=current_limits, input_threshold_factor=input_threshold_factor, pullup_capable=True, pulldown_capable=True ) diff --git a/edg/parts/Microcontroller_Stm32g431.py b/edg/parts/Microcontroller_Stm32g431.py index dc430e415..f429cad50 100644 --- a/edg/parts/Microcontroller_Stm32g431.py +++ b/edg/parts/Microcontroller_Stm32g431.py @@ -58,7 +58,7 @@ def _io_pinmap(self) -> PinMapUtil: dio_tt_model = DigitalBidir.from_supply( self.gnd, self.pwr, voltage_limit_tolerance=(-0.3, 0.3) * Volt, # Table 19 - current_draw=(0, 0) * Amp, current_limits=current_limits, + current_limits=current_limits, input_threshold_factor=input_threshold_factor, pullup_capable=True, pulldown_capable=True ) diff --git a/edg/parts/Microcontroller_nRF52840.py b/edg/parts/Microcontroller_nRF52840.py index 1a74b481f..44bd589f1 100644 --- a/edg/parts/Microcontroller_nRF52840.py +++ b/edg/parts/Microcontroller_nRF52840.py @@ -32,7 +32,6 @@ def _dio_model(self, pwr: Port[VoltageLink]) -> DigitalBidir: self.gnd, pwr, voltage_limit_tolerance=(-0.3, 0.3) * Volt, current_limits=(-6, 6)*mAmp, # minimum current, high drive, Vdd>2.7 - current_draw=(0, 0)*Amp, input_threshold_factor=(0.3, 0.7), pullup_capable=True, pulldown_capable=True, ) diff --git a/edg/parts/MotorDriver_Drv8870.py b/edg/parts/MotorDriver_Drv8870.py index 5b39ab5a1..b6602fb6a 100644 --- a/edg/parts/MotorDriver_Drv8870.py +++ b/edg/parts/MotorDriver_Drv8870.py @@ -13,7 +13,6 @@ def __init__(self) -> None: self.vref = self.Port(VoltageSink.from_gnd( self.gnd, voltage_limits=(0.3, 5)*Volt, # operational from 0-0.3v, but degraded accuracy - current_draw=(0, 0)*Amp )) din_model = DigitalSink.from_supply( diff --git a/edg/parts/Opamp_Lmv321.py b/edg/parts/Opamp_Lmv321.py index 11b7489c1..7ca13a946 100644 --- a/edg/parts/Opamp_Lmv321.py +++ b/edg/parts/Opamp_Lmv321.py @@ -14,7 +14,6 @@ def __init__(self): self.vss, self.vcc, voltage_limit_abs=(-0.2, 5.7), signal_limit_bound=(0, -1.0*Volt), # assumed, from Vcc = 2.7v and 5v tables - current_draw=(0, 0)*pAmp # TODO: should bias current be modeled here? ) self.vinp = self.Port(analog_in_model) self.vinn = self.Port(analog_in_model) diff --git a/edg/parts/Opamp_Mcp6001.py b/edg/parts/Opamp_Mcp6001.py index 6d92a64f9..2294418ad 100644 --- a/edg/parts/Opamp_Mcp6001.py +++ b/edg/parts/Opamp_Mcp6001.py @@ -15,7 +15,6 @@ def __init__(self): voltage_limit_tolerance=(-1.0, 1.0)*Volt, # absolute maximum ratings signal_limit_bound=(0.3*Volt, -0.3*Volt), # common-mode input range impedance=1e13*Ohm(tol=0), # no tolerance bounds given on datasheet - current_draw=(0, 0)*pAmp # TODO: should bias current be modeled here? ) self.vinp = self.Port(analog_in_model) self.vinn = self.Port(analog_in_model) diff --git a/edg/parts/PowerConditioning.py b/edg/parts/PowerConditioning.py index 3a0622e4b..0b9244ae3 100644 --- a/edg/parts/PowerConditioning.py +++ b/edg/parts/PowerConditioning.py @@ -67,14 +67,12 @@ def __init__(self, charging_current: RangeLike, sense_resistance: RangeLike, drain_voltage=(0, max_in_voltage), drain_current=(0, max_charge_current), gate_voltage=(self.pwr.link().voltage.lower(), max_in_voltage), rds_on=(0, 0.5)*Ohm, # TODO kind of arbitrary - gate_charge=(0, float('inf')), power=(0, max_in_voltage * max_charge_current) )) self.connect(self.fet.source, self.sense.b) self.diode = self.Block(Diode( reverse_voltage=(0, max_in_voltage), current=self.charging_current, voltage_drop=self.voltage_drop, - reverse_recovery_time=(0, float('inf')) )) self.connect(self.diode.anode.adapt_to(VoltageSink()), self.fet.drain.adapt_to(VoltageSource( @@ -140,8 +138,7 @@ def __init__(self, voltage_drop: RangeLike, reverse_recovery_time: RangeLike = R self.pwr_in, self.diode.cathode.adapt_to(VoltageSource( voltage_out=(self.pwr_in_diode.link().voltage.lower() - self.diode.voltage_drop.upper(), - self.pwr_in_diode.link().voltage.upper() - self.diode.voltage_drop.lower()), - current_limits=(-float('inf'), float('inf')) + self.pwr_in_diode.link().voltage.upper() - self.diode.voltage_drop.lower()) )) ) self.connect(self.merge.pwr_out, self.pwr_out) @@ -175,21 +172,17 @@ def __init__(self, voltage_drop: RangeLike, reverse_recovery_time: RangeLike = ( self.merge = self.Block(MergedVoltageSource()).connected_from( self.diode1.cathode.adapt_to(VoltageSource( voltage_out=(self.pwr_in1.link().voltage.lower() - self.diode1.voltage_drop.upper(), - self.pwr_in1.link().voltage.upper()), - current_limits=(-float('inf'), float('inf')) + self.pwr_in1.link().voltage.upper()) )), self.diode2.cathode.adapt_to(VoltageSource( voltage_out=(self.pwr_in2.link().voltage.lower() - self.diode2.voltage_drop.upper(), - self.pwr_in2.link().voltage.upper()), - current_limits=(-float('inf'), float('inf')) + self.pwr_in2.link().voltage.upper()) )) ) self.connect(self.diode1.anode.adapt_to(VoltageSink( - voltage_limits=(-float('inf'), float('inf')), current_draw=self.pwr_out.link().current_drawn )), self.pwr_in1) self.connect(self.diode2.anode.adapt_to(VoltageSink( - voltage_limits=(-float('inf'), float('inf')), current_draw=self.pwr_out.link().current_drawn )), self.pwr_in2) diff --git a/edg/parts/RfModules.py b/edg/parts/RfModules.py index 54e85b7cf..b54d701e3 100644 --- a/edg/parts/RfModules.py +++ b/edg/parts/RfModules.py @@ -17,7 +17,6 @@ def __init__(self): digital_model = DigitalBidir.from_supply( self.gnd, self.pwr, voltage_limit_tolerance=(-0.3, 0.3)*Volt, # TODO speculative deafult - current_draw=(0, 0), # TODO actually an unspecified default current_limits=(-2, 2) * mAmp, input_threshold_factor=(0.3, 0.7), output_threshold_factor=(0.05, 0.95) @@ -89,7 +88,6 @@ def __init__(self) -> None: self.pwr = self.Port(VoltageSink( voltage_limits=(3, 6) * Volt, # TODO added a -10% tolerance on the low side so things still work - technically out of spec - current_draw=(0, 0), # TODO actually an unspecified default ), [Power]) self.gnd = self.Port(Ground(), [Common]) @@ -97,7 +95,6 @@ def __init__(self) -> None: pos=self.pwr, neg=self.gnd, voltage_limit_tolerance=(-0.3, 0.3)*Volt, # TODO actually an unspecified default # TODO other parameters given the logic conversion circuit - current_draw=(0, 0), # TODO actually an unspecified default input_threshold_factor=(0.5, 0.5) # TODO completely wild relaxed unrealistic guess ) diff --git a/edg/parts/Rtc_Pcf2129.py b/edg/parts/Rtc_Pcf2129.py index eec13059b..cda7c22a0 100644 --- a/edg/parts/Rtc_Pcf2129.py +++ b/edg/parts/Rtc_Pcf2129.py @@ -18,7 +18,6 @@ def __init__(self) -> None: dio_model = DigitalBidir( voltage_limits=(-0.5, self.pwr.link().voltage.lower() + 0.5), - current_draw=(0, 0), voltage_out=(0, self.pwr.link().voltage.lower()), current_limits=(-1, 1) * mAmp, # TODO higher sink current on SDA/nCE input_thresholds=(0.25 * self.pwr.link().voltage.upper(), From e6a8ae795710ab8350741608e101b9733bd0d8ff Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 15:33:21 -0700 Subject: [PATCH 12/14] cleaning --- examples/test_iot_display.py | 2 -- examples/test_usb_source_measure.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/examples/test_iot_display.py b/examples/test_iot_display.py index 4887b33fe..136222ccd 100644 --- a/examples/test_iot_display.py +++ b/examples/test_iot_display.py @@ -28,8 +28,6 @@ def contents(self): drain_current=self.output.link().current_drawn, gate_voltage=self.control.link().voltage - self.pwr.link().voltage, # TODO needs to be diff from pwr.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=self.control.link().current_limits # TODO this is kind of a max drive current )) diff --git a/examples/test_usb_source_measure.py b/examples/test_usb_source_measure.py index 64a8bdcf6..8ed0505c2 100644 --- a/examples/test_usb_source_measure.py +++ b/examples/test_usb_source_measure.py @@ -165,14 +165,12 @@ def contents(self) -> None: drain_current=self.current, gate_voltage=gate_voltage, rds_on=self.rds_on, - gate_charge=RangeExpr.ALL, # don't care, it's analog not switching power=self.pwr.link().voltage * self.current)) self.low_fet = self.Block(Fet.PFet( drain_voltage=self.pwr.link().voltage, drain_current=self.current, gate_voltage=gate_voltage, rds_on=self.rds_on, - gate_charge=RangeExpr.ALL, # don't care, it's analog not switching power=self.pwr.link().voltage * self.current)) self.import_kicad(self.file_path("resources", f"{self.__class__.__name__}.kicad_sch"), From c358979defbada5537baf2e7c5b66d09faca066e Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 15:43:00 -0700 Subject: [PATCH 13/14] eliminate mgc --- edg/parts/JlcFet.py | 6 ++++++ examples/test_iot_iron.py | 2 -- examples/test_usb_source_measure.py | 4 ---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index 3e737f212..c6e29477b 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -154,6 +154,12 @@ class JlcBaseFet(JlcTableSelector): 'AO4485': 42e-9, # @ Vgs=10 'AO4459': 9.2e-9, # @ Vgs=10 'AO4468': 15e-9, # @ Vgs=10 + 'IRF7458TRPBF': 39e-9, # @ Vgs=10 + + 'AO4407A': 30e-9, # @ Vgs=10 + + # DPAK devices + 'KIA50N03BD': 25e-9, # @ Vgs=10 } @classmethod diff --git a/examples/test_iot_iron.py b/examples/test_iot_iron.py index 40d62ca07..2b1fff449 100644 --- a/examples/test_iot_iron.py +++ b/examples/test_iot_iron.py @@ -239,8 +239,6 @@ def refinements(self) -> Refinements: (['reg_gate', 'ic', 'actual_dropout'], Range.exact(0)), # allow tracking (['conv', 'sw', 'high_fet', 'part'], ParamValue(['conv', 'sw', 'low_fet', 'part'])), - (['conv', 'sw', 'low_fet', 'manual_gate_charge'], Range.exact(100e-9)), # reasonable worst case estimate - (['conv', 'sw', 'high_fet', 'manual_gate_charge'], ParamValue(['conv', 'sw', 'low_fet', 'manual_gate_charge'])), ], class_refinements=[ (HalfBridgeDriver, Ucc27282), diff --git a/examples/test_usb_source_measure.py b/examples/test_usb_source_measure.py index 8ed0505c2..cbacf24f9 100644 --- a/examples/test_usb_source_measure.py +++ b/examples/test_usb_source_measure.py @@ -797,10 +797,6 @@ def refinements(self) -> Refinements: )), # TODO model is broken for unknown reasons (['boot', 'c_fly_pos', 'voltage_rating_derating'], 0.85), (['boot', 'c_fly_neg', 'voltage_rating_derating'], 0.85), - (['conv', 'buck_sw', 'low_fet', 'manual_gate_charge'], Range.exact(100e-9)), # reasonable worst case estimate - (['conv', 'buck_sw', 'high_fet', 'manual_gate_charge'], ParamValue(['conv', 'buck_sw', 'low_fet', 'manual_gate_charge'])), - (['conv', 'boost_sw', 'low_fet', 'manual_gate_charge'], ParamValue(['conv', 'buck_sw', 'low_fet', 'manual_gate_charge'])), - (['conv', 'boost_sw', 'high_fet', 'manual_gate_charge'], ParamValue(['conv', 'buck_sw', 'low_fet', 'manual_gate_charge'])), # require all FETs to be the same; note boost must elaborate first (['conv', 'buck_sw', 'low_fet', 'part'], ParamValue(['conv', 'boost_sw', 'low_fet', 'actual_part'])), (['conv', 'buck_sw', 'high_fet', 'part'], ParamValue(['conv', 'boost_sw', 'low_fet', 'actual_part'])), From 671b0de0ff8dfa94a57bd9b3f09e9de973801cd7 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Sun, 19 Oct 2025 16:05:45 -0700 Subject: [PATCH 14/14] regenerate --- edg/parts/JlcFet.py | 6 +++++- examples/Fcml/Fcml.net | 24 ++++++++++++------------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/edg/parts/JlcFet.py b/edg/parts/JlcFet.py index c6e29477b..bf94c9273 100644 --- a/edg/parts/JlcFet.py +++ b/edg/parts/JlcFet.py @@ -155,10 +155,14 @@ class JlcBaseFet(JlcTableSelector): 'AO4459': 9.2e-9, # @ Vgs=10 'AO4468': 15e-9, # @ Vgs=10 'IRF7458TRPBF': 39e-9, # @ Vgs=10 - 'AO4407A': 30e-9, # @ Vgs=10 # DPAK devices + 'IRLR024NTRPBF': 15e-9, # @ Vgs=5 + 'AOD413A': 16.2e-9, # @ Vgs=-10 + 'IRLR8726TRPBF': 15e-9, # @ Vgs=4.5 + 'IRLR8726TRLPBF': 15e-9, # @ Vgs=4.5 + 'IRFR5410TRPBF': 58e-9, # @ Vgs=-10 'KIA50N03BD': 25e-9, # @ Vgs=10 } diff --git a/examples/Fcml/Fcml.net b/examples/Fcml/Fcml.net index fa2840cb1..fb80d6ef9 100644 --- a/examples/Fcml/Fcml.net +++ b/examples/Fcml/Fcml.net @@ -476,8 +476,8 @@ (property (name "edg_path") (value "conv.sw[0].high_fet")) (property (name "edg_short_path") (value "conv.sw[0].high_fet")) (property (name "edg_refdes") (value "Q1")) - (property (name "edg_part") (value "HSM4410 (HUASHUO)")) - (property (name "edg_value") (value "30V 12A 2.5W 9.5mΩ@10V,12A 2.5V@250μA N Channel SOP-8 MOSFETs ROHS")) + (property (name "edg_part") (value "ET4410 (ETERNAL)")) + (property (name "edg_value") (value "30V 10A 9mΩ@10V,10A 2.5W 1.7V@250uA 145pF@15V N Channel 710pF@15V 8nC@4.5V -55℃~+150℃@(Tj) SOP-8 MOSFETs ROHS")) (sheetpath (names "/conv/sw[0]/") (tstamps "/042f01b7/05ee01d3/")) (tstamps "0e85033f")) (comp (ref "Q2") @@ -488,8 +488,8 @@ (property (name "edg_path") (value "conv.sw[0].low_fet")) (property (name "edg_short_path") (value "conv.sw[0].low_fet")) (property (name "edg_refdes") (value "Q2")) - (property (name "edg_part") (value "HSM4410 (HUASHUO)")) - (property (name "edg_value") (value "30V 12A 2.5W 9.5mΩ@10V,12A 2.5V@250μA N Channel SOP-8 MOSFETs ROHS")) + (property (name "edg_part") (value "ET4410 (ETERNAL)")) + (property (name "edg_value") (value "30V 10A 9mΩ@10V,10A 2.5W 1.7V@250uA 145pF@15V N Channel 710pF@15V 8nC@4.5V -55℃~+150℃@(Tj) SOP-8 MOSFETs ROHS")) (sheetpath (names "/conv/sw[0]/") (tstamps "/042f01b7/05ee01d3/")) (tstamps "0bd402f1")) (comp (ref "C15") @@ -680,8 +680,8 @@ (property (name "edg_path") (value "conv.sw[1].high_fet")) (property (name "edg_short_path") (value "conv.sw[1].high_fet")) (property (name "edg_refdes") (value "Q3")) - (property (name "edg_part") (value "HSM4410 (HUASHUO)")) - (property (name "edg_value") (value "30V 12A 2.5W 9.5mΩ@10V,12A 2.5V@250μA N Channel SOP-8 MOSFETs ROHS")) + (property (name "edg_part") (value "ET4410 (ETERNAL)")) + (property (name "edg_value") (value "30V 10A 9mΩ@10V,10A 2.5W 1.7V@250uA 145pF@15V N Channel 710pF@15V 8nC@4.5V -55℃~+150℃@(Tj) SOP-8 MOSFETs ROHS")) (sheetpath (names "/conv/sw[1]/") (tstamps "/042f01b7/05f001d4/")) (tstamps "0e85033f")) (comp (ref "Q4") @@ -692,8 +692,8 @@ (property (name "edg_path") (value "conv.sw[1].low_fet")) (property (name "edg_short_path") (value "conv.sw[1].low_fet")) (property (name "edg_refdes") (value "Q4")) - (property (name "edg_part") (value "HSM4410 (HUASHUO)")) - (property (name "edg_value") (value "30V 12A 2.5W 9.5mΩ@10V,12A 2.5V@250μA N Channel SOP-8 MOSFETs ROHS")) + (property (name "edg_part") (value "ET4410 (ETERNAL)")) + (property (name "edg_value") (value "30V 10A 9mΩ@10V,10A 2.5W 1.7V@250uA 145pF@15V N Channel 710pF@15V 8nC@4.5V -55℃~+150℃@(Tj) SOP-8 MOSFETs ROHS")) (sheetpath (names "/conv/sw[1]/") (tstamps "/042f01b7/05f001d4/")) (tstamps "0bd402f1")) (comp (ref "D4") @@ -908,8 +908,8 @@ (property (name "edg_path") (value "conv.sw[2].high_fet")) (property (name "edg_short_path") (value "conv.sw[2].high_fet")) (property (name "edg_refdes") (value "Q5")) - (property (name "edg_part") (value "HSM4410 (HUASHUO)")) - (property (name "edg_value") (value "30V 12A 2.5W 9.5mΩ@10V,12A 2.5V@250μA N Channel SOP-8 MOSFETs ROHS")) + (property (name "edg_part") (value "ET4410 (ETERNAL)")) + (property (name "edg_value") (value "30V 10A 9mΩ@10V,10A 2.5W 1.7V@250uA 145pF@15V N Channel 710pF@15V 8nC@4.5V -55℃~+150℃@(Tj) SOP-8 MOSFETs ROHS")) (sheetpath (names "/conv/sw[2]/") (tstamps "/042f01b7/05f201d5/")) (tstamps "0e85033f")) (comp (ref "Q6") @@ -920,8 +920,8 @@ (property (name "edg_path") (value "conv.sw[2].low_fet")) (property (name "edg_short_path") (value "conv.sw[2].low_fet")) (property (name "edg_refdes") (value "Q6")) - (property (name "edg_part") (value "HSM4410 (HUASHUO)")) - (property (name "edg_value") (value "30V 12A 2.5W 9.5mΩ@10V,12A 2.5V@250μA N Channel SOP-8 MOSFETs ROHS")) + (property (name "edg_part") (value "ET4410 (ETERNAL)")) + (property (name "edg_value") (value "30V 10A 9mΩ@10V,10A 2.5W 1.7V@250uA 145pF@15V N Channel 710pF@15V 8nC@4.5V -55℃~+150℃@(Tj) SOP-8 MOSFETs ROHS")) (sheetpath (names "/conv/sw[2]/") (tstamps "/042f01b7/05f201d5/")) (tstamps "0bd402f1")) (comp (ref "D6")