Skip to content

Commit 3dad7fd

Browse files
authored
Improved FET parsing + clean out unspecified/ideal params (#422)
Improves how gate charge is handled in JLC FET parsing. Adds supplemental gate charge data into the JlcFet class for an arbitrary selection of FETs. Renames manual_gate_charge to fallback_gate_charge, which is now only used when a parsed gate charge is not available and uses a pessimistic max. Expands footprint names map for JlcParts. Changes parts table behavior to crash (assertion out) when there is no match, to better distinguish it from a typical electrical model failure. Eliminates manual_gate_charge from refinements, which is no longer relevant. Also refactors a bunch of library to delete ideal modeling, delegating it to the ideal defaults. This was the cause some failures from gate_charge, which was being set as 0 -> inf instead of Range.all() NOTES: - When the fallback gate charge is used, it may generate unreasonably high gate drive currents. TBD how to deal with this.
1 parent d07a176 commit 3dad7fd

35 files changed

+143
-131
lines changed

edg/abstract_parts/DigitalAmplifiers.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ def generate(self):
4949
drain_current=(0, pull_current_max),
5050
gate_voltage=(self.control.link().output_thresholds.upper(), self.control.link().voltage.upper()),
5151
rds_on=(0, low_amp_rds_max), # TODO size on turnon time
52-
gate_charge=(0, float('inf')), # TODO size on turnon time
53-
power=(0, 0) * Watt,
5452
frequency=self.frequency,
5553
drive_current=self.control.link().current_limits # TODO this is kind of a max drive current
5654
))
@@ -71,8 +69,6 @@ def generate(self):
7169
drain_current=self.output.link().current_drawn,
7270
gate_voltage=pass_gate_voltage,
7371
rds_on=(0, self.max_rds),
74-
gate_charge=(0, float('inf')), # TODO size on turnon time
75-
power=(0, 0) * Watt,
7672
frequency=self.frequency,
7773
drive_current=(-1 * pwr_voltage.lower() / pull_resistance.upper(),
7874
pwr_voltage.lower() / low_amp_rds_max) # TODO simultaneously solve both FETs

edg/abstract_parts/PartsTable.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,10 @@ def filter(self, fn: Callable[[PartsTableRow], bool]) -> PartsTable:
103103

104104
def map_new_columns(self, fn: Callable[[PartsTableRow], Optional[Dict[PartsTableColumn[Any], Any]]],
105105
overwrite: bool = False) -> PartsTable:
106-
"""Creates a new table (deep copy) with additional rows."""
106+
"""Creates a new table (deep copy) with additional rows.
107+
All entries must have the same keys.
108+
Specify overwrite=True to overwrite an existing row. To preserve the existing value (no-op), return it.
109+
Return None to drop the row."""
107110
new_rows: List[PartsTableRow] = []
108111
first_keys: Optional[KeysView] = None
109112
for row in self.rows:

edg/abstract_parts/PartsTablePart.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,9 @@ def generate(self):
8181
postprocessed_table = self._table_postprocess(matching_table)
8282
postprocessed_table = postprocessed_table.sort_by(self._row_sort_by)
8383
self.assign(self.matching_parts, postprocessed_table.map(lambda row: row[self.PART_NUMBER_COL]))
84-
if len(postprocessed_table) > 0:
85-
selected_row = postprocessed_table.first()
86-
self._row_generate(selected_row)
87-
else: # if no matching part, generate a parameter error instead of crashing
88-
self.require(False, "no matching part")
84+
assert len(postprocessed_table) > 0, "no matching part" # crash to make generator failures more obvious
85+
selected_row = postprocessed_table.first()
86+
self._row_generate(selected_row)
8987

9088

9189
@abstract_block

edg/abstract_parts/test_resistor_generic.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ def __init__(self):
88
super().__init__()
99
self.dut = self.Block(GenericChipResistor(
1010
resistance=1 * kOhm(tol=0.1),
11-
power=(0, 0)*Watt
1211
))
1312
(self.dummya, ), _ = self.chain(self.dut.a, self.Block(DummyPassive()))
1413
(self.dummyb, ), _ = self.chain(self.dut.b, self.Block(DummyPassive()))
@@ -30,7 +29,6 @@ def __init__(self):
3029
super().__init__()
3130
self.dut = self.Block(GenericChipResistor(
3231
resistance=8.06 * kOhm(tol=0.01),
33-
power=(0, 0)*Watt
3432
))
3533
(self.dummya, ), _ = self.chain(self.dut.a, self.Block(DummyPassive()))
3634
(self.dummyb, ), _ = self.chain(self.dut.b, self.Block(DummyPassive()))

edg/electronics_model/DigitalPorts.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,8 @@ def __init__(self):
252252
current_draw=RangeExpr()
253253
))
254254
self.dst = self.Port(VoltageSource(
255-
voltage_out=(self.src.link().output_thresholds.upper(), self.src.link().voltage.upper()),
256-
current_limits=(-float('inf'), float('inf'))))
255+
voltage_out=(self.src.link().output_thresholds.upper(), self.src.link().voltage.upper())
256+
))
257257
self.assign(self.src.current_draw, self.dst.link().current_drawn)
258258

259259

edg/electronics_model/GroundPort.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ def __init__(self):
9191
self.dst = self.Port(AnalogSource(
9292
voltage_out=self.src.link().voltage,
9393
signal_out=self.src.link().voltage,
94-
impedance=(0, 0)*Ohm,
9594
))
9695

9796

edg/electronics_model/VoltagePorts.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ def __init__(self):
176176

177177
super().__init__()
178178
self.src = self.Port(VoltageSink(
179-
voltage_limits=(-float('inf'), float('inf'))*Volt,
180179
current_draw=RangeExpr()
181180
))
182181
self.dst = self.Port(AnalogSource(

edg/jlcparts/JlcPartsFet.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ class JlcPartsBaseFet(JlcPartsBase, BaseTableFet):
88
_JLC_PARTS_FILE_NAMES = ["TransistorsMOSFETs"]
99
_CHANNEL_MAP = {
1010
'N Channel': 'N',
11+
'1 N-channel': 'N',
12+
'1 N-Channel': 'N',
1113
'P Channel': 'P',
14+
'1 Piece P-Channel': 'P',
1215
}
1316

1417
@classmethod
@@ -39,14 +42,18 @@ def _entry_to_table_row(cls, row_dict: Dict[PartsTableColumn, Any], filename: st
3942
input_capacitance: Optional[float] = attributes.get("Input capacitance (ciss@vds)", float, sub='capacity')
4043
except (KeyError, TypeError):
4144
input_capacitance = None
45+
4246
try: # not specified for most parts apparently
43-
gate_charge = attributes.get("Total gate charge (qg@vgs)", float, sub='charge')
47+
gate_charge: Optional[float] = attributes.get("Total gate charge (qg@vgs)", float, sub='charge')
4448
except (KeyError, TypeError):
4549
if input_capacitance is not None: # not strictly correct but kind of a guesstimate
4650
gate_charge = input_capacitance * vgs_for_ids
4751
else:
48-
gate_charge = 3000e-9 # very pessimistic upper bound
49-
row_dict[cls.GATE_CHARGE] = Range.exact(gate_charge)
52+
gate_charge = None
53+
if gate_charge is not None:
54+
row_dict[cls.GATE_CHARGE] = Range.exact(gate_charge)
55+
else:
56+
row_dict[cls.GATE_CHARGE] = Range.all()
5057

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

5966

6067
class JlcPartsSwitchFet(PartsTableSelectorFootprint, JlcPartsBaseFet, TableSwitchFet):
61-
@init_in_parent
62-
def __init__(self, *args, manual_gate_charge: RangeLike = RangeExpr.ZERO, **kwargs):
63-
super().__init__(*args, **kwargs)
64-
# allow the user to specify a gate charge
65-
self.manual_gate_charge = self.ArgParameter(manual_gate_charge)
66-
self.generator_param(self.manual_gate_charge)
67-
68-
def _table_postprocess(self, table: PartsTable) -> PartsTable:
69-
manual_gate_charge = self.get(self.manual_gate_charge)
70-
def process_row(row: PartsTableRow) -> Optional[Dict[PartsTableColumn, Any]]:
71-
return {self.GATE_CHARGE: manual_gate_charge}
72-
73-
# must run before TableFet power calculations
74-
if not manual_gate_charge == Range.exact(0):
75-
table = table.map_new_columns(process_row, overwrite=True)
76-
77-
return super()._table_postprocess(table)
68+
pass
7869

7970

8071
lambda: JlcPartsFet(), JlcPartsSwitchFet() # ensure class is instantiable (non-abstract)

edg/parts/AdcSpi_Mcp3201.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ def __init__(self) -> None:
2323
dio_model = DigitalBidir.from_supply(
2424
self.vss, self.vdd,
2525
voltage_limit_tolerance=(-0.6, 0.6)*Volt,
26-
current_draw=(0, 0), # leakage current not modeled
2726
input_threshold_factor=(0.3, 0.7)
2827
)
2928
# Datasheet section 6.2, minimum clock speed

edg/parts/BatteryProtector_S8261A.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,13 @@ def __init__(self) -> None:
5252

5353
self.do_fet = self.Block(Fet.NFet(
5454
drain_current=self.pwr_in.link().current_drawn,
55-
power=RangeExpr.ZERO,
5655
gate_voltage=self.pwr_in.link().voltage,
57-
gate_charge=RangeExpr.ALL,
5856
rds_on=(0, 0.1)*Ohm,
5957
drain_voltage=self.pwr_in.link().voltage
6058
))
6159
self.co_fet = self.Block(Fet.NFet(
6260
drain_current=self.pwr_in.link().current_drawn,
63-
power=RangeExpr.ZERO,
6461
gate_voltage=self.pwr_in.link().voltage,
65-
gate_charge=RangeExpr.ALL,
6662
rds_on=(0, 0.1)*Ohm,
6763
drain_voltage=self.pwr_in.link().voltage
6864
))

0 commit comments

Comments
 (0)