Permalink
Switch branches/tags
Nothing to show
Find file Copy path
97ceda3 Dec 22, 2017
1 contributor

Users who have contributed to this file

309 lines (248 sloc) 11.5 KB
import pdb
import pcbnew
board = pcbnew.GetBoard()
# the internal coorinate space of pcbnew is 10E-6 mm. (a millionth of a mm)
# the coordinate 121550000 corresponds to 121.550000
SCALE = 1000000
if hasattr(pcbnew, "LAYER_ID_COUNT"):
pcbnew.PCB_LAYER_ID_COUNT = pcbnew.LAYER_ID_COUNT
def coordsFromPolySet(ps):
str = ps.Format()
lines = str.split('\n')
numpts = int(lines[2])
pts = [[int(n) for n in x.split(" ")] for x in lines[3:-2]] # -1 because of the extra two \n
return pts
def padsForNet(net):
retval = []
for pad in board.GetPads():
# first get the netinfo, then get the netcode (int)
if pad.GetNet().GetNet() == net:
retval.append(pad)
#for pad in retval:
# print("pad {} connected to {}".format(pad.GetName(), board.GetNetsByNetcode()[net].GetNetname()));
return retval
from collections import defaultdict
class SheetInstance:
# "static" helper functions
@staticmethod
def GetSheetChildId(child):
global depth_of_array
path = child.GetPath().split('/')
path.pop(0) # pop the empty head
# the path will be missing if you have modules added
# directly in pcbnew, not imported from eeschema netlist.
if (len(path) == 0):
return (None, None)
# if there are multiple sheet heirarchies, where's the replication?
# does the top cell contain an arrayed child? or is the child of top
# arrayed.
# if you have a sheet heirarchy like this: /58DED9F1/58F8C609/58F8CB4E
# 58F8CB4E is the lowest child thing/package (ie not a sheet)
# 58DED9F1 is the top child
# 58F8C609 is the instance in the middle.
sheetid = "/".join(path[0:-1])
childid = "/".join(path[-1:])
return (sheetid, childid)
@staticmethod
def GetNetCanonical(net):
pads = []
for pad in padsForNet(net.GetNet()):
sheetid, childid = SheetInstance.GetSheetChildId(pad.GetParent())
pads.append((childid, pad.GetPadName()))
pads.sort()
cname = "_".join([i[0]+":"+i[1] for i in pads])
#print("for net {} pads {}".format(net.GetNetname(), cname))
return cname
@staticmethod
def NetIsSheetInternal(net):
commonsheet = None
#print("for net " + net.GetNetname())
for pad in padsForNet(net.GetNet()):
mod = pad.GetParent()
sheetid, childid = SheetInstance.GetSheetChildId(mod)
#print(" sheet {} child {} {}:{}".format(sheetid, str(childid), mod.GetReference(), pad.GetPadName()))
if (childid == None):
return None
if commonsheet == None:
commonsheet = sheetid
if commonsheet != sheetid:
return None
return commonsheet
@staticmethod
def RegisterModulesAndNets(board):
SheetInstance.__sheetinstances = {}
SheetInstance.__child2sheetinstance = defaultdict(lambda: list())
for child in board.GetModules():
sheetid, childid = SheetInstance.GetSheetChildId(child)
if (sheetid not in SheetInstance.__sheetinstances):
SheetInstance.__sheetinstances[sheetid] = SheetInstance(sheetid)
si = SheetInstance.__sheetinstances[sheetid]
si.addChild(child)
SheetInstance.__child2sheetinstance[childid].append(si)
for net in board.GetNetsByNetcode().values():
common = SheetInstance.NetIsSheetInternal(net)
if common == None:
continue
si = SheetInstance.__sheetinstances[common]
si.addInternalNet(net)
@staticmethod
def GetSheetInstanceForModule(child):
return SheetInstance.__sheetinstances[SheetInstance.GetSheetChildId(child)[0]]
@staticmethod
def GetSheetInstances(child):
sheetid, childid = SheetInstance.GetSheetChildId(child)
return SheetInstance.__child2sheetinstance[childid]
# methods
def __init__(self, id):
self.id = id
self.children = {}
self.internalnets = {}
def __str__(self):
retval = "sheet id is :" + self.id + " {"
retval += ", ".join([m.GetReference() for m in self.children.values()])
retval += "} internalnets: {"
retval += ", ".join([n.GetNetname() for n in self.internalnets.values()])
return retval
def addChild(self, child):
sheetid,childid = SheetInstance.GetSheetChildId(child)
if (childid == None):
return
self.children[childid] = child
def addInternalNet(self, net):
cannon = SheetInstance.GetNetCanonical(net)
self.internalnets[cannon] = net
def getChildById(self, id):
return self.children[id]
def getChildCorrespondingToModule(self, child):
sheetid, childid = SheetInstance.GetSheetChildId(child)
if (childid not in self.children):
print("missing child {} others {}".format(mod.GetReference(),
", ".join([m.GetReference()+" "+id+" "+m.GetPath() for id,m in self.children.items()])))
return self.children[childid]
def getChildren(self):
return self.children.values()
SheetInstance.RegisterModulesAndNets(board)
# this trick came from here: http://stackoverflow.com/a/2669158
import re
tokenize = re.compile(r'(\d+)|(\D+)').findall
def natural_sortkey(string):
return tuple(int(num) if num else alpha for num, alpha in tokenize(string))
def replicate_sheet_trackst(fromnet, tonet, offset):
board = tonet.GetParent()
# remove tonet's old routing
for track in board.TracksInNet(tonet.GetNet()):
board.Remove(track)
for track in board.TracksInNet(fromnet.GetNet()):
if track.GetClass() == "VIA":
# cloning is an easier way, but I want to ensure I
# can create a Via from scratch
#newvia = track.Clone()
oldvia = board.GetViaByPosition(track.GetPosition())
newvia = pcbnew.VIA(board)
# need to add before SetNet will work, so just doing it first
board.Add(newvia)
toplayer=-1
bottomlayer=pcbnew.PCB_LAYER_ID_COUNT
for l in range(pcbnew.PCB_LAYER_ID_COUNT):
if not track.IsOnLayer(l):
continue
toplayer = max(toplayer, l)
bottomlayer = min(bottomlayer, l)
newvia.SetLayerPair(toplayer, bottomlayer)
newvia.SetPosition(pcbnew.wxPoint(track.GetPosition().x+offset[0],
track.GetPosition().y+offset[1]))
newvia.SetViaType(oldvia.GetViaType())
newvia.SetWidth(oldvia.GetWidth())
newvia.SetNet(tonet)
else:
newtrack = pcbnew.TRACK(board)
# need to add before SetNet will work, so just doing it first
board.Add(newtrack)
newtrack.SetStart(pcbnew.wxPoint(track.GetStart().x+offset[0],
track.GetStart().y+offset[1]))
newtrack.SetEnd(pcbnew.wxPoint(track.GetEnd().x+offset[0],
track.GetEnd().y+offset[1]))
newtrack.SetWidth(track.GetWidth())
newtrack.SetLayer(track.GetLayer())
newtrack.SetNet(tonet)
fromzones = []
tozones = []
for zoneid in range(board.GetAreaCount()):
zone = board.GetArea(zoneid)
if (zone.GetNet().GetNetname() == fromnet.GetNetname()):
fromzones.append(zone)
continue;
if (zone.GetNet().GetNetname() == tonet.GetNetname()):
tozones.append(zone)
continue;
for zone in tozones:
board.Remove(zone)
for zone in fromzones:
coords = coordsFromPolySet(zone.Outline())
#pdb.set_trace()
newzone = board.InsertArea(tonet.GetNet(), 0, zone.GetLayer(),
coords[0][0]+int(offset[0]), coords[0][1]+int(offset[1]),
pcbnew.CPolyLine.DIAGONAL_EDGE)
newoutline = newzone.Outline()
for pt in coords[1:]:
newoutline.Append(int(pt[0]+offset[0]), int(pt[1]+offset[1]))
newzone.Hatch()
def place_instances(mainref, pitch):
pitch = (pitch[0] * SCALE, pitch[1] * SCALE)
pivotmod = board.FindModuleByReference(mainref)
sheetinstance = SheetInstance.GetSheetInstanceForModule(pivotmod)
#peers = instances[pivotsheet]
#print("getting for {}".format(pivotmod.GetReference()))
arrayedsheets = sorted(SheetInstance.GetSheetInstances(pivotmod),
key = lambda elt: natural_sortkey(elt.getChildCorrespondingToModule(pivotmod).GetReference()))
#replicasheets = sorted(children[pivotinstance], key=lambda elt: natural_sortkey(elt[2]))
print("children of the same instance as {}: {}".format(mainref,
",".join([m.GetReference() for m in sheetinstance.getChildren()])))
basepositions = {}
for mod in sheetinstance.getChildren():
sheetid, childid = SheetInstance.GetSheetChildId(mod)
basepositions[childid] = (mod.GetPosition().x,
mod.GetPosition().y,
mod.GetOrientation(),
mod.IsFlipped())
print("basepositions {}".format(str(basepositions)))
instnum = -1
for i, si in enumerate(arrayedsheets):
if (si.getChildCorrespondingToModule(pivotmod).GetReference() == mainref):
instnum = i
break
print("{} is in index {}".format(mainref, instnum))
# we start with index=-instnum because we want the pivot module to stay where it is.
for idx, si in enumerate(arrayedsheets, start=-instnum):
#print("placing instance {} {}".format(idx, si.id))
if idx == 0:
continue
#first move the modules
for peer in si.getChildren():
sheetid, childid = SheetInstance.GetSheetChildId(peer)
newposition = basepositions[childid]
newposition = (int(newposition[0] + idx*pitch[0]),
int(newposition[1] + idx*pitch[1]))
#print("moving peer {} to {},{}".format(peer.GetReference(), newposition[0], newposition[1]))
if (peer.IsFlipped() != basepositions[childid][3]):
peer.Flip(peer.GetPosition())
peer.SetPosition(pcbnew.wxPoint(*newposition))
peer.SetOrientation(basepositions[childid][2])
#copy the nets
for fromnetid, fromnet in sheetinstance.internalnets.items():
if fromnetid not in si.internalnets:
#print("{} is missing from {}\n".format(fromnetid, ", ".join(si.internalnets.keys())))
print("{} is missing\n".format(fromnetid))
continue
tonet = si.internalnets[fromnetid]
#print("copying net {} to {}".format(fromnet.GetNetname(), tonet.GetNetname()))
replicate_sheet_trackst(fromnet, tonet, (idx*pitch[0],idx*pitch[1]))
#place_instances("U7", (0, -45))
#place_instances("U71", (0, -45))
place_instances("J8", (30, 0))
place_instances("U7", (30, 0))
# In the future, this build connectivity call will not be neccessary.
# I have submitted a patch to include this in the code for Refresh.
# You'll know you needed it if pcbnew crashes without it.
board.BuildConnectivity()
pcbnew.Refresh();