Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into circle
Browse files Browse the repository at this point in the history
# Conflicts:
#	examples/PcbBot/PcbBot.net
  • Loading branch information
Suke0811 committed Oct 29, 2024
2 parents 27eb475 + 437c4b4 commit 241affa
Show file tree
Hide file tree
Showing 93 changed files with 108,444 additions and 1,447 deletions.
3 changes: 2 additions & 1 deletion edg/abstract_parts/AbstractAntenna.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ def __init__(self, frequency: RangeLike, impedance: RangeLike = Range.all(), pow
self.power = self.ArgParameter(power)
self.actual_power_rating = self.Parameter(RangeExpr())

self.a = self.Port(Passive.empty())
self.a = self.Port(Passive.empty(), [Input])
self.gnd = self.Port(Ground.empty(), [Common])


@non_library
Expand Down
10 changes: 4 additions & 6 deletions edg/abstract_parts/AbstractBjt.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ class BjtStandardFootprint(StandardFootprint['Bjt']):
REFDES_PREFIX = 'Q'

FOOTPRINT_PINNING_MAP = {
'Package_TO_SOT_SMD:SOT-23': lambda block: {
(
'Package_TO_SOT_SMD:SOT-23',
'Package_TO_SOT_SMD:SOT-323_SC-70',
): lambda block: {
'1': block.base,
'2': block.emitter,
'3': block.collector,
Expand All @@ -21,11 +24,6 @@ class BjtStandardFootprint(StandardFootprint['Bjt']):
'2': block.collector,
'3': block.emitter,
},
'Package_TO_SOT_SMD:SOT-323_SC-70': lambda block: {
'1': block.base,
'2': block.emitter,
'3': block.collector,
},
}


Expand Down
30 changes: 29 additions & 1 deletion edg/abstract_parts/AbstractConnector.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from ..electronics_model import *
from .Categories import Connector
from .AbstractAntenna import Antenna


@abstract_block
Expand All @@ -21,7 +22,7 @@ class RfConnector(Connector):
"""Base class for a RF connector, with a signal and ground. Signal is passive-typed."""
def __init__(self) -> None:
super().__init__()
self.sig = self.Port(Passive.empty())
self.sig = self.Port(Passive.empty(), [Input])
self.gnd = self.Port(Ground(), [Common])


Expand All @@ -33,5 +34,32 @@ def __init__(self, name: StringLike):
self.tp_name = self.ArgParameter(name)


class RfConnectorAntenna(Antenna):
"""RF connector used as an antenna"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.conn = self.Block(RfConnector())
self.connect(self.conn.sig, self.a)
self.connect(self.conn.gnd, self.gnd)


@abstract_block
class UflConnector(RfConnector):
"""Base class for a U.FL / IPEX / UMCC connector, miniature RF connector."""


@abstract_block
class SmaConnector(RfConnector):
"""Base class for a SMA coax connector."""


@abstract_block
class SmaMConnector(SmaConnector):
"""Base class for a SMA M connector, pin with internal threads.
Typically used on the antenna itself."""


@abstract_block
class SmaFConnector(SmaConnector):
"""Base class for a SMA F connector, socket with external threads.
Typically used for an antenna connector for sub-2.4GHz applications; 2.4GHz uses RP-SMA."""
5 changes: 4 additions & 1 deletion edg/abstract_parts/AbstractFets.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ class FetStandardFootprint(StandardFootprint['Fet']):
REFDES_PREFIX = 'Q'

FOOTPRINT_PINNING_MAP = {
'Package_TO_SOT_SMD:SOT-23': lambda block: {
(
'Package_TO_SOT_SMD:SOT-23',
'Package_TO_SOT_SMD:SOT-323_SC-70',
): lambda block: {
'1': block.gate,
'2': block.source,
'3': block.drain,
Expand Down
17 changes: 11 additions & 6 deletions edg/abstract_parts/AbstractInductor.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]:
@init_in_parent
def __init__(self, inductance: RangeLike,
current: RangeLike = RangeExpr.ZERO,
frequency: RangeLike = RangeExpr.ZERO) -> None:
frequency: RangeLike = RangeExpr.ZERO,
resistance_dc: RangeLike = (0, 1)*Ohm # generic sane choice?
) -> None:
super().__init__()

self.a = self.Port(Passive.empty())
Expand All @@ -102,12 +104,12 @@ def __init__(self, inductance: RangeLike,
self.inductance = self.ArgParameter(inductance)
self.current = self.ArgParameter(current) # defined as operating current range, non-directioned
self.frequency = self.ArgParameter(frequency) # defined as operating frequency range
# TODO: in the future, when we consider efficiency - for now, use current ratings
# self.resistance_dc = self.Parameter(RangeExpr())
self.resistance_dc = self.ArgParameter(resistance_dc)

self.actual_inductance = self.Parameter(RangeExpr())
self.actual_current_rating = self.Parameter(RangeExpr())
self.actual_frequency_rating = self.Parameter(RangeExpr())
self.actual_resistance_dc = self.Parameter(RangeExpr())

def contents(self):
super().contents()
Expand All @@ -118,7 +120,9 @@ def contents(self):
"<b>current rating:</b> ", DescriptionString.FormatUnits(self.actual_current_rating, "A"),
" <b>of operating:</b> ", DescriptionString.FormatUnits(self.current, "A"), "\n",
"<b>frequency rating:</b> ", DescriptionString.FormatUnits(self.actual_frequency_rating, "Hz"),
" <b>of operating:</b> ", DescriptionString.FormatUnits(self.frequency, "Hz")
" <b>of operating:</b> ", DescriptionString.FormatUnits(self.frequency, "Hz"), "\n",
"<b>dc resistance:</b> ", DescriptionString.FormatUnits(self.actual_resistance_dc, "Ω"),
" <b>of spec:</b> ", DescriptionString.FormatUnits(self.resistance_dc, "Ω"),
)


Expand All @@ -132,21 +136,22 @@ class TableInductor(PartsTableSelector, Inductor):
@init_in_parent
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.generator_param(self.inductance, self.current, self.frequency)
self.generator_param(self.inductance, self.current, self.frequency, self.resistance_dc)

def _row_filter(self, row: PartsTableRow) -> bool:
# TODO eliminate arbitrary DCR limit in favor of exposing max DCR to upper levels
return super()._row_filter(row) and \
row[self.INDUCTANCE].fuzzy_in(self.get(self.inductance)) and \
self.get(self.current).fuzzy_in(row[self.CURRENT_RATING]) and \
row[self.DC_RESISTANCE].fuzzy_in(Range.zero_to_upper(1.0)) and \
row[self.DC_RESISTANCE].fuzzy_in(self.get(self.resistance_dc)) and \
self.get(self.frequency).fuzzy_in(row[self.FREQUENCY_RATING])

def _row_generate(self, row: PartsTableRow) -> None:
super()._row_generate(row)
self.assign(self.actual_inductance, row[self.INDUCTANCE])
self.assign(self.actual_current_rating, row[self.CURRENT_RATING])
self.assign(self.actual_frequency_rating, row[self.FREQUENCY_RATING])
self.assign(self.actual_resistance_dc, row[self.DC_RESISTANCE])


class SeriesPowerInductor(DiscreteApplication):
Expand Down
15 changes: 6 additions & 9 deletions edg/abstract_parts/AbstractPowerConverters.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
78 changes: 78 additions & 0 deletions edg/abstract_parts/LevelShifter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from ..electronics_model import *
from .Categories import *
from .AbstractResistor import PullupResistor
from .AbstractFets import Fet
from .DummyDevices import DummyVoltageSink

class BidirectionaLevelShifter(Interface, GeneratorBlock):
"""Bidirectional level shifter for low(ish) frequency signals.
Circuit design from Phillips AN97055, https://cdn-shop.adafruit.com/datasheets/an97055.pdf
When both sides are floating or driving high, the FET is off and the pullups provide the high signal.
When the LV side drives low, the FET source goes to ground, putting the FET into conduction and pulling HV low.
When the HV side drives low, the body diode pulls the FET source low, then goes into conduction.
Use infinity resistance to not generate a resistor, for example if it is known there is already a resistor
on that side.
src_hint = 'lv' | 'hv' | '' determines the 'source' side to help the electronics model resolve directionality
and does not affect circuit generation or functionality.
If empty, both sides are assumed to be able to drive the shifter and must have voltages and output thresholds
modeled. TODO: this mode may be brittle
"""
@init_in_parent
def __init__(self, lv_res: RangeLike = 4.7*kOhm(tol=0.05), hv_res: RangeLike = 4.7*kOhm(tol=0.05),
src_hint: StringLike = '') -> None:
super().__init__()
self.lv_pwr = self.Port(VoltageSink.empty())
self.lv_io = self.Port(DigitalBidir.empty())
self.hv_pwr = self.Port(VoltageSink.empty())
self.hv_io = self.Port(DigitalBidir.empty())

self.lv_res = self.ArgParameter(lv_res)
self.hv_res = self.ArgParameter(hv_res)
self.src_hint = self.ArgParameter(src_hint)
self.generator_param(self.lv_res, self.hv_res, self.src_hint)

def generate(self) -> None:
super().generate()

self.fet = self.Block(Fet.NFet(
drain_voltage=self.hv_pwr.link().voltage.hull(self.hv_io.link().voltage),
drain_current=self.lv_io.link().current_drawn.hull(self.hv_io.link().current_drawn),
gate_voltage=self.lv_pwr.link().voltage - self.lv_io.link().voltage,
rds_on=(0, 1)*Ohm # arbitrary
))

if self.get(self.src_hint) == 'lv': # LV is source, HV model is incomplete
lv_io_model = DigitalBidir(
voltage_out=self.lv_pwr.link().voltage, # this is not driving, effectively only a pullup
output_thresholds=self.lv_pwr.link().voltage.hull(-float('inf'))
)
else: # HV model is complete, can use its thresholds
lv_io_model = DigitalBidir(
voltage_out=self.lv_pwr.link().voltage.hull(self.hv_io.link().voltage.lower()),
output_thresholds=self.lv_pwr.link().voltage.hull(self.hv_io.link().voltage.lower())
)

if self.get(self.src_hint) == 'hv': # HV is source, LV model is incomplete
hv_io_model = DigitalBidir(
voltage_out=self.hv_pwr.link().voltage, # this is not driving, effectively only a pullup
output_thresholds=self.hv_pwr.link().voltage.hull(-float('inf'))
)
else: # HV model is complete, can use its thresholds
hv_io_model = DigitalBidir(
voltage_out=self.hv_pwr.link().voltage.hull(self.lv_io.link().voltage.lower()),
output_thresholds=self.hv_pwr.link().voltage.hull(self.lv_io.link().voltage.lower())
)

self.connect(self.lv_io, self.fet.source.adapt_to(lv_io_model))
self.connect(self.hv_io, self.fet.drain.adapt_to(hv_io_model))
self.connect(self.lv_pwr, self.fet.gate.adapt_to(VoltageSink()))

if self.get(self.lv_res) != RangeExpr.INF:
self.lv_pu = self.Block(PullupResistor(self.lv_res)).connected(self.lv_pwr, self.lv_io)
if self.get(self.hv_res) != RangeExpr.INF:
self.hv_pu = self.Block(PullupResistor(self.hv_res)).connected(self.hv_pwr, self.hv_io)
else:
self.dummy_hv = self.Block(DummyVoltageSink()) # must be connected
self.connect(self.dummy_hv.pwr, self.hv_pwr)
Loading

0 comments on commit 241affa

Please sign in to comment.