Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions edg/abstract_parts/OpampCircuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,9 @@ class IntegratorInverting(OpampApplication, KiCadSchematicBlock, KiCadImportable
From https://en.wikipedia.org/wiki/Operational_amplifier_applications#Inverting_integrator:
Vout = - 1/RC * int(Vin) (integrating over time)

Series is lower and tolerance is higher because there's a cap involved
TODO - separate series for cap, and series and tolerance by decade?
1/RC (in units 1/s or Hz) is the integrator gain.
One intuitive interpretation is, for a DC input, one period of the frequency
is the time it takes for the output voltage to change by the input voltage.
"""

@override
Expand All @@ -387,7 +388,7 @@ def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]:
}
return mapping[symbol_name]

def __init__(self, factor: RangeLike, capacitance: RangeLike, *, series: IntLike = 6, tolerance: FloatLike = 0.05):
def __init__(self, gain: RangeLike, capacitance: RangeLike):
super().__init__()

self.amp = self.Block(Opamp())
Expand All @@ -398,23 +399,23 @@ def __init__(self, factor: RangeLike, capacitance: RangeLike, *, series: IntLike
self.output = self.Port(AnalogSource.empty())
self.reference = self.Port(AnalogSink.empty()) # negative reference for the input and output signals

self.factor = self.ArgParameter(factor) # output scale factor, 1/RC in units of 1/s
self.gain = self.ArgParameter(gain) # 1/RC in units of 1/s
self.capacitance = self.ArgParameter(capacitance)

self.actual_factor = self.Parameter(RangeExpr())
self.actual_gain = self.Parameter(RangeExpr())

@override
def contents(self) -> None:
super().contents()

self.description = DescriptionString(
"<b>factor:</b> ",
DescriptionString.FormatUnits(self.actual_factor, ""),
"<b>gain:</b> ",
DescriptionString.FormatUnits(self.actual_gain, ""),
" <b>of spec:</b> ",
DescriptionString.FormatUnits(self.factor, ""),
DescriptionString.FormatUnits(self.gain, ""),
)

self.r = self.Block(Resistor((1 / self.factor).shrink_multiply(1 / self.capacitance)))
self.r = self.Block(Resistor((1 / self.gain).shrink_multiply(1 / self.capacitance)))
self.c = self.Block(Capacitor(capacitance=self.capacitance, voltage=self.output.link().voltage))

self.import_kicad(
Expand All @@ -427,7 +428,7 @@ def contents(self) -> None:
},
)

self.assign(self.actual_factor, 1 / self.r.actual_resistance / self.c.actual_capacitance)
self.assign(self.actual_gain, 1 / self.r.actual_resistance / self.c.actual_capacitance)


class SummingAmplifier(OpampApplication):
Expand Down
11 changes: 10 additions & 1 deletion edg/abstract_parts/PowerCircuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,21 @@ def contents(self) -> None:
voltage=(0 * Volt(tol=0)).hull(self.pwr_in.link().voltage),
)
)

# because PMOS is source-referenced from vin, calculate the Vgs from GND by subtracting from Vin
# however, we can't calculate a fixed Vgs range for all Vin, since it would be overly restrictive,
# so instead we calculate ratios at the Vin corners, then take the intersection of the ratios
Copy link

Copilot AI Jan 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The calculation may produce unexpected results when target_vgs.upper() exceeds pwr_in.voltage.lower(). In this case, vgs_ratio_low will contain negative values, which may not be properly handled by the resistive divider. Consider adding an assertion or validation to ensure target_vgs.upper() is less than pwr_in.voltage.lower(), or document this as a precondition for using this block.

Suggested change
# so instead we calculate ratios at the Vin corners, then take the intersection of the ratios
# so instead we calculate ratios at the Vin corners, then take the intersection of the ratios
# Precondition: ensure the maximum target Vgs is less than the minimum input voltage so that
# the computed Vgs ratios remain non-negative and are meaningful for the resistive divider.
assert (
self.target_vgs.upper() < self.pwr_in.link().voltage.lower()
), "target_vgs.upper() must be less than pwr_in.voltage.lower()"

Copilot uses AI. Check for mistakes.
# this may generate intermediate negative ratios, but the intersection should clamp those
# for reasonable target_vgs
vgs_ratio_low = (self.pwr_in.link().voltage.lower() - self.target_vgs) / self.pwr_in.link().voltage.lower()
vgs_ratio_hi = (self.pwr_in.link().voltage.upper() - self.target_vgs) / self.pwr_in.link().voltage.upper()

# dV/dt over a capacitor is I / C => I = Cgd * dV/dt
# then calculate to get the target I: Vgs,th = I * Reff => Reff = Vgs,th / I = Vgs,th / (Cgd * dV/dt)
# we assume Vgs,th is exact, and only contributing sources come from elsewhere
self.div = self.Block(
ResistiveDivider(
ratio=self.target_vgs.shrink_multiply(1 / self.pwr_in.link().voltage),
ratio=vgs_ratio_low.intersect(vgs_ratio_hi),
impedance=(1 / self.target_ramp).shrink_multiply(
self.drv.actual_gate_drive.lower() / (self.cap_gd.actual_capacitance)
),
Expand Down
6 changes: 3 additions & 3 deletions examples/UsbSourceMeasure/SourceMeasureControl.kicad_sch
Original file line number Diff line number Diff line change
Expand Up @@ -6838,15 +6838,15 @@
(hide yes)
)
)
(property "Value2" "factor=1/4.7e-6*Ratio(tol=0.15),"
(property "Value2" "gain=100/4.7*kHertz(tol=0.15),"
(at 113.03 101.346 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Value3" "capacitance=1*nFarad(tol=0.1))"
(property "Value3" "capacitance=10*nFarad(tol=0.1))"
(at 113.03 103.632 0)
(effects
(font
Expand Down Expand Up @@ -9786,7 +9786,7 @@
(hide yes)
)
)
(property "Value2" "ratio=10*Ratio(tol=0.05))"
(property "Value2" "ratio=5*Ratio(tol=0.05))"
(at 237.49 128.016 0)
(effects
(font
Expand Down
16 changes: 8 additions & 8 deletions examples/UsbSourceMeasure/UsbSourceMeasure.net
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@
(property (name "edg_path") (value "ramp.div.top_res"))
(property (name "edg_short_path") (value "ramp.div.top_res"))
(property (name "edg_refdes") (value "R2"))
(property (name "edg_part") (value "0603WAF2203T5E (UNI-ROYAL(Uniroyal Elec))"))
(property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±100ppm/℃ -55℃~+155℃ 220kΩ 0603 Chip Resistor - Surface Mount ROHS"))
(property (name "edg_part") (value "0603WAF6803T5E (UNI-ROYAL(Uniroyal Elec))"))
(property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±100ppm/℃ -55℃~+155℃ 680kΩ 0603 Chip Resistor - Surface Mount ROHS"))
(sheetpath (names "/ramp/div/") (tstamps "/043901b1/02770144/"))
(tstamps "0c0c02fd"))
(comp (ref "R3")
Expand All @@ -188,8 +188,8 @@
(property (name "edg_path") (value "ramp.div.bottom_res"))
(property (name "edg_short_path") (value "ramp.div.bottom_res"))
(property (name "edg_refdes") (value "R3"))
(property (name "edg_part") (value "0603WAF6803T5E (UNI-ROYAL(Uniroyal Elec))"))
(property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±100ppm/℃ -55℃~+155℃ 680kΩ 0603 Chip Resistor - Surface Mount ROHS"))
(property (name "edg_part") (value "0603WAF2203T5E (UNI-ROYAL(Uniroyal Elec))"))
(property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±100ppm/℃ -55℃~+155℃ 220kΩ 0603 Chip Resistor - Surface Mount ROHS"))
(sheetpath (names "/ramp/div/") (tstamps "/043901b1/02770144/"))
(tstamps "175b043f"))
(comp (ref "Q2")
Expand Down Expand Up @@ -1628,8 +1628,8 @@
(property (name "edg_path") (value "control.int.c"))
(property (name "edg_short_path") (value "control.int.c"))
(property (name "edg_refdes") (value "C52"))
(property (name "edg_part") (value "CL10B102KB8NNNC (Samsung Electro-Mechanics)"))
(property (name "edg_value") (value "50V 1nF X7R ±10% 0603 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS"))
(property (name "edg_part") (value "0603B103K500NT (FH(Guangdong Fenghua Advanced Tech))"))
(property (name "edg_value") (value "50V 10nF X7R ±10% 0603 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS"))
(sheetpath (names "/control/int/") (tstamps "/0bec0302/028e014c/"))
(tstamps "00640064"))
(comp (ref "R35")
Expand Down Expand Up @@ -2180,8 +2180,8 @@
(property (name "edg_path") (value "control.imeas.rg"))
(property (name "edg_short_path") (value "control.imeas.rg"))
(property (name "edg_refdes") (value "R54"))
(property (name "edg_part") (value "0603WAF5601T5E (UNI-ROYAL(Uniroyal Elec))"))
(property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±100ppm/℃ -55℃~+155℃ 5.6kΩ 0603 Chip Resistor - Surface Mount ROHS"))
(property (name "edg_part") (value "0603WAF1202T5E (UNI-ROYAL(Uniroyal Elec))"))
(property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±100ppm/℃ -55℃~+155℃ 12kΩ 0603 Chip Resistor - Surface Mount ROHS"))
(sheetpath (names "/control/imeas/") (tstamps "/0bec0302/062a0210/"))
(tstamps "014d00da"))
(comp (ref "Q9")
Expand Down
2 changes: 1 addition & 1 deletion examples/test_usb_source_measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ def contents(self) -> None:
(self.ramp, self.cap_conv), _ = self.chain(
self.vusb,
imp.Block(
RampLimiter(target_vgs=(3.7, 19) * Volt)
RampLimiter(target_vgs=(3.5, 17.5) * Volt)
), # avoid excess capacitance on VBus which may cause the PD source to reset
imp.Block(DecouplingCapacitor(47 * uFarad(tol=0.25))),
)
Expand Down
Loading