From 437c4b4e65237e0fd3585d966e7578555dc13154 Mon Sep 17 00:00:00 2001 From: Richard Lin Date: Mon, 28 Oct 2024 02:37:06 -0700 Subject: [PATCH] Strict LDO dropout (#386) Prior, the LDO dropout would propagate to the output voltage and assume that the LDO would track. This creates an assertion that the input voltage is greater than the dropout. Not super strict, only checks that the input lower bound is greater than the output lower bound + dropout. Rationale: voltage_out is a spec, and it's not being met. Users can change the dropout voltage in refinements to tighten bounds (eg, LDO operating at less than max current) or allow the regulator to go into tracking. Also refactors out actual_target_voltage since it's no longer needed. --- edg/abstract_parts/AbstractPowerConverters.py | 15 ++++++-------- edg/parts/LinearRegulators.py | 20 +++++++++---------- edg/parts/VoltageReferences.py | 2 +- examples/test_pcbbot.py | 1 + 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/edg/abstract_parts/AbstractPowerConverters.py b/edg/abstract_parts/AbstractPowerConverters.py index 7cc00ba38..ad9108649 100644 --- a/edg/abstract_parts/AbstractPowerConverters.py +++ b/edg/abstract_parts/AbstractPowerConverters.py @@ -101,25 +101,22 @@ def __init__(self) -> None: # these device model parameters must be provided by subtypes self.actual_dropout = self.Parameter(RangeExpr()) self.actual_quiescent_current = self.Parameter(RangeExpr()) - self.actual_target_voltage = self.Parameter(RangeExpr()) + self.gnd = self.Port(Ground(), [Common]) self.pwr_in = self.Port(VoltageSink( - voltage_limits=RangeExpr(), + voltage_limits=RangeExpr(), # parameters set by subtype current_draw=RangeExpr() ), [Power, Input]) - # dropout voltage is modeled to expand the tolerance range for actual output voltage self.pwr_out = self.Port(VoltageSource( - voltage_out=( # bounds are lowest of the target voltage or dropout voltage - self.actual_target_voltage.lower().min(self.pwr_in.link().voltage.lower() - self.actual_dropout.upper()), - self.actual_target_voltage.upper().min(self.pwr_in.link().voltage.upper() - self.actual_dropout.lower()), - ), + voltage_out=self.RangeExpr(), # parameters set by subtype current_limits=RangeExpr() ), [Output]) - self.gnd = self.Port(Ground(), [Common]) - self.assign(self.pwr_in.current_draw, self.pwr_out.link().current_drawn + self.actual_quiescent_current) + self.require(self.pwr_out.voltage_out.lower() + self.actual_dropout.upper() <= self.pwr_in.link().voltage.lower(), + "excessive dropout") + @abstract_block class SwitchingVoltageRegulator(VoltageRegulator): diff --git a/edg/parts/LinearRegulators.py b/edg/parts/LinearRegulators.py index 0f75ed9e6..1e88435c2 100644 --- a/edg/parts/LinearRegulators.py +++ b/edg/parts/LinearRegulators.py @@ -31,7 +31,7 @@ def generate(self): assert suitable_parts, "no regulator with compatible output" part_output_voltage, part_number, lcsc_part = suitable_parts[0] - self.assign(self.actual_target_voltage, part_output_voltage) + self.assign(self.pwr_out.voltage_out, part_output_voltage) self.assign(self.lcsc_part, lcsc_part) self.assign(self.actual_basic_part, False) self.footprint( @@ -89,7 +89,7 @@ def generate(self): assert suitable_parts, "no regulator with compatible output" part_output_voltage_nominal, part_number, jlc_number = suitable_parts[0] - self.assign(self.actual_target_voltage, part_output_voltage_nominal * Volt(tol=TOLERANCE)) + self.assign(self.pwr_out.voltage_out, part_output_voltage_nominal * Volt(tol=TOLERANCE)) self.footprint( 'U', 'Package_TO_SOT_SMD:SOT-223-3_TabPin2', { @@ -159,7 +159,7 @@ def generate(self): assert suitable_parts, "no regulator with compatible output" part_output_voltage_nominal, part_number, jlc_number = suitable_parts[0] - self.assign(self.actual_target_voltage, part_output_voltage_nominal * Volt(tol=TOLERANCE)) + self.assign(self.pwr_out.voltage_out, part_output_voltage_nominal * Volt(tol=TOLERANCE)) self.footprint( 'U', 'Package_TO_SOT_SMD:SOT-23-5', { @@ -204,7 +204,7 @@ def __init__(self, output_voltage: RangeLike): self.assign(self.pwr_out.current_limits, (0, 0.6) * Amp) self.assign(self.actual_quiescent_current, (50, 80) * uAmp) self.assign(self.actual_dropout, (0, 0.25) * Volt) # worst-case @ 100mA Iout - self.assign(self.actual_target_voltage, (3.234, 3.366) * Volt) + self.assign(self.pwr_out.voltage_out, (3.234, 3.366) * Volt) self.footprint( 'U', 'Package_TO_SOT_SMD:SOT-89-3', { @@ -273,7 +273,7 @@ def generate(self): assert suitable_parts, "no regulator with compatible output" part_output_voltage, part_number, part_dropout, part_max_current, lcsc_part, basic_part = suitable_parts[0] - self.assign(self.actual_target_voltage, part_output_voltage * Volt) + self.assign(self.pwr_out.voltage_out, part_output_voltage * Volt) self.assign(self.actual_dropout, part_dropout * Volt) self.assign(self.pwr_out.current_limits, (0, part_max_current) * Amp) self.footprint( @@ -337,7 +337,7 @@ def generate(self): assert suitable_parts, "no regulator with compatible output" part_output_voltage_nominal, part_number, part_dropout, lcsc_part = suitable_parts[0] - self.assign(self.actual_target_voltage, part_output_voltage_nominal * Volt(tol=TOLERANCE)) + self.assign(self.pwr_out.voltage_out, part_output_voltage_nominal * Volt(tol=TOLERANCE)) self.assign(self.actual_dropout, part_dropout * Volt) self.footprint( 'U', 'Package_TO_SOT_SMD:SOT-23-5', @@ -408,7 +408,7 @@ def generate(self): assert suitable_parts, "no regulator with compatible output" part_output_voltage_nominal, part_number, jlc_number = suitable_parts[0] - self.assign(self.actual_target_voltage, part_output_voltage_nominal * Volt(tol=TOLERANCE)) + self.assign(self.pwr_out.voltage_out, part_output_voltage_nominal * Volt(tol=TOLERANCE)) self.footprint( 'U', 'Package_TO_SOT_SMD:SOT-23-5', { @@ -496,7 +496,7 @@ def generate(self): assert suitable_parts, "no regulator with compatible output" part_output_voltage, part_number, footprint, jlc_number = suitable_parts[0] - self.assign(self.actual_target_voltage, part_output_voltage) + self.assign(self.pwr_out.voltage_out, part_output_voltage) if footprint == 'Package_TO_SOT_SMD:SOT-23-5': pinning: Dict[str, CircuitPort] = { '1': self.pwr_in, @@ -591,7 +591,7 @@ def generate(self): assert suitable_parts, "no regulator with compatible output" part_output_voltage, part_number, part_dropout, lcsc = suitable_parts[0] - self.assign(self.actual_target_voltage, part_output_voltage) + self.assign(self.pwr_out.voltage_out, part_output_voltage) self.assign(self.actual_dropout, (0, part_dropout)*Volt) self.footprint( @@ -659,7 +659,7 @@ def generate(self): if part[0] in self.get(self.output_voltage)] assert suitable_parts, "no regulator with compatible output" - self.assign(self.actual_target_voltage, suitable_parts[0][0]) + self.assign(self.pwr_out.voltage_out, suitable_parts[0][0]) self.assign(self.pwr_in.voltage_limits, suitable_parts[0][1]) self.assign(self.pwr_out.current_limits, (0, 100)*mAmp) self.assign(self.actual_quiescent_current, suitable_parts[0][2]) diff --git a/edg/parts/VoltageReferences.py b/edg/parts/VoltageReferences.py index 165a6d6ee..975e7321f 100644 --- a/edg/parts/VoltageReferences.py +++ b/edg/parts/VoltageReferences.py @@ -30,7 +30,7 @@ def generate(self): assert suitable_parts, "no regulator with compatible output" part_output_voltage, part_number, lcsc_part = suitable_parts[0] - self.assign(self.actual_target_voltage, part_output_voltage) + self.assign(self.pwr_out.voltage_out, part_output_voltage) self.assign(self.lcsc_part, lcsc_part) self.assign(self.actual_basic_part, False) self.footprint( diff --git a/examples/test_pcbbot.py b/examples/test_pcbbot.py index b72a39c4e..293ebf7c0 100644 --- a/examples/test_pcbbot.py +++ b/examples/test_pcbbot.py @@ -208,6 +208,7 @@ def refinements(self) -> Refinements: ]), (['prot_batt', 'diode', 'footprint_spec'], 'Diode_SMD:D_SMA'), # big diodes to dissipate more power + (['reg_3v3', 'ic', 'actual_dropout'], Range(0.0, 0.3)) # tighter dropout from lower current draw ], class_refinements=[ (PassiveConnector, JstPhKVertical), # default connector series unless otherwise specified