aboutsummaryrefslogtreecommitdiff
path: root/fix-pcb.py
blob: d04d7a5d49a464f8d58d0574b0f4b6bcdc497efa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/python

import sys
import pcbnew


def remove_tracks(board, layer):
    """
    The tracks on Layer four are actually where there *should not* be copper.
    L4 is an inverted layer, but that information actually isn't in the files -
    it is specified in an excel sheet sent to the PCB house.

    By removing them and then setting some parameters carefully on the zones
    on that layer, we get a close-to-perfect (but no longer inverted) result
    in KiCAD.
    """
    for this in [x for x in board.GetTracks() if x.GetLayerName() == layer]:
        print('Removing track {}'.format(this))
        board.Delete(this)

#def set_tracks_width(board, layer, width):
#    for this in [x for x in board.GetTracks() if x.GetLayerName() == layer]:
#        this.SetWidth(width)
#
#def move_tracks(board, from_layer, to_layer):
#    for this in [x for x in board.GetTracks() if x.GetLayerName() == from_layer]:
#        this.SetLayer(board.GetLayerID(to_layer))


def hide_layers_except(board, layers):
    """
    As a convenience when working on getting a particular layer right, hide other layers.
    """
    lset = board.GetVisibleLayers()
    print('Layer set: {} / {}'.format(lset, lset.FmtHex()))
    #help(lset)
    mask = 0
    for x in lset.Seq():
        name = board.GetLayerName(x)
        print('  {} {}'.format(x, name))
        if name in layers:
            mask |= 1 << x
    hexset = '{0:013x}'.format(mask)
    visible_set = pcbnew.LSET()
    visible_set.ParseHex(hexset, len(hexset))
    board.SetVisibleLayers(visible_set)


def layer_zone_fixes(board, layer, gnd_clearance=0.25):
    for i in range(board.GetAreaCount()):
        area = board.GetArea(i)
        if area.GetLayerName() != layer:
            continue
        print('Area {} {}'.format(area, area.GetNetname()))
        # This makes sharp edges matching Altium Designer
        # 0.0255 is the minimum KiCad wants in order to allow changes to the zone inside KiCad
        area.SetMinThickness(int(2 * 0.0255 * 1000000))
        # 0.25 works better for the distance between zones in the bottom half of layer 4,
        # but does not allow copper between the vias under the FPGA
        #area.SetZoneClearance(int(0.25 * 1000000))
        #
        # Values below 0.15 or somewhere there does not seem to make a difference at all
        area.SetZoneClearance(int(0.15 * 1000000))
        if area.GetNetname() == 'GND':
            area.SetPriority(50)
            # 0.25 clearance on the 'background' GND zone keeps the distance to the island
            # zones in the bottom half of layer 4, matching the clearance between the areas
            # but creating a bit more clearance around vias in KiCAD plot than in Altium
            area.SetZoneClearance(int(gnd_clearance * 1000000))
            area.SetThermalReliefCopperBridge(int(0.5 * 1000000))
            #help(area)


def fix_layer_4(board):
    """

    Layer 4 is a layer with large polygons (GND and Power). In Altium, this was made
    like an inverted layer with drawn lines separating polygons. Issues were with
    line thickness in Kicad, clearance parameters preventing copper to flow in between
    vias under the FPGA and the fact that KiCAD doesn't do inverted layers so the
    lines had to be removed and zone clearances adjusted to create the same result.

    Compare with

      $ gerbv 'rev03-KiCad/GerberOutput/Cryptech Alpha-In4.Cu.gbr' /path/to/CrypTech.GP3
    """
    remove_tracks(board, 'In4.Cu')
    #set_tracks_width(board, 'In4.Cu', int(0.15 * 1000000))
    #move_tracks(board, 'In4.Cu', 'Eco2.User')
    layer_zone_fixes(board, 'In4.Cu', gnd_clearance=0.25)


def fix_layer_6(board):
    """
    Layer 6 has a GND polygon that needs a little less clearance in order to fill in between
    the vias of the FPGA.

    Compare with

      $ gerbv 'rev03-KiCad/GerberOutput/Cryptech Alpha-In6.Cu.gbr' /path/to/CrypTech.GP4
    """
    layer_zone_fixes(board, 'In6.Cu', gnd_clearance=0.15)


def fix_layer_bottom(board):
    """
    There is one small segment that ends up on Net 1 instead of GND (Net 7) for some reason:

        #Tracks#3898: 200C000500FFFFFFFFFFFFFFFF41A799058A60450341A7990561AE4803E50106000000000000000000FFFF0001
        -  (segment (start 38.75 -74.875) (end 38.75 -75.425) (width 1) (layer B.Cu) (net 1))
        +  (segment (start 38.75 -74.875) (end 38.75 -75.425) (width 1) (layer B.Cu) (net 7))

    I'm guessing the surrounding GND polygon somehow has priority in Altium so this small
    bug is not visible there. This is a segment connected to one of the seven stiching vias
    for the GND polygon under and to the right of U14 (bottom left one of the four grouped together).

    The OSHW logo is lost, but I think we can live with that.

    The copper print saying PCB rev.03 is messed up, but I don't think we can expect the fonts
    to be similar in Altium and KiCAD anyways, so might as well just redo that.

    Compare with

      $ gerbv 'rev03-KiCad/GerberOutput/Cryptech Alpha-B.Cu.gbr' /path/to/CrypTech.GBL
    """
    for this in [x for x in board.GetTracks() if x.GetLayerName() == 'B.Cu']:
        if tuple(this.GetStart()) == (38750000, -74875000):
            print('Moving track to net GND: {}'.format(this))
            this.SetNet(board.FindNet('GND'))

    layer_zone_fixes(board, 'B.Cu', gnd_clearance=0.15)


def main(in_fn='rev03-KiCad/convert.kicad_pcb', out_fn='rev03-KiCad/Cryptech Alpha.kicad_pcb'):
    board = pcbnew.LoadBoard(in_fn)
    pcbnew.SaveBoard(in_fn + '.before-fix-layer-4', board)

    fix_layer_4(board)
    fix_layer_6(board)
    fix_layer_bottom(board)

    hide_layers_except(board, ['B.Cu'])

    # Only show Through Via while working on Layer 4
    board.SetVisibleElements(0x7FFC0009)

    pcbnew.SaveBoard(out_fn, board)
    return True

if __name__ == '__main__':
    try:
        if len(sys.argv) != 3:
            sys.stderr.write('Syntax: fix-layer-4.py infile.kicad_pcb outfile.kicad_pcb\n')
            sys.exit(1)
        res = main(sys.argv[1], sys.argv[2])
        if res:
            sys.exit(0)
        sys.exit(1)
    except KeyboardInterrupt:
        pass