Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
WIP: Exporter: PCB netlist exporter
Browse files Browse the repository at this point in the history
  • Loading branch information
iopapamanoglou committed Sep 9, 2024
1 parent 6cf4dc7 commit 30bb41f
Show file tree
Hide file tree
Showing 7 changed files with 471 additions and 34 deletions.
231 changes: 231 additions & 0 deletions src/faebryk/exporters/pcb/kicad/pcb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT

import logging
from dataclasses import asdict
from pathlib import Path

from faebryk.libs.kicad.fileformats import (
C_footprint,
C_kicad_footprint_file,
C_kicad_fp_lib_table_file,
C_kicad_netlist_file,
C_kicad_pcb_file,
C_xyr,
)
from faebryk.libs.kicad.fileformats_old import C_kicad_footprint_file_easyeda
from faebryk.libs.sexp.dataclass_sexp import get_parent
from faebryk.libs.util import NotNone, find, find_or

logger = logging.getLogger(__name__)


def _nets_same(
pcb_net: tuple[
C_kicad_pcb_file.C_kicad_pcb.C_net,
list[C_kicad_pcb_file.C_kicad_pcb.C_pcb_footprint.C_pad],
],
nl_net: C_kicad_netlist_file.C_netlist.C_nets.C_net,
) -> bool:
pcb_pads = {
f"{get_parent(p, C_kicad_pcb_file.C_kicad_pcb.C_pcb_footprint)
.propertys['Reference'].value}.{p.name}"
for p in pcb_net[1]
}
nl_pads = {f"{n.ref}.{n.pin}" for n in nl_net.nodes}
return pcb_pads == nl_pads


def _get_footprint(
identifier: str, fp_lib_path: Path
) -> C_kicad_footprint_file.C_footprint_in_file:
fp_lib_table = C_kicad_fp_lib_table_file.loads(fp_lib_path)
lib_id, fp_name = identifier.split(":")
lib = find(fp_lib_table.fp_lib_table.libs, lambda x: x.name == lib_id)
dir_path = Path(lib.uri.replace("${KIPRJMOD}", str(fp_lib_path.parent)))
path = dir_path / f"{fp_name}.kicad_mod"

# TODO this should be handled in fileformats itself
if path.read_text().startswith("(module"):
return C_kicad_footprint_file_easyeda.loads(path).convert_to_new().footprint

return C_kicad_footprint_file.loads(path).footprint


class PCB:
@staticmethod
def apply_netlist(pcb_path: Path, netlist_path: Path):
from faebryk.exporters.pcb.kicad.transformer import gen_uuid

fp_lib_path = pcb_path.parent / "fp-lib-table"

pcb = C_kicad_pcb_file.loads(pcb_path)
netlist = C_kicad_netlist_file.loads(netlist_path)

# update nets
# load footprints
# - set layer & pos
# - per pad set net
# - load ref & value from netlist
# - set uuid for all (pads, geos, ...)
# - drop fp_text value & fp_text user

# notes:
# - netcode in netlist unrelated to netcode in pcb
# - matched by name + check for same pads
# - only works if net nodes not changed
# - what happens if net is renamed?
# - segments & vias etc use netcodes
# - if net code removed, recalculated
# -> DIFFICULT: need to know which net/pads they are touching
# - zone & pads uses netcode & netname
# - empty nets ignored (consider removing them from netlist export)
# - components matched by ref (no fallback)
# - if pad no net, just dont put net in sexp

nl_nets = {n.name: n for n in netlist.export.nets.nets if n.nodes}

# Components
pcb_comps = {
c.propertys["Reference"].value: c for c in pcb.kicad_pcb.footprints
}
nl_comps = {c.ref: c for c in netlist.export.components.comps}
comps_added = nl_comps.keys() - pcb_comps.keys()
comps_removed = pcb_comps.keys() - nl_comps.keys()

print("Comps removed:", comps_removed)
for comp_name in comps_removed:
comp = pcb_comps[comp_name]
pcb.kicad_pcb.footprints.remove(comp)

print("Comps added:", comps_added)
for comp_name in comps_added:
comp = nl_comps[comp_name]
footprint_identifier = comp.footprint
footprint = _get_footprint(footprint_identifier, fp_lib_path)
pads = {
p.pin: n
for n in nl_nets.values()
for p in n.nodes
if p.ref == comp_name
}

texts = [t for t in footprint.fp_texts if t.type not in ("user", "value")]
for t in texts:
if t.type == "reference":
t.text = comp_name

propertys: dict[str, C_footprint.C_property] = {
name: C_footprint.C_property(
name=name,
value=k.text,
at=k.at,
layer=k.layer,
uuid=k.uuid,
effects=k.effects,
)
for k in footprint.fp_texts
if (name := k.type.capitalize()) in ("Reference", "Value")
}

propertys["Reference"].value = comp_name
propertys["Value"].value = comp.value

pcb_comp = C_kicad_pcb_file.C_kicad_pcb.C_pcb_footprint(
uuid=gen_uuid(mark=""),
at=C_xyr(x=0, y=0, r=0),
pads=[
C_kicad_pcb_file.C_kicad_pcb.C_pcb_footprint.C_pad(
uuid=gen_uuid(mark=""),
net=C_kicad_pcb_file.C_kicad_pcb.C_pcb_footprint.C_pad.C_net(
number=pads[p.name].code,
name=pads[p.name].name,
)
if p.name in pads
else None,
#
**asdict(p),
)
for p in footprint.pads
],
#
name=footprint_identifier,
layer=footprint.layer,
propertys=propertys,
attr=footprint.attr,
fp_lines=footprint.fp_lines,
fp_arcs=footprint.fp_arcs,
fp_circles=footprint.fp_circles,
fp_rects=footprint.fp_rects,
fp_texts=texts,
fp_poly=footprint.fp_poly,
model=footprint.model,
)

pcb.kicad_pcb.footprints.append(pcb_comp)

# Nets
pcb_nets = {
n.name: (
n,
[
p
for f in pcb.kicad_pcb.footprints
for p in f.pads
if p.net and p.net.name == n.name
],
)
for n in pcb.kicad_pcb.nets
if n.name
}

nets_added = nl_nets.keys() - pcb_nets.keys()
nets_removed = pcb_nets.keys() - nl_nets.keys()

# Match renamed nets by pads
matched_nets = {
nl_net_name: pcb_net_name
for nl_net_name in nets_added
if (
pcb_net_name := find_or(
nets_removed,
lambda x: _nets_same(pcb_nets[NotNone(x)], nl_nets[nl_net_name]),
default=None,
)
)
}
nets_added.difference_update(matched_nets.keys())
nets_removed.difference_update(matched_nets.values())

if nets_removed:
# TODO
raise NotImplementedError(
f"Nets removed from netlist not implemented: {nets_removed}"
)

# Rename nets
print("Renamed nets:", matched_nets)
for new_name, old_name in matched_nets.items():
pcb_nets[old_name][0].name = new_name
for pad in pcb_nets[old_name][1]:
assert pad.net
pad.net.name = new_name
for zone in pcb.kicad_pcb.zones:
if zone.net_name == old_name:
zone.net_name = new_name

# Add new nets
print("New nets", nets_added)
for net_name in nets_added:
nl_net = nl_nets[net_name]

Check failure on line 220 in src/faebryk/exporters/pcb/kicad/pcb.py

View workflow job for this annotation

GitHub Actions / test

Ruff (F841)

src/faebryk/exporters/pcb/kicad/pcb.py:220:13: F841 Local variable `nl_net` is assigned to but never used
pcb_net = C_kicad_pcb_file.C_kicad_pcb.C_net(
name=net_name,
number=len(pcb.kicad_pcb.nets),
)
# TODO

pcb.kicad_pcb.nets.append(pcb_net)

# ---
print("Save PCB", pcb_path)
pcb.dumps(pcb_path)
15 changes: 2 additions & 13 deletions src/faebryk/exporters/pcb/kicad/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import logging
import pprint
import re
import uuid
from abc import abstractmethod
from dataclasses import fields
from enum import Enum, auto
Expand Down Expand Up @@ -40,6 +39,7 @@
C_xyr,
C_xyz,
E_fill,
gen_uuid as _gen_uuid,
)
from faebryk.libs.sexp.dataclass_sexp import dataclass_dfs
from faebryk.libs.util import KeyErrorNotFound, cast_assert, find, get_key
Expand Down Expand Up @@ -72,18 +72,7 @@


def gen_uuid(mark: str = "") -> UUID:
# format: d864cebe-263c-4d3f-bbd6-bb51c6d2a608
value = uuid.uuid4().hex

suffix = mark.encode().hex()
value = value[: -len(suffix)] + suffix

DASH_IDX = [8, 12, 16, 20]
formatted = value
for i, idx in enumerate(DASH_IDX):
formatted = formatted[: idx + i] + "-" + formatted[idx + i :]

return UUID(formatted)
return _gen_uuid(mark)


def is_marked(uuid: UUID, mark: str):
Expand Down
2 changes: 1 addition & 1 deletion src/faebryk/libs/app/kicad_netlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging
from pathlib import Path

from faebryk.core.graph import Graph
from faebryk.core.graphinterface import Graph
from faebryk.exporters.netlist.graph import attach_nets_and_kicad_info
from faebryk.exporters.netlist.kicad.netlist_kicad import from_faebryk_t2_netlist
from faebryk.exporters.netlist.netlist import make_t2_netlist_from_graph
Expand Down
2 changes: 1 addition & 1 deletion src/faebryk/libs/app/pcb.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import Any, Callable

import faebryk.library._F as F
from faebryk.core.graph import Graph
from faebryk.core.graphinterface import Graph
from faebryk.core.module import Module
from faebryk.core.node import Node
from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer
Expand Down
Loading

0 comments on commit 30bb41f

Please sign in to comment.