Source code for jitx.substrate
"""
Substrate definitions and fabrication constraints
=================================================
This module provides classes for defining substrates with routing structures
and fabrication constraints for manufacturing.
"""
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass
from typing import Protocol
from jitx._structural import Critical, Structural
from jitx.decorators import early
from jitx.context import Context
from jitx.inspect import decompose
from jitx.stackup import Stackup
from jitx.si import DifferentialRoutingStructure, RoutingStructure
from jitx.toleranced import Toleranced
from jitx.units import PlainQuantity, ohm
class _HasImpedance(Protocol):
impedance: PlainQuantity
def _lookup_closest[T: _HasImpedance](
impedance: float | Toleranced | PlainQuantity, structures: Iterable[T]
) -> T:
"""Look up the closest routing structure for a given impedance.
Args:
impedance: Target impedance value.
structures: Mapping of impedance values to routing structures.
Returns:
The closest matching routing structure.
Raises:
ValueError: If no applicable routing structure is found.
"""
if isinstance(impedance, PlainQuantity):
search = ohm.m_from(impedance)
else:
search = impedance
search = Toleranced.exact(search)
selected = min(
(
struc
for struc in structures
if search.in_range(ohm.m_from(struc.impedance, strict=False))
),
key=lambda struc: abs(ohm.m_from(struc.impedance) - search.typ),
default=None,
)
if selected:
return selected
raise ValueError(f"No applicable routing structure found for {impedance} Ohms")
[docs]
class Substrate(Structural, early=True):
"""Substrate definition with routing structures and fabrication constraints.
Substrates can be equipped with routing and differential routing structures
which will be introspected when an attempt to lookup a particular impedance
is made.
A substrate should also contain the permissible :py:class:`Via` types as
well as the :py:class:`FabricationConstraints`.
>>> class MySubstrate(Substrate):
... constraints = MyFabricationConstraints()
...
... class THVia(Via):
... start_layer = 0
... stop_layer = 1
... diameter = 0.45
... hole_diameter = 0.3
... type = ViaType.MechanicalDrill
...
... RS_50 = RoutingStructure(symmetric_routing_layers(
... name="RS_50",
... impedance=50 * ohm,
... layers=symmetric_routing_layers({
... 0: RoutingLayer(
... trace_width=0.1176,
... clearance=0.2,
... velocity=191335235228,
... insertion_loss=0.0178,
... )
... })
... )
>>> MySubstrate().routing_structure(50 * ohm)
RoutingStructure(name="RS_50", impedance=50 Ω)
"""
stackup: Stackup
constraints: FabricationConstraints
@early
def __setup(self):
SubstrateContext(self).set()
[docs]
def routing_structure(
self, impedance: Toleranced | PlainQuantity | float
) -> RoutingStructure:
"""
Look up a routing structure for a given impedance. The default
implementation introspects the substrate for
:py:class:`RoutingStructure` objects. Override this method if you need
to implement a custom lookup mechanism.
"""
return _lookup_closest(impedance, decompose(self, RoutingStructure))
[docs]
def differential_routing_structure(
self, impedance: Toleranced | float
) -> DifferentialRoutingStructure:
"""
Look up a differential routing structure for a given impedance. The
default implementation introspects the substrate for
:py:class:`RoutingStructure` objects. Override this method if you need
to implement a custom lookup mechanism.
"""
return _lookup_closest(impedance, decompose(self, DifferentialRoutingStructure))
[docs]
@dataclass
class SubstrateContext(Context):
"""Access the substrate of the current design. Note that for normal use
there's a :py:attr:`jitx.Current.subtrate` convenience property that can be
used instead.
>>> class MyDesign(Design):
... substrate = MySubstrate()
... circuit = MyCircuit()
>>> class MyCircuit(Circuit):
... def __init__(self):
... assert isinstance(SubstrateContext.require().substrate, MySubstrate)
... # or the preferred and equivalent
... assert isinstance(jitx.current.substrate, MySubstrate)
"""
substrate: Substrate
"""The substrate in the current design context."""
[docs]
class FabricationConstraints(Critical):
"""Fabrication constraints for a substrate. These constraints are used to
ensure that the design is manufacturable. Unless otherwise specified, these
constraints are not enforced by the jitx engine. They are used for
documentation purposes and can be queried by user code to generate
appropriate design elements."""
min_copper_width: float
"""Minimum permissible copper width. This constraint will be enforced by
the engine for generated copper shapes and will take precedence over other
constraints and rules, such as trace width."""
min_copper_copper_space: float
"""Minimum permissible copper-to-copper spacing. This constraint will be
enforced by the engine for generated copper shapes and will take precedence
over other constraints and rules, such as clearance."""
min_copper_hole_space: float
"""Minimum permissible copper-to-hole spacing. This constraint will be
enforced by the engine for generated copper shapes and will take precedence
over other constraints and rules, such as clearance."""
min_copper_edge_space: float
"""Minimum permissible copper-to-board-edge spacing. This constraint will be
enforced by the engine for generated copper shapes and will take precedence
over other constraints and rules, such as clearance."""
min_annular_ring: float
"""Minimum annular ring around a hole or via."""
min_drill_diameter: float
"""Minimum diameter of a hole either in a pad or a via."""
min_pitch_leaded: float
"""Minimum distance between pad centers for leaded packages."""
min_pitch_bga: float
"""Minimum distance between pad centers for BGA packages."""
max_board_width: float
"""Maximum width of a board."""
max_board_height: float
"""Maximum height of a board."""
min_silkscreen_width: float
"""Minimum width of silkscreen."""
min_silk_solder_mask_space: float
"""Minimum distance between silkscreen and soldermask features."""
min_silkscreen_text_height: float
"""Minimum height of silkscreen text."""
solder_mask_registration: float
"""Minimum distance between soldermask and the edge of a copper pad."""
min_soldermask_opening: float
"""Minimum size of a soldermask opening shape."""
min_soldermask_bridge: float
"""Minimum distance between two soldermask features."""
min_th_pad_expand_outer: float
"""Minimum through-hole pad expansion on outer layers."""
min_hole_to_hole: float
"""Minimum distance between two holes, such as through-hole pads or vias."""
min_pth_pin_solder_clearance: float
"""Minimum distance from the outer edge of a through-hole pad to the
soldermask."""