Implement LDO DUT model
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.
This commit is contained in:
@@ -3,3 +3,8 @@
|
|||||||
Provides thermal and electrical models for various device types
|
Provides thermal and electrical models for various device types
|
||||||
including LDO regulators, op-amps, and other components.
|
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"]
|
||||||
|
|||||||
219
src/py_dvt_ate/simulation/physics/models/ldo.py
Normal file
219
src/py_dvt_ate/simulation/physics/models/ldo.py
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user