diff --git a/README.md b/README.md index 07624326..b486823b 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@ https://github.com/mguthaus/OpenRAM/blob/master/OpenRAM_ICCAD_2016_presentation. The OpenRAM compiler has very few dependencies: * ngspice-26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later) * Python 3.5 and higher -* Python numpy -* flask_table -* a setup script for each technology +* Python numpy (pip3 install numpy to install) +* flask_table (pip3 install flask to install) +* a setup script for each technology you want to use * a technology directory for each technology with the base cells If you want to perform DRC and LVS, you will need either: @@ -21,13 +21,13 @@ the compiler source directory. OPENERAM_TECH should point to a root technology directory that contains subdirs of all other technologies. For example, in bash, add to your .bashrc: ``` - export OPENRAM_HOME="$HOME/OpenRAM/compiler" - export OPENRAM_TECH="$HOME/OpenRAM/technology" + export OPENRAM_HOME="$HOME/openram/compiler" + export OPENRAM_TECH="$HOME/openram/technology" ``` For example, in csh/tcsh, add to your .cshrc/.tcshrc: ``` - setenv OPENRAM_HOME "$HOME/OpenRAM/compiler" - setenv OPENRAM_TECH "$HOME/OpenRAM/technology" + setenv OPENRAM_HOME "$HOME/openram/compiler" + setenv OPENRAM_TECH "$HOME/openram/technology" ``` We include the tech files necessary for FreePDK and SCMOS. The SCMOS @@ -49,7 +49,7 @@ We do not distribute the PDK, but you may get it from: If you are using SCMOS, you should install Magic and netgen from: http://opencircuitdesign.com/magic/ http://opencircuitdesign.com/netgen/ -We have included the SCN3ME design rules from QFlow: +We have included the SCN4M design rules from QFlow: http://opencircuitdesign.com/qflow/ # DIRECTORY STRUCTURE @@ -57,17 +57,19 @@ We have included the SCN3ME design rules from QFlow: * compiler - openram compiler itself (pointed to by OPENRAM_HOME) * compiler/base - base data structure modules * compiler/pgates - parameterized cells (e.g. logic gates) + * compiler/bitcells - various bitcell styles * compiler/modules - high-level modules (e.g. decoders, etc.) - * compiler/verify - DRC and LVS verification wrappers + * compiler/verify - DRC and LVS verification wrappers * compiler/characterizer - timing characterization code * compiler/gdsMill - GDSII reader/writer - * compiler/router - detailed router + * compiler/router - router for signals and power supplies * compiler/tests - unit tests * technology - openram technology directory (pointed to by OPENRAM_TECH) * technology/freepdk45 - example configuration library for freepdk45 technology node - * technology/scn3me_subm - example configuration library SCMOS technology node + * technology/scn4m_subm - example configuration library SCMOS technology node + * technology/scn3me_subm - unsupported configuration (not enough metal layers) * technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies -* docs - LaTeX manual (likely outdated) +* docs - LaTeX manual (outdated) * lib - IP library of pregenerated memories @@ -90,8 +92,8 @@ To increase the verbosity of the test, add one (or more) -v options: python tests/00_code_format_check_test.py -v -t freepdk45 ``` To specify a particular technology use "-t " such as -"-t scn3me_subm". The default for a unit test is freepdk45 whereas -the default for openram.py is specified in the configuration file. +"-t freepdk45" or "-t scn4m_subm". The default for a unit test is scn4m_subm. +The default for openram.py is specified in the configuration file. # CREATING CUSTOM TECHNOLOGIES diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 1d4beb11..a2758c56 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -73,21 +73,21 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_name = second_layer def setup_layout_constants(self): - self.contact_width = drc["minwidth_{0}". format(self.via_layer_name)] - contact_to_contact = drc["{0}_to_{0}".format(self.via_layer_name)] + self.contact_width = drc("minwidth_{0}". format(self.via_layer_name)) + contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name)) self.contact_pitch = self.contact_width + contact_to_contact self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch # DRC rules - first_layer_minwidth = drc["minwidth_{0}".format(self.first_layer_name)] - first_layer_minarea = drc["minarea_{0}".format(self.first_layer_name)] - first_layer_enclosure = drc["{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)] - first_layer_extend = drc["{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)] - second_layer_minwidth = drc["minwidth_{0}".format(self.second_layer_name)] - second_layer_minarea = drc["minarea_{0}".format(self.second_layer_name)] - second_layer_enclosure = drc["{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)] - second_layer_extend = drc["{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)] + first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) + first_layer_minarea = drc("minarea_{0}".format(self.first_layer_name)) + first_layer_enclosure = drc("{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)) + first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)) + second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name)) + second_layer_minarea = drc("minarea_{0}".format(self.second_layer_name)) + second_layer_enclosure = drc("{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)) + second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)) self.first_layer_horizontal_enclosure = max((first_layer_minwidth - self.contact_array_width) / 2, first_layer_enclosure) @@ -145,16 +145,16 @@ class contact(hierarchy_design.hierarchy_design): height=self.second_layer_height) def create_implant_well_enclosures(self): - implant_position = self.first_layer_position - [drc["implant_enclosure_active"]]*2 - implant_width = self.first_layer_width + 2*drc["implant_enclosure_active"] - implant_height = self.first_layer_height + 2*drc["implant_enclosure_active"] + implant_position = self.first_layer_position - [drc("implant_enclosure_active")]*2 + implant_width = self.first_layer_width + 2*drc("implant_enclosure_active") + implant_height = self.first_layer_height + 2*drc("implant_enclosure_active") self.add_rect(layer="{}implant".format(self.implant_type), offset=implant_position, width=implant_width, height=implant_height) - well_position = self.first_layer_position - [drc["well_enclosure_active"]]*2 - well_width = self.first_layer_width + 2*drc["well_enclosure_active"] - well_height = self.first_layer_height + 2*drc["well_enclosure_active"] + well_position = self.first_layer_position - [drc("well_enclosure_active")]*2 + well_width = self.first_layer_width + 2*drc("well_enclosure_active") + well_height = self.first_layer_height + 2*drc("well_enclosure_active") self.add_rect(layer="{}well".format(self.well_type), offset=well_position, width=well_width, diff --git a/compiler/base/design.py b/compiler/base/design.py index cbe4f3cf..211133a1 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -29,25 +29,25 @@ class design(hierarchy_design): def setup_drc_constants(self): """ These are some DRC constants used in many places in the compiler.""" from tech import drc - self.well_width = drc["minwidth_well"] - self.poly_width = drc["minwidth_poly"] - self.poly_space = drc["poly_to_poly"] - self.m1_width = drc["minwidth_metal1"] - self.m1_space = drc["metal1_to_metal1"] - self.m2_width = drc["minwidth_metal2"] - self.m2_space = drc["metal2_to_metal2"] - self.m3_width = drc["minwidth_metal3"] - self.m3_space = drc["metal3_to_metal3"] - self.active_width = drc["minwidth_active"] - self.contact_width = drc["minwidth_contact"] + self.well_width = drc("minwidth_well") + self.poly_width = drc("minwidth_poly") + self.poly_space = drc("poly_to_poly") + self.m1_width = drc("minwidth_metal1") + self.m1_space = drc("metal1_to_metal1") + self.m2_width = drc("minwidth_metal2") + self.m2_space = drc("metal2_to_metal2") + self.m3_width = drc("minwidth_metal3") + self.m3_space = drc("metal3_to_metal3") + self.active_width = drc("minwidth_active") + self.contact_width = drc("minwidth_contact") - self.poly_to_active = drc["poly_to_active"] - self.poly_extend_active = drc["poly_extend_active"] - self.poly_to_polycontact = drc["poly_to_polycontact"] - self.contact_to_gate = drc["contact_to_gate"] - self.well_enclose_active = drc["well_enclosure_active"] - self.implant_enclose_active = drc["implant_enclosure_active"] - self.implant_space = drc["implant_to_implant"] + self.poly_to_active = drc("poly_to_active") + self.poly_extend_active = drc("poly_extend_active") + self.poly_to_polycontact = drc("poly_to_polycontact") + self.contact_to_gate = drc("contact_to_gate") + self.well_enclose_active = drc("well_enclosure_active") + self.implant_enclose_active = drc("implant_enclosure_active") + self.implant_space = drc("implant_to_implant") def setup_multiport_constants(self): """ These are contants and lists that aid multiport design """ @@ -83,3 +83,14 @@ class design(hierarchy_design): for inst in self.insts: total_module_power += inst.mod.analytical_power(proc, vdd, temp, load) return total_module_power + + def __str__(self): + """ override print function output """ + pins = ",".join(self.pins) + insts = [" {}".format(x) for x in self.insts] + objs = [" {}".format(x) for x in self.objs] + s = "********** design {0} **********\n".format(self.name) + s += "\n pins ({0})={1}\n".format(len(self.pins), pins) + s += "\n objs ({0})=\n{1}".format(len(self.objs), "\n".join(objs)) + s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts)) + return s diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 7144a2cf..b766b1af 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -6,6 +6,7 @@ from vector import vector import tech import math from globals import OPTS +from utils import round_to_grid class geometry: """ @@ -46,8 +47,8 @@ class geometry: def normalize(self): """ Re-find the LL and UR points after a transform """ (first,second)=self.boundary - ll = vector(min(first[0],second[0]),min(first[1],second[1])) - ur = vector(max(first[0],second[0]),max(first[1],second[1])) + ll = vector(min(first[0],second[0]),min(first[1],second[1])).snap_to_grid() + ur = vector(max(first[0],second[0]),max(first[1],second[1])).snap_to_grid() self.boundary=[ll,ur] def update_boundary(self): @@ -142,8 +143,12 @@ class instance(geometry): self.rotate = rotate self.offset = vector(offset).snap_to_grid() self.mirror = mirror - self.width = mod.width - self.height = mod.height + if OPTS.netlist_only: + self.width = 0 + self.height = 0 + 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) @@ -191,15 +196,15 @@ class instance(geometry): self.mod.gds_write_file(self.gds) # now write an instance of my module/structure new_layout.addInstance(self.gds, - offsetInMicrons=self.offset, - mirror=self.mirror, - rotate=self.rotate) - + offsetInMicrons=self.offset, + mirror=self.mirror, + rotate=self.rotate) + def place(self, offset, mirror="R0", rotate=0): """ This updates the placement of an instance. """ debug.info(3, "placing instance {}".format(self.name)) # Update the placement of an already added instance - self.offset = vector(offset) + self.offset = vector(offset).snap_to_grid() self.mirror = mirror self.rotate = rotate self.update_boundary() @@ -238,7 +243,7 @@ class instance(geometry): def __str__(self): """ override print function output """ - return "inst: " + self.name + " mod=" + self.mod.name + return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")" def __repr__(self): """ override print function output """ @@ -260,13 +265,13 @@ class path(geometry): # supported right now. It might not work in gdsMill. assert(0) - def gds_write_file(self, newLayout): + def gds_write_file(self, new_layout): """Writes the path to GDS""" debug.info(4, "writing path (" + str(self.layerNumber) + "): " + self.coordinates) - newLayout.addPath(layerNumber=self.layerNumber, - purposeNumber=0, - coordinates=self.coordinates, - width=self.path_width) + new_layout.addPath(layerNumber=self.layerNumber, + purposeNumber=0, + coordinates=self.coordinates, + width=self.path_width) def get_blockages(self, layer): """ Fail since we don't support paths yet. """ @@ -301,15 +306,15 @@ class label(geometry): debug.info(4,"creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset)) - def gds_write_file(self, newLayout): + def gds_write_file(self, new_layout): """Writes the text label to GDS""" debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text) - newLayout.addText(text=self.text, - layerNumber=self.layerNumber, - purposeNumber=0, - offsetInMicrons=self.offset, - magnification=self.zoom, - rotate=None) + new_layout.addText(text=self.text, + layerNumber=self.layerNumber, + purposeNumber=0, + offsetInMicrons=self.offset, + magnification=self.zoom, + rotate=None) def get_blockages(self, layer): """ Returns an empty list since text cannot be blockages. """ @@ -321,7 +326,7 @@ class label(geometry): def __repr__(self): """ override print function output """ - return "( label: " + self.text + " @" + str(self.offset) + " layer=" + self.layerNumber + " )" + return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " )" class rectangle(geometry): """Represents a rectangular shape""" @@ -333,8 +338,8 @@ class rectangle(geometry): self.layerNumber = layerNumber self.offset = vector(offset).snap_to_grid() self.size = vector(width, height).snap_to_grid() - self.width = self.size.x - self.height = self.size.y + self.width = round_to_grid(self.size.x) + self.height = round_to_grid(self.size.y) self.compute_boundary(offset,"",0) debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): " @@ -348,16 +353,16 @@ class rectangle(geometry): else: return [] - def gds_write_file(self, newLayout): + def gds_write_file(self, new_layout): """Writes the rectangular shape to GDS""" debug.info(4, "writing rectangle (" + str(self.layerNumber) + "):" + str(self.width) + "x" + str(self.height) + " @ " + str(self.offset)) - newLayout.addBox(layerNumber=self.layerNumber, - purposeNumber=0, - offsetInMicrons=self.offset, - width=self.width, - height=self.height, - center=False) + new_layout.addBox(layerNumber=self.layerNumber, + purposeNumber=0, + offsetInMicrons=self.offset, + width=self.width, + height=self.height, + center=False) def __str__(self): """ override print function output """ diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index b67e910c..32af57c1 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -28,7 +28,7 @@ class layout(lef.lef): self.insts = [] # Holds module/cell layout instances self.objs = [] # Holds all other objects (labels, geometries, etc) self.pin_map = {} # Holds name->pin_layout map for all pins - self.visited = False # Flag for traversing the hierarchy + self.visited = [] # List of modules we have already visited self.is_library_cell = False # Flag for library cells self.gds_read() @@ -134,11 +134,13 @@ class layout(lef.lef): return inst return None - def add_rect(self, layer, offset, width=0, height=0): - """Adds a rectangle on a given layer,offset with width and height""" - if width==0: + def add_rect(self, layer, offset, width=None, height=None): + """ + Adds a rectangle on a given layer,offset with width and height + """ + if not width: width=drc["minwidth_{}".format(layer)] - if height==0: + if not height: height=drc["minwidth_{}".format(layer)] # negative layers indicate "unused" layers in a given technology layer_num = techlayer[layer] @@ -147,11 +149,13 @@ class layout(lef.lef): return self.objs[-1] return None - def add_rect_center(self, layer, offset, width=0, height=0): - """Adds a rectangle on a given layer at the center point with width and height""" - if width==0: + def add_rect_center(self, layer, offset, width=None, height=None): + """ + Adds a rectangle on a given layer at the center point with width and height + """ + if not width: width=drc["minwidth_{}".format(layer)] - if height==0: + if not height: height=drc["minwidth_{}".format(layer)] # negative layers indicate "unused" layers in a given technology layer_num = techlayer[layer] @@ -163,7 +167,9 @@ class layout(lef.lef): def add_segment_center(self, layer, start, end): - """ Add a min-width rectanglular segment using center line on the start to end point """ + """ + Add a min-width rectanglular segment using center line on the start to end point + """ minwidth_layer = drc["minwidth_{}".format(layer)] if start.x!=end.x and start.y!=end.y: debug.error("Nonrectilinear center rect!",-1) @@ -177,7 +183,9 @@ class layout(lef.lef): def get_pin(self, text): - """ Return the pin or list of pins """ + """ + Return the pin or list of pins + """ try: if len(self.pin_map[text])>1: debug.error("Should use a pin iterator since more than one pin {}".format(text),-1) @@ -192,8 +200,13 @@ class layout(lef.lef): def get_pins(self, text): - """ Return a pin list (instead of a single pin) """ - return self.pin_map[text] + """ + Return a pin list (instead of a single pin) + """ + if text in self.pin_map.keys(): + return self.pin_map[text] + else: + return [] def copy_layout_pin(self, instance, pin_name, new_name=""): """ @@ -207,7 +220,9 @@ class layout(lef.lef): self.add_layout_pin(new_name, pin.layer, pin.ll(), pin.width(), pin.height()) def add_layout_pin_segment_center(self, text, layer, start, end): - """ Creates a path like pin with center-line convention """ + """ + Creates a path like pin with center-line convention + """ debug.check(start.x==end.x or start.y==end.y,"Cannot have a non-manhatten layout pin.") @@ -232,9 +247,9 @@ class layout(lef.lef): def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None): """ Creates a path like pin with center-line convention """ - if width==None: + if not width: width=drc["minwidth_{0}".format(layer)] - if height==None: + if not height: height=drc["minwidth_{0}".format(layer)] ll_offset = offset - vector(0.5*width,0.5*height) @@ -243,14 +258,18 @@ class layout(lef.lef): def remove_layout_pin(self, text): - """Delete a labeled pin (or all pins of the same name)""" + """ + Delete a labeled pin (or all pins of the same name) + """ self.pin_map[text]=[] def add_layout_pin(self, text, layer, offset, width=None, height=None): - """Create a labeled pin """ - if width==None: + """ + Create a labeled pin + """ + if not width: width=drc["minwidth_{0}".format(layer)] - if height==None: + if not height: height=drc["minwidth_{0}".format(layer)] new_pin = pin_layout(text, [offset,offset+vector(width,height)], layer) @@ -270,13 +289,14 @@ class layout(lef.lef): return new_pin def add_label_pin(self, text, layer, offset, width=None, height=None): - """Create a labeled pin WITHOUT the pin data structure. This is not an + """ + Create a labeled pin WITHOUT the pin data structure. This is not an actual pin but a named net so that we can add a correspondence point in LVS. """ - if width==None: + if not width: width=drc["minwidth_{0}".format(layer)] - if height==None: + if not height: height=drc["minwidth_{0}".format(layer)] self.add_rect(layer=layer, offset=offset, @@ -313,7 +333,7 @@ class layout(lef.lef): position_list=coordinates, width=width) - def add_route(self, layers, coordinates): + def add_route(self, layers, coordinates, layer_widths): """Connects a routing path on given layer,coordinates,width. The layers are the (horizontal, via, vertical). add_wire assumes preferred direction routing whereas this includes layers in @@ -324,7 +344,8 @@ class layout(lef.lef): # add an instance of our path that breaks down into rectangles and contacts route.route(obj=self, layer_stack=layers, - path=coordinates) + path=coordinates, + layer_widths=layer_widths) def add_wire(self, layers, coordinates): @@ -432,59 +453,66 @@ class layout(lef.lef): # open the gds file if it exists or else create a blank layout if os.path.isfile(self.gds_file): - debug.info(3, "opening %s" % self.gds_file) + debug.info(3, "opening {}".format(self.gds_file)) self.is_library_cell=True self.gds = gdsMill.VlsiLayout(units=GDS["unit"]) reader = gdsMill.Gds2reader(self.gds) reader.loadFromFile(self.gds_file) else: - debug.info(4, "creating structure %s" % self.name) + debug.info(3, "Creating layout structure {}".format(self.name)) self.gds = gdsMill.VlsiLayout(name=self.name, units=GDS["unit"]) def print_gds(self, gds_file=None): """Print the gds file (not the vlsi class) to the terminal """ if gds_file == None: gds_file = self.gds_file - debug.info(4, "Printing %s" % gds_file) + debug.info(4, "Printing {}".format(gds_file)) arrayCellLayout = gdsMill.VlsiLayout(units=GDS["unit"]) reader = gdsMill.Gds2reader(arrayCellLayout, debugToTerminal=1) reader.loadFromFile(gds_file) def clear_visited(self): """ Recursively clear the visited flag """ - if not self.visited: - for i in self.insts: - i.mod.clear_visited() - self.visited = False + self.visited = [] - def gds_write_file(self, newLayout): + def gds_write_file(self, gds_layout): """Recursive GDS write function""" # Visited means that we already prepared self.gds for this subtree - if self.visited: + if self.name in self.visited: return for i in self.insts: - i.gds_write_file(newLayout) + i.gds_write_file(gds_layout) for i in self.objs: - i.gds_write_file(newLayout) + i.gds_write_file(gds_layout) for pin_name in self.pin_map.keys(): for pin in self.pin_map[pin_name]: - pin.gds_write_file(newLayout) - self.visited = True + pin.gds_write_file(gds_layout) + self.visited.append(self.name) def gds_write(self, gds_name): """Write the entire gds of the object to the file.""" - debug.info(3, "Writing to {0}".format(gds_name)) + debug.info(3, "Writing to {}".format(gds_name)) + + # If we already wrote a GDS, we need to reset and traverse it again in + # case we made changes. + if not self.is_library_cell and self.visited: + debug.info(3, "Creating layout structure {}".format(self.name)) + self.gds = gdsMill.VlsiLayout(name=self.name, units=GDS["unit"]) writer = gdsMill.Gds2writer(self.gds) # MRG: 3/2/18 We don't want to clear the visited flag since # this would result in duplicates of all instances being placed in self.gds # which may have been previously processed! - #self.clear_visited() + # MRG: 10/4/18 We need to clear if we make changes and write a second GDS! + self.clear_visited() + # recursively create all the remaining objects self.gds_write_file(self.gds) + # populates the xyTree data structure for gds # self.gds.prepareForWrite() writer.writeToFile(gds_name) + debug.info(3, "Done writing to {}".format(gds_name)) def get_boundary(self): """ Return the lower-left and upper-right coordinates of boundary """ @@ -875,6 +903,22 @@ class layout(lef.lef): width=xmax-xmin, height=ymax-ymin) + + def copy_power_pins(self, inst, name): + """ + This will copy a power pin if it is on M3. If it is on M1, it will add a power via too. + """ + pins=inst.get_pins(name) + for pin in pins: + if pin.layer=="metal3": + self.add_layout_pin(name, pin.layer, pin.ll(), pin.width(), pin.height()) + elif pin.layer=="metal1": + self.add_power_pin(name, pin.center()) + else: + debug.warning("{0} pins of {1} should be on metal3 or metal1 for supply router.".format(name,inst.name)) + + + def add_power_pin(self, name, loc, rotate=90): """ Add a single power pin from M3 down to M1 at the given center location @@ -1008,7 +1052,7 @@ class layout(lef.lef): def pdf_write(self, pdf_name): # NOTE: Currently does not work (Needs further research) #self.pdf_name = self.name + ".pdf" - debug.info(0, "Writing to %s" % pdf_name) + debug.info(0, "Writing to {}".format(pdf_name)) pdf = gdsMill.pdfLayout(self.gds) return diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index a82eba1b..ff87f9a5 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -91,9 +91,9 @@ class spice(verilog.verilog): group of modules are generated.""" if (check and (len(self.insts[-1].mod.pins) != len(args))): - import pprint - modpins_string=pprint.pformat(self.insts[-1].mod.pins) - argpins_string=pprint.pformat(args) + from pprint import pformat + modpins_string=pformat(self.insts[-1].mod.pins) + argpins_string=pformat(args) debug.error("Connections: {}".format(modpins_string)) debug.error("Connections: {}".format(argpins_string)) debug.error("Number of net connections ({0}) does not match last instance ({1})".format(len(self.insts[-1].mod.pins), @@ -101,9 +101,9 @@ class spice(verilog.verilog): self.conns.append(args) if check and (len(self.insts)!=len(self.conns)): - import pprint - insts_string=pprint.pformat(self.insts) - conns_string=pprint.pformat(self.conns) + from pprint import pformat + insts_string=pformat(self.insts) + conns_string=pformat(self.conns) debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, len(self.insts), diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index bb65f771..7565c6ce 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -30,9 +30,26 @@ class pin_layout: return "({} layer={} ll={} ur={})".format(self.name,self.layer,self.rect[0],self.rect[1]) def __repr__(self): - """ override print function output """ - return "({} layer={} ll={} ur={})".format(self.name,self.layer,self.rect[0],self.rect[1]) + """ + override repr function output (don't include + name since pin shapes could have same shape but diff name e.g. blockage vs A) + """ + return "(layer={} ll={} ur={})".format(self.layer,self.rect[0],self.rect[1]) + def __hash__(self): + """ Implement the hash function for sets etc. """ + return hash(repr(self)) + + def __lt__(self, other): + """ Provide a function for ordering items by the ll point """ + (ll, ur) = self.rect + (oll, our) = other.rect + + if ll.x < oll.x and ll.y < oll.y: + return True + + return False + def __eq__(self, other): """ Check if these are the same pins for duplicate checks """ if isinstance(other, self.__class__): @@ -46,7 +63,7 @@ class pin_layout: and return the new rectangle. """ if not spacing: - spacing = drc["{0}_to_{0}".format(self.layer)] + spacing = 0.5*drc("{0}_to_{0}".format(self.layer)) (ll,ur) = self.rect spacing = vector(spacing, spacing) @@ -54,21 +71,39 @@ class pin_layout: newur = ur + spacing return (newll, newur) - - def overlaps(self, other): + + def intersection(self, other): """ Check if a shape overlaps with a rectangle """ (ll,ur) = self.rect (oll,our) = other.rect - # Start assuming no overlaps + + min_x = max(ll.x, oll.x) + max_x = min(ll.x, oll.x) + min_y = max(ll.y, oll.y) + max_y = min(ll.y, oll.y) + + return [vector(min_x,min_y),vector(max_x,max_y)] + + def xoverlaps(self, other): + """ Check if shape has x overlap """ + (ll,ur) = self.rect + (oll,our) = other.rect x_overlaps = False - y_overlaps = False # check if self is within other x range if (ll.x >= oll.x and ll.x <= our.x) or (ur.x >= oll.x and ur.x <= our.x): x_overlaps = True # check if other is within self x range if (oll.x >= ll.x and oll.x <= ur.x) or (our.x >= ll.x and our.x <= ur.x): x_overlaps = True - + + return x_overlaps + + def yoverlaps(self, other): + """ Check if shape has x overlap """ + (ll,ur) = self.rect + (oll,our) = other.rect + y_overlaps = False + # check if self is within other y range if (ll.y >= oll.y and ll.y <= our.y) or (ur.y >= oll.y and ur.y <= our.y): y_overlaps = True @@ -76,7 +111,42 @@ class pin_layout: if (oll.y >= ll.y and oll.y <= ur.y) or (our.y >= ll.y and our.y <= ur.y): y_overlaps = True + return y_overlaps + + def contains(self, other): + """ Check if a shape contains another rectangle """ + # 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): + return False + + if not (oll.x >= ll.x and oll.x <= ur.x): + return False + + return True + + + def overlaps(self, other): + """ Check if a shape overlaps with a rectangle """ + # Can only overlap on the same layer + if self.layer != other.layer: + return False + + x_overlaps = self.xoverlaps(other) + y_overlaps = self.yoverlaps(other) + return x_overlaps and y_overlaps + + def area(self): + """ Return the area. """ + return self.height()*self.width() + def height(self): """ Return height. Abs is for pre-normalized value.""" return abs(self.rect[1].y-self.rect[0].y) diff --git a/compiler/base/route.py b/compiler/base/route.py index beff1a58..0397e383 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -10,15 +10,17 @@ class route(design): """ Object route (used by the router module) Add a route of minimium metal width between a set of points. + The widths are the layer widths of the layer stack. + (Vias are in numer of vias.) The wire must be completely rectilinear and the - z-dimension of the points refers to the layers (plus via) + z-dimension of the points refers to the layers. The points are the center of the wire. This can have non-preferred direction routing. """ unique_route_id = 0 - def __init__(self, obj, layer_stack, path): + def __init__(self, obj, layer_stack, path, layer_widths=[None,1,None]): name = "route_{0}".format(route.unique_route_id) route.unique_route_id += 1 design.__init__(self, name) @@ -26,6 +28,7 @@ class route(design): self.obj = obj self.layer_stack = layer_stack + self.layer_widths = layer_widths self.path = path self.setup_layers() @@ -33,16 +36,16 @@ class route(design): def setup_layers(self): - (horiz_layer, via_layer, vert_layer) = self.layer_stack - self.via_layer_name = via_layer - - self.vert_layer_name = vert_layer - self.vert_layer_width = drc["minwidth_{0}".format(vert_layer)] - - self.horiz_layer_name = horiz_layer - self.horiz_layer_width = drc["minwidth_{0}".format(horiz_layer)] + (self.horiz_layer_name, self.via_layer, self.vert_layer_name) = self.layer_stack + (self.horiz_layer_width, self.num_vias, self.vert_layer_width) = self.layer_widths + + if not self.vert_layer_width: + self.vert_layer_width = drc("minwidth_{0}".format(self.vert_layer_name)) + if not self.horiz_layer_width: + self.horiz_layer_width = drc("minwidth_{0}".format(self.horiz_layer_name)) + # offset this by 1/2 the via size - self.c=contact(self.layer_stack, (1, 1)) + self.c=contact(self.layer_stack, (self.num_vias, self.num_vias)) def create_wires(self): @@ -63,7 +66,8 @@ class route(design): #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) - self.obj.add_via(self.layer_stack,via_offset,rotate=90) + via_size = [self.num_vias]*2 + self.obj.add_via(self.layer_stack,via_offset,size=via_size,rotate=90) elif p0.x != p1.x and p0.y != p1.y: # diagonal! debug.error("Non-changing direction!") else: @@ -79,14 +83,36 @@ class route(design): self.draw_corner_wire(plist[-1][1]) - + def get_layer_width(self, layer_zindex): + """ + Return the layer width + """ + if layer_zindex==0: + return self.horiz_layer_width + elif layer_zindex==1: + return self.vert_layer_width + else: + debug.error("Incorrect layer zindex.",-1) + + def get_layer_name(self, layer_zindex): + """ + Return the layer name + """ + if layer_zindex==0: + return self.horiz_layer_name + elif layer_zindex==1: + return self.vert_layer_name + else: + debug.error("Incorrect layer zindex.",-1) + def draw_wire(self, p0, p1): """ This draws a straight wire with layer_minwidth """ - layer_name = self.layer_stack[2*p0.z] - layer_width = drc["minwidth_{0}".format(layer_name)] + + layer_width = self.get_layer_width(p0.z) + layer_name = self.get_layer_name(p0.z) # always route left to right or bottom to top if p0.z != p1.z: @@ -120,8 +146,8 @@ class route(design): """ This function adds the corner squares since the center line convention only draws to the center of the corner.""" - layer_name = self.layer_stack[2*p0.z] - layer_width = drc["minwidth_{0}".format(layer_name)] + layer_width = self.get_layer_width(p0.z) + layer_name = self.get_layer_name(p0.z) offset = vector(p0.x-0.5*layer_width,p0.y-0.5*layer_width) self.obj.add_rect(layer=layer_name, offset=offset, diff --git a/compiler/base/utils.py b/compiler/base/utils.py index b13f2f7e..28c5f997 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -3,6 +3,7 @@ import gdsMill import tech import math import globals +import debug from vector import vector from pin_layout import pin_layout @@ -65,6 +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)) cell_vlsi = gdsMill.VlsiLayout(units=units) reader = gdsMill.Gds2reader(cell_vlsi) reader.loadFromFile(gds_filename) @@ -72,6 +74,7 @@ def get_gds_size(name, gds_filename, units, layer): cell = {} measure_result = cell_vlsi.getLayoutBorder(layer) if measure_result == None: + debug.info(2,"Layout border failed. Trying to measure size for {}".format(name)) measure_result = cell_vlsi.measureSize(name) # returns width,height return measure_result diff --git a/compiler/base/vector.py b/compiler/base/vector.py index 3d79b90a..6069a1ab 100644 --- a/compiler/base/vector.py +++ b/compiler/base/vector.py @@ -11,24 +11,24 @@ class vector(): concise vector operations, output, and other more complex data structures like lists. """ - def __init__(self, x, y=None): + def __init__(self, x, y=0): """ init function support two init method""" # will take single input as a coordinate - if y==None: - self.x = x[0] - self.y = x[1] + if isinstance(x, (list,tuple,vector)): + self.x = float(x[0]) + self.y = float(x[1]) #will take two inputs as the values of a coordinate else: - self.x = x - self.y = y + self.x = float(x) + self.y = float(y) def __str__(self): """ override print function output """ - return "["+str(self.x)+","+str(self.y)+"]" + return "v["+str(self.x)+","+str(self.y)+"]" def __repr__(self): """ override print function output """ - return "["+str(self.x)+","+str(self.y)+"]" + return "v["+str(self.x)+","+str(self.y)+"]" def __setitem__(self, index, value): """ @@ -36,12 +36,12 @@ class vector(): can set value by vector[index]=value """ if index==0: - self.x=value + self.x=float(value) elif index==1: - self.y=value + self.y=float(value) else: - self.x=value[0] - self.y=value[1] + self.x=float(value[0]) + self.y=float(value[1]) def __getitem__(self, index): """ @@ -84,6 +84,14 @@ class vector(): """ return vector(other[0]- self.x, other[1] - self.y) + def __hash__(self): + """ + Override - function (hash) + Note: This assumes that you DON'T CHANGE THE VECTOR or it will + break things. + """ + return hash((self.x,self.y)) + def snap_to_grid(self): self.x = self.snap_offset_to_grid(self.x) self.y = self.snap_offset_to_grid(self.y) diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 9220c77a..1565d10e 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -33,14 +33,14 @@ class wire(path): self.via_layer_name = via_layer self.vert_layer_name = vert_layer - self.vert_layer_width = drc["minwidth_{0}".format(vert_layer)] + self.vert_layer_width = drc("minwidth_{0}".format(vert_layer)) self.horiz_layer_name = horiz_layer - self.horiz_layer_width = drc["minwidth_{0}".format(horiz_layer)] + self.horiz_layer_width = drc("minwidth_{0}".format(horiz_layer)) via_connect = contact(self.layer_stack, (1, 1)) - self.node_to_node = [drc["minwidth_" + str(self.horiz_layer_name)] + via_connect.width, - drc["minwidth_" + str(self.horiz_layer_name)] + via_connect.height] + self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width, + drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height] # create a 1x1 contact def create_vias(self): diff --git a/compiler/modules/bitcell.py b/compiler/bitcells/bitcell.py similarity index 100% rename from compiler/modules/bitcell.py rename to compiler/bitcells/bitcell.py diff --git a/compiler/bitcells/bitcell_1rw_1r.py b/compiler/bitcells/bitcell_1rw_1r.py new file mode 100644 index 00000000..9cec7d8d --- /dev/null +++ b/compiler/bitcells/bitcell_1rw_1r.py @@ -0,0 +1,98 @@ +import design +import debug +import utils +from tech import GDS,layer + +class bitcell_1rw_1r(design.design): + """ + A single bit cell (6T, 8T, etc.) 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("cell_1rw_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"], layer["boundary"]) + + def __init__(self): + design.design.__init__(self, "cell_1rw_1r") + debug.info(2, "Create bitcell with 1RW and 1R Port") + + self.width = bitcell.width + self.height = bitcell.height + self.pin_map = bitcell.pin_map + + def analytical_delay(self, slew, load=0, swing = 0.5): + # delay of bit cell is not like a driver(from WL) + # so the slew used should be 0 + # it should not be slew dependent? + # because the value is there + # the delay is only over half transsmission gate + from tech import spice + r = spice["min_tx_r"]*3 + c_para = spice["min_tx_drain_c"] + result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = swing) + return result + + + def list_bitcell_pins(self, col, row): + """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ + bitcell_pins = ["bl0[{0}]".format(col), + "br0[{0}]".format(col), + "bl1[{0}]".format(col), + "br1[{0}]".format(col), + "wl0[{0}]".format(row), + "wl1[{0}]".format(row), + "vdd", + "gnd"] + return bitcell_pins + + def list_all_wl_names(self): + """ Creates a list of all wordline pin names """ + row_pins = ["wl0", "wl1"] + return row_pins + + def list_all_bitline_names(self): + """ Creates a list of all bitline pin names (both bl and br) """ + column_pins = ["bl0", "br0", "bl1", "br1"] + return column_pins + + def list_all_bl_names(self): + """ Creates a list of all bl pins names """ + column_pins = ["bl0", "bl1"] + return column_pins + + def list_all_br_names(self): + """ Creates a list of all br pins names """ + column_pins = ["br0", "br1"] + return column_pins + + def list_read_bl_names(self): + """ Creates a list of bl pin names associated with read ports """ + column_pins = ["bl0", "bl1"] + return column_pins + + def list_read_br_names(self): + """ Creates a list of br pin names associated with read ports """ + column_pins = ["br0", "br1"] + return column_pins + + def list_write_bl_names(self): + """ Creates a list of bl pin names associated with write ports """ + column_pins = ["bl0"] + return column_pins + + def list_write_br_names(self): + """ Creates a list of br pin names asscociated with write ports""" + column_pins = ["br0"] + return column_pins + + def analytical_power(self, proc, vdd, temp, load): + """Bitcell power in nW. Only characterizes leakage.""" + from tech import spice + leakage = spice["bitcell_leakage"] + dynamic = 0 #temporary + total_power = self.return_power(dynamic, leakage) + return total_power + diff --git a/compiler/pgates/pbitcell.py b/compiler/bitcells/pbitcell.py similarity index 84% rename from compiler/pgates/pbitcell.py rename to compiler/bitcells/pbitcell.py index 5816e350..908df0e0 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -11,52 +11,51 @@ class pbitcell(design.design): This module implements a parametrically sized multi-port bitcell, with a variable number of read/write, write, and read ports """ - + def __init__(self, replica_bitcell=False): - + self.num_rw_ports = OPTS.num_rw_ports self.num_w_ports = OPTS.num_w_ports self.num_r_ports = OPTS.num_r_ports self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports - + self.replica_bitcell = replica_bitcell - + if self.replica_bitcell: name = "replica_pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports) else: name = "pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports) - # This is not a pgate because pgates depend on the bitcell height! + design.design.__init__(self, name) debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, self.num_w_ports, - self.num_r_ports)) + self.num_r_ports)) self.create_netlist() - # We must always create the bitcell layout because - # some transistor sizes in the other netlists depend on it + # We must always create the bitcell layout because some transistor sizes in the other netlists depend on it self.create_layout() - + def create_netlist(self): self.add_pins() self.add_modules() self.create_storage() - + if(self.num_rw_ports > 0): self.create_readwrite_ports() if(self.num_w_ports > 0): self.create_write_ports() if(self.num_r_ports > 0): self.create_read_ports() - + def create_layout(self): self.calculate_spacing() self.calculate_postions() - + self.place_storage() self.route_storage() - + self.route_rails() - + if(self.num_rw_ports > 0): self.place_readwrite_ports() self.route_readwrite_access() @@ -67,23 +66,24 @@ class pbitcell(design.design): self.place_read_ports() self.route_read_access() self.extend_well() - + self.route_wordlines() self.route_bitlines() self.route_supply() - + if self.replica_bitcell: self.route_rbc_short() - - # in netlist_only mode, calling offset_all_coordinates will not be possible + + # in netlist_only mode, calling offset_all_coordinates or translate_all will not be possible # this function is not needed to calculate the dimensions of pbitcell in netlist_only mode though if not OPTS.netlist_only: self.offset_all_coordinates() gnd_overlap = vector(0, 0.5*contact.well.width) self.translate_all(gnd_overlap) self.DRC_LVS() - + def add_pins(self): + """ add pins and set names for bitlines and wordlines """ self.rw_bl_names = [] self.rw_br_names = [] self.w_bl_names = [] @@ -94,7 +94,7 @@ class pbitcell(design.design): self.w_wl_names = [] self.r_wl_names = [] port = 0 - + for k in range(self.num_rw_ports): self.add_pin("bl{}".format(port)) self.add_pin("br{}".format(port)) @@ -113,7 +113,7 @@ class pbitcell(design.design): self.r_bl_names.append("bl{}".format(port)) self.r_br_names.append("br{}".format(port)) port += 1 - + port = 0 for k in range(self.num_rw_ports): self.add_pin("wl{}".format(port)) @@ -127,19 +127,18 @@ class pbitcell(design.design): self.add_pin("wl{}".format(port)) self.r_wl_names.append("wl{}".format(port)) port += 1 - + self.add_pin("vdd") self.add_pin("gnd") - + + # if this is a replica bitcell, replace the instances of Q_bar with vdd if self.replica_bitcell: self.Q_bar = "vdd" else: self.Q_bar = "Q_bar" - + def add_modules(self): - """ - Determine size of transistors and add ptx modules - """ + """ Determine size of transistors and add ptx modules """ # if there are any read/write ports, then the inverter nmos is sized based the number of read/write ports if(self.num_rw_ports > 0): inverter_nmos_width = self.num_rw_ports*parameter["6T_inv_nmos_size"] @@ -147,7 +146,7 @@ class pbitcell(design.design): readwrite_nmos_width = parameter["6T_access_size"] write_nmos_width = parameter["6T_access_size"] read_nmos_width = 2*parameter["6T_inv_pmos_size"] - + # if there are no read/write ports, then the inverter nmos is statically sized for the dual port case else: inverter_nmos_width = 2*parameter["6T_inv_pmos_size"] @@ -155,7 +154,7 @@ class pbitcell(design.design): readwrite_nmos_width = parameter["6T_access_size"] write_nmos_width = parameter["6T_access_size"] read_nmos_width = 2*parameter["6T_inv_pmos_size"] - + # create ptx for inverter transistors self.inverter_nmos = ptx(width=inverter_nmos_width, tx_type="nmos") @@ -164,46 +163,46 @@ class pbitcell(design.design): self.inverter_pmos = ptx(width=inverter_pmos_width, tx_type="pmos") self.add_mod(self.inverter_pmos) - + # create ptx for readwrite transitors self.readwrite_nmos = ptx(width=readwrite_nmos_width, tx_type="nmos") self.add_mod(self.readwrite_nmos) - + # create ptx for write transitors self.write_nmos = ptx(width=write_nmos_width, tx_type="nmos") self.add_mod(self.write_nmos) - + # create ptx for read transistors self.read_nmos = ptx(width=read_nmos_width, tx_type="nmos") self.add_mod(self.read_nmos) - - def calculate_spacing(self): + + def calculate_spacing(self): """ Calculate transistor spacings """ # calculate metal contact extensions over transistor active readwrite_nmos_contact_extension = 0.5*(self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height) write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height) read_nmos_contact_extension = 0.5*(self.read_nmos.active_contact.height - self.read_nmos.active_height) max_contact_extension = max(readwrite_nmos_contact_extension, write_nmos_contact_extension, read_nmos_contact_extension) - + # y-offset for the access transistor's gate contact self.gate_contact_yoffset = max_contact_extension + self.m2_space + 0.5*max(contact.poly.height, contact.m1m2.height) - + # y-position of access transistors self.port_ypos = self.m1_space + 0.5*contact.m1m2.height + self.gate_contact_yoffset - + # y-position of inverter nmos self.inverter_nmos_ypos = self.port_ypos - + # spacing between ports - self.bitline_offset = -self.active_width + 0.5*contact.m1m2.height + self.m2_space + self.m2_width + self.bitline_offset = -self.active_width + 0.5*contact.m1m2.height + self.m2_space + self.m2_width self.port_spacing = self.bitline_offset + self.m2_space - + # spacing between cross coupled inverters self.inverter_to_inverter_spacing = contact.poly.height + self.m1_space - + # calculations related to inverter connections inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) inverter_nmos_contact_extension = 0.5*(self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height) @@ -217,125 +216,117 @@ class pbitcell(design.design): + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + self.poly_to_polycontact \ + 1.5*contact.poly.width - + # spacing between wordlines (and gnd) self.rowline_spacing = self.m1_space + contact.m1m2.width - + self.rowline_offset = -0.5*self.m1_width + # spacing for vdd implant_constraint = max(inverter_pmos_contact_extension, 0) + 2*self.implant_enclose_active + 0.5*(contact.well.width - self.m1_width) metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width - + # read port dimensions width_reduction = self.read_nmos.active_width - self.read_nmos.get_pin("D").cx() self.read_port_width = 2*self.read_nmos.active_width - 2*width_reduction - + def calculate_postions(self): - """ - Calculate positions that describe the edges and dimensions of the cell - """ + """ Calculate positions that describe the edges and dimensions of the cell """ self.botmost_ypos = -0.5*self.m1_width - self.total_ports*self.rowline_spacing self.topmost_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset - + self.leftmost_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width \ - self.num_rw_ports*(self.readwrite_nmos.active_width + self.port_spacing) \ - self.num_w_ports*(self.write_nmos.active_width + self.port_spacing) \ - self.num_r_ports*(self.read_port_width + self.port_spacing) \ - self.bitline_offset - 0.5*self.m2_width - + self.width = -2*self.leftmost_xpos self.height = self.topmost_ypos - self.botmost_ypos - - self.y_center = 0.5*(self.topmost_ypos + self.botmost_ypos) + + self.center_ypos = 0.5*(self.topmost_ypos + self.botmost_ypos) def create_storage(self): """ Creates the crossed coupled inverters that act as storage for the bitcell. The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". """ - # create active for nmos self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", mod=self.inverter_nmos) self.connect_inst(["Q", self.Q_bar, "gnd", "gnd"]) - + self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", mod=self.inverter_nmos) self.connect_inst(["gnd", "Q", self.Q_bar, "gnd"]) - + # create active for pmos self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", mod=self.inverter_pmos) self.connect_inst(["Q", self.Q_bar, "vdd", "vdd"]) - + self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", mod=self.inverter_pmos) self.connect_inst(["vdd", "Q", self.Q_bar, "vdd"]) - + def place_storage(self): - """ - Places the transistors for the crossed coupled inverters in the bitcell - """ - + """ Places the transistors for the crossed coupled inverters in the bitcell """ # calculate transistor offsets left_inverter_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width right_inverter_xpos = 0.5*self.inverter_to_inverter_spacing inverter_pmos_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - + # create active for nmos self.inverter_nmos_left.place([left_inverter_xpos, self.inverter_nmos_ypos]) self.inverter_nmos_right.place([right_inverter_xpos, self.inverter_nmos_ypos]) - + # create active for pmos self.inverter_pmos_left.place([left_inverter_xpos, inverter_pmos_ypos]) self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos]) - + + # update furthest left and right transistor edges (this will propagate to further transistor offset calculations) self.left_building_edge = left_inverter_xpos self.right_building_edge = right_inverter_xpos + self.inverter_nmos.active_width - + def route_storage(self): - """ - Routes inputs and outputs of inverters to cross couple them - """ + """ Routes inputs and outputs of inverters to cross couple them """ # connect input (gate) of inverters self.add_path("poly", [self.inverter_nmos_left.get_pin("G").uc(), self.inverter_pmos_left.get_pin("G").bc()]) - self.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()]) - + self.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()]) + # connect output (drain/source) of inverters self.add_path("metal1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], width=contact.well.second_layer_width) self.add_path("metal1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.well.second_layer_width) - + # add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar) contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.height, self.cross_couple_upper_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=contact_offset_left, rotate=90) - + contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.height, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=contact_offset_right, rotate=90) - + # connect contacts to gate poly (cross couple connections) gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, contact_offset_left.y) self.add_path("poly", [contact_offset_left, gate_offset_right]) - + gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x, contact_offset_right.y) self.add_path("poly", [contact_offset_right, gate_offset_left]) - + def route_rails(self): - """ - Adds gnd and vdd rails and connects them to the inverters - """ + """ Adds gnd and vdd rails and connects them to the inverters """ # Add rails for vdd and gnd - gnd_ypos = -0.5*self.m1_width - self.total_ports*self.rowline_spacing + gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing self.gnd_position = vector(0, gnd_ypos) self.gnd = self.add_layout_pin_rect_center(text="gnd", layer="metal1", offset=self.gnd_position, width=self.width, height=self.m1_width) - + vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset self.vdd_position = vector(0, vdd_ypos) self.vdd = self.add_layout_pin_rect_center(text="vdd", @@ -348,69 +339,61 @@ class pbitcell(design.design): """ Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell. A read or write is enabled by setting a Read-Write-Wordline (RWWL) high, subsequently turning on the transistor. - The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q). + The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q). In a write operation, driving RWBL high or low sets the value of the cell. In a read operation, RWBL is precharged, then is either remains high or is discharged depending on the value of the cell. - This is a differential design, so each write port has a mirrored port that connects RWBL_bar to Q_bar. + This is a differential design, so each write port has a mirrored port that connects RWBR to Q_bar. """ - - # define write transistor variables as empty arrays based on the number of write ports - self.readwrite_nmos_left = [None] * self.num_rw_ports + # define read/write transistor variables as empty arrays based on the number of read/write ports + self.readwrite_nmos_left = [None] * self.num_rw_ports self.readwrite_nmos_right = [None] * self.num_rw_ports - + # iterate over the number of read/write ports for k in range(0,self.num_rw_ports): # add read/write transistors self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), mod=self.readwrite_nmos) self.connect_inst([self.rw_bl_names[k], self.rw_wl_names[k], "Q", "gnd"]) - + self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k), mod=self.readwrite_nmos) self.connect_inst([self.Q_bar, self.rw_wl_names[k], self.rw_br_names[k], "gnd"]) def place_readwrite_ports(self): - """ - Places read/write ports in the bit cell. - """ - - # Define variables relevant to write transistors + """ Places read/write ports in the bit cell """ + # define read/write transistor variables as empty arrays based on the number of read/write ports self.rwwl_positions = [None] * self.num_rw_ports self.rwbl_positions = [None] * self.num_rw_ports self.rwbr_positions = [None] * self.num_rw_ports - + # iterate over the number of read/write ports for k in range(0,self.num_rw_ports): - # Add transistors - # calculate read/write transistor offsets + # calculate read/write transistor offsets left_readwrite_transistor_xpos = self.left_building_edge \ - (k+1)*self.port_spacing \ - (k+1)*self.readwrite_nmos.active_width - + right_readwrite_transistor_xpos = self.right_building_edge \ + (k+1)*self.port_spacing \ + k*self.readwrite_nmos.active_width - - # add read/write transistors + + # place read/write transistors self.readwrite_nmos_left[k].place(offset=[left_readwrite_transistor_xpos, self.port_ypos]) - + self.readwrite_nmos_right[k].place(offset=[right_readwrite_transistor_xpos, self.port_ypos]) - - # Add RWWL lines - # calculate RWWL position - rwwl_ypos = -0.5*self.m1_width - k*self.rowline_spacing - self.rwwl_positions[k] = vector(0, rwwl_ypos) - + # add pin for RWWL + rwwl_ypos = self.rowline_offset - k*self.rowline_spacing + self.rwwl_positions[k] = vector(0, rwwl_ypos) self.add_layout_pin_rect_center(text=self.rw_wl_names[k], layer="metal1", offset=self.rwwl_positions[k], width=self.width, height=self.m1_width) - - # add pins for RWBL and RWBL_bar, overlaid on source contacts + + # add pins for RWBL and RWBR rwbl_xpos = left_readwrite_transistor_xpos - self.bitline_offset + self.m2_width - self.rwbl_positions[k] = vector(rwbl_xpos, self.y_center) + self.rwbl_positions[k] = vector(rwbl_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.rw_bl_names[k], layer="metal2", offset=self.rwbl_positions[k], @@ -418,89 +401,76 @@ class pbitcell(design.design): height=self.height) rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width + self.bitline_offset - self.m2_width - self.rwbr_positions[k] = vector(rwbr_xpos, self.y_center) + self.rwbr_positions[k] = vector(rwbr_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.rw_br_names[k], layer="metal2", offset=self.rwbr_positions[k], width=drc["minwidth_metal2"], height=self.height) - - # update furthest left and right transistor edges + + # update furthest left and right transistor edges self.left_building_edge = left_readwrite_transistor_xpos self.right_building_edge = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width - + def create_write_ports(self): """ Creates write ports in the bit cell. A differential pair of transistors can write only. A write is enabled by setting a Write-Rowline (WWL) high, subsequently turning on the transistor. - The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q). + The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q). In a write operation, driving WBL high or low sets the value of the cell. - This is a differential design, so each write port has a mirrored port that connects WBL_bar to Q_bar. + This is a differential design, so each write port has a mirrored port that connects WBR to Q_bar. """ - - # Define variables relevant to write transistors - # define offset correction due to rotation of the ptx module - write_rotation_correct = self.write_nmos.active_height - # define write transistor variables as empty arrays based on the number of write ports - self.write_nmos_left = [None] * self.num_w_ports + self.write_nmos_left = [None] * self.num_w_ports self.write_nmos_right = [None] * self.num_w_ports - + # iterate over the number of write ports for k in range(0,self.num_w_ports): # add write transistors self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k), mod=self.write_nmos) self.connect_inst([self.w_bl_names[k], self.w_wl_names[k], "Q", "gnd"]) - + self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k), mod=self.write_nmos) self.connect_inst([self.Q_bar, self.w_wl_names[k], self.w_br_names[k], "gnd"]) def place_write_ports(self): - """ - Places write ports in the bit cell. - """ - # Define variables relevant to write transistors + """ Places write ports in the bit cell """ + # define write transistor variables as empty arrays based on the number of write ports self.wwl_positions = [None] * self.num_w_ports self.wbl_positions = [None] * self.num_w_ports - self.wbr_positions = [None] * self.num_w_ports + self.wbr_positions = [None] * self.num_w_ports - # define offset correction due to rotation of the ptx module - write_rotation_correct = self.write_nmos.active_height - # iterate over the number of write ports for k in range(0,self.num_w_ports): # Add transistors - # calculate write transistor offsets + # calculate write transistor offsets left_write_transistor_xpos = self.left_building_edge \ - (k+1)*self.port_spacing \ - (k+1)*self.write_nmos.active_width - + right_write_transistor_xpos = self.right_building_edge \ + (k+1)*self.port_spacing \ + k*self.write_nmos.active_width - + # add write transistors self.write_nmos_left[k].place(offset=[left_write_transistor_xpos, self.port_ypos]) - + self.write_nmos_right[k].place(offset=[right_write_transistor_xpos, self.port_ypos]) - - # Add WWL lines - # calculate WWL position - wwl_ypos = rwwl_ypos = -0.5*self.m1_width - self.num_rw_ports*self.rowline_spacing - k*self.rowline_spacing - self.wwl_positions[k] = vector(0, wwl_ypos) - + # add pin for WWL + wwl_ypos = rwwl_ypos = self.rowline_offset - self.num_rw_ports*self.rowline_spacing - k*self.rowline_spacing + self.wwl_positions[k] = vector(0, wwl_ypos) self.add_layout_pin_rect_center(text=self.w_wl_names[k], layer="metal1", offset=self.wwl_positions[k], width=self.width, height=self.m1_width) - - # add pins for WBL and WBL_bar, overlaid on source contacts + + # add pins for WBL and WBR wbl_xpos = left_write_transistor_xpos - self.bitline_offset + self.m2_width - self.wbl_positions[k] = vector(wbl_xpos, self.y_center) + self.wbl_positions[k] = vector(wbl_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.w_bl_names[k], layer="metal2", offset=self.wbl_positions[k], @@ -508,20 +478,20 @@ class pbitcell(design.design): height=self.height) wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset - self.m2_width - self.wbr_positions[k] = vector(wbr_xpos, self.y_center) + self.wbr_positions[k] = vector(wbr_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.w_br_names[k], layer="metal2", offset=self.wbr_positions[k], width=drc["minwidth_metal2"], height=self.height) - - # update furthest left and right transistor edges + + # update furthest left and right transistor edges self.left_building_edge = left_write_transistor_xpos self.right_building_edge = right_write_transistor_xpos + self.write_nmos.active_width - + def create_read_ports(self): """ - Creates read ports in the bit cell. A differential pair of ports can read only. + Creates read ports in the bit cell. A differential pair of ports can read only. Two transistors function as a read port, denoted as the "read transistor" and the "read-access transistor". The read transistor is connected to RWL (gate), RBL (drain), and the read-access transistor (source). The read-access transistor is connected to Q_bar (gate), gnd (source), and the read transistor (drain). @@ -530,85 +500,76 @@ class pbitcell(design.design): is turned on, creating a connection between RBL and gnd. RBL subsequently discharges allowing for a differential read using sense amps. This is a differential design, so each read port has a mirrored port that connects RBL_bar to Q. """ - + # define read transistor variables as empty arrays based on the number of read ports - self.read_nmos_left = [None] * self.num_r_ports + self.read_nmos_left = [None] * self.num_r_ports self.read_nmos_right = [None] * self.num_r_ports - self.read_access_nmos_left = [None] * self.num_r_ports + self.read_access_nmos_left = [None] * self.num_r_ports self.read_access_nmos_right = [None] * self.num_r_ports - + # iterate over the number of read ports for k in range(0,self.num_r_ports): # add read-access transistors self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), mod=self.read_nmos) self.connect_inst(["RA_to_R_left{}".format(k), self.Q_bar, "gnd", "gnd"]) - + self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right{}".format(k), mod=self.read_nmos) self.connect_inst(["gnd", "Q", "RA_to_R_right{}".format(k), "gnd"]) - + # add read transistors self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k), mod=self.read_nmos) self.connect_inst([self.r_bl_names[k], self.r_wl_names[k], "RA_to_R_left{}".format(k), "gnd"]) - + self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k), mod=self.read_nmos) self.connect_inst(["RA_to_R_right{}".format(k), self.r_wl_names[k], self.r_br_names[k], "gnd"]) - + def place_read_ports(self): - """ - Places the read ports in the bit cell. - """ - # Define variables relevant to read transistors + """ Places the read ports in the bit cell """ + # define read transistor variables as empty arrays based on the number of read ports self.rwl_positions = [None] * self.num_r_ports self.rbl_positions = [None] * self.num_r_ports self.rbr_positions = [None] * self.num_r_ports - - # define offset correction due to rotation of the ptx module - read_rotation_correct = self.read_nmos.active_height - + # calculate offset to overlap the drain of the read-access transistor with the source of the read transistor overlap_offset = self.read_nmos.get_pin("D").cx() - self.read_nmos.get_pin("S").cx() - + # iterate over the number of read ports for k in range(0,self.num_r_ports): - # Add transistors # calculate transistor offsets left_read_transistor_xpos = self.left_building_edge \ - (k+1)*self.port_spacing \ - (k+1)*self.read_port_width - + right_read_transistor_xpos = self.right_building_edge \ + (k+1)*self.port_spacing \ + k*self.read_port_width - + # add read-access transistors self.read_access_nmos_left[k].place(offset=[left_read_transistor_xpos+overlap_offset, self.port_ypos]) - + self.read_access_nmos_right[k].place(offset=[right_read_transistor_xpos, self.port_ypos]) - + # add read transistors self.read_nmos_left[k].place(offset=[left_read_transistor_xpos, self.port_ypos]) - + self.read_nmos_right[k].place(offset=[right_read_transistor_xpos+overlap_offset, self.port_ypos]) - - # Add RWL lines - # calculate RWL position - rwl_ypos = rwwl_ypos = -0.5*self.m1_width - self.num_rw_ports*self.rowline_spacing - self.num_w_ports*self.rowline_spacing - k*self.rowline_spacing - self.rwl_positions[k] = vector(0, rwl_ypos) - + # add pin for RWL + rwl_ypos = rwwl_ypos = self.rowline_offset - self.num_rw_ports*self.rowline_spacing - self.num_w_ports*self.rowline_spacing - k*self.rowline_spacing + self.rwl_positions[k] = vector(0, rwl_ypos) self.add_layout_pin_rect_center(text=self.r_wl_names[k], layer="metal1", offset=self.rwl_positions[k], width=self.width, height=self.m1_width) - - # add pins for RBL and RBL_bar, overlaid on drain contacts + + # add pins for RBL and RBR rbl_xpos = left_read_transistor_xpos - self.bitline_offset + self.m2_width - self.rbl_positions[k] = vector(rbl_xpos, self.y_center) + self.rbl_positions[k] = vector(rbl_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.r_bl_names[k], layer="metal2", offset=self.rbl_positions[k], @@ -616,17 +577,15 @@ class pbitcell(design.design): height=self.height) rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset - self.m2_width - self.rbr_positions[k] = vector(rbr_xpos, self.y_center) + self.rbr_positions[k] = vector(rbr_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.r_br_names[k], layer="metal2", offset=self.rbr_positions[k], width=drc["minwidth_metal2"], height=self.height) - + def route_wordlines(self): - """ - Routes gate of transistors to their respective wordlines - """ + """ Routes gate of transistors to their respective wordlines """ port_transistors = [] for k in range(self.num_rw_ports): port_transistors.append(self.readwrite_nmos_left[k]) @@ -637,7 +596,7 @@ class pbitcell(design.design): for k in range(self.num_r_ports): port_transistors.append(self.read_nmos_left[k]) port_transistors.append(self.read_nmos_right[k]) - + wl_positions = [] for k in range(self.num_rw_ports): wl_positions.append(self.rwwl_positions[k]) @@ -648,37 +607,35 @@ class pbitcell(design.design): for k in range(self.num_r_ports): wl_positions.append(self.rwl_positions[k]) wl_positions.append(self.rwl_positions[k]) - - + for k in range(2*self.total_ports): gate_offset = port_transistors[k].get_pin("G").bc() port_contact_offset = gate_offset + vector(0, -self.gate_contact_yoffset + self.poly_extend_active) wl_contact_offset = vector(gate_offset.x, wl_positions[k].y) - + + # first transistor on either side of the cross coupled inverters does not need to route to wordline on metal2 if (k == 0) or (k == 1): self.add_contact_center(layers=("poly", "contact", "metal1"), offset=port_contact_offset) - - self.add_path("poly", [gate_offset, port_contact_offset]) + + self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("metal1", [port_contact_offset, wl_contact_offset]) - + else: self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=port_contact_offset) + offset=port_contact_offset) self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=port_contact_offset) - + self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=wl_contact_offset, rotate=90) - - self.add_path("poly", [gate_offset, port_contact_offset]) + + self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("metal2", [port_contact_offset, wl_contact_offset]) - + def route_bitlines(self): - """ - Routes read/write transistors to their respective bitlines - """ + """ Routes read/write transistors to their respective bitlines """ left_port_transistors = [] right_port_transistors = [] for k in range(self.num_rw_ports): @@ -690,7 +647,7 @@ class pbitcell(design.design): for k in range(self.num_r_ports): left_port_transistors.append(self.read_nmos_left[k]) right_port_transistors.append(self.read_nmos_right[k]) - + bl_positions = [] br_positions = [] for k in range(self.num_rw_ports): @@ -702,164 +659,155 @@ class pbitcell(design.design): for k in range(self.num_r_ports): bl_positions.append(self.rbl_positions[k]) br_positions.append(self.rbr_positions[k]) - + for k in range(self.total_ports): port_contact_offest = left_port_transistors[k].get_pin("S").center() bl_offset = vector(bl_positions[k].x, port_contact_offest.y) - + self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=port_contact_offest) - + self.add_path("metal2", [port_contact_offest, bl_offset], width=contact.m1m2.height) - + for k in range(self.total_ports): port_contact_offest = right_port_transistors[k].get_pin("D").center() br_offset = vector(br_positions[k].x, port_contact_offest.y) - + self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=port_contact_offest) - + self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height) - + def route_supply(self): - # route inverter nmos and read-access transistors to gnd + """ Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """ + # route inverter nmos and read-access nmos to gnd nmos_contact_positions = [] nmos_contact_positions.append(self.inverter_nmos_left.get_pin("S").center()) nmos_contact_positions.append(self.inverter_nmos_right.get_pin("D").center()) for k in range(self.num_r_ports): nmos_contact_positions.append(self.read_access_nmos_left[k].get_pin("D").center()) nmos_contact_positions.append(self.read_access_nmos_right[k].get_pin("S").center()) - + for position in nmos_contact_positions: self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=position) - + supply_offset = vector(position.x, self.gnd_position.y) self.add_contact_center(layers=("metal1", "via1", "metal2"), offset=supply_offset, rotate=90) - + self.add_path("metal2", [position, supply_offset]) - + # route inverter pmos to vdd vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x, self.vdd_position.y) self.add_path("metal1", [self.inverter_pmos_left.get_pin("S").uc(), vdd_pos_left]) - + vdd_pos_right = vector(self.inverter_nmos_right.get_pin("D").uc().x, self.vdd_position.y) self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) - + def route_readwrite_access(self): - """ - Routes read/write transistors to the storage component of the bitcell - """ + """ Routes read/write transistors to the storage component of the bitcell """ for k in range(self.num_rw_ports): mid = vector(self.readwrite_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [mid, Q_pos]) - + mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) self.add_path("metal1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [mid, Q_bar_pos]) - + def route_write_access(self): - """ - Routes read/write transistors to the storage component of the bitcell - """ + """ Routes read/write transistors to the storage component of the bitcell """ for k in range(self.num_w_ports): mid = vector(self.write_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [mid, Q_pos]) - + mid = vector(self.write_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) self.add_path("metal1", [self.write_nmos_right[k].get_pin("S").uc(), mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [mid, Q_bar_pos]) - + def route_read_access(self): - """ - Routes read access transistors to the storage component of the bitcell - """ + """ Routes read access transistors to the storage component of the bitcell """ + # add poly to metal1 contacts for gates of the inverters left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_upper_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=left_storage_contact, rotate=90) - + right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_upper_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=right_storage_contact, rotate=90) - + inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos) self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) - + inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_upper_ypos) self.add_path("poly", [right_storage_contact, inverter_gate_offset_right]) - + + # add poly to metal1 contacts for gates of read-access transistors + # route from read-access contacts to inverter contacts on metal1 for k in range(self.num_r_ports): port_contact_offset = self.read_access_nmos_left[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=port_contact_offset) - + self.add_path("poly", [self.read_access_nmos_left[k].get_pin("G").uc(), port_contact_offset]) - + mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) self.add_path("metal1", [port_contact_offset, mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [mid, left_storage_contact]) - + port_contact_offset = self.read_access_nmos_right[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=port_contact_offset) - + self.add_path("poly", [self.read_access_nmos_right[k].get_pin("G").uc(), port_contact_offset]) - + mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) self.add_path("metal1", [port_contact_offset, mid+vector(0,0.5*self.m1_width)], width=contact.poly.second_layer_width) self.add_path("metal1", [mid, right_storage_contact]) - + def extend_well(self): """ - Connects wells between ptx modules to avoid drc spacing issues. - Since the pwell of the read ports rise higher than the nwell of the inverters, - the well connections must be done piecewise to avoid pwell and nwell overlap. - """ - + Connects wells between ptx modules and places well contacts""" + # extend pwell to encompass entire nmos region of the cell up to the height of the tallest nmos transistor max_nmos_well_height = max(self.inverter_nmos.cell_well_height, - self.readwrite_nmos.cell_well_height, - self.write_nmos.cell_well_height, - self.read_nmos.cell_well_height) - + self.readwrite_nmos.cell_well_height, + self.write_nmos.cell_well_height, + self.read_nmos.cell_well_height) well_height = max_nmos_well_height + self.port_ypos - self.well_enclose_active - self.gnd_position.y - - # extend pwell to encompass entire nmos region of the cell up to the height of the inverter nmos well offset = vector(self.leftmost_xpos, self.botmost_ypos) self.add_rect(layer="pwell", offset=offset, width=self.width, - height=well_height) - + height=well_height) + # extend nwell to encompass inverter_pmos # calculate offset of the left pmos well inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) - drc["well_enclosure_active"] inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - drc["well_enclosure_active"] - + # calculate width of the two combined nwells # calculate height to encompass nimplant connected to vdd well_width = 2*(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) + 2*drc["well_enclosure_active"] well_height = self.vdd_position.y - inverter_well_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"] - + offset = [inverter_well_xpos,inverter_well_ypos] self.add_rect(layer="nwell", offset=offset, width=well_width, height=well_height) - - - # add well contacts + + # add well contacts # connect pimplants to gnd offset = vector(0, self.gnd_position.y) self.add_contact_center(layers=("active", "contact", "metal1"), @@ -867,15 +815,15 @@ class pbitcell(design.design): rotate=90, implant_type="p", well_type="p") - + # connect nimplants to vdd offset = vector(0, self.vdd_position.y) self.add_contact_center(layers=("active", "contact", "metal1"), offset=offset, rotate=90, implant_type="n", - well_type="n") - + well_type="n") + def list_bitcell_pins(self, col, row): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ bitcell_pins = [] @@ -887,12 +835,12 @@ class pbitcell(design.design): bitcell_pins.append("vdd") bitcell_pins.append("gnd") return bitcell_pins - + def list_all_wl_names(self): """ Creates a list of all wordline pin names """ wordline_names = self.rw_wl_names + self.w_wl_names + self.r_wl_names return wordline_names - + def list_all_bitline_names(self): """ Creates a list of all bitline pin names (both bl and br) """ bitline_pins = [] @@ -900,42 +848,39 @@ class pbitcell(design.design): bitline_pins.append("bl{0}".format(port)) bitline_pins.append("br{0}".format(port)) return bitline_pins - + def list_all_bl_names(self): """ Creates a list of all bl pins names """ - bl_pins = self.rw_bl_names + self.w_bl_names + self.r_bl_names + bl_pins = self.rw_bl_names + self.w_bl_names + self.r_bl_names return bl_pins - + def list_all_br_names(self): """ Creates a list of all br pins names """ - br_pins = self.rw_br_names + self.w_br_names + self.r_br_names + br_pins = self.rw_br_names + self.w_br_names + self.r_br_names return br_pins - + def list_read_bl_names(self): """ Creates a list of bl pin names associated with read ports """ - bl_pins = self.rw_bl_names + self.r_bl_names + bl_pins = self.rw_bl_names + self.r_bl_names return bl_pins - + def list_read_br_names(self): """ Creates a list of br pin names associated with read ports """ - br_pins = self.rw_br_names + self.r_br_names + br_pins = self.rw_br_names + self.r_br_names return br_pins - + def list_write_bl_names(self): """ Creates a list of bl pin names associated with write ports """ - bl_pins = self.rw_bl_names + self.w_bl_names + bl_pins = self.rw_bl_names + self.w_bl_names return bl_pins - + def list_write_br_names(self): """ Creates a list of br pin names asscociated with write ports""" - br_pins = self.rw_br_names + self.w_br_names + br_pins = self.rw_br_names + self.w_br_names return br_pins - + def route_rbc_short(self): """ route the short from Q_bar to gnd necessary for the replica bitcell """ Q_bar_pos = self.inverter_pmos_right.get_pin("S").center() vdd_pos = self.inverter_pmos_right.get_pin("D").center() - #vdd_pos = vector(Q_bar_pos.x, self.vdd_position.y) - self.add_path("metal1", [Q_bar_pos, vdd_pos]) - \ No newline at end of file diff --git a/compiler/modules/replica_bitcell.py b/compiler/bitcells/replica_bitcell.py similarity index 100% rename from compiler/modules/replica_bitcell.py rename to compiler/bitcells/replica_bitcell.py diff --git a/compiler/modules/replica_pbitcell.py b/compiler/bitcells/replica_pbitcell.py similarity index 100% rename from compiler/modules/replica_pbitcell.py rename to compiler/bitcells/replica_pbitcell.py diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index ea99c51c..4f32beb3 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -6,6 +6,7 @@ from .lib import * from .delay import * from .setup_hold import * from .functional import * +from .worst_case import * from .simulation import * @@ -17,7 +18,7 @@ if not OPTS.analytical_delay: if OPTS.spice_name != "": OPTS.spice_exe=find_exe(OPTS.spice_name) - if OPTS.spice_exe=="": + if OPTS.spice_exe=="" or OPTS.spice_exe==None: debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_name),1) else: (OPTS.spice_name,OPTS.spice_exe) = get_tool("spice",["xa", "hspice", "ngspice", "ngspice.exe"]) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 0d5b347b..d699dc06 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -7,8 +7,9 @@ from .trim_spice import * from .charutils import * import utils from globals import OPTS +from .simulation import simulation -class delay(): +class delay(simulation): """Functions to measure the delay and power of an SRAM at a given address and data bit. @@ -26,27 +27,14 @@ class delay(): """ def __init__(self, sram, spfile, corner): - self.sram = sram - self.name = sram.name - self.word_size = self.sram.word_size - self.addr_size = self.sram.addr_size - self.num_cols = self.sram.num_cols - self.num_rows = self.sram.num_rows - self.num_banks = self.sram.num_banks - self.sp_file = spfile - - self.total_ports = self.sram.total_ports - self.total_write = self.sram.total_write - self.total_read = self.sram.total_read - self.read_index = self.sram.read_index - self.write_index = self.sram.write_index - self.port_id = self.sram.port_id + simulation.__init__(self, sram, spfile, corner) # These are the member variables for a simulation + self.targ_read_ports = [] + self.targ_write_ports = [] self.period = 0 self.set_load_slew(0,0) self.set_corner(corner) - self.create_port_names() self.create_signal_names() #Create global measure names. Should maybe be an input at some point. @@ -66,34 +54,6 @@ class delay(): #This is TODO once multiport control has been finalized. #self.control_name = "CSB" - def create_port_names(self): - """Generates the port names to be used in characterization and sets default simulation target ports""" - self.write_ports = [] - self.read_ports = [] - self.total_port_num = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports - - #save a member variable to avoid accessing global. readwrite ports have different control signals. - self.readwrite_port_num = OPTS.num_rw_ports - - #Generate the port names. readwrite ports are required to be added first for this to work. - for readwrite_port_num in range(OPTS.num_rw_ports): - self.read_ports.append(readwrite_port_num) - self.write_ports.append(readwrite_port_num) - #This placement is intentional. It makes indexing input data easier. See self.data_values - for write_port_num in range(OPTS.num_rw_ports, OPTS.num_rw_ports+OPTS.num_w_ports): - self.write_ports.append(write_port_num) - for read_port_num in range(OPTS.num_rw_ports+OPTS.num_w_ports, OPTS.num_rw_ports+OPTS.num_w_ports+OPTS.num_r_ports): - self.read_ports.append(read_port_num) - - #Set the default target ports for simulation. Default is all the ports. - self.targ_read_ports = self.read_ports - self.targ_write_ports = self.write_ports - - def set_corner(self,corner): - """ Set the corner values """ - self.corner = corner - (self.process, self.vdd_voltage, self.temperature) = corner - def set_load_slew(self,load,slew): """ Set the load and slew """ self.load = load @@ -113,9 +73,9 @@ class delay(): debug.error("Given probe_data is not an integer to specify a data bit",1) #Adding port options here which the characterizer cannot handle. Some may be added later like ROM - if len(self.read_ports) == 0: + if len(self.read_index) == 0: debug.error("Characterizer does not currently support SRAMs without read ports.",1) - if len(self.write_ports) == 0: + if len(self.write_index) == 0: debug.error("Characterizer does not currently support SRAMs without write ports.",1) def write_generic_stimulus(self): @@ -129,12 +89,12 @@ class delay(): self.sf.write("\n* Instantiation of the SRAM\n") self.stim.inst_sram(sram=self.sram, port_signal_names=(self.addr_name,self.din_name,self.dout_name), - port_info=(self.total_port_num,self.write_ports,self.read_ports), + port_info=(self.total_ports,self.write_index,self.read_index), abits=self.addr_size, dbits=self.word_size, sram_name=self.name) self.sf.write("\n* SRAM output loads\n") - for port in self.read_ports: + for port in self.read_index: for i in range(self.word_size): self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port,i,self.dout_name,self.load)) @@ -172,7 +132,7 @@ class delay(): self.gen_control() self.sf.write("\n* Generation of Port clock signal\n") - for port in range(self.total_port_num): + for port in range(self.total_ports): self.stim.gen_pulse(sig_name="CLK{0}".format(port), v1=0, v2=self.vdd_voltage, @@ -195,9 +155,6 @@ class delay(): """ self.check_arguments() - # obtains list of time-points for each rising clk edge - #self.create_test_cycles() - # creates and opens stimulus file for writing temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) self.sf = open(temp_stim, "w") @@ -214,24 +171,24 @@ class delay(): # generate data and addr signals self.sf.write("\n* Generation of data and address signals\n") - for write_port in self.write_ports: + for write_port in self.write_index: for i in range(self.word_size): self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i), v_val=0) - for port in range(self.total_port_num): + for port in range(self.total_ports): for i in range(self.addr_size): self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name,port, i), v_val=0) # generate control signals self.sf.write("\n* Generation of control signals\n") - for port in range(self.total_port_num): + for port in range(self.total_ports): self.stim.gen_constant(sig_name="CSB{0}".format(port), v_val=self.vdd_voltage) - if port in self.write_ports and port in self.read_ports: + if port in self.write_index and port in self.read_index: self.stim.gen_constant(sig_name="WEB{0}".format(port), v_val=self.vdd_voltage) self.sf.write("\n* Generation of global clock signal\n") - for port in range(self.total_port_num): + for port in range(self.total_ports): self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0) self.write_power_measures() @@ -257,10 +214,10 @@ class delay(): trig_name = trig_clk_name if 'lh' in delay_name: targ_dir="RISE" - trig_td = targ_td = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]] + trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read1"]] else: targ_dir="FALL" - trig_td = targ_td = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]] + trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]] elif 'slew' in delay_name: trig_name = targ_name @@ -268,12 +225,12 @@ class delay(): trig_val = trig_slew_low targ_val = targ_slew_high targ_dir = trig_dir = "RISE" - trig_td = targ_td = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]] + trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read1"]] else: trig_val = targ_slew_high targ_val = trig_slew_low targ_dir = trig_dir = "FALL" - trig_td = targ_td = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]] + trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]] else: debug.error(1, "Measure command {0} not recognized".format(delay_name)) return (meas_name,trig_name,targ_name,trig_val,targ_val,trig_dir,targ_dir,trig_td,targ_td) @@ -294,11 +251,11 @@ class delay(): #Different naming schemes are used for the measure cycle dict and measurement names. #TODO: make them the same so they can be indexed the same. if '1' in pname: - t_initial = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]+1] + t_initial = self.cycle_times[self.measure_cycles[port]["read1"]] + t_final = self.cycle_times[self.measure_cycles[port]["read1"]+1] elif '0' in pname: - t_initial = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]+1] + t_initial = self.cycle_times[self.measure_cycles[port]["read0"]] + t_final = self.cycle_times[self.measure_cycles[port]["read0"]+1] self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port), t_initial=t_initial, t_final=t_final) @@ -311,11 +268,11 @@ class delay(): for pname in self.power_meas_names: if "write" not in pname: continue - t_initial = self.cycle_times[self.measure_cycles["write0_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["write0_{0}".format(port)]+1] + t_initial = self.cycle_times[self.measure_cycles[port]["write0"]] + t_final = self.cycle_times[self.measure_cycles[port]["write0"]+1] if '1' in pname: - t_initial = self.cycle_times[self.measure_cycles["write1_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["write1_{0}".format(port)]+1] + t_initial = self.cycle_times[self.measure_cycles[port]["write1"]] + t_final = self.cycle_times[self.measure_cycles[port]["write1"]+1] self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port), t_initial=t_initial, @@ -360,10 +317,9 @@ class delay(): double the period until we find a valid period to use as a starting point. """ - debug.check(port in self.read_ports, "Characterizer requires a read port to determine a period.") + debug.check(port in self.read_index, "Characterizer requires a read port to determine a period.") feasible_period = float(tech.spice["feasible_period"]) - #feasible_period = float(2.5)#What happens if feasible starting point is wrong? time_out = 9 while True: time_out -= 1 @@ -406,19 +362,18 @@ class delay(): Loops through all read ports determining the feasible period and collecting delay information from each port. """ - feasible_delays = [{} for i in range(self.total_port_num)] - self.period = float(tech.spice["feasible_period"]) + feasible_delays = [{} for i in range(self.total_ports)] #Get initial feasible delays from first port - feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[0]) + feasible_delays[self.read_index[0]] = self.find_feasible_period_one_port(self.read_index[0]) previous_period = self.period #Loops through all the ports checks if the feasible period works. Everything restarts it if does not. #Write ports do not produce delays which is why they are not included here. i = 1 - while i < len(self.read_ports): - port = self.read_ports[i] + while i < len(self.read_index): + port = self.read_index[i] #Only extract port values from the specified port, not the entire results. feasible_delays[port].update(self.find_feasible_period_one_port(port)) #Function sets the period. Restart the entire process if period changes to collect accurate delays @@ -461,7 +416,7 @@ class delay(): #Sanity Check debug.check(self.period > 0, "Target simulation period non-positive") - result = [{} for i in range(self.total_port_num)] + result = [{} for i in range(self.total_ports)] # Checking from not data_value to data_value self.write_delay_stimulus() @@ -563,7 +518,7 @@ class delay(): #Find the minimum period for all ports. Start at one port and perform binary search then use that delay as a starting position. #For testing purposes, only checks read ports. - for port in self.read_ports: + for port in self.read_index: target_period = self.find_min_period_one_port(feasible_delays, port, lb_period, ub_period, target_period) #The min period of one port becomes the new lower bound. Reset the upper_bound. lb_period = target_period @@ -728,8 +683,8 @@ class delay(): """Simulate all specified output loads and input slews pairs of all ports""" measure_data = self.get_empty_measure_data_dict() #Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways. - self.targ_read_ports = self.read_ports - self.targ_write_ports = self.write_ports + self.targ_read_ports = self.read_index + self.targ_write_ports = self.write_index for slew in slews: for load in loads: self.set_load_slew(load,slew) @@ -738,7 +693,7 @@ class delay(): debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load)) debug.info(1, "Simulation Passed: Port {0} slew={1} load={2}".format("All", self.slew,self.load)) #The results has a dict for every port but dicts can be empty (e.g. ports were not targeted). - for port in range(self.total_port_num): + for port in range(self.total_ports): for mname,value in delay_results[port].items(): if "power" in mname: # Subtract partial array leakage and add full array leakage for the power measures @@ -746,119 +701,8 @@ class delay(): else: measure_data[port][mname].append(value) return measure_data - - def add_data(self, data, port): - """ Add the array of data values """ - debug.check(len(data)==self.word_size, "Invalid data word size.") - debug.check(port < len(self.data_values), "Port number cannot index data values.") - index = 0 - for c in data: - if c=="0": - self.data_values[port][index].append(0) - elif c=="1": - self.data_values[port][index].append(1) - else: - debug.error("Non-binary data string",1) - index += 1 - - def add_address(self, address, port): - """ Add the array of address values """ - debug.check(len(address)==self.addr_size, "Invalid address size.") - index = 0 - for c in address: - if c=="0": - self.addr_values[port][index].append(0) - elif c=="1": - self.addr_values[port][index].append(1) - else: - debug.error("Non-binary address string",1) - index += 1 - def add_noop_one_port(self, address, data, port): - """ Add the control values for a noop to a single port. """ - #This is to be used as a helper function for the other add functions. Cycle and comments are omitted. - self.add_control_one_port(port, "noop") - if port in self.write_ports: - self.add_data(data,port) - self.add_address(address, port) - - def add_noop_all_ports(self, comment, address, data): - """ Add the control values for a noop to all ports. """ - self.add_comment("All", comment) - self.cycle_times.append(self.t_current) - self.t_current += self.period - - for port in range(self.total_port_num): - self.add_noop_one_port(address, data, port) - - - def add_read(self, comment, address, data, port): - """ Add the control values for a read cycle. """ - debug.check(port in self.read_ports, "Cannot add read cycle to a write port.") - self.add_comment(port, comment) - self.cycle_times.append(self.t_current) - self.t_current += self.period - self.add_control_one_port(port, "read") - - #If the port is also a readwrite then add data. - if port in self.write_ports: - self.add_data(data,port) - self.add_address(address, port) - - #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port - noop_data = "0"*self.word_size - #Add noops to all other ports. - for unselected_port in range(self.total_port_num): - if unselected_port != port: - self.add_noop_one_port(address, noop_data, unselected_port) - def add_write(self, comment, address, data, port): - """ Add the control values for a write cycle. """ - debug.check(port in self.write_ports, "Cannot add read cycle to a read port.") - self.add_comment(port, comment) - self.cycle_times.append(self.t_current) - self.t_current += self.period - - self.add_control_one_port(port, "write") - self.add_data(data,port) - self.add_address(address,port) - - #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port - noop_data = "0"*self.word_size - #Add noops to all other ports. - for unselected_port in range(self.total_port_num): - if unselected_port != port: - self.add_noop_one_port(address, noop_data, unselected_port) - - def add_control_one_port(self, port, op): - """Appends control signals for operation to a given port""" - #Determine values to write to port - web_val = 1 - csb_val = 1 - if op == "read": - csb_val = 0 - elif op == "write": - csb_val = 0 - web_val = 0 - elif op != "noop": - debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port,op),1) - - #Append the values depending on the type of port - self.csb_values[port].append(csb_val) - #If port is in both lists, add rw control signal. Condition indicates its a RW port. - if port in self.write_ports and port in self.read_ports: - self.web_values[port].append(web_val) - - def add_comment(self, port, comment): - """Add comment to list to be printed in stimulus file""" - #Clean up time before appending. Make spacing dynamic as well. - time = "{0:.2f} ns:".format(self.t_current) - time_spacing = len(time)+6 - self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times), - port, - time, - time_spacing, - comment)) def gen_test_cycles_one_port(self, read_port, write_port): """Intended but not implemented: Returns a list of key time-points [ns] of the waveform (each rising edge) of the cycles to do a timing evaluation of a single port. Current: Values overwritten for multiple calls""" @@ -886,8 +730,7 @@ class delay(): self.add_write("W data 0 address 11..11 to write value", self.probe_address,data_zeros,write_port) - self.measure_cycles["write0_{0}".format(write_port)] = len(self.cycle_times)-1 - #self.write0_cycle=len(self.cycle_times)-1 # Remember for power measure + self.measure_cycles[write_port]["write0"] = len(self.cycle_times)-1 # This also ensures we will have a H->L transition on the next read self.add_read("R data 1 address 00..00 to set DOUT caps", @@ -895,18 +738,14 @@ class delay(): self.add_read("R data 0 address 11..11 to check W0 worked", self.probe_address,data_zeros,read_port) - self.measure_cycles["read0_{0}".format(read_port)] = len(self.cycle_times)-1 - #self.read0_cycle=len(self.cycle_times)-1 # Remember for power measure + self.measure_cycles[read_port]["read0"] = len(self.cycle_times)-1 self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)", inverse_address,data_zeros) - #Does not seem like is is used anywhere commenting out for now. - #self.idle_cycle=len(self.cycle_times)-1 # Remember for power measure self.add_write("W data 1 address 11..11 to write value", self.probe_address,data_ones,write_port) - self.measure_cycles["write1_{0}".format(write_port)] = len(self.cycle_times)-1 - #self.write1_cycle=len(self.cycle_times)-1 # Remember for power measure + self.measure_cycles[write_port]["write1"] = len(self.cycle_times)-1 self.add_write("W data 0 address 00..00 to clear DIN caps", inverse_address,data_zeros,write_port) @@ -917,19 +756,22 @@ class delay(): self.add_read("R data 1 address 11..11 to check W1 worked", self.probe_address,data_zeros,read_port) - self.measure_cycles["read1_{0}".format(read_port)] = len(self.cycle_times)-1 - #self.read1_cycle=len(self.cycle_times)-1 # Remember for power measure + self.measure_cycles[read_port]["read1"] = len(self.cycle_times)-1 self.add_noop_all_ports("Idle cycle (if read takes >1 cycle))", self.probe_address,data_zeros) def get_available_port(self,get_read_port): """Returns the first accessible read or write port. """ - if get_read_port and len(self.read_ports) > 0: - return self.read_ports[0] - elif not get_read_port and len(self.write_ports) > 0: - return self.write_ports[0] + if get_read_port and len(self.read_index) > 0: + return self.read_index[0] + elif not get_read_port and len(self.write_index) > 0: + return self.write_index[0] return None + + def set_stimulus_variables(self): + simulation.set_stimulus_variables(self) + self.measure_cycles = [{} for port in range(self.total_ports)] def create_test_cycles(self): """Returns a list of key time-points [ns] of the waveform (each rising edge) @@ -937,35 +779,16 @@ class delay(): and does not need a rising edge.""" #Using this requires setting at least one port to target for simulation. if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0: - debug.error("No ports selected for characterization.",1) - - # Start at time 0 - self.t_current = 0 - - # Cycle times (positive edge) with comment - self.cycle_comments = [] - self.cycle_times = [] - self.measure_cycles = {} - - # Control signals for ports. These are not the final signals and will likely be changed later. - #web is the enable for write ports. Dicts used for simplicity as ports are not necessarily incremental. - self.web_values = {port:[] for port in self.write_ports} - #csb acts as an enable for the read ports. - self.csb_values = {port:[] for port in range(self.total_port_num)} - - # Address and data values for each address/data bit. A 3d list of size #ports x bits x cycles. - self.data_values=[[[] for bit in range(self.word_size)] for port in range(len(self.write_ports))] - self.addr_values=[[[] for bit in range(self.addr_size)] for port in range(self.total_port_num)] - + debug.error("No port selected for characterization.",1) + self.set_stimulus_variables() + #Get any available read/write port in case only a single write or read ports is being characterized. cur_read_port = self.get_available_port(get_read_port=True) cur_write_port = self.get_available_port(get_read_port=False) - - #These checks should be superceded by check_arguments which should have been called earlier, so this is a double check. debug.check(cur_read_port != None, "Characterizer requires at least 1 read port") debug.check(cur_write_port != None, "Characterizer requires at least 1 write port") - #Characterizing the remaining target ports. Not the final design. + #Create test cycles for specified target ports. write_pos = 0 read_pos = 0 while True: @@ -997,7 +820,7 @@ class delay(): for slew in slews: for load in loads: self.set_load_slew(load,slew) - bank_delay = sram.analytical_delay(self.slew,self.load) + bank_delay = sram.analytical_delay(self.vdd_voltage, self.slew,self.load) # Convert from ps to ns delay_lh.append(bank_delay.delay/1e3) delay_hl.append(bank_delay.delay/1e3) @@ -1026,7 +849,7 @@ class delay(): def gen_data(self): """ Generates the PWL data inputs for a simulation timing test. """ - for write_port in self.write_ports: + for write_port in self.write_index: for i in range(self.word_size): sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05) @@ -1036,16 +859,16 @@ class delay(): Generates the address inputs for a simulation timing test. This alternates between all 1's and all 0's for the address. """ - for port in range(self.total_port_num): + for port in range(self.total_ports): for i in range(self.addr_size): sig_name = "{0}{1}_{2}".format(self.addr_name,port,i) self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05) def gen_control(self): """ Generates the control signals """ - for port in range(self.total_port_num): + for port in range(self.total_ports): self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) - if port in self.read_ports and port in self.write_ports: + if port in self.read_index and port in self.write_index: self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) @@ -1053,5 +876,5 @@ class delay(): """Make a dict of lists for each type of delay and power measurement to append results to""" measure_names = self.delay_meas_names + self.power_meas_names #Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. - measure_data = [{mname:[] for mname in measure_names} for i in range(self.total_port_num)] + measure_data = [{mname:[] for mname in measure_names} for i in range(self.total_ports)] return measure_data diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 75df4b69..d6579ab5 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -52,18 +52,18 @@ class functional(simulation): rw_ops = ["noop", "write", "read"] w_ops = ["noop", "write"] r_ops = ["noop", "read"] - rw_read_data = "0"*self.word_size + rw_read_din_data = "0"*self.word_size check = 0 # First cycle idle - self.add_noop_all_ports("Idle at time {0}n".format(self.t_current), - "0"*self.addr_size, "0"*self.word_size) + comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, 0, self.t_current) + self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size) # Write at least once addr = self.gen_addr() word = self.gen_data() - self.add_write("Writing {0} to address {1} (from port {2}) at time {3}n".format(word, addr, 0, self.t_current), - addr, word, 0) + comment = self.gen_cycle_comment("write", word, addr, 0, self.t_current) + self.add_write(comment, addr, word, 0) self.stored_words[addr] = word # Read at least once. For multiport, it is important that one read cycle uses all RW and R port to read from the same address simultaniously. @@ -72,8 +72,8 @@ class functional(simulation): if self.port_id[port] == "w": self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) else: - self.add_read_one_port("Reading {0} from address {1} (from port {2}) at time {3}n".format(word, addr, port, self.t_current), - addr, rw_read_data, port) + comment = self.gen_cycle_comment("read", word, addr, port, self.t_current) + self.add_read_one_port(comment, addr, rw_read_din_data, port) self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check]) check += 1 self.cycle_times.append(self.t_current) @@ -101,8 +101,8 @@ class functional(simulation): if addr in w_addrs: self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) else: - self.add_write_one_port("Writing {0} to address {1} (from port {2}) at time {3}n".format(word, addr, port, self.t_current), - addr, word, port) + comment = self.gen_cycle_comment("write", word, addr, port, self.t_current) + self.add_write_one_port(comment, addr, word, port) self.stored_words[addr] = word w_addrs.append(addr) else: @@ -111,8 +111,8 @@ class functional(simulation): if addr in w_addrs: self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) else: - self.add_read_one_port("Reading {0} from address {1} (from port {2}) at time {3}n".format(word, addr, port, self.t_current), - addr, rw_read_data, port) + comment = self.gen_cycle_comment("read", word, addr, port, self.t_current) + self.add_read_one_port(comment, addr, rw_read_din_data, port) self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check]) check += 1 @@ -120,8 +120,8 @@ class functional(simulation): self.t_current += self.period # Last cycle idle needed to correctly measure the value on the second to last clock edge - self.add_noop_all_ports("Idle at time {0}n".format(self.t_current), - "0"*self.addr_size, "0"*self.word_size) + comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, 0, self.t_current) + self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size) def read_stim_results(self): # Extrat DOUT values from spice timing.lis @@ -129,17 +129,17 @@ class functional(simulation): sp_read_value = "" for bit in range(self.word_size): value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check)) - if value > 0.9 * self.vdd_voltage: + if value > 0.88 * self.vdd_voltage: sp_read_value = "1" + sp_read_value - elif value < 0.1 * self.vdd_voltage: + elif value < 0.12 * self.vdd_voltage: sp_read_value = "0" + sp_read_value else: error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port, bit, value, eo_period, - 0.1*self.vdd_voltage, - 0.9*self.vdd_voltage) + 0.12*self.vdd_voltage, + 0.88*self.vdd_voltage) return (0, error) self.read_check.append([sp_read_value, dout_port, eo_period, check]) @@ -148,10 +148,11 @@ class functional(simulation): def check_stim_results(self): for i in range(len(self.write_check)): if self.write_check[i][0] != self.read_check[i][0]: - error = "FAILED: {0} value {1} does not match written value {2} read at time {3}n".format(self.read_check[i][1], - self.read_check[i][0], - self.write_check[i][0], - self.read_check[i][2]) + error = "FAILED: {0} value {1} does not match written value {2} read during cycle {3} at time {4}n".format(self.read_check[i][1], + self.read_check[i][0], + self.write_check[i][0], + int((self.read_check[i][2]-self.period)/self.period), + self.read_check[i][2]) return(0, error) return(1, "SUCCESS") @@ -224,6 +225,11 @@ class functional(simulation): sig_name="{0}{1}_{2} ".format(self.dout_name, self.read_index[port], bit) self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(self.read_index[port], bit, sig_name, self.load)) + # Write debug comments to stim file + self.sf.write("\n\n * Sequence of operations\n") + for comment in self.fn_cycle_comments: + self.sf.write("*{}\n".format(comment)) + # Generate data input bits self.sf.write("\n* Generation of data and address signals\n") for port in range(self.total_write): diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 436ee301..7ae25b73 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -17,7 +17,8 @@ class lib: self.sram = sram self.sp_file = sp_file self.use_model = use_model - self.gen_port_names() #copy and paste from delay.py, names are not final will likely be changed later. + #self.gen_port_names() #copy and paste from delay.py, names are not final will likely be changed later. + self.set_port_indices() self.prepare_tables() @@ -25,7 +26,10 @@ class lib: self.characterize_corners() - + def set_port_indices(self): + self.total_port_num = self.sram.total_ports + self.read_ports = self.sram.read_index + self.write_ports = self.sram.write_index def gen_port_names(self): """Generates the port names to be written to the lib file""" @@ -108,21 +112,21 @@ class lib: self.write_header() - #Loop over all readwrite ports. This is debugging. Will change later. + #Loop over all ports. for port in range(self.total_port_num): #set the read and write port as inputs. self.write_data_bus(port) self.write_addr_bus(port) self.write_control_pins(port) #need to split this into sram and port control signals - - self.write_clk_timing_power() + self.write_clk_timing_power(port) self.write_footer() def write_footer(self): """ Write the footer """ - self.lib.write("}\n") + self.lib.write(" }\n") #Closing brace for the cell + self.lib.write("}\n") #Closing brace for the library def write_header(self): """ Write the header information """ @@ -151,7 +155,7 @@ class lib: self.lib.write(" dont_touch : true;\n") self.lib.write(" area : {};\n\n".format(self.sram.width * self.sram.height)) - #Build string of all control signals. This is subject to change once control signals finalized. + #Build string of all control signals. control_str = 'CSb0' #assume at least 1 port for i in range(1, self.total_port_num): control_str += ' & CSb{0}'.format(i) @@ -296,12 +300,12 @@ class lib: self.lib.write(" }\n\n") - def write_FF_setuphold(self): + def write_FF_setuphold(self, port): """ Adds Setup and Hold timing results""" self.lib.write(" timing(){ \n") self.lib.write(" timing_type : setup_rising; \n") - self.lib.write(" related_pin : \"clk\"; \n") + self.lib.write(" related_pin : \"clk{0}\"; \n".format(port)) self.lib.write(" rise_constraint(CONSTRAINT_TABLE) {\n") rounded_values = list(map(round_time,self.times["setup_times_LH"])) self.write_values(rounded_values,len(self.slews)," ") @@ -313,7 +317,7 @@ class lib: self.lib.write(" }\n") self.lib.write(" timing(){ \n") self.lib.write(" timing_type : hold_rising; \n") - self.lib.write(" related_pin : \"clk\"; \n") + self.lib.write(" related_pin : \"clk{0}\"; \n".format(port)) self.lib.write(" rise_constraint(CONSTRAINT_TABLE) {\n") rounded_values = list(map(round_time,self.times["hold_times_LH"])) self.write_values(rounded_values,len(self.slews)," ") @@ -339,10 +343,9 @@ class lib: self.lib.write(" pin(DOUT{1}[{0}:0]){{\n".format(self.sram.word_size - 1, read_port)) - self.write_FF_setuphold() self.lib.write(" timing(){ \n") self.lib.write(" timing_sense : non_unate; \n") - self.lib.write(" related_pin : \"clk\"; \n") + self.lib.write(" related_pin : \"clk{0}\"; \n".format(read_port)) self.lib.write(" timing_type : rising_edge; \n") self.lib.write(" cell_rise(CELL_TABLE) {\n") self.write_values(self.char_port_results[read_port]["delay_lh"],len(self.loads)," ") @@ -361,7 +364,7 @@ class lib: self.lib.write(" }\n\n") # bus def write_data_bus_input(self, write_port): - """ Adds data bus timing results.""" + """ Adds DIN data bus timing results.""" self.lib.write(" bus(DIN{0}){{\n".format(write_port)) self.lib.write(" bus_type : DATA; \n") @@ -370,9 +373,12 @@ class lib: self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) self.lib.write(" memory_write(){ \n") self.lib.write(" address : ADDR{0}; \n".format(write_port)) - self.lib.write(" clocked_on : clk; \n") - self.lib.write(" }\n") - self.lib.write(" }\n") + self.lib.write(" clocked_on : clk{0}; \n".format(write_port)) + self.lib.write(" }\n") + self.lib.write(" pin(DIN{1}[{0}:0]){{\n".format(self.sram.word_size - 1, write_port)) + self.write_FF_setuphold(write_port) + self.lib.write(" }\n") # pin + self.lib.write(" }\n") #bus def write_data_bus(self, port): """ Adds data bus timing results.""" @@ -392,7 +398,7 @@ class lib: self.lib.write(" pin(ADDR{1}[{0}:0])".format(self.sram.addr_size - 1, port)) self.lib.write("{\n") - self.write_FF_setuphold() + self.write_FF_setuphold(port) self.lib.write(" }\n") self.lib.write(" }\n\n") @@ -409,28 +415,25 @@ class lib: self.lib.write("{\n") self.lib.write(" direction : input; \n") self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) - self.write_FF_setuphold() + self.write_FF_setuphold(port) self.lib.write(" }\n\n") - def write_clk_timing_power(self): + def write_clk_timing_power(self, port): """ Adds clk pin timing results.""" - self.lib.write(" pin(clk){\n") + self.lib.write(" pin(clk{0}){{\n".format(port)) self.lib.write(" clock : true;\n") self.lib.write(" direction : input; \n") # FIXME: This depends on the clock buffer size in the control logic self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) - #Add power values for the ports. lib generated with this is not syntactically correct. TODO once - #top level is done. - for port in range(self.total_port_num): - self.add_clk_control_power(port) + self.add_clk_control_power(port) min_pulse_width = round_time(self.char_sram_results["min_period"])/2.0 min_period = round_time(self.char_sram_results["min_period"]) self.lib.write(" timing(){ \n") self.lib.write(" timing_type :\"min_pulse_width\"; \n") - self.lib.write(" related_pin : clk; \n") + self.lib.write(" related_pin : clk{0}; \n".format(port)) self.lib.write(" rise_constraint(scalar) {\n") self.lib.write(" values(\"{0}\"); \n".format(min_pulse_width)) self.lib.write(" }\n") @@ -440,7 +443,7 @@ class lib: self.lib.write(" }\n") self.lib.write(" timing(){ \n") self.lib.write(" timing_type :\"minimum_period\"; \n") - self.lib.write(" related_pin : clk; \n") + self.lib.write(" related_pin : clk{0}; \n".format(port)) self.lib.write(" rise_constraint(scalar) {\n") self.lib.write(" values(\"{0}\"); \n".format(min_period)) self.lib.write(" }\n") @@ -448,8 +451,7 @@ class lib: self.lib.write(" values(\"{0}\"); \n".format(min_period)) self.lib.write(" }\n") self.lib.write(" }\n") - self.lib.write(" }\n") - self.lib.write(" }\n") + self.lib.write(" }\n\n") def add_clk_control_power(self, port): """Writes powers under the clock pin group for a specified port""" @@ -461,7 +463,7 @@ class lib: web_name = " & !WEb{0}".format(port) avg_write_power = np.mean(self.char_port_results[port]["write1_power"] + self.char_port_results[port]["write0_power"]) self.lib.write(" internal_power(){\n") - self.lib.write(" when : \"!CSb{0} & clk{1}\"; \n".format(port, web_name)) + self.lib.write(" when : \"!CSb{0} & clk{0}{1}\"; \n".format(port, web_name)) self.lib.write(" rise_power(scalar){\n") self.lib.write(" values(\"{0}\");\n".format(avg_write_power/2.0)) self.lib.write(" }\n") @@ -475,7 +477,7 @@ class lib: web_name = " & WEb{0}".format(port) avg_read_power = np.mean(self.char_port_results[port]["read1_power"] + self.char_port_results[port]["read0_power"]) self.lib.write(" internal_power(){\n") - self.lib.write(" when : \"!CSb{0} & !clk{1}\"; \n".format(port, web_name)) + self.lib.write(" when : \"!CSb{0} & !clk{0}{1}\"; \n".format(port, web_name)) self.lib.write(" rise_power(scalar){\n") self.lib.write(" values(\"{0}\");\n".format(avg_read_power/2.0)) self.lib.write(" }\n") diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index c76d702e..3d156d2d 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -58,6 +58,7 @@ class simulation(): # For generating comments in SPICE stimulus self.cycle_comments = [] + self.fn_cycle_comments = [] def add_control_one_port(self, port, op): """Appends control signals for operation to a given port""" @@ -81,7 +82,6 @@ class simulation(): def add_data(self, data, port): """ Add the array of data values """ debug.check(len(data)==self.word_size, "Invalid data word size.") - #debug.check(port < len(self.data_values), "Port number cannot index data values.") bit = self.word_size - 1 for c in data: @@ -109,12 +109,11 @@ class simulation(): def add_write(self, comment, address, data, port): """ Add the control values for a write cycle. """ - debug.info(1, comment) debug.check(port in self.write_index, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_index)) - self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments), - self.t_current, - comment, - port)) + debug.info(2, comment) + self.fn_cycle_comments.append(comment) + self.append_cycle_comment(port, comment) + self.cycle_times.append(self.t_current) self.t_current += self.period @@ -129,21 +128,20 @@ class simulation(): if unselected_port != port: self.add_noop_one_port(address, noop_data, unselected_port) - def add_read(self, comment, address, data, port): + def add_read(self, comment, address, din_data, port): """ Add the control values for a read cycle. """ - debug.info(1, comment) debug.check(port in self.read_index, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_index)) - self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments), - self.t_current, - comment, - port)) + debug.info(2, comment) + self.fn_cycle_comments.append(comment) + self.append_cycle_comment(port, comment) + self.cycle_times.append(self.t_current) self.t_current += self.period self.add_control_one_port(port, "read") #If the port is also a readwrite then add data. if port in self.write_index: - self.add_data(data,port) + self.add_data(din_data,port) self.add_address(address, port) #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port @@ -155,10 +153,10 @@ class simulation(): def add_noop_all_ports(self, comment, address, data): """ Add the control values for a noop to all ports. """ - debug.info(1, comment) - self.cycle_comments.append("Cycle {0:2d}\tPort All\t{1:5.2f}ns:\t{2}".format(len(self.cycle_times), - self.t_current, - comment)) + debug.info(2, comment) + self.fn_cycle_comments.append(comment) + self.append_cycle_comment("All", comment) + self.cycle_times.append(self.t_current) self.t_current += self.period @@ -168,27 +166,23 @@ class simulation(): def add_write_one_port(self, comment, address, data, port): """ Add the control values for a write cycle. Does not increment the period. """ debug.check(port in self.write_index, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_index)) - debug.info(1, comment) - self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments), - self.t_current, - comment, - port)) + debug.info(2, comment) + self.fn_cycle_comments.append(comment) + self.add_control_one_port(port, "write") self.add_data(data,port) self.add_address(address,port) - def add_read_one_port(self, comment, address, data, port): + def add_read_one_port(self, comment, address, din_data, port): """ Add the control values for a read cycle. Does not increment the period. """ debug.check(port in self.read_index, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_index)) - debug.info(1, comment) - self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments), - self.t_current, - comment, - port)) + debug.info(2, comment) + self.fn_cycle_comments.append(comment) + self.add_control_one_port(port, "read") #If the port is also a readwrite then add data. if port in self.write_index: - self.add_data(data,port) + self.add_data(din_data,port) self.add_address(address, port) def add_noop_one_port(self, address, data, port): @@ -197,4 +191,36 @@ class simulation(): if port in self.write_index: self.add_data(data,port) self.add_address(address, port) - \ No newline at end of file + + def append_cycle_comment(self, port, comment): + """Add comment to list to be printed in stimulus file""" + #Clean up time before appending. Make spacing dynamic as well. + time = "{0:.2f} ns:".format(self.t_current) + time_spacing = len(time)+6 + self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times), + port, + time, + time_spacing, + comment)) + + def gen_cycle_comment(self, op, word, addr, port, t_current): + if op == "noop": + comment = "\tIdle during cycle {0} ({1}ns - {2}ns)".format(int(t_current/self.period), + 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, + 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, + addr, + port, + int(t_current/self.period), + t_current, + t_current+self.period) + return comment + diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 14f780c2..6fa7a481 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -232,8 +232,19 @@ class stimuli(): measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final) self.sf.write(measure_string) - def write_control(self, end_time): + def write_control(self, end_time, runlvl=4): """ Write the control cards to run and end the simulation """ + + # These are guesses... + if runlvl==1: + reltol = 0.02 # 2% + elif runlvl==2: + reltol = 0.01 # 1% + elif runlvl==3: + reltol = 0.005 # 0.5% + else: + reltol = 0.001 # 0.1% + # UIC is needed for ngspice to converge self.sf.write(".TRAN 5p {0}n UIC\n".format(end_time)) if OPTS.spice_name == "ngspice": @@ -241,9 +252,9 @@ class stimuli(): # which is more accurate, but slower than the default trapezoid method # Do not remove this or it may not converge due to some "pa_00" nodes # unless you figure out what these are. - self.sf.write(".OPTIONS POST=1 RUNLVL=4 PROBE method=gear TEMP={}\n".format(self.temperature)) + self.sf.write(".OPTIONS POST=1 RELTOL={0} PROBE method=gear TEMP={1}\n".format(reltol,self.temperature)) else: - self.sf.write(".OPTIONS POST=1 RUNLVL=4 PROBE TEMP={}\n".format(self.temperature)) + self.sf.write(".OPTIONS POST=1 RUNLVL={0} PROBE TEMP={1}\n".format(runlvl,self.temperature)) # create plots for all signals self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n") diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index 3518fac0..a33df26c 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -111,6 +111,7 @@ class trim_spice(): match of the line with a term so you can search for a single net connection, the instance name, anything.. """ + removed_insts = 0 #Expects keep_inst_list are regex patterns. Compile them here. compiled_patterns = [re.compile(pattern) for pattern in keep_inst_list] @@ -127,11 +128,14 @@ class trim_spice(): new_buffer.append(line) in_subckt=False elif in_subckt: + removed_insts += 1 for pattern in compiled_patterns: if pattern.search(line) != None: new_buffer.append(line) + removed_insts -= 1 break else: new_buffer.append(line) self.sp_buffer = new_buffer + debug.info(2, "Removed {} instances from {} subcircuit.".format(removed_insts, subckt_name)) diff --git a/compiler/characterizer/worst_case.py b/compiler/characterizer/worst_case.py new file mode 100644 index 00000000..6dad95d9 --- /dev/null +++ b/compiler/characterizer/worst_case.py @@ -0,0 +1,77 @@ +import sys,re,shutil +import debug +import tech +import math +from .stimuli import * +from .trim_spice import * +from .charutils import * +import utils +from globals import OPTS +from .delay import delay + +class worst_case(delay): + """Functions to test for the worst case delay in a target SRAM + + The current worst case determines a feasible period for the SRAM then tests + several bits and record the delay and differences between the bits. + + """ + + def __init__(self, sram, spfile, corner): + delay.__init__(self,sram,spfile,corner) + + + def analyze(self,probe_address, probe_data, slews, loads): + """ + Main function to test the delays of different bits. + """ + debug.check(OPTS.num_rw_ports < 2 and OPTS.num_w_ports < 1 and OPTS.num_r_ports < 1 , + "Bit testing does not currently support multiport.") + #Dict to hold all characterization values + char_sram_data = {} + + self.set_probe(probe_address, probe_data) + #self.prepare_netlist() + + self.load=max(loads) + self.slew=max(slews) + + # 1) Find a feasible period and it's corresponding delays using the trimmed array. + feasible_delays = self.find_feasible_period() + + # 2) Find the delays of several bits + test_bits = self.get_test_bits() + bit_delays = self.simulate_for_bit_delays(test_bits) + + for i in range(len(test_bits)): + debug.info(1, "Bit tested: addr {0[0]} data_pos {0[1]}\n Values {1}".format(test_bits[i], bit_delays[i])) + + def simulate_for_bit_delays(self, test_bits): + """Simulates the delay of the sram of over several bits.""" + bit_delays = [{} for i in range(len(test_bits))] + + #Assumes a bitcell with only 1 rw port. (6t, port 0) + port = 0 + self.targ_read_ports = [self.read_ports[port]] + self.targ_write_ports = [self.write_ports[port]] + + for i in range(len(test_bits)): + (bit_addr, bit_data) = test_bits[i] + self.set_probe(bit_addr, bit_data) + debug.info(1,"Delay bit test: period {}, addr {}, data_pos {}".format(self.period, bit_addr, bit_data)) + (success, results)=self.run_delay_simulation() + debug.check(success, "Bit Test Failed: period {}, addr {}, data_pos {}".format(self.period, bit_addr, bit_data)) + bit_delays[i] = results[port] + + return bit_delays + + + def get_test_bits(self): + """Statically determines address and bit values to test""" + #First and last address, first middle, and last bit. Last bit is repeated twice with different data position. + bit_addrs = ["0"*self.addr_size, "0"+"1"*(self.addr_size-1), "1"*self.addr_size, "1"*self.addr_size] + data_positions = [0, (self.word_size-1)//2, 0, self.word_size-1] + #Return them in a tuple form + return [(bit_addrs[i], data_positions[i]) for i in range(len(bit_addrs))] + + diff --git a/compiler/drc/design_rules.py b/compiler/drc/design_rules.py new file mode 100644 index 00000000..4bc2eaa6 --- /dev/null +++ b/compiler/drc/design_rules.py @@ -0,0 +1,40 @@ +import debug +from drc_value import * +from drc_lut import * + +class design_rules(): + """ + This is a class that implements the design rules structures. + """ + def __init__(self, name): + self.tech_name = name + self.rules = {} + + def add(self, name, value): + self.rules[name] = value + + def __call__(self, name, *args): + rule = self.rules[name] + if callable(rule): + return rule(*args) + else: + return rule + + def __setitem__(self, b, c): + """ + For backward compatibility with existing rules. + """ + self.rules[b] = c + + def __getitem__(self, b): + """ + For backward compatibility with existing rules. + """ + rule = self.rules[b] + if not callable(rule): + return rule + else: + debug.error("Must call complex DRC rule {} with arguments.".format(b),-1) + + + diff --git a/compiler/drc/drc_lut.py b/compiler/drc/drc_lut.py new file mode 100644 index 00000000..07e482c8 --- /dev/null +++ b/compiler/drc/drc_lut.py @@ -0,0 +1,43 @@ +import debug + +class drc_lut(): + """ + Implement a lookup table of rules. + Each element is a tuple with the last value being the rule. + It searches through backwards until all of the key values are + met and returns the rule value. + For exampe, the key values can be width and length, + and it would return the rule for a wire of at least a given width and length. + A dimension can be ignored by passing inf. + """ + def __init__(self, table): + self.table = table + + def __call__(self, *key): + """ + Lookup a given tuple in the table. + """ + if len(key)==0: + first_key = list(sorted(self.table.keys()))[0] + return self.table[first_key] + + for table_key in sorted(self.table.keys(), reverse=True): + if self.match(key, table_key): + return self.table[table_key] + + + def match(self, key1, key2): + """ + Determine if key1>=key2 for all tuple pairs. + (i.e. return false if key1left_bott_X: @@ -713,9 +714,9 @@ class VlsiLayout: # Convert to user units new_boundaries = [] for pin_boundary in pin_boundaries: - new_boundaries.append([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_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): diff --git a/compiler/globals.py b/compiler/globals.py index 11606bf2..0b04079b 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -24,7 +24,7 @@ def parse_args(): global OPTS option_list = { - optparse.make_option("-b", "--backannotated", action="store_true", dest="run_pex", + optparse.make_option("-b", "--backannotated", action="store_true", dest="use_pex", help="Back annotate simulation"), optparse.make_option("-o", "--output", dest="output_name", help="Base output file name(s) prefix", metavar="FILE"), @@ -295,7 +295,8 @@ def setup_paths(): # Add all of the subdirs to the python path # These subdirs are modules and don't need to be added: characterizer, verify - for subdir in ["gdsMill", "tests", "modules", "base", "pgates", "datasheet"]: + subdirlist = [ item for item in os.listdir(OPENRAM_HOME) if os.path.isdir(os.path.join(OPENRAM_HOME, item)) ] + for subdir in subdirlist: full_path = "{0}/{1}".format(OPENRAM_HOME,subdir) debug.check(os.path.isdir(full_path), "$OPENRAM_HOME/{0} does not exist: {1}".format(subdir,full_path)) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index ebb759d4..0fb6be60 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -107,8 +107,7 @@ class bank(design.design): if self.num_banks > 1: self.route_bank_select() - self.route_vdd_gnd() - + self.route_supplies() def create_modules(self): """ Add modules. The order should not matter! """ @@ -201,7 +200,7 @@ class bank(design.design): self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width # A space for wells or jogging m2 - self.m2_gap = max(2*drc["pwell_to_nwell"] + drc["well_enclosure_active"], + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), 2*self.m2_pitch) @@ -527,10 +526,13 @@ class bank(design.design): # FIXME: place for multiport for port in range(self.total_ports): + col_decoder_inst = self.col_decoder_inst[port] + # Place the col decoder right aligned with row decoder x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) - y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) - self.col_decoder_inst[port].place(vector(x_off,y_off)) + y_off = -(self.col_decoder.height + 2*drc("well_to_well")) + col_decoder_inst.place(vector(x_off,y_off)) + def create_bank_select(self): @@ -565,47 +567,17 @@ class bank(design.design): y_off = min(self.col_decoder_inst[port].by(), self.col_mux_array_inst[port].by()) else: y_off = self.row_decoder_inst[port].by() - y_off -= (self.bank_select.height + drc["well_to_well"]) + y_off -= (self.bank_select.height + drc("well_to_well")) self.bank_select_pos = vector(x_off,y_off) self.bank_select_inst[port].place(self.bank_select_pos) - def route_vdd_gnd(self): + def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ + for inst in self.insts: + self.copy_power_pins(inst,"vdd") + self.copy_power_pins(inst,"gnd") - # These are the instances that every bank has - top_instances = [self.bitcell_array_inst] - for port in range(self.total_read): - #top_instances.append(self.precharge_array_inst[port]) - top_instances.append(self.sense_amp_array_inst[port]) - for port in range(self.total_write): - top_instances.append(self.write_driver_array_inst[port]) - for port in range(self.total_ports): - top_instances.extend([self.row_decoder_inst[port], - self.wordline_driver_inst[port]]) - # Add these if we use the part... - if self.col_addr_size > 0: - top_instances.append(self.col_decoder_inst[port]) - #top_instances.append(self.col_mux_array_inst[port]) - - if self.num_banks > 1: - top_instances.append(self.bank_select_inst[port]) - - if self.col_addr_size > 0: - for port in range(self.total_ports): - self.copy_layout_pin(self.col_mux_array_inst[port], "gnd") - for port in range(self.total_read): - self.copy_layout_pin(self.precharge_array_inst[port], "vdd") - - for inst in top_instances: - # Column mux has no vdd - #if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst[0]): - self.copy_layout_pin(inst, "vdd") - # Precharge has no gnd - #if inst != self.precharge_array_inst[port]: - self.copy_layout_pin(inst, "gnd") - - def route_bank_select(self): """ Route the bank select logic. """ @@ -953,7 +925,7 @@ class bank(design.design): rotate=90) - def analytical_delay(self, slew, load): + def analytical_delay(self, vdd, slew, load): """ return analytical delay of the bank""" decoder_delay = self.row_decoder.analytical_delay(slew, self.wordline_driver.input_load()) @@ -961,10 +933,17 @@ class bank(design.design): bitcell_array_delay = self.bitcell_array.analytical_delay(word_driver_delay.slew) - bl_t_data_out_delay = self.sense_amp_array.analytical_delay(bitcell_array_delay.slew, + if self.words_per_row > 1: + port = 0 #Analytical delay only supports single port + column_mux_delay = self.column_mux_array[port].analytical_delay(vdd, bitcell_array_delay.slew, + self.sense_amp_array.input_load()) + else: + column_mux_delay = self.return_delay(delay = 0.0, slew=word_driver_delay.slew) + + bl_t_data_out_delay = self.sense_amp_array.analytical_delay(column_mux_delay.slew, self.bitcell_array.output_load()) # output load of bitcell_array is set to be only small part of bl for sense amp. - result = decoder_delay + word_driver_delay + bitcell_array_delay + bl_t_data_out_delay + result = decoder_delay + word_driver_delay + bitcell_array_delay + column_mux_delay + bl_t_data_out_delay return result diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 8af2704f..2b78f739 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -67,7 +67,7 @@ class bank_select(design.design): self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() - height = self.bitcell.height + drc["poly_to_active"] + height = self.bitcell.height + drc("poly_to_active") # 1x Inverter self.inv_sel = pinv(height=height) @@ -88,8 +88,8 @@ class bank_select(design.design): def calculate_module_offsets(self): - self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"] - self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"] + self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") + self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") self.xoffset_inv = max(self.xoffset_nand + self.nand2.width, self.xoffset_nor + self.nor2.width) self.xoffset_bank_sel_inv = 0 self.xoffset_inputs = 0 diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 97c62e63..511ef9a8 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -39,7 +39,7 @@ class bitcell_array(design.design): def create_layout(self): # We increase it by a well enclosure so the precharges don't overlap our wells - self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width + self.height = self.row_size*self.cell.height + drc("well_enclosure_active") + self.m1_width self.width = self.column_size*self.cell.width + self.m1_width xoffset = 0.0 @@ -199,13 +199,13 @@ class bitcell_array(design.design): return total_power def gen_wl_wire(self): - wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc["minwidth_metal1"]) + wl_wire = self.generate_rc_net(int(self.column_size), self.width, drc("minwidth_metal1")) wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell return wl_wire def gen_bl_wire(self): bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc["minwidth_metal1"]) + bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), self.height, drc("minwidth_metal1")) bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index cc721b39..97e82e24 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -136,11 +136,12 @@ class dff_array(design.design): # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") + clk_ypos = 2*self.m3_pitch+self.m3_width debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") self.add_layout_pin_segment_center(text="clk", layer="metal3", - start=vector(0,self.m3_pitch+self.m3_width), - end=vector(self.width,self.m3_pitch+self.m3_width)) + start=vector(0,clk_ypos), + end=vector(self.width,clk_ypos)) for col in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk") # Make a vertical strip for each column @@ -150,7 +151,7 @@ class dff_array(design.design): height=self.height) # Drop a via to the M3 pin self.add_via_center(layers=("metal2","via2","metal3"), - offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width)) + offset=vector(clk_pin.cx(),clk_ypos)) diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index d396b903..9223e276 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -153,6 +153,7 @@ class dff_buf_array(design.design): # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") + clk_ypos = 2*self.m3_pitch+self.m3_width debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") if self.columns==1: self.add_layout_pin(text="clk", @@ -163,8 +164,8 @@ class dff_buf_array(design.design): else: self.add_layout_pin_segment_center(text="clk", layer="metal3", - start=vector(0,self.m3_pitch+self.m3_width), - end=vector(self.width,self.m3_pitch+self.m3_width)) + start=vector(0,clk_ypos), + end=vector(self.width,clk_ypos)) for col in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk") @@ -175,7 +176,7 @@ class dff_buf_array(design.design): height=self.height) # Drop a via to the M3 pin self.add_via_center(layers=("metal2","via2","metal3"), - offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width)) + offset=vector(clk_pin.cx(),clk_ypos)) diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index 06df3fb1..aafe87e2 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -153,6 +153,7 @@ class dff_inv_array(design.design): # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") + clk_ypos = 2*self.m3_pitch+self.m3_width debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") if self.columns==1: self.add_layout_pin(text="clk", @@ -163,8 +164,8 @@ class dff_inv_array(design.design): else: self.add_layout_pin_segment_center(text="clk", layer="metal3", - start=vector(0,self.m3_pitch+self.m3_width), - end=vector(self.width,self.m3_pitch+self.m3_width)) + start=vector(0,clk_ypos), + end=vector(self.width,clk_ypos)) for col in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk") # Make a vertical strip for each column @@ -174,7 +175,7 @@ class dff_inv_array(design.design): height=self.height) # Drop a via to the M3 pin self.add_via_center(layers=("metal2","via2","metal3"), - offset=vector(clk_pin.cx(),self.m3_pitch+self.m3_width)) + offset=vector(clk_pin.cx(),clk_ypos)) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index b5872b04..967b93cd 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -548,30 +548,27 @@ class hierarchical_decoder(design.design): def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - # Find the x offsets for where the vias/pins should be placed - a_xoffset = self.inv_inst[0].lx() - b_xoffset = self.inv_inst[0].rx() - + # The vias will be placed in the center and right of the cells, respectively. + xoffset = self.nand_inst[0].cx() for num in range(0,self.rows): - # this will result in duplicate polygons for rails, but who cares - - # Route both supplies - for n in ["vdd", "gnd"]: - supply_pin = self.inv_inst[num].get_pin(n) - - # Add pins in two locations - for xoffset in [a_xoffset, b_xoffset]: - pin_pos = vector(xoffset, supply_pin.cy()) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=pin_pos, - rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=pin_pos, - rotate=90) - self.add_layout_pin_rect_center(text=n, - layer="metal3", - offset=pin_pos) + for pin_name in ["vdd", "gnd"]: + # The nand and inv are the same height rows... + supply_pin = self.nand_inst[num].get_pin(pin_name) + pin_pos = vector(xoffset, supply_pin.cy()) + self.add_power_pin(name=pin_name, + loc=pin_pos) + # Make a redundant rail too + for num in range(0,self.rows,2): + for pin_name in ["vdd", "gnd"]: + start = self.nand_inst[num].get_pin(pin_name).lc() + end = self.inv_inst[num].get_pin(pin_name).rc() + mid = (start+end).scale(0.5,0.5) + self.add_rect_center(layer="metal1", + offset=mid, + width=end.x-start.x) + + # Copy the pins from the predecoders for pre in self.pre2x4_inst + self.pre3x8_inst: self.copy_layout_pin(pre, "vdd") diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 59de33f4..dc6622ed 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -282,15 +282,7 @@ class hierarchical_predecode(design.design): # Add pins in two locations for xoffset in [in_xoffset, out_xoffset]: pin_pos = vector(xoffset, nand_pin.cy()) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=pin_pos, - rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=pin_pos, - rotate=90) - self.add_layout_pin_rect_center(text=n, - layer="metal3", - offset=pin_pos) + self.add_power_pin(n, pin_pos) diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index 10fa4c4e..5de24948 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -109,7 +109,7 @@ class multibank(design.design): if self.num_banks > 1: self.route_bank_select() - self.route_vdd_gnd() + self.route_supplies() def add_modules(self): """ Add modules. The order should not matter! """ @@ -170,7 +170,7 @@ class multibank(design.design): self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width # A space for wells or jogging m2 - self.m2_gap = max(2*drc["pwell_to_nwell"] + drc["well_enclosure_active"], + self.m2_gap = max(2*drc("pwell_to_nwell"] + drc["well_enclosure_active"), 2*self.m2_pitch) @@ -382,7 +382,7 @@ class multibank(design.design): """ # Place the col decoder right aligned with row decoder x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) - y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) + y_off = -(self.col_decoder.height + 2*drc("well_to_well")) self.col_decoder_inst=self.add_inst(name="col_address_decoder", mod=self.col_decoder, offset=vector(x_off,y_off)) @@ -427,7 +427,7 @@ class multibank(design.design): y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by()) else: y_off = self.row_decoder_inst.by() - y_off -= (self.bank_select.height + drc["well_to_well"]) + y_off -= (self.bank_select.height + drc("well_to_well")) self.bank_select_pos = vector(x_off,y_off) self.bank_select_inst = self.add_inst(name="bank_select", mod=self.bank_select, @@ -440,33 +440,11 @@ class multibank(design.design): temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def route_vdd_gnd(self): + def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ - - # These are the instances that every bank has - top_instances = [self.bitcell_array_inst, - self.precharge_array_inst, - self.sense_amp_array_inst, - self.write_driver_array_inst, -# self.tri_gate_array_inst, - self.row_decoder_inst, - self.wordline_driver_inst] - # Add these if we use the part... - if self.col_addr_size > 0: - top_instances.append(self.col_decoder_inst) - top_instances.append(self.col_mux_array_inst) - - if self.num_banks > 1: - top_instances.append(self.bank_select_inst) - - - for inst in top_instances: - # Column mux has no vdd - if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst): - self.copy_layout_pin(inst, "vdd") - # Precharge has no gnd - if inst != self.precharge_array_inst: - self.copy_layout_pin(inst, "gnd") + for inst in self.insts: + self.copy_power_pins(inst,"vdd") + self.copy_power_pins(inst,"gnd") def route_bank_select(self): """ Route the bank select logic. """ diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 4bf8b9ce..7e0ee718 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -63,7 +63,7 @@ class precharge_array(design.design): layer="metal1", offset=self.pc_cell.get_pin("en").ll(), width=self.width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) for inst in self.local_insts: self.copy_layout_pin(inst, "vdd") @@ -74,13 +74,13 @@ class precharge_array(design.design): self.add_layout_pin(text="bl_{0}".format(i), layer="metal2", offset=bl_pin.ll(), - width=drc["minwidth_metal2"], + width=drc("minwidth_metal2"), height=bl_pin.height()) br_pin = inst.get_pin("br") self.add_layout_pin(text="br_{0}".format(i), layer="metal2", offset=br_pin.ll(), - width=drc["minwidth_metal2"], + width=drc("minwidth_metal2"), height=bl_pin.height()) diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index bf635538..8095d049 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -196,7 +196,7 @@ class replica_bitline(design.design): wl_last = self.wl_list[self.total_ports-1]+"_{}".format(row) pin_last = self.rbl_inst.get_pin(wl_last) - correct = vector(0.5*drc["minwidth_metal1"], 0) + correct = vector(0.5*drc("minwidth_metal1"), 0) self.add_path("metal1", [pin.rc()-correct, pin_last.rc()-correct]) def route_supplies(self): @@ -255,7 +255,7 @@ class replica_bitline(design.design): # 3. Route the contact of previous route to the bitcell WL # route bend of previous net to bitcell WL wl_offset = self.rbc_inst.get_pin(self.wl_list[0]).lc() - wl_mid1 = wl_offset - vector(1.5*drc["minwidth_metal1"], 0) + wl_mid1 = wl_offset - vector(1.5*drc("minwidth_metal1"), 0) wl_mid2 = vector(wl_mid1.x, contact_offset.y) #xmid_point= 0.5*(wl_offset.x+contact_offset.x) #wl_mid1 = vector(xmid_point,contact_offset.y) @@ -268,7 +268,7 @@ class replica_bitline(design.design): pin = self.rbc_inst.get_pin(wl) pin_last = self.rbc_inst.get_pin(wl_last) - correct = vector(0.5*drc["minwidth_metal1"], 0) + correct = vector(0.5*drc("minwidth_metal1"), 0) self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct]) # DRAIN ROUTE diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index 45a195fc..6187b37a 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -23,6 +23,11 @@ class sense_amp(design.design): self.height = sense_amp.height self.pin_map = sense_amp.pin_map + def input_load(self): + #Input load for the bitlines which are connected to the source/drain of a TX. Not the selects. + 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 + def analytical_delay(self, slew, load=0.0): from tech import spice r = spice["min_tx_r"]/(10) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 1f44a612..1bbbf02e 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -132,8 +132,11 @@ class sense_amp_array(design.design): layer="metal1", offset=sclk_offset, width=self.width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) + def input_load(self): + return self.amp.input_load() + def analytical_delay(self, slew, load=0.0): return self.amp.analytical_delay(slew=slew, load=load) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 56333c20..a74f8514 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -170,23 +170,23 @@ class single_level_column_mux_array(design.design): self.add_rect(layer="metal1", offset=bl_out_offset, width=width, - height=drc["minwidth_metal2"]) + height=drc("minwidth_metal2")) self.add_rect(layer="metal1", offset=br_out_offset, width=width, - height=drc["minwidth_metal2"]) + height=drc("minwidth_metal2")) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux self.add_layout_pin(text="bl_out_{}".format(int(j/self.words_per_row)), layer="metal2", offset=bl_out_offset.scale(1,0), - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height) self.add_layout_pin(text="br_out_{}".format(int(j/self.words_per_row)), layer="metal2", offset=br_out_offset.scale(1,0), - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height) # This via is on the right of the wire @@ -202,7 +202,7 @@ class single_level_column_mux_array(design.design): self.add_rect(layer="metal2", offset=bl_out_offset, - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height-bl_out_offset.y) # This via is on the right of the wire self.add_via(layers=("metal1", "via1", "metal2"), @@ -210,12 +210,20 @@ class single_level_column_mux_array(design.design): rotate=90) self.add_rect(layer="metal2", offset=br_out_offset, - width=drc['minwidth_metal2'], + width=drc('minwidth_metal2'), height=self.route_height-br_out_offset.y) # This via is on the left of the wire self.add_via(layers=("metal1", "via1", "metal2"), offset= br_out_offset, rotate=90) - + def analytical_delay(self, vdd, slew, load=0.0): + from tech import spice + 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 + volt_swing = spice["v_threshold_typical"]/vdd + + result = self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew, swing = volt_swing) + return self.return_delay(result.delay, result.slew) diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index d6c5e725..5ca992b3 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -107,14 +107,14 @@ class tri_gate_array(design.design): layer="metal1", offset=en_pin.ll().scale(0, 1), width=width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) enbar_pin = self.tri_inst[0].get_pin("en_bar") self.add_layout_pin(text="en_bar", layer="metal1", offset=enbar_pin.ll().scale(0, 1), width=width, - height=drc["minwidth_metal1"]) + height=drc("minwidth_metal1")) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index e7f6b79b..61fe8c24 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -130,7 +130,7 @@ class write_driver_array(design.design): layer="metal1", offset=self.driver_insts[0].get_pin("en").ll().scale(0,1), width=self.width, - height=drc['minwidth_metal1']) + height=drc('minwidth_metal1')) diff --git a/compiler/openram.py b/compiler/openram.py index 2163ae5c..ee43749f 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -7,7 +7,6 @@ a spice (.sp) file for circuit simulation a GDS2 (.gds) file containing the layout a LEF (.lef) file for preliminary P&R (real one should be from layout) a Liberty (.lib) file for timing analysis/optimization - """ import sys,os diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index ec3bed0c..fc839270 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -1,7 +1,7 @@ import contact import design import debug -from tech import drc, parameter, spice, info +from tech import drc, parameter, spice from ptx import ptx from vector import vector from globals import OPTS @@ -110,7 +110,7 @@ class pgate(design.design): max_y_offset = self.height + 0.5*self.m1_width self.nwell_position = middle_position nwell_height = max_y_offset - middle_position.y - if info["has_nwell"]: + if drc("has_nwell"): self.add_rect(layer="nwell", offset=middle_position, width=self.well_width, @@ -122,7 +122,7 @@ class pgate(design.design): pwell_position = vector(0,-0.5*self.m1_width) pwell_height = middle_position.y-pwell_position.y - if info["has_pwell"]: + if drc("has_pwell"): self.add_rect(layer="pwell", offset=pwell_position, width=self.well_width, @@ -138,7 +138,7 @@ class pgate(design.design): layer_stack = ("active", "contact", "metal1") # To the right a spacing away from the pmos right active edge - contact_xoffset = pmos_pos.x + pmos.active_width + drc["active_to_body_active"] + contact_xoffset = pmos_pos.x + pmos.active_width + drc("active_to_body_active") # Must be at least an well enclosure of active down from the top of the well # OR align the active with the top of PMOS active. max_y_offset = self.height + 0.5*self.m1_width @@ -185,7 +185,7 @@ class pgate(design.design): pwell_position = vector(0,-0.5*self.m1_width) # To the right a spacing away from the nmos right active edge - contact_xoffset = nmos_pos.x + nmos.active_width + drc["active_to_body_active"] + contact_xoffset = nmos_pos.x + nmos.active_width + drc("active_to_body_active") # Must be at least an well enclosure of active up from the bottom of the well contact_yoffset = max(nmos_pos.y, self.well_enclose_active - nmos.active_contact.first_layer_height/2) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 8b3d1716..0ffd4f66 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -1,7 +1,7 @@ import contact import pgate import debug -from tech import drc, parameter, spice, info +from tech import drc, parameter, spice from ptx import ptx from vector import vector from math import ceil @@ -76,8 +76,8 @@ class pinv(pgate.pgate): # This may make the result differ when the layout is created... if OPTS.netlist_only: self.tx_mults = 1 - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") return # Do a quick sanity check and bail if unlikely feasible height @@ -85,16 +85,16 @@ class pinv(pgate.pgate): # Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain) # plus the tx height nmos = ptx(tx_type="nmos") - pmos = ptx(width=drc["minwidth_tx"], tx_type="pmos") + pmos = ptx(width=drc("minwidth_tx"), tx_type="pmos") tx_height = nmos.poly_height + pmos.poly_height # rotated m1 pitch or poly to active spacing min_channel = max(contact.poly.width + self.m1_space, - contact.poly.width + 2*drc["poly_to_active"]) + contact.poly.width + 2*drc("poly_to_active")) # This is the extra space needed to ensure DRC rules to the active contacts extra_contact_space = max(-nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) total_height = tx_height + min_channel + 2*self.top_bottom_space debug.check(self.height> total_height,"Cell height {0} too small for simple min height {1}.".format(self.height,total_height)) @@ -103,16 +103,16 @@ class pinv(pgate.pgate): # Divide the height in half. Could divide proportional to beta, but this makes # connecting wells of multiple cells easier. # Subtract the poly space under the rail of the tx - nmos_height_available = 0.5 * tx_height_available - 0.5*drc["poly_to_poly"] - pmos_height_available = 0.5 * tx_height_available - 0.5*drc["poly_to_poly"] + nmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly") + pmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly") debug.info(2,"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available, nmos_height_available, pmos_height_available)) # Determine the number of mults for each to fit width into available space - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") nmos_required_mults = max(int(ceil(self.nmos_width/nmos_height_available)),1) pmos_required_mults = max(int(ceil(self.pmos_width/pmos_height_available)),1) # The mults must be the same for easy connection of poly @@ -124,9 +124,9 @@ class pinv(pgate.pgate): # We also need to round the width to the grid or we will end up with LVS property # mismatch errors when fingers are not a grid length and get rounded in the offset geometry. self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) - debug.check(self.nmos_width>=drc["minwidth_tx"],"Cannot finger NMOS transistors to fit cell height.") + debug.check(self.nmos_width>=drc("minwidth_tx"),"Cannot finger NMOS transistors to fit cell height.") self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) - debug.check(self.pmos_width>=drc["minwidth_tx"],"Cannot finger PMOS transistors to fit cell height.") + debug.check(self.pmos_width>=drc("minwidth_tx"),"Cannot finger PMOS transistors to fit cell height.") def setup_layout_constants(self): @@ -137,7 +137,7 @@ class pinv(pgate.pgate): # the well width is determined the multi-finger PMOS device width plus # the well contact width and half well enclosure on both sides self.well_width = self.pmos.active_width + self.pmos.active_contact.width \ - + drc["active_to_body_active"] + 2*drc["well_enclosure_active"] + + drc("active_to_body_active") + 2*drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 328836dc..76b3c929 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -94,16 +94,16 @@ class pinvbuf(design.design): self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) def place_modules(self): - # Add INV1 to the right (capacitance shield) + # Add INV1 to the left (capacitance shield) self.inv1_inst.place(vector(0,0)) - # Add INV2 to the right + # Add INV2 to the right of INV1 self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) - # Add INV3 to the right + # Add INV3 to the right of INV2 self.inv3_inst.place(vector(self.inv2_inst.rx(),0)) - # Add INV4 to the bottom + # Add INV4 flipped to the bottom aligned with INV2 self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),2*self.inv2.height), mirror = "MX") diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 14923a84..1a31e3be 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -23,8 +23,8 @@ class pnand2(pgate.pgate): self.nmos_size = 2*size self.pmos_size = parameter["beta"]*size - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnand2 is only supported now.") @@ -91,7 +91,7 @@ class pnand2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 2*self.pmos.active_width + contact.active.width \ - + 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"] + + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -100,7 +100,7 @@ class pnand2(pgate.pgate): extra_contact_space = max(-self.nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 75887ed3..3247a371 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -25,8 +25,8 @@ class pnand3(pgate.pgate): # If we relax this, we could size this better. self.nmos_size = 2*size self.pmos_size = parameter["beta"]*size - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnand3 is only supported now.") @@ -83,7 +83,7 @@ class pnand3(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 3*self.pmos.active_width + self.pmos.active_contact.width \ - + 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"] \ + + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") \ - self.overlap_offset.x self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -96,7 +96,7 @@ class pnand3(pgate.pgate): extra_contact_space = max(-nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ @@ -191,7 +191,7 @@ class pnand3(pgate.pgate): metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width, self.m1_space + 0.5*contact.poly.width + 0.5*self.m1_width) - active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc["poly_to_active"]) + active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc("poly_to_active")) inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing self.route_input_gate(self.pmos3_inst, self.nmos3_inst, inputC_yoffset, "C", position="center") diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 87196342..65aaf7f8 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -24,8 +24,8 @@ class pnor2(pgate.pgate): self.nmos_size = size # We will just make this 1.5 times for now. NORs are not ideal anyhow. self.pmos_size = 1.5*parameter["beta"]*size - self.nmos_width = self.nmos_size*drc["minwidth_tx"] - self.pmos_width = self.pmos_size*drc["minwidth_tx"] + self.nmos_width = self.nmos_size*drc("minwidth_tx") + self.pmos_width = self.pmos_size*drc("minwidth_tx") # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnor2 is only supported now.") @@ -92,7 +92,7 @@ class pnor2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 2*self.pmos.active_width + self.pmos.active_contact.width \ - + 2*drc["active_to_body_active"] + 2*drc["well_enclosure_active"] + + 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -101,7 +101,7 @@ class pnor2(pgate.pgate): extra_contact_space = max(-self.nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc["poly_extend_active"], self.poly_space) + drc("poly_extend_active"), self.poly_space) def add_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 9dd2157d..ea67eeb5 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -163,7 +163,7 @@ class precharge(pgate.pgate): """Adds a nwell tap to connect to the vdd rail""" # adds the contact from active to metal1 well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \ - + vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc["well_extend_active"]) + + vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active")) self.add_contact_center(layers=("active", "contact", "metal1"), offset=well_contact_pos, implant_type="n", @@ -185,7 +185,7 @@ class precharge(pgate.pgate): self.add_layout_pin(text="bl", layer="metal2", offset=offset, - width=drc['minwidth_metal2'], + width=drc("minwidth_metal2"), height=self.height) # adds the BR on metal 2 @@ -193,7 +193,7 @@ class precharge(pgate.pgate): self.add_layout_pin(text="br", layer="metal2", offset=offset, - width=drc['minwidth_metal2'], + width=drc("minwidth_metal2"), height=self.height) def connect_to_bitlines(self): diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index e5a1a51b..07d04028 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -1,6 +1,6 @@ import design import debug -from tech import drc, info, spice +from tech import drc, spice from vector import vector from contact import contact from globals import OPTS @@ -15,7 +15,7 @@ class ptx(design.design): you to connect the fingered gates and active for parallel devices. """ - def __init__(self, width=drc["minwidth_tx"], mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None): + def __init__(self, width=drc("minwidth_tx"), mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None): # We need to keep unique names because outputting to GDSII # will use the last record with a given name. I.e., you will # over-write a design in GDS if one has and the other doesn't @@ -66,12 +66,12 @@ class ptx(design.design): # self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # " ".join(self.pins))) # Just make a guess since these will actually be decided in the layout later. - area_sd = 2.5*drc["minwidth_poly"]*self.tx_width - perimeter_sd = 2*drc["minwidth_poly"] + 2*self.tx_width + area_sd = 2.5*drc("minwidth_poly")*self.tx_width + perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4}u ps={4}u as={5}p ad={5}p".format(spice[self.tx_type], self.mults, self.tx_width, - drc["minwidth_poly"], + drc("minwidth_poly"), perimeter_sd, area_sd) self.spice.append("\n* ptx " + self.spice_device) @@ -109,7 +109,7 @@ class ptx(design.design): self.contact_pitch = 2*self.contact_to_gate + self.contact_width + self.poly_width # The enclosure of an active contact. Not sure about second term. - active_enclose_contact = max(drc["active_enclosure_contact"], + active_enclose_contact = max(drc("active_enclosure_contact"), (self.active_width - self.contact_width)/2) # This is the distance from the edge of poly to the contacted end of active self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate @@ -129,7 +129,7 @@ class ptx(design.design): self.active_offset = vector([self.well_enclose_active]*2) # Well enclosure of active, ensure minwidth as well - if info["has_{}well".format(self.well_type)]: + if drc("has_{}well".format(self.well_type)): self.cell_well_width = max(self.active_width + 2*self.well_enclose_active, self.well_width) self.cell_well_height = max(self.tx_width + 2*self.well_enclose_active, @@ -151,9 +151,9 @@ class ptx(design.design): # Min area results are just flagged for now. - debug.check(self.active_width*self.active_height>=drc["minarea_active"],"Minimum active area violated.") + debug.check(self.active_width*self.active_height>=drc("minarea_active"),"Minimum active area violated.") # We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue. - debug.check(self.poly_width*self.poly_height>=drc["minarea_poly"],"Minimum poly area violated.") + debug.check(self.poly_width*self.poly_height>=drc("minarea_poly"),"Minimum poly area violated.") def connect_fingered_poly(self, poly_positions): """ @@ -181,7 +181,7 @@ class ptx(design.design): layer="poly", offset=poly_offset, width=poly_width, - height=drc["minwidth_poly"]) + height=drc("minwidth_poly")) def connect_fingered_active(self, drain_positions, source_positions): @@ -269,7 +269,7 @@ class ptx(design.design): height=self.active_height) # If the implant must enclose the active, shift offset # and increase width/height - enclose_width = drc["implant_enclosure_active"] + enclose_width = drc("implant_enclosure_active") enclose_offset = [enclose_width]*2 self.add_rect(layer="{}implant".format(self.implant_type), offset=self.active_offset - enclose_offset, @@ -280,7 +280,7 @@ class ptx(design.design): """ Add an (optional) well and implant for the type of transistor. """ - if info["has_{}well".format(self.well_type)]: + if drc("has_{}well".format(self.well_type)): self.add_rect(layer="{}well".format(self.well_type), offset=(0,0), width=self.cell_well_width, diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 0e1cd88f..ddfca30c 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -1,6 +1,6 @@ import design import debug -from tech import drc, info +from tech import drc from vector import vector import contact from ptx import ptx @@ -52,7 +52,7 @@ class single_level_column_mux(design.design): self.bitcell = self.mod_bitcell() # Adds nmos_lower,nmos_upper to the module - self.ptx_width = self.tx_size * drc["minwidth_tx"] + self.ptx_width = self.tx_size * drc("minwidth_tx") self.nmos = ptx(width=self.ptx_width) self.add_mod(self.nmos) diff --git a/compiler/router/direction.py b/compiler/router/direction.py new file mode 100644 index 00000000..95980618 --- /dev/null +++ b/compiler/router/direction.py @@ -0,0 +1,9 @@ +from enum import Enum + +class direction(Enum): + NORTH = 1 + SOUTH = 2 + EAST = 3 + WEST = 4 + UP = 5 + DOWN = 6 diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 8eb063cf..b43c36b2 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -1,95 +1,138 @@ import numpy as np import string -from itertools import tee import debug from vector3d import vector3d -from cell import cell -import os +from grid_cell import grid_cell class grid: """ A two layer routing map. Each cell can be blocked in the vertical or horizontal layer. """ + # costs are relative to a unit grid + # non-preferred cost allows an off-direction jog of 1 grid + # rather than 2 vias + preferred direction (cost 5) + VIA_COST = 2 + NONPREFERRED_COST = 4 + PREFERRED_COST = 1 - def __init__(self): + + def __init__(self, ll, ur, track_width): """ Initialize the map and define the costs. """ - # costs are relative to a unit grid - # non-preferred cost allows an off-direction jog of 1 grid - # rather than 2 vias + preferred direction (cost 5) - self.VIA_COST = 2 - self.NONPREFERRED_COST = 4 - self.PREFERRED_COST = 1 + # list of the source/target grid coordinates + self.source = [] + self.target = [] + self.track_width = track_width + self.track_widths = [self.track_width, self.track_width, 1.0] + self.track_factor = [1/self.track_width, 1/self.track_width, 1.0] + + # The bounds are in grids for this + # This is really lower left bottom layer and upper right top layer in 3D. + self.ll = vector3d(ll.x,ll.y,0).scale(self.track_factor).round() + self.ur = vector3d(ur.x,ur.y,1).scale(self.track_factor).round() + # let's leave the map sparse, cells are created on demand to reduce memory self.map={} - def set_blocked(self,n): - self.add_map(n) - self.map[n].blocked=True + def add_all_grids(self): + for x in range(self.ll.x, self.ur.x, 1): + for y in range(self.ll.y, self.ur.y, 1): + self.add_map(vector3d(x,y,0)) + self.add_map(vector3d(x,y,1)) + + def set_blocked(self,n,value=True): + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + self.set_blocked(item,value) + else: + self.add_map(n) + self.map[n].blocked=value def is_blocked(self,n): - self.add_map(n) - return self.map[n].blocked + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + if self.is_blocked(item): + return True + else: + return False + else: + self.add_map(n) + return self.map[n].blocked - def add_blockage_shape(self,ll,ur,z): - debug.info(3,"Adding 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): - for y in range(int(ll[1]),int(ur[1])+1): - block_list.append(vector3d(x,y,z)) + def set_path(self,n,value=True): + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + self.set_path(item,value) + else: + self.add_map(n) + self.map[n].path=value - self.add_blockage(block_list) + def clear_blockages(self): + self.set_blocked(set(self.map.keys()),False) + + def set_source(self,n,value=True): + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + self.set_source(item,value) + else: + self.add_map(n) + self.map[n].source=value + self.source.append(n) + + def set_target(self,n,value=True): + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + self.set_target(item,value) + else: + self.add_map(n) + self.map[n].target=value + self.target.append(n) - def add_blockage(self,block_list): - debug.info(2,"Adding blockage list={0}".format(str(block_list))) - for n in block_list: - self.set_blocked(n) + + def add_source(self,track_list,value=True): + debug.info(3,"Adding source list={0}".format(str(track_list))) + for n in track_list: + debug.info(4,"Adding source ={0}".format(str(n))) + self.set_source(n,value) + self.set_blocked(n,False) - def add_map(self,p): + + def add_target(self,track_list,value=True): + debug.info(3,"Adding target list={0}".format(str(track_list))) + for n in track_list: + debug.info(4,"Adding target ={0}".format(str(n))) + self.set_target(n,value) + self.set_blocked(n,False) + + def is_target(self,point): + """ + Point is in the target set, so we are done. + """ + return point in self.target + + def add_map(self,n): """ Add a point to the map if it doesn't exist. """ - if p not in self.map.keys(): - self.map[p]=cell() + if isinstance(n, (list,tuple,set,frozenset)): + for item in n: + self.add_map(item) + else: + if n not in self.map.keys(): + self.map[n]=grid_cell() - def add_path(self,path): + + def block_path(self,path): """ - Mark the path in the routing grid for visualization + Mark the path in the routing grid as blocked. + Also unsets the path flag. """ - self.path=path - for p in path: - self.map[p].path=True - - def cost(self,path): - """ - The cost of the path is the length plus a penalty for the number - of vias. We assume that non-preferred direction is penalized. - """ - - # Ignore the source pin layer change, FIXME? - def pairwise(iterable): - "s -> (s0,s1), (s1,s2), (s2, s3), ..." - a, b = tee(iterable) - next(b, None) - return zip(a, b) - - - plist = pairwise(path) - cost = 0 - for p0,p1 in plist: - if p0.z != p1.z: # via - cost += self.VIA_COST - elif p0.x != p1.x: # horizontal - cost += self.NONPREFERRED_COST if (p0.z == 1) else self.PREFERRED_COST - elif p0.y != p1.y: # vertical - cost += self.NONPREFERRED_COST if (p0.z == 0) else self.PREFERRED_COST - else: - debug.error("Non-changing direction!") - - return cost + path.set_path(False) + path.set_blocked(True) + diff --git a/compiler/router/cell.py b/compiler/router/grid_cell.py similarity index 93% rename from compiler/router/cell.py rename to compiler/router/grid_cell.py index e70e3474..3f145ef4 100644 --- a/compiler/router/cell.py +++ b/compiler/router/grid_cell.py @@ -1,10 +1,9 @@ -class cell: +class grid_cell: """ A single cell that can be occupied in a given layer, blocked, visited, etc. """ def __init__(self): - self.visited = False self.path = False self.blocked = False self.source = False @@ -17,7 +16,6 @@ class cell: Reset the dynamic info about routing. The pins/blockages are not reset so that they can be reused. """ - self.visited=False self.min_cost=-1 self.min_path=None self.blocked=False diff --git a/compiler/router/grid_path.py b/compiler/router/grid_path.py new file mode 100644 index 00000000..437b6acb --- /dev/null +++ b/compiler/router/grid_path.py @@ -0,0 +1,227 @@ +import debug +from vector3d import vector3d +from itertools import tee +from grid import grid +from grid_cell import grid_cell +from direction import direction + +class grid_path: + """ + A grid path is a list of lists of grid cells. + It can have a width that is more than one cell. + All of the sublists will be the same dimension. + Cells should be continguous. + It can have a name to define pin shapes as well. + """ + + def __init__(self, items=[], name=""): + self.name = name + if items: + self.pathlist = [items] + else: + self.pathlist = [] + + def __str__(self): + #import pprint + p = str(self.pathlist) #pprint.pformat(self.pathlist) + if self.name != "": + return (str(self.name) + " : " + p) + return p + + def __setitem__(self, index, value): + """ + override setitem function + can set value by pathinstance[index]=value + """ + self.pathlist[index]=value + + def __getitem__(self, index): + """ + override getitem function + can get value by value=pathinstance[index] + """ + return self.pathlist[index] + + def __contains__(self, key): + """ + Determine if cell exists in this path + """ + # FIXME: Could maintain a hash to make in O(1) + for sublist in self.pathlist: + for item in sublist: + if item == key: + return True + else: + return False + + def __add__(self, items): + """ + Override add to do append + """ + return self.pathlist.extend(items) + + def __len__(self): + return len(self.pathlist) + + def trim_last(self): + """ + Drop the last item + """ + if len(self.pathlist)>0: + self.pathlist.pop() + + def trim_first(self): + """ + Drop the first item + """ + if len(self.pathlist)>0: + self.pathlist.pop(0) + + def append(self,item): + """ + Append the list of items to the cells + """ + self.pathlist.append(item) + + def extend(self,item): + """ + Extend the list of items to the cells + """ + self.pathlist.extend(item) + + def set_path(self,value=True): + for sublist in self.pathlist: + for p in sublist: + p.path=value + + def set_blocked(self,value=True): + for sublist in self.pathlist: + for p in sublist: + p.blocked=value + + def get_grids(self): + """ + Return a set of all the grids in this path. + """ + newset = set() + for sublist in self.pathlist: + newset.update(sublist) + return newset + + def get_wire_grids(self, start_index, end_index): + """ + Return a set of all the wire grids in this path. + These are the indices in the wave path in a certain range. + """ + newset = set() + for sublist in self.pathlist: + newset.update(sublist[start_index:end_index]) + return newset + + def cost(self): + """ + The cost of the path is the length plus a penalty for the number + of vias. We assume that non-preferred direction is penalized. + This cost only works with 1 wide tracks. + """ + + # Ignore the source pin layer change, FIXME? + def pairwise(iterable): + "s -> (s0,s1), (s1,s2), (s2, s3), ..." + a, b = tee(iterable) + next(b, None) + return zip(a, b) + + plist = list(pairwise(self.pathlist)) + cost = 0 + for p0list,p1list in plist: + # This is because they are "waves" so pick the first item + p0=p0list[0] + p1=p1list[0] + + if p0.z != p1.z: # via + cost += grid.VIA_COST + elif p0.x != p1.x and p0.z==1: # horizontal on vertical layer + cost += grid.NONPREFERRED_COST + elif p0.y != p1.y and p0.z==0: # vertical on horizontal layer + cost += grid.NONPREFERRED_COST + else: + cost += grid.PREFERRED_COST + + return cost + + def expand_dirs(self,up_down_too=True): + """ + Expand from the end in each of the four cardinal directions plus up + or down but not expanding to blocked cells. Expands in all + directions regardless of preferred directions. + + If the width is more than one, it can only expand in one direction + (for now). This is assumed for the supply router for now. + + """ + neighbors = [] + + for d in list(direction): + if not up_down_too and (d==direction.UP or d==direction.DOWN): + continue + n = self.neighbor(d) + if n: + neighbors.append(n) + + 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) + + newwave = [point + offset for point in self.pathlist[-1]] + + if newwave in self.pathlist: + return None + elif newwave[0].z>1 or newwave[0].z<0: + return None + + return newwave + + + def set_layer(self, zindex): + new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave] + self.pathlist = new_pathlist + + + def overlap(self, other): + """ + Return the overlap waves ignoring different layers + """ + + my_zindex = self.pathlist[0][0].z + other_flat_cells = [vector3d(item.x,item.y,my_zindex) for wave in other.pathlist for item in wave] + # This keeps the wave structure of the self layer + shared_waves = [] + for wave in self.pathlist: + for item in wave: + # If any item in the wave is not contained, skip it + if not item in other_flat_cells: + break + else: + shared_waves.append(wave) + + if len(shared_waves)>0: + ll = shared_waves[0][0] + ur = shared_waves[-1][-1] + return [ll,ur] + return None + diff --git a/compiler/router/grid_utils.py b/compiler/router/grid_utils.py new file mode 100644 index 00000000..62caebe9 --- /dev/null +++ b/compiler/router/grid_utils.py @@ -0,0 +1,111 @@ +""" +Some utility functions for sets of grid cells. +""" + +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 remove_border(curset, direct): + """ + Remove the cells on a given border. + """ + border = get_border(curset, direct) + curset.difference_update(border) + + +def get_upper_right(curset): + ur = None + for p in curset: + if ur == None or (p.x>=ur.x and p.y>=ur.y): + ur = p + return ur + +def get_lower_left(curset): + ll = None + for p in curset: + if ll == None or (p.x<=ll.x and p.y<=ll.y): + ll = p + 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.x1 + def set_layers(self, layers): - """Allows us to change the layers that we are routing on. First layer + """ + 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 - (horiz_layer, via_layer, vert_layer) = self.layers + (self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers - self.vert_layer_name = vert_layer - self.vert_layer_width = tech.drc["minwidth_{0}".format(vert_layer)] - self.vert_layer_spacing = tech.drc[str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name)] - self.vert_layer_number = tech.layer[vert_layer] - - self.horiz_layer_name = horiz_layer - self.horiz_layer_width = tech.drc["minwidth_{0}".format(horiz_layer)] - self.horiz_layer_spacing = tech.drc[str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)] - self.horiz_layer_number = tech.layer[horiz_layer] - - # Contacted track spacing. + # 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 @@ -87,26 +140,37 @@ class router: 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 find_pin(self,pin_name): - """ - Finds the pin shapes and converts to tracks. - Pin can either be a label or a location,layer pair: [[x,y],layer]. + def retrieve_pins(self,pin_name): """ - - shape_list=self.layout.getPinShapeByLabel(str(pin_name)) - pin_list = [] + Retrieve the pin shapes from the layout. + """ + shape_list=self.layout.getAllPinShapesByLabel(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])] pin = pin_layout(pin_name, rect, layer) - pin_list.append(pin) + pin_set.add(pin) - debug.check(len(pin_list)>0,"Did not find any pin shapes for {0}.".format(str(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) - return pin_list + for pin in self.pins[pin_name]: + debug.info(2,"Retrieved pin {}".format(str(pin))) + + + + def find_pins(self,pin_name): + """ + Finds the pin shapes and converts to tracks. + Pin can either be a label or a location,layer pair: [[x,y],layer]. + """ + self.retrieve_pins(pin_name) + self.analyze_pins(pin_name) def find_blockages(self): @@ -115,23 +179,75 @@ class router: This doesn't consider whether the obstacles will be pins or not. They get reset later if they are not actually a blockage. """ - #for layer in [self.vert_layer_number,self.horiz_layer_number]: - # self.get_blockages(layer) - self.get_blockages(self.horiz_layer_number) + for layer in [self.vert_layer_number,self.horiz_layer_number]: + self.retrieve_blockages(layer) - def clear_pins(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.pins = {} - # DO NOT clear the blockages as these don't change - self.rg.reinit() + + # # 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. + for pin in pin_list: + self.find_pins(pin) + + # This will get all shapes as blockages and convert to grid units + # This ignores shapes that were pins + self.find_blockages() + + # Convert the blockages to grid units + self.convert_blockages() + + # This will convert the pins to grid units + # It must be done after blockages to ensure no DRCs between expanded pins and blocked grids + for pin in pin_list: + self.convert_pins(pin) + + # Enclose the continguous grid units in a metal rectangle to fix some DRCs + self.enclose_pins() + + def prepare_blockages(self, pin_name): + """ + Reset and add all of the blockages in the design. + Names is a list of pins to add as a blockage. + """ + debug.info(3,"Preparing blockages.") + + # Start fresh. Not the best for run-time, but simpler. + self.clear_blockages() + # This adds the initial blockges of the design + #print("BLOCKING:",self.blocked_grids) + self.set_blockages(self.blocked_grids,True) + + # Block all of the supply rails (some will be unblocked if they're a target) + 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) + + + # 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) + + # 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 @@ -147,7 +263,7 @@ class router: """ Scale a shape (two vector list) to user units """ - unit_factor = [tech.GDS["unit"][0]] * 2 + unit_factor = [GDS["unit"][0]] * 2 ll=shape[0].scale(unit_factor) ur=shape[1].scale(unit_factor) return [ll,ur] @@ -180,23 +296,6 @@ class router: return 2 - def contract_path(self,path): - """ - Remove intermediate points in a rectilinear path. - """ - newpath = [path[0]] - for i in range(1,len(path)-1): - prev_inertia=self.get_inertia(path[i-1],path[i]) - next_inertia=self.get_inertia(path[i],path[i+1]) - # if we switch directions, add the point, otherwise don't - if prev_inertia!=next_inertia: - newpath.append(path[i]) - - # always add the last path - newpath.append(path[-1]) - return newpath - - def add_path_blockages(self): """ Go through all of the past paths and add them as blockages. @@ -205,36 +304,55 @@ class router: for path in self.paths: for grid in path: self.rg.set_blocked(grid) + + def clear_blockages(self): + """ + Clear all blockages on the grid. + """ + debug.info(3,"Clearing all blockages") + self.rg.clear_blockages() + def set_blockages(self, blockages, value=True): + """ Flag the blockages in the grid """ + self.rg.set_blocked(blockages, value) - def add_blockages(self): - """ Add the blockages except the pin shapes. Also remove the pin shapes from the blockages list. """ - # Join all the pin shapes into one big list - all_pins = [item for sublist in list(self.pins.values()) for item in sublist] + 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)) - # Do an n^2 check to see if any shapes are the same, otherwise add them - # FIXME: Make faster, but number of pins won't be *that* large - real_blockages = [] + block_list = [] + for x in range(int(ll[0]),int(ur[0])+1): + for y in range(int(ll[1]),int(ur[1])+1): + block_list.append(vector3d(x,y,z)) + + return set(block_list) + + def convert_blockage(self, blockage): + """ + Convert a pin layout blockage shape to routing grid tracks. + """ + # Inflate the blockage by half a spacing rule + [ll,ur]=self.convert_blockage_to_tracks(blockage.inflate()) + zlayer = self.get_zindex(blockage.layer_num) + blockage_tracks = self.get_blockage_tracks(ll, ur, zlayer) + return blockage_tracks + + def convert_blockages(self): + """ Convert blockages to grid tracks. """ + for blockage in self.blockages: - for pin in all_pins: - # If the blockage overlaps the pin and is on the same layer, - # it must be connected, so skip it. - if blockage==pin: - debug.info(1,"Removing blockage for pin {}".format(str(pin))) - break - else: - debug.info(2,"Adding blockage {}".format(str(blockage))) - # Inflate the blockage by spacing rule - [ll,ur]=self.convert_blockage_to_tracks(blockage.inflate()) - zlayer = self.get_zindex(blockage.layer_num) - self.rg.add_blockage_shape(ll,ur,zlayer) - real_blockages.append(blockage) - - # Remember the filtered blockages - self.blockages = real_blockages - + debug.info(3,"Converting blockage {}".format(str(blockage))) + blockage_list = self.convert_blockage(blockage) + self.blocked_grids.update(blockage_list) - def get_blockages(self, layer_num): + + def retrieve_blockages(self, layer_num): """ Recursive find boundaries as blockages to the routing grid. """ @@ -245,24 +363,34 @@ class router: ur = vector(boundary[2],boundary[3]) rect = [ll,ur] new_pin = pin_layout("blockage{}".format(len(self.blockages)),rect,layer_num) - self.blockages.append(new_pin) + + # If there is a rectangle that is the same in the pins, it isn't a blockage! + if new_pin not in self.all_pins: + self.blockages.append(new_pin) - def convert_point_to_units(self,p): + def convert_point_to_units(self, p): """ Convert a path set of tracks to center line path. """ pt = vector3d(p) - pt=pt.scale(self.track_widths[0],self.track_widths[1],1) + pt = pt.scale(self.track_widths[0],self.track_widths[1],1) return pt - def convert_blockage_to_tracks(self,shape): + def convert_wave_to_units(self, wave): + """ + Convert a wave to a set of center points + """ + return [self.convert_point_to_units(i) for i in wave] + + + def convert_blockage_to_tracks(self, shape): """ Convert a rectangular blockage shape into track units. """ (ll,ur) = shape - # ll = snap_to_grid(ll) - # ur = snap_to_grid(ur) + ll = snap_to_grid(ll) + ur = snap_to_grid(ur) # to scale coordinates to tracks debug.info(3,"Converting [ {0} , {1} ]".format(ll,ur)) @@ -281,59 +409,110 @@ class router: # debug.info(0,"Pin {}".format(pin)) return [ll,ur] - def convert_pin_to_tracks(self, pin): + def convert_pin_to_tracks(self, pin_name, pin): """ Convert a rectangular pin shape into a list of track locations,layers. - If no on-grid pins are found, it searches for the nearest off-grid pin(s). - If a pin has insufficent overlap, it returns the blockage list to avoid it. + If no pins are "on-grid" (i.e. sufficient overlap) it makes the one with most overlap if it is not blocked. """ (ll,ur) = pin.rect - #debug.info(1,"Converting [ {0} , {1} ]".format(ll,ur)) + debug.info(3,"Converting pin [ {0} , {1} ]".format(ll,ur)) # scale the size bigger to include neaby tracks ll=ll.scale(self.track_factor).floor() ur=ur.scale(self.track_factor).ceil() - # width depends on which layer it is + # Keep tabs on tracks with sufficient and insufficient overlap + sufficient_list = set() + insufficient_list = set() + zindex=self.get_zindex(pin.layer_num) - if zindex: - width = self.vert_layer_width + for x in range(int(ll[0]),int(ur[0])+1): + for y in range(int(ll[1]),int(ur[1])+1): + debug.info(4,"Converting [ {0} , {1} ]".format(x,y)) + (full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex)) + if full_overlap: + sufficient_list.update([full_overlap]) + if partial_overlap: + insufficient_list.update([partial_overlap]) + + if len(sufficient_list)>0: + return sufficient_list + elif len(insufficient_list)>0: + # If there wasn't a sufficient grid, find the best and patch it to be on grid. + return self.get_best_offgrid_pin(pin, insufficient_list) else: - width = self.horiz_layer_width + debug.error("Unable to find any overlapping grids.", -1) + - track_list = [] - block_list = [] + def get_best_offgrid_pin(self, pin, insufficient_list): + """ + Given a pin and a list of partial overlap grids: + 1) Find the unblocked grids. + 2) If one, use it. + 3) If not, find the greatest overlap. + 4) Add a pin with the most overlap to make it "on grid" + that is not blocked. + """ + #print("INSUFFICIENT LIST",insufficient_list) + # Find the coordinate with the most overlap + best_coord = None + best_overlap = -math.inf + for coord in insufficient_list: + full_rect = self.convert_track_to_pin(coord) + # Compute the overlap with that rectangle + overlap_rect=self.compute_overlap(pin.rect,full_rect) + # Determine the min x or y overlap + min_overlap = min(overlap_rect) + if min_overlap>best_overlap: + best_overlap=min_overlap + best_coord=coord + + return set([best_coord]) - track_area = self.track_width*self.track_width - for x in range(ll[0],ur[0]): - for y in range(ll[1],ur[1]): - #debug.info(1,"Converting [ {0} , {1} ]".format(x,y)) + + 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) - # however, if there is not enough overlap, then if there is any overlap at all, - # we need to block it to prevent routes coming in on that grid - full_rect = self.convert_track_to_shape(vector3d(x,y,zindex)) - overlap_rect=self.compute_overlap(pin.rect,full_rect) - overlap_area = overlap_rect[0]*overlap_rect[1] - #debug.info(1,"Check overlap: {0} {1} max={2}".format(shape,rect,max_overlap)) - - # Assume if more than half the area, it is occupied - overlap_ratio = overlap_area/track_area - if overlap_ratio > 0.5: - track_list.append(vector3d(x,y,zindex)) - # otherwise, the pin may not be accessible, so block it - elif overlap_ratio > 0: - block_list.append(vector3d(x,y,zindex)) - else: - debug.info(4,"No overlap: {0} {1} max={2}".format(pin.rect,rect,max_overlap)) - print("H:",x,y) - if x>38 and x<42 and y>42 and y<45: - print(pin) - print(full_rect, overlap_rect, overlap_ratio) - #debug.warning("Off-grid pin for {0}.".format(str(pin))) - #debug.info(1,"Converted [ {0} , {1} ]".format(ll,ur)) - return (track_list,block_list) + min_width = drc("minwidth_{0}".format(layer_name), width, length) + min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), width, length) - def compute_overlap(self,r1,r2): + 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. + If it does, add additional metal to make the pin "on grid". + If it doesn't, add it to the blocked grid list. + """ + + (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) + + debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_rect, overlap_width)) + # 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)) + 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)) + 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 @@ -348,15 +527,124 @@ class router: return [dx,dy] else: return [0,0] - - def convert_track_to_pin(self,track): + 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 + + + + def convert_track_to_pin(self, track): """ Convert a grid point into a rectangle shape that is centered track in the track and leaves half a DRC space in each direction. """ # space depends on which layer it is - if track[2]==0: + if self.get_layer(track[2])==self.horiz_layer_name: space = 0.5*self.horiz_layer_spacing else: space = 0.5*self.vert_layer_spacing @@ -373,61 +661,642 @@ class router: return [ll,ur] - def convert_track_to_shape(self,track): + def convert_track_to_shape(self, track): """ Convert a grid point into a rectangle shape that occupies the entire centered track. """ # to scale coordinates to tracks - x = track.x*self.track_width - 0.5*self.track_width - y = track.y*self.track_width - 0.5*self.track_width + x = track[0]*self.track_width - 0.5*self.track_width + y = track[1]*self.track_width - 0.5*self.track_width # offset lowest corner object to to (-track halo,-track halo) ll = snap_to_grid(vector(x,y)) ur = snap_to_grid(ll + vector(self.track_width,self.track_width)) return [ll,ur] - - def get_pin(self,pin_name): + def analyze_pins(self, pin_name): """ - Gets the pin shapes only. Doesn't add to grid. + Analyze the shapes of a pin and combine them into groups which are connected. """ - self.pins[pin_name] = self.find_pin(pin_name) + pin_set = self.pins[pin_name] + 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: + debug.info(0,"INITIAL\n",equiv_classes) - def add_pin(self,pin_name,is_source=False): + def compare_classes(class1, class2): + """ + Determine if two classes should be combined and if so return + the combined set. Otherwise, return None. + """ + if local_debug: + debug.info(0,"CLASS1:\n",class1) + debug.info(0,"CLASS2:\n",class2) + # Compare each pin in each class, + # and if any overlap, return the combined the class + for p1 in class1: + for p2 in class2: + if p1.overlaps(p2): + combined_class = class1 | class2 + if local_debug: + debug.info(0,"COMBINE:",pformat(combined_class)) + return combined_class + + if local_debug: + debug.info(0,"NO COMBINE") + return None + + + def combine_classes(equiv_classes): + """ Recursive function to combine classes. """ + if local_debug: + debug.info(0,"\nRECURSE:\n",pformat(equiv_classes)) + if len(equiv_classes)==1: + return(equiv_classes) + + for class1 in equiv_classes: + for class2 in equiv_classes: + if class1 == class2: + continue + class3 = compare_classes(class1, class2) + if class3: + new_classes = equiv_classes + new_classes.remove(class1) + new_classes.remove(class2) + new_classes.append(class3) + return(combine_classes(new_classes)) + else: + return(equiv_classes) + + reduced_classes = combine_classes(equiv_classes) + if local_debug: + debug.info(0,"FINAL ",reduced_classes) + self.pin_groups[pin_name]=reduced_classes + + def convert_pins(self, pin_name): """ - Mark the grids that are in the pin rectangle ranges to have the pin property. - pin can be a location or a label. + 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 - for pin in self.pins[pin_name]: - (pin_in_tracks,blockage_in_tracks)=self.convert_pin_to_tracks(pin) - if (len(pin_in_tracks)>0): - found_pin=True - if is_source: - debug.info(1,"Set source: " + str(pin_name) + " " + str(pin_in_tracks)) - self.rg.add_source(pin_in_tracks) - else: - debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) - self.rg.add_target(pin_in_tracks) - self.rg.add_blockage(blockage_in_tracks) - - if not found_pin: - self.write_debug_gds() - debug.check(found_pin,"Unable to find pin on grid.") + 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) - def write_debug_gds(self): + # 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) + + + 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()1: + newpath.append(path[-1]) + return newpath + + + 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: + self.rg.block_path(path) + + def run_router(self, detour_scale): + """ + This assumes the blockages, source, and target are all set up. + """ + # returns the path in tracks + (path,cost) = self.rg.route(detour_scale) + if path: + debug.info(2,"Found path: cost={0} ".format(cost)) + debug.info(3,str(path)) + self.paths.append(path) + self.add_route(path) + else: + self.write_debug_gds("failed_route.gds") + # clean up so we can try a reroute + self.rg.reinit() + return False + return True + + + def annotate_pin_and_tracks(self, pin, tracks): + """" + Annotate some shapes for debug purposes + """ + debug.info(0,"Annotating\n pin {0}\n tracks {1}".format(pin,tracks)) + for coord in tracks: + (ll,ur) = self.convert_track_to_shape(coord) + self.cell.add_rect(layer="text", + offset=ll, + width=ur[0]-ll[0], + height=ur[1]-ll[1]) + (ll,ur) = self.convert_track_to_pin(coord) + self.cell.add_rect(layer="boundary", + offset=ll, + width=ur[0]-ll[0], + height=ur[1]-ll[1]) + (ll,ur) = pin.rect + self.cell.add_rect(layer="text", + offset=ll, + width=ur[0]-ll[0], + height=ur[1]-ll[1]) + + def write_debug_gds(self, gds_name="debug_route.gds", stop_program=True): """ Write out a GDS file with the routing grid and search information annotated on it. """ - # Only add the debug info to the gds file if we have any debugging on. - # This is because we may reroute a wire with detours and don't want the debug information. - if OPTS.debug_level==0: return - self.add_router_info() - debug.error("Writing debug_route.gds") - self.cell.gds_write("debug_route.gds") + self.cell.gds_write(gds_name) + + if stop_program: + import sys + sys.exit(1) + + def annotate_grid(self, g): + """ + Display grid information in the GDS file for a single grid cell. + """ + shape = self.convert_track_to_shape(g) + partial_track=vector(0,self.track_width/6.0) + self.cell.add_rect(layer="text", + offset=shape[0], + width=shape[1].x-shape[0].x, + height=shape[1].y-shape[0].y) + t=self.rg.map[g].get_type() + + # midpoint offset + off=vector((shape[1].x+shape[0].x)/2, + (shape[1].y+shape[0].y)/2) + if g[2]==1: + # Upper layer is upper right label + type_off=off+partial_track + else: + # Lower layer is lower left label + type_off=off-partial_track + if t!=None: + self.cell.add_label(text=str(t), + layer="text", + offset=type_off) + self.cell.add_label(text="{0},{1}".format(g[0],g[1]), + layer="text", + offset=shape[0], + zoom=0.05) def add_router_info(self): """ @@ -436,63 +1305,60 @@ class router: called once or the labels will overlap. """ debug.info(0,"Adding router info") - grid_keys=self.rg.map.keys() - partial_track=vector(0,self.track_width/6.0) - for g in grid_keys: - shape = self.convert_track_to_shape(g) - self.cell.add_rect(layer="text", - offset=shape[0], - width=shape[1].x-shape[0].x, - height=shape[1].y-shape[0].y) - # These are the on grid pins - #rect = self.convert_track_to_pin(g) - #self.cell.add_rect(layer="boundary", - # offset=rect[0], - # width=rect[1].x-rect[0].x, - # height=rect[1].y-rect[0].y) - - t=self.rg.map[g].get_type() - - # midpoint offset - off=vector((shape[1].x+shape[0].x)/2, - (shape[1].y+shape[0].y)/2) - if g[2]==1: - # Upper layer is upper right label - type_off=off+partial_track - else: - # Lower layer is lower left label - type_off=off-partial_track - if t!=None: - self.cell.add_label(text=str(t), - layer="text", - offset=type_off) - self.cell.add_label(text="{0},{1}".format(g[0],g[1]), - layer="text", - offset=shape[0], - zoom=0.05) - - for blockage in self.blockages: - # Display the inflated blockage - (ll,ur) = blockage.inflate() - self.cell.add_rect(layer="blockage", - offset=ll, - width=ur.x-ll.x, - height=ur.y-ll.y) + show_blockages = True + show_blockage_grids = True + show_enclosures = False + show_connectors = False + show_all_grids = True + if show_all_grids: + self.rg.add_all_grids() + for g in self.rg.map.keys(): + self.annotate_grid(g) + + if show_blockages: + # Display the inflated blockage + for blockage in self.blockages: + debug.info(1,"Adding {}".format(blockage)) + (ll,ur) = blockage.inflate() + self.cell.add_rect(layer="text", + offset=ll, + width=ur.x-ll.x, + height=ur.y-ll.y) + if show_blockage_grids: + self.set_blockages(self.blocked_grids,True) + grid_keys=self.rg.map.keys() + for g in grid_keys: + self.annotate_grid(g) + + if show_connectors: + for pin in self.connector_enclosure: + #print("connector: ",str(pin)) + self.cell.add_rect(layer="text", + offset=pin.ll(), + width=pin.width(), + height=pin.height()) + if show_enclosures: + for pin in self.enclosures: + #print("enclosure: ",pin.name,pin.ll(),pin.width(),pin.height()) + self.cell.add_rect(layer="text", + offset=pin.ll(), + width=pin.width(), + height=pin.height()) + # FIXME: This should be replaced with vector.snap_to_grid at some point def snap_to_grid(offset): """ Changes the coodrinate to match the grid settings """ - grid = tech.drc["grid"] - x = offset[0] - y = offset[1] - # this gets the nearest integer value - xgrid = int(round(round((x / grid), 2), 0)) - ygrid = int(round(round((y / grid), 2), 0)) - xoff = xgrid * grid - yoff = ygrid * grid + xoff = snap_val_to_grid(offset[0]) + yoff = snap_val_to_grid(offset[1]) return vector(xoff, yoff) +def snap_val_to_grid(x): + grid = drc("grid") + xgrid = int(round(round((x / grid), 2), 0)) + xoff = xgrid * grid + return xoff diff --git a/compiler/router/astar_grid.py b/compiler/router/signal_grid.py similarity index 50% rename from compiler/router/astar_grid.py rename to compiler/router/signal_grid.py index 65d9e245..0f8d6315 100644 --- a/compiler/router/astar_grid.py +++ b/compiler/router/signal_grid.py @@ -1,55 +1,24 @@ from itertools import tee import debug -from vector3d import vector3d -import grid from heapq import heappush,heappop +from copy import deepcopy -class astar_grid(grid.grid): +from grid import grid +from grid_path import grid_path +from vector3d import vector3d + +class signal_grid(grid): """ Expand the two layer grid to include A* search functions for a source and target. """ - def __init__(self): + def __init__(self, ll, ur, track_factor): """ Create a routing map of width x height cells and 2 in the z-axis. """ - grid.grid.__init__(self) + grid.__init__(self, ll, ur, track_factor) - # list of the source/target grid coordinates - self.source = [] - self.target = [] - # priority queue for the maze routing self.q = [] - def set_source(self,n): - self.add_map(n) - self.map[n].source=True - self.source.append(n) - - def set_target(self,n): - self.add_map(n) - self.map[n].target=True - self.target.append(n) - - - def add_source(self,track_list): - debug.info(2,"Adding source list={0}".format(str(track_list))) - for n in track_list: - if not self.is_blocked(n): - debug.info(3,"Adding source ={0}".format(str(n))) - self.set_source(n) - - def add_target(self,track_list): - debug.info(2,"Adding target list={0}".format(str(track_list))) - for n in track_list: - if not self.is_blocked(n): - self.set_target(n) - - def is_target(self,point): - """ - Point is in the target set, so we are done. - """ - return point in self.target - def reinit(self): """ Reinitialize everything for a new route. """ @@ -73,112 +42,98 @@ class astar_grid(grid.grid): We will use an A* search, so this cost must be pessimistic. Cost so far will be the length of the path. """ - debug.info(4,"Initializing queue.") - - # uniquify the source (and target while we are at it) - self.source = list(set(self.source)) - self.target = list(set(self.target)) + #debug.info(3,"Initializing queue.") # Counter is used to not require data comparison in Python 3.x # Items will be returned in order they are added during cost ties self.counter = 0 for s in self.source: cost = self.cost_to_target(s) - debug.info(1,"Init: cost=" + str(cost) + " " + str([s])) - heappush(self.q,(cost,self.counter,[s])) + debug.info(3,"Init: cost=" + str(cost) + " " + str([s])) + heappush(self.q,(cost,self.counter,grid_path([vector3d(s)]))) self.counter+=1 def route(self,detour_scale): """ This does the A* maze routing with preferred direction routing. + This only works for 1 track wide routes! """ - + # We set a cost bound of the HPWL for run-time. This can be # over-ridden if the route fails due to pruning a feasible solution. - cost_bound = detour_scale*self.cost_to_target(self.source[0])*self.PREFERRED_COST + cost_bound = detour_scale*self.cost_to_target(self.source[0])*grid.PREFERRED_COST + # Check if something in the queue is already a source and a target! + for s in self.source: + if self.is_target(s): + return((grid_path([vector3d(s)]),0)) + # Make sure the queue is empty if we run another route while len(self.q)>0: heappop(self.q) - + # Put the source items into the queue self.init_queue() cheapest_path = None cheapest_cost = None - + + # Keep expanding and adding to the priority queue until we are done while len(self.q)>0: # should we keep the path in the queue as well or just the final node? - (cost,count,path) = heappop(self.q) - debug.info(2,"Queue size: size=" + str(len(self.q)) + " " + str(cost)) - debug.info(3,"Expanding: cost=" + str(cost) + " " + str(path)) + (cost,count,curpath) = heappop(self.q) + debug.info(3,"Queue size: size=" + str(len(self.q)) + " " + str(cost)) + debug.info(4,"Expanding: cost=" + str(cost) + " " + str(curpath)) # expand the last element - neighbors = self.expand_dirs(path) - debug.info(3,"Neighbors: " + str(neighbors)) + neighbors = self.expand_dirs(curpath) + debug.info(4,"Neighbors: " + str(neighbors)) for n in neighbors: + # make a new copy of the path to not update the old ones + newpath = deepcopy(curpath) # node is added to the map by the expand routine - newpath = path + [n] + newpath.append(n) # check if we hit the target and are done - if self.is_target(n): - return (newpath,self.cost(newpath)) - elif not self.map[n].visited: + if self.is_target(n[0]): # This uses the [0] item because we are assuming 1-track wide + return (newpath,newpath.cost()) + else: # current path cost + predicted cost - current_cost = self.cost(newpath) - target_cost = self.cost_to_target(n) + current_cost = newpath.cost() + target_cost = self.cost_to_target(n[0]) predicted_cost = current_cost + target_cost # only add the cost if it is less than our bound if (predicted_cost < cost_bound): - if (self.map[n].min_cost==-1 or current_cost=0 and not self.is_blocked(down) and not down in path: - neighbors.append(down) - - return neighbors + return unblocked_neighbors def hpwl(self, src, dest): @@ -191,7 +146,7 @@ class astar_grid(grid.grid): hpwl += max(abs(src.y-dest.y),abs(dest.y-src.y)) hpwl += max(abs(src.z-dest.z),abs(dest.z-src.z)) if src.x!=dest.x or src.y!=dest.y: - hpwl += self.VIA_COST + hpwl += grid.VIA_COST return hpwl def cost_to_target(self,source): @@ -202,37 +157,10 @@ class astar_grid(grid.grid): cost = self.hpwl(source,self.target[0]) for t in self.target: cost = min(self.hpwl(source,t),cost) - return cost - - - def cost(self,path): - """ - The cost of the path is the length plus a penalty for the number - of vias. We assume that non-preferred direction is penalized. - """ - - # Ignore the source pin layer change, FIXME? - def pairwise(iterable): - "s -> (s0,s1), (s1,s2), (s2, s3), ..." - a, b = tee(iterable) - next(b, None) - return zip(a, b) - - - plist = pairwise(path) - cost = 0 - for p0,p1 in plist: - if p0.z != p1.z: # via - cost += self.VIA_COST - elif p0.x != p1.x: # horizontal - cost += self.NONPREFERRED_COST if (p0.z == 1) else self.PREFERRED_COST - elif p0.y != p1.y: # vertical - cost += self.NONPREFERRED_COST if (p0.z == 0) else self.PREFERRED_COST - else: - debug.error("Non-changing direction!") return cost - + + def get_inertia(self,p0,p1): """ Sets the direction based on the previous direction we came from. diff --git a/compiler/router/signal_router.py b/compiler/router/signal_router.py index ebc85eb6..eb706b5b 100644 --- a/compiler/router/signal_router.py +++ b/compiler/router/signal_router.py @@ -4,24 +4,21 @@ from contact import contact import math import debug from pin_layout import pin_layout -from vector import vector -from vector3d import vector3d from globals import OPTS from router import router class signal_router(router): - """A router class to read an obstruction map from a gds and plan a + """ + A router class to read an obstruction map from a gds and plan a route on a given layer. This is limited to two layer routes. """ - def __init__(self, gds_name=None, module=None): - """Use the gds file for the blockages with the top module topName and - layers for the layers to route on + def __init__(self, layers, design, gds_filename=None): """ - router.__init__(self, gds_name, module) - - # all the paths we've routed so far (to supplement the blockages) - self.paths = [] + This will route on layers in design. It will get the blockages from + either the gds file name or the design itself (by saving to a gds file). + """ + router.__init__(self, layers, design, gds_filename) def create_routing_grid(self): @@ -33,130 +30,51 @@ class signal_router(router): size = self.ur - self.ll debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) - import astar_grid - self.rg = astar_grid.astar_grid() + import signal_grid + self.rg = signal_grid.signal_grid(self.ll, self.ur, self.track_width) - def route(self, cell, layers, src, dest, detour_scale=5): + def route(self, src, dest, detour_scale=5): """ Route a single source-destination net and return the simplified rectilinear path. Cost factor is how sub-optimal to explore for a feasible route. This is used to speed up the routing when there is not much detouring needed. """ debug.info(1,"Running signal router from {0} to {1}...".format(src,dest)) - self.cell = cell - self.source_pin_name = src - self.target_pin_name = dest + self.pins[src] = [] + self.pins[dest] = [] + # Clear the pins if we have previously routed if (hasattr(self,'rg')): self.clear_pins() else: - # Set up layers and track sizes - self.set_layers(layers) # Creat a routing grid over the entire area # FIXME: This could be created only over the routing region, # but this is simplest for now. self.create_routing_grid() - # This will get all shapes as blockages - self.find_blockages() # Get the pin shapes - self.get_pin(src) - self.get_pin(dest) + self.find_pins_and_blockages([src, dest]) - # Now add the blockages (all shapes except the src/tgt pins) - self.add_blockages() - # Add blockages from previous paths - self.add_path_blockages() - + # Block everything + self.prepare_blockages() + # Clear the pins we are routing + self.set_blockages(self.pin_components[src],False) + self.set_blockages(self.pin_components[dest],False) + # Now add the src/tgt if they are not blocked by other shapes - self.add_pin(src,True) - self.add_pin(dest,False) + self.add_source(src) + self.add_target(dest) - - # returns the path in tracks - (path,cost) = self.rg.route(detour_scale) - if path: - debug.info(1,"Found path: cost={0} ".format(cost)) - debug.info(2,str(path)) - self.add_route(path) - return True - else: - self.write_debug_gds() - # clean up so we can try a reroute - self.clear_pins() - - - return False + if not self.run_router(detour_scale=detour_scale): + self.write_debug_gds(stop_program=False) + return False + + self.write_debug_gds(stop_program=False) + return True - def add_route(self,path): - """ - Add the current wire route to the given design instance. - """ - debug.info(3,"Set path: " + str(path)) - - # Keep track of path for future blockages - self.paths.append(path) - - # This is marked for debug - self.rg.add_path(path) - - # For debugging... if the path failed to route. - if False or path==None: - self.write_debug_gds() - - - # First, simplify the path for - #debug.info(1,str(self.path)) - contracted_path = self.contract_path(path) - debug.info(1,str(contracted_path)) - - # convert the path back to absolute units from tracks - abs_path = map(self.convert_point_to_units,contracted_path) - debug.info(1,str(abs_path)) - self.cell.add_route(self.layers,abs_path) - - - def get_inertia(self,p0,p1): - """ - Sets the direction based on the previous direction we came from. - """ - # direction (index) of movement - if p0.x!=p1.x: - return 0 - elif p0.y!=p1.y: - return 1 - else: - # z direction - return 2 - - def contract_path(self,path): - """ - Remove intermediate points in a rectilinear path. - """ - newpath = [path[0]] - for i in range(1,len(path)-1): - prev_inertia=self.get_inertia(path[i-1],path[i]) - next_inertia=self.get_inertia(path[i],path[i+1]) - # if we switch directions, add the point, otherwise don't - if prev_inertia!=next_inertia: - newpath.append(path[i]) - - # always add the last path - newpath.append(path[-1]) - return newpath - - - 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) diff --git a/compiler/router/supply_grid.py b/compiler/router/supply_grid.py index b9e1f81b..bd9dab87 100644 --- a/compiler/router/supply_grid.py +++ b/compiler/router/supply_grid.py @@ -1,27 +1,79 @@ import debug from vector3d import vector3d -import grid +from grid import grid +from signal_grid import signal_grid +from grid_path import grid_path +from direction import direction -class supply_grid(grid.grid): +class supply_grid(signal_grid): """ - A two layer routing map. Each cell can be blocked in the vertical - or horizontal layer. + This routes a supply grid. It is derived from a signal grid because it still + routes the pins to the supply rails using the same routines. + It has a few extra routines to support "waves" which are multiple track wide + directional routes (no bends). """ - def __init__(self): + def __init__(self, ll, ur, track_width): """ Create a routing map of width x height cells and 2 in the z-axis. """ - grid.grid.__init__(self) + signal_grid.__init__(self, ll, ur, track_width) - # list of the vdd/gnd rail cells - self.vdd_rails = [] - self.gnd_rails = [] - def reinit(self): """ Reinitialize everything for a new route. """ - + self.source = [] + self.target = [] # Reset all the cells in the map for p in self.map.values(): p.reset() + def find_start_wave(self, wave, width, direct): + """ + Finds the first loc starting at loc and up that is open. + Returns None if it reaches max size first. + """ + # Don't expand outside the bounding box + if wave[0].x > self.ur.x: + return None + if wave[-1].y > self.ur.y: + return None + + while wave and self.is_wave_blocked(wave): + wf=grid_path(wave) + wave=wf.neighbor(direct) + # Bail out if we couldn't increment futher + if wave[0].x > self.ur.x or wave[-1].y > self.ur.y: + return None + # Return a start if it isn't blocked + if not self.is_wave_blocked(wave): + return wave + + return wave + + + def is_wave_blocked(self, wave): + """ + Checks if any of the locations are blocked + """ + for v in wave: + if self.is_blocked(v): + return True + else: + return False + + + def probe(self, wave, direct): + """ + Expand the wave until there is a blockage and return + the wave path. + """ + wave_path = grid_path() + while wave and not self.is_wave_blocked(wave): + if wave[0].x > self.ur.x or wave[-1].y > self.ur.y: + break + wave_path.append(wave) + wave = wave_path.neighbor(direct) + + return wave_path + + diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index 908e8686..dd9c3fd1 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -3,12 +3,13 @@ import tech from contact import contact import math import debug -import grid -from pin_layout import pin_layout -from vector import vector -from vector3d import vector3d from globals import OPTS +from pin_layout import pin_layout +from vector3d import vector3d from router import router +from direction import direction +import grid +import grid_utils class supply_router(router): """ @@ -16,131 +17,504 @@ class supply_router(router): routes a grid to connect the supply on the two layers. """ - def __init__(self, gds_name=None, module=None): - """Use the gds file for the blockages with the top module topName and - layers for the layers to route on + def __init__(self, layers, design, gds_filename=None): """ - router.__init__(self, gds_name, module) - - self.pins = {} + This will route on layers in design. It will get the blockages from + either the gds file name or the design itself (by saving to a gds file). + """ + router.__init__(self, layers, design, gds_filename) - - def clear_pins(self): - """ - Convert the routed path to blockages. - Keep the other blockages unchanged. - """ - self.pins = {} - self.rg.reinit() - + # We over-ride the regular router costs to allow + # more off-direction router in the supply grid + grid.VIA_COST = 1 + grid.NONPREFERRED_COST = 1 + grid.PREFERRED_COST = 1 - def route(self, cell, layers, vdd_name="vdd", gnd_name="gnd"): + # The list of supply rails (grid sets) that may be routed + self.supply_rails = {} + self.supply_rail_wires = {} + # This is the same as above but as a sigle set for the all the rails + self.supply_rail_tracks = {} + self.supply_rail_wire_tracks = {} + + # Power rail width in grid units. + self.rail_track_width = 2 + + + def create_routing_grid(self): """ - Route a single source-destination net and return - the simplified rectilinear path. + Create a sprase routing grid with A* expansion functions. + """ + size = self.ur - self.ll + debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) + + import supply_grid + self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width) + + def route(self, vdd_name="vdd", gnd_name="gnd"): + """ + Add power supply rails and connect all pins to these rails. """ debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, gnd_name)) - self.cell = cell - self.pins[vdd_name] = [] - self.pins[gnd_name] = [] + self.vdd_name = vdd_name + self.gnd_name = gnd_name # Clear the pins if we have previously routed if (hasattr(self,'rg')): self.clear_pins() else: - # Set up layers and track sizes - self.set_layers(layers) # Creat a routing grid over the entire area # FIXME: This could be created only over the routing region, # but this is simplest for now. self.create_routing_grid() - # This will get all shapes as blockages - self.find_blockages() # Get the pin shapes - self.get_pin(vdd_name) - self.get_pin(gnd_name) - - # Now add the blockages (all shapes except the src/tgt pins) - self.add_blockages() - # Add blockages from previous routes - self.add_path_blockages() + self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) + #self.write_debug_gds("pin_enclosures.gds",stop_program=False) - # source pin will be a specific layout pin - # target pin will be the rails only + # Add the supply rails in a mesh network and connect H/V with vias + # Block everything + self.prepare_blockages(self.gnd_name) + # Determine the rail locations + self.route_supply_rails(self.gnd_name,0) + + # Block everything + self.prepare_blockages(self.vdd_name) + # Determine the rail locations + self.route_supply_rails(self.vdd_name,1) + #self.write_debug_gds("debug_rails.gds",stop_program=True) + + remaining_vdd_pin_indices = self.route_simple_overlaps(vdd_name) + remaining_gnd_pin_indices = self.route_simple_overlaps(gnd_name) + #self.write_debug_gds("debug_simple_route.gds",stop_program=True) + + # Route the supply pins to the supply rails + # Route vdd first since we want it to be shorter + self.route_pins_to_rails(vdd_name, remaining_vdd_pin_indices) + self.route_pins_to_rails(gnd_name, remaining_gnd_pin_indices) + #self.write_debug_gds("debug_pin_routes.gds",stop_program=True) + + #self.write_debug_gds("final.gds") + + return True + + + + + + def route_simple_overlaps(self, pin_name): + """ + This checks for simple cases where a pin component already overlaps a supply rail. + It will add an enclosure to ensure the overlap in wide DRC rule cases. + """ + num_components = self.num_pin_components(pin_name) + remaining_pins = [] + supply_tracks = self.supply_rail_tracks[pin_name] + + for index in range(num_components): + pin_in_tracks = self.pin_grids[pin_name][index] + common_set = supply_tracks & pin_in_tracks + + if len(common_set)==0: + # if no overlap, add it to the complex route pins + remaining_pins.append(index) + else: + self.create_simple_overlap_enclosure(pin_name, common_set) - # returns the path in tracks - # (path,cost) = self.rg.route(detour_scale) - # if path: - # debug.info(1,"Found path: cost={0} ".format(cost)) - # debug.info(2,str(path)) - # self.add_route(path) - # return True - # else: - # self.write_debug_gds() - # # clean up so we can try a reroute - # self.clear_pins() + return remaining_pins + + def recurse_simple_overlap_enclosure(self, pin_name, 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.supply_rail_tracks[pin_name] + supply_wire_tracks = self.supply_rail_wire_tracks[pin_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(pin_name, 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() - self.write_debug_gds() + return new_set + + 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()) + + + + + def finalize_supply_rails(self, name): + """ + Determine which supply rails overlap and can accomodate a via. + Remove any supply rails that do not have a via since they are disconnected. + NOTE: It is still possible though unlikely that there are disconnected groups of rails. + """ + + all_rails = self.supply_rail_wires[name] + + connections = set() + via_areas = [] + for i1,r1 in enumerate(all_rails): + # Only consider r1 horizontal rails + e = next(iter(r1)) + if e.z==1: + continue + + # We need to move this rail to the other layer for the z indices to match + # during the intersection. This also makes a copy. + new_r1 = {vector3d(i.x,i.y,1) for i in r1} + + # If horizontal, subtract off the left/right track to prevent end of rail via + #ll = grid_utils.get_lower_left(new_r1) + #ur = grid_utils.get_upper_right(new_r1) + grid_utils.remove_border(new_r1, direction.EAST) + grid_utils.remove_border(new_r1, direction.WEST) + + for i2,r2 in enumerate(all_rails): + # Never compare to yourself + if i1==i2: + continue + + # Only consider r2 vertical rails + e = next(iter(r2)) + if e.z==0: + continue + + # Need to maek a copy to consider via overlaps to ignore the end-caps + new_r2 = r2.copy() + grid_utils.remove_border(new_r2, direction.NORTH) + grid_utils.remove_border(new_r2, direction.SOUTH) + + # Determine if we hhave sufficient overlap and, if so, + # remember: + # the indices to determine a rail is connected to another + # 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) + via_areas.append(overlap) + + # Go through and add the vias at the center of the intersection + for area in via_areas: + ll = grid_utils.get_lower_left(area) + ur = grid_utils.get_upper_right(area) + center = (ll + ur).scale(0.5,0.5,0) + 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 + # 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: + 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)) + self.supply_rails[name].pop(rail_index) + self.supply_rail_wires[name].pop(rail_index) + + # Make the supply rails into a big giant set of grids for easy blockages. + # Must be done after we determine which ones are connected. + self.create_supply_track_set(name) + + + def add_supply_rails(self, name): + """ + Add the shapes that represent the routed supply rails. + This is after the paths have been pruned and only include rails that are + connected with vias. + """ + for rail in self.supply_rails[name]: + ll = grid_utils.get_lower_left(rail) + 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)) + self.cell.add_layout_pin(text=name, + layer=pin.layer, + offset=pin.ll(), + width=pin.width(), + height=pin.height()) + + def compute_supply_rail_dimensions(self): + """ + Compute the supply rail dimensions including wide metal spacing rules. + """ + + self.max_yoffset = self.rg.ur.y + self.max_xoffset = self.rg.ur.x + + # Longest length is conservative + rail_length = max(self.max_yoffset,self.max_xoffset) + # Convert the number of tracks to dimensions to get the design rule spacing + rail_width = self.track_width*self.rail_track_width + + # Get the conservative width and spacing of the top rails + (horizontal_width, horizontal_space) = self.get_layer_width_space(0, rail_width, rail_length) + (vertical_width, vertical_space) = self.get_layer_width_space(1, rail_width, rail_length) + width = max(horizontal_width, vertical_width) + space = max(horizontal_space, vertical_space) + + # This is the supply rail pitch in terms of routing grids + # i.e. a rail of self.rail_track_width needs this many tracks including + # space + track_pitch = self.rail_track_width*width + space + + # Determine the pitch (in tracks) of the rail wire + spacing + self.supply_rail_width = math.ceil(track_pitch/self.track_width) + debug.info(1,"Rail step: {}".format(self.supply_rail_width)) + + # Conservatively determine the number of tracks that the rail actually occupies + space_tracks = math.ceil(space/self.track_width) + self.supply_rail_wire_width = self.supply_rail_width - space_tracks + debug.info(1,"Rail wire tracks: {}".format(self.supply_rail_wire_width)) + total_space = self.supply_rail_width - self.supply_rail_wire_width + self.supply_rail_space_width = math.floor(0.5*total_space) + debug.info(1,"Rail space tracks: {} (on both sides)".format(self.supply_rail_space_width)) + + + def compute_supply_rails(self, name, supply_number): + """ + Compute the unblocked locations for the horizontal and vertical supply rails. + Go in a raster order from bottom to the top (for horizontal) and left to right + (for vertical). Start with an initial start_offset in x and y direction. + """ + + self.supply_rails[name]=[] + self.supply_rail_wires[name]=[] + + start_offset = supply_number*self.supply_rail_width + + # Horizontal supply rails + for offset in range(start_offset, self.max_yoffset, 2*self.supply_rail_width): + # Seed the function at the location with the given width + wave = [vector3d(0,offset+i,0) for i in range(self.supply_rail_width)] + # 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) + else: + wave = None + + + # Vertical supply rails + max_offset = self.rg.ur.x + for offset in range(start_offset, self.max_xoffset, 2*self.supply_rail_width): + # Seed the function at the location with the given width + wave = [vector3d(offset+i,0,1) for i in range(self.supply_rail_width)] + # 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) + else: + wave = None + + def find_supply_rail(self, name, seed_wave, direct): + """ + Find a start location, probe in the direction, and see if the rail is big enough + to contain a via, and, if so, add it. + """ + start_wave = self.find_supply_rail_start(name, seed_wave, direct) + 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 + + def find_supply_rail_start(self, name, seed_wave, direct): + """ + This finds the first valid starting location and routes a supply rail + in the given direction. + It returns the space after the end of the rail to seed another call for multiple + supply rails in the same "track" when there is a blockage. + """ + # Sweep to find an initial unblocked valid wave + start_wave = self.rg.find_start_wave(seed_wave, len(seed_wave), direct) + + return start_wave + + def probe_supply_rail(self, name, start_wave, direct): + """ + This finds the first valid starting location and routes a supply rail + in the given direction. + It returns the space after the end of the rail to seed another call for multiple + supply rails in the same "track" when there is a blockage. + """ + + # Expand the wave to the right + wave_path = self.rg.probe(start_wave, direct) + + if not wave_path: + return None + + # drop the first and last steps to leave escape routing room + # around the blockage that stopped the probe + # except, don't drop the first if it is the first in a row/column + if (direct==direction.NORTH and start_wave[0].y>0): + wave_path.trim_first() + elif (direct == direction.EAST and start_wave[0].x>0): + wave_path.trim_first() + + wave_path.trim_last() + + return wave_path + + def approve_supply_rail(self, name, wave_path): + """ + Check if the supply rail is sufficient (big enough) and add it to the + data structure. Return whether it was added or not. + """ + # We must have at least 2 tracks to drop plus 2 tracks for a via + if len(wave_path)>=4*self.rail_track_width: + grid_set = wave_path.get_grids() + self.supply_rails[name].append(grid_set) + + start_wire_index = self.supply_rail_space_width + end_wire_index = self.supply_rail_width - self.supply_rail_space_width + wire_set = wave_path.get_wire_grids(start_wire_index,end_wire_index) + self.supply_rail_wires[name].append(wire_set) + return True + return False - - def add_route(self,path): - """ - Add the current wire route to the given design instance. + + + + + def route_supply_rails(self, name, supply_number): + """ + Route the horizontal and vertical supply rails across the entire design. + Must be done with lower left at 0,0 """ - debug.info(3,"Set path: " + str(path)) - # Keep track of path for future blockages - self.paths.append(path) + # Compute the grid dimensions + self.compute_supply_rail_dimensions() - # This is marked for debug - self.rg.add_path(path) + # Compute the grid locations of the supply rails + self.compute_supply_rails(name, supply_number) + + # Add the supply rail vias (and prune disconnected rails) + self.finalize_supply_rails(name) - # For debugging... if the path failed to route. - if False or path==None: - self.write_debug_gds() + # Add the rails themselves + self.add_supply_rails(name) - # First, simplify the path for - #debug.info(1,str(self.path)) - contracted_path = self.contract_path(path) - debug.info(1,str(contracted_path)) + + def create_supply_track_set(self, pin_name): + """ + Make a single set of all the tracks for the rail and wire itself. + """ + rail_set = set() + for rail in self.supply_rails[pin_name]: + rail_set.update(rail) + self.supply_rail_tracks[pin_name] = rail_set - # convert the path back to absolute units from tracks - abs_path = map(self.convert_point_to_units,contracted_path) - debug.info(1,str(abs_path)) - self.cell.add_route(self.layers,abs_path) + wire_set = set() + for rail in self.supply_rail_wires[pin_name]: + wire_set.update(rail) + self.supply_rail_wire_tracks[pin_name] = wire_set + + + def route_pins_to_rails(self, pin_name, remaining_component_indices): + """ + 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))) + + recent_paths = [] + # For every component + for index in remaining_component_indices: + debug.info(2,"Routing component {0} {1}".format(pin_name, index)) + + self.rg.reinit() + + self.prepare_blockages(pin_name) + + # Add the single component of the pin as the source + # which unmarks it as a blockage too + self.add_pin_component_source(pin_name,index) + + # Add all of the rails as targets + # 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 create_routing_grid(self): - """ - Create a sprase routing grid with A* expansion functions. + def add_supply_rail_target(self, pin_name): """ - # We will add a halo around the boundary - # of this many tracks - size = self.ur - self.ll - debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) + Add the supply rails of given name as a routing target. + """ + debug.info(2,"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 + self.rg.set_blocked(self.supply_rail_tracks[pin_name],False) - import supply_grid - self.rg = supply_grid.supply_grid() - - - ########################## - # Gridded supply route functions - ########################## - def create_grid(self, ll, ur): - """ Create alternating vdd/gnd lines horizontally """ - - self.create_horizontal_grid() - self.create_vertical_grid() - - - def create_horizontal_grid(self): - """ Create alternating vdd/gnd lines horizontally """ - - pass - - def create_vertical_grid(self): - """ Create alternating vdd/gnd lines horizontally """ - pass - + + def set_supply_rail_blocked(self, value=True): + """ + Add the supply rails of given name as a routing target. + """ + debug.info(3,"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/tests/01_no_blockages_test.py b/compiler/router/tests/01_no_blockages_test.py index c7344a64..4197f714 100755 --- a/compiler/router/tests/01_no_blockages_test.py +++ b/compiler/router/tests/01_no_blockages_test.py @@ -37,9 +37,9 @@ class no_blockages_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") - self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) + r=router(layer_stack,self,gds_file) + self.assertTrue(r.route(src="A",dest="B")) r=routing("01_no_blockages_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/01_no_blockages_test_scn3me_subm.gds b/compiler/router/tests/01_no_blockages_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/01_no_blockages_test_scn3me_subm.gds rename to compiler/router/tests/01_no_blockages_test_scn4m_subm.gds diff --git a/compiler/router/tests/02_blockages_test.py b/compiler/router/tests/02_blockages_test.py index 2e85b1c2..6e3bee08 100755 --- a/compiler/router/tests/02_blockages_test.py +++ b/compiler/router/tests/02_blockages_test.py @@ -37,9 +37,9 @@ class blockages_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") - self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) + r=router(layer_stack,self,gds_file) + self.assertTrue(r.route(src="A",dest="B")) r=routing("02_blockages_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/02_blockages_test_scn3me_subm.gds b/compiler/router/tests/02_blockages_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/02_blockages_test_scn3me_subm.gds rename to compiler/router/tests/02_blockages_test_scn4m_subm.gds diff --git a/compiler/router/tests/03_same_layer_pins_test.py b/compiler/router/tests/03_same_layer_pins_test.py index 98ce3a2a..726cd02b 100755 --- a/compiler/router/tests/03_same_layer_pins_test.py +++ b/compiler/router/tests/03_same_layer_pins_test.py @@ -36,9 +36,9 @@ class same_layer_pins_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") - self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) + r=router(layer_stack,self,gds_file) + self.assertTrue(r.route(src="A",dest="B")) r = routing("03_same_layer_pins_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/03_same_layer_pins_test_scn3me_subm.gds b/compiler/router/tests/03_same_layer_pins_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/03_same_layer_pins_test_scn3me_subm.gds rename to compiler/router/tests/03_same_layer_pins_test_scn4m_subm.gds diff --git a/compiler/router/tests/04_diff_layer_pins_test.py b/compiler/router/tests/04_diff_layer_pins_test.py index cbc21470..2882156f 100755 --- a/compiler/router/tests/04_diff_layer_pins_test.py +++ b/compiler/router/tests/04_diff_layer_pins_test.py @@ -38,9 +38,9 @@ class diff_layer_pins_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") - self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) + r=router(layer_stack,self,gds_file) + self.assertTrue(r.route(src="A",dest="B")) r = routing("04_diff_layer_pins_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/04_diff_layer_pins_test_scn3me_subm.gds b/compiler/router/tests/04_diff_layer_pins_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/04_diff_layer_pins_test_scn3me_subm.gds rename to compiler/router/tests/04_diff_layer_pins_test_scn4m_subm.gds diff --git a/compiler/router/tests/05_two_nets_test.py b/compiler/router/tests/05_two_nets_test.py index 166292d0..e71920a8 100755 --- a/compiler/router/tests/05_two_nets_test.py +++ b/compiler/router/tests/05_two_nets_test.py @@ -38,10 +38,10 @@ class two_nets_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") - self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) - self.assertTrue(r.route(self,layer_stack,src="C",dest="D")) + r=router(layer_stack,self,gds_file) + self.assertTrue(r.route(src="A",dest="B")) + self.assertTrue(r.route(src="C",dest="D")) r = routing("05_two_nets_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/05_two_nets_test_scn3me_subm.gds b/compiler/router/tests/05_two_nets_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/05_two_nets_test_scn3me_subm.gds rename to compiler/router/tests/05_two_nets_test_scn4m_subm.gds diff --git a/compiler/router/tests/06_pin_location_test.py b/compiler/router/tests/06_pin_location_test.py index e67fed53..f469d326 100755 --- a/compiler/router/tests/06_pin_location_test.py +++ b/compiler/router/tests/06_pin_location_test.py @@ -37,13 +37,13 @@ class pin_location_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") + r=router(layer_stack,self,gds_file) # these are user coordinates and layers src_pin = [[0.52, 4.099],11] tgt_pin = [[3.533, 1.087],11] #r.route(layer_stack,src="A",dest="B") - self.assertTrue(r.route(self,layer_stack,src=src_pin,dest=tgt_pin)) + self.assertTrue(r.route(src=src_pin,dest=tgt_pin)) # This only works for freepdk45 since the coordinates are hard coded if OPTS.tech_name == "freepdk45": diff --git a/compiler/router/tests/07_big_test.py b/compiler/router/tests/07_big_test.py index 5f844ec5..8fcf2826 100755 --- a/compiler/router/tests/07_big_test.py +++ b/compiler/router/tests/07_big_test.py @@ -37,8 +37,8 @@ class big_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") + r=router(layer_stack,self,gds_file) connections=[('out_0_2', 'a_0_0'), ('out_0_3', 'b_0_0'), ('out_0_0', 'a_0_1'), @@ -61,7 +61,7 @@ class big_test(openram_test): ('out_4_1', 'a_4_3'), ('out_4_5', 'b_4_3')] for (src,tgt) in connections: - self.assertTrue(r.route(self,layer_stack,src=src,dest=tgt)) + self.assertTrue(r.route(src=src,dest=tgt)) # This test only runs on scn3me_subm tech if OPTS.tech_name=="scn3me_subm": diff --git a/compiler/router/tests/07_big_test_scn3me_subm.gds b/compiler/router/tests/07_big_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/07_big_test_scn3me_subm.gds rename to compiler/router/tests/07_big_test_scn4m_subm.gds diff --git a/compiler/router/tests/08_expand_region_test.py b/compiler/router/tests/08_expand_region_test.py index 96edab57..3e63bd51 100755 --- a/compiler/router/tests/08_expand_region_test.py +++ b/compiler/router/tests/08_expand_region_test.py @@ -37,12 +37,12 @@ class expand_region_test(openram_test): offset=[0,0]) self.connect_inst([]) - r=router(gds_file) layer_stack =("metal1","via1","metal2") + r=router(layer_stack,self,gds_file) # This should be infeasible because it is blocked without a detour. - self.assertFalse(r.route(self,layer_stack,src="A",dest="B",detour_scale=1)) + self.assertFalse(r.route(src="A",dest="B",detour_scale=1)) # This should be feasible because we allow it to detour - self.assertTrue(r.route(self,layer_stack,src="A",dest="B",detour_scale=3)) + self.assertTrue(r.route(src="A",dest="B",detour_scale=3)) r = routing("08_expand_region_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) diff --git a/compiler/router/tests/08_expand_region_test_scn3me_subm.gds b/compiler/router/tests/08_expand_region_test_scn4m_subm.gds similarity index 100% rename from compiler/router/tests/08_expand_region_test_scn3me_subm.gds rename to compiler/router/tests/08_expand_region_test_scn4m_subm.gds diff --git a/compiler/router/tests/10_supply_grid_test.py b/compiler/router/tests/10_supply_grid_test.py index 2fa5cb7b..ef9a1be3 100755 --- a/compiler/router/tests/10_supply_grid_test.py +++ b/compiler/router/tests/10_supply_grid_test.py @@ -17,36 +17,26 @@ class no_blockages_test(openram_test): def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) - from gds_cell import gds_cell - from design import design from supply_router import supply_router as router - class routing(design, openram_test): - """ - A generic GDS design that we can route on. - """ - def __init__(self, name): - design.__init__(self, "top") + if False: + from control_logic import control_logic + cell = control_logic(16) + else: + 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 + sram = sram(c, "sram1") + cell = sram.s - # Instantiate a GDS cell with the design - globals.setup_paths() - from control_logic import control_logic - cell = control_logic(16) - #from pinv import pinv - #cell = pinv() - #gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),"control_logic") - #cell = gds_cell(name, gds_file) - self.add_inst(name=name, - mod=cell, - offset=[0,0]) - self.connect_inst(cell.pin_map.keys()) - - r=router(module=cell) - layer_stack =("metal3","via2","metal2") - self.assertTrue(r.route(self,layer_stack)) - - r=routing("10_supply_grid_test_{0}".format(OPTS.tech_name)) - self.local_drc_check(r) + layer_stack =("metal3","via3","metal4") + rtr=router(layer_stack, cell) + self.assertTrue(rtr.route()) + self.local_check(cell,True) # fails if there are any DRC errors on any cells globals.end_openram() diff --git a/compiler/router/tests/10_supply_grid_test_scn3me_subm.gds b/compiler/router/tests/10_supply_grid_test_scn3me_subm.gds deleted file mode 100644 index 7cfbc0cb..00000000 Binary files a/compiler/router/tests/10_supply_grid_test_scn3me_subm.gds and /dev/null differ diff --git a/compiler/router/tests/config_scn3me_subm.py b/compiler/router/tests/config_scn4m_subm.py similarity index 79% rename from compiler/router/tests/config_scn3me_subm.py rename to compiler/router/tests/config_scn4m_subm.py index 330d463b..e3aa1498 100755 --- a/compiler/router/tests/config_scn3me_subm.py +++ b/compiler/router/tests/config_scn4m_subm.py @@ -1,7 +1,7 @@ word_size = 1 num_words = 16 -tech_name = "scn3me_subm" +tech_name = "scn4m_subm" process_corners = ["TT"] supply_voltages = [5.0] temperatures = [25] diff --git a/compiler/router/tests/regress.py b/compiler/router/tests/regress.py index 02c077f1..b40263c7 100755 --- a/compiler/router/tests/regress.py +++ b/compiler/router/tests/regress.py @@ -4,7 +4,6 @@ import re import unittest import sys,os sys.path.append(os.path.join(sys.path[0],"../../compiler")) -print(sys.path) import globals (OPTS, args) = globals.parse_args() diff --git a/compiler/router/vector3d.py b/compiler/router/vector3d.py index b84f2eda..42bc35d4 100644 --- a/compiler/router/vector3d.py +++ b/compiler/router/vector3d.py @@ -15,20 +15,20 @@ class vector3d(): self.x = x[0] self.y = x[1] self.z = x[2] - #will take two inputs as the values of a coordinate + #will take inputs as the values of a coordinate else: self.x = x self.y = y self.z = z - self.tpl=(x,y,z) + def __str__(self): """ override print function output """ - return "vector3d:["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" + return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" def __repr__(self): """ override print function output """ - return "["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" + return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" def __setitem__(self, index, value): """ @@ -89,7 +89,7 @@ class vector3d(): Note: This assumes that you DON'T CHANGE THE VECTOR or it will break things. """ - return hash(self.tpl) + return hash((self.x,self.y,self.z)) def __rsub__(self, other): @@ -118,12 +118,39 @@ class vector3d(): x_factor=x_factor[0] return vector3d(self.y*x_factor,self.x*y_factor,self.z*z_factor) + def floor(self): + """ + Override floor function + """ + return vector3d(int(math.floor(self.x)),int(math.floor(self.y)), self.z) + + def ceil(self): + """ + Override ceil function + """ + return vector3d(int(math.ceil(self.x)),int(math.ceil(self.y)), self.z) + + def round(self): + """ + Override round function + """ + return vector3d(int(round(self.x)),int(round(self.y)), self.z) + def __eq__(self, other): """Override the default Equals behavior""" if isinstance(other, self.__class__): - return self.__dict__ == other.__dict__ + return self.x==other.x and self.y==other.y and self.z==other.z return False + def __lt__(self, other): + """Override the default less than behavior""" + if isinstance(other, self.__class__): + if self.x 0: - top_instances.append(self.col_decoder_inst) - top_instances.append(self.col_mux_array_inst) - - if self.num_banks > 1: - top_instances.append(self.bank_select_inst) - - - for inst in top_instances: - # Column mux has no vdd - if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst): - self.copy_layout_pin(inst, "vdd") - # Precharge has no gnd - if inst != self.precharge_array_inst: - self.copy_layout_pin(inst, "gnd") - def add_multi_bank_modules(self): """ Create the multibank address flops and bank decoder """ @@ -459,8 +451,8 @@ class sram_base(design): sp.close() - def analytical_delay(self,slew,load): + def analytical_delay(self, vdd, slew,load): """ LH and HL are the same in analytical model. """ - return self.bank.analytical_delay(slew,load) + return self.bank.analytical_delay(vdd,slew,load) diff --git a/compiler/sram_config.py b/compiler/sram_config.py index e7c80fd8..3c3892a5 100644 --- a/compiler/sram_config.py +++ b/compiler/sram_config.py @@ -53,7 +53,7 @@ class sram_config: # Estimate the number of rows given the tentative words per row self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) - + # Fix the number of columns and rows self.num_cols = int(self.words_per_row*self.word_size) self.num_rows = int(self.num_words_per_bank/self.words_per_row) diff --git a/compiler/tests/00_code_format_check_test.py b/compiler/tests/00_code_format_check_test.py index 7ed46e50..026f7e2b 100755 --- a/compiler/tests/00_code_format_check_test.py +++ b/compiler/tests/00_code_format_check_test.py @@ -31,12 +31,12 @@ class code_format_test(openram_test): continue if re.search("testutils.py$", code): continue - if re.search("grid.py$", code): - continue if re.search("globals.py$", code): continue if re.search("openram.py$", code): continue + if re.search("sram.py$", code): + continue if re.search("gen_stimulus.py$", code): continue errors += check_print_output(code) @@ -52,7 +52,7 @@ def setup_files(path): for f in current_files: files.append(os.path.join(dir, f)) nametest = re.compile("\.py$", re.IGNORECASE) - select_files = filter(nametest.search, files) + select_files = list(filter(nametest.search, files)) return select_files @@ -102,16 +102,17 @@ def check_print_output(file_name): """Check if any files (except debug.py) call the _print_ function. We should use the debug output with verbosity instead!""" file = open(file_name, "r+b") - line = file.read() + line = file.read().decode('utf-8') # skip comments with a hash line = re.sub(r'#.*', '', line) # skip doc string comments line=re.sub(r'\"\"\"[^\"]*\"\"\"', '', line, flags=re.S|re.M) - count = len(re.findall("\s*print\s+", line)) + count = len(re.findall("[^p]+print\(", line)) if count > 0: debug.info(0, "\nFound " + str(count) + " _print_ calls " + str(file_name)) + file.close() return(count) diff --git a/compiler/tests/04_bitcell_1rw_1r_test.py b/compiler/tests/04_bitcell_1rw_1r_test.py new file mode 100755 index 00000000..67db3710 --- /dev/null +++ b/compiler/tests/04_bitcell_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +""" +Run regresion tests on a parameterized bitcell +""" + +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 + +OPTS = globals.OPTS + +@unittest.skip("SKIPPING 04_bitcell_1rw_1r_test") +class bitcell_1rw_1r_test(openram_test): + + def runTest(self): + OPTS.bitcell = "bitcell_1rw_1r" + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from bitcell import bitcell + from bitcell_1rw_1r import bitcell_1rw_1r + import tech + OPTS.num_rw_ports=1 + OPTS.num_w_ports=0 + OPTS.num_r_ports=1 + debug.info(2, "Bitcell with 1 read/write and 1 read port") + #tx = bitcell_1rw_1r() + tx = bitcell() + self.local_check(tx) + + 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/04_replica_pbitcell_test.py b/compiler/tests/04_replica_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/14_replica_bitline_test.py b/compiler/tests/14_replica_bitline_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/16_control_logic_test.py b/compiler/tests/16_control_logic_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/19_bank_select_test.py b/compiler/tests/19_bank_select_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index 9bab2a4f..4fceafec 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -11,6 +11,7 @@ import globals from globals import OPTS import debug +@unittest.skip("SKIPPING 19_multi_bank_test") class multi_bank_test(openram_test): def runTest(self): diff --git a/compiler/tests/19_pmulti_bank_test.py b/compiler/tests/19_pmulti_bank_test.py old mode 100644 new mode 100755 index 7ec80647..03544587 --- a/compiler/tests/19_pmulti_bank_test.py +++ b/compiler/tests/19_pmulti_bank_test.py @@ -11,6 +11,7 @@ import globals from globals import OPTS import debug +@unittest.skip("SKIPPING 19_pmulti_bank_test") class multi_bank_test(openram_test): def runTest(self): diff --git a/compiler/tests/20_psram_1bank_test.py b/compiler/tests/20_psram_1bank_nomux_test.py similarity index 98% rename from compiler/tests/20_psram_1bank_test.py rename to compiler/tests/20_psram_1bank_nomux_test.py index 7ca2e33c..6106763c 100755 --- a/compiler/tests/20_psram_1bank_test.py +++ b/compiler/tests/20_psram_1bank_nomux_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") +@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") class sram_1bank_test(openram_test): def runTest(self): diff --git a/compiler/tests/20_sram_1bank_2mux_test.py b/compiler/tests/20_sram_1bank_2mux_test.py new file mode 100755 index 00000000..db018f1e --- /dev/null +++ b/compiler/tests/20_sram_1bank_2mux_test.py @@ -0,0 +1,37 @@ +#!/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_sram_1bank_2mux_test") +class sram_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 + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + + 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() + +# 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_4mux_test.py b/compiler/tests/20_sram_1bank_4mux_test.py new file mode 100755 index 00000000..35416bbe --- /dev/null +++ b/compiler/tests/20_sram_1bank_4mux_test.py @@ -0,0 +1,37 @@ +#!/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_sram_1bank_2mux_test") +class sram_1bank_4mux_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 + c = sram_config(word_size=4, + num_words=64, + num_banks=1) + + c.words_per_row=4 + debug.info(1, "Single bank, four way column mux with control logic") + a = sram(c, "sram") + 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_8mux_test.py b/compiler/tests/20_sram_1bank_8mux_test.py new file mode 100755 index 00000000..d09165a2 --- /dev/null +++ b/compiler/tests/20_sram_1bank_8mux_test.py @@ -0,0 +1,37 @@ +#!/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_sram_1bank_8mux_test") +class sram_1bank_8mux_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 + c = sram_config(word_size=2, + num_words=128, + num_banks=1) + + c.words_per_row=8 + debug.info(1, "Single bank, eight way column mux with control logic") + a = sram(c, "sram") + 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_test.py b/compiler/tests/20_sram_1bank_nomux_test.py similarity index 58% rename from compiler/tests/20_sram_1bank_test.py rename to compiler/tests/20_sram_1bank_nomux_test.py index ce482b6d..26f5e9ba 100755 --- a/compiler/tests/20_sram_1bank_test.py +++ b/compiler/tests/20_sram_1bank_nomux_test.py @@ -20,29 +20,10 @@ class sram_1bank_test(openram_test): 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") + a = sram(c, "sram") self.local_check(a, final_verification=True) globals.end_openram() diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index 62352b69..a5aca3e8 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -17,16 +17,13 @@ class timing_sram_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) OPTS.spice_name="hspice" OPTS.analytical_delay = False - + OPTS.netlist_only = True # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer reload(characterizer) from characterizer import delay - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - from sram import sram from sram_config import sram_config c = sram_config(word_size=1, diff --git a/compiler/tests/21_hspice_setuphold_test.py b/compiler/tests/21_hspice_setuphold_test.py index 2969f95e..9bfdb24b 100755 --- a/compiler/tests/21_hspice_setuphold_test.py +++ b/compiler/tests/21_hspice_setuphold_test.py @@ -17,15 +17,13 @@ class timing_setup_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) OPTS.spice_name="hspice" OPTS.analytical_delay = False - + OPTS.netlist_only = True + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer reload(characterizer) from characterizer import setup_hold - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram import tech slews = [tech.spice["rise_time"]*2] diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 37572318..45a9b7f6 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -24,9 +24,6 @@ class timing_sram_test(openram_test): import characterizer reload(characterizer) from characterizer import delay - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - from sram import sram from sram_config import sram_config c = sram_config(word_size=1, diff --git a/compiler/tests/21_ngspice_setuphold_test.py b/compiler/tests/21_ngspice_setuphold_test.py index d86fcb23..d58bfc50 100755 --- a/compiler/tests/21_ngspice_setuphold_test.py +++ b/compiler/tests/21_ngspice_setuphold_test.py @@ -17,15 +17,13 @@ class timing_setup_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) OPTS.spice_name="ngspice" OPTS.analytical_delay = False - + OPTS.netlist_only = True + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer reload(characterizer) from characterizer import setup_hold - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram import tech slews = [tech.spice["rise_time"]*2] diff --git a/compiler/tests/22_psram_func_test.py b/compiler/tests/22_psram_1bank_2mux_func_test.py old mode 100644 new mode 100755 similarity index 65% rename from compiler/tests/22_psram_func_test.py rename to compiler/tests/22_psram_1bank_2mux_func_test.py index ab59b8b8..d8233d08 --- a/compiler/tests/22_psram_func_test.py +++ b/compiler/tests/22_psram_1bank_2mux_func_test.py @@ -11,12 +11,11 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 22_psram_func_test") -class psram_func_test(openram_test): +#@unittest.skip("SKIPPING 22_psram_1bank_2mux_func_test") +class psram_1bank_2mux_func_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - #OPTS.spice_name="hspice" OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.bitcell = "pbitcell" @@ -27,33 +26,27 @@ class psram_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - from sram import sram from sram_config import sram_config c = sram_config(word_size=4, num_words=64, num_banks=1) c.words_per_row=2 - - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 1 - OPTS.num_r_ports = 1 - - debug.info(1, "Functional test for 1bit, 16word SRAM, with 1 bank. Multiport with {}RW {}W {}R.".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)) - s = sram(c, name="sram1") - - tempspice = OPTS.openram_temp + "temp.sp" + debug.info(1, "Functional test for psram 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 = 5 - (fail,error) = f.run() + 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 copdsay of the class to actually run the test diff --git a/compiler/tests/22_psram_1bank_4mux_func_test.py b/compiler/tests/22_psram_1bank_4mux_func_test.py new file mode 100755 index 00000000..1ae684d9 --- /dev/null +++ b/compiler/tests/22_psram_1bank_4mux_func_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +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_psram_1bank_4mux_func_test") +class psram_1bank_4mux_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 = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + + # 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=256, + num_banks=1) + c.words_per_row=4 + debug.info(1, "Functional test for psram 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 copdsay 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/22_psram_1bank_8mux_func_test.py b/compiler/tests/22_psram_1bank_8mux_func_test.py new file mode 100755 index 00000000..d81e76f9 --- /dev/null +++ b/compiler/tests/22_psram_1bank_8mux_func_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +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_psram_1bank_8mux_func_test") +class psram_1bank_8mux_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 = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + + # 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=512, + 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, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + 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/22_psram_1bank_nomux_func_test.py b/compiler/tests/22_psram_1bank_nomux_func_test.py new file mode 100755 index 00000000..681e24d5 --- /dev/null +++ b/compiler/tests/22_psram_1bank_nomux_func_test.py @@ -0,0 +1,57 @@ +#!/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_psram_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 = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + + # 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 psram 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/22_sram_func_test.py b/compiler/tests/22_sram_1bank_2mux_func_test.py old mode 100644 new mode 100755 similarity index 63% rename from compiler/tests/22_sram_func_test.py rename to compiler/tests/22_sram_1bank_2mux_func_test.py index a2f3787e..7779ed4f --- a/compiler/tests/22_sram_func_test.py +++ b/compiler/tests/22_sram_1bank_2mux_func_test.py @@ -11,41 +11,40 @@ import globals from globals import OPTS import debug -#@unittest.skip("SKIPPING 22_sram_func_test") -class sram_func_test(openram_test): +#@unittest.skip("SKIPPING 22_sram_1bank_2mux_func_test") +class sram_1bank_2mux_func_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - #OPTS.spice_name="hspice" OPTS.analytical_delay = False + OPTS.netlist_only = True # 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 - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - from sram import sram from sram_config import sram_config c = sram_config(word_size=4, num_words=64, num_banks=1) c.words_per_row=2 - debug.info(1, "Functional test for 1bit, 16word SRAM, with 1 bank") - s = sram(c, name="sram1") - - tempspice = OPTS.openram_temp + "temp.sp" + debug.info(1, "Functional test for sram 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 copdsay of the class to actually run the test diff --git a/compiler/tests/22_sram_1bank_4mux_func_test.py b/compiler/tests/22_sram_1bank_4mux_func_test.py new file mode 100755 index 00000000..c16b86fe --- /dev/null +++ b/compiler/tests/22_sram_1bank_4mux_func_test.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +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_1bank_4mux_func_test") +class sram_1bank_4mux_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 + + # 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=256, + num_banks=1) + c.words_per_row=4 + debug.info(1, "Functional test for sram 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 copdsay 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/22_sram_1bank_8mux_func_test.py b/compiler/tests/22_sram_1bank_8mux_func_test.py new file mode 100755 index 00000000..be8e538f --- /dev/null +++ b/compiler/tests/22_sram_1bank_8mux_func_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +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_1bank_8mux_func_test") +class sram_1bank_8mux_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 + + # 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 + if not OPTS.spice_exe: + debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) + + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=512, + 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, + c.num_words, + c.words_per_row, + c.num_banks)) + s = sram(c, name="sram") + 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/22_sram_1bank_nomux_func_test.py b/compiler/tests/22_sram_1bank_nomux_func_test.py new file mode 100755 index 00000000..52d63f4a --- /dev/null +++ b/compiler/tests/22_sram_1bank_nomux_func_test.py @@ -0,0 +1,55 @@ +#!/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_func_test") +class sram_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 + + # 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 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/26_pex_test.py b/compiler/tests/26_pex_test.py index 7755a2c7..edb344f9 100755 --- a/compiler/tests/26_pex_test.py +++ b/compiler/tests/26_pex_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 22_sram_pex_test") +@unittest.skip("SKIPPING 26_pex_test") class sram_func_test(openram_test): def runTest(self): diff --git a/compiler/tests/27_worst_case_delay_test.py b/compiler/tests/27_worst_case_delay_test.py new file mode 100755 index 00000000..42a07bef --- /dev/null +++ b/compiler/tests/27_worst_case_delay_test.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +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 27_worst_case_delay_test") +class worst_case_timing_sram_test(openram_test): + + def runTest(self): + OPTS.tech_name = "freepdk45" + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.spice_name="hspice" + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + OPTS.check_lvsdrc = True + + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import worst_case + if not OPTS.spice_exe: + debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) + + word_size, num_words, num_banks = 2, 16, 1 + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=word_size, + num_words=num_words, + num_banks=num_banks) + c.words_per_row=1 + #c.compute_sizes() + debug.info(1, "Testing the timing different bitecells inside a {}bit, {} words SRAM with {} bank".format( + word_size, num_words, num_banks)) + s = sram(c, name="sram1") + + sp_netlist_file = OPTS.openram_temp + "temp.sp" + s.sp_write(sp_netlist_file) + + if OPTS.use_pex: + gdsname = OPTS.output_path + s.name + ".gds" + s.gds_write(gdsname) + + import verify + reload(verify) + # Output the extracted design if requested + sp_pex_file = OPTS.output_path + s.name + "_pex.sp" + verify.run_pex(s.name, gdsname, sp_netlist_file, output=sp_pex_file) + sp_sim_file = sp_pex_file + debug.info(1, "Performing spice simulations with backannotated spice file.") + else: + sp_sim_file = sp_netlist_file + debug.info(1, "Performing spice simulations with spice netlist.") + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + wc = worst_case(s.s, sp_sim_file, corner) + import tech + loads = [tech.spice["msflop_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + probe_address = "1" * s.s.addr_size + probe_data = s.s.word_size - 1 + wc.analyze(probe_address, probe_data, slews, loads) + + globals.end_openram() + +# instantiate a copdsay 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/config_20_freepdk45.py b/compiler/tests/config_20_freepdk45.py old mode 100644 new mode 100755 diff --git a/compiler/tests/config_20_scn3me_subm.py b/compiler/tests/config_20_scn3me_subm.py old mode 100644 new mode 100755 diff --git a/compiler/tests/config_20_scn4m_subm.py b/compiler/tests/config_20_scn4m_subm.py old mode 100644 new mode 100755 diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib index 84f301f8..45813c16 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib @@ -78,214 +78,216 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 948.52275; + area : 977.4951374999999; leakage_power () { - when : "CSb"; - value : 0.0021292; + when : "CSb0"; + value : 0.0011164579999999999; } cell_leakage_power : 0; - bus(DIN){ + bus(DIN0){ bus_type : DATA; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR; - clocked_on : clk; + address : ADDR0; + clocked_on : clk0; + } + pin(DIN0[1:0]){ + timing(){ + timing_type : setup_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); + } + } } } - bus(DOUT){ + bus(DOUT0){ bus_type : DATA; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR; - } - pin(DOUT[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); - } + address : ADDR0; } + pin(DOUT0[1:0]){ timing(){ timing_sense : non_unate; - related_pin : "clk"; + related_pin : "clk0"; timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("0.229, 0.23, 0.234",\ - "0.23, 0.23, 0.234",\ - "0.236, 0.236, 0.24"); + values("0.235, 0.235, 0.239",\ + "0.235, 0.236, 0.24",\ + "0.241, 0.242, 0.246"); } cell_fall(CELL_TABLE) { - values("2.555, 2.556, 2.568",\ - "2.555, 2.557, 2.569",\ - "2.562, 2.563, 2.575"); + values("2.583, 2.585, 2.612",\ + "2.584, 2.585, 2.613",\ + "2.59, 2.592, 2.62"); } rise_transition(CELL_TABLE) { - values("0.02, 0.021, 0.028",\ - "0.02, 0.021, 0.028",\ - "0.02, 0.021, 0.028"); + values("0.022, 0.022, 0.03",\ + "0.022, 0.023, 0.03",\ + "0.022, 0.022, 0.03"); } fall_transition(CELL_TABLE) { - values("0.111, 0.112, 0.115",\ - "0.111, 0.111, 0.115",\ - "0.111, 0.111, 0.116"); + values("0.078, 0.079, 0.083",\ + "0.078, 0.079, 0.083",\ + "0.079, 0.079, 0.083"); } } } } - bus(ADDR){ + bus(ADDR0){ bus_type : ADDR; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR[3:0]){ + pin(ADDR0[3:0]){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } } - pin(CSb){ + pin(CSb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } - pin(WEb){ + pin(WEb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb & clk & !WEb"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("0.027431397222222223"); + values("0.03599689694444445"); } fall_power(scalar){ - values("0.027431397222222223"); + values("0.03599689694444445"); } } internal_power(){ - when : "!CSb & !clk & WEb"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("0.026240397222222222"); + values("0.029906643888888886"); } fall_power(scalar){ - values("0.026240397222222222"); + values("0.029906643888888886"); } } internal_power(){ - when : "CSb"; + when : "CSb0"; rise_power(scalar){ values("0"); } @@ -295,7 +297,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("2.422"); } @@ -305,7 +307,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("4.844"); } @@ -314,5 +316,6 @@ cell (sram_2_16_1_freepdk45){ } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib index 2fbbd8b8..13c6d975 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib @@ -78,96 +78,98 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 948.52275; + area : 977.4951374999999; leakage_power () { - when : "CSb"; - value : 0.000168; + when : "CSb0"; + value : 0.000179; } cell_leakage_power : 0; - bus(DIN){ + bus(DIN0){ bus_type : DATA; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR; - clocked_on : clk; + address : ADDR0; + clocked_on : clk0; + } + pin(DIN0[1:0]){ + timing(){ + timing_type : setup_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + } } } - bus(DOUT){ + bus(DOUT0){ bus_type : DATA; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR; - } - pin(DOUT[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } + address : ADDR0; } + pin(DOUT0[1:0]){ timing(){ timing_sense : non_unate; - related_pin : "clk"; + related_pin : "clk0"; timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("0.103, 0.104, 0.113",\ - "0.103, 0.104, 0.113",\ - "0.103, 0.104, 0.113"); + values("0.098, 0.098, 0.098",\ + "0.098, 0.098, 0.098",\ + "0.098, 0.098, 0.098"); } cell_fall(CELL_TABLE) { - values("0.103, 0.104, 0.113",\ - "0.103, 0.104, 0.113",\ - "0.103, 0.104, 0.113"); + values("0.098, 0.098, 0.098",\ + "0.098, 0.098, 0.098",\ + "0.098, 0.098, 0.098"); } rise_transition(CELL_TABLE) { - values("0.006, 0.007, 0.018",\ - "0.006, 0.007, 0.018",\ - "0.006, 0.007, 0.018"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } fall_transition(CELL_TABLE) { - values("0.006, 0.007, 0.018",\ - "0.006, 0.007, 0.018",\ - "0.006, 0.007, 0.018"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } } - bus(ADDR){ + bus(ADDR0){ bus_type : ADDR; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR[3:0]){ + pin(ADDR0[3:0]){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -181,7 +183,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -196,12 +198,12 @@ cell (sram_2_16_1_freepdk45){ } } - pin(CSb){ + pin(CSb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -215,7 +217,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -229,12 +231,12 @@ cell (sram_2_16_1_freepdk45){ } } - pin(WEb){ + pin(WEb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -248,7 +250,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -262,30 +264,30 @@ cell (sram_2_16_1_freepdk45){ } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb & clk & !WEb"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("0.0739870044551111"); + values("0.0747594982142222"); } fall_power(scalar){ - values("0.0739870044551111"); + values("0.0747594982142222"); } } internal_power(){ - when : "!CSb & !clk & WEb"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("0.0739870044551111"); + values("0.0747594982142222"); } fall_power(scalar){ - values("0.0739870044551111"); + values("0.0747594982142222"); } } internal_power(){ - when : "CSb"; + when : "CSb0"; rise_power(scalar){ values("0"); } @@ -295,7 +297,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("0.0"); } @@ -305,7 +307,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("0"); } @@ -314,5 +316,6 @@ cell (sram_2_16_1_freepdk45){ } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_pruned.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_pruned.lib index a3ec121c..4d0defaf 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_pruned.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_pruned.lib @@ -78,214 +78,216 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 948.52275; + area : 977.4951374999999; leakage_power () { - when : "CSb"; - value : 0.0021292; + when : "CSb0"; + value : 0.0011164579999999999; } cell_leakage_power : 0; - bus(DIN){ + bus(DIN0){ bus_type : DATA; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR; - clocked_on : clk; + address : ADDR0; + clocked_on : clk0; + } + pin(DIN0[1:0]){ + timing(){ + timing_type : setup_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); + } + } } } - bus(DOUT){ + bus(DOUT0){ bus_type : DATA; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR; - } - pin(DOUT[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); - } + address : ADDR0; } + pin(DOUT0[1:0]){ timing(){ timing_sense : non_unate; - related_pin : "clk"; + related_pin : "clk0"; timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("0.227, 0.227, 0.231",\ - "0.227, 0.228, 0.232",\ - "0.233, 0.234, 0.238"); + values("0.233, 0.233, 0.237",\ + "0.233, 0.234, 0.237",\ + "0.239, 0.24, 0.244"); } cell_fall(CELL_TABLE) { - values("2.555, 2.557, 2.569",\ - "2.556, 2.557, 2.569",\ - "2.562, 2.563, 2.576"); + values("2.584, 2.585, 2.611",\ + "2.584, 2.585, 2.612",\ + "2.591, 2.592, 2.618"); } rise_transition(CELL_TABLE) { - values("0.02, 0.021, 0.028",\ - "0.02, 0.021, 0.028",\ - "0.02, 0.021, 0.028"); + values("0.022, 0.022, 0.03",\ + "0.022, 0.023, 0.03",\ + "0.022, 0.023, 0.03"); } fall_transition(CELL_TABLE) { - values("0.11, 0.11, 0.114",\ - "0.109, 0.11, 0.113",\ - "0.11, 0.11, 0.114"); + values("0.076, 0.077, 0.082",\ + "0.077, 0.077, 0.082",\ + "0.077, 0.077, 0.082"); } } } } - bus(ADDR){ + bus(ADDR0){ bus_type : ADDR; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR[3:0]){ + pin(ADDR0[3:0]){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } } - pin(CSb){ + pin(CSb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } - pin(WEb){ + pin(WEb0){ direction : input; capacitance : 0.2091; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027",\ - "0.009, 0.015, 0.027"); + values("0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039",\ + "0.033, 0.033, 0.039"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015",\ - "0.009, 0.009, 0.015"); + values("0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033",\ + "0.027, 0.027, 0.033"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004",\ - "0.002, 0.002, -0.004"); + values("-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022",\ + "-0.01, -0.016, -0.022"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016",\ - "-0.004, -0.004, -0.016"); + values("-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016",\ + "-0.016, -0.016, -0.016"); } } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb & clk & !WEb"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("0.025181683333333333"); + values("0.03334771594444444"); } fall_power(scalar){ - values("0.025181683333333333"); + values("0.03334771594444444"); } } internal_power(){ - when : "!CSb & !clk & WEb"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("0.024945991666666667"); + values("0.028457026222222223"); } fall_power(scalar){ - values("0.024945991666666667"); + values("0.028457026222222223"); } } internal_power(){ - when : "CSb"; + when : "CSb0"; rise_power(scalar){ values("0"); } @@ -295,7 +297,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("2.422"); } @@ -305,7 +307,7 @@ cell (sram_2_16_1_freepdk45){ } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("4.844"); } @@ -314,5 +316,6 @@ cell (sram_2_16_1_freepdk45){ } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C.lib index e6aa54f9..57d36974 100644 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C.lib +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C.lib @@ -314,5 +314,6 @@ cell (sram_2_16_1_scn3me_subm){ } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_analytical.lib index 8d774ce5..c79394e3 100644 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_analytical.lib @@ -314,5 +314,6 @@ cell (sram_2_16_1_scn3me_subm){ } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib index b514a858..ff297ad2 100644 --- a/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm_TT_5p0V_25C_pruned.lib @@ -314,5 +314,6 @@ cell (sram_2_16_1_scn3me_subm){ } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib index 89f40320..affdf7a9 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib @@ -78,11 +78,11 @@ cell (sram_2_16_1_scn4m_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 60176.520000000004; + area : 60774.3; leakage_power () { when : "CSb0"; - value : 0.000175; + value : 0.0009813788999999999; } cell_leakage_power : 0; bus(DIN0){ @@ -91,7 +91,37 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; memory_write(){ address : ADDR0; - clocked_on : clk; + clocked_on : clk0; + } + pin(DIN0[1:0]){ + timing(){ + timing_type : setup_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); + } + } } } bus(DOUT0){ @@ -103,57 +133,29 @@ cell (sram_2_16_1_scn4m_subm){ address : ADDR0; } pin(DOUT0[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } timing(){ timing_sense : non_unate; - related_pin : "clk"; + related_pin : "clk0"; timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268"); + values("1.556, 1.576, 1.751",\ + "1.559, 1.579, 1.754",\ + "1.624, 1.643, 1.819"); } cell_fall(CELL_TABLE) { - values("0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268"); + values("3.445, 3.504, 3.926",\ + "3.448, 3.507, 3.93",\ + "3.49, 3.549, 3.972"); } rise_transition(CELL_TABLE) { - values("0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004"); + values("0.13, 0.169, 0.574",\ + "0.13, 0.169, 0.574",\ + "0.13, 0.169, 0.574"); } fall_transition(CELL_TABLE) { - values("0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004"); + values("0.467, 0.49, 0.959",\ + "0.467, 0.49, 0.959",\ + "0.47, 0.493, 0.96"); } } } @@ -167,30 +169,30 @@ cell (sram_2_16_1_scn4m_subm){ pin(ADDR0[3:0]){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } @@ -201,30 +203,30 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } @@ -234,54 +236,54 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk & !WEb0"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("11.3007276371"); + values("9.972790277777777"); } fall_power(scalar){ - values("11.3007276371"); + values("9.972790277777777"); } } internal_power(){ - when : "!CSb0 & !clk & WEb0"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("11.3007276371"); + values("8.899322499999998"); } fall_power(scalar){ - values("11.3007276371"); + values("8.899322499999998"); } } internal_power(){ @@ -295,24 +297,25 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { - values("0.0"); + values("2.344"); } fall_constraint(scalar) { - values("0.0"); + values("2.344"); } } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { - values("0"); + values("4.688"); } fall_constraint(scalar) { - values("0"); + values("4.688"); } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib index 89f40320..bb254713 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib @@ -78,11 +78,11 @@ cell (sram_2_16_1_scn4m_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 60176.520000000004; + area : 60774.3; leakage_power () { when : "CSb0"; - value : 0.000175; + value : 0.000179; } cell_leakage_power : 0; bus(DIN0){ @@ -91,21 +91,12 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; memory_write(){ address : ADDR0; - clocked_on : clk; + clocked_on : clk0; } - } - bus(DOUT0){ - bus_type : DATA; - direction : output; - max_capacitance : 78.5936; - min_capacitance : 2.45605; - memory_read(){ - address : ADDR0; - } - pin(DOUT0[1:0]){ + pin(DIN0[1:0]){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -119,7 +110,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -131,9 +122,20 @@ cell (sram_2_16_1_scn4m_subm){ "0.001, 0.001, 0.001"); } } + } + } + bus(DOUT0){ + bus_type : DATA; + direction : output; + max_capacitance : 78.5936; + min_capacitance : 2.45605; + memory_read(){ + address : ADDR0; + } + pin(DOUT0[1:0]){ timing(){ timing_sense : non_unate; - related_pin : "clk"; + related_pin : "clk0"; timing_type : rising_edge; cell_rise(CELL_TABLE) { values("0.268, 0.268, 0.268",\ @@ -167,7 +169,7 @@ cell (sram_2_16_1_scn4m_subm){ pin(ADDR0[3:0]){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -181,7 +183,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -201,7 +203,7 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -215,7 +217,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -234,7 +236,7 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.009, 0.009, 0.009",\ "0.009, 0.009, 0.009",\ @@ -248,7 +250,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("0.001, 0.001, 0.001",\ "0.001, 0.001, 0.001",\ @@ -262,26 +264,26 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk & !WEb0"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("11.3007276371"); + values("11.3049604371"); } fall_power(scalar){ - values("11.3007276371"); + values("11.3049604371"); } } internal_power(){ - when : "!CSb0 & !clk & WEb0"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("11.3007276371"); + values("11.3049604371"); } fall_power(scalar){ - values("11.3007276371"); + values("11.3049604371"); } } internal_power(){ @@ -295,7 +297,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("0.0"); } @@ -305,7 +307,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("0"); } @@ -314,5 +316,6 @@ cell (sram_2_16_1_scn4m_subm){ } } } + } } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_pruned.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_pruned.lib index 8509fc30..2ce6b2e9 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_pruned.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_pruned.lib @@ -78,11 +78,11 @@ cell (sram_2_16_1_scn4m_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 60176.520000000004; + area : 60774.3; leakage_power () { when : "CSb0"; - value : 0.025716199999999998; + value : 0.0009813788999999999; } cell_leakage_power : 0; bus(DIN0){ @@ -91,7 +91,37 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; memory_write(){ address : ADDR0; - clocked_on : clk; + clocked_on : clk0; + } + pin(DIN0[1:0]){ + timing(){ + timing_type : setup_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk0"; + rise_constraint(CONSTRAINT_TABLE) { + values("-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114",\ + "-0.065, -0.071, -0.114"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); + } + } } } bus(DOUT0){ @@ -103,57 +133,29 @@ cell (sram_2_16_1_scn4m_subm){ address : ADDR0; } pin(DOUT0[1:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095"); - } - } timing(){ timing_sense : non_unate; - related_pin : "clk"; + related_pin : "clk0"; timing_type : rising_edge; cell_rise(CELL_TABLE) { - values("1.277, 1.297, 1.475",\ - "1.28, 1.3, 1.479",\ - "1.347, 1.367, 1.545"); + values("1.542, 1.562, 1.738",\ + "1.545, 1.565, 1.741",\ + "1.609, 1.629, 1.805"); } cell_fall(CELL_TABLE) { - values("3.217, 3.281, 3.71",\ - "3.22, 3.285, 3.714",\ - "3.261, 3.325, 3.75"); + values("3.446, 3.505, 3.924",\ + "3.45, 3.508, 3.927",\ + "3.491, 3.55, 3.97"); } rise_transition(CELL_TABLE) { - values("0.122, 0.164, 0.579",\ - "0.122, 0.164, 0.578",\ - "0.122, 0.164, 0.58"); + values("0.129, 0.169, 0.573",\ + "0.129, 0.169, 0.573",\ + "0.129, 0.169, 0.573"); } fall_transition(CELL_TABLE) { - values("0.363, 0.396, 0.958",\ - "0.363, 0.396, 0.957",\ - "0.366, 0.399, 0.951"); + values("0.457, 0.481, 0.956",\ + "0.457, 0.481, 0.956",\ + "0.459, 0.483, 0.957"); } } } @@ -167,30 +169,30 @@ cell (sram_2_16_1_scn4m_subm){ pin(ADDR0[3:0]){ timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } @@ -201,30 +203,30 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } @@ -234,54 +236,54 @@ cell (sram_2_16_1_scn4m_subm){ capacitance : 9.8242; timing(){ timing_type : setup_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228",\ - "0.179, 0.173, 0.228"); + values("0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228",\ + "0.167, 0.167, 0.228"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143",\ - "0.125, 0.125, 0.143"); + values("0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137",\ + "0.131, 0.125, 0.137"); } } timing(){ timing_type : hold_rising; - related_pin : "clk"; + related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { values("-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114",\ "-0.065, -0.071, -0.114"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095",\ - "-0.089, -0.089, -0.095"); + values("-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089",\ + "-0.089, -0.089, -0.089"); } } } - pin(clk){ + pin(clk0){ clock : true; direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk & !WEb0"; + when : "!CSb0 & clk0 & !WEb0"; rise_power(scalar){ - values("9.141838916666668"); + values("9.602821763527778"); } fall_power(scalar){ - values("9.141838916666668"); + values("9.602821763527778"); } } internal_power(){ - when : "!CSb0 & !clk & WEb0"; + when : "!CSb0 & !clk0 & WEb0"; rise_power(scalar){ - values("8.304491694444444"); + values("8.647938152416664"); } fall_power(scalar){ - values("8.304491694444444"); + values("8.647938152416664"); } } internal_power(){ @@ -295,7 +297,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type :"min_pulse_width"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("2.344"); } @@ -305,7 +307,7 @@ cell (sram_2_16_1_scn4m_subm){ } timing(){ timing_type :"minimum_period"; - related_pin : clk; + related_pin : clk0; rise_constraint(scalar) { values("4.688"); } @@ -314,5 +316,6 @@ cell (sram_2_16_1_scn4m_subm){ } } } + } } diff --git a/compiler/tests/out.log b/compiler/tests/out.log deleted file mode 100644 index e69de29b..00000000 diff --git a/compiler/tests/sram1.gds b/compiler/tests/sram1.gds deleted file mode 100644 index d6ed28eb..00000000 Binary files a/compiler/tests/sram1.gds and /dev/null differ diff --git a/compiler/tests/sram1.lef b/compiler/tests/sram1.lef deleted file mode 100644 index c0563063..00000000 --- a/compiler/tests/sram1.lef +++ /dev/null @@ -1,19 +0,0 @@ -VERSION 5.4 ; -NAMESCASESENSITIVE ON ; -BUSBITCHARS "[]" ; -DIVIDERCHAR "/" ; -UNITS - DATABASE MICRONS 1000 ; -END UNITS -SITE MacroSite - CLASS Core ; - SIZE 324000.0 by 421500.0 ; -END MacroSite -MACRO sram1 - CLASS BLOCK ; - SIZE 324000.0 BY 421500.0 ; - SYMMETRY X Y R90 ; - SITE MacroSite ; - PIN DIN[0] - DIRECTION INPUT ; - PORT diff --git a/compiler/tests/sram1.sp b/compiler/tests/sram1.sp deleted file mode 100644 index 622af672..00000000 --- a/compiler/tests/sram1.sp +++ /dev/null @@ -1,602 +0,0 @@ -************************************************** -* OpenRAM generated memory. -* Words: 16 -* Data bits: 4 -* Banks: 1 -* Column mux: 1:1 -************************************************** -* Positive edge-triggered FF -.subckt dff D Q clk vdd gnd -M0 vdd clk a_2_6# vdd p w=12u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M1 a_17_74# D vdd vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M2 a_22_6# clk a_17_74# vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M3 a_31_74# a_2_6# a_22_6# vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M4 vdd a_34_4# a_31_74# vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M5 a_34_4# a_22_6# vdd vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M6 a_61_74# a_34_4# vdd vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M7 a_66_6# a_2_6# a_61_74# vdd p w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M8 a_76_84# clk a_66_6# vdd p w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M9 vdd Q a_76_84# vdd p w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M10 gnd clk a_2_6# gnd n w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M11 Q a_66_6# vdd vdd p w=12u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M12 a_17_6# D gnd gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M13 a_22_6# a_2_6# a_17_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M14 a_31_6# clk a_22_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M15 gnd a_34_4# a_31_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M16 a_34_4# a_22_6# gnd gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M17 a_61_6# a_34_4# gnd gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M18 a_66_6# clk a_61_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M19 a_76_6# a_2_6# a_66_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M20 gnd Q a_76_6# gnd n w=3u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -M21 Q a_66_6# gnd gnd n w=6u l=0.6u -+ ad=0p pd=0u as=0p ps=0u -.ends dff - -* ptx M{0} {1} n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p - -* ptx M{0} {1} p m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p - -.SUBCKT pinv_2 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p -Mpinv_nmos Z A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pinv_2 - -.SUBCKT dff_inv_2 D Q Qb clk vdd gnd -Xdff_inv_dff D Q clk vdd gnd dff -Xdff_inv_inv1 Q Qb vdd gnd pinv_2 -.ENDS dff_inv_2 - -.SUBCKT dff_array_3x1 din[0] din[1] din[2] dout[0] dout_bar[0] dout[1] dout_bar[1] dout[2] dout_bar[2] clk vdd gnd -XXdff_r0_c0 din[0] dout[0] dout_bar[0] clk vdd gnd dff_inv_2 -XXdff_r1_c0 din[1] dout[1] dout_bar[1] clk vdd gnd dff_inv_2 -XXdff_r2_c0 din[2] dout[2] dout_bar[2] clk vdd gnd dff_inv_2 -.ENDS dff_array_3x1 - -* ptx M{0} {1} p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p - -.SUBCKT pnand2_1 A B Z vdd gnd -Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand2_1 - -.SUBCKT pnand3_1 A B C Z vdd gnd -Mpnand3_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos3 Z C vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos1 Z C net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos2 net1 B net2 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos3 net2 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand3_1 - -* ptx M{0} {1} n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p - -.SUBCKT pinv_3 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_3 - -* ptx M{0} {1} n m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p - -* ptx M{0} {1} p m=1 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p - -.SUBCKT pinv_4 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p -Mpinv_nmos Z A gnd gnd n m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p -.ENDS pinv_4 - -* ptx M{0} {1} n m=4 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p - -* ptx M{0} {1} p m=4 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p - -.SUBCKT pinv_5 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=4 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p -Mpinv_nmos Z A gnd gnd n m=4 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p -.ENDS pinv_5 - -.SUBCKT pinvbuf_4_16 A Zb Z vdd gnd -Xbuf_inv1 A zb_int vdd gnd pinv_3 -Xbuf_inv2 zb_int z_int vdd gnd pinv_4 -Xbuf_inv3 z_int Zb vdd gnd pinv_5 -Xbuf_inv4 zb_int Z vdd gnd pinv_5 -.ENDS pinvbuf_4_16 - -.SUBCKT pinv_6 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_6 - -.SUBCKT pinv_7 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p -Mpinv_nmos Z A gnd gnd n m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p -.ENDS pinv_7 - -.SUBCKT pinv_8 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=4 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p -Mpinv_nmos Z A gnd gnd n m=4 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p -.ENDS pinv_8 - -*********************** "cell_6t" ****************************** -.SUBCKT replica_cell_6t bl br wl vdd gnd -M_1 gnd net_2 vdd vdd p W='0.9u' L=1.2u -M_2 net_2 gnd vdd vdd p W='0.9u' L=1.2u -M_3 br wl net_2 gnd n W='1.2u' L=0.6u -M_4 bl wl gnd gnd n W='1.2u' L=0.6u -M_5 net_2 gnd gnd gnd n W='2.4u' L=0.6u -M_6 gnd net_2 gnd gnd n W='2.4u' L=0.6u -.ENDS $ replica_cell_6t - -*********************** "cell_6t" ****************************** -.SUBCKT cell_6t bl br wl vdd gnd -M_1 net_1 net_2 vdd vdd p W='0.9u' L=1.2u -M_2 net_2 net_1 vdd vdd p W='0.9u' L=1.2u -M_3 br wl net_2 gnd n W='1.2u' L=0.6u -M_4 bl wl net_1 gnd n W='1.2u' L=0.6u -M_5 net_2 net_1 gnd gnd n W='2.4u' L=0.6u -M_6 net_1 net_2 gnd gnd n W='2.4u' L=0.6u -.ENDS $ cell_6t - -.SUBCKT bitline_load bl[0] br[0] wl[0] wl[1] wl[2] wl[3] vdd gnd -Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t -Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t -Xbit_r2_c0 bl[0] br[0] wl[2] vdd gnd cell_6t -Xbit_r3_c0 bl[0] br[0] wl[3] vdd gnd cell_6t -.ENDS bitline_load - -.SUBCKT pinv_9 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_9 - -.SUBCKT delay_chain in out vdd gnd -Xdinv0 in dout_1 vdd gnd pinv_9 -Xdload_0_0 dout_1 n_0_0 vdd gnd pinv_9 -Xdload_0_1 dout_1 n_0_1 vdd gnd pinv_9 -Xdload_0_2 dout_1 n_0_2 vdd gnd pinv_9 -Xdinv1 dout_1 dout_2 vdd gnd pinv_9 -Xdload_1_0 dout_2 n_1_0 vdd gnd pinv_9 -Xdload_1_1 dout_2 n_1_1 vdd gnd pinv_9 -Xdload_1_2 dout_2 n_1_2 vdd gnd pinv_9 -Xdinv2 dout_2 out vdd gnd pinv_9 -Xdload_2_0 out n_2_0 vdd gnd pinv_9 -Xdload_2_1 out n_2_1 vdd gnd pinv_9 -Xdload_2_2 out n_2_2 vdd gnd pinv_9 -.ENDS delay_chain - -.SUBCKT pinv_10 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_10 - -* ptx M{0} {1} p m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p - -.SUBCKT replica_bitline en out vdd gnd -Xrbl_inv bl[0] out vdd gnd pinv_10 -Mrbl_access_tx vdd delayed_en bl[0] vdd p m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -Xdelay_chain en delayed_en vdd gnd delay_chain -Xbitcell bl[0] br[0] delayed_en vdd gnd replica_cell_6t -Xload bl[0] br[0] gnd gnd gnd gnd vdd gnd bitline_load -.ENDS replica_bitline - -.SUBCKT control_logic csb web oeb clk s_en w_en tri_en tri_en_bar clk_buf_bar clk_buf vdd gnd -Xctrl_dffs csb web oeb cs_bar cs we_bar we oe_bar oe clk_buf vdd gnd dff_array_3x1 -Xclkbuf clk clk_buf_bar clk_buf vdd gnd pinvbuf_4_16 -Xnand3_w_en_bar clk_buf_bar cs we w_en_bar vdd gnd pnand3_1 -Xinv_pre_w_en w_en_bar pre_w_en vdd gnd pinv_6 -Xinv_pre_w_en_bar pre_w_en pre_w_en_bar vdd gnd pinv_7 -Xinv_w_en2 pre_w_en_bar w_en vdd gnd pinv_8 -Xinv_tri_en1 pre_tri_en_bar pre_tri_en1 vdd gnd pinv_7 -Xtri_en_buf1 pre_tri_en1 pre_tri_en_bar1 vdd gnd pinv_7 -Xtri_en_buf2 pre_tri_en_bar1 tri_en vdd gnd pinv_8 -Xnand2_tri_en clk_buf_bar oe pre_tri_en_bar vdd gnd pnand2_1 -Xtri_en_bar_buf1 pre_tri_en_bar pre_tri_en2 vdd gnd pinv_7 -Xtri_en_bar_buf2 pre_tri_en2 tri_en_bar vdd gnd pinv_8 -Xnand3_rblk_bar clk_buf_bar oe cs rblk_bar vdd gnd pnand3_1 -Xinv_rblk rblk_bar rblk vdd gnd pinv_6 -Xinv_s_en pre_s_en_bar s_en vdd gnd pinv_8 -Xinv_pre_s_en_bar pre_s_en pre_s_en_bar vdd gnd pinv_7 -Xreplica_bitline rblk pre_s_en vdd gnd replica_bitline -.ENDS control_logic - -.SUBCKT dff_array din[0] din[1] din[2] din[3] dout[0] dout[1] dout[2] dout[3] clk vdd gnd -XXdff_r0_c0 din[0] dout[0] clk vdd gnd dff -XXdff_r1_c0 din[1] dout[1] clk vdd gnd dff -XXdff_r2_c0 din[2] dout[2] clk vdd gnd dff -XXdff_r3_c0 din[3] dout[3] clk vdd gnd dff -.ENDS dff_array - -.SUBCKT bitcell_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd -Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t -Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t -Xbit_r2_c0 bl[0] br[0] wl[2] vdd gnd cell_6t -Xbit_r3_c0 bl[0] br[0] wl[3] vdd gnd cell_6t -Xbit_r4_c0 bl[0] br[0] wl[4] vdd gnd cell_6t -Xbit_r5_c0 bl[0] br[0] wl[5] vdd gnd cell_6t -Xbit_r6_c0 bl[0] br[0] wl[6] vdd gnd cell_6t -Xbit_r7_c0 bl[0] br[0] wl[7] vdd gnd cell_6t -Xbit_r8_c0 bl[0] br[0] wl[8] vdd gnd cell_6t -Xbit_r9_c0 bl[0] br[0] wl[9] vdd gnd cell_6t -Xbit_r10_c0 bl[0] br[0] wl[10] vdd gnd cell_6t -Xbit_r11_c0 bl[0] br[0] wl[11] vdd gnd cell_6t -Xbit_r12_c0 bl[0] br[0] wl[12] vdd gnd cell_6t -Xbit_r13_c0 bl[0] br[0] wl[13] vdd gnd cell_6t -Xbit_r14_c0 bl[0] br[0] wl[14] vdd gnd cell_6t -Xbit_r15_c0 bl[0] br[0] wl[15] vdd gnd cell_6t -Xbit_r0_c1 bl[1] br[1] wl[0] vdd gnd cell_6t -Xbit_r1_c1 bl[1] br[1] wl[1] vdd gnd cell_6t -Xbit_r2_c1 bl[1] br[1] wl[2] vdd gnd cell_6t -Xbit_r3_c1 bl[1] br[1] wl[3] vdd gnd cell_6t -Xbit_r4_c1 bl[1] br[1] wl[4] vdd gnd cell_6t -Xbit_r5_c1 bl[1] br[1] wl[5] vdd gnd cell_6t -Xbit_r6_c1 bl[1] br[1] wl[6] vdd gnd cell_6t -Xbit_r7_c1 bl[1] br[1] wl[7] vdd gnd cell_6t -Xbit_r8_c1 bl[1] br[1] wl[8] vdd gnd cell_6t -Xbit_r9_c1 bl[1] br[1] wl[9] vdd gnd cell_6t -Xbit_r10_c1 bl[1] br[1] wl[10] vdd gnd cell_6t -Xbit_r11_c1 bl[1] br[1] wl[11] vdd gnd cell_6t -Xbit_r12_c1 bl[1] br[1] wl[12] vdd gnd cell_6t -Xbit_r13_c1 bl[1] br[1] wl[13] vdd gnd cell_6t -Xbit_r14_c1 bl[1] br[1] wl[14] vdd gnd cell_6t -Xbit_r15_c1 bl[1] br[1] wl[15] vdd gnd cell_6t -Xbit_r0_c2 bl[2] br[2] wl[0] vdd gnd cell_6t -Xbit_r1_c2 bl[2] br[2] wl[1] vdd gnd cell_6t -Xbit_r2_c2 bl[2] br[2] wl[2] vdd gnd cell_6t -Xbit_r3_c2 bl[2] br[2] wl[3] vdd gnd cell_6t -Xbit_r4_c2 bl[2] br[2] wl[4] vdd gnd cell_6t -Xbit_r5_c2 bl[2] br[2] wl[5] vdd gnd cell_6t -Xbit_r6_c2 bl[2] br[2] wl[6] vdd gnd cell_6t -Xbit_r7_c2 bl[2] br[2] wl[7] vdd gnd cell_6t -Xbit_r8_c2 bl[2] br[2] wl[8] vdd gnd cell_6t -Xbit_r9_c2 bl[2] br[2] wl[9] vdd gnd cell_6t -Xbit_r10_c2 bl[2] br[2] wl[10] vdd gnd cell_6t -Xbit_r11_c2 bl[2] br[2] wl[11] vdd gnd cell_6t -Xbit_r12_c2 bl[2] br[2] wl[12] vdd gnd cell_6t -Xbit_r13_c2 bl[2] br[2] wl[13] vdd gnd cell_6t -Xbit_r14_c2 bl[2] br[2] wl[14] vdd gnd cell_6t -Xbit_r15_c2 bl[2] br[2] wl[15] vdd gnd cell_6t -Xbit_r0_c3 bl[3] br[3] wl[0] vdd gnd cell_6t -Xbit_r1_c3 bl[3] br[3] wl[1] vdd gnd cell_6t -Xbit_r2_c3 bl[3] br[3] wl[2] vdd gnd cell_6t -Xbit_r3_c3 bl[3] br[3] wl[3] vdd gnd cell_6t -Xbit_r4_c3 bl[3] br[3] wl[4] vdd gnd cell_6t -Xbit_r5_c3 bl[3] br[3] wl[5] vdd gnd cell_6t -Xbit_r6_c3 bl[3] br[3] wl[6] vdd gnd cell_6t -Xbit_r7_c3 bl[3] br[3] wl[7] vdd gnd cell_6t -Xbit_r8_c3 bl[3] br[3] wl[8] vdd gnd cell_6t -Xbit_r9_c3 bl[3] br[3] wl[9] vdd gnd cell_6t -Xbit_r10_c3 bl[3] br[3] wl[10] vdd gnd cell_6t -Xbit_r11_c3 bl[3] br[3] wl[11] vdd gnd cell_6t -Xbit_r12_c3 bl[3] br[3] wl[12] vdd gnd cell_6t -Xbit_r13_c3 bl[3] br[3] wl[13] vdd gnd cell_6t -Xbit_r14_c3 bl[3] br[3] wl[14] vdd gnd cell_6t -Xbit_r15_c3 bl[3] br[3] wl[15] vdd gnd cell_6t -.ENDS bitcell_array - -* ptx M{0} {1} p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p - -.SUBCKT precharge bl br en vdd -Mlower_pmos bl en br vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mupper_pmos1 bl en vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mupper_pmos2 br en vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS precharge - -.SUBCKT precharge_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] en vdd -Xpre_column_0 bl[0] br[0] en vdd precharge -Xpre_column_1 bl[1] br[1] en vdd precharge -Xpre_column_2 bl[2] br[2] en vdd precharge -Xpre_column_3 bl[3] br[3] en vdd precharge -.ENDS precharge_array -*********************** "sense_amp" ****************************** - -.SUBCKT sense_amp bl br dout en vdd gnd -M_1 dout net_1 vdd vdd p W='5.4*1u' L=0.6u -M_2 dout net_1 net_2 gnd n W='2.7*1u' L=0.6u -M_3 net_1 dout vdd vdd p W='5.4*1u' L=0.6u -M_4 net_1 dout net_2 gnd n W='2.7*1u' L=0.6u -M_5 bl en dout vdd p W='7.2*1u' L=0.6u -M_6 br en net_1 vdd p W='7.2*1u' L=0.6u -M_7 net_2 en gnd gnd n W='2.7*1u' L=0.6u -.ENDS sense_amp - - -.SUBCKT sense_amp_array data[0] bl[0] br[0] data[1] bl[1] br[1] data[2] bl[2] br[2] data[3] bl[3] br[3] en vdd gnd -Xsa_d0 bl[0] br[0] data[0] en vdd gnd sense_amp -Xsa_d1 bl[1] br[1] data[1] en vdd gnd sense_amp -Xsa_d2 bl[2] br[2] data[2] en vdd gnd sense_amp -Xsa_d3 bl[3] br[3] data[3] en vdd gnd sense_amp -.ENDS sense_amp_array -*********************** Write_Driver ****************************** -.SUBCKT write_driver din bl br en vdd gnd - -**** Inverter to conver Data_in to data_in_bar ****** -M_1 net_3 din gnd gnd n W='1.2*1u' L=0.6u -M_2 net_3 din vdd vdd p W='2.1*1u' L=0.6u - -**** 2input nand gate follwed by inverter to drive BL ****** -M_3 net_2 en net_7 gnd n W='2.1*1u' L=0.6u -M_4 net_7 din gnd gnd n W='2.1*1u' L=0.6u -M_5 net_2 en vdd vdd p W='2.1*1u' L=0.6u -M_6 net_2 din vdd vdd p W='2.1*1u' L=0.6u - - -M_7 net_1 net_2 vdd vdd p W='2.1*1u' L=0.6u -M_8 net_1 net_2 gnd gnd n W='1.2*1u' L=0.6u - -**** 2input nand gate follwed by inverter to drive BR****** - -M_9 net_4 en vdd vdd p W='2.1*1u' L=0.6u -M_10 net_4 en net_8 gnd n W='2.1*1u' L=0.6u -M_11 net_8 net_3 gnd gnd n W='2.1*1u' L=0.6u -M_12 net_4 net_3 vdd vdd p W='2.1*1u' L=0.6u - -M_13 net_6 net_4 vdd vdd p W='2.1*1u' L=0.6u -M_14 net_6 net_4 gnd gnd n W='1.2*1u' L=0.6u - -************************************************ - -M_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u -M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u -M_17 net_5 en gnd gnd n W='3.6*1u' L=0.6u - - - -.ENDS $ write_driver - - -.SUBCKT write_driver_array data[0] data[1] data[2] data[3] bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] en vdd gnd -XXwrite_driver0 data[0] bl[0] br[0] en vdd gnd write_driver -XXwrite_driver1 data[1] bl[1] br[1] en vdd gnd write_driver -XXwrite_driver2 data[2] bl[2] br[2] en vdd gnd write_driver -XXwrite_driver3 data[3] bl[3] br[3] en vdd gnd write_driver -.ENDS write_driver_array - -.SUBCKT pinv_11 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_11 - -.SUBCKT pnand2_2 A B Z vdd gnd -Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand2_2 - -.SUBCKT pnand3_2 A B C Z vdd gnd -Mpnand3_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos3 Z C vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos1 Z C net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos2 net1 B net2 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos3 net2 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand3_2 - -.SUBCKT pinv_12 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_12 - -.SUBCKT pnand2_3 A B Z vdd gnd -Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand2_3 - -.SUBCKT pre2x4 in[0] in[1] out[0] out[1] out[2] out[3] vdd gnd -XXpre_inv[0] in[0] inbar[0] vdd gnd pinv_12 -XXpre_inv[1] in[1] inbar[1] vdd gnd pinv_12 -XXpre_nand_inv[0] Z[0] out[0] vdd gnd pinv_12 -XXpre_nand_inv[1] Z[1] out[1] vdd gnd pinv_12 -XXpre_nand_inv[2] Z[2] out[2] vdd gnd pinv_12 -XXpre_nand_inv[3] Z[3] out[3] vdd gnd pinv_12 -XXpre2x4_nand[0] inbar[0] inbar[1] Z[0] vdd gnd pnand2_3 -XXpre2x4_nand[1] in[0] inbar[1] Z[1] vdd gnd pnand2_3 -XXpre2x4_nand[2] inbar[0] in[1] Z[2] vdd gnd pnand2_3 -XXpre2x4_nand[3] in[0] in[1] Z[3] vdd gnd pnand2_3 -.ENDS pre2x4 - -.SUBCKT pinv_13 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_13 - -.SUBCKT pnand3_3 A B C Z vdd gnd -Mpnand3_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_pmos3 Z C vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos1 Z C net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos2 net1 B net2 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand3_nmos3 net2 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand3_3 - -.SUBCKT pre3x8 in[0] in[1] in[2] out[0] out[1] out[2] out[3] out[4] out[5] out[6] out[7] vdd gnd -XXpre_inv[0] in[0] inbar[0] vdd gnd pinv_13 -XXpre_inv[1] in[1] inbar[1] vdd gnd pinv_13 -XXpre_inv[2] in[2] inbar[2] vdd gnd pinv_13 -XXpre_nand_inv[0] Z[0] out[0] vdd gnd pinv_13 -XXpre_nand_inv[1] Z[1] out[1] vdd gnd pinv_13 -XXpre_nand_inv[2] Z[2] out[2] vdd gnd pinv_13 -XXpre_nand_inv[3] Z[3] out[3] vdd gnd pinv_13 -XXpre_nand_inv[4] Z[4] out[4] vdd gnd pinv_13 -XXpre_nand_inv[5] Z[5] out[5] vdd gnd pinv_13 -XXpre_nand_inv[6] Z[6] out[6] vdd gnd pinv_13 -XXpre_nand_inv[7] Z[7] out[7] vdd gnd pinv_13 -XXpre3x8_nand[0] inbar[0] inbar[1] inbar[2] Z[0] vdd gnd pnand3_3 -XXpre3x8_nand[1] in[0] inbar[1] inbar[2] Z[1] vdd gnd pnand3_3 -XXpre3x8_nand[2] inbar[0] in[1] inbar[2] Z[2] vdd gnd pnand3_3 -XXpre3x8_nand[3] in[0] in[1] inbar[2] Z[3] vdd gnd pnand3_3 -XXpre3x8_nand[4] inbar[0] inbar[1] in[2] Z[4] vdd gnd pnand3_3 -XXpre3x8_nand[5] in[0] inbar[1] in[2] Z[5] vdd gnd pnand3_3 -XXpre3x8_nand[6] inbar[0] in[1] in[2] Z[6] vdd gnd pnand3_3 -XXpre3x8_nand[7] in[0] in[1] in[2] Z[7] vdd gnd pnand3_3 -.ENDS pre3x8 - -.SUBCKT hierarchical_decoder_16rows A[0] A[1] A[2] A[3] decode[0] decode[1] decode[2] decode[3] decode[4] decode[5] decode[6] decode[7] decode[8] decode[9] decode[10] decode[11] decode[12] decode[13] decode[14] decode[15] vdd gnd -Xpre[0] A[0] A[1] out[0] out[1] out[2] out[3] vdd gnd pre2x4 -Xpre[1] A[2] A[3] out[4] out[5] out[6] out[7] vdd gnd pre2x4 -XDEC_NAND[0] out[0] out[4] Z[0] vdd gnd pnand2_2 -XDEC_NAND[1] out[0] out[5] Z[1] vdd gnd pnand2_2 -XDEC_NAND[2] out[0] out[6] Z[2] vdd gnd pnand2_2 -XDEC_NAND[3] out[0] out[7] Z[3] vdd gnd pnand2_2 -XDEC_NAND[4] out[1] out[4] Z[4] vdd gnd pnand2_2 -XDEC_NAND[5] out[1] out[5] Z[5] vdd gnd pnand2_2 -XDEC_NAND[6] out[1] out[6] Z[6] vdd gnd pnand2_2 -XDEC_NAND[7] out[1] out[7] Z[7] vdd gnd pnand2_2 -XDEC_NAND[8] out[2] out[4] Z[8] vdd gnd pnand2_2 -XDEC_NAND[9] out[2] out[5] Z[9] vdd gnd pnand2_2 -XDEC_NAND[10] out[2] out[6] Z[10] vdd gnd pnand2_2 -XDEC_NAND[11] out[2] out[7] Z[11] vdd gnd pnand2_2 -XDEC_NAND[12] out[3] out[4] Z[12] vdd gnd pnand2_2 -XDEC_NAND[13] out[3] out[5] Z[13] vdd gnd pnand2_2 -XDEC_NAND[14] out[3] out[6] Z[14] vdd gnd pnand2_2 -XDEC_NAND[15] out[3] out[7] Z[15] vdd gnd pnand2_2 -XDEC_INV_[0] Z[0] decode[0] vdd gnd pinv_11 -XDEC_INV_[1] Z[1] decode[1] vdd gnd pinv_11 -XDEC_INV_[2] Z[2] decode[2] vdd gnd pinv_11 -XDEC_INV_[3] Z[3] decode[3] vdd gnd pinv_11 -XDEC_INV_[4] Z[4] decode[4] vdd gnd pinv_11 -XDEC_INV_[5] Z[5] decode[5] vdd gnd pinv_11 -XDEC_INV_[6] Z[6] decode[6] vdd gnd pinv_11 -XDEC_INV_[7] Z[7] decode[7] vdd gnd pinv_11 -XDEC_INV_[8] Z[8] decode[8] vdd gnd pinv_11 -XDEC_INV_[9] Z[9] decode[9] vdd gnd pinv_11 -XDEC_INV_[10] Z[10] decode[10] vdd gnd pinv_11 -XDEC_INV_[11] Z[11] decode[11] vdd gnd pinv_11 -XDEC_INV_[12] Z[12] decode[12] vdd gnd pinv_11 -XDEC_INV_[13] Z[13] decode[13] vdd gnd pinv_11 -XDEC_INV_[14] Z[14] decode[14] vdd gnd pinv_11 -XDEC_INV_[15] Z[15] decode[15] vdd gnd pinv_11 -.ENDS hierarchical_decoder_16rows -*********************** tri_gate ****************************** - -.SUBCKT tri_gate in out en en_bar vdd gnd - -M_1 net_2 in_inv gnd gnd n W='1.2*1u' L=0.6u -M_2 net_3 in_inv vdd vdd p W='2.4*1u' L=0.6u -M_3 out en_bar net_3 vdd p W='2.4*1u' L=0.6u -M_4 out en net_2 gnd n W='1.2*1u' L=0.6u -M_5 in_inv in vdd vdd p W='2.4*1u' L=0.6u -M_6 in_inv in gnd gnd n W='1.2*1u' L=0.6u - - -.ENDS - -.SUBCKT tri_gate_array in[0] in[1] in[2] in[3] out[0] out[1] out[2] out[3] en en_bar vdd gnd -XXtri_gate0 in[0] out[0] en en_bar vdd gnd tri_gate -XXtri_gate1 in[1] out[1] en en_bar vdd gnd tri_gate -XXtri_gate2 in[2] out[2] en en_bar vdd gnd tri_gate -XXtri_gate3 in[3] out[3] en en_bar vdd gnd tri_gate -.ENDS tri_gate_array - -.SUBCKT pinv_14 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_14 - -.SUBCKT pinv_15 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_15 - -.SUBCKT pnand2_4 A B Z vdd gnd -Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -.ENDS pnand2_4 - -.SUBCKT wordline_driver in[0] in[1] in[2] in[3] in[4] in[5] in[6] in[7] in[8] in[9] in[10] in[11] in[12] in[13] in[14] in[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] en vdd gnd -Xwl_driver_inv_en0 en en_bar[0] vdd gnd pinv_15 -Xwl_driver_nand0 en_bar[0] in[0] net[0] vdd gnd pnand2_4 -Xwl_driver_inv0 net[0] wl[0] vdd gnd pinv_14 -Xwl_driver_inv_en1 en en_bar[1] vdd gnd pinv_15 -Xwl_driver_nand1 en_bar[1] in[1] net[1] vdd gnd pnand2_4 -Xwl_driver_inv1 net[1] wl[1] vdd gnd pinv_14 -Xwl_driver_inv_en2 en en_bar[2] vdd gnd pinv_15 -Xwl_driver_nand2 en_bar[2] in[2] net[2] vdd gnd pnand2_4 -Xwl_driver_inv2 net[2] wl[2] vdd gnd pinv_14 -Xwl_driver_inv_en3 en en_bar[3] vdd gnd pinv_15 -Xwl_driver_nand3 en_bar[3] in[3] net[3] vdd gnd pnand2_4 -Xwl_driver_inv3 net[3] wl[3] vdd gnd pinv_14 -Xwl_driver_inv_en4 en en_bar[4] vdd gnd pinv_15 -Xwl_driver_nand4 en_bar[4] in[4] net[4] vdd gnd pnand2_4 -Xwl_driver_inv4 net[4] wl[4] vdd gnd pinv_14 -Xwl_driver_inv_en5 en en_bar[5] vdd gnd pinv_15 -Xwl_driver_nand5 en_bar[5] in[5] net[5] vdd gnd pnand2_4 -Xwl_driver_inv5 net[5] wl[5] vdd gnd pinv_14 -Xwl_driver_inv_en6 en en_bar[6] vdd gnd pinv_15 -Xwl_driver_nand6 en_bar[6] in[6] net[6] vdd gnd pnand2_4 -Xwl_driver_inv6 net[6] wl[6] vdd gnd pinv_14 -Xwl_driver_inv_en7 en en_bar[7] vdd gnd pinv_15 -Xwl_driver_nand7 en_bar[7] in[7] net[7] vdd gnd pnand2_4 -Xwl_driver_inv7 net[7] wl[7] vdd gnd pinv_14 -Xwl_driver_inv_en8 en en_bar[8] vdd gnd pinv_15 -Xwl_driver_nand8 en_bar[8] in[8] net[8] vdd gnd pnand2_4 -Xwl_driver_inv8 net[8] wl[8] vdd gnd pinv_14 -Xwl_driver_inv_en9 en en_bar[9] vdd gnd pinv_15 -Xwl_driver_nand9 en_bar[9] in[9] net[9] vdd gnd pnand2_4 -Xwl_driver_inv9 net[9] wl[9] vdd gnd pinv_14 -Xwl_driver_inv_en10 en en_bar[10] vdd gnd pinv_15 -Xwl_driver_nand10 en_bar[10] in[10] net[10] vdd gnd pnand2_4 -Xwl_driver_inv10 net[10] wl[10] vdd gnd pinv_14 -Xwl_driver_inv_en11 en en_bar[11] vdd gnd pinv_15 -Xwl_driver_nand11 en_bar[11] in[11] net[11] vdd gnd pnand2_4 -Xwl_driver_inv11 net[11] wl[11] vdd gnd pinv_14 -Xwl_driver_inv_en12 en en_bar[12] vdd gnd pinv_15 -Xwl_driver_nand12 en_bar[12] in[12] net[12] vdd gnd pnand2_4 -Xwl_driver_inv12 net[12] wl[12] vdd gnd pinv_14 -Xwl_driver_inv_en13 en en_bar[13] vdd gnd pinv_15 -Xwl_driver_nand13 en_bar[13] in[13] net[13] vdd gnd pnand2_4 -Xwl_driver_inv13 net[13] wl[13] vdd gnd pinv_14 -Xwl_driver_inv_en14 en en_bar[14] vdd gnd pinv_15 -Xwl_driver_nand14 en_bar[14] in[14] net[14] vdd gnd pnand2_4 -Xwl_driver_inv14 net[14] wl[14] vdd gnd pinv_14 -Xwl_driver_inv_en15 en en_bar[15] vdd gnd pinv_15 -Xwl_driver_nand15 en_bar[15] in[15] net[15] vdd gnd pnand2_4 -Xwl_driver_inv15 net[15] wl[15] vdd gnd pinv_14 -.ENDS wordline_driver - -.SUBCKT pinv_16 A Z vdd gnd -Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p -Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p -.ENDS pinv_16 - -.SUBCKT bank DOUT[0] DOUT[1] DOUT[2] DOUT[3] DIN[0] DIN[1] DIN[2] DIN[3] A[0] A[1] A[2] A[3] s_en w_en tri_en_bar tri_en clk_buf_bar clk_buf vdd gnd -Xbitcell_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd bitcell_array -Xprecharge_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] clk_buf_bar vdd precharge_array -Xsense_amp_array sa_out[0] bl[0] br[0] sa_out[1] bl[1] br[1] sa_out[2] bl[2] br[2] sa_out[3] bl[3] br[3] s_en vdd gnd sense_amp_array -Xwrite_driver_array DIN[0] DIN[1] DIN[2] DIN[3] bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] w_en vdd gnd write_driver_array -Xtri_gate_array sa_out[0] sa_out[1] sa_out[2] sa_out[3] DOUT[0] DOUT[1] DOUT[2] DOUT[3] tri_en tri_en_bar vdd gnd tri_gate_array -Xrow_decoder A[0] A[1] A[2] A[3] dec_out[0] dec_out[1] dec_out[2] dec_out[3] dec_out[4] dec_out[5] dec_out[6] dec_out[7] dec_out[8] dec_out[9] dec_out[10] dec_out[11] dec_out[12] dec_out[13] dec_out[14] dec_out[15] vdd gnd hierarchical_decoder_16rows -Xwordline_driver dec_out[0] dec_out[1] dec_out[2] dec_out[3] dec_out[4] dec_out[5] dec_out[6] dec_out[7] dec_out[8] dec_out[9] dec_out[10] dec_out[11] dec_out[12] dec_out[13] dec_out[14] dec_out[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] clk_buf vdd gnd wordline_driver -.ENDS bank - -.SUBCKT sram1 DIN[0] DIN[1] DIN[2] DIN[3] ADDR[0] ADDR[1] ADDR[2] ADDR[3] csb web oeb clk DOUT[0] DOUT[1] DOUT[2] DOUT[3] vdd gnd -Xbank0 DOUT[0] DOUT[1] DOUT[2] DOUT[3] DIN[0] DIN[1] DIN[2] DIN[3] A[0] A[1] A[2] A[3] s_en w_en tri_en_bar tri_en clk_buf_bar clk_buf vdd gnd bank -Xcontrol csb_s web_s oeb_s clk s_en w_en tri_en tri_en_bar clk_buf_bar clk_buf vdd gnd control_logic -Xaddress ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A[1] A[2] A[3] clk_buf vdd gnd dff_array -Xaddress ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A[1] A[2] A[3] clk_buf vdd gnd dff_array -.ENDS sram1 diff --git a/compiler/tests/sram1_TT_5p0V_25C.lib b/compiler/tests/sram1_TT_5p0V_25C.lib deleted file mode 100644 index ddf17785..00000000 --- a/compiler/tests/sram1_TT_5p0V_25C.lib +++ /dev/null @@ -1,347 +0,0 @@ -library (sram1_TT_5p0V_25C_lib){ - delay_model : "table_lookup"; - time_unit : "1ns" ; - voltage_unit : "1v" ; - current_unit : "1mA" ; - resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; - leakage_power_unit : "1mW" ; - pulling_resistance_unit :"1kohm" ; - operating_conditions(OC){ - process : 1.0 ; - voltage : 5.0 ; - temperature : 25; - } - - input_threshold_pct_fall : 50.0 ; - output_threshold_pct_fall : 50.0 ; - input_threshold_pct_rise : 50.0 ; - output_threshold_pct_rise : 50.0 ; - slew_lower_threshold_pct_fall : 10.0 ; - slew_upper_threshold_pct_fall : 90.0 ; - slew_lower_threshold_pct_rise : 10.0 ; - slew_upper_threshold_pct_rise : 90.0 ; - - nom_voltage : 5.0; - nom_temperature : 25; - nom_process : 1.0; - default_cell_leakage_power : 0.0 ; - default_leakage_power_density : 0.0 ; - default_input_pin_cap : 1.0 ; - default_inout_pin_cap : 1.0 ; - default_output_pin_cap : 0.0 ; - default_max_transition : 0.5 ; - default_fanout_load : 1.0 ; - default_max_fanout : 4.0 ; - default_connection_class : universal ; - - lu_table_template(CELL_TABLE){ - variable_1 : input_net_transition; - variable_2 : total_output_net_capacitance; - index_1("0.0125, 0.05, 0.4"); - index_2("2.45605, 9.8242, 78.5936"); - } - - lu_table_template(CONSTRAINT_TABLE){ - variable_1 : related_pin_transition; - variable_2 : constrained_pin_transition; - index_1("0.0125, 0.05, 0.4"); - index_2("0.0125, 0.05, 0.4"); - } - - default_operating_conditions : OC; - - - type (DATA){ - base_type : array; - data_type : bit; - bit_width : 4; - bit_from : 0; - bit_to : 3; - } - - type (ADDR){ - base_type : array; - data_type : bit; - bit_width : 4; - bit_from : 0; - bit_to : 3; - } - -cell (sram1){ - memory(){ - type : ram; - address_width : 4; - word_width : 4; - } - interface_timing : true; - dont_use : true; - map_only : true; - dont_touch : true; - area : 136566.0; - - leakage_power () { - when : "CSb"; - value : 0.000202; - } - cell_leakage_power : 0; - bus(DATA){ - bus_type : DATA; - direction : inout; - max_capacitance : 78.5936; - min_capacitance : 2.45605; - three_state : "!OEb & !clk"; - memory_write(){ - address : ADDR; - clocked_on : clk; - } - memory_read(){ - address : ADDR; - } - pin(DATA[3:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - timing(){ - timing_sense : non_unate; - related_pin : "clk"; - timing_type : falling_edge; - cell_rise(CELL_TABLE) { - values("0.612, 0.66, 1.1",\ - "0.612, 0.66, 1.1",\ - "0.612, 0.66, 1.1"); - } - cell_fall(CELL_TABLE) { - values("0.612, 0.66, 1.1",\ - "0.612, 0.66, 1.1",\ - "0.612, 0.66, 1.1"); - } - rise_transition(CELL_TABLE) { - values("0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61"); - } - fall_transition(CELL_TABLE) { - values("0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61",\ - "0.024, 0.081, 0.61"); - } - } - } - } - - bus(ADDR){ - bus_type : ADDR; - direction : input; - capacitance : 9.8242; - max_transition : 0.4; - pin(ADDR[3:0]){ - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - } - - pin(CSb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - - pin(OEb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - - pin(WEb){ - direction : input; - capacitance : 9.8242; - timing(){ - timing_type : setup_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009",\ - "0.009, 0.009, 0.009"); - } - } - timing(){ - timing_type : hold_rising; - related_pin : "clk"; - rise_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - fall_constraint(CONSTRAINT_TABLE) { - values("0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001",\ - "0.001, 0.001, 0.001"); - } - } - } - - pin(clk){ - clock : true; - direction : input; - capacitance : 9.8242; - internal_power(){ - when : "!CSb & clk & !WEb"; - rise_power(scalar){ - values("10.812808757533329"); - } - fall_power(scalar){ - values("10.812808757533329"); - } - } - internal_power(){ - when : "!CSb & !clk & WEb"; - rise_power(scalar){ - values("10.812808757533329"); - } - fall_power(scalar){ - values("10.812808757533329"); - } - } - internal_power(){ - when : "CSb"; - rise_power(scalar){ - values("0"); - } - fall_power(scalar){ - values("0"); - } - } - timing(){ - timing_type :"min_pulse_width"; - related_pin : clk; - rise_constraint(scalar) { - values("0.0"); - } - fall_constraint(scalar) { - values("0.0"); - } - } - timing(){ - timing_type :"minimum_period"; - related_pin : clk; - rise_constraint(scalar) { - values("0"); - } - fall_constraint(scalar) { - values("0"); - } - } - } - } -} diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py old mode 100644 new mode 100755 diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 4cc3a093..7510b340 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -70,7 +70,7 @@ num_drc_runs = 0 num_lvs_runs = 0 num_pex_runs = 0 -def run_drc(cell_name, gds_name): +def run_drc(cell_name, gds_name, extract=False, final_verification=False): """Run DRC check on a given top-level name which is implemented in gds_name.""" @@ -175,18 +175,21 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): 'cmnFDIUseLayerMap': 1, 'cmnTranscriptFile': './lvs.log', 'cmnTranscriptEchoToFile': 1, - 'lvsRecognizeGates': 'NONE', - # FIXME: Remove when vdd/gnd connected - 'cmnVConnectNamesState' : 'ALL', #connects all nets with the same namee - # FIXME: Remove when vdd/gnd connected - 'lvsAbortOnSupplyError' : 0 + 'lvsRecognizeGates': 'NONE', } + # FIXME: Remove when vdd/gnd connected + #'cmnVConnectNamesState' : 'ALL', #connects all nets with the same namee + # FIXME: Remove when vdd/gnd connected + #'lvsAbortOnSupplyError' : 0 # This should be removed for final verification if not final_verification: lvs_runset['cmnVConnectReport']=1 lvs_runset['cmnVConnectNamesState']='SOME' lvs_runset['cmnVConnectNames']='vdd gnd' + else: + lvs_runset['lvsAbortOnSupplyError']=1 + # write the runset file diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 55e803b4..bb116da3 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -26,7 +26,7 @@ num_drc_runs = 0 num_lvs_runs = 0 num_pex_runs = 0 -def write_magic_script(cell_name, gds_name, extract=False): +def write_magic_script(cell_name, gds_name, extract=False, final_verification=False): """ Write a magic script to perform DRC and optionally extraction. """ global OPTS @@ -41,23 +41,28 @@ def write_magic_script(cell_name, gds_name, extract=False): f.write("load {}\n".format(cell_name)) # Flatten the cell to get rid of DRCs spanning multiple layers # (e.g. with routes) - f.write("flatten {}_new\n".format(cell_name)) - f.write("load {}_new\n".format(cell_name)) - f.write("cellname rename {0}_new {0}\n".format(cell_name)) - f.write("load {}\n".format(cell_name)) + #f.write("flatten {}_new\n".format(cell_name)) + #f.write("load {}_new\n".format(cell_name)) + #f.write("cellname rename {0}_new {0}\n".format(cell_name)) + #f.write("load {}\n".format(cell_name)) f.write("writeall force\n") f.write("drc check\n") f.write("drc catchup\n") f.write("drc count total\n") f.write("drc count\n") - if extract: - f.write("extract all\n") - f.write("ext2spice hierarchy on\n") - f.write("ext2spice scale off\n") - # Can choose hspice, ngspice, or spice3, - # but they all seem compatible enough. - #f.write("ext2spice format ngspice\n") - f.write("ext2spice\n") + if not extract: + pre = "#" + else: + pre = "" + if final_verification: + f.write(pre+"extract unique\n") + f.write(pre+"extract\n") + f.write(pre+"ext2spice hierarchy on\n") + f.write(pre+"ext2spice scale off\n") + # Can choose hspice, ngspice, or spice3, + # but they all seem compatible enough. + #f.write(pre+"ext2spice format ngspice\n") + f.write(pre+"ext2spice\n") f.write("quit -noprompt\n") f.write("EOF\n") @@ -68,11 +73,13 @@ def write_netgen_script(cell_name, sp_name): """ Write a netgen script to perform LVS. """ global OPTS - # This is a hack to prevent netgen from re-initializing the LVS - # commands. It will be unnecessary after Tim adds the nosetup option. - setup_file = OPTS.openram_temp + "setup.tcl" - f = open(setup_file, "w") - f.close() + + setup_file = OPTS.openram_tech + "mag_lib/setup.tcl" + if os.path.exists(setup_file): + # Copy setup.tcl file into temp dir + shutil.copy(setup_file, OPTS.openram_temp) + else: + setup_file = 'nosetup' run_file = OPTS.openram_temp + "run_lvs.sh" f = open(run_file, "w") @@ -86,44 +93,27 @@ def write_netgen_script(cell_name, sp_name): # cell_name)) # f.write("property {{{0}{1}.spice pfet}} tolerance {{w 0.1}}\n".format(OPTS.openram_temp, # cell_name)) - f.write("lvs {0}.spice {{{1} {0}}} setup.tcl {0}.lvs.report\n".format(cell_name, sp_name)) + f.write("lvs {0}.spice {{{1} {0}}} {2} {0}.lvs.report\n".format(cell_name, sp_name, setup_file)) f.write("quit\n") f.write("EOF\n") f.close() os.system("chmod u+x {}".format(run_file)) - setup_file = OPTS.openram_temp + "setup.tcl" - f = open(setup_file, "w") - f.write("ignore class c\n") - f.write("equate class {{nfet {0}.spice}} {{n {1}}}\n".format(cell_name, sp_name)) - f.write("equate class {{pfet {0}.spice}} {{p {1}}}\n".format(cell_name, sp_name)) - # This circuit has symmetries and needs to be flattened to resolve them or the banks won't pass - # Is there a more elegant way to add this when needed? - f.write("flatten class {{{0}.spice precharge_array_1}}\n".format(cell_name)) - f.write("flatten class {{{0}.spice precharge_array_2}}\n".format(cell_name)) - f.write("flatten class {{{0}.spice precharge_array_3}}\n".format(cell_name)) - f.write("flatten class {{{0}.spice precharge_array_4}}\n".format(cell_name)) - f.write("property {{nfet {0}.spice}} remove as ad ps pd\n".format(cell_name)) - f.write("property {{pfet {0}.spice}} remove as ad ps pd\n".format(cell_name)) - f.write("property {{n {0}}} remove as ad ps pd\n".format(sp_name)) - f.write("property {{p {0}}} remove as ad ps pd\n".format(sp_name)) - f.write("permute transistors\n") - f.write("permute pins n source drain\n") - f.write("permute pins p source drain\n") - f.close() - -def run_drc(cell_name, gds_name, extract=False): +def run_drc(cell_name, gds_name, extract=False, final_verification=False): """Run DRC check on a cell which is implemented in gds_name.""" global num_drc_runs num_drc_runs += 1 # Copy .magicrc file into temp dir - shutil.copy(OPTS.openram_tech + "/mag_lib/.magicrc", - OPTS.openram_temp) + magic_file = OPTS.openram_tech + "mag_lib/.magicrc" + if os.path.exists(magic_file): + shutil.copy(magic_file, OPTS.openram_temp) + else: + debug.warning("Could not locate .magicrc file: {}".format(magic_file)) - write_magic_script(cell_name, gds_name, extract) + write_magic_script(cell_name, gds_name, extract, final_verification) # run drc cwd = os.getcwd() @@ -176,7 +166,7 @@ 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) + 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/cell_1rw_1r.gds b/technology/freepdk45/gds_lib/cell_1rw_1r.gds new file mode 100644 index 00000000..9f4e0650 Binary files /dev/null and b/technology/freepdk45/gds_lib/cell_1rw_1r.gds differ diff --git a/technology/freepdk45/sp_lib/cell_1rw_1r.sp b/technology/freepdk45/sp_lib/cell_1rw_1r.sp new file mode 100644 index 00000000..483a0b4b --- /dev/null +++ b/technology/freepdk45/sp_lib/cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT 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 Q_bar 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 Q_bar wl0 br0 gnd NMOS_VTG W=135.00n L=50n m=1 +MM1 Q Q_bar gnd gnd NMOS_VTG W=270.0n L=50n m=1 +MM0 Q_bar Q gnd gnd NMOS_VTG W=270.0n L=50n m=1 +MM3 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n m=1 +MM2 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n m=1 +.ENDS + diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 6c407ec0..6cbeabdd 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -1,15 +1,10 @@ import os +from design_rules import * """ File containing the process technology parameters for FreePDK 45nm. """ -info = {} -info["name"] = "freepdk45" -info["body_tie_down"] = 0 -info["has_pwell"] = True -info["has_nwell"] = True - #GDS file info GDS = {} # gds units @@ -76,7 +71,13 @@ parameter["6T_inv_pmos_size"] = 0.09 parameter["6T_access_size"] = 0.135 drclvs_home=os.environ.get("DRCLVS_HOME") -drc={} + +drc = design_rules("freepdk45") + +drc["body_tie_down"] = 0 +drc["has_pwell"] = True +drc["has_nwell"] = True + #grid size drc["grid"] = 0.0025 @@ -87,7 +88,7 @@ drc["xrc_rules"]=drclvs_home+"/calibrexRC.rul" drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/freepdk45/layers.map" # minwidth_tx with contact (no dog bone transistors) -drc["minwidth_tx"]=0.09 +drc["minwidth_tx"] = 0.09 drc["minlength_channel"] = 0.05 # WELL.2 Minimum spacing of nwell/pwell at different potential @@ -200,7 +201,18 @@ drc["via2_to_via2"] = 0.075 # METALINT.1 Minimum width of intermediate metal drc["minwidth_metal3"] = 0.07 # METALINT.2 Minimum spacing of intermediate metal -drc["metal3_to_metal3"] = 0.07 +#drc["metal3_to_metal3"] = 0.07 +# Minimum spacing of metal3 wider than 0.09 & longer than 0.3 = 0.09 +# Minimum spacing of metal3 wider than 0.27 & longer than 0.9 = 0.27 +# Minimum spacing of metal3 wider than 0.5 & longer than 1.8 = 0.5 +# Minimum spacing of metal3 wider than 0.9 & longer than 2.7 = 0.9 +# Minimum spacing of metal3 wider than 1.5 & longer than 4.0 = 1.5 +drc["metal3_to_metal3"] = drc_lut({(0.00, 0.0) : 0.07, + (0.09, 0.3) : 0.09, + (0.27, 0.9) : 0.27, + (0.50, 1.8) : 0.5, + (0.90, 2.7) : 0.9, + (1.50, 4.0) : 1.5}) # METALINT.3 Minimum enclosure around via1 on two opposite sides drc["metal3_extend_via2"] = 0.035 # Reserved for asymmetric enclosures @@ -213,22 +225,29 @@ drc["metal3_enclosure_via3"] = 0 drc["minarea_metal3"] = 0 # VIA2-3.1 Minimum width of Via[2-3] -drc["minwidth_via3"] = 0.065 +drc["minwidth_via3"] = 0.07 # VIA2-3.2 Minimum spacing of Via[2-3] -drc["via3_to_via3"] = 0.07 +drc["via3_to_via3"] = 0.085 # METALSMG.1 Minimum width of semi-global metal drc["minwidth_metal4"] = 0.14 # METALSMG.2 Minimum spacing of semi-global metal -drc["metal4_to_metal4"] = 0.14 +#drc["metal4_to_metal4"] = 0.14 +# Minimum spacing of metal4 wider than 0.27 & longer than 0.9 = 0.27 +# Minimum spacing of metal4 wider than 0.5 & longer than 1.8 = 0.5 +# Minimum spacing of metal4 wider than 0.9 & longer than 2.7 = 0.9 +# Minimum spacing of metal4 wider than 1.5 & longer than 4.0 = 1.5 +drc["metal4_to_metal4"] = drc_lut({(0.00, 0.0) : 0.14, + (0.27, 0.9) : 0.27, + (0.50, 1.8) : 0.5, + (0.90, 2.7) : 0.9, + (1.50, 4.0) : 1.5}) # METALSMG.3 Minimum enclosure around via[3-6] on two opposite sides -drc["metal4_extend_via3"] = 0.07 +drc["metal4_extend_via3"] = 0.0025 # Reserved for asymmetric enclosure -drc["metal4_enclosure_via3"] = 0 -# METALSMG.3 Minimum enclosure around via[3-6] on two opposite sides -drc["metal4_enclosure_via4"] = 0 -# Reserved for asymmetric enclosure -drc["metal4_extend_via4"] = 0.07 +drc["metal4_enclosure_via3"] = 0.0025 +# Not a rule +drc["minarea_metal4"] = 0 # Metal 5-10 are ommitted @@ -276,6 +295,7 @@ spice["channel"] = drc["minlength_channel"] spice["clk"] = "clk" # analytical delay parameters +spice["v_threshold_typical"] = 0.4 # Typical Threshold voltage in Volts spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms diff --git a/technology/scn3me_subm/mag_lib/setup.tcl b/technology/scn3me_subm/mag_lib/setup.tcl new file mode 100644 index 00000000..af55a416 --- /dev/null +++ b/technology/scn3me_subm/mag_lib/setup.tcl @@ -0,0 +1,15 @@ +# Setup file for netgen +ignore class c +equate class {-circuit1 nfet} {-circuit2 n} +equate class {-circuit1 pfet} {-circuit2 p} +# This circuit has symmetries and needs to be flattened to resolve them +# or the banks won't pass +flatten class {-circuit1 precharge_array_1} +flatten class {-circuit1 precharge_array_2} +flatten class {-circuit1 precharge_array_3} +flatten class {-circuit1 precharge_array_4} +property {-circuit1 nfet} remove as ad ps pd +property {-circuit1 pfet} remove as ad ps pd +property {-circuit2 n} remove as ad ps pd +property {-circuit2 p} remove as ad ps pd +permute transistors diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index c24d532a..d448b5dc 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -1,15 +1,10 @@ import os +from design_rules import * """ File containing the process technology parameters for SCMOS 3me, subm, 180nm. """ -info={} -info["name"]="scn3me_subm" -info["body_tie_down"] = 0 -info["has_pwell"] = True -info["has_nwell"] = True - #GDS file info GDS={} # gds units @@ -63,7 +58,13 @@ parameter["6T_access_size"] = 4*_lambda_ drclvs_home=os.environ.get("DRCLVS_HOME") -drc={} +drc = design_rules("scn3me_subm") + +drc["body_tie_down"] = 0 +drc["has_pwell"] = True +drc["has_nwell"] = True + + #grid size is 1/2 a lambda drc["grid"]=0.5*_lambda_ #DRC/LVS test set_up @@ -239,6 +240,7 @@ spice["clk"] = "clk" # analytical delay parameters # FIXME: These need to be updated for SCMOS, they are copied from FreePDK45. +spice["v_threshold_typical"] = 1.3 # Typical Threshold voltage in Volts spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms diff --git a/technology/scn3me_subm/tf/display.drf b/technology/scn3me_subm/tf/display.drf index 047879b6..4bd251e8 100644 --- a/technology/scn3me_subm/tf/display.drf +++ b/technology/scn3me_subm/tf/display.drf @@ -625,7 +625,7 @@ drDefinePacket( ( display deviceAnt stipple0 solid yellow yellow solid ) ( display winBottomShadow solid solid winColor1 winColor1 solid ) ( display PselectNet dots4 solid brown brown outlineStipple) - ( display comment stipple0 lineStyle0 winBack winBack outline ) + ( display comment stipple0 lineStyle0 winBack winBack outlineStipple) ( display Poly1 dots lineStyle0 red red outlineStipple) ( display Unrouted stipple0 lineStyle1 winColor5 winColor5 solid ) ( display stretch stipple0 solid yellow yellow solid ) diff --git a/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag new file mode 100644 index 00000000..7d3823b8 --- /dev/null +++ b/technology/scn4m_subm/mag_lib/cell_1rw_1r.mag @@ -0,0 +1,146 @@ +magic +tech scmos +timestamp 1539900829 +<< nwell >> +rect -18 -1 32 26 +<< pwell >> +rect -18 -51 32 -6 +<< ntransistor >> +rect -6 -18 -4 -12 +rect 2 -24 4 -12 +rect 10 -24 12 -12 +rect 18 -18 20 -12 +rect -6 -36 -4 -28 +rect 2 -36 4 -28 +rect 10 -36 12 -28 +rect 18 -36 20 -28 +<< ptransistor >> +rect 2 5 4 9 +rect 10 5 12 9 +<< ndiffusion >> +rect -11 -14 -6 -12 +rect -7 -18 -6 -14 +rect -4 -18 -3 -12 +rect 1 -20 2 -12 +rect -3 -24 2 -20 +rect 4 -24 5 -12 +rect 9 -24 10 -12 +rect 12 -20 13 -12 +rect 17 -18 18 -12 +rect 20 -14 25 -12 +rect 20 -18 21 -14 +rect 12 -24 17 -20 +rect -11 -30 -6 -28 +rect -7 -34 -6 -30 +rect -11 -36 -6 -34 +rect -4 -36 2 -28 +rect 4 -36 5 -28 +rect 9 -36 10 -28 +rect 12 -36 18 -28 +rect 20 -30 25 -28 +rect 20 -34 21 -30 +rect 20 -36 25 -34 +<< pdiffusion >> +rect 1 5 2 9 +rect 4 5 5 9 +rect 9 5 10 9 +rect 12 5 13 9 +<< ndcontact >> +rect -11 -18 -7 -14 +rect -3 -20 1 -12 +rect 5 -24 9 -12 +rect 13 -20 17 -12 +rect 21 -18 25 -14 +rect -11 -34 -7 -30 +rect 5 -36 9 -28 +rect 21 -34 25 -30 +<< pdcontact >> +rect -3 5 1 9 +rect 5 5 9 9 +rect 13 5 17 9 +<< psubstratepcontact >> +rect 5 -44 9 -40 +<< nsubstratencontact >> +rect 5 19 9 23 +<< polysilicon >> +rect 2 9 4 11 +rect 10 9 12 11 +rect 2 -5 4 5 +rect 10 2 12 5 +rect 11 -2 12 2 +rect -6 -12 -4 -7 +rect 2 -9 3 -5 +rect 2 -12 4 -9 +rect 10 -12 12 -2 +rect 18 -12 20 -7 +rect -6 -20 -4 -18 +rect 18 -20 20 -18 +rect -6 -28 -4 -27 +rect 2 -28 4 -24 +rect 10 -28 12 -24 +rect 18 -28 20 -27 +rect -6 -38 -4 -36 +rect 2 -38 4 -36 +rect 10 -38 12 -36 +rect 18 -38 20 -36 +<< polycontact >> +rect 7 -2 11 2 +rect -10 -11 -6 -7 +rect 3 -9 7 -5 +rect 20 -11 24 -7 +rect -8 -27 -4 -23 +rect 18 -27 22 -23 +<< metal1 >> +rect -18 19 5 23 +rect 9 19 32 23 +rect -18 12 32 16 +rect -10 -7 -6 12 +rect -3 2 0 5 +rect -3 -2 7 2 +rect -3 -12 0 -2 +rect 14 -5 17 5 +rect 7 -9 17 -5 +rect 14 -12 17 -9 +rect 20 -7 24 12 +rect -14 -18 -11 -14 +rect 25 -18 28 -14 +rect 5 -28 9 -24 +rect 5 -40 9 -36 +rect -17 -44 5 -40 +rect 9 -44 31 -40 +rect -17 -51 -4 -47 +rect 0 -51 14 -47 +rect 18 -51 31 -47 +<< m2contact >> +rect 5 19 9 23 +rect 5 5 9 9 +rect -18 -18 -14 -14 +rect -4 -27 0 -23 +rect 28 -18 32 -14 +rect 14 -27 18 -23 +rect -11 -34 -7 -30 +rect 21 -34 25 -30 +rect -4 -51 0 -47 +rect 14 -51 18 -47 +<< metal2 >> +rect -18 -14 -14 23 +rect -18 -51 -14 -18 +rect -11 -30 -7 23 +rect 5 9 9 19 +rect -11 -51 -7 -34 +rect -4 -47 0 -27 +rect 14 -47 18 -27 +rect 21 -30 25 23 +rect 21 -51 25 -34 +rect 28 -14 32 23 +rect 28 -51 32 -18 +<< labels >> +rlabel metal1 7 -49 7 -49 1 wl1 +rlabel psubstratepcontact 7 -42 7 -42 1 gnd +rlabel m2contact 7 21 7 21 5 vdd +rlabel metal1 -1 14 -1 14 1 wl0 +rlabel metal2 -16 -46 -16 -46 2 bl0 +rlabel metal2 -9 -46 -9 -46 1 bl1 +rlabel metal2 23 -46 23 -46 1 br1 +rlabel metal2 30 -46 30 -46 8 br0 +<< end >> diff --git a/technology/scn4m_subm/mag_lib/setup.tcl b/technology/scn4m_subm/mag_lib/setup.tcl new file mode 100644 index 00000000..caf7550b --- /dev/null +++ b/technology/scn4m_subm/mag_lib/setup.tcl @@ -0,0 +1,16 @@ +# Setup file for netgen +ignore class c +equate class {-circuit1 nfet} {-circuit2 n} +equate class {-circuit1 pfet} {-circuit2 p} +# This circuit has symmetries and needs to be flattened to resolve them +# or the banks won't pass +flatten class {-circuit1 bitcell_array} +flatten class {-circuit1 precharge_array_1} +flatten class {-circuit1 precharge_array_2} +flatten class {-circuit1 precharge_array_3} +flatten class {-circuit1 precharge_array_4} +property {-circuit1 nfet} remove as ad ps pd +property {-circuit1 pfet} remove as ad ps pd +property {-circuit2 n} remove as ad ps pd +property {-circuit2 p} remove as ad ps pd +permute transistors diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 68f70bcc..0e81953a 100755 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -1,15 +1,10 @@ import os +from design_rules import * """ File containing the process technology parameters for SCMOS 3me, subm, 180nm. """ -info={} -info["name"]="scn3me_subm" -info["body_tie_down"] = 0 -info["has_pwell"] = True -info["has_nwell"] = True - #GDS file info GDS={} # gds units @@ -65,14 +60,19 @@ parameter["6T_access_size"] = 4*_lambda_ drclvs_home=os.environ.get("DRCLVS_HOME") -drc={} +drc = design_rules("scn4me_sub") + +drc["body_tie_down"] = 0 +drc["has_pwell"] = True +drc["has_nwell"] = True + #grid size is 1/2 a lambda drc["grid"]=0.5*_lambda_ + #DRC/LVS test set_up drc["drc_rules"]=drclvs_home+"/calibreDRC_scn3me_subm.rul" drc["lvs_rules"]=drclvs_home+"/calibreLVS_scn3me_subm.rul" drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/scn3me_subm/layers.map" - # minwidth_tx with contact (no dog bone transistors) drc["minwidth_tx"] = 4*_lambda_ @@ -261,6 +261,7 @@ spice["clk"] = "clk" # analytical delay parameters # FIXME: These need to be updated for SCMOS, they are copied from FreePDK45. +spice["v_threshold_typical"] = 1.3 # Typical Threshold voltage in Volts spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms diff --git a/technology/scn4m_subm/tf/display.drf b/technology/scn4m_subm/tf/display.drf index aeeefe2c..39349998 100644 --- a/technology/scn4m_subm/tf/display.drf +++ b/technology/scn4m_subm/tf/display.drf @@ -546,7 +546,7 @@ drDefinePacket( ( display background stipple1 lineStyle0 black black outlineStipple) ( display y9 stipple0 solid silver silver solid ) ( display Metal3Net dots4 solid navy navy outlineStipple) - ( display Metal3Net dots4 solid tan tan outlineStipple) + ( display Metal4Net dots4 solid tan tan outlineStipple) ( display A1 stipple0 lineStyle0 winBack winBack solid ) ( display pin solid lineStyle0 red red solid ) ( display XPNet blank solid yellow yellow outline ) @@ -640,6 +640,7 @@ drDefinePacket( ( display Canplace blank solid cyan cyan outline ) ( display annotate7 stipple0 solid red red solid ) ( display Via2 solid solid navy navy solid ) + ( display Via3 solid solid tan tan solid ) ( display Metal2Pin stipple0 lineStyle0 magenta magenta solid ) ( display annotate4 stipple0 solid yellow yellow solid ) ( display device1 stipple1 lineStyle0 green green outlineStipple) diff --git a/technology/scn4m_subm/tf/glade_scn4me_subm.py b/technology/scn4m_subm/tf/glade_scn4m_subm.py similarity index 64% rename from technology/scn4m_subm/tf/glade_scn4me_subm.py rename to technology/scn4m_subm/tf/glade_scn4m_subm.py index d2f9aa7e..314727c3 100644 --- a/technology/scn4m_subm/tf/glade_scn4me_subm.py +++ b/technology/scn4m_subm/tf/glade_scn4m_subm.py @@ -1,5 +1,5 @@ import os -CWD = os.environ.get("OPENRAM_TECH") + "/scn3me_subm/tf" +CWD = os.environ.get("OPENRAM_TECH") + "/scn4m_subm/tf" ui().importCds("default", CWD+"/display.drf", CWD+"/mosis.tf", 1000, 1, CWD+"/layers.map") diff --git a/technology/scn4m_subm/tf/mosis.tf b/technology/scn4m_subm/tf/mosis.tf index e48d76a0..bae7f07a 100644 --- a/technology/scn4m_subm/tf/mosis.tf +++ b/technology/scn4m_subm/tf/mosis.tf @@ -147,6 +147,8 @@ layerDefinitions( ( Metal2 drawing ) ( Via2 drawing ) ( Metal3 drawing ) + ( Via3 drawing ) + ( Metal4 drawing ) ( annotate drawing ) ( annotate drawing1 ) ( annotate drawing2 ) @@ -161,6 +163,7 @@ layerDefinitions( ( Metal1 pin ) ( Metal2 pin ) ( Metal3 pin ) + ( Metal4 pin ) ( Glass drawing ) ( XP drawing ) ( prBoundary drawing ) @@ -203,6 +206,8 @@ layerDefinitions( ( Via net ) ( Metal3 net ) ( Via2 net ) + ( Metal4 net ) + ( Via3 net ) ( pin label ) ( text drawing ) ( pin drawing ) @@ -313,11 +318,14 @@ layerDefinitions( ( annotate drawing9 annotate9 t t nil t nil ) ( Via2 drawing Via2 t t t t t ) ( Metal3 drawing Metal3 t t t t t ) + ( Via3 drawing Via3 t t t t t ) + ( Metal4 drawing Metal4 t t t t t ) ( Glass drawing Glass t t t nil t ) ( XP drawing XP t t t nil t ) ( Metal1 pin Metal1Pin t t t nil t ) ( Metal2 pin Metal2Pin t t t nil t ) ( Metal3 pin Metal3Pin t t t nil t ) + ( Metal4 pin Metal4Pin t t t nil t ) ( Poly1 pin Poly1Pin t t t nil t ) ( prBoundary drawing prBoundary t t nil t nil ) ( prBoundary boundary prBoundaryBnd t t nil t nil ) @@ -356,6 +364,7 @@ layerDefinitions( ( device annotate deviceAnt t t t t nil ) ( Metal2 net Metal2Net t t t nil nil ) ( Metal3 net Metal3Net t t t nil nil ) + ( Metal4 net Metal4Net t t t nil nil ) ( device label deviceLbl t t t t nil ) ( Via net ViaNet t t t nil nil ) ( Via2 net Via2Net t t t nil nil )