mirror of
https://github.com/diodeinc/pcb.git
synced 2026-09-14 20:17:19 +08:00
78b63cd9c1
* Sync units, board_config Signed-off-by: akhilles <akhilvelagapudi@gmail.com> * Parse netclasses Signed-off-by: akhilles <akhilvelagapudi@gmail.com> * Auto assign netclasses by matching impedances Signed-off-by: akhilles <akhilvelagapudi@gmail.com> * Only match net -> SE, DiffPair -> diff Signed-off-by: akhilles <akhilvelagapudi@gmail.com> --------- Signed-off-by: akhilles <akhilvelagapudi@gmail.com>
693 lines
24 KiB
Python
693 lines
24 KiB
Python
load("units.zen", "Impedance")
|
|
|
|
# Copper constraints - all clearances and widths for copper features
|
|
Copper = record(
|
|
minimum_clearance=field(float | None, None), # Minimum clearance between copper features
|
|
minimum_track_width=field(float | None, None), # Minimum track width
|
|
minimum_connection_width=field(float | None, None), # Minimum connection width (KiCad: m_MinConn)
|
|
minimum_annular_width=field(float | None, None), # Minimum annular ring width for vias
|
|
minimum_via_diameter=field(float | None, None), # Minimum via diameter
|
|
copper_to_hole_clearance=field(float | None, None), # Clearance from copper to holes
|
|
copper_to_edge_clearance=field(float | None, None), # Clearance from copper to board edge
|
|
)
|
|
|
|
# Hole constraints - drill sizes and clearances
|
|
Holes = record(
|
|
minimum_through_hole=field(float | None, None), # Minimum through hole diameter
|
|
hole_to_hole_clearance=field(float | None, None), # Minimum clearance between holes
|
|
)
|
|
|
|
# Micro via constraints - specialized via rules
|
|
Uvias = record(
|
|
minimum_uvia_diameter=field(float | None, None), # Minimum micro via diameter
|
|
minimum_uvia_hole=field(float | None, None), # Minimum micro via hole diameter
|
|
)
|
|
|
|
# Silkscreen constraints - text and graphics rules
|
|
Silkscreen = record(
|
|
minimum_item_clearance=field(float | None, None), # Minimum clearance for silkscreen items
|
|
minimum_text_height=field(float | None, None), # Minimum text height
|
|
# Note: minimum_text_thickness is complex in KiCad API, not supported yet
|
|
)
|
|
|
|
# All design rule constraints grouped together
|
|
Constraints = record(
|
|
copper=field(Copper | None, None),
|
|
holes=field(Holes | None, None),
|
|
uvias=field(Uvias | None, None),
|
|
silkscreen=field(Silkscreen | None, None),
|
|
)
|
|
|
|
# Via dimensions for pre-defined sizes
|
|
ViaDimension = record(
|
|
diameter=field(float | None, None), # Via diameter in mm
|
|
drill=field(float | None, None), # Via drill size in mm
|
|
)
|
|
|
|
# NOTE: Differential pair dimensions are not supported due to missing SWIG template
|
|
# in KiCad Python API. The following would be needed in board.i:
|
|
# %template(DIFF_PAIR_DIMENSION_Vector) std::vector<DIFF_PAIR_DIMENSION>;
|
|
#
|
|
# DiffPairDimension = record(
|
|
# width=float, # Track width in mm
|
|
# gap=float, # Gap between tracks in mm
|
|
# via_gap=float, # Gap for vias in mm
|
|
# )
|
|
|
|
# Pre-defined sizes that appear in KiCad GUI dropdowns
|
|
PredefinedSizes = record(
|
|
track_widths=field(list | None, None), # List of track widths in mm (e.g. [0.1, 0.15, 0.2])
|
|
via_dimensions=field(list | None, None), # List of ViaDimension objects
|
|
# diff_pair_dimensions=list, # Not supported - missing KiCad SWIG template
|
|
)
|
|
|
|
# Netclass definition for PCB routing rules
|
|
NetClass = record(
|
|
name=field(str | None, None), # Netclass name
|
|
clearance=field(float | None, None), # Clearance in mm
|
|
track_width=field(float | None, None), # Track width in mm
|
|
via_diameter=field(float | None, None), # Via diameter in mm
|
|
via_drill=field(float | None, None), # Via drill/hole in mm
|
|
microvia_diameter=field(float | None, None), # Microvia diameter in mm
|
|
microvia_drill=field(float | None, None), # Microvia drill/hole in mm
|
|
diff_pair_width=field(float | None, None), # Differential pair width in mm
|
|
diff_pair_gap=field(float | None, None), # Differential pair gap in mm
|
|
diff_pair_via_gap=field(float | None, None), # Differential pair via gap in mm
|
|
priority=field(int | None, None), # Priority for netclass resolution (higher = higher priority)
|
|
color=field(str | None, None), # PCB color (hex like "#FF0000" or CSS name)
|
|
single_ended_impedance=field(Impedance | None, None),
|
|
differential_pair_impedance=field(Impedance | None, None),
|
|
)
|
|
|
|
# Design rules container
|
|
DesignRules = record(
|
|
constraints=field(Constraints | None, None),
|
|
predefined_sizes=field(PredefinedSizes | None, None),
|
|
netclasses=field(list | None, None), # List of NetClass objects
|
|
)
|
|
|
|
# Material definition for stackup layers
|
|
Material = record(
|
|
name=field(str | None, None), # Material name
|
|
vendor=field(str | None, None), # Optional vendor
|
|
relative_permittivity=field(float | None, None), # Epsilon R (dielectric constant)
|
|
loss_tangent=field(float | None, None), # Loss tangent
|
|
reference_frequency=field(float | None, None), # Optional frequency in Hz
|
|
)
|
|
|
|
# Copper layer definition for stackup
|
|
CopperLayer = record(
|
|
thickness=field(float | None, None), # Thickness in mm
|
|
role=field(str | None, None), # "signal", "power", "mixed"
|
|
)
|
|
|
|
# Dielectric layer definition for stackup
|
|
DielectricLayer = record(
|
|
thickness=field(float | None, None), # Thickness in mm
|
|
material=field(str | None, None), # Material name (ref to materials list)
|
|
form=field(str | None, None), # "core" or "prepreg"
|
|
)
|
|
|
|
# Board stackup configuration
|
|
# Default soldermask thickness assumptions (not currently configurable):
|
|
# - On FR-4: 1.2 mil (0.03048 mm)
|
|
# - On Copper: 0.6 mil (0.01524 mm)
|
|
# - Between Traces: 1.2 mil (0.03048 mm)
|
|
# - Dielectric Constant: 3.8
|
|
Stackup = record(
|
|
materials=field(list[Material] | None, None), # List of Material objects
|
|
silk_screen_color=field(str | None, None), # Hex color like "#44805BFF"
|
|
solder_mask_color=field(str | None, None), # Hex color like "#191919E6"
|
|
thickness=field(float | None, None), # Total thickness in mm
|
|
symmetric=field(bool | None, None), # Assert symmetric stackup (default: True)
|
|
layers=field(list[CopperLayer | DielectricLayer] | None, None), # Ordered list of stackup layers
|
|
copper_finish=field(str | None, None), # Surface finish: "ENIG", "HAL SnPb", "HAL lead-free"
|
|
)
|
|
|
|
# Complete board configuration
|
|
BoardConfig = record(
|
|
design_rules=field(DesignRules | None, None),
|
|
stackup=field(Stackup | None, None), # Board stackup configuration
|
|
num_user_layers=field(int, 4), # Number of User.N layers (User.1, User.2, etc.)
|
|
spec=field(str | None, None), # Path to a specification file
|
|
)
|
|
|
|
|
|
def deep_merge(base, override):
|
|
"""Deep merge two records, with override values taking precedence.
|
|
|
|
For records: recursively merges fields
|
|
For lists: override replaces base entirely
|
|
For primitives: override value used if not None
|
|
"""
|
|
|
|
if override == None:
|
|
return base
|
|
if base == None:
|
|
return override
|
|
|
|
# Handle list types - override replaces entirely
|
|
if type(base) == "list":
|
|
return override if type(override) == "list" else base
|
|
|
|
# Check if both are records by checking if they have getattr/type
|
|
base_type = type(base)
|
|
override_type = type(override)
|
|
|
|
# Only merge if both are record types (not primitive types)
|
|
if base_type == "record" and override_type == "record":
|
|
merged = {}
|
|
|
|
# Get all field names from both records
|
|
base_fields = dir(base)
|
|
override_fields = dir(override)
|
|
all_fields = set(base_fields + override_fields)
|
|
|
|
for field in all_fields:
|
|
if field.startswith("_"): # Skip private fields
|
|
continue
|
|
|
|
base_val = getattr(base, field, None) if field in base_fields else None
|
|
override_val = getattr(override, field, None) if field in override_fields else None
|
|
|
|
if override_val != None:
|
|
if base_val != None and type(base_val) != "list":
|
|
# Recursively merge if both exist and not lists
|
|
merged[field] = deep_merge(base_val, override_val)
|
|
else:
|
|
# Use override value (including for lists)
|
|
merged[field] = override_val
|
|
elif base_val != None:
|
|
# Keep base value when override is None but base has a value
|
|
|
|
merged[field] = base_val
|
|
else:
|
|
# Both are None, keep None
|
|
merged[field] = None
|
|
|
|
# Extract record type from string representation
|
|
base_str = str(base)
|
|
if base_str.startswith("record["):
|
|
type_name = base_str.split("[")[1].split("]")[0]
|
|
if type_name == "BoardConfig":
|
|
return BoardConfig(**merged)
|
|
elif type_name == "DesignRules":
|
|
return DesignRules(**merged)
|
|
elif type_name == "Constraints":
|
|
return Constraints(**merged)
|
|
elif type_name == "PredefinedSizes":
|
|
return PredefinedSizes(**merged)
|
|
elif type_name == "Copper":
|
|
return Copper(**merged)
|
|
elif type_name == "Holes":
|
|
return Holes(**merged)
|
|
elif type_name == "Uvias":
|
|
return Uvias(**merged)
|
|
elif type_name == "Silkscreen":
|
|
return Silkscreen(**merged)
|
|
elif type_name == "ViaDimension":
|
|
return ViaDimension(**merged)
|
|
elif type_name == "NetClass":
|
|
return NetClass(**merged)
|
|
|
|
elif type_name == "Stackup":
|
|
return Stackup(**merged)
|
|
elif type_name == "Material":
|
|
return Material(**merged)
|
|
elif type_name == "CopperLayer":
|
|
return CopperLayer(**merged)
|
|
elif type_name == "DielectricLayer":
|
|
return DielectricLayer(**merged)
|
|
else:
|
|
error("Unknown record type for merge: " + type_name)
|
|
else:
|
|
error("Unable to determine record type from: " + base_str)
|
|
|
|
# Not records, override wins
|
|
return override
|
|
|
|
|
|
def merge_configs(*configs: BoardConfig) -> BoardConfig:
|
|
"""Merge multiple BoardConfig objects, with later configs taking precedence.
|
|
|
|
Args:
|
|
*configs: Variable number of BoardConfig objects to merge
|
|
|
|
Returns:
|
|
BoardConfig: Merged configuration with later configs overriding earlier ones
|
|
"""
|
|
if not configs:
|
|
return BoardConfig()
|
|
|
|
result = configs[0]
|
|
for config in configs[1:]:
|
|
result = deep_merge(result, config)
|
|
|
|
return result
|
|
|
|
|
|
def Board(
|
|
name: str,
|
|
layout_path: str,
|
|
config: BoardConfig | None = None,
|
|
layers: int | None = None,
|
|
layout_hints: list | None = None,
|
|
default: bool = False,
|
|
):
|
|
# If config is not provided, construct it from layers
|
|
if config == None:
|
|
if layers == None:
|
|
error("Either 'config' or 'layers' must be provided")
|
|
elif layers == 2:
|
|
stackup = BASE_2L_STACKUP
|
|
netclasses = BASE_2L_NETCLASSES
|
|
elif layers == 4:
|
|
stackup = BASE_4L_STACKUP
|
|
netclasses = BASE_4L_NETCLASSES
|
|
elif layers == 6:
|
|
stackup = BASE_6L_STACKUP
|
|
netclasses = BASE_6L_NETCLASSES
|
|
elif layers == 8:
|
|
stackup = BASE_8L_STACKUP
|
|
netclasses = BASE_8L_NETCLASSES
|
|
else:
|
|
error("layers must be 2, 4, 6, or 8")
|
|
|
|
design_rules = DesignRules(
|
|
constraints=BASE_CONSTRAINTS,
|
|
predefined_sizes=BASE_PREDEFINED_SIZES,
|
|
netclasses=netclasses,
|
|
)
|
|
config = BoardConfig(
|
|
design_rules=design_rules,
|
|
stackup=stackup,
|
|
)
|
|
|
|
builtin.add_board_config(
|
|
name=name,
|
|
default=default,
|
|
config=config,
|
|
)
|
|
add_property("layout_path", Path(layout_path, allow_not_exist=True))
|
|
if layout_hints:
|
|
add_property("layout_hints", layout_hints)
|
|
|
|
|
|
# Standard Base Configurations
|
|
|
|
# Default netclass for general-purpose PCB manufacturing
|
|
DEFAULT_NETCLASS = NetClass(
|
|
name="Default",
|
|
clearance=0.15, # Conservative clearance (different nets)
|
|
track_width=0.15, # Safe track width for most manufacturers
|
|
via_diameter=0.5, # Standard via size (5.3:1 aspect ratio on 1.6mm board)
|
|
via_drill=0.3, # Standard drill size
|
|
diff_pair_width=0.2, # Reasonable diff pair width
|
|
diff_pair_gap=0.2, # Reasonable diff pair gap
|
|
)
|
|
|
|
# Common materials that can be reused across board configurations
|
|
|
|
# Standard PCB Materials
|
|
FR4_CORE = Material(
|
|
name="FR4-Core",
|
|
relative_permittivity=4.6,
|
|
loss_tangent=0.025, # Typical for standard FR4 core
|
|
)
|
|
|
|
BASE_PREPREG = Material(
|
|
name="Prepreg",
|
|
relative_permittivity=4.4,
|
|
loss_tangent=0.025, # Standard prepreg material
|
|
)
|
|
|
|
# Stackup Design Philosophy
|
|
#
|
|
# Layer convention for 4L/6L/8L stackups:
|
|
# - First 3 layers: SIG/GND/PWR (always)
|
|
# - Last layer: SIG (always)
|
|
#
|
|
# This enables module reusability - a 4L module can be ported to 6L/8L by using the
|
|
# same top 3 layers and bottom layer. Internal layers provide additional routing.
|
|
#
|
|
# Impedance Control:
|
|
# - GCPW (Grounded Coplanar Waveguide) for all impedance-controlled routing
|
|
# - L1 (top) is primary impedance-controlled layer
|
|
# - L6/L8 (bottom) serve as backup impedance-controlled layers on 6L/8L
|
|
# - 8L also provides L6 as symmetric stripline (GND/SIG/GND)
|
|
#
|
|
# Selection:
|
|
# - 2L: Simple, low speed
|
|
# - 4L: Standard, L1 impedance-controlled
|
|
# - 6L: More routing, L1 + L6 impedance-controlled
|
|
# - 8L: High density, L1 + L6 (stripline) + L8 impedance-controlled
|
|
|
|
# Common stackup configurations
|
|
|
|
# Base 2-layer stackup (1.6mm, 1oz outer)
|
|
# SIG/SIG
|
|
# Use case: Simple designs without dedicated power/ground planes
|
|
BASE_2L_STACKUP = Stackup(
|
|
materials=[FR4_CORE],
|
|
thickness=1.6,
|
|
symmetric=True,
|
|
copper_finish="HAL SnPb",
|
|
silk_screen_color="White",
|
|
solder_mask_color="Black",
|
|
layers=[
|
|
CopperLayer(thickness=0.035, role="mixed"), # Top layer (1oz)
|
|
DielectricLayer(thickness=1.53, material="FR4-Core", form="core"),
|
|
CopperLayer(thickness=0.035, role="mixed"), # Bottom layer (1oz)
|
|
],
|
|
)
|
|
BASE_2L_NETCLASSES = [DEFAULT_NETCLASS]
|
|
|
|
# Base 4-layer stackup (1.6mm, 1oz outer/0.5oz inner)
|
|
# SIG/GND/PWR/SIG
|
|
#
|
|
# Layers:
|
|
# L1: Mixed + impedance-controlled (GCPW, ref L2, 0.21mm coupling)
|
|
# L2: GND plane
|
|
# L3: PWR plane
|
|
# L4: Mixed signal
|
|
#
|
|
# Use: Standard stackup with dedicated GND/PWR planes. L1 for high-speed routing.
|
|
BASE_4L_STACKUP = Stackup(
|
|
materials=[FR4_CORE, BASE_PREPREG],
|
|
thickness=1.6,
|
|
symmetric=True,
|
|
copper_finish="HAL SnPb",
|
|
silk_screen_color="White",
|
|
solder_mask_color="Black",
|
|
layers=[
|
|
CopperLayer(thickness=0.035, role="mixed"), # Top layer (1oz)
|
|
DielectricLayer(thickness=0.21040, material="Prepreg", form="prepreg"),
|
|
CopperLayer(thickness=0.0152, role="ground"), # Inner L2 (0.5oz)
|
|
DielectricLayer(thickness=1.065, material="FR4-Core", form="core"),
|
|
CopperLayer(thickness=0.0152, role="power"), # Inner L3 (0.5oz)
|
|
DielectricLayer(thickness=0.21040, material="Prepreg", form="prepreg"),
|
|
CopperLayer(thickness=0.035, role="mixed"), # Bottom layer (1oz)
|
|
],
|
|
)
|
|
|
|
# 4L Impedance-Controlled Netclasses (L1 GCPW, 0.21mm dielectric, 0.2mm coplanar gap)
|
|
# Calculated using Sierra Circuits impedance calculator
|
|
BASE_4L_NETCLASSES = [
|
|
DEFAULT_NETCLASS,
|
|
NetClass(
|
|
name="50Ohm SE",
|
|
clearance=0.2,
|
|
track_width=0.2908,
|
|
color="#0000C2FF",
|
|
single_ended_impedance=Impedance(50),
|
|
),
|
|
NetClass(
|
|
name="85Ohm Diff",
|
|
clearance=0.2,
|
|
track_width=0.2235,
|
|
diff_pair_width=0.2235,
|
|
diff_pair_gap=0.127,
|
|
color="#C200C2FF",
|
|
differential_pair_impedance=Impedance(85),
|
|
),
|
|
NetClass(
|
|
name="90Ohm Diff",
|
|
clearance=0.2,
|
|
track_width=0.1867,
|
|
diff_pair_width=0.1867,
|
|
diff_pair_gap=0.127,
|
|
color="#00C200FF",
|
|
differential_pair_impedance=Impedance(90),
|
|
),
|
|
NetClass(
|
|
name="100Ohm Diff",
|
|
clearance=0.2,
|
|
track_width=0.1334,
|
|
diff_pair_width=0.1334,
|
|
diff_pair_gap=0.127,
|
|
color="#C2C200FF",
|
|
differential_pair_impedance=Impedance(100),
|
|
),
|
|
]
|
|
|
|
# Base 6-layer stackup (1.6mm, 1oz outer/0.5oz inner)
|
|
# SIG/GND/PWR/SIG/GND/SIG
|
|
#
|
|
# Layers:
|
|
# L1: Primary impedance-controlled (GCPW, ref L2, 0.099mm coupling)
|
|
# L2: GND plane
|
|
# L3: PWR plane
|
|
# L4: Mixed signal
|
|
# L5: GND plane
|
|
# L6: Backup impedance-controlled (GCPW, ref L5, 0.099mm coupling)
|
|
#
|
|
# Use: More routing space than 4L. Two ground planes for return paths.
|
|
BASE_6L_STACKUP = Stackup(
|
|
materials=[
|
|
FR4_CORE,
|
|
Material(
|
|
name="2116",
|
|
relative_permittivity=4.16,
|
|
loss_tangent=0.025,
|
|
),
|
|
Material(
|
|
name="3313",
|
|
relative_permittivity=4.1,
|
|
loss_tangent=0.025,
|
|
),
|
|
],
|
|
thickness=1.6,
|
|
symmetric=True,
|
|
copper_finish="HAL SnPb",
|
|
silk_screen_color="White",
|
|
solder_mask_color="Black",
|
|
layers=[
|
|
CopperLayer(thickness=0.035, role="mixed"), # Top layer (1oz)
|
|
DielectricLayer(thickness=0.09940, material="3313", form="prepreg"),
|
|
CopperLayer(thickness=0.0152, role="ground"), # Inner L2 (0.5oz)
|
|
DielectricLayer(thickness=0.55, material="FR4-Core", form="core"),
|
|
CopperLayer(thickness=0.0152, role="power"), # Inner L3 (0.5oz)
|
|
DielectricLayer(thickness=0.11640, material="2116", form="prepreg"),
|
|
CopperLayer(thickness=0.0152, role="mixed"), # Inner L4 (0.5oz)
|
|
DielectricLayer(thickness=0.55, material="FR4-Core", form="core"),
|
|
CopperLayer(thickness=0.0152, role="ground"), # Inner L5 (0.5oz)
|
|
DielectricLayer(thickness=0.09940, material="3313", form="prepreg"),
|
|
CopperLayer(thickness=0.035, role="mixed"), # Bottom layer (1oz)
|
|
],
|
|
)
|
|
|
|
# 6L Impedance-Controlled Netclasses (L1 GCPW, 0.099mm dielectric, 0.2mm coplanar gap)
|
|
# Calculated using Sierra Circuits impedance calculator
|
|
BASE_6L_NETCLASSES = [
|
|
DEFAULT_NETCLASS,
|
|
NetClass(
|
|
name="50Ohm SE",
|
|
clearance=0.2,
|
|
track_width=0.1461,
|
|
color="#0000C2FF",
|
|
single_ended_impedance=Impedance(50),
|
|
),
|
|
NetClass(
|
|
name="85Ohm Diff",
|
|
clearance=0.2,
|
|
track_width=0.141,
|
|
diff_pair_width=0.141,
|
|
diff_pair_gap=0.127,
|
|
color="#C200C2FF",
|
|
differential_pair_impedance=Impedance(85),
|
|
),
|
|
NetClass(
|
|
name="90Ohm Diff",
|
|
clearance=0.2,
|
|
track_width=0.1359,
|
|
diff_pair_width=0.1359,
|
|
diff_pair_gap=0.1524,
|
|
color="#00C200FF",
|
|
differential_pair_impedance=Impedance(90),
|
|
),
|
|
NetClass(
|
|
name="100Ohm Diff",
|
|
clearance=0.2,
|
|
track_width=0.127,
|
|
diff_pair_width=0.127,
|
|
diff_pair_gap=0.2286,
|
|
color="#C2C200FF",
|
|
differential_pair_impedance=Impedance(100),
|
|
),
|
|
]
|
|
|
|
# Base 8-layer stackup (1.6mm, 1oz outer/0.5oz inner)
|
|
# SIG/GND/PWR/SIG/GND/SIG/GND/SIG
|
|
#
|
|
# Layers:
|
|
# L1: Primary impedance-controlled (GCPW, ref L2, 0.116mm coupling)
|
|
# L2: GND plane
|
|
# L3: PWR plane
|
|
# L4: Mixed signal
|
|
# L5: GND plane
|
|
# L6: Secondary impedance-controlled (symmetric stripline, L5/L7 refs)
|
|
# L7: GND plane
|
|
# L8: Backup impedance-controlled (GCPW, ref L7)
|
|
#
|
|
# Use: High routing density. Three ground planes. L6 symmetric stripline for EMI control.
|
|
BASE_8L_STACKUP = Stackup(
|
|
materials=[
|
|
FR4_CORE,
|
|
Material(
|
|
name="2116",
|
|
relative_permittivity=4.16,
|
|
loss_tangent=0.025,
|
|
),
|
|
Material(
|
|
name="1080",
|
|
relative_permittivity=3.91,
|
|
loss_tangent=0.025,
|
|
),
|
|
],
|
|
thickness=1.6,
|
|
symmetric=True,
|
|
copper_finish="HAL SnPb",
|
|
silk_screen_color="White",
|
|
solder_mask_color="Black",
|
|
layers=[
|
|
CopperLayer(thickness=0.035, role="mixed"), # Top layer (1oz)
|
|
DielectricLayer(thickness=0.1164, material="2116", form="prepreg"),
|
|
CopperLayer(thickness=0.0152, role="ground"), # Inner L2 (0.5oz)
|
|
DielectricLayer(thickness=0.3, material="FR4-Core", form="core"),
|
|
CopperLayer(thickness=0.0152, role="power"), # Inner L3 (0.5oz)
|
|
DielectricLayer(thickness=0.0764, material="1080", form="prepreg"),
|
|
DielectricLayer(thickness=0.0764, material="1080", form="prepreg"),
|
|
CopperLayer(thickness=0.0152, role="mixed"), # Inner L4 (0.5oz)
|
|
DielectricLayer(thickness=0.3, material="FR4-Core", form="core"),
|
|
CopperLayer(thickness=0.0152, role="ground"), # Inner L5 (0.5oz)
|
|
DielectricLayer(thickness=0.0764, material="1080", form="prepreg"),
|
|
DielectricLayer(thickness=0.0764, material="1080", form="prepreg"),
|
|
CopperLayer(thickness=0.0152, role="mixed"), # Inner L6 (0.5oz)
|
|
DielectricLayer(thickness=0.3, material="FR4-Core", form="core"),
|
|
CopperLayer(thickness=0.0152, role="ground"), # Inner L7 (0.5oz)
|
|
DielectricLayer(thickness=0.1164, material="2116", form="prepreg"),
|
|
CopperLayer(thickness=0.035, role="mixed"), # Bottom layer (1oz)
|
|
],
|
|
)
|
|
|
|
# 8L Impedance-Controlled Netclasses (L1 GCPW, 0.1164mm dielectric, 0.2mm coplanar gap)
|
|
# Calculated using Sierra Circuits impedance calculator
|
|
# Note: These geometries also work on L6 (asymmetric stripline) with acceptable tolerance:
|
|
# 50Ohm SE → 48.5Ohm (-3%), 85Ohm Diff → 83.5Ohm (-1.7%), 90Ohm Diff → 88.0Ohm (-2.2%), 100Ohm Diff → 97.6Ohm (-2.4%)
|
|
# Worst case is still within reasonable bounds for most applications.
|
|
BASE_8L_NETCLASSES = [
|
|
DEFAULT_NETCLASS,
|
|
NetClass(
|
|
name="50Ohm SE",
|
|
clearance=0.2,
|
|
track_width=0.1702,
|
|
color="#0000C2FF",
|
|
single_ended_impedance=Impedance(50),
|
|
),
|
|
NetClass(
|
|
name="85Ohm Diff",
|
|
clearance=0.2,
|
|
track_width=0.1588,
|
|
diff_pair_width=0.1588,
|
|
diff_pair_gap=0.127,
|
|
color="#C200C2FF",
|
|
differential_pair_impedance=Impedance(85),
|
|
),
|
|
NetClass(
|
|
name="90Ohm Diff",
|
|
clearance=0.2,
|
|
track_width=0.1384,
|
|
diff_pair_width=0.1384,
|
|
diff_pair_gap=0.127,
|
|
color="#00C200FF",
|
|
differential_pair_impedance=Impedance(90),
|
|
),
|
|
NetClass(
|
|
name="100Ohm Diff",
|
|
clearance=0.2,
|
|
track_width=0.127,
|
|
diff_pair_width=0.127,
|
|
diff_pair_gap=0.1778,
|
|
color="#C2C200FF",
|
|
differential_pair_impedance=Impedance(100),
|
|
),
|
|
]
|
|
|
|
# Common constraint sets for different fabrication capabilities
|
|
|
|
# Base PCB Constraints (1oz copper)
|
|
BASE_CONSTRAINTS = Constraints(
|
|
copper=Copper(
|
|
minimum_clearance=0.09, # Min spacing between copper features (1oz)
|
|
minimum_track_width=0.09, # Min track width (1oz)
|
|
minimum_via_diameter=0.25, # Min via diameter (0.25mm)
|
|
copper_to_hole_clearance=0.2, # Inner layer via hole to copper clearance
|
|
copper_to_edge_clearance=0.3, # Reasonable edge clearance
|
|
),
|
|
holes=Holes(
|
|
minimum_through_hole=0.15, # Min via hole size
|
|
hole_to_hole_clearance=0.2, # Via hole-to-hole spacing
|
|
),
|
|
uvias=Uvias(
|
|
minimum_uvia_diameter=0.15, # Micro via diameter (if supported)
|
|
minimum_uvia_hole=0.1, # Micro via hole (if supported)
|
|
),
|
|
silkscreen=Silkscreen(
|
|
minimum_item_clearance=0.1, # Reasonable silkscreen clearance
|
|
minimum_text_height=0.8, # Reasonable minimum text height
|
|
),
|
|
)
|
|
|
|
# Heavy Copper Constraints (2oz copper)
|
|
HEAVY_COPPER_CONSTRAINTS = Constraints(
|
|
copper=Copper(
|
|
minimum_clearance=0.2, # Min spacing (2oz copper)
|
|
minimum_track_width=0.16, # Min track width (2oz copper)
|
|
minimum_via_diameter=0.25, # Same via diameter
|
|
copper_to_hole_clearance=0.2, # Same hole clearance
|
|
copper_to_edge_clearance=0.3, # Same edge clearance
|
|
),
|
|
holes=Holes(
|
|
minimum_through_hole=0.15, # Same hole size
|
|
hole_to_hole_clearance=0.2, # Same hole spacing
|
|
),
|
|
uvias=Uvias(
|
|
minimum_uvia_diameter=0.15,
|
|
minimum_uvia_hole=0.1,
|
|
),
|
|
silkscreen=Silkscreen(
|
|
minimum_item_clearance=0.1,
|
|
minimum_text_height=0.8,
|
|
),
|
|
)
|
|
|
|
|
|
# Common predefined sizes for design tool dropdowns
|
|
|
|
# Base predefined sizes for common PCB design
|
|
BASE_PREDEFINED_SIZES = PredefinedSizes(
|
|
track_widths=[
|
|
0.127,
|
|
0.2,
|
|
0.4,
|
|
0.8,
|
|
],
|
|
via_dimensions=[
|
|
ViaDimension(diameter=0.4, drill=0.2),
|
|
ViaDimension(diameter=0.5, drill=0.3),
|
|
],
|
|
)
|
|
|
|
# Base board configurations that can be extended
|
|
BASE_DESIGN_RULES = DesignRules(
|
|
constraints=BASE_CONSTRAINTS,
|
|
predefined_sizes=BASE_PREDEFINED_SIZES,
|
|
netclasses=[DEFAULT_NETCLASS],
|
|
)
|
|
|
|
# Complete base 4-layer board configuration (1oz outer, 0.5oz inner, base constraints)
|
|
BASE_4L = BoardConfig(
|
|
design_rules=BASE_DESIGN_RULES,
|
|
stackup=BASE_4L_STACKUP,
|
|
)
|