Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1 @@
*.zen linguist-language=Starlark
**/*.zen linguist-language=Starlark
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
with:
version: HEAD
- name: Build .zen files
run: pcb build . -Dwarnings
run: pcb build . -Dwarnings -S"deprecated.kicad"
- name: Check formatting
run: pcb fmt . --check

Expand All @@ -31,4 +31,4 @@ jobs:
with:
version: latest
- name: Build .zen files
run: pcb build . -Dwarnings
run: pcb build . -Dwarnings -S"deprecated.kicad"
12 changes: 12 additions & 0 deletions bom/helpers.zen
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ def prop(c, names):
return None


def merge_parts(parts: list[dict]) -> dict:
"""Merge two part lists."""
merged = {}
for part in parts:
for key, value in part.items():
if key not in merged:
merged[key] = value
else:
merged[key] += value
return merged


def set_primary_and_alts(c, primary, manufacturer, alts):
"""Set primary MPN and alternatives on component.

Expand Down
89 changes: 89 additions & 0 deletions bom/manufactuers/wurth_electronik.zen
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""Würth Elektronik component helpers.

Functions to generate Würth Elektronik part numbers by series.
Each function is named after the series and returns (mpn, manufacturer).
"""

MANUFACTURER = "Würth Elektronik"


def WR_PHD_2_54_SMD_MALE_SINGLE(positions: int) -> list[(str, str)]:
"""WR-PHD 2.54mm pitch SMD Male pin header.

Series: 610xxx, SMD, 2.54mm pitch, 1 row, vertical
Available pins: 4, 6, 10, 14, 15, 16, 24, 32

Returns:
List of alternate (mpn, manufacturer)
"""
available_positions = [4, 6, 10, 14, 16, 24, 32]

if positions not in available_positions:
error(
"WR_PHD_2_54_SMD_Male 1-row: pins must be one of "
+ str(sorted(available_positions))
+ ", got "
+ str(positions)
)

prefix = "61000" if positions < 10 else "6100"
type1 = "18221" # Type 1 (preferred)
type2 = "18321" # Type 2 (fallback)

return [(f"{prefix}{positions}{type1}", MANUFACTURER), (f"{prefix}{positions}{type2}", MANUFACTURER)]


def WR_PHD_2_54_SMD_MALE_DUAL(positions: int) -> list[(str, str)]:
"""WR-PHD 2.54mm pitch SMD Male pin header, 2 rows.

Series: 610xxx, SMD, 2.54mm pitch, 2 rows, vertical (Straight)
Available pins: 4, 6, 8, 10, 12, 14, 16, 20, 22, 24, 26, 32, 34, 36

Returns:
List of alternate (mpn, manufacturer)
"""
available_positions = [4, 6, 8, 10, 12, 14, 16, 20, 22, 24, 26, 32, 34, 36]

if positions not in available_positions:
error(
"WR_PHD_2_54_SMD_Male 2-row: pins must be one of "
+ str(sorted(available_positions))
+ ", got "
+ str(positions)
)

# Format pin count with leading zero for single digit
pin_str = f"0{positions}" if positions < 10 else str(positions)

type1_prefix = "6100" # Type 1 (preferred)
type2_prefix = "6103" # Type 2 (fallback)
suffix = "21121"

return [(f"{type1_prefix}{pin_str}{suffix}", MANUFACTURER), (f"{type2_prefix}{pin_str}{suffix}", MANUFACTURER)]


WURTH_ELEKTRONIK_WR_PHD = {
# 2.54mm pitch, 1 row, vertical, Male
("2.54mm", 1, 4, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_SINGLE(4),
("2.54mm", 1, 6, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_SINGLE(6),
("2.54mm", 1, 10, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_SINGLE(10),
("2.54mm", 1, 14, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_SINGLE(14),
("2.54mm", 1, 16, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_SINGLE(16),
("2.54mm", 1, 24, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_SINGLE(24),
("2.54mm", 1, 32, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_SINGLE(32),
# 2.54mm pitch, 2 rows, vertical, Male
("2.54mm", 2, 2, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(4),
("2.54mm", 2, 3, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(6),
("2.54mm", 2, 4, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(8),
("2.54mm", 2, 5, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(10),
("2.54mm", 2, 6, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(12),
("2.54mm", 2, 7, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(14),
("2.54mm", 2, 8, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(16),
("2.54mm", 2, 10, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(20),
("2.54mm", 2, 11, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(22),
("2.54mm", 2, 12, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(24),
("2.54mm", 2, 13, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(26),
("2.54mm", 2, 16, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(32),
("2.54mm", 2, 17, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(34),
("2.54mm", 2, 18, "Vertical", "Male", "SMD"): WR_PHD_2_54_SMD_MALE_DUAL(36),
}
65 changes: 40 additions & 25 deletions bom/match_generics.zen
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Automatically assigns house MPNs to resistors and capacitors.
"""

load("manufactuers/wurth_electronik.zen", "WURTH_ELEKTRONIK_WR_PHD")
load(
"../units.zen",
"Voltage",
Expand Down Expand Up @@ -33,9 +34,9 @@ load(
"pin_header",
"tvs",
"sinhoo_standoff",
"merge_parts",
)


# Dielectric quality ranking: C0G/NP0 > X7T > X7R > X7S > X5R > Y5V > Z5U
_DIELECTRIC_RANK = {
"C0G": 0,
Expand Down Expand Up @@ -228,29 +229,34 @@ HOUSE_FB_BY_PKG = {

# House pin header catalog (Würth Elektronik WR-PHD series)
# Key: (pitch, rows, pins, orientation) -> [(mpn, manufacturer), ...]
HOUSE_PH = {
# 1.27mm pitch, 2 rows, vertical
("1.27mm", 2, 2, "Vertical"): [("62200421121", "Würth Elektronik")],
("1.27mm", 2, 4, "Vertical"): [("62200821121", "Würth Elektronik")],
("1.27mm", 2, 5, "Vertical"): [("62201021121", "Würth Elektronik"), ("ZX-PZ1.27-2-5PZZ", "Megastar")],
("1.27mm", 2, 10, "Vertical"): [("62202021121", "Würth Elektronik")],
# 2.54mm pitch, 1 row, vertical
("2.54mm", 1, 2, "Vertical"): [("61300211121", "Würth Elektronik")],
("2.54mm", 1, 3, "Vertical"): [("61300311121", "Würth Elektronik")],
("2.54mm", 1, 4, "Vertical"): [("61300411121", "Würth Elektronik")],
("2.54mm", 1, 5, "Vertical"): [("61300511121", "Würth Elektronik")],
("2.54mm", 1, 6, "Vertical"): [("61300611121", "Würth Elektronik")],
("2.54mm", 1, 8, "Vertical"): [("61300811121", "Würth Elektronik")],
("2.54mm", 1, 10, "Vertical"): [("61301011121", "Würth Elektronik")],
("2.54mm", 1, 15, "Vertical"): [("61301511121", "Würth Elektronik")],
# 2.54mm pitch, 2 rows, vertical
("2.54mm", 2, 2, "Vertical"): [("61300421121", "Würth Elektronik")],
("2.54mm", 2, 3, "Vertical"): [("61300621121", "Würth Elektronik")],
("2.54mm", 2, 4, "Vertical"): [("61300821121", "Würth Elektronik")],
("2.54mm", 2, 5, "Vertical"): [("61301021121", "Würth Elektronik")],
("2.54mm", 2, 10, "Vertical"): [("61302021121", "Würth Elektronik")],
("2.54mm", 2, 20, "Vertical"): [("61304021121", "Würth Elektronik")],
}
HOUSE_PH = merge_parts(
[
WURTH_ELEKTRONIK_WR_PHD,
{
# 1.27mm pitch, 2 rows, vertical
("1.27mm", 2, 2, "Vertical"): [("62200421121", "Würth Elektronik")],
("1.27mm", 2, 4, "Vertical"): [("62200821121", "Würth Elektronik")],
("1.27mm", 2, 5, "Vertical"): [("62201021121", "Würth Elektronik"), ("ZX-PZ1.27-2-5PZZ", "Megastar")],
("1.27mm", 2, 10, "Vertical"): [("62202021121", "Würth Elektronik")],
# 2.54mm pitch, 1 row, vertical
("2.54mm", 1, 2, "Vertical"): [("61300211121", "Würth Elektronik")],
("2.54mm", 1, 3, "Vertical"): [("61300311121", "Würth Elektronik")],
("2.54mm", 1, 4, "Vertical"): [("61300411121", "Würth Elektronik")],
("2.54mm", 1, 5, "Vertical"): [("61300511121", "Würth Elektronik")],
("2.54mm", 1, 6, "Vertical"): [("61300611121", "Würth Elektronik")],
("2.54mm", 1, 8, "Vertical"): [("61300811121", "Würth Elektronik")],
("2.54mm", 1, 10, "Vertical"): [("61301011121", "Würth Elektronik")],
("2.54mm", 1, 15, "Vertical"): [("61301511121", "Würth Elektronik")],
# 2.54mm pitch, 2 rows, vertical
("2.54mm", 2, 2, "Vertical"): [("61300421121", "Würth Elektronik")],
("2.54mm", 2, 3, "Vertical"): [("61300621121", "Würth Elektronik")],
("2.54mm", 2, 4, "Vertical"): [("61300821121", "Würth Elektronik")],
("2.54mm", 2, 5, "Vertical"): [("61301021121", "Würth Elektronik")],
("2.54mm", 2, 10, "Vertical"): [("61302021121", "Würth Elektronik")],
("2.54mm", 2, 20, "Vertical"): [("61304021121", "Würth Elektronik")],
},
]
)

# House TVS diode catalog by package (8/20µs pulse ratings per IEC 61000-4-5)
HOUSE_TVS_BY_PKG = {
Expand Down Expand Up @@ -799,9 +805,18 @@ def assign_house_pin_header(c, house_ph):
rows_req = prop(c, ["rows", "Rows"])
pitch_req = prop(c, ["pitch", "Pitch"])
orientation_req = prop(c, ["orientation", "Orientation"])
gender_req = prop(c, ["gender", "Gender"])
mount_req = prop(c, ["mount", "Mount"])

# O(1) lookup using tuple key
key = (pitch_req, rows_req, pins_req, orientation_req)
if mount_req and gender_req:
# Generic pin header with mount and gender
key = (pitch_req, rows_req, pins_req, orientation_req, gender_req, mount_req)
else:
# Legacy style pin header (deprecated)
key = (pitch_req, rows_req, pins_req, orientation_req)

# Check for an exact match
matches = house_ph.get(key, [])

if matches:
Expand Down
37 changes: 37 additions & 0 deletions bom/test/test_match_PH.zen
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Comprehensive test for PinHeader component."""

load("../../generics/PinHeader.zen", "Mount", "Pitch", "Orientation", "Gender")
load("../match_generics.zen", "assign_house_parts")

PinHeader = Module("../../generics/PinHeader.zen")

PH_SINGLE_ROW_PINS = [4, 6, 10, 14, 16, 24, 32]
PH_DUAL_ROW_PINS = [2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 16, 17, 18]
PH_HOUSE_PARTS = [
(pitch, rows, pins, orientation, gender, mount)
for pitch in ["2.54mm"]
for rows in [1, 2]
for pins in {1: PH_SINGLE_ROW_PINS, 2: PH_DUAL_ROW_PINS}[rows]
for orientation in ["Vertical"]
for gender in ["Male"]
for mount in ["SMD"]
]

for id, (pitch, rows, pins, orientation, gender, mount) in enumerate(PH_HOUSE_PARTS):
pitch_str = pitch.replace(".", "_")
gender_str = gender
orientation_str = orientation
mount_str = mount
nets = {f"P{n}": Net(f"PH{id}_{n}") for n in range(1, pins * rows + 1)}
PinHeader(
name=f"PH_{id}",
mount=mount,
pins=pins,
rows=rows,
orientation=orientation,
pitch=pitch,
gender=gender,
**nets,
)

builtin.add_component_modifier(assign_house_parts)
Loading