From 3db4969e440d91220126ecb5e50702c46514f7a6 Mon Sep 17 00:00:00 2001 From: Kai Chappell Date: Thu, 6 Mar 2025 21:24:17 +0000 Subject: [PATCH] Implement LDO DUT model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Temperature-dependent LDO voltage regulator model with: - Output voltage tempco (ppm/°C) - Quiescent current tempco - Dropout voltage temperature dependence - Power dissipation calculation (Vin-Vout)*Iload + Vin*Iq - Dropout detection Implements DUTModel protocol for physics engine integration. --- .../simulation/physics/models/__init__.py | 5 + .../simulation/physics/models/ldo.py | 219 ++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 src/py_dvt_ate/simulation/physics/models/ldo.py diff --git a/src/py_dvt_ate/simulation/physics/models/__init__.py b/src/py_dvt_ate/simulation/physics/models/__init__.py index ca08517..756aa99 100644 --- a/src/py_dvt_ate/simulation/physics/models/__init__.py +++ b/src/py_dvt_ate/simulation/physics/models/__init__.py @@ -3,3 +3,8 @@ Provides thermal and electrical models for various device types including LDO regulators, op-amps, and other components. """ + +from py_dvt_ate.simulation.physics.models.base import DUTModel +from py_dvt_ate.simulation.physics.models.ldo import LDOModel, LDOParameters + +__all__ = ["DUTModel", "LDOModel", "LDOParameters"] diff --git a/src/py_dvt_ate/simulation/physics/models/ldo.py b/src/py_dvt_ate/simulation/physics/models/ldo.py new file mode 100644 index 0000000..44b7371 --- /dev/null +++ b/src/py_dvt_ate/simulation/physics/models/ldo.py @@ -0,0 +1,219 @@ +"""LDO (Low Dropout Regulator) DUT model. + +Implements temperature-dependent electrical behaviour for an LDO voltage +regulator, including output voltage tempco, quiescent current variation, +and power dissipation calculations. +""" + +from dataclasses import dataclass + + +@dataclass(frozen=True) +class LDOParameters: + """Configuration parameters for an LDO model. + + All temperature coefficients are referenced to 25°C. + + Attributes: + nominal_output_voltage: Nominal output voltage at 25°C in volts. + tempco_ppm_per_c: Output voltage temperature coefficient in ppm/°C. + quiescent_current_a: Quiescent current at 25°C in amps. + quiescent_current_tempco: Quiescent current temperature coefficient (1/°C). + dropout_voltage: Dropout voltage at 25°C in volts. + max_output_current: Maximum output current in amps. + """ + + nominal_output_voltage: float = 3.3 + tempco_ppm_per_c: float = 50.0 + quiescent_current_a: float = 50e-6 # 50 µA + quiescent_current_tempco: float = 0.003 # 0.3%/°C + dropout_voltage: float = 0.3 + max_output_current: float = 0.5 + + +# Reference temperature for all calculations +REFERENCE_TEMPERATURE_C = 25.0 + + +class LDOModel: + """Temperature-dependent LDO voltage regulator model. + + Models the electrical behaviour of a linear voltage regulator with: + - Output voltage that varies with temperature (tempco in ppm/°C) + - Quiescent current that varies with temperature + - Dropout voltage that increases with temperature + - Power dissipation from (Vin - Vout) × Iload + Vin × Iq + + This class implements the DUTModel protocol. + """ + + def __init__( + self, + params: LDOParameters | None = None, + input_voltage: float = 5.0, + load_current: float = 0.0, + ) -> None: + """Initialise the LDO model. + + Args: + params: LDO parameters. Uses defaults if None. + input_voltage: Initial input voltage in volts. + load_current: Initial load current in amps. + """ + self._params = params or LDOParameters() + self._input_voltage = input_voltage + self._load_current = load_current + + @property + def params(self) -> LDOParameters: + """Get the LDO parameters.""" + return self._params + + @property + def input_voltage(self) -> float: + """Get the current input voltage.""" + return self._input_voltage + + @input_voltage.setter + def input_voltage(self, value: float) -> None: + """Set the input voltage.""" + self._input_voltage = value + + @property + def load_current(self) -> float: + """Get the current load current.""" + return self._load_current + + @load_current.setter + def load_current(self, value: float) -> None: + """Set the load current.""" + self._load_current = value + + def calculate_output_voltage(self, junction_temperature: float) -> float: + """Calculate the output voltage at the given junction temperature. + + Implements: V_out(T) = V_nom × (1 + TC_vout × (T - 25) × 1e-6) + + The output voltage is clamped to not exceed (Vin - Vdropout) when + the regulator is in dropout. + + Args: + junction_temperature: DUT junction temperature in degrees Celsius. + + Returns: + Output voltage in volts. + """ + delta_t = junction_temperature - REFERENCE_TEMPERATURE_C + tempco_factor = 1.0 + self._params.tempco_ppm_per_c * delta_t * 1e-6 + + ideal_vout = self._params.nominal_output_voltage * tempco_factor + + # Calculate dropout voltage at temperature + v_dropout = self._calculate_dropout_voltage(junction_temperature) + + # Clamp output to not exceed (Vin - Vdropout) + max_vout = max(0.0, self._input_voltage - v_dropout) + + return min(ideal_vout, max_vout) + + def calculate_quiescent_current(self, junction_temperature: float) -> float: + """Calculate the quiescent current at the given junction temperature. + + Implements: I_q(T) = I_q_25 × (1 + TC_iq × (T - 25)) + + Args: + junction_temperature: DUT junction temperature in degrees Celsius. + + Returns: + Quiescent current in amps. + """ + delta_t = junction_temperature - REFERENCE_TEMPERATURE_C + tempco_factor = 1.0 + self._params.quiescent_current_tempco * delta_t + + return self._params.quiescent_current_a * tempco_factor + + def calculate_power_dissipation( + self, + input_voltage: float, + load_current: float, + junction_temperature: float, + ) -> float: + """Calculate the power dissipation for given operating conditions. + + Implements: P_diss = (V_in - V_out) × I_load + V_in × I_q + + The power dissipation comes from: + - Voltage drop across the pass element times load current + - Quiescent current times input voltage + + Args: + input_voltage: Input voltage in volts. + load_current: Load current in amps. + junction_temperature: DUT junction temperature in degrees Celsius. + + Returns: + Power dissipation in watts. + """ + # Temporarily set input voltage for output voltage calculation + original_vin = self._input_voltage + self._input_voltage = input_voltage + + try: + v_out = self.calculate_output_voltage(junction_temperature) + i_q = self.calculate_quiescent_current(junction_temperature) + + # Power in pass element + p_pass = (input_voltage - v_out) * load_current + + # Power from quiescent current + p_quiescent = input_voltage * i_q + + return p_pass + p_quiescent + finally: + self._input_voltage = original_vin + + def _calculate_dropout_voltage(self, junction_temperature: float) -> float: + """Calculate the dropout voltage at the given temperature. + + Implements: V_do(T) = V_do_25 × (T_K / 300)^1.5 + + where T_K is the temperature in Kelvin. + + Dropout voltage increases with temperature due to increased + resistance of the pass element. + + Args: + junction_temperature: Junction temperature in degrees Celsius. + + Returns: + Dropout voltage in volts. + """ + # Convert to Kelvin + t_kelvin = junction_temperature + 273.15 + + # Temperature ratio (reference is approximately 300K ≈ 27°C) + temp_ratio = t_kelvin / 300.0 + + return self._params.dropout_voltage * (temp_ratio**1.5) + + def is_in_dropout(self, junction_temperature: float) -> bool: + """Check if the LDO is in dropout at current operating point. + + The LDO is in dropout when the input voltage minus dropout voltage + is less than the nominal output voltage. + + Args: + junction_temperature: Junction temperature in degrees Celsius. + + Returns: + True if in dropout, False otherwise. + """ + v_dropout = self._calculate_dropout_voltage(junction_temperature) + headroom = self._input_voltage - v_dropout + + # Get the ideal (temperature-adjusted) output voltage + delta_t = junction_temperature - REFERENCE_TEMPERATURE_C + tempco_factor = 1.0 + self._params.tempco_ppm_per_c * delta_t * 1e-6 + ideal_vout = self._params.nominal_output_voltage * tempco_factor + + return headroom < ideal_vout