← Commit history

Delete design/gen/build_board.py

John Lauer ·1c5d9109e3 ·7d ago ·parent ef24070
1 file changed −207
design/gen/build_board.pydeleted−207
@@ -1,207 +0,0 @@-"""Run with KiCad 10's python.exe. Stage 'base': footprints, nets, outline -> board + pads.json.-Stage 'final': base + tracks/vias/zones from routes.json, zones filled, saved."""-import json, os, sys, math-import pcbnew--HERE = os.path.dirname(os.path.abspath(__file__))-ROOT = os.path.dirname(HERE)-STD_FP = r"C:\Users\john\AppData\Local\Programs\KiCad\10.0\share\kicad\footprints"-PRJ_FP = os.path.join(ROOT, "kicad", "lib")-OUT = os.path.join(ROOT, "kicad", "ocp-oculink-riser.kicad_pcb")-STAGE = sys.argv[1] if len(sys.argv) > 1 else "base"--nl = json.load(open(os.path.join(ROOT, "design", "netlist.json")))-lay = json.load(open(os.path.join(ROOT, "design", "layout.json")))-MM = pcbnew.FromMM---def V(x, y):-    return pcbnew.VECTOR2I(MM(x), MM(y))---LAYERS = {"L1": pcbnew.F_Cu, "L3": pcbnew.In2_Cu, "L5": pcbnew.In4_Cu, "L6": pcbnew.B_Cu,-          "L2": pcbnew.In1_Cu, "L4": pcbnew.In3_Cu}---def new_board():-    b = pcbnew.BOARD()-    b.SetCopperLayerCount(6)-    ds = b.GetDesignSettings()-    ds.SetBoardThickness(MM(1.6))-    # JLCPCB 6-layer standard process minimums (adom/pcb-design-rules jlcpcb-6L)-    ds.m_MinClearance = MM(0.09); ds.m_TrackMinWidth = MM(0.09)-    ds.m_ViasMinSize = MM(0.25); ds.m_ViasMinAnnularWidth = MM(0.05)-    ds.m_MinThroughDrill = MM(0.15); ds.m_HoleToHoleMin = MM(0.2)-    ds.m_HoleClearance = MM(0.12); ds.m_CopperEdgeClearance = MM(0.2)-    try:-        nc = ds.m_NetSettings.GetDefaultNetclass()-    except Exception:-        nc = None-    if nc is not None:-        nc.SetClearance(MM(0.12)); nc.SetTrackWidth(MM(0.15))-        nc.SetViaDiameter(MM(0.4)); nc.SetViaDrill(MM(0.2))-        try:-            nc.SetDiffPairWidth(MM(0.163)); nc.SetDiffPairGap(MM(0.15))-        except Exception:-            pass-    b.SetLayerName(pcbnew.In1_Cu, "In1.Cu")-    # nets-    nets = {}-    ncp = os.path.join(ROOT, "design", "nc_nets.json")-    nc = json.load(open(ncp)) if os.path.exists(ncp) else {}-    for name in sorted(set(nl["nets"]) | set(nc.values())):-        ni = pcbnew.NETINFO_ITEM(b, name)-        b.Add(ni)-        nets[name] = ni-    pin_net = {}-    for name, pins in nl["nets"].items():-        for p in pins:-            pin_net[p] = name-    pin_net.update(nc)-    # footprints-    for c in nl["components"]:-        lib, fpname = c["fp"].split(":")-        libdir = os.path.join(PRJ_FP, lib + ".pretty") if lib == "Riser" else os.path.join(STD_FP, lib + ".pretty")-        fp = pcbnew.FootprintLoad(libdir, fpname)-        if fp is None:-            raise SystemExit("cannot load " + c["fp"])-        fp.SetReference(c["ref"])-        fp.SetValue(c["value"])-        fp.SetFPIDAsString(c["fp"])-        for k, v in (("MPN", c.get("mpn", "")), ("Manufacturer", c.get("mfr", "")), ("Source", c.get("src", "")),-                     ("Description", c.get("desc", ""))):-            fp.SetField(k, v or "")-            f_ = fp.GetField(k)-            if f_ is not None:-                f_.SetVisible(False)-        # silk: the silkscreen step places every reference; until then they are hidden. Chip R/C keep no body silk-        # use the project's no-silk copies (Riser:*_NoSilk); ICs, LEDs and connectors keep their pin-1 / polarity marks.-        fp.Reference().SetVisible(False)-        x, y, rot, side = lay["placement"][c["ref"]]-        b.Add(fp)-        fp.SetPosition(V(x, y))-        fp.SetOrientationDegrees(rot)-        if side == "B":-            fp.Flip(V(x, y), pcbnew.FLIP_DIRECTION_TOP_BOTTOM)-        for pad in fp.Pads():-            key = f'{c["ref"]}.{pad.GetNumber()}'-            if key in pin_net:-                pad.SetNet(nets[pin_net[key]])-        # 3D model binding-        if c.get("model"):-            fp.Models().clear()-            m = pcbnew.FP_3DMODEL()-            m.m_Filename = c["model"]-            fp.Models().push_back(m)-    # outline: lines and arcs (every corner filleted)-    for e in lay["edges"]:-        s = pcbnew.PCB_SHAPE(b)-        if e[0] == "line":-            s.SetShape(pcbnew.SHAPE_T_SEGMENT)-            s.SetStart(V(e[1], e[2])); s.SetEnd(V(e[3], e[4]))-        else:-            s.SetShape(pcbnew.SHAPE_T_ARC)-            s.SetArcGeometry(V(e[1], e[2]), V(e[3], e[4]), V(e[5], e[6]))-        s.SetLayer(pcbnew.Edge_Cuts); s.SetWidth(MM(0.05))-        b.Add(s)-    return b, nets---def export_pads(b):-    out = []-    for fp in b.GetFootprints():-        for pad in fp.Pads():-            sz = pad.GetSize(pcbnew.F_Cu)-            lset = [pcbnew.BOARD.GetStandardLayerName(l) for l in pad.GetLayerSet().CuStack()]-            poly = pad.GetEffectivePolygon(pcbnew.F_Cu if pad.IsOnLayer(pcbnew.F_Cu) else pcbnew.B_Cu, pcbnew.ERROR_INSIDE)-            outline = []-            for oi in range(poly.OutlineCount()):-                ol = poly.Outline(oi)-                outline.append([(pcbnew.ToMM(ol.CPoint(i).x), pcbnew.ToMM(ol.CPoint(i).y)) for i in range(ol.PointCount())])-            dr = pad.GetDrillSize()-            out.append(dict(ref=fp.GetReference(), num=pad.GetNumber(), net=pad.GetNetname(),-                            x=pcbnew.ToMM(pad.GetPosition().x), y=pcbnew.ToMM(pad.GetPosition().y),-                            w=pcbnew.ToMM(sz.x), h=pcbnew.ToMM(sz.y), layers=lset, poly=outline,-                            drill=[pcbnew.ToMM(dr.x), pcbnew.ToMM(dr.y)], attr=str(pad.GetAttribute())))-    return out---def add_routes(b, nets, which="routes.json"):-    r = json.load(open(os.path.join(ROOT, "design", which)))-    for (lay_, net, x1, y1, x2, y2, w) in r["tracks"]:-        t = pcbnew.PCB_TRACK(b)-        t.SetStart(V(x1, y1)); t.SetEnd(V(x2, y2)); t.SetWidth(MM(w))-        t.SetLayer(LAYERS[lay_]); t.SetNet(nets[net])-        b.Add(t)-    for (x, y, net, d, h) in r["vias"]:-        v = pcbnew.PCB_VIA(b)-        v.SetPosition(V(x, y)); v.SetWidth(MM(d)); v.SetDrill(MM(h))-        v.SetViaType(pcbnew.VIATYPE_THROUGH)-        v.SetLayerPair(pcbnew.F_Cu, pcbnew.B_Cu)-        v.SetNet(nets[net])-        b.Add(v)-    for z in r.get("zones", []):-        zone = pcbnew.ZONE(b)-        zone.SetLayer(LAYERS[z["layer"]] if z["layer"] in LAYERS else getattr(pcbnew, z["layer"]))-        if z.get("keepout"):-            zone.SetIsRuleArea(True)-            zone.SetDoNotAllowTracks(z.get("no_tracks", True)); zone.SetDoNotAllowVias(z.get("no_vias", True))-            zone.SetDoNotAllowZoneFills(z.get("no_pour", True)); zone.SetDoNotAllowPads(False)-            zone.SetDoNotAllowFootprints(False)-            if z.get("layers"):-                ls = pcbnew.LSET()-                for ln in z["layers"]:-                    ls.AddLayer(LAYERS[ln])-                zone.SetLayerSet(ls)-        else:-            zone.SetNet(nets[z["net"]])-            zone.SetMinThickness(MM(0.15))-            zone.SetLocalClearance(MM(z.get("clearance", 0.2)))-            zone.SetAssignedPriority(z.get("priority", 0))-            zone.SetPadConnection(pcbnew.ZONE_CONNECTION_THERMAL if z.get("thermal", True) else pcbnew.ZONE_CONNECTION_FULL)-            zone.SetThermalReliefGap(MM(0.25)); zone.SetThermalReliefSpokeWidth(MM(0.3))-            zone.SetIslandRemovalMode(pcbnew.ISLAND_REMOVAL_MODE_ALWAYS)-        ol = zone.Outline()-        ol.NewOutline()-        for (x, y) in z["poly"]:-            ol.Append(MM(x), MM(y))-        b.Add(zone)-    for t in r.get("texts", []):-        tx = pcbnew.PCB_TEXT(b)-        tx.SetText(t["text"]); tx.SetPosition(V(t["x"], t["y"]))-        tx.SetLayer(getattr(pcbnew, t["layer"].replace(".", "_")))-        tx.SetTextSize(V(t["size"], t["size"])); tx.SetTextThickness(MM(t["size"] * 0.15))-        if t.get("angle"):-            tx.SetTextAngleDegrees(t["angle"])-        if t["layer"].startswith("B."):-            tx.SetMirrored(True)-        b.Add(tx)---if __name__ == "__main__":-    b, nets = new_board()-    if STAGE == "base":-        json.dump(export_pads(b), open(os.path.join(ROOT, "design", "pads.json"), "w"))-        pcbnew.SaveBoard(OUT.replace(".kicad_pcb", "-base.kicad_pcb"), b)-        print("base ok", len(b.GetFootprints()), "footprints")-    elif STAGE == "start":-        # the run's start board: interface parts in place, the rest staged beside the outline, live copper held back-        st = json.load(open(os.path.join(ROOT, "design", "routes_start.json")))-        for fp in b.GetFootprints():-            ref = fp.GetReference()-            if ref in st["stage"]:-                x, y, rot, side = st["stage"][ref]-                fp.SetPosition(V(x, y)); fp.SetOrientationDegrees(rot)-        add_routes(b, nets, "routes_start.json")-        filler = pcbnew.ZONE_FILLER(b)-        filler.Fill(b.Zones())-        out = os.path.join(ROOT, "aiflow", "start", "ocp-oculink-riser.kicad_pcb")-        os.makedirs(os.path.dirname(out), exist_ok=True)-        pcbnew.SaveBoard(out, b)-        print("start ok", len(b.GetTracks()), "items")-    else:-        add_routes(b, nets)-        filler = pcbnew.ZONE_FILLER(b)-        filler.Fill(b.Zones())-        pcbnew.SaveBoard(OUT, b)-        print("final ok", len(b.GetTracks()), "track/via items,", b.GetAreaCount(), "zones")