diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6eceeaaf..582a998e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,7 +20,7 @@ other OpenRAM features. Please see the README.md file on how to run the unit tests. Unit tests should work in all technologies. We will run the tests on your contributions before they will be accepted. -# Internally Development +# Internal Development For internal development, follow all of the following steps EXCEPT do not fork your own copy. Instead, create a branch in our private repository @@ -32,21 +32,21 @@ All unit tests should pass first. 1. One time, create a GitHub account at http://github.com 2. Create a fork of the OpenRAM project on the github web page: - https://github.com/mguthaus/OpenRAM + https://github.com/vlsida/openram It is on the upper right and says "Fork": This will make your own OpenRAM repository on GitHub in your account. 3. Clone your repository (or use an existing cloned copy if you've already done this once): ``` - git clone https://github.com//OpenRAM.git - cd OpenRAM + git clone https://github.com//oepnram.git + cd openram ``` 4. Set up a new upstream that points to MY OpenRAM repository that you forked (only first time): ``` - git remote add upstream https://github.com/mguthaus/OpenRAM.git + git remote add upstream https://github.com/vlsida/openram.git ``` You now have two remotes for this project: * origin which points to your GitHub fork of the project. You can read diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index b766b1af..33bcaaa2 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -147,8 +147,12 @@ class instance(geometry): self.width = 0 self.height = 0 else: - self.width = round_to_grid(mod.width) - self.height = round_to_grid(mod.height) + if mirror in ["R90","R270"] or rotate in [90,270]: + self.width = round_to_grid(mod.height) + self.height = round_to_grid(mod.width) + else: + self.width = round_to_grid(mod.width) + self.height = round_to_grid(mod.height) self.compute_boundary(offset,mirror,rotate) debug.info(4, "creating instance: " + self.name) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 7565c6ce..c5434246 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -2,6 +2,7 @@ import debug from tech import GDS, drc from vector import vector from tech import layer +import math class pin_layout: """ @@ -18,6 +19,10 @@ class pin_layout: self.rect = [vector(rect[0]),vector(rect[1])] # snap the rect to the grid self.rect = [x.snap_to_grid() for x in self.rect] + + debug.check(self.width()>0,"Zero width pin.") + debug.check(self.height()>0,"Zero height pin.") + # if it's a layer number look up the layer name. this assumes a unique layer number. if type(layer_name_num)==int: self.layer = list(layer.keys())[list(layer.values()).index(layer_name_num)] @@ -113,24 +118,45 @@ class pin_layout: return y_overlaps + def xcontains(self, other): + """ Check if shape contains the x overlap """ + (ll,ur) = self.rect + (oll,our) = other.rect + + return (oll.x >= ll.x and our.x <= ur.x) + + def ycontains(self, other): + """ Check if shape contains the y overlap """ + (ll,ur) = self.rect + (oll,our) = other.rect + + return (oll.y >= ll.y and our.y <= ur.y) + def contains(self, other): """ Check if a shape contains another rectangle """ + # If it is the same shape entirely, it is contained! + if self == other: + return True + # Can only overlap on the same layer if self.layer != other.layer: return False - (ll,ur) = self.rect - (oll,our) = other.rect - - - if not (oll.y >= ll.y and oll.y <= ur.y): + if not self.xcontains(other): return False - if not (oll.x >= ll.x and oll.x <= ur.x): + if not self.ycontains(other): return False return True - + + def contained_by_any(self, shape_list): + """ Checks if shape is contained by any in the list """ + for shape in shape_list: + if shape.contains(self): + return True + return False + def overlaps(self, other): """ Check if a shape overlaps with a rectangle """ @@ -274,3 +300,127 @@ class pin_layout: magnification=GDS["zoom"], rotate=None) + + def compute_overlap(self, other): + """ Calculate the rectangular overlap of two rectangles. """ + (r1_ll,r1_ur) = self.rect + (r2_ll,r2_ur) = other.rect + + #ov_ur = vector(min(r1_ur.x,r2_ur.x),min(r1_ur.y,r2_ur.y)) + #ov_ll = vector(max(r1_ll.x,r2_ll.x),max(r1_ll.y,r2_ll.y)) + + dy = min(r1_ur.y,r2_ur.y)-max(r1_ll.y,r2_ll.y) + dx = min(r1_ur.x,r2_ur.x)-max(r1_ll.x,r2_ll.x) + + if dx>=0 and dy>=0: + return [dx,dy] + else: + return [0,0] + + def overlap_length(self, other): + """ + Calculate the intersection segment and determine its length + """ + + if self.contains(other): + return math.inf + elif other.contains(self): + return math.inf + else: + intersections = self.compute_overlap_segment(other) + # This is the common case where two pairs of edges overlap + # at two points, so just find the distance between those two points + if len(intersections)==2: + (p1,p2) = intersections + return math.sqrt(pow(p1[0]-p2[0],2) + pow(p1[1]-p2[1],2)) + else: + # This is where we had a corner intersection or none + return 0 + + + def compute_overlap_segment(self, other): + """ + Calculate the intersection segment of two rectangles + (if any) + """ + (r1_ll,r1_ur) = self.rect + (r2_ll,r2_ur) = other.rect + + # The other corners besides ll and ur + r1_ul = vector(r1_ll.x, r1_ur.y) + r1_lr = vector(r1_ur.x, r1_ll.y) + r2_ul = vector(r2_ll.x, r2_ur.y) + r2_lr = vector(r2_ur.x, r2_ll.y) + + from itertools import tee + def pairwise(iterable): + "s -> (s0,s1), (s1,s2), (s2, s3), ..." + a, b = tee(iterable) + next(b, None) + return zip(a, b) + + # R1 edges CW + r1_cw_points = [r1_ll, r1_ul, r1_ur, r1_lr, r1_ll] + r1_edges = [] + for (p,q) in pairwise(r1_cw_points): + r1_edges.append([p,q]) + + # R2 edges CW + r2_cw_points = [r2_ll, r2_ul, r2_ur, r2_lr, r2_ll] + r2_edges = [] + for (p,q) in pairwise(r2_cw_points): + r2_edges.append([p,q]) + + # There are 4 edges on each rectangle + # so just brute force check intersection of each + # Two pairs of them should intersect + intersections = [] + for r1e in r1_edges: + for r2e in r2_edges: + i = self.segment_intersection(r1e, r2e) + if i: + intersections.append(i) + + return intersections + + def on_segment(self, p, q, r): + """ + Given three co-linear points, determine if q lies on segment pr + """ + if q[0] <= max(p[0], r[0]) and \ + q[0] >= min(p[0], r[0]) and \ + q[1] <= max(p[1], r[1]) and \ + q[1] >= min(p[1], r[1]): + return True + + return False + + def segment_intersection(self, s1, s2): + """ + Determine the intersection point of two segments + Return the a segment if they overlap. + Return None if they don't. + """ + (a,b) = s1 + (c,d) = s2 + # Line AB represented as a1x + b1y = c1 + a1 = b.y - a.y + b1 = a.x - b.x + c1 = a1*a.x + b1*a.y + + # Line CD represented as a2x + b2y = c2 + a2 = d.y - c.y + b2 = c.x - d.x + c2 = a2*c.x + b2*c.y + + determinant = a1*b2 - a2*b1 + + if determinant!=0: + x = (b2*c1 - b1*c2)/determinant + y = (a1*c2 - a2*c1)/determinant + + r = [x,y] + if self.on_segment(a, r, b) and self.on_segment(c, r, d): + return [x, y] + + return None diff --git a/compiler/base/route.py b/compiler/base/route.py index 0397e383..09925e07 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -62,14 +62,10 @@ class route(design): plist = list(pairwise(self.path)) for p0,p1 in plist: if p0.z != p1.z: # via - # offset if not rotated - #via_offset = vector(p0.x+0.5*self.c.width,p0.y+0.5*self.c.height) - # offset if rotated - via_offset = vector(p0.x+0.5*self.c.height,p0.y-0.5*self.c.width) via_size = [self.num_vias]*2 - self.obj.add_via(self.layer_stack,via_offset,size=via_size,rotate=90) + self.obj.add_via_center(self.layer_stack,vector(p0.x,p0.y),size=via_size,rotate=90) elif p0.x != p1.x and p0.y != p1.y: # diagonal! - debug.error("Non-changing direction!") + debug.error("Diagonal route! {}".format(self.path),-3) else: # this will draw an extra corner at the end but that is ok self.draw_corner_wire(p1) diff --git a/compiler/base/utils.py b/compiler/base/utils.py index 28c5f997..8ca22fa9 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -66,7 +66,7 @@ def get_gds_size(name, gds_filename, units, layer): Open a GDS file and return the size from either the bounding box or a border layer. """ - debug.info(2,"Creating VLSI layout for {}".format(name)) + debug.info(4,"Creating VLSI layout for {}".format(name)) cell_vlsi = gdsMill.VlsiLayout(units=units) reader = gdsMill.Gds2reader(cell_vlsi) reader.loadFromFile(gds_filename) @@ -88,9 +88,9 @@ def get_libcell_size(name, units, layer): return(get_gds_size(name, cell_gds, units, layer)) -def get_gds_pins(pin_list, name, gds_filename, units, layer): +def get_gds_pins(pin_names, name, gds_filename, units): """ - Open a GDS file and find the pins in pin_list as text on a given layer. + Open a GDS file and find the pins in pin_names as text on a given layer. Return these as a rectangle layer pair for each pin. """ cell_vlsi = gdsMill.VlsiLayout(units=units) @@ -98,23 +98,23 @@ def get_gds_pins(pin_list, name, gds_filename, units, layer): reader.loadFromFile(gds_filename) cell = {} - for pin in pin_list: - cell[str(pin)]=[] - label_list=cell_vlsi.getPinShapeByLabel(str(pin)) - for label in label_list: - (name,layer,boundary)=label + for pin_name in pin_names: + cell[str(pin_name)]=[] + pin_list=cell_vlsi.getPinShape(str(pin_name)) + for pin_shape in pin_list: + (layer,boundary)=pin_shape rect=[vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])] # this is a list because other cells/designs may have must-connect pins - cell[str(pin)].append(pin_layout(pin, rect, layer)) + cell[str(pin_name)].append(pin_layout(pin_name, rect, layer)) return cell -def get_libcell_pins(pin_list, name, units, layer): +def get_libcell_pins(pin_list, name, units): """ Open a GDS file and find the pins in pin_list as text on a given layer. Return these as a rectangle layer pair for each pin. """ cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" - return(get_gds_pins(pin_list, name, cell_gds, units, layer)) + return(get_gds_pins(pin_list, name, cell_gds, units)) diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index e6943f47..5df86c87 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -13,7 +13,7 @@ class bitcell(design.design): pin_names = ["bl", "br", "wl", "vdd", "gnd"] (width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) def __init__(self): design.design.__init__(self, "cell_6t") diff --git a/compiler/bitcells/bitcell_1rw_1r.py b/compiler/bitcells/bitcell_1rw_1r.py index 2f13a910..47ebe5fc 100644 --- a/compiler/bitcells/bitcell_1rw_1r.py +++ b/compiler/bitcells/bitcell_1rw_1r.py @@ -13,7 +13,7 @@ class bitcell_1rw_1r(design.design): pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] (width,height) = utils.get_libcell_size("cell_1rw_1r", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"]) def __init__(self): design.design.__init__(self, "cell_1rw_1r") diff --git a/compiler/bitcells/replica_bitcell.py b/compiler/bitcells/replica_bitcell.py index 7bbdbe06..ca4b72c0 100644 --- a/compiler/bitcells/replica_bitcell.py +++ b/compiler/bitcells/replica_bitcell.py @@ -12,7 +12,7 @@ class replica_bitcell(design.design): pin_names = ["bl", "br", "wl", "vdd", "gnd"] (width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"]) def __init__(self): design.design.__init__(self, "replica_cell_6t") diff --git a/compiler/bitcells/replica_bitcell_1rw_1r.py b/compiler/bitcells/replica_bitcell_1rw_1r.py new file mode 100644 index 00000000..aaf5b1dc --- /dev/null +++ b/compiler/bitcells/replica_bitcell_1rw_1r.py @@ -0,0 +1,23 @@ +import design +import debug +import utils +from tech import GDS,layer + +class replica_bitcell_1rw_1r(design.design): + """ + A single bit cell which is forced to store a 0. + This module implements the single memory cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. """ + + pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + (width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"]) + + def __init__(self): + design.design.__init__(self, "replica_cell_1rw_1r") + debug.info(2, "Create replica bitcell 1rw+1r object") + + self.width = replica_bitcell_1rw_1r.width + self.height = replica_bitcell_1rw_1r.height + self.pin_map = replica_bitcell_1rw_1r.pin_map diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index d6579ab5..b69ab2e8 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -20,6 +20,10 @@ class functional(simulation): def __init__(self, sram, spfile, corner): simulation.__init__(self, sram, spfile, corner) + + # Seed the characterizer with a constant seed for unit tests + if OPTS.is_unit_test: + random.seed(12345) self.set_corner(corner) self.set_spice_constants() @@ -31,7 +35,15 @@ class functional(simulation): self.stored_words = {} self.write_check = [] self.read_check = [] - + + def set_spice_constants(self): + """Spice constants for functional test""" + simulation.set_spice_constants(self) + #Heuristic increase for functional period. Base feasible period typically does not pass the functional test + #for column mux of this size. Increase the feasible period by 20% for this case. + if self.sram.words_per_row >= 4: + self.period = self.period*1.2 + def run(self): # Generate a random sequence of reads and writes self.write_random_memory_sequence() diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 3d156d2d..e624250a 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -3,7 +3,6 @@ from design import design import debug import math import tech -import random from .stimuli import * from .trim_spice import * from .charutils import * @@ -209,14 +208,14 @@ class simulation(): t_current, t_current+self.period) elif op == "write": - comment = "\tWriting {0} to address {1} (from port {2}) during cylce {3} ({4}ns - {5}ns)".format(word, + comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word, addr, port, int(t_current/self.period), t_current, t_current+self.period) else: - comment = "\tReading {0} from address {1} (from port {2}) during cylce {3} ({4}ns - {5}ns)".format(word, + comment = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word, addr, port, int(t_current/self.period), diff --git a/compiler/example_config_freepdk45.py b/compiler/example_config_freepdk45.py index 5e7a689f..ac02e514 100644 --- a/compiler/example_config_freepdk45.py +++ b/compiler/example_config_freepdk45.py @@ -9,10 +9,3 @@ temperatures = [25] output_path = "temp" output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) -#Setting for multiport -# netlist_only = True -# bitcell = "pbitcell" -# replica_bitcell="replica_pbitcell" -# num_rw_ports = 1 -# num_r_ports = 0 -# num_w_ports = 1 diff --git a/compiler/example_config_scn4m_subm.py b/compiler/example_config_scn4m_subm.py index 436d0ffd..4e3b5aa2 100644 --- a/compiler/example_config_scn4m_subm.py +++ b/compiler/example_config_scn4m_subm.py @@ -3,16 +3,9 @@ num_words = 16 tech_name = "scn4m_subm" process_corners = ["TT"] -supply_voltages = [ 5.0 ] +supply_voltages = [ 3.3 ] temperatures = [ 25 ] output_path = "temp" output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) -#Setting for multiport -netlist_only = True -bitcell = "pbitcell" -replica_bitcell="replica_pbitcell" -num_rw_ports = 1 -num_r_ports = 1 -num_w_ports = 1 \ No newline at end of file diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index f6d301a1..92d7d2a2 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -60,6 +60,11 @@ class VlsiLayout: self.tempCoordinates=None self.tempPassFail = True + # This is a dict indexed by the pin labels. + # It contains a list of list of shapes, one for each occurance of the label. + # Multiple labels may be disconnected. + self.pins = {} + def rotatedCoordinates(self,coordinatesToRotate,rotateAngle): #helper method to rotate a list of coordinates angle=math.radians(float(0)) @@ -206,7 +211,11 @@ class VlsiLayout: def initialize(self): self.deduceHierarchy() #self.traverseTheHierarchy() - self.populateCoordinateMap() + self.populateCoordinateMap() + + for layerNumber in self.layerNumbersInUse: + self.processLabelPins(layerNumber) + def populateCoordinateMap(self): def addToXyTree(startingStructureName = None,transformPath = None): @@ -478,6 +487,10 @@ class VlsiLayout: return False #these shapes are ok def isPointInsideOfBox(self,pointCoordinates,boxCoordinates): + """ + Check if a point is contained in the shape + """ + debug.check(len(boxCoordinates)==4,"Invalid number of coordinates for box.") leftBound = boxCoordinates[0][0] rightBound = boxCoordinates[0][0] topBound = boxCoordinates[0][1] @@ -499,7 +512,9 @@ class VlsiLayout: return True def isShapeInsideOfBox(self,shapeCoordinates, boxCoordinates): - #go through every point in the shape to test if they are all inside the box + """ + Go through every point in the shape to test if they are all inside the box. + """ for point in shapeCoordinates: if not self.isPointInsideOfBox(point,boxCoordinates): return False @@ -634,160 +649,89 @@ class VlsiLayout: return cellBoundary - def getLabelDBInfo(self,label_name): + def getTexts(self, layer): """ - Return the coordinates in DB units and layer of all matching labels + Get all of the labels on a given layer only at the root level. """ - label_list = [] - label_layer = None - label_coordinate = [None, None] - - # Why must this be the last one found? It breaks if we return the first. + text_list = [] for Text in self.structures[self.rootStructureName].texts: - if Text.textString == label_name or Text.textString == label_name+"\x00": - label_layer = Text.drawingLayer - label_coordinate = Text.coordinates[0] - if label_layer!=None: - label_list.append((label_coordinate,label_layer)) - - debug.check(len(label_list)>0,"Did not find labels {0}.".format(label_name)) - return label_list - - - def getLabelInfo(self,label_name): - """ - Return the coordinates in USER units and layer of a label - """ - label_list=self.getLabelDBInfo(label_name) - new_list=[] - for label in label_list: - (label_coordinate,label_layer)=label - user_coordinates = [x*self.units[0] for x in label_coordinate] - new_list.append(user_coordinates,label_layer) - return new_list + if Text.drawingLayer == layer: + text_list.append(Text) + return text_list - def getPinShapeByLocLayer(self, coordinate, layer): - """ - Return the largest enclosing rectangle on a layer and at a location. - Coordinates should be in USER units. - """ - db_coordinate = [x/self.units[0] for x in coordinate] - return self.getPinShapeByDBLocLayer(db_coordinate, layer) - - def getPinShapeByDBLocLayer(self, coordinate, layer): - """ - Return the largest enclosing rectangle on a layer and at a location. - Coordinates should be in DB units. - """ - pin_boundaries=self.getAllPinShapesInStructureList(coordinate, layer) - - if len(pin_boundaries) == 0: - debug.warning("Did not find pin on layer {0} at coordinate {1}".format(layer, coordinate)) - - # sort the boundaries, return the max area pin boundary - pin_boundaries.sort(key=boundaryArea,reverse=True) - pin_boundary=pin_boundaries[0] - - # Convert to USER units - 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]] - - # Make a name if we don't have the pin name - return ["p"+str(coordinate)+"_"+str(layer), layer, pin_boundary] - - def getAllPinShapesByLocLayer(self, coordinate, layer): - """ - Return ALL the enclosing rectangles on the same layer - at the given coordinate. Coordinates should be in USER units. - """ - db_coordinate = [int(x/self.units[0]) for x in coordinate] - return self.getAllPinShapesByDBLocLayer(db_coordinate, layer) - - def getAllPinShapesByDBLocLayer(self, coordinate, layer): - """ - Return ALL the enclosing rectangles on the same layer - at the given coordinate. Input coordinates should be in DB units. - Returns user unit shapes. - """ - pin_boundaries=self.getAllPinShapesInStructureList(coordinate, layer) - - # Convert to user units - new_boundaries = [] - for pin_boundary in pin_boundaries: - new_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]] - new_boundaries.append(["p"+str(coordinate)+"_"+str(layer), layer, new_pin_boundary]) - return new_boundaries - - def getPinShapeByLabel(self,label_name): + def getPinShape(self, pin_name): """ Search for a pin label and return the largest enclosing rectangle on the same layer as the pin label. + If there are multiple pin lists, return the max of each. """ - label_list=self.getLabelDBInfo(label_name) - shape_list=[] - for label in label_list: - (label_coordinate,label_layer)=label - shape = self.getPinShapeByDBLocLayer(label_coordinate, label_layer) - shape_list.append(shape) - return shape_list + pin_map = self.pins[pin_name] + max_pins = [] + for pin_list in pin_map: + max_pin = None + max_area = 0 + for pin in pin_list: + (layer,boundary) = pin + new_area = boundaryArea(boundary) + if max_pin == None or new_area>max_area: + max_pin = pin + max_area = new_area + max_pins.append(max_pin) - def getAllPinShapesByLabel(self,label_name): + return max_pins + + + def getAllPinShapes(self, pin_name): """ Search for a pin label and return ALL the enclosing rectangles on the same layer as the pin label. """ - - label_list=self.getLabelDBInfo(label_name) - shape_list=[] - for label in label_list: - (label_coordinate,label_layer)=label - shape_list.extend(self.getAllPinShapesByDBLocLayer(label_coordinate, label_layer)) + shape_list = [] + pin_map = self.pins[pin_name] + for pin_list in pin_map: + for pin in pin_list: + (pin_layer, boundary) = pin + shape_list.append(pin) + return shape_list - - def getAllPinShapesInStructureList(self,coordinates,layer): + + + def processLabelPins(self, layer): """ - Given a coordinate, search for enclosing structures on the given layer. - Return all pin shapes. + Find all text labels and create a map to a list of shapes that + they enclose on the given layer. """ - boundaries = [] - for TreeUnit in self.xyTree: - boundaries.extend(self.getPinInStructure(coordinates,layer,TreeUnit)) + # Get the labels on a layer in the root level + labels = self.getTexts(layer) + # Get all of the shapes on the layer at all levels + # and transform them to the current level + shapes = self.getAllShapes(layer) - return boundaries + for label in labels: + label_coordinate = label.coordinates[0] + user_coordinate = [x*self.units[0] for x in label_coordinate] + pin_shapes = [] + for boundary in shapes: + if self.labelInRectangle(user_coordinate,boundary): + pin_shapes.append((layer, boundary)) + label_text = label.textString + # Remove the padding if it exists + if label_text[-1] == "\x00": + label_text = label_text[0:-1] - def getPinInStructure(self,coordinates,layer,structure): - """ - Go through all the shapes in a structure and return the list of shapes - that the label coordinates are inside. + try: + self.pins[label_text] + except KeyError: + self.pins[label_text] = [] + self.pins[label_text].append(pin_shapes) + + + + def getAllShapes(self,layer): """ - - (structureName,structureOrigin,structureuVector,structurevVector)=structure - boundaries = [] - for boundary in self.structures[str(structureName)].boundaries: - # Pin enclosures only work on rectangular pins so ignore any non rectangle - # This may report not finding pins, but the user should fix this by adding a rectangle. - if len(boundary.coordinates)!=5: - continue - if layer==boundary.drawingLayer: - left_bottom=boundary.coordinates[0] - right_top=boundary.coordinates[2] - # Rectangle is [leftx, bottomy, rightx, topy]. - boundaryRect=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]] - boundaryRect=self.transformRectangle(boundaryRect,structureuVector,structurevVector) - boundaryRect=[boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(), - boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item()] - - if self.labelInRectangle(coordinates,boundaryRect): - boundaries.append(boundaryRect) - - return boundaries - - - def getAllShapesInStructureList(self,layer): - """ - Return all pin shapes on a given layer. + Return all gshapes on a given layer in [llx, lly, urx, ury] format and + user units. """ boundaries = [] for TreeUnit in self.xyTree: @@ -812,7 +756,8 @@ class VlsiLayout: def getShapesInStructure(self,layer,structure): """ - Go through all the shapes in a structure and return the list of shapes. + Go through all the shapes in a structure and return the list of shapes in + the form [llx, lly, urx, ury] """ (structureName,structureOrigin,structureuVector,structurevVector)=structure @@ -820,6 +765,7 @@ class VlsiLayout: boundaries = [] for boundary in self.structures[str(structureName)].boundaries: # FIXME: Right now, this only supports rectangular shapes! + #debug.check(len(boundary.coordinates)==5,"Non-rectangular shape.") if len(boundary.coordinates)!=5: continue if layer==boundary.drawingLayer: @@ -874,8 +820,8 @@ class VlsiLayout: """ Checks if a coordinate is within a given rectangle. Rectangle is [leftx, bottomy, rightx, topy]. """ - coordinate_In_Rectangle_x_range=(coordinate[0]>=int(rectangle[0]))&(coordinate[0]<=int(rectangle[2])) - coordinate_In_Rectangle_y_range=(coordinate[1]>=int(rectangle[1]))&(coordinate[1]<=int(rectangle[3])) + coordinate_In_Rectangle_x_range=(coordinate[0]>=rectangle[0])&(coordinate[0]<=rectangle[2]) + coordinate_In_Rectangle_y_range=(coordinate[1]>=rectangle[1])&(coordinate[1]<=rectangle[3]) if coordinate_In_Rectangle_x_range & coordinate_In_Rectangle_y_range: return True else: diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index e6662617..4ac32967 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -18,13 +18,14 @@ class control_logic(design.design): Dynamically generated Control logic for the total SRAM circuit. """ - def __init__(self, num_rows, port_type="rw"): + def __init__(self, num_rows, words_per_row, port_type="rw"): """ Constructor """ name = "control_logic_" + port_type design.design.__init__(self, name) debug.info(1, "Creating {}".format(name)) self.num_rows = num_rows + self.words_per_row = words_per_row self.port_type = port_type if self.port_type == "rw": @@ -92,14 +93,25 @@ class control_logic(design.design): from importlib import reload c = reload(__import__(OPTS.replica_bitline)) replica_bitline = getattr(c, OPTS.replica_bitline) - # FIXME: These should be tuned according to the size! - delay_stages = 4 # Must be non-inverting - delay_fanout = 3 # This can be anything >=2 + + delay_stages, delay_fanout = self.get_delay_chain_size() bitcell_loads = int(math.ceil(self.num_rows / 2.0)) self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads, name="replica_bitline_"+self.port_type) self.add_mod(self.replica_bitline) - + def get_delay_chain_size(self): + """Determine the size of the delay chain used for the Sense Amp Enable """ + # FIXME: These should be tuned according to the additional size parameters + delay_fanout = 3 # This can be anything >=2 + # Delay stages Must be non-inverting + if self.words_per_row >= 8: + delay_stages = 8 + elif self.words_per_row == 4: + delay_stages = 6 + else: + delay_stages = 4 + return (delay_stages, delay_fanout) + def setup_signal_busses(self): """ Setup bus names, determine the size of the busses etc """ diff --git a/compiler/modules/dff.py b/compiler/modules/dff.py index d72aae2e..936bc822 100644 --- a/compiler/modules/dff.py +++ b/compiler/modules/dff.py @@ -12,7 +12,7 @@ class dff(design.design): pin_names = ["D", "Q", "clk", "vdd", "gnd"] (width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"]) def __init__(self, name="dff"): design.design.__init__(self, name) diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index 8095d049..84aaa63d 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -189,16 +189,66 @@ class replica_bitline(design.design): if pin.layer != "metal1": continue - self.add_path("metal1", [pin_right, pin_extension]) + pin_width_ydir = pin.uy()-pin.by() + #Width is set to pin y width to avoid DRC issues with m1 gaps + self.add_path("metal1", [pin_right, pin_extension], pin_width_ydir) self.add_power_pin("gnd", pin_extension) - # for multiport, need to short wordlines to each other so they all connect to gnd + # for multiport, need to short wordlines to each other so they all connect to gnd. wl_last = self.wl_list[self.total_ports-1]+"_{}".format(row) pin_last = self.rbl_inst.get_pin(wl_last) + self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0)) + + def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None): + """Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins.""" + #Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord. + #This is my (Hunter) first time editing layout in openram so this function is likely not optimal. + if self.total_ports > 1: + #1. Create vertical metal for all the bitlines to connect to + #m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped + correct_y = vector(0, 0.5*drc("minwidth_metal1")) + #x spacing depends on the side being drawn. Unknown to me (Hunter) why the size of the space differs by the side. + #I assume this is related to how a wire is draw, but I have not investigated the issue. + if pin_side == "right": + correct_x = vector(0.5*drc("minwidth_metal1"), 0) + if offset_x_vec != None: + correct_x = offset_x_vec + else: + correct_x = vector(1.5*drc("minwidth_metal1"), 0) + + if wl_pin_a.uy() > wl_pin_b.uy(): + self.add_path("metal1", [wl_pin_a.rc()+correct_x+correct_y, wl_pin_b.rc()+correct_x-correct_y]) + else: + self.add_path("metal1", [wl_pin_a.rc()+correct_x-correct_y, wl_pin_b.rc()+correct_x+correct_y]) + elif pin_side == "left": + if offset_x_vec != None: + correct_x = offset_x_vec + else: + correct_x = vector(1.5*drc("minwidth_metal1"), 0) + + if wl_pin_a.uy() > wl_pin_b.uy(): + self.add_path("metal1", [wl_pin_a.lc()-correct_x+correct_y, wl_pin_b.lc()-correct_x-correct_y]) + else: + self.add_path("metal1", [wl_pin_a.lc()-correct_x-correct_y, wl_pin_b.lc()-correct_x+correct_y]) + else: + debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1) - correct = vector(0.5*drc("minwidth_metal1"), 0) - self.add_path("metal1", [pin.rc()-correct, pin_last.rc()-correct]) - + #2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this. + for port in range(self.total_ports): + if is_replica_cell: + wl = self.wl_list[port] + pin = self.rbc_inst.get_pin(wl) + else: + wl = self.wl_list[port]+"_{}".format(cell_row) + pin = self.rbl_inst.get_pin(wl) + + if pin_side == "left": + self.add_path("metal1", [pin.lc()-correct_x, pin.lc()]) + elif pin_side == "right": + self.add_path("metal1", [pin.rc()+correct_x, pin.rc()]) + + + def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ @@ -217,8 +267,13 @@ class replica_bitline(design.design): # Replica bitcell needs to be routed up to M3 pin=self.rbc_inst.get_pin("vdd") - # Don't rotate this via to vit in FreePDK45 - self.add_power_pin("vdd", pin.center(), rotate=0) + # Don't rotate this via to vit in FreePDK45. In the custom cell, the pin cannot be placed + # directly on vdd or there will be a drc error with a wordline. Place the pin slightly farther + # away then route to it. A better solution would be to rotate the m1 in the via or move the pin + # a m1_pitch below the entire cell. + pin_extension = pin.center() - vector(0,self.m1_pitch) + self.add_power_pin("vdd", pin_extension, rotate=0) + self.add_path("metal1", [pin.center(), pin_extension]) for pin in self.rbc_inst.get_pins("gnd"): self.add_power_pin("gnd", pin.center()) @@ -267,9 +322,10 @@ class replica_bitline(design.design): wl_last = self.wl_list[self.total_ports-1] pin = self.rbc_inst.get_pin(wl) pin_last = self.rbc_inst.get_pin(wl_last) + x_offset = self.short_wordlines(pin, pin_last, "left", True) - correct = vector(0.5*drc("minwidth_metal1"), 0) - self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct]) + #correct = vector(0.5*drc("minwidth_metal1"), 0) + #self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct]) # DRAIN ROUTE # Route the drain to the vdd rail diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index 6187b37a..e2a0e131 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -13,7 +13,7 @@ class sense_amp(design.design): pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"] (width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"]) def __init__(self, name): design.design.__init__(self, name) @@ -25,6 +25,7 @@ class sense_amp(design.design): def input_load(self): #Input load for the bitlines which are connected to the source/drain of a TX. Not the selects. + from tech import spice, parameter bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file. return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index a74f8514..d78e0fdc 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -218,7 +218,7 @@ class single_level_column_mux_array(design.design): rotate=90) def analytical_delay(self, vdd, slew, load=0.0): - from tech import spice + from tech import spice, parameter r = spice["min_tx_r"]/(self.mux.ptx_width/parameter["min_tx_size"]) #Drains of mux transistors make up capacitance. c_para = spice["min_tx_drain_c"]*(self.mux.ptx_width/parameter["min_tx_size"])*self.words_per_row#ff diff --git a/compiler/modules/tri_gate.py b/compiler/modules/tri_gate.py index cce7683c..da42c7a8 100644 --- a/compiler/modules/tri_gate.py +++ b/compiler/modules/tri_gate.py @@ -12,7 +12,7 @@ class tri_gate(design.design): pin_names = ["in", "en", "en_bar", "out", "gnd", "vdd"] (width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"]) unique_id = 1 diff --git a/compiler/modules/write_driver.py b/compiler/modules/write_driver.py index 67477a8d..8d1291a7 100644 --- a/compiler/modules/write_driver.py +++ b/compiler/modules/write_driver.py @@ -13,7 +13,7 @@ class write_driver(design.design): pin_names = ["din", "bl", "br", "en", "gnd", "vdd"] (width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"]) def __init__(self, name): design.design.__init__(self, name) diff --git a/compiler/router/direction.py b/compiler/router/direction.py index 95980618..8a6681a7 100644 --- a/compiler/router/direction.py +++ b/compiler/router/direction.py @@ -1,4 +1,5 @@ from enum import Enum +from vector3d import vector3d class direction(Enum): NORTH = 1 @@ -7,3 +8,60 @@ class direction(Enum): WEST = 4 UP = 5 DOWN = 6 + NORTHEAST = 7 + NORTHWEST = 8 + SOUTHEAST = 9 + SOUTHWEST = 10 + + + def get_offset(direct): + """ + Returns the vector offset for a given direction. + """ + if direct==direction.NORTH: + offset = vector3d(0,1,0) + elif direct==direction.SOUTH: + offset = vector3d(0,-1,0) + elif direct==direction.EAST: + offset = vector3d(1,0,0) + elif direct==direction.WEST: + offset = vector3d(-1,0,0) + elif direct==direction.UP: + offset = vector3d(0,0,1) + elif direct==direction.DOWN: + offset = vector3d(0,0,-1) + elif direct==direction.NORTHEAST: + offset = vector3d(1,1,0) + elif direct==direction.NORTHWEST: + offset = vector3d(-1,1,0) + elif direct==direction.SOUTHEAST: + offset = vector3d(1,-1,0) + elif direct==direction.SOUTHWEST: + offset = vector3d(-1,-1,0) + else: + debug.error("Invalid direction {}".format(direct)) + + return offset + + def cardinal_directions(up_down_too=False): + temp_dirs = [direction.NORTH, direction.EAST, direction.SOUTH, direction.WEST] + if up_down_too: + temp_dirs.extend([direction.UP, direction.DOWN]) + return temp_dirs + + def cardinal_offsets(up_down_too=False): + return [direction.get_offset(d) for d in direction.cardinal_directions(up_down_too)] + + def all_directions(): + return [direction.NORTH, direction.EAST, direction.SOUTH, direction.WEST, + direction.NORTHEAST, direction.NORTHWEST, direction.SOUTHEAST, direction.SOUTHWEST] + + def all_offsets(): + return [direction.get_offset(d) for d in direction.all_directions()] + + def all_neighbors(cell): + return [cell+x for x in direction.all_offsets()] + + def cardinal_neighbors(cell): + return [cell+x for x in direction.cardinal_offsets()] + diff --git a/compiler/router/grid_cell.py b/compiler/router/grid_cell.py index 3f145ef4..cb78116c 100644 --- a/compiler/router/grid_cell.py +++ b/compiler/router/grid_cell.py @@ -22,6 +22,11 @@ class grid_cell: self.source=False self.target=False + def get_cost(self): + # We can display the cost of the frontier + if self.min_cost > 0: + return self.min_cost + def get_type(self): if self.blocked: @@ -36,8 +41,4 @@ class grid_cell: if self.path: return "P" - # We can display the cost of the frontier - if self.min_cost > 0: - return self.min_cost - return None diff --git a/compiler/router/grid_path.py b/compiler/router/grid_path.py index 437b6acb..cbe739ef 100644 --- a/compiler/router/grid_path.py +++ b/compiler/router/grid_path.py @@ -150,7 +150,7 @@ class grid_path: return cost - def expand_dirs(self,up_down_too=True): + def expand_dirs(self): """ Expand from the end in each of the four cardinal directions plus up or down but not expanding to blocked cells. Expands in all @@ -162,9 +162,7 @@ class grid_path: """ neighbors = [] - for d in list(direction): - if not up_down_too and (d==direction.UP or d==direction.DOWN): - continue + for d in direction.cardinal_directions(True): n = self.neighbor(d) if n: neighbors.append(n) @@ -172,20 +170,7 @@ class grid_path: return neighbors def neighbor(self, d): - if d==direction.EAST: - offset = vector3d(1,0,0) - elif d==direction.WEST: - offset = vector3d(-1,0,0) - elif d==direction.NORTH: - offset = vector3d(0,1,0) - elif d==direction.SOUTH: - offset = vector3d(0,-1,0) - elif d==direction.UP: - offset = vector3d(0,0,1) - elif d==direction.DOWN: - offset = vector3d(0,0,-1) - else: - debug.error("Invalid direction {}".format(d),-1) + offset = direction.get_offset(d) newwave = [point + offset for point in self.pathlist[-1]] diff --git a/compiler/router/grid_utils.py b/compiler/router/grid_utils.py index 62caebe9..23fe23ea 100644 --- a/compiler/router/grid_utils.py +++ b/compiler/router/grid_utils.py @@ -6,31 +6,20 @@ import debug from direction import direction from vector3d import vector3d -def increment_set(curset, direct): - """ - Return the cells incremented in given direction - """ - if direct==direction.NORTH: - offset = vector3d(0,1,0) - elif direct==direction.SOUTH: - offset = vector3d(0,-1,0) - elif direct==direction.EAST: - offset = vector3d(1,0,0) - elif direct==direction.WEST: - offset = vector3d(-1,0,0) - elif direct==direction.UP: - offset = vector3d(0,0,1) - elif direct==direction.DOWN: - offset = vector3d(0,0,-1) - else: - debug.error("Invalid direction {}".format(dirct)) - - newset = set() - for c in curset: - newc = c+offset - newset.add(newc) - return newset +def increment_set(curset, direct): + """ + Return the cells incremented in given direction + """ + offset = direction.get_offset(direct) + + newset = set() + for c in curset: + newc = c+offset + newset.add(newc) + + return newset + def remove_border(curset, direct): """ @@ -38,7 +27,7 @@ def remove_border(curset, direct): """ border = get_border(curset, direct) curset.difference_update(border) - + def get_upper_right(curset): ur = None @@ -55,48 +44,48 @@ def get_lower_left(curset): return ll def get_border( curset, direct): - """ - Return the furthest cell(s) in a given direction. - """ - - # find direction-most cell(s) - maxc = [] - if direct==direction.NORTH: - for c in curset: - if len(maxc)==0 or c.y>maxc[0].y: - maxc = [c] - elif c.y==maxc[0].y: - maxc.append(c) - elif direct==direct.SOUTH: - for c in curset: - if len(maxc)==0 or c.ymaxc[0].x: - maxc = [c] - elif c.x==maxc[0].x: - maxc.append(c) - elif direct==direct.WEST: - for c in curset: - if len(maxc)==0 or c.xmaxc[0].y: + maxc = [c] + elif c.y==maxc[0].y: + maxc.append(c) + elif direct==direct.SOUTH: + for c in curset: + if len(maxc)==0 or c.ymaxc[0].x: + maxc = [c] + elif c.x==maxc[0].x: + maxc.append(c) + elif direct==direct.WEST: + for c in curset: + if len(maxc)==0 or c.x=pin.uy(): + above_item = item + else: + break + + # There was nothing + if above_item==None: + return None + # If it already overlaps, no connector needed + if above_item.overlaps(pin): + return None + + # Otherwise, make a connector to the item + p = self.compute_connector(pin, above_item) + return p + + def find_below_connector(self, pin, enclosures): + """ + Find the enclosure that is below the pin + and make a connector to it's upper edge. + """ + # Create the list of shapes that contain the pin edge + edge_list = [] + for shape in enclosures: + if shape.xcontains(pin): + edge_list.append(shape) + + # Sort them by their upper edge + edge_list.sort(key=lambda x: x.uy()) + + # Find the upper edge that is next to the pin's bottom edge + bottom_item = None + for item in edge_list: + if item.uy()<=pin.by(): + bottom_item = item + else: + break + + # There was nothing to the left + if bottom_item==None: + return None + # If it already overlaps, no connector needed + if bottom_item.overlaps(pin): + return None + + # Otherwise, make a connector to the item + p = self.compute_connector(pin, bottom_item) + return p + + def find_left_connector(self, pin, enclosures): + """ + Find the enclosure that is to the left of the pin + and make a connector to it's right edge. + """ + # Create the list of shapes that contain the pin edge + edge_list = [] + for shape in enclosures: + if shape.ycontains(pin): + edge_list.append(shape) + + # Sort them by their right edge + edge_list.sort(key=lambda x: x.rx()) + + # Find the right edge that is to the pin's left edge + left_item = None + for item in edge_list: + if item.rx()<=pin.lx(): + left_item = item + else: + break + + # There was nothing to the left + if left_item==None: + return None + # If it already overlaps, no connector needed + if left_item.overlaps(pin): + return None + + # Otherwise, make a connector to the item + p = self.compute_connector(pin, left_item) + return p + + def find_right_connector(self, pin, enclosures): + """ + Find the enclosure that is to the right of the pin + and make a connector to it's left edge. + """ + # Create the list of shapes that contain the pin edge + edge_list = [] + for shape in enclosures: + if shape.ycontains(pin): + edge_list.append(shape) + + # Sort them by their right edge + edge_list.sort(key=lambda x: x.lx(), reverse=True) + + # Find the left edge that is next to the pin's right edge + right_item = None + for item in edge_list: + if item.lx()>=pin.rx(): + right_item = item + else: + break + + # There was nothing to the right + if right_item==None: + return None + # If it already overlaps, no connector needed + if right_item.overlaps(pin): + return None + + # Otherwise, make a connector to the item + p = self.compute_connector(pin, right_item) + return p + + def find_smallest_connector(self, pin_list, shape_list): + """ + Compute all of the connectors between the overlapping pins and enclosure shape list.. + Return the smallest. + """ + smallest = None + for pin in pin_list: + for enclosure in shape_list: + new_enclosure = self.compute_connector(pin, enclosure) + if smallest == None or new_enclosure.area() min_width: + if smallest_shape == None or other.area() biggest.area(): + biggest = pin + + return pin + + def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST): + """ + This encloses a single pin component with a rectangle + starting with the seed and expanding right until blocked + and then up until blocked. + dir1 and dir2 should be two orthogonal directions. + """ + + offset1= direction.get_offset(dir1) + offset2= direction.get_offset(dir2) + + # We may have started with an empty set + if not self.grids: + return None + + # Start with the ll and make the widest row + row = [ll] + # Move in dir1 while we can + while True: + next_cell = row[-1] + offset1 + # Can't move if not in the pin shape + if next_cell in self.grids and next_cell not in self.router.blocked_grids: + row.append(next_cell) + else: + break + # Move in dir2 while we can + while True: + next_row = [x+offset2 for x in row] + for cell in next_row: + # Can't move if any cell is not in the pin shape + if cell not in self.grids or cell in self.router.blocked_grids: + break + else: + row = next_row + # Skips the second break + continue + # Breaks from the nested break + break + + # Add a shape from ll to ur + ur = row[-1] + return (ll,ur) + + + def enclose_pin(self): + """ + If there is one set of connected pin shapes, + this will find the smallest rectangle enclosure that overlaps with any pin. + If there is not, it simply returns all the enclosures. + """ + self.enclosed = True + + # Compute the enclosure pin_layout list of the set of tracks + self.enclosures = self.compute_enclosures() + + for pin_list in self.pins: + for pin in pin_list: + + # If it is contained, it won't need a connector + if pin.contained_by_any(self.enclosures): + continue + + left_connector = self.find_left_connector(pin, self.enclosures) + right_connector = self.find_right_connector(pin, self.enclosures) + above_connector = self.find_above_connector(pin, self.enclosures) + below_connector = self.find_below_connector(pin, self.enclosures) + for connector in [left_connector, right_connector, above_connector, below_connector]: + if connector: + self.enclosures.append(connector) + + # Now, make sure each pin touches an enclosure. If not, add a connector. + # This could only happen when there was no enclosure in any cardinal direction from a pin + for pin_list in self.pins: + if not self.overlap_any_shape(pin_list, self.enclosures): + connector = self.find_smallest_connector(pin_list, self.enclosures) + debug.check(connector!=None, "Could not find a connector for {} with {}".format(pin_list, self.enclosures)) + self.enclosures.append(connector) + + + debug.info(3,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name, + self.pins, + self.grids, + self.enclosures)) + + def combine_groups(self, pg1, pg2): + """ + Combine two pin groups into one. + """ + self.pins = [*pg1.pins, *pg2.pins] # Join the two lists of pins + self.grids = pg1.grids | pg2.grids # OR the set of grid locations + self.secondary_grids = pg1.secondary_grids | pg2.secondary_grids + + def add_enclosure(self, cell): + """ + Add the enclosure shape to the given cell. + """ + for enclosure in self.enclosures: + debug.info(2,"Adding enclosure {0} {1}".format(self.name, enclosure)) + cell.add_rect(layer=enclosure.layer, + offset=enclosure.ll(), + width=enclosure.width(), + height=enclosure.height()) + + + def perimeter_grids(self): + """ + Return a list of the grids on the perimeter. + This assumes that we have a single contiguous shape. + """ + perimeter_set = set() + cardinal_offsets = direction.cardinal_offsets() + for g1 in self.grids: + neighbor_grids = [g1 + offset for offset in cardinal_offsets] + neighbor_count = sum([x in self.grids for x in neighbor_grids]) + # If we aren't completely enclosed, we are on the perimeter + if neighbor_count < 4: + perimeter_set.add(g1) + + return perimeter_set + + def adjacent(self, other): + """ + Chck if the two pin groups have at least one adjacent pin grid. + """ + # We could optimize this to just check the boundaries + for g1 in self.perimeter_grids(): + for g2 in other.perimeter_grids(): + if g1.adjacent(g2): + return True + + return False + + + def adjacent_grids(self, other, separation): + """ + Determine the sets of grids that are within a separation distance + of any grid in the other set. + """ + # We could optimize this to just check the boundaries + g1_grids = set() + g2_grids = set() + for g1 in self.grids: + for g2 in other.grids: + if g1.distance(g2) <= separation: + g1_grids.add(g1) + g2_grids.add(g2) + + return g1_grids,g2_grids + + def convert_pin(self): + """ + Convert the list of pin shapes into sets of routing grids. + The secondary set of grids are "optional" pin shapes that could be + should be either blocked or part of the pin. + """ + pin_set = set() + blockage_set = set() + + for pin_list in self.pins: + for pin in pin_list: + debug.info(2," Converting {0}".format(pin)) + # Determine which tracks the pin overlaps + pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin) + + pin_set.update(pin_in_tracks) + # Blockages will be a super-set of pins since it uses the inflated pin shape. + blockage_in_tracks = self.router.convert_blockage(pin) + + blockage_set.update(blockage_in_tracks) + + # If we have a blockage, we must remove the grids + # Remember, this excludes the pin blockages already + shared_set = pin_set & self.router.blocked_grids + if len(shared_set)>0: + debug.info(2,"Removing pins {}".format(shared_set)) + pin_set.difference_update(self.router.blocked_grids) + + shared_set = blockage_set & self.router.blocked_grids + if len(shared_set)>0: + debug.info(2,"Removing blocks {}".format(shared_set)) + blockage_set.difference_update(self.router.blocked_grids) + + # At least one of the groups must have some valid tracks + if (len(pin_set)==0 and len(blockage_set)==0): + self.write_debug_gds("blocked_pin.gds") + debug.error("Unable to find unblocked pin on grid.") + + # We need to route each of the components, so don't combine the groups + self.grids = pin_set | blockage_set + # Remember the secondary grids for removing adjacent pins in wide metal spacing + self.secondary_grids = blockage_set - pin_set + + debug.info(2," pins {}".format(self.grids)) + debug.info(2," secondary {}".format(self.secondary_grids)) + + def recurse_simple_overlap_enclosure(self, start_set, direct): + """ + Recursive function to return set of tracks that connects to + the actual supply rail wire in a given direction (or terminating + when any track is no longer in the supply rail. + """ + next_set = grid_utils.expand_border(start_set, direct) + + supply_tracks = self.router.supply_rail_tracks[self.name] + supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name] + + supply_overlap = next_set & supply_tracks + wire_overlap = next_set & supply_wire_tracks + + # If the rail overlap is the same, we are done, since we connected to the actual wire + if len(wire_overlap)==len(start_set): + new_set = start_set | wire_overlap + # If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region + elif len(supply_overlap)==len(start_set): + recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct) + new_set = start_set | supply_overlap | recurse_set + else: + # If we got no next set, we are done, can't expand! + new_set = set() + + return new_set + + def create_simple_overlap_enclosure(self, start_set): + """ + This takes a set of tracks that overlap a supply rail and creates an enclosure + that is ensured to overlap the supply rail wire. + It then adds rectangle(s) for the enclosure. + """ + + additional_set = set() + # Check the layer of any element in the pin to determine which direction to route it + e = next(iter(start_set)) + new_set = start_set.copy() + if e.z==0: + new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH) + if not new_set: + new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH) + else: + new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST) + if not new_set: + new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST) + + # Expand the pin grid set to include some extra grids that connect the supply rail + self.grids.update(new_set) + + # Add the inflated set so we don't get wide metal spacing issues (if it exists) + self.blockages.update(grid_utils.inflate_set(new_set,self.router.supply_rail_space_width)) + + # Add the polygon enclosures and set this pin group as routed + self.set_routed() + self.enclosures = self.compute_enclosures() + diff --git a/compiler/router/router.py b/compiler/router/router.py index 2cb9fb71..7f88934a 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -1,17 +1,19 @@ import sys import gdsMill -from tech import drc,GDS,layer -from contact import contact +from tech import drc,GDS +from tech import layer as techlayer import math import debug +from router_tech import router_tech from pin_layout import pin_layout +from pin_group import pin_group from vector import vector from vector3d import vector3d from globals import OPTS from pprint import pformat import grid_utils -class router: +class router(router_tech): """ A router class to read an obstruction map from a gds and plan a route on a given layer. This is limited to two layer routes. @@ -23,6 +25,8 @@ class router: This will instantiate a copy of the gds file or the module at (0,0) and route on top of this. The blockages from the gds/module will be considered. """ + router_tech.__init__(self, layers) + self.cell = design # If didn't specify a gds blockage file, write it out to read the gds @@ -36,23 +40,16 @@ class router: self.reader = gdsMill.Gds2reader(self.layout) self.reader.loadFromFile(gds_filename) self.top_name = self.layout.rootStructureName - - # Set up layers and track sizes - self.set_layers(layers) ### The pin data structures - # A map of pin names to pin structures + # A map of pin names to a set of pin_layout structures self.pins = {} - # This is a set of all pins so that we don't create blockages for these shapes. + # This is a set of all pins (ignoring names) so that can quickly not create blockages for pins + # (They will be blocked based on the names we are routing) self.all_pins = set() - # This is a set of pin groups. Each group consists of overlapping pin shapes on the same layer. + # A map of pin names to a list of pin groups self.pin_groups = {} - # These are the corresponding pin grids for each pin group. - self.pin_grids = {} - # The corresponding set of partially blocked grids for each pin group. - # These are blockages for other nets but unblocked for this component. - self.pin_blockages = {} ### The blockage data structures # A list of metal shapes (using the same pin_layout structure) that are not pins but blockages. @@ -63,12 +60,15 @@ class router: ### The routed data structures # A list of paths that have been "routed" self.paths = [] + # A list of path blockages (they might be expanded for wide metal DRC) + self.path_blockages = [] # The boundary will determine the limits to the size of the routing grid self.boundary = self.layout.measureBoundary(self.top_name) # These must be un-indexed to get rid of the matrix type self.ll = vector(self.boundary[0][0], self.boundary[0][1]) - self.ur = vector(self.boundary[1][0], self.boundary[1][1]) + # Pad the UR by a few tracks to add an extra rail possibly + self.ur = vector(self.boundary[1][0], self.boundary[1][1]) + self.track_widths.scale(5,5) def clear_pins(self): """ @@ -78,8 +78,6 @@ class router: self.pins = {} self.all_pins = set() self.pin_groups = {} - self.pin_grids = {} - self.pin_blockages = {} # DO NOT clear the blockages as these don't change self.rg.reinit() @@ -88,19 +86,6 @@ class router: """ If we want to route something besides the top-level cell.""" self.top_name = top_name - def get_zindex(self,layer_num): - if layer_num==self.horiz_layer_number: - return 0 - else: - return 1 - - def get_layer(self, zindex): - if zindex==1: - return self.vert_layer_name - elif zindex==0: - return self.horiz_layer_name - else: - debug.error("Invalid zindex {}".format(zindex),-1) def is_wave(self,path): """ @@ -108,59 +93,31 @@ class router: """ return len(path[0])>1 - def set_layers(self, layers): - """ - Allows us to change the layers that we are routing on. First layer - is always horizontal, middle is via, and last is always - vertical. - """ - self.layers = layers - (self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers - - # This is the minimum routed track spacing - via_connect = contact(self.layers, (1, 1)) - self.max_via_size = max(via_connect.width,via_connect.height) - - self.vert_layer_minwidth = drc("minwidth_{0}".format(self.vert_layer_name)) - self.vert_layer_spacing = drc(str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name)) - self.vert_layer_number = layer[self.vert_layer_name] - - self.horiz_layer_minwidth = drc("minwidth_{0}".format(self.horiz_layer_name)) - self.horiz_layer_spacing = drc(str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)) - self.horiz_layer_number = layer[self.horiz_layer_name] - - self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing - self.vert_track_width = self.max_via_size + self.vert_layer_spacing - - # We'll keep horizontal and vertical tracks the same for simplicity. - self.track_width = max(self.horiz_track_width,self.vert_track_width) - debug.info(1,"Track width: "+str(self.track_width)) - - self.track_widths = [self.track_width] * 2 - self.track_factor = [1/self.track_width] * 2 - debug.info(1,"Track factor: {0}".format(self.track_factor)) - - # When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side) - self.layer_widths = [self.track_width - self.horiz_layer_spacing, 1, self.track_width - self.vert_layer_spacing] def retrieve_pins(self,pin_name): """ - Retrieve the pin shapes from the layout. + Retrieve the pin shapes on metal 3 from the layout. """ - shape_list=self.layout.getAllPinShapesByLabel(str(pin_name)) + debug.info(2,"Retrieving pins for {}.".format(pin_name)) + shape_list=self.layout.getAllPinShapes(str(pin_name)) pin_set = set() for shape in shape_list: - (name,layer,boundary)=shape - rect = [vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])] + (layer,boundary)=shape + # GDSMill boundaries are in (left, bottom, right, top) order + # so repack and snap to the grid + ll = vector(boundary[0],boundary[1]).snap_to_grid() + ur = vector(boundary[2],boundary[3]).snap_to_grid() + rect = [ll,ur] pin = pin_layout(pin_name, rect, layer) pin_set.add(pin) debug.check(len(pin_set)>0,"Did not find any pin shapes for {0}.".format(str(pin_name))) + self.pins[pin_name] = pin_set self.all_pins.update(pin_set) for pin in self.pins[pin_name]: - debug.info(2,"Retrieved pin {}".format(str(pin))) + debug.info(3,"Retrieved pin {}".format(str(pin))) @@ -169,38 +126,28 @@ class router: Finds the pin shapes and converts to tracks. Pin can either be a label or a location,layer pair: [[x,y],layer]. """ + debug.info(1,"Finding pins for {}.".format(pin_name)) self.retrieve_pins(pin_name) self.analyze_pins(pin_name) - def find_blockages(self): """ Iterate through all the layers and write the obstacles to the routing grid. This doesn't consider whether the obstacles will be pins or not. They get reset later if they are not actually a blockage. """ + debug.info(1,"Finding blockages.") for layer in [self.vert_layer_number,self.horiz_layer_number]: self.retrieve_blockages(layer) - # # def reinit(self): - # # """ - # # Reset the source and destination pins to start a new routing. - # # Convert the source/dest pins to blockages. - # # Convert the routed path to blockages. - # # Keep the other blockages unchanged. - # # """ - # # self.clear_pins() - # # # DO NOT clear the blockages as these don't change - # # self.rg.reinit() - - def find_pins_and_blockages(self, pin_list): """ Find the pins and blockages in the design """ # This finds the pin shapes and sorts them into "groups" that are connected - # This must come before the blockages, so we can ignore metal shapes that are blockages. + # This must come before the blockages, so we can not count the pins themselves + # as blockages. for pin in pin_list: self.find_pins(pin) @@ -216,9 +163,159 @@ class router: for pin in pin_list: self.convert_pins(pin) + for pin in pin_list: + self.combine_adjacent_pins(pin) + #self.write_debug_gds("debug_combine_pins.gds",stop_program=True) + + # Separate any adjacent grids of differing net names to prevent wide metal DRC violations + # Must be done before enclosing pins + self.separate_adjacent_pins(self.supply_rail_space_width) + # For debug + #self.separate_adjacent_pins(1) + # Enclose the continguous grid units in a metal rectangle to fix some DRCs self.enclose_pins() + #self.write_debug_gds("debug_enclose_pins.gds",stop_program=True) + + def combine_adjacent_pins_pass(self, pin_name): + """ + Find pins that have adjacent routing tracks and merge them into a + single pin_group. The pins themselves may not be touching, but + enclose_pis in the next step will ensure they are touching. + """ + + # Make a copy since we are going to add to (and then reduce) this list + pin_groups = self.pin_groups[pin_name].copy() + + # Start as None to signal the first iteration + remove_indices = set() + + for index1,pg1 in enumerate(self.pin_groups[pin_name]): + # Cannot combine more than once + if index1 in remove_indices: + continue + for index2,pg2 in enumerate(self.pin_groups[pin_name]): + # Cannot combine with yourself + if index1==index2: + continue + # Cannot combine more than once + if index2 in remove_indices: + continue + + # Combine if at least 1 grid cell is adjacent + if pg1.adjacent(pg2): + combined = pin_group(pin_name, [], self) + combined.combine_groups(pg1, pg2) + debug.info(3,"Combining {0} {1} {2}:".format(pin_name, index1, index2)) + debug.info(3, " {0}\n {1}".format(pg1.pins, pg2.pins)) + debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids)) + remove_indices.update([index1,index2]) + pin_groups.append(combined) + break + + # Remove them in decreasing order to not invalidate the indices + debug.info(4,"Removing {}".format(sorted(remove_indices))) + for i in sorted(remove_indices, reverse=True): + del pin_groups[i] + + # Use the new pin group! + self.pin_groups[pin_name] = pin_groups + + removed_pairs = int(len(remove_indices)/2) + debug.info(1, "Combined {0} pin pairs for {1}".format(removed_pairs,pin_name)) + + return removed_pairs + + def combine_adjacent_pins(self, pin_name): + """ + Make multiple passes of the combine adjacent pins until we have no + more combinations or hit an iteration limit. + """ + debug.info(1,"Combining adjacent pins for {}.".format(pin_name)) + # Start as None to signal the first iteration + num_removed_pairs = None + + # Just used in case there's a circular combination or something weird + for iteration_count in range(10): + num_removed_pairs = self.combine_adjacent_pins_pass(pin_name) + if num_removed_pairs==0: + break + else: + debug.warning("Did not converge combining adjacent pins in supply router.") + + def separate_adjacent_pins(self, separation): + """ + This will try to separate all grid pins by the supplied number of separation + tracks (default is to prevent adjacency). + """ + debug.info(1,"Separating adjacent pins.") + # Commented out to debug with SCMOS + #if separation==0: + # return + + pin_names = self.pin_groups.keys() + for pin_name1 in pin_names: + for pin_name2 in pin_names: + if pin_name1==pin_name2: + continue + self.separate_adjacent_pin(pin_name1, pin_name2, separation) + + def separate_adjacent_pin(self, pin_name1, pin_name2, separation): + """ + Go through all of the pin groups and check if any other pin group is + within a separation of it. + If so, reduce the pin group grid to not include the adjacent grid. + Try to do this intelligently to keep th pins enclosed. + """ + debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2)) + for index1,pg1 in enumerate(self.pin_groups[pin_name1]): + for index2,pg2 in enumerate(self.pin_groups[pin_name2]): + # FIXME: Use separation distance and edge grids only + grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation) + # These should have the same length, so... + if len(grids_g1)>0: + debug.info(3,"Adjacent grids {0} {1} {2} {3}".format(index1,grids_g1,index2,grids_g2)) + self.remove_adjacent_grid(pg1, grids_g1, pg2, grids_g2) + + def remove_adjacent_grid(self, pg1, grids1, pg2, grids2): + """ + Remove one of the adjacent grids in a heuristic manner. + """ + # Determine the bigger and smaller group + if pg1.size()>pg2.size(): + bigger = pg1 + bigger_grids = grids1 + smaller = pg2 + smaller_grids = grids2 + else: + bigger = pg2 + bigger_grids = grids2 + smaller = pg1 + smaller_grids = grids1 + + # First, see if we can remove grids that are in the secondary grids + # i.e. they aren't necessary to the pin grids + if bigger_grids.issubset(bigger.secondary_grids): + debug.info(3,"Removing {} from bigger {}".format(str(bigger_grids), bigger)) + bigger.grids.difference_update(bigger_grids) + self.blocked_grids.update(bigger_grids) + return + elif smaller_grids.issubset(smaller.secondary_grids): + debug.info(3,"Removing {} from smaller {}".format(str(smaller_grids), smaller)) + smaller.grids.difference_update(smaller_grids) + self.blocked_grids.update(smaller_grids) + return + + # If that fails, just randomly remove from the bigger one and give a warning. + # This might fail later. + debug.warning("Removing arbitrary grids from a pin group {} {}".format(bigger, bigger_grids)) + debug.check(len(bigger.grids)>len(bigger_grids),"Zero size pin group after adjacency removal {} {}".format(bigger, bigger_grids)) + bigger.grids.difference_update(bigger_grids) + self.blocked_grids.update(bigger_grids) + + + def prepare_blockages(self, pin_name): """ Reset and add all of the blockages in the design. @@ -236,28 +333,34 @@ class router: self.set_supply_rail_blocked(True) # Block all of the pin components (some will be unblocked if they're a source/target) - for name in self.pin_grids.keys(): - self.set_blockages(self.pin_grids[name],True) + # Also block the previous routes + for name in self.pin_groups.keys(): + blockage_grids = {y for x in self.pin_groups[name] for y in x.grids} + self.set_blockages(blockage_grids,True) + blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages} + self.set_blockages(blockage_grids,True) + # FIXME: These duplicate a bit of work + # These are the paths that have already been routed. + self.set_blockages(self.path_blockages) # Don't mark the other components as targets since we want to route # directly to a rail, but unblock all the source components so we can # route over them - self.set_blockages(self.pin_grids[pin_name],False) + blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids} + self.set_blockages(blockage_grids,False) - # These are the paths that have already been routed. - self.set_path_blockages() - 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 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 convert_shape_to_units(self, shape): """ @@ -295,16 +398,6 @@ class router: # z direction return 2 - - def add_path_blockages(self): - """ - Go through all of the past paths and add them as blockages. - This is so we don't have to write/reload the GDS. - """ - for path in self.paths: - for grid in path: - self.rg.set_blocked(grid) - def clear_blockages(self): """ Clear all blockages on the grid. @@ -316,15 +409,8 @@ class router: """ Flag the blockages in the grid """ self.rg.set_blocked(blockages, value) - def set_path_blockages(self,value=True): - """ Flag the paths as blockages """ - # These are the paths that have already been routed. - # This adds the initial blockges of the design - for p in self.paths: - p.set_blocked(value) - def get_blockage_tracks(self, ll, ur, z): - debug.info(4,"Converting blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) + debug.info(3,"Converting blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) block_list = [] for x in range(int(ll[0]),int(ur[0])+1): @@ -345,7 +431,7 @@ class router: def convert_blockages(self): """ Convert blockages to grid tracks. """ - + debug.info(1,"Converting blockages.") for blockage in self.blockages: debug.info(3,"Converting blockage {}".format(str(blockage))) blockage_list = self.convert_blockage(blockage) @@ -357,7 +443,7 @@ class router: Recursive find boundaries as blockages to the routing grid. """ - shapes = self.layout.getAllShapesInStructureList(layer_num) + shapes = self.layout.getAllShapes(layer_num) for boundary in shapes: ll = vector(boundary[0],boundary[1]) ur = vector(boundary[2],boundary[3]) @@ -458,9 +544,9 @@ class router: best_coord = None best_overlap = -math.inf for coord in insufficient_list: - full_rect = self.convert_track_to_pin(coord) + full_pin = self.convert_track_to_pin(coord) # Compute the overlap with that rectangle - overlap_rect=self.compute_overlap(pin.rect,full_rect) + overlap_rect=pin.compute_overlap(full_pin) # Determine the min x or y overlap min_overlap = min(overlap_rect) if min_overlap>best_overlap: @@ -470,23 +556,6 @@ class router: return set([best_coord]) - def get_layer_width_space(self, zindex, width=0, length=0): - """ - Return the width and spacing of a given layer - and wire of a given width and length. - """ - if zindex==1: - layer_name = self.vert_layer_name - elif zindex==0: - layer_name = self.horiz_layer_name - else: - debug.error("Invalid zindex for track", -1) - - min_width = drc("minwidth_{0}".format(layer_name), width, length) - min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), width, length) - - return (min_width,min_spacing) - def convert_pin_coord_to_tracks(self, pin, coord): """ Given a pin and a track coordinate, determine if the pin overlaps enough. @@ -497,144 +566,21 @@ class router: (width, spacing) = self.get_layer_width_space(coord.z) # This is the rectangle if we put a pin in the center of the track - track_rect = self.convert_track_to_pin(coord) - overlap_width = self.compute_overlap_width(pin.rect, track_rect) + track_pin = self.convert_track_to_pin(coord) + overlap_length = pin.overlap_length(track_pin) - debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_rect, overlap_width)) + debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length)) # If it overlaps by more than the min width DRC, we can just use the track - if overlap_width==math.inf or snap_val_to_grid(overlap_width) >= snap_val_to_grid(width): - debug.info(3," Overlap: {0} >? {1}".format(overlap_width,spacing)) + if overlap_length==math.inf or snap_val_to_grid(overlap_length) >= snap_val_to_grid(width): + debug.info(3," Overlap: {0} >? {1}".format(overlap_length,spacing)) return (coord, None) # Otherwise, keep track of the partial overlap grids in case we need to patch it later. else: - debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_width,spacing)) + debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_length,spacing)) return (None, coord) - def compute_overlap(self, r1, r2): - """ Calculate the rectangular overlap of two rectangles. """ - (r1_ll,r1_ur) = r1 - (r2_ll,r2_ur) = r2 - - #ov_ur = vector(min(r1_ur.x,r2_ur.x),min(r1_ur.y,r2_ur.y)) - #ov_ll = vector(max(r1_ll.x,r2_ll.x),max(r1_ll.y,r2_ll.y)) - - dy = min(r1_ur.y,r2_ur.y)-max(r1_ll.y,r2_ll.y) - dx = min(r1_ur.x,r2_ur.x)-max(r1_ll.x,r2_ll.x) - - if dx>0 and dy>0: - return [dx,dy] - else: - return [0,0] - - def compute_overlap_width(self, r1, r2): - """ - Calculate the intersection segment and determine its width. - """ - intersections = self.compute_overlap_segment(r1,r2) - - if len(intersections)==2: - (p1,p2) = intersections - return math.sqrt(pow(p1[0]-p2[0],2) + pow(p1[1]-p2[1],2)) - else: - # we either have no overlap or complete overlap - # Compute the width of the overlap of the two rectangles - overlap_rect=self.compute_overlap(r1, r2) - # Determine the min x or y overlap - min_overlap = min(overlap_rect) - if min_overlap>0: - return math.inf - else: - return 0 - - - def compute_overlap_segment(self, r1, r2): - """ - Calculate the intersection segment of two rectangles - (if any) - """ - (r1_ll,r1_ur) = r1 - (r2_ll,r2_ur) = r2 - - # The other corners besides ll and ur - r1_ul = vector(r1_ll.x, r1_ur.y) - r1_lr = vector(r1_ur.x, r1_ll.y) - r2_ul = vector(r2_ll.x, r2_ur.y) - r2_lr = vector(r2_ur.x, r2_ll.y) - - from itertools import tee - def pairwise(iterable): - "s -> (s0,s1), (s1,s2), (s2, s3), ..." - a, b = tee(iterable) - next(b, None) - return zip(a, b) - - # R1 edges CW - r1_cw_points = [r1_ll, r1_ul, r1_ur, r1_lr, r1_ll] - r1_edges = [] - for (p,q) in pairwise(r1_cw_points): - r1_edges.append([p,q]) - - # R2 edges CW - r2_cw_points = [r2_ll, r2_ul, r2_ur, r2_lr, r2_ll] - r2_edges = [] - for (p,q) in pairwise(r2_cw_points): - r2_edges.append([p,q]) - - # There are 4 edges on each rectangle - # so just brute force check intersection of each - # Two pairs of them should intersect - intersections = [] - for r1e in r1_edges: - for r2e in r2_edges: - i = self.segment_intersection(r1e, r2e) - if i: - intersections.append(i) - - return intersections - - def on_segment(self, p, q, r): - """ - Given three co-linear points, determine if q lies on segment pr - """ - if q[0] <= max(p[0], r[0]) and \ - q[0] >= min(p[0], r[0]) and \ - q[1] <= max(p[1], r[1]) and \ - q[1] >= min(p[1], r[1]): - return True - - return False - - def segment_intersection(self, s1, s2): - """ - Determine the intersection point of two segments - Return the a segment if they overlap. - Return None if they don't. - """ - (a,b) = s1 - (c,d) = s2 - # Line AB represented as a1x + b1y = c1 - a1 = b.y - a.y - b1 = a.x - b.x - c1 = a1*a.x + b1*a.y - - # Line CD represented as a2x + b2y = c2 - a2 = d.y - c.y - b2 = c.x - d.x - c2 = a2*c.x + b2*c.y - - determinant = a1*b2 - a2*b1 - - if determinant!=0: - x = (b2*c1 - b1*c2)/determinant - y = (a1*c2 - a2*c1)/determinant - - r = [x,y] - if self.on_segment(a, r, b) and self.on_segment(c, r, d): - return [x, y] - - return None @@ -659,7 +605,8 @@ class router: y = track.y*self.track_width + 0.5*self.track_width - space ur = snap_to_grid(vector(x,y)) - return [ll,ur] + p = pin_layout("", [ll, ur], self.get_layer(track[2])) + return p def convert_track_to_shape(self, track): """ @@ -679,8 +626,11 @@ class router: """ Analyze the shapes of a pin and combine them into groups which are connected. """ + debug.info(2,"Analyzing pin groups for {}.".format(pin_name)) + pin_set = self.pins[pin_name] - local_debug=False + local_debug = False + # Put each pin in an equivalence class of it's own equiv_classes = [set([x]) for x in pin_set] if local_debug: @@ -711,6 +661,8 @@ class router: def combine_classes(equiv_classes): """ Recursive function to combine classes. """ + local_debug = False + if local_debug: debug.info(0,"\nRECURSE:\n",pformat(equiv_classes)) if len(equiv_classes)==1: @@ -733,178 +685,17 @@ class router: reduced_classes = combine_classes(equiv_classes) if local_debug: debug.info(0,"FINAL ",reduced_classes) - self.pin_groups[pin_name]=reduced_classes + self.pin_groups[pin_name] = [pin_group(name=pin_name, pin_set=x, router=self) for x in reduced_classes] def convert_pins(self, pin_name): """ Convert the pin groups into pin tracks and blockage tracks. """ - try: - self.pin_grids[pin_name] - except: - self.pin_grids[pin_name] = [] - - found_pin = False + debug.info(1,"Converting pins for {}.".format(pin_name)) for pg in self.pin_groups[pin_name]: - #print("PG ",pg) - # Keep the same groups for each pin - pin_set = set() - blockage_set = set() - for pin in pg: - debug.info(2," Converting {0}".format(pin)) - # Determine which tracks the pin overlaps - pin_in_tracks=self.convert_pin_to_tracks(pin_name, pin) - pin_set.update(pin_in_tracks) - # Blockages will be a super-set of pins since it uses the inflated pin shape. - blockage_in_tracks = self.convert_blockage(pin) - blockage_set.update(blockage_in_tracks) - - # If we have a blockage, we must remove the grids - # Remember, this excludes the pin blockages already - shared_set = pin_set & self.blocked_grids - if shared_set: - debug.info(2,"Removing pins {}".format(shared_set)) - shared_set = blockage_set & self.blocked_grids - if shared_set: - debug.info(2,"Removing blocks {}".format(shared_set)) - pin_set.difference_update(self.blocked_grids) - blockage_set.difference_update(self.blocked_grids) - debug.info(2," pins {}".format(pin_set)) - debug.info(2," blocks {}".format(blockage_set)) - - # At least one of the groups must have some valid tracks - if (len(pin_set)==0 and len(blockage_set)==0): - self.write_debug_gds("blocked_pin.gds") - debug.error("Unable to find unblocked pin on grid.") - - # We need to route each of the components, so don't combine the groups - self.pin_grids[pin_name].append(pin_set | blockage_set) - - # Add all of the partial blocked grids to the set for the design - # if they are not blocked by other metal - #partial_set = blockage_set - pin_set - #self.pin_blockages[pin_name].append(partial_set) - - # We should not have added the pins to the blockages, - # but remove them just in case - # Partial set may still be in the blockages if there were - # other shapes disconnected from the pins that were also overlapping - #self.blocked_grids.difference_update(pin_set) + pg.convert_pin() - def enclose_pin_grids(self, grids, seed): - """ - This encloses a single pin component with a rectangle - starting with the seed and expanding right until blocked - and then up until blocked. - """ - - # We may have started with an empty set - if not grids: - return None - - # Start with the seed - ll = seed - - # Start with the ll and make the widest row - row = [ll] - # Move right while we can - while True: - right = row[-1] + vector3d(1,0,0) - # Can't move if not in the pin shape - if right in grids and right not in self.blocked_grids: - row.append(right) - else: - break - # Move up while we can - while True: - next_row = [x+vector3d(0,1,0) for x in row] - for cell in next_row: - # Can't move if any cell is not in the pin shape - if cell not in grids or cell in self.blocked_grids: - break - else: - row = next_row - # Skips the second break - continue - # Breaks from the nested break - break - - # Add a shape from ll to ur - ur = row[-1] - return self.compute_pin_enclosure(ll, ur, ll.z) - - def remove_redundant_shapes(self, pin_list): - """ - Remove any pin layout that is contained within another. - """ - local_debug = False - if local_debug: - debug.info(0,"INITIAL:",pin_list) - - # Make a copy of the list to start - new_pin_list = pin_list.copy() - - # This is n^2, but the number is small - for pin1 in pin_list: - for pin2 in pin_list: - # Can't contain yourself - if pin1 == pin2: - continue - if pin2.contains(pin1): - # It may have already been removed by being enclosed in another pin - if pin1 in new_pin_list: - new_pin_list.remove(pin1) - - if local_debug: - debug.info(0,"FINAL :",new_pin_list) - return new_pin_list - - def compute_enclosures(self, tracks): - """ - Find the minimum rectangle enclosures of the given tracks. - """ - pin_list = [] - for seed in tracks: - pin_list.append(self.enclose_pin_grids(tracks, seed)) - - return self.remove_redundant_shapes(pin_list) - - def overlap_any_shape(self, pin_list, shape_list): - """ - Does the given pin overlap any of the shapes in the pin list. - """ - for pin in pin_list: - for other in shape_list: - if pin.overlaps(other): - return True - - return False - - def max_pin_layout(self, pin_list): - """ - Return the max area pin_layout - """ - biggest = pin_list[0] - for pin in pin_list: - if pin.area() > biggest.area(): - biggest = pin - - return pin - - def find_smallest_connector(self, pin_list, enclosure_list): - """ - Compute all of the connectors between non-overlapping pins and enclosures. - Return the smallest. - """ - smallest = None - for pin in pin_list: - for enclosure in enclosure_list: - new_enclosure = self.compute_enclosure(pin, enclosure) - if smallest == None or new_enclosure.area()0: + pg.set_routed() + continue + + # Else, if we overlap some of the space track, we can patch it with an enclosure + common_set = supply_tracks & pg.grids + if len(common_set)>0: + pg.create_simple_overlap_enclosure(common_set) + pg.add_enclosure(self.cell) - def create_simple_overlap_enclosure(self, pin_name, start_set): - """ - This takes a set of tracks that overlap a supply rail and creates an enclosure - that is ensured to overlap the supply rail wire. - It then adds rectangle(s) for the enclosure. - """ - additional_set = set() - # Check the layer of any element in the pin to determine which direction to route it - e = next(iter(start_set)) - new_set = start_set.copy() - if e.z==0: - new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.NORTH) - if not new_set: - new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.SOUTH) - else: - new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.EAST) - if not new_set: - new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.WEST) - - enclosure_list = self.compute_enclosures(new_set) - for pin in enclosure_list: - debug.info(2,"Adding simple overlap enclosure {0} {1}".format(pin_name, pin)) - self.cell.add_rect(layer=pin.layer, - offset=pin.ll(), - width=pin.width(), - height=pin.height()) - @@ -228,9 +178,8 @@ class supply_router(router): # the overlap area for placement of a via overlap = new_r1 & new_r2 if len(overlap) >= self.supply_rail_wire_width**2: - debug.info(2,"Via overlap {0} {1} {2}".format(len(overlap),self.supply_rail_wire_width**2,overlap)) - connections.add(i1) - connections.add(i2) + debug.info(3,"Via overlap {0} {1} {2}".format(len(overlap),self.supply_rail_wire_width**2,overlap)) + connections.update([i1,i2]) via_areas.append(overlap) # Go through and add the vias at the center of the intersection @@ -241,11 +190,12 @@ class supply_router(router): self.add_via(center,self.rail_track_width) # Determien which indices were not connected to anything above - all_indices = set([x for x in range(len(self.supply_rails[name]))]) - missing_indices = all_indices ^ connections + missing_indices = set([x for x in range(len(self.supply_rails[name]))]) + missing_indices.difference_update(connections) + # Go through and remove those disconnected indices # (No via was added, so that doesn't need to be removed) - for rail_index in missing_indices: + for rail_index in sorted(missing_indices, reverse=True): ll = grid_utils.get_lower_left(all_rails[rail_index]) ur = grid_utils.get_upper_right(all_rails[rail_index]) debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur)) @@ -268,7 +218,7 @@ class supply_router(router): ur = grid_utils.get_upper_right(rail) z = ll.z pin = self.compute_wide_enclosure(ll, ur, z, name) - debug.info(1,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin)) + debug.info(2,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin)) self.cell.add_layout_pin(text=name, layer=pin.layer, offset=pin.ll(), @@ -331,11 +281,12 @@ class supply_router(router): # While we can keep expanding east in this horizontal track while wave and wave[0].x < self.max_xoffset: added_rail = self.find_supply_rail(name, wave, direction.EAST) - if added_rail: - wave = added_rail.neighbor(direction.EAST) + if not added_rail: + # Just seed with the next one + wave = [x+vector3d(1,0,0) for x in wave] else: - wave = None - + # Seed with the neighbor of the end of the last rail + wave = added_rail.neighbor(direction.EAST) # Vertical supply rails max_offset = self.rg.ur.x @@ -345,10 +296,12 @@ class supply_router(router): # While we can keep expanding north in this vertical track while wave and wave[0].y < self.max_yoffset: added_rail = self.find_supply_rail(name, wave, direction.NORTH) - if added_rail: - wave = added_rail.neighbor(direction.NORTH) + if not added_rail: + # Just seed with the next one + wave = [x+vector3d(0,1,0) for x in wave] else: - wave = None + # Seed with the neighbor of the end of the last rail + wave = added_rail.neighbor(direction.NORTH) def find_supply_rail(self, name, seed_wave, direct): """ @@ -356,15 +309,18 @@ class supply_router(router): to contain a via, and, if so, add it. """ start_wave = self.find_supply_rail_start(name, seed_wave, direct) + + # This means there were no more unblocked grids in the row/col if not start_wave: return None - + wave_path = self.probe_supply_rail(name, start_wave, direct) - if self.approve_supply_rail(name, wave_path): - return wave_path - else: - return None + self.approve_supply_rail(name, wave_path) + + # Return the rail whether we approved it or not, + # as it will be used to find the next start location + return wave_path def find_supply_rail_start(self, name, seed_wave, direct): """ @@ -431,10 +387,8 @@ class supply_router(router): Route the horizontal and vertical supply rails across the entire design. Must be done with lower left at 0,0 """ + debug.info(1,"Routing supply rail {0}.".format(name)) - # Compute the grid dimensions - self.compute_supply_rail_dimensions() - # Compute the grid locations of the supply rails self.compute_supply_rails(name, supply_number) @@ -460,23 +414,27 @@ class supply_router(router): self.supply_rail_wire_tracks[pin_name] = wire_set - def route_pins_to_rails(self, pin_name, remaining_component_indices): + def route_pins_to_rails(self, pin_name): """ This will route each of the remaining pin components to the supply rails. After it is done, the cells are added to the pin blockage list. """ - - debug.info(1,"Pin {0} has {1} remaining components to route.".format(pin_name, - len(remaining_component_indices))) + remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name]) + debug.info(1,"Routing {0} with {1} pin components to route.".format(pin_name, + remaining_components)) - recent_paths = [] - # For every component - for index in remaining_component_indices: - debug.info(2,"Routing component {0} {1}".format(pin_name, index)) + for index,pg in enumerate(self.pin_groups[pin_name]): + if pg.is_routed(): + continue + debug.info(3,"Routing component {0} {1}".format(pin_name, index)) + + # Clear everything in the routing grid. self.rg.reinit() - + + # This is inefficient since it is non-incremental, but it was + # easier to debug. self.prepare_blockages(pin_name) # Add the single component of the pin as the source @@ -487,23 +445,16 @@ class supply_router(router): # Don't add the other pins, but we could? self.add_supply_rail_target(pin_name) - # Add the previous paths as targets too - #self.add_path_target(recent_paths) - - #print(self.rg.target) - # Actually run the A* router if not self.run_router(detour_scale=5): self.write_debug_gds() - - recent_paths.append(self.paths[-1]) def add_supply_rail_target(self, pin_name): """ Add the supply rails of given name as a routing target. """ - debug.info(2,"Add supply rail target {}".format(pin_name)) + debug.info(4,"Add supply rail target {}".format(pin_name)) # Add the wire itself as the target self.rg.set_target(self.supply_rail_wire_tracks[pin_name]) # But unblock all the rail tracks including the space @@ -514,7 +465,7 @@ class supply_router(router): """ Add the supply rails of given name as a routing target. """ - debug.info(3,"Blocking supply rail") + debug.info(4,"Blocking supply rail") for rail_name in self.supply_rail_tracks: self.rg.set_blocked(self.supply_rail_tracks[rail_name]) diff --git a/compiler/router/vector3d.py b/compiler/router/vector3d.py index 42bc35d4..a6e61078 100644 --- a/compiler/router/vector3d.py +++ b/compiler/router/vector3d.py @@ -163,3 +163,21 @@ class vector3d(): """ Min of both values """ return vector3d(min(self.x,other.x),min(self.y,other.y),min(self.z,other.z)) + def distance(self, other): + """ Return the planar distance between two values """ + return abs(self.x-other.x)+abs(self.y-other.y) + + + def adjacent(self, other): + """ Is the one grid adjacent in any planar direction to the other """ + if self == other + vector3d(1,0,0): + return True + elif self == other + vector3d(-1,0,0): + return True + elif self == other + vector3d(0,1,0): + return True + elif self == other + vector3d(0,-1,0): + return True + else: + return False + diff --git a/compiler/sram_base.py b/compiler/sram_base.py index cc8247ed..a1be1f30 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -223,13 +223,13 @@ class sram_base(design): from control_logic import control_logic # Create the control logic module for each port type if OPTS.num_rw_ports>0: - self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, port_type="rw") + self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="rw") self.add_mod(self.control_logic_rw) if OPTS.num_w_ports>0: - self.control_logic_w = control_logic(num_rows=self.num_rows, port_type="w") + self.control_logic_w = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="w") self.add_mod(self.control_logic_w) if OPTS.num_r_ports>0: - self.control_logic_r = control_logic(num_rows=self.num_rows, port_type="r") + self.control_logic_r = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="r") self.add_mod(self.control_logic_r) # Create the address and control flops (but not the clk) diff --git a/compiler/tests/00_code_format_check_test.py b/compiler/tests/00_code_format_check_test.py index 026f7e2b..869e81bd 100755 --- a/compiler/tests/00_code_format_check_test.py +++ b/compiler/tests/00_code_format_check_test.py @@ -116,7 +116,7 @@ def check_print_output(file_name): return(count) -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/01_library_drc_test.py b/compiler/tests/01_library_drc_test.py index b809c14d..046e6378 100755 --- a/compiler/tests/01_library_drc_test.py +++ b/compiler/tests/01_library_drc_test.py @@ -39,7 +39,7 @@ def setup_files(): return (gds_dir, gds_files) -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index 0b35f159..4ec40dc7 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -16,22 +16,25 @@ class library_lvs_test(openram_test): import verify (gds_dir, sp_dir, allnames) = setup_files() + drc_errors = 0 lvs_errors = 0 debug.info(1, "Performing LVS on: " + ", ".join(allnames)) for f in allnames: gds_name = "{0}/{1}.gds".format(gds_dir, f) sp_name = "{0}/{1}.sp".format(sp_dir, f) + name = re.sub('\.gds$', '', f) if not os.path.isfile(gds_name): lvs_errors += 1 debug.error("Missing GDS file {}".format(gds_name)) if not os.path.isfile(sp_name): lvs_errors += 1 debug.error("Missing SPICE file {}".format(gds_name)) + drc_errors += verify.run_drc(name, gds_name) lvs_errors += verify.run_lvs(f, gds_name, sp_name) # fail if the error count is not zero - self.assertEqual(lvs_errors, 0) + self.assertEqual(drc_errors+lvs_errors, 0) globals.end_openram() def setup_files(): @@ -59,7 +62,7 @@ def setup_files(): return (gds_dir, sp_dir, allnames) -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index 2fab1c4e..33aa45ae 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -43,7 +43,7 @@ class contact_test(openram_test): -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_path_test.py b/compiler/tests/03_path_test.py index f70d00be..915c5c78 100755 --- a/compiler/tests/03_path_test.py +++ b/compiler/tests/03_path_test.py @@ -84,7 +84,7 @@ class path_test(openram_test): -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_1finger_nmos_test.py b/compiler/tests/03_ptx_1finger_nmos_test.py index 727c24f0..9a81810e 100755 --- a/compiler/tests/03_ptx_1finger_nmos_test.py +++ b/compiler/tests/03_ptx_1finger_nmos_test.py @@ -25,7 +25,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_1finger_pmos_test.py b/compiler/tests/03_ptx_1finger_pmos_test.py index 04b9ab64..a3ed99ff 100755 --- a/compiler/tests/03_ptx_1finger_pmos_test.py +++ b/compiler/tests/03_ptx_1finger_pmos_test.py @@ -25,7 +25,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_3finger_nmos_test.py b/compiler/tests/03_ptx_3finger_nmos_test.py index 20343b2e..e1febdbc 100755 --- a/compiler/tests/03_ptx_3finger_nmos_test.py +++ b/compiler/tests/03_ptx_3finger_nmos_test.py @@ -27,7 +27,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_3finger_pmos_test.py b/compiler/tests/03_ptx_3finger_pmos_test.py index 37933702..af9a5d42 100755 --- a/compiler/tests/03_ptx_3finger_pmos_test.py +++ b/compiler/tests/03_ptx_3finger_pmos_test.py @@ -27,7 +27,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_4finger_nmos_test.py b/compiler/tests/03_ptx_4finger_nmos_test.py index 09788a5e..08a20898 100755 --- a/compiler/tests/03_ptx_4finger_nmos_test.py +++ b/compiler/tests/03_ptx_4finger_nmos_test.py @@ -27,7 +27,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_ptx_4finger_pmos_test.py b/compiler/tests/03_ptx_4finger_pmos_test.py index f43d7dc7..01857eda 100755 --- a/compiler/tests/03_ptx_4finger_pmos_test.py +++ b/compiler/tests/03_ptx_4finger_pmos_test.py @@ -27,7 +27,7 @@ class ptx_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py index 557fee5b..1b18e14b 100755 --- a/compiler/tests/03_wire_test.py +++ b/compiler/tests/03_wire_test.py @@ -121,7 +121,7 @@ class wire_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 0b6bd8f5..e5dbbc5e 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -94,7 +94,7 @@ class pbitcell_test(openram_test): -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pinv_10x_test.py b/compiler/tests/04_pinv_10x_test.py index 3a7f846a..d457d2a9 100755 --- a/compiler/tests/04_pinv_10x_test.py +++ b/compiler/tests/04_pinv_10x_test.py @@ -25,7 +25,7 @@ class pinv_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pinv_1x_beta_test.py b/compiler/tests/04_pinv_1x_beta_test.py index c1bb6aba..77ff5454 100755 --- a/compiler/tests/04_pinv_1x_beta_test.py +++ b/compiler/tests/04_pinv_1x_beta_test.py @@ -24,7 +24,7 @@ class pinv_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pinv_1x_test.py b/compiler/tests/04_pinv_1x_test.py index 555aa0e5..49cb1cb1 100755 --- a/compiler/tests/04_pinv_1x_test.py +++ b/compiler/tests/04_pinv_1x_test.py @@ -23,7 +23,7 @@ class pinv_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pinv_2x_test.py b/compiler/tests/04_pinv_2x_test.py index 6882a719..84bc55ee 100755 --- a/compiler/tests/04_pinv_2x_test.py +++ b/compiler/tests/04_pinv_2x_test.py @@ -25,7 +25,7 @@ class pinv_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pinvbuf_test.py b/compiler/tests/04_pinvbuf_test.py index 9c55ebe3..d35f1ec7 100755 --- a/compiler/tests/04_pinvbuf_test.py +++ b/compiler/tests/04_pinvbuf_test.py @@ -23,7 +23,7 @@ class pinvbuf_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py index b6739e4e..a2ac9288 100755 --- a/compiler/tests/04_pnand2_test.py +++ b/compiler/tests/04_pnand2_test.py @@ -27,7 +27,7 @@ class pnand2_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pnand3_test.py b/compiler/tests/04_pnand3_test.py index db3817f5..f6daedda 100755 --- a/compiler/tests/04_pnand3_test.py +++ b/compiler/tests/04_pnand3_test.py @@ -27,7 +27,7 @@ class pnand3_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py index 978c03ad..ce4b19ae 100755 --- a/compiler/tests/04_pnor2_test.py +++ b/compiler/tests/04_pnor2_test.py @@ -26,7 +26,7 @@ class pnor2_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index 02de2efd..e5419dab 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -39,7 +39,7 @@ class precharge_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_replica_pbitcell_test.py b/compiler/tests/04_replica_pbitcell_test.py index 7a803d1c..ce9f00b9 100755 --- a/compiler/tests/04_replica_pbitcell_test.py +++ b/compiler/tests/04_replica_pbitcell_test.py @@ -37,7 +37,7 @@ class replica_pbitcell_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/04_single_level_column_mux_test.py b/compiler/tests/04_single_level_column_mux_test.py index 3a7d6399..c43b15fd 100755 --- a/compiler/tests/04_single_level_column_mux_test.py +++ b/compiler/tests/04_single_level_column_mux_test.py @@ -41,7 +41,7 @@ class single_level_column_mux_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/05_bitcell_1rw_1r_array_test.py b/compiler/tests/05_bitcell_1rw_1r_array_test.py index 68dcc409..1223085e 100755 --- a/compiler/tests/05_bitcell_1rw_1r_array_test.py +++ b/compiler/tests/05_bitcell_1rw_1r_array_test.py @@ -29,7 +29,7 @@ class bitcell_1rw_1r_array_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index 4ea5c65a..93668e05 100755 --- a/compiler/tests/05_bitcell_array_test.py +++ b/compiler/tests/05_bitcell_array_test.py @@ -25,7 +25,7 @@ class array_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 4fc75ac5..4da5bec9 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -44,7 +44,7 @@ class pbitcell_array_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 513e07a8..09201149 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -69,7 +69,7 @@ class hierarchical_decoder_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index bcfd207a..e16916d6 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -35,7 +35,7 @@ class hierarchical_predecode2x4_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index b89f4bea..ed5da57c 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -35,7 +35,7 @@ class hierarchical_predecode3x8_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index 63f69bc5..800292b6 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -54,7 +54,7 @@ class single_level_column_mux_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index be8fc9d7..5ea9931b 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -39,7 +39,7 @@ class precharge_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/08_wordline_driver_test.py index 7f0ca275..369b6774 100755 --- a/compiler/tests/08_wordline_driver_test.py +++ b/compiler/tests/08_wordline_driver_test.py @@ -37,7 +37,7 @@ class wordline_driver_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index af2c974c..a18631f9 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -42,7 +42,7 @@ class sense_amp_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py index ab9dc615..fa374181 100755 --- a/compiler/tests/10_write_driver_array_test.py +++ b/compiler/tests/10_write_driver_array_test.py @@ -42,7 +42,7 @@ class write_driver_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/11_dff_array_test.py b/compiler/tests/11_dff_array_test.py index a55c6407..eed41dda 100755 --- a/compiler/tests/11_dff_array_test.py +++ b/compiler/tests/11_dff_array_test.py @@ -31,7 +31,7 @@ class dff_array_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/11_dff_buf_array_test.py b/compiler/tests/11_dff_buf_array_test.py index f0b75552..d2932cac 100755 --- a/compiler/tests/11_dff_buf_array_test.py +++ b/compiler/tests/11_dff_buf_array_test.py @@ -31,7 +31,7 @@ class dff_buf_array_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/11_dff_buf_test.py b/compiler/tests/11_dff_buf_test.py index f434f768..c9c25f16 100755 --- a/compiler/tests/11_dff_buf_test.py +++ b/compiler/tests/11_dff_buf_test.py @@ -23,7 +23,7 @@ class dff_buf_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/11_dff_inv_array_test.py b/compiler/tests/11_dff_inv_array_test.py index 2196a3f2..ed03e6bc 100755 --- a/compiler/tests/11_dff_inv_array_test.py +++ b/compiler/tests/11_dff_inv_array_test.py @@ -31,7 +31,7 @@ class dff_inv_array_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/11_dff_inv_test.py b/compiler/tests/11_dff_inv_test.py index 43d49246..53a92852 100755 --- a/compiler/tests/11_dff_inv_test.py +++ b/compiler/tests/11_dff_inv_test.py @@ -23,7 +23,7 @@ class dff_inv_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/12_tri_gate_array_test.py b/compiler/tests/12_tri_gate_array_test.py index 4f9cfa3e..cb789155 100755 --- a/compiler/tests/12_tri_gate_array_test.py +++ b/compiler/tests/12_tri_gate_array_test.py @@ -27,7 +27,7 @@ class tri_gate_array_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/13_delay_chain_test.py b/compiler/tests/13_delay_chain_test.py index 2cc745c2..1052f0de 100755 --- a/compiler/tests/13_delay_chain_test.py +++ b/compiler/tests/13_delay_chain_test.py @@ -23,7 +23,7 @@ class delay_chain_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/14_replica_bitline_test.py b/compiler/tests/14_replica_bitline_test.py index 6797bc65..08d9be5f 100755 --- a/compiler/tests/14_replica_bitline_test.py +++ b/compiler/tests/14_replica_bitline_test.py @@ -24,6 +24,26 @@ class replica_bitline_test(openram_test): debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) a = replica_bitline.replica_bitline(stages,fanout,rows) self.local_check(a) + #debug.error("Exiting...", 1) + + stages=8 + rows=100 + debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) + a = replica_bitline.replica_bitline(stages,fanout,rows) + self.local_check(a) + + #check replica bitline in handmade multi-port 1rw+1r cell + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 1 + stages=4 + fanout=4 + rows=13 + debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) + a = replica_bitline.replica_bitline(stages,fanout,rows) + self.local_check(a) stages=8 rows=100 @@ -31,7 +51,7 @@ class replica_bitline_test(openram_test): a = replica_bitline.replica_bitline(stages,fanout,rows) self.local_check(a) - # check replica bitline in multi-port + # check replica bitline in pbitcell multi-port OPTS.bitcell = "pbitcell" OPTS.replica_bitcell = "replica_pbitcell" OPTS.num_rw_ports = 1 @@ -61,7 +81,7 @@ class replica_bitline_test(openram_test): debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) a = replica_bitline.replica_bitline(stages,fanout,rows) self.local_check(a) - + stages=8 rows=100 debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) @@ -70,7 +90,7 @@ class replica_bitline_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/16_control_logic_test.py b/compiler/tests/16_control_logic_test.py index 7a4ff768..818c2eaf 100755 --- a/compiler/tests/16_control_logic_test.py +++ b/compiler/tests/16_control_logic_test.py @@ -20,7 +20,7 @@ class control_logic_test(openram_test): # check control logic for single port debug.info(1, "Testing sample for control_logic") - a = control_logic.control_logic(num_rows=128) + a = control_logic.control_logic(num_rows=128, words_per_row=1) self.local_check(a) # check control logic for multi-port @@ -31,7 +31,7 @@ class control_logic_test(openram_test): OPTS.num_r_ports = 0 debug.info(1, "Testing sample for control_logic for multiport") - a = control_logic.control_logic(num_rows=128) + a = control_logic.control_logic(num_rows=128, words_per_row=1) self.local_check(a) # Check port specific control logic @@ -40,20 +40,20 @@ class control_logic_test(openram_test): OPTS.num_r_ports = 1 debug.info(1, "Testing sample for control_logic for multiport, only write control logic") - a = control_logic.control_logic(num_rows=128, port_type="rw") + a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="rw") self.local_check(a) debug.info(1, "Testing sample for control_logic for multiport, only write control logic") - a = control_logic.control_logic(num_rows=128, port_type="w") + a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="w") self.local_check(a) debug.info(1, "Testing sample for control_logic for multiport, only read control logic") - a = control_logic.control_logic(num_rows=128, port_type="r") + a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="r") self.local_check(a) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/19_bank_select_test.py b/compiler/tests/19_bank_select_test.py index 23b7ec46..1245926b 100755 --- a/compiler/tests/19_bank_select_test.py +++ b/compiler/tests/19_bank_select_test.py @@ -40,7 +40,7 @@ class bank_select_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index 4fceafec..9bf32423 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -49,7 +49,7 @@ class multi_bank_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/19_pmulti_bank_test.py b/compiler/tests/19_pmulti_bank_test.py index 03544587..32d3917a 100755 --- a/compiler/tests/19_pmulti_bank_test.py +++ b/compiler/tests/19_pmulti_bank_test.py @@ -54,7 +54,7 @@ class multi_bank_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index d45283e0..dee59175 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -137,7 +137,7 @@ class psingle_bank_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index 3c32b30d..da411d1f 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -48,7 +48,7 @@ class single_bank_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/20_psram_1bank_2mux_test.py b/compiler/tests/20_psram_1bank_2mux_test.py new file mode 100755 index 00000000..e382eac4 --- /dev/null +++ b/compiler/tests/20_psram_1bank_2mux_test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") +class psram_1bank_2mux_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + + # testing layout of sram using pbitcell with 1 RW port (a 6T-cell equivalent) + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + c.num_words=32 + c.words_per_row=2 + debug.info(1, "Single bank two way column mux with control logic") + a = sram(c, "sram") + self.local_check(a, final_verification=True) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/20_psram_1bank_nomux_test.py b/compiler/tests/20_psram_1bank_nomux_test.py deleted file mode 100755 index 6106763c..00000000 --- a/compiler/tests/20_psram_1bank_nomux_test.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - -import unittest -from testutils import header,openram_test -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug - -@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") -class sram_1bank_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram - from sram_config import sram_config - OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell="replica_pbitcell" - - # testing layout of sram using pbitcell with 1 RW port (a 6T-cell equivalent) - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - c = sram_config(word_size=4, - num_words=16, - num_banks=1) - c.words_per_row=1 - - debug.info(1, "Single bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - - c.num_words=32 - c.words_per_row=2 - debug.info(1, "Single bank two way column mux with control logic") - a = sram(c, "sram2") - self.local_check(a, final_verification=True) - """ - c.num_words=64 - c.words_per_row=4 - debug.info(1, "Single bank, four way column mux with control logic") - a = sram(c, "sram3") - self.local_check(a, final_verification=True) - - c.word_size=2 - c.num_words=128 - c.words_per_row=8 - debug.info(1, "Single bank, eight way column mux with control logic") - a = sram(c, "sram4") - self.local_check(a, final_verification=True) - - # testing sram using pbitcell in various port combinations - # layout for multiple ports does not work yet - """ - OPTS.netlist_only = True - - c.num_words=16 - c.words_per_row=1 - - OPTS.num_rw_ports = 2 - OPTS.num_w_ports = 2 - OPTS.num_r_ports = 2 - - debug.info(1, "Single bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - """ - OPTS.num_rw_ports = 0 - OPTS.num_w_ports = 2 - OPTS.num_r_ports = 2 - - debug.info(1, "Single bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - - OPTS.num_rw_ports = 2 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 2 - - debug.info(1, "Single bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - - OPTS.num_rw_ports = 2 - OPTS.num_w_ports = 2 - OPTS.num_r_ports = 0 - - debug.info(1, "Single bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - - OPTS.num_rw_ports = 2 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - debug.info(1, "Single bank, no column mux with control logic") - a = sram(c, "sram1") - self.local_check(a, final_verification=True) - - # testing with various column muxes - OPTS.num_rw_ports = c.num_rw_ports = 2 - OPTS.num_w_ports = c.num_w_ports = 2 - OPTS.num_r_ports = c.num_r_ports = 2 - - c.num_words=32 - c.words_per_row=2 - debug.info(1, "Single bank two way column mux with control logic") - a = sram(c, "sram2") - self.local_check(a, final_verification=True) - - c.num_words=64 - c.words_per_row=4 - debug.info(1, "Single bank, four way column mux with control logic") - a = sram(c, "sram3") - self.local_check(a, final_verification=True) - - c.word_size=2 - c.num_words=128 - c.words_per_row=8 - debug.info(1, "Single bank, eight way column mux with control logic") - a = sram(c, "sram4") - self.local_check(a, final_verification=True) - """ - 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() diff --git a/compiler/tests/20_sram_1bank_2mux_test.py b/compiler/tests/20_sram_1bank_2mux_test.py index db018f1e..2c8e28f0 100755 --- a/compiler/tests/20_sram_1bank_2mux_test.py +++ b/compiler/tests/20_sram_1bank_2mux_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 20_sram_1bank_2mux_test") +#@unittest.skip("SKIPPING 20_sram_1bank_2mux_test") class sram_1bank_2mux_test(openram_test): def runTest(self): @@ -29,7 +29,7 @@ class sram_1bank_2mux_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/20_sram_1bank_4mux_test.py b/compiler/tests/20_sram_1bank_4mux_test.py index 35416bbe..489ff354 100755 --- a/compiler/tests/20_sram_1bank_4mux_test.py +++ b/compiler/tests/20_sram_1bank_4mux_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 20_sram_1bank_2mux_test") +#@unittest.skip("SKIPPING 20_sram_1bank_4mux_test") class sram_1bank_4mux_test(openram_test): def runTest(self): @@ -29,7 +29,7 @@ class sram_1bank_4mux_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/20_sram_1bank_8mux_test.py b/compiler/tests/20_sram_1bank_8mux_test.py index d09165a2..2595582f 100755 --- a/compiler/tests/20_sram_1bank_8mux_test.py +++ b/compiler/tests/20_sram_1bank_8mux_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 20_sram_1bank_8mux_test") +#@unittest.skip("SKIPPING 20_sram_1bank_8mux_test") class sram_1bank_8mux_test(openram_test): def runTest(self): @@ -29,7 +29,7 @@ class sram_1bank_8mux_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/20_sram_1bank_nomux_test.py b/compiler/tests/20_sram_1bank_nomux_test.py index 26f5e9ba..783bcad2 100755 --- a/compiler/tests/20_sram_1bank_nomux_test.py +++ b/compiler/tests/20_sram_1bank_nomux_test.py @@ -11,7 +11,8 @@ import globals from globals import OPTS import debug -class sram_1bank_test(openram_test): +#@unittest.skip("SKIPPING 20_sram_1bank_nomux_test") +class sram_1bank_nomux_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) @@ -28,7 +29,7 @@ class sram_1bank_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index ff9fbaea..ab8c6ec2 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -48,7 +48,7 @@ class sram_2bank_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/20_sram_4bank_test.py b/compiler/tests/20_sram_4bank_test.py index fb34d3b0..25649e8e 100755 --- a/compiler/tests/20_sram_4bank_test.py +++ b/compiler/tests/20_sram_4bank_test.py @@ -47,7 +47,7 @@ class sram_4bank_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index a5aca3e8..5facb482 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -81,7 +81,7 @@ class timing_sram_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/21_hspice_setuphold_test.py b/compiler/tests/21_hspice_setuphold_test.py index 9bfdb24b..faa8617d 100755 --- a/compiler/tests/21_hspice_setuphold_test.py +++ b/compiler/tests/21_hspice_setuphold_test.py @@ -52,7 +52,7 @@ class timing_setup_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 45a9b7f6..3ef27fc5 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -81,7 +81,7 @@ class timing_sram_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/21_ngspice_setuphold_test.py b/compiler/tests/21_ngspice_setuphold_test.py index d58bfc50..924d05a5 100755 --- a/compiler/tests/21_ngspice_setuphold_test.py +++ b/compiler/tests/21_ngspice_setuphold_test.py @@ -53,7 +53,7 @@ class timing_setup_test(openram_test): reload(characterizer) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_psram_1bank_2mux_func_test.py b/compiler/tests/22_psram_1bank_2mux_func_test.py index d8233d08..f2679c03 100755 --- a/compiler/tests/22_psram_1bank_2mux_func_test.py +++ b/compiler/tests/22_psram_1bank_2mux_func_test.py @@ -20,6 +20,9 @@ class psram_1bank_2mux_func_test(openram_test): OPTS.netlist_only = True OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload @@ -49,7 +52,7 @@ class psram_1bank_2mux_func_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_psram_1bank_4mux_func_test.py b/compiler/tests/22_psram_1bank_4mux_func_test.py index 1ae684d9..a8d6dab2 100755 --- a/compiler/tests/22_psram_1bank_4mux_func_test.py +++ b/compiler/tests/22_psram_1bank_4mux_func_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test") +#@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test") class psram_1bank_4mux_func_test(openram_test): def runTest(self): @@ -20,6 +20,9 @@ class psram_1bank_4mux_func_test(openram_test): OPTS.netlist_only = True OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload @@ -49,7 +52,7 @@ class psram_1bank_4mux_func_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_psram_1bank_8mux_func_test.py b/compiler/tests/22_psram_1bank_8mux_func_test.py index d81e76f9..1a0a1ec5 100755 --- a/compiler/tests/22_psram_1bank_8mux_func_test.py +++ b/compiler/tests/22_psram_1bank_8mux_func_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 22_psram_1bank_8mux_func_test") +#@unittest.skip("SKIPPING 22_psram_1bank_8mux_func_test") class psram_1bank_8mux_func_test(openram_test): def runTest(self): @@ -20,6 +20,9 @@ class psram_1bank_8mux_func_test(openram_test): OPTS.netlist_only = True OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload @@ -29,7 +32,7 @@ class psram_1bank_8mux_func_test(openram_test): from sram import sram from sram_config import sram_config c = sram_config(word_size=4, - num_words=512, + num_words=256, num_banks=1) c.words_per_row=8 debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, @@ -37,6 +40,7 @@ class psram_1bank_8mux_func_test(openram_test): c.words_per_row, c.num_banks)) s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) @@ -48,7 +52,7 @@ class psram_1bank_8mux_func_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_psram_1bank_nomux_func_test.py b/compiler/tests/22_psram_1bank_nomux_func_test.py index 681e24d5..7817b055 100755 --- a/compiler/tests/22_psram_1bank_nomux_func_test.py +++ b/compiler/tests/22_psram_1bank_nomux_func_test.py @@ -20,6 +20,9 @@ class psram_1bank_nomux_func_test(openram_test): OPTS.netlist_only = True OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload @@ -49,7 +52,7 @@ class psram_1bank_nomux_func_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_sram_1bank_2mux_func_test.py b/compiler/tests/22_sram_1bank_2mux_func_test.py index 7779ed4f..8b195c95 100755 --- a/compiler/tests/22_sram_1bank_2mux_func_test.py +++ b/compiler/tests/22_sram_1bank_2mux_func_test.py @@ -47,7 +47,7 @@ class sram_1bank_2mux_func_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_sram_1bank_4mux_func_test.py b/compiler/tests/22_sram_1bank_4mux_func_test.py index c16b86fe..0df3ff0e 100755 --- a/compiler/tests/22_sram_1bank_4mux_func_test.py +++ b/compiler/tests/22_sram_1bank_4mux_func_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 22_sram_1bank_4mux_func_test") +#@unittest.skip("SKIPPING 22_sram_1bank_4mux_func_test") class sram_1bank_4mux_func_test(openram_test): def runTest(self): @@ -47,7 +47,7 @@ class sram_1bank_4mux_func_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_sram_1bank_8mux_func_test.py b/compiler/tests/22_sram_1bank_8mux_func_test.py index be8e538f..16122a49 100755 --- a/compiler/tests/22_sram_1bank_8mux_func_test.py +++ b/compiler/tests/22_sram_1bank_8mux_func_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 22_sram_1bank_8mux_func_test") +#@unittest.skip("SKIPPING 22_sram_1bank_8mux_func_test") class sram_1bank_8mux_func_test(openram_test): def runTest(self): @@ -30,7 +30,7 @@ class sram_1bank_8mux_func_test(openram_test): from sram import sram from sram_config import sram_config c = sram_config(word_size=4, - num_words=512, + num_words=256, num_banks=1) c.words_per_row=8 debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, @@ -38,6 +38,8 @@ class sram_1bank_8mux_func_test(openram_test): c.words_per_row, c.num_banks)) s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) @@ -49,7 +51,7 @@ class sram_1bank_8mux_func_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_sram_1bank_nomux_func_test.py b/compiler/tests/22_sram_1bank_nomux_func_test.py index 52d63f4a..e6a5bcda 100755 --- a/compiler/tests/22_sram_1bank_nomux_func_test.py +++ b/compiler/tests/22_sram_1bank_nomux_func_test.py @@ -39,7 +39,7 @@ class sram_1bank_nomux_func_test(openram_test): s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - + f = functional(s.s, tempspice, corner) f.num_cycles = 10 (fail, error) = f.run() @@ -47,7 +47,7 @@ class sram_1bank_nomux_func_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py new file mode 100755 index 00000000..aa80656d --- /dev/null +++ b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +""" +Run a functioal test on 1 bank SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test") +class psram_1bank_nomux_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 1 + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + c.words_per_row=1 + debug.info(1, "Functional test for sram 1rw,1r with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + + f = functional(s.s, tempspice, corner) + f.num_cycles = 10 + (fail, error) = f.run() + self.assertTrue(fail,error) + + 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() diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index 8d5064d5..b111a57d 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -44,7 +44,7 @@ class lib_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index 952072aa..7f0a9c47 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -55,7 +55,7 @@ class lib_test(openram_test): reload(characterizer) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index 1b2d317c..5534598e 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -54,7 +54,7 @@ class lib_test(openram_test): reload(characterizer) globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/24_lef_sram_test.py b/compiler/tests/24_lef_sram_test.py index f66104b6..d4bf2619 100755 --- a/compiler/tests/24_lef_sram_test.py +++ b/compiler/tests/24_lef_sram_test.py @@ -42,7 +42,7 @@ class lef_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index 48ba29e8..eebeb258 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -38,7 +38,7 @@ class verilog_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/26_pex_test.py b/compiler/tests/26_pex_test.py index edb344f9..d374e485 100755 --- a/compiler/tests/26_pex_test.py +++ b/compiler/tests/26_pex_test.py @@ -306,7 +306,7 @@ class sram_func_test(openram_test): sti_file.file.close() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/27_worst_case_delay_test.py b/compiler/tests/27_worst_case_delay_test.py index 42a07bef..52dd3422 100755 --- a/compiler/tests/27_worst_case_delay_test.py +++ b/compiler/tests/27_worst_case_delay_test.py @@ -73,7 +73,7 @@ class worst_case_timing_sram_test(openram_test): globals.end_openram() -# instantiate a copdsay of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py index ab0aa4d8..a8a16426 100755 --- a/compiler/tests/30_openram_test.py +++ b/compiler/tests/30_openram_test.py @@ -88,7 +88,7 @@ class openram_test(openram_test): globals.end_openram() -# instantiate a copy of the class to actually run the test +# run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index bb116da3..490a09a1 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -100,7 +100,7 @@ def write_netgen_script(cell_name, sp_name): os.system("chmod u+x {}".format(run_file)) -def run_drc(cell_name, gds_name, extract=False, final_verification=False): +def run_drc(cell_name, gds_name, extract=True, final_verification=False): """Run DRC check on a cell which is implemented in gds_name.""" global num_drc_runs @@ -166,7 +166,6 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): global num_lvs_runs num_lvs_runs += 1 - run_drc(cell_name, gds_name, extract=True, final_verification=final_verification) write_netgen_script(cell_name, sp_name) # run LVS diff --git a/technology/freepdk45/gds_lib/replica_cell_1rw_1r.gds b/technology/freepdk45/gds_lib/replica_cell_1rw_1r.gds new file mode 100644 index 00000000..8bc45cbb Binary files /dev/null and b/technology/freepdk45/gds_lib/replica_cell_1rw_1r.gds differ diff --git a/technology/freepdk45/gds_lib/replica_cell_6t.gds b/technology/freepdk45/gds_lib/replica_cell_6t.gds index 2881a883..38c69cf9 100644 Binary files a/technology/freepdk45/gds_lib/replica_cell_6t.gds and b/technology/freepdk45/gds_lib/replica_cell_6t.gds differ diff --git a/technology/freepdk45/sp_lib/cell_6t.sp b/technology/freepdk45/sp_lib/cell_6t.sp index cb9cbc3c..e1e4936d 100644 --- a/technology/freepdk45/sp_lib/cell_6t.sp +++ b/technology/freepdk45/sp_lib/cell_6t.sp @@ -1,10 +1,15 @@ .SUBCKT cell_6t bl br wl vdd gnd +* Inverter 1 +MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n + +* Inverer 2 +MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n + +* Access transistors MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n -MM2 br wl Qb gnd NMOS_VTG W=135.00n L=50n -MM1 Q Qb gnd gnd NMOS_VTG W=205.00n L=50n -MM0 Qb Q gnd gnd NMOS_VTG W=205.00n L=50n -MM5 Q Qb vdd vdd PMOS_VTG W=90n L=50n -MM4 Qb Q vdd vdd PMOS_VTG W=90n L=50n +MM2 br wl Qbar gnd NMOS_VTG W=135.00n L=50n .ENDS cell_6t diff --git a/technology/freepdk45/sp_lib/replica_cell_1rw_1r.sp b/technology/freepdk45/sp_lib/replica_cell_1rw_1r.sp new file mode 100644 index 00000000..72a014ec --- /dev/null +++ b/technology/freepdk45/sp_lib/replica_cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT replica_cell_1rw_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd NMOS_VTG W=180.0n L=50n m=1 +MM8 RA_to_R_right Q gnd gnd NMOS_VTG W=180.0n L=50n m=1 +MM7 RA_to_R_left vdd gnd gnd NMOS_VTG W=180.0n L=50n m=1 +MM6 RA_to_R_left wl1 bl1 gnd NMOS_VTG W=180.0n L=50n m=1 +MM5 Q wl0 bl0 gnd NMOS_VTG W=135.00n L=50n m=1 +MM4 vdd wl0 br0 gnd NMOS_VTG W=135.00n L=50n m=1 +MM1 Q vdd gnd gnd NMOS_VTG W=270.0n L=50n m=1 +MM0 vdd Q gnd gnd NMOS_VTG W=270.0n L=50n m=1 +MM3 Q vdd vdd vdd PMOS_VTG W=90n L=50n m=1 +MM2 vdd Q vdd vdd PMOS_VTG W=90n L=50n m=1 +.ENDS + diff --git a/technology/freepdk45/sp_lib/replica_cell_6t.sp b/technology/freepdk45/sp_lib/replica_cell_6t.sp index 3a7c40dd..dd29028a 100644 --- a/technology/freepdk45/sp_lib/replica_cell_6t.sp +++ b/technology/freepdk45/sp_lib/replica_cell_6t.sp @@ -1,10 +1,15 @@ .SUBCKT replica_cell_6t bl br wl vdd gnd -MM3 bl wl gnd gnd NMOS_VTG W=135.00n L=50n -MM2 br wl net4 gnd NMOS_VTG W=135.00n L=50n -MM1 gnd net4 gnd gnd NMOS_VTG W=205.00n L=50n -MM0 net4 gnd gnd gnd NMOS_VTG W=205.00n L=50n -MM5 gnd net4 vdd vdd PMOS_VTG W=90n L=50n -MM4 net4 gnd vdd vdd PMOS_VTG W=90n L=50n -.ENDS replica_cell_6t +* Inverter 1 +MM0 vdd Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 vdd Q vdd vdd PMOS_VTG W=90n L=50n + +* Inverer 2 +MM1 Q vdd gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q vdd vdd vdd PMOS_VTG W=90n L=50n + +* Access transistors +MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n +MM2 br wl vdd gnd NMOS_VTG W=135.00n L=50n +.ENDS cell_6t diff --git a/technology/freepdk45/tf/FreePDK45.tf b/technology/freepdk45/tf/FreePDK45.tf index 0021e644..f0bfc606 100644 --- a/technology/freepdk45/tf/FreePDK45.tf +++ b/technology/freepdk45/tf/FreePDK45.tf @@ -172,9 +172,9 @@ layerDefinitions( ( align drawing ) ( hardFence drawing ) ( softFence drawing ) - ( text drawing ) - ( text drawing1 ) - ( text drawing2 ) + ( comment drawing ) + ( comment drawing1 ) + ( comment drawing2 ) ( border drawing ) ( device drawing ) ( device label ) @@ -379,9 +379,9 @@ layerDefinitions( ( align drawing align t t t t nil ) ( hardFence drawing hardFence t t t t nil ) ( softFence drawing softFence t t t t nil ) - ( text drawing text t t t t t ) - ( text drawing1 text1 t t t t nil ) - ( text drawing2 text2 t t t t nil ) + ( comment drawing comment t t t t t ) + ( comment drawing1 comment1 t t t t nil ) + ( comment drawing2 comment2 t t t t nil ) ( border drawing border t t t t nil ) ( device drawing device t t t t nil ) ( device label deviceLbl t t t t nil ) diff --git a/technology/freepdk45/tf/layers.map b/technology/freepdk45/tf/layers.map index 8c0c18d2..1bc4d413 100644 --- a/technology/freepdk45/tf/layers.map +++ b/technology/freepdk45/tf/layers.map @@ -27,4 +27,4 @@ via8 drawing 26 0 metal9 drawing 27 0 via9 drawing 28 0 metal10 drawing 29 0 -text drawing 239 0 +comment drawing 239 0 diff --git a/technology/scn4m_subm/gds_lib/cell_1rw_1r.gds b/technology/scn4m_subm/gds_lib/cell_1rw_1r.gds index 0b13d628..4254ba26 100644 Binary files a/technology/scn4m_subm/gds_lib/cell_1rw_1r.gds and b/technology/scn4m_subm/gds_lib/cell_1rw_1r.gds differ diff --git a/technology/scn4m_subm/gds_lib/replica_cell_1rw_1r.gds b/technology/scn4m_subm/gds_lib/replica_cell_1rw_1r.gds new file mode 100644 index 00000000..836708c3 Binary files /dev/null and b/technology/scn4m_subm/gds_lib/replica_cell_1rw_1r.gds differ diff --git a/technology/scn4m_subm/gds_lib/replica_cell_6t.gds b/technology/scn4m_subm/gds_lib/replica_cell_6t.gds index 6a6b32ad..f16f7b13 100644 Binary files a/technology/scn4m_subm/gds_lib/replica_cell_6t.gds and b/technology/scn4m_subm/gds_lib/replica_cell_6t.gds differ diff --git a/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag index e6420e89..85323b17 100644 --- a/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag +++ b/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1540504134 +timestamp 1541193956 << nwell >> rect 0 50 54 79 << pwell >> @@ -139,10 +139,10 @@ rect 0 0 54 74 << labels >> rlabel metal1 27 4 27 4 1 wl1 rlabel psubstratepcontact 27 11 27 11 1 gnd -rlabel m2contact 27 74 27 74 5 vdd rlabel metal1 19 67 19 67 1 wl0 rlabel metal2 4 7 4 7 2 bl0 rlabel metal2 11 7 11 7 1 bl1 rlabel metal2 43 7 43 7 1 br1 rlabel metal2 50 7 50 7 8 br0 +rlabel metal1 19 74 19 74 5 vdd << end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag new file mode 100644 index 00000000..38edce07 --- /dev/null +++ b/technology/scn4m_subm/mag_lib/replica_cell_1rw_1r.mag @@ -0,0 +1,149 @@ +magic +tech scmos +timestamp 1541194096 +<< nwell >> +rect 0 50 54 79 +<< pwell >> +rect 0 0 54 50 +<< ntransistor >> +rect 14 35 16 41 +rect 22 29 24 41 +rect 30 29 32 41 +rect 38 35 40 41 +rect 14 17 16 25 +rect 22 17 24 25 +rect 30 17 32 25 +rect 38 17 40 25 +<< ptransistor >> +rect 22 58 24 62 +rect 30 58 32 62 +<< ndiffusion >> +rect 9 39 14 41 +rect 13 35 14 39 +rect 16 35 17 41 +rect 21 33 22 41 +rect 17 29 22 33 +rect 24 29 25 41 +rect 29 29 30 41 +rect 32 33 33 41 +rect 37 35 38 41 +rect 40 39 45 41 +rect 40 35 41 39 +rect 32 29 37 33 +rect 9 23 14 25 +rect 13 19 14 23 +rect 9 17 14 19 +rect 16 17 22 25 +rect 24 17 25 25 +rect 29 17 30 25 +rect 32 17 38 25 +rect 40 23 45 25 +rect 40 19 41 23 +rect 40 17 45 19 +<< pdiffusion >> +rect 21 58 22 62 +rect 24 58 25 62 +rect 29 58 30 62 +rect 32 58 33 62 +<< ndcontact >> +rect 9 35 13 39 +rect 17 33 21 41 +rect 25 29 29 41 +rect 33 33 37 41 +rect 41 35 45 39 +rect 9 19 13 23 +rect 25 17 29 25 +rect 41 19 45 23 +<< pdcontact >> +rect 17 58 21 62 +rect 25 58 29 62 +rect 33 58 37 62 +<< psubstratepcontact >> +rect 25 9 29 13 +<< nsubstratencontact >> +rect 25 72 29 76 +<< polysilicon >> +rect 22 62 24 64 +rect 30 62 32 64 +rect 22 48 24 58 +rect 30 55 32 58 +rect 31 51 32 55 +rect 14 41 16 46 +rect 22 44 23 48 +rect 22 41 24 44 +rect 30 41 32 51 +rect 38 41 40 46 +rect 14 33 16 35 +rect 38 33 40 35 +rect 14 25 16 26 +rect 22 25 24 29 +rect 30 25 32 29 +rect 38 25 40 26 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 27 51 31 55 +rect 10 42 14 46 +rect 23 44 27 48 +rect 40 42 44 46 +rect 12 26 16 30 +rect 38 26 42 30 +<< metal1 >> +rect 0 72 25 76 +rect 29 72 54 76 +rect 0 65 54 69 +rect 10 46 14 65 +rect 29 58 33 62 +rect 17 55 20 58 +rect 17 51 27 55 +rect 17 41 20 51 +rect 34 48 37 58 +rect 27 44 37 48 +rect 34 41 37 44 +rect 40 46 44 65 +rect 6 35 9 39 +rect 45 35 48 39 +rect 25 25 29 29 +rect 25 13 29 17 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 25 72 29 76 +rect 25 58 29 62 +rect 2 35 6 39 +rect 16 26 20 30 +rect 48 35 52 39 +rect 34 26 38 30 +rect 9 19 13 23 +rect 41 19 45 23 +rect 16 2 20 6 +rect 34 2 38 6 +<< metal2 >> +rect 2 39 6 76 +rect 2 0 6 35 +rect 9 23 13 76 +rect 25 62 29 72 +rect 9 0 13 19 +rect 16 6 20 26 +rect 34 6 38 26 +rect 41 23 45 76 +rect 41 0 45 19 +rect 48 39 52 76 +rect 48 0 52 35 +<< bb >> +rect 0 0 54 74 +<< labels >> +rlabel metal1 27 4 27 4 1 wl1 +rlabel psubstratepcontact 27 11 27 11 1 gnd +rlabel metal1 19 67 19 67 1 wl0 +rlabel metal2 4 7 4 7 2 bl0 +rlabel metal2 11 7 11 7 1 bl1 +rlabel metal2 43 7 43 7 1 br1 +rlabel metal2 50 7 50 7 8 br0 +rlabel metal1 19 74 19 74 5 vdd +<< end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_6t.mag b/technology/scn4m_subm/mag_lib/replica_cell_6t.mag index d0dc472f..c28cb2c6 100644 --- a/technology/scn4m_subm/mag_lib/replica_cell_6t.mag +++ b/technology/scn4m_subm/mag_lib/replica_cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1536091380 +timestamp 1541443051 << nwell >> rect -8 29 42 51 << pwell >> @@ -76,15 +76,15 @@ rect 17 6 21 10 rect -2 44 15 48 rect 19 44 32 48 rect -2 40 2 44 +rect 22 40 26 44 rect 32 40 36 44 rect 11 36 12 40 rect 26 36 27 40 rect -2 26 2 29 -rect 11 22 15 36 +rect -2 16 2 22 +rect 11 18 15 36 rect 23 24 27 36 -rect -2 18 15 22 rect 25 20 27 24 -rect -2 16 2 18 rect 14 14 15 18 rect 23 18 27 20 rect 32 26 36 29 diff --git a/technology/scn4m_subm/sp_lib/cell_6t.sp b/technology/scn4m_subm/sp_lib/cell_6t.sp index 846cc371..bb430893 100644 --- a/technology/scn4m_subm/sp_lib/cell_6t.sp +++ b/technology/scn4m_subm/sp_lib/cell_6t.sp @@ -3,11 +3,16 @@ .SUBCKT cell_6t bl br wl vdd gnd * SPICE3 file created from cell_6t.ext - technology: scmos -M1000 a_36_40# a_28_32# vdd vdd p w=0.6u l=0.8u -M1001 vdd a_36_40# a_28_32# vdd p w=0.6u l=0.8u -M1002 a_36_40# a_28_32# gnd gnd n w=1.6u l=0.4u -M1003 gnd a_36_40# a_28_32# gnd n w=1.6u l=0.4u -M1004 a_36_40# wl bl gnd n w=0.8u l=0.4u -M1005 a_28_32# wl br gnd n w=0.8u l=0.4u +* Inverter 1 +M1000 Q Qbar vdd vdd p w=0.6u l=0.8u +M1002 Q Qbar gnd gnd n w=1.6u l=0.4u + +* Inverter 2 +M1001 vdd Q Qbar vdd p w=0.6u l=0.8u +M1003 gnd Q Qbar gnd n w=1.6u l=0.4u + +* Access transistors +M1004 Q wl bl gnd n w=0.8u l=0.4u +M1005 Qbar wl br gnd n w=0.8u l=0.4u .ENDS diff --git a/technology/scn4m_subm/sp_lib/replica_cell_1rw_1r.sp b/technology/scn4m_subm/sp_lib/replica_cell_1rw_1r.sp new file mode 100644 index 00000000..0a235af8 --- /dev/null +++ b/technology/scn4m_subm/sp_lib/replica_cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT replica_cell_1rw_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd n w=1.6u l=0.4u +MM8 RA_to_R_right Q gnd gnd n w=1.6u l=0.4u +MM7 RA_to_R_left vdd gnd gnd n w=1.6u l=0.4u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.6u l=0.4u +MM5 Q wl0 bl0 gnd n w=1.2u l=0.4u +MM4 vdd wl0 br0 gnd n w=1.2u l=0.4u +MM1 Q vdd gnd gnd n w=2.4u l=0.4u +MM0 vdd Q gnd gnd n w=2.4u l=0.4u +MM3 Q vdd vdd vdd p w=0.8u l=0.4u +MM2 vdd Q vdd vdd p w=0.8u l=0.4u +.ENDS + diff --git a/technology/scn4m_subm/sp_lib/replica_cell_6t.sp b/technology/scn4m_subm/sp_lib/replica_cell_6t.sp index d26d600f..a9d41398 100644 --- a/technology/scn4m_subm/sp_lib/replica_cell_6t.sp +++ b/technology/scn4m_subm/sp_lib/replica_cell_6t.sp @@ -1,14 +1,18 @@ *********************** "cell_6t" ****************************** .SUBCKT replica_cell_6t bl br wl vdd gnd -* SPICE3 file created from replica_cell_6t.ext - technology: scmos +* SPICE3 file created from cell_6t.ext - technology: scmos -M1000 gnd a_28_32# vdd vdd p w=0.6u l=0.8u -M1001 vdd gnd a_28_32# vdd p w=0.6u l=0.8u -** SOURCE/DRAIN TIED -M1002 gnd a_28_32# gnd gnd n w=1.6u l=0.4u -M1003 gnd gnd a_28_32# gnd n w=1.6u l=0.4u -M1004 gnd wl bl gnd n w=0.8u l=0.4u -M1005 a_28_32# wl br gnd n w=0.8u l=0.4u +* Inverter 1 +M1000 Q vdd vdd vdd p w=0.6u l=0.8u +M1002 Q vdd gnd gnd n w=1.6u l=0.4u + +* Inverter 2 +M1001 vdd Q vdd vdd p w=0.6u l=0.8u +M1003 gnd Q vdd gnd n w=1.6u l=0.4u + +* Access transistors +M1004 Q wl bl gnd n w=0.8u l=0.4u +M1005 vdd wl br gnd n w=0.8u l=0.4u .ENDS diff --git a/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech b/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech index bb2c2490..f39aa84f 100644 --- a/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech +++ b/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech @@ -1,5 +1,5 @@ tech - format 29 + format 31 scmos end @@ -301,11 +301,6 @@ style lambda=0.20(p) scalefactor 20 10 options calma-permissive-labels - # This is a custom section to add bounding boxes in OpenRAM - layer BB bb - labels bb - calma 63 0 - layer CWN nwell,rnw,nwr,nwsd,nwsc bloat-or pdiff,apres,rpd,pdc/a,pfet * 120 bloat-or nsd,nsc/a * 60 @@ -1769,11 +1764,6 @@ cifinput style lambda=0.20(p) scalefactor 20 - # This is a custom section to add bounding boxes in OpenRAM - layer bb BB - labels BB - calma 63 0 - layer nwell CWN and-not CWNR and-not CTA @@ -6701,7 +6691,7 @@ drc edge4way nfet,pfet,fet space/active,ndiff,anres,rnd,ndc/a,pdiff,apres,rpd,pdc/a 3 ndiff,anres,rnd,ndc/a,pdiff,apres,rpd,pdc/a,nfet,pfet,fet 0 0 \ "N-Diffusion,P-Diffusion overhang of Transistor < 3 (Mosis #3.4)" active - edge4way poly,fp,rp,pc/a ~(poly,fp,pres,rp,pc/a,nfet,pfet,fet,prp)/active 1 space space 1 \ + edge4way poly,fp,rp,pc/a ~(poly,fp,pres,rp,pc/a,nfet,pfet,fet,prp)/active 1 space/a space/a 1 \ "Poly spacing to Diffusion < 1 (Mosis #3.5)" edge4way nfet ~(nfet)/active 2 ~(pselect)/select ~(nfet)/active 2 \ @@ -7212,13 +7202,15 @@ extract planeorder via3 14 planeorder fill 15 + substrate *psd,space/w,pwell well + resist (ndiff,anres,rnd,ndc,nsd,nwsd,nsc,nwsc)/active 3700 resist (pdiff,apres,rpd,pdc,psd,psc)/active 2800 resist (nwell)/well 1018000 - resist (rnw,nwr)/active 1018000 + resist (rnw,nwr)/active 1018000 0.5 resist (pwell)/well 1 resist (poly,fp,rp,pc,pc,nfet,pfet,fet)/active 6000 - resist (pres)/active 6000 + resist (pres)/active 6000 0.5 resist (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c,m2c)/metal1 80 resist (m2,fm2,rm2,m2c,m3c,m3c)/metal2 70 resist (m3,fm3,rm3,m3c,m4c,m4c)/metal3 80 @@ -7416,33 +7408,30 @@ extract #metali -#fets +#devices - fet pfet pdiff,pdc 2 pfet Vdd! nwell 52 181 - fet pfet pdiff,pdc 1 pfet Vdd! nwell 52 181 - - fet nfet ndiff,ndc 2 nfet Gnd! pwell 55 182 - fet nfet ndiff,ndc 1 nfet Gnd! pwell 55 182 + device mosfet pfet pfet pdiff,pdc nwell ERROR 52 181 + device mosfet nfet nfet ndiff,ndc pwell,space/w ERROR 55 182 fetresis pfet linear 12182 fetresis pfet saturation 12182 fetresis nfet linear 3961 fetresis nfet saturation 3961 - fet rnwell nsd,nsc 2 nwellResistor Gnd! nwell,pwell 0 0 - fet rpoly poly,pc 2 polyResistor Gnd! nwell,pwell 0 0 - fet nwr nwsd 2 nwellFig1bResistor Gnd! nwell,pwell 0 0 - fet rndiff ndiff,ndc 2 ndiffResistor Gnd! nwell,pwell 0 0 - fet rpdiff pdiff,pdc 2 pdiffResistor Gnd! nwell,pwell 0 0 + device resistor nwellResistor rnwell *nsd + device resistor polyResistor rpoly *poly + device resistor nwellFig1bResistor nwr nwsd + device resistor ndiffResistor rndiff *ndiff + device resistor pdiffResistor rpdiff *pdiff - fet rmetal1 metal1 2 metal1Resistor Gnd! nwell,pwell 0 0 - fet rmetal2 metal2 2 metal2Resistor Gnd! nwell,pwell 0 0 - fet rmetal3 metal3 2 metal3Resistor Gnd! nwell,pwell 0 0 - fet rmetal4 metal4 2 metal4Resistor Gnd! nwell,pwell 0 0 + device resistor metal1Resistor rmetal1 *metal1 + device resistor metal2Resistor rmetal2 *metal2 + device resistor metal3Resistor rmetal3 *metal3 + device resistor metal4Resistor rmetal4 *metal4 - fet pres poly,pc 2 presResistor Gnd! nwell,pwell 0 0 - fet anres ndiff,ndc 2 anresResistor Gnd! nwell,pwell 0 0 - fet apres pdiff,pdc 2 apresResistor Gnd! nwell,pwell 0 0 + device resistor presResistor pres *poly + device resistor anresResistor anres *ndiff + device resistor apresResistor apres *pdiff end diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 0e81953a..25afd844 100755 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -240,7 +240,7 @@ spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+" #spice stimulus related variables -spice["feasible_period"] = 5 # estimated feasible period in ns +spice["feasible_period"] = 5 # estimated feasible period in ns spice["supply_voltages"] = [4.5, 5.0, 5.5] # Supply voltage corners in [Volts] spice["nom_supply_voltage"] = 5.0 # Nominal supply voltage in [Volts] spice["rise_time"] = 0.05 # rise time in [Nano-seconds]