Skip to content

Commit

Permalink
Resettable mixin (#280)
Browse files Browse the repository at this point in the history
Add external reset pin support to microcontrollers via a Resettable
mixin which can be applied to any Block. Refactor VoltageRegulatorEnable
to use the Resettable mixin instead.

Resettable defines an optional reset port, which when not connected
generates internal reset circuitry to keep the device always enabled,
and when connected directly connects the digital reset line (without
generating any interface circuitry - it is up to the system designer to
create a proper circuit).

Also break up big SWD, by separating out the optional (from the spec
points of view) reset line. SWD connector now only defines the base gnd,
power, and SWD lines, with reset, SWO, JTAG TDI moved into optional
mixins.
  • Loading branch information
ducky64 authored Aug 10, 2023
1 parent 77fbd01 commit f557b64
Show file tree
Hide file tree
Showing 45 changed files with 400 additions and 282 deletions.
2 changes: 1 addition & 1 deletion edg/BoardTop.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def refinements(self) -> Refinements:
(CanEsdDiode, Pesd1can),
(TestPoint, TeRc),

(SwdCortexTargetWithSwoTdiConnector, SwdCortexTargetHeader),
(SwdCortexTargetConnector, SwdCortexTargetHeader),

(SpiMemory, W25q),

Expand Down
2 changes: 1 addition & 1 deletion edg_core/BlockInterfaceMixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def _get_mixin_base(cls) -> Type['BlockInterfaceMixin']:
mixin_base = bcls_args[0]
if mixin_base is None:
raise BlockDefinitionError(cls, "no mixin base defined")
if (mixin_base, AbstractBlockProperty) not in mixin_base._elt_properties:
if (mixin_base, AbstractBlockProperty) not in mixin_base._elt_properties and mixin_base is not Block:
raise BlockDefinitionError(cls, "mixin base must be abstract")
return mixin_base

Expand Down
23 changes: 16 additions & 7 deletions electronics_abstract_parts/AbstractDebugHeaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,22 @@ def __init__(self) -> None:
self.swd = self.Port(SwdHostPort.empty(), [Output])


@abstract_block
class SwdCortexTargetWithSwoTdiConnector(SwdCortexTargetConnector):
"""SWD programming header (power + SWD) with additional optional and generic-digital
SWO (optional SWD pin) and TDI (if a JTAG header is used) pins which can be used as GPIOs
for side-channel data like a supplementary UART console."""
def __init__(self) -> None:
super().__init__()
class SwdCortexTargetConnectorReset(BlockInterfaceMixin[SwdCortexTargetConnector]):
"""Mixin for SWD connectors with adding the optional reset pin"""
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.reset = self.Port(DigitalSource.empty())


class SwdCortexTargetConnectorSwo(BlockInterfaceMixin[SwdCortexTargetConnector]):
"""Mixin for SWD connectors with adding the optional SWO pin"""
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.swo = self.Port(DigitalBidir.empty(), optional=True)


class SwdCortexTargetConnectorTdi(BlockInterfaceMixin[SwdCortexTargetConnector]):
"""Mixin for SWD connectors with adding the NONSTANDARD TDI pin (where pins are shared with JTAG)"""
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.tdi = self.Port(DigitalBidir.empty(), optional=True)
42 changes: 18 additions & 24 deletions electronics_abstract_parts/AbstractPowerConverters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .Categories import *
from .AbstractCapacitor import DecouplingCapacitor
from .AbstractInductor import Inductor
from .Resettable import Resettable


@abstract_block_default(lambda: IdealVoltageRegulator)
Expand Down Expand Up @@ -33,32 +34,25 @@ def contents(self):
"Output voltage must be within spec")


class VoltageRegulatorEnable(BlockInterfaceMixin[VoltageRegulator]):
"""Mixin for VoltageRegulator with an active-high enable pin (or active-low disable pin)."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.enable = self.Port(DigitalSink.empty(), optional=True)


class VoltageRegulatorEnableWrapper(VoltageRegulatorEnable, VoltageRegulator, GeneratorBlock):
"""Implementation mixin for a voltage regulator wrapper block where the inner device has an enable pin
class VoltageRegulatorEnableWrapper(Resettable, VoltageRegulator, GeneratorBlock):
"""Implementation mixin for a voltage regulator wrapper block where the inner device has a reset/enable pin
(active-high enable / active-low shutdown) that is automatically tied high if not externally connected.
Mix this into a VoltageRegulator to automatically handle the enable pin."""
Mix this into a VoltageRegulator to automatically handle the reset pin."""
@abstractmethod
def _generator_inner_enable_pin(self) -> Port[DigitalLink]:
"""Returns the inner device's enable pin, to be connected in the generator.
def _generator_inner_reset_pin(self) -> Port[DigitalLink]:
"""Returns the inner device's reset pin, to be connected in the generator.
Only called within a generator."""

def contents(self):
super().contents()
self.generator_param(self.enable.is_connected())
self.generator_param(self.reset.is_connected())

def generate(self):
super().generate()
if self.get(self.enable.is_connected()):
self.connect(self.enable, self._generator_inner_enable_pin())
if self.get(self.reset.is_connected()):
self.connect(self.reset, self._generator_inner_reset_pin())
else: # by default tie high to enable regulator
self.connect(self.pwr_in.as_digital_source(), self._generator_inner_enable_pin())
self.connect(self.pwr_in.as_digital_source(), self._generator_inner_reset_pin())


@abstract_block_default(lambda: IdealLinearRegulator)
Expand All @@ -71,7 +65,7 @@ class VoltageReference(LinearRegulator):
"""Voltage reference, generally provides high accuracy but limited current"""


class IdealLinearRegulator(VoltageRegulatorEnable, LinearRegulator, IdealModel):
class IdealLinearRegulator(Resettable, LinearRegulator, IdealModel):
"""Ideal linear regulator, draws the output current and produces spec output voltage limited by input voltage"""
def contents(self):
super().contents()
Expand All @@ -81,7 +75,7 @@ def contents(self):
current_draw=self.pwr_out.link().current_drawn))
self.pwr_out.init_from(VoltageSource(
voltage_out=effective_output_voltage))
self.enable.init_from(DigitalSink())
self.reset.init_from(DigitalSink())


@non_library
Expand Down Expand Up @@ -173,7 +167,7 @@ class DiscreteBuckConverter(BuckConverter):
"""Category for discrete buck converter subcircuits (as opposed to integrated components)"""


class IdealBuckConverter(VoltageRegulatorEnable, DiscreteBuckConverter, IdealModel):
class IdealBuckConverter(Resettable, DiscreteBuckConverter, IdealModel):
"""Ideal buck converter producing the spec output voltage (buck-boost) limited by input voltage
and drawing input current from conversation of power"""
def contents(self):
Expand All @@ -184,7 +178,7 @@ def contents(self):
current_draw=effective_output_voltage / self.pwr_in.link().voltage * self.pwr_out.link().current_drawn))
self.pwr_out.init_from(VoltageSource(
voltage_out=effective_output_voltage))
self.enable.init_from(DigitalSink())
self.reset.init_from(DigitalSink())


class BuckConverterPowerPath(InternalSubcircuit, GeneratorBlock):
Expand Down Expand Up @@ -334,7 +328,7 @@ class DiscreteBoostConverter(BoostConverter):
"""Category for discrete boost converter subcircuits (as opposed to integrated components)"""


class IdealBoostConverter(VoltageRegulatorEnable, DiscreteBoostConverter, IdealModel):
class IdealBoostConverter(Resettable, DiscreteBoostConverter, IdealModel):
"""Ideal boost converter producing the spec output voltage (buck-boost) limited by input voltage
and drawing input current from conversation of power"""
def contents(self):
Expand All @@ -345,7 +339,7 @@ def contents(self):
current_draw=effective_output_voltage / self.pwr_in.link().voltage * self.pwr_out.link().current_drawn))
self.pwr_out.init_from(VoltageSource(
voltage_out=effective_output_voltage))
self.enable.init_from(DigitalSink())
self.reset.init_from(DigitalSink())


class BoostConverterPowerPath(InternalSubcircuit, GeneratorBlock):
Expand Down Expand Up @@ -485,7 +479,7 @@ class DiscreteBuckBoostConverter(BuckBoostConverter):
"""Category for discrete buck-boost converter subcircuits (as opposed to integrated components)"""


class IdealVoltageRegulator(VoltageRegulatorEnable, DiscreteBuckBoostConverter, IdealModel):
class IdealVoltageRegulator(Resettable, DiscreteBuckBoostConverter, IdealModel):
"""Ideal buck-boost / general DC-DC converter producing the spec output voltage
and drawing input current from conversation of power"""
def contents(self):
Expand All @@ -495,7 +489,7 @@ def contents(self):
current_draw=self.output_voltage / self.pwr_in.link().voltage * self.pwr_out.link().current_drawn))
self.pwr_out.init_from(VoltageSource(
voltage_out=self.output_voltage))
self.enable.init_from(DigitalSink())
self.reset.init_from(DigitalSink())


class BuckBoostConverterPowerPath(InternalSubcircuit, GeneratorBlock):
Expand Down
15 changes: 9 additions & 6 deletions electronics_abstract_parts/IoControllerProgramming.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from electronics_model import *
from .AbstractDebugHeaders import SwdCortexTargetWithSwoTdiConnector
from .AbstractDebugHeaders import SwdCortexTargetConnector, SwdCortexTargetConnectorReset, SwdCortexTargetConnectorSwo, \
SwdCortexTargetConnectorTdi
from .IoController import BaseIoControllerExportable, IoController


Expand All @@ -18,13 +19,15 @@ def __init__(self, swd_swo_pin: StringLike = "NC", swd_tdi_pin: StringLike = "NC
self.swd_tdi_pin = self.ArgParameter(swd_tdi_pin)
self.generator_param(self.swd_swo_pin, self.swd_tdi_pin)
self.swd_node = self.connect() # connect this internal node to the microcontroller
self.reset_node = self.connect() # connect this internal node to the microcontroller

def contents(self):
super().contents()
self.swd = self.Block(SwdCortexTargetWithSwoTdiConnector())
self.connect(self.swd_node, self.swd.swd)
self.connect(self.swd.pwr, self.pwr)
self.swd = self.Block(SwdCortexTargetConnector())
self.connect(self.swd.gnd, self.gnd)
self.connect(self.swd.pwr, self.pwr)
self.connect(self.swd_node, self.swd.swd)
self.connect(self.reset_node, self.swd.with_mixin(SwdCortexTargetConnectorReset()).reset)

def _inner_pin_assigns(self) -> list[str]:
pin_assigns = super()._inner_pin_assigns()
Expand All @@ -37,6 +40,6 @@ def _inner_pin_assigns(self) -> list[str]:
def generate(self):
super().generate()
if self.get(self.swd_swo_pin) != 'NC':
self.connect(self.ic.gpio.request('swd_swo'), self.swd.swo)
self.connect(self.ic.gpio.request('swd_swo'), self.swd.with_mixin(SwdCortexTargetConnectorSwo()).swo)
if self.get(self.swd_tdi_pin) != 'NC':
self.connect(self.ic.gpio.request('swd_tdi'), self.swd.tdi)
self.connect(self.ic.gpio.request('swd_tdi'), self.swd.with_mixin(SwdCortexTargetConnectorTdi()).tdi)
15 changes: 10 additions & 5 deletions electronics_abstract_parts/PassiveFilters.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,23 @@ class PullupDelayRc(DigitalFilter, Block):
@init_in_parent
def __init__(self, impedance: RangeLike, time_constant: RangeLike):
super().__init__()
self.input = self.Port(VoltageSink.empty(), [Power])
self.pwr = self.Port(VoltageSink.empty(), [Power])
self.time_constant = self.ArgParameter(time_constant)

self.rc = self.Block(LowPassRc(impedance=impedance, cutoff_freq=1/(2 * pi * self.time_constant),
voltage=self.input.link().voltage))
voltage=self.pwr.link().voltage))

self.connect(self.input, self.rc.input.adapt_to(VoltageSink()))
self.io = self.Export(self.rc.output.adapt_to(DigitalSingleSource.high_from_supply(self.input)), [Output])
self.connect(self.pwr, self.rc.input.adapt_to(VoltageSink()))
self.io = self.Export(self.rc.output.adapt_to(DigitalSingleSource.high_from_supply(self.pwr)), [Output])
self.gnd = self.Export(self.rc.gnd.adapt_to(Ground()), [Common])

def connected(self, io: Optional[Port[DigitalLink]] = None) -> 'PullupDelayRc':
def connected(self, *, gnd: Optional[Port[VoltageLink]] = None, pwr: Optional[Port[VoltageLink]] = None,
io: Optional[Port[DigitalLink]] = None) -> 'PullupDelayRc':
"""Convenience function to connect both ports, returning this object so it can still be given a name."""
if gnd is not None:
cast(Block, builder.get_enclosing_block()).connect(gnd, self.gnd)
if pwr is not None:
cast(Block, builder.get_enclosing_block()).connect(pwr, self.pwr)
if io is not None:
cast(Block, builder.get_enclosing_block()).connect(io, self.io)
return self
Expand Down
22 changes: 22 additions & 0 deletions electronics_abstract_parts/Resettable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from electronics_model import *


class Resettable(BlockInterfaceMixin[Block]):
"""Mixin for all devices that specifies a digital reset pin (active-low reset / active-high enable).
THIS IS AN ADVANCED FEATURE - BE SURE TO UNDERSTAND THE RESET REQUIREMENTS OF DEVICES.
When disconnected (mixin not used or port not connected), reset circuitry is automatically generated if needed.
When connected, no additional reset circuitry is generated and the system designer is responsible for providing
appropriate reset signals.
Note that some chips have built-in pull-ups on their reset lines, these are not affected, but no external reset
circuitry will be generated.
Devices may optionally require the reset pin where a power-on reset pulse is required and tying / pulling the
pin high is insufficient.
Microcontrollers may generate internal programming connectors that drive this signal, and system designers must
connect microcontroller resets with this in mind - for example, only driving them in open-drain mode.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.reset = self.Port(DigitalSink.empty(), optional=True)
6 changes: 4 additions & 2 deletions electronics_abstract_parts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from .ResistiveDivider import FeedbackVoltageDivider, SignalDivider
from .PassiveFilters import LowPassRc, DigitalLowPassRc, DigitalLowPassRcArray, LowPassRcDac, PullupDelayRc
from .I2cPullup import I2cPullup
from .Resettable import Resettable

from .AbstractDiodes import BaseDiode, Diode, BaseDiodeStandardFootprint, TableDiode
from .AbstractDiodes import ZenerDiode, TableZenerDiode, ProtectionZenerDiode
Expand All @@ -54,15 +55,16 @@
from .AbstractSpiMemory import SpiMemory
from .OpampCurrentSensor import OpampCurrentSensor
from .DigitalAmplifiers import HighSideSwitch, HalfBridgeNFet, OpenDrainDriver
from .AbstractPowerConverters import VoltageRegulator, VoltageRegulatorEnable, VoltageRegulatorEnableWrapper
from .AbstractPowerConverters import VoltageRegulator, VoltageRegulatorEnableWrapper
from .AbstractPowerConverters import LinearRegulator, VoltageReference, LinearRegulatorDevice, SwitchingVoltageRegulator
from .AbstractPowerConverters import BuckConverter, DiscreteBuckConverter, BoostConverter, DiscreteBoostConverter
from .AbstractPowerConverters import BuckConverterPowerPath, BoostConverterPowerPath, BuckBoostConverterPowerPath
from .AbstractLedDriver import LedDriver, LedDriverPwm, LedDriverSwitchingConverter
from .AbstractFuse import Fuse, PptcFuse, FuseStandardFootprint, TableFuse, SeriesPowerPptcFuse
from .AbstractCrystal import Crystal, TableCrystal, OscillatorReference, CeramicResonator
from .AbstractOscillator import Oscillator, TableOscillator
from .AbstractDebugHeaders import SwdCortexTargetConnector, SwdCortexTargetWithSwoTdiConnector
from .AbstractDebugHeaders import SwdCortexTargetConnector, SwdCortexTargetConnectorReset, \
SwdCortexTargetConnectorSwo, SwdCortexTargetConnectorTdi
from .AbstractTestPoint import TestPoint, VoltageTestPoint, DigitalTestPoint, DigitalArrayTestPoint, AnalogTestPoint
from .AbstractTestPoint import I2cTestPoint, CanControllerTestPoint
from .AbstractJumper import Jumper, DigitalJumper
Expand Down
2 changes: 1 addition & 1 deletion electronics_lib/BoostConverters_AnalogDevices.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Ltc3429(VoltageRegulatorEnableWrapper, DiscreteBoostConverter):
"""Low-input-voltage boost converter (starts as low as 0.85V).
Pin-compatible with the less-expensive UM3429S"""
NMOS_CURRENT_LIMIT = 0.6
def _generator_inner_enable_pin(self) -> Port[DigitalLink]:
def _generator_inner_reset_pin(self) -> Port[DigitalLink]:
return self.ic.nshdn

def contents(self):
Expand Down
2 changes: 1 addition & 1 deletion electronics_lib/BoostConverters_DiodesInc.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def contents(self):

class Ap3012(VoltageRegulatorEnableWrapper, DiscreteBoostConverter):
"""Adjustable boost converter in SOT-23-5 with integrated switch"""
def _generator_inner_enable_pin(self) -> Port[DigitalLink]:
def _generator_inner_reset_pin(self) -> Port[DigitalLink]:
return self.ic.nshdn

def contents(self):
Expand Down
8 changes: 4 additions & 4 deletions electronics_lib/BoostConverters_Torex.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ def generate(self) -> None:
)


class Xc9142(VoltageRegulatorEnable, DiscreteBoostConverter, GeneratorBlock):
class Xc9142(Resettable, DiscreteBoostConverter, GeneratorBlock):
"""Low-input-voltage boost converter (starts as low as 0.9V) with fixed output.
XC9142 has PWM/PFM functionality, compared to PWM only for XC9141.
Semi pin compatible with XC9140, LTC3525, MAX1724."""
def contents(self):
super().contents()
self.generator_param(self.enable.is_connected())
self.generator_param(self.reset.is_connected())

with self.implicit_connect(
ImplicitConnect(self.pwr_in, [Power]),
Expand All @@ -98,7 +98,7 @@ def contents(self):

def generate(self):
super().generate()
if self.get(self.enable.is_connected()):
self.connect(self.enable, self.ic.ce)
if self.get(self.reset.is_connected()):
self.connect(self.reset, self.ic.ce)
else: # CE resistor: recommended through a <1M resistor; must not be left open
self.ce_res = self.Block(PullupResistor(100*kOhm(tol=0.2))).connected(pwr=self.pwr_in, io=self.ic.ce)
2 changes: 1 addition & 1 deletion electronics_lib/BuckConverter_Ap3418.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def contents(self) -> None:

class Ap3418(VoltageRegulatorEnableWrapper, DiscreteBuckConverter):
"""Adjustable synchronous buck converter in SOT-23-5 with integrated switch"""
def _generator_inner_enable_pin(self) -> Port[DigitalLink]:
def _generator_inner_reset_pin(self) -> Port[DigitalLink]:
return self.ic.en

def contents(self):
Expand Down
10 changes: 5 additions & 5 deletions electronics_lib/BuckConverter_TexasInstruments.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def contents(self) -> None:

class Tps561201(VoltageRegulatorEnableWrapper, DiscreteBuckConverter):
"""Adjustable synchronous buck converter in SOT-23-6 with integrated switch"""
def _generator_inner_enable_pin(self) -> Port[DigitalLink]:
def _generator_inner_reset_pin(self) -> Port[DigitalLink]:
return self.ic.en

def contents(self):
Expand Down Expand Up @@ -122,14 +122,14 @@ def contents(self) -> None:
self.assign(self.actual_basic_part, False)


class Tps54202h(VoltageRegulatorEnable, DiscreteBuckConverter, GeneratorBlock):
class Tps54202h(Resettable, DiscreteBuckConverter, GeneratorBlock):
"""Adjustable synchronous buck converter in SOT-23-6 with integrated switch, 4.5-24v capable
Note: TPS54202 has frequency spread-spectrum operation and internal pull-up on EN
TPS54202H has no internal EN pull-up but a Zener diode clamp to limit voltage.
"""
def contents(self):
super().contents()
self.generator_param(self.enable.is_connected())
self.generator_param(self.reset.is_connected())

self.assign(self.frequency, (390, 590)*kHertz)

Expand Down Expand Up @@ -169,8 +169,8 @@ def contents(self):

def generate(self):
super().generate()
if self.get(self.enable.is_connected()):
self.connect(self.enable, self.ic.en)
if self.get(self.reset.is_connected()):
self.connect(self.reset, self.ic.en)
else: # by default tie high to enable regulator
# an internal 6.9v Zener clamps the enable voltage, datasheet recommends at 510k resistor
# a pull-up resistor isn't used because
Expand Down
Loading

0 comments on commit f557b64

Please sign in to comment.