Skip to content

Commit cc1cbd3

Browse files
committed
remove fcml specific params
1 parent c4a9415 commit cc1cbd3

File tree

2 files changed

+85
-7
lines changed

2 files changed

+85
-7
lines changed

edg/abstract_parts/AbstractPowerConverters.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ def contents(self):
189189
self.reset.init_from(DigitalSink())
190190

191191

192-
ParamRangeType = TypeVar('ParamRangeType', bound=Union[RangeExpr, Range])
193192
class BuckConverterPowerPath(InternalSubcircuit, GeneratorBlock):
194193
"""A helper block to generate the power path (inductors, capacitors) for a switching buck converter.
195194
@@ -246,8 +245,7 @@ def __init__(self, input_voltage: RangeLike, output_voltage: RangeLike, frequenc
246245
input_voltage_ripple: FloatLike,
247246
output_voltage_ripple: FloatLike,
248247
efficiency: RangeLike = (0.9, 1.0), # from TI reference
249-
dutycycle_limit: RangeLike = (0.1, 0.9),
250-
inductor_scale: FloatLike = 1.0): # arbitrary
248+
dutycycle_limit: RangeLike = (0.1, 0.9)):
251249
super().__init__()
252250

253251
self.pwr_in = self.Port(VoltageSink.empty(), [Power]) # models the input cap only
@@ -269,7 +267,6 @@ def __init__(self, input_voltage: RangeLike, output_voltage: RangeLike, frequenc
269267
self.input_voltage_ripple, self.output_voltage_ripple, self.dutycycle_limit)
270268

271269
self.current_limits = self.ArgParameter(current_limits)
272-
self.inductor_scale = self.ArgParameter(inductor_scale)
273270

274271
self.actual_dutycycle = self.Parameter(RangeExpr())
275272
self.actual_inductor_current_ripple = self.Parameter(RangeExpr())
@@ -310,15 +307,15 @@ def generate(self) -> None:
310307
# TODO maximum current depends on the inductance, but this just uses a worst-case value for simplicity
311308
# TODO ideally the inductor selector would take a function that can account for this coupled equation
312309
self.inductor = self.Block(Inductor(
313-
inductance=values.inductance*Henry / self.inductor_scale,
310+
inductance=values.inductance*Henry,
314311
current=values.inductor_peak_currents,
315312
frequency=self.frequency
316313
))
317314

318315
# expand out the equation to avoid double-counting tolerance
319316
actual_peak_ripple = (self.output_voltage.lower() * (self.input_voltage.upper() - self.output_voltage.lower()) /
320317
(self.inductor.actual_inductance * self.frequency.lower() * self.input_voltage.upper()))
321-
self.assign(self.actual_inductor_current_ripple, actual_peak_ripple / self.inductor_scale)
318+
self.assign(self.actual_inductor_current_ripple, actual_peak_ripple)
322319

323320
self.connect(self.switch, self.inductor.a.adapt_to(VoltageSink(
324321
voltage_limits=RangeExpr.ALL,

examples/test_fcml.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,87 @@ def generate(self):
172172
})
173173

174174

175+
class FcmlPowerPath(InternalSubcircuit, GeneratorBlock):
176+
"""FCML power path that accounts for inductor scaling behavior
177+
TODO: this basically completely duplicates BuckConverterPowerPath, but adds a scaling factor that doesn't exist there
178+
"""
179+
@init_in_parent
180+
def __init__(self, input_voltage: RangeLike, output_voltage: RangeLike, frequency: RangeLike,
181+
output_current: RangeLike, current_limits: RangeLike, inductor_current_ripple: RangeLike, *,
182+
input_voltage_ripple: FloatLike,
183+
output_voltage_ripple: FloatLike,
184+
efficiency: RangeLike = (0.9, 1.0), # from TI reference
185+
dutycycle_limit: RangeLike = (0.1, 0.9),
186+
inductor_scale: FloatLike = 1.0): # arbitrary
187+
super().__init__()
188+
189+
self.pwr_in = self.Port(VoltageSink.empty(), [Power]) # models the input cap only
190+
self.pwr_out = self.Port(VoltageSource.empty()) # models the output cap and inductor power source
191+
self.switch = self.Port(VoltageSink.empty()) # current draw defined as average
192+
self.gnd = self.Port(Ground.empty(), [Common])
193+
194+
self.input_voltage = self.ArgParameter(input_voltage)
195+
self.output_voltage = self.ArgParameter(output_voltage)
196+
self.frequency = self.ArgParameter(frequency)
197+
self.output_current = self.ArgParameter(output_current)
198+
self.inductor_current_ripple = self.ArgParameter(inductor_current_ripple)
199+
self.efficiency = self.ArgParameter(efficiency)
200+
self.input_voltage_ripple = self.ArgParameter(input_voltage_ripple)
201+
self.output_voltage_ripple = self.ArgParameter(output_voltage_ripple)
202+
self.dutycycle_limit = self.ArgParameter(dutycycle_limit)
203+
self.generator_param(self.input_voltage, self.output_voltage, self.frequency, self.output_current,
204+
self.inductor_current_ripple, self.efficiency,
205+
self.input_voltage_ripple, self.output_voltage_ripple, self.dutycycle_limit)
206+
207+
self.current_limits = self.ArgParameter(current_limits)
208+
self.inductor_scale = self.ArgParameter(inductor_scale)
209+
210+
self.actual_dutycycle = self.Parameter(RangeExpr())
211+
self.actual_inductor_current_ripple = self.Parameter(RangeExpr())
212+
213+
def generate(self) -> None:
214+
super().generate()
215+
values = BuckConverterPowerPath.calculate_parameters(
216+
self.get(self.input_voltage), self.get(self.output_voltage),
217+
self.get(self.frequency), self.get(self.output_current), self.get(self.inductor_current_ripple),
218+
self.get(self.input_voltage_ripple), self.get(self.output_voltage_ripple),
219+
efficiency=self.get(self.efficiency), dutycycle_limit=self.get(self.dutycycle_limit))
220+
self.assign(self.actual_dutycycle, values.dutycycle)
221+
self.require(values.dutycycle == values.effective_dutycycle, "dutycycle outside limit")
222+
223+
# TODO maximum current depends on the inductance, but this just uses a worst-case value for simplicity
224+
# TODO ideally the inductor selector would take a function that can account for this coupled equation
225+
self.inductor = self.Block(Inductor(
226+
inductance=values.inductance*Henry / self.inductor_scale,
227+
current=values.inductor_peak_currents,
228+
frequency=self.frequency
229+
))
230+
231+
# expand out the equation to avoid double-counting tolerance
232+
actual_peak_ripple = (self.output_voltage.lower() * (self.input_voltage.upper() - self.output_voltage.lower()) /
233+
(self.inductor.actual_inductance * self.frequency.lower() * self.input_voltage.upper()))
234+
self.assign(self.actual_inductor_current_ripple, actual_peak_ripple / self.inductor_scale)
235+
236+
self.connect(self.switch, self.inductor.a.adapt_to(VoltageSink(
237+
voltage_limits=RangeExpr.ALL,
238+
current_draw=self.pwr_out.link().current_drawn * values.dutycycle
239+
)))
240+
self.connect(self.pwr_out, self.inductor.b.adapt_to(VoltageSource(
241+
voltage_out=self.output_voltage,
242+
current_limits=(0, self.current_limits.intersect(self.inductor.actual_current_rating).upper() -
243+
(self.actual_inductor_current_ripple.upper() / 2))
244+
)))
245+
246+
self.in_cap = self.Block(DecouplingCapacitor(
247+
capacitance=values.input_capacitance * Farad,
248+
exact_capacitance=True
249+
)).connected(self.gnd, self.pwr_in)
250+
self.out_cap = self.Block(DecouplingCapacitor(
251+
capacitance=values.output_capacitance * Farad,
252+
exact_capacitance=True
253+
)).connected(self.gnd, self.pwr_out)
254+
255+
175256
class DiscreteMutlilevelBuckConverter(PowerConditioner, GeneratorBlock):
176257
"""Flying capacitor multilevel buck converter. Trades more switches for smaller inductor size:
177258
for number of levels N, inductor value is reduced by a factor of (N-1)^2.
@@ -214,7 +295,7 @@ def generate(self):
214295
super().generate()
215296
levels = self.get(self.levels)
216297
assert levels >= 2, "levels must be 2 or more"
217-
self.power_path = self.Block(BuckConverterPowerPath(
298+
self.power_path = self.Block(FcmlPowerPath(
218299
self.pwr_in.link().voltage, self.pwr_in.link().voltage * self.get(self.ratios), self.frequency,
219300
self.pwr_out.link().current_drawn, Range.all(), # TODO add current limits from FETs
220301
inductor_current_ripple=self.inductor_current_ripple,

0 commit comments

Comments
 (0)