#!/usr/bin/python
"""
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
pins 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
"""
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 layer_zone_fixes(board, layer):
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(0.25 * 1000000))
area.SetThermalReliefCopperBridge(int(0.5 * 1000000))
#help(area)
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 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)
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')
hide_layers_except(board, ['In4.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