mirror of https://github.com/VLSIDA/OpenRAM.git
Add router data structure, blockage parser, pin parser, initial unit tests
This commit is contained in:
parent
d0782df9fe
commit
b947989970
|
|
@ -602,9 +602,9 @@ class VlsiLayout:
|
||||||
for boundary in self.structures[self.rootStructureName].boundaries:
|
for boundary in self.structures[self.rootStructureName].boundaries:
|
||||||
if boundary.drawingLayer==borderlayer:
|
if boundary.drawingLayer==borderlayer:
|
||||||
debug.info(debug_level,"Find border "+str(boundary.coordinates))
|
debug.info(debug_level,"Find border "+str(boundary.coordinates))
|
||||||
left_button=boundary.coordinates[0]
|
left_bottom=boundary.coordinates[0]
|
||||||
right_top=boundary.coordinates[2]
|
right_top=boundary.coordinates[2]
|
||||||
cellSize=[right_top[0]-left_button[0],right_top[1]-left_button[1]]
|
cellSize=[right_top[0]-left_bottom[0],right_top[1]-left_bottom[1]]
|
||||||
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
|
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
|
||||||
if not(cellSizeMicron):
|
if not(cellSizeMicron):
|
||||||
debug.error("Error: "+str(self.rootStructureName)+".cell_size information not found yet")
|
debug.error("Error: "+str(self.rootStructureName)+".cell_size information not found yet")
|
||||||
|
|
@ -620,6 +620,15 @@ class VlsiLayout:
|
||||||
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
|
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
|
||||||
return cellSizeMicron
|
return cellSizeMicron
|
||||||
|
|
||||||
|
def measureBoundary(self,startStructure):
|
||||||
|
self.rootStructureName=startStructure
|
||||||
|
self.populateCoordinateMap()
|
||||||
|
cellBoundary = [None, None, None, None]
|
||||||
|
for TreeUnit in self.xyTree:
|
||||||
|
cellBoundary=self.measureSizeInStruture(TreeUnit,cellBoundary)
|
||||||
|
return [[self.units[0]*cellBoundary[0],self.units[0]*cellBoundary[1]],
|
||||||
|
[self.units[0]*cellBoundary[2],self.units[0]*cellBoundary[3]]]
|
||||||
|
|
||||||
def measureSizeInStruture(self,Struture,cellBoundary):
|
def measureSizeInStruture(self,Struture,cellBoundary):
|
||||||
StrutureName=Struture[0]
|
StrutureName=Struture[0]
|
||||||
StrutureOrgin=[Struture[1][0],Struture[1][1]]
|
StrutureOrgin=[Struture[1][0],Struture[1][1]]
|
||||||
|
|
@ -631,9 +640,9 @@ class VlsiLayout:
|
||||||
debug.info(debug_level,"-Structure direction: vVector["+str(StruturevVector)+"]")
|
debug.info(debug_level,"-Structure direction: vVector["+str(StruturevVector)+"]")
|
||||||
|
|
||||||
for boundary in self.structures[str(StrutureName)].boundaries:
|
for boundary in self.structures[str(StrutureName)].boundaries:
|
||||||
left_button=boundary.coordinates[0]
|
left_bottom=boundary.coordinates[0]
|
||||||
right_top=boundary.coordinates[2]
|
right_top=boundary.coordinates[2]
|
||||||
thisBoundary=[left_button[0],left_button[1],right_top[0],right_top[1]]
|
thisBoundary=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]]
|
||||||
thisBoundary=self.tranformRectangle(thisBoundary,StrutureuVector,StruturevVector)
|
thisBoundary=self.tranformRectangle(thisBoundary,StrutureuVector,StruturevVector)
|
||||||
thisBoundary=[thisBoundary[0]+StrutureOrgin[0],thisBoundary[1]+StrutureOrgin[1],
|
thisBoundary=[thisBoundary[0]+StrutureOrgin[0],thisBoundary[1]+StrutureOrgin[1],
|
||||||
thisBoundary[2]+StrutureOrgin[0],thisBoundary[3]+StrutureOrgin[1]]
|
thisBoundary[2]+StrutureOrgin[0],thisBoundary[3]+StrutureOrgin[1]]
|
||||||
|
|
@ -670,7 +679,8 @@ class VlsiLayout:
|
||||||
pin_boundary=self.readPinInStructureList(label_coordinate, label_layer)
|
pin_boundary=self.readPinInStructureList(label_coordinate, label_layer)
|
||||||
debug.info(debug_level, "Find pin covers "+str(label_name)+" at "+str(pin_boundary))
|
debug.info(debug_level, "Find pin covers "+str(label_name)+" at "+str(pin_boundary))
|
||||||
|
|
||||||
pin_boundary=[pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0],pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]]
|
pin_boundary=[pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0],
|
||||||
|
pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]]
|
||||||
return [label_name, label_layer, pin_boundary]
|
return [label_name, label_layer, pin_boundary]
|
||||||
|
|
||||||
def readPinInStructureList(self,label_coordinates,layer):
|
def readPinInStructureList(self,label_coordinates,layer):
|
||||||
|
|
@ -692,9 +702,9 @@ class VlsiLayout:
|
||||||
|
|
||||||
for boundary in self.structures[str(StrutureName)].boundaries:
|
for boundary in self.structures[str(StrutureName)].boundaries:
|
||||||
if layer==boundary.drawingLayer:
|
if layer==boundary.drawingLayer:
|
||||||
left_button=boundary.coordinates[0]
|
left_bottom=boundary.coordinates[0]
|
||||||
right_top=boundary.coordinates[2]
|
right_top=boundary.coordinates[2]
|
||||||
MetalBoundary=[left_button[0],left_button[1],right_top[0],right_top[1]]
|
MetalBoundary=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]]
|
||||||
MetalBoundary=self.tranformRectangle(MetalBoundary,StrutureuVector,StruturevVector)
|
MetalBoundary=self.tranformRectangle(MetalBoundary,StrutureuVector,StruturevVector)
|
||||||
MetalBoundary=[MetalBoundary[0]+StrutureOrgin[0],MetalBoundary[1]+StrutureOrgin[1],
|
MetalBoundary=[MetalBoundary[0]+StrutureOrgin[0],MetalBoundary[1]+StrutureOrgin[1],
|
||||||
MetalBoundary[2]+StrutureOrgin[0],MetalBoundary[3]+StrutureOrgin[1]]
|
MetalBoundary[2]+StrutureOrgin[0],MetalBoundary[3]+StrutureOrgin[1]]
|
||||||
|
|
@ -707,18 +717,18 @@ class VlsiLayout:
|
||||||
return label_boundary
|
return label_boundary
|
||||||
|
|
||||||
def tranformRectangle(self,orignalRectangle,uVector,vVector):
|
def tranformRectangle(self,orignalRectangle,uVector,vVector):
|
||||||
LeftButton=mpmath.matrix([orignalRectangle[0],orignalRectangle[1]])
|
LeftBottom=mpmath.matrix([orignalRectangle[0],orignalRectangle[1]])
|
||||||
LeftButton=self.tranformCoordinate(LeftButton,uVector,vVector)
|
LeftBottom=self.tranformCoordinate(LeftBottom,uVector,vVector)
|
||||||
|
|
||||||
RightUp=mpmath.matrix([orignalRectangle[2],orignalRectangle[3]])
|
RightUp=mpmath.matrix([orignalRectangle[2],orignalRectangle[3]])
|
||||||
RightUp=self.tranformCoordinate(RightUp,uVector,vVector)
|
RightUp=self.tranformCoordinate(RightUp,uVector,vVector)
|
||||||
|
|
||||||
Left=min(LeftButton[0],RightUp[0])
|
Left=min(LeftBottom[0],RightUp[0])
|
||||||
Button=min(LeftButton[1],RightUp[1])
|
Bottom=min(LeftBottom[1],RightUp[1])
|
||||||
Right=max(LeftButton[0],RightUp[0])
|
Right=max(LeftBottom[0],RightUp[0])
|
||||||
Up=max(LeftButton[1],RightUp[1])
|
Up=max(LeftBottom[1],RightUp[1])
|
||||||
|
|
||||||
return [Left,Button,Right,Up]
|
return [Left,Bottom,Right,Up]
|
||||||
|
|
||||||
def tranformCoordinate(self,Coordinate,uVector,vVector):
|
def tranformCoordinate(self,Coordinate,uVector,vVector):
|
||||||
x=Coordinate[0]*uVector[0]+Coordinate[1]*uVector[1]
|
x=Coordinate[0]*uVector[0]+Coordinate[1]*uVector[1]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
from PIL import ImageColor
|
||||||
|
|
||||||
|
class cell:
|
||||||
|
"""
|
||||||
|
A single cell that can be occupied in a given layer, blocked,
|
||||||
|
visited, etc.
|
||||||
|
"""
|
||||||
|
scale=1
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.visited = 0
|
||||||
|
|
||||||
|
self.blocked = False
|
||||||
|
|
||||||
|
self.is_source = False
|
||||||
|
self.is_target = False
|
||||||
|
|
||||||
|
|
||||||
|
def get_color(self):
|
||||||
|
|
||||||
|
# Blues are horizontal
|
||||||
|
if self.blocked:
|
||||||
|
return ImageColor.getrgb("Blue")
|
||||||
|
# Reds are source/sink
|
||||||
|
if self.is_source or self.is_target:
|
||||||
|
return ImageColor.getrgb("Red")
|
||||||
|
|
||||||
|
if self.visited>0:
|
||||||
|
return [255-min(int(self.visited/cell.scale * 255),255)] * 3
|
||||||
|
|
||||||
|
return [255,255,255]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
import debug
|
||||||
|
|
||||||
|
from cell import cell
|
||||||
|
|
||||||
|
class grid:
|
||||||
|
"""A two layer routing map. Each cell can be blocked in the vertical
|
||||||
|
or horizontal layer.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, width, height):
|
||||||
|
""" Create a routing map of width x height cells and 2 in the z-axis. """
|
||||||
|
self.width=width
|
||||||
|
self.height=height
|
||||||
|
self.map={}
|
||||||
|
for x in range(width):
|
||||||
|
for y in range(height):
|
||||||
|
for z in range(2):
|
||||||
|
self.map[x,y,z]=cell()
|
||||||
|
|
||||||
|
def view(self,):
|
||||||
|
"""
|
||||||
|
View the data by creating an RGB array and mapping the data
|
||||||
|
structure to the RGB color palette.
|
||||||
|
"""
|
||||||
|
|
||||||
|
v_map = np.zeros((self.width,self.height,3), 'uint8')
|
||||||
|
mid_map = np.ones((25,self.height,3), 'uint8')
|
||||||
|
h_map = np.ones((self.width,self.height,3), 'uint8')
|
||||||
|
|
||||||
|
# We shouldn't have a path greater than 50% the HPWL
|
||||||
|
# so scale all visited indices by this value for colorization
|
||||||
|
cell.scale = 1.5 * (self.width+self.height)
|
||||||
|
for x in range(self.width):
|
||||||
|
for y in range(self.height):
|
||||||
|
h_map[x,y] = self.map[x,y,0].get_color()
|
||||||
|
v_map[x,y] = self.map[x,y,1].get_color()
|
||||||
|
|
||||||
|
v_img = Image.fromarray(v_map, 'RGB').rotate(90)
|
||||||
|
mid_img = Image.fromarray(mid_map, 'RGB').rotate(90)
|
||||||
|
h_img = Image.fromarray(h_map, 'RGB').rotate(90)
|
||||||
|
|
||||||
|
# concatenate them into a plot with the two layers
|
||||||
|
img = Image.new('RGB', (2*self.width+25, self.height))
|
||||||
|
img.paste(h_img, (0,0))
|
||||||
|
img.paste(mid_img, (self.width,0))
|
||||||
|
img.paste(v_img, (self.width+25,0))
|
||||||
|
img.show()
|
||||||
|
|
||||||
|
def set_property(self,ll,ur,z,name,value=True):
|
||||||
|
for x in range(int(ll[0]),int(ur[0])):
|
||||||
|
for y in range(int(ll[1]),int(ur[1])):
|
||||||
|
setattr (self.map[x,y,z], name, True)
|
||||||
|
|
||||||
|
def add_blockage(self,ll,ur,z):
|
||||||
|
debug.info(1,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
|
||||||
|
self.set_property(ll,ur,z,"blocked")
|
||||||
|
|
||||||
|
def set_source(self,ll,ur,z):
|
||||||
|
debug.info(1,"Adding source ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
|
||||||
|
self.set_property(ll,ur,z,"is_source")
|
||||||
|
|
||||||
|
def set_target(self,ll,ur,z):
|
||||||
|
debug.info(1,"Adding target ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
|
||||||
|
self.set_property(ll,ur,z,"is_target")
|
||||||
|
|
||||||
|
|
@ -1,40 +1,120 @@
|
||||||
import gdsMill
|
import gdsMill
|
||||||
import tech
|
import tech
|
||||||
|
from contact import contact
|
||||||
import math
|
import math
|
||||||
import debug
|
import debug
|
||||||
from collections import defaultdict
|
from vector import vector
|
||||||
|
import grid
|
||||||
|
|
||||||
|
|
||||||
class router:
|
class router:
|
||||||
"""A router class to read an obstruction map from a gds and plan a
|
"""A router class to read an obstruction map from a gds and plan a
|
||||||
route on a given layer.
|
route on a given layer. This is limited to two layer routes.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, gdsName, topName, layers):
|
def __init__(self, gds_name):
|
||||||
"""Use the gds file for the blockages with the top module topName and
|
"""Use the gds file for the blockages with the top module topName and
|
||||||
layers for the layers to route on
|
layers for the layers to route on
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.topName = topName
|
self.gds_name = gds_name
|
||||||
self.gdsName = gdsName
|
self.layout = gdsMill.VlsiLayout()
|
||||||
self.layout = gdsMill.VlsiLayout(units=tech.GDS["unit"])
|
|
||||||
self.reader = gdsMill.Gds2reader(self.layout)
|
self.reader = gdsMill.Gds2reader(self.layout)
|
||||||
self.reader.loadFromFile(gdsName)
|
self.reader.loadFromFile(gds_name)
|
||||||
|
self.top_name = self.layout.rootStructureName
|
||||||
self.unit = float(self.layout.info['units'][0])
|
self.unit = float(self.layout.info['units'][0])
|
||||||
self.layers = layers
|
print "Units:",self.unit
|
||||||
|
|
||||||
self.find_blockages()
|
self.pin_names = []
|
||||||
def create_map(self):
|
self.pin_shapes = {}
|
||||||
pass
|
self.pin_layers = {}
|
||||||
|
|
||||||
|
self.boundary = self.layout.measureBoundary(self.top_name)
|
||||||
|
self.ll = vector(self.boundary[0])
|
||||||
|
self.ur = vector(self.boundary[1])
|
||||||
|
self.size = self.ur - self.ll
|
||||||
|
self.width = self.size.x
|
||||||
|
self.height = self.size.y
|
||||||
|
|
||||||
|
print "Boundary: ",self.boundary
|
||||||
|
print "Size: ", self.width,self.height
|
||||||
|
|
||||||
|
# to scale coordinates by units
|
||||||
|
self.unit_factor = [self.unit] * 2
|
||||||
|
|
||||||
|
# We will offset so ll is at (0,0)
|
||||||
|
self.offset = self.ll
|
||||||
|
print "Offset: ",self.offset
|
||||||
|
|
||||||
|
|
||||||
|
def set_top(self,top_name):
|
||||||
|
""" If we want to route something besides the top-level cell."""
|
||||||
|
self.top_name = top_name
|
||||||
|
|
||||||
|
|
||||||
|
def set_layers(self, layers):
|
||||||
|
""" Allows us to change the layers that we are routing on. """
|
||||||
|
self.layers = layers
|
||||||
|
(horiz_layer, via_layer, vert_layer) = self.layers
|
||||||
|
if (via_layer != None):
|
||||||
|
self.via_layer_name = via_layer
|
||||||
|
else:
|
||||||
|
self.via_layer_name = None
|
||||||
|
|
||||||
|
self.vert_layer_name = vert_layer
|
||||||
|
self.vert_layer_width = tech.drc["minwidth_{0}".format(vert_layer)]
|
||||||
|
self.vert_layer_number = tech.layer[vert_layer]
|
||||||
|
|
||||||
|
self.horiz_layer_name = horiz_layer
|
||||||
|
self.horiz_layer_width = tech.drc["minwidth_{0}".format(horiz_layer)]
|
||||||
|
self.horiz_layer_number = tech.layer[horiz_layer]
|
||||||
|
|
||||||
|
# contacted track spacing
|
||||||
|
via_connect = contact(self.layers, (1, 1))
|
||||||
|
self.horiz_track_width = tech.drc[str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)] + via_connect.width
|
||||||
|
self.vert_track_width = tech.drc[str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name)] + via_connect.width
|
||||||
|
|
||||||
|
# This is so we can use a single resolution grid for both layers
|
||||||
|
self.track_width = max(self.horiz_track_width,self.vert_track_width)
|
||||||
|
print "Track width:",self.track_width
|
||||||
|
|
||||||
|
# to scale coordinates to tracks
|
||||||
|
self.track_factor = [1/self.track_width] * 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def create_routing_grid(self):
|
||||||
|
""" Create a routing grid that spans given area. Wires cannot exist outside region. """
|
||||||
|
|
||||||
|
self.width_in_tracks = int(math.ceil(self.width/self.track_width))
|
||||||
|
self.height_in_tracks = int(math.ceil(self.height/self.track_width))
|
||||||
|
|
||||||
|
print "Size (in tracks): ", self.width_in_tracks, self.height_in_tracks
|
||||||
|
|
||||||
|
self.rg = grid.grid(self.width_in_tracks,self.height_in_tracks)
|
||||||
|
|
||||||
|
|
||||||
|
def find_pin(self,pin):
|
||||||
|
""" Finds the offsets to the gds pins """
|
||||||
|
(pin_name,pin_layer,pin_shape) = self.layout.readPin(str(pin))
|
||||||
|
# repack the shape as a pair of vectors rather than four values
|
||||||
|
new_shape = self.convert_to_tracks([vector(pin_shape[0],pin_shape[1]),vector(pin_shape[2],pin_shape[3])])
|
||||||
|
self.pin_names.append(pin_name)
|
||||||
|
self.pin_shapes[str(pin)] = new_shape
|
||||||
|
self.pin_layers[str(pin)] = pin_layer
|
||||||
|
return new_shape
|
||||||
|
|
||||||
def find_blockages(self):
|
def find_blockages(self):
|
||||||
for layer in self.layer:
|
if len(self.pin_names)!=2:
|
||||||
debug.info("Layer: " + layer)
|
debug.error("Must set pins before creating blockages.",-1)
|
||||||
self.writeObstruct(self.topName, layer)
|
|
||||||
|
for layer in self.layers:
|
||||||
|
self.write_obstacle(self.top_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_route(self,start, end, layerstack):
|
def add_route(self,start, end, layerstack):
|
||||||
""" Add a wire route from the start to the end point"""
|
""" Add a wire route from the start to the end point"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def create_steiner_routes(self,pins):
|
def create_steiner_routes(self,pins):
|
||||||
|
|
@ -46,30 +126,115 @@ class router:
|
||||||
""" Find the set of steiner points and return them."""
|
""" Find the set of steiner points and return them."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def translate_coordinates(self, coord, mirr, angle, xyShift):
|
||||||
|
"""Calculate coordinates after flip, rotate, and shift"""
|
||||||
|
coordinate = []
|
||||||
|
for item in coord:
|
||||||
|
x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0])
|
||||||
|
y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1])
|
||||||
|
coordinate += [(x, y)]
|
||||||
|
return coordinate
|
||||||
|
|
||||||
def writeObstruct(self, sr, lay, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0)):
|
def min_max_coord(self, coordTrans):
|
||||||
|
"""Find the lowest and highest conner of a Rectangle"""
|
||||||
|
coordinate = []
|
||||||
|
minx = min(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0])
|
||||||
|
maxx = max(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0])
|
||||||
|
miny = min(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1])
|
||||||
|
maxy = max(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1])
|
||||||
|
coordinate += [vector(minx, miny)]
|
||||||
|
coordinate += [vector(maxx, maxy)]
|
||||||
|
return coordinate
|
||||||
|
|
||||||
|
def set_source(self,name):
|
||||||
|
shape = self.find_pin(name)
|
||||||
|
zindex = 0 if self.pin_layers[name]==self.horiz_layer_number else 1
|
||||||
|
debug.info(0,"Set source: " + str(name) + " " + str(shape) + " z=" + str(zindex))
|
||||||
|
self.rg.set_source(shape[0],shape[1],zindex)
|
||||||
|
|
||||||
|
def set_target(self,name):
|
||||||
|
shape = self.find_pin(name)
|
||||||
|
zindex = 0 if self.pin_layers[name]==self.horiz_layer_number else 1
|
||||||
|
debug.info(0,"Set target: " + str(name) + " " + str(shape) + " z=" + str(zindex))
|
||||||
|
self.rg.set_target(shape[0],shape[1],zindex)
|
||||||
|
|
||||||
|
def write_obstacle(self, sref, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0)):
|
||||||
"""Recursive write boundaries on each Structure in GDS file to LEF"""
|
"""Recursive write boundaries on each Structure in GDS file to LEF"""
|
||||||
for boundary in self.layout.structures[sr].boundaries:
|
|
||||||
coordTrans = self.coordinatesTranslate(boundary.coordinates, mirr, angle, xyShift)
|
|
||||||
rect = self.minMaxCoord(coordTrans)
|
|
||||||
lay_convert = tech.layer[lay]
|
|
||||||
if boundary.drawingLayer == lay_convert:
|
|
||||||
text = " RECT "
|
|
||||||
for item in rect:
|
|
||||||
text += " {0} {1}".format(item[0]*self.unit, item[1]*self.unit)
|
|
||||||
debug.info(text)
|
|
||||||
|
|
||||||
for sref in self.layout.structures[sr].srefs:
|
for boundary in self.layout.structures[sref].boundaries:
|
||||||
|
coordTrans = self.translate_coordinates(boundary.coordinates, mirr, angle, xyShift)
|
||||||
|
shape = self.min_max_coord(coordTrans)
|
||||||
|
|
||||||
|
if boundary.drawingLayer in [self.vert_layer_number,self.horiz_layer_number]:
|
||||||
|
ll_microns=shape[0].scale(self.unit_factor)
|
||||||
|
ur_microns=shape[1].scale(self.unit_factor)
|
||||||
|
|
||||||
|
shape_tracks=self.convert_to_tracks([ll_microns,ur_microns])
|
||||||
|
|
||||||
|
if shape_tracks not in self.pin_shapes.values():
|
||||||
|
# inflate the ll and ur by 1 track in each direction
|
||||||
|
[ll,ur]=shape_tracks
|
||||||
|
ll = vector(0,0).max(ll + vector(-1,-1))
|
||||||
|
ur = vector(self.width_in_tracks-1,self.height_in_tracks-1).min(ur + vector(1,1))
|
||||||
|
zlayer = 0 if boundary.drawingLayer==self.horiz_layer_number else 1
|
||||||
|
debug.info(1,"Blockage: "+str([ll,ur])+" z="+str(zlayer))
|
||||||
|
self.rg.add_blockage(ll,ur,zlayer)
|
||||||
|
else:
|
||||||
|
debug.info(2,"Skip: "+str(shape_tracks))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# recurse given the mirror, angle, etc.
|
||||||
|
for cur_sref in self.layout.structures[sref].srefs:
|
||||||
sMirr = 1
|
sMirr = 1
|
||||||
if sref.transFlags[0] == True:
|
if sref.transFlags[0] == True:
|
||||||
sMirr = -1
|
sMirr = -1
|
||||||
sAngle = math.radians(float(0))
|
sAngle = math.radians(float(0))
|
||||||
if sref.rotateAngle:
|
if sref.rotateAngle:
|
||||||
sAngle = math.radians(float(sref.rotateAngle))
|
sAngle = math.radians(float(cur_sref.rotateAngle))
|
||||||
sAngle += angle
|
sAngle += angle
|
||||||
x = sref.coordinates[0]
|
x = cur_sref.coordinates[0]
|
||||||
y = sref.coordinates[1]
|
y = cur_sref.coordinates[1]
|
||||||
newX = (x)*math.cos(angle) - mirr*(y)*math.sin(angle) + xyShift[0]
|
newX = (x)*math.cos(angle) - mirr*(y)*math.sin(angle) + xyShift[0]
|
||||||
newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1]
|
newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1]
|
||||||
sxyShift = (newX, newY)
|
sxyShift = (newX, newY)
|
||||||
self.writeObstruct(sref.sName, lay,sMirr, sAngle, sxyShift)
|
|
||||||
|
self.write_obstacle(cur_sref.sName, layer,sMirr, sAngle, sxyShift)
|
||||||
|
|
||||||
|
def inflate_obstacle(self,shape):
|
||||||
|
# TODO: inflate by the layer design rules
|
||||||
|
return shape
|
||||||
|
|
||||||
|
def convert_to_tracks(self,shape):
|
||||||
|
"""
|
||||||
|
Convert a rectangular shape into track units.
|
||||||
|
"""
|
||||||
|
[ll,ur] = shape
|
||||||
|
|
||||||
|
# fix offset
|
||||||
|
ll = snap_to_grid(ll-self.offset)
|
||||||
|
ur = snap_to_grid(ur-self.offset)
|
||||||
|
|
||||||
|
# always round down, because we will add a track
|
||||||
|
# to inflate each object later
|
||||||
|
ll = ll.scale(self.track_factor).ceil()
|
||||||
|
ur = ur.scale(self.track_factor).floor()
|
||||||
|
|
||||||
|
return [ll,ur]
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME: This should be replaced with vector.snap_to_grid at some point
|
||||||
|
def snap_to_grid(offset):
|
||||||
|
"""
|
||||||
|
Changes the coodrinate to match the grid settings
|
||||||
|
"""
|
||||||
|
grid = tech.drc["grid"]
|
||||||
|
x = offset[0]
|
||||||
|
y = offset[1]
|
||||||
|
# this gets the nearest integer value
|
||||||
|
xgrid = int(round(round((x / grid), 2), 0))
|
||||||
|
ygrid = int(round(round((y / grid), 2), 0))
|
||||||
|
xoff = xgrid * grid
|
||||||
|
yoff = ygrid * grid
|
||||||
|
return vector(xoff, yoff)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env python2.7
|
||||||
|
"Run a regresion test the library cells for DRC"
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from testutils import header
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.path.join(sys.path[0],"../.."))
|
||||||
|
sys.path.append(os.path.join(sys.path[0],".."))
|
||||||
|
import globals
|
||||||
|
import debug
|
||||||
|
import calibre
|
||||||
|
import vector
|
||||||
|
|
||||||
|
class no_blockages_test(unittest.TestCase):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||||
|
|
||||||
|
import router
|
||||||
|
#r=router.router("A_to_B_no_blockages.gds")
|
||||||
|
r=router.router("A_to_B_m1m2_blockages.gds")
|
||||||
|
|
||||||
|
r.set_layers(("metal1","via1","metal2"))
|
||||||
|
|
||||||
|
r.create_routing_grid()
|
||||||
|
|
||||||
|
r.set_source("A")
|
||||||
|
|
||||||
|
r.set_target("B")
|
||||||
|
|
||||||
|
r.find_blockages()
|
||||||
|
r.rg.view()
|
||||||
|
|
||||||
|
#drc_errors = calibre.run_drc(name, gds_name)
|
||||||
|
drc_errors = 1
|
||||||
|
|
||||||
|
# fails if there are any DRC errors on any cells
|
||||||
|
self.assertEqual(drc_errors, 0)
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# instantiate a copy of the class to actually run the test
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main()
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,23 @@
|
||||||
|
word_size = 1
|
||||||
|
num_words = 16
|
||||||
|
num_banks = 1
|
||||||
|
|
||||||
|
tech_name = "freepdk45"
|
||||||
|
|
||||||
|
decoder = "hierarchical_decoder"
|
||||||
|
ms_flop = "ms_flop"
|
||||||
|
ms_flop_array = "ms_flop_array"
|
||||||
|
control_logic = "control_logic"
|
||||||
|
bitcell_array = "bitcell_array"
|
||||||
|
sense_amp = "sense_amp"
|
||||||
|
sense_amp_array = "sense_amp_array"
|
||||||
|
precharge_array = "precharge_array"
|
||||||
|
column_mux_array = "single_level_column_mux_array"
|
||||||
|
write_driver = "write_driver"
|
||||||
|
write_driver_array = "write_driver_array"
|
||||||
|
tri_gate = "tri_gate"
|
||||||
|
tri_gate_array = "tri_gate_array"
|
||||||
|
wordline_driver = "wordline_driver"
|
||||||
|
replica_bitcell = "replica_bitcell"
|
||||||
|
bitcell = "bitcell"
|
||||||
|
delay_chain = "logic_effort_dc"
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
word_size = 1
|
||||||
|
num_words = 16
|
||||||
|
num_banks = 1
|
||||||
|
|
||||||
|
tech_name = "scn3me_subm"
|
||||||
|
|
||||||
|
decoder = "hierarchical_decoder"
|
||||||
|
ms_flop = "ms_flop"
|
||||||
|
ms_flop_array = "ms_flop_array"
|
||||||
|
control_logic = "control_logic"
|
||||||
|
bitcell_array = "bitcell_array"
|
||||||
|
sense_amp = "sense_amp"
|
||||||
|
sense_amp_array = "sense_amp_array"
|
||||||
|
precharge_array = "precharge_array"
|
||||||
|
column_mux_array = "single_level_column_mux_array"
|
||||||
|
write_driver = "write_driver"
|
||||||
|
write_driver_array = "write_driver_array"
|
||||||
|
tri_gate = "tri_gate"
|
||||||
|
tri_gate_array = "tri_gate_array"
|
||||||
|
wordline_driver = "wordline_driver"
|
||||||
|
replica_bitcell = "replica_bitcell"
|
||||||
|
bitcell = "bitcell"
|
||||||
|
delay_chain = "logic_effort_dc"
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env python2.7
|
||||||
|
|
||||||
|
import re
|
||||||
|
import unittest
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.path.join(sys.path[0],".."))
|
||||||
|
import globals
|
||||||
|
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
|
||||||
|
from testutils import header
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
|
||||||
|
# get a list of all files in the tests directory
|
||||||
|
files = os.listdir(sys.path[0])
|
||||||
|
|
||||||
|
# assume any file that ends in "test.py" in it is a regression test
|
||||||
|
nametest = re.compile("test\.py$", re.IGNORECASE)
|
||||||
|
tests = filter(nametest.search, files)
|
||||||
|
tests.sort()
|
||||||
|
|
||||||
|
# import all of the modules
|
||||||
|
filenameToModuleName = lambda f: os.path.splitext(f)[0]
|
||||||
|
moduleNames = map(filenameToModuleName, tests)
|
||||||
|
modules = map(__import__, moduleNames)
|
||||||
|
suite = unittest.TestSuite()
|
||||||
|
load = unittest.defaultTestLoader.loadTestsFromModule
|
||||||
|
suite.addTests(map(load, modules))
|
||||||
|
unittest.TextTestRunner(verbosity=2).run(suite)
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import debug
|
import debug
|
||||||
|
import math
|
||||||
|
|
||||||
class vector():
|
class vector():
|
||||||
"""
|
"""
|
||||||
|
|
@ -61,6 +61,7 @@ class vector():
|
||||||
"""
|
"""
|
||||||
return vector(self.x + other[0], self.y + other[1])
|
return vector(self.x + other[0], self.y + other[1])
|
||||||
|
|
||||||
|
|
||||||
def __radd__(self, other):
|
def __radd__(self, other):
|
||||||
"""
|
"""
|
||||||
Override + function (right add)
|
Override + function (right add)
|
||||||
|
|
@ -99,3 +100,33 @@ class vector():
|
||||||
y_factor=x_factor[1]
|
y_factor=x_factor[1]
|
||||||
x_factor=x_factor[0]
|
x_factor=x_factor[0]
|
||||||
return vector(self.y*x_factor,self.x*y_factor)
|
return vector(self.y*x_factor,self.x*y_factor)
|
||||||
|
|
||||||
|
def floor(self):
|
||||||
|
"""
|
||||||
|
Override floor function
|
||||||
|
"""
|
||||||
|
return vector(int(math.floor(self.x)),int(math.floor(self.y)))
|
||||||
|
|
||||||
|
def ceil(self):
|
||||||
|
"""
|
||||||
|
Override ceil function
|
||||||
|
"""
|
||||||
|
return vector(int(math.ceil(self.x)),int(math.ceil(self.y)))
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""Override the default Equals behavior"""
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
return self.__dict__ == other.__dict__
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
"""Override the default non-equality behavior"""
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def max(self, other):
|
||||||
|
""" Max of both values """
|
||||||
|
return vector(max(self.x,other.x),max(self.y,other.y))
|
||||||
|
|
||||||
|
def min(self, other):
|
||||||
|
""" Min of both values """
|
||||||
|
return vector(min(self.x,other.x),min(self.y,other.y))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue