diff --git a/.coveragerc b/.coveragerc index 0df8ae34..df762ed7 100644 --- a/.coveragerc +++ b/.coveragerc @@ -8,6 +8,8 @@ omit = */tests/* # ignore the debug utilities debug.py + # ignore the no DRC/LVS tool options + none.py [paths] source = ../.. @@ -21,6 +23,8 @@ source = exclude_lines = pragma: no cover def __repr__ + def __str__ + class gdsStreamer except Exception raise AssertionError raise NotImplementedError diff --git a/OpenRAM_ICCAD_2016_presentation.pdf b/OpenRAM_ICCAD_2016_presentation.pdf deleted file mode 100644 index 0575e52b..00000000 Binary files a/OpenRAM_ICCAD_2016_presentation.pdf and /dev/null differ diff --git a/README.md b/README.md index d6a5b581..961c6272 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ [![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE) Master: -[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/master/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/OpenRAM/commits/master) -![Coverage](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/master/coverage.svg?private_token=ynB6rSFLzvKUseoBPcwV) +%[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/master/pipeline.svg)](https://github.com/VLSIDA/OpenRAM/commits/master) +![Coverage](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/master/coverage.svg) [![Download](./images/download-stable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/master.zip) Dev: -[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/dev/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/OpenRAM/commits/dev) -![Coverage](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/dev/coverage.svg?private_token=ynB6rSFLzvKUseoBPcwV) +[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/dev/pipeline.svg)](https://github.com/VLSIDA/OpenRAM/commits/dev) +![Coverage](https://scone.soe.ucsc.edu:8888/mrg/OpenRAM/badges/dev/coverage.svg) [![Download](./images/download-unstable-blue.svg)](https://github.com/VLSIDA/OpenRAM/archive/dev.zip) An open-source static random access memory (SRAM) compiler. @@ -24,6 +24,13 @@ other views necessary to use SRAMs in ASIC design. OpenRAM supports integration in both commercial and open-source flows with both predictive and fabricable technologies. +# Documentation + +Please take a look at our presentation We have created a detailed +presentation that serves as our [documentation][documentation]. +This is the most up-to-date information, so please let us know if you see +things that need to be fixed. + # Basic Setup ## Docker Image @@ -67,7 +74,7 @@ You may also wish to add OPENRAM\_HOME to your PYTHONPATH: export PYTHONPATH="$PYTHONPATH:$OPENRAM_HOME" ``` -We include the tech files necessary for [FreePDK45] and [SCMOS] +We include the tech files necessary for [SCMOS] SCN4M_SUBM. The [SCMOS] spice models, however, are generic and should be replaced with foundry models. If you are using [FreePDK45], you should also have that set up and have the environment variable point @@ -196,6 +203,7 @@ Each specific technology (e.g., [FreePDK45]) should be a subdirectory # Further Help + [Additional hints](./HINTS.md) ++ [Documentation][documentation] + [OpenRAM Slack Workspace][Slack] + [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe]) + [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe]) @@ -232,7 +240,7 @@ If I forgot to add you, please let me know! [Github pull request]: https://github.com/VLSIDA/OpenRAM/pulls [Github projects]: https://github.com/VLSIDA/OpenRAM/projects -[email me]: mailto:mrg+openram@ucsc.edu +[documentation]: https://docs.google.com/presentation/d/10InGB33N51I6oBHnqpU7_w9DXlx-qe9zdrlco2Yc5co/edit?usp=sharing [dev-group]: mailto:openram-dev-group@ucsc.edu [user-group]: mailto:openram-user-group@ucsc.edu [dev-group-subscribe]: mailto:openram-dev-group+subscribe@ucsc.edu diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 39cb7b52..be3af1ce 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -1,7 +1,14 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import hierarchy_design import debug import utils -from tech import drc +from tech import drc,layer from vector import vector @@ -9,44 +16,36 @@ class contact(hierarchy_design.hierarchy_design): """ Object for a contact shape with its conductor enclosures. Creates a contact array minimum active or poly enclosure and metal1 enclosure. - This class has enclosure on multiple sides of the contact whereas a via may - have extension on two or four sides. + This class has enclosure on two or four sides of the contact. + The direction specifies whether the first and second layer have asymmetric extension in the H or V direction. + The well/implant_type is an option to add a select/implant layer enclosing the contact. This is necessary to import layouts into Magic which requires the select to be in the same GDS hierarchy as the contact. """ - def __init__(self, layer_stack, dimensions=[1,1], implant_type=None, well_type=None, name=""): + def __init__(self, layer_stack, dimensions=(1,1), directions=("V","V"), implant_type=None, well_type=None, name=""): # This will ignore the name parameter since we can guarantee a unique name here - if implant_type or well_type: - name = "{0}_{1}_{2}_{3}x{4}_{5}{6}".format(layer_stack[0], - layer_stack[1], - layer_stack[2], - dimensions[0], - dimensions[1], - implant_type, - well_type) - - else: - name = "{0}_{1}_{2}_{3}x{4}".format(layer_stack[0], - layer_stack[1], - layer_stack[2], - dimensions[0], - dimensions[1]) - hierarchy_design.hierarchy_design.__init__(self, name) debug.info(4, "create contact object {0}".format(name)) - + self.add_comment("layers: {0}".format(layer_stack)) + self.add_comment("dimensions: {0}".format(dimensions)) + if implant_type or well_type: + self.add_comment("implant type: {0}\nwell_type: {1}".format(implant_type,well_type)) + self.layer_stack = layer_stack self.dimensions = dimensions + self.directions = directions self.offset = vector(0,0) self.implant_type = implant_type self.well_type = well_type - self.pins = [] # used for matching parm lengths + # Module does not have pins, but has empty pin list. + self.pins = [] self.create_layout() def create_layout(self): + self.setup_layers() self.setup_layout_constants() self.create_contact_array() @@ -63,6 +62,8 @@ class contact(hierarchy_design.hierarchy_design): debug.error(-1,"Must define both implant and well type or none at all.") def setup_layers(self): + """ Locally assign the layer names. """ + (first_layer, via_layer, second_layer) = self.layer_stack self.first_layer_name = first_layer self.via_layer_name = via_layer @@ -75,37 +76,58 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_name = second_layer def setup_layout_constants(self): + """ Determine the design rules for the enclosure layers """ + 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 + # The extend rule applies to asymmetric enclosures in one direction. + # The enclosure rule applies to symmetric enclosure component. + 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) - self.first_layer_vertical_enclosure = max(utils.ceil((first_layer_minarea - / (self.contact_array_width + 2*self.first_layer_horizontal_enclosure) - - self.contact_array_height)/2), - (first_layer_minwidth - self.contact_array_height)/2, - first_layer_extend) - self.second_layer_horizontal_enclosure = max((second_layer_minwidth - self.contact_array_width)/2, - second_layer_enclosure) - self.second_layer_vertical_enclosure = max(utils.ceil((second_layer_minarea - / (self.contact_array_width + 2*self.second_layer_horizontal_enclosure) - - self.contact_array_height)/2), - (second_layer_minwidth - self.contact_array_height)/2, - second_layer_extend) + # In some technologies, the minimum width may be larger than the overlap requirement around the via, so + # check this for each dimension. + if self.directions[0] == "V": + self.first_layer_horizontal_enclosure = max(first_layer_enclosure, + (first_layer_minwidth - self.contact_array_width)/2) + self.first_layer_vertical_enclosure = max(first_layer_extend, + (first_layer_minwidth - self.contact_array_height)/2) + elif self.directions[0] == "H": + self.first_layer_horizontal_enclosure = max(first_layer_extend, + (first_layer_minwidth - self.contact_array_width)/2) + self.first_layer_vertical_enclosure = max(first_layer_enclosure, + (first_layer_minwidth - self.contact_array_height)/2) + else: + debug.error("Invalid first layer direction.", -1) + + # In some technologies, the minimum width may be larger than the overlap requirement around the via, so + # check this for each dimension. + if self.directions[1] == "V": + self.second_layer_horizontal_enclosure = max(second_layer_enclosure, + (second_layer_minwidth - self.contact_array_width)/2) + self.second_layer_vertical_enclosure = max(second_layer_extend, + (second_layer_minwidth - self.contact_array_height)/2) + elif self.directions[1] == "H": + self.second_layer_horizontal_enclosure = max(second_layer_extend, + (second_layer_minwidth - self.contact_array_height)/2) + self.second_layer_vertical_enclosure = max(second_layer_enclosure, + (second_layer_minwidth - self.contact_array_width)/2) + else: + debug.error("Invalid second layer direction.", -1) + def create_contact_array(self): """ Create the contact array at the origin""" @@ -169,10 +191,13 @@ class contact(hierarchy_design.hierarchy_design): from sram_factory import factory # This is not instantiated and used for calculations only. # These are static 1x1 contacts to reuse in all the design modules. -well = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1")) -active = factory.create(module_type="contact", layer_stack=("active", "contact", "poly")) -poly = factory.create(module_type="contact", layer_stack=("poly", "contact", "metal1")) -m1m2 = factory.create(module_type="contact", layer_stack=("metal1", "via1", "metal2")) -m2m3 = factory.create(module_type="contact", layer_stack=("metal2", "via2", "metal3")) -m3m4 = factory.create(module_type="contact", layer_stack=("metal3", "via3", "metal4")) +well = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1"), directions=("H","V")) +active = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1"), directions=("H","V")) +poly = factory.create(module_type="contact", layer_stack=("poly", "contact", "metal1"), directions=("V","H")) +m1m2 = factory.create(module_type="contact", layer_stack=("metal1", "via1", "metal2"), directions=("H","V")) +m2m3 = factory.create(module_type="contact", layer_stack=("metal2", "via2", "metal3"), directions=("V","H")) +if "metal4" in layer.keys(): + m3m4 = factory.create(module_type="contact", layer_stack=("metal3", "via3", "metal4"), directions=("H","V")) +else: + m3m4 = None diff --git a/compiler/base/delay_data.py b/compiler/base/delay_data.py new file mode 100644 index 00000000..e3d5a8bc --- /dev/null +++ b/compiler/base/delay_data.py @@ -0,0 +1,43 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +class delay_data(): + """ + This is the delay class to represent the delay information + Time is 50% of the signal to 50% of reference signal delay. + Slew is the 10% of the signal to 90% of signal + """ + def __init__(self, delay=0.0, slew=0.0): + """ init function support two init method""" + # will take single input as a coordinate + self.delay = delay + self.slew = slew + + def __str__(self): + """ override print function output """ + return "Delay Data: Delay "+str(self.delay)+", Slew "+str(self.slew)+"" + + def __add__(self, other): + """ + Override - function (left), for delay_data: a+b != b+a + """ + assert isinstance(other,delay_data) + return delay_data(other.delay + self.delay, + other.slew) + + def __radd__(self, other): + """ + Override - function (right), for delay_data: a+b != b+a + """ + assert isinstance(other,delay_data) + return delay_data(other.delay + self.delay, + self.slew) + + + + diff --git a/compiler/base/design.py b/compiler/base/design.py index 4e76b40a..da23aa9e 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from hierarchy_design import hierarchy_design import contact import globals @@ -19,13 +26,17 @@ class design(hierarchy_design): self.setup_drc_constants() self.setup_multiport_constants() + from tech import layer self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space, self.m2_space) self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space, self.m3_space) - self.m3_pitch = max(contact.m3m4.width,contact.m3m4.height) + max(self.m3_space, self.m4_space) + if "metal4" in layer: + self.m3_pitch = max(contact.m3m4.width,contact.m3m4.height) + max(self.m3_space, self.m4_space) + else: + self.m3_pitch = self.m2_pitch def setup_drc_constants(self): """ These are some DRC constants used in many places in the compiler.""" - from tech import drc + from tech import drc,layer self.well_width = drc("minwidth_well") self.poly_width = drc("minwidth_poly") self.poly_space = drc("poly_to_poly") @@ -35,8 +46,9 @@ class design(hierarchy_design): self.m2_space = drc("metal2_to_metal2") self.m3_width = drc("minwidth_metal3") self.m3_space = drc("metal3_to_metal3") - self.m4_width = drc("minwidth_metal4") - self.m4_space = drc("metal4_to_metal4") + if "metal4" in layer: + self.m4_width = drc("minwidth_metal4") + self.m4_space = drc("metal4_to_metal4") self.active_width = drc("minwidth_active") self.active_space = drc("active_to_body_active") self.contact_width = drc("minwidth_contact") @@ -86,8 +98,8 @@ class design(hierarchy_design): for port in range(OPTS.num_r_ports): self.read_ports.append(port_number) self.readonly_ports.append(port_number) - port_number += 1 - + port_number += 1 + def analytical_power(self, corner, load): """ Get total power of a module """ total_module_power = self.return_power() @@ -95,13 +107,3 @@ class design(hierarchy_design): total_module_power += inst.mod.analytical_power(corner, 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 838828df..7063cf81 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ This provides a set of useful generic types for the gdsMill interface. """ @@ -158,7 +165,7 @@ class instance(geometry): debug.info(4, "creating instance: " + self.name) def get_blockages(self, layer, top=False): - """ Retrieve rectangular blockages of all modules in this instance. + """ Retrieve blockages of all modules in this instance. Apply the transform of the instance placement to give absolute blockages.""" angle = math.radians(float(self.rotate)) mirr = 1 @@ -176,21 +183,20 @@ class instance(geometry): elif self.mirror=="XY": mirr = 1 angle += math.radians(180.0) - - if self.mod.is_library_cell: - # For lib cells, block the whole thing except on metal3 - # since they shouldn't use metal3 - if layer==tech.layer["metal1"] or layer==tech.layer["metal2"]: - return [self.transform_coords(self.mod.get_boundary(), self.offset, mirr, angle)] - else: - return [] - else: - blockages = self.mod.get_blockages(layer) - new_blockages = [] + new_blockages = [] + if self.mod.is_library_cell: + # Writes library cell blockages as shapes instead of a large metal blockage + blockages = [] + blockages = self.mod.gds.getBlockages(layer) for b in blockages: new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) - return new_blockages + else: + blockages = self.mod.get_blockages(layer) + for b in blockages: + new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) + return new_blockages + def gds_write_file(self, new_layout): """Recursively writes all the sub-modules in this instance""" @@ -371,8 +377,8 @@ class rectangle(geometry): def __str__(self): """ override print function output """ - return "rect: @" + str(self.offset) + " " + str(self.width) + "x" + str(self.height) + " layer=" +str(self.layerNumber) + return self.__repr__() def __repr__(self): """ override print function output """ - return "( rect: @" + str(self.offset) + " " + str(self.width) + "x" + str(self.height) + " layer=" + str(self.layerNumber) + " )" + return "( rect: @" + str(self.offset) + " WxH=" + str(self.width) + "x" + str(self.height) + " layer=" + str(self.layerNumber) + " )" diff --git a/compiler/base/graph_util.py b/compiler/base/graph_util.py new file mode 100644 index 00000000..5d1ee692 --- /dev/null +++ b/compiler/base/graph_util.py @@ -0,0 +1,130 @@ +import os, copy +from collections import defaultdict + +import gdsMill +import tech +import math +import globals +import debug +from vector import vector +from pin_layout import pin_layout + +class timing_graph(): + """ + Implements a directed graph + Nodes are currently just Strings. + """ + + def __init__(self): + self.graph = defaultdict(set) + self.all_paths = [] + self.edge_mods = {} + + def add_edge(self, src_node, dest_node, edge_mod): + """Adds edge to graph. Nodes added as well if they do not exist. + Module which defines the edge must be provided for timing information.""" + + src_node = src_node.lower() + dest_node = dest_node.lower() + self.graph[src_node].add(dest_node) + self.edge_mods[(src_node, dest_node)] = edge_mod + + def add_node(self, node): + """Add node to graph with no edges""" + + node = node.lower() + if not node in self.graph: + self.graph[node] = set() + + def remove_edges(self, node): + """Helper function to remove edges, useful for removing vdd/gnd""" + + node = node.lower() + self.graph[node] = set() + + def get_all_paths(self, src_node, dest_node, remove_rail_nodes=True, reduce_paths=True): + """Traverse all paths from source to destination""" + + src_node = src_node.lower() + dest_node = dest_node.lower() + + # Remove vdd and gnd by default + # Will require edits if separate supplies are implemented. + if remove_rail_nodes: + # Names are also assumed. + self.remove_edges('vdd') + self.remove_edges('gnd') + + # Mark all the vertices as not visited + visited = set() + + # Create an array to store paths + path = [] + self.all_paths = [] + + # Call the recursive helper function to print all paths + self.get_all_paths_util(src_node, dest_node, visited, path) + debug.info(2, "Paths found={}".format(len(self.all_paths))) + + if reduce_paths: + self.reduce_paths() + + return self.all_paths + + def reduce_paths(self): + """ Remove any path that is a subset of another path """ + + self.all_paths = [p1 for p1 in self.all_paths if not any(set(p1)<=set(p2) for p2 in self.all_paths if p1 is not p2)] + + def get_all_paths_util(self, cur_node, dest_node, visited, path): + """Recursive function to find all paths in a Depth First Search manner""" + + # Mark the current node as visited and store in path + visited.add(cur_node) + path.append(cur_node) + + # If current vertex is same as destination, then print + # current path[] + if cur_node == dest_node: + self.all_paths.append(copy.deepcopy(path)) + else: + # If current vertex is not destination + # Recur for all the vertices adjacent to this vertex + for node in self.graph[cur_node]: + if node not in visited: + self.get_all_paths_util(node, dest_node, visited, path) + + # Remove current vertex from path[] and mark it as unvisited + path.pop() + visited.remove(cur_node) + + def get_timing(self, path, corner, slew, load): + """Returns the analytical delays in the input path""" + + if len(path) == 0: + return [] + + delays = [] + cur_slew = slew + for i in range(len(path)-1): + + path_edge_mod = self.edge_mods[(path[i], path[i+1])] + + # On the output of the current stage, get COUT from all other mods connected + cout = 0 + for node in self.graph[path[i+1]]: + output_edge_mod = self.edge_mods[(path[i+1], node)] + cout+=output_edge_mod.get_cin() + # If at the last output, include the final output load + if i == len(path)-2: + cout+=load + + delays.append(path_edge_mod.analytical_delay(corner, slew, cout)) + cur_slew = delays[-1].slew + + return delays + + def __str__(self): + """ override print function output """ + + return "Nodes: {}\nEdges:{} ".format(list(self.graph), self.graph) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 1dc052e3..e4c77987 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import hierarchy_layout import hierarchy_spice import globals @@ -5,6 +12,7 @@ import verify import debug import os from globals import OPTS +import graph_util total_drc_errors = 0 total_lvs_errors = 0 @@ -21,37 +29,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp" self.name = name - hierarchy_layout.layout.__init__(self, name) hierarchy_spice.spice.__init__(self, name) - + hierarchy_layout.layout.__init__(self, name) + self.init_graph_params() - # Check if the name already exists, if so, give an error - # because each reference must be a unique name. - # These modules ensure unique names or have no changes if they - # aren't unique - ok_list = ['contact', - 'ptx', - 'pbitcell', - 'replica_pbitcell', - 'sram', - 'hierarchical_predecode2x4', - 'hierarchical_predecode3x8'] - - # Library cells don't change - if self.is_library_cell: - return - # Name is unique so far - elif name not in hierarchy_design.name_map: - hierarchy_design.name_map.append(name) - else: - # Name is in our list of exceptions (they don't change) - for ok_names in ok_list: - if ok_names == self.__class__.__name__: - break - else: - debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1) - - def get_layout_pins(self,inst): """ Return a map of pin locations of the instance offset """ # find the instance @@ -64,12 +45,17 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): return inst_map - def DRC_LVS(self, final_verification=False): + def DRC_LVS(self, final_verification=False, top_level=False): """Checks both DRC and LVS for a module""" + + # Final verification option does not allow nets to be connected by label. # Unit tests will check themselves. + if OPTS.is_unit_test: + return + if not OPTS.check_lvsdrc: + return # Do not run if disabled in options. - - if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): + if (OPTS.inline_lvsdrc or top_level): global total_drc_errors global total_lvs_errors @@ -78,8 +64,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.sp_write(tempspice) self.gds_write(tempgds) - num_drc_errors = verify.run_drc(self.name, tempgds, final_verification) - num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification) + num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) + num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors)) debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors)) total_drc_errors += num_drc_errors @@ -97,7 +83,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): global total_drc_errors tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) self.gds_write(tempgds) - num_errors = verify.run_drc(self.name, tempgds, final_verification) + num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification) total_drc_errors += num_errors debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error)) @@ -114,15 +100,137 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) self.sp_write(tempspice) self.gds_write(tempgds) - num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification) + num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) total_lvs_errors += num_errors debug.check(num_errors == 0,"LVS failed for {0} with {1} error(s)".format(self.name,num_errors)) os.remove(tempspice) - os.remove(tempgds) + os.remove(tempgds) + + def init_graph_params(self): + """Initializes parameters relevant to the graph creation""" + #Only initializes a set for checking instances which should not be added + self.graph_inst_exclude = set() + + def build_graph(self, graph, inst_name, port_nets): + """Recursively create graph from instances in module.""" + + #Translate port names to external nets + if len(port_nets) != len(self.pins): + debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,self.pins),1) + port_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + debug.info(3, "Instance name={}".format(inst_name)) + for subinst, conns in zip(self.insts, self.conns): + if subinst in self.graph_inst_exclude: + continue + subinst_name = inst_name+'.X'+subinst.name + subinst_ports = self.translate_nets(conns, port_dict, inst_name) + subinst.mod.build_graph(graph, subinst_name, subinst_ports) + + def build_names(self, name_dict, inst_name, port_nets): + """Collects all the nets and the parent inst of that net.""" + #Translate port names to external nets + if len(port_nets) != len(self.pins): + debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,self.pins),1) + port_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + debug.info(3, "Instance name={}".format(inst_name)) + for subinst, conns in zip(self.insts, self.conns): + subinst_name = inst_name+'.X'+subinst.name + subinst_ports = self.translate_nets(conns, port_dict, inst_name) + for si_port, conn in zip(subinst_ports, conns): + #Only add for first occurrence + if si_port.lower() not in name_dict: + mod_info = {'mod':self, 'int_net':conn} + name_dict[si_port.lower()] = mod_info + subinst.mod.build_names(name_dict, subinst_name, subinst_ports) + def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None): + """Given a list of nets, will compare the internal alias of a mod to determine + if the nets have a connection to this mod's net (but not inst). + """ + if exclusion_set == None: + exclusion_set = set() + try: + self.name_dict + except AttributeError: + self.name_dict = {} + self.build_names(self.name_dict, inst_name, port_nets) + aliases = [] + for net in path_nets: + net = net.lower() + int_net = self.name_dict[net]['int_net'] + int_mod = self.name_dict[net]['mod'] + if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set): + aliases.append(net) + return aliases + + def is_net_alias(self, known_net, net_alias, mod, exclusion_set): + """Checks if the alias_net in input mod is the same as the input net for this mod (self).""" + if self in exclusion_set: + return False + #Check ports of this mod + for pin in self.pins: + if self.is_net_alias_name_check(known_net, pin, net_alias, mod): + return True + #Check connections of all other subinsts + mod_set = set() + for subinst, inst_conns in zip(self.insts, self.conns): + for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins): + if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod): + return True + elif inst_conn.lower() == known_net.lower() and subinst.mod not in mod_set: + if subinst.mod.is_net_alias(mod_pin, net_alias, mod, exclusion_set): + return True + mod_set.add(subinst.mod) + return False + + def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod): + """Utility function for checking single net alias.""" + return self == mod and \ + child_net.lower() == alias_net.lower() and \ + parent_net.lower() == alias_net.lower() + + def get_mod_net(self, parent_net, child_inst, child_conns): + """Given an instance and net, returns the internal net in the mod + corresponding to input net.""" + for conn, pin in zip(child_conns, child_inst.mod.pins): + if parent_net.lower() == conn.lower(): + return pin + return None + + def translate_nets(self, subinst_ports, port_dict, inst_name): + """Converts connection names to their spice hierarchy equivalent""" + converted_conns = [] + for conn in subinst_ports: + if conn in port_dict: + converted_conns.append(port_dict[conn]) + else: + converted_conns.append("{}.{}".format(inst_name, conn)) + return converted_conns + + def add_graph_edges(self, graph, port_nets): + """For every input, adds an edge to every output. + Only intended to be used for gates and other simple modules.""" + #The final pin names will depend on the spice hierarchy, so + #they are passed as an input. + pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + input_pins = self.get_inputs() + output_pins = self.get_outputs() + inout_pins = self.get_inouts() + for inp in input_pins+inout_pins: + for out in output_pins+inout_pins: + if inp != out: #do not add self loops + graph.add_edge(pin_dict[inp], pin_dict[out], self) + def __str__(self): """ override print function output """ - return "design: " + self.name + pins = ",".join(self.pins) + insts = [" {}".format(x) for x in self.insts] + objs = [" {}".format(x) for x in self.objs] + s = "********** design {0} **********".format(self.name) + s += "\n pins ({0})={1}\n".format(len(self.pins), pins) + s += "\n objs ({0})=\n{1}\n".format(len(self.objs), "\n".join(objs)) + s += "\n insts ({0})=\n{1}\n".format(len(self.insts), "\n".join(insts)) + return s def __repr__(self): """ override print function output """ diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 7c998176..2fea5c61 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1,4 +1,12 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import itertools +import collections import geometry import gdsMill import debug @@ -190,7 +198,8 @@ class layout(): debug.error("Should use a pin iterator since more than one pin {}".format(text),-1) # If we have one pin, return it and not the list. # Otherwise, should use get_pins() - return self.pin_map[text][0] + any_pin = next(iter(self.pin_map[text])) + return any_pin except Exception as e: #print e self.gds_write("missing_pin.gds") @@ -205,19 +214,36 @@ class layout(): if text in self.pin_map.keys(): return self.pin_map[text] else: - return [] - + return set() + + def get_pin_names(self): + """ + Return a pin list of all pins + """ + return self.pin_map.keys() + def copy_layout_pin(self, instance, pin_name, new_name=""): """ Create a copied version of the layout pin at the current level. You can optionally rename the pin to a new name. """ pins=instance.get_pins(pin_name) + + debug.check(len(pins)>0,"Could not find pin {}".format(pin_name)) + for pin in pins: if new_name=="": new_name = pin.name self.add_layout_pin(new_name, pin.layer, pin.ll(), pin.width(), pin.height()) + def copy_layout_pins(self, instance, prefix=""): + """ + Create a copied version of the layout pin at the current level. + You can optionally rename the pin to a new name. + """ + for pin_name in self.pin_map.keys(): + self.copy_layout_pin(instance, pin_name, prefix+pin_name) + def add_layout_pin_segment_center(self, text, layer, start, end): """ Creates a path like pin with center-line convention @@ -260,7 +286,7 @@ class layout(): """ Delete a labeled pin (or all pins of the same name) """ - self.pin_map[text]=[] + self.pin_map[text]=set() def add_layout_pin(self, text, layer, offset, width=None, height=None): """ @@ -277,13 +303,11 @@ class layout(): # Check if there's a duplicate! # and if so, silently ignore it. # Rounding errors may result in some duplicates. - pin_list = self.pin_map[text] - for pin in pin_list: - if pin == new_pin: - return pin - self.pin_map[text].append(new_pin) + if new_pin not in self.pin_map[text]: + self.pin_map[text].add(new_pin) except KeyError: - self.pin_map[text] = [new_pin] + self.pin_map[text] = set() + self.pin_map[text].add(new_pin) return new_pin @@ -356,75 +380,59 @@ class layout(): layer_stack=layers, position_list=coordinates) - def add_contact(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): - """ This is just an alias for a via.""" - return self.add_via(layers=layers, - offset=offset, - size=size, - mirror=mirror, - rotate=rotate, - implant_type=implant_type, - well_type=well_type) + def get_preferred_direction(self, layer): + """ Return the preferred routing directions """ + if layer in ["metal1", "metal3", "metal5"]: + return "H" + elif layer in ["active", "poly", "metal2", "metal4"]: + return "V" + else: + return "N" - def add_contact_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): - """ This is just an alias for a via.""" - return self.add_via_center(layers=layers, - offset=offset, - size=size, - mirror=mirror, - rotate=rotate, - implant_type=implant_type, - well_type=well_type) - - def add_via(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): + + def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None): """ Add a three layer via structure. """ + + if directions==None: + directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2])) + from sram_factory import factory via = factory.create(module_type="contact", layer_stack=layers, dimensions=size, + directions=directions, implant_type=implant_type, well_type=well_type) self.add_mod(via) inst=self.add_inst(name=via.name, mod=via, - offset=offset, - mirror=mirror, - rotate=rotate) + offset=offset) # We don't model the logical connectivity of wires/paths self.connect_inst([]) return inst - def add_via_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): + def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None): """ Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """ + + if directions==None: + directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2])) + from sram_factory import factory via = factory.create(module_type="contact", layer_stack=layers, dimensions=size, + directions=directions, implant_type=implant_type, well_type=well_type) height = via.height width = via.width - debug.check(mirror=="R0","Use rotate to rotate vias instead of mirror.") - if rotate==0: - corrected_offset = offset + vector(-0.5*width,-0.5*height) - elif rotate==90: - corrected_offset = offset + vector(0.5*height,-0.5*width) - elif rotate==180: - corrected_offset = offset + vector(0.5*width,0.5*height) - elif rotate==270: - corrected_offset = offset + vector(-0.5*height,0.5*width) - else: - debug.error("Invalid rotation argument.",-1) - + corrected_offset = offset + vector(-0.5*width,-0.5*height) - #print(rotate,offset,"->",corrected_offset) self.add_mod(via) inst=self.add_inst(name=via.name, mod=via, - offset=corrected_offset, - mirror=mirror, - rotate=rotate) + offset=corrected_offset) # We don't model the logical connectivity of wires/paths self.connect_inst([]) return inst @@ -673,90 +681,99 @@ class layout(): self.add_via_center(layers=layer_stack, offset=bus_pos, rotate=90) - + def get_layer_pitch(self, layer): + """ Return the track pitch on a given layer """ + if layer=="metal1": + return (self.m1_pitch,self.m1_pitch-self.m1_space,self.m1_space) + elif layer=="metal2": + return (self.m2_pitch,self.m2_pitch-self.m2_space,self.m2_space) + elif layer=="metal3": + return (self.m3_pitch,self.m3_pitch-self.m3_space,self.m3_space) + elif layer=="metal4": + from tech import layer as tech_layer + if "metal4" in tech_layer: + return (self.m3_pitch,self.m3_pitch-self.m4_space,self.m4_space) + else: + return (self.m3_pitch,self.m3_pitch-self.m3_space,self.m3_space) + else: + debug.error("Cannot find layer pitch.") + def add_horizontal_trunk_route(self, pins, trunk_offset, - layer_stack=("metal1", "via1", "metal2"), - pitch=None): + layer_stack, + pitch): """ Create a trunk route for all pins with the trunk located at the given y offset. """ - if not pitch: - pitch = self.m1_pitch - max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) - - half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[0])] # if we are less than a pitch, just create a non-preferred layer jog - if max_x-min_x < pitch: + if max_x-min_x <= pitch: + + half_layer_width = 0.5*drc["minwidth_{0}".format(self.vertical_layer)] + # Add the horizontal trunk on the vertical layer! - self.add_path(layer_stack[2],[vector(min_x-half_minwidth,trunk_offset.y), vector(max_x+half_minwidth,trunk_offset.y)]) + self.add_path(self.vertical_layer,[vector(min_x-half_layer_width,trunk_offset.y), vector(max_x+half_layer_width,trunk_offset.y)]) # Route each pin to the trunk for pin in pins: # No bend needed here mid = vector(pin.center().x, trunk_offset.y) - self.add_path(layer_stack[2], [pin.center(), mid]) + self.add_path(self.vertical_layer, [pin.center(), mid]) else: # Add the horizontal trunk - self.add_path(layer_stack[0],[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)]) + self.add_path(self.horizontal_layer,[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)]) trunk_mid = vector(0.5*(max_x+min_x),trunk_offset.y) # Route each pin to the trunk for pin in pins: mid = vector(pin.center().x, trunk_offset.y) - self.add_path(layer_stack[2], [pin.center(), mid]) + self.add_path(self.vertical_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid) def add_vertical_trunk_route(self, pins, trunk_offset, - layer_stack=("metal1", "via1", "metal2"), - pitch=None): + layer_stack, + pitch): """ Create a trunk route for all pins with the trunk located at the given x offset. """ - if not pitch: - pitch = self.m2_pitch - max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) - # Add the vertical trunk - half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])] - # if we are less than a pitch, just create a non-preferred layer jog - if max_y-min_y < pitch: - # Add the horizontal trunk on the vertical layer! - self.add_path(layer_stack[0],[vector(trunk_offset.x,min_y-half_minwidth), vector(trunk_offset.x,max_y+half_minwidth)]) + if max_y-min_y <= pitch: + + half_layer_width = 0.5*drc["minwidth_{0}".format(self.horizontal_layer)] + + # Add the vertical trunk on the horizontal layer! + self.add_path(self.horizontal_layer,[vector(trunk_offset.x,min_y-half_layer_width), vector(trunk_offset.x,max_y+half_layer_width)]) # Route each pin to the trunk for pin in pins: # No bend needed here mid = vector(trunk_offset.x, pin.center().y) - self.add_path(layer_stack[0], [pin.center(), mid]) + self.add_path(self.horizontal_layer, [pin.center(), mid]) else: # Add the vertical trunk - self.add_path(layer_stack[2],[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)]) + self.add_path(self.vertical_layer,[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)]) trunk_mid = vector(trunk_offset.x,0.5*(max_y+min_y),) # Route each pin to the trunk for pin in pins: mid = vector(trunk_offset.x, pin.center().y) - self.add_path(layer_stack[0], [pin.center(), mid]) + self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, - offset=mid, - rotate=90) + offset=mid) def create_channel_route(self, netlist, offset, layer_stack=("metal1", "via1", "metal2"), - pitch=None, vertical=False): """ The net list is a list of the nets. Each net is a list of pins @@ -780,7 +797,7 @@ class layout(): g[other_pin]=conflicts return g - def vcg_nets_overlap(net1, net2, vertical): + def vcg_nets_overlap(net1, net2, vertical, pitch): """ Check all the pin pairs on two nets and return a pin overlap if any pin overlaps @@ -788,12 +805,12 @@ class layout(): for pin1 in net1: for pin2 in net2: - if vcg_pin_overlap(pin1, pin2, vertical): + if vcg_pin_overlap(pin1, pin2, vertical, pitch): return True return False - def vcg_pin_overlap(pin1, pin2, vertical): + def vcg_pin_overlap(pin1, pin2, vertical, pitch): """ Check for vertical or horizontal overlap of the two pins """ # FIXME: If the pins are not in a row, this may break. # However, a top pin shouldn't overlap another top pin, for example, so the @@ -807,10 +824,15 @@ class layout(): overlaps = (not vertical and x_overlap) or (vertical and y_overlap) return overlaps + if self.get_preferred_direction(layer_stack[0])=="V": + self.vertical_layer = layer_stack[0] + self.horizontal_layer = layer_stack[2] + else: + self.vertical_layer = layer_stack[2] + self.horizontal_layer = layer_stack[0] - - if not pitch: - pitch = self.m2_pitch + (self.vertical_pitch,self.vertical_width,self.vertical_space) = self.get_layer_pitch(self.vertical_layer) + (self.horizontal_pitch,self.horizontal_width,self.horizontal_space) = self.get_layer_pitch(self.horizontal_layer) # FIXME: Must extend this to a horizontal conflict graph too if we want to minimize the @@ -818,10 +840,10 @@ class layout(): #hcg = {} # Initialize the vertical conflict graph (vcg) and make a list of all pins - vcg = {} + vcg = collections.OrderedDict() # Create names for the nets for the graphs - nets = {} + nets = collections.OrderedDict() index = 0 #print(netlist) for pin_list in netlist: @@ -840,7 +862,9 @@ class layout(): # Skip yourself if net_name1 == net_name2: continue - if vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical): + if vertical and vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical, self.vertical_pitch): + vcg[net_name2].append(net_name1) + elif not vertical and vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical, self.horizontal_pitch): vcg[net_name2].append(net_name1) # list of routes to do @@ -868,28 +892,33 @@ class layout(): # Add the trunk routes from the bottom up for horizontal or the left to right for vertical if vertical: - self.add_vertical_trunk_route(pin_list, offset, layer_stack, pitch) - offset += vector(pitch,0) + self.add_vertical_trunk_route(pin_list, offset, layer_stack, self.vertical_pitch) + offset += vector(self.vertical_pitch,0) else: - self.add_horizontal_trunk_route(pin_list, offset, layer_stack, pitch) - offset += vector(0,pitch) + self.add_horizontal_trunk_route(pin_list, offset, layer_stack, self.horizontal_pitch) + offset += vector(0,self.horizontal_pitch) def create_vertical_channel_route(self, netlist, offset, - layer_stack=("metal1", "via1", "metal2"), - pitch=None): + layer_stack=("metal1", "via1", "metal2")): """ Wrapper to create a vertical channel route """ - self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=True) + self.create_channel_route(netlist, offset, layer_stack, vertical=True) def create_horizontal_channel_route(self, netlist, offset, - layer_stack=("metal1", "via1", "metal2"), - pitch=None): + layer_stack=("metal1", "via1", "metal2")): """ Wrapper to create a horizontal channel route """ - self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=False) + self.create_channel_route(netlist, offset, layer_stack, vertical=False) + + def add_boundary(self, offset=vector(0,0)): + """ Add boundary for debugging dimensions """ + self.add_rect(layer="boundary", + offset=offset, + height=self.height, + width=self.width) def add_enclosure(self, insts, layer="nwell"): """ Add a layer that surrounds the given instances. Useful @@ -927,20 +956,27 @@ class layout(): - def add_power_pin(self, name, loc, rotate=90, start_layer="metal1"): + def add_power_pin(self, name, loc, vertical=False, start_layer="metal1"): """ Add a single power pin from M3 down to M1 at the given center location. The starting layer is specified to determine which vias are needed. """ - + if vertical: + direction=("V","V") + else: + direction=("H","H") + if start_layer=="metal1": self.add_via_center(layers=("metal1", "via1", "metal2"), offset=loc, - rotate=float(rotate)) + directions=direction) + + if start_layer=="metal1" or start_layer=="metal2": via=self.add_via_center(layers=("metal2", "via2", "metal3"), offset=loc, - rotate=float(rotate)) + directions=direction) + if start_layer=="metal3": self.add_layout_pin_rect_center(text=name, layer="metal3", diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 5f3245a8..707e7bc8 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -1,8 +1,19 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import re import os import math import tech +from delay_data import * +from wire_spice_model import * +from power_data import * +import logical_effort class spice(): """ @@ -17,6 +28,7 @@ class spice(): def __init__(self, name): self.name = name + self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"] # Holds subckts/mods for this module self.mods = [] # Holds the pins for this module @@ -28,7 +40,10 @@ class spice(): # Spice format) self.conns = [] # Keep track of any comments to add the the spice - self.comments = [] + try: + self.commments + except: + self.comments = [] self.sp_read() @@ -38,29 +53,52 @@ class spice(): def add_comment(self, comment): """ Add a comment to the spice file """ + + try: + self.commments + except: + self.comments = [] + self.comments.append(comment) def add_pin(self, name, pin_type="INOUT"): """ Adds a pin to the pins list. Default type is INOUT signal. """ self.pins.append(name) self.pin_type[name]=pin_type + debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(name,pin_type)) - def add_pin_list(self, pin_list, pin_type_list="INOUT"): + def add_pin_list(self, pin_list, pin_type="INOUT"): """ Adds a pin_list to the pins list """ # The type list can be a single type for all pins # or a list that is the same length as the pin list. - if type(pin_type_list)==str: + if type(pin_type)==str: for pin in pin_list: - self.add_pin(pin,pin_type_list) - elif len(pin_type_list)==len(pin_list): - for (pin,ptype) in zip(pin_list, pin_type_list): + debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(pin,pin_type)) + self.add_pin(pin,pin_type) + + elif len(pin_type)==len(pin_list): + for (pin,ptype) in zip(pin_list, pin_type): + debug.check(ptype in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(pin,ptype)) self.add_pin(pin,ptype) else: debug.error("Mismatch in type and pin list lengths.", -1) + def add_pin_types(self, type_list): + """Add pin types for all the cell's pins. + Typically, should only be used for handmade cells.""" + #This only works if self.pins == bitcell.pin_names + if self.pin_names != self.pins: + debug.error("{} spice subcircuit port names do not match pin_names\ + \n SPICE names={}\ + \n Module names={}\ + ".format(self.name, self.pin_names, self.pins),1) + self.pin_type = {pin:type for pin,type in zip(self.pin_names, type_list)} + def get_pin_type(self, name): """ Returns the type of the signal pin. """ - return self.pin_type[name] + pin_type = self.pin_type[name] + debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(name,pin_type)) + return pin_type def get_pin_dir(self, name): """ Returns the direction of the pin. (Supply/ground are INOUT). """ @@ -88,17 +126,31 @@ class spice(): return output_list + def copy_pins(self, other_module, suffix=""): + """ This will copy all of the pins from the other module and add an optional suffix.""" + for pin in other_module.pins: + self.add_pin(pin+suffix, other_module.get_pin_type(pin)) + + def get_inouts(self): + """ These use pin types to determine pin lists. These + may be over-ridden by submodules that didn't use pin directions yet.""" + inout_list = [] + for pin in self.pins: + if self.pin_type[pin]=="INOUT": + inout_list.append(pin) + return inout_list + def add_mod(self, mod): """Adds a subckt/submodule to the subckt hierarchy""" self.mods.append(mod) + def connect_inst(self, args, check=True): """Connects the pins of the last instance added It is preferred to use the function with the check to find if there is a problem. The check option can be set to false where we dynamically generate groups of connections after a group of modules are generated.""" - if (check and (len(self.insts[-1].mod.pins) != len(args))): from pprint import pformat modpins_string=pformat(self.insts[-1].mod.pins) @@ -121,7 +173,13 @@ class spice(): debug.error("-----") debug.error("Connections: \n"+str(conns_string),1) - + def get_conns(self, inst): + """Returns the connections of a given instance.""" + for i in range(len(self.insts)): + if inst is self.insts[i]: + return self.conns[i] + #If not found, returns None + return None def sp_read(self): """Reads the sp file (and parse the pins) from the library @@ -142,6 +200,28 @@ class spice(): else: self.spice = [] + def check_net_in_spice(self, net_name): + """Checks if a net name exists in the current. Intended to be check nets in hand-made cells.""" + #Remove spaces and lower case then add spaces. Nets are separated by spaces. + net_formatted = ' '+net_name.lstrip().rstrip().lower()+' ' + for line in self.spice: + #Lowercase the line and remove any part of the line that is a comment. + line = line.lower().split('*')[0] + + #Skip .subckt or .ENDS lines + if line.find('.') == 0: + continue + if net_formatted in line: + return True + return False + + def do_nets_exist(self, nets): + """For handmade cell, checks sp file contains the storage nodes.""" + nets_match = True + for net in nets: + nets_match = nets_match and self.check_net_in_spice(net) + return nets_match + def contains(self, mod, modlist): for x in modlist: if x.name == mod.name: @@ -169,9 +249,12 @@ class spice(): sp.write("\n.SUBCKT {0} {1}\n".format(self.name, " ".join(self.pins))) + for pin in self.pins: + sp.write("* {1:6}: {0} \n".format(pin,self.pin_type[pin])) + for line in self.comments: sp.write("* {}\n".format(line)) - + # every instance must have a set of connections, even if it is empty. if len(self.insts)!=len(self.conns): debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, @@ -221,27 +304,56 @@ class spice(): def analytical_delay(self, corner, slew, load=0.0): """Inform users undefined delay module while building new modules""" - debug.warning("Design Class {0} delay function needs to be defined" + + # FIXME: Slew is not used in the model right now. Can be added heuristically as linear factor + relative_cap = logical_effort.convert_farad_to_relative_c(load) + stage_effort = self.get_stage_effort(relative_cap) + + # If it fails, then keep running with a valid object. + if stage_effort == None: + return delay_data(0.0, 0.0) + + abs_delay = stage_effort.get_absolute_delay() + corner_delay = self.apply_corners_analytically(abs_delay, corner) + SLEW_APPROXIMATION = 0.1 + corner_slew = SLEW_APPROXIMATION*corner_delay + return delay_data(corner_delay, corner_slew) + + def get_stage_effort(self, cout, inp_is_rise=True): + """Inform users undefined delay module while building new modules""" + debug.warning("Design Class {0} logical effort function needs to be defined" + .format(self.__class__.__name__)) + debug.warning("Class {0} name {1}" + .format(self.__class__.__name__, + self.name)) + return None + + def get_cin(self): + """Returns input load in Femto-Farads. All values generated using + relative capacitance function then converted based on tech file parameter.""" + + # Override this function within a module if a more accurate input capacitance is needed. + # Input/outputs with differing capacitances is not implemented. + relative_cap = self.input_load() + return logical_effort.convert_relative_c_to_farad(relative_cap) + + def input_load(self): + """Inform users undefined relative capacitance functions used for analytical delays.""" + debug.warning("Design Class {0} input capacitance function needs to be defined" .format(self.__class__.__name__)) debug.warning("Class {0} name {1}" .format(self.__class__.__name__, self.name)) - # return 0 to keep code running while building - return delay_data(0.0, 0.0) - + return 0 + def cal_delay_with_rc(self, corner, r, c ,slew, swing = 0.5): """ Calculate the delay of a mosfet by modeling it as a resistance driving a capacitance """ swing_factor = abs(math.log(1-swing)) # time constant based on swing - proc,vdd,temp = corner - #FIXME: type of delay is needed to know which process to use. - proc_mult = max(self.get_process_delay_factor(proc)) - volt_mult = self.get_voltage_delay_factor(vdd) - temp_mult = self.get_temp_delay_factor(temp) delay = swing_factor * r * c #c is in ff and delay is in fs - delay = delay * proc_mult * volt_mult * temp_mult + delay = self.apply_corners_analytically(delay, corner) delay = delay * 0.001 #make the unit to ps # Output slew should be linear to input slew which is described @@ -254,6 +366,15 @@ class spice(): slew = delay * 0.6 * 2 + 0.005 * slew return delay_data(delay = delay, slew = slew) + def apply_corners_analytically(self, delay, corner): + """Multiply delay by corner factors""" + proc,vdd,temp = corner + #FIXME: type of delay is needed to know which process to use. + proc_mult = max(self.get_process_delay_factor(proc)) + volt_mult = self.get_voltage_delay_factor(vdd) + temp_mult = self.get_temp_delay_factor(temp) + return delay * proc_mult * volt_mult * temp_mult + def get_process_delay_factor(self, proc): """Returns delay increase estimate based off process Currently does +/-10 for fast/slow corners.""" @@ -271,7 +392,7 @@ class spice(): """Returns delay increase due to voltage. Implemented as linear factor based off nominal voltage. """ - return tech.spice['vdd_nominal']/voltage + return tech.spice["nom_supply_voltage"]/voltage def get_temp_delay_factor(self, temp): """Returns delay increase due to temperature (in C). @@ -279,11 +400,11 @@ class spice(): """ #Some portions of equation condensed (phi_t = k*T/q for T in Kelvin) in mV #(k/q)/100 = .008625, The division 100 simplifies the conversion from C to K and mV to V - thermal_voltage_nom = .008625*tech.spice["temp_nominal"] - thermal_voltage = .008625*temp - vthresh = (tech.spice["v_threshold_typical"]+2*(thermal_voltage-thermal_voltage_nom)) + thermal_voltage_nom = 0.008625*tech.spice["nom_temperature"] + thermal_voltage = 0.008625*temp + vthresh = (tech.spice["nom_threshold"]+2*(thermal_voltage-thermal_voltage_nom)) #Calculate effect on Vdd-Vth. The current vdd is not used here. A separate vdd factor is calculated. - return (tech.spice['vdd_nominal'] - tech.spice["v_threshold_typical"])/(tech.spice['vdd_nominal']-vthresh) + return (tech.spice["nom_supply_voltage"] - tech.spice["nom_threshold"])/(tech.spice["nom_supply_voltage"]-vthresh) def return_delay(self, delay, slew): return delay_data(delay, slew) @@ -310,102 +431,3 @@ class spice(): def return_power(self, dynamic=0.0, leakage=0.0): return power_data(dynamic, leakage) -class delay_data: - """ - This is the delay class to represent the delay information - Time is 50% of the signal to 50% of reference signal delay. - Slew is the 10% of the signal to 90% of signal - """ - def __init__(self, delay=0.0, slew=0.0): - """ init function support two init method""" - # will take single input as a coordinate - self.delay = delay - self.slew = slew - - def __str__(self): - """ override print function output """ - return "Delay Data: Delay "+str(self.delay)+", Slew "+str(self.slew)+"" - - def __add__(self, other): - """ - Override - function (left), for delay_data: a+b != b+a - """ - assert isinstance(other,delay_data) - return delay_data(other.delay + self.delay, - other.slew) - - def __radd__(self, other): - """ - Override - function (right), for delay_data: a+b != b+a - """ - assert isinstance(other,delay_data) - return delay_data(other.delay + self.delay, - self.slew) - -class power_data: - """ - This is the power class to represent the power information - Dynamic and leakage power are stored as a single object with this class. - """ - def __init__(self, dynamic=0.0, leakage=0.0): - """ init function support two init method""" - # will take single input as a coordinate - self.dynamic = dynamic - self.leakage = leakage - - def __str__(self): - """ override print function output """ - return "Power Data: Dynamic "+str(self.dynamic)+", Leakage "+str(self.leakage)+" in nW" - - def __add__(self, other): - """ - Override - function (left), for power_data: a+b != b+a - """ - assert isinstance(other,power_data) - return power_data(other.dynamic + self.dynamic, - other.leakage + self.leakage) - - def __radd__(self, other): - """ - Override - function (left), for power_data: a+b != b+a - """ - assert isinstance(other,power_data) - return power_data(other.dynamic + self.dynamic, - other.leakage + self.leakage) - - -class wire_spice_model: - """ - This is the spice class to represent a wire - """ - def __init__(self, lump_num, wire_length, wire_width): - self.lump_num = lump_num # the number of segment the wire delay has - self.wire_c = self.cal_wire_c(wire_length, wire_width) # c in each segment - self.wire_r = self.cal_wire_r(wire_length, wire_width) # r in each segment - - def cal_wire_c(self, wire_length, wire_width): - from tech import spice - total_c = spice["wire_unit_c"] * wire_length * wire_width - wire_c = total_c / self.lump_num - return wire_c - - def cal_wire_r(self, wire_length, wire_width): - from tech import spice - total_r = spice["wire_unit_r"] * wire_length / wire_width - wire_r = total_r / self.lump_num - return wire_r - - def return_input_cap(self): - return 0.5 * self.wire_c * self.lump_num - - def return_delay_over_wire(self, slew, swing = 0.5): - # delay will be sum of arithmetic sequence start from - # rc to self.lump_num*rc with step of rc - - swing_factor = abs(math.log(1-swing)) # time constant based on swing - sum_factor = (1+self.lump_num) * self.lump_num * 0.5 # sum of the arithmetic sequence - delay = sum_factor * swing_factor * self.wire_r * self.wire_c - slew = delay * 2 + slew - result= delay_data(delay, slew) - return result - diff --git a/compiler/base/lef.py b/compiler/base/lef.py index 270057cb..af539742 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import gdsMill import tech import globals @@ -5,6 +12,7 @@ import math import debug import datetime from collections import defaultdict +import pdb class lef: """ @@ -14,9 +22,11 @@ class lef: """ def __init__(self,layers): # LEF db units per micron - self.lef_units = 1000 + self.lef_units = 2000 # These are the layers of the obstructions self.lef_layers = layers + # Round to ensure float values are divisible by 0.0025 (the manufacturing grid) + self.round_grid = 4; def lef_write(self, lef_name): """Write the entire lef of the object to the file.""" @@ -41,25 +51,14 @@ class lef: self.lef.write("UNITS\n") self.lef.write(" DATABASE MICRONS {0} ;\n".format(self.lef_units)) self.lef.write("END UNITS\n") - - self.lef.write("SITE MacroSite\n") - self.indent += " " - self.lef.write("{0}CLASS Core ;\n".format(self.indent)) - self.lef.write("{0}SIZE {1} by {2} ;\n".format(self.indent, - self.lef_units*self.width, - self.lef_units*self.height)) - self.indent = self.indent[:-3] - self.lef.write("END MacroSite\n") self.lef.write("{0}MACRO {1}\n".format(self.indent,self.name)) self.indent += " " self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent)) self.lef.write("{0}SIZE {1} BY {2} ;\n" .format(self.indent, - self.lef_units*self.width, - self.lef_units*self.height)) + round(self.width,self.round_grid), + round(self.height,self.round_grid))) self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent)) - self.lef.write("{0}SITE MacroSite ;\n".format(self.indent)) - def lef_write_footer(self): self.lef.write("{0}END {1}\n".format(self.indent,self.name)) @@ -86,7 +85,7 @@ class lef: pin_list = self.get_pins(name) for pin in pin_list: self.lef.write("{0}LAYER {1} ;\n".format(self.indent,pin.layer)) - self.lef_write_rect(pin.rect) + self.lef_write_shape(pin.rect) # End the PORT self.indent = self.indent[:-3] @@ -102,15 +101,29 @@ class lef: for layer in self.lef_layers: self.lef.write("{0}LAYER {1} ;\n".format(self.indent,layer)) self.indent += " " + # pdb.set_trace() blockages = self.get_blockages(layer,True) for b in blockages: - self.lef_write_rect(b) + # if len(b) > 2: + # print(b) + self.lef_write_shape(b) self.indent = self.indent[:-3] self.lef.write("{0}END\n".format(self.indent)) - def lef_write_rect(self, rect): - """ Write a LEF rectangle """ - self.lef.write("{0}RECT ".format(self.indent)) - for item in rect: - self.lef.write(" {0} {1}".format(self.lef_units*item[0], self.lef_units*item[1])) - self.lef.write(" ;\n") + def lef_write_shape(self, rect): + if len(rect) == 2: + """ Write a LEF rectangle """ + self.lef.write("{0}RECT ".format(self.indent)) + for item in rect: + # print(rect) + self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid))) + self.lef.write(" ;\n") + else: + """ Write a LEF polygon """ + self.lef.write("{0}POLYGON ".format(self.indent)) + for item in rect: + self.lef.write(" {0} {1}".format(round(item[0],self.round_grid), round(item[1],self.round_grid))) + # for i in range(0,len(rect)): + # self.lef.write(" {0} {1}".format(round(rect[i][0],self.round_grid), round(rect[i][1],self.round_grid))) + self.lef.write(" ;\n") + diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 44b005f6..2d389119 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from tech import GDS, drc from vector import vector diff --git a/compiler/base/power_data.py b/compiler/base/power_data.py new file mode 100644 index 00000000..77d50d34 --- /dev/null +++ b/compiler/base/power_data.py @@ -0,0 +1,38 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +class power_data(): + """ + This is the power class to represent the power information + Dynamic and leakage power are stored as a single object with this class. + """ + def __init__(self, dynamic=0.0, leakage=0.0): + """ init function support two init method""" + # will take single input as a coordinate + self.dynamic = dynamic + self.leakage = leakage + + def __str__(self): + """ override print function output """ + return "Power Data: Dynamic "+str(self.dynamic)+", Leakage "+str(self.leakage)+" in nW" + + def __add__(self, other): + """ + Override - function (left), for power_data: a+b != b+a + """ + assert isinstance(other,power_data) + return power_data(other.dynamic + self.dynamic, + other.leakage + self.leakage) + + def __radd__(self, other): + """ + Override - function (left), for power_data: a+b != b+a + """ + assert isinstance(other,power_data) + return power_data(other.dynamic + self.dynamic, + other.leakage + self.leakage) diff --git a/compiler/base/route.py b/compiler/base/route.py index c6ce906b..c3e446a3 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from tech import drc import debug from design import design @@ -65,7 +72,7 @@ class route(design): for p0,p1 in plist: if p0.z != p1.z: # via via_size = [self.num_vias]*2 - self.obj.add_via_center(self.layer_stack,vector(p0.x,p0.y),size=via_size,rotate=90) + self.obj.add_via_center(self.layer_stack,vector(p0.x,p0.y),size=via_size) elif p0.x != p1.x and p0.y != p1.y: # diagonal! debug.error("Diagonal route! {}".format(self.path),-3) else: diff --git a/compiler/base/utils.py b/compiler/base/utils.py index 8ca22fa9..61e12096 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import os import gdsMill import tech diff --git a/compiler/base/vector.py b/compiler/base/vector.py index 6069a1ab..8bf09f7d 100644 --- a/compiler/base/vector.py +++ b/compiler/base/vector.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import math import tech diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index a2ddcadf..ff13c4f4 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug class verilog: @@ -14,7 +21,11 @@ class verilog: self.vf.write("// OpenRAM SRAM model\n") self.vf.write("// Words: {0}\n".format(self.num_words)) - self.vf.write("// Word size: {0}\n\n".format(self.word_size)) + self.vf.write("// Word size: {0}\n".format(self.word_size)) + if self.write_size: + self.vf.write("// Write size: {0}\n\n".format(self.write_size)) + else: + self.vf.write("\n") self.vf.write("module {0}(\n".format(self.name)) for port in self.all_ports: @@ -25,16 +36,25 @@ class verilog: elif port in self.write_ports: self.vf.write("// Port {0}: W\n".format(port)) if port in self.readwrite_ports: - self.vf.write(" clk{0},csb{0},web{0},ADDR{0},DIN{0},DOUT{0}".format(port)) + self.vf.write(" clk{0},csb{0},web{0},".format(port)) + if self.write_size: + self.vf.write("wmask{},".format(port)) + self.vf.write("addr{0},din{0},dout{0}".format(port)) elif port in self.write_ports: - self.vf.write(" clk{0},csb{0},ADDR{0},DIN{0}".format(port)) + self.vf.write(" clk{0},csb{0},".format(port)) + if self.write_size: + self.vf.write("wmask{},".format(port)) + self.vf.write("addr{0},din{0}".format(port)) elif port in self.read_ports: - self.vf.write(" clk{0},csb{0},ADDR{0},DOUT{0}".format(port)) + self.vf.write(" clk{0},csb{0},addr{0},dout{0}".format(port)) # Continue for every port on a new line if port != self.all_ports[-1]: self.vf.write(",\n") self.vf.write("\n );\n\n") - + + if self.write_size: + self.num_wmasks = int(self.word_size/self.write_size) + self.vf.write(" parameter NUM_WMASKS = {0} ;\n".format(self.num_wmasks)) self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size)) self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_size)) self.vf.write(" parameter RAM_DEPTH = 1 << ADDR_WIDTH;\n") @@ -78,11 +98,14 @@ class verilog: self.vf.write(" reg csb{0}_reg;\n".format(port)) if port in self.readwrite_ports: self.vf.write(" reg web{0}_reg;\n".format(port)) - self.vf.write(" reg [ADDR_WIDTH-1:0] ADDR{0}_reg;\n".format(port)) if port in self.write_ports: - self.vf.write(" reg [DATA_WIDTH-1:0] DIN{0}_reg;\n".format(port)) + if self.write_size: + self.vf.write(" reg [NUM_WMASKS-1:0] wmask{0}_reg;\n".format(port)) + self.vf.write(" reg [ADDR_WIDTH-1:0] addr{0}_reg;\n".format(port)) + if port in self.write_ports: + self.vf.write(" reg [DATA_WIDTH-1:0] din{0}_reg;\n".format(port)) if port in self.read_ports: - self.vf.write(" reg [DATA_WIDTH-1:0] DOUT{0};\n".format(port)) + self.vf.write(" reg [DATA_WIDTH-1:0] dout{0};\n".format(port)) def add_flops(self, port): """ @@ -95,24 +118,33 @@ class verilog: self.vf.write(" csb{0}_reg = csb{0};\n".format(port)) if port in self.readwrite_ports: self.vf.write(" web{0}_reg = web{0};\n".format(port)) - self.vf.write(" ADDR{0}_reg = ADDR{0};\n".format(port)) if port in self.write_ports: - self.vf.write(" DIN{0}_reg = DIN{0};\n".format(port)) + if self.write_size: + self.vf.write(" wmask{0}_reg = wmask{0};\n".format(port)) + self.vf.write(" addr{0}_reg = addr{0};\n".format(port)) + if port in self.write_ports: + self.vf.write(" din{0}_reg = din{0};\n".format(port)) if port in self.read_ports: - self.vf.write(" DOUT{0} = {1}'bx;\n".format(port,self.word_size)) + self.vf.write(" dout{0} = {1}'bx;\n".format(port,self.word_size)) if port in self.readwrite_ports: self.vf.write(" if ( !csb{0}_reg && web{0}_reg ) \n".format(port)) - self.vf.write(" $display($time,\" Reading %m ADDR{0}=%b DOUT{0}=%b\",ADDR{0}_reg,mem[ADDR{0}_reg]);\n".format(port)) + self.vf.write(" $display($time,\" Reading %m addr{0}=%b dout{0}=%b\",addr{0}_reg,mem[addr{0}_reg]);\n".format(port)) elif port in self.read_ports: self.vf.write(" if ( !csb{0}_reg ) \n".format(port)) - self.vf.write(" $display($time,\" Reading %m ADDR{0}=%b DOUT{0}=%b\",ADDR{0}_reg,mem[ADDR{0}_reg]);\n".format(port)) - + self.vf.write(" $display($time,\" Reading %m addr{0}=%b dout{0}=%b\",addr{0}_reg,mem[addr{0}_reg]);\n".format(port)) if port in self.readwrite_ports: self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port)) - self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b\",ADDR{0}_reg,DIN{0}_reg);\n".format(port)) + if self.write_size: + self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b wmask{0}=%b\",addr{0}_reg,din{0}_reg,wmask{0}_reg);\n".format(port)) + else: + self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b\",addr{0}_reg,din{0}_reg);\n".format(port)) elif port in self.write_ports: self.vf.write(" if ( !csb{0}_reg )\n".format(port)) - self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b\",ADDR{0}_reg,DIN{0}_reg);\n".format(port)) + if self.write_size: + self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b wmask{0}=%b\",addr{0}_reg,din{0}_reg,wmask{0}_reg);\n".format(port)) + else: + self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b\",addr{0}_reg,din{0}_reg);\n".format(port)) + self.vf.write(" end\n\n") @@ -124,11 +156,13 @@ class verilog: self.vf.write(" input csb{0}; // active low chip select\n".format(port)) if port in self.readwrite_ports: self.vf.write(" input web{0}; // active low write control\n".format(port)) - self.vf.write(" input [ADDR_WIDTH-1:0] ADDR{0};\n".format(port)) + if self.write_size: + self.vf.write(" input [NUM_WMASKS-1:0] wmask{0}; // write mask\n".format(port)) + self.vf.write(" input [ADDR_WIDTH-1:0] addr{0};\n".format(port)) if port in self.write_ports: - self.vf.write(" input [DATA_WIDTH-1:0] DIN{0};\n".format(port)) + self.vf.write(" input [DATA_WIDTH-1:0] din{0};\n".format(port)) if port in self.read_ports: - self.vf.write(" output [DATA_WIDTH-1:0] DOUT{0};\n".format(port)) + self.vf.write(" output [DATA_WIDTH-1:0] dout{0};\n".format(port)) def add_write_block(self, port): """ @@ -141,10 +175,25 @@ class verilog: self.vf.write(" always @ (negedge clk{0})\n".format(port)) self.vf.write(" begin : MEM_WRITE{0}\n".format(port)) if port in self.readwrite_ports: - self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port)) + if self.write_size: + self.vf.write(" if ( !csb{0}_reg && !web{0}_reg ) begin\n".format(port)) + else: + self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port)) else: - self.vf.write(" if (!csb{0}_reg)\n".format(port)) - self.vf.write(" mem[ADDR{0}_reg] = DIN{0}_reg;\n".format(port)) + if self.write_size: + self.vf.write(" if (!csb{0}_reg) begin\n".format(port)) + else: + self.vf.write(" if (!csb{0}_reg)\n".format(port)) + + if self.write_size: + for mask in range(0,self.num_wmasks): + lower = mask * self.write_size + upper = lower + self.write_size-1 + self.vf.write(" if (wmask{0}_reg[{1}])\n".format(port,mask)) + self.vf.write(" mem[addr{0}_reg][{1}:{2}] = din{0}_reg[{1}:{2}];\n".format(port,upper,lower)) + self.vf.write(" end\n") + else: + self.vf.write(" mem[addr{0}_reg] = din{0}_reg;\n".format(port)) self.vf.write(" end\n") def add_read_block(self, port): @@ -160,6 +209,6 @@ class verilog: self.vf.write(" if (!csb{0}_reg && web{0}_reg)\n".format(port)) else: self.vf.write(" if (!csb{0}_reg)\n".format(port)) - self.vf.write(" DOUT{0} <= #(DELAY) mem[ADDR{0}_reg];\n".format(port)) + self.vf.write(" dout{0} <= #(DELAY) mem[addr{0}_reg];\n".format(port)) self.vf.write(" end\n") diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 8bc250fa..bf1daa6a 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from tech import drc import debug from wire_path import wire_path @@ -62,18 +69,15 @@ class wire(wire_path): continue if a[1] == c[1]: continue - via_offset = [offset[0] + 0.5*c_height, - offset[1] - 0.5*c_width] - self.obj.add_via(layers=self.layer_stack, - offset=via_offset, - rotate=90) - corner_offset = [offset[0] - 0.5*(c_height + self.vert_layer_width), - offset[1] + 0.5*(c_width - self.horiz_layer_width)] + self.obj.add_via_center(layers=self.layer_stack, + offset=offset) def create_rectangles(self): - """ Create the actual rectangles on teh appropriate layers - using the position list of the corners. """ + """ + Create the actual rectangles on the appropriate layers + using the position list of the corners. + """ pl = self.position_list # position list for index in range(len(pl) - 1): if pl[index][0] != pl[index + 1][0]: diff --git a/compiler/base/wire_path.py b/compiler/base/wire_path.py index ec5d01bf..ebfb8a0a 100644 --- a/compiler/base/wire_path.py +++ b/compiler/base/wire_path.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from tech import drc from tech import layer as techlayer import debug diff --git a/compiler/base/wire_spice_model.py b/compiler/base/wire_spice_model.py new file mode 100644 index 00000000..5624b575 --- /dev/null +++ b/compiler/base/wire_spice_model.py @@ -0,0 +1,42 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +class wire_spice_model(): + """ + This is the spice class to represent a wire + """ + def __init__(self, lump_num, wire_length, wire_width): + self.lump_num = lump_num # the number of segment the wire delay has + self.wire_c = self.cal_wire_c(wire_length, wire_width) # c in each segment + self.wire_r = self.cal_wire_r(wire_length, wire_width) # r in each segment + + def cal_wire_c(self, wire_length, wire_width): + from tech import spice + total_c = spice["wire_unit_c"] * wire_length * wire_width + wire_c = total_c / self.lump_num + return wire_c + + def cal_wire_r(self, wire_length, wire_width): + from tech import spice + total_r = spice["wire_unit_r"] * wire_length / wire_width + wire_r = total_r / self.lump_num + return wire_r + + def return_input_cap(self): + return 0.5 * self.wire_c * self.lump_num + + def return_delay_over_wire(self, slew, swing = 0.5): + # delay will be sum of arithmetic sequence start from + # rc to self.lump_num*rc with step of rc + + swing_factor = abs(math.log(1-swing)) # time constant based on swing + sum_factor = (1+self.lump_num) * self.lump_num * 0.5 # sum of the arithmetic sequence + delay = sum_factor * swing_factor * self.wire_r * self.wire_c + slew = delay * 2 + slew + result= delay_data(delay, slew) + return result diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index ed1647a8..cad069a5 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -1,7 +1,15 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import design import debug import utils from tech import GDS,layer,parameter,drc +import logical_effort class bitcell(design.design): """ @@ -12,6 +20,8 @@ class bitcell(design.design): """ pin_names = ["bl", "br", "wl", "vdd", "gnd"] + storage_nets = ['Q', 'Qbar'] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] (width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) @@ -23,49 +33,50 @@ class bitcell(design.design): self.width = bitcell.width self.height = bitcell.height self.pin_map = bitcell.pin_map + self.add_pin_types(self.type_list) + self.nets_match = self.do_nets_exist(self.storage_nets) - def analytical_delay(self, corner, 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(corner, r = r, c = c_para+load, slew = slew, swing = swing) - return result - + def get_stage_effort(self, load): + parasitic_delay = 1 + size = 0.5 #This accounts for bitline being drained thought the access TX and internal node + cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. + return logical_effort.logical_effort('bitline', size, cin, load, parasitic_delay, False) - 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 = ["bl_{0}".format(col), - "br_{0}".format(col), - "wl_{0}".format(row), - "vdd", - "gnd"] - return bitcell_pins - - def list_all_wl_names(self): + def get_all_wl_names(self): """ Creates a list of all wordline pin names """ row_pins = ["wl"] return row_pins - def list_all_bitline_names(self): + def get_all_bitline_names(self): """ Creates a list of all bitline pin names (both bl and br) """ column_pins = ["bl", "br"] return column_pins - def list_all_bl_names(self): + def get_all_bl_names(self): """ Creates a list of all bl pins names """ column_pins = ["bl"] return column_pins - def list_all_br_names(self): + def get_all_br_names(self): """ Creates a list of all br pins names """ column_pins = ["br"] return column_pins + def get_bl_name(self, port=0): + """Get bl name""" + debug.check(port==0,"One port for bitcell only.") + return "bl" + + def get_br_name(self, port=0): + """Get bl name""" + debug.check(port==0,"One port for bitcell only.") + return "br" + + def get_wl_name(self, port=0): + """Get wl name""" + debug.check(port==0,"One port for bitcell only.") + return "wl" + def analytical_power(self, corner, load): """Bitcell power in nW. Only characterizes leakage.""" from tech import spice @@ -73,10 +84,23 @@ class bitcell(design.design): dynamic = 0 #temporary total_power = self.return_power(dynamic, leakage) return total_power - - def get_wl_cin(self): + + def get_storage_net_names(self): + """Returns names of storage nodes in bitcell in [non-inverting, inverting] format.""" + #Checks that they do exist + if self.nets_match: + return self.storage_nets + else: + debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets)) + return None + + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + + # FIXME: This applies to bitline capacitances as well. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges based on inputs/outputs. Overrides base class function.""" + self.add_graph_edges(graph, port_nets) diff --git a/compiler/bitcells/bitcell_1rw_1r.py b/compiler/bitcells/bitcell_1rw_1r.py index a96dcff0..0d536b38 100644 --- a/compiler/bitcells/bitcell_1rw_1r.py +++ b/compiler/bitcells/bitcell_1rw_1r.py @@ -1,7 +1,15 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import design import debug import utils from tech import GDS,layer,parameter,drc +import logical_effort class bitcell_1rw_1r(design.design): """ @@ -12,6 +20,8 @@ class bitcell_1rw_1r(design.design): """ pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] + storage_nets = ['Q', 'Q_bar'] (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"]) @@ -23,21 +33,17 @@ class bitcell_1rw_1r(design.design): self.width = bitcell_1rw_1r.width self.height = bitcell_1rw_1r.height self.pin_map = bitcell_1rw_1r.pin_map - - def analytical_delay(self, corner, 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(corner, r = r, c = c_para+load, slew = slew, swing = swing) - return result + self.add_pin_types(self.type_list) + self.nets_match = self.do_nets_exist(self.storage_nets) + + def get_stage_effort(self, load): + parasitic_delay = 1 + size = 0.5 #This accounts for bitline being drained thought the access TX and internal node + cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. + read_port_load = 0.5 #min size NMOS gate load + return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) - - def list_bitcell_pins(self, col, row): + def get_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), @@ -49,46 +55,61 @@ class bitcell_1rw_1r(design.design): "gnd"] return bitcell_pins - def list_all_wl_names(self): + def get_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): + def get_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): + def get_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): + def get_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): + def get_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): + def get_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): + def get_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): + def get_write_br_names(self): """ Creates a list of br pin names asscociated with write ports""" column_pins = ["br0"] return column_pins + def get_bl_name(self, port=0): + """Get bl name by port""" + debug.check(port<2,"Two ports for bitcell_1rw_1r only.") + return "bl{}".format(port) + + def get_br_name(self, port=0): + """Get bl name by port""" + debug.check(port<2,"Two ports for bitcell_1rw_1r only.") + return "br{}".format(port) + + def get_wl_name(self, port=0): + """Get wl name by port""" + debug.check(port<2,"Two ports for bitcell_1rw_1r only.") + return "wl{}".format(port) + def analytical_power(self, corner, load): """Bitcell power in nW. Only characterizes leakage.""" from tech import spice @@ -97,10 +118,31 @@ class bitcell_1rw_1r(design.design): total_power = self.return_power(dynamic, leakage) return total_power - def get_wl_cin(self): + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. - #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + + # FIXME: This applies to bitline capacitances as well. + # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin + + def get_storage_net_names(self): + """Returns names of storage nodes in bitcell in [non-inverting, inverting] format.""" + #Checks that they do exist + if self.nets_match: + return self.storage_nets + else: + debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets)) + return None + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges to graph. Multiport bitcell timing graph is too complex + to use the add_graph_edges function.""" + pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + #Edges hardcoded here. Essentially wl->bl/br for both ports. + # Port 0 edges + graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self) + graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self) + # Port 1 edges + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) diff --git a/compiler/bitcells/bitcell_1w_1r.py b/compiler/bitcells/bitcell_1w_1r.py index e2cc662b..7e8c9e75 100644 --- a/compiler/bitcells/bitcell_1w_1r.py +++ b/compiler/bitcells/bitcell_1w_1r.py @@ -1,7 +1,15 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import design import debug import utils from tech import GDS,layer,parameter,drc +import logical_effort class bitcell_1w_1r(design.design): """ @@ -12,6 +20,8 @@ class bitcell_1w_1r(design.design): """ pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] + storage_nets = ['Q', 'Q_bar'] (width,height) = utils.get_libcell_size("cell_1w_1r", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"]) @@ -23,21 +33,17 @@ class bitcell_1w_1r(design.design): self.width = bitcell_1w_1r.width self.height = bitcell_1w_1r.height self.pin_map = bitcell_1w_1r.pin_map + self.add_pin_types(self.type_list) + self.nets_match = self.do_nets_exist(self.storage_nets) - def analytical_delay(self, corner, 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(corner, r = r, c = c_para+load, slew = slew, swing = swing) - return result + def get_stage_effort(self, load): + parasitic_delay = 1 + size = 0.5 #This accounts for bitline being drained thought the access TX and internal node + cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. + read_port_load = 0.5 #min size NMOS gate load + return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) - - def list_bitcell_pins(self, col, row): + def get_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), @@ -49,46 +55,59 @@ class bitcell_1w_1r(design.design): "gnd"] return bitcell_pins - def list_all_wl_names(self): + def get_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): + def get_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): + def get_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): + def get_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): + def get_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): + def get_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): + def get_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): + def get_write_br_names(self): """ Creates a list of br pin names asscociated with write ports""" column_pins = ["br0"] return column_pins + def get_bl_name(self, port=0): + """Get bl name by port""" + return "bl{}".format(port) + + def get_br_name(self, port=0): + """Get bl name by port""" + return "br{}".format(port) + + def get_wl_name(self, port=0): + """Get wl name by port""" + debug.check(port<2,"Two ports for bitcell_1rw_1r only.") + return "wl{}".format(port) + def analytical_power(self, corner, load): """Bitcell power in nW. Only characterizes leakage.""" from tech import spice @@ -97,10 +116,29 @@ class bitcell_1w_1r(design.design): total_power = self.return_power(dynamic, leakage) return total_power - def get_wl_cin(self): + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. - #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + + # FIXME: This applies to bitline capacitances as well. + # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin + + def get_storage_net_names(self): + """Returns names of storage nodes in bitcell in [non-inverting, inverting] format.""" + #Checks that they do exist + if self.nets_match: + return self.storage_nets + else: + debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets)) + return None + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges to graph. Multiport bitcell timing graph is too complex + to use the add_graph_edges function.""" + pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + #Edges hardcoded here. Essentially wl->bl/br for both ports. + # Port 0 edges + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) + # Port 1 is a write port, so its timing is not considered here. diff --git a/compiler/bitcells/dummy_bitcell.py b/compiler/bitcells/dummy_bitcell.py new file mode 100644 index 00000000..db748203 --- /dev/null +++ b/compiler/bitcells/dummy_bitcell.py @@ -0,0 +1,48 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +import debug +import utils +from tech import GDS,layer,parameter,drc +import logical_effort + +class dummy_bitcell(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 = ["bl", "br", "wl", "vdd", "gnd"] + (width,height) = utils.get_libcell_size("dummy_cell_6t", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_6t", GDS["unit"]) + + def __init__(self, name=""): + # Ignore the name argument + design.design.__init__(self, "dummy_cell_6t") + debug.info(2, "Create dummy bitcell") + + self.width = dummy_bitcell.width + self.height = dummy_bitcell.height + self.pin_map = dummy_bitcell.pin_map + + def analytical_power(self, corner, 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 + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] + return 2*access_tx_cin diff --git a/compiler/bitcells/dummy_bitcell_1rw_1r.py b/compiler/bitcells/dummy_bitcell_1rw_1r.py new file mode 100644 index 00000000..f8986f2d --- /dev/null +++ b/compiler/bitcells/dummy_bitcell_1rw_1r.py @@ -0,0 +1,45 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +import debug +import utils +from tech import GDS,layer,drc,parameter + +class dummy_bitcell_1rw_1r(design.design): + """ + A single bit cell which is forced to store a 0. + This module implements the single memory cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. """ + + pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] + (width,height) = utils.get_libcell_size("dummy_cell_1rw_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1rw_1r", GDS["unit"]) + + def __init__(self, name=""): + # Ignore the name argument + design.design.__init__(self, "dummy_cell_1rw_1r") + debug.info(2, "Create dummy bitcell 1rw+1r object") + + self.width = dummy_bitcell_1rw_1r.width + self.height = dummy_bitcell_1rw_1r.height + self.pin_map = dummy_bitcell_1rw_1r.pin_map + self.add_pin_types(self.type_list) + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] + return 2*access_tx_cin + + def build_graph(self, graph, inst_name, port_nets): + """Dummy bitcells are cannot form a path and be part of the timing graph""" + return diff --git a/compiler/bitcells/dummy_bitcell_1w_1r.py b/compiler/bitcells/dummy_bitcell_1w_1r.py new file mode 100644 index 00000000..ef451b8c --- /dev/null +++ b/compiler/bitcells/dummy_bitcell_1w_1r.py @@ -0,0 +1,45 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +import debug +import utils +from tech import GDS,layer,drc,parameter + +class dummy_bitcell_1w_1r(design.design): + """ + A single bit cell which is forced to store a 0. + This module implements the single memory cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. """ + + pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] + (width,height) = utils.get_libcell_size("dummy_cell_1w_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1w_1r", GDS["unit"]) + + def __init__(self, name=""): + # Ignore the name argument + design.design.__init__(self, "dummy_cell_1w_1r") + debug.info(2, "Create dummy bitcell 1w+1r object") + + self.width = dummy_bitcell_1w_1r.width + self.height = dummy_bitcell_1w_1r.height + self.pin_map = dummy_bitcell_1w_1r.pin_map + self.add_pin_types(self.type_list) + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This is a handmade cell so the value must be entered in the tech.py file or estimated. + #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] + return 2*access_tx_cin + + def build_graph(self, graph, inst_name, port_nets): + """Dummy bitcells are cannot form a path and be part of the timing graph""" + return diff --git a/compiler/bitcells/dummy_pbitcell.py b/compiler/bitcells/dummy_pbitcell.py new file mode 100644 index 00000000..ee15e03c --- /dev/null +++ b/compiler/bitcells/dummy_pbitcell.py @@ -0,0 +1,91 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +import design +from tech import drc, spice,parameter +from vector import vector +from globals import OPTS +from sram_factory import factory + +class dummy_pbitcell(design.design): + """ + Creates a replica bitcell using pbitcell + """ + + def __init__(self, name): + 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 + + design.design.__init__(self, name) + debug.info(1, "create a dummy bitcell using pbitcell 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.create_netlist() + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_modules() + + def create_layout(self): + self.place_pbitcell() + self.route_rbc_connections() + self.DRC_LVS() + + def add_pins(self): + for port in range(self.total_ports): + self.add_pin("bl{}".format(port)) + self.add_pin("br{}".format(port)) + + for port in range(self.total_ports): + self.add_pin("wl{}".format(port)) + + self.add_pin("vdd") + self.add_pin("gnd") + + def add_modules(self): + self.prbc = factory.create(module_type="pbitcell",dummy_bitcell=True) + self.add_mod(self.prbc) + + self.height = self.prbc.height + self.width = self.prbc.width + + def create_modules(self): + self.prbc_inst = self.add_inst(name="pbitcell", + mod=self.prbc) + + temp = [] + for port in range(self.total_ports): + temp.append("bl{}".format(port)) + temp.append("br{}".format(port)) + for port in range(self.total_ports): + temp.append("wl{}".format(port)) + temp.append("vdd") + temp.append("gnd") + self.connect_inst(temp) + + def place_pbitcell(self): + self.prbc_inst.place(offset=vector(0,0)) + + def route_rbc_connections(self): + for port in range(self.total_ports): + self.copy_layout_pin(self.prbc_inst, "bl{}".format(port)) + self.copy_layout_pin(self.prbc_inst, "br{}".format(port)) + for port in range(self.total_ports): + self.copy_layout_pin(self.prbc_inst, "wl{}".format(port)) + self.copy_layout_pin(self.prbc_inst, "vdd") + self.copy_layout_pin(self.prbc_inst, "gnd") + + def get_wl_cin(self): + """Return the relative capacitance of the access transistor gates""" + #This module is made using a pbitcell. Get the cin from that module + return self.prbc.get_wl_cin() diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 55712e44..919a0316 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import contact import design import debug @@ -5,6 +12,7 @@ from tech import drc, parameter, spice from vector import vector from ptx import ptx from globals import OPTS +import logical_effort class pbitcell(design.design): """ @@ -12,22 +20,32 @@ class pbitcell(design.design): with a variable number of read/write, write, and read ports """ - def __init__(self, name, replica_bitcell=False): + def __init__(self, name, replica_bitcell=False, dummy_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 + self.dummy_bitcell = dummy_bitcell 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)) + info_string = "{0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, + self.num_w_ports, + self.num_r_ports) + debug.info(2, "create a multi-port bitcell with {}".format(info_string)) + self.add_comment(info_string) + + if self.dummy_bitcell: + self.add_comment("dummy bitcell") + if self.replica_bitcell: + self.add_comment("replica bitcell") 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() + self.add_boundary() def create_netlist(self): self.add_pins() @@ -71,9 +89,7 @@ class pbitcell(design.design): # 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.translate_all(vector(self.leftmost_xpos, self.botmost_ypos)) def add_pins(self): @@ -90,50 +106,53 @@ class pbitcell(design.design): port = 0 for k in range(self.num_rw_ports): - self.add_pin("bl{}".format(port)) - self.add_pin("br{}".format(port)) + self.add_pin("bl{}".format(port), "OUTPUT") + self.add_pin("br{}".format(port), "OUTPUT") self.rw_bl_names.append("bl{}".format(port)) self.rw_br_names.append("br{}".format(port)) port += 1 for k in range(self.num_w_ports): - self.add_pin("bl{}".format(port)) - self.add_pin("br{}".format(port)) + self.add_pin("bl{}".format(port), "INPUT") + self.add_pin("br{}".format(port), "INPUT") self.w_bl_names.append("bl{}".format(port)) self.w_br_names.append("br{}".format(port)) port += 1 for k in range(self.num_r_ports): - self.add_pin("bl{}".format(port)) - self.add_pin("br{}".format(port)) + self.add_pin("bl{}".format(port), "OUTPUT") + self.add_pin("br{}".format(port), "OUTPUT") 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)) + self.add_pin("wl{}".format(port), "INPUT") self.rw_wl_names.append("wl{}".format(port)) port += 1 for k in range(self.num_w_ports): - self.add_pin("wl{}".format(port)) + self.add_pin("wl{}".format(port), "INPUT") self.w_wl_names.append("wl{}".format(port)) port += 1 for k in range(self.num_r_ports): - self.add_pin("wl{}".format(port)) + self.add_pin("wl{}".format(port), "INPUT") self.r_wl_names.append("wl{}".format(port)) port += 1 - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") # 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" - + self.Q = "Q" + self.storage_nets = [self.Q, self.Q_bar] + def add_modules(self): """ 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 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"] inverter_pmos_width = parameter["6T_inv_pmos_size"] @@ -141,7 +160,8 @@ class pbitcell(design.design): 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 + # 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"] inverter_pmos_width = parameter["6T_inv_pmos_size"] @@ -175,14 +195,21 @@ class pbitcell(design.design): 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) + 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) + 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 @@ -191,13 +218,15 @@ class pbitcell(design.design): self.inverter_nmos_ypos = self.port_ypos # spacing between ports (same for read/write and write ports) - self.bitline_offset = -0.5*self.readwrite_nmos.active_width + 0.5*contact.m1m2.height + self.m2_space + self.m2_width - m2_constraint = self.bitline_offset + self.m2_space + 0.5*contact.m1m2.height - 0.5*self.readwrite_nmos.active_width + self.bitline_offset = -0.5*self.readwrite_nmos.active_width + 0.5*contact.m1m2.height \ + + self.m2_space + self.m2_width + m2_constraint = self.bitline_offset + self.m2_space + 0.5*contact.m1m2.height \ + - 0.5*self.readwrite_nmos.active_width self.write_port_spacing = max(self.active_space, self.m1_space, m2_constraint) self.read_port_spacing = self.bitline_offset + self.m2_space # spacing between cross coupled inverters - self.inverter_to_inverter_spacing = contact.poly.height + self.m1_space + self.inverter_to_inverter_spacing = contact.poly.width + 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) @@ -214,11 +243,11 @@ class pbitcell(design.design): + 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 + self.m1_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) + 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 @@ -228,8 +257,10 @@ class pbitcell(design.design): def calculate_postions(self): """ 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.botmost_ypos = self.m1_offset - self.total_ports*self.m1_pitch + 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.write_port_spacing) \ @@ -239,9 +270,9 @@ class pbitcell(design.design): self.width = -2*self.leftmost_xpos self.height = 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. @@ -250,20 +281,20 @@ class pbitcell(design.design): # 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.connect_inst([self.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"]) + self.connect_inst(["gnd", self.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.connect_inst([self.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"]) + self.connect_inst(["vdd", self.Q, self.Q_bar, "vdd"]) def place_storage(self): """ Places the transistors for the crossed coupled inverters in the bitcell """ @@ -291,19 +322,26 @@ class pbitcell(design.design): 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) + self.add_path("metal1", + [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], + width=contact.active.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.active.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_left = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.height, + self.cross_couple_upper_ypos) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=contact_offset_left, + directions=("H","H")) - 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) + + 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_via_center(layers=("poly", "contact", "metal1"), + offset=contact_offset_right, + directions=("H","H")) # 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) @@ -315,21 +353,21 @@ class pbitcell(design.design): def route_rails(self): """ Adds gnd and vdd rails and connects them to the inverters """ # Add rails for vdd and gnd - gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing + gnd_ypos = self.m1_offset - self.total_ports*self.m1_pitch self.gnd_position = vector(0, gnd_ypos) self.add_rect_center(layer="metal1", offset=self.gnd_position, - width=self.width, - height=self.m1_width) + width=self.width) self.add_power_pin("gnd", vector(0,gnd_ypos)) - vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset + 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.add_rect_center(layer="metal1", offset=self.vdd_position, - width=self.width, - height=self.m1_width) + width=self.width) self.add_power_pin("vdd", vector(0,vdd_ypos)) def create_readwrite_ports(self): @@ -347,14 +385,20 @@ class pbitcell(design.design): # iterate over the number of read/write ports for k in range(0,self.num_rw_ports): + bl_name = self.rw_bl_names[k] + br_name = self.rw_br_names[k] + if self.dummy_bitcell: + bl_name += "_noconn" + br_name += "_noconn" + # 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.connect_inst([bl_name, self.rw_wl_names[k], self.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"]) + self.connect_inst([self.Q_bar, self.rw_wl_names[k], br_name, "gnd"]) def place_readwrite_ports(self): """ Places read/write ports in the bit cell """ @@ -380,13 +424,12 @@ class pbitcell(design.design): self.readwrite_nmos_right[k].place(offset=[right_readwrite_transistor_xpos, self.port_ypos]) # add pin for RWWL - rwwl_ypos = self.rowline_offset - k*self.rowline_spacing + rwwl_ypos = self.m1_offset - k*self.m1_pitch 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) + width=self.width) # add pins for RWBL and RWBR rwbl_xpos = left_readwrite_transistor_xpos - self.bitline_offset + 0.5*self.m2_width @@ -394,15 +437,14 @@ class pbitcell(design.design): self.add_layout_pin_rect_center(text=self.rw_bl_names[k], layer="metal2", offset=self.rwbl_positions[k], - width=drc["minwidth_metal2"], height=self.height) - rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width + self.bitline_offset - 0.5*self.m2_width + rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width \ + + self.bitline_offset - 0.5*self.m2_width 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 @@ -423,14 +465,20 @@ class pbitcell(design.design): # iterate over the number of write ports for k in range(0,self.num_w_ports): + bl_name = self.w_bl_names[k] + br_name = self.w_br_names[k] + if self.dummy_bitcell: + bl_name += "_noconn" + br_name += "_noconn" + # 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.connect_inst([bl_name, self.w_wl_names[k], self.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"]) + self.connect_inst([self.Q_bar, self.w_wl_names[k], br_name, "gnd"]) def place_write_ports(self): """ Places write ports in the bit cell """ @@ -457,13 +505,13 @@ class pbitcell(design.design): self.write_nmos_right[k].place(offset=[right_write_transistor_xpos, self.port_ypos]) # add pin for WWL - wwl_ypos = rwwl_ypos = self.rowline_offset - self.num_rw_ports*self.rowline_spacing - k*self.rowline_spacing + wwl_ypos = rwwl_ypos = self.m1_offset - self.num_rw_ports*self.m1_pitch \ + - k*self.m1_pitch 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) + width=self.width) # add pins for WBL and WBR wbl_xpos = left_write_transistor_xpos - self.bitline_offset + 0.5*self.m2_width @@ -471,15 +519,14 @@ class pbitcell(design.design): self.add_layout_pin_rect_center(text=self.w_bl_names[k], layer="metal2", offset=self.wbl_positions[k], - width=drc["minwidth_metal2"], height=self.height) - wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset - 0.5*self.m2_width + wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset \ + - 0.5*self.m2_width 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 @@ -506,6 +553,12 @@ class pbitcell(design.design): # iterate over the number of read ports for k in range(0,self.num_r_ports): + bl_name = self.r_bl_names[k] + br_name = self.r_br_names[k] + if self.dummy_bitcell: + bl_name += "_noconn" + br_name += "_noconn" + # add read-access transistors self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), mod=self.read_nmos) @@ -513,16 +566,16 @@ class pbitcell(design.design): 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"]) + self.connect_inst(["gnd", self.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.connect_inst([bl_name, 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"]) + self.connect_inst(["RA_to_R_right{}".format(k), self.r_wl_names[k], br_name, "gnd"]) def place_read_ports(self): """ Places the read ports in the bit cell """ @@ -556,13 +609,13 @@ class pbitcell(design.design): self.read_nmos_right[k].place(offset=[right_read_transistor_xpos+overlap_offset, self.port_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 + rwl_ypos = rwwl_ypos = self.m1_offset - self.num_rw_ports*self.m1_pitch \ + - self.num_w_ports*self.m1_pitch - k*self.m1_pitch 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) + width=self.width) # add pins for RBL and RBR rbl_xpos = left_read_transistor_xpos - self.bitline_offset + 0.5*self.m2_width @@ -570,15 +623,14 @@ class pbitcell(design.design): self.add_layout_pin_rect_center(text=self.r_bl_names[k], layer="metal2", offset=self.rbl_positions[k], - width=drc["minwidth_metal2"], height=self.height) - rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset - 0.5*self.m2_width + rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset \ + - 0.5*self.m2_width 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): @@ -612,21 +664,21 @@ class pbitcell(design.design): # 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_via_center(layers=("poly", "contact", "metal1"), + 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) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=port_contact_offset) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=port_contact_offset) + self.add_via_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_via_center(layers=("metal1", "via1", "metal2"), + offset=wl_contact_offset, + directions=("H","H")) self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("metal2", [port_contact_offset, wl_contact_offset]) @@ -661,7 +713,9 @@ class pbitcell(design.design): 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"), + # Leave bitline disconnected if a dummy cell + if not self.dummy_bitcell: + self.add_via_center(layers=("metal1", "via1", "metal2"), offset=port_contact_offest) self.add_path("metal2", [port_contact_offest, bl_offset], width=contact.m1m2.height) @@ -670,7 +724,9 @@ class pbitcell(design.design): 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"), + # Leave bitline disconnected if a dummy cell + if not self.dummy_bitcell: + self.add_via_center(layers=("metal1", "via1", "metal2"), offset=port_contact_offest) self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height) @@ -686,17 +742,17 @@ class pbitcell(design.design): 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) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=position) if position.x > 0: contact_correct = 0.5*contact.m1m2.height else: contact_correct = -0.5*contact.m1m2.height supply_offset = vector(position.x + contact_correct, self.gnd_position.y) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=supply_offset, - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=supply_offset, + directions=("H","H")) self.add_path("metal2", [position, supply_offset]) @@ -712,39 +768,37 @@ class pbitcell(design.design): 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]) + self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), 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]) + self.add_path("metal1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos]) def route_write_access(self): """ 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]) + self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), 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]) + self.add_path("metal1", [self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos]) def route_read_access(self): """ 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) + left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - self.poly_to_polycontact - 0.5*contact.poly.width, + self.cross_couple_upper_ypos) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=left_storage_contact, + directions=("H","H")) - 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) + right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + self.poly_to_polycontact + 0.5*contact.poly.width, + self.cross_couple_upper_ypos) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=right_storage_contact, + directions=("H","H")) 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]) @@ -757,25 +811,23 @@ class pbitcell(design.design): 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_via_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]) + self.add_path("metal1", [port_contact_offset, 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_via_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]) + self.add_path("metal1", [port_contact_offset, mid, right_storage_contact]) def extend_well(self): """ @@ -794,13 +846,13 @@ class pbitcell(design.design): # 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"] + inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) - self.well_enclose_active + inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - self.well_enclose_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"] + well_width = 2*(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) + 2*self.well_enclose_active + well_height = self.vdd_position.y - inverter_well_ypos + self.well_enclose_active + drc["minwidth_tx"] offset = [inverter_well_xpos,inverter_well_ypos] self.add_rect(layer="nwell", @@ -811,21 +863,21 @@ class pbitcell(design.design): # add well contacts # connect pimplants to gnd offset = vector(0, self.gnd_position.y) - self.add_contact_center(layers=("active", "contact", "metal1"), - offset=offset, - rotate=90, - implant_type="p", - well_type="p") + self.add_via_center(layers=("active", "contact", "metal1"), + offset=offset, + directions=("H","H"), + 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") + self.add_via_center(layers=("active", "contact", "metal1"), + offset=offset, + directions=("H","H"), + implant_type="n", + well_type="n") - def list_bitcell_pins(self, col, row): + def get_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 = [] for port in range(self.total_ports): @@ -837,12 +889,12 @@ class pbitcell(design.design): bitcell_pins.append("gnd") return bitcell_pins - def list_all_wl_names(self): + def get_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): + def get_all_bitline_names(self): """ Creates a list of all bitline pin names (both bl and br) """ bitline_pins = [] for port in range(self.total_ports): @@ -850,29 +902,49 @@ class pbitcell(design.design): bitline_pins.append("br{0}".format(port)) return bitline_pins - def list_all_bl_names(self): + def get_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 - return bl_pins + return self.rw_bl_names + self.w_bl_names + self.r_bl_names - def list_all_br_names(self): + def get_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 - return br_pins + return self.rw_br_names + self.w_br_names + self.r_br_names 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() self.add_path("metal1", [Q_bar_pos, vdd_pos]) + + def get_storage_net_names(self): + """Returns names of storage nodes in bitcell in [non-inverting, inverting] format.""" + return self.storage_nets + + def get_bl_name(self, port=0): + """Get bl name by port""" + return "bl{}".format(port) + + def get_br_name(self, port=0): + """Get bl name by port""" + return "br{}".format(port) - def analytical_delay(self, corner, slew, load=0, swing = 0.5): - #FIXME: Delay copied exactly over from bitcell - from tech import spice - r = spice["min_tx_r"]*3 - c_para = spice["min_tx_drain_c"] - result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = swing) - return result + def get_wl_name(self, port=0): + """Get wl name by port""" + debug.check(port<2,"Two ports for bitcell_1rw_1r only.") + return "wl{}".format(port) + + + def get_stage_effort(self, load): + parasitic_delay = 1 + size = 0.5 #This accounts for bitline being drained thought the access TX and internal node + cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. + + #Internal loads due to port configs are halved. This is to account for the size already being halved + #for stacked TXs, but internal loads do not see this size estimation. + write_port_load = self.num_w_ports*logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])/2 + read_port_load = self.num_r_ports/2 #min size NMOS gate load + total_load = load+read_port_load+write_port_load + return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) def analytical_power(self, corner, load): """Bitcell power in nW. Only characterizes leakage.""" @@ -882,9 +954,27 @@ class pbitcell(design.design): total_power = self.return_power(dynamic, leakage) return total_power - def get_wl_cin(self): + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #pbitcell uses the different sizing for the port access tx's. Not accounted for in this model. + + # FIXME: This applies to bitline capacitances as well. + # pbitcell uses the different sizing for the port access tx's. Not accounted for in this model. access_tx_cin = self.readwrite_nmos.get_cin() return 2*access_tx_cin + def build_graph(self, graph, inst_name, port_nets): + """Adds edges to graph for pbitcell. Only readwrite and read ports.""" + + if self.dummy_bitcell: + return + + pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + # Edges added wl->bl, wl->br for every port except write ports + rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names) + r_pin_names = zip(self.rw_wl_names, self.rw_bl_names, self.rw_br_names) + + for pin_zip in [rw_pin_names, r_pin_names]: + for wl,bl,br in pin_zip: + graph.add_edge(pin_dict[wl],pin_dict[bl], self) + graph.add_edge(pin_dict[wl],pin_dict[br], self) + diff --git a/compiler/bitcells/replica_bitcell.py b/compiler/bitcells/replica_bitcell.py index d8b8b76e..2f804bf0 100644 --- a/compiler/bitcells/replica_bitcell.py +++ b/compiler/bitcells/replica_bitcell.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import design import debug import utils @@ -11,6 +18,7 @@ class replica_bitcell(design.design): the technology library. """ pin_names = ["bl", "br", "wl", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] (width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"]) @@ -22,10 +30,30 @@ class replica_bitcell(design.design): self.width = replica_bitcell.width self.height = replica_bitcell.height self.pin_map = replica_bitcell.pin_map - - def get_wl_cin(self): + self.add_pin_types(self.type_list) + + def get_stage_effort(self, load): + parasitic_delay = 1 + size = 0.5 #This accounts for bitline being drained thought the access TX and internal node + cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. + read_port_load = 0.5 #min size NMOS gate load + return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) + + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. + + # FIXME: This applies to bitline capacitances as well. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] - return 2*access_tx_cin + return 2*access_tx_cin + + def analytical_power(self, corner, 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 + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges based on inputs/outputs. Overrides base class function.""" + self.add_graph_edges(graph, port_nets) \ No newline at end of file diff --git a/compiler/bitcells/replica_bitcell_1rw_1r.py b/compiler/bitcells/replica_bitcell_1rw_1r.py index 8f7b3b38..0f56319e 100644 --- a/compiler/bitcells/replica_bitcell_1rw_1r.py +++ b/compiler/bitcells/replica_bitcell_1rw_1r.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import design import debug import utils @@ -11,6 +18,7 @@ class replica_bitcell_1rw_1r(design.design): the technology library. """ pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] (width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"]) @@ -22,11 +30,31 @@ class replica_bitcell_1rw_1r(design.design): self.width = replica_bitcell_1rw_1r.width self.height = replica_bitcell_1rw_1r.height self.pin_map = replica_bitcell_1rw_1r.pin_map + self.add_pin_types(self.type_list) - def get_wl_cin(self): + def get_stage_effort(self, load): + parasitic_delay = 1 + size = 0.5 #This accounts for bitline being drained thought the access TX and internal node + cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. + read_port_load = 0.5 #min size NMOS gate load + return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) + + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. - #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + + # FIXME: This applies to bitline capacitances as well. + # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges to graph. Multiport bitcell timing graph is too complex + to use the add_graph_edges function.""" + pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + #Edges hardcoded here. Essentially wl->bl/br for both ports. + # Port 0 edges + graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self) + graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self) + # Port 1 edges + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) \ No newline at end of file diff --git a/compiler/bitcells/replica_bitcell_1w_1r.py b/compiler/bitcells/replica_bitcell_1w_1r.py index 6f59adec..b903e0ad 100644 --- a/compiler/bitcells/replica_bitcell_1w_1r.py +++ b/compiler/bitcells/replica_bitcell_1w_1r.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import design import debug import utils @@ -11,6 +18,7 @@ class replica_bitcell_1w_1r(design.design): the technology library. """ pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] (width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"]) @@ -22,11 +30,30 @@ class replica_bitcell_1w_1r(design.design): self.width = replica_bitcell_1w_1r.width self.height = replica_bitcell_1w_1r.height self.pin_map = replica_bitcell_1w_1r.pin_map + self.add_pin_types(self.type_list) - def get_wl_cin(self): + def get_stage_effort(self, load): + parasitic_delay = 1 + size = 0.5 #This accounts for bitline being drained thought the access TX and internal node + cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. + read_port_load = 0.5 #min size NMOS gate load + return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) + + def input_load(self): """Return the relative capacitance of the access transistor gates""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width. - #FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. + + # FIXME: This applies to bitline capacitances as well. + # FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed. access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges to graph. Multiport bitcell timing graph is too complex + to use the add_graph_edges function.""" + debug.info(1,'Adding edges for {}'.format(inst_name)) + pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + #Edges hardcoded here. Essentially wl->bl/br for the read port. + # Port 1 edges + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) + # Port 0 is a write port, so its timing is not considered here. \ No newline at end of file diff --git a/compiler/bitcells/replica_pbitcell.py b/compiler/bitcells/replica_pbitcell.py index 4d2ecd70..4fcfb4c5 100644 --- a/compiler/bitcells/replica_pbitcell.py +++ b/compiler/bitcells/replica_pbitcell.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import design from tech import drc, spice,parameter @@ -67,8 +74,7 @@ class replica_pbitcell(design.design): self.connect_inst(temp) def place_pbitcell(self): - offset = [0,0] - self.prbc_inst.place(offset=offset) + self.prbc_inst.place(offset=vector(0,0)) def route_rbc_connections(self): for port in range(self.total_ports): @@ -78,8 +84,4 @@ class replica_pbitcell(design.design): self.copy_layout_pin(self.prbc_inst, "wl{}".format(port)) self.copy_layout_pin(self.prbc_inst, "vdd") self.copy_layout_pin(self.prbc_inst, "gnd") - - def get_wl_cin(self): - """Return the relative capacitance of the access transistor gates""" - #This module is made using a pbitcell. Get the cin from that module - return self.prbc.get_wl_cin() + \ No newline at end of file diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index fc42a889..93dd5bcb 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import os import debug import globals @@ -6,9 +13,7 @@ from .lib import * from .delay import * from .setup_hold import * from .functional import * -from .worst_case import * from .simulation import * -from .bitline_delay import * from .measurements import * from .model_check import * diff --git a/compiler/characterizer/bit_polarity.py b/compiler/characterizer/bit_polarity.py new file mode 100644 index 00000000..c14c167e --- /dev/null +++ b/compiler/characterizer/bit_polarity.py @@ -0,0 +1,14 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from enum import Enum + +class bit_polarity(Enum): + NONINVERTING = 0 + INVERTING = 1 + diff --git a/compiler/characterizer/bitline_delay.py b/compiler/characterizer/bitline_delay.py deleted file mode 100644 index c85f06bc..00000000 --- a/compiler/characterizer/bitline_delay.py +++ /dev/null @@ -1,241 +0,0 @@ -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 bitline_delay(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) - self.period = tech.spice["feasible_period"] - self.is_bitline_measure = True - - def create_signal_names(self): - delay.create_signal_names(self) - self.bl_signal_names = ["Xsram.Xbank0.bl", "Xsram.Xbank0.br"] - self.sen_name = "Xsram.s_en" - - def create_measurement_names(self): - """Create measurement names. The names themselves currently define the type of measurement""" - #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. - self.bl_volt_meas_names = ["volt_bl", "volt_br"] - self.bl_delay_meas_names = ["delay_bl", "delay_br"] #only used in SPICE simulation - self.bl_delay_result_name = "delay_bl_vth" #Used in the return value - - def set_probe(self,probe_address, probe_data): - """ Probe address and data can be set separately to utilize other - functions in this characterizer besides analyze.""" - delay.set_probe(self,probe_address, probe_data) - self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) - - def write_delay_measures(self): - """ - Write the measure statements to quantify the bitline voltage at sense amp enable 50%. - """ - self.sf.write("\n* Measure statements for delay and power\n") - - # Output some comments to aid where cycles start and - for comment in self.cycle_comments: - self.sf.write("* {}\n".format(comment)) - - for read_port in self.targ_read_ports: - self.write_bitline_voltage_measures(read_port) - self.write_bitline_delay_measures(read_port) - - def write_bitline_voltage_measures(self, port): - """ - Add measurments to capture the bitline voltages at 50% Sense amp enable - """ - debug.info(2, "Measuring bitline column={}, port={}".format(self.bitline_column,port)) - if len(self.all_ports) == 1: #special naming case for single port sram bitlines - bitline_port = "" - else: - bitline_port = str(port) - - sen_port_name = "{}{}".format(self.sen_name,port) - for (measure_name, bl_signal_name) in zip(self.bl_volt_meas_names, self.bl_signal_names): - bl_port_name = "{}{}_{}".format(bl_signal_name, bitline_port, self.bitline_column) - measure_port_name = "{}{}".format(measure_name,port) - self.stim.gen_meas_find_voltage(measure_port_name, sen_port_name, bl_port_name, .5, "RISE", self.cycle_times[self.measure_cycles[port]["read0"]]) - - def write_bitline_delay_measures(self, port): - """ - Write the measure statements to quantify the delay and power results for a read port. - """ - # add measure statements for delays/slews - for (measure_name, bl_signal_name) in zip(self.bl_delay_meas_names, self.bl_signal_names): - meas_values = self.get_delay_meas_values(measure_name, bl_signal_name, port) - self.stim.gen_meas_delay(*meas_values) - - def get_delay_meas_values(self, delay_name, bitline_name, port): - """Get the values needed to generate a Spice measurement statement based on the name of the measurement.""" - if len(self.all_ports) == 1: #special naming case for single port sram bitlines - bitline_port = "" - else: - bitline_port = str(port) - - meas_name="{0}{1}".format(delay_name, port) - targ_name = "{0}{1}_{2}".format(bitline_name,bitline_port,self.bitline_column) - half_vdd = 0.5 * self.vdd_voltage - trig_val = half_vdd - targ_val = self.vdd_voltage-tech.spice["v_threshold_typical"] - trig_name = "clk{0}".format(port) - trig_dir="FALL" - targ_dir="FALL" - #Half period added to delay measurement to negative clock edge - trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2 - return (meas_name,trig_name,targ_name,trig_val,targ_val,trig_dir,targ_dir,trig_td,targ_td) - - def gen_test_cycles_one_port(self, read_port, write_port): - """Sets 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 """ - - # Create the inverse address for a scratch address - inverse_address = self.calculate_inverse_address() - - # For now, ignore data patterns and write ones or zeros - data_ones = "1"*self.word_size - data_zeros = "0"*self.word_size - - if self.t_current == 0: - self.add_noop_all_ports("Idle cycle (no positive clock edge)", - inverse_address, data_zeros) - - self.add_write("W data 1 address {}".format(inverse_address), - inverse_address,data_ones,write_port) - - self.add_write("W data 0 address {} to write value".format(self.probe_address), - self.probe_address,data_zeros,write_port) - 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 {} to set DOUT caps".format(inverse_address), - inverse_address,data_zeros,read_port) - self.measure_cycles[read_port]["read1"] = len(self.cycle_times)-1 - - self.add_read("R data 0 address {} to check W0 worked".format(self.probe_address), - self.probe_address,data_zeros,read_port) - self.measure_cycles[read_port]["read0"] = len(self.cycle_times)-1 - - def get_data_bit_column_number(self, probe_address, probe_data): - """Calculates bitline column number of data bit under test using bit position and mux size""" - if self.sram.col_addr_size>0: - col_address = int(probe_address[0:self.sram.col_addr_size],2) - else: - col_address = 0 - bl_column = int(self.sram.words_per_row*probe_data + col_address) - return bl_column - - def run_delay_simulation(self): - """ - This tries to simulate a period and checks if the result works. If - so, it returns True and the delays, slews, and powers. It - works on the trimmed netlist by default, so powers do not - include leakage of all cells. - """ - #Sanity Check - debug.check(self.period > 0, "Target simulation period non-positive") - - result = [{} for i in self.all_ports] - # Checking from not data_value to data_value - self.write_delay_stimulus() - - self.stim.run_sim() #running sim prodoces spice output file. - - for port in self.targ_read_ports: - #Parse and check the voltage measurements - bl_volt_meas_dict = {} - for mname in self.bl_volt_meas_names: - mname_port = "{}{}".format(mname,port) - volt_meas_val = parse_spice_list("timing", mname_port) - if type(volt_meas_val)!=float: - debug.error("Failed to Parse Bitline Voltage:\n\t\t{0}={1}".format(mname,volt_meas_val),1) - bl_volt_meas_dict[mname] = volt_meas_val - result[port].update(bl_volt_meas_dict) - - #Parse and check the delay measurements. Intended that one measurement will fail, save the delay that did not fail. - bl_delay_meas_dict = {} - values_added = 0 #For error checking - for mname in self.bl_delay_meas_names: #Parse - mname_port = "{}{}".format(mname,port) - delay_meas_val = parse_spice_list("timing", mname_port) - if type(delay_meas_val)==float: #Only add if value is float, do not error. - bl_delay_meas_dict[self.bl_delay_result_name] = delay_meas_val * 1e9 #convert to ns - values_added+=1 - debug.check(values_added>0, "Bitline delay measurements failed in SPICE simulation.") - debug.check(values_added<2, "Both bitlines experienced a Vth drop, check simulation results.") - result[port].update(bl_delay_meas_dict) - - # The delay is from the negative edge for our SRAM - return (True,result) - - def check_bitline_all_results(self, results): - """Checks the bitline values measured for each tested port""" - for port in self.targ_read_ports: - self.check_bitline_port_results(results[port]) - - def check_bitline_port_results(self, port_results): - """Performs three different checks for the bitline values: functionality, bitline swing from vdd, and differential bit swing""" - bl_volt, br_volt = port_results["volt_bl"], port_results["volt_br"] - self.check_functionality(bl_volt,br_volt) - self.check_swing_from_vdd(bl_volt,br_volt) - self.check_differential_swing(bl_volt,br_volt) - - def check_functionality(self, bl_volt, br_volt): - """Checks whether the read failed or not. Measured values are hardcoded with the intention of reading a 0.""" - if bl_volt > br_volt: - debug.error("Read failure. Value 1 was read instead of 0.",1) - - def check_swing_from_vdd(self, bl_volt, br_volt): - """Checks difference on discharging bitline from VDD to see if it is within margin of the RBL height parameter.""" - if bl_volt < br_volt: - discharge_volt = bl_volt - else: - discharge_volt = br_volt - desired_bl_volt = tech.parameter["rbl_height_percentage"]*self.vdd_voltage - debug.info(1, "Active bitline={:.3f}v, Desired bitline={:.3f}v".format(discharge_volt,desired_bl_volt)) - vdd_error_margin = .2 #20% of vdd margin for bitline, a little high for now. - if abs(discharge_volt - desired_bl_volt) > vdd_error_margin*self.vdd_voltage: - debug.warning("Bitline voltage is not within {}% Vdd margin. Delay chain/RBL could need resizing.".format(vdd_error_margin*100)) - - def check_differential_swing(self, bl_volt, br_volt): - """This check looks at the difference between the bitline voltages. This needs to be large enough to prevent - sensing errors.""" - bitline_swing = abs(bl_volt-br_volt) - debug.info(1,"Bitline swing={:.3f}v".format(bitline_swing)) - vdd_error_margin = .2 #20% of vdd margin for bitline, a little high for now. - if bitline_swing < vdd_error_margin*self.vdd_voltage: - debug.warning("Bitline swing less than {}% Vdd margin. Sensing errors more likely to occur.".format(vdd_error_margin)) - - def analyze(self, probe_address, probe_data, slews, loads): - """Measures the bitline swing of the differential bitlines (bl/br) at 50% s_en """ - self.set_probe(probe_address, probe_data) - self.load=max(loads) - self.slew=max(slews) - - read_port = self.read_ports[0] #only test the first read port - bitline_swings = {} - self.targ_read_ports = [read_port] - self.targ_write_ports = [self.write_ports[0]] - debug.info(1,"Bitline swing test: corner {}".format(self.corner)) - (success, results)=self.run_delay_simulation() - debug.check(success, "Bitline Failed: period {}".format(self.period)) - debug.info(1,"Bitline values (voltages/delays):\n\t {}".format(results[read_port])) - self.check_bitline_all_results(results) - - return results - - - diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py index 061972cf..fa49b1ed 100644 --- a/compiler/characterizer/charutils.py +++ b/compiler/characterizer/charutils.py @@ -1,8 +1,15 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import re import debug from globals import OPTS - + def relative_compare(value1,value2,error_tolerance=0.001): """ This is used to compare relative values for convergence. """ return (abs(value1 - value2) / abs(max(value1,value2)) <= error_tolerance) @@ -25,7 +32,6 @@ def parse_spice_list(filename, key): f.close() # val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents) val = re.search(r"{0}\s*=\s*(-?\d+.?\d*[e]?[-+]?[0-9]*\S*)\s+.*".format(key), contents) - if val != None: debug.info(4, "Key = " + key + " Val = " + val.group(1)) return convert_to_float(val.group(1)) @@ -86,4 +92,4 @@ def check_dict_values_is_float(dict): for key, value in dict.items(): if type(value)!=float: return False - return True \ No newline at end of file + return True diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 29b12ae9..31dc898a 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -1,17 +1,30 @@ -import sys,re,shutil +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import sys,re,shutil,copy import debug import tech import math from .stimuli import * from .trim_spice import * from .charutils import * +from .sram_op import * +from .bit_polarity import * import utils from globals import OPTS from .simulation import simulation from .measurements import * +import logical_effort +import graph_util +from sram_factory import factory class delay(simulation): - """Functions to measure the delay and power of an SRAM at a given address and + """ + Functions to measure the delay and power of an SRAM at a given address and data bit. In general, this will perform the following actions: @@ -30,113 +43,323 @@ class delay(simulation): def __init__(self, sram, spfile, corner): 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 + if self.write_size: + self.num_wmasks = int(self.word_size / self.write_size) + else: + self.num_wmasks = 0 self.set_load_slew(0,0) self.set_corner(corner) + self.create_signal_names() + self.add_graph_exclusions() def create_measurement_names(self): - """Create measurement names. The names themselves currently define the type of measurement""" - #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. + """ Create measurement names. The names themselves currently define the type of measurement """ + self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"] - self.voltage_when_names = ["volt_bl", "volt_br"] - self.bitline_delay_names = ["delay_bl", "delay_br"] + # self.voltage_when_names = ["volt_bl", "volt_br"] + # self.bitline_delay_names = ["delay_bl", "delay_br"] def create_measurement_objects(self): - """Create the measurements used for read and write ports""" - self.create_read_port_measurement_objects() - self.create_write_port_measurement_objects() - + """ Create the measurements used for read and write ports """ + + self.read_meas_lists = self.create_read_port_measurement_objects() + self.write_meas_lists = self.create_write_port_measurement_objects() + self.check_meas_names(self.read_meas_lists+self.write_meas_lists) + + def check_meas_names(self, measures_lists): + """ + Given measurements (in 2d list), checks that their names are unique. + Spice sim will fail otherwise. + """ + name_set = set() + for meas_list in measures_lists: + for meas in meas_list: + name = meas.name.lower() + debug.check(name not in name_set,("SPICE measurements must have unique names. " + "Duplicate name={}").format(name)) + name_set.add(name) + def create_read_port_measurement_objects(self): """Create the measurements used for read ports: delays, slews, powers""" - - self.read_meas_objs = [] - trig_delay_name = "clk{0}" - targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit - self.read_meas_objs.append(delay_measure("delay_lh", trig_delay_name, targ_name, "RISE", "RISE", measure_scale=1e9)) - self.read_meas_objs[-1].meta_str = "read1" #Used to index time delay values when measurements written to spice file. - self.read_meas_objs.append(delay_measure("delay_hl", trig_delay_name, targ_name, "FALL", "FALL", measure_scale=1e9)) - self.read_meas_objs[-1].meta_str = "read0" - - self.read_meas_objs.append(slew_measure("slew_lh", targ_name, "RISE", measure_scale=1e9)) - self.read_meas_objs[-1].meta_str = "read1" - self.read_meas_objs.append(slew_measure("slew_hl", targ_name, "FALL", measure_scale=1e9)) - self.read_meas_objs[-1].meta_str = "read0" - - self.read_meas_objs.append(power_measure("read1_power", "RISE", measure_scale=1e3)) - self.read_meas_objs[-1].meta_str = "read1" - self.read_meas_objs.append(power_measure("read0_power", "FALL", measure_scale=1e3)) - self.read_meas_objs[-1].meta_str = "read0" - - #This will later add a half-period to the spice time delay. Only for reading 0. - for obj in self.read_meas_objs: - if obj.meta_str is "read0": - obj.meta_add_delay = True - trig_name = "Xsram.s_en{}" #Sense amp enable - if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name - port_format = "" - else: - port_format = "{}" + self.read_lib_meas = [] + self.clk_frmt = "clk{0}" # Unformatted clock name + targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) # Empty values are the port and probe data bit + self.delay_meas = [] + self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "RISE", "RISE", measure_scale=1e9)) + self.delay_meas[-1].meta_str = sram_op.READ_ONE # Used to index time delay values when measurements written to spice file. + self.delay_meas.append(delay_measure("delay_hl", self.clk_frmt, targ_name, "FALL", "FALL", measure_scale=1e9)) + self.delay_meas[-1].meta_str = sram_op.READ_ZERO + self.read_lib_meas+=self.delay_meas - bl_name = "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column) - br_name = "Xsram.Xbank0.br{}_{}".format(port_format, self.bitline_column) - self.read_meas_objs.append(voltage_when_measure(self.voltage_when_names[0], trig_name, bl_name, "RISE", .5)) - self.read_meas_objs.append(voltage_when_measure(self.voltage_when_names[1], trig_name, br_name, "RISE", .5)) + self.slew_meas = [] + self.slew_meas.append(slew_measure("slew_lh", targ_name, "RISE", measure_scale=1e9)) + self.slew_meas[-1].meta_str = sram_op.READ_ONE + self.slew_meas.append(slew_measure("slew_hl", targ_name, "FALL", measure_scale=1e9)) + self.slew_meas[-1].meta_str = sram_op.READ_ZERO + self.read_lib_meas+=self.slew_meas - #These are read values but need to be separated for unique error checking. - self.create_bitline_delay_measurement_objects() + self.read_lib_meas.append(power_measure("read1_power", "RISE", measure_scale=1e3)) + self.read_lib_meas[-1].meta_str = sram_op.READ_ONE + self.read_lib_meas.append(power_measure("read0_power", "FALL", measure_scale=1e3)) + self.read_lib_meas[-1].meta_str = sram_op.READ_ZERO + + # This will later add a half-period to the spice time delay. Only for reading 0. + for obj in self.read_lib_meas: + if obj.meta_str is sram_op.READ_ZERO: + obj.meta_add_delay = True + + read_measures = [] + read_measures.append(self.read_lib_meas) + # Other measurements associated with the read port not included in the liberty file + read_measures.append(self.create_bitline_measurement_objects()) + read_measures.append(self.create_debug_measurement_objects()) + read_measures.append(self.create_read_bit_measures()) + + return read_measures - def create_bitline_delay_measurement_objects(self): - """Create the measurements used for bitline delay values. Due to unique error checking, these are separated from other measurements. - These measurements are only associated with read values + def create_bitline_measurement_objects(self): + """ + Create the measurements used for bitline delay values. Due to + unique error checking, these are separated from other measurements. + These measurements are only associated with read values. """ - self.bitline_delay_objs = [] - trig_name = "clk{0}" - if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name - port_format = "" - else: - port_format = "{}" - bl_name = "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column) - br_name = "Xsram.Xbank0.br{}_{}".format(port_format, self.bitline_column) - targ_val = (self.vdd_voltage - tech.spice["v_threshold_typical"])/self.vdd_voltage #Calculate as a percentage of vdd - targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit - self.bitline_delay_objs.append(delay_measure(self.bitline_delay_names[0], trig_name, bl_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9)) - self.bitline_delay_objs[-1].meta_str = "read0" - self.bitline_delay_objs.append(delay_measure(self.bitline_delay_names[1], trig_name, br_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9)) - self.bitline_delay_objs[-1].meta_str = "read1" - #Enforces the time delay on the bitline measurements for read0 or read1 - for obj in self.bitline_delay_objs: - obj.meta_add_delay = True + self.bitline_volt_meas = [] + + self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO", + self.bl_name)) + self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO + self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ZERO", + self.br_name)) + self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO + + self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ONE", + self.bl_name)) + self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE + self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ONE", + self.br_name)) + self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE + return self.bitline_volt_meas def create_write_port_measurement_objects(self): """Create the measurements used for read ports: delays, slews, powers""" - self.write_meas_objs = [] + + self.write_lib_meas = [] - self.write_meas_objs.append(power_measure("write1_power", "RISE", measure_scale=1e3)) - self.write_meas_objs[-1].meta_str = "write1" - self.write_meas_objs.append(power_measure("write0_power", "FALL", measure_scale=1e3)) - self.write_meas_objs[-1].meta_str = "write0" + self.write_lib_meas.append(power_measure("write1_power", "RISE", measure_scale=1e3)) + self.write_lib_meas[-1].meta_str = sram_op.WRITE_ONE + self.write_lib_meas.append(power_measure("write0_power", "FALL", measure_scale=1e3)) + self.write_lib_meas[-1].meta_str = sram_op.WRITE_ZERO + + write_measures = [] + write_measures.append(self.write_lib_meas) + write_measures.append(self.create_write_bit_measures()) + return write_measures + + def create_debug_measurement_objects(self): + """Create debug measurement to help identify failures.""" + + self.dout_volt_meas = [] + for meas in self.delay_meas: + # Output voltage measures + self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name), + meas.targ_name_no_port)) + self.dout_volt_meas[-1].meta_str = meas.meta_str + + self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name, "FALL", "RISE", measure_scale=1e9) + self.sen_meas.meta_str = sram_op.READ_ZERO + self.sen_meas.meta_add_delay = True + + return self.dout_volt_meas+[self.sen_meas] - def create_signal_names(self): - self.addr_name = "A" - self.din_name = "DIN" - self.dout_name = "DOUT" + def create_read_bit_measures(self): + """ Adds bit measurements for read0 and read1 cycles """ - #This is TODO once multiport control has been finalized. - #self.control_name = "CSB" + self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} + meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE) + for cycle in meas_cycles: + meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) + single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) + for polarity,meas in single_bit_meas.items(): + meas.meta_str = cycle + self.bit_meas[polarity].append(meas) + # Dictionary values are lists, reduce to a single list of measurements + return [meas for meas_list in self.bit_meas.values() for meas in meas_list] + + def create_write_bit_measures(self): + """ Adds bit measurements for write0 and write1 cycles """ + self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} + meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE) + for cycle in meas_cycles: + meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) + single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) + for polarity,meas in single_bit_meas.items(): + meas.meta_str = cycle + self.bit_meas[polarity].append(meas) + # Dictionary values are lists, reduce to a single list of measurements + return [meas for meas_list in self.bit_meas.values() for meas in meas_list] + + def get_bit_measures(self, meas_tag, probe_address, probe_data): + """ + Creates measurements for the q/qbar of input bit position. + meas_tag is a unique identifier for the measurement. + """ + + bit_col = self.get_data_bit_column_number(probe_address, probe_data) + bit_row = self.get_address_row_number(probe_address) + (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, bit_row, bit_col) + storage_names = cell_inst.mod.get_storage_net_names() + debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes" + "supported for characterization. Storage nets={}").format(storage_names)) + q_name = cell_name+'.'+str(storage_names[0]) + qbar_name = cell_name+'.'+str(storage_names[1]) + + # Bit measures, measurements times to be defined later. The measurement names must be unique + # but they is enforced externally + q_meas = voltage_at_measure("v_q_{}".format(meas_tag), q_name, has_port=False) + qbar_meas = voltage_at_measure("v_qbar_{}".format(meas_tag), qbar_name, has_port=False) + + return {bit_polarity.NONINVERTING:q_meas, bit_polarity.INVERTING:qbar_meas} + def set_load_slew(self,load,slew): """ Set the load and slew """ + self.load = load self.slew = slew + def add_graph_exclusions(self): + """Exclude portions of SRAM from timing graph which are not relevant""" + + # other initializations can only be done during analysis when a bit has been selected + # for testing. + self.sram.bank.graph_exclude_precharge() + self.sram.graph_exclude_addr_dff() + self.sram.graph_exclude_data_dff() + self.sram.graph_exclude_ctrl_dffs() + self.sram.bank.bitcell_array.graph_exclude_replica_col_bits() + + def create_graph(self): + """Creates timing graph to generate the timing paths for the SRAM output.""" + + self.sram.bank.bitcell_array.bitcell_array.init_graph_params() # Removes previous bit exclusions + self.sram.bank.bitcell_array.graph_exclude_bits(self.wordline_row, self.bitline_column) + + # Generate new graph every analysis as edges might change depending on test bit + self.graph = graph_util.timing_graph() + self.sram_spc_name = "X{}".format(self.sram.name) + self.sram.build_graph(self.graph,self.sram_spc_name,self.pins) + + def set_internal_spice_names(self): + """Sets important names for characterization such as Sense amp enable and internal bit nets.""" + + port = self.read_ports[0] + self.graph.get_all_paths('{}{}'.format("clk", port), + '{}{}_{}'.format(self.dout_name, port, self.probe_data)) + + self.sen_name = self.get_sen_name(self.graph.all_paths) + debug.info(2,"s_en name = {}".format(self.sen_name)) + + self.bl_name,self.br_name = self.get_bl_name(self.graph.all_paths, port) + debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name)) + + def get_sen_name(self, paths): + """ + Gets the signal name associated with the sense amp enable from input paths. + Only expects a single path to contain the sen signal name. + """ + + sa_mods = factory.get_mods(OPTS.sense_amp) + # Any sense amp instantiated should be identical, any change to that + # will require some identification to determine the mod desired. + debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.") + enable_name = sa_mods[0].get_enable_name() + sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0]) + return sen_name + + def get_bl_name(self, paths, port): + """Gets the signal name associated with the bitlines in the bank.""" + + cell_mod = factory.create(module_type=OPTS.bitcell) + cell_bl = cell_mod.get_bl_name(port) + cell_br = cell_mod.get_br_name(port) + + bl_found = False + # Only a single path should contain a single s_en name. Anything else is an error. + bl_names = [] + exclude_set = self.get_bl_name_search_exclusions() + for int_net in [cell_bl, cell_br]: + bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set)) + + return bl_names[0], bl_names[1] + + + def get_bl_name_search_exclusions(self): + """Gets the mods as a set which should be excluded while searching for name.""" + + # Exclude the RBL as it contains bitcells which are not in the main bitcell array + # so it makes the search awkward + return set(factory.get_mods(OPTS.replica_bitline)) + + def get_primary_cell_mod(self, cell_mods): + """ + Distinguish bitcell array mod from replica bitline array. + Assume there are no replica bitcells in the primary array. + """ + if len(cell_mods) == 1: + return cell_mods[0] + rbc_mods = factory.get_mods(OPTS.replica_bitcell) + non_rbc_mods = [] + for bitcell in cell_mods: + has_cell = False + for replica_cell in rbc_mods: + has_cell = has_cell or replica_cell.contains(bitcell, replica_cell.mods) + if not has_cell: + non_rbc_mods.append(bitcell) + if len(non_rbc_mods) != 1: + debug.error('Multiple bitcell mods found. Cannot distinguish for characterization',1) + return non_rbc_mods[0] + + def are_mod_pins_equal(self, mods): + """Determines if there are pins differences in the input mods""" + + if len(mods) == 0: + return True + pins = mods[0].pins + for mod in mods[1:]: + if pins != mod.pins: + return False + return True + + def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None): + """ + Finds a single alias for the int_net in given paths. + More or less hits cause an error + """ + + net_found = False + for path in paths: + aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, int_net, mod, exclusion_set) + if net_found and len(aliases) >= 1: + debug.error('Found multiple paths with {} net.'.format(int_net),1) + elif len(aliases) > 1: + debug.error('Found multiple {} nets in single path.'.format(int_net),1) + elif not net_found and len(aliases) == 1: + path_net_name = aliases[0] + net_found = True + if not net_found: + debug.error("Could not find {} net in timing paths.".format(int_net),1) + + return path_net_name + def check_arguments(self): """Checks if arguments given for write_stimulus() meets requirements""" + try: int(self.probe_address, 2) except ValueError: @@ -148,7 +371,7 @@ class delay(simulation): if not isinstance(self.probe_data, int) or self.probe_data>self.word_size or self.probe_data<0: 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 + # Adding port options here which the characterizer cannot handle. Some may be added later like ROM if len(self.read_ports) == 0: debug.error("Characterizer does not currently support SRAMs without read ports.",1) if len(self.write_ports) == 0: @@ -163,12 +386,8 @@ class delay(simulation): # instantiate the sram 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=(len(self.all_ports),self.write_ports,self.read_ports), - abits=self.addr_size, - dbits=self.word_size, - sram_name=self.name) + self.stim.inst_model(pins=self.pins, + model_name=self.sram.name) self.sf.write("\n* SRAM output loads\n") for port in self.read_ports: for i in range(self.word_size): @@ -176,10 +395,12 @@ class delay(simulation): def write_delay_stimulus(self): - """ Creates a stimulus file for simulations to probe a bitcell at a given clock period. + """ + Creates a stimulus file for simulations to probe a bitcell at a given clock period. Address and bit were previously set with set_probe(). Input slew (in ns) and output capacitive load (in fF) are required for charaterization. """ + self.check_arguments() # obtains list of time-points for each rising clk edge @@ -274,30 +495,47 @@ class delay(simulation): self.sf.close() - def get_read_measure_variants(self, port, measure_obj): - """Checks the measurement object and calls respective function for related measurement inputs.""" + def get_measure_variants(self, port, measure_obj, measure_type=None): + """ + Checks the measurement object and calls respective function for + related measurement inputs. + """ + meas_type = type(measure_obj) if meas_type is delay_measure or meas_type is slew_measure: - return self.get_delay_measure_variants(port, measure_obj) + variant_tuple = self.get_delay_measure_variants(port, measure_obj) elif meas_type is power_measure: - return self.get_power_measure_variants(port, measure_obj, "read") + variant_tuple = self.get_power_measure_variants(port, measure_obj, measure_type) elif meas_type is voltage_when_measure: - return self.get_volt_when_measure_variants(port, measure_obj) + variant_tuple = self.get_volt_when_measure_variants(port, measure_obj) + elif meas_type is voltage_at_measure: + variant_tuple = self.get_volt_at_measure_variants(port, measure_obj) else: debug.error("Input function not defined for measurement type={}".format(meas_type)) - + # Removes port input from any object which does not use it. This shorthand only works if + # the measurement has port as the last input. Could be implemented by measurement type or + # remove entirely from measurement classes. + if not measure_obj.has_port: + variant_tuple = variant_tuple[:-1] + return variant_tuple + def get_delay_measure_variants(self, port, delay_obj): - """Get the measurement values that can either vary from simulation to simulation (vdd, address) or port to port (time delays)""" - #Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port - #vdd is arguably constant as that is true for a single lib file. - if delay_obj.meta_str == "read0": - #Falling delay are measured starting from neg. clk edge. Delay adjusted to that. + """ + Get the measurement values that can either vary from simulation to + simulation (vdd, address) or port to port (time delays) + """ + + # Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port + # vdd is arguably constant as that is true for a single lib file. + if delay_obj.meta_str == sram_op.READ_ZERO: + # Falling delay are measured starting from neg. clk edge. Delay adjusted to that. meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]] - elif delay_obj.meta_str == "read1": + elif delay_obj.meta_str == sram_op.READ_ONE: meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]] else: debug.error("Unrecognised delay Index={}".format(delay_obj.meta_str),1) + # These measurements have there time further delayed to the neg. edge of the clock. if delay_obj.meta_add_delay: meas_cycle_delay += self.period/2 @@ -305,16 +543,33 @@ class delay(simulation): def get_power_measure_variants(self, port, power_obj, operation): """Get the measurement values that can either vary port to port (time delays)""" - #Return value is intended to match the power measure format: t_initial, t_final, port + + # Return value is intended to match the power measure format: t_initial, t_final, port t_initial = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]] t_final = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]+1] return (t_initial, t_final, port) - def get_volt_when_measure_variants(self, port, power_obj): - """Get the measurement values that can either vary port to port (time delays)""" - #Only checking 0 value reads for now. - t_trig = meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + def get_volt_at_measure_variants(self, port, volt_meas): + """ + Get the measurement values that can either vary port to port (time delays) + """ + + meas_cycle = self.cycle_times[self.measure_cycles[port][volt_meas.meta_str]] + + # Measurement occurs slightly into the next period so we know that the value + # "stuck" after the end of the period -> current period start + 1.25*period + at_time = meas_cycle+1.25*self.period + + return (at_time, port) + + def get_volt_when_measure_variants(self, port, volt_meas): + """ + Get the measurement values that can either vary port to port (time delays) + """ + + # Only checking 0 value reads for now. + t_trig = meas_cycle_delay = self.cycle_times[self.measure_cycles[port][sram_op.READ_ZERO]] return (t_trig, self.vdd_voltage, port) @@ -322,43 +577,45 @@ class delay(simulation): """ Write the measure statements to quantify the delay and power results for a read port. """ + # add measure statements for delays/slews - for measure in self.read_meas_objs+self.bitline_delay_objs: - measure_variant_inp_tuple = self.get_read_measure_variants(port, measure) - measure.write_measure(self.stim, measure_variant_inp_tuple) + for meas_list in self.read_meas_lists: + for measure in meas_list: + measure_variant_inp_tuple = self.get_measure_variants(port, measure, "read") + measure.write_measure(self.stim, measure_variant_inp_tuple) - def get_write_measure_variants(self, port, measure_obj): - """Checks the measurement object and calls respective function for related measurement inputs.""" - meas_type = type(measure_obj) - if meas_type is power_measure: - return self.get_power_measure_variants(port, measure_obj, "write") - else: - debug.error("Input function not defined for measurement type={}".format(meas_type)) def write_delay_measures_write_port(self, port): """ Write the measure statements to quantify the power results for a write port. """ + # add measure statements for power - for measure in self.write_meas_objs: - measure_variant_inp_tuple = self.get_write_measure_variants(port, measure) - measure.write_measure(self.stim, measure_variant_inp_tuple) + for meas_list in self.write_meas_lists: + for measure in meas_list: + measure_variant_inp_tuple = self.get_measure_variants(port, measure, "write") + measure.write_measure(self.stim, measure_variant_inp_tuple) def write_delay_measures(self): """ Write the measure statements to quantify the delay and power results for all targeted ports. """ + self.sf.write("\n* Measure statements for delay and power\n") # Output some comments to aid where cycles start and # what is happening for comment in self.cycle_comments: self.sf.write("* {}\n".format(comment)) - + + self.sf.write("\n") for read_port in self.targ_read_ports: - self.write_delay_measures_read_port(read_port) + self.sf.write("* Read ports {}\n".format(read_port)) + self.write_delay_measures_read_port(read_port) + for write_port in self.targ_write_ports: - self.write_delay_measures_write_port(write_port) + self.sf.write("* Write ports {}\n".format(write_port)) + self.write_delay_measures_write_port(write_port) def write_power_measures(self): @@ -392,35 +649,36 @@ class delay(simulation): if (time_out <= 0): debug.error("Timed out, could not find a feasible period.",2) - #Clear any write target ports and set read port - self.targ_write_ports = [] + # Clear any write target ports and set read port + self.targ_write_ports = [port] self.targ_read_ports = [port] - success = False debug.info(1, "Trying feasible period: {0}ns on Port {1}".format(feasible_period, port)) self.period = feasible_period (success, results)=self.run_delay_simulation() - #Clear these target ports after simulation + + # Clear these target ports after simulation + self.targ_write_ports = [] self.targ_read_ports = [] if not success: feasible_period = 2 * feasible_period continue - #Positions of measurements currently hardcoded. First 2 are delays, next 2 are slews + # Positions of measurements currently hardcoded. First 2 are delays, next 2 are slews feasible_delays = [results[port][mname] for mname in self.delay_meas_names if "delay" in mname] feasible_slews = [results[port][mname] for mname in self.delay_meas_names if "slew" in mname] delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(*feasible_delays) slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(*feasible_slews) debug.info(2, "feasible_period passed for Port {3}: {0}ns {1} {2} ".format(feasible_period, - delay_str, - slew_str, - port)) + delay_str, + slew_str, + port)) if success: debug.info(2, "Found feasible_period for port {0}: {1}ns".format(port, feasible_period)) self.period = feasible_period - #Only return results related to input port. + # Only return results related to input port. return results[port] def find_feasible_period(self): @@ -430,19 +688,19 @@ class delay(simulation): """ feasible_delays = [{} for i in self.all_ports] - #Get initial feasible delays from first port + # Get initial feasible delays from first port feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[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. + # 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] - #Only extract port values from the specified port, not the entire results. + # 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 + # Function sets the period. Restart the entire process if period changes to collect accurate delays if self.period > previous_period: i = 0 else: @@ -458,71 +716,174 @@ class delay(simulation): works on the trimmed netlist by default, so powers do not include leakage of all cells. """ - #Sanity Check + debug.check(self.period > 0, "Target simulation period non-positive") - result = [{} for i in self.all_ports] - # Checking from not data_value to data_value self.write_delay_stimulus() self.stim.run_sim() + + return self.check_measurements() + + def check_measurements(self): + """ Check the write and read measurements """ - #Loop through all targeted ports and collect delays and powers. - #Too much duplicate code here. Try reducing - for port in self.targ_read_ports: - debug.info(2, "Check delay values for port {}".format(port)) - read_port_dict = {} - #Get measurements from output file - for measure in self.read_meas_objs: - read_port_dict[measure.name] = measure.retrieve_measure(port=port) - - #Check timing for read ports. Power is only checked if it was read correctly - if not self.check_valid_delays(read_port_dict): - return (False,{}) - if not check_dict_values_is_float(read_port_dict): - debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict),1) #Printing the entire dict looks bad. - - result[port].update(read_port_dict) - - bitline_delay_dict = self.evaluate_bitline_delay(port) - result[port].update(bitline_delay_dict) - + # Loop through all targeted ports and collect delays and powers. + result = [{} for i in self.all_ports] + + + # First, check that the memory has the right values at the right times + if not self.check_bit_measures(): + return(False,{}) + for port in self.targ_write_ports: + debug.info(2, "Checking write values for port {}".format(port)) write_port_dict = {} - for measure in self.write_meas_objs: + for measure in self.write_lib_meas: write_port_dict[measure.name] = measure.retrieve_measure(port=port) if not check_dict_values_is_float(write_port_dict): - debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict),1) #Printing the entire dict looks bad. + debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict),1) result[port].update(write_port_dict) - # The delay is from the negative edge for our SRAM + + for port in self.targ_read_ports: + debug.info(2, "Checking read delay values for port {}".format(port)) + # Check sen timing, then bitlines, then general measurements. + if not self.check_sen_measure(port): + return (False,{}) + + if not self.check_read_debug_measures(port): + return (False,{}) + + # Check timing for read ports. Power is only checked if it was read correctly + read_port_dict = {} + for measure in self.read_lib_meas: + read_port_dict[measure.name] = measure.retrieve_measure(port=port) + + if not self.check_valid_delays(read_port_dict): + return (False,{}) + + if not check_dict_values_is_float(read_port_dict): + debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict),1) + + result[port].update(read_port_dict) + return (True,result) - def evaluate_bitline_delay(self, port): - """Parse and check the bitline delay. One of the measurements is expected to fail which warrants its own function.""" - bl_delay_meas_dict = {} - values_added = 0 #For error checking - for measure in self.bitline_delay_objs: - bl_delay_val = measure.retrieve_measure(port=port) - if type(bl_delay_val) != float or 0 > bl_delay_val or bl_delay_val > self.period/2: #Only add if value is valid, do not error. - debug.error("Bitline delay measurement failed: half-period={}, {}={}".format(self.period/2, measure.name, bl_delay_val),1) - bl_delay_meas_dict[measure.name] = bl_delay_val - return bl_delay_meas_dict + def check_sen_measure(self, port): + """Checks that the sen occurred within a half-period""" + + sen_val = self.sen_meas.retrieve_measure(port=port) + debug.info(2,"s_en delay={}ns".format(sen_val)) + if self.sen_meas.meta_add_delay: + max_delay = self.period/2 + else: + max_delay = self.period + return not (type(sen_val) != float or sen_val > max_delay) + + + def check_read_debug_measures(self, port): + """Debug measures that indicate special conditions.""" + + # Currently, only check if the opposite than intended value was read during + # the read cycles i.e. neither of these measurements should pass. + # FIXME: these checks need to be re-done to be more robust against possible errors + bl_vals = {} + br_vals = {} + for meas in self.bitline_volt_meas: + val = meas.retrieve_measure(port=port) + if self.bl_name == meas.targ_name_no_port: + bl_vals[meas.meta_str] = val + elif self.br_name == meas.targ_name_no_port: + br_vals[meas.meta_str] = val + + debug.info(2,"{}={}".format(meas.name,val)) + + dout_success = True + bl_success = False + for meas in self.dout_volt_meas: + val = meas.retrieve_measure(port=port) + debug.info(2,"{}={}".format(meas.name, val)) + debug.check(type(val)==float, "Error retrieving numeric measurement: {0} {1}".format(meas.name,val)) + + if meas.meta_str == sram_op.READ_ONE and val < self.vdd_voltage*0.1: + dout_success = False + debug.info(1, "Debug measurement failed. Value {}V was read on read 1 cycle.".format(val)) + bl_success = self.check_bitline_meas(bl_vals[sram_op.READ_ONE], br_vals[sram_op.READ_ONE]) + elif meas.meta_str == sram_op.READ_ZERO and val > self.vdd_voltage*0.9: + dout_success = False + debug.info(1, "Debug measurement failed. Value {}V was read on read 0 cycle.".format(val)) + bl_success = self.check_bitline_meas(br_vals[sram_op.READ_ONE], bl_vals[sram_op.READ_ONE]) + + # If the bitlines have a correct value while the output does not then that is a + # sen error. FIXME: there are other checks that can be done to solidfy this conclusion. + if not dout_success and bl_success: + debug.error("Sense amp enable timing error. Increase the delay chain through the configuration file.",1) + + return dout_success + + + def check_bit_measures(self): + """ + Checks the measurements which represent the internal storage voltages + at the end of the read cycle. + """ + success = False + for polarity, meas_list in self.bit_meas.items(): + for meas in meas_list: + val = meas.retrieve_measure() + debug.info(2,"{}={}".format(meas.name, val)) + if type(val) != float: + continue + meas_cycle = meas.meta_str + # Loose error conditions. Assume it's not metastable but account for noise during reads. + if (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.NONINVERTING) or\ + (meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.INVERTING): + success = val < self.vdd_voltage/2 + elif (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.INVERTING) or\ + (meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.NONINVERTING): + success = val > self.vdd_voltage/2 + elif (meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.INVERTING) or\ + (meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.NONINVERTING): + success = val > self.vdd_voltage/2 + elif (meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.INVERTING) or\ + (meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.NONINVERTING): + success = val < self.vdd_voltage/2 + if not success: + debug.info(1,("Wrong value detected on probe bit during read/write cycle. " + "Check writes and control logic for bugs.\n measure={}, op={}, " + "bit_storage={}, V(bit)={}").format(meas.name, meas_cycle.name, polarity.name,val)) + + return success + + def check_bitline_meas(self, v_discharged_bl, v_charged_bl): + """ + Checks the value of the discharging bitline. Confirms s_en timing errors. + Returns true if the bitlines are at there expected value. + """ + # The inputs looks at discharge/charged bitline rather than left or right (bl/br) + # Performs two checks, discharging bitline is at least 10% away from vdd and there is a + # 10% vdd difference between the bitlines. Both need to fail to be considered a s_en error. + min_dicharge = v_discharged_bl < self.vdd_voltage*0.9 + min_diff = (v_charged_bl - v_discharged_bl) > self.vdd_voltage*0.1 + + debug.info(1,"min_dicharge={}, min_diff={}".format(min_dicharge,min_diff)) + return (min_dicharge and min_diff) def run_power_simulation(self): """ This simulates a disabled SRAM to get the leakage power when it is off. - """ + debug.info(1, "Performing leakage power simulations.") self.write_power_stimulus(trim=False) self.stim.run_sim() leakage_power=parse_spice_list("timing", "leakage_power") debug.check(leakage_power!="Failed","Could not measure leakage power.") debug.info(1, "Leakage power of full array is {0} mW".format(leakage_power*1e3)) - #debug - #sys.exit(1) + # debug + # sys.exit(1) self.write_power_stimulus(trim=True) self.stim.run_sim() @@ -531,12 +892,13 @@ class delay(simulation): debug.info(1, "Leakage power of trimmed array is {0} mW".format(trim_leakage_power*1e3)) # For debug, you sometimes want to inspect each simulation. - #key=raw_input("press return to continue") + # key=raw_input("press return to continue") return (leakage_power*1e3, trim_leakage_power*1e3) def check_valid_delays(self, result_dict): """ Check if the measurements are defined and if they are valid. """ - #Hard coded names currently + + # Hard coded names currently delay_hl = result_dict["delay_hl"] delay_lh = result_dict["delay_lh"] slew_hl = result_dict["slew_hl"] @@ -554,8 +916,9 @@ class delay(simulation): delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh) slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh) - half_period = self.period/2 #high-to-low delays start at neg. clk edge, so they need to be less than half_period - if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period: + half_period = self.period/2 # high-to-low delays start at neg. clk edge, so they need to be less than half_period + if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period \ + or delay_hl<0 or delay_lh<0 or slew_hl<0 or slew_lh<0: debug.info(2,"UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, delays_str, slews_str)) @@ -576,15 +939,15 @@ class delay(simulation): lb_period = 0.0 target_period = 0.5 * (ub_period + lb_period) - #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. + # 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: 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. + # The min period of one port becomes the new lower bound. Reset the upper_bound. lb_period = target_period ub_period = feasible_period - #Clear the target ports before leaving + # Clear the target ports before leaving self.targ_read_ports = [] self.targ_write_ports = [] return target_period @@ -595,13 +958,14 @@ class delay(simulation): long period. For the current logic to characterize multiport, bounds are required as an input. """ - #previous_period = ub_period = self.period - #ub_period = self.period - #lb_period = 0.0 - #target_period = 0.5 * (ub_period + lb_period) + # previous_period = ub_period = self.period + # ub_period = self.period + # lb_period = 0.0 + # target_period = 0.5 * (ub_period + lb_period) # Binary search algorithm to find the min period (max frequency) of input port time_out = 25 + self.targ_write_ports = [port] self.targ_read_ports = [port] while True: time_out -= 1 @@ -610,9 +974,9 @@ class delay(simulation): self.period = target_period debug.info(1, "MinPeriod Search Port {3}: {0}ns (ub: {1} lb: {2})".format(target_period, - ub_period, - lb_period, - port)) + ub_period, + lb_period, + port)) if self.try_period(feasible_delays): ub_period = target_period @@ -623,9 +987,9 @@ class delay(simulation): # ub_period is always feasible. return ub_period - #Update target + # Update target target_period = 0.5 * (ub_period + lb_period) - #key=input("press return to continue") + # key=input("press return to continue") def try_period(self, feasible_delays): @@ -633,18 +997,20 @@ class delay(simulation): This tries to simulate a period and checks if the result works. If it does and the delay is within 5% still, it returns True. """ + # Run Delay simulation but Power results not used. (success, results) = self.run_delay_simulation() if not success: return False - #Check the values of target readwrite and read ports. Write ports do not produce delays in this current version - for port in self.targ_read_ports: - for dname in self.delay_meas_names: #check that the delays and slews do not degrade with tested period. + # Check the values of target readwrite and read ports. Write ports do not produce delays in this current version + for port in self.targ_read_ports: + # check that the delays and slews do not degrade with tested period. + for dname in self.delay_meas_names: - #FIXME: This is a hack solution to fix the min period search. The slew will always be based on the period when there - #is a column mux. Therefore, the checks are skipped for this condition. This is hard to solve without changing the netlist. - #Delays/slews based on the period will cause the min_period search to come to the wrong period. + # FIXME: This is a hack solution to fix the min period search. The slew will always be based on the period when there + # is a column mux. Therefore, the checks are skipped for this condition. This is hard to solve without changing the netlist. + # Delays/slews based on the period will cause the min_period search to come to the wrong period. if self.sram.col_addr_size>0 and "slew" in dname: continue @@ -652,9 +1018,8 @@ class delay(simulation): debug.info(2,"Delay too big {0} vs {1}".format(results[port][dname],feasible_delays[port][dname])) return False - #key=raw_input("press return to continue") + # key=raw_input("press return to continue") - #Dynamic way to build string. A bit messy though. delay_str = ', '.join("{0}={1}ns".format(mname, results[port][mname]) for mname in self.delay_meas_names) debug.info(2,"Successful period {0}, Port {2}, {1}".format(self.period, delay_str, @@ -662,8 +1027,11 @@ class delay(simulation): return True def set_probe(self,probe_address, probe_data): - """ Probe address and data can be set separately to utilize other - functions in this characterizer besides analyze.""" + """ + Probe address and data can be set separately to utilize other + functions in this characterizer besides analyze. + """ + self.probe_address = probe_address self.probe_data = probe_data self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) @@ -672,6 +1040,7 @@ class delay(simulation): def get_data_bit_column_number(self, probe_address, probe_data): """Calculates bitline column number of data bit under test using bit position and mux size""" + if self.sram.col_addr_size>0: col_address = int(probe_address[0:self.sram.col_addr_size],2) else: @@ -681,6 +1050,7 @@ class delay(simulation): def get_address_row_number(self, probe_address): """Calculates wordline row number of data bit under test using address and column mux size""" + return int(probe_address[self.sram.col_addr_size:],2) def prepare_netlist(self): @@ -704,31 +1074,26 @@ class delay(simulation): # Make a copy in temp for debugging shutil.copy(self.sp_file, self.sim_sp_file) - - - def analyze(self,probe_address, probe_data, slews, loads): - """ - Main function to characterize an SRAM for a table. Computes both delay and power characterization. - """ - #Dict to hold all characterization values - char_sram_data = {} + def analysis_init(self, probe_address, probe_data): + """Sets values which are dependent on the data address/bit being tested.""" self.set_probe(probe_address, probe_data) - self.create_signal_names() + self.create_graph() + self.set_internal_spice_names() self.create_measurement_names() self.create_measurement_objects() + def analyze(self, probe_address, probe_data, slews, loads): + """ + Main function to characterize an SRAM for a table. Computes both delay and power characterization. + """ + + # Dict to hold all characterization values + char_sram_data = {} + self.analysis_init(probe_address, probe_data) + self.load=max(loads) self.slew=max(slews) - # This is for debugging a full simulation - # debug.info(0,"Debug simulation running...") - # target_period=50.0 - # feasible_delay_lh=0.059083183 - # feasible_delay_hl=0.17953789 - # load=1.6728 - # slew=0.04 - # self.try_period(target_period, feasible_delay_lh, feasible_delay_hl) - # sys.exit(1) # 1) Find a feasible period and it's corresponding delays using the trimmed array. feasible_delays = self.find_feasible_period() @@ -748,22 +1113,24 @@ class delay(simulation): self.period = min_period char_port_data = self.simulate_loads_and_slews(slews, loads, leakage_offset) - #FIXME: low-to-high delays are altered to be independent of the period. This makes the lib results less accurate. + # FIXME: low-to-high delays are altered to be independent of the period. This makes the lib results less accurate. self.alter_lh_char_data(char_port_data) return (char_sram_data, char_port_data) def alter_lh_char_data(self, char_port_data): """Copies high-to-low data to low-to-high data to make them consistent on the same clock edge.""" - #This is basically a hack solution which should be removed/fixed later. + + # This is basically a hack solution which should be removed/fixed later. for port in self.all_ports: char_port_data[port]['delay_lh'] = char_port_data[port]['delay_hl'] char_port_data[port]['slew_lh'] = char_port_data[port]['slew_hl'] def simulate_loads_and_slews(self, slews, loads, leakage_offset): """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. + # 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 for slew in slews: @@ -773,7 +1140,7 @@ class delay(simulation): (success, delay_results) = self.run_delay_simulation() 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). + # The results has a dict for every port but dicts can be empty (e.g. ports were not targeted). for port in self.all_ports: for mname,value in delay_results[port].items(): if "power" in mname: @@ -785,11 +1152,12 @@ class delay(simulation): def calculate_inverse_address(self): """Determine dummy test address based on probe address and column mux size.""" - #The inverse address needs to share the same bitlines as the probe address as the trimming will remove all other bitlines - #This is only an issue when there is a column mux and the address maps to different bitlines. - column_addr = self.probe_address[:self.sram.col_addr_size] #do not invert this part + + # The inverse address needs to share the same bitlines as the probe address as the trimming will remove all other bitlines + # This is only an issue when there is a column mux and the address maps to different bitlines. + column_addr = self.probe_address[:self.sram.col_addr_size] # do not invert this part inverse_address = "" - for c in self.probe_address[self.sram.col_addr_size:]: #invert everything else + for c in self.probe_address[self.sram.col_addr_size:]: # invert everything else if c=="0": inverse_address += "1" elif c=="1": @@ -808,48 +1176,51 @@ class delay(simulation): # For now, ignore data patterns and write ones or zeros data_ones = "1"*self.word_size data_zeros = "0"*self.word_size + wmask_ones = "1"*self.num_wmasks + wmask_zeroes = "0"*self.num_wmasks if self.t_current == 0: self.add_noop_all_ports("Idle cycle (no positive clock edge)", - inverse_address, data_zeros) + inverse_address, data_zeros,wmask_zeroes) self.add_write("W data 1 address {}".format(inverse_address), - inverse_address,data_ones,write_port) + inverse_address,data_ones,wmask_ones,write_port) self.add_write("W data 0 address {} to write value".format(self.probe_address), - self.probe_address,data_zeros,write_port) - self.measure_cycles[write_port]["write0"] = len(self.cycle_times)-1 + self.probe_address,data_zeros,wmask_ones,write_port) + self.measure_cycles[write_port][sram_op.WRITE_ZERO] = 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 {} to set DOUT caps".format(inverse_address), - inverse_address,data_zeros,read_port) + self.add_read("R data 1 address {} to set dout caps".format(inverse_address), + inverse_address,data_zeros,wmask_ones,read_port) self.add_read("R data 0 address {} to check W0 worked".format(self.probe_address), - self.probe_address,data_zeros,read_port) - self.measure_cycles[read_port]["read0"] = len(self.cycle_times)-1 + self.probe_address,data_zeros,wmask_ones,read_port) + self.measure_cycles[read_port][sram_op.READ_ZERO] = len(self.cycle_times)-1 self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)", - inverse_address,data_zeros) + inverse_address,data_zeros,wmask_zeroes) self.add_write("W data 1 address {} to write value".format(self.probe_address), - self.probe_address,data_ones,write_port) - self.measure_cycles[write_port]["write1"] = len(self.cycle_times)-1 + self.probe_address,data_ones,wmask_ones,write_port) + self.measure_cycles[write_port][sram_op.WRITE_ONE] = len(self.cycle_times)-1 - self.add_write("W data 0 address {} to clear DIN caps".format(inverse_address), - inverse_address,data_zeros,write_port) + self.add_write("W data 0 address {} to clear din caps".format(inverse_address), + inverse_address,data_zeros,wmask_ones,write_port) # This also ensures we will have a L->H transition on the next read - self.add_read("R data 0 address {} to clear DOUT caps".format(inverse_address), - inverse_address,data_zeros,read_port) + self.add_read("R data 0 address {} to clear dout caps".format(inverse_address), + inverse_address,data_zeros,wmask_ones,read_port) self.add_read("R data 1 address {} to check W1 worked".format(self.probe_address), - self.probe_address,data_zeros,read_port) - self.measure_cycles[read_port]["read1"] = len(self.cycle_times)-1 + self.probe_address,data_zeros,wmask_ones,read_port) + self.measure_cycles[read_port][sram_op.READ_ONE] = len(self.cycle_times)-1 self.add_noop_all_ports("Idle cycle (if read takes >1 cycle))", - self.probe_address,data_zeros) + self.probe_address,data_zeros,wmask_zeroes) 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] @@ -862,29 +1233,32 @@ class delay(simulation): self.measure_cycles = [{} for port in self.all_ports] def create_test_cycles(self): - """Returns a list of key time-points [ns] of the waveform (each rising edge) + """ + Returns a list of key time-points [ns] of the waveform (each rising edge) of the cycles to do a timing evaluation. The last time is the end of the simulation - and does not need a rising edge.""" - #Using this requires setting at least one port to target for simulation. + 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 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. + # 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) 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") - #Create test cycles for specified target ports. + # Create test cycles for specified target ports. write_pos = 0 read_pos = 0 while True: - #Exit when all ports have been characterized + # Exit when all ports have been characterized if write_pos >= len(self.targ_write_ports) and read_pos >= len(self.targ_read_ports): break - #Select new write and/or read ports for the next cycle. Use previous port if none remaining. + # Select new write and/or read ports for the next cycle. Use previous port if none remaining. if write_pos < len(self.targ_write_ports): cur_write_port = self.targ_write_ports[write_pos] write_pos+=1 @@ -892,47 +1266,81 @@ class delay(simulation): cur_read_port = self.targ_read_ports[read_pos] read_pos+=1 - #Add test cycle of read/write port pair. One port could have been used already, but the other has not. + # Add test cycle of read/write port pair. One port could have been used already, but the other has not. self.gen_test_cycles_one_port(cur_read_port, cur_write_port) + def sum_delays(self, delays): + """Adds the delays (delay_data objects) so the correct slew is maintained""" + + delay = delays[0] + for i in range(1, len(delays)): + delay+=delays[i] + return delay + def analytical_delay(self, slews, loads): - """ Return the analytical model results for the SRAM. + """ + Return the analytical model results for the SRAM. """ if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0: debug.warning("Analytical characterization results are not supported for multiport.") - self.create_signal_names() + + # Probe set to 0th bit, does not matter for analytical delay. + self.set_probe('0'*self.addr_size, 0) + self.create_graph() + self.set_internal_spice_names() self.create_measurement_names() - power = self.analytical_power(slews, loads) + + port = self.read_ports[0] + self.graph.get_all_paths('{}{}'.format("clk", port), + '{}{}_{}'.format(self.dout_name, port, self.probe_data)) + + # Select the path with the bitline (bl) + bl_name,br_name = self.get_bl_name(self.graph.all_paths, port) + bl_path = [path for path in self.graph.all_paths if bl_name in path][0] + + # Set delay/power for slews and loads port_data = self.get_empty_measure_data_dict() + power = self.analytical_power(slews, loads) + debug.info(1,'Slew, Load, Delay(ns), Slew(ns)') + max_delay = 0.0 for slew in slews: for load in loads: - self.set_load_slew(load,slew) - bank_delay = self.sram.analytical_delay(self.corner, self.slew,self.load) + # Calculate delay based on slew and load + path_delays = self.graph.get_timing(bl_path, self.corner, slew, load) + + total_delay = self.sum_delays(path_delays) + max_delay = max(max_delay, total_delay.delay) + debug.info(1,'{}, {}, {}, {}'.format(slew,load,total_delay.delay/1e3, total_delay.slew/1e3)) + + # Delay is only calculated on a single port and replicated for now. for port in self.all_ports: for mname in self.delay_meas_names+self.power_meas_names: if "power" in mname: port_data[port][mname].append(power.dynamic) - elif "delay" in mname: - port_data[port][mname].append(bank_delay[port].delay/1e3) - elif "slew" in mname: - port_data[port][mname].append(bank_delay[port].slew/1e3) + elif "delay" in mname and port in self.read_ports: + port_data[port][mname].append(total_delay.delay/1e3) + elif "slew" in mname and port in self.read_ports: + port_data[port][mname].append(total_delay.slew/1e3) else: debug.error("Measurement name not recognized: {}".format(mname),1) + + # Estimate the period as double the delay with margin period_margin = 0.1 - risefall_delay = bank_delay[self.read_ports[0]].delay/1e3 - sram_data = { "min_period":risefall_delay*2*period_margin, + sram_data = { "min_period":(max_delay/1e3)*2*period_margin, "leakage_power": power.leakage} + debug.info(2,"SRAM Data:\n{}".format(sram_data)) debug.info(2,"Port Data:\n{}".format(port_data)) - return (sram_data,port_data) - + return (sram_data,port_data) + def analytical_power(self, slews, loads): """Get the dynamic and leakage power from the SRAM""" - #slews unused, only last load is used + + # slews unused, only last load is used load = loads[-1] power = self.sram.analytical_power(self.corner, load) - #convert from nW to mW + # convert from nW to mW power.dynamic /= 1e6 power.leakage /= 1e6 debug.info(1,"Dynamic Power: {0} mW".format(power.dynamic)) @@ -941,6 +1349,7 @@ class delay(simulation): def gen_data(self): """ Generates the PWL data inputs for a simulation timing test. """ + for write_port in self.write_ports: for i in range(self.word_size): sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i) @@ -951,6 +1360,7 @@ class delay(simulation): 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 self.all_ports: for i in range(self.addr_size): sig_name = "{0}{1}_{2}".format(self.addr_name,port,i) @@ -958,6 +1368,7 @@ class delay(simulation): def gen_control(self): """ Generates the control signals """ + for port in self.all_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.readwrite_ports: @@ -966,7 +1377,8 @@ class delay(simulation): def get_empty_measure_data_dict(self): """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 + self.voltage_when_names + self.bitline_delay_names - #Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. + + 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 self.all_ports] return measure_data diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index ca05648a..49907b4b 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -1,4 +1,12 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import sys,re,shutil +import collections from design import design import debug import math @@ -8,9 +16,10 @@ from .stimuli import * from .charutils import * import utils from globals import OPTS - from .simulation import simulation from .delay import delay +import graph_util +from sram_factory import factory class functional(simulation): """ @@ -25,19 +34,38 @@ class functional(simulation): if OPTS.is_unit_test: random.seed(12345) + if self.write_size: + self.num_wmasks = int(self.word_size / self.write_size) + else: + self.num_wmasks = 0 + self.set_corner(corner) self.set_spice_constants() - #self.set_feasible_period(sram, spfile, corner) self.set_stimulus_variables() - self.create_signal_names() + # For the debug signal names + self.create_signal_names() + self.add_graph_exclusions() + self.create_graph() + self.set_internal_spice_names() + self.initialize_wmask() + # Number of checks can be changed - self.num_cycles = 2 - self.stored_words = {} + self.num_cycles = 15 + # This is to have ordered keys for random selection + self.stored_words = collections.OrderedDict() self.write_check = [] self.read_check = [] - + + + def initialize_wmask(self): + self.wmask = "" + if self.write_size: + # initialize all wmask bits to 1 + for bit in range(self.num_wmasks): + self.wmask += "1" + def run(self, feasible_period=None): if feasible_period: #period defaults to tech.py feasible period otherwise. self.period = feasible_period @@ -48,7 +76,7 @@ class functional(simulation): self.write_functional_stimulus() self.stim.run_sim() - # read DOUT values from SPICE simulation. If the values do not fall within the noise margins, return the error. + # read dout values from SPICE simulation. If the values do not fall within the noise margins, return the error. (success, error) = self.read_stim_results() if not success: return (0, error) @@ -57,37 +85,42 @@ class functional(simulation): return self.check_stim_results() def write_random_memory_sequence(self): - rw_ops = ["noop", "write", "read"] - w_ops = ["noop", "write"] + if self.write_size: + rw_ops = ["noop", "write", "partial_write", "read"] + w_ops = ["noop", "write", "partial_write"] + else: + rw_ops = ["noop", "write", "read"] + w_ops = ["noop", "write"] r_ops = ["noop", "read"] rw_read_din_data = "0"*self.word_size check = 0 - + # First cycle idle - 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) + comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, self.wmask, 0, self.t_current) + self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks) # Write at least once addr = self.gen_addr() word = self.gen_data() - comment = self.gen_cycle_comment("write", word, addr, 0, self.t_current) - self.add_write(comment, addr, word, 0) + comment = self.gen_cycle_comment("write", word, addr, self.wmask, 0, self.t_current) + self.add_write(comment, addr, word, self.wmask, 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. # This will test the viablilty of the transistor sizing in the bitcell. for port in self.all_ports: if port in self.write_ports: - self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) + self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port) else: - comment = self.gen_cycle_comment("read", word, addr, port, self.t_current) - self.add_read_one_port(comment, addr, rw_read_din_data, port) + comment = self.gen_cycle_comment("read", word, addr, self.wmask, port, self.t_current) + self.add_read_one_port(comment, addr, rw_read_din_data, "0"*self.num_wmasks, 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) self.t_current += self.period # Perform a random sequence of writes and reads on random ports, using random addresses and random words + # and random write masks (if applicable) for i in range(self.num_cycles): w_addrs = [] for port in self.all_ports: @@ -101,26 +134,48 @@ class functional(simulation): if op == "noop": addr = "0"*self.addr_size word = "0"*self.word_size - self.add_noop_one_port(addr, word, port) + wmask = "0" * self.num_wmasks + self.add_noop_one_port(addr, word, wmask, port) elif op == "write": addr = self.gen_addr() word = self.gen_data() # two ports cannot write to the same address if addr in w_addrs: - self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) + self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port) else: - comment = self.gen_cycle_comment("write", word, addr, port, self.t_current) - self.add_write_one_port(comment, addr, word, port) + comment = self.gen_cycle_comment("write", word, addr, self.wmask, port, self.t_current) + self.add_write_one_port(comment, addr, word, self.wmask, port) self.stored_words[addr] = word w_addrs.append(addr) + elif op == "partial_write": + # write only to a word that's been written to + (addr,old_word) = self.get_data() + word = self.gen_data() + wmask = self.gen_wmask() + new_word = word + for bit in range(len(wmask)): + # When the write mask's bits are 0, the old data values should appear in the new word + # as to not overwrite the old values + if wmask[bit] == "0": + lower = bit * self.write_size + upper = lower + self.write_size - 1 + new_word = new_word[:lower] + old_word[lower:upper+1] + new_word[upper + 1:] + # two ports cannot write to the same address + if addr in w_addrs: + self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port) + else: + comment = self.gen_cycle_comment("partial_write", word, addr, wmask, port, self.t_current) + self.add_write_one_port(comment, addr, word, wmask, port) + self.stored_words[addr] = new_word + w_addrs.append(addr) else: (addr,word) = random.choice(list(self.stored_words.items())) # cannot read from an address that is currently being written to if addr in w_addrs: - self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) + self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port) else: - comment = self.gen_cycle_comment("read", word, addr, port, self.t_current) - self.add_read_one_port(comment, addr, rw_read_din_data, port) + comment = self.gen_cycle_comment("read", word, addr, self.wmask, port, self.t_current) + self.add_read_one_port(comment, addr, rw_read_din_data, "0"*self.num_wmasks, port) self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check]) check += 1 @@ -128,11 +183,11 @@ class functional(simulation): self.t_current += self.period # Last cycle idle needed to correctly measure the value on the second to last clock edge - 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) + comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, self.wmask, 0, self.t_current) + self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks) def read_stim_results(self): - # Extrat DOUT values from spice timing.lis + # Extrat dout values from spice timing.lis for (word, dout_port, eo_period, check) in self.write_check: sp_read_value = "" for bit in range(self.word_size): @@ -163,23 +218,47 @@ class functional(simulation): self.read_check[i][2]) return(0, error) return(1, "SUCCESS") - + + def gen_wmask(self): + wmask = "" + # generate a random wmask + for bit in range(self.num_wmasks): + rand = random.randint(0, 1) + wmask += str(rand) + # prevent the wmask from having all bits on or off (this is not a partial write) + all_zeroes = True + all_ones = True + for bit in range(self.num_wmasks): + if wmask[bit]=="0": + all_ones = False + elif wmask[bit]=="1": + all_zeroes = False + if all_zeroes: + index = random.randint(0, self.num_wmasks - 1) + wmask = wmask[:index] + "1" + wmask[index + 1:] + elif all_ones: + index = random.randint(0, self.num_wmasks - 1) + wmask = wmask[:index] + "0" + wmask[index + 1:] + # wmask must be reversed since a python list goes right to left and sram bits go left to right. + return wmask[::-1] + + def gen_data(self): """ Generates a random word to write. """ - rand = random.randint(0,(2**self.word_size)-1) - data_bits = self.convert_to_bin(rand,False) + random_value = random.randint(0,(2**self.word_size)-1) + data_bits = self.convert_to_bin(random_value,False) return data_bits - + def gen_addr(self): """ Generates a random address value to write to. """ - rand = random.randint(0,(2**self.addr_size)-1) - addr_bits = self.convert_to_bin(rand,True) - return addr_bits + random_value = random.randint(0,(2**self.addr_size)-1) + addr_bits = self.convert_to_bin(random_value,True) + return addr_bits def get_data(self): """ Gets an available address and corresponding word. """ - # Currently unused but may need later depending on how the functional test develops - addr = random.choice(self.stored_words.keys()) + # Used for write masks since they should be writing to previously written addresses + addr = random.choice(list(self.stored_words.keys())) word = self.stored_words[addr] return (addr,word) @@ -189,17 +268,13 @@ class functional(simulation): if(is_addr): expected_value = self.addr_size else: + expected_value = self.word_size for i in range (expected_value - len(new_value)): new_value = "0" + new_value #print("Binary Conversion: {} to {}".format(value, new_value)) - return new_value - - def create_signal_names(self): - self.addr_name = "A" - self.din_name = "DIN" - self.dout_name = "DOUT" + return new_value def write_functional_stimulus(self): """ Writes SPICE stimulus. """ @@ -209,9 +284,7 @@ class functional(simulation): self.stim = stimuli(self.sf,self.corner) #Write include statements - self.sram_sp_file = "{}sram.sp".format(OPTS.openram_temp) - shutil.copy(self.sp_file, self.sram_sp_file) - self.stim.write_include(self.sram_sp_file) + self.stim.write_include(self.sp_file) #Write Vdd/Gnd statements self.sf.write("\n* Global Power Supplies\n") @@ -219,12 +292,8 @@ class functional(simulation): #Instantiate the SRAM 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=(len(self.all_ports), self.write_ports, self.read_ports), - abits=self.addr_size, - dbits=self.word_size, - sram_name=self.name) + self.stim.inst_model(pins=self.pins, + model_name=self.sram.name) # Add load capacitance to each of the read ports self.sf.write("\n* SRAM output loads\n") @@ -232,9 +301,17 @@ class functional(simulation): for bit in range(self.word_size): sig_name="{0}{1}_{2} ".format(self.dout_name, port, bit) self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(port, bit, sig_name, self.load)) + + # Write important signals to stim file + self.sf.write("\n\n* Important signals for debug\n") + self.sf.write("* bl: {}\n".format(self.bl_name)) + self.sf.write("* br: {}\n".format(self.br_name)) + self.sf.write("* s_en: {}\n".format(self.sen_name)) + self.sf.write("* q: {}\n".format(self.q_name)) + self.sf.write("* qbar: {}\n".format(self.qbar_name)) # Write debug comments to stim file - self.sf.write("\n\n * Sequence of operations\n") + self.sf.write("\n\n* Sequence of operations\n") for comment in self.fn_cycle_comments: self.sf.write("*{}\n".format(comment)) @@ -259,9 +336,20 @@ class functional(simulation): for port in self.readwrite_ports: self.stim.gen_pwl("WEB{}".format(port), self.cycle_times , self.web_values[port], self.period, self.slew, 0.05) + # Generate wmask bits + for port in self.write_ports: + if self.write_size: + self.sf.write("\n* Generation of wmask signals\n") + for bit in range(self.num_wmasks): + sig_name = "WMASK{0}_{1} ".format(port, bit) + # self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, + # self.slew, 0.05) + self.stim.gen_pwl(sig_name, self.cycle_times, self.wmask_values[port][bit], self.period, + self.slew, 0.05) + # Generate CLK signals for port in self.all_ports: - self.stim.gen_pulse(sig_name="{0}{1}".format(tech.spice["clk"], port), + self.stim.gen_pulse(sig_name="{0}{1}".format("clk", port), v1=self.gnd_voltage, v2=self.vdd_voltage, offset=self.period, @@ -269,7 +357,7 @@ class functional(simulation): t_rise=self.slew, t_fall=self.slew) - # Generate DOUT value measurements + # Generate dout value measurements self.sf.write("\n * Generation of dout measurements\n") for (word, dout_port, eo_period, check) in self.write_check: t_intital = eo_period - 0.01*self.period @@ -283,4 +371,117 @@ class functional(simulation): self.stim.write_control(self.cycle_times[-1] + self.period) self.sf.close() + # FIXME: refactor to share with delay.py + def add_graph_exclusions(self): + """Exclude portions of SRAM from timing graph which are not relevant""" + + # other initializations can only be done during analysis when a bit has been selected + # for testing. + self.sram.bank.graph_exclude_precharge() + self.sram.graph_exclude_addr_dff() + self.sram.graph_exclude_data_dff() + self.sram.graph_exclude_ctrl_dffs() + self.sram.bank.bitcell_array.graph_exclude_replica_col_bits() + + # FIXME: refactor to share with delay.py + def create_graph(self): + """Creates timing graph to generate the timing paths for the SRAM output.""" + + self.sram.bank.bitcell_array.init_graph_params() # Removes previous bit exclusions + # Does wordline=0 and column=0 just for debug names + self.sram.bank.bitcell_array.graph_exclude_bits(0, 0) + + # Generate new graph every analysis as edges might change depending on test bit + self.graph = graph_util.timing_graph() + self.sram_spc_name = "X{}".format(self.sram.name) + self.sram.build_graph(self.graph,self.sram_spc_name,self.pins) + # FIXME: refactor to share with delay.py + def set_internal_spice_names(self): + """Sets important names for characterization such as Sense amp enable and internal bit nets.""" + + # For now, only testing these using first read port. + port = self.read_ports[0] + self.graph.get_all_paths('{}{}'.format("clk", port), + '{}{}_{}'.format(self.dout_name, port, 0).lower()) + + self.sen_name = self.get_sen_name(self.graph.all_paths) + debug.info(2,"s_en name = {}".format(self.sen_name)) + + self.bl_name,self.br_name = self.get_bl_name(self.graph.all_paths, port) + debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name)) + + self.q_name,self.qbar_name = self.get_bit_name() + debug.info(2,"q name={}\nqbar name={}".format(self.q_name,self.qbar_name)) + + def get_bit_name(self): + """ Get a bit cell name """ + (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0) + storage_names = cell_inst.mod.get_storage_net_names() + debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes" + "supported for characterization. Storage nets={}").format(storage_names)) + q_name = cell_name+'.'+str(storage_names[0]) + qbar_name = cell_name+'.'+str(storage_names[1]) + + return (q_name,qbar_name) + + # FIXME: refactor to share with delay.py + def get_sen_name(self, paths): + """ + Gets the signal name associated with the sense amp enable from input paths. + Only expects a single path to contain the sen signal name. + """ + + sa_mods = factory.get_mods(OPTS.sense_amp) + # Any sense amp instantiated should be identical, any change to that + # will require some identification to determine the mod desired. + debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.") + enable_name = sa_mods[0].get_enable_name() + sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0]) + return sen_name + + # FIXME: refactor to share with delay.py + def get_bl_name(self, paths, port): + """Gets the signal name associated with the bitlines in the bank.""" + + cell_mod = factory.create(module_type=OPTS.bitcell) + cell_bl = cell_mod.get_bl_name(port) + cell_br = cell_mod.get_br_name(port) + + bl_found = False + # Only a single path should contain a single s_en name. Anything else is an error. + bl_names = [] + exclude_set = self.get_bl_name_search_exclusions() + for int_net in [cell_bl, cell_br]: + bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set)) + + return bl_names[0], bl_names[1] + + def get_bl_name_search_exclusions(self): + """Gets the mods as a set which should be excluded while searching for name.""" + + # Exclude the RBL as it contains bitcells which are not in the main bitcell array + # so it makes the search awkward + return set(factory.get_mods(OPTS.replica_bitline)) + + def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None): + """ + Finds a single alias for the int_net in given paths. + More or less hits cause an error + """ + + net_found = False + for path in paths: + aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, int_net, mod, exclusion_set) + if net_found and len(aliases) >= 1: + debug.error('Found multiple paths with {} net.'.format(int_net),1) + elif len(aliases) > 1: + debug.error('Found multiple {} nets in single path.'.format(int_net),1) + elif not net_found and len(aliases) == 1: + path_net_name = aliases[0] + net_found = True + if not net_found: + debug.error("Could not find {} net in timing paths.".format(int_net),1) + + return path_net_name + diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index d24c931d..07a753a7 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import os,sys,re import debug import math @@ -102,6 +109,8 @@ class lib: #set the read and write port as inputs. self.write_data_bus(port) self.write_addr_bus(port) + if self.sram.write_size: + self.write_wmask_bus(port) self.write_control_pins(port) #need to split this into sram and port control signals self.write_clk_timing_power(port) @@ -141,9 +150,9 @@ class lib: self.lib.write(" area : {};\n\n".format(self.sram.width * self.sram.height)) #Build string of all control signals. - control_str = 'CSb0' #assume at least 1 port + control_str = 'csb0' #assume at least 1 port for i in range(1, self.total_port_num): - control_str += ' & CSb{0}'.format(i) + control_str += ' & csb{0}'.format(i) # Leakage is included in dynamic when macro is enabled self.lib.write(" leakage_power () {\n") @@ -154,13 +163,17 @@ class lib: def write_units(self): - """ Adds default units for time, voltage, current,...""" - + """ Adds default units for time, voltage, current,... + Valid values are 1mV, 10mV, 100mV, and 1V. + For time: Valid values are 1ps, 10ps, 100ps, and 1ns. + For power: Valid values are 1mW, 100uW (for 100mW), 10uW (for 10mW), + 1uW (for 1mW), 100nW, 10nW, 1nW, 100pW, 10pW, and 1pW. + """ self.lib.write(" time_unit : \"1ns\" ;\n") - self.lib.write(" voltage_unit : \"1v\" ;\n") + self.lib.write(" voltage_unit : \"1V\" ;\n") self.lib.write(" current_unit : \"1mA\" ;\n") self.lib.write(" resistance_unit : \"1kohm\" ;\n") - self.lib.write(" capacitive_load_unit(1 ,fF) ;\n") + self.lib.write(" capacitive_load_unit(1, pF) ;\n") self.lib.write(" leakage_power_unit : \"1mW\" ;\n") self.lib.write(" pulling_resistance_unit :\"1kohm\" ;\n") self.lib.write(" operating_conditions(OC){\n") @@ -232,7 +245,9 @@ class lib: self.lib.write(" variable_1 : input_net_transition;\n") self.lib.write(" variable_2 : total_output_net_capacitance;\n") self.write_index(1,self.slews) - self.write_index(2,self.loads) + # Dividing by 1000 to all cap values since output of .sp is in fF, + # and it needs to be in pF for Innovus. + self.write_index(2,self.loads/1000) self.lib.write(" }\n\n") CONS = ["CONSTRAINT_TABLE"] @@ -265,10 +280,10 @@ class lib: # self.lib.write(" }\n\n") def write_bus(self): - """ Adds format of DATA and ADDR bus.""" + """ Adds format of data and addr bus.""" self.lib.write("\n\n") - self.lib.write(" type (DATA){\n") + self.lib.write(" type (data){\n") self.lib.write(" base_type : array;\n") self.lib.write(" data_type : bit;\n") self.lib.write(" bit_width : {0};\n".format(self.sram.word_size)) @@ -276,7 +291,7 @@ class lib: self.lib.write(" bit_to : {0};\n".format(self.sram.word_size - 1)) self.lib.write(" }\n\n") - self.lib.write(" type (ADDR){\n") + self.lib.write(" type (addr){\n") self.lib.write(" base_type : array;\n") self.lib.write(" data_type : bit;\n") self.lib.write(" bit_width : {0};\n".format(self.sram.addr_size)) @@ -284,6 +299,15 @@ class lib: self.lib.write(" bit_to : {0};\n".format(self.sram.addr_size - 1)) self.lib.write(" }\n\n") + if self.sram.write_size: + self.lib.write(" type (wmask){\n") + self.lib.write(" base_type : array;\n") + self.lib.write(" data_type : bit;\n") + self.lib.write(" bit_width : {0};\n".format(self.sram.num_wmasks)) + self.lib.write(" bit_from : 0;\n") + self.lib.write(" bit_to : {0};\n".format(self.sram.num_wmasks - 1)) + self.lib.write(" }\n\n") + def write_FF_setuphold(self, port): """ Adds Setup and Hold timing results""" @@ -316,18 +340,18 @@ class lib: def write_data_bus_output(self, read_port): """ Adds data bus timing results.""" - self.lib.write(" bus(DOUT{0}){{\n".format(read_port)) - self.lib.write(" bus_type : DATA; \n") + self.lib.write(" bus(dout{0}){{\n".format(read_port)) + self.lib.write(" bus_type : data; \n") self.lib.write(" direction : output; \n") # This is conservative, but limit to range that we characterized. - self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads))) - self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads))) + self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads)/1000)) + self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads)/1000)) self.lib.write(" memory_read(){ \n") - self.lib.write(" address : ADDR{0}; \n".format(read_port)) + self.lib.write(" address : addr{0}; \n".format(read_port)) self.lib.write(" }\n") - self.lib.write(" pin(DOUT{0}[{1}:0]){{\n".format(read_port,self.sram.word_size-1)) + self.lib.write(" pin(dout{0}[{1}:0]){{\n".format(read_port,self.sram.word_size-1)) self.lib.write(" timing(){ \n") self.lib.write(" timing_sense : non_unate; \n") self.lib.write(" related_pin : \"clk{0}\"; \n".format(read_port)) @@ -349,18 +373,18 @@ class lib: self.lib.write(" }\n\n") # bus def write_data_bus_input(self, write_port): - """ Adds DIN 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") + self.lib.write(" bus(din{0}){{\n".format(write_port)) + self.lib.write(" bus_type : data; \n") self.lib.write(" direction : input; \n") # This is conservative, but limit to range that we characterized. - self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) + self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"]/1000)) self.lib.write(" memory_write(){ \n") - self.lib.write(" address : ADDR{0}; \n".format(write_port)) + self.lib.write(" address : addr{0}; \n".format(write_port)) self.lib.write(" clocked_on : clk{0}; \n".format(write_port)) self.lib.write(" }\n") - self.lib.write(" pin(DIN{0}[{1}:0]){{\n".format(write_port,self.sram.word_size-1)) + self.lib.write(" pin(din{0}[{1}:0]){{\n".format(write_port,self.sram.word_size-1)) self.write_FF_setuphold(write_port) self.lib.write(" }\n") # pin self.lib.write(" }\n") #bus @@ -375,31 +399,45 @@ class lib: def write_addr_bus(self, port): """ Adds addr bus timing results.""" - self.lib.write(" bus(ADDR{0}){{\n".format(port)) - self.lib.write(" bus_type : ADDR; \n") + self.lib.write(" bus(addr{0}){{\n".format(port)) + self.lib.write(" bus_type : addr; \n") self.lib.write(" direction : input; \n") - self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) + self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"]/1000)) self.lib.write(" max_transition : {0};\n".format(self.slews[-1])) - self.lib.write(" pin(ADDR{0}[{1}:0])".format(port,self.sram.addr_size-1)) + self.lib.write(" pin(addr{0}[{1}:0])".format(port,self.sram.addr_size-1)) self.lib.write("{\n") self.write_FF_setuphold(port) self.lib.write(" }\n") self.lib.write(" }\n\n") + def write_wmask_bus(self, port): + """ Adds addr bus timing results.""" + + self.lib.write(" bus(wmask{0}){{\n".format(port)) + self.lib.write(" bus_type : wmask; \n") + self.lib.write(" direction : input; \n") + self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"] / 1000)) + self.lib.write(" max_transition : {0};\n".format(self.slews[-1])) + self.lib.write(" pin(wmask{0}[{1}:0])".format(port, self.sram.num_wmasks - 1)) + self.lib.write("{\n") + + self.write_FF_setuphold(port) + self.lib.write(" }\n") + self.lib.write(" }\n\n") def write_control_pins(self, port): """ Adds control pins timing results.""" #The control pins are still to be determined. This is a placeholder for what could be. - ctrl_pin_names = ["CSb{0}".format(port)] + ctrl_pin_names = ["csb{0}".format(port)] if port in self.readwrite_ports: - ctrl_pin_names.append("WEb{0}".format(port)) + ctrl_pin_names.append("web{0}".format(port)) for i in ctrl_pin_names: self.lib.write(" pin({0})".format(i)) self.lib.write("{\n") self.lib.write(" direction : input; \n") - self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"])) + self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"]/1000)) self.write_FF_setuphold(port) self.lib.write(" }\n\n") @@ -410,7 +448,7 @@ class lib: 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"])) + self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"]/1000)) self.add_clk_control_power(port) @@ -445,10 +483,10 @@ class lib: if port in self.write_ports: if port in self.read_ports: - web_name = " & !WEb{0}".format(port) + 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{0}{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") @@ -459,10 +497,10 @@ class lib: if port in self.read_ports: if port in self.write_ports: - web_name = " & WEb{0}".format(port) + 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{0}{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") @@ -473,7 +511,7 @@ class lib: # Have 0 internal power when disabled, this will be represented as leakage power. self.lib.write(" internal_power(){\n") - self.lib.write(" when : \"CSb{0}\"; \n".format(port)) + self.lib.write(" when : \"csb{0}\"; \n".format(port)) self.lib.write(" rise_power(scalar){\n") self.lib.write(" values(\"0\");\n") self.lib.write(" }\n") @@ -564,10 +602,10 @@ class lib: datasheet.write(str(self.sram.width * self.sram.height)+',') # write timing information for all ports for port in self.all_ports: - #DIN timings + #din timings if port in self.write_ports: datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( - "DIN{1}[{0}:0]".format(self.sram.word_size - 1, port), + "din{1}[{0}:0]".format(self.sram.word_size - 1, port), min(list(map(round_time,self.times["setup_times_LH"]))), max(list(map(round_time,self.times["setup_times_LH"]))), @@ -583,10 +621,10 @@ class lib: )) for port in self.all_ports: - #DOUT timing + #dout timing if port in self.read_ports: datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( - "DOUT{1}[{0}:0]".format(self.sram.word_size - 1, port), + "dout{1}[{0}:0]".format(self.sram.word_size - 1, port), min(list(map(round_time,self.char_port_results[port]["delay_lh"]))), max(list(map(round_time,self.char_port_results[port]["delay_lh"]))), @@ -603,9 +641,9 @@ class lib: )) for port in self.all_ports: - #CSb timings + #csb timings datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( - "CSb{0}".format(port), + "csb{0}".format(port), min(list(map(round_time,self.times["setup_times_LH"]))), max(list(map(round_time,self.times["setup_times_LH"]))), @@ -621,9 +659,9 @@ class lib: )) for port in self.all_ports: - #ADDR timings + #addr timings datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( - "ADDR{1}[{0}:0]".format(self.sram.addr_size - 1, port), + "addr{1}[{0}:0]".format(self.sram.addr_size - 1, port), min(list(map(round_time,self.times["setup_times_LH"]))), max(list(map(round_time,self.times["setup_times_LH"]))), @@ -642,9 +680,9 @@ class lib: for port in self.all_ports: if port in self.readwrite_ports: - #WEb timings + #web timings datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( - "WEb{0}".format(port), + "web{0}".format(port), min(list(map(round_time,self.times["setup_times_LH"]))), max(list(map(round_time,self.times["setup_times_LH"]))), @@ -666,8 +704,8 @@ class lib: # write dynamic power usage if port in self.read_ports: - web_name = " & !WEb{0}".format(port) - name = "!CSb{0} & clk{0}{1}".format(port, web_name) + web_name = " & !web{0}".format(port) + name = "!csb{0} & clk{0}{1}".format(port, web_name) read_write = 'Read' datasheet.write("{0},{1},{2},{3},".format( @@ -679,8 +717,8 @@ class lib: )) if port in self.write_ports: - web_name = " & WEb{0}".format(port) - name = "!CSb{0} & !clk{0}{1}".format(port, web_name) + web_name = " & web{0}".format(port) + name = "!csb{0} & !clk{0}{1}".format(port, web_name) read_write = 'Write' datasheet.write("{0},{1},{2},{3},".format( @@ -692,9 +730,9 @@ class lib: )) # write leakage power - control_str = 'CSb0' + control_str = 'csb0' for i in range(1, self.total_port_num): - control_str += ' & CSb{0}'.format(i) + control_str += ' & csb{0}'.format(i) datasheet.write("{0},{1},{2},".format('leak', control_str, self.char_sram_results["leakage_power"])) diff --git a/compiler/characterizer/logical_effort.py b/compiler/characterizer/logical_effort.py index c80e69a2..42ac8861 100644 --- a/compiler/characterizer/logical_effort.py +++ b/compiler/characterizer/logical_effort.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from tech import drc, parameter, spice @@ -9,51 +16,69 @@ class logical_effort(): beta = parameter["beta"] min_inv_cin = 1+beta pinv=parameter["min_inv_para_delay"] + tau = parameter['le_tau'] def __init__(self, name, size, cin, cout, parasitic, out_is_rise=True): self.name = name self.cin = cin self.cout = cout self.logical_effort = (self.cin/size)/logical_effort.min_inv_cin - self.eletrical_effort = self.cout/self.cin + self.electrical_effort = self.cout/self.cin self.parasitic_scale = parasitic self.is_rise = out_is_rise def __str__(self): return "Name={}, g={}, h={}, p={}*pinv, rise_delay={}".format(self.name, self.logical_effort, - self.eletrical_effort, + self.electrical_effort, self.parasitic_scale, self.is_rise ) def get_stage_effort(self): - return self.logical_effort*self.eletrical_effort + return self.logical_effort*self.electrical_effort - def get_parasitic_delay(self, pinv): - return pinv * self.parasitic_scale + def get_parasitic_delay(self): + return logical_effort.pinv*self.parasitic_scale - def get_stage_delay(self, pinv): - return self.get_stage_effort()+self.get_parasitic_delay(pinv) + def get_stage_delay(self): + return self.get_stage_effort()+self.get_parasitic_delay() -def calculate_delays(stage_effort_list, pinv): + def get_absolute_delay(self): + return logical_effort.tau*self.get_stage_delay() + +def calculate_delays(stage_effort_list): """Convert stage effort objects to list of delay values""" - return [stage.get_stage_delay(pinv) for stage in stage_effort_list] + return [stage.get_stage_delay() for stage in stage_effort_list] -def calculate_relative_delay(stage_effort_list, pinv=parameter["min_inv_para_delay"]): +def calculate_relative_delay(stage_effort_list): """Calculates the total delay of a given delay path made of a list of logical effort objects.""" - total_rise_delay, total_fall_delay = calculate_relative_rise_fall_delays(stage_effort_list, pinv) + total_rise_delay, total_fall_delay = calculate_relative_rise_fall_delays(stage_effort_list) return total_rise_delay + total_fall_delay + +def calculate_absolute_delay(stage_effort_list): + """Calculates the total delay of a given delay path made of a list of logical effort objects.""" + total_delay = 0 + for stage in stage_effort_list: + total_delay+=stage.get_absolute_delay() + return total_delay -def calculate_relative_rise_fall_delays(stage_effort_list, pinv=parameter["min_inv_para_delay"]): +def calculate_relative_rise_fall_delays(stage_effort_list): """Calculates the rise/fall delays of a given delay path made of a list of logical effort objects.""" debug.info(2, "Calculating rise/fall relative delays") total_rise_delay, total_fall_delay = 0,0 for stage in stage_effort_list: debug.info(2, stage) if stage.is_rise: - total_rise_delay += stage.get_stage_delay(pinv) + total_rise_delay += stage.get_stage_delay() else: - total_fall_delay += stage.get_stage_delay(pinv) + total_fall_delay += stage.get_stage_delay() return total_rise_delay, total_fall_delay - \ No newline at end of file + +def convert_farad_to_relative_c(c_farad): + """Converts capacitance in Femto-Farads to relative capacitance.""" + return c_farad*parameter['cap_relative_per_ff'] + +def convert_relative_c_to_farad(c_relative): + """Converts capacitance in logical effort relative units to Femto-Farads.""" + return c_relative/parameter['cap_relative_per_ff'] \ No newline at end of file diff --git a/compiler/characterizer/measurements.py b/compiler/characterizer/measurements.py index e3d16584..54f9973f 100644 --- a/compiler/characterizer/measurements.py +++ b/compiler/characterizer/measurements.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from tech import drc, parameter, spice from abc import ABC, abstractmethod @@ -6,10 +13,11 @@ from .charutils import * class spice_measurement(ABC): """Base class for spice stimulus measurements.""" - def __init__(self, measure_name, measure_scale=None): + def __init__(self, measure_name, measure_scale=None, has_port=True): #Names must be unique for correct spice simulation, but not enforced here. self.name = measure_name self.measure_scale = measure_scale + self.has_port = has_port #Needed for error checking #Some meta values used externally. variables are added here for consistency accross the objects self.meta_str = None self.meta_add_delay = False @@ -28,18 +36,29 @@ class spice_measurement(ABC): measure_vals = self.get_measure_values(*input_tuple) measure_func(stim_obj, *measure_vals) - def retrieve_measure(self, port=""): - value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port)) + def retrieve_measure(self, port=None): + self.port_error_check(port) + if port != None: + value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port)) + else: + value = parse_spice_list("timing", "{0}".format(self.name.lower())) if type(value)!=float or self.measure_scale == None: return value else: return value*self.measure_scale - + + def port_error_check(self, port): + if self.has_port and port == None: + debug.error("Cannot retrieve measurement, port input was expected.",1) + elif not self.has_port and port != None: + debug.error("Unexpected port input received during measure retrieval.",1) + class delay_measure(spice_measurement): """Generates a spice measurement for the delay of 50%-to-50% points of two signals.""" - def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd=0.5, targ_vdd=0.5, measure_scale=None): - spice_measurement.__init__(self, measure_name, measure_scale) + def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str,\ + trig_vdd=0.5, targ_vdd=0.5, measure_scale=None, has_port=True): + spice_measurement.__init__(self, measure_name, measure_scale, has_port) self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd) def get_measure_function(self): @@ -49,10 +68,8 @@ class delay_measure(spice_measurement): """Set the constants for this measurement: signal names, directions, and trigger scales""" self.trig_dir_str = trig_dir_str self.targ_dir_str = targ_dir_str - self.trig_val_of_vdd = trig_vdd self.targ_val_of_vdd = targ_vdd - self.trig_name_no_port = trig_name self.targ_name_no_port = targ_name @@ -60,9 +77,10 @@ class delay_measure(spice_measurement): def get_measure_values(self, trig_td, targ_td, vdd_voltage, port=None): """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" + self.port_error_check(port) trig_val = self.trig_val_of_vdd * vdd_voltage targ_val = self.targ_val_of_vdd * vdd_voltage - + if port != None: #For dictionary indexing reasons, the name is formatted differently than the signals meas_name = "{}{}".format(self.name, port) @@ -72,13 +90,12 @@ class delay_measure(spice_measurement): meas_name = self.name trig_name = self.trig_name_no_port targ_name = self.targ_name_no_port - return (meas_name,trig_name,targ_name,trig_val,targ_val,self.trig_dir_str,self.targ_dir_str,trig_td,targ_td) class slew_measure(delay_measure): - def __init__(self, measure_name, signal_name, slew_dir_str, measure_scale=None): - spice_measurement.__init__(self, measure_name, measure_scale) + def __init__(self, measure_name, signal_name, slew_dir_str, measure_scale=None, has_port=True): + spice_measurement.__init__(self, measure_name, measure_scale, has_port) self.set_meas_constants(signal_name, slew_dir_str) def set_meas_constants(self, signal_name, slew_dir_str): @@ -94,7 +111,6 @@ class slew_measure(delay_measure): self.targ_val_of_vdd = 0.1 else: debug.error("Unrecognised slew measurement direction={}".format(slew_dir_str),1) - self.trig_name_no_port = signal_name self.targ_name_no_port = signal_name @@ -103,8 +119,8 @@ class slew_measure(delay_measure): class power_measure(spice_measurement): """Generates a spice measurement for the average power between two time points.""" - def __init__(self, measure_name, power_type="", measure_scale=None): - spice_measurement.__init__(self, measure_name, measure_scale) + def __init__(self, measure_name, power_type="", measure_scale=None, has_port=True): + spice_measurement.__init__(self, measure_name, measure_scale, has_port) self.set_meas_constants(power_type) def get_measure_function(self): @@ -117,6 +133,7 @@ class power_measure(spice_measurement): def get_measure_values(self, t_initial, t_final, port=None): """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" + self.port_error_check(port) if port != None: meas_name = "{}{}".format(self.name, port) else: @@ -126,8 +143,8 @@ class power_measure(spice_measurement): class voltage_when_measure(spice_measurement): """Generates a spice measurement to measure the voltage of a signal based on the voltage of another.""" - def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, trig_vdd, measure_scale=None): - spice_measurement.__init__(self, measure_name, measure_scale) + def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, trig_vdd, measure_scale=None, has_port=True): + spice_measurement.__init__(self, measure_name, measure_scale, has_port) self.set_meas_constants(trig_name, targ_name, trig_dir_str, trig_vdd) def get_measure_function(self): @@ -137,13 +154,12 @@ class voltage_when_measure(spice_measurement): """Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)""" self.trig_dir_str = trig_dir_str self.trig_val_of_vdd = trig_vdd - self.trig_name_no_port = trig_name self.targ_name_no_port = targ_name def get_measure_values(self, trig_td, vdd_voltage, port=None): """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" - + self.port_error_check(port) if port != None: #For dictionary indexing reasons, the name is formatted differently than the signals meas_name = "{}{}".format(self.name, port) @@ -153,7 +169,33 @@ class voltage_when_measure(spice_measurement): meas_name = self.name trig_name = self.trig_name_no_port targ_name = self.targ_name_no_port - - trig_voltage = self.trig_val_of_vdd*vdd_voltage - - return (meas_name,trig_name,targ_name,trig_voltage,self.trig_dir_str,trig_td) \ No newline at end of file + trig_voltage = self.trig_val_of_vdd*vdd_voltage + return (meas_name,trig_name,targ_name,trig_voltage,self.trig_dir_str,trig_td) + +class voltage_at_measure(spice_measurement): + """Generates a spice measurement to measure the voltage at a specific time. + The time is considered variant with different periods.""" + + def __init__(self, measure_name, targ_name, measure_scale=None, has_port=True): + spice_measurement.__init__(self, measure_name, measure_scale, has_port) + self.set_meas_constants(targ_name) + + def get_measure_function(self): + return stimuli.gen_meas_find_voltage_at_time + + def set_meas_constants(self, targ_name): + """Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)""" + self.targ_name_no_port = targ_name + + def get_measure_values(self, time_at, port=None): + """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" + self.port_error_check(port) + if port != None: + #For dictionary indexing reasons, the name is formatted differently than the signals + meas_name = "{}{}".format(self.name, port) + targ_name = self.targ_name_no_port.format(port) + else: + meas_name = self.name + targ_name = self.targ_name_no_port + return (meas_name,targ_name,time_at) + \ No newline at end of file diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index 12bc5d58..b35c3c47 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import sys,re,shutil import debug import tech @@ -18,69 +25,113 @@ class model_check(delay): """ - def __init__(self, sram, spfile, corner): + def __init__(self, sram, spfile, corner, custom_delaychain=False): delay.__init__(self,sram,spfile,corner) self.period = tech.spice["feasible_period"] self.create_data_names() + self.custom_delaychain=custom_delaychain def create_data_names(self): self.wl_meas_name, self.wl_model_name = "wl_measures", "wl_model" self.sae_meas_name, self.sae_model_name = "sae_measures", "sae_model" self.wl_slew_name, self.sae_slew_name = "wl_slews", "sae_slews" + self.bl_meas_name, self.bl_slew_name = "bl_measures", "bl_slews" + self.power_name = "total_power" - def create_measurement_names(self): + def create_measurement_names(self, port): """Create measurement names. The names themselves currently define the type of measurement""" #Create delay measurement names wl_en_driver_delay_names = ["delay_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] wl_driver_delay_names = ["delay_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] sen_driver_delay_names = ["delay_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] - dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] + if self.custom_delaychain: + dc_delay_names = ['delay_dc_out_final'] + else: + dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] self.wl_delay_meas_names = wl_en_driver_delay_names+["delay_wl_en", "delay_wl_bar"]+wl_driver_delay_names+["delay_wl"] - self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names + if port not in self.sram.readonly_ports: + self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names + else: + self.rbl_delay_meas_names = ["delay_gated_clk_nand"]+dc_delay_names self.sae_delay_meas_names = ["delay_pre_sen"]+sen_driver_delay_names+["delay_sen"] + # if self.custom_delaychain: + # self.delay_chain_indices = (len(self.rbl_delay_meas_names), len(self.rbl_delay_meas_names)+1) + # else: self.delay_chain_indices = (len(self.rbl_delay_meas_names)-len(dc_delay_names), len(self.rbl_delay_meas_names)) #Create slew measurement names wl_en_driver_slew_names = ["slew_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] wl_driver_slew_names = ["slew_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] sen_driver_slew_names = ["slew_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] - dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] + if self.custom_delaychain: + dc_slew_names = ['slew_dc_out_final'] + else: + dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"]+wl_en_driver_slew_names+["slew_wl_en", "slew_wl_bar"]+wl_driver_slew_names+["slew_wl"] - self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names + if port not in self.sram.readonly_ports: + self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names + else: + self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar"]+dc_slew_names self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen"]+sen_driver_slew_names+["slew_sen"] - def create_signal_names(self): + self.bitline_meas_names = ["delay_wl_to_bl", "delay_bl_to_dout"] + self.power_meas_names = ['read0_power'] + + def create_signal_names(self, port): """Creates list of the signal names used in the spice file along the wl and sen paths. Names are re-harded coded here; i.e. the names are hardcoded in most of OpenRAM and are replicated here. """ delay.create_signal_names(self) #Signal names are all hardcoded, need to update to make it work for probe address and different configurations. - wl_en_driver_signals = ["Xsram.Xcontrol0.Xbuf_wl_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] - wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver0.Xwl_driver_inv{}.Zb{}_int".format(self.wordline_row, stage) for stage in range(1,self.get_num_wl_driver_stages())] - sen_driver_signals = ["Xsram.Xcontrol0.Xbuf_s_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] - delay_chain_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_{}".format(stage) for stage in range(1,self.get_num_delay_stages())] - - self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar"]+\ + wl_en_driver_signals = ["Xsram.Xcontrol{}.Xbuf_wl_en.Zb{}_int".format('{}', stage) for stage in range(1,self.get_num_wl_en_driver_stages())] + wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver{}.Xwl_driver_inv{}.Zb{}_int".format('{}', self.wordline_row, stage) for stage in range(1,self.get_num_wl_driver_stages())] + sen_driver_signals = ["Xsram.Xcontrol{}.Xbuf_s_en.Zb{}_int".format('{}',stage) for stage in range(1,self.get_num_sen_driver_stages())] + if self.custom_delaychain: + delay_chain_signal_names = [] + else: + delay_chain_signal_names = ["Xsram.Xcontrol{}.Xreplica_bitline.Xdelay_chain.dout_{}".format('{}', stage) for stage in range(1,self.get_num_delay_stages())] + if len(self.sram.all_ports) > 1: + port_format = '{}' + else: + port_format = '' + self.wl_signal_names = ["Xsram.Xcontrol{}.gated_clk_bar".format('{}')]+\ wl_en_driver_signals+\ - ["Xsram.wl_en0", "Xsram.Xbank0.Xwordline_driver0.wl_bar_{}".format(self.wordline_row)]+\ + ["Xsram.wl_en{}".format('{}'), "Xsram.Xbank0.Xwordline_driver{}.wl_bar_{}".format('{}',self.wordline_row)]+\ wl_driver_signals+\ - ["Xsram.Xbank0.wl_{}".format(self.wordline_row)] - pre_delay_chain_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in"] + ["Xsram.Xbank0.wl{}_{}".format(port_format, self.wordline_row)] + pre_delay_chain_names = ["Xsram.Xcontrol{}.gated_clk_bar".format('{}')] + if port not in self.sram.readonly_ports: + pre_delay_chain_names+= ["Xsram.Xcontrol{}.Xand2_rbl_in.zb_int".format('{}'), "Xsram.Xcontrol{}.rbl_in".format('{}')] + self.rbl_en_signal_names = pre_delay_chain_names+\ delay_chain_signal_names+\ - ["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] - self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en"]+\ + ["Xsram.Xcontrol{}.Xreplica_bitline.delayed_en".format('{}')] + + + self.sae_signal_names = ["Xsram.Xcontrol{}.Xreplica_bitline.bl0_0".format('{}'), "Xsram.Xcontrol{}.pre_s_en".format('{}')]+\ sen_driver_signals+\ - ["Xsram.s_en0"] + ["Xsram.s_en{}".format('{}')] + + dout_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit + self.bl_signal_names = ["Xsram.Xbank0.wl{}_{}".format(port_format, self.wordline_row),\ + "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column),\ + dout_name] def create_measurement_objects(self): """Create the measurements used for read and write ports""" - self.create_wordline_measurement_objects() - self.create_sae_measurement_objects() - self.all_measures = self.wl_meas_objs+self.sae_meas_objs + self.create_wordline_meas_objs() + self.create_sae_meas_objs() + self.create_bl_meas_objs() + self.create_power_meas_objs() + self.all_measures = self.wl_meas_objs+self.sae_meas_objs+self.bl_meas_objs+self.power_meas_objs - def create_wordline_measurement_objects(self): + def create_power_meas_objs(self): + """Create power measurement object. Only one.""" + self.power_meas_objs = [] + self.power_meas_objs.append(power_measure(self.power_meas_names[0], "FALL", measure_scale=1e3)) + + def create_wordline_meas_objs(self): """Create the measurements to measure the wordline path from the gated_clk_bar signal""" self.wl_meas_objs = [] trig_dir = "RISE" @@ -102,7 +153,19 @@ class model_check(delay): targ_dir = temp_dir self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[-1], self.wl_signal_names[-1], trig_dir, measure_scale=1e9)) - def create_sae_measurement_objects(self): + def create_bl_meas_objs(self): + """Create the measurements to measure the bitline to dout, static stages""" + #Bitline has slightly different measurements, objects appends hardcoded. + self.bl_meas_objs = [] + trig_dir, targ_dir = "RISE", "FALL" #Only check read 0 + self.bl_meas_objs.append(delay_measure(self.bitline_meas_names[0], + self.bl_signal_names[0], + self.bl_signal_names[-1], + trig_dir, + targ_dir, + measure_scale=1e9)) + + def create_sae_meas_objs(self): """Create the measurements to measure the sense amp enable path from the gated_clk_bar signal. The RBL splits this path into two.""" self.sae_meas_objs = [] @@ -123,6 +186,13 @@ class model_check(delay): temp_dir = trig_dir trig_dir = targ_dir targ_dir = temp_dir + if self.custom_delaychain: #Hack for custom delay chains + self.sae_meas_objs[-2] = delay_measure(self.rbl_delay_meas_names[-1], + self.rbl_en_signal_names[-2], + self.rbl_en_signal_names[-1], + "RISE", + "RISE", + measure_scale=1e9) self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[-1], self.rbl_en_signal_names[-1], trig_dir, @@ -131,7 +201,6 @@ class model_check(delay): #Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL. trig_dir = "FALL" targ_dir = "RISE" - #Add measurements from gated_clk_bar to RBL for i in range(1, len(self.sae_signal_names)): self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1], self.sae_signal_names[i-1], @@ -169,10 +238,22 @@ class model_check(delay): or port to port (time delays)""" #Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port #Assuming only read 0 for now - if not (type(measure_obj) is delay_measure or type(measure_obj) is slew_measure): + debug.info(3,"Power measurement={}".format(measure_obj)) + if (type(measure_obj) is delay_measure or type(measure_obj) is slew_measure): + meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2 + return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) + elif type(measure_obj) is power_measure: + return self.get_power_measure_variants(port, measure_obj, "read") + else: debug.error("Measurement not recognized by the model checker.",1) - meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2 - return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) + + def get_power_measure_variants(self, port, power_obj, operation): + """Get the measurement values that can either vary port to port (time delays)""" + #Return value is intended to match the power measure format: t_initial, t_final, port + t_initial = self.cycle_times[self.measure_cycles[port]["read0"]] + t_final = self.cycle_times[self.measure_cycles[port]["read0"]+1] + + return (t_initial, t_final, port) def write_measures_read_port(self, port): """ @@ -186,7 +267,8 @@ class model_check(delay): def get_measurement_values(self, meas_objs, port): """Gets the delays and slews from a specified port from the spice output file and returns them as lists.""" delay_meas_list = [] - slew_meas_list = [] + slew_meas_list = [] + power_meas_list=[] for measure in meas_objs: measure_value = measure.retrieve_measure(port=port) if type(measure_value) != float: @@ -195,9 +277,11 @@ class model_check(delay): delay_meas_list.append(measure_value) elif type(measure)is slew_measure: slew_meas_list.append(measure_value) + elif type(measure)is power_measure: + power_meas_list.append(measure_value) else: debug.error("Measurement object not recognized.",1) - return delay_meas_list, slew_meas_list + return delay_meas_list, slew_meas_list,power_meas_list def run_delay_simulation(self): """ @@ -213,6 +297,9 @@ class model_check(delay): wl_slew_result = [[] for i in self.all_ports] sae_delay_result = [[] for i in self.all_ports] sae_slew_result = [[] for i in self.all_ports] + bl_delay_result = [[] for i in self.all_ports] + bl_slew_result = [[] for i in self.all_ports] + power_result = [[] for i in self.all_ports] # Checking from not data_value to data_value self.write_delay_stimulus() @@ -221,9 +308,11 @@ class model_check(delay): #Retrieve the results from the output file for port in self.targ_read_ports: #Parse and check the voltage measurements - wl_delay_result[port], wl_slew_result[port] = self.get_measurement_values(self.wl_meas_objs, port) - sae_delay_result[port], sae_slew_result[port] = self.get_measurement_values(self.sae_meas_objs, port) - return (True,wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result) + wl_delay_result[port], wl_slew_result[port],_ = self.get_measurement_values(self.wl_meas_objs, port) + sae_delay_result[port], sae_slew_result[port],_ = self.get_measurement_values(self.sae_meas_objs, port) + bl_delay_result[port], bl_slew_result[port],_ = self.get_measurement_values(self.bl_meas_objs, port) + _,__,power_result[port] = self.get_measurement_values(self.power_meas_objs, port) + return (True,wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result, bl_delay_result, bl_slew_result, power_result) def get_model_delays(self, port): """Get model delays based on port. Currently assumes single RW port.""" @@ -292,37 +381,43 @@ class model_check(delay): errors = self.calculate_error_l2_norm(scaled_meas, scaled_model) debug.info(1, "Errors:\n{}\n".format(errors)) - def analyze(self, probe_address, probe_data, slews, loads): + def analyze(self, probe_address, probe_data, slews, loads, port): """Measures entire delay path along the wordline and sense amp enable and compare it to the model delays.""" self.load=max(loads) self.slew=max(slews) self.set_probe(probe_address, probe_data) - self.create_signal_names() - self.create_measurement_names() + self.create_signal_names(port) + self.create_measurement_names(port) self.create_measurement_objects() data_dict = {} read_port = self.read_ports[0] #only test the first read port + read_port = port self.targ_read_ports = [read_port] self.targ_write_ports = [self.write_ports[0]] debug.info(1,"Model test: corner {}".format(self.corner)) - (success, wl_delays, sae_delays, wl_slews, sae_slews)=self.run_delay_simulation() + (success, wl_delays, sae_delays, wl_slews, sae_slews, bl_delays, bl_slews, powers)=self.run_delay_simulation() debug.check(success, "Model measurements Failed: period={}".format(self.period)) - wl_model_delays, sae_model_delays = self.get_model_delays(read_port) debug.info(1,"Measured Wordline delays (ns):\n\t {}".format(wl_delays[read_port])) - debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays)) debug.info(1,"Measured Wordline slews:\n\t {}".format(wl_slews[read_port])) debug.info(1,"Measured SAE delays (ns):\n\t {}".format(sae_delays[read_port])) - debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays)) debug.info(1,"Measured SAE slews:\n\t {}".format(sae_slews[read_port])) + debug.info(1,"Measured Bitline delays (ns):\n\t {}".format(bl_delays[read_port])) data_dict[self.wl_meas_name] = wl_delays[read_port] - data_dict[self.wl_model_name] = wl_model_delays data_dict[self.sae_meas_name] = sae_delays[read_port] - data_dict[self.sae_model_name] = sae_model_delays data_dict[self.wl_slew_name] = wl_slews[read_port] data_dict[self.sae_slew_name] = sae_slews[read_port] + data_dict[self.bl_meas_name] = bl_delays[read_port] + data_dict[self.power_name] = powers[read_port] + + if not OPTS.use_tech_delay_chain_size: #Model is not used in this case + wl_model_delays, sae_model_delays = self.get_model_delays(read_port) + debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays)) + debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays)) + data_dict[self.wl_model_name] = wl_model_delays + data_dict[self.sae_model_name] = sae_model_delays #Some evaluations of the model and measured values # debug.info(1, "Comparing wordline measurements and model.") @@ -337,11 +432,17 @@ class model_check(delay): name_dict = {} #Signal names are more descriptive than the measurement names, first value trimmed to match size of measurements names. name_dict[self.wl_meas_name] = self.wl_signal_names[1:] - name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured. name_dict[self.sae_meas_name] = self.rbl_en_signal_names[1:]+self.sae_signal_names[1:] - name_dict[self.sae_model_name] = name_dict["sae_measures"] name_dict[self.wl_slew_name] = self.wl_slew_meas_names name_dict[self.sae_slew_name] = self.rbl_slew_meas_names+self.sae_slew_meas_names + name_dict[self.bl_meas_name] = self.bitline_meas_names[0:1] + name_dict[self.power_name] = self.power_meas_names + #name_dict[self.wl_slew_name] = self.wl_slew_meas_names + + if not OPTS.use_tech_delay_chain_size: + name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured. + name_dict[self.sae_model_name] = name_dict["sae_measures"] + return name_dict diff --git a/compiler/characterizer/setup_hold.py b/compiler/characterizer/setup_hold.py index 8a515776..8def076d 100644 --- a/compiler/characterizer/setup_hold.py +++ b/compiler/characterizer/setup_hold.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import sys import tech from .stimuli import * @@ -329,10 +336,10 @@ class setup_hold(): for self.related_input_slew in related_slews: for self.constrained_input_slew in constrained_slews: # convert from ps to ns - LH_setup.append(tech.spice["msflop_setup"]/1e3) - HL_setup.append(tech.spice["msflop_setup"]/1e3) - LH_hold.append(tech.spice["msflop_hold"]/1e3) - HL_hold.append(tech.spice["msflop_hold"]/1e3) + LH_setup.append(tech.spice["dff_setup"]/1e3) + HL_setup.append(tech.spice["dff_setup"]/1e3) + LH_hold.append(tech.spice["dff_hold"]/1e3) + HL_hold.append(tech.spice["dff_hold"]/1e3) times = {"setup_times_LH": LH_setup, "setup_times_HL": HL_setup, diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index beca0502..5203c732 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import sys,re,shutil from design import design import debug @@ -17,15 +24,18 @@ class simulation(): self.name = self.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.write_size = self.sram.write_size self.sp_file = spfile self.all_ports = self.sram.all_ports self.readwrite_ports = self.sram.readwrite_ports self.read_ports = self.sram.read_ports self.write_ports = self.sram.write_ports + if self.write_size: + self.num_wmasks = int(self.word_size/self.write_size) + else: + self.num_wmasks = 0 + def set_corner(self,corner): """ Set the corner values """ @@ -36,12 +46,25 @@ class simulation(): """ sets feasible timing parameters """ self.period = tech.spice["feasible_period"] self.slew = tech.spice["rise_time"]*2 - self.load = tech.spice["msflop_in_cap"]*4 + self.load = tech.spice["dff_in_cap"]*4 - self.v_high = self.vdd_voltage - tech.spice["v_threshold_typical"] - self.v_low = tech.spice["v_threshold_typical"] + self.v_high = self.vdd_voltage - tech.spice["nom_threshold"] + self.v_low = tech.spice["nom_threshold"] self.gnd_voltage = 0 + def create_signal_names(self): + self.addr_name = "a" + self.din_name = "din" + self.dout_name = "dout" + self.pins = self.gen_pin_names(port_signal_names=(self.addr_name,self.din_name,self.dout_name), + port_info=(len(self.all_ports),self.write_ports,self.read_ports), + abits=self.addr_size, + dbits=self.word_size) + debug.check(len(self.sram.pins) == len(self.pins), "Number of pins generated for characterization \ + do match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,self.pins)) + #This is TODO once multiport control has been finalized. + #self.control_name = "CSB" + def set_stimulus_variables(self): # Clock signals self.cycle_times = [] @@ -54,6 +77,7 @@ class simulation(): # Three dimensional list to handle each addr and data bits for wach port over the number of checks self.addr_values = [[[] for bit in range(self.addr_size)] for port in self.all_ports] self.data_values = [[[] for bit in range(self.word_size)] for port in self.write_ports] + self.wmask_values = [[[] for bit in range(self.num_wmasks)] for port in self.write_ports] # For generating comments in SPICE stimulus self.cycle_comments = [] @@ -105,8 +129,22 @@ class simulation(): else: debug.error("Non-binary address string",1) bit -= 1 + + def add_wmask(self, wmask, port): + """ Add the array of address values """ + debug.check(len(wmask) == self.num_wmasks, "Invalid wmask size.") + + bit = self.num_wmasks - 1 + for c in wmask: + if c == "0": + self.wmask_values[port][bit].append(0) + elif c == "1": + self.wmask_values[port][bit].append(1) + else: + debug.error("Non-binary wmask string", 1) + bit -= 1 - def add_write(self, comment, address, data, port): + def add_write(self, comment, address, data, wmask, port): """ Add the control values for a write cycle. """ debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports)) debug.info(2, comment) @@ -119,15 +157,16 @@ class simulation(): self.add_control_one_port(port, "write") self.add_data(data,port) self.add_address(address,port) + self.add_wmask(wmask,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 self.all_ports: if unselected_port != port: - self.add_noop_one_port(address, noop_data, unselected_port) + self.add_noop_one_port(address, noop_data, wmask, unselected_port) - def add_read(self, comment, address, din_data, port): + def add_read(self, comment, address, din_data, wmask, port): """ Add the control values for a read cycle. """ debug.check(port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports)) debug.info(2, comment) @@ -141,6 +180,7 @@ class simulation(): #If the port is also a readwrite then add data. if port in self.write_ports: self.add_data(din_data,port) + self.add_wmask(wmask,port) self.add_address(address, port) #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port @@ -148,9 +188,9 @@ class simulation(): #Add noops to all other ports. for unselected_port in self.all_ports: if unselected_port != port: - self.add_noop_one_port(address, noop_data, unselected_port) + self.add_noop_one_port(address, noop_data, wmask, unselected_port) - def add_noop_all_ports(self, comment, address, data): + def add_noop_all_ports(self, comment, address, data, wmask): """ Add the control values for a noop to all ports. """ debug.info(2, comment) self.fn_cycle_comments.append(comment) @@ -160,19 +200,20 @@ class simulation(): self.t_current += self.period for port in self.all_ports: - self.add_noop_one_port(address, data, port) - - def add_write_one_port(self, comment, address, data, port): + self.add_noop_one_port(address, data, wmask, port) + + def add_write_one_port(self, comment, address, data, wmask, port): """ Add the control values for a write cycle. Does not increment the period. """ debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports)) 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) + self.add_wmask(wmask,port) - def add_read_one_port(self, comment, address, din_data, port): + def add_read_one_port(self, comment, address, din_data, wmask, port): """ Add the control values for a read cycle. Does not increment the period. """ debug.check(port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports)) debug.info(2, comment) @@ -182,13 +223,15 @@ class simulation(): #If the port is also a readwrite then add data. if port in self.write_ports: self.add_data(din_data,port) + self.add_wmask(wmask,port) self.add_address(address, port) - def add_noop_one_port(self, address, data, port): + def add_noop_one_port(self, address, data, wmask, port): """ Add the control values for a noop to a single port. Does not increment the period. """ self.add_control_one_port(port, "noop") if port in self.write_ports: self.add_data(data,port) + self.add_wmask(wmask,port) self.add_address(address, port) def append_cycle_comment(self, port, comment): @@ -202,24 +245,74 @@ class simulation(): time_spacing, comment)) - def gen_cycle_comment(self, op, word, addr, port, t_current): + def gen_cycle_comment(self, op, word, addr, wmask, 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 cycle {3} ({4}ns - {5}ns)".format(word, - addr, - port, - int(t_current/self.period), - t_current, - t_current+self.period) + addr, + port, + int(t_current/self.period), + t_current, + t_current+self.period) + elif op == "partial_write": + comment = "\tWriting (partial) {0} to address {1} with mask bit {2} (from port {3}) during cycle {4} ({5}ns - {6}ns)".format(word, + addr, + wmask, + port, + int(t_current / self.period), + t_current, + t_current + self.period) else: comment = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word, - addr, - port, - int(t_current/self.period), - t_current, - t_current+self.period) + addr, + port, + int(t_current/self.period), + t_current, + t_current+self.period) + + return comment + def gen_pin_names(self, port_signal_names, port_info, abits, dbits): + """Creates the pins names of the SRAM based on the no. of ports.""" + #This may seem redundant as the pin names are already defined in the sram. However, it is difficult + #to extract the functionality from the names, so they are recreated. As the order is static, changing + #the order of the pin names will cause issues here. + pin_names = [] + (addr_name, din_name, dout_name) = port_signal_names + (total_ports, write_index, read_index) = port_info + + for write_input in write_index: + for i in range(dbits): + pin_names.append("{0}{1}_{2}".format(din_name,write_input, i)) + + for port in range(total_ports): + for i in range(abits): + pin_names.append("{0}{1}_{2}".format(addr_name,port,i)) + + #Control signals not finalized. + for port in range(total_ports): + pin_names.append("CSB{0}".format(port)) + for port in range(total_ports): + if (port in read_index) and (port in write_index): + pin_names.append("WEB{0}".format(port)) + + for port in range(total_ports): + pin_names.append("{0}{1}".format("clk", port)) + + if self.write_size: + for port in write_index: + for bit in range(self.num_wmasks): + pin_names.append("WMASK{0}_{1}".format(port,bit)) + + for read_output in read_index: + for i in range(dbits): + pin_names.append("{0}{1}_{2}".format(dout_name,read_output, i)) + + pin_names.append("{0}".format("vdd")) + pin_names.append("{0}".format("gnd")) + return pin_names + diff --git a/compiler/characterizer/sram_op.py b/compiler/characterizer/sram_op.py new file mode 100644 index 00000000..58999ca0 --- /dev/null +++ b/compiler/characterizer/sram_op.py @@ -0,0 +1,15 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +from enum import Enum + +class sram_op(Enum): + READ_ZERO = 0 + READ_ONE = 1 + WRITE_ZERO = 2 + WRITE_ONE = 3 diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 3b569b2a..41a3428f 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ This file generates simple spice cards for simulation. There are various functions that can be be used to generate stimulus for other @@ -17,63 +24,27 @@ class stimuli(): """ Class for providing stimuli functions """ def __init__(self, stim_file, corner): - self.vdd_name = tech.spice["vdd_name"] - self.gnd_name = tech.spice["gnd_name"] + self.vdd_name = "vdd" + self.gnd_name = "gnd" self.pmos_name = tech.spice["pmos"] self.nmos_name = tech.spice["nmos"] - self.tx_width = tech.spice["minwidth_tx"] - self.tx_length = tech.spice["channel"] + self.tx_width = tech.drc["minwidth_tx"] + self.tx_length = tech.drc["minlength_channel"] self.sf = stim_file (self.process, self.voltage, self.temperature) = corner self.device_models = tech.spice["fet_models"][self.process] - def inst_sram(self, sram, port_signal_names, port_info, abits, dbits, sram_name): + self.sram_name = "Xsram" + + def inst_sram(self, pins, inst_name): """ Function to instatiate an SRAM subckt. """ - pin_names = self.gen_pin_names(port_signal_names, port_info, abits, dbits) - #Only checking length. This should check functionality as well (TODO) and/or import that information from the SRAM - debug.check(len(sram.pins) == len(pin_names), "Number of pins generated for characterization do match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(sram.pins,pin_names)) - - self.sf.write("Xsram ") - for pin in pin_names: + + self.sf.write("{} ".format(self.sram_name)) + for pin in self.sram_pins: self.sf.write("{0} ".format(pin)) - self.sf.write("{0}\n".format(sram_name)) - - def gen_pin_names(self, port_signal_names, port_info, abits, dbits): - """Creates the pins names of the SRAM based on the no. of ports.""" - #This may seem redundant as the pin names are already defined in the sram. However, it is difficult to extract the - #functionality from the names, so they are recreated. As the order is static, changing the order of the pin names - #will cause issues here. - pin_names = [] - (addr_name, din_name, dout_name) = port_signal_names - (total_ports, write_index, read_index) = port_info - - for write_input in write_index: - for i in range(dbits): - pin_names.append("{0}{1}_{2}".format(din_name,write_input, i)) - - for port in range(total_ports): - for i in range(abits): - pin_names.append("{0}{1}_{2}".format(addr_name,port,i)) - - #Control signals not finalized. - for port in range(total_ports): - pin_names.append("CSB{0}".format(port)) - for port in range(total_ports): - if (port in read_index) and (port in write_index): - pin_names.append("WEB{0}".format(port)) - - for port in range(total_ports): - pin_names.append("{0}{1}".format(tech.spice["clk"], port)) - - for read_output in read_index: - for i in range(dbits): - pin_names.append("{0}{1}_{2}".format(dout_name,read_output, i)) - - pin_names.append("{0}".format(self.vdd_name)) - pin_names.append("{0}".format(self.gnd_name)) - return pin_names + self.sf.write("{0}\n".format(inst_name)) def inst_model(self, pins, model_name): """ Function to instantiate a generic model with a set of pins """ @@ -226,6 +197,13 @@ class stimuli(): trig_dir, trig_td)) + def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at): + """ Creates the .meas statement for voltage at time""" + measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n" + self.sf.write(measure_string.format(meas_name, + targ_name, + time_at)) + def gen_meas_power(self, meas_name, t_initial, t_final): """ Creates the .meas statement for the measurement of avg power """ # power mea cmd is different in different spice: diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index 88195fee..ffc45a9c 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from math import log import re diff --git a/compiler/characterizer/worst_case.py b/compiler/characterizer/worst_case.py deleted file mode 100644 index 6dad95d9..00000000 --- a/compiler/characterizer/worst_case.py +++ /dev/null @@ -1,77 +0,0 @@ -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/datasheet/add_db.py b/compiler/datasheet/add_db.py new file mode 100644 index 00000000..9514cf1b --- /dev/null +++ b/compiler/datasheet/add_db.py @@ -0,0 +1,48 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from pathlib import Path +import glob +import os +import sys + +# This is the path to the directory you would like to search +# This directory is searched recursively for .html files + +path_to_files = sys.argv[1] + + +def get_file_tree(path): + return list(Path(path).rglob("*.html")) + + +def parse_html(file, comment): + start_tag = '' + + with open(file, 'r') as f: + + file_string = f.read() + + with open(file, 'w') as f: + + file_string = file_string.replace(start_tag,"") + file_string = file_string.replace(end_tag,"") + + f.write(file_string) + +def uncomment(comments): + comment_files = [] + for datasheet in datasheet_list: + for comment in comments: + if glob.glob(os.path.dirname(datasheet)+'/*' + comment): + parse_html(datasheet, comment) + +datasheet_list = get_file_tree(path_to_files) +comments = ['.db'] +uncomment(comments) + diff --git a/compiler/datasheet/datasheet.py b/compiler/datasheet/datasheet.py index a7700349..35091d36 100644 --- a/compiler/datasheet/datasheet.py +++ b/compiler/datasheet/datasheet.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from table_gen import * import os import base64 @@ -38,6 +45,9 @@ class datasheet(): with open(os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/OpenRAM_logo.png', "rb") as image_file: openram_logo = base64.b64encode(image_file.read()) + #comment table rows which we may want to enable after compile time + comments = ['.db'] + self.html += 'VLSIDAOpenRAM'.format(str(vlsi_logo)[2:-1], str(openram_logo)[2:-1]) self.html += '

' + \ @@ -51,11 +61,11 @@ class datasheet(): 'Git commit id: ' + str(self.git_id) + '

' # print port table self.html += '

Ports and Configuration

' - self.html += self.io_table.to_html() + self.html += self.io_table.to_html(comments) # print operating condidition information self.html += '

Operating Conditions

' - self.html += self.operating_table.to_html() + self.html += self.operating_table.to_html(comments) # check if analytical model is being used self.html += '

Timing Data

' @@ -66,13 +76,14 @@ class datasheet(): model = "spice characterizer" # display timing data self.html += '

Using '+model+'

' - self.html += self.timing_table.to_html() + self.html += self.timing_table.to_html(comments) # display power data self.html += '

Power Data

' - self.html += self.power_table.to_html() + self.html += self.power_table.to_html(comments) # display corner information self.html += '

Characterization Corners

' - self.html += self.corners_table.to_html() + self.html += self.corners_table.to_html(comments) # display deliverables table self.html += '

Deliverables

' - self.html += self.dlv_table.to_html() + self.dlv_table.sort() + self.html += self.dlv_table.to_html(comments) diff --git a/compiler/datasheet/datasheet_gen.py b/compiler/datasheet/datasheet_gen.py index 3b4fe2ac..02ecc5d4 100644 --- a/compiler/datasheet/datasheet_gen.py +++ b/compiler/datasheet/datasheet_gen.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 """ This is a script to load data from the characterization and layout processes into @@ -154,7 +161,7 @@ def parse_characterizer_csv(f, pages): # check current .lib file produces the slowest timing results while(True): col_start = col - if(row[col].startswith('DIN')): + if(row[col].startswith('din')): start = col for item in sheet.timing_table.rows: if item[0].startswith(row[col]): @@ -193,7 +200,7 @@ def parse_characterizer_csv(f, pages): col += 1 - elif(row[col].startswith('DOUT')): + elif(row[col].startswith('dout')): start = col for item in sheet.timing_table.rows: if item[0].startswith(row[col]): @@ -232,7 +239,7 @@ def parse_characterizer_csv(f, pages): col += 1 - elif(row[col].startswith('CSb')): + elif(row[col].startswith('csb')): start = col for item in sheet.timing_table.rows: if item[0].startswith(row[col]): @@ -271,7 +278,7 @@ def parse_characterizer_csv(f, pages): col += 1 - elif(row[col].startswith('WEb')): + elif(row[col].startswith('web')): start = col for item in sheet.timing_table.rows: if item[0].startswith(row[col]): @@ -310,7 +317,7 @@ def parse_characterizer_csv(f, pages): col += 1 - elif(row[col].startswith('ADDR')): + elif(row[col].startswith('addr')): start = col for item in sheet.timing_table.rows: if item[0].startswith(row[col]): @@ -386,6 +393,9 @@ def parse_characterizer_csv(f, pages): [PROC, VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')]) new_sheet.dlv_table.add_row( ['.lib', 'Synthesis models', '{1}'.format(LIB_NAME, LIB_NAME.replace(OUT_DIR, ''))]) + new_sheet.dlv_table.add_row( + ['.db', 'Compiled .lib', '{1}'.format(LIB_NAME[:-3] + 'db', LIB_NAME.replace(OUT_DIR, '')[:-3] + 'db')]) + if found == 0: @@ -431,7 +441,7 @@ def parse_characterizer_csv(f, pages): # parse initial timing information while(True): col_start = col - if(row[col].startswith('DIN')): + if(row[col].startswith('din')): start = col new_sheet.timing_table.add_row( @@ -455,7 +465,7 @@ def parse_characterizer_csv(f, pages): col += 1 - elif(row[col].startswith('DOUT')): + elif(row[col].startswith('dout')): start = col new_sheet.timing_table.add_row( @@ -479,7 +489,7 @@ def parse_characterizer_csv(f, pages): col += 1 - elif(row[col].startswith('CSb')): + elif(row[col].startswith('csb')): start = col new_sheet.timing_table.add_row( @@ -503,7 +513,7 @@ def parse_characterizer_csv(f, pages): col += 1 - elif(row[col].startswith('WEb')): + elif(row[col].startswith('web')): start = col new_sheet.timing_table.add_row( @@ -527,7 +537,7 @@ def parse_characterizer_csv(f, pages): col += 1 - elif(row[col].startswith('ADDR')): + elif(row[col].startswith('addr')): start = col new_sheet.timing_table.add_row( @@ -603,6 +613,8 @@ def parse_characterizer_csv(f, pages): ['.html', 'This datasheet', '{0}.{1}'.format(OPTS.output_name, 'html')]) new_sheet.dlv_table.add_row( ['.lib', 'Synthesis models', '{1}'.format(LIB_NAME, LIB_NAME.replace(OUT_DIR, ''))]) + new_sheet.dlv_table.add_row( + ['.db', 'Compiled .lib', '{1}'.format(LIB_NAME[:-3] + 'db', LIB_NAME.replace(OUT_DIR, '')[:-3] + 'db')]) new_sheet.dlv_table.add_row( ['.py', 'OpenRAM configuration file', '{0}.{1}'.format(OPTS.output_name, 'py')]) new_sheet.dlv_table.add_row( diff --git a/compiler/datasheet/table_gen.py b/compiler/datasheet/table_gen.py index 8f94e896..85a6cfba 100644 --- a/compiler/datasheet/table_gen.py +++ b/compiler/datasheet/table_gen.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# class table_gen: """small library of functions to generate the html tables""" @@ -22,27 +29,38 @@ class table_gen: html += '' return html - def gen_table_body(self): + def gen_table_body(self,comments): """generate html body (used after gen_table_head)""" html = '' html += '' html += '' for row in self.rows[1:]: - html += '' - for col in row: - html += '' + str(col) + '' - html += '' + + if row[0] not in comments: + html += '' + for col in row: + html += '' + str(col) + '' + html += '' + + else: + html += '' + html += '' html += '' return html + def sort(self): + self.rows[1:] = sorted(self.rows[1:], key=lambda x : x[0]) - def to_html(self): + def to_html(self,comments): """writes table_gen object to inline html""" html = '' html += '' html += self.gen_table_head() - html += self.gen_table_body() - html += '
' + html += self.gen_table_body(comments) + html += '\n' return html diff --git a/compiler/debug.py b/compiler/debug.py index 9ce1bf3f..02a28c22 100644 --- a/compiler/debug.py +++ b/compiler/debug.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import os import inspect import globals @@ -58,6 +65,8 @@ def log(str): # in another log file if the path or name changes. if not globals.OPTS.output_path.endswith('/'): globals.OPTS.output_path += "/" + if not os.path.isdir(globals.OPTS.output_path): + os.mkdir(globals.OPTS.output_path) compile_log = open(globals.OPTS.output_path + globals.OPTS.output_name + '.log', "w+") log.create_file = 0 @@ -76,7 +85,7 @@ def log(str): # use a static list of strings to store messages until the global paths are set up log.setup_output = [] -log.create_file = 1 +log.create_file = True def info(lev, str): diff --git a/compiler/drc/design_rules.py b/compiler/drc/design_rules.py index 4bc2eaa6..1017aca6 100644 --- a/compiler/drc/design_rules.py +++ b/compiler/drc/design_rules.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from drc_value import * from drc_lut import * @@ -35,6 +42,7 @@ class design_rules(): 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 index 07e482c8..0ad0fde4 100644 --- a/compiler/drc/drc_lut.py +++ b/compiler/drc/drc_lut.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug class drc_lut(): diff --git a/compiler/drc/drc_value.py b/compiler/drc/drc_value.py index c4eab3d4..1649e203 100644 --- a/compiler/drc/drc_value.py +++ b/compiler/drc/drc_value.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# class drc_value(): """ diff --git a/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py b/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py index 8703967b..5dad0207 100644 --- a/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py +++ b/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py @@ -10,6 +10,9 @@ process_corners = ["TT"] supply_voltages = [5.0] temperatures = [25] +route_supplies = True +check_lvsdrc = True + output_path = "temp" output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size,num_words,tech_name) diff --git a/compiler/example_configs/example_config_1w_1r_scn4m_subm.py b/compiler/example_configs/example_config_1w_1r_scn4m_subm.py index 56f6edfd..c698a035 100644 --- a/compiler/example_configs/example_config_1w_1r_scn4m_subm.py +++ b/compiler/example_configs/example_config_1w_1r_scn4m_subm.py @@ -10,6 +10,9 @@ process_corners = ["TT"] supply_voltages = [5.0] temperatures = [25] +route_supplies = True +check_lvsdrc = True + output_path = "temp" output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,num_words,tech_name) diff --git a/compiler/example_configs/example_config_freepdk45.py b/compiler/example_configs/example_config_freepdk45.py index ac02e514..73e15b6d 100644 --- a/compiler/example_configs/example_config_freepdk45.py +++ b/compiler/example_configs/example_config_freepdk45.py @@ -6,6 +6,9 @@ process_corners = ["TT"] supply_voltages = [1.0] temperatures = [25] +route_supplies = True +check_lvsdrc = True + output_path = "temp" output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) diff --git a/compiler/example_configs/example_config_scn4m_subm.py b/compiler/example_configs/example_config_scn4m_subm.py index 7fafeb08..cf973225 100644 --- a/compiler/example_configs/example_config_scn4m_subm.py +++ b/compiler/example_configs/example_config_scn4m_subm.py @@ -6,6 +6,9 @@ process_corners = ["TT"] supply_voltages = [5.0] temperatures = [25] +route_supplies = True +check_lvsdrc = True + output_path = "temp" output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) diff --git a/compiler/example_configs/giant_config_scn4m_subm.py b/compiler/example_configs/giant_config_scn4m_subm.py new file mode 100644 index 00000000..74d52fe6 --- /dev/null +++ b/compiler/example_configs/giant_config_scn4m_subm.py @@ -0,0 +1,14 @@ +word_size = 64 +num_words = 1024 + +tech_name = "scn4m_subm" +process_corners = ["TT"] +supply_voltages = [ 5.0 ] +temperatures = [ 25 ] + +output_path = "temp" +output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) + +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index 03873ed4..f4248ebd 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -2,6 +2,7 @@ from .gdsPrimitives import * from datetime import * #from mpmath import matrix #from numpy import matrix +from vector import vector import numpy as np #import gdsPrimitives import debug @@ -725,12 +726,26 @@ class VlsiLayout: self.pins[label_text] = [] self.pins[label_text].append(pin_shapes) - + + def getBlockages(self,layer): + """ + Return all blockages on a given layer in [coordinate 1, coordinate 2,...] format and + user units. + """ + blockages = [] + + shapes = self.getAllShapes(layer) + for boundary in shapes: + vectors = [] + for i in range(0,len(boundary),2): + vectors.append(vector(boundary[i],boundary[i+1])) + blockages.append(vectors) + return blockages def getAllShapes(self,layer): """ - Return all gshapes on a given layer in [llx, lly, urx, ury] format and - user units. + Return all shapes on a given layer in [llx, lly, urx, ury] format and user units for rectangles + and [coordinate 1, coordinate 2,...] format and user units for polygons. """ boundaries = set() for TreeUnit in self.xyTree: @@ -740,42 +755,67 @@ class VlsiLayout: # Convert to user units user_boundaries = [] for boundary in boundaries: - user_boundaries.append([boundary[0]*self.units[0],boundary[1]*self.units[0], - boundary[2]*self.units[0],boundary[3]*self.units[0]]) - + boundaries_list = [] + for i in range(0,len(boundary)): + boundaries_list.append(boundary[i]*self.units[0]) + user_boundaries.append(boundaries_list) return user_boundaries def getShapesInStructure(self,layer,structure): """ Go through all the shapes in a structure and return the list of shapes in - the form [llx, lly, urx, ury] + the form [llx, lly, urx, ury] for rectangles and [coordinate 1, coordinate 2,...] for polygons. """ - (structureName,structureOrigin,structureuVector,structurevVector)=structure #print(structureName,"u",structureuVector.transpose(),"v",structurevVector.transpose(),"o",structureOrigin.transpose()) boundaries = [] for boundary in self.structures[str(structureName)].boundaries: - # FIXME: Right now, this only supports rectangular shapes! - # We should trigger an error but some FreePDK45 library cells contain paths. - # These get saved fine, but we cannot parse them as blockages... - #debug.check(len(boundary.coordinates)==5,"Non-rectangular shapes are not supported.") - if len(boundary.coordinates)!=5: - continue if layer==boundary.drawingLayer: - left_bottom=boundary.coordinates[0] - right_top=boundary.coordinates[2] - # Rectangle is [leftx, bottomy, rightx, topy]. - boundaryRect=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]] - # perform the rotation - boundaryRect=self.transformRectangle(boundaryRect,structureuVector,structurevVector) - # add the offset and make it a tuple - boundaryRect=(boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(), - boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item()) - boundaries.append(boundaryRect) - + if len(boundary.coordinates)!=5: + # if shape is a polygon (used in DFF) + boundaryPolygon = [] + # Polygon is a list of coordinates going ccw + for coord in range(0,len(boundary.coordinates)): + boundaryPolygon.append(boundary.coordinates[coord][0]) + boundaryPolygon.append(boundary.coordinates[coord][1]) + # perform the rotation + boundaryPolygon=self.transformPolygon(boundaryPolygon,structureuVector,structurevVector) + # add the offset + polygon = [] + for i in range(0,len(boundaryPolygon),2): + polygon.append(boundaryPolygon[i]+structureOrigin[0].item()) + polygon.append(boundaryPolygon[i+1]+structureOrigin[1].item()) + # make it a tuple + polygon = tuple(polygon) + boundaries.append(polygon) + else: + # else shape is a rectangle + left_bottom=boundary.coordinates[0] + right_top=boundary.coordinates[2] + # Rectangle is [leftx, bottomy, rightx, topy]. + boundaryRect=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]] + # perform the rotation + boundaryRect=self.transformRectangle(boundaryRect,structureuVector,structurevVector) + # add the offset and make it a tuple + boundaryRect=(boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(), + boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item()) + boundaries.append(boundaryRect) return boundaries - + + + def transformPolygon(self,originalPolygon,uVector,vVector): + """ + Transforms the coordinates of a polygon in space. + """ + polygon = [] + newPolygon = [] + for i in range(0,len(originalPolygon),2): + polygon.append(self.transformCoordinate([originalPolygon[i],originalPolygon[i+1]],uVector,vVector)) + newPolygon.append(polygon[int(i/2)][0]) + newPolygon.append(polygon[int(i/2)][1]) + return newPolygon + def transformRectangle(self,originalRectangle,uVector,vVector): """ Transforms the four coordinates of a rectangle in space @@ -799,7 +839,7 @@ class VlsiLayout: """ Rotate a coordinate in space. """ - # MRG: 9/3/18 Incorrect matrixi multiplication! + # MRG: 9/3/18 Incorrect matrix multiplication! # This is fixed to be: # |u[0] v[0]| |x| |x'| # |u[1] v[1]|x|y|=|y'| diff --git a/compiler/gen_stimulus.py b/compiler/gen_stimulus.py index 07bd1b1c..6a6be1d1 100755 --- a/compiler/gen_stimulus.py +++ b/compiler/gen_stimulus.py @@ -1,4 +1,11 @@ #!/usr/bin/env python +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ This script will generate a stimulus file for a given period, load, and slew input for the given dimension SRAM. It is useful for debugging after an SRAM has been diff --git a/compiler/globals.py b/compiler/globals.py index c1422592..e08e381f 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ This is called globals.py, but it actually parses all the arguments and performs the global OpenRAM setup as well. @@ -12,9 +19,10 @@ import re import copy import importlib -USAGE = "Usage: openram.py [options] \nUse -h for help.\n" +VERSION = "1.1.0" +NAME = "OpenRAM v{}".format(VERSION) +USAGE = "openram.py [options] \nUse -h for help.\n" -# Anonymous object that will be the options OPTS = options.options() CHECKPOINT_OPTS=None @@ -50,9 +58,9 @@ def parse_args(): } parser = optparse.OptionParser(option_list=option_list, - description="Compile and/or characterize an SRAM.", + description=NAME, usage=USAGE, - version="OpenRAM") + version=VERSION) (options, args) = parser.parse_args(values=OPTS) # If we don't specify a tech, assume scmos. @@ -72,17 +80,12 @@ def print_banner(): return debug.print_raw("|==============================================================================|") - name = "OpenRAM Compiler" - debug.print_raw("|=========" + name.center(60) + "=========|") + debug.print_raw("|=========" + NAME.center(60) + "=========|") debug.print_raw("|=========" + " ".center(60) + "=========|") debug.print_raw("|=========" + "VLSI Design and Automation Lab".center(60) + "=========|") debug.print_raw("|=========" + "Computer Science and Engineering Department".center(60) + "=========|") debug.print_raw("|=========" + "University of California Santa Cruz".center(60) + "=========|") debug.print_raw("|=========" + " ".center(60) + "=========|") - debug.print_raw("|=========" + "VLSI Computer Architecture Research Group".center(60) + "=========|") - debug.print_raw("|=========" + "Electrical and Computer Engineering Department".center(60) + "=========|") - debug.print_raw("|=========" + "Oklahoma State University".center(60) + "=========|") - debug.print_raw("|=========" + " ".center(60) + "=========|") user_info = "Usage help: openram-user-group@ucsc.edu" debug.print_raw("|=========" + user_info.center(60) + "=========|") dev_info = "Development help: openram-dev-group@ucsc.edu" @@ -128,6 +131,8 @@ def init_openram(config_file, is_unit_test=True): import_tech() + set_default_corner() + init_paths() from sram_factory import factory @@ -135,10 +140,6 @@ def init_openram(config_file, is_unit_test=True): setup_bitcell() - # Reset the static duplicate name checker for unit tests. - import hierarchy_design - hierarchy_design.hierarchy_design.name_map=[] - global OPTS global CHECKPOINT_OPTS @@ -168,11 +169,12 @@ def setup_bitcell(): # If we have non-1rw ports, # and the user didn't over-ride the bitcell manually, # figure out the right bitcell to use - if (OPTS.bitcell=="bitcell" and OPTS.replica_bitcell=="replica_bitcell"): + if (OPTS.bitcell=="bitcell"): if (OPTS.num_rw_ports==1 and OPTS.num_w_ports==0 and OPTS.num_r_ports==0): OPTS.bitcell = "bitcell" OPTS.replica_bitcell = "replica_bitcell" + OPTS.dummy_bitcell = "dummy_bitcell" else: ports = "" if OPTS.num_rw_ports>0: @@ -184,20 +186,26 @@ def setup_bitcell(): OPTS.bitcell = "bitcell_"+ports OPTS.replica_bitcell = "replica_bitcell_"+ports - - # See if a custom bitcell exists - from importlib import find_loader - bitcell_loader = find_loader(OPTS.bitcell) - replica_bitcell_loader = find_loader(OPTS.replica_bitcell) + OPTS.dummy_bitcell = "dummy_bitcell_"+ports + else: + OPTS.replica_bitcell = "replica_" + OPTS.bitcell + OPTS.replica_bitcell = "dummy_" + OPTS.bitcell + + # See if bitcell exists + from importlib import find_loader + try: + __import__(OPTS.bitcell) + __import__(OPTS.replica_bitcell) + __import__(OPTS.dummy_bitcell) + except ImportError: # Use the pbitcell if we couldn't find a custom bitcell # or its custom replica bitcell - if bitcell_loader==None or replica_bitcell_loader==None: - # Use the pbitcell (and give a warning if not in unit test mode) - OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell = "replica_pbitcell" - if not OPTS.is_unit_test: - debug.warning("Using the parameterized bitcell which may have suboptimal density.") - + # Use the pbitcell (and give a warning if not in unit test mode) + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.replica_bitcell = "dummy_pbitcell" + if not OPTS.is_unit_test: + debug.warning("Using the parameterized bitcell which may have suboptimal density.") debug.info(1,"Using bitcell: {}".format(OPTS.bitcell)) @@ -392,9 +400,20 @@ def init_paths(): except: debug.error("Unable to make output directory.",-1) +def set_default_corner(): + """ Set the default corner. """ + + # Set some default options now based on the technology... + if (OPTS.process_corners == ""): + OPTS.process_corners = tech.spice["fet_models"].keys() + if (OPTS.supply_voltages == ""): + OPTS.supply_voltages = tech.spice["supply_voltages"] + if (OPTS.temperatures == ""): + OPTS.temperatures = tech.spice["temperatures"] + -# imports correct technology directories for testing def import_tech(): + """ Dynamically adds the tech directory to the path and imports it. """ global OPTS debug.info(2,"Importing technology: " + OPTS.tech_name) @@ -403,34 +422,30 @@ def import_tech(): try: OPENRAM_TECH = os.path.abspath(os.environ.get("OPENRAM_TECH")) except: - debug.error("$OPENRAM_TECH is not properly defined.",1) - debug.check(os.path.isdir(OPENRAM_TECH),"$OPENRAM_TECH does not exist: {0}".format(OPENRAM_TECH)) - - OPTS.openram_tech = OPENRAM_TECH + "/" + OPTS.tech_name - if not OPTS.openram_tech.endswith('/'): - OPTS.openram_tech += "/" - debug.info(1, "Technology path is " + OPTS.openram_tech) + debug.error("$OPENRAM_TECH environment variable is not defined.",1) + # Add all of the paths + for tech_path in OPENRAM_TECH.split(":"): + debug.check(os.path.isdir(tech_path),"$OPENRAM_TECH does not exist: {0}".format(tech_path)) + sys.path.append(tech_path) + debug.info(1, "Adding technology path: {}".format(tech_path)) + + # Import the tech try: - filename = "setup_openram_{0}".format(OPTS.tech_name) - # we assume that the setup scripts (and tech dirs) are located at the - # same level as the compielr itself, probably not a good idea though. - path = "{0}/setup_scripts".format(os.environ.get("OPENRAM_TECH")) - debug.check(os.path.isdir(path),"OPENRAM_TECH does not exist: {0}".format(path)) - sys.path.append(os.path.abspath(path)) - __import__(filename) + tech_mod = __import__(OPTS.tech_name) except ImportError: - debug.error("Nonexistent technology_setup_file: {0}.py".format(filename)) - sys.exit(1) + debug.error("Nonexistent technology_setup_file: {0}.py".format(filename), -1) - import tech - # Set some default options now based on the technology... - if (OPTS.process_corners == ""): - OPTS.process_corners = tech.spice["fet_models"].keys() - if (OPTS.supply_voltages == ""): - OPTS.supply_voltages = tech.spice["supply_voltages"] - if (OPTS.temperatures == ""): - OPTS.temperatures = tech.spice["temperatures"] + OPTS.openram_tech = os.path.dirname(tech_mod.__file__) + "/" + + + # Add the tech directory + tech_path = OPTS.openram_tech + sys.path.append(tech_path) + try: + import tech + except ImportError: + debug.error("Could not load tech module.", -1) def print_time(name, now_time, last_time=None, indentation=2): @@ -455,6 +470,20 @@ def report_status(): debug.error("{0} is not an integer in config file.".format(OPTS.word_size)) if type(OPTS.num_words)!=int: debug.error("{0} is not an integer in config file.".format(OPTS.sram_size)) + if type(OPTS.write_size) is not int and OPTS.write_size is not None: + debug.error("{0} is not an integer in config file.".format(OPTS.write_size)) + + # If a write mask is specified by the user, the mask write size should be the same as + # the word size so that an entire word is written at once. + if OPTS.write_size is not None: + if (OPTS.word_size % OPTS.write_size != 0): + debug.error("Write size needs to be an integer multiple of word size.") + # If write size is more than half of the word size, then it doesn't need a write mask. It would be writing + # the whole word. + if (OPTS.write_size < 1 or OPTS.write_size > OPTS.word_size/2): + debug.error("Write size needs to be between 1 bit and {0} bits/2.".format(OPTS.word_size)) + + if not OPTS.tech_name: debug.error("Tech name must be specified in config file.") @@ -468,15 +497,30 @@ def report_status(): debug.print_raw("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size, OPTS.num_words, OPTS.num_banks)) + if (OPTS.write_size != OPTS.word_size): + debug.print_raw("Write size: {}".format(OPTS.write_size)) debug.print_raw("RW ports: {0}\nR-only ports: {1}\nW-only ports: {2}".format(OPTS.num_rw_ports, OPTS.num_r_ports, OPTS.num_w_ports)) + if OPTS.netlist_only: - debug.print_raw("Netlist only mode (no physical design is being done).") + debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).") + if not OPTS.route_supplies: + debug.print_raw("Design supply routing skipped. Supplies will have multiple must-connect pins. (route_supplies=True to enable supply routing).") + if not OPTS.inline_lvsdrc: - debug.print_raw("DRC/LVS/PEX is only run on the top-level design.") + debug.print_raw("DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to do inline checking).") if not OPTS.check_lvsdrc: - debug.print_raw("DRC/LVS/PEX is completely disabled.") + debug.print_raw("DRC/LVS/PEX is disabled (check_lvsdrc=True to enable).") + + if OPTS.analytical_delay: + debug.print_raw("Characterization is disabled (using analytical delay models) (analytical_delay=False to simulate).") + else: + if OPTS.spice_name!="": + debug.print_raw("Performing simulation-based characterization with {}".format(OPTS.spice_name)) + if OPTS.trim_netlist: + debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).") + diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 7eddc3bb..3e105d09 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import sys from tech import drc, parameter import debug @@ -21,13 +28,19 @@ class bank(design.design): def __init__(self, sram_config, name=""): + self.sram_config = sram_config sram_config.set_local_config(self) + if self.write_size: + self.num_wmasks = int(self.word_size/self.write_size) + else: + self.num_wmasks = 0 if name == "": name = "bank_{0}_{1}".format(self.word_size, self.num_words) design.design.__init__(self, name) debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words)) + # The local control signals are gated when we have bank select logic, # so this prefix will be added to all of the input signals to create # the internal gated signals. @@ -40,12 +53,13 @@ class bank(design.design): if not OPTS.netlist_only: debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.") self.create_layout() + self.add_boundary() def create_netlist(self): self.compute_sizes() - self.add_pins() self.add_modules() + self.add_pins() # Must create the replica bitcell array first self.create_instances() @@ -60,17 +74,21 @@ class bank(design.design): # Remember the bank center for further placement self.bank_array_ll = self.offset_all_coordinates().scale(-1,-1) self.bank_array_ur = self.bitcell_array_inst.ur() - + self.bank_array_ul = self.bitcell_array_inst.ul() + + self.DRC_LVS() def add_pins(self): """ Adding pins for Bank module""" for port in self.read_ports: for bit in range(self.word_size): - self.add_pin("dout{0}_{1}".format(port,bit),"OUT") + self.add_pin("dout{0}_{1}".format(port,bit),"OUTPUT") + for port in self.all_ports: + self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]),"OUTPUT") for port in self.write_ports: for bit in range(self.word_size): - self.add_pin("din{0}_{1}".format(port,bit),"IN") + self.add_pin("din{0}_{1}".format(port,bit),"INPUT") for port in self.all_ports: for bit in range(self.addr_size): self.add_pin("addr{0}_{1}".format(port,bit),"INPUT") @@ -82,10 +100,12 @@ class bank(design.design): self.add_pin("bank_sel{}".format(port),"INPUT") for port in self.read_ports: self.add_pin("s_en{0}".format(port), "INPUT") - for port in self.read_ports: + for port in self.all_ports: self.add_pin("p_en_bar{0}".format(port), "INPUT") for port in self.write_ports: self.add_pin("w_en{0}".format(port), "INPUT") + for bit in range(self.num_wmasks): + self.add_pin("bank_wmask{0}_{1}".format(port,bit),"INPUT") for port in self.all_ports: self.add_pin("wl_en{0}".format(port), "INPUT") self.add_pin("vdd","POWER") @@ -98,52 +118,45 @@ class bank(design.design): for port in self.all_ports: self.route_bitlines(port) - self.route_wordline_driver(port) - self.route_row_decoder(port) + self.route_rbl(port) + self.route_port_address(port) self.route_column_address_lines(port) self.route_control_lines(port) if self.num_banks > 1: - self.route_bank_select(port) + self.route_bank_select(port) self.route_supplies() + def route_rbl(self,port): + """ Route the rbl_bl and rbl_wl """ + + bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) + bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name) + self.add_layout_pin(text="rbl_bl{0}".format(port), + layer=bl_pin.layer, + offset=bl_pin.ll(), + height=bl_pin.height(), + width=bl_pin.width()) + + + def route_bitlines(self, port): """ Route the bitlines depending on the port type rw, w, or r. """ + + if port in self.write_ports: + self.route_port_data_in(port) + if port in self.read_ports: + self.route_port_data_out(port) + self.route_port_data_to_bitcell_array(port) - if port in self.readwrite_ports: - # write_driver -> sense_amp -> (column_mux) -> precharge -> bitcell_array - self.route_write_driver_in(port) - self.route_sense_amp_out(port) - self.route_write_driver_to_sense_amp(port) - self.route_sense_amp_to_column_mux_or_precharge_array(port) - self.route_column_mux_to_precharge_array(port) - self.route_precharge_to_bitcell_array(port) - elif port in self.read_ports: - # sense_amp -> (column_mux) -> precharge -> bitcell_array - self.route_sense_amp_out(port) - self.route_sense_amp_to_column_mux_or_precharge_array(port) - self.route_column_mux_to_precharge_array(port) - self.route_precharge_to_bitcell_array(port) - else: - # write_driver -> (column_mux) -> bitcell_array - self.route_write_driver_in(port) - self.route_write_driver_to_column_mux_or_bitcell_array(port) - self.route_column_mux_to_bitcell_array(port) def create_instances(self): """ Create the instances of the netlist. """ self.create_bitcell_array() - - self.create_precharge_array() - self.create_column_mux_array() - self.create_sense_amp_array() - self.create_write_driver_array() - - self.create_row_decoder() - self.create_wordline_driver() + self.create_port_data() + self.create_port_address() self.create_column_decoder() - self.create_bank_select() def compute_instance_offsets(self): @@ -151,39 +164,26 @@ class bank(design.design): Compute the empty instance offsets for port0 and port1 (if needed) """ - # These are created even if the port type (e.g. read only) - # doesn't need the instance (e.g. write driver). - - # Create the bottom-up and left to right order of components in each port - # which deepends on the port type rw, w, r - self.vertical_port_order = [] - self.vertical_port_offsets = [] - for port in self.all_ports: - self.vertical_port_order.append([]) - self.vertical_port_offsets.append([None]*4) - - # For later placement, these are fixed in the order: write driver, - # sense amp, clumn mux, precharge, even if the item is not used - # in a given port (it will be None then) - self.vertical_port_order[port].append(self.write_driver_array_inst[port]) - self.vertical_port_order[port].append(self.sense_amp_array_inst[port]) - self.vertical_port_order[port].append(self.column_mux_array_inst[port]) - self.vertical_port_order[port].append(self.precharge_array_inst[port]) - - # For the odd ones they will go on top, so reverse in place - if port%2: - self.vertical_port_order[port]=self.vertical_port_order[port][::-1] - - self.write_driver_offsets = [None]*len(self.all_ports) - self.sense_amp_offsets = [None]*len(self.all_ports) - self.column_mux_offsets = [None]*len(self.all_ports) - self.precharge_offsets = [None]*len(self.all_ports) - - self.wordline_driver_offsets = [None]*len(self.all_ports) - self.row_decoder_offsets = [None]*len(self.all_ports) + self.port_data_offsets = [None]*len(self.all_ports) + self.port_address_offsets = [None]*len(self.all_ports) self.column_decoder_offsets = [None]*len(self.all_ports) self.bank_select_offsets = [None]*len(self.all_ports) + + + # The center point for these cells are the upper-right corner of + # the bitcell array. + # The port address decoder/driver logic is placed on the right and mirrored on Y-axis. + # The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis. + self.bitcell_array_top = self.bitcell_array.height + self.bitcell_array_right = self.bitcell_array.width + + # These are the offsets of the main array (excluding dummy and replica rows/cols) + self.main_bitcell_array_top = self.bitcell_array.bitcell_array_inst.uy() + # Just past the dummy column + self.main_bitcell_array_left = self.bitcell_array.bitcell_array_inst.lx() + # Just past the dummy row and replica row + self.main_bitcell_array_bottom = self.bitcell_array.bitcell_array_inst.by() self.compute_instance_port0_offsets() if len(self.all_ports)==2: @@ -192,7 +192,7 @@ class bank(design.design): def compute_instance_port0_offsets(self): """ - Compute the instance offsets for port0. + Compute the instance offsets for port0 on the left/bottom of the bank. """ port = 0 @@ -203,110 +203,71 @@ class bank(design.design): # LOWER RIGHT QUADRANT # Below the bitcell array - y_height = 0 - for p in self.vertical_port_order[port]: - if p==None: - continue - y_height += p.height + self.m2_gap - - y_offset = -y_height - for i,p in enumerate(self.vertical_port_order[port]): - if p==None: - continue - self.vertical_port_offsets[port][i]=vector(0,y_offset) - y_offset += (p.height + self.m2_gap) - - self.write_driver_offsets[port] = self.vertical_port_offsets[port][0] - self.sense_amp_offsets[port] = self.vertical_port_offsets[port][1] - self.column_mux_offsets[port] = self.vertical_port_offsets[port][2] - self.precharge_offsets[port] = self.vertical_port_offsets[port][3] + self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width,0) # UPPER LEFT QUADRANT # To the left of the bitcell array - # The wordline driver is placed to the right of the main decoder width. - x_offset = self.m2_gap + self.wordline_driver.width - self.wordline_driver_offsets[port] = vector(-x_offset,0) - x_offset += self.row_decoder.width + self.m2_gap - self.row_decoder_offsets[port] = vector(-x_offset,0) + x_offset = self.m2_gap + self.port_address.width + self.port_address_offsets[port] = vector(-x_offset,self.main_bitcell_array_bottom) # LOWER LEFT QUADRANT - # Place the col decoder left aligned with wordline driver plus halfway under row decoder - # Place the col decoder left aligned with row decoder (x_offset doesn't change) - # Below the bitcell array with well spacing - x_offset = self.central_bus_width[port] + self.wordline_driver.width + # Place the col decoder left aligned with wordline driver + # This is also placed so that it's supply rails do not align with the SRAM-level + # control logic to allow control signals to easily pass over in M3 + # by placing 1/2 a cell pitch down + x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width - y_offset = self.m2_gap + self.column_decoder.height + y_offset = 0.5*self.dff.height + self.column_decoder.height else: y_offset = 0 - y_offset += 2*drc("well_to_well") self.column_decoder_offsets[port] = vector(-x_offset,-y_offset) # Bank select gets placed below the column decoder (x_offset doesn't change) if self.col_addr_size > 0: - y_offset = min(self.column_decoder_offsets[port].y, self.column_mux_offsets[port].y) + y_offset = min(self.column_decoder_offsets[port].y, self.port_data[port].column_mux_offset.y) else: - y_offset = self.row_decoder_offsets[port].y + y_offset = self.port_address_offsets[port].y if self.num_banks > 1: y_offset += self.bank_select.height + drc("well_to_well") self.bank_select_offsets[port] = vector(-x_offset,-y_offset) def compute_instance_port1_offsets(self): """ - Compute the instance offsets for port1 on the top of the bank. + Compute the instance offsets for port1 on the right/top of the bank. """ port=1 - # The center point for these cells are the upper-right corner of - # the bitcell array. - # The decoder/driver logic is placed on the right and mirrored on Y-axis. - # The write/sense/precharge/mux is placed on the top and mirrored on the X-axis. - # LOWER LEFT QUADRANT # Bitcell array is placed at (0,0) # UPPER LEFT QUADRANT # Above the bitcell array - y_offset = self.bitcell_array.height + self.m2_gap - for i,p in enumerate(self.vertical_port_order[port]): - if p==None: - continue - y_offset += (p.height + self.m2_gap) - self.vertical_port_offsets[port][i]=vector(0,y_offset) - - # Reversed order - self.write_driver_offsets[port] = self.vertical_port_offsets[port][3] - self.sense_amp_offsets[port] = self.vertical_port_offsets[port][2] - self.column_mux_offsets[port] = self.vertical_port_offsets[port][1] - self.precharge_offsets[port] = self.vertical_port_offsets[port][0] + self.port_data_offsets[port] = vector(self.main_bitcell_array_left, self.bitcell_array_top) # LOWER RIGHT QUADRANT # To the left of the bitcell array - # The wordline driver is placed to the right of the main decoder width. - x_offset = self.bitcell_array.width + self.m2_gap + self.wordline_driver.width - self.wordline_driver_offsets[port] = vector(x_offset,0) - x_offset += self.row_decoder.width + self.m2_gap - self.row_decoder_offsets[port] = vector(x_offset,0) + x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap + self.port_address_offsets[port] = vector(x_offset,self.main_bitcell_array_bottom) # UPPER RIGHT QUADRANT - # Place the col decoder right aligned with wordline driver plus halfway under row decoder + # Place the col decoder right aligned with wordline driver # Above the bitcell array with a well spacing - x_offset = self.bitcell_array.width + self.central_bus_width[port] + self.wordline_driver.width + x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width - y_offset = self.bitcell_array.height + self.column_decoder.height + self.m2_gap + y_offset = self.bitcell_array_top + 0.5*self.dff.height + self.column_decoder.height else: - y_offset = self.bitcell_array.height - y_offset += 2*drc("well_to_well") + y_offset = self.bitcell_array_top self.column_decoder_offsets[port] = vector(x_offset,y_offset) # Bank select gets placed above the column decoder (x_offset doesn't change) if self.col_addr_size > 0: y_offset = max(self.column_decoder_offsets[port].y + self.column_decoder.height, - self.column_mux_offsets[port].y + self.column_mux_array[port].height) + self.port_data[port].column_mux_offset.y + self.port_data[port].column_mux_array.height) else: - y_offset = self.row_decoder_offsets[port].y + y_offset = self.port_address_offsets[port].y self.bank_select_offsets[port] = vector(x_offset,y_offset) def place_instances(self): @@ -314,22 +275,12 @@ class bank(design.design): self.compute_instance_offsets() - # UPPER RIGHT QUADRANT self.place_bitcell_array(self.bitcell_array_offset) - # LOWER RIGHT QUADRANT - # These are fixed in the order: write driver, sense amp, clumn mux, precharge, - # even if the item is not used in a given port (it will be None then) - self.place_write_driver_array(self.write_driver_offsets) - self.place_sense_amp_array(self.sense_amp_offsets) - self.place_column_mux_array(self.column_mux_offsets) - self.place_precharge_array(self.precharge_offsets) + self.place_port_data(self.port_data_offsets) - # UPPER LEFT QUADRANT - self.place_row_decoder(self.row_decoder_offsets) - self.place_wordline_driver(self.wordline_driver_offsets) + self.place_port_address(self.port_address_offsets) - # LOWER LEFT QUADRANT self.place_column_decoder(self.column_decoder_offsets) self.place_bank_select(self.bank_select_offsets) @@ -347,22 +298,17 @@ class bank(design.design): debug.check(self.num_rows*self.num_cols==self.word_size*self.num_words,"Invalid bank sizes.") debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.") - # Width for the vdd/gnd rails - self.supply_rail_width = 4*self.m2_width - # FIXME: This spacing should be width dependent... - self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space - # The order of the control signals on the control bus: self.input_control_signals = [] port_num = 0 for port in range(OPTS.num_rw_ports): - self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)]) + self.input_control_signals.append(["w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num), "wl_en{}".format(port_num)]) port_num += 1 for port in range(OPTS.num_w_ports): - self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num)]) + self.input_control_signals.append(["w_en{}".format(port_num), "p_en_bar{}".format(port_num), "wl_en{}".format(port_num)]) port_num += 1 for port in range(OPTS.num_r_ports): - self.input_control_signals.append(["wl_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)]) + self.input_control_signals.append(["s_en{}".format(port_num), "p_en_bar{}".format(port_num), "wl_en{}".format(port_num)]) port_num += 1 # Number of control lines in the bus for each port @@ -394,65 +340,40 @@ class bank(design.design): def add_modules(self): """ Add all the modules using the class loader """ - - self.bitcell_array = factory.create(module_type="bitcell_array", - cols=self.num_cols, - rows=self.num_rows) - self.add_mod(self.bitcell_array) - # create arrays of bitline and bitline_bar names for read, write, or all ports self.bitcell = factory.create(module_type="bitcell") - self.bl_names = self.bitcell.list_all_bl_names() - self.br_names = self.bitcell.list_all_br_names() - self.wl_names = self.bitcell.list_all_wl_names() - self.bitline_names = self.bitcell.list_all_bitline_names() + self.bl_names = self.bitcell.get_all_bl_names() + self.br_names = self.bitcell.get_all_br_names() + self.wl_names = self.bitcell.get_all_wl_names() + self.bitline_names = self.bitcell.get_all_bitline_names() - self.precharge_array = [] + self.port_data = [] for port in self.all_ports: - if port in self.read_ports: - temp_pre = factory.create(module_type="precharge_array", - columns=self.num_cols, - bitcell_bl=self.bl_names[port], - bitcell_br=self.br_names[port]) - self.precharge_array.append(temp_pre) - self.add_mod(self.precharge_array[port]) - else: - self.precharge_array.append(None) - - if self.col_addr_size > 0: - self.column_mux_array = [] - for port in self.all_ports: - temp_col = factory.create(module_type="column_mux_array", - columns=self.num_cols, - word_size=self.word_size, - bitcell_bl=self.bl_names[port], - bitcell_br=self.br_names[port]) - self.column_mux_array.append(temp_col) - self.add_mod(self.column_mux_array[port]) + temp_pre = factory.create(module_type="port_data", + sram_config=self.sram_config, + port=port) + self.port_data.append(temp_pre) + self.add_mod(self.port_data[port]) - self.sense_amp_array = factory.create(module_type="sense_amp_array", - word_size=self.word_size, - words_per_row=self.words_per_row) - self.add_mod(self.sense_amp_array) + self.port_address = factory.create(module_type="port_address", + cols=self.num_cols, + rows=self.num_rows) + self.add_mod(self.port_address) - self.write_driver_array = factory.create(module_type="write_driver_array", - columns=self.num_cols, - word_size=self.word_size) - self.add_mod(self.write_driver_array) - self.row_decoder = factory.create(module_type="decoder", - rows=self.num_rows) - self.add_mod(self.row_decoder) + self.port_rbl_map = self.all_ports + self.num_rbl = len(self.all_ports) + + self.bitcell_array = factory.create(module_type="replica_bitcell_array", + cols=self.num_cols, + rows=self.num_rows, + left_rbl=1, + right_rbl=1 if len(self.all_ports)>1 else 0, + bitcell_ports=self.all_ports) + self.add_mod(self.bitcell_array) + - self.wordline_driver = factory.create(module_type="wordline_driver", - rows=self.num_rows, - cols=self.num_cols) - self.add_mod(self.wordline_driver) - - self.inv = factory.create(module_type="pinv") - self.add_mod(self.inv) - if(self.num_banks > 1): self.bank_select = factory.create(module_type="bank_select") self.add_mod(self.bank_select) @@ -461,17 +382,23 @@ class bank(design.design): def create_bitcell_array(self): """ Creating Bitcell Array """ - self.bitcell_array_inst=self.add_inst(name="bitcell_array", + self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array", mod=self.bitcell_array) - temp = [] for col in range(self.num_cols): for bitline in self.bitline_names: - temp.append(bitline+"_{0}".format(col)) + temp.append("{0}_{1}".format(bitline,col)) + for rbl in range(self.num_rbl): + rbl_bl_name=self.bitcell_array.get_rbl_bl_name(rbl) + temp.append(rbl_bl_name) + rbl_br_name=self.bitcell_array.get_rbl_br_name(rbl) + temp.append(rbl_br_name) for row in range(self.num_rows): for wordline in self.wl_names: - temp.append(wordline+"_{0}".format(row)) + temp.append("{0}_{1}".format(wordline,row)) + for port in self.all_ports: + temp.append("wl_en{0}".format(port)) temp.append("vdd") temp.append("gnd") self.connect_inst(temp) @@ -482,161 +409,73 @@ class bank(design.design): self.bitcell_array_inst.place(offset) - def create_precharge_array(self): - """ Creating Precharge """ - - self.precharge_array_inst = [None]*len(self.all_ports) - for port in self.read_ports: - self.precharge_array_inst[port]=self.add_inst(name="precharge_array{}".format(port), - mod=self.precharge_array[port]) - temp = [] - for i in range(self.num_cols): - temp.append(self.bl_names[port]+"_{0}".format(i)) - temp.append(self.br_names[port]+"_{0}".format(i)) - temp.extend([self.prefix+"p_en_bar{0}".format(port), "vdd"]) - self.connect_inst(temp) - - - def place_precharge_array(self, offsets): - """ Placing Precharge """ - - debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place precharge array.") - - for port in self.read_ports: - if port%2 == 1: - mirror = "MX" - else: - mirror = "R0" - self.precharge_array_inst[port].place(offset=offsets[port], mirror=mirror) - - - def create_column_mux_array(self): - """ Creating Column Mux when words_per_row > 1 . """ - self.column_mux_array_inst = [None]*len(self.all_ports) - - if self.col_addr_size == 0: - return + def create_port_data(self): + """ Creating Port Data """ + self.port_data_inst = [None]*len(self.all_ports) for port in self.all_ports: - self.column_mux_array_inst[port] = self.add_inst(name="column_mux_array{}".format(port), - mod=self.column_mux_array[port]) + self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port), + mod=self.port_data[port]) temp = [] - for col in range(self.num_cols): - temp.append(self.bl_names[port]+"_{0}".format(col)) - temp.append(self.br_names[port]+"_{0}".format(col)) - for word in range(self.words_per_row): - temp.append("sel{0}_{1}".format(port,word)) - for bit in range(self.word_size): - temp.append(self.bl_names[port]+"_out_{0}".format(bit)) - temp.append(self.br_names[port]+"_out_{0}".format(bit)) - temp.append("gnd") + rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) + rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port]) + temp.append(rbl_bl_name) + temp.append(rbl_br_name) + for col in range(self.num_cols): + temp.append("{0}_{1}".format(self.bl_names[port],col)) + temp.append("{0}_{1}".format(self.br_names[port],col)) + if port in self.read_ports: + for bit in range(self.word_size): + temp.append("dout{0}_{1}".format(port,bit)) + if port in self.write_ports: + for bit in range(self.word_size): + temp.append("din{0}_{1}".format(port,bit)) + # Will be empty if no col addr lines + sel_names = ["sel{0}_{1}".format(port,x) for x in range(self.num_col_addr_lines)] + temp.extend(sel_names) + if port in self.read_ports: + temp.append("s_en{0}".format(port)) + temp.append("p_en_bar{0}".format(port)) + if port in self.write_ports: + temp.append("w_en{0}".format(port)) + for bit in range(self.num_wmasks): + temp.append("bank_wmask{0}_{1}".format(port, bit)) + temp.extend(["vdd","gnd"]) + self.connect_inst(temp) - - def place_column_mux_array(self, offsets): - """ Placing Column Mux when words_per_row > 1 . """ - if self.col_addr_size == 0: - return - - debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column mux array.") + def place_port_data(self, offsets): + """ Placing Port Data """ for port in self.all_ports: + # Top one is unflipped, bottom is flipped along X direction if port%2 == 1: - mirror = "MX" - else: mirror = "R0" - self.column_mux_array_inst[port].place(offset=offsets[port], mirror=mirror) - - - def create_sense_amp_array(self): - """ Creating Sense amp """ - - self.sense_amp_array_inst = [None]*len(self.all_ports) - for port in self.read_ports: - self.sense_amp_array_inst[port] = self.add_inst(name="sense_amp_array{}".format(port), - mod=self.sense_amp_array) - - temp = [] - for bit in range(self.word_size): - temp.append("dout{0}_{1}".format(port,bit)) - if self.words_per_row == 1: - temp.append(self.bl_names[port]+"_{0}".format(bit)) - temp.append(self.br_names[port]+"_{0}".format(bit)) - else: - temp.append(self.bl_names[port]+"_out_{0}".format(bit)) - temp.append(self.br_names[port]+"_out_{0}".format(bit)) - - temp.extend([self.prefix+"s_en{}".format(port), "vdd", "gnd"]) - self.connect_inst(temp) - - - def place_sense_amp_array(self, offsets): - """ Placing Sense amp """ - - debug.check(len(offsets)>=len(self.read_ports), "Insufficient offsets to place sense amp array.") - for port in self.read_ports: - if port%2 == 1: - mirror = "MX" else: - mirror = "R0" - self.sense_amp_array_inst[port].place(offset=offsets[port], mirror=mirror) - - - def create_write_driver_array(self): - """ Creating Write Driver """ - - self.write_driver_array_inst = [None]*len(self.all_ports) - for port in self.write_ports: - self.write_driver_array_inst[port] = self.add_inst(name="write_driver_array{}".format(port), - mod=self.write_driver_array) - - temp = [] - for bit in range(self.word_size): - temp.append("din{0}_{1}".format(port,bit)) - for bit in range(self.word_size): - if (self.words_per_row == 1): - temp.append(self.bl_names[port]+"_{0}".format(bit)) - temp.append(self.br_names[port]+"_{0}".format(bit)) - else: - temp.append(self.bl_names[port]+"_out_{0}".format(bit)) - temp.append(self.br_names[port]+"_out_{0}".format(bit)) - temp.extend([self.prefix+"w_en{0}".format(port), "vdd", "gnd"]) - self.connect_inst(temp) - - - def place_write_driver_array(self, offsets): - """ Placing Write Driver """ - - debug.check(len(offsets)>=len(self.write_ports), "Insufficient offsets to place write driver array.") - - for port in self.write_ports: - if port%2 == 1: mirror = "MX" - else: - mirror = "R0" - self.write_driver_array_inst[port].place(offset=offsets[port], mirror=mirror) - + self.port_data_inst[port].place(offset=offsets[port], mirror=mirror) - def create_row_decoder(self): + def create_port_address(self): """ Create the hierarchical row decoder """ - self.row_decoder_inst = [None]*len(self.all_ports) + self.port_address_inst = [None]*len(self.all_ports) for port in self.all_ports: - self.row_decoder_inst[port] = self.add_inst(name="row_decoder{}".format(port), - mod=self.row_decoder) + self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port), + mod=self.port_address) temp = [] for bit in range(self.row_addr_size): temp.append("addr{0}_{1}".format(port,bit+self.col_addr_size)) + temp.append("wl_en{0}".format(port)) for row in range(self.num_rows): - temp.append("dec_out{0}_{1}".format(port,row)) + temp.append("{0}_{1}".format(self.wl_names[port],row)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def place_row_decoder(self, offsets): + def place_port_address(self, offsets): """ Place the hierarchical row decoder """ debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.") @@ -648,43 +487,11 @@ class bank(design.design): # The address flop and decoder are aligned in the x coord. for port in self.all_ports: - if port%2 == 1: + if port%2: mirror = "MY" else: mirror = "R0" - self.row_decoder_inst[port].place(offset=offsets[port], mirror=mirror) - - - def create_wordline_driver(self): - """ Create the Wordline Driver """ - - self.wordline_driver_inst = [None]*len(self.all_ports) - for port in self.all_ports: - self.wordline_driver_inst[port] = self.add_inst(name="wordline_driver{}".format(port), - mod=self.wordline_driver) - - temp = [] - for row in range(self.num_rows): - temp.append("dec_out{0}_{1}".format(port,row)) - for row in range(self.num_rows): - temp.append(self.wl_names[port]+"_{0}".format(row)) - temp.append(self.prefix+"wl_en{0}".format(port)) - temp.append("vdd") - temp.append("gnd") - self.connect_inst(temp) - - - def place_wordline_driver(self, offsets): - """ Place the Wordline Driver """ - - debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place wordline driver array.") - - for port in self.all_ports: - if port%2 == 1: - mirror = "MY" - else: - mirror = "R0" - self.wordline_driver_inst[port].place(offset=offsets[port], mirror=mirror) + self.port_address_inst[port].place(offset=offsets[port], mirror=mirror) def create_column_decoder(self): @@ -692,16 +499,16 @@ class bank(design.design): Create a 2:4 or 3:8 column address decoder. """ - dff = factory.create(module_type="dff") + self.dff = factory.create(module_type="dff") if self.col_addr_size == 0: return elif self.col_addr_size == 1: - self.column_decoder = factory.create(module_type="pinvbuf", height=dff.height) + self.column_decoder = factory.create(module_type="pinvbuf", height=self.dff.height) elif self.col_addr_size == 2: - self.column_decoder = factory.create(module_type="hierarchical_predecode2x4", height=dff.height) + self.column_decoder = factory.create(module_type="hierarchical_predecode2x4", height=self.dff.height) elif self.col_addr_size == 3: - self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=dff.height) + self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=self.dff.height) else: # No error checking before? debug.error("Invalid column decoder?",-1) @@ -783,8 +590,8 @@ class bank(design.design): bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"] gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"] elif self.port_id[port] == "w": - bank_sel_signals = ["clk_buf", "w_en", "bank_sel"] - gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en"] + bank_sel_signals = ["clk_buf", "w_en", "p_en_bar", "bank_sel"] + gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_p_en_bar"] else: bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"] gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"] @@ -800,14 +607,11 @@ class bank(design.design): bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y) self.add_path("metal3",[out_pos, bus_pos]) self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=bus_pos, - rotate=90) + offset=bus_pos) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=out_pos, - rotate=90) + offset=out_pos) self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=out_pos, - rotate=90) + offset=out_pos) def setup_routing_constraints(self): @@ -841,9 +645,9 @@ class bank(design.design): # Port 0 # The bank is at (0,0), so this is to the left of the y-axis. # 2 pitches on the right for vias/jogs to access the inputs - control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_width, self.min_y_offset) + control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_pitch, self.min_y_offset) # The control bus is routed up to two pitches below the bitcell array - control_bus_length = -2*self.m1_pitch - self.min_y_offset + control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2*self.m1_pitch self.bus_xoffset[0] = self.create_bus(layer="metal2", pitch=self.m2_pitch, offset=control_bus_offset, @@ -855,122 +659,52 @@ class bank(design.design): # Port 1 if len(self.all_ports)==2: # The other control bus is routed up to two pitches above the bitcell array - control_bus_length = self.max_y_offset - self.bitcell_array.height - 2*self.m1_pitch - control_bus_offset = vector(self.bitcell_array.width + self.m2_width, + control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2*self.m1_pitch + control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch, self.max_y_offset - control_bus_length) - + # The bus for the right port is reversed so that the rbl_wl is closest to the array self.bus_xoffset[1] = self.create_bus(layer="metal2", pitch=self.m2_pitch, offset=control_bus_offset, - names=self.control_signals[1], + names=list(reversed(self.control_signals[1])), length=control_bus_length, vertical=True, make_pins=(self.num_banks==1)) - def route_precharge_to_bitcell_array(self, port): - """ Routing of BL and BR between pre-charge and bitcell array """ + def route_port_data_to_bitcell_array(self, port): + """ Routing of BL and BR between port data and bitcell array """ - inst2 = self.precharge_array_inst[port] + # Connect the regular bitlines + inst2 = self.port_data_inst[port] inst1 = self.bitcell_array_inst inst1_bl_name = self.bl_names[port]+"_{}" inst1_br_name = self.br_names[port]+"_{}" + self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) + + # Connect the replica bitlines + rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) + rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port]) + self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl") + self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br") - - def route_column_mux_to_precharge_array(self, port): - """ Routing of BL and BR between col mux and precharge array """ - - # Only do this if we have a column mux! - if self.col_addr_size==0: - return - inst1 = self.column_mux_array_inst[port] - inst2 = self.precharge_array_inst[port] - self.connect_bitlines(inst1, inst2, self.num_cols) + def route_port_data_out(self, port): + """ Add pins for the port data out """ - def route_column_mux_to_bitcell_array(self, port): - """ Routing of BL and BR between col mux bitcell array """ - - # Only do this if we have a column mux! - if self.col_addr_size==0: - return - - inst2 = self.column_mux_array_inst[port] - inst1 = self.bitcell_array_inst - inst1_bl_name = self.bl_names[port]+"_{}" - inst1_br_name = self.br_names[port]+"_{}" - - # The column mux is constructed to match the bitline pitch, so we can directly connect - # here and not channel route the bitlines. - self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, - inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) - - - - def route_sense_amp_to_column_mux_or_precharge_array(self, port): - """ Routing of BL and BR between sense_amp and column mux or precharge array """ - inst2 = self.sense_amp_array_inst[port] - - if self.col_addr_size>0: - # Sense amp is connected to the col mux - inst1 = self.column_mux_array_inst[port] - inst1_bl_name = "bl_out_{}" - inst1_br_name = "br_out_{}" - else: - # Sense amp is directly connected to the precharge array - inst1 = self.precharge_array_inst[port] - inst1_bl_name = "bl_{}" - inst1_br_name = "br_{}" - - self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, - inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) - - def route_write_driver_to_column_mux_or_bitcell_array(self, port): - """ Routing of BL and BR between sense_amp and column mux or bitcell array """ - inst2 = self.write_driver_array_inst[port] - - if self.col_addr_size>0: - # Write driver is connected to the col mux - inst1 = self.column_mux_array_inst[port] - inst1_bl_name = "bl_out_{}" - inst1_br_name = "br_out_{}" - else: - # Write driver is directly connected to the bitcell array - inst1 = self.bitcell_array_inst - inst1_bl_name = self.bl_names[port]+"_{}" - inst1_br_name = self.br_names[port]+"_{}" - - self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, - inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) - - def route_write_driver_to_sense_amp(self, port): - """ Routing of BL and BR between write driver and sense amp """ - - inst1 = self.write_driver_array_inst[port] - inst2 = self.sense_amp_array_inst[port] - - # These should be pitch matched in the cell library, - # but just in case, do a channel route. - self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size) - - - - def route_sense_amp_out(self, port): - """ Add pins for the sense amp output """ - for bit in range(self.word_size): - data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit)) + data_pin = self.port_data_inst[port].get_pin("dout_{0}".format(bit)) self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port,bit), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), width=data_pin.width()) + - - def route_row_decoder(self, port): + def route_port_address_in(self, port): """ Routes the row decoder inputs and supplies """ # Create inputs for the row address lines @@ -978,16 +712,25 @@ class bank(design.design): addr_idx = row + self.col_addr_size decoder_name = "addr_{}".format(row) addr_name = "addr{0}_{1}".format(port,addr_idx) - self.copy_layout_pin(self.row_decoder_inst[port], decoder_name, addr_name) - - - def route_write_driver_in(self, port): - """ Connecting write driver """ + self.copy_layout_pin(self.port_address_inst[port], decoder_name, addr_name) + + + + def route_port_data_in(self, port): + """ Connecting port data in """ for row in range(self.word_size): - data_name = "data_{}".format(row) + data_name = "din_{}".format(row) din_name = "din{0}_{1}".format(port,row) - self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name) + self.copy_layout_pin(self.port_data_inst[port], data_name, din_name) + + if self.word_size: + for row in range(self.num_wmasks): + wmask_name = "bank_wmask_{}".format(row) + bank_wmask_name = "bank_wmask{0}_{1}".format(port, row) + self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name) + + def channel_route_bitlines(self, inst1, inst2, num_bits, inst1_bl_name="bl_{}", inst1_br_name="br_{}", @@ -1015,12 +758,9 @@ class bank(design.design): route_map = list(zip(bottom_names, top_names)) self.create_horizontal_channel_route(route_map, offset) - - def connect_bitlines(self, inst1, inst2, num_bits, - inst1_bl_name="bl_{}", inst1_br_name="br_{}", - inst2_bl_name="bl_{}", inst2_br_name="br_{}"): + def connect_bitline(self, inst1, inst2, inst1_name, inst2_name): """ - Connect the bl and br of two modules. + Connect two pins of two modules. This assumes that they have sufficient space to create a jog in the middle between the two modules (if needed). """ @@ -1028,66 +768,66 @@ class bank(design.design): # determine top and bottom automatically. # since they don't overlap, we can just check the bottom y coordinate. if inst1.by() < inst2.by(): - (bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name) - (top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name) + (bottom_inst, bottom_name) = (inst1, inst1_name) + (top_inst, top_name) = (inst2, inst2_name) else: - (bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name) - (top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name) + (bottom_inst, bottom_name) = (inst2, inst2_name) + (top_inst, top_name) = (inst1, inst1_name) - for col in range(num_bits): - bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col)).uc() - bottom_br = bottom_inst.get_pin(bottom_br_name.format(col)).uc() - top_bl = top_inst.get_pin(top_bl_name.format(col)).bc() - top_br = top_inst.get_pin(top_br_name.format(col)).bc() - - yoffset = 0.5*(top_bl.y+bottom_bl.y) - self.add_path("metal2",[bottom_bl, vector(bottom_bl.x,yoffset), - vector(top_bl.x,yoffset), top_bl]) - self.add_path("metal2",[bottom_br, vector(bottom_br.x,yoffset), - vector(top_br.x,yoffset), top_br]) + bottom_pin = bottom_inst.get_pin(bottom_name) + top_pin = top_inst.get_pin(top_name) + debug.check(bottom_pin.layer == top_pin.layer, "Pin layers do not match.") + + bottom_loc = bottom_pin.uc() + top_loc = top_pin.bc() + + yoffset = 0.5*(top_loc.y+bottom_loc.y) + self.add_path(top_pin.layer,[bottom_loc, vector(bottom_loc.x,yoffset), + vector(top_loc.x,yoffset), top_loc]) - def route_wordline_driver(self, port): + def connect_bitlines(self, inst1, inst2, num_bits, + inst1_bl_name="bl_{}", inst1_br_name="br_{}", + inst2_bl_name="bl_{}", inst2_br_name="br_{}"): + """ + Connect the bl and br of two modules. + """ + + for col in range(num_bits): + self.connect_bitline(inst1, inst2, inst1_bl_name.format(col), inst2_bl_name.format(col)) + self.connect_bitline(inst1, inst2, inst1_br_name.format(col), inst2_br_name.format(col)) + + + def route_port_address(self, port): """ Connect Wordline driver to bitcell array wordline """ + + self.route_port_address_in(port) + if port%2: - self.route_wordline_driver_right(port) + self.route_port_address_right(port) else: - self.route_wordline_driver_left(port) + self.route_port_address_left(port) - def route_wordline_driver_left(self, port): + def route_port_address_left(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ for row in range(self.num_rows): - # The pre/post is to access the pin from "outside" the cell to avoid DRCs - decoder_out_pos = self.row_decoder_inst[port].get_pin("decode_{}".format(row)).rc() - driver_in_pos = self.wordline_driver_inst[port].get_pin("in_{}".format(row)).lc() - mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) - mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) - self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) - # The mid guarantees we exit the input cell to the right. - driver_wl_pos = self.wordline_driver_inst[port].get_pin("wl_{}".format(row)).rc() + driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).rc() bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).lc() - mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.wordline_driver_inst[port].rx() + 0.5*self.bitcell_array_inst.lx(),0) + mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].rx() + 0.5*self.bitcell_array_inst.lx(),0) mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0.5,1) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) - def route_wordline_driver_right(self, port): + def route_port_address_right(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ for row in range(self.num_rows): - # The pre/post is to access the pin from "outside" the cell to avoid DRCs - decoder_out_pos = self.row_decoder_inst[port].get_pin("decode_{}".format(row)).lc() - driver_in_pos = self.wordline_driver_inst[port].get_pin("in_{}".format(row)).rc() - mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) - mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) - self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) - # The mid guarantees we exit the input cell to the right. - driver_wl_pos = self.wordline_driver_inst[port].get_pin("wl_{}".format(row)).lc() + driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).lc() bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).rc() - mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.wordline_driver_inst[port].lx() + 0.5*self.bitcell_array_inst.rx(),0) + mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].lx() + 0.5*self.bitcell_array_inst.rx(),0) mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0,1) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) @@ -1122,7 +862,7 @@ class bank(design.design): decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names] sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] - column_mux_pins = [self.column_mux_array_inst[port].get_pin(x) for x in sel_names] + column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names] route_map = list(zip(decode_pins, column_mux_pins)) self.create_vertical_channel_route(route_map, offset) @@ -1184,86 +924,61 @@ class bank(design.design): read_inst = 0 connection = [] - if port in self.read_ports: - connection.append((self.prefix+"p_en_bar{}".format(port), self.precharge_array_inst[port].get_pin("en_bar").lc())) - - if port in self.write_ports: - connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc())) - - if port in self.read_ports: - connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[port].get_pin("en").lc())) - - for (control_signal, pin_pos) in connection: - control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y) - self.add_path("metal1", [control_pos, pin_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=control_pos, - rotate=90) + connection.append((self.prefix+"p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar").lc())) + rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port]) + connection.append((self.prefix+"wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc())) + + if port in self.write_ports: + if port % 2: + connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").rc())) + else: + connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").lc())) + + if port in self.read_ports: + connection.append((self.prefix+"s_en{}".format(port), self.port_data_inst[port].get_pin("s_en").lc())) + + for (control_signal, pin_pos) in connection: + control_mid_pos = self.bus_xoffset[port][control_signal] + control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y) + self.add_wire(("metal1","via1","metal2"), [control_mid_pos, control_pos, pin_pos]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=control_pos) + + # clk to wordline_driver control_signal = self.prefix+"wl_en{}".format(port) if port%2: - pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").uc() - mid_pos = pin_pos + vector(0,self.m2_gap) # to route down to the top of the bus + pin_pos = self.port_address_inst[port].get_pin("wl_en").uc() + mid_pos = pin_pos + vector(0,2*self.m2_gap) # to route down to the top of the bus else: - pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").bc() - mid_pos = pin_pos - vector(0,self.m2_gap) # to route down to the top of the bus + pin_pos = self.port_address_inst[port].get_pin("wl_en").bc() + mid_pos = pin_pos - vector(0,2*self.m2_gap) # to route down to the top of the bus control_x_offset = self.bus_xoffset[port][control_signal].x control_pos = vector(control_x_offset, mid_pos.y) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=control_pos, - rotate=90) - - - def analytical_delay(self, corner, slew, load): - """ return analytical delay of the bank""" - results = [] - - decoder_delay = self.row_decoder.analytical_delay(corner, slew, self.wordline_driver.input_load()) - - word_driver_delay = self.wordline_driver.analytical_delay(corner, - decoder_delay.slew, - self.bitcell_array.input_load()) - - #FIXME: Array delay is the same for every port. - bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_delay.slew) - - #This also essentially creates the same delay for each port. Good structure, no substance - for port in self.all_ports: - if self.words_per_row > 1: - column_mux_delay = self.column_mux_array[port].analytical_delay(corner, - 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(corner, - 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. - results.append(decoder_delay + word_driver_delay + bitcell_array_delay + column_mux_delay + bl_t_data_out_delay) - - return results - + offset=control_pos) + def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): """Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline""" #Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption stage_effort_list = [] wordline_cout = self.bitcell_array.get_wordline_cin() + external_cout - stage_effort_list += self.wordline_driver.determine_wordline_stage_efforts(wordline_cout,inp_is_rise) + stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout,inp_is_rise) return stage_effort_list def get_wl_en_cin(self): """Get the relative capacitance of all the clk connections in the bank""" #wl_en only used in the wordline driver. - return self.wordline_driver.get_wl_en_cin() + return self.port_address.wordline_driver.get_wl_en_cin() def get_w_en_cin(self): """Get the relative capacitance of all the clk connections in the bank""" #wl_en only used in the wordline driver. - return self.write_driver.get_w_en_cin() + port = self.write_ports[0] + return self.port_data[port].write_driver.get_w_en_cin() def get_clk_bar_cin(self): """Get the relative capacitance of all the clk_bar connections in the bank""" @@ -1271,9 +986,21 @@ class bank(design.design): #Precharges are the all the same in Mulitport, one is picked port = self.read_ports[0] - return self.precharge_array[port].get_en_cin() + return self.port_data[port].precharge_array.get_en_cin() def get_sen_cin(self): """Get the relative capacitance of all the sense amp enable connections in the bank""" #Current bank only uses sen as an enable for the sense amps. - return self.sense_amp_array.get_en_cin() + port = self.read_ports[0] + return self.port_data[port].sense_amp_array.get_en_cin() + + def graph_exclude_precharge(self): + """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" + for port in self.read_ports: + if self.port_data[port]: + self.port_data[port].graph_exclude_precharge() + + def get_cell_name(self, inst_name, row, col): + """Gets the spice name of the target bitcell.""" + return self.bitcell_array_inst.mod.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col) + diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 8e44dfa2..296cef8b 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import sys from tech import drc, parameter import debug @@ -35,6 +42,7 @@ class bank_select(design.design): self.place_instances() self.route_instances() + self.add_boundary() self.DRC_LVS() @@ -81,7 +89,7 @@ class bank_select(design.design): self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4) self.add_mod(self.inv4x_nor) - self.nand2 = factory.create(module_type="pnand2") + self.nand2 = factory.create(module_type="pnand2", height=height) self.add_mod(self.nand2) def calculate_module_offsets(self): @@ -213,7 +221,7 @@ class bank_select(design.design): end=bank_sel_pin_end) self.add_via_center(layers=("metal2","via2","metal3"), offset=bank_sel_pin_end, - rotate=90) + directions=("H","H")) # bank_sel_bar is vertical wire bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z") @@ -252,7 +260,7 @@ class bank_select(design.design): self.add_path("metal2",[logic_pos, input_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=logic_pos, - rotate=90) + directions=("H","H")) # Connect the logic A input to the input pin @@ -260,10 +268,10 @@ class bank_select(design.design): input_pos = vector(0,logic_pos.y) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=logic_pos, - rotate=90) + directions=("H","H")) self.add_via_center(layers=("metal2", "via2", "metal3"), offset=logic_pos, - rotate=90) + directions=("H","H")) self.add_layout_pin_segment_center(text=input_name, layer="metal3", start=input_pos, @@ -295,10 +303,10 @@ class bank_select(design.design): pin_pos = vector(xoffset, supply_pin.cy()) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=pin_pos, - rotate=90) + directions=("H","H")) self.add_via_center(layers=("metal2", "via2", "metal3"), offset=pin_pos, - rotate=90) + directions=("H","H")) self.add_layout_pin_rect_center(text=n, layer="metal3", offset=pin_pos) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index ea8fc3f2..b1b61487 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -1,9 +1,17 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import design from tech import drc, spice from vector import vector from globals import OPTS from sram_factory import factory +import logical_effort class bitcell_array(design.design): """ @@ -37,8 +45,8 @@ 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.width = self.column_size*self.cell.width + self.m1_width + self.height = self.row_size*self.cell.height + self.width = self.column_size*self.cell.width xoffset = 0.0 for col in range(self.column_size): @@ -60,25 +68,45 @@ class bitcell_array(design.design): self.add_layout_pins() + self.add_boundary() + self.DRC_LVS() def add_pins(self): - row_list = self.cell.list_all_wl_names() - column_list = self.cell.list_all_bitline_names() + row_list = self.cell.get_all_wl_names() + column_list = self.cell.get_all_bitline_names() for col in range(self.column_size): for cell_column in column_list: - self.add_pin(cell_column+"_{0}".format(col)) + self.add_pin(cell_column+"_{0}".format(col), "INOUT") for row in range(self.row_size): for cell_row in row_list: - self.add_pin(cell_row+"_{0}".format(row)) - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin(cell_row+"_{0}".format(row), "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def add_modules(self): """ Add the modules used in this design """ self.cell = factory.create(module_type="bitcell") self.add_mod(self.cell) + def get_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 = [] + + pin_names = self.cell.get_all_bitline_names() + for pin in pin_names: + bitcell_pins.append(pin+"_{0}".format(col)) + pin_names = self.cell.get_all_wl_names() + for pin in pin_names: + bitcell_pins.append(pin+"_{0}".format(row)) + bitcell_pins.append("vdd") + bitcell_pins.append("gnd") + + return bitcell_pins + + def create_instances(self): """ Create the module instances used in this design """ self.cell_inst = {} @@ -87,66 +115,40 @@ class bitcell_array(design.design): name = "bit_r{0}_c{1}".format(row, col) self.cell_inst[row,col]=self.add_inst(name=name, mod=self.cell) - self.connect_inst(self.cell.list_bitcell_pins(col, row)) + self.connect_inst(self.get_bitcell_pins(col, row)) def add_layout_pins(self): """ Add the layout pins """ - row_list = self.cell.list_all_wl_names() - column_list = self.cell.list_all_bitline_names() + row_list = self.cell.get_all_wl_names() + column_list = self.cell.get_all_bitline_names() - offset = vector(0.0, 0.0) for col in range(self.column_size): for cell_column in column_list: bl_pin = self.cell_inst[0,col].get_pin(cell_column) self.add_layout_pin(text=cell_column+"_{0}".format(col), - layer="metal2", - offset=bl_pin.ll(), + layer=bl_pin.layer, + offset=bl_pin.ll().scale(1,0), width=bl_pin.width(), height=self.height) - - # increments to the next column width - offset.x += self.cell.width - offset.x = 0.0 for row in range(self.row_size): for cell_row in row_list: wl_pin = self.cell_inst[row,0].get_pin(cell_row) self.add_layout_pin(text=cell_row+"_{0}".format(row), - layer="metal1", - offset=wl_pin.ll(), + layer=wl_pin.layer, + offset=wl_pin.ll().scale(0,1), width=self.width, height=wl_pin.height()) - # increments to the next row height - offset.y += self.cell.height - # For every second row and column, add a via for gnd and vdd for row in range(self.row_size): for col in range(self.column_size): inst = self.cell_inst[row,col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): - self.add_power_pin(pin_name, pin.center(), 0, pin.layer) - + self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) - def analytical_delay(self, corner, slew, load=0): - from tech import drc - wl_wire = self.gen_wl_wire() - wl_wire.return_delay_over_wire(slew) - - wl_to_cell_delay = wl_wire.return_delay_over_wire(slew) - # hypothetical delay from cell to bl end without sense amp - bl_wire = self.gen_bl_wire() - cell_load = 2 * bl_wire.return_input_cap() # we ingore the wire r - # hence just use the whole c - bl_swing = 0.1 - cell_delay = self.cell.analytical_delay(corner, wl_to_cell_delay.slew, cell_load, swing = bl_swing) - - #we do not consider the delay over the wire for now - return self.return_delay(cell_delay.delay+wl_to_cell_delay.delay, - wl_to_cell_delay.slew) - def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" from tech import drc, parameter @@ -154,8 +156,8 @@ class bitcell_array(design.design): # Dynamic Power from Bitline bl_wire = self.gen_bl_wire() cell_load = 2 * bl_wire.return_input_cap() - bl_swing = parameter["rbl_height_percentage"] - freq = spice["default_event_rate"] + bl_swing = OPTS.rbl_delay_percentage + freq = spice["default_event_frequency"] bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) #Calculate the bitcell power which currently only includes leakage @@ -185,18 +187,22 @@ class bitcell_array(design.design): bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire - def output_load(self, bl_pos=0): - bl_wire = self.gen_bl_wire() - return bl_wire.wire_c # sense amp only need to charge small portion of the bl - # set as one segment for now - - def input_load(self): - wl_wire = self.gen_wl_wire() - return wl_wire.return_input_cap() - def get_wordline_cin(self): """Get the relative input capacitance from the wordline connections in all the bitcell""" #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns bitcell_wl_cin = self.cell.get_wl_cin() total_cin = bitcell_wl_cin * self.column_size return total_cin + + def graph_exclude_bits(self, targ_row, targ_col): + """Excludes bits in column from being added to graph except target""" + #Function is not robust with column mux configurations + for row in range(self.row_size): + for col in range(self.column_size): + if row == targ_row and col == targ_col: + continue + self.graph_inst_exclude.add(self.cell_inst[row,col]) + + def get_cell_name(self, inst_name, row, col): + """Gets the spice name of the target bitcell.""" + return inst_name+'.x'+self.cell_inst[row,col].name, self.cell_inst[row,col] diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index b8532ee9..06883125 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from math import log import design from tech import drc, parameter @@ -14,7 +21,7 @@ class control_logic(design.design): Dynamically generated Control logic for the total SRAM circuit. """ - def __init__(self, num_rows, words_per_row, word_size, sram=None, port_type="rw"): + def __init__(self, num_rows, words_per_row, word_size, sram=None, port_type="rw", name=""): """ Constructor """ name = "control_logic_" + port_type design.design.__init__(self, name) @@ -32,11 +39,11 @@ class control_logic(design.design): self.num_cols = word_size*words_per_row self.num_words = num_rows*words_per_row - self.enable_delay_chain_resizing = True + self.enable_delay_chain_resizing = False + self.inv_parasitic_delay = logical_effort.logical_effort.pinv #Determines how much larger the sen delay should be. Accounts for possible error in model. self.wl_timing_tolerance = 1 - self.parasitic_inv_delay = parameter["min_inv_para_delay"] self.wl_stage_efforts = None self.sen_stage_efforts = None @@ -60,15 +67,14 @@ class control_logic(design.design): self.place_instances() self.route_all() #self.add_lvs_correspondence_points() + self.add_boundary() self.DRC_LVS() def add_pins(self): """ Add the pins to the control logic module. """ - for pin in self.input_list + ["clk"]: - self.add_pin(pin,"INPUT") - for pin in self.output_list: - self.add_pin(pin,"OUTPUT") + self.add_pin_list(self.input_list + ["clk"] + self.rbl_list, "INPUT") + self.add_pin_list(self.output_list,"OUTPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") @@ -88,18 +94,30 @@ class control_logic(design.design): size=4, height=dff_height) self.add_mod(self.and2) + + self.rbl_driver = factory.create(module_type="pbuf", + size=self.num_cols, + height=dff_height) + self.add_mod(self.rbl_driver) - # clk_buf drives a flop for every address and control bit + + # clk_buf drives a flop for every address + addr_flops = math.log(self.num_words,2) + math.log(self.words_per_row,2) + # plus data flops and control flops + num_flops = addr_flops + self.word_size + self.num_control_signals + # each flop internally has a FO 5 approximately # plus about 5 fanouts for the control logic - # each flop internally has a FO 4 approximately - clock_fanout = 4*(math.log(self.num_words,2) + math.log(self.words_per_row,2) \ - + self.num_control_signals) + 5 + clock_fanout = 5*num_flops + 5 self.clk_buf_driver = factory.create(module_type="pdriver", fanout=clock_fanout, height=dff_height) self.add_mod(self.clk_buf_driver) + # We will use the maximum since this same value is used to size the wl_en + # and the p_en_bar drivers + max_fanout = max(self.num_rows,self.num_cols) + # wl_en drives every row in the bank self.wl_en_driver = factory.create(module_type="pdriver", fanout=self.num_rows, @@ -107,70 +125,79 @@ class control_logic(design.design): self.add_mod(self.wl_en_driver) # w_en drives every write driver - self.w_en_driver = factory.create(module_type="pdriver", - fanout=self.word_size+8, - height=dff_height) - self.add_mod(self.w_en_driver) + self.wen_and = factory.create(module_type="pand3", + size=self.word_size+8, + height=dff_height) + self.add_mod(self.wen_and) # s_en drives every sense amp - self.s_en_driver = factory.create(module_type="pdriver", - fanout=self.word_size, - height=dff_height) - self.add_mod(self.s_en_driver) + self.sen_and3 = factory.create(module_type="pand3", + size=self.word_size, + height=dff_height) + self.add_mod(self.sen_and3) # used to generate inverted signals with low fanout self.inv = factory.create(module_type="pinv", - size=1, - height=dff_height) + size=1, + height=dff_height) self.add_mod(self.inv) - - # p_en_bar drives every column in the bicell array + + # p_en_bar drives every column in the bitcell array + # but it is sized the same as the wl_en driver with + # prepended 3 inverter stages to guarantee it is slower and odd polarity self.p_en_bar_driver = factory.create(module_type="pdriver", - neg_polarity=True, fanout=self.num_cols, height=dff_height) self.add_mod(self.p_en_bar_driver) + + + self.nand2 = factory.create(module_type="pnand2", + height=dff_height) + self.add_mod(self.nand2) - if (self.port_type == "rw") or (self.port_type == "r"): - from importlib import reload - self.delay_chain_resized = False - c = reload(__import__(OPTS.replica_bitline)) - replica_bitline = getattr(c, OPTS.replica_bitline) - bitcell_loads = int(math.ceil(self.num_rows * parameter["rbl_height_percentage"])) - #Use a model to determine the delays with that heuristic - if OPTS.use_tech_delay_chain_size: #Use tech parameters if set. - fanout_list = parameter["static_fanout_list"] - debug.info(1, "Using tech parameters to size delay chain: fanout_list={}".format(fanout_list)) - self.replica_bitline = factory.create(module_type="replica_bitline", - delay_fanout_list=fanout_list, - bitcell_loads=bitcell_loads) - if self.sram != None: #Calculate model value even for specified sizes - self.set_sen_wl_delays() + # if (self.port_type == "rw") or (self.port_type == "r"): + # from importlib import reload + # self.delay_chain_resized = False + # c = reload(__import__(OPTS.replica_bitline)) + # replica_bitline = getattr(c, OPTS.replica_bitline) + # bitcell_loads = int(math.ceil(self.num_rows * OPTS.rbl_delay_percentage)) + # #Use a model to determine the delays with that heuristic + # if OPTS.use_tech_delay_chain_size: #Use tech parameters if set. + # fanout_list = OPTS.delay_chain_stages*[OPTS.delay_chain_fanout_per_stage] + # debug.info(1, "Using tech parameters to size delay chain: fanout_list={}".format(fanout_list)) + # self.replica_bitline = factory.create(module_type="replica_bitline", + # delay_fanout_list=fanout_list, + # bitcell_loads=bitcell_loads) + # if self.sram != None: #Calculate model value even for specified sizes + # self.set_sen_wl_delays() - else: #Otherwise, use a heuristic and/or model based sizing. - #First use a heuristic - delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size() - self.replica_bitline = factory.create(module_type="replica_bitline", - delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic, - bitcell_loads=bitcell_loads) - #Resize if necessary, condition depends on resizing method - if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match(): - #This resizes to match fall and rise delays, can make the delay chain weird sizes. - stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) - self.replica_bitline = factory.create(module_type="replica_bitline", - delay_fanout_list=stage_list, - bitcell_loads=bitcell_loads) + # else: #Otherwise, use a heuristic and/or model based sizing. + # #First use a heuristic + # delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size() + # self.replica_bitline = factory.create(module_type="replica_bitline", + # delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic, + # bitcell_loads=bitcell_loads) + # #Resize if necessary, condition depends on resizing method + # if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match(): + # #This resizes to match fall and rise delays, can make the delay chain weird sizes. + # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) + # self.replica_bitline = factory.create(module_type="replica_bitline", + # delay_fanout_list=stage_list, + # bitcell_loads=bitcell_loads) - #This resizes based on total delay. - # delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) - # self.replica_bitline = factory.create(module_type="replica_bitline", - # delay_fanout_list=[delay_fanout]*delay_stages, - # bitcell_loads=bitcell_loads) + # #This resizes based on total delay. + # # delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) + # # self.replica_bitline = factory.create(module_type="replica_bitline", + # # delay_fanout_list=[delay_fanout]*delay_stages, + # # bitcell_loads=bitcell_loads) - self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing - self.delay_chain_resized = True - - self.add_mod(self.replica_bitline) + # self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing + # self.delay_chain_resized = True + + debug.check(OPTS.delay_chain_stages%2, "Must use odd number of delay chain stages for inverting delay chain.") + self.delay_chain=factory.create(module_type="delay_chain", + fanout_list = OPTS.delay_chain_stages*[OPTS.delay_chain_fanout_per_stage]) + self.add_mod(self.delay_chain) def get_heuristic_delay_chain_size(self): """Use a basic heuristic to determine the size of the delay chain used for the Sense Amp Enable """ @@ -219,7 +246,7 @@ class control_logic(design.design): def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout): """Determine the size of the delay chain used for the Sense Amp Enable using path delays""" from math import ceil - previous_delay_chain_delay = (previous_fanout+1+self.parasitic_inv_delay)*previous_stages + previous_delay_chain_delay = (previous_fanout+1+self.inv_parasitic_delay)*previous_stages debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) delay_fanout = 3 # This can be anything >=2 @@ -227,7 +254,7 @@ class control_logic(design.design): #inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value required_delay = self.wl_delay*self.wl_timing_tolerance - (self.sen_delay-previous_delay_chain_delay) debug.check(required_delay > 0, "Cannot size delay chain to have negative delay") - delay_stages = ceil(required_delay/(delay_fanout+1+self.parasitic_inv_delay)) + delay_stages = ceil(required_delay/(delay_fanout+1+self.inv_parasitic_delay)) if delay_stages%2 == 1: #force an even number of stages. delay_stages+=1 #Fanout can be varied as well but is a little more complicated but potentially optimal. @@ -237,7 +264,7 @@ class control_logic(design.design): def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout): """Determine the size of the delay chain used for the Sense Amp Enable using path delays""" - previous_delay_chain_delay = (previous_fanout+1+self.parasitic_inv_delay)*previous_stages + previous_delay_chain_delay = (previous_fanout+1+self.inv_parasitic_delay)*previous_stages debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) fanout_rise = fanout_fall = 2 # This can be anything >=2 @@ -284,9 +311,9 @@ class control_logic(design.design): def calculate_stages_with_fixed_fanout(self, required_delay, fanout): from math import ceil #Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay - if required_delay <= 3+self.parasitic_inv_delay: #3 is the minimum delay per stage (with pinv=0). + if required_delay <= 3+self.inv_parasitic_delay: #3 is the minimum delay per stage (with pinv=0). return 1 - delay_stages = ceil(required_delay/(fanout+1+self.parasitic_inv_delay)) + delay_stages = ceil(required_delay/(fanout+1+self.inv_parasitic_delay)) return delay_stages def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall): @@ -304,9 +331,11 @@ class control_logic(design.design): # List of input control signals if self.port_type == "rw": self.input_list = ["csb", "web"] + self.rbl_list = ["rbl_bl"] else: self.input_list = ["csb"] - + self.rbl_list = ["rbl_bl"] + if self.port_type == "rw": self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"] else: @@ -314,21 +343,22 @@ class control_logic(design.design): # list of output control signals (for making a vertical bus) if self.port_type == "rw": - self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"] + self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"] elif self.port_type == "r": - self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"] + self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"] else: - self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"] + self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"] # leave space for the bus plus one extra space self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch # Outputs to the bank if self.port_type == "rw": - self.output_list = ["s_en", "w_en", "p_en_bar"] + self.output_list = ["s_en", "w_en"] elif self.port_type == "r": - self.output_list = ["s_en", "p_en_bar"] + self.output_list = ["s_en"] else: self.output_list = ["w_en"] + self.output_list.append("p_en_bar") self.output_list.append("wl_en") self.output_list.append("clk_buf") @@ -351,13 +381,13 @@ class control_logic(design.design): self.create_gated_clk_buf_row() self.create_wlen_row() if (self.port_type == "rw") or (self.port_type == "w"): + self.create_rbl_delay_row() self.create_wen_row() - if self.port_type == "rw": - self.create_rbl_in_row() - if (self.port_type == "rw") or (self.port_type == "r"): - self.create_pen_row() + if (self.port_type == "rw") or (self.port_type == "r"): self.create_sen_row() - self.create_rbl() + self.create_delay() + self.create_pen_row() + def place_instances(self): @@ -384,20 +414,20 @@ class control_logic(design.design): row += 1 if (self.port_type == "rw") or (self.port_type == "w"): self.place_wen_row(row) - height = self.w_en_inst.uy() - control_center_y = self.w_en_inst.uy() + height = self.w_en_gate_inst.uy() + control_center_y = self.w_en_gate_inst.uy() row += 1 - if self.port_type == "rw": - self.place_rbl_in_row(row) + self.place_pen_row(row) + row += 1 + if (self.port_type == "rw") or (self.port_type == "w"): + self.place_rbl_delay_row(row) row += 1 if (self.port_type == "rw") or (self.port_type == "r"): - self.place_pen_row(row) - row += 1 self.place_sen_row(row) row += 1 - self.place_rbl(row) - height = self.rbl_inst.uy() - control_center_y = self.rbl_inst.by() + self.place_delay(row) + height = self.delay_inst.uy() + control_center_y = self.delay_inst.by() # This offset is used for placement of the control logic in the SRAM level. self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y) @@ -407,7 +437,7 @@ class control_logic(design.design): # Max of modules or logic rows self.width = max([inst.rx() for inst in self.row_end_inst]) if (self.port_type == "rw") or (self.port_type == "r"): - self.width = max(self.rbl_inst.rx() , self.width) + self.width = max(self.delay_inst.rx() , self.width) self.width += self.m2_pitch def route_all(self): @@ -416,35 +446,48 @@ class control_logic(design.design): self.route_dffs() self.route_wlen() if (self.port_type == "rw") or (self.port_type == "w"): + self.route_rbl_delay() self.route_wen() if (self.port_type == "rw") or (self.port_type == "r"): - self.route_rbl_in() - self.route_pen() self.route_sen() + self.route_delay() + self.route_pen() self.route_clk_buf() self.route_gated_clk_bar() self.route_gated_clk_buf() self.route_supply() - def create_rbl(self): + def create_delay(self): """ Create the replica bitline """ - if self.port_type == "r": - input_name = "gated_clk_bar" - else: - input_name = "rbl_in" - self.rbl_inst=self.add_inst(name="replica_bitline", - mod=self.replica_bitline) - self.connect_inst([input_name, "pre_s_en", "vdd", "gnd"]) + self.delay_inst=self.add_inst(name="delay_chain", + mod=self.delay_chain) + self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"]) - def place_rbl(self,row): + def place_delay(self,row): """ Place the replica bitline """ y_off = row * self.and2.height + 2*self.m1_pitch # Add the RBL above the rows # Add to the right of the control rows and routing channel - offset = vector(0, y_off) - self.rbl_inst.place(offset) + offset = vector(self.delay_chain.width, y_off) + self.delay_inst.place(offset, mirror="MY") + + def route_delay(self): + + out_pos = self.delay_inst.get_pin("out").bc() + # Connect to the rail level with the vdd rail + # Use pen since it is in every type of control logic + vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by() + in_pos = vector(self.rail_offsets["rbl_bl_delay"].x,vdd_ypos) + mid1 = vector(out_pos.x,in_pos.y) + self.add_wire(("metal1","via1","metal2"),[out_pos, mid1, in_pos]) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=in_pos) + + + # Input from RBL goes to the delay line for futher delay + self.copy_layout_pin(self.delay_inst, "in", "rbl_bl") def create_clk_buf_row(self): @@ -454,12 +497,9 @@ class control_logic(design.design): self.connect_inst(["clk","clk_buf","vdd","gnd"]) def place_clk_buf_row(self,row): - """ Place the multistage clock buffer below the control flops """ - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) + x_offset = self.control_x_offset - offset = vector(x_off,y_off) - self.clk_buf_inst.place(offset, mirror) + x_offset = self.place_util(self.clk_buf_inst, x_offset, row) self.row_end_inst.append(self.clk_buf_inst) @@ -496,17 +536,10 @@ class control_logic(design.design): self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"]) def place_gated_clk_bar_row(self,row): - """ Place the gated clk logic below the control flops """ - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) + x_offset = self.control_x_offset - offset = vector(x_off,y_off) - self.clk_bar_inst.place(offset, mirror) - - x_off += self.inv.width - - offset = vector(x_off,y_off) - self.gated_clk_bar_inst.place(offset, mirror) + x_offset = self.place_util(self.clk_bar_inst, x_offset, row) + x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row) self.row_end_inst.append(self.gated_clk_bar_inst) @@ -540,12 +573,9 @@ class control_logic(design.design): self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"]) def place_gated_clk_buf_row(self,row): - """ Place the gated clk logic below the control flops """ - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) - - offset = vector(x_off,y_off) - self.gated_clk_buf_inst.place(offset, mirror) + x_offset = self.control_x_offset + + x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row) self.row_end_inst.append(self.gated_clk_buf_inst) @@ -567,174 +597,139 @@ class control_logic(design.design): self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"]) def place_wlen_row(self, row): - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) - - offset = vector(x_off, y_off) - self.wl_en_inst.place(offset, mirror) + x_offset = self.control_x_offset + + x_offset = self.place_util(self.wl_en_inst, x_offset, row) self.row_end_inst.append(self.wl_en_inst) def route_wlen(self): wlen_map = zip(["A"], ["gated_clk_bar"]) - self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.rail_offsets) + self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.rail_offsets) + self.connect_output(self.wl_en_inst, "Z", "wl_en") - def create_rbl_in_row(self): - - # input: gated_clk_bar, we_bar, output: rbl_in - self.rbl_in_inst=self.add_inst(name="and2_rbl_in", - mod=self.and2) - self.connect_inst(["gated_clk_bar", "we_bar", "rbl_in", "vdd", "gnd"]) - - def place_rbl_in_row(self,row): - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) - - offset = vector(x_off, y_off) - self.rbl_in_inst.place(offset, mirror) - - self.row_end_inst.append(self.rbl_in_inst) - - def route_rbl_in(self): - """ Connect the logic for the rbl_in generation """ - - if self.port_type == "rw": - input_name = "we_bar" - # Connect the NAND gate inputs to the bus - rbl_in_map = zip(["A", "B"], ["gated_clk_bar", "we_bar"]) - self.connect_vertical_bus(rbl_in_map, self.rbl_in_inst, self.rail_offsets) - - - # Connect the output of the precharge enable to the RBL input - if self.port_type == "rw": - out_pos = self.rbl_in_inst.get_pin("Z").center() - else: - out_pos = vector(self.rail_offsets["gated_clk_bar"].x, self.rbl_inst.by()-3*self.m2_pitch) - in_pos = self.rbl_inst.get_pin("en").center() - mid1 = vector(in_pos.x,out_pos.y) - self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=out_pos, - rotate=90) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=out_pos, - rotate=90) - def create_pen_row(self): - if self.port_type == "rw": - # input: gated_clk_bar, we_bar, output: pre_p_en - self.pre_p_en_inst=self.add_inst(name="and2_pre_p_en", - mod=self.and2) - self.connect_inst(["gated_clk_buf", "we_bar", "pre_p_en", "vdd", "gnd"]) - input_name = "pre_p_en" - else: - input_name = "gated_clk_buf" + self.p_en_bar_nand_inst=self.add_inst(name="nand_p_en_bar", + mod=self.nand2) + self.connect_inst(["gated_clk_buf", "rbl_bl_delay", "p_en_bar_unbuf", "vdd", "gnd"]) - # input: pre_p_en, output: p_en_bar - self.p_en_bar_inst=self.add_inst(name="inv_p_en_bar", - mod=self.p_en_bar_driver) - self.connect_inst([input_name, "p_en_bar", "vdd", "gnd"]) - + self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar", + mod=self.p_en_bar_driver) + self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"]) def place_pen_row(self,row): - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) - - if self.port_type == "rw": - offset = vector(x_off, y_off) - self.pre_p_en_inst.place(offset, mirror) + x_offset = self.control_x_offset - x_off += self.and2.width - - offset = vector(x_off,y_off) - self.p_en_bar_inst.place(offset, mirror) + x_offset = self.place_util(self.p_en_bar_nand_inst, x_offset, row) + x_offset = self.place_util(self.p_en_bar_driver_inst, x_offset, row) - self.row_end_inst.append(self.p_en_bar_inst) + self.row_end_inst.append(self.p_en_bar_driver_inst) def route_pen(self): - if self.port_type == "rw": - # Connect the NAND gate inputs to the bus - pre_p_en_in_map = zip(["A", "B"], ["gated_clk_buf", "we_bar"]) - self.connect_vertical_bus(pre_p_en_in_map, self.pre_p_en_inst, self.rail_offsets) + in_map = zip(["A", "B"], ["gated_clk_buf", "rbl_bl_delay"]) + self.connect_vertical_bus(in_map, self.p_en_bar_nand_inst, self.rail_offsets) - out_pos = self.pre_p_en_inst.get_pin("Z").center() - in_pos = self.p_en_bar_inst.get_pin("A").lc() - mid1 = vector(out_pos.x,in_pos.y) - self.add_wire(("metal1","via1","metal2"),[out_pos,mid1,in_pos]) - else: - in_map = zip(["A"], ["gated_clk_buf"]) - self.connect_vertical_bus(in_map, self.p_en_bar_inst, self.rail_offsets) + out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc() + in_pos = self.p_en_bar_driver_inst.get_pin("A").lc() + mid1 = vector(out_pos.x,in_pos.y) + self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos]) - self.connect_output(self.p_en_bar_inst, "Z", "p_en_bar") + self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar") def create_sen_row(self): """ Create the sense enable buffer. """ - # BUFFER FOR S_EN - # input: pre_s_en, output: s_en - self.s_en_inst=self.add_inst(name="buf_s_en", - mod=self.s_en_driver) - self.connect_inst(["pre_s_en", "s_en", "vdd", "gnd"]) + if self.port_type=="rw": + input_name = "we_bar" + else: + input_name = "cs_bar" + # GATE FOR S_EN + self.s_en_gate_inst = self.add_inst(name="buf_s_en_and", + mod=self.sen_and3) + self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"]) + def place_sen_row(self,row): - """ - The sense enable buffer gets placed to the far right of the - row. - """ - x_off = self.control_x_offset - (y_off,mirror)=self.get_offset(row) + x_offset = self.control_x_offset - offset = vector(x_off, y_off) - self.s_en_inst.place(offset, mirror) + x_offset = self.place_util(self.s_en_gate_inst, x_offset, row) - self.row_end_inst.append(self.s_en_inst) + self.row_end_inst.append(self.s_en_gate_inst) def route_sen(self): - - out_pos = self.rbl_inst.get_pin("out").bc() - in_pos = self.s_en_inst.get_pin("A").lc() - mid1 = vector(out_pos.x,in_pos.y) - self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos]) - self.connect_output(self.s_en_inst, "Z", "s_en") + if self.port_type=="rw": + input_name = "we_bar" + else: + input_name = "cs_bar" + + sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name]) + self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.rail_offsets) + self.connect_output(self.s_en_gate_inst, "Z", "s_en") + + + def create_rbl_delay_row(self): + + self.rbl_bl_delay_inv_inst = self.add_inst(name="rbl_bl_delay_inv", + mod=self.inv) + self.connect_inst(["rbl_bl_delay", "rbl_bl_delay_bar", "vdd", "gnd"]) + + def place_rbl_delay_row(self,row): + x_offset = self.control_x_offset + + x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row) + + self.row_end_inst.append(self.rbl_bl_delay_inv_inst) + + def route_rbl_delay(self): + # Connect from delay line + # Connect to rail + + rbl_map = zip(["Z"], ["rbl_bl_delay_bar"]) + self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + # The pin is on M1, so we need another via as well + self.add_via_center(layers=("metal1","via1","metal2"), + offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center()) + + + rbl_map = zip(["A"], ["rbl_bl_delay"]) + self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets) def create_wen_row(self): + # input: we (or cs) output: w_en if self.port_type == "rw": input_name = "we" else: # No we for write-only reports, so use cs input_name = "cs" - - # BUFFER FOR W_EN - self.w_en_inst = self.add_inst(name="buf_w_en_buf", - mod=self.w_en_driver) - self.connect_inst([input_name, "w_en", "vdd", "gnd"]) + # GATE THE W_EN + self.w_en_gate_inst = self.add_inst(name="w_en_and", + mod=self.wen_and) + self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"]) + def place_wen_row(self,row): - x_off = self.ctrl_dff_inst.width + self.internal_bus_width - (y_off,mirror)=self.get_offset(row) - - offset = vector(x_off, y_off) - self.w_en_inst.place(offset, mirror) + x_offset = self.control_x_offset + + x_offset = self.place_util(self.w_en_gate_inst, x_offset, row) - self.row_end_inst.append(self.w_en_inst) + self.row_end_inst.append(self.w_en_gate_inst) def route_wen(self): - if self.port_type == "rw": input_name = "we" else: # No we for write-only reports, so use cs input_name = "cs" - wen_map = zip(["A"], [input_name]) - self.connect_vertical_bus(wen_map, self.w_en_inst, self.rail_offsets) + wen_map = zip(["A", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"]) + self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.rail_offsets) - self.connect_output(self.w_en_inst, "Z", "w_en") + self.connect_output(self.w_en_gate_inst, "Z", "w_en") def create_dffs(self): self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs", @@ -746,7 +741,7 @@ class control_logic(design.design): def route_dffs(self): if self.port_type == "rw": - dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"]) + dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"]) elif self.port_type == "r": dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"]) else: @@ -759,8 +754,7 @@ class control_logic(design.design): rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y) self.add_wire(("metal1","via1","metal2"),[in_pos, mid_pos, rail_pos]) self.add_via_center(layers=("metal1","via1","metal2"), - offset=rail_pos, - rotate=90) + offset=rail_pos) self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb") if (self.port_type == "rw"): @@ -811,9 +805,8 @@ class control_logic(design.design): self.add_power_pin("gnd", pin_loc) self.add_path("metal1", [row_loc, pin_loc]) - if (self.port_type == "rw") or (self.port_type == "r"): - self.copy_layout_pin(self.rbl_inst,"gnd") - self.copy_layout_pin(self.rbl_inst,"vdd") + self.copy_layout_pin(self.delay_inst,"gnd") + self.copy_layout_pin(self.delay_inst,"vdd") self.copy_layout_pin(self.ctrl_dff_inst,"gnd") self.copy_layout_pin(self.ctrl_dff_inst,"vdd") @@ -839,7 +832,7 @@ class control_logic(design.design): # height=pin.height(), # width=pin.width()) - pin=self.rbl_inst.get_pin("out") + pin=self.delay_inst.get_pin("out") self.add_label_pin(text="out", layer=pin.layer, offset=pin.ll(), @@ -850,14 +843,14 @@ class control_logic(design.design): def get_delays_to_wl(self): """Get the delay (in delay units) of the clk to a wordline in the bitcell array""" debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") - self.wl_stage_efforts = self.determine_wordline_stage_efforts() - clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts, self.parasitic_inv_delay) + self.wl_stage_efforts = self.get_wordline_stage_efforts() + clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts) total_delay = clk_to_wl_rise + clk_to_wl_fall debug.info(1, "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, clk_to_wl_fall,total_delay)) return clk_to_wl_rise,clk_to_wl_fall - def determine_wordline_stage_efforts(self): + def get_wordline_stage_efforts(self): """Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts""" stage_effort_list = [] @@ -871,7 +864,7 @@ class control_logic(design.design): last_stage_is_rise = stage_effort_list[-1].is_rise #Then ask the sram for the other path delays (from the bank) - stage_effort_list += self.sram.determine_wordline_stage_efforts(last_stage_is_rise) + stage_effort_list += self.sram.get_wordline_stage_efforts(last_stage_is_rise) return stage_effort_list @@ -880,17 +873,15 @@ class control_logic(design.design): This does not incorporate the delay of the replica bitline. """ debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") - self.sen_stage_efforts = self.determine_sa_enable_stage_efforts() - clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts, self.parasitic_inv_delay) + self.sen_stage_efforts = self.get_sa_enable_stage_efforts() + clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts) total_delay = clk_to_sen_rise + clk_to_sen_fall debug.info(1, "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, clk_to_sen_fall,total_delay)) return clk_to_sen_rise, clk_to_sen_fall - def determine_sa_enable_stage_efforts(self): + def get_sa_enable_stage_efforts(self): """Follows the gated_clk_bar signal to the sense amp enable signal adding each stages stage effort to a list""" stage_effort_list = [] - #Calculate the load on clk_buf_bar - ext_clk_buf_cout = self.sram.get_clk_bar_cin() #Initial direction of clock signal for this path last_stage_rise = True @@ -902,7 +893,7 @@ class control_logic(design.design): last_stage_rise = stage_effort_list[-1].is_rise #Replica bitline stage, rbl_in -(rbl)-> pre_s_en - stage2_cout = self.s_en_driver.get_cin() + stage2_cout = self.sen_and2.get_cin() stage_effort_list += self.replica_bitline.determine_sen_stage_efforts(stage2_cout, last_stage_rise) last_stage_rise = stage_effort_list[-1].is_rise @@ -915,9 +906,77 @@ class control_logic(design.design): def get_wl_sen_delays(self): """Gets a list of the stages and delays in order of their path.""" + if self.sen_stage_efforts == None or self.wl_stage_efforts == None: debug.error("Model delays not calculated for SRAM.", 1) - wl_delays = logical_effort.calculate_delays(self.wl_stage_efforts, self.parasitic_inv_delay) - sen_delays = logical_effort.calculate_delays(self.sen_stage_efforts, self.parasitic_inv_delay) + wl_delays = logical_effort.calculate_delays(self.wl_stage_efforts) + sen_delays = logical_effort.calculate_delays(self.sen_stage_efforts) return wl_delays, sen_delays + def analytical_delay(self, corner, slew, load): + """Gets the analytical delay from clk input to wl_en output""" + + stage_effort_list = [] + #Calculate the load on clk_buf_bar + ext_clk_buf_cout = self.sram.get_clk_bar_cin() + + #Operations logic starts on negative edge + last_stage_rise = False + + #First stage(s), clk -(pdriver)-> clk_buf. + #clk_buf_cout = self.replica_bitline.get_en_cin() + clk_buf_cout = 0 + stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise) + last_stage_rise = stage_effort_list[-1].is_rise + + #Second stage, clk_buf -(inv)-> clk_bar + clk_bar_cout = self.and2.get_cin() + stage_effort_list += self.and2.get_stage_efforts(clk_bar_cout, last_stage_rise) + last_stage_rise = stage_effort_list[-1].is_rise + + #Third stage clk_bar -(and)-> gated_clk_bar + gated_clk_bar_cin = self.get_gated_clk_bar_cin() + stage_effort_list.append(self.inv.get_stage_effort(gated_clk_bar_cin, last_stage_rise)) + last_stage_rise = stage_effort_list[-1].is_rise + + #Stages from gated_clk_bar -------> wordline + stage_effort_list += self.get_wordline_stage_efforts() + return stage_effort_list + + def get_clk_buf_cin(self): + """ + Get the loads that are connected to the buffered clock. + Includes all the DFFs and some logic. + """ + + #Control logic internal load + int_clk_buf_cap = self.inv.get_cin() + self.ctrl_dff_array.get_clk_cin() + self.and2.get_cin() + + #Control logic external load (in the other parts of the SRAM) + ext_clk_buf_cap = self.sram.get_clk_bar_cin() + + return int_clk_buf_cap + ext_clk_buf_cap + + def get_gated_clk_bar_cin(self): + """Get intermediates net gated_clk_bar's capacitance""" + + total_cin = 0 + total_cin += self.wl_en_driver.get_cin() + if self.port_type == 'rw': + total_cin +=self.and2.get_cin() + return total_cin + + def graph_exclude_dffs(self): + """Exclude dffs from graph as they do not represent critical path""" + + self.graph_inst_exclude.add(self.ctrl_dff_inst) + if self.port_type=="rw" or self.port_type=="w": + self.graph_inst_exclude.add(self.w_en_gate_inst) + + def place_util(self, inst, x_offset, row): + """ Utility to place a row and compute the next offset """ + + (y_offset,mirror)=self.get_offset(row) + offset = vector(x_offset, y_offset) + inst.place(offset, mirror) + return x_offset+inst.width diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index f3d15ba3..bc932a26 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import design from tech import drc @@ -45,14 +52,15 @@ class delay_chain(design.design): self.place_inverters() self.route_inverters() self.add_layout_pins() + self.add_boundary() self.DRC_LVS() - + def add_pins(self): """ Add the pins of the delay chain""" - self.add_pin("in") - self.add_pin("out") - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin("in", "INPUT") + self.add_pin("out", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def add_modules(self): self.inv = factory.create(module_type="pinv", route_output=False) diff --git a/compiler/modules/dff.py b/compiler/modules/dff.py index 753ae41a..3cb1bcf1 100644 --- a/compiler/modules/dff.py +++ b/compiler/modules/dff.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import globals import design from math import log @@ -11,6 +18,7 @@ class dff(design.design): """ pin_names = ["D", "Q", "clk", "vdd", "gnd"] + type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] (width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"]) @@ -20,13 +28,14 @@ class dff(design.design): self.width = dff.width self.height = dff.height self.pin_map = dff.pin_map + self.add_pin_types(self.type_list) def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" c_eff = self.calculate_effective_capacitance(load) - freq = spice["default_event_rate"] + freq = spice["default_event_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) - power_leak = spice["msflop_leakage"] + power_leak = spice["dff_leakage"] total_power = self.return_power(power_dyn, power_leak) return total_power @@ -35,18 +44,17 @@ class dff(design.design): """Computes effective capacitance. Results in fF""" from tech import parameter c_load = load - c_para = spice["flop_para_cap"]#ff - transition_prob = spice["flop_transition_prob"] + c_para = spice["dff_out_cap"]#ff + transition_prob = 0.5 return transition_prob*(c_load + c_para) - def analytical_delay(self, corner, slew, load = 0.0): - # dont know how to calculate this now, use constant in tech file - result = self.return_delay(spice["dff_delay"], spice["dff_slew"]) - return result - def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" #This is a handmade cell so the value must be entered in the tech.py file or estimated. #Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width. return parameter["dff_clk_cin"] + def build_graph(self, graph, inst_name, port_nets): + """Adds edges based on inputs/outputs. Overrides base class function.""" + self.add_graph_edges(graph, port_nets) + diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 9d11b811..89b29476 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import design from tech import drc @@ -37,6 +44,7 @@ class dff_array(design.design): self.place_dff_array() self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_modules(self): @@ -46,13 +54,13 @@ class dff_array(design.design): def add_pins(self): for row in range(self.rows): for col in range(self.columns): - self.add_pin(self.get_din_name(row,col)) + self.add_pin(self.get_din_name(row,col), "INPUT") for row in range(self.rows): for col in range(self.columns): - self.add_pin(self.get_dout_name(row,col)) - self.add_pin("clk") - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin(self.get_dout_name(row,col), "OUTPUT") + self.add_pin("clk", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def create_dff_array(self): self.dff_insts={} @@ -152,11 +160,6 @@ class dff_array(design.design): self.add_via_center(layers=("metal2","via2","metal3"), offset=vector(clk_pin.cx(),clk_ypos)) - - - def analytical_delay(self, corner, slew, load=0.0): - return self.dff.analytical_delay(corner, slew=slew, load=load) - def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" dff_clk_cin = self.dff.get_clk_cin() diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index fa3285ba..f6fc1cf2 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import design from tech import drc,parameter @@ -48,6 +55,7 @@ class dff_buf(design.design): self.place_instances() self.route_wires() self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_modules(self): @@ -67,12 +75,12 @@ class dff_buf(design.design): def add_pins(self): - self.add_pin("D") - self.add_pin("Q") - self.add_pin("Qb") - self.add_pin("clk") - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin("D", "INPUT") + self.add_pin("Q", "OUTPUT") + self.add_pin("Qb", "OUTPUT") + self.add_pin("clk", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def create_instances(self): self.dff_inst=self.add_inst(name="dff_buf_dff", @@ -169,16 +177,7 @@ class dff_buf(design.design): self.add_path("metal1", [self.mid_qb_pos, qb_pos]) self.add_via_center(layers=("metal1","via1","metal2"), offset=qb_pos) - - - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of DFF-> INV -> INV """ - dff_delay=self.dff.analytical_delay(corner, slew=slew, load=self.inv1.input_load()) - inv1_delay = self.inv1.analytical_delay(corner, slew=dff_delay.slew, load=self.inv2.input_load()) - inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load) - return dff_delay + inv1_delay + inv2_delay - + def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" #This is a handmade cell so the value must be entered in the tech.py file or estimated. diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 56b20dfb..2fafee76 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import design from tech import drc @@ -42,19 +49,20 @@ class dff_buf_array(design.design): self.height = self.rows * self.dff.height self.place_dff_array() self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_pins(self): for row in range(self.rows): for col in range(self.columns): - self.add_pin(self.get_din_name(row,col)) + self.add_pin(self.get_din_name(row,col), "INPUT") for row in range(self.rows): for col in range(self.columns): - self.add_pin(self.get_dout_name(row,col)) - self.add_pin(self.get_dout_bar_name(row,col)) - self.add_pin("clk") - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin(self.get_dout_name(row,col), "OUTPUT") + self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT") + self.add_pin("clk", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def add_modules(self): self.dff = factory.create(module_type="dff_buf", @@ -185,11 +193,6 @@ class dff_buf_array(design.design): self.add_via_center(layers=("metal2","via2","metal3"), offset=vector(clk_pin.cx(),clk_ypos)) - - - def analytical_delay(self, corner, slew, load=0.0): - return self.dff.analytical_delay(slew=slew, load=load) - def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" dff_clk_cin = self.dff.get_clk_cin() diff --git a/compiler/modules/dff_inv.py b/compiler/modules/dff_inv.py index 9b901d3b..207a5ad0 100644 --- a/compiler/modules/dff_inv.py +++ b/compiler/modules/dff_inv.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import design from tech import drc @@ -46,6 +53,7 @@ class dff_inv(design.design): self.add_wires() self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_pins(self): @@ -142,15 +150,7 @@ class dff_inv(design.design): offset=dout_pin.center()) self.add_via_center(layers=("metal1","via1","metal2"), offset=dout_pin.center()) - - - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of DFF-> INV -> INV """ - dff_delay=self.dff.analytical_delay(corner, slew=slew, load=self.inv1.input_load()) - inv1_delay = self.inv1.analytical_delay(corner, slew=dff_delay.slew, load=load) - return dff_delay + inv1_delay - + def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" return self.dff.get_clk_cin() diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index 624e13d0..760a2337 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import design from tech import drc @@ -42,6 +49,7 @@ class dff_inv_array(design.design): self.place_dff_array() self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_modules(self): @@ -51,14 +59,14 @@ class dff_inv_array(design.design): def add_pins(self): for row in range(self.rows): for col in range(self.columns): - self.add_pin(self.get_din_name(row,col)) + self.add_pin(self.get_din_name(row,col), "INPUT") for row in range(self.rows): for col in range(self.columns): - self.add_pin(self.get_dout_name(row,col)) - self.add_pin(self.get_dout_bar_name(row,col)) - self.add_pin("clk") - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin(self.get_dout_name(row,col), "OUTPUT") + self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT") + self.add_pin("clk", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def create_dff_array(self): self.dff_insts={} @@ -182,12 +190,6 @@ class dff_inv_array(design.design): self.add_via_center(layers=("metal2","via2","metal3"), offset=vector(clk_pin.cx(),clk_ypos)) - - - - def analytical_delay(self, corner, slew, load=0.0): - return self.dff.analytical_delay(corner, slew=slew, load=load) - def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" dff_clk_cin = self.dff.get_clk_cin() diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py new file mode 100644 index 00000000..f1f433ce --- /dev/null +++ b/compiler/modules/dummy_array.py @@ -0,0 +1,157 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import debug +import design +from tech import drc +import contact +from sram_factory import factory +from vector import vector +from globals import OPTS + +class dummy_array(design.design): + """ + Generate a dummy row/column for the replica array. + """ + def __init__(self, cols, rows, mirror=0, name=""): + design.design.__init__(self, name) + debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) + self.add_comment("rows: {0} cols: {1}".format(rows, cols)) + + self.column_size = cols + self.row_size = rows + self.mirror = mirror + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + + def create_netlist(self): + """ Create and connect the netlist """ + self.add_modules() + self.add_pins() + self.create_instances() + + 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.dummy_cell.height + self.width = self.column_size*self.dummy_cell.width + + xoffset = 0.0 + for col in range(self.column_size): + yoffset = 0.0 + for row in range(self.row_size): + name = "dummy_r{0}_c{1}".format(row, col) + + if (row+self.mirror) % 2: + tempy = yoffset + self.dummy_cell.height + dir_key = "MX" + else: + tempy = yoffset + dir_key = "" + + self.cell_inst[row,col].place(offset=[xoffset, tempy], + mirror=dir_key) + yoffset += self.dummy_cell.height + xoffset += self.dummy_cell.width + + self.add_layout_pins() + + self.add_boundary() + + self.DRC_LVS() + + def add_pins(self): + row_list = self.cell.get_all_wl_names() + column_list = self.cell.get_all_bitline_names() + for col in range(self.column_size): + for cell_column in column_list: + self.add_pin(cell_column+"_{0}".format(col), "INOUT") + for row in range(self.row_size): + for cell_row in row_list: + self.add_pin(cell_row+"_{0}".format(row), "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def add_modules(self): + """ Add the modules used in this design """ + self.dummy_cell = factory.create(module_type="dummy_bitcell") + self.add_mod(self.dummy_cell) + + self.cell = factory.create(module_type="bitcell") + + def get_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 = [] + + pin_names = self.cell.get_all_bitline_names() + for pin in pin_names: + bitcell_pins.append(pin+"_{0}".format(col)) + pin_names = self.cell.get_all_wl_names() + for pin in pin_names: + bitcell_pins.append(pin+"_{0}".format(row)) + bitcell_pins.append("vdd") + bitcell_pins.append("gnd") + + return bitcell_pins + + + def create_instances(self): + """ Create the module instances used in this design """ + self.cell_inst = {} + for col in range(self.column_size): + for row in range(self.row_size): + name = "bit_r{0}_c{1}".format(row, col) + self.cell_inst[row,col]=self.add_inst(name=name, + mod=self.dummy_cell) + self.connect_inst(self.get_bitcell_pins(col, row)) + + def add_layout_pins(self): + """ Add the layout pins """ + + row_list = self.cell.get_all_wl_names() + column_list = self.cell.get_all_bitline_names() + + for col in range(self.column_size): + for cell_column in column_list: + bl_pin = self.cell_inst[0,col].get_pin(cell_column) + self.add_layout_pin(text=cell_column+"_{0}".format(col), + layer="metal2", + offset=bl_pin.ll(), + width=bl_pin.width(), + height=self.height) + + for row in range(self.row_size): + for cell_row in row_list: + wl_pin = self.cell_inst[row,0].get_pin(cell_row) + self.add_layout_pin(text=cell_row+"_{0}".format(row), + layer="metal1", + offset=wl_pin.ll(), + width=self.width, + height=wl_pin.height()) + + # For every second row and column, add a via for gnd and vdd + for row in range(self.row_size): + for col in range(self.column_size): + inst = self.cell_inst[row,col] + for pin_name in ["vdd", "gnd"]: + for pin in inst.get_pins(pin_name): + self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) + + + def input_load(self): + wl_wire = self.gen_wl_wire() + return wl_wire.return_input_cap() + + def get_wordline_cin(self): + """Get the relative input capacitance from the wordline connections in all the bitcell""" + #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns + bitcell_wl_cin = self.cell.get_wl_cin() + total_cin = bitcell_wl_cin * self.column_size + return total_cin diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index bd17b37e..0e70db55 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from tech import drc import debug import design @@ -47,6 +54,7 @@ class hierarchical_decoder(design.design): self.route_predecode_rails() self.route_vdd_gnd() self.offset_all_coordinates() + self.add_boundary() self.DRC_LVS() def add_modules(self): @@ -213,11 +221,9 @@ class hierarchical_decoder(design.design): """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=input_offset, - rotate=90) + offset=input_offset) self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=output_offset, - rotate=90) + offset=output_offset) self.add_path(("metal3"), [input_offset, output_offset]) @@ -225,12 +231,12 @@ class hierarchical_decoder(design.design): """ Add the module pins """ for i in range(self.num_inputs): - self.add_pin("addr_{0}".format(i)) + self.add_pin("addr_{0}".format(i), "INPUT") for j in range(self.rows): - self.add_pin("decode_{0}".format(j)) - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin("decode_{0}".format(j), "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def create_pre_decoder(self): @@ -575,8 +581,7 @@ class hierarchical_decoder(design.design): rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y) self.add_path("metal1", [rail_pos, pin.lc()]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=rail_pos, - rotate=90) + offset=rail_pos) def route_predecode_rail_m3(self, rail_name, pin): @@ -586,38 +591,15 @@ class hierarchical_decoder(design.design): mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2) rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=pin.center(), - rotate=90) + offset=pin.center()) self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()]) self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=rail_pos, - rotate=90) + offset=rail_pos) - - def analytical_delay(self, corner, slew, load = 0.0): - # A -> out - if self.determine_predecodes(self.num_inputs)[1]==0: - pre = self.pre2_4 - nand = self.nand2 - else: - pre = self.pre3_8 - nand = self.nand3 - a_t_out_delay = pre.analytical_delay(corner, slew=slew,load = nand.input_load()) - - # out -> z - out_t_z_delay = nand.analytical_delay(corner, slew= a_t_out_delay.slew, - load = self.inv.input_load()) - result = a_t_out_delay + out_t_z_delay - - # Z -> decode_out - z_t_decodeout_delay = self.inv.analytical_delay(corner, slew = out_t_z_delay.slew , load = load) - result = result + z_t_decodeout_delay - return result - - def input_load(self): if self.determine_predecodes(self.num_inputs)[1]==0: pre = self.pre2_4 else: pre = self.pre3_8 return pre.input_load() + diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 147c3018..bec0ce06 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import design import math @@ -19,11 +26,11 @@ class hierarchical_predecode(design.design): def add_pins(self): for k in range(self.number_of_inputs): - self.add_pin("in_{0}".format(k)) + self.add_pin("in_{0}".format(k), "INPUT") for i in range(self.number_of_outputs): - self.add_pin("out_{0}".format(i)) - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin("out_{0}".format(i), "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def add_modules(self): """ Add the INV and NAND gate modules """ @@ -178,11 +185,9 @@ class hierarchical_predecode(design.design): a_pos = vector(self.decode_rails[a_pin].x,y_offset) self.add_path("metal1",[in_pos, a_pos]) self.add_via_center(layers = ("metal1", "via1", "metal2"), - offset=[self.input_rails[in_pin].x, y_offset], - rotate=90) + offset=[self.input_rails[in_pin].x, y_offset]) self.add_via_center(layers = ("metal1", "via1", "metal2"), - offset=[self.decode_rails[a_pin].x, y_offset], - rotate=90) + offset=[self.decode_rails[a_pin].x, y_offset]) def route_output_inverters(self): """ @@ -223,8 +228,7 @@ class hierarchical_predecode(design.design): rail_pos = vector(self.decode_rails[out_pin].x,y_offset) self.add_path("metal1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) self.add_via_center(layers = ("metal1", "via1", "metal2"), - offset=rail_pos, - rotate=90) + offset=rail_pos) #route input @@ -232,8 +236,7 @@ class hierarchical_predecode(design.design): in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y) self.add_path("metal1", [in_pos, inv_in_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=in_pos, - rotate=90) + offset=in_pos) def route_nand_to_rails(self): @@ -254,8 +257,8 @@ class hierarchical_predecode(design.design): rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) self.add_path("metal1", [rail_pos, pin_pos]) self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=rail_pos, - rotate=90) + offset=rail_pos) + diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index f50a43c1..f05f54b0 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from tech import drc import debug import design @@ -40,6 +47,7 @@ class hierarchical_predecode2x4(hierarchical_predecode): self.place_output_inverters() self.place_nand_array() self.route() + self.add_boundary() self.DRC_LVS() def get_nand_input_line_combination(self): @@ -48,21 +56,4 @@ class hierarchical_predecode2x4(hierarchical_predecode): ["A_0", "Abar_1"], ["Abar_0", "A_1"], ["A_0", "A_1"]] - return combination - - - def analytical_delay(self, corner, slew, load = 0.0 ): - # in -> inbar - a_t_b_delay = self.inv.analytical_delay(corner, slew=slew, load=self.nand.input_load()) - - # inbar -> z - b_t_z_delay = self.nand.analytical_delay(corner, slew=a_t_b_delay.slew, load=self.inv.input_load()) - - # Z -> out - a_t_out_delay = self.inv.analytical_delay(corner, slew=b_t_z_delay.slew, load=load) - - return a_t_b_delay + b_t_z_delay + a_t_out_delay - - - def input_load(self): - return self.nand.input_load() + return combination \ No newline at end of file diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index 820427c7..20b629bb 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from tech import drc import debug import design @@ -45,6 +52,7 @@ class hierarchical_predecode3x8(hierarchical_predecode): self.place_output_inverters() self.place_nand_array() self.route() + self.add_boundary() self.DRC_LVS() def get_nand_input_line_combination(self): @@ -57,21 +65,4 @@ class hierarchical_predecode3x8(hierarchical_predecode): ["A_0", "Abar_1", "A_2"], ["Abar_0", "A_1", "A_2"], ["A_0", "A_1", "A_2"]] - return combination - - - def analytical_delay(self, corner, slew, load = 0.0 ): - # A -> Abar - a_t_b_delay = self.inv.analytical_delay(corner, slew=slew, load=self.nand.input_load()) - - # Abar -> z - b_t_z_delay = self.nand.analytical_delay(corner, slew=a_t_b_delay.slew, load=self.inv.input_load()) - - # Z -> out - a_t_out_delay = self.inv.analytical_delay(corner, slew=b_t_z_delay.slew, load=load) - - return a_t_b_delay + b_t_z_delay + a_t_out_delay - - - def input_load(self): - return self.nand.input_load() + return combination \ No newline at end of file diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index 15882f8b..2f933a2d 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import sys from tech import drc, parameter import debug @@ -58,11 +65,11 @@ class multibank(design.design): def add_pins(self): """ Adding pins for Bank module""" for i in range(self.word_size): - self.add_pin("DOUT_{0}".format(i),"OUT") + self.add_pin("dout_{0}".format(i),"OUT") for i in range(self.word_size): - self.add_pin("BANK_DIN_{0}".format(i),"IN") + self.add_pin("bank_din_{0}".format(i),"IN") for i in range(self.addr_size): - self.add_pin("A_{0}".format(i),"INPUT") + self.add_pin("a_{0}".format(i),"INPUT") # For more than one bank, we have a bank select and name # the signals gated_*. @@ -180,9 +187,15 @@ class multibank(design.design): words_per_row=self.words_per_row) self.add_mod(self.sense_amp_array) - self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols, - word_size=self.word_size) - self.add_mod(self.write_driver_array) + if self.write_size: + self.write_mask_driver_array = self.mod_write_mask_driver_array(columns=self.num_cols, + word_size=self.word_size, + write_size=self.write_size) + self.add_mod(self.write_mask_driver_array) + else: + self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols, + word_size=self.word_size) + self.add_mod(self.write_driver_array) self.row_decoder = self.mod_decoder(rows=self.num_rows) self.add_mod(self.row_decoder) @@ -289,7 +302,7 @@ class multibank(design.design): temp = [] for i in range(self.word_size): - temp.append("BANK_DIN_{0}".format(i)) + temp.append("bank_din_{0}".format(i)) for i in range(self.word_size): if (self.words_per_row == 1): temp.append("bl_{0}".format(i)) @@ -312,7 +325,7 @@ class multibank(design.design): for i in range(self.word_size): temp.append("sa_out_{0}".format(i)) for i in range(self.word_size): - temp.append("DOUT_{0}".format(i)) + temp.append("dout_{0}".format(i)) temp.extend([self.prefix+"tri_en", self.prefix+"tri_en_bar", "vdd", "gnd"]) self.connect_inst(temp) @@ -583,7 +596,7 @@ class multibank(design.design): """ Add pins for the sense amp output """ for i in range(self.word_size): data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(i)) - self.add_layout_pin_rect_center(text="DOUT_{}".format(i), + self.add_layout_pin_rect_center(text="dout_{}".format(i), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), @@ -593,7 +606,7 @@ class multibank(design.design): """ Metal 3 routing of tri_gate output data """ for i in range(self.word_size): data_pin = self.tri_gate_array_inst.get_pin("out_{}".format(i)) - self.add_layout_pin_rect_center(text="DOUT_{}".format(i), + self.add_layout_pin_rect_center(text="dout_{}".format(i), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), @@ -606,8 +619,8 @@ class multibank(design.design): # Create inputs for the row address lines for i in range(self.row_addr_size): addr_idx = i + self.col_addr_size - decoder_name = "A_{}".format(i) - addr_name = "A_{}".format(addr_idx) + decoder_name = "a_{}".format(i) + addr_name = "a_{}".format(addr_idx) self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name) @@ -616,7 +629,7 @@ class multibank(design.design): for i in range(self.word_size): data_name = "data_{}".format(i) - din_name = "BANK_DIN_{}".format(i) + din_name = "bank_din_{}".format(i) self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) @@ -655,7 +668,7 @@ class multibank(design.design): decode_names = ["Zb", "Z"] # The Address LSB - self.copy_layout_pin(self.col_decoder_inst, "A", "A[0]") + self.copy_layout_pin(self.col_decoder_inst, "A", "a[0]") elif self.col_addr_size > 1: decode_names = [] @@ -664,7 +677,7 @@ class multibank(design.design): for i in range(self.col_addr_size): decoder_name = "in_{}".format(i) - addr_name = "A_{}".format(i) + addr_name = "a_{}".format(i) self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name) @@ -815,21 +828,3 @@ class multibank(design.design): self.add_via(layers=("metal2","via2","metal3"), offset=in_pin + self.m2m3_via_offset, rotate=90) - - def analytical_delay(self, corner, slew, load): - """ return analytical delay of the bank""" - decoder_delay = self.row_decoder.analytical_delay(corner, slew, self.wordline_driver.input_load()) - - word_driver_delay = self.wordline_driver.analytical_delay(corner, decoder_delay.slew, self.bitcell_array.input_load()) - - bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_delay.slew) - - bl_t_data_out_delay = self.sense_amp_array.analytical_delay(corner, bitcell_array_delay.slew, - self.bitcell_array.output_load()) - # output load of bitcell_array is set to be only small part of bl for sense amp. - - data_t_DATA_delay = self.tri_gate_array.analytical_delay(corner, bl_t_data_out_delay.slew, load) - - result = decoder_delay + word_driver_delay + bitcell_array_delay + bl_t_data_out_delay + data_t_DATA_delay - return result - diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py new file mode 100644 index 00000000..0a624c60 --- /dev/null +++ b/compiler/modules/port_address.py @@ -0,0 +1,162 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import sys +from tech import drc, parameter +from math import log +import debug +import design +from sram_factory import factory +from vector import vector + +from globals import OPTS + +class port_address(design.design): + """ + Create the address port (row decoder and wordline driver).. + """ + + def __init__(self, cols, rows, name=""): + + self.num_cols = cols + self.num_rows = rows + self.addr_size = int(log(self.num_rows, 2)) + + if name == "": + name = "port_address_{0}_{1}".format(cols,rows) + design.design.__init__(self, name) + debug.info(2, "create data port of cols {0} rows {1}".format(cols,rows)) + + self.create_netlist() + if not OPTS.netlist_only: + debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.") + self.create_layout() + self.add_boundary() + + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_row_decoder() + self.create_wordline_driver() + + def create_layout(self): + self.place_instances() + self.route_layout() + self.DRC_LVS() + + def add_pins(self): + """ Adding pins for port address module""" + + for bit in range(self.addr_size): + self.add_pin("addr_{0}".format(bit),"INPUT") + + self.add_pin("wl_en", "INPUT") + + for bit in range(self.num_rows): + self.add_pin("wl_{0}".format(bit),"OUTPUT") + + self.add_pin("vdd","POWER") + self.add_pin("gnd","GROUND") + + + def route_layout(self): + """ Create routing amoung the modules """ + self.route_pins() + self.route_internal() + self.route_supplies() + + 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") + + def route_pins(self): + for row in range(self.addr_size): + decoder_name = "addr_{}".format(row) + self.copy_layout_pin(self.row_decoder_inst, decoder_name) + + for row in range(self.num_rows): + driver_name = "wl_{}".format(row) + self.copy_layout_pin(self.wordline_driver_inst, driver_name) + + self.copy_layout_pin(self.wordline_driver_inst, "en", "wl_en") + + def route_internal(self): + for row in range(self.num_rows): + # The pre/post is to access the pin from "outside" the cell to avoid DRCs + decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc() + driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc() + mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) + mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) + self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) + + + + + def add_modules(self): + + self.row_decoder = factory.create(module_type="decoder", + rows=self.num_rows) + self.add_mod(self.row_decoder) + + self.wordline_driver = factory.create(module_type="wordline_driver", + rows=self.num_rows, + cols=self.num_cols) + self.add_mod(self.wordline_driver) + + + def create_row_decoder(self): + """ Create the hierarchical row decoder """ + + self.row_decoder_inst = self.add_inst(name="row_decoder", + mod=self.row_decoder) + + temp = [] + for bit in range(self.addr_size): + temp.append("addr_{0}".format(bit)) + for row in range(self.num_rows): + temp.append("dec_out_{0}".format(row)) + temp.extend(["vdd", "gnd"]) + self.connect_inst(temp) + + + + def create_wordline_driver(self): + """ Create the Wordline Driver """ + + self.wordline_driver_inst = self.add_inst(name="wordline_driver", + mod=self.wordline_driver) + + temp = [] + for row in range(self.num_rows): + temp.append("dec_out_{0}".format(row)) + for row in range(self.num_rows): + temp.append("wl_{0}".format(row)) + temp.append("wl_en") + temp.append("vdd") + temp.append("gnd") + self.connect_inst(temp) + + + + def place_instances(self): + """ + Compute the offsets and place the instances. + """ + + # A space for wells or jogging m2 + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), + 3*self.m2_pitch) + + row_decoder_offset = vector(0,0) + wordline_driver_offset = vector(self.row_decoder.width + self.m2_gap,0) + + self.wordline_driver_inst.place(wordline_driver_offset) + self.row_decoder_inst.place(row_decoder_offset) + + self.height = self.row_decoder.height + self.width = self.wordline_driver_inst.rx() diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py new file mode 100644 index 00000000..c3473803 --- /dev/null +++ b/compiler/modules/port_data.py @@ -0,0 +1,667 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import sys +from tech import drc, parameter +import debug +import design +from sram_factory import factory +from vector import vector + +from globals import OPTS + +class port_data(design.design): + """ + Create the data port (column mux, sense amps, write driver, etc.) for the given port number. + Port 0 always has the RBL on the left while port 1 is on the right. + """ + + def __init__(self, sram_config, port, name=""): + + sram_config.set_local_config(self) + self.port = port + if self.write_size is not None: + self.num_wmasks = int(self.word_size/self.write_size) + else: + self.num_wmasks = 0 + + if name == "": + name = "port_data_{0}".format(self.port) + design.design.__init__(self, name) + debug.info(2, "create data port of size {0} with {1} words per row".format(self.word_size,self.words_per_row)) + + self.create_netlist() + if not OPTS.netlist_only: + debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.") + self.create_layout() + self.add_boundary() + + + def create_netlist(self): + self.precompute_constants() + self.add_pins() + self.add_modules() + self.create_instances() + + def create_instances(self): + if self.precharge_array: + self.create_precharge_array() + else: + self.precharge_array_inst = None + + if self.sense_amp_array: + self.create_sense_amp_array() + else: + self.sense_amp_array_inst = None + + if self.write_driver_array: + self.create_write_driver_array() + if self.write_size is not None: + self.create_write_mask_and_array() + else: + self.write_mask_and_array_inst = None + else: + self.write_driver_array_inst = None + self.write_mask_and_array_inst = None + + if self.column_mux_array: + self.create_column_mux_array() + else: + self.column_mux_array_inst = None + + + + def create_layout(self): + self.compute_instance_offsets() + self.place_instances() + self.route_layout() + self.DRC_LVS() + + def add_pins(self): + """ Adding pins for port address module""" + + self.add_pin("rbl_bl","INOUT") + self.add_pin("rbl_br","INOUT") + for bit in range(self.num_cols): + self.add_pin("{0}_{1}".format(self.bl_names[self.port], bit),"INOUT") + self.add_pin("{0}_{1}".format(self.br_names[self.port], bit),"INOUT") + if self.port in self.read_ports: + for bit in range(self.word_size): + self.add_pin("dout_{}".format(bit),"OUTPUT") + if self.port in self.write_ports: + for bit in range(self.word_size): + self.add_pin("din_{}".format(bit),"INPUT") + # Will be empty if no col addr lines + sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] + for pin_name in sel_names: + self.add_pin(pin_name,"INPUT") + if self.port in self.read_ports: + self.add_pin("s_en", "INPUT") + self.add_pin("p_en_bar", "INPUT") + if self.port in self.write_ports: + self.add_pin("w_en", "INPUT") + for bit in range(self.num_wmasks): + self.add_pin("bank_wmask_{}".format(bit),"INPUT") + self.add_pin("vdd","POWER") + self.add_pin("gnd","GROUND") + + + def route_layout(self): + """ Create routing among the modules """ + self.route_data_lines() + self.route_layout_pins() + self.route_supplies() + + def route_layout_pins(self): + """ Add the pins """ + self.route_bitline_pins() + self.route_control_pins() + + def route_data_lines(self): + """ Route the bitlines depending on the port type rw, w, or r. """ + + if self.port in self.readwrite_ports: + # (write_mask_and ->) write_driver -> sense_amp -> (column_mux ->) precharge -> bitcell_array + self.route_write_mask_and_array_in(self.port) + self.route_write_mask_and_array_to_write_driver(self.port) + self.route_write_driver_in(self.port) + self.route_sense_amp_out(self.port) + self.route_write_driver_to_sense_amp(self.port) + self.route_sense_amp_to_column_mux_or_precharge_array(self.port) + self.route_column_mux_to_precharge_array(self.port) + elif self.port in self.read_ports: + # sense_amp -> (column_mux) -> precharge -> bitcell_array + self.route_sense_amp_out(self.port) + self.route_sense_amp_to_column_mux_or_precharge_array(self.port) + self.route_column_mux_to_precharge_array(self.port) + else: + # (write_mask_and ->) write_driver -> (column_mux ->) precharge -> bitcell_array + self.route_write_mask_and_array_in(self.port) + self.route_write_mask_and_array_to_write_driver(self.port) + self.route_write_driver_in(self.port) + self.route_write_driver_to_column_mux_or_precharge_array(self.port) + self.route_column_mux_to_precharge_array(self.port) + + 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") + + def add_modules(self): + + # Extra column +1 is for RBL + # Precharge will be shifted left if needed + self.precharge_array = factory.create(module_type="precharge_array", + columns=self.num_cols + 1, + bitcell_bl=self.bl_names[self.port], + bitcell_br=self.br_names[self.port]) + self.add_mod(self.precharge_array) + + if self.port in self.read_ports: + self.sense_amp_array = factory.create(module_type="sense_amp_array", + word_size=self.word_size, + words_per_row=self.words_per_row) + self.add_mod(self.sense_amp_array) + else: + self.sense_amp_array = None + + + if self.col_addr_size > 0: + self.column_mux_array = factory.create(module_type="column_mux_array", + columns=self.num_cols, + word_size=self.word_size, + bitcell_bl=self.bl_names[self.port], + bitcell_br=self.br_names[self.port]) + self.add_mod(self.column_mux_array) + else: + self.column_mux_array = None + + + if self.port in self.write_ports: + self.write_driver_array = factory.create(module_type="write_driver_array", + columns=self.num_cols, + word_size=self.word_size, + write_size=self.write_size) + self.add_mod(self.write_driver_array) + if self.write_size is not None: + self.write_mask_and_array = factory.create(module_type="write_mask_and_array", + columns=self.num_cols, + word_size=self.word_size, + write_size=self.write_size, + port = self.port) + self.add_mod(self.write_mask_and_array) + else: + self.write_mask_and_array = None + + else: + self.write_driver_array = None + self.write_mask_and_array = None + + def precompute_constants(self): + """ Get some preliminary data ready """ + + # The central bus is the column address (one hot) and row address (binary) + if self.col_addr_size>0: + self.num_col_addr_lines = 2**self.col_addr_size + else: + self.num_col_addr_lines = 0 + + + # A space for wells or jogging m2 between modules + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), + 3*self.m2_pitch) + + + # create arrays of bitline and bitline_bar names for read, write, or all ports + self.bitcell = factory.create(module_type="bitcell") + self.bl_names = self.bitcell.get_all_bl_names() + self.br_names = self.bitcell.get_all_br_names() + self.wl_names = self.bitcell.get_all_wl_names() + + def create_precharge_array(self): + """ Creating Precharge """ + if not self.precharge_array: + self.precharge_array_inst = None + return + + self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port), + mod=self.precharge_array) + + temp = [] + # Use left BLs for RBL + if self.port==0: + temp.append("rbl_bl") + temp.append("rbl_br") + for bit in range(self.num_cols): + temp.append(self.bl_names[self.port]+"_{0}".format(bit)) + temp.append(self.br_names[self.port]+"_{0}".format(bit)) + # Use right BLs for RBL + if self.port==1: + temp.append("rbl_bl") + temp.append("rbl_br") + temp.extend(["p_en_bar", "vdd"]) + self.connect_inst(temp) + + + def place_precharge_array(self, offset): + """ Placing Precharge """ + + self.precharge_array_inst.place(offset=offset, mirror="MX") + + + def create_column_mux_array(self): + """ Creating Column Mux when words_per_row > 1 . """ + + self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port), + mod=self.column_mux_array) + + temp = [] + for col in range(self.num_cols): + temp.append(self.bl_names[self.port]+"_{0}".format(col)) + temp.append(self.br_names[self.port]+"_{0}".format(col)) + for word in range(self.words_per_row): + temp.append("sel_{}".format(word)) + for bit in range(self.word_size): + temp.append(self.bl_names[self.port]+"_out_{0}".format(bit)) + temp.append(self.br_names[self.port]+"_out_{0}".format(bit)) + temp.append("gnd") + self.connect_inst(temp) + + + def place_column_mux_array(self, offset): + """ Placing Column Mux when words_per_row > 1 . """ + if self.col_addr_size == 0: + return + + self.column_mux_array_inst.place(offset=offset, mirror="MX") + + + def create_sense_amp_array(self): + """ Creating Sense amp """ + self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port), + mod=self.sense_amp_array) + + temp = [] + for bit in range(self.word_size): + temp.append("dout_{}".format(bit)) + if self.words_per_row == 1: + temp.append(self.bl_names[self.port]+"_{0}".format(bit)) + temp.append(self.br_names[self.port]+"_{0}".format(bit)) + else: + temp.append(self.bl_names[self.port]+"_out_{0}".format(bit)) + temp.append(self.br_names[self.port]+"_out_{0}".format(bit)) + + temp.extend(["s_en", "vdd", "gnd"]) + self.connect_inst(temp) + + + def place_sense_amp_array(self, offset): + """ Placing Sense amp """ + self.sense_amp_array_inst.place(offset=offset, mirror="MX") + + + def create_write_driver_array(self): + """ Creating Write Driver """ + self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port), + mod=self.write_driver_array) + + temp = [] + for bit in range(self.word_size): + temp.append("din_{}".format(bit)) + + for bit in range(self.word_size): + if (self.words_per_row == 1): + temp.append(self.bl_names[self.port] + "_{0}".format(bit)) + temp.append(self.br_names[self.port] + "_{0}".format(bit)) + else: + temp.append(self.bl_names[self.port] + "_out_{0}".format(bit)) + temp.append(self.br_names[self.port] + "_out_{0}".format(bit)) + + if self.write_size is not None: + for i in range(self.num_wmasks): + temp.append("wdriver_sel_{}".format(i)) + else: + temp.append("w_en") + temp.extend(["vdd", "gnd"]) + + self.connect_inst(temp) + + + def place_write_driver_array(self, offset): + """ Placing Write Driver """ + self.write_driver_array_inst.place(offset=offset, mirror="MX") + + + def create_write_mask_and_array(self): + """ Creating Write Mask AND Array """ + self.write_mask_and_array_inst = self.add_inst(name="write_mask_and_array{}".format(self.port), + mod=self.write_mask_and_array) + + temp = [] + for bit in range(self.num_wmasks): + temp.append("bank_wmask_{}".format(bit)) + temp.extend(["w_en"]) + for bit in range(self.num_wmasks): + temp.append("wdriver_sel_{}".format(bit)) + temp.extend(["vdd", "gnd"]) + self.connect_inst(temp) + + + def place_write_mask_and_array(self, offset): + """ Placing Write Mask AND array """ + self.write_mask_and_array_inst.place(offset=offset, mirror="MX") + + + def compute_instance_offsets(self): + """ + Compute the empty instance offsets for port0 and port1 (if needed) + """ + + vertical_port_order = [] + vertical_port_order.append(self.precharge_array_inst) + vertical_port_order.append(self.column_mux_array_inst) + vertical_port_order.append(self.sense_amp_array_inst) + vertical_port_order.append(self.write_driver_array_inst) + vertical_port_order.append(self.write_mask_and_array_inst) + + # Add one column for the the RBL + if self.port==0: + x_offset = self.bitcell.width + else: + x_offset = 0 + + vertical_port_offsets = 5 * [None] + self.width = x_offset + self.height = 0 + for i, p in enumerate(vertical_port_order): + if p == None: + continue + self.height += (p.height + self.m2_gap) + self.width = max(self.width, p.width) + vertical_port_offsets[i] = vector(x_offset, self.height) + + # Reversed order + self.write_mask_and_offset = vertical_port_offsets[4] + self.write_driver_offset = vertical_port_offsets[3] + self.sense_amp_offset = vertical_port_offsets[2] + self.column_mux_offset = vertical_port_offsets[1] + self.precharge_offset = vertical_port_offsets[0] + # Shift the precharge left if port 0 + if self.precharge_offset and self.port == 0: + self.precharge_offset -= vector(x_offset, 0) + + def place_instances(self): + """ Place the instances. """ + + # These are fixed in the order: write mask ANDs, write driver, sense amp, column mux, precharge, + # even if the item is not used in a given port (it will be None then) + if self.write_mask_and_offset: + self.place_write_mask_and_array(self.write_mask_and_offset) + if self.write_driver_offset: + self.place_write_driver_array(self.write_driver_offset) + if self.sense_amp_offset: + self.place_sense_amp_array(self.sense_amp_offset) + if self.precharge_offset: + self.place_precharge_array(self.precharge_offset) + if self.column_mux_offset: + self.place_column_mux_array(self.column_mux_offset) + + + def route_sense_amp_out(self, port): + """ Add pins for the sense amp output """ + + for bit in range(self.word_size): + data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(bit)) + self.add_layout_pin_rect_center(text="dout_{0}".format(bit), + layer=data_pin.layer, + offset=data_pin.center(), + height=data_pin.height(), + width=data_pin.width()) + + + def route_write_driver_in(self, port): + """ Connecting write driver """ + + for row in range(self.word_size): + data_name = "data_{}".format(row) + din_name = "din_{}".format(row) + self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) + + + def route_write_mask_and_array_in(self, port): + """ Add pins for the write mask and array input """ + + for bit in range(self.num_wmasks): + wmask_in_name = "wmask_in_{}".format(bit) + bank_wmask_name = "bank_wmask_{}".format(bit) + self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name) + + + def route_write_mask_and_array_to_write_driver(self,port): + """ Routing of wdriver_sel_{} between write mask AND array and write driver array. Adds layout pin for write + mask AND array output and via for write driver enable """ + + inst1 = self.write_mask_and_array_inst + inst2 = self.write_driver_array_inst + + loc = 0 + for bit in range(self.num_wmasks): + # Bring write mask AND array output pin to port data level + self.copy_layout_pin(inst1, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit)) + + wmask_out_pin = inst1.get_pin("wmask_out_{0}".format(bit)) + wdriver_en_pin = inst2.get_pin("en_{0}".format(bit)) + + # The metal2 wdriver_sel_{} wire must hit the en_{} pin after the closest bitline pin that's right of the + # the wdriver_sel_{} pin in the write driver AND array. + if bit == 0: + # When the write mask output pin is right of the bitline, the target is found + while (wmask_out_pin.lx() + self.m2_pitch > inst2.get_pin("data_{0}".format(loc)).rx()): + loc += 1 + length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch + debug.check(loc<=self.num_wmasks,"Couldn't route the write mask select.") + else: + # Stride by the write size rather than finding the next pin to the right + loc += self.write_size + length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch + + + beg_pos = wmask_out_pin.center() + middle_pos = vector(length,wmask_out_pin.cy()) + end_pos = vector(length, wdriver_en_pin.cy()) + + # Add via for the write driver array's enable input + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=end_pos) + + # Route between write mask AND array and write driver array + self.add_wire(("metal1","via1","metal2"), [beg_pos, middle_pos, end_pos]) + + + def route_column_mux_to_precharge_array(self, port): + """ Routing of BL and BR between col mux and precharge array """ + + # Only do this if we have a column mux! + if self.col_addr_size==0: + return + + inst1 = self.column_mux_array_inst + inst2 = self.precharge_array_inst + if self.port==0: + self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1) + else: + self.connect_bitlines(inst1, inst2, self.num_cols) + + + def route_sense_amp_to_column_mux_or_precharge_array(self, port): + """ Routing of BL and BR between sense_amp and column mux or precharge array """ + inst2 = self.sense_amp_array_inst + + if self.col_addr_size>0: + # Sense amp is connected to the col mux + inst1 = self.column_mux_array_inst + inst1_bl_name = "bl_out_{}" + inst1_br_name = "br_out_{}" + start_bit = 0 + else: + # Sense amp is directly connected to the precharge array + inst1 = self.precharge_array_inst + inst1_bl_name = "bl_{}" + inst1_br_name = "br_{}" + if self.port==0: + start_bit=1 + else: + start_bit=0 + + + self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, + inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit) + + + def route_write_driver_to_column_mux_or_precharge_array(self, port): + """ Routing of BL and BR between sense_amp and column mux or precharge array """ + inst2 = self.write_driver_array_inst + + if self.col_addr_size>0: + # Write driver is connected to the col mux + inst1 = self.column_mux_array_inst + inst1_bl_name = "bl_out_{}" + inst1_br_name = "br_out_{}" + start_bit = 0 + else: + # Sense amp is directly connected to the precharge array + inst1 = self.precharge_array_inst + inst1_bl_name = "bl_{}" + inst1_br_name = "br_{}" + if self.port==0: + start_bit=1 + else: + start_bit=0 + + self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, + inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit) + + + def route_write_driver_to_sense_amp(self, port): + """ Routing of BL and BR between write driver and sense amp """ + + inst1 = self.write_driver_array_inst + inst2 = self.sense_amp_array_inst + + # These should be pitch matched in the cell library, + # but just in case, do a channel route. + self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size) + + + def route_bitline_pins(self): + """ Add the bitline pins for the given port """ + + # Connect one bitline to the RBL and offset the indices for the other BLs + if self.port==0: + self.copy_layout_pin(self.precharge_array_inst, "bl_0", "rbl_bl") + self.copy_layout_pin(self.precharge_array_inst, "br_0", "rbl_br") + bit_offset=1 + elif self.port==1: + self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(self.num_cols), "rbl_bl") + self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(self.num_cols), "rbl_br") + bit_offset=0 + else: + bit_offset=0 + + for bit in range(self.num_cols): + if self.precharge_array_inst: + self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit+bit_offset), "bl_{}".format(bit)) + self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit+bit_offset), "br_{}".format(bit)) + else: + debug.error("Didn't find precharge array.") + + + def route_control_pins(self): + """ Add the control pins: s_en, p_en_bar, w_en """ + if self.precharge_array_inst: + self.copy_layout_pin(self.precharge_array_inst, "en_bar", "p_en_bar") + if self.column_mux_array_inst: + sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] + for pin_name in sel_names: + self.copy_layout_pin(self.column_mux_array_inst, pin_name) + if self.sense_amp_array_inst: + self.copy_layout_pin(self.sense_amp_array_inst, "en", "s_en") + if self.write_driver_array_inst: + if self.write_mask_and_array_inst: + for bit in range(self.num_wmasks): + # Add write driver's en_{} pins + self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit), "wdriver_sel_{}".format(bit)) + else: + self.copy_layout_pin(self.write_driver_array_inst, "en", "w_en") + if self.write_mask_and_array_inst: + self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en") + + + + def channel_route_bitlines(self, inst1, inst2, num_bits, + inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0, + inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0): + """ + Route the bl and br of two modules using the channel router. + """ + + # determine top and bottom automatically. + # since they don't overlap, we can just check the bottom y coordinate. + if inst1.by() < inst2.by(): + (bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit) + (top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit) + else: + (bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit) + (top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit) + + + # Channel route each mux separately since we don't minimize the number + # of tracks in teh channel router yet. If we did, we could route all the bits at once! + offset = bottom_inst.ul() + vector(0,self.m1_pitch) + for bit in range(num_bits): + bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit+bottom_start_bit)), bottom_inst.get_pin(bottom_br_name.format(bit+bottom_start_bit))] + top_names = [top_inst.get_pin(top_bl_name.format(bit+top_start_bit)), top_inst.get_pin(top_br_name.format(bit+top_start_bit))] + route_map = list(zip(bottom_names, top_names)) + self.create_horizontal_channel_route(route_map, offset) + + + def connect_bitlines(self, inst1, inst2, num_bits, + inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0, + inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0): + + """ + Connect the bl and br of two modules. + This assumes that they have sufficient space to create a jog + in the middle between the two modules (if needed). + """ + + # determine top and bottom automatically. + # since they don't overlap, we can just check the bottom y coordinate. + if inst1.by() < inst2.by(): + (bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit) + (top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit) + else: + (bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit) + (top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit) + + for col in range(num_bits): + bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col+bottom_start_bit)).uc() + bottom_br = bottom_inst.get_pin(bottom_br_name.format(col+bottom_start_bit)).uc() + top_bl = top_inst.get_pin(top_bl_name.format(col+top_start_bit)).bc() + top_br = top_inst.get_pin(top_br_name.format(col+top_start_bit)).bc() + + yoffset = 0.5*(top_bl.y+bottom_bl.y) + self.add_path("metal2",[bottom_bl, vector(bottom_bl.x,yoffset), + vector(top_bl.x,yoffset), top_bl]) + self.add_path("metal2",[bottom_br, vector(bottom_br.x,yoffset), + vector(top_br.x,yoffset), top_br]) + + def graph_exclude_precharge(self): + """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" + if self.precharge_array_inst: + self.graph_inst_exclude.add(self.precharge_array_inst) + diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 67581e1a..2d98ba14 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import design import debug from tech import drc @@ -28,10 +35,11 @@ class precharge_array(design.design): def add_pins(self): """Adds pins for spice file""" for i in range(self.columns): - self.add_pin("bl_{0}".format(i)) - self.add_pin("br_{0}".format(i)) - self.add_pin("en_bar") - self.add_pin("vdd") + # These are outputs from the precharge only + self.add_pin("bl_{0}".format(i), "OUTPUT") + self.add_pin("br_{0}".format(i), "OUTPUT") + self.add_pin("en_bar", "INPUT") + self.add_pin("vdd", "POWER") def create_netlist(self): self.add_modules() @@ -44,6 +52,7 @@ class precharge_array(design.design): self.place_insts() self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_modules(self): @@ -107,3 +116,4 @@ class precharge_array(design.design): #Assume single port precharge_en_cin = self.pc_cell.get_en_cin() return precharge_en_cin*self.columns + diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py new file mode 100644 index 00000000..639d0714 --- /dev/null +++ b/compiler/modules/replica_bitcell_array.py @@ -0,0 +1,425 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# + +import debug +import design +from tech import drc, spice +from vector import vector +from globals import OPTS +from sram_factory import factory +import logical_effort +import bitcell_array +import replica_column +import dummy_array + +class replica_bitcell_array(design.design): + """ + Creates a bitcell arrow of cols x rows and then adds the replica + and dummy columns and rows. Replica columns are on the left and + right, respectively and connected to the given bitcell ports. + Dummy are the outside columns/rows with WL and BL tied to gnd. + Requires a regular bitcell array, replica bitcell, and dummy + bitcell (Bl/BR disconnected). + """ + def __init__(self, cols, rows, left_rbl, right_rbl, bitcell_ports, name): + design.design.__init__(self, name) + debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) + self.add_comment("rows: {0} cols: {1}".format(rows, cols)) + + self.column_size = cols + self.row_size = rows + self.left_rbl = left_rbl + self.right_rbl = right_rbl + self.bitcell_ports = bitcell_ports + + debug.check(left_rbl+right_rbl==len(self.all_ports),"Invalid number of RBLs for port configuration.") + debug.check(left_rbl+right_rbl==len(self.bitcell_ports),"Bitcell ports must match total RBLs.") + + # Two dummy rows/cols plus replica for each port + self.extra_rows = 2 + left_rbl + right_rbl + self.extra_cols = 2 + left_rbl + right_rbl + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + # We don't offset this because we need to align + # the replica bitcell in the control logic + #self.offset_all_coordinates() + + + def create_netlist(self): + """ Create and connect the netlist """ + self.add_modules() + self.add_pins() + self.create_instances() + + def add_modules(self): + """ Array and dummy/replica columns + + d or D = dummy cell (caps to distinguish grouping) + r or R = replica cell (caps to distinguish grouping) + b or B = bitcell + replica columns 1 + v v + bdDDDDDDDDDDDDDDdb <- Dummy row + bdDDDDDDDDDDDDDDrb <- Dummy row + br--------------rb + br| Array |rb + br| row x col |rb + br--------------rb + brDDDDDDDDDDDDDDdb <- Dummy row + bdDDDDDDDDDDDDDDdb <- Dummy row + + ^^^^^^^^^^^^^^^ + dummy rows cols x 1 + + ^ dummy columns ^ + 1 x (rows + 4) + """ + + # Bitcell for port names only + self.cell = factory.create(module_type="bitcell") + + # Bitcell array + self.bitcell_array = factory.create(module_type="bitcell_array", + cols=self.column_size, + rows=self.row_size) + self.add_mod(self.bitcell_array) + + # Replica bitlines + self.replica_columns = {} + for bit in range(self.left_rbl+self.right_rbl): + if bit1 port) + for port in range(self.left_rbl): + # Make names for all RBLs + wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.all_ports))] + # Keep track of the pin that is the RBL + self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] + self.replica_col_wl_names.extend(wl_names) + # Regular WLs + self.replica_col_wl_names.extend(self.bitcell_array_wl_names) + # Right port WLs (one dummy for each port when we allow >1 port) + for port in range(self.left_rbl,self.left_rbl+self.right_rbl): + # Make names for all RBLs + wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.all_ports))] + # Keep track of the pin that is the RBL + self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] + self.replica_col_wl_names.extend(wl_names) + self.replica_col_wl_names.extend(["{0}_top".format(x) for x in self.dummy_cell_wl_names]) + + # Left/right dummy columns are connected identically to the replica column + self.dummy_col_wl_names = self.replica_col_wl_names + + + # Per port bitline names + self.replica_bl_names = {} + self.replica_wl_names = {} + # Array of all port bitline names + for port in range(self.left_rbl+self.right_rbl): + left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x),port) for x in range(len(self.all_ports))] + right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x),port) for x in range(len(self.all_ports))] + # Keep track of the left pins that are the RBL + self.rbl_bl_names[port]=left_names[self.bitcell_ports[port]] + self.rbl_br_names[port]=right_names[self.bitcell_ports[port]] + # Interleave the left and right lists + bl_names = [x for t in zip(left_names, right_names) for x in t] + self.replica_bl_names[port] = bl_names + + wl_names = ["rbl_{0}_{1}".format(x,port) for x in self.cell.get_all_wl_names()] + #wl_names[port] = "rbl_wl{}".format(port) + self.replica_wl_names[port] = wl_names + + + # External pins + self.add_pin_list(self.bitcell_array_bl_names, "INOUT") + # Need to sort by port order since dictionary values may not be in order + bl_names = [self.rbl_bl_names[x] for x in sorted(self.rbl_bl_names.keys())] + br_names = [self.rbl_br_names[x] for x in sorted(self.rbl_br_names.keys())] + for (bl_name,br_name) in zip(bl_names,br_names): + self.add_pin(bl_name,"OUTPUT") + self.add_pin(br_name,"OUTPUT") + self.add_pin_list(self.bitcell_array_wl_names, "INPUT") + # Need to sort by port order since dictionary values may not be in order + wl_names = [self.rbl_wl_names[x] for x in sorted(self.rbl_wl_names.keys())] + for pin_name in wl_names: + self.add_pin(pin_name,"INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + + def create_instances(self): + """ Create the module instances used in this design """ + + supplies = ["vdd", "gnd"] + + # Used for names/dimensions only + self.cell = factory.create(module_type="bitcell") + + # Main array + self.bitcell_array_inst=self.add_inst(name="bitcell_array", + mod=self.bitcell_array) + self.connect_inst(self.bitcell_array_bl_names + self.bitcell_array_wl_names + supplies) + + # Replica columns + self.replica_col_inst = {} + for port in range(self.left_rbl+self.right_rbl): + self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port), + mod=self.replica_columns[port]) + self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies) + + + # Dummy rows under the bitcell array (connected with with the replica cell wl) + self.dummy_row_replica_inst = {} + for port in range(self.left_rbl+self.right_rbl): + self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port), + mod=self.dummy_row) + self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names[port] + supplies) + + + # Top/bottom dummy rows + self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot", + mod=self.dummy_row) + self.connect_inst(self.dummy_row_bl_names + [x+"_bot" for x in self.dummy_cell_wl_names] + supplies) + self.dummy_row_top_inst=self.add_inst(name="dummy_row_top", + mod=self.dummy_row) + self.connect_inst(self.dummy_row_bl_names + [x+"_top" for x in self.dummy_cell_wl_names] + supplies) + + + # Left/right Dummy columns + self.dummy_col_left_inst=self.add_inst(name="dummy_col_left", + mod=self.dummy_col) + self.connect_inst([x+"_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) + self.dummy_col_right_inst=self.add_inst(name="dummy_col_right", + mod=self.dummy_col) + self.connect_inst([x+"_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) + + + + def create_layout(self): + + self.height = (self.row_size+self.extra_rows)*self.dummy_row.height + self.width = (self.column_size+self.extra_cols)*self.cell.width + + # This is a bitcell x bitcell offset to scale + offset = vector(self.cell.width, self.cell.height) + + self.bitcell_array_inst.place(offset=[0,0]) + + # To the left of the bitcell array + for bit in range(self.left_rbl): + self.replica_col_inst[bit].place(offset=offset.scale(-bit-1,-self.left_rbl-1)) + # To the right of the bitcell array + for bit in range(self.right_rbl): + self.replica_col_inst[self.left_rbl+bit].place(offset=offset.scale(bit,-self.left_rbl-1)+self.bitcell_array_inst.lr()) + + + # Far top dummy row (first row above array is NOT flipped) + flip_dummy = self.right_rbl%2 + self.dummy_row_top_inst.place(offset=offset.scale(0,self.right_rbl+flip_dummy)+self.bitcell_array_inst.ul(), + mirror="MX" if flip_dummy else "R0") + # Far bottom dummy row (first row below array IS flipped) + flip_dummy = (self.left_rbl+1)%2 + self.dummy_row_bot_inst.place(offset=offset.scale(0,-self.left_rbl-1+flip_dummy), + mirror="MX" if flip_dummy else "R0") + # Far left dummy col + self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl-1,-self.left_rbl-1)) + # Far right dummy col + self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl,-self.left_rbl-1)+self.bitcell_array_inst.lr()) + + # Replica dummy rows + for bit in range(self.left_rbl): + self.dummy_row_replica_inst[bit].place(offset=offset.scale(0,-bit-bit%2), + mirror="R0" if bit%2 else "MX") + for bit in range(self.right_rbl): + self.dummy_row_replica_inst[self.left_rbl+bit].place(offset=offset.scale(0,bit+bit%2)+self.bitcell_array_inst.ul(), + mirror="MX" if bit%2 else "R0") + + + self.translate_all(offset.scale(-1-self.left_rbl,-1-self.left_rbl)) + + self.add_layout_pins() + + self.add_boundary() + + self.DRC_LVS() + + + def add_layout_pins(self): + """ Add the layout pins """ + + # Main array wl and bl/br + pin_names = self.bitcell_array.get_pin_names() + for pin_name in pin_names: + if pin_name.startswith("wl"): + pin_list = self.bitcell_array_inst.get_pins(pin_name) + for pin in pin_list: + self.add_layout_pin(text=pin_name, + layer=pin.layer, + offset=pin.ll().scale(0,1), + width=self.width, + height=pin.height()) + elif pin_name.startswith("bl") or pin_name.startswith("br"): + pin_list = self.bitcell_array_inst.get_pins(pin_name) + for pin in pin_list: + self.add_layout_pin(text=pin_name, + layer=pin.layer, + offset=pin.ll().scale(1,0), + width=pin.width(), + height=self.height) + + + # Replica wordlines + for port in range(self.left_rbl+self.right_rbl): + inst = self.replica_col_inst[port] + for (pin_name,wl_name) in zip(self.cell.get_all_wl_names(),self.replica_wl_names[port]): + # +1 for dummy row + pin_bit = port+1 + # +row_size if above the array + if port>=self.left_rbl: + pin_bit += self.row_size + + pin_name += "_{}".format(pin_bit) + pin = inst.get_pin(pin_name) + if wl_name in self.rbl_wl_names.values(): + self.add_layout_pin(text=wl_name, + layer=pin.layer, + offset=pin.ll().scale(0,1), + width=self.width, + height=pin.height()) + + + # Replica bitlines + for port in range(self.left_rbl+self.right_rbl): + inst = self.replica_col_inst[port] + for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(),self.replica_bl_names[port]): + pin = inst.get_pin(pin_name) + if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names: + name = bl_name + else: + name = "rbl_{0}_{1}".format(pin_name,port) + self.add_layout_pin(text=name, + layer=pin.layer, + offset=pin.ll().scale(1,0), + width=pin.width(), + height=self.height) + + + for pin_name in ["vdd","gnd"]: + for inst in self.insts: + pin_list = inst.get_pins(pin_name) + for pin in pin_list: + self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) + + + + + def get_rbl_wl_name(self, port): + """ Return the WL for the given RBL port """ + return self.rbl_wl_names[port] + + def get_rbl_bl_name(self, port): + """ Return the BL for the given RBL port """ + return self.rbl_bl_names[port] + + def get_rbl_br_name(self, port): + """ Return the BR for the given RBL port """ + return self.rbl_br_names[port] + + def analytical_power(self, corner, load): + """Power of Bitcell array and bitline in nW.""" + from tech import drc, parameter + + # Dynamic Power from Bitline + bl_wire = self.gen_bl_wire() + cell_load = 2 * bl_wire.return_input_cap() + bl_swing = OPTS.rbl_delay_percentage + freq = spice["default_event_frequency"] + bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) + + #Calculate the bitcell power which currently only includes leakage + cell_power = self.cell.analytical_power(corner, load) + + #Leakage power grows with entire array and bitlines. + total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, + cell_power.leakage * self.column_size * self.row_size) + return total_power + + def gen_bl_wire(self): + if OPTS.netlist_only: + height = 0 + else: + height = self.height + bl_pos = 0 + bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), 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 + + def get_wordline_cin(self): + """Get the relative input capacitance from the wordline connections in all the bitcell""" + #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns + bitcell_wl_cin = self.cell.get_wl_cin() + total_cin = bitcell_wl_cin * self.column_size + return total_cin + + def graph_exclude_bits(self, targ_row, targ_col): + """Excludes bits in column from being added to graph except target""" + self.bitcell_array.graph_exclude_bits(targ_row, targ_col) + + def graph_exclude_replica_col_bits(self): + """Exclude all replica/dummy cells in the replica columns except the replica bit.""" + + for port in range(self.left_rbl+self.right_rbl): + self.replica_columns[port].exclude_all_but_replica() + + def get_cell_name(self, inst_name, row, col): + """Gets the spice name of the target bitcell.""" + return self.bitcell_array.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col) diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py deleted file mode 100644 index 8d93b257..00000000 --- a/compiler/modules/replica_bitline.py +++ /dev/null @@ -1,627 +0,0 @@ -import debug -import design -from tech import drc -import contact -from sram_factory import factory -from vector import vector -from globals import OPTS - -class replica_bitline(design.design): - """ - Generate a module that simulates the delay of control logic - and bit line charging. Stages is the depth of the delay - line and rows is the height of the replica bit loads. - """ - - def __init__(self, name, delay_fanout_list, bitcell_loads): - design.design.__init__(self, name) - - self.bitcell_loads = bitcell_loads - self.delay_fanout_list = delay_fanout_list - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - def create_netlist(self): - self.add_modules() - self.add_pins() - self.create_instances() - - def create_layout(self): - self.calculate_module_offsets() - self.place_instances() - self.route() - self.add_layout_pins() - - self.offset_all_coordinates() - - #self.add_lvs_correspondence_points() - - # Extra pitch on top and right - self.width = self.rbl_inst.rx() - self.dc_inst.lx() + self.m2_pitch - self.height = max(self.rbl_inst.uy(), self.dc_inst.uy()) + self.m3_pitch - - self.DRC_LVS() - - def add_pins(self): - for pin in ["en", "out", "vdd", "gnd"]: - self.add_pin(pin) - - def calculate_module_offsets(self): - """ Calculate all the module offsets """ - - # These aren't for instantiating, but we use them to get the dimensions - self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height) - - # Quadrant 1: Replica bitline and such are not rotated, but they must be placed far enough - # away from the delay chain/inverter with space for three M2 tracks - self.bitcell_offset = vector(0,self.replica_bitcell.height) - self.rbl_offset = self.bitcell_offset - - # Gap between the delay chain and RBL - gap_width = 2*self.m2_pitch - - # Quadrant 4: with some space below it and tracks on the right for vdd/gnd - self.delay_chain_offset = vector(-self.delay_chain.width-gap_width,self.replica_bitcell.height) - - # Will be flipped vertically below the delay chain - # Align it with the inverters in the delay chain to simplify supply connections - self.rbl_inv_offset = self.delay_chain_offset + vector(2*self.inv.width, 0) - - # Placed next to the replica bitcell - self.access_tx_offset = vector(-gap_width-self.access_tx.width-self.inv.width, 0.5*self.inv.height) - - - def add_modules(self): - """ Add the modules for later usage """ - - self.replica_bitcell = factory.create(module_type="replica_bitcell") - self.add_mod(self.replica_bitcell) - - # This is the replica bitline load column that is the height of our array - self.rbl = factory.create(module_type="bitcell_array", - cols=1, - rows=self.bitcell_loads) - self.add_mod(self.rbl) - - # FIXME: The FO and depth of this should be tuned - self.delay_chain = factory.create(module_type="delay_chain", - fanout_list=self.delay_fanout_list) - self.add_mod(self.delay_chain) - - self.inv = factory.create(module_type="pinv") - self.add_mod(self.inv) - - self.access_tx = factory.create(module_type="ptx", - tx_type="pmos") - self.add_mod(self.access_tx) - - def create_instances(self): - """ Create all of the module instances in the logical netlist """ - - # This is the threshold detect inverter on the output of the RBL - self.rbl_inv_inst=self.add_inst(name="rbl_inv", - mod=self.inv) - self.connect_inst(["bl0_0", "out", "vdd", "gnd"]) - - self.tx_inst=self.add_inst(name="rbl_access_tx", - mod=self.access_tx) - # D, G, S, B - self.connect_inst(["vdd", "delayed_en", "bl0_0", "vdd"]) - # add the well and poly contact - - self.dc_inst=self.add_inst(name="delay_chain", - mod=self.delay_chain) - self.connect_inst(["en", "delayed_en", "vdd", "gnd"]) - - self.rbc_inst=self.add_inst(name="bitcell", - mod=self.replica_bitcell) - temp = [] - for port in self.all_ports: - temp.append("bl{}_0".format(port)) - temp.append("br{}_0".format(port)) - for port in self.all_ports: - temp.append("delayed_en") - temp.append("vdd") - temp.append("gnd") - self.connect_inst(temp) - - self.rbl_inst=self.add_inst(name="load", - mod=self.rbl) - - temp = [] - for port in self.all_ports: - temp.append("bl{}_0".format(port)) - temp.append("br{}_0".format(port)) - for wl in range(self.bitcell_loads): - for port in self.all_ports: - temp.append("gnd") - temp.append("vdd") - temp.append("gnd") - self.connect_inst(temp) - - self.wl_list = self.rbl.cell.list_all_wl_names() - self.bl_list = self.rbl.cell.list_all_bl_names() - - def place_instances(self): - """ Add all of the module instances in the logical netlist """ - - # This is the threshold detect inverter on the output of the RBL - self.rbl_inv_inst.place(offset=self.rbl_inv_offset, - rotate=180) - - self.tx_inst.place(self.access_tx_offset) - - self.dc_inst.place(self.delay_chain_offset) - - self.rbc_inst.place(offset=self.bitcell_offset, - mirror="MX") - - self.rbl_inst.place(self.rbl_offset) - - - def route(self): - """ Connect all the signals together """ - self.route_supplies() - self.route_wl() - self.route_access_tx() - - def route_wl(self): - """ Connect the RBL word lines to gnd """ - # Connect the WL and gnd pins directly to the center and right gnd rails - for row in range(self.bitcell_loads): - wl = self.wl_list[0]+"_{}".format(row) - pin = self.rbl_inst.get_pin(wl) - - # Route the connection to the right so that it doesn't interfere with the cells - # Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions - pin_right = pin.rc() - pin_extension = pin_right + vector(self.m3_pitch,0) - - if pin.layer != "metal1": - continue - pin_width_ydir = pin.uy()-pin.by() - #Width is set to pin y width to avoid DRC issues with m1 gaps - self.add_path("metal1", [pin_right, pin_extension], pin_width_ydir) - self.add_power_pin("gnd", pin_extension) - - # for multiport, need to short wordlines to each other so they all connect to gnd. - wl_last = self.wl_list[-1]+"_{}".format(row) - pin_last = self.rbl_inst.get_pin(wl_last) - self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0)) - - def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None): - """Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins.""" - #Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord. - #This is my (Hunter) first time editing layout in openram so this function is likely not optimal. - if len(self.all_ports) > 1: - #1. Create vertical metal for all the bitlines to connect to - #m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped - correct_y = vector(0, 0.5*drc("minwidth_metal1")) - #x spacing depends on the side being drawn. Unknown to me (Hunter) why the size of the space differs by the side. - #I assume this is related to how a wire is draw, but I have not investigated the issue. - if pin_side == "right": - correct_x = vector(0.5*drc("minwidth_metal1"), 0) - if offset_x_vec != None: - correct_x = offset_x_vec - else: - correct_x = vector(1.5*drc("minwidth_metal1"), 0) - - if wl_pin_a.uy() > wl_pin_b.uy(): - self.add_path("metal1", [wl_pin_a.rc()+correct_x+correct_y, wl_pin_b.rc()+correct_x-correct_y]) - else: - self.add_path("metal1", [wl_pin_a.rc()+correct_x-correct_y, wl_pin_b.rc()+correct_x+correct_y]) - elif pin_side == "left": - if offset_x_vec != None: - correct_x = offset_x_vec - else: - correct_x = vector(1.5*drc("minwidth_metal1"), 0) - - if wl_pin_a.uy() > wl_pin_b.uy(): - self.add_path("metal1", [wl_pin_a.lc()-correct_x+correct_y, wl_pin_b.lc()-correct_x-correct_y]) - else: - self.add_path("metal1", [wl_pin_a.lc()-correct_x-correct_y, wl_pin_b.lc()-correct_x+correct_y]) - else: - debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1) - - #2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this. - for port in self.all_ports: - if is_replica_cell: - wl = self.wl_list[port] - pin = self.rbc_inst.get_pin(wl) - else: - wl = self.wl_list[port]+"_{}".format(cell_row) - pin = self.rbl_inst.get_pin(wl) - - if pin_side == "left": - self.add_path("metal1", [pin.lc()-correct_x, pin.lc()]) - elif pin_side == "right": - self.add_path("metal1", [pin.rc()+correct_x, pin.rc()]) - - - - def route_supplies(self): - """ Propagate all vdd/gnd pins up to this level for all modules """ - - # These are the instances that every bank has - top_instances = [self.rbl_inst, - self.dc_inst] - for inst in top_instances: - self.copy_layout_pin(inst, "vdd") - self.copy_layout_pin(inst, "gnd") - - - # Route the inverter supply pin from M1 - # Only vdd is needed because gnd shares a rail with the delay chain - pin = self.rbl_inv_inst.get_pin("vdd") - self.add_power_pin("vdd", pin.lc()) - - pin=self.rbc_inst.get_pin("vdd") - self.add_power_pin("vdd", pin.center(), 0, pin.layer) - - for pin in self.rbc_inst.get_pins("gnd"): - self.add_power_pin("gnd", pin.center()) - - - - def route_access_tx(self): - # GATE ROUTE - # 1. Add the poly contact and nwell enclosure - # Determines the y-coordinate of where to place the gate input poly pin - # (middle in between the pmos and nmos) - - poly_pin = self.tx_inst.get_pin("G") - poly_offset = poly_pin.uc() - # This centers the contact above the poly by one pitch - contact_offset = poly_offset + vector(0,self.m2_pitch) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=contact_offset) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=contact_offset) - self.add_segment_center(layer="poly", - start=poly_offset, - end=contact_offset) - nwell_offset = self.rbl_inv_offset + vector(-self.inv.height,self.inv.width) - # self.add_rect(layer="nwell", - # offset=nwell_offset, - # width=0.5*self.inv.height, - # height=self.delay_chain_offset.y-nwell_offset.y) - - # 2. Route delay chain output to access tx gate - delay_en_offset = self.dc_inst.get_pin("out").bc() - self.add_path("metal2", [delay_en_offset,contact_offset]) - - # 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_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) - #wl_mid2 = vector(xmid_point,wl_offset.y) - self.add_path("metal1", [wl_offset, wl_mid1, wl_mid2, contact_offset]) - - # 4. Short wodlines if multiport - wl = self.wl_list[0] - wl_last = self.wl_list[-1] - pin = self.rbc_inst.get_pin(wl) - pin_last = self.rbc_inst.get_pin(wl_last) - x_offset = self.short_wordlines(pin, pin_last, "left", True) - - #correct = vector(0.5*drc("minwidth_metal1"), 0) - #self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct]) - - # DRAIN ROUTE - # Route the drain to the vdd rail - drain_offset = self.tx_inst.get_pin("D").center() - self.add_power_pin("vdd", drain_offset, rotate=0) - - # SOURCE ROUTE - # Route the drain to the RBL inverter input - source_offset = self.tx_inst.get_pin("S").center() - inv_A_offset = self.rbl_inv_inst.get_pin("A").center() - self.add_path("metal1",[source_offset, inv_A_offset]) - - # Route the connection of the source route to the RBL bitline (left) - # Via will go halfway down from the bitcell - bl_offset = self.rbc_inst.get_pin(self.bl_list[0]).bc() - # Route down a pitch so we can use M2 routing - bl_down_offset = bl_offset - vector(0, self.m2_pitch) - self.add_path("metal2",[source_offset, bl_down_offset, bl_offset]) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=source_offset) - - # BODY ROUTE - # Connect it to the inverter well - nwell_offset = self.rbl_inv_inst.lr() - ur_offset = self.tx_inst.ur() - self.add_rect(layer="nwell", - offset=nwell_offset, - width=ur_offset.x-nwell_offset.x, - height=ur_offset.y-nwell_offset.y) - - def route_vdd(self): - """ Route all signals connected to vdd """ - - self.copy_layout_pin(self.dc_inst,"vdd") - self.copy_layout_pin(self.rbc_inst,"vdd") - - # Connect the WL and vdd pins directly to the center and right vdd rails - # Connect RBL vdd pins to center and right rails - rbl_vdd_pins = self.rbl_inst.get_pins("vdd") - for pin in rbl_vdd_pins: - if pin.layer != "metal1": - continue - start = vector(self.center_vdd_pin.cx(),pin.cy()) - end = vector(self.right_vdd_pin.cx(),pin.cy()) - self.add_layout_pin_segment_center(text="vdd", - layer="metal1", - start=start, - end=end) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=start, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end, - rotate=90) - - # Add via for the inverter - pin = self.rbl_inv_inst.get_pin("vdd") - start = vector(self.left_vdd_pin.cx(),pin.cy()) - end = vector(self.center_vdd_pin.cx(),pin.cy()) - self.add_segment_center(layer="metal1", - start=start, - end=end) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=start, - rotate=90) - - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end, - rotate=90) - - - # Add via for the RBC - pin = self.rbc_inst.get_pin("vdd") - start = pin.lc() - end = vector(self.right_vdd_pin.cx(),pin.cy()) - self.add_segment_center(layer="metal1", - start=start, - end=end) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end, - rotate=90) - - # Create the RBL rails too - rbl_pins = self.rbl_inst.get_pins("vdd") - for pin in rbl_pins: - if pin.layer != "metal1": - continue - # If above the delay line, route the full width - left = vector(self.left_vdd_pin.cx(),pin.cy()) - center = vector(self.center_vdd_pin.cx(),pin.cy()) - if pin.cy() > self.dc_inst.uy() + self.m1_pitch: - start = left - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left, - rotate=90) - else: - start = center - end = vector(self.right_vdd_pin.cx()+0.5*self.m1_width,pin.cy()) - self.add_layout_pin_segment_center(text="vdd", - layer="metal1", - start=start, - end=end) - - - - - - - def route_gnd(self): - """ Route all signals connected to gnd """ - - # Route the gnd lines from left to right - - # Add via for the delay chain - left_gnd_start = self.dc_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0) - left_gnd_end = vector(left_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch) - self.left_gnd_pin=self.add_segment_center(layer="metal2", - start=left_gnd_start, - end=left_gnd_end) - - # Gnd line to the left of the replica bitline - center_gnd_start = self.rbc_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0) - center_gnd_end = vector(center_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch) - self.center_gnd_pin=self.add_segment_center(layer="metal2", - start=center_gnd_start, - end=center_gnd_end) - - # Gnd line to the right of the replica bitline - right_gnd_start = self.rbc_inst.lr().scale(1,0) + vector(self.m2_pitch,0) - right_gnd_end = vector(right_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch) - self.right_gnd_pin=self.add_segment_center(layer="metal2", - start=right_gnd_start, - end=right_gnd_end) - - - - # Connect the WL and gnd pins directly to the center and right gnd rails - for row in range(self.bitcell_loads): - wl = self.wl_list[0]+"_{}".format(row) - pin = self.rbl_inst.get_pin(wl) - if pin.layer != "metal1": - continue - # If above the delay line, route the full width - left = vector(self.left_gnd_pin.cx(),pin.cy()) - center = vector(self.center_gnd_pin.cx(),pin.cy()) - if pin.cy() > self.dc_inst.uy() + self.m1_pitch: - start = left - else: - start = center - end = vector(self.right_gnd_pin.cx(),pin.cy()) - self.add_layout_pin_segment_center(text="gnd", - layer="metal1", - start=start, - end=end) - if start == left: - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=center, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end, - rotate=90) - - - rbl_gnd_pins = self.rbl_inst.get_pins("gnd") - # Add L shapes to each vertical gnd rail - for pin in rbl_gnd_pins: - if pin.layer != "metal1": - continue - # If above the delay line, route the full width - left = vector(self.left_gnd_pin.cx(),pin.cy()) - center = vector(self.center_gnd_pin.cx(),pin.cy()) - if pin.cy() > self.dc_inst.uy() + self.m1_pitch: - start = left - else: - start = center - end = vector(self.right_gnd_pin.cx(),pin.cy()) - self.add_segment_center(layer="metal1", - start=start, - end=end) - if start == left: - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=left, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=center, - rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=end, - rotate=90) - - - - # Connect the gnd pins of the delay chain to the left rails - dc_gnd_pins = self.dc_inst.get_pins("gnd") - for pin in dc_gnd_pins: - if pin.layer != "metal1": - continue - start = vector(self.left_gnd_pin.cx(),pin.cy()) - # Note, we don't connect to the center rails because of - # via conflicts with the RBL - #end = vector(self.center_gnd_pin.cx(),pin.cy()) - end = pin.rc() - self.add_segment_center(layer="metal1", - start=start, - end=end) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=start, - rotate=90) - - # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=end, - #rotate=90) - - - # Add via for the inverter - # pin = self.rbl_inv_inst.get_pin("gnd") - # start = vector(self.left_gnd_pin.cx(),pin.cy()) - # end = vector(self.center_gnd_pin.cx(),pin.cy()) - # self.add_segment_center(layer="metal1", - # start=start, - # end=end) - # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=start, - #rotate=90) - # self.add_via_center(layers=("metal1", "via1", "metal2"), - # offset=end, - #rotate=90) - - - - # Create RBL rails too - rbl_pins = self.rbl_inst.get_pins("gnd") - for pin in rbl_pins: - if pin.layer != "metal2": - continue - start = vector(pin.cx(),self.right_gnd_pin.by()) - end = vector(pin.cx(),self.right_gnd_pin.uy()) - self.add_layout_pin_segment_center(text="gnd", - layer="metal2", - start=start, - end=end) - - - - def add_layout_pins(self): - """ Route the input and output signal """ - en_offset = self.dc_inst.get_pin("in").bc() - self.add_layout_pin_segment_center(text="en", - layer="metal2", - start=en_offset, - end=en_offset.scale(1,0)) - - out_offset = self.rbl_inv_inst.get_pin("Z").center() - self.add_layout_pin_segment_center(text="out", - layer="metal2", - start=out_offset, - end=out_offset.scale(1,0)) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=out_offset) - - def add_lvs_correspondence_points(self): - """ This adds some points for easier debugging if LVS goes wrong. - These should probably be turned off by default though, since extraction - will show these as ports in the extracted netlist. - """ - - pin = self.rbl_inv_inst.get_pin("A") - self.add_label_pin(text="bl[0]", - layer=pin.layer, - offset=pin.ll(), - height=pin.height(), - width=pin.width()) - - pin = self.dc_inst.get_pin("out") - self.add_label_pin(text="delayed_en", - layer=pin.layer, - offset=pin.ll(), - height=pin.height(), - width=pin.width()) - - def get_en_cin(self): - """Get the enable input relative capacitance""" - #The enable is only connected to the delay, get the cin from that module - en_cin = self.delay_chain.get_cin() - return en_cin - - def determine_sen_stage_efforts(self, ext_cout, inp_is_rise=True): - """Get the stage efforts from the en to s_en. Does not compute the delay for the bitline load.""" - stage_effort_list = [] - #Stage 1 is the delay chain - stage1_cout = self.get_delayed_en_cin() - stage1 = self.delay_chain.determine_delayed_en_stage_efforts(stage1_cout, inp_is_rise) - stage_effort_list += stage1 - - #There is a disconnect between the delay chain and inverter. The rise/fall of the input to the inverter - #Will be the negation of the previous stage. - last_stage_is_rise = not stage_effort_list[-1].is_rise - - #The delay chain triggers the enable on the replica bitline (rbl). This is used to track the bitline delay whereas this - #model is intended to track every but that. Therefore, the next stage is the inverter after the rbl. - stage2 = self.inv.get_stage_effort(ext_cout, last_stage_is_rise) - stage_effort_list.append(stage2) - - return stage_effort_list - - def get_delayed_en_cin(self): - """Get the fanout capacitance (relative) of the delayed enable from the delay chain.""" - access_tx_cin = self.access_tx.get_cin() - rbc_cin = self.replica_bitcell.get_wl_cin() - return access_tx_cin + rbc_cin - diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py new file mode 100644 index 00000000..c3f63b19 --- /dev/null +++ b/compiler/modules/replica_column.py @@ -0,0 +1,161 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import debug +import design +from tech import drc +import contact +from sram_factory import factory +from vector import vector +from globals import OPTS + +class replica_column(design.design): + """ + Generate a replica bitline column for the replica array. + Rows is the total number of rows i the main array. + Left_rbl and right_rbl are the number of left and right replica bitlines. + Replica bit specifies which replica column this is (to determine where to put the + replica cell. + """ + + def __init__(self, name, rows, left_rbl, right_rbl, replica_bit): + design.design.__init__(self, name) + + self.rows = rows + self.left_rbl = left_rbl + self.right_rbl = right_rbl + self.replica_bit = replica_bit + # left, right, regular rows plus top/bottom dummy cells + self.total_size = self.left_rbl+rows+self.right_rbl+2 + + debug.check(replica_bit!=0 and replica_bit!=rows,"Replica bit cannot be the dummy row.") + debug.check(replica_bit<=left_rbl or replica_bit>=self.total_size-right_rbl-1, + "Replica bit cannot be in the regular array.") + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_instances() + + def create_layout(self): + self.height = self.total_size*self.cell.height + self.width = self.cell.width + + self.place_instances() + self.add_layout_pins() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + + for bl_name in self.cell.get_all_bitline_names(): + # In the replica column, these are only outputs! + self.add_pin("{0}_{1}".format(bl_name,0), "OUTPUT") + + for row in range(self.total_size): + for wl_name in self.cell.get_all_wl_names(): + self.add_pin("{0}_{1}".format(wl_name,row), "INPUT") + + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def add_modules(self): + self.replica_cell = factory.create(module_type="replica_bitcell") + self.add_mod(self.replica_cell) + self.dummy_cell = factory.create(module_type="dummy_bitcell") + self.add_mod(self.dummy_cell) + # Used for pin names only + self.cell = factory.create(module_type="bitcell") + + def create_instances(self): + self.cell_inst = {} + for row in range(self.total_size): + name="rbc_{0}".format(row) + # Top/bottom cell are always dummy cells. + # Regular array cells are replica cells (>left_rbl and self.left_rbl and row net - decode_t_net = self.nand2.analytical_delay(corner, slew, self.inv.input_load()) - - # net -> wl - net_t_wl = self.inv.analytical_delay(corner, decode_t_net.slew, load) - - return decode_t_net + net_t_wl - - - def input_load(self): - """Gets the capacitance of the wordline driver in absolute units (fF)""" - return self.nand2.input_load() - def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): """Follows the clk_buf to a wordline signal adding each stages stage effort to a list""" stage_effort_list = [] diff --git a/compiler/modules/write_driver.py b/compiler/modules/write_driver.py index 34c51245..85a58fd5 100644 --- a/compiler/modules/write_driver.py +++ b/compiler/modules/write_driver.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import design import utils @@ -11,7 +18,8 @@ class write_driver(design.design): the technology library. """ - pin_names = ["din", "bl", "br", "en", "gnd", "vdd"] + pin_names = ["din", "bl", "br", "en", "vdd", "gnd"] + type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] (width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"]) @@ -22,9 +30,13 @@ class write_driver(design.design): self.width = write_driver.width self.height = write_driver.height self.pin_map = write_driver.pin_map - + self.add_pin_types(self.type_list) def get_w_en_cin(self): """Get the relative capacitance of a single input""" # This is approximated from SCMOS. It has roughly 5 3x transistor gates. return 5*3 + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges based on inputs/outputs. Overrides base class function.""" + self.add_graph_edges(graph, port_nets) \ No newline at end of file diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 3367cc00..100ee3a2 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from math import log import design from tech import drc @@ -12,7 +19,7 @@ class write_driver_array(design.design): Dynamically generated write driver array of all bitlines. """ - def __init__(self, name, columns, word_size): + def __init__(self, name, columns, word_size,write_size=None): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) @@ -20,8 +27,12 @@ class write_driver_array(design.design): self.columns = columns self.word_size = word_size + self.write_size = write_size self.words_per_row = int(columns / word_size) + if self.write_size: + self.num_wmasks = int(self.word_size/self.write_size) + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -38,22 +49,26 @@ class write_driver_array(design.design): self.width = self.columns * self.bitcell.width else: self.width = self.columns * self.driver.width - self.height = self.driver.height self.place_write_array() self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_pins(self): for i in range(self.word_size): - self.add_pin("data_{0}".format(i)) + self.add_pin("data_{0}".format(i), "INPUT") for i in range(self.word_size): - self.add_pin("bl_{0}".format(i)) - self.add_pin("br_{0}".format(i)) - self.add_pin("en") - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin("bl_{0}".format(i), "OUTPUT") + self.add_pin("br_{0}".format(i), "OUTPUT") + if self.write_size: + for i in range(self.num_wmasks): + self.add_pin("en_{0}".format(i), "INPUT") + else: + self.add_pin("en", "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def add_modules(self): self.driver = factory.create(module_type="write_driver") @@ -65,27 +80,39 @@ class write_driver_array(design.design): def create_write_array(self): self.driver_insts = {} + w = 0 + windex=0 for i in range(0,self.columns,self.words_per_row): name = "write_driver{}".format(i) index = int(i/self.words_per_row) self.driver_insts[index]=self.add_inst(name=name, mod=self.driver) - self.connect_inst(["data_{0}".format(index), - "bl_{0}".format(index), - "br_{0}".format(index), - "en", "vdd", "gnd"]) + if self.write_size: + self.connect_inst(["data_{0}".format(index), + "bl_{0}".format(index), + "br_{0}".format(index), + "en_{0}".format(windex), "vdd", "gnd"]) + w+=1 + # when w equals write size, the next en pin can be connected since we are now at the next wmask bit + if w == self.write_size: + w = 0 + windex+=1 + else: + self.connect_inst(["data_{0}".format(index), + "bl_{0}".format(index), + "br_{0}".format(index), + "en", "vdd", "gnd"]) def place_write_array(self): if self.bitcell.width > self.driver.width: - driver_spacing = self.bitcell.width + self.driver_spacing = self.bitcell.width else: - driver_spacing = self.driver.width - + self.driver_spacing = self.driver.width for i in range(0,self.columns,self.words_per_row): - index = int(i/self.words_per_row) - base = vector(i * driver_spacing,0) + index = int(i/self.words_per_row) + base = vector(i * self.driver_spacing, 0) self.driver_insts[index].place(base) @@ -121,15 +148,28 @@ class write_driver_array(design.design): self.add_layout_pin_rect_center(text=n, layer="metal3", offset=pin_pos) + if self.write_size: + for bit in range(self.num_wmasks): + en_pin = self.driver_insts[bit*self.write_size].get_pin("en") + # Determine width of wmask modified en_pin with/without col mux + wmask_en_len = self.words_per_row*(self.write_size * self.driver_spacing) + if (self.words_per_row == 1): + en_gap = self.driver_spacing - en_pin.width() + else: + en_gap = self.driver_spacing + + self.add_layout_pin(text="en_{0}".format(bit), + layer=en_pin.layer, + offset=en_pin.ll(), + width=wmask_en_len-en_gap, + height=en_pin.height()) + else: + self.add_layout_pin(text="en", + layer="metal1", + offset=self.driver_insts[0].get_pin("en").ll().scale(0,1), + width=self.width) - - self.add_layout_pin(text="en", - layer="metal1", - offset=self.driver_insts[0].get_pin("en").ll().scale(0,1), - width=self.width, - height=drc('minwidth_metal1')) - def get_w_en_cin(self): diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py new file mode 100644 index 00000000..258bbd8d --- /dev/null +++ b/compiler/modules/write_mask_and_array.py @@ -0,0 +1,156 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from math import log +import design +from tech import drc +import debug +from sram_factory import factory +from vector import vector +from globals import OPTS + + +class write_mask_and_array(design.design): + """ + Array of AND gates to turn write mask signal on only when w_en is on. + The write mask AND array goes between the write driver array and the sense amp array. + """ + + def __init__(self, name, columns, word_size, write_size, port=0): + design.design.__init__(self, name) + debug.info(1, "Creating {0}".format(self.name)) + self.add_comment("columns: {0}".format(columns)) + self.add_comment("word_size {0}".format(word_size)) + self.add_comment("write_size {0}".format(write_size)) + + self.columns = columns + self.word_size = word_size + self.write_size = write_size + self.port = port + self.words_per_row = int(columns / word_size) + self.num_wmasks = int(word_size / write_size) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_and2_array() + + + def create_layout(self): + self.place_and2_array() + spacing = self.wmask_en_len - self.and2.width + self.width = (self.num_wmasks*self.and2.width) + ((self.num_wmasks-1)*spacing) + self.height = self.and2.height + self.add_layout_pins() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + for bit in range(self.num_wmasks): + self.add_pin("wmask_in_{}".format(bit),"INPUT") + self.add_pin("en", "INPUT") + for bit in range(self.num_wmasks): + self.add_pin("wmask_out_{}".format(bit),"OUTPUT") + self.add_pin("vdd","POWER") + self.add_pin("gnd","GROUND") + + def add_modules(self): + # Size the AND gate for the number of write drivers it drives, which is equal to the write size. + # Assume stage effort of 3 to compute the size + self.and2 = factory.create(module_type="pand2", + size=self.write_size/4.0) + self.add_mod(self.and2) + + + def create_and2_array(self): + self.and2_insts = {} + for bit in range(self.num_wmasks): + name = "and2_{}".format(bit) + self.and2_insts[bit] = self.add_inst(name=name, + mod=self.and2) + self.connect_inst(["wmask_in_{}".format(bit), + "en", + "wmask_out_{}".format(bit), + "vdd", "gnd"]) + + + def place_and2_array(self): + # Place the write mask AND array at the start of each write driver enable length. + # This ensures the write mask AND array will be directly under the corresponding write driver enable wire. + + # This is just used for measurements, so don't add the module + self.bitcell = factory.create(module_type="bitcell") + self.driver = factory.create(module_type="write_driver") + if self.bitcell.width > self.driver.width: + self.driver_spacing = self.bitcell.width + else: + self.driver_spacing = self.driver.width + + self.wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing) + debug.check(self.wmask_en_len >= self.and2.width, + "Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width,self.wmask_en_len)) + + for i in range(self.num_wmasks): + base = vector(i * self.wmask_en_len, 0) + self.and2_insts[i].place(base) + + + def add_layout_pins(self): + self.nand2 = factory.create(module_type="pnand2") + supply_pin=self.nand2.get_pin("vdd") + + # Create the enable pin that connects all write mask AND array's B pins + beg_en_pin = self.and2_insts[0].get_pin("B") + end_en_pin = self.and2_insts[self.num_wmasks-1].get_pin("B") + if self.port % 2: + # Extend metal3 to edge of AND array in multiport + en_to_edge = self.and2.width - beg_en_pin.cx() + self.add_layout_pin(text="en", + layer="metal3", + offset=beg_en_pin.bc(), + width=end_en_pin.cx() - beg_en_pin.cx() + en_to_edge) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy())) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy())) + else: + self.add_layout_pin(text="en", + layer="metal3", + offset=beg_en_pin.bc(), + width=end_en_pin.cx() - beg_en_pin.cx()) + + for i in range(self.num_wmasks): + # Copy remaining layout pins + self.copy_layout_pin(self.and2_insts[i],"A","wmask_in_{0}".format(i)) + self.copy_layout_pin(self.and2_insts[i],"Z","wmask_out_{0}".format(i)) + + # Add via connections to metal3 for AND array's B pin + en_pin = self.and2_insts[i].get_pin("B") + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=en_pin.center()) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=en_pin.center()) + + self.add_power_pin("gnd", vector(supply_pin.width() + i * self.wmask_en_len, 0)) + self.add_power_pin("vdd", vector(supply_pin.width() + i * self.wmask_en_len, self.height)) + # Route power and ground rails together + if i < self.num_wmasks-1: + for n in ["gnd","vdd"]: + pin = self.and2_insts[i].get_pin(n) + next_pin = self.and2_insts[i+1].get_pin(n) + self.add_path("metal1",[pin.center(),next_pin.center()]) + + def get_cin(self): + """Get the relative capacitance of all the input connections in the bank""" + # The enable is connected to an and2 for every row. + return self.and2.get_cin() * len(self.and2_insts) + + diff --git a/compiler/openram.py b/compiler/openram.py index 0fe3f7cd..97ada256 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -1,4 +1,11 @@ #!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ SRAM Compiler @@ -43,13 +50,16 @@ from sram_config import sram_config # Configure the SRAM organization c = sram_config(word_size=OPTS.word_size, - num_words=OPTS.num_words) + num_words=OPTS.num_words, + write_size=OPTS.write_size) debug.print_raw("Words per row: {}".format(c.words_per_row)) #from parser import * output_extensions = ["sp","v","lib","py","html","log"] +# Only output lef/gds if back-end if not OPTS.netlist_only: - output_extensions.extend(["gds","lef"]) + output_extensions.extend(["lef","gds"]) + output_files = ["{0}{1}.{2}".format(OPTS.output_path,OPTS.output_name,x) for x in output_extensions] debug.print_raw("Output files are: ") for path in output_files: diff --git a/compiler/options.py b/compiler/options.py index 4d3461e6..ce974d63 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -1,6 +1,14 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import optparse import getpass import os +#import sram_config class options(optparse.Values): """ @@ -8,22 +16,82 @@ class options(optparse.Values): that is the sole required command-line positional argument for openram.py. """ + ################### + # Configuration options + ################### # This is the technology directory. openram_tech = "" + # This is the name of the technology. tech_name = "" + + # Port configuration (1-2 ports allowed) + num_rw_ports = 1 + num_r_ports = 0 + num_w_ports = 0 + + # Write mask size, default will be overwritten with word_size if not user specified + write_size = None + + # These will get initialized by the user or the tech file + supply_voltages = "" + temperatures = "" + process_corners = "" + + # Size parameters must be specified by user in config file. + #num_words = 0 + #word_size = 0 + # You can manually specify banks, but it is better to auto-detect it. + num_banks = 1 + + ################### + # Optimization options + ################### + rbl_delay_percentage = 0.5 #Approximate percentage of delay compared to bitlines + + # Allow manual adjustment of the delay chain over automatic + use_tech_delay_chain_size = False + delay_chain_stages = 9 + delay_chain_fanout_per_stage = 4 + + + + ################### + # Debug options. + ################### # This is the temp directory where all intermediate results are stored. - openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid()) - #openram_temp = "{0}/openram_temp/".format(os.getenv("HOME")) + try: + # If user defined the temporary location in their environment, use it + openram_temp = os.path.abspath(os.environ.get("OPENRAM_TMP")) + except: + # Else use a unique temporary directory + openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid()) # This is the verbosity level to control debug information. 0 is none, 1 # is minimal, etc. debug_level = 0 + + ################### + # Run-time vs accuracy options. + # Default, sacrifice accuracy/completeness for speed. + # Must turn on options for verification, final routing, etc. + ################### # When enabled, layout is not generated (and no DRC or LVS are performed) netlist_only = False + # Whether we should do the final power routing + route_supplies = False # This determines whether LVS and DRC is checked at all. - check_lvsdrc = True + check_lvsdrc = False # This determines whether LVS and DRC is checked for every submodule. inline_lvsdrc = False + # Remove noncritical memory cells for characterization speed-up + trim_netlist = False + # Run with extracted parasitics + use_pex = False + + + ################### + # Tool options + ################### # Variable to select the variant of spice spice_name = "" # The spice executable being used which is derived from the user PATH. @@ -36,12 +104,9 @@ class options(optparse.Values): drc_exe = None lvs_exe = None pex_exe = None + # Should we print out the banner at startup print_banner = True - # Run with extracted parasitics - use_pex = False - # Remove noncritical memory cells for characterization speed-up - trim_netlist = True # Use detailed LEF blockages detailed_blockages = True # Define the output file paths @@ -53,28 +118,10 @@ class options(optparse.Values): # Purge the temp directory after a successful run (doesn't purge on errors, anyhow) purge_temp = True - # These are the configuration parameters - num_rw_ports = 1 - num_r_ports = 0 - num_w_ports = 0 - - # These will get initialized by the the file - supply_voltages = "" - temperatures = "" - process_corners = "" - # These are the main configuration parameters that should be over-ridden - # in a configuration file. - #num_words = 0 - #word_size = 0 - - # You can manually specify banks, but it is better to auto-detect it. - num_banks = 1 - - #Uses the delay chain size in the tech.py file rather automatic sizing. - use_tech_delay_chain_size = False - + ################### # These are the default modules that can be over-riden + ################### bank_select = "bank_select" bitcell_array = "bitcell_array" bitcell = "bitcell" @@ -84,6 +131,7 @@ class options(optparse.Values): delay_chain = "delay_chain" dff_array = "dff_array" dff = "dff" + dummy_bitcell = "dummy_bitcell" precharge_array = "precharge_array" ptx = "ptx" replica_bitcell = "replica_bitcell" @@ -95,4 +143,5 @@ class options(optparse.Values): wordline_driver = "wordline_driver" write_driver_array = "write_driver_array" write_driver = "write_driver" + write_mask_and_array = "write_mask_and_array" diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 54787282..d71b1e92 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from tech import drc from math import log @@ -11,16 +18,13 @@ class pand2(pgate.pgate): This is a simple buffer used for driving loads. """ def __init__(self, name, size=1, height=None): - self.size = size - - pgate.pgate.__init__(self, name, height) - debug.info(1, "Creating {}".format(self.name)) + debug.info(1, "Creating pnand2 {}".format(name)) self.add_comment("size: {}".format(size)) - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - + self.size = size + + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) def create_netlist(self): self.add_pins() @@ -31,8 +35,8 @@ class pand2(pgate.pgate): # Shield the cap, but have at least a stage effort of 4 self.nand = factory.create(module_type="pnand2",height=self.height) self.add_mod(self.nand) - - self.inv = factory.create(module_type="pinv", size=self.size, height=self.height) + + self.inv = factory.create(module_type="pdriver", neg_polarity=True, fanout=3*self.size, height=self.height) self.add_mod(self.inv) def create_layout(self): @@ -40,13 +44,14 @@ class pand2(pgate.pgate): self.place_insts() self.add_wires() self.add_layout_pins() + self.DRC_LVS() def add_pins(self): - self.add_pin("A") - self.add_pin("B") - self.add_pin("Z") - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def create_insts(self): self.nand_inst=self.add_inst(name="pand2_nand", @@ -105,14 +110,6 @@ class pand2(pgate.pgate): width=pin.width(), height=pin.height()) - - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of DFF-> INV -> INV """ - nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load()) - inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load) - return nand_delay + inv_delay - def get_stage_efforts(self, external_cout, inp_is_rise=False): """Get the stage efforts of the A or B -> Z path""" stage_effort_list = [] @@ -125,3 +122,8 @@ class pand2(pgate.pgate): stage_effort_list.append(stage2) return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py new file mode 100644 index 00000000..22864e5a --- /dev/null +++ b/compiler/pgates/pand3.py @@ -0,0 +1,138 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from tech import drc +from math import log +from vector import vector +from globals import OPTS +import pgate +from sram_factory import factory + +class pand3(pgate.pgate): + """ + This is a simple buffer used for driving loads. + """ + def __init__(self, name, size=1, height=None): + debug.info(1, "Creating pand3 {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + # Shield the cap, but have at least a stage effort of 4 + self.nand = factory.create(module_type="pnand3",height=self.height) + self.add_mod(self.nand) + + self.inv = factory.create(module_type="pinv", size=self.size, height=self.height) + self.add_mod(self.inv) + + def create_layout(self): + self.width = self.nand.width + self.inv.width + self.place_insts() + self.add_wires() + self.add_layout_pins() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("C", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst=self.add_inst(name="pand3_nand", + mod=self.nand) + self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"]) + + self.inv_inst=self.add_inst(name="pand3_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0,0)) + + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(),0)) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) + mid2_point = vector(mid1_point, a2_pin.cy()) + self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) + + + def add_layout_pins(self): + # Continous vdd rail along with label. + vdd_pin=self.inv_inst.get_pin("vdd") + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + # Continous gnd rail along with label. + gnd_pin=self.inv_inst.get_pin("gnd") + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_pin.ll().scale(0,1), + width=self.width, + height=vdd_pin.height()) + + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A","B", "C"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + + + def analytical_delay(self, corner, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load()) + inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load) + return nand_delay + inv_delay + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index 9d73c004..fcfc6586 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from tech import drc from math import log @@ -12,17 +19,15 @@ class pbuf(pgate.pgate): """ def __init__(self, name, size=4, height=None): + debug.info(1, "creating {0} with size of {1}".format(name,size)) + self.add_comment("size: {}".format(size)) + self.stage_effort = 4 self.size = size self.height = height + # Creates the netlist and layout pgate.pgate.__init__(self, name, height) - debug.info(1, "creating {0} with size of {1}".format(self.name,self.size)) - self.add_comment("size: {}".format(size)) - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() def create_netlist(self): @@ -37,10 +42,10 @@ class pbuf(pgate.pgate): self.add_layout_pins() def add_pins(self): - self.add_pin("A") - self.add_pin("Z") - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin("A", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def create_modules(self): # Shield the cap, but have at least a stage effort of 4 @@ -108,14 +113,6 @@ class pbuf(pgate.pgate): width=a_pin.width(), height=a_pin.height()) - - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of DFF-> INV -> INV """ - inv1_delay = self.inv1.analytical_delay(corner, slew=slew, load=self.inv2.input_load()) - inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load) - return inv1_delay + inv2_delay - def get_stage_efforts(self, external_cout, inp_is_rise=False): """Get the stage efforts of the A -> Z path""" stage_effort_list = [] diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index daf4d334..ec55f0c7 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import pgate import math @@ -13,27 +20,24 @@ class pdriver(pgate.pgate): """ def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None): + debug.info(1, "creating pdriver {}".format(name)) + self.stage_effort = 3 self.height = height self.neg_polarity = neg_polarity self.size_list = size_list self.fanout = fanout + if size_list == None and self.fanout == 0: + debug.error("Either fanout or size list must be specified.", -1) if self.size_list and self.fanout != 0: debug.error("Cannot specify both size_list and fanout.", -1) if self.size_list and self.neg_polarity: debug.error("Cannot specify both size_list and neg_polarity.", -1) + # Creates the netlist and layout pgate.pgate.__init__(self, name, height) - debug.info(1, "Creating {}".format(self.name)) - self.compute_sizes() - - self.add_comment("sizes: {}".format(str(self.size_list))) - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() def compute_sizes(self): # size_list specified @@ -41,7 +45,7 @@ class pdriver(pgate.pgate): self.num_stages = len(self.size_list) else: # Find the optimal number of stages for the given effort - self.num_stages = max(1,int(round(log(self.fanout)/log(self.stage_effort)))) + self.num_stages = max(1,int(round(self.fanout**(1/self.stage_effort)))) # Increase the number of stages if we need to fix polarity if self.neg_polarity and (self.num_stages%2==0): @@ -49,18 +53,20 @@ class pdriver(pgate.pgate): elif not self.neg_polarity and (self.num_stages%2): self.num_stages += 1 - self.size_list = [] - # compute sizes backwards from the fanout - fanout_prev = self.fanout - for x in range(self.num_stages): - fanout_prev = max(round(fanout_prev/self.stage_effort),1) - self.size_list.append(fanout_prev) + self.size_list = [] + # compute sizes backwards from the fanout + fanout_prev = self.fanout + for x in range(self.num_stages): + fanout_prev = max(round(fanout_prev/self.stage_effort),1) + self.size_list.append(fanout_prev) - # reverse the sizes to be from input to output - self.size_list.reverse() + # reverse the sizes to be from input to output + self.size_list.reverse() def create_netlist(self): + self.compute_sizes() + self.add_comment("sizes: {}".format(str(self.size_list))) self.add_pins() self.add_modules() self.create_insts() @@ -73,13 +79,12 @@ class pdriver(pgate.pgate): self.width = self.inv_inst_list[-1].rx() self.height = self.inv_inst_list[0].height - self.DRC_LVS() def add_pins(self): - self.add_pin("A") - self.add_pin("Z") - self.add_pin("vdd") - self.add_pin("gnd") + self.add_pin("A", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def add_modules(self): self.inv_list = [] @@ -168,34 +173,13 @@ class pdriver(pgate.pgate): offset=a_pin.center(), width = a_pin.width(), height = a_pin.height()) - - def input_load(self): - return self.inv_list[0].input_load() - - def analytical_delay(self, corner, slew, load=0.0): - """Calculate the analytical delay of INV1 -> ... -> INVn""" - - cout_list = [] - for prev_inv,inv in zip(self.inv_list, self.inv_list[1:]): - cout_list.append(inv.input_load()) - cout_list.append(load) - - input_slew = slew - - delays = [] - for inv,cout in zip(self.inv_list,cout_list): - delays.append(inv.analytical_delay(corner, slew=input_slew, load=cout)) - input_slew = delays[-1].slew - - delay = delays[0] - for i in range(len(delays)-1): - delay += delays[i] - - return delay - + def get_sizes(self): + """ Return the relative sizes of the buffers """ + return self.size_list + def get_stage_efforts(self, external_cout, inp_is_rise=False): - """Get the stage efforts of the A -> Z path""" + """ Get the stage efforts of the A -> Z path """ cout_list = [] for prev_inv,inv in zip(self.inv_list, self.inv_list[1:]): cout_list.append(inv.get_cin()) @@ -212,5 +196,5 @@ class pdriver(pgate.pgate): return stage_effort_list def get_cin(self): - """Returns the relative capacitance of the input""" + """ Returns the relative capacitance of the input """ return self.inv_list[0].get_cin() diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 2f641da9..f974f0c4 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import contact import design import debug @@ -21,6 +28,20 @@ class pgate(design.design): b = factory.create(module_type="bitcell") self.height = b.height + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + self.add_boundary() + self.DRC_LVS() + + + def create_netlist(self): + """ Pure virtual function """ + debug.error("Must over-ride create_netlist.",-1) + + def create_layout(self): + """ Pure virtual function """ + debug.error("Must over-ride create_layout.",-1) def connect_pin_to_rail(self,inst,pin,supply): """ Connects a ptx pin to a supply rail. """ @@ -42,7 +63,7 @@ class pgate(design.design): height=height, width=source_pin.width()) - def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=90): + def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=False): """ Route the input gate to the left side of the cell for access. Position specifies to place the contact the left, center, or right of gate. """ @@ -61,17 +82,21 @@ class pgate(design.design): left_gate_offset = vector(nmos_gate_pin.lx(),ypos) # Center is completely symmetric. - if rotate==90: + if rotate: contact_width = contact.poly.height contact_m1_width = contact.poly.second_layer_height contact_m1_height = contact.poly.second_layer_width + directions = ("H","V") else: contact_width = contact.poly.width contact_m1_width = contact.poly.second_layer_width contact_m1_height = contact.poly.second_layer_height + directions = ("V","H") if position=="center": contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0) + elif position=="farleft": + contact_offset = left_gate_offset - vector(0.5*contact.poly.width, 0) elif position=="left": contact_offset = left_gate_offset - vector(0.5*contact_width - 0.5*self.poly_width, 0) elif position=="right": @@ -79,9 +104,12 @@ class pgate(design.design): else: debug.error("Invalid contact placement option.", -1) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=contact_offset, - rotate=rotate) + # Non-preferred direction via + + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=contact_offset, + directions=directions) + # self.add_layout_pin_segment_center(text=name, # layer="metal1", # start=left_gate_offset.scale(0,1), @@ -145,10 +173,11 @@ class pgate(design.design): # Offset by half a contact in x and y contact_offset += vector(0.5*pmos.active_contact.first_layer_width, 0.5*pmos.active_contact.first_layer_height) - self.nwell_contact=self.add_contact_center(layers=layer_stack, - offset=contact_offset, - implant_type="n", - well_type="n") + self.nwell_contact=self.add_via_center(layers=layer_stack, + offset=contact_offset, + directions=("H","V"), + implant_type="n", + well_type="n") self.add_rect_center(layer="metal1", offset=contact_offset + vector(0,0.5*(self.height-contact_offset.y)), width=self.nwell_contact.mod.second_layer_width, @@ -191,10 +220,11 @@ class pgate(design.design): # Offset by half a contact contact_offset += vector(0.5*nmos.active_contact.first_layer_width, 0.5*nmos.active_contact.first_layer_height) - self.pwell_contact=self.add_contact_center(layers=layer_stack, - offset=contact_offset, - implant_type="p", - well_type="p") + self.pwell_contact=self.add_via_center(layers=layer_stack, + offset=contact_offset, + directions=("H","V"), + implant_type="p", + well_type="p") self.add_rect_center(layer="metal1", offset=contact_offset.scale(1,0.5), width=self.pwell_contact.mod.second_layer_width, @@ -216,4 +246,3 @@ class pgate(design.design): # offset=implant_offset, # width=implant_width, # height=implant_height) - diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index f00e9af7..275ccbe6 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import contact import pgate import debug @@ -20,28 +27,17 @@ class pinv(pgate.pgate): """ def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True): - # 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 - # have poly connected, for example. - pgate.pgate.__init__(self, name, height) - debug.info(2, "create pinv structure {0} with size of {1}".format(name, size)) + + debug.info(2, "creating pinv structure {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) - + self.size = size self.nmos_size = size self.pmos_size = beta*size self.beta = beta self.route_output = False - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - # for run-time, we won't check every transitor DRC/LVS independently - # but this may be uncommented for debug purposes - #self.DRC_LVS() + pgate.pgate.__init__(self, name, height) def create_netlist(self): """ Calls all functions related to the generation of the netlist """ @@ -58,12 +54,14 @@ class pinv(pgate.pgate): self.add_well_contacts() self.extend_wells(self.well_pos) self.connect_rails() - self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", rotate=0) + self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", position="farleft") self.route_outputs() def add_pins(self): """ Adds pins for spice netlist """ - self.add_pin_list(["A", "Z", "vdd", "gnd"]) + pin_list = ["A", "Z", "vdd", "gnd"] + dir_list = ["INPUT", "OUTPUT", "POWER", "GROUND"] + self.add_pin_list(pin_list, dir_list) def determine_tx_mults(self): @@ -222,8 +220,8 @@ class pinv(pgate.pgate): pmos_drain_pin = self.pmos_inst.get_pin("D") # Pick point at right most of NMOS and connect down to PMOS - nmos_drain_pos = nmos_drain_pin.lr() - vector(0.5*self.m1_width,0) - pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.bc().y) + nmos_drain_pos = nmos_drain_pin.bc() + pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y) self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos]) # Remember the mid for the output @@ -257,19 +255,10 @@ class pinv(pgate.pgate): self.connect_pin_to_rail(self.pmos_inst,"S","vdd") - - def input_load(self): - return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - - def analytical_delay(self, corner, slew, load=0.0): - r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) - def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" c_eff = self.calculate_effective_capacitance(load) - freq = spice["default_event_rate"] + freq = spice["default_event_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["inv_leakage"] @@ -280,16 +269,26 @@ class pinv(pgate.pgate): """Computes effective capacitance. Results in fF""" c_load = load c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - transition_prob = spice["inv_transition_prob"] + transition_prob = 0.5 return transition_prob*(c_load + c_para) - def get_cin(self): - """Return the capacitance of the gate connection in generic capacitive units relative to the minimum width of a transistor""" + def input_load(self): + """Return the capacitance of the gate connection in generic capacitive + units relative to the minimum width of a transistor""" return self.nmos_size + self.pmos_size - + def get_stage_effort(self, cout, inp_is_rise=True): """Returns an object representing the parameters for delay in tau units. Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ parasitic_delay = 1 - return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges based on inputs/outputs. Overrides base class function.""" + self.add_graph_edges(graph, port_nets) diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 06936895..a8d2c8b8 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -1,18 +1,28 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug -import design +import pgate from tech import drc from math import log from vector import vector from globals import OPTS from sram_factory import factory -class pinvbuf(design.design): +class pinvbuf(pgate.pgate): """ This is a simple inverter/buffer used for driving loads. It is used in the column decoder for 1:2 decoding and as the clock buffer. """ def __init__(self, name, size=4, height=None): + debug.info(1, "creating pinvbuf {}".format(name)) + self.add_comment("size: {}".format(size)) + self.stage_effort = 4 self.row_height = height # FIXME: Change the number of stages to support high drives. @@ -23,13 +33,8 @@ class pinvbuf(design.design): self.size = size self.predriver_size = max(int(self.size/(self.stage_effort/2)),1) - design.design.__init__(self, name) - debug.info(1, "Creating {}".format(self.name)) - self.add_comment("size: {}".format(size)) - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + # Creates the netlist and layout + pgate.pgate.__init__(self, name) def create_netlist(self): @@ -48,7 +53,6 @@ class pinvbuf(design.design): self.offset_all_coordinates() - self.DRC_LVS() def add_pins(self): self.add_pin("A") @@ -174,15 +178,7 @@ class pinvbuf(design.design): offset=a_pin.center()) self.add_via_center(layers=("metal1","via1","metal2"), offset=a_pin.center()) - - - - def analytical_delay(self, corner, slew, load=0.0): - """ Calculate the analytical delay of DFF-> INV -> INV """ - inv1_delay = self.inv1.analytical_delay(corner, slew=slew, load=self.inv2.input_load()) - inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load) - return inv1_delay + inv2_delay - + def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False): """Get the stage efforts of the clk -> clk_buf path""" stage_effort_list = [] diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 95a9bc28..791097e5 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import contact import pgate import debug @@ -14,8 +21,8 @@ class pnand2(pgate.pgate): """ def __init__(self, name, size=1, height=None): """ Creates a cell for a simple 2 input nand """ - pgate.pgate.__init__(self, name, height) - debug.info(2, "create pnand2 structure {0} with size of {1}".format(name, size)) + + debug.info(2, "creating pnand2 structure {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) self.size = size @@ -28,11 +35,9 @@ class pnand2(pgate.pgate): debug.check(size==1,"Size 1 pnand2 is only supported now.") self.tx_mults = 1 - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) + def create_netlist(self): self.add_pins() self.add_ptx() @@ -52,7 +57,9 @@ class pnand2(pgate.pgate): def add_pins(self): """ Adds pins for spice netlist """ - self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) + pin_list = ["A", "B", "Z", "vdd", "gnd"] + dir_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + self.add_pin_list(pin_list, dir_list) def add_ptx(self): @@ -203,13 +210,15 @@ class pnand2(pgate.pgate): mid1_offset = vector(out_offset.x, top_pin_offset.y) mid2_offset = vector(out_offset.x, bottom_pin_offset.y) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=pmos_pin.center()) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=nmos_pin.center()) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=out_offset, - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pmos_pin.center(), + directions=("V","H")) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=nmos_pin.center(), + directions=("V","H")) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=out_offset) + # PMOS1 to mid-drain to NMOS2 drain self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset]) @@ -221,21 +230,10 @@ class pnand2(pgate.pgate): width=contact.m1m2.first_layer_height, height=contact.m1m2.first_layer_width) - - - - def input_load(self): - return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - - def analytical_delay(self, corner, slew, load=0.0): - r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) - def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" c_eff = self.calculate_effective_capacitance(load) - freq = spice["default_event_rate"] + freq = spice["default_event_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["nand2_leakage"] @@ -246,16 +244,20 @@ class pnand2(pgate.pgate): """Computes effective capacitance. Results in fF""" c_load = load c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - transition_prob = spice["nand2_transition_prob"] + transition_prob = 0.1875 return transition_prob*(c_load + c_para) - def get_cin(self): + def input_load(self): """Return the relative input capacitance of a single input""" return self.nmos_size+self.pmos_size - + def get_stage_effort(self, cout, inp_is_rise=True): """Returns an object representing the parameters for delay in tau units. Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ parasitic_delay = 2 - return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) + return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges based on inputs/outputs. Overrides base class function.""" + self.add_graph_edges(graph, port_nets) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index d0c37b55..508db024 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import contact import pgate import debug @@ -14,8 +21,8 @@ class pnand3(pgate.pgate): """ def __init__(self, name, size=1, height=None): """ Creates a cell for a simple 3 input nand """ - pgate.pgate.__init__(self, name, height) - debug.info(2, "create pnand3 structure {0} with size of {1}".format(name, size)) + + debug.info(2, "creating pnand3 structure {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) # We have trouble pitch matching a 3x sizes to the bitcell... @@ -30,14 +37,15 @@ class pnand3(pgate.pgate): debug.check(size==1,"Size 1 pnand3 is only supported now.") self.tx_mults = 1 - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) def add_pins(self): """ Adds pins for spice netlist """ - self.add_pin_list(["A", "B", "C", "Z", "vdd", "gnd"]) + pin_list = ["A", "B", "C", "Z", "vdd", "gnd"] + dir_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + self.add_pin_list(pin_list, dir_list) def create_netlist(self): self.add_pins() @@ -213,12 +221,12 @@ class pnand3(pgate.pgate): nmos3_pin = self.nmos3_inst.get_pin("D") # Go up to metal2 for ease on all output pins - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=pmos1_pin.center()) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=pmos3_pin.center()) - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=nmos3_pin.center()) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pmos1_pin.center()) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pmos3_pin.center()) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=nmos3_pin.center()) # PMOS3 and NMOS3 are drain aligned self.add_path("metal2",[pmos3_pin.bc(), nmos3_pin.uc()]) @@ -227,28 +235,18 @@ class pnand3(pgate.pgate): self.add_path("metal2",[pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) # This extends the output to the edge of the cell - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=mid_offset) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=mid_offset) self.add_layout_pin_rect_center(text="Z", layer="metal1", offset=mid_offset, width=contact.m1m2.first_layer_width, height=contact.m1m2.first_layer_height) - - - def input_load(self): - return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - - def analytical_delay(self, corner, slew, load=0.0): - r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) - def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" c_eff = self.calculate_effective_capacitance(load) - freq = spice["default_event_rate"] + freq = spice["default_event_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["nand3_leakage"] @@ -259,10 +257,10 @@ class pnand3(pgate.pgate): """Computes effective capacitance. Results in fF""" c_load = load c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - transition_prob = spice["nand3_transition_prob"] + transition_prob = 0.1094 return transition_prob*(c_load + c_para) - def get_cin(self): + def input_load(self): """Return the relative input capacitance of a single input""" return self.nmos_size+self.pmos_size @@ -271,4 +269,8 @@ class pnand3(pgate.pgate): Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ parasitic_delay = 3 - return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) + return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges based on inputs/outputs. Overrides base class function.""" + self.add_graph_edges(graph, port_nets) diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 8a5c80d4..ed7388e9 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -1,9 +1,17 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import contact import pgate import debug from tech import drc, parameter, spice from vector import vector from globals import OPTS -import contact +import logical_effort from sram_factory import factory class pnor2(pgate.pgate): @@ -13,8 +21,8 @@ class pnor2(pgate.pgate): """ def __init__(self, name, size=1, height=None): """ Creates a cell for a simple 2 input nor """ - pgate.pgate.__init__(self, name, height) - debug.info(2, "create pnor2 structure {0} with size of {1}".format(name, size)) + + debug.info(2, "creating pnor2 structure {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) self.nmos_size = size @@ -27,32 +35,34 @@ class pnor2(pgate.pgate): debug.check(size==1,"Size 1 pnor2 is only supported now.") self.tx_mults = 1 - self.create_netlist() - self.create_layout() - #self.DRC_LVS() + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) - def add_pins(self): - """ Adds pins for spice netlist """ - self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) - def create_netlist(self): self.add_pins() + self.add_ptx() + self.create_ptx() def create_layout(self): """ Calls all functions related to the generation of the layout """ - - self.create_ptx() + self.setup_layout_constants() - self.add_supply_rails() - self.add_ptx() + self.route_supply_rails() + self.place_ptx() self.connect_rails() self.add_well_contacts() self.extend_wells(self.well_pos) self.route_inputs() self.route_output() - def create_ptx(self): + def add_pins(self): + """ Adds pins for spice netlist """ + pin_list = ["A", "B", "Z", "vdd", "gnd"] + dir_list = ["INPUT", "INPUT", "OUTPUT", "INOUT", "INOUT"] + self.add_pin_list(pin_list, dir_list) + + def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos = factory.create(module_type="ptx", width=self.nmos_width, @@ -97,7 +107,7 @@ class pnor2(pgate.pgate): self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, drc("poly_extend_active"), self.poly_space) - def add_supply_rails(self): + def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", layer="metal1", @@ -109,7 +119,31 @@ class pnor2(pgate.pgate): offset=vector(0.5*self.width,self.height), width=self.width) - def add_ptx(self): + def create_ptx(self): + """ + Add PMOS and NMOS to the layout at the upper-most and lowest position + to provide maximum routing in channel + """ + + self.pmos1_inst=self.add_inst(name="pnor2_pmos1", + mod=self.pmos) + self.connect_inst(["vdd", "A", "net1", "vdd"]) + + self.pmos2_inst = self.add_inst(name="pnor2_pmos2", + mod=self.pmos) + self.connect_inst(["net1", "B", "Z", "vdd"]) + + + self.nmos1_inst=self.add_inst(name="pnor2_nmos1", + mod=self.nmos) + self.connect_inst(["Z", "A", "gnd", "gnd"]) + + self.nmos2_inst=self.add_inst(name="pnor2_nmos2", + mod=self.nmos) + self.connect_inst(["Z", "B", "gnd", "gnd"]) + + + def place_ptx(self): """ Add PMOS and NMOS to the layout at the upper-most and lowest position to provide maximum routing in channel @@ -117,29 +151,16 @@ class pnor2(pgate.pgate): pmos1_pos = vector(self.pmos.active_offset.x, self.height - self.pmos.active_height - self.top_bottom_space) - self.pmos1_inst=self.add_inst(name="pnor2_pmos1", - mod=self.pmos, - offset=pmos1_pos) - self.connect_inst(["vdd", "A", "net1", "vdd"]) + self.pmos1_inst.place(pmos1_pos) self.pmos2_pos = pmos1_pos + self.overlap_offset - self.pmos2_inst = self.add_inst(name="pnor2_pmos2", - mod=self.pmos, - offset=self.pmos2_pos) - self.connect_inst(["net1", "B", "Z", "vdd"]) - + self.pmos2_inst.place(self.pmos2_pos) nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space) - self.nmos1_inst=self.add_inst(name="pnor2_nmos1", - mod=self.nmos, - offset=nmos1_pos) - self.connect_inst(["Z", "A", "gnd", "gnd"]) - + self.nmos1_inst.place(nmos1_pos) + self.nmos2_pos = nmos1_pos + self.overlap_offset - self.nmos2_inst=self.add_inst(name="pnor2_nmos2", - mod=self.nmos, - offset=self.nmos2_pos) - self.connect_inst(["Z", "B", "gnd", "gnd"]) + self.nmos2_inst.place(self.nmos2_pos) # Output position will be in between the PMOS and NMOS self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height)) @@ -184,11 +205,11 @@ class pnor2(pgate.pgate): nmos2_pin = self.nmos2_inst.get_pin("D") # Go up to metal2 for ease on all output pins - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=pmos_pin.center()) - m1m2_contact=self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=nmos_pin.center(), - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pmos_pin.center()) + m1m2_contact=self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=nmos_pin.center()) + mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y) mid2_offset = vector(pmos_pin.center().x,self.inputA_yoffset) @@ -198,29 +219,18 @@ class pnor2(pgate.pgate): self.add_path("metal2",[pmos_pin.bc(), mid2_offset, mid3_offset]) self.add_path("metal2",[nmos_pin.rc(), mid1_offset, mid2_offset]) # This extends the output to the edge of the cell - self.add_contact_center(layers=("metal1", "via1", "metal2"), - offset=mid3_offset, - rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=mid3_offset) self.add_layout_pin_rect_center(text="Z", layer="metal1", offset=mid3_offset, width=contact.m1m2.first_layer_height, height=contact.m1m2.first_layer_width) - - - def input_load(self): - return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] - - def analytical_delay(self, corner, slew, load=0.0): - r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"]) - c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew) - def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" c_eff = self.calculate_effective_capacitance(load) - freq = spice["default_event_rate"] + freq = spice["default_event_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["nor2_leakage"] @@ -231,6 +241,9 @@ class pnor2(pgate.pgate): """Computes effective capacitance. Results in fF""" c_load = load c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff - transition_prob = spice["nor2_transition_prob"] + transition_prob = 0.1875 return transition_prob*(c_load + c_para) + def build_graph(self, graph, inst_name, port_nets): + """Adds edges based on inputs/outputs. Overrides base class function.""" + self.add_graph_edges(graph, port_nets) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index cea9c845..b4423bed 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -1,31 +1,42 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import contact -import pgate +import design import debug from tech import drc, parameter from vector import vector from globals import OPTS from sram_factory import factory -class precharge(pgate.pgate): +class precharge(design.design): """ Creates a single precharge cell This module implements the precharge bitline cell used in the design. """ def __init__(self, name, size=1, bitcell_bl="bl", bitcell_br="br"): - pgate.pgate.__init__(self, name) - debug.info(2, "create single precharge cell: {0}".format(name)) + + debug.info(2, "creating precharge cell {0}".format(name)) + design.design.__init__(self, name) self.bitcell = factory.create(module_type="bitcell") - self.beta = parameter["beta"] self.ptx_width = self.beta*parameter["min_tx_size"] self.width = self.bitcell.width self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br - + + + # Creates the netlist and layout + # Since it has variable height, it is not a pgate. self.create_netlist() - if not OPTS.netlist_only: + if not OPTS.netlist_only: self.create_layout() + self.DRC_LVS() def create_netlist(self): self.add_pins() @@ -40,10 +51,9 @@ class precharge(pgate.pgate): self.route_vdd_rail() self.route_bitlines() self.connect_to_bitlines() - self.DRC_LVS() def add_pins(self): - self.add_pin_list(["bl", "br", "en_bar", "vdd"]) + self.add_pin_list(["bl", "br", "en_bar", "vdd"], ["OUTPUT", "OUTPUT", "INPUT", "POWER"]) def add_ptx(self): """ @@ -75,7 +85,7 @@ class precharge(pgate.pgate): self.add_path("metal1", [pmos_pin.uc(), pmos_vdd_pos]) # Add vdd pin above the transistor - self.add_power_pin("vdd", pmos_pin.center(), rotate=0) + self.add_power_pin("vdd", pmos_pin.center(), vertical=True) def create_ptx(self): @@ -149,9 +159,8 @@ class precharge(pgate.pgate): # adds the en contact to connect the gates to the en rail on metal1 offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space) - self.add_contact_center(layers=("poly", "contact", "metal1"), - offset=offset, - rotate=90) + self.add_via_center(layers=("poly", "contact", "metal1"), + offset=offset) # adds the en rail on metal1 self.add_layout_pin_segment_center(text="en_bar", @@ -168,10 +177,10 @@ class precharge(pgate.pgate): # 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")) - self.add_contact_center(layers=("active", "contact", "metal1"), - offset=well_contact_pos, - implant_type="n", - well_type="n") + self.add_via_center(layers=("active", "contact", "metal1"), + offset=well_contact_pos, + implant_type="n", + well_type="n") # leave an extra pitch for the height self.height = well_contact_pos.y + contact.well.height + self.m1_pitch @@ -225,16 +234,20 @@ class precharge(pgate.pgate): lower_pin = self.lower_pmos_inst.get_pin("S") # BL goes up to M2 at the transistor - self.bl_contact=self.add_contact_center(layers=stack, - offset=upper_pin.center()) - self.add_contact_center(layers=stack, - offset=lower_pin.center()) + self.bl_contact=self.add_via_center(layers=stack, + offset=upper_pin.center(), + directions=("V","V")) + self.add_via_center(layers=stack, + offset=lower_pin.center(), + directions=("V","V")) # BR routes over on M1 first - self.add_contact_center(layers=stack, - offset = vector(self.br_pin.cx(), upper_pin.cy())) - self.add_contact_center(layers=stack, - offset = vector(self.br_pin.cx(), lower_pin.cy())) + self.add_via_center(layers=stack, + offset = vector(self.br_pin.cx(), upper_pin.cy()), + directions=("V","V")) + self.add_via_center(layers=stack, + offset = vector(self.br_pin.cx(), lower_pin.cy()), + directions=("V","V")) def connect_pmos_m1(self, pmos_pin, bit_pin): """ diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py new file mode 100644 index 00000000..0564bf86 --- /dev/null +++ b/compiler/pgates/ptristate_inv.py @@ -0,0 +1,220 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import contact +import pgate +import debug +from tech import drc, parameter, spice +from vector import vector +from math import ceil +from globals import OPTS +from utils import round_to_grid +import logical_effort +from sram_factory import factory + +class ptristate_inv(pgate.pgate): + """ + ptristate generates gds of a parametrically sized tristate inverter. + + There is some flexibility in the size, but we do not allow multiple fingers + to fit in the cell height. + + """ + + def __init__(self, name, size=1, height=None): + + debug.info(2, "creating ptristate inv {0} with size of {1}".format(name, size)) + self.add_comment("size: {}".format(size)) + + # We are 2x since there are two series devices + self.size = 2*size + self.nmos_size = size + self.beta = parameter["beta"] + self.pmos_size = self.beta*size + + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") + + # Creates the netlist and layout + pgate.pgate.__init__(self, name, height) + + def create_netlist(self): + """ Calls all functions related to the generation of the netlist """ + self.add_pins() + self.add_ptx() + self.create_ptx() + + def create_layout(self): + """ Calls all functions related to the generation of the layout """ + self.setup_layout_constants() + self.route_supply_rails() + self.place_ptx() + self.add_well_contacts() + self.extend_wells(self.well_pos) + self.connect_rails() + self.route_inputs() + self.route_outputs() + + def add_pins(self): + """ Adds pins for spice netlist """ + self.add_pin_list(["in", "out", "en", "en_bar", "vdd", "gnd"]) + + + def setup_layout_constants(self): + """ + Pre-compute some handy layout parameters. + """ + + # Compute the other pmos2 location, but determining offset to overlap the + # source and drain pins + self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() + + # Two PMOS devices and a well contact. Separation between each. + # Enclosure space on the sides. + self.well_width = 2*self.pmos.active_width + drc("well_enclosure_active") + + # Add an extra space because we route the output on the right of the S/D + self.width = self.well_width + 0.5*self.m1_space + # Height is an input parameter, so it is not recomputed. + + # Make sure we can put a well above and below + self.top_bottom_space = max(contact.well.width, contact.well.height) + + + def add_ptx(self): + """ Create the PMOS and NMOS transistors. """ + self.nmos = factory.create(module_type="ptx", + width=self.nmos_width, + mults=1, + tx_type="nmos") + self.add_mod(self.nmos) + + self.pmos = factory.create(module_type="ptx", + width=self.pmos_width, + mults=1, + tx_type="pmos") + + self.add_mod(self.pmos) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top and bottom. """ + self.add_layout_pin_rect_center(text="gnd", + layer="metal1", + offset=vector(0.5*self.width,0), + width=self.width) + + self.add_layout_pin_rect_center(text="vdd", + layer="metal1", + offset=vector(0.5*self.width,self.height), + width=self.width) + + + def create_ptx(self): + """ + Create the PMOS and NMOS netlist. + """ + + # These are the inverter PMOS/NMOS + self.pmos1_inst=self.add_inst(name="ptri_pmos1", mod=self.pmos) + self.connect_inst(["vdd", "in", "n1", "vdd"]) + self.nmos1_inst=self.add_inst(name="ptri_nmos1", mod=self.nmos) + self.connect_inst(["gnd", "in", "n2", "gnd"]) + + + # These are the tristate PMOS/NMOS + self.pmos2_inst = self.add_inst(name="ptri_pmos2", mod=self.pmos) + self.connect_inst(["out", "en_bar", "n1", "vdd"]) + self.nmos2_inst=self.add_inst(name="ptri_nmos2", mod=self.nmos) + self.connect_inst(["out", "en", "n2", "gnd"]) + + + + def place_ptx(self): + """ + Place PMOS and NMOS to the layout at the upper-most and lowest position + to provide maximum routing in channel + """ + + pmos_yoff = self.height - self.pmos.active_height - self.top_bottom_space - 0.5*contact.well.height + nmos_yoff = self.top_bottom_space + 0.5*contact.well.height + + # Tristate transistors + pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff) + self.pmos1_inst.place(pmos1_pos) + nmos1_pos = vector(self.pmos.active_offset.x, nmos_yoff) + self.nmos1_inst.place(nmos1_pos) + + # Inverter transistors + self.pmos2_pos = pmos1_pos + self.overlap_offset + self.pmos2_inst.place(self.pmos2_pos) + self.nmos2_pos = nmos1_pos + self.overlap_offset + self.nmos2_inst.place(self.nmos2_pos) + + # Output position will be in between the PMOS and NMOS + self.output_pos = vector(0, 0.5*(pmos_yoff + nmos_yoff + self.nmos.height)) + + # This will help with the wells + self.well_pos = vector(0,self.nmos1_inst.uy()) + + + def route_inputs(self): + """ Route the gates """ + + self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.output_pos.y, "in", position="farleft") + self.route_single_gate(self.pmos2_inst, "en_bar", position="left") + self.route_single_gate(self.nmos2_inst, "en", position="left") + + + def route_outputs(self): + """ Route the output (drains) together. """ + + nmos_drain_pin = self.nmos2_inst.get_pin("D") + pmos_drain_pin = self.pmos2_inst.get_pin("D") + + nmos_drain_pos = nmos_drain_pin.lr() + pmos_drain_pos = pmos_drain_pin.ur() + + self.add_layout_pin(text="out", + layer="metal1", + offset=nmos_drain_pos, + height=pmos_drain_pos.y-nmos_drain_pos.y) + + + def add_well_contacts(self): + """ Add n/p well taps to the layout and connect to supplies AFTER the wells are created """ + + layer_stack = ("active", "contact", "metal1") + + drain_pos = self.nmos1_inst.get_pin("S").center() + vdd_pos = self.get_pin("vdd").center() + self.nwell_contact=self.add_via_center(layers=layer_stack, + offset=vector(drain_pos.x,vdd_pos.y), + implant_type="n", + well_type="n") + + gnd_pos = self.get_pin("gnd").center() + self.pwell_contact=self.add_via_center(layers=layer_stack, + offset=vector(drain_pos.x,gnd_pos.y), + implant_type="p", + well_type="p") + + + + def connect_rails(self): + """ Connect the nmos and pmos to its respective power rails """ + + self.connect_pin_to_rail(self.nmos1_inst,"S","gnd") + self.connect_pin_to_rail(self.pmos1_inst,"S","vdd") + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + #Power in this module currently not defined. Returns 0 nW (leakage and dynamic). + total_power = self.return_power() + return total_power + + def get_cin(self): + return 9*spice["min_tx_gate_c"] diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 3a127454..79a29d07 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import design import debug from tech import drc, spice @@ -19,7 +26,7 @@ class ptx(design.design): # 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 # have poly connected, for example. - name = "{0}_m{1}_w{2}".format(tx_type, mults, width) + name = "{0}_m{1}_w{2:.3f}".format(tx_type, mults, width) if connect_active: name += "_a" if connect_poly: @@ -28,9 +35,8 @@ class ptx(design.design): name += "_c{}".format(num_contacts) # replace periods with underscore for newer spice compatibility name=name.replace('.','_') - + debug.info(3, "creating ptx {0}".format(name)) design.design.__init__(self, name) - debug.info(3, "create ptx2 structure {0}".format(name)) self.tx_type = tx_type self.mults = mults @@ -39,6 +45,8 @@ class ptx(design.design): self.connect_poly = connect_poly self.num_contacts = num_contacts + # Do NOT create the netlist and layout (not a pgate) + # Since it has variable height, it is not a pgate. self.create_netlist() # We must always create ptx layout for pbitcell # some transistor sizes in other netlist depend on pbitcell @@ -60,7 +68,13 @@ class ptx(design.design): #self.DRC() def create_netlist(self): - self.add_pin_list(["D", "G", "S", "B"]) + pin_list = ["D", "G", "S", "B"] + if self.tx_type=="nmos": + body_dir = 'GROUND' + else: #Assumed that the check for either pmos or nmos is done elsewhere. + body_dir = 'POWER' + dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir] + self.add_pin_list(pin_list, dir_list) # self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # " ".join(self.pins))) @@ -326,11 +340,12 @@ class ptx(design.design): [source_positions,drain_positions] = self.get_contact_positions() for pos in source_positions: - contact=self.add_contact_center(layers=("active", "contact", "metal1"), - offset=pos, - size=(1, self.num_contacts), - implant_type=self.implant_type, - well_type=self.well_type) + contact=self.add_via_center(layers=("active", "contact", "metal1"), + offset=pos, + size=(1, self.num_contacts), + directions=("H","V"), + implant_type=self.implant_type, + well_type=self.well_type) self.add_layout_pin_rect_center(text="S", layer="metal1", offset=pos, @@ -339,11 +354,12 @@ class ptx(design.design): for pos in drain_positions: - contact=self.add_contact_center(layers=("active", "contact", "metal1"), - offset=pos, - size=(1, self.num_contacts), - implant_type=self.implant_type, - well_type=self.well_type) + contact=self.add_via_center(layers=("active", "contact", "metal1"), + offset=pos, + size=(1, self.num_contacts), + directions=("H","V"), + implant_type=self.implant_type, + well_type=self.well_type) self.add_layout_pin_rect_center(text="D", layer="metal1", offset=pos, @@ -356,3 +372,8 @@ class ptx(design.design): def get_cin(self): """Returns the relative gate cin of the tx""" return self.tx_width/drc("minwidth_tx") + + def build_graph(self, graph, inst_name, port_nets): + """Adds edges based on inputs/outputs. Overrides base class function.""" + self.add_graph_edges(graph, port_nets) + \ No newline at end of file diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index f03a22e8..999d3ccd 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -1,12 +1,20 @@ -import design +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import pgate import debug from tech import drc from vector import vector import contact from globals import OPTS from sram_factory import factory +import logical_effort -class single_level_column_mux(design.design): +class single_level_column_mux(pgate.pgate): """ This module implements the columnmux bitline cell used in the design. Creates a single columnmux cell with the given integer size relative @@ -14,18 +22,14 @@ class single_level_column_mux(design.design): Column-mux transistors driven by the decoder must be sized for optimal speed """ def __init__(self, name, tx_size=8, bitcell_bl="bl", bitcell_br="br"): + + debug.info(2, "creating single column mux cell: {0}".format(name)) self.tx_size = int(tx_size) - - design.design.__init__(self, name) - debug.info(2, "create single column mux cell: {0}".format(name)) - self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + pgate.pgate.__init__(self, name) def create_netlist(self): self.add_modules() @@ -126,13 +130,18 @@ class single_level_column_mux(design.design): # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D self.add_via_center(layers=("metal1","via1","metal2"), - offset=bl_pin.bc()) + offset=bl_pin.bc(), + directions=("V","V")) self.add_via_center(layers=("metal1","via1","metal2"), - offset=br_out_pin.uc()) + offset=br_out_pin.uc(), + directions=("V","V")) self.add_via_center(layers=("metal1","via1","metal2"), - offset=nmos_upper_s_pin.center()) + offset=nmos_upper_s_pin.center(), + directions=("V","V")) self.add_via_center(layers=("metal1","via1","metal2"), - offset=nmos_lower_d_pin.center()) + offset=nmos_lower_d_pin.center(), + directions=("V","V")) + # bl -> nmos_upper/D on metal1 # bl_out -> nmos_upper/S on metal2 @@ -180,5 +189,9 @@ class single_level_column_mux(design.design): width=self.bitcell.width, height=self.height) - + def get_stage_effort(self, corner, slew, load): + """Returns relative delay that the column mux. Difficult to convert to LE model.""" + parasitic_delay = 1 + cin = 2*self.tx_size #This is not CMOS, so using this may be incorrect. + return logical_effort.logical_effort('column_mux', self.tx_size, cin, load, parasitic_delay, False) diff --git a/compiler/router/direction.py b/compiler/router/direction.py index 8a6681a7..a04fc18c 100644 --- a/compiler/router/direction.py +++ b/compiler/router/direction.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from enum import Enum from vector3d import vector3d diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 245bcb68..af182125 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import numpy as np import string import debug @@ -43,7 +50,7 @@ class grid: self.add_map(vector3d(x,y,1)) def set_blocked(self,n,value=True): - if isinstance(n, (list,tuple,set,frozenset)): + if not isinstance(n, vector3d): for item in n: self.set_blocked(item,value) else: @@ -51,7 +58,7 @@ class grid: self.map[n].blocked=value def is_blocked(self,n): - if isinstance(n, (list,tuple,set,frozenset)): + if not isinstance(n, vector3d): for item in n: if self.is_blocked(item): return True @@ -75,7 +82,7 @@ class grid: self.map[k].blocked=False def set_source(self,n,value=True): - if isinstance(n, (list,tuple,set,frozenset)): + if not isinstance(n, vector3d): for item in n: self.set_source(item,value) else: @@ -84,7 +91,7 @@ class grid: self.source.add(n) def set_target(self,n,value=True): - if isinstance(n, (list,tuple,set,frozenset)): + if not isinstance(n, vector3d): for item in n: self.set_target(item,value) else: @@ -118,7 +125,7 @@ class grid: """ Add a point to the map if it doesn't exist. """ - if isinstance(n, (list,tuple,set,frozenset)): + if not isinstance(n, vector3d): for item in n: self.add_map(item) else: diff --git a/compiler/router/grid_cell.py b/compiler/router/grid_cell.py index cb78116c..2a09477a 100644 --- a/compiler/router/grid_cell.py +++ b/compiler/router/grid_cell.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# class grid_cell: """ A single cell that can be occupied in a given layer, blocked, @@ -29,16 +36,21 @@ class grid_cell: def get_type(self): + type_string = "" + if self.blocked: - return "X" + type_string += "X" if self.source: - return "S" + type_string += "S" if self.target: - return "T" + type_string += "T" if self.path: - return "P" + type_string += "P" + if type_string != "": + return type_string + return None diff --git a/compiler/router/grid_path.py b/compiler/router/grid_path.py index cbe739ef..e4e0995d 100644 --- a/compiler/router/grid_path.py +++ b/compiler/router/grid_path.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from vector3d import vector3d from itertools import tee diff --git a/compiler/router/grid_utils.py b/compiler/router/grid_utils.py index 7ad864aa..a9a4c08d 100644 --- a/compiler/router/grid_utils.py +++ b/compiler/router/grid_utils.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ Some utility functions for sets of grid cells. """ diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 13368d12..99986e76 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from direction import direction from pin_layout import pin_layout from vector3d import vector3d @@ -25,7 +32,7 @@ class pin_group: # This is a list because we can have a pin group of disconnected sets of pins # and these are represented by separate lists - self.pins = [set(irredundant_pin_set)] + self.pins = set(irredundant_pin_set) self.router = router # These are the corresponding pin grids for each pin group. @@ -55,7 +62,7 @@ class pin_group: total_string += grids_string if self.enclosed: - enlosure_string = "\n enclose={}".format(self.enclosures) + enclosure_string = "\n enclose={}".format(self.enclosures) total_string += enclosure_string total_string += ")" @@ -74,25 +81,6 @@ class pin_group: def is_routed(self): return self.routed - def pins_enclosed(self): - """ - Check if all of the pin shapes are enclosed. - Does not check if the DRC is correct, but just touching. - """ - for pin_list in self.pins: - pin_is_enclosed=False - for pin in pin_list: - if pin_is_enclosed: - break - for encosure in self.enclosures: - if pin.overlaps(enclosure): - pin_is_enclosed=True - break - else: - return False - - return True - def remove_redundant_shapes(self, pin_list): """ Remove any pin layout that is contained within another. @@ -135,7 +123,6 @@ class pin_group: return new_pin_list - # FIXME: This relies on some technology parameters from router which is not clean. def compute_enclosures(self): """ Find the minimum rectangle enclosures of the given tracks. @@ -406,8 +393,8 @@ class pin_group: def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST): """ This encloses a single pin component with a rectangle - starting with the seed and expanding right until blocked - and then up until blocked. + starting with the seed and expanding dir1 until blocked + and then dir2 until blocked. dir1 and dir2 should be two orthogonal directions. """ @@ -458,61 +445,86 @@ class pin_group: # Compute the enclosure pin_layout list of the set of tracks self.enclosures = self.compute_enclosures() - for pin_list in self.pins: - for pin in pin_list: + # Find a connector to every pin and add it to the enclosures + for pin in self.pins: - # If it is contained, it won't need a connector - if pin.contained_by_any(self.enclosures): - continue + # If it is contained, it won't need a connector + if pin.contained_by_any(self.enclosures): + continue - # Find a connector in the cardinal directions - # If there is overlap, but it isn't contained, these could all be None - # These could also be none if the pin is diagonal from the enclosure - left_connector = self.find_left_connector(pin, self.enclosures) - right_connector = self.find_right_connector(pin, self.enclosures) - above_connector = self.find_above_connector(pin, self.enclosures) - below_connector = self.find_below_connector(pin, self.enclosures) - connector_list = [left_connector, right_connector, above_connector, below_connector] - filtered_list = list(filter(lambda x: x!=None, connector_list)) - if (len(filtered_list)>0): - import copy - bbox_connector = copy.copy(pin) - bbox_connector.bbox(filtered_list) - self.enclosures.append(bbox_connector) + # Find a connector in the cardinal directions + # If there is overlap, but it isn't contained, these could all be None + # These could also be none if the pin is diagonal from the enclosure + left_connector = self.find_left_connector(pin, self.enclosures) + right_connector = self.find_right_connector(pin, self.enclosures) + above_connector = self.find_above_connector(pin, self.enclosures) + below_connector = self.find_below_connector(pin, self.enclosures) + connector_list = [left_connector, right_connector, above_connector, below_connector] + filtered_list = list(filter(lambda x: x!=None, connector_list)) + if (len(filtered_list)>0): + import copy + bbox_connector = copy.copy(pin) + bbox_connector.bbox(filtered_list) + self.enclosures.append(bbox_connector) # Now, make sure each pin touches an enclosure. If not, add another (diagonal) connector. # This could only happen when there was no enclosure in any cardinal direction from a pin - for pin_list in self.pins: - if not self.overlap_any_shape(pin_list, self.enclosures): - connector = self.find_smallest_connector(pin_list, self.enclosures) - if connector==None: - debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures)) - self.router.write_debug_gds("no_connector.gds") - self.enclosures.append(connector) - + if not self.overlap_any_shape(self.pins, self.enclosures): + connector = self.find_smallest_connector(self.pins, self.enclosures) + if connector==None: + debug.error("Could not find a connector for {} with {}".format(self.pins, self.enclosures)) + self.router.write_debug_gds("no_connector.gds") + self.enclosures.append(connector) + + # At this point, the pins are overlapping, but there might be more than one! + overlap_set = set() + for pin in self.pins: + overlap_set.update(self.transitive_overlap(pin, self.enclosures)) + # Use the new enclosures and recompute the grids that correspond to them + if len(overlap_set) len(old_connected_set): + old_connected_set = connected_set + connected_set = set([shape]) + for old_shape in old_connected_set: + for cur_shape in augmented_shape_list: + if old_shape.overlaps(cur_shape): + connected_set.add(cur_shape) + + + # Remove the original shape + connected_set.remove(shape) + + # if len(connected_set) {0}\n {1}".format(combined.pins,combined.grids)) + # new_pin_groups.append(combined) + + # # Add the pin groups that weren't added to the new set + # for index in all_indices: + # new_pin_groups.append(self.pin_groups[pin_name][index]) + + # old_size = len(self.pin_groups[pin_name]) + # # Use the new pin group! + # self.pin_groups[pin_name] = new_pin_groups + # removed_pairs = old_size - len(new_pin_groups) + # debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name)) - # Now reconstruct the new groups - new_pin_groups = [] - for index1,index2_set in adjacent_pins.items(): - # Remove the indices if they are added to the new set - all_indices.discard(index1) - all_indices.difference_update(index2_set) - - # Create the combined group starting with the first item - combined = self.pin_groups[pin_name][index1] - # Add all of the other items that overlapped - for index2 in index2_set: - pg = self.pin_groups[pin_name][index2] - combined.add_group(pg) - debug.info(3,"Combining {0} {1}:".format(pin_name, index2)) - debug.info(3, " {0}\n {1}".format(combined.pins, pg.pins)) - debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids)) - new_pin_groups.append(combined) - - # Add the pin groups that weren't added to the new set - for index in all_indices: - new_pin_groups.append(self.pin_groups[pin_name][index]) - - old_size = len(self.pin_groups[pin_name]) - # Use the new pin group! - self.pin_groups[pin_name] = new_pin_groups - removed_pairs = old_size - len(new_pin_groups) - debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name)) - - return removed_pairs + # return removed_pairs def separate_adjacent_pins(self, separation): @@ -657,7 +668,10 @@ class router(router_tech): track. """ # to scale coordinates to tracks - x = track[0]*self.track_width - 0.5*self.track_width + try: + x = track[0]*self.track_width - 0.5*self.track_width + except TypeError: + print(track[0],type(track[0]),self.track_width,type(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)) @@ -748,44 +762,10 @@ class router(router_tech): if gid not in group_map: group_map[gid] = pin_group(name=pin_name, pin_set=[], router=self) # We always add it to the first set since they are touching - group_map[gid].pins[0].add(pin) + group_map[gid].pins.add(pin) self.pin_groups[pin_name] = list(group_map.values()) - # This is the old O(n^2) implementation - # def analyze_pins(self, pin_name): - # """ - # Analyze the shapes of a pin and combine them into pin_groups which are connected. - # """ - # debug.info(2,"Analyzing pin groups for {}.".format(pin_name)) - - # pin_set = self.pins[pin_name] - - # # Put each pin in an equivalence class of it's own - # equiv_classes = [set([x]) for x in pin_set] - # def combine_classes(equiv_classes): - # for class1 in equiv_classes: - # for class2 in equiv_classes: - # if class1 == class2: - # continue - # # Compare each pin in each class, - # # and if any overlap, update equiv_classes to include the combined the class - # for p1 in class1: - # for p2 in class2: - # if p1.overlaps(p2): - # combined_class = class1 | class2 - # equiv_classes.remove(class1) - # equiv_classes.remove(class2) - # equiv_classes.append(combined_class) - # return(equiv_classes) - # return(equiv_classes) - - # old_length = math.inf - # while (len(equiv_classes)1: + self.cell.add_route(layers=self.layers, + coordinates=abs_path, + layer_widths=self.layer_widths) + else: + self.cell.add_path(layer=self.layers[0], + coordinates=abs_path, + width=self.layer_widths[0]) def add_single_enclosure(self, track): """ @@ -979,12 +973,19 @@ class router(router_tech): """ This assumes the blockages, source, and target are all set up. """ + + # Double check source and taget are not same node, if so, we are done! + for k,v in self.rg.map.items(): + if v.source and v.target: + debug.error("Grid cell is source and target! {}".format(k)) + return False + # 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)) - + debug.info(1,"Found path: cost={0} ".format(cost)) + debug.info(1,str(path)) + self.paths.append(path) self.add_route(path) @@ -1025,6 +1026,7 @@ class router(router_tech): Write out a GDS file with the routing grid and search information annotated on it. """ debug.info(0,"Writing annotated router gds file to {}".format(gds_name)) + self.del_router_info() self.add_router_info() self.cell.gds_write(gds_name) @@ -1076,6 +1078,15 @@ class router(router_tech): offset=shape[0], zoom=0.05) + def del_router_info(self): + """ + Erase all of the comments on the current level. + """ + debug.info(0,"Erasing router info") + layer_num = techlayer["text"] + self.cell.objs = [x for x in self.cell.objs if x.layerNumber != layer_num] + + def add_router_info(self): """ Write the routing grid and router cost, blockage, pins on diff --git a/compiler/router/router_tech.py b/compiler/router/router_tech.py index 02b6894f..49df06fd 100644 --- a/compiler/router/router_tech.py +++ b/compiler/router/router_tech.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from tech import drc,layer from contact import contact from pin_group import pin_group @@ -17,19 +24,28 @@ class router_tech: """ self.layers = layers self.rail_track_width = rail_track_width - - (self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers - # This is the minimum routed track spacing - via_connect = contact(self.layers, (1, 1)) - max_via_size = max(via_connect.width,via_connect.height) - self.horiz_layer_number = layer[self.horiz_layer_name] - self.vert_layer_number = layer[self.vert_layer_name] - - if self.rail_track_width>1: + if len(self.layers)==1: + self.horiz_layer_name = self.vert_layer_name = self.layers[0] + self.horiz_layer_number = self.vert_layer_number = layer[self.layers[0]] + (self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1) (self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0) + + self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing + self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing + else: + (self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers + via_connect = contact(self.layers, (1, 1)) + max_via_size = max(via_connect.width,via_connect.height) + + self.horiz_layer_number = layer[self.horiz_layer_name] + self.vert_layer_number = layer[self.vert_layer_name] + + (self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1) + (self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0) + # For supplies, we will make the wire wider than the vias self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size) self.horiz_layer_minwidth = max(self.horiz_layer_minwidth, max_via_size) @@ -37,13 +53,6 @@ class router_tech: self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing - else: - (self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1) - (self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0) - - self.horiz_track_width = max_via_size + self.horiz_layer_spacing - self.vert_track_width = max_via_size + self.vert_layer_spacing - # We'll keep horizontal and vertical tracks the same for simplicity. self.track_width = max(self.horiz_track_width,self.vert_track_width) debug.info(1,"Track width: {:.3f}".format(self.track_width)) @@ -73,24 +82,6 @@ class router_tech: else: debug.error("Invalid zindex {}".format(zindex),-1) - def get_layer_width_space(self, zindex, width=0, length=0): - """ - Return the width and spacing of a given layer - and wire of a given width and length. - """ - if zindex==1: - layer_name = self.vert_layer_name - elif zindex==0: - layer_name = self.horiz_layer_name - else: - debug.error("Invalid zindex for track", -1) - - min_width = drc("minwidth_{0}".format(layer_name), width, length) - min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), width, length) - - return (min_width,min_spacing) - - def get_supply_layer_width_space(self, zindex): """ These are the width and spacing of a supply layer given a supply rail diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py index 59d75c7a..cb012a5e 100644 --- a/compiler/router/signal_grid.py +++ b/compiler/router/signal_grid.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from itertools import tee import debug from heapq import heappush,heappop diff --git a/compiler/router/signal_router.py b/compiler/router/signal_router.py index eb706b5b..9390deaf 100644 --- a/compiler/router/signal_router.py +++ b/compiler/router/signal_router.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import gdsMill import tech from contact import contact diff --git a/compiler/router/supply_grid.py b/compiler/router/supply_grid.py index 88b436af..98c6f031 100644 --- a/compiler/router/supply_grid.py +++ b/compiler/router/supply_grid.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from vector3d import vector3d from grid import grid @@ -27,7 +34,7 @@ class supply_grid(signal_grid): p.reset() - def find_start_wave(self, wave, width, direct): + def find_start_wave(self, wave, direct): """ Finds the first loc starting at loc and up that is open. Returns None if it reaches max size first. diff --git a/compiler/router/supply_router.py b/compiler/router/supply_grid_router.py similarity index 91% rename from compiler/router/supply_router.py rename to compiler/router/supply_grid_router.py index 9f2ddf57..5ddabc98 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_grid_router.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import gdsMill import tech import math @@ -13,7 +20,7 @@ from datetime import datetime import grid import grid_utils -class supply_router(router): +class supply_grid_router(router): """ A router class to read an obstruction map from a gds and routes a grid to connect the supply on the two layers. @@ -81,7 +88,7 @@ class supply_router(router): # Determine the rail locations self.route_supply_rails(self.vdd_name,1) print_time("Routing supply rails",datetime.now(), start_time, 3) - + start_time = datetime.now() self.route_simple_overlaps(vdd_name) self.route_simple_overlaps(gnd_name) @@ -94,10 +101,23 @@ class supply_router(router): self.route_pins_to_rails(gnd_name) print_time("Maze routing supplies",datetime.now(), start_time, 3) #self.write_debug_gds("final.gds",False) + + # Did we route everything?? + if not self.check_all_routed(vdd_name): + return False + if not self.check_all_routed(gnd_name): + return False return True + def check_all_routed(self, pin_name): + """ + Check that all pin groups are routed. + """ + for pg in self.pin_groups[pin_name]: + if not pg.is_routed(): + return False def route_simple_overlaps(self, pin_name): """ @@ -146,13 +166,7 @@ class supply_router(router): # 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: @@ -163,16 +177,11 @@ class supply_router(router): 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, + # Determine if we have 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 + overlap = new_r1 & r2 if len(overlap) >= 1: debug.info(3,"Via overlap {0} {1}".format(len(overlap),overlap)) connections.update([i1,i2]) @@ -213,7 +222,7 @@ class supply_router(router): ur = grid_utils.get_upper_right(rail) z = ll.z pin = self.compute_pin_enclosure(ll, ur, z, name) - debug.info(2,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin)) + debug.info(3,"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(), @@ -270,7 +279,8 @@ class supply_router(router): 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) + # Sweep to find an initial unblocked valid wave + start_wave = self.rg.find_start_wave(seed_wave, direct) # This means there were no more unblocked grids in the row/col if not start_wave: @@ -284,17 +294,6 @@ class supply_router(router): # as it will be used to find the next start location return wave_path - 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): """ @@ -392,7 +391,7 @@ class supply_router(router): # 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) + self.add_pin_component_source(pin_name, index) # Add all of the rails as targets # Don't add the other pins, but we could? @@ -400,7 +399,10 @@ class supply_router(router): # Actually run the A* router if not self.run_router(detour_scale=5): - self.write_debug_gds() + self.write_debug_gds("debug_route.gds",False) + + #if index==3 and pin_name=="vdd": + # self.write_debug_gds("route.gds",False) def add_supply_rail_target(self, pin_name): diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py new file mode 100644 index 00000000..7def03e0 --- /dev/null +++ b/compiler/router/supply_tree_router.py @@ -0,0 +1,193 @@ +# See LICENSE for licensing information. +# +#Copyright (c) 2016-2019 Regents of the University of California and The Board +#of Regents for the Oklahoma Agricultural and Mechanical College +#(acting for and on behalf of Oklahoma State University) +#All rights reserved. +# +import gdsMill +import tech +import math +import debug +from globals import OPTS,print_time +from contact import contact +from pin_group import pin_group +from pin_layout import pin_layout +from vector3d import vector3d +from router import router +from direction import direction +from datetime import datetime +import grid +import grid_utils + +class supply_tree_router(router): + """ + A router class to read an obstruction map from a gds and + routes a grid to connect the supply on the two layers. + """ + + def __init__(self, layers, design, gds_filename=None): + """ + 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). + """ + # Power rail width in minimum wire widths + self.rail_track_width = 3 + + router.__init__(self, layers, design, gds_filename, self.rail_track_width) + + + def create_routing_grid(self): + """ + 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"): + """ + Route the two nets in a single layer) + """ + debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, 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: + # 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() + + # Get the pin shapes + start_time = datetime.now() + self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) + print_time("Finding pins and blockages",datetime.now(), start_time, 3) + + # Add the supply rails in a mesh network and connect H/V with vias + start_time = datetime.now() + # Block everything + self.prepare_blockages(self.gnd_name) + self.prepare_blockages(self.vdd_name) + + # Route the supply pins to the supply rails + # Route vdd first since we want it to be shorter + start_time = datetime.now() + self.route_pins(vdd_name) + self.route_pins(gnd_name) + print_time("Maze routing supplies",datetime.now(), start_time, 3) + + #self.write_debug_gds("final.gds",False) + + # Did we route everything?? + if not self.check_all_routed(vdd_name): + return False + if not self.check_all_routed(gnd_name): + return False + + return True + + + def check_all_routed(self, pin_name): + """ + Check that all pin groups are routed. + """ + for pg in self.pin_groups[pin_name]: + if not pg.is_routed(): + return False + + 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 pin components (some will be unblocked if they're a source/target) + # Also block the previous routes + for name in self.pin_groups: + blockage_grids = {y for x in self.pin_groups[name] for y in x.grids} + self.set_blockages(blockage_grids,True) + blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages} + self.set_blockages(blockage_grids,True) + + # FIXME: These duplicate a bit of work + # These are the paths that have already been routed. + self.set_blockages(self.path_blockages) + + # Don't mark the other components as targets since we want to route + # directly to a rail, but unblock all the source components so we can + # route over them + blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids} + self.set_blockages(blockage_grids,False) + + + + def route_pins(self, pin_name): + """ + This will route each of the remaining pin components to the other pins. + After it is done, the cells are added to the pin blockage list. + """ + + remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name]) + debug.info(1,"Maze routing {0} with {1} pin components to connect.".format(pin_name, + remaining_components)) + + for index,pg in enumerate(self.pin_groups[pin_name]): + if pg.is_routed(): + continue + + debug.info(1,"Routing component {0} {1}".format(pin_name, index)) + + # Clear everything in the routing grid. + self.rg.reinit() + + # This is inefficient since it is non-incremental, but it was + # easier to debug. + self.prepare_blockages(pin_name) + + # Add the single component of the pin as the source + # which unmarks it as a blockage too + self.add_pin_component_source(pin_name,index) + + # Marks all pin components except index as target + self.add_pin_component_target_except(pin_name,index) + # Add the prevous paths as a target too + self.add_path_target(self.paths) + + print("SOURCE: ") + for k,v in self.rg.map.items(): + if v.source: + print(k) + + print("TARGET: ") + for k,v in self.rg.map.items(): + if v.target: + print(k) + + import pdb; pdb.set_trace() + if index==1: + self.write_debug_gds("debug{}.gds".format(pin_name),False) + + # Actually run the A* router + if not self.run_router(detour_scale=5): + self.write_debug_gds("debug_route.gds",True) + + #if index==3 and pin_name=="vdd": + # self.write_debug_gds("route.gds",False) + + + + + diff --git a/compiler/router/tests/01_no_blockages_test.py b/compiler/router/tests/01_no_blockages_test.py old mode 100755 new mode 100644 index 4197f714..4a673c24 --- a/compiler/router/tests/01_no_blockages_test.py +++ b/compiler/router/tests/01_no_blockages_test.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 "Run a regresion test the library cells for DRC" diff --git a/compiler/router/tests/02_blockages_test.py b/compiler/router/tests/02_blockages_test.py old mode 100755 new mode 100644 index 6e3bee08..e317b642 --- a/compiler/router/tests/02_blockages_test.py +++ b/compiler/router/tests/02_blockages_test.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 "Run a regresion test the library cells for DRC" diff --git a/compiler/router/tests/03_same_layer_pins_test.py b/compiler/router/tests/03_same_layer_pins_test.py old mode 100755 new mode 100644 index 726cd02b..10a3544a --- a/compiler/router/tests/03_same_layer_pins_test.py +++ b/compiler/router/tests/03_same_layer_pins_test.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 "Run a regresion test the library cells for DRC" diff --git a/compiler/router/tests/04_diff_layer_pins_test.py b/compiler/router/tests/04_diff_layer_pins_test.py old mode 100755 new mode 100644 index 2882156f..8966d2e4 --- a/compiler/router/tests/04_diff_layer_pins_test.py +++ b/compiler/router/tests/04_diff_layer_pins_test.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 "Run a regresion test the library cells for DRC" diff --git a/compiler/router/tests/05_two_nets_test.py b/compiler/router/tests/05_two_nets_test.py old mode 100755 new mode 100644 index e71920a8..ebf84745 --- a/compiler/router/tests/05_two_nets_test.py +++ b/compiler/router/tests/05_two_nets_test.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 "Run a regresion test the library cells for DRC" diff --git a/compiler/router/tests/06_pin_location_test.py b/compiler/router/tests/06_pin_location_test.py old mode 100755 new mode 100644 index f469d326..a035cc59 --- a/compiler/router/tests/06_pin_location_test.py +++ b/compiler/router/tests/06_pin_location_test.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 "Run a regresion test the library cells for DRC" diff --git a/compiler/router/tests/07_big_test.py b/compiler/router/tests/07_big_test.py old mode 100755 new mode 100644 index 8fcf2826..5410dcbf --- a/compiler/router/tests/07_big_test.py +++ b/compiler/router/tests/07_big_test.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 "Run a regresion test the library cells for DRC" diff --git a/compiler/router/tests/08_expand_region_test.py b/compiler/router/tests/08_expand_region_test.py old mode 100755 new mode 100644 index 3e63bd51..ab4bce59 --- a/compiler/router/tests/08_expand_region_test.py +++ b/compiler/router/tests/08_expand_region_test.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 "Run a regresion test the library cells for DRC" diff --git a/compiler/router/tests/10_supply_grid_test.py b/compiler/router/tests/10_supply_grid_test.py old mode 100755 new mode 100644 index 7258ab40..73e41b86 --- a/compiler/router/tests/10_supply_grid_test.py +++ b/compiler/router/tests/10_supply_grid_test.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 "Run a regresion test the library cells for DRC" diff --git a/compiler/router/tests/config_freepdk45.py b/compiler/router/tests/config_freepdk45.py old mode 100755 new mode 100644 index aaaa4c37..bdc576bf --- a/compiler/router/tests/config_freepdk45.py +++ b/compiler/router/tests/config_freepdk45.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# word_size = 1 num_words = 16 diff --git a/compiler/router/tests/config_scn4m_subm.py b/compiler/router/tests/config_scn4m_subm.py old mode 100755 new mode 100644 index e3aa1498..bfaa01f8 --- a/compiler/router/tests/config_scn4m_subm.py +++ b/compiler/router/tests/config_scn4m_subm.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# word_size = 1 num_words = 16 diff --git a/compiler/router/tests/gds_cell.py b/compiler/router/tests/gds_cell.py index 5c1e0f91..b93b512a 100644 --- a/compiler/router/tests/gds_cell.py +++ b/compiler/router/tests/gds_cell.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# from design import design class gds_cell(design): """ diff --git a/compiler/router/tests/regress.py b/compiler/router/tests/regress.py old mode 100755 new mode 100644 index b40263c7..db21bbd9 --- a/compiler/router/tests/regress.py +++ b/compiler/router/tests/regress.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/env python3 import re diff --git a/compiler/router/tests/testutils.py b/compiler/router/tests/testutils.py old mode 100755 new mode 100644 index 4bea5d15..c90046f3 --- a/compiler/router/tests/testutils.py +++ b/compiler/router/tests/testutils.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest,warnings import sys,os,glob,copy sys.path.append(os.path.join(sys.path[0],"../..")) diff --git a/compiler/router/vector3d.py b/compiler/router/vector3d.py index 1d0d083e..0d183021 100644 --- a/compiler/router/vector3d.py +++ b/compiler/router/vector3d.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug import math diff --git a/compiler/run_profile.sh b/compiler/run_profile.sh index 37a130a4..1234427d 100755 --- a/compiler/run_profile.sh +++ b/compiler/run_profile.sh @@ -1,3 +1,3 @@ #!/bin/bash -python3 -m cProfile -o profile.dat ./openram.py example_configs/medium_config_scn4m_subm.py -v | tee -i medium.log +python3 -m cProfile -o profile.dat ./openram.py example_configs/giant_config_scn4m_subm.py -v | tee -i big.log echo "Run view_profile.py to view results" diff --git a/compiler/sram.py b/compiler/sram/sram.py similarity index 92% rename from compiler/sram.py rename to compiler/sram/sram.py index 5ff28d47..c4f41f77 100644 --- a/compiler/sram.py +++ b/compiler/sram/sram.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import sys import datetime import getpass @@ -27,7 +34,6 @@ class sram(): start_time = datetime.datetime.now() self.name = name - if self.num_banks == 1: from sram_1bank import sram_1bank as sram @@ -62,6 +68,13 @@ class sram(): """ Save all the output files while reporting time to do it as well. """ if not OPTS.netlist_only: + # Create a LEF physical model + start_time = datetime.datetime.now() + lefname = OPTS.output_path + self.s.name + ".lef" + debug.print_raw("LEF: Writing to {0}".format(lefname)) + self.lef_write(lefname) + print_time("LEF", datetime.datetime.now(), start_time) + # Write the layout start_time = datetime.datetime.now() gdsname = OPTS.output_path + self.s.name + ".gds" @@ -69,13 +82,6 @@ class sram(): self.gds_write(gdsname) print_time("GDS", datetime.datetime.now(), start_time) - # Create a LEF physical model - start_time = datetime.datetime.now() - lefname = OPTS.output_path + self.s.name + ".lef" - debug.print_raw("LEF: Writing to {0}".format(lefname)) - self.lef_write(lefname) - print_time("LEF", datetime.datetime.now(), start_time) - # Save the spice file start_time = datetime.datetime.now() spname = OPTS.output_path + self.s.name + ".sp" @@ -99,13 +105,6 @@ class sram(): start_time = datetime.datetime.now() from characterizer import lib debug.print_raw("LIB: Characterizing... ") - if OPTS.analytical_delay: - debug.print_raw("Using analytical delay models (no characterization)") - else: - if OPTS.spice_name!="": - debug.print_raw("Performing simulation-based characterization with {}".format(OPTS.spice_name)) - if OPTS.trim_netlist: - debug.print_raw("Trimming netlist to speed up characterization.") lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) print_time("Characterization", datetime.datetime.now(), start_time) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py new file mode 100644 index 00000000..9e578c7c --- /dev/null +++ b/compiler/sram/sram_1bank.py @@ -0,0 +1,491 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import sys +from tech import drc, spice +import debug +from math import log,sqrt,ceil +import datetime +import getpass +import numpy as np +from vector import vector +from globals import OPTS, print_time + +from sram_base import sram_base +from bank import bank +from contact import m2m3 +from dff_buf_array import dff_buf_array +from dff_array import dff_array + + +class sram_1bank(sram_base): + """ + Procedures specific to a one bank SRAM. + """ + def __init__(self, name, sram_config): + sram_base.__init__(self, name, sram_config) + + def create_modules(self): + """ + This adds the modules for a single bank SRAM with control + logic. + """ + + self.bank_inst=self.create_bank(0) + + self.control_logic_insts = self.create_control_logic() + + self.row_addr_dff_insts = self.create_row_addr_dff() + + if self.col_addr_dff: + self.col_addr_dff_insts = self.create_col_addr_dff() + + if self.write_size: + self.wmask_dff_insts = self.create_wmask_dff() + self.data_dff_insts = self.create_data_dff() + else: + self.data_dff_insts = self.create_data_dff() + + def place_instances(self): + """ + This places the instances for a single bank SRAM with control + logic and up to 2 ports. + """ + + # No orientation or offset + self.place_bank(self.bank_inst, [0, 0], 1, 1) + + # The control logic is placed such that the vertical center (between the delay/RBL and + # the actual control logic is aligned with the vertical center of the bank (between + # the sense amps/column mux and cell array) + # The x-coordinate is placed to allow a single clock wire (plus an extra pitch) + # up to the row address DFFs. + control_pos = [None]*len(self.all_ports) + row_addr_pos = [None]*len(self.all_ports) + col_addr_pos = [None]*len(self.all_ports) + wmask_pos = [None]*len(self.all_ports) + data_pos = [None]*len(self.all_ports) + + if self.write_size: + max_gap_size = self.m3_pitch*self.word_size + 2*self.m1_pitch + max_gap_size_wmask = self.m2_pitch*max(self.num_wmasks+1,self.col_addr_size+1) + 2*self.m1_pitch + else: + # This is M2 pitch even though it is on M1 to help stem via spacings on the trunk + # The M1 pitch is for supply rail spacings + max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch + + # Port 0 + port = 0 + + if port in self.write_ports: + if self.write_size: + # Add the write mask flops below the write mask AND array. + wmask_pos[port] = vector(self.bank.bank_array_ll.x, + -max_gap_size_wmask - self.dff.height) + self.wmask_dff_insts[port].place(wmask_pos[port]) + + # Add the data flops below the write mask flops. + data_pos[port] = vector(self.bank.bank_array_ll.x, + -max_gap_size - max_gap_size_wmask - 2*self.dff.height) + self.data_dff_insts[port].place(data_pos[port]) + else: + # Add the data flops below the bank to the right of the lower-left of bank array + # This relies on the lower-left of the array of the bank + # decoder in upper left, bank in upper right, sensing in lower right. + # These flops go below the sensing and leave a gap to channel route to the + # sense amps. + if port in self.write_ports: + data_pos[port] = vector(self.bank.bank_array_ll.x, + -max_gap_size - self.dff.height) + self.data_dff_insts[port].place(data_pos[port]) + else: + wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0) + data_pos[port] = vector(self.bank.bank_array_ll.x,0) + + + # Add the col address flops below the bank to the left of the lower-left of bank array + if self.col_addr_dff: + if self.write_size: + col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, + -max_gap_size_wmask - self.col_addr_dff_insts[port].height) + else: + col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, + -max_gap_size - self.col_addr_dff_insts[port].height) + self.col_addr_dff_insts[port].place(col_addr_pos[port]) + else: + col_addr_pos[port] = vector(self.bank.bank_array_ll.x,0) + + # This includes 2 M2 pitches for the row addr clock line. + control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch, + self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - 2*self.bank.m2_gap ) + self.control_logic_insts[port].place(control_pos[port]) + + # The row address bits are placed above the control logic aligned on the right. + x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width + # It is above the control logic but below the top of the bitcell array + y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height) + row_addr_pos[port] = vector(x_offset, y_offset) + self.row_addr_dff_insts[port].place(row_addr_pos[port]) + + if len(self.all_ports)>1: + # Port 1 + port = 1 + + if port in self.write_ports: + if self.write_size: + # Add the write mask flops below the write mask AND array. + wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width, + self.bank.height + max_gap_size_wmask + self.dff.height) + self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX") + + # Add the data flops below the write mask flops + data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, + self.bank.height + max_gap_size_wmask + max_gap_size + 2*self.dff.height) + self.data_dff_insts[port].place(data_pos[port], mirror="MX") + else: + # Add the data flops above the bank to the left of the upper-right of bank array + # This relies on the upper-right of the array of the bank + # decoder in upper left, bank in upper right, sensing in lower right. + # These flops go below the sensing and leave a gap to channel route to the + # sense amps. + data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, + self.bank.height + max_gap_size + self.dff.height) + self.data_dff_insts[port].place(data_pos[port], mirror="MX") + + # Add the col address flops above the bank to the right of the upper-right of bank array + if self.col_addr_dff: + if self.write_size: + col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, + self.bank.height + max_gap_size_wmask + self.dff.height) + else: + col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, + self.bank.height + max_gap_size + self.dff.height) + self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") + else: + col_addr_pos[port] = self.bank_inst.ur() + + # This includes 2 M2 pitches for the row addr clock line + control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch, + self.bank.bank_array_ur.y + self.control_logic_insts[port].height - + (self.control_logic_insts[port].height - self.control_logic_insts[port].mod.control_logic_center.y) + + 2*self.bank.m2_gap) + #import pdb; pdb.set_trace() + self.control_logic_insts[port].place(control_pos[port], mirror="XY") + + # The row address bits are placed above the control logic aligned on the left. + x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width + # It is below the control logic but below the bottom of the bitcell array + y_offset = min(self.control_logic_insts[port].by(), self.bank_inst.by() + self.row_addr_dff_insts[port].height) + row_addr_pos[port] = vector(x_offset, y_offset) + self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY") + + + def add_layout_pins(self): + """ + Add the top-level pins for a single bank SRAM with control. + """ + for port in self.all_ports: + # Connect the control pins as inputs + for signal in self.control_logic_inputs[port] + ["clk"]: + self.copy_layout_pin(self.control_logic_insts[port], signal, signal+"{}".format(port)) + + if port in self.read_ports: + for bit in range(self.word_size): + self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "dout{0}[{1}]".format(port,bit)) + + # Lower address bits + for bit in range(self.col_addr_size): + self.copy_layout_pin(self.col_addr_dff_insts[port], "din_{}".format(bit),"addr{0}[{1}]".format(port,bit)) + # Upper address bits + for bit in range(self.row_addr_size): + self.copy_layout_pin(self.row_addr_dff_insts[port], "din_{}".format(bit),"addr{0}[{1}]".format(port,bit+self.col_addr_size)) + + if port in self.write_ports: + for bit in range(self.word_size): + self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "din{0}[{1}]".format(port,bit)) + + if self.write_size: + for bit in range(self.num_wmasks): + self.copy_layout_pin(self.wmask_dff_insts[port], "din_{}".format(bit), "wmask{0}[{1}]".format(port,bit)) + + + def route_layout(self): + """ Route a single bank SRAM """ + + self.add_layout_pins() + + self.route_clk() + + self.route_control_logic() + + self.route_row_addr_dff() + + if self.col_addr_dff: + self.route_col_addr_dff() + + self.route_data_dff() + + if self.write_size: + self.route_wmask_dff() + + def route_clk(self): + """ Route the clock network """ + + # This is the actual input to the SRAM + for port in self.all_ports: + self.copy_layout_pin(self.control_logic_insts[port], "clk", "clk{}".format(port)) + + # Connect all of these clock pins to the clock in the central bus + # This is something like a "spine" clock distribution. The two spines + # are clk_buf and clk_buf_bar + control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf") + control_clk_buf_pos = control_clk_buf_pin.center() + + # This uses a metal2 track to the right (for port0) of the control/row addr DFF + # to route vertically. For port1, it is to the left. + row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk") + if port%2: + control_clk_buf_pos = control_clk_buf_pin.lc() + row_addr_clk_pos = row_addr_clk_pin.lc() + mid1_pos = vector(self.row_addr_dff_insts[port].lx() - self.m2_pitch, + row_addr_clk_pos.y) + else: + control_clk_buf_pos = control_clk_buf_pin.rc() + row_addr_clk_pos = row_addr_clk_pin.rc() + mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch, + row_addr_clk_pos.y) + + # This is the steiner point where the net branches out + clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y) + self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos]) + self.add_via_center(layers=("metal1","via1","metal2"), + offset=clk_steiner_pos) + + # Note, the via to the control logic is taken care of above + self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos]) + + if self.col_addr_dff: + dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") + dff_clk_pos = dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) + self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, clk_steiner_pos]) + + if port in self.write_ports: + data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") + data_dff_clk_pos = data_dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y) + # In some designs, the steiner via will be too close to the mid_pos via + # so make the wire as wide as the contacts + self.add_path("metal2",[mid_pos, clk_steiner_pos], width=max(m2m3.width,m2m3.height)) + self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos]) + + if self.write_size: + wmask_dff_clk_pin = self.wmask_dff_insts[port].get_pin("clk") + wmask_dff_clk_pos = wmask_dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, wmask_dff_clk_pos.y) + # In some designs, the steiner via will be too close to the mid_pos via + # so make the wire as wide as the contacts + self.add_path("metal2", [mid_pos, clk_steiner_pos], width=max(m2m3.width, m2m3.height)) + self.add_wire(("metal3", "via2", "metal2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos]) + + + def route_control_logic(self): + """ Route the control logic pins that are not inputs """ + + for port in self.all_ports: + for signal in self.control_logic_outputs[port]: + # The clock gets routed separately and is not a part of the bank + if "clk" in signal: + continue + src_pin = self.control_logic_insts[port].get_pin(signal) + dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) + self.connect_vbus_m2m3(src_pin, dest_pin) + + for port in self.all_ports: + # Only input (besides pins) is the replica bitline + src_pin = self.control_logic_insts[port].get_pin("rbl_bl") + dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port)) + self.connect_vbus_m2m3(src_pin, dest_pin) + + + def route_row_addr_dff(self): + """ Connect the output of the row flops to the bank pins """ + for port in self.all_ports: + for bit in range(self.row_addr_size): + flop_name = "dout_{}".format(bit) + bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size) + flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name) + bank_pin = self.bank_inst.get_pin(bank_name) + flop_pos = flop_pin.center() + bank_pos = bank_pin.center() + mid_pos = vector(bank_pos.x,flop_pos.y) + self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos]) + self.add_via_center(layers=("metal2","via2","metal3"), + offset=flop_pos) + + def route_col_addr_dff(self): + """ Connect the output of the row flops to the bank pins """ + for port in self.all_ports: + if port%2: + offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) + else: + offset = self.col_addr_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) + + bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)] + col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1", + pitch=self.m1_pitch, + offset=offset, + names=bus_names, + length=self.col_addr_dff_insts[port].width) + + dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] + data_dff_map = zip(dff_names, bus_names) + self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_insts[port], col_addr_bus_offsets) + + bank_names = ["addr{0}_{1}".format(port,x) for x in range(self.col_addr_size)] + data_bank_map = zip(bank_names, bus_names) + self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets) + + + def route_data_dff(self): + """ Connect the output of the data flops to the write driver """ + # This is where the channel will start (y-dimension at least) + for port in self.write_ports: + if self.write_size: + if port % 2: + offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2)*self.m3_pitch) + else: + offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m3_pitch) + else: + if port%2: + offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) + else: + offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) + + dff_names = ["dout_{}".format(x) for x in range(self.word_size)] + dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] + if self.write_size: + for x in dff_names: + pin_offset = self.data_dff_insts[port].get_pin(x).center() + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pin_offset, + directions = ("V", "V")) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=pin_offset) + self.add_via_center(layers=("metal3", "via3", "metal4"), + offset=pin_offset) + + bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + if self.write_size: + for x in bank_names: + if port % 2: + pin_offset = self.bank_inst.get_pin(x).uc() + else: + pin_offset = self.bank_inst.get_pin(x).bc() + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=pin_offset) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=pin_offset) + self.add_via_center(layers=("metal3", "via3", "metal4"), + offset=pin_offset) + + route_map = list(zip(bank_pins, dff_pins)) + if self.write_size: + self.create_horizontal_channel_route(netlist=route_map, + offset=offset, + layer_stack=("metal3", "via3", "metal4")) + else: + self.create_horizontal_channel_route(route_map, offset) + + def route_wmask_dff(self): + """ Connect the output of the wmask flops to the write mask AND array """ + # This is where the channel will start (y-dimension at least) + for port in self.write_ports: + if port % 2: + offset = self.wmask_dff_insts[port].ll() - vector(0, (self.num_wmasks+2) * self.m1_pitch) + else: + offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch) + + dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)] + dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names] + for x in dff_names: + offset_pin = self.wmask_dff_insts[port].get_pin(x).center() + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=offset_pin, + directions=("V", "V")) + + bank_names = ["bank_wmask{0}_{1}".format(port, x) for x in range(self.num_wmasks)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + for x in bank_names: + offset_pin = self.bank_inst.get_pin(x).center() + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=offset_pin) + + + route_map = list(zip(bank_pins, dff_pins)) + self.create_horizontal_channel_route(route_map,offset) + + + def add_lvs_correspondence_points(self): + """ + This adds some points for easier debugging if LVS goes wrong. + These should probably be turned off by default though, since extraction + will show these as ports in the extracted netlist. + """ + + for n in self.control_logic_outputs[0]: + pin = self.control_logic_insts[0].get_pin(n) + self.add_label(text=n, + layer=pin.layer, + offset=pin.center()) + + def graph_exclude_data_dff(self): + """Removes data dff and wmask dff (if applicable) from search graph. """ + #Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay. + for inst in self.data_dff_insts: + self.graph_inst_exclude.add(inst) + if self.write_size: + for inst in self.wmask_dff_insts: + self.graph_inst_exclude.add(inst) + + def graph_exclude_addr_dff(self): + """Removes data dff from search graph. """ + #Address is considered not part of the critical path, subjectively removed + for inst in self.row_addr_dff_insts: + self.graph_inst_exclude.add(inst) + + if self.col_addr_dff: + for inst in self.col_addr_dff_insts: + self.graph_inst_exclude.add(inst) + + def graph_exclude_ctrl_dffs(self): + """Exclude dffs for CSB, WEB, etc from graph""" + #Insts located in control logic, exclusion function called here + for inst in self.control_logic_insts: + inst.mod.graph_exclude_dffs() + + def get_sen_name(self, sram_name, port=0): + """Returns the s_en spice name.""" + #Naming scheme is hardcoded using this function, should be built into the + #graph in someway. + sen_name = "s_en{}".format(port) + control_conns = self.get_conns(self.control_logic_insts[port]) + #Sanity checks + if sen_name not in control_conns: + debug.error("Signal={} not contained in control logic connections={}"\ + .format(sen_name, control_conns)) + if sen_name in self.pins: + debug.error("Internal signal={} contained in port list. Name defined by the parent.") + return "X{}.{}".format(sram_name, sen_name) + + def get_cell_name(self, inst_name, row, col): + """Gets the spice name of the target bitcell.""" + #Sanity check in case it was forgotten + if inst_name.find('x') != 0: + inst_name = 'x'+inst_name + return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col) diff --git a/compiler/sram_2bank.py b/compiler/sram/sram_2bank.py similarity index 96% rename from compiler/sram_2bank.py rename to compiler/sram/sram_2bank.py index daf02563..e1224795 100644 --- a/compiler/sram_2bank.py +++ b/compiler/sram/sram_2bank.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import sys from tech import drc, spice import debug @@ -81,7 +88,7 @@ class sram_2bank(sram_base): mod=self.msb_address, offset=self.msb_address_position, rotate=270) - self.msb_bank_sel_addr = "ADDR[{}]".format(self.addr_size-1) + self.msb_bank_sel_addr = "addr[{}]".format(self.addr_size-1) self.connect_inst([self.msb_bank_sel_addr,"bank_sel[1]","bank_sel[0]","clk_buf", "vdd", "gnd"]) diff --git a/compiler/sram_base.py b/compiler/sram/sram_base.py similarity index 81% rename from compiler/sram_base.py rename to compiler/sram/sram_base.py index 611900e4..853772e8 100644 --- a/compiler/sram_base.py +++ b/compiler/sram/sram_base.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import sys import datetime import getpass @@ -11,6 +18,7 @@ from design import design from verilog import verilog from lef import lef from sram_factory import factory +import logical_effort class sram_base(design, verilog, lef): """ @@ -26,19 +34,25 @@ class sram_base(design, verilog, lef): sram_config.set_local_config(self) self.bank_insts = [] - + + if self.write_size: + self.num_wmasks = int(self.word_size/self.write_size) + else: + self.num_wmasks = 0 + #For logical effort delay calculations. self.all_mods_except_control_done = False def add_pins(self): """ Add pins for entire SRAM. """ + for port in self.write_ports: for bit in range(self.word_size): - self.add_pin("DIN{0}[{1}]".format(port,bit),"INPUT") + self.add_pin("din{0}[{1}]".format(port,bit),"INPUT") for port in self.all_ports: for bit in range(self.addr_size): - self.add_pin("ADDR{0}[{1}]".format(port,bit),"INPUT") + self.add_pin("addr{0}[{1}]".format(port,bit),"INPUT") # These are used to create the physical pins self.control_logic_inputs = [] @@ -60,10 +74,13 @@ class sram_base(design, verilog, lef): self.add_pin("web{}".format(port),"INPUT") for port in self.all_ports: self.add_pin("clk{}".format(port),"INPUT") - + # add the optional write mask pins + for port in self.write_ports: + for bit in range(self.num_wmasks): + self.add_pin("wmask{0}[{1}]".format(port,bit),"INPUT") for port in self.read_ports: for bit in range(self.word_size): - self.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT") + self.add_pin("dout{0}[{1}]".format(port,bit),"OUTPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") @@ -103,14 +120,15 @@ class sram_base(design, verilog, lef): self.add_lvs_correspondence_points() - self.offset_all_coordinates() + #self.offset_all_coordinates() highest_coord = self.find_highest_coords() self.width = highest_coord[0] self.height = highest_coord[1] start_time = datetime.now() - self.DRC_LVS(final_verification=True) + # We only enable final verification if we have routed the design + self.DRC_LVS(final_verification=OPTS.route_supplies, top_level=True) if not OPTS.is_unit_test: print_time("Verification",datetime.now(), start_time) @@ -120,18 +138,27 @@ class sram_base(design, verilog, lef): def route_supplies(self): """ Route the supply grid and connect the pins to them. """ + # Copy the pins to the top level + # This will either be used to route or left unconnected. for inst in self.insts: self.copy_power_pins(inst,"vdd") self.copy_power_pins(inst,"gnd") + + import tech + if not OPTS.route_supplies: + # Do not route the power supply (leave as must-connect pins) + return + elif "metal4" in tech.layer: + # Route a M3/M4 grid + from supply_grid_router import supply_grid_router as router + rtr=router(("metal3","via3","metal4"), self) + elif "metal3" in tech.layer: + from supply_tree_router import supply_tree_router as router + rtr=router(("metal3",), self) - from supply_router import supply_router as router - layer_stack =("metal3","via3","metal4") - rtr=router(layer_stack, self) rtr.route() - - def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """ @@ -170,7 +197,7 @@ class sram_base(design, verilog, lef): if self.port_id[port] == "r": self.control_bus_names[port].extend([sen, pen]) elif self.port_id[port] == "w": - self.control_bus_names[port].extend([wen]) + self.control_bus_names[port].extend([wen, pen]) else: self.control_bus_names[port].extend([sen, wen, pen]) self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2", @@ -241,6 +268,7 @@ class sram_base(design, verilog, lef): def add_modules(self): self.bitcell = factory.create(module_type=OPTS.bitcell) + self.dff = factory.create(module_type="dff") # Create the address and control flops (but not the clk) from dff_array import dff_array @@ -255,6 +283,11 @@ class sram_base(design, verilog, lef): self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size) self.add_mod(self.data_dff) + + if self.write_size: + self.wmask_dff = dff_array(name="wmask_dff", rows=1, columns=self.num_wmasks) + self.add_mod(self.wmask_dff) + # Create the bank module (up to four are instantiated) from bank import bank @@ -268,9 +301,6 @@ class sram_base(design, verilog, lef): self.bank_count = 0 - self.supply_rail_width = self.bank.supply_rail_width - self.supply_rail_pitch = self.bank.supply_rail_pitch - #The control logic can resize itself based on the other modules. Requires all other modules added before control logic. self.all_mods_except_control_done = True @@ -282,21 +312,21 @@ class sram_base(design, verilog, lef): self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, - sram=self, + sram=self, port_type="rw") self.add_mod(self.control_logic_rw) if len(self.writeonly_ports)>0: self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, - sram=self, + sram=self, port_type="w") self.add_mod(self.control_logic_w) if len(self.readonly_ports)>0: self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, - sram=self, + sram=self, port_type="r") self.add_mod(self.control_logic_r) @@ -308,22 +338,26 @@ class sram_base(design, verilog, lef): temp = [] for port in self.read_ports: for bit in range(self.word_size): - temp.append("DOUT{0}[{1}]".format(port,bit)) + temp.append("dout{0}[{1}]".format(port,bit)) + for port in self.all_ports: + temp.append("rbl_bl{0}".format(port)) for port in self.write_ports: for bit in range(self.word_size): - temp.append("BANK_DIN{0}[{1}]".format(port,bit)) + temp.append("bank_din{0}[{1}]".format(port,bit)) for port in self.all_ports: for bit in range(self.bank_addr_size): - temp.append("A{0}[{1}]".format(port,bit)) + temp.append("a{0}[{1}]".format(port,bit)) if(self.num_banks > 1): for port in self.all_ports: temp.append("bank_sel{0}[{1}]".format(port,bank_num)) for port in self.read_ports: temp.append("s_en{0}".format(port)) - for port in self.read_ports: + for port in self.all_ports: temp.append("p_en_bar{0}".format(port)) for port in self.write_ports: temp.append("w_en{0}".format(port)) + for bit in range(self.num_wmasks): + temp.append("bank_wmask{}[{}]".format(port, bit)) for port in self.all_ports: temp.append("wl_en{0}".format(port)) temp.extend(["vdd", "gnd"]) @@ -374,8 +408,8 @@ class sram_base(design, verilog, lef): inputs = [] outputs = [] for bit in range(self.row_addr_size): - inputs.append("ADDR{}[{}]".format(port,bit+self.col_addr_size)) - outputs.append("A{}[{}]".format(port,bit+self.col_addr_size)) + inputs.append("addr{}[{}]".format(port,bit+self.col_addr_size)) + outputs.append("a{}[{}]".format(port,bit+self.col_addr_size)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) @@ -393,8 +427,8 @@ class sram_base(design, verilog, lef): inputs = [] outputs = [] for bit in range(self.col_addr_size): - inputs.append("ADDR{}[{}]".format(port,bit)) - outputs.append("A{}[{}]".format(port,bit)) + inputs.append("addr{}[{}]".format(port,bit)) + outputs.append("a{}[{}]".format(port,bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) @@ -416,13 +450,35 @@ class sram_base(design, verilog, lef): inputs = [] outputs = [] for bit in range(self.word_size): - inputs.append("DIN{}[{}]".format(port,bit)) - outputs.append("BANK_DIN{}[{}]".format(port,bit)) + inputs.append("din{}[{}]".format(port,bit)) + outputs.append("bank_din{}[{}]".format(port,bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) return insts + def create_wmask_dff(self): + """ Add and place all wmask flops """ + insts = [] + for port in self.all_ports: + if port in self.write_ports: + insts.append(self.add_inst(name="wmask_dff{}".format(port), + mod=self.wmask_dff)) + else: + insts.append(None) + continue + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.num_wmasks): + inputs.append("wmask{}[{}]".format(port, bit)) + outputs.append("bank_wmask{}[{}]".format(port, bit)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + + return insts + def create_control_logic(self): """ Add control logic instances """ @@ -443,36 +499,45 @@ class sram_base(design, verilog, lef): if port in self.readwrite_ports: temp.append("web{}".format(port)) temp.append("clk{}".format(port)) + temp.append("rbl_bl{}".format(port)) - # Ouputs + # Outputs if port in self.read_ports: temp.append("s_en{}".format(port)) if port in self.write_ports: temp.append("w_en{}".format(port)) - if port in self.read_ports: - temp.append("p_en_bar{}".format(port)) + temp.append("p_en_bar{}".format(port)) temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"]) self.connect_inst(temp) return insts - def connect_rail_from_left_m2m3(self, src_pin, dest_pin): - """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ - in_pos = src_pin.rc() - out_pos = dest_pin.center() + def connect_vbus_m2m3(self, src_pin, dest_pin): + """ Helper routine to connect an instance to a vertical bus. + Routes horizontal then vertical L shape. + Dest pin is assumed to be on M2. + Src pin can be on M1/M2/M3.""" + + if src_pin.cx() 0: + total_cin += self.col_addr_dff.get_clk_cin() + return total_cin diff --git a/compiler/sram_config.py b/compiler/sram/sram_config.py similarity index 82% rename from compiler/sram_config.py rename to compiler/sram/sram_config.py index 5edf9282..376bf42b 100644 --- a/compiler/sram_config.py +++ b/compiler/sram/sram_config.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from math import log,sqrt,ceil from importlib import reload @@ -7,16 +14,18 @@ from sram_factory import factory class sram_config: """ This is a structure that is used to hold the SRAM configuration options. """ - def __init__(self, word_size, num_words, num_banks=1, words_per_row=None): + def __init__(self, word_size, num_words, write_size = None, num_banks=1, words_per_row=None): self.word_size = word_size self.num_words = num_words + self.write_size = write_size self.num_banks = num_banks # This will get over-written when we determine the organization self.words_per_row = words_per_row + self.compute_sizes() - + def set_local_config(self, module): """ Copy all of the member variables to the given module for convenience """ @@ -30,7 +39,7 @@ class sram_config: def compute_sizes(self): """ Computes the organization of the memory using bitcell size by trying to make it square.""" - self.bitcell = factory.create(module_type="bitcell") + bitcell = factory.create(module_type="bitcell") debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.") @@ -41,18 +50,17 @@ class sram_config: # If this was hard coded, don't dynamically compute it! if not self.words_per_row: # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) - self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank + self.bank_area = bitcell.width*bitcell.height*self.num_bits_per_bank self.bank_side_length = sqrt(self.bank_area) # Estimate the words per row given the height of the bitcell and the square side length - self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width) + self.tentative_num_cols = int(self.bank_side_length/bitcell.width) self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size) # 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) - debug.info(1,"Words per row: {}".format(self.words_per_row)) self.recompute_sizes() def recompute_sizes(self): @@ -62,6 +70,8 @@ class sram_config: SRAM for testing. """ + debug.info(1,"Recomputing with words per row: {}".format(self.words_per_row)) + # If the banks changed self.num_words_per_bank = self.num_words/self.num_banks self.num_bits_per_bank = self.word_size*self.num_words_per_bank @@ -69,12 +79,16 @@ class sram_config: # 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) + debug.info(1,"Rows: {} Cols: {}".format(self.num_rows,self.num_cols)) # Compute the address and bank sizes self.row_addr_size = int(log(self.num_rows, 2)) self.col_addr_size = int(log(self.words_per_row, 2)) self.bank_addr_size = self.col_addr_size + self.row_addr_size self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) + debug.info(1,"Row addr size: {}".format(self.row_addr_size) + + " Col addr size: {}".format(self.col_addr_size) + + " Bank addr size: {}".format(self.bank_addr_size)) def estimate_words_per_row(self,tentative_num_cols, word_size): diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py deleted file mode 100644 index cbfec653..00000000 --- a/compiler/sram_1bank.py +++ /dev/null @@ -1,307 +0,0 @@ -import sys -from tech import drc, spice -import debug -from math import log,sqrt,ceil -import datetime -import getpass -import numpy as np -from vector import vector -from globals import OPTS, print_time - -from sram_base import sram_base -from bank import bank -from contact import m2m3 -from dff_buf_array import dff_buf_array -from dff_array import dff_array - - -class sram_1bank(sram_base): - """ - Procedures specific to a one bank SRAM. - """ - def __init__(self, name, sram_config): - sram_base.__init__(self, name, sram_config) - - def create_modules(self): - """ - This adds the modules for a single bank SRAM with control - logic. - """ - - self.bank_inst=self.create_bank(0) - - self.control_logic_insts = self.create_control_logic() - - self.row_addr_dff_insts = self.create_row_addr_dff() - - if self.col_addr_dff: - self.col_addr_dff_insts = self.create_col_addr_dff() - - self.data_dff_insts = self.create_data_dff() - - def place_instances(self): - """ - This places the instances for a single bank SRAM with control - logic and up to 2 ports. - """ - - # No orientation or offset - self.place_bank(self.bank_inst, [0, 0], 1, 1) - - # The control logic is placed such that the vertical center (between the delay/RBL and - # the actual control logic is aligned with the vertical center of the bank (between - # the sense amps/column mux and cell array) - # The x-coordinate is placed to allow a single clock wire (plus an extra pitch) - # up to the row address DFFs. - control_pos = [None]*len(self.all_ports) - row_addr_pos = [None]*len(self.all_ports) - col_addr_pos = [None]*len(self.all_ports) - data_pos = [None]*len(self.all_ports) - - # This is M2 pitch even though it is on M1 to help stem via spacings on the trunk - # The M1 pitch is for supply rail spacings - max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch - - # Port 0 - port = 0 - - # This includes 2 M2 pitches for the row addr clock line. - # It is also placed to align with the column decoder (if it exists hence the bank gap) - control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch, - self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - self.bank.m2_gap) - self.control_logic_insts[port].place(control_pos[port]) - - # The row address bits are placed above the control logic aligned on the right. - x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width - # It is aove the control logic but below the top of the bitcell array - y_offset = max(self.control_logic_insts[port].uy(), self.bank.bank_array_ur.y - self.row_addr_dff_insts[port].height) - row_addr_pos[port] = vector(x_offset, y_offset) - self.row_addr_dff_insts[port].place(row_addr_pos[port]) - - # Add the col address flops below the bank to the left of the lower-left of bank array - if self.col_addr_dff: - col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, - -max_gap_size - self.col_addr_dff_insts[port].height) - self.col_addr_dff_insts[port].place(col_addr_pos[port]) - - # Add the data flops below the bank to the right of the lower-left of bank array - # This relies on the lower-left of the array of the bank - # decoder in upper left, bank in upper right, sensing in lower right. - # These flops go below the sensing and leave a gap to channel route to the - # sense amps. - if port in self.write_ports: - data_pos[port] = vector(self.bank.bank_array_ll.x, - -max_gap_size - self.data_dff_insts[port].height) - self.data_dff_insts[port].place(data_pos[port]) - - - if len(self.all_ports)>1: - # Port 1 - port = 1 - - # This includes 2 M2 pitches for the row addr clock line - # It is also placed to align with the column decoder (if it exists hence the bank gap) - control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch, - self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y + self.bank.m2_gap) - self.control_logic_insts[port].place(control_pos[port], mirror="MY") - - # The row address bits are placed above the control logic aligned on the left. - x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width - # It is above the control logic but below the top of the bitcell array - y_offset = max(self.control_logic_insts[port].uy(), self.bank.bank_array_ur.y - self.row_addr_dff_insts[port].height) - row_addr_pos[port] = vector(x_offset, y_offset) - self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="MY") - - # Add the col address flops above the bank to the right of the upper-right of bank array - if self.col_addr_dff: - col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, - self.bank.height + max_gap_size + self.col_addr_dff_insts[port].height) - self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") - - # Add the data flops above the bank to the left of the upper-right of bank array - # This relies on the upper-right of the array of the bank - # decoder in upper left, bank in upper right, sensing in lower right. - # These flops go below the sensing and leave a gap to channel route to the - # sense amps. - if port in self.write_ports: - data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, - self.bank.height + max_gap_size + self.data_dff_insts[port].height) - self.data_dff_insts[port].place(data_pos[port], mirror="MX") - - - def add_layout_pins(self): - """ - Add the top-level pins for a single bank SRAM with control. - """ - for port in self.all_ports: - # Connect the control pins as inputs - for signal in self.control_logic_inputs[port] + ["clk"]: - self.copy_layout_pin(self.control_logic_insts[port], signal, signal+"{}".format(port)) - - if port in self.read_ports: - for bit in range(self.word_size): - self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "DOUT{0}[{1}]".format(port,bit)) - - # Lower address bits - for bit in range(self.col_addr_size): - self.copy_layout_pin(self.col_addr_dff_insts[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit)) - # Upper address bits - for bit in range(self.row_addr_size): - self.copy_layout_pin(self.row_addr_dff_insts[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit+self.col_addr_size)) - - if port in self.write_ports: - for bit in range(self.word_size): - self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit)) - - def route_layout(self): - """ Route a single bank SRAM """ - - self.add_layout_pins() - - self.route_clk() - - self.route_control_logic() - - self.route_row_addr_dff() - - if self.col_addr_dff: - self.route_col_addr_dff() - - self.route_data_dff() - - def route_clk(self): - """ Route the clock network """ - - # This is the actual input to the SRAM - for port in self.all_ports: - self.copy_layout_pin(self.control_logic_insts[port], "clk", "clk{}".format(port)) - - # Connect all of these clock pins to the clock in the central bus - # This is something like a "spine" clock distribution. The two spines - # are clk_buf and clk_buf_bar - control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf") - control_clk_buf_pos = control_clk_buf_pin.center() - - # This uses a metal2 track to the right (for port0) of the control/row addr DFF - # to route vertically. For port1, it is to the left. - row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk") - if port%2: - control_clk_buf_pos = control_clk_buf_pin.lc() - row_addr_clk_pos = row_addr_clk_pin.lc() - mid1_pos = vector(self.row_addr_dff_insts[port].lx() - self.m2_pitch, - row_addr_clk_pos.y) - else: - control_clk_buf_pos = control_clk_buf_pin.rc() - row_addr_clk_pos = row_addr_clk_pin.rc() - mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch, - row_addr_clk_pos.y) - - # This is the steiner point where the net branches out - clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y) - self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=clk_steiner_pos, - rotate=90) - - # Note, the via to the control logic is taken care of above - self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos]) - - if self.col_addr_dff: - dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") - dff_clk_pos = dff_clk_pin.center() - mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) - self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, clk_steiner_pos]) - - if port in self.write_ports: - data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") - data_dff_clk_pos = data_dff_clk_pin.center() - mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y) - # In some designs, the steiner via will be too close to the mid_pos via - # so make the wire as wide as the contacts - self.add_path("metal2",[mid_pos, clk_steiner_pos], width=max(m2m3.width,m2m3.height)) - self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos]) - - - def route_control_logic(self): - """ Route the outputs from the control logic module """ - for port in self.all_ports: - for signal in self.control_logic_outputs[port]: - # The clock gets routed separately and is not a part of the bank - if "clk" in signal: - continue - src_pin = self.control_logic_insts[port].get_pin(signal) - dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) - self.connect_rail_from_left_m2m3(src_pin, dest_pin) - self.add_via_center(layers=("metal1","via1","metal2"), - offset=src_pin.rc(), - rotate=90) - - - def route_row_addr_dff(self): - """ Connect the output of the row flops to the bank pins """ - for port in self.all_ports: - for bit in range(self.row_addr_size): - flop_name = "dout_{}".format(bit) - bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size) - flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name) - bank_pin = self.bank_inst.get_pin(bank_name) - flop_pos = flop_pin.center() - bank_pos = bank_pin.center() - mid_pos = vector(bank_pos.x,flop_pos.y) - self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos]) - self.add_via_center(layers=("metal2","via2","metal3"), - offset=flop_pos, - rotate=90) - - def route_col_addr_dff(self): - """ Connect the output of the row flops to the bank pins """ - for port in self.all_ports: - bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)] - col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1", - pitch=self.m1_pitch, - offset=self.col_addr_dff_insts[port].ul() + vector(0, self.m1_pitch), - names=bus_names, - length=self.col_addr_dff_insts[port].width) - - dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] - data_dff_map = zip(dff_names, bus_names) - self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_insts[port], col_addr_bus_offsets) - - bank_names = ["addr{0}_{1}".format(port,x) for x in range(self.col_addr_size)] - data_bank_map = zip(bank_names, bus_names) - self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets) - - - def route_data_dff(self): - """ Connect the output of the data flops to the write driver """ - # This is where the channel will start (y-dimension at least) - for port in self.write_ports: - if port%2: - offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) - else: - offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) - - - dff_names = ["dout_{}".format(x) for x in range(self.word_size)] - dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] - - bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)] - bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] - - route_map = list(zip(bank_pins, dff_pins)) - self.create_horizontal_channel_route(route_map, offset) - - - - def add_lvs_correspondence_points(self): - """ - This adds some points for easier debugging if LVS goes wrong. - These should probably be turned off by default though, since extraction - will show these as ports in the extracted netlist. - """ - - for n in self.control_logic_outputs[0]: - pin = self.control_logic_insts[0].get_pin(n) - self.add_label(text=n, - layer=pin.layer, - offset=pin.center()) diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 7b420a6b..0083841d 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -1,7 +1,12 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import debug from globals import OPTS -from importlib import reload - class sram_factory: """ @@ -39,40 +44,57 @@ class sram_factory: if hasattr(OPTS, module_type): # Retrieve the name from OPTS if it exists, # otherwise just use the name - module_name = getattr(OPTS, module_type) - else: - module_name = module_type - + module_type = getattr(OPTS, module_type) + # Either retrieve the already loaded module or load it try: mod = self.modules[module_type] except KeyError: - c = reload(__import__(module_name)) - mod = getattr(c, module_name) + import importlib + c = importlib.reload(__import__(module_type)) + mod = getattr(c, module_type) self.modules[module_type] = mod self.module_indices[module_type] = 0 self.objects[module_type] = [] # Either retreive a previous object or create a new one + #print("new",kwargs) for obj in self.objects[module_type]: (obj_kwargs, obj_item) = obj # Must have the same dictionary exactly (conservative) if obj_kwargs == kwargs: - #debug.info(1, "Existing module: type={0} name={1} kwargs={2}".format(module_type, obj_item.name, str(kwargs))) + #debug.info(0, "Existing module: type={0} name={1} kwargs={2}".format(module_type, obj_item.name, str(kwargs))) return obj_item + #else: + # print("obj",obj_kwargs) # Use the default name if there are default arguments # This is especially for library cells so that the spice and gds files can be found. if len(kwargs)>0: # Create a unique name and increment the index - module_name = "{0}_{1}".format(module_name, self.module_indices[module_type]) + module_name = "{0}_{1}".format(module_type, self.module_indices[module_type]) self.module_indices[module_type] += 1 - #debug.info(1, "New module: type={0} name={1} kwargs={2}".format(module_type,module_name,str(kwargs))) + else: + module_name = module_type + + #debug.info(0, "New module: type={0} name={1} kwargs={2}".format(module_type,module_name,str(kwargs))) obj = mod(name=module_name,**kwargs) self.objects[module_type].append((kwargs,obj)) return obj - + def get_mods(self, module_type): + """Returns list of all objects of module name's type.""" + if hasattr(OPTS, module_type): + # Retrieve the name from OPTS if it exists, + # otherwise just use the input + module_type = getattr(OPTS, module_type) + try: + mod_tuples = self.objects[module_type] + mods = [mod for kwargs,mod in mod_tuples] + except KeyError: + mods = [] + return mods + # Make a factory factory = sram_factory() diff --git a/compiler/tests/00_code_format_check_test.py b/compiler/tests/00_code_format_check_test.py index 98799ee8..571f4be7 100755 --- a/compiler/tests/00_code_format_check_test.py +++ b/compiler/tests/00_code_format_check_test.py @@ -1,9 +1,16 @@ #!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os,re -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals import debug @@ -44,7 +51,7 @@ def setup_files(path): files = [] for (dir, _, current_files) in os.walk(path): for f in current_files: - files.append(os.path.join(dir, f)) + files.append(os.getenv("OPENRAM_HOME")) nametest = re.compile("\.py$", re.IGNORECASE) select_files = list(filter(nametest.search, files)) return select_files @@ -115,4 +122,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/01_library_drc_test.py b/compiler/tests/01_library_drc_test.py index 046e6378..f8da685b 100755 --- a/compiler/tests/01_library_drc_test.py +++ b/compiler/tests/01_library_drc_test.py @@ -1,10 +1,15 @@ #!/usr/bin/env python3 -"Run a regression test the library cells for DRC" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os,re -#sys.path.append(os.path.join(sys.path[0],"..")) +#sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug @@ -12,7 +17,7 @@ import debug class library_drc_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) import verify (gds_dir, gds_files) = setup_files() @@ -44,5 +49,5 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index 4ec40dc7..ad150a2b 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -1,10 +1,15 @@ #!/usr/bin/env python3 -"Run a regression test the library cells for LVS" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os,re -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug @@ -12,7 +17,7 @@ import debug class library_lvs_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) import verify (gds_dir, sp_dir, allnames) = setup_files() @@ -67,4 +72,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index 33aa45ae..3d7254c3 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -1,42 +1,61 @@ #!/usr/bin/env python3 -"Run a regression test for DRC on basic contacts of different array sizes" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class contact_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import contact + globals.init_openram("config_{0}".format(OPTS.tech_name)) - - for layer_stack in [("poly", "contact", "metal1"), ("metal1", "via1", "metal2")]: + for layer_stack in [("metal1", "via1", "metal2"), ("poly", "contact", "metal1")]: stack_name = ":".join(map(str, layer_stack)) # Check single 1 x 1 contact" debug.info(2, "1 x 1 {} test".format(stack_name)) - c = contact.contact(layer_stack, (1, 1)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1)) + self.local_drc_check(c) + + # Check single 1 x 1 contact" + debug.info(2, "1 x 1 {} test".format(stack_name)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1), directions=("H","V")) self.local_drc_check(c) + # Check single x 1 contact" + debug.info(2, "1 x 1 {} test".format(stack_name)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1), directions=("H","H")) + self.local_drc_check(c) + + # Check single 1 x 1 contact" + debug.info(2, "1 x 1 {} test".format(stack_name)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1), directions=("V","V")) + self.local_drc_check(c) + # check vertical array with one in the middle and two ends debug.info(2, "1 x 3 {} test".format(stack_name)) - c = contact.contact(layer_stack, (1, 3)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 3)) self.local_drc_check(c) # check horizontal array with one in the middle and two ends debug.info(2, "3 x 1 {} test".format(stack_name)) - c = contact.contact(layer_stack, (3, 1)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(3, 1)) self.local_drc_check(c) # check 3x3 array for all possible neighbors debug.info(2, "3 x 3 {} test".format(stack_name)) - c = contact.contact(layer_stack, (3, 3)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(3, 3)) self.local_drc_check(c) globals.end_openram() @@ -48,4 +67,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/03_path_test.py b/compiler/tests/03_path_test.py index f0fc2299..21001718 100755 --- a/compiler/tests/03_path_test.py +++ b/compiler/tests/03_path_test.py @@ -1,10 +1,15 @@ #!/usr/bin/env python3 -"Run a regression test on a basic path" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug @@ -12,7 +17,7 @@ import debug class path_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) import wire_path import tech import design @@ -89,4 +94,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/03_ptx_1finger_nmos_test.py b/compiler/tests/03_ptx_1finger_nmos_test.py index 9a81810e..f436d7d0 100755 --- a/compiler/tests/03_ptx_1finger_nmos_test.py +++ b/compiler/tests/03_ptx_1finger_nmos_test.py @@ -1,25 +1,31 @@ #!/usr/bin/env python3 -"Run a regression test on a basic parameterized transistors" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug -class ptx_test(openram_test): +class ptx_1finger_nmos_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import ptx + globals.init_openram("config_{0}".format(OPTS.tech_name)) import tech debug.info(2, "Checking min size NMOS with 1 finger") - fet = ptx.ptx(width=tech.drc["minwidth_tx"], - mults=1, - tx_type="nmos") + fet = factory.create(module_type="ptx", + width=tech.drc["minwidth_tx"], + mults=1, + tx_type="nmos") self.local_drc_check(fet) globals.end_openram() @@ -30,4 +36,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/03_ptx_1finger_pmos_test.py b/compiler/tests/03_ptx_1finger_pmos_test.py index a3ed99ff..ae8078e7 100755 --- a/compiler/tests/03_ptx_1finger_pmos_test.py +++ b/compiler/tests/03_ptx_1finger_pmos_test.py @@ -1,25 +1,31 @@ #!/usr/bin/env python3 -"Run a regression test on a basic parameterized transistors" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug -class ptx_test(openram_test): +class ptx_1finger_pmos_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import ptx + globals.init_openram("config_{0}".format(OPTS.tech_name)) import tech debug.info(2, "Checking min size PMOS with 1 finger") - fet = ptx.ptx(width=tech.drc["minwidth_tx"], - mults=1, - tx_type="pmos") + fet = factory.create(module_type="ptx", + width=tech.drc["minwidth_tx"], + mults=1, + tx_type="pmos") self.local_drc_check(fet) globals.end_openram() @@ -30,4 +36,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/03_ptx_3finger_nmos_test.py b/compiler/tests/03_ptx_3finger_nmos_test.py index e1febdbc..c010a948 100755 --- a/compiler/tests/03_ptx_3finger_nmos_test.py +++ b/compiler/tests/03_ptx_3finger_nmos_test.py @@ -1,27 +1,33 @@ #!/usr/bin/env python3 -"Run a regression test on a basic parameterized transistors" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug -class ptx_test(openram_test): +class ptx_3finger_nmos_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import ptx + globals.init_openram("config_{0}".format(OPTS.tech_name)) import tech debug.info(2, "Checking three fingers NMOS") - fet = ptx.ptx(width=tech.drc["minwidth_tx"], - mults=3, - tx_type="nmos", - connect_active=True, - connect_poly=True) + fet = factory.create(module_type="ptx", + width=tech.drc["minwidth_tx"], + mults=3, + tx_type="nmos", + connect_active=True, + connect_poly=True) self.local_drc_check(fet) globals.end_openram() @@ -32,4 +38,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/03_ptx_3finger_pmos_test.py b/compiler/tests/03_ptx_3finger_pmos_test.py index af9a5d42..85cca9e2 100755 --- a/compiler/tests/03_ptx_3finger_pmos_test.py +++ b/compiler/tests/03_ptx_3finger_pmos_test.py @@ -1,27 +1,33 @@ #!/usr/bin/env python3 -"Run a regression test on a basic parameterized transistors" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug -class ptx_test(openram_test): +class ptx_3finger_pmos_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import ptx + globals.init_openram("config_{0}".format(OPTS.tech_name)) import tech debug.info(2, "Checking three fingers PMOS") - fet = ptx.ptx(width=tech.drc["minwidth_tx"], - mults=3, - tx_type="pmos", - connect_active=True, - connect_poly=True) + fet = factory.create(module_type="ptx", + width=tech.drc["minwidth_tx"], + mults=3, + tx_type="pmos", + connect_active=True, + connect_poly=True) self.local_drc_check(fet) globals.end_openram() @@ -32,4 +38,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/03_ptx_4finger_nmos_test.py b/compiler/tests/03_ptx_4finger_nmos_test.py index 08a20898..4a410d51 100755 --- a/compiler/tests/03_ptx_4finger_nmos_test.py +++ b/compiler/tests/03_ptx_4finger_nmos_test.py @@ -1,27 +1,33 @@ #!/usr/bin/env python3 -"Run a regression test on a basic parameterized transistors" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug -class ptx_test(openram_test): +class ptx_4finger_nmos_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import ptx + globals.init_openram("config_{0}".format(OPTS.tech_name)) import tech debug.info(2, "Checking three fingers NMOS") - fet = ptx.ptx(width=tech.drc["minwidth_tx"], - mults=4, - tx_type="nmos", - connect_active=True, - connect_poly=True) + fet = factory.create(module_type="ptx", + width= tech.drc["minwidth_tx"], + mults=4, + tx_type="nmos", + connect_active=True, + connect_poly=True) self.local_drc_check(fet) globals.end_openram() @@ -32,4 +38,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/03_ptx_4finger_pmos_test.py b/compiler/tests/03_ptx_4finger_pmos_test.py index 01857eda..34fbaf9f 100755 --- a/compiler/tests/03_ptx_4finger_pmos_test.py +++ b/compiler/tests/03_ptx_4finger_pmos_test.py @@ -1,27 +1,33 @@ #!/usr/bin/env python3 -"Run a regression test on a basic parameterized transistors" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class ptx_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import ptx + globals.init_openram("config_{0}".format(OPTS.tech_name)) import tech debug.info(2, "Checking three fingers PMOS") - fet = ptx.ptx(width=tech.drc["minwidth_tx"], - mults=4, - tx_type="pmos", - connect_active=True, - connect_poly=True) + fet = factory.create(module_type="ptx", + width=tech.drc["minwidth_tx"], + mults=4, + tx_type="pmos", + connect_active=True, + connect_poly=True) self.local_drc_check(fet) globals.end_openram() @@ -32,4 +38,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py index 1b18e14b..4ee360ce 100755 --- a/compiler/tests/03_wire_test.py +++ b/compiler/tests/03_wire_test.py @@ -1,10 +1,15 @@ #!/usr/bin/env python3 -"Run a regression test on a basic wire" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug @@ -12,7 +17,7 @@ import debug class wire_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) import wire import tech import design @@ -126,4 +131,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_dummy_pbitcell_test.py b/compiler/tests/04_dummy_pbitcell_test.py new file mode 100755 index 00000000..e8e3cc1c --- /dev/null +++ b/compiler/tests/04_dummy_pbitcell_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class replica_pbitcell_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + import dummy_pbitcell + + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + + factory.reset() + debug.info(2, "Checking dummy bitcell using pbitcell (small cell)") + tx = dummy_pbitcell.dummy_pbitcell(name="rpbc") + self.local_check(tx) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + factory.reset() + debug.info(2, "Checking dummy bitcell using pbitcell (large cell)") + tx = dummy_pbitcell.dummy_pbitcell(name="rpbc") + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pand2_test.py b/compiler/tests/04_pand2_test.py index 91b3458e..42045b43 100755 --- a/compiler/tests/04_pand2_test.py +++ b/compiler/tests/04_pand2_test.py @@ -1,20 +1,24 @@ #!/usr/bin/env python3 -""" -Run a regression test on a pand2 cell -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pand2_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) global verify import verify @@ -31,4 +35,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pand3_test.py b/compiler/tests/04_pand3_test.py new file mode 100755 index 00000000..4408f6e8 --- /dev/null +++ b/compiler/tests/04_pand3_test.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class pand3_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + global verify + import verify + + import pand3 + + debug.info(2, "Testing pand3 gate 4x") + a = pand3.pand3(name="pand3x4", size=4) + self.local_check(a) + + 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(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index c78b3284..0f14c4c5 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -1,12 +1,15 @@ #!/usr/bin/env python3 -""" -Run regresion tests on a parameterized bitcell -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug @@ -16,15 +19,14 @@ from sram_factory import factory class pbitcell_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from pbitcell import pbitcell + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.num_rw_ports=1 OPTS.num_w_ports=1 OPTS.num_r_ports=1 factory.reset() debug.info(2, "Bitcell with 1 of each port: read/write, write, and read") - tx = pbitcell(name="pbc") + tx = factory.create(module_type="pbitcell") self.local_check(tx) OPTS.num_rw_ports=0 @@ -32,7 +34,7 @@ class pbitcell_test(openram_test): OPTS.num_r_ports=1 factory.reset() debug.info(2, "Bitcell with 0 read/write ports") - tx = pbitcell(name="pbc") + tx = factory.create(module_type="pbitcell") self.local_check(tx) OPTS.num_rw_ports=1 @@ -40,7 +42,7 @@ class pbitcell_test(openram_test): OPTS.num_r_ports=1 factory.reset() debug.info(2, "Bitcell with 0 write ports") - tx = pbitcell(name="pbc") + tx = factory.create(module_type="pbitcell") self.local_check(tx) OPTS.num_rw_ports=1 @@ -48,7 +50,7 @@ class pbitcell_test(openram_test): OPTS.num_r_ports=0 factory.reset() debug.info(2, "Bitcell with 0 read ports") - tx = pbitcell(name="pbc") + tx = factory.create(module_type="pbitcell") self.local_check(tx) OPTS.num_rw_ports=1 @@ -56,7 +58,7 @@ class pbitcell_test(openram_test): OPTS.num_r_ports=0 factory.reset() debug.info(2, "Bitcell with 0 read ports and 0 write ports") - tx = pbitcell(name="pbc") + tx = factory.create(module_type="pbitcell") self.local_check(tx) OPTS.num_rw_ports=2 @@ -64,7 +66,7 @@ class pbitcell_test(openram_test): OPTS.num_r_ports=2 factory.reset() debug.info(2, "Bitcell with 2 of each port: read/write, write, and read") - tx = pbitcell(name="pbc") + tx = factory.create(module_type="pbitcell") self.local_check(tx) OPTS.num_rw_ports=0 @@ -72,7 +74,7 @@ class pbitcell_test(openram_test): OPTS.num_r_ports=2 factory.reset() debug.info(2, "Bitcell with 0 read/write ports") - tx = pbitcell(name="pbc") + tx = factory.create(module_type="pbitcell") self.local_check(tx) OPTS.num_rw_ports=2 @@ -80,7 +82,7 @@ class pbitcell_test(openram_test): OPTS.num_r_ports=2 factory.reset() debug.info(2, "Bitcell with 0 write ports") - tx = pbitcell(name="pbc") + tx = factory.create(module_type="pbitcell") self.local_check(tx) OPTS.num_rw_ports=2 @@ -88,7 +90,7 @@ class pbitcell_test(openram_test): OPTS.num_r_ports=0 factory.reset() debug.info(2, "Bitcell with 0 read ports") - tx = pbitcell(name="pbc") + tx = factory.create(module_type="pbitcell") self.local_check(tx) OPTS.num_rw_ports=2 @@ -96,7 +98,7 @@ class pbitcell_test(openram_test): OPTS.num_r_ports=0 factory.reset() debug.info(2, "Bitcell with 0 read ports and 0 write ports") - tx = pbitcell(name="pbc") + tx = factory.create(module_type="pbitcell") self.local_check(tx) globals.end_openram() @@ -108,4 +110,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pbuf_test.py b/compiler/tests/04_pbuf_test.py index ed5b8627..35db8ccf 100755 --- a/compiler/tests/04_pbuf_test.py +++ b/compiler/tests/04_pbuf_test.py @@ -1,27 +1,27 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 2-row buffer cell -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pbuf_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - - import pbuf + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Testing inverter/buffer 4x 8x") - a = pbuf.pbuf(name="pbufx8", size=8) + a = factory.create(module_type="pbuf", size=8) self.local_check(a) globals.end_openram() @@ -31,4 +31,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pdriver_test.py b/compiler/tests/04_pdriver_test.py index ba89961f..abaab4a0 100755 --- a/compiler/tests/04_pdriver_test.py +++ b/compiler/tests/04_pdriver_test.py @@ -1,43 +1,43 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 2-row buffer cell -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pdriver_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - - import pdriver + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Testing inverter/buffer 4x 8x") # a tests the error message for specifying conflicting conditions #a = pdriver.pdriver(fanout = 4,size_list = [1,2,4,8]) #self.local_check(a) - b = pdriver.pdriver(name="pdriver1", size_list = [1,2,4,8]) + b = factory.create(module_type="pdriver", size_list = [1,2,4,8]) self.local_check(b) - c = pdriver.pdriver(name="pdriver2", fanout = 50) + c = factory.create(module_type="pdriver", fanout = 50) self.local_check(c) - d = pdriver.pdriver(name="pdriver3", fanout = 50, neg_polarity = True) + d = factory.create(module_type="pdriver", fanout = 50, neg_polarity = True) self.local_check(d) - e = pdriver.pdriver(name="pdriver4", fanout = 64) + e = factory.create(module_type="pdriver", fanout = 64) self.local_check(e) - f = pdriver.pdriver(name="pdriver5", fanout = 64, neg_polarity = True) + f = factory.create(module_type="pdriver", fanout = 64, neg_polarity = True) self.local_check(f) globals.end_openram() @@ -47,4 +47,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pinv_10x_test.py b/compiler/tests/04_pinv_10x_test.py index 42c38ca1..2ccce34a 100755 --- a/compiler/tests/04_pinv_10x_test.py +++ b/compiler/tests/04_pinv_10x_test.py @@ -1,25 +1,27 @@ #!/usr/bin/env python3 -""" -Run regression tests on a parameterized inverter -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pinv_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import pinv - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) - debug.info(2, "Checking 10x inverter") - tx = pinv.pinv(name="pinvx10",size=8) + debug.info(2, "Checking 8x inverter") + tx = factory.create(module_type="pinv", size=8) self.local_check(tx) globals.end_openram() @@ -30,4 +32,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pinv_1x_beta_test.py b/compiler/tests/04_pinv_1x_beta_test.py index 9ac66a65..2f96020c 100755 --- a/compiler/tests/04_pinv_1x_beta_test.py +++ b/compiler/tests/04_pinv_1x_beta_test.py @@ -1,25 +1,27 @@ #!/usr/bin/env python3 -""" -Run regression tests on a parameterized inverter -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pinv_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import pinv - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Checking 1x beta=3 size inverter") - tx = pinv.pinv(name="pinvx1b", size=1, beta=3) + tx = factory.create(module_type="pinv", size=1, beta=3) self.local_check(tx) globals.end_openram() @@ -29,4 +31,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pinv_1x_test.py b/compiler/tests/04_pinv_1x_test.py index 850aa78e..9b0f1bc6 100755 --- a/compiler/tests/04_pinv_1x_test.py +++ b/compiler/tests/04_pinv_1x_test.py @@ -1,24 +1,27 @@ #!/usr/bin/env python3 -""" -Run regression tests on a parameterized inverter -""" +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pinv_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import pinv - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Checking 1x size inverter") - tx = pinv.pinv(name="pinvx1", size=1) + tx = factory.create(module_type="pinv", size=1) self.local_check(tx) globals.end_openram() @@ -28,4 +31,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pinv_2x_test.py b/compiler/tests/04_pinv_2x_test.py index 33950da9..d8a7598f 100755 --- a/compiler/tests/04_pinv_2x_test.py +++ b/compiler/tests/04_pinv_2x_test.py @@ -1,25 +1,27 @@ #!/usr/bin/env python3 -""" -Run regression tests on a parameterized inverter -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pinv_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import pinv - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Checking 2x size inverter") - tx = pinv.pinv(name="pinvx2", size=2) + tx = factory.create(module_type="pinv", size=2) self.local_check(tx) globals.end_openram() @@ -30,4 +32,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pinvbuf_test.py b/compiler/tests/04_pinvbuf_test.py index 53814628..86af0708 100755 --- a/compiler/tests/04_pinvbuf_test.py +++ b/compiler/tests/04_pinvbuf_test.py @@ -1,24 +1,27 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 2-row buffer cell -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pinvbuf_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import pinvbuf + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Testing inverter/buffer 4x 8x") - a = pinvbuf.pinvbuf(name="pinvufx8", size=8) + a = factory.create(module_type="pinvbuf", size=8) self.local_check(a) globals.end_openram() @@ -28,4 +31,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py index cb0b65c6..bc066cfc 100755 --- a/compiler/tests/04_pnand2_test.py +++ b/compiler/tests/04_pnand2_test.py @@ -1,27 +1,27 @@ #!/usr/bin/env python3 -""" -Run regression tests on a parameterized nand 2. This module doesn't -generate a multi_finger 2-input nand gate. It generates only a minimum -size 2-input nand gate. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pnand2_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import pnand2 - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Checking 2-input nand gate") - tx = pnand2.pnand2(name="pnand2", size=1) + tx = factory.create(module_type="pnand2", size=1) self.local_check(tx) globals.end_openram() @@ -32,4 +32,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pnand3_test.py b/compiler/tests/04_pnand3_test.py index f3bbdb73..8bf5098f 100755 --- a/compiler/tests/04_pnand3_test.py +++ b/compiler/tests/04_pnand3_test.py @@ -1,27 +1,27 @@ #!/usr/bin/env python3 -""" -Run regression tests on a parameterized pnand3. -This module doesn't generate a multi-finger 3-input nand gate. -It generates only a minimum size 3-input nand gate. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pnand3_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import pnand3 - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Checking 3-input nand gate") - tx = pnand3.pnand3(name="pnand3", size=1) + tx = factory.create(module_type="pnand3", size=1) self.local_check(tx) globals.end_openram() @@ -32,4 +32,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py index 32214ded..0e524506 100755 --- a/compiler/tests/04_pnor2_test.py +++ b/compiler/tests/04_pnor2_test.py @@ -1,27 +1,27 @@ #!/usr/bin/env python3 -""" -Run regression tests on a parameterized nor 2. This module doesn't -generate a multi_finger 2-input nor gate. It generates only a minimum -size 2-input nor gate. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class pnor2_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import pnor2 - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Checking 2-input nor gate") - tx = pnor2.pnor2(name="pnor2", size=1) + tx = factory.create(module_type="pnor2", size=1) self.local_check(tx) globals.end_openram() @@ -31,4 +31,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index a73595eb..9b2addd5 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -1,27 +1,28 @@ #!/usr/bin/env python3 -""" -Run a regression test on a precharge cell -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -import debug from sram_factory import factory +import debug class precharge_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import precharge - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) # check precharge in single port debug.info(2, "Checking precharge for handmade bitcell") - tx = precharge.precharge(name="precharge_driver", size=1) + tx = factory.create(module_type="precharge", size=1) self.local_check(tx) # check precharge in multi-port @@ -32,15 +33,17 @@ class precharge_test(openram_test): factory.reset() debug.info(2, "Checking precharge for pbitcell (innermost connections)") - tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl0", bitcell_br="br0") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) + factory.reset() debug.info(2, "Checking precharge for pbitcell (innermost connections)") - tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl1", bitcell_br="br1") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1") self.local_check(tx) + factory.reset() debug.info(2, "Checking precharge for pbitcell (outermost connections)") - tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl2", bitcell_br="br2") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl2", bitcell_br="br2") self.local_check(tx) globals.end_openram() @@ -50,4 +53,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_replica_pbitcell_test.py b/compiler/tests/04_replica_pbitcell_test.py index 9a672419..65ce5ecf 100755 --- a/compiler/tests/04_replica_pbitcell_test.py +++ b/compiler/tests/04_replica_pbitcell_test.py @@ -1,21 +1,24 @@ #!/usr/bin/env python3 -""" -Run a regression test on a replica pbitcell -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -import debug from sram_factory import factory +import debug class replica_pbitcell_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) import replica_pbitcell OPTS.bitcell = "pbitcell" @@ -44,4 +47,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_single_level_column_mux_test.py b/compiler/tests/04_single_level_column_mux_test.py index 2a107b9a..3ecbbe9d 100755 --- a/compiler/tests/04_single_level_column_mux_test.py +++ b/compiler/tests/04_single_level_column_mux_test.py @@ -1,29 +1,30 @@ #!/usr/bin/env python3 -""" -Run a regression test on a wordline_driver array -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -import debug from sram_factory import factory +import debug #@unittest.skip("SKIPPING 04_driver_test") class single_level_column_mux_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import single_level_column_mux - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) # check single level column mux in single port debug.info(2, "Checking column mux") - tx = single_level_column_mux.single_level_column_mux(name="mux8", tx_size=8) + tx = factory.create(module_type="single_level_column_mux", tx_size=8) self.local_check(tx) # check single level column mux in multi-port @@ -34,12 +35,12 @@ class single_level_column_mux_test(openram_test): factory.reset() debug.info(2, "Checking column mux for pbitcell (innermost connections)") - tx = single_level_column_mux.single_level_column_mux(name="mux8_2", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) factory.reset() debug.info(2, "Checking column mux for pbitcell (outermost connections)") - tx = single_level_column_mux.single_level_column_mux(name="mux8_3", tx_size=8, bitcell_bl="bl2", bitcell_br="br2") + tx = factory.create(module_type="single_level_column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2") self.local_check(tx) globals.end_openram() @@ -49,4 +50,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/05_bitcell_1rw_1r_array_test.py b/compiler/tests/05_bitcell_1rw_1r_array_test.py index 1223085e..972fb8e6 100755 --- a/compiler/tests/05_bitcell_1rw_1r_array_test.py +++ b/compiler/tests/05_bitcell_1rw_1r_array_test.py @@ -1,14 +1,18 @@ #!/usr/bin/env python3 -""" -Run a regression test on a basic array -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 05_bitcell_1rw_1r_array_test") @@ -16,17 +20,20 @@ import debug class bitcell_1rw_1r_array_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import bitcell_array - debug.info(2, "Testing 4x4 array for cell_1rw_1r") + globals.init_openram("config_{0}".format(OPTS.tech_name)) + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - a = bitcell_array.bitcell_array(name="bitcell_1rw_1r_array", cols=4, rows=4) + + debug.info(2, "Testing 4x4 array for cell_1rw_1r") + a = factory.create(module_type="bitcell_array", cols=4, rows=4) self.local_check(a) - + globals.end_openram() # run the test from the command line @@ -34,4 +41,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index 93668e05..6a561019 100755 --- a/compiler/tests/05_bitcell_array_test.py +++ b/compiler/tests/05_bitcell_array_test.py @@ -1,14 +1,18 @@ #!/usr/bin/env python3 -""" -Run a regression test on a basic array -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 05_array_test") @@ -16,13 +20,12 @@ import debug class array_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import bitcell_array + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Testing 4x4 array for 6t_cell") - a = bitcell_array.bitcell_array(name="bitcell_array", cols=4, rows=4) + a = factory.create(module_type="bitcell_array", cols=4, rows=4) self.local_check(a) - + globals.end_openram() # run the test from the command line @@ -30,4 +33,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/05_dummy_array_test.py b/compiler/tests/05_dummy_array_test.py new file mode 100755 index 00000000..de379a97 --- /dev/null +++ b/compiler/tests/05_dummy_array_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class dummy_row_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + + debug.info(2, "Testing dummy row for 6t_cell") + a = factory.create(module_type="dummy_array", rows=1, cols=4) + self.local_check(a) + + debug.info(2, "Testing dummy column for 6t_cell") + a = factory.create(module_type="dummy_array", rows=4, cols=1) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 4da5bec9..91bf7522 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -1,29 +1,32 @@ #!/usr/bin/env python3 -""" -Run a regression test on a basic array -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 05_pbitcell_array_test") class pbitcell_array_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import bitcell_array + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell") OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 2 OPTS.num_r_ports = 2 OPTS.num_w_ports = 2 - a = bitcell_array.bitcell_array(name="pbitcell_array_Rport_edge", cols=4, rows=4) + a = factory.create(module_type="bitcell_array", cols=4, rows=4) self.local_check(a) debug.info(2, "Testing 4x4 array for multiport bitcell, with write ports at the edge of the bit cell") @@ -31,7 +34,7 @@ class pbitcell_array_test(openram_test): OPTS.num_rw_ports = 2 OPTS.num_r_ports = 0 OPTS.num_w_ports = 2 - a = bitcell_array.bitcell_array(name="pbitcell_array_Wport_edge", cols=4, rows=4) + a = factory.create(module_type="bitcell_array", cols=4, rows=4) self.local_check(a) debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell") @@ -39,7 +42,7 @@ class pbitcell_array_test(openram_test): OPTS.num_rw_ports = 2 OPTS.num_r_ports = 0 OPTS.num_w_ports = 0 - a = bitcell_array.bitcell_array(name="pbitcell_array_RWport_edge", cols=4, rows=4) + a = factory.create(module_type="bitcell_array", cols=4, rows=4) self.local_check(a) globals.end_openram() @@ -49,4 +52,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/05_replica_pbitcell_array_test.py b/compiler/tests/05_replica_pbitcell_array_test.py new file mode 100755 index 00000000..2bc4a0d2 --- /dev/null +++ b/compiler/tests/05_replica_pbitcell_array_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class replica_bitcell_array_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.dummy_bitcell = "dummy_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(2, "Testing 4x4 array for pbitcell") + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0,1]) + self.local_check(a) + + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.dummy_bitcell = "dummy_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + + factory.reset() + debug.info(2, "Testing 4x4 array for pbitcell") + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0]) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 2400d3c2..c349e889 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -1,24 +1,24 @@ #!/usr/bin/env python3 -""" -Run a regression test on a hierarchical_decoder. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -import debug from sram_factory import factory +import debug class hierarchical_decoder_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import hierarchical_decoder - import tech - + globals.init_openram("config_{0}".format(OPTS.tech_name)) # Doesn't require hierarchical decoder # debug.info(1, "Testing 4 row sample for hierarchical_decoder") # a = hierarchical_decoder.hierarchical_decoder(name="hd1, rows=4) @@ -31,19 +31,19 @@ class hierarchical_decoder_test(openram_test): # check hierarchical decoder for single port debug.info(1, "Testing 16 row sample for hierarchical_decoder") - a = hierarchical_decoder.hierarchical_decoder(name="hd3", rows=16) + a = factory.create(module_type="hierarchical_decoder", rows=16) self.local_check(a) debug.info(1, "Testing 32 row sample for hierarchical_decoder") - a = hierarchical_decoder.hierarchical_decoder(name="hd4", rows=32) + a = factory.create(module_type="hierarchical_decoder", rows=32) self.local_check(a) debug.info(1, "Testing 128 row sample for hierarchical_decoder") - a = hierarchical_decoder.hierarchical_decoder(name="hd5", rows=128) + a = factory.create(module_type="hierarchical_decoder", rows=128) self.local_check(a) debug.info(1, "Testing 512 row sample for hierarchical_decoder") - a = hierarchical_decoder.hierarchical_decoder(name="hd6", rows=512) + a = factory.create(module_type="hierarchical_decoder", rows=512) self.local_check(a) # check hierarchical decoder for multi-port @@ -54,19 +54,19 @@ class hierarchical_decoder_test(openram_test): factory.reset() debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") - a = hierarchical_decoder.hierarchical_decoder(name="hd7", rows=16) + a = factory.create(module_type="hierarchical_decoder", rows=16) self.local_check(a) debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)") - a = hierarchical_decoder.hierarchical_decoder(name="hd8", rows=32) + a = factory.create(module_type="hierarchical_decoder", rows=32) self.local_check(a) debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)") - a = hierarchical_decoder.hierarchical_decoder(name="hd9", rows=128) + a = factory.create(module_type="hierarchical_decoder", rows=128) self.local_check(a) debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)") - a = hierarchical_decoder.hierarchical_decoder(name="hd10", rows=512) + a = factory.create(module_type="hierarchical_decoder", rows=512) self.local_check(a) globals.end_openram() @@ -76,4 +76,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index 6fbba350..0a5363ab 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -1,26 +1,28 @@ #!/usr/bin/env python3 -""" -Run a regression test on a hierarchical_predecode2x4. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class hierarchical_predecode2x4_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import hierarchical_predecode2x4 as pre - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) # checking hierarchical precode 2x4 for single port debug.info(1, "Testing sample for hierarchy_predecode2x4") - a = pre.hierarchical_predecode2x4(name="pre1") + a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) # checking hierarchical precode 2x4 for multi-port @@ -30,7 +32,7 @@ class hierarchical_predecode2x4_test(openram_test): OPTS.num_r_ports = 0 debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)") - a = pre.hierarchical_predecode2x4(name="pre2") + a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) globals.end_openram() @@ -40,4 +42,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index b704a50d..b2a8d438 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -1,26 +1,28 @@ #!/usr/bin/env python3 -""" -Run a regression test on a hierarchical_predecode3x8. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class hierarchical_predecode3x8_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import hierarchical_predecode3x8 as pre - import tech + globals.init_openram("config_{0}".format(OPTS.tech_name)) # checking hierarchical precode 3x8 for single port debug.info(1, "Testing sample for hierarchy_predecode3x8") - a = pre.hierarchical_predecode3x8(name="pre1") + a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) # checking hierarchical precode 3x8 for multi-port @@ -30,7 +32,7 @@ class hierarchical_predecode3x8_test(openram_test): OPTS.num_r_ports = 0 debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)") - a = pre.hierarchical_predecode3x8(name="pre2") + a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) globals.end_openram() @@ -40,4 +42,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index 8cc16f56..c6cd7ed2 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -1,33 +1,36 @@ #!/usr/bin/env python3 -""" -Run a regression test on a single transistor column_mux. -""" - -from testutils import header,openram_test,unittest +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -import debug from sram_factory import factory +import debug class single_level_column_mux_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) import single_level_column_mux_array # check single level column mux array in single port debug.info(1, "Testing sample for 2-way column_mux_array") - a = single_level_column_mux_array.single_level_column_mux_array(name="mux1", columns=16, word_size=8) + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8) self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array") - a = single_level_column_mux_array.single_level_column_mux_array(name="mux2", columns=16, word_size=4) + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4) self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array") - a = single_level_column_mux_array.single_level_column_mux_array(name="mux3", columns=32, word_size=4) + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4) self.local_check(a) # check single level column mux array in multi-port @@ -38,19 +41,19 @@ class single_level_column_mux_test(openram_test): factory.reset() debug.info(1, "Testing sample for 2-way column_mux_array in multi-port") - a = single_level_column_mux_array.single_level_column_mux_array(name="mux4", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array in multi-port") - a = single_level_column_mux_array.single_level_column_mux_array(name="mux5", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)") - a = single_level_column_mux_array.single_level_column_mux_array(name="mux6", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)") - a = single_level_column_mux_array.single_level_column_mux_array(name="mux7", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2") + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2") self.local_check(a) globals.end_openram() @@ -61,4 +64,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index c31f133a..ee29211b 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -1,26 +1,28 @@ #!/usr/bin/env python3 -""" -Run a regression test on a precharge array -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -import debug from sram_factory import factory +import debug class precharge_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import precharge_array + globals.init_openram("config_{0}".format(OPTS.tech_name)) # check precharge array in single port debug.info(2, "Checking 3 column precharge") - pc = precharge_array.precharge_array(name="pre1", columns=3) + pc = factory.create(module_type="precharge_array", columns=3) self.local_check(pc) # check precharge array in multi-port @@ -31,7 +33,7 @@ class precharge_test(openram_test): factory.reset() debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") - pc = precharge_array.precharge_array(name="pre2", columns=3, bitcell_bl="bl0", bitcell_br="br0") + pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0") self.local_check(pc) # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") @@ -49,4 +51,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/08_wordline_driver_test.py index bc69c776..31415a6c 100755 --- a/compiler/tests/08_wordline_driver_test.py +++ b/compiler/tests/08_wordline_driver_test.py @@ -1,28 +1,30 @@ #!/usr/bin/env python3 -""" -Run a regression test on a wordline_driver array -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -import debug from sram_factory import factory +import debug #@unittest.skip("SKIPPING 04_driver_test") class wordline_driver_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import wordline_driver + globals.init_openram("config_{0}".format(OPTS.tech_name)) # check wordline driver for single port debug.info(2, "Checking driver") - tx = wordline_driver.wordline_driver(name="wld1", rows=8, cols=32) + tx = factory.create(module_type="wordline_driver", rows=8, cols=32) self.local_check(tx) # check wordline driver for multi-port @@ -33,7 +35,7 @@ class wordline_driver_test(openram_test): factory.reset() debug.info(2, "Checking driver (multi-port case)") - tx = wordline_driver.wordline_driver(name="wld2", rows=8, cols=64) + tx = factory.create(module_type="wordline_driver", rows=8, cols=64) self.local_check(tx) globals.end_openram() @@ -43,4 +45,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index c144b12b..e35ea3c3 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -1,30 +1,32 @@ #!/usr/bin/env python3 -""" -Run a regression test on a sense amp array -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -import debug from sram_factory import factory +import debug class sense_amp_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import sense_amp_array + globals.init_openram("config_{0}".format(OPTS.tech_name)) # check sense amp array for single port debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") - a = sense_amp_array.sense_amp_array(name="sa1", word_size=4, words_per_row=2) + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2) self.local_check(a) debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4") - a = sense_amp_array.sense_amp_array(name="sa2", word_size=4, words_per_row=4) + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4) self.local_check(a) # check sense amp array for multi-port @@ -35,11 +37,11 @@ class sense_amp_test(openram_test): factory.reset() debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)") - a = sense_amp_array.sense_amp_array(name="sa3", word_size=4, words_per_row=2) + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2) self.local_check(a) debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)") - a = sense_amp_array.sense_amp_array(name="sa4", word_size=4, words_per_row=4) + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4) self.local_check(a) globals.end_openram() @@ -49,4 +51,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py index 98507b60..20dacca6 100755 --- a/compiler/tests/10_write_driver_array_test.py +++ b/compiler/tests/10_write_driver_array_test.py @@ -1,30 +1,32 @@ #!/usr/bin/env python3 -""" -Run a regression test on a write driver array -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -import debug from sram_factory import factory +import debug class write_driver_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import write_driver_array + globals.init_openram("config_{0}".format(OPTS.tech_name)) # check write driver array for single port debug.info(2, "Testing write_driver_array for columns=8, word_size=8") - a = write_driver_array.write_driver_array(name="wd1", columns=8, word_size=8) + a = factory.create(module_type="write_driver_array", columns=8, word_size=8) self.local_check(a) debug.info(2, "Testing write_driver_array for columns=16, word_size=8") - a = write_driver_array.write_driver_array(name="wd2", columns=16, word_size=8) + a = factory.create(module_type="write_driver_array", columns=16, word_size=8) self.local_check(a) # check write driver array for multi-port @@ -35,11 +37,11 @@ class write_driver_test(openram_test): factory.reset() debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)") - a = write_driver_array.write_driver_array(name="wd3", columns=8, word_size=8) + a = factory.create(module_type="write_driver_array", columns=8, word_size=8) self.local_check(a) debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case)") - a = write_driver_array.write_driver_array(name="wd4", columns=16, word_size=8) + a = factory.create(module_type="write_driver_array", columns=16, word_size=8) self.local_check(a) globals.end_openram() @@ -49,4 +51,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/10_write_driver_array_wmask_test.py b/compiler/tests/10_write_driver_array_wmask_test.py new file mode 100755 index 00000000..d09286b5 --- /dev/null +++ b/compiler/tests/10_write_driver_array_wmask_test.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class write_driver_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + + # check write driver array for single port + debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4") + a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=16, write_size=2") + a = factory.create(module_type="write_driver_array", columns=16, word_size=16, write_size=2) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=8, write_size=4") + a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4) + self.local_check(a) + + # check write driver array for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4 (multi-port case)") + a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=8, write_size=4 (multi-port case)") + a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) \ No newline at end of file diff --git a/compiler/tests/10_write_mask_and_array_test.py b/compiler/tests/10_write_mask_and_array_test.py new file mode 100755 index 00000000..91155467 --- /dev/null +++ b/compiler/tests/10_write_mask_and_array_test.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class write_mask_and_array_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + + # check write driver array for single port + debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4") + a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4) + self.local_check(a) + + debug.info(2, "Testing write_mask_and_array for columns=16, word_size=16, write_size=4") + a = factory.create(module_type="write_mask_and_array", columns=16, word_size=16, write_size=4) + self.local_check(a) + + debug.info(2, "Testing write_mask_and_array for columns=16, word_size=8, write_size=2") + a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2) + self.local_check(a) + + # check write driver array for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4 (multi-port case)") + a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4) + self.local_check(a) + + debug.info(2, "Testing write_mask_and_array for columns=16, word_size=8, write_size=2 (multi-port case)") + a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/11_dff_array_test.py b/compiler/tests/11_dff_array_test.py index eed41dda..b843a6bb 100755 --- a/compiler/tests/11_dff_array_test.py +++ b/compiler/tests/11_dff_array_test.py @@ -1,32 +1,35 @@ #!/usr/bin/env python3 -""" -Run a regression test on a dff_array. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class dff_array_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import dff_array + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Testing dff_array for 3x3") - a = dff_array.dff_array(rows=3, columns=3) + a = factory.create(module_type="dff_array", rows=3, columns=3) self.local_check(a) debug.info(2, "Testing dff_array for 1x3") - a = dff_array.dff_array(rows=1, columns=3) + a = factory.create(module_type="dff_array", rows=1, columns=3) self.local_check(a) debug.info(2, "Testing dff_array for 3x1") - a = dff_array.dff_array(rows=3, columns=1) + a = factory.create(module_type="dff_array", rows=3, columns=1) self.local_check(a) globals.end_openram() @@ -36,4 +39,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/11_dff_buf_array_test.py b/compiler/tests/11_dff_buf_array_test.py index d2932cac..ec0e7742 100755 --- a/compiler/tests/11_dff_buf_array_test.py +++ b/compiler/tests/11_dff_buf_array_test.py @@ -1,32 +1,35 @@ #!/usr/bin/env python3 -""" -Run a regression test on a dff_array. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class dff_buf_array_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import dff_buf_array + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Testing dff_buf_array for 3x3") - a = dff_buf_array.dff_buf_array(rows=3, columns=3) + a = factory.create(module_type="dff_buf_array", rows=3, columns=3) self.local_check(a) debug.info(2, "Testing dff_buf_array for 1x3") - a = dff_buf_array.dff_buf_array(rows=1, columns=3) + a = factory.create(module_type="dff_buf_array", rows=1, columns=3) self.local_check(a) debug.info(2, "Testing dff_buf_array for 3x1") - a = dff_buf_array.dff_buf_array(rows=3, columns=1) + a = factory.create(module_type="dff_buf_array", rows=3, columns=1) self.local_check(a) globals.end_openram() @@ -36,4 +39,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/11_dff_buf_test.py b/compiler/tests/11_dff_buf_test.py index c9c25f16..161deaa2 100755 --- a/compiler/tests/11_dff_buf_test.py +++ b/compiler/tests/11_dff_buf_test.py @@ -1,24 +1,27 @@ #!/usr/bin/env python3 -""" -Run a regression test on a dff_buf. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class dff_buf_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import dff_buf + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Testing dff_buf 4x 8x") - a = dff_buf.dff_buf(4, 8) + a = factory.create(module_type="dff_buf", inv1_size=4, inv2_size=8) self.local_check(a) globals.end_openram() @@ -28,4 +31,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/12_tri_gate_array_test.py b/compiler/tests/12_tri_gate_array_test.py index cb789155..0bd5f60c 100755 --- a/compiler/tests/12_tri_gate_array_test.py +++ b/compiler/tests/12_tri_gate_array_test.py @@ -1,28 +1,31 @@ #!/usr/bin/env python3 -""" -Run a regression test on a tri_gate_array. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class tri_gate_array_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import tri_gate_array + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(1, "Testing tri_gate_array for columns=8, word_size=8") - a = tri_gate_array.tri_gate_array(columns=8, word_size=8) + a = factory.create(module_type="tri_gate_array", columns=8, word_size=8) self.local_check(a) - + debug.info(1, "Testing tri_gate_array for columns=16, word_size=8") - a = tri_gate_array.tri_gate_array(columns=16, word_size=8) + a = factory.create(module_type="tri_gate_array", columns=16, word_size=8) self.local_check(a) globals.end_openram() @@ -32,4 +35,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/13_delay_chain_test.py b/compiler/tests/13_delay_chain_test.py index bfe2b3ff..9dc8faeb 100755 --- a/compiler/tests/13_delay_chain_test.py +++ b/compiler/tests/13_delay_chain_test.py @@ -1,24 +1,27 @@ #!/usr/bin/env python3 -""" -Run a test on a delay chain -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class delay_chain_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import delay_chain + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(2, "Testing delay_chain") - a = delay_chain.delay_chain(name="dc", fanout_list=[4, 4, 4, 4]) + a = factory.create(module_type="delay_chain", fanout_list=[4, 4, 4, 4]) self.local_check(a) globals.end_openram() @@ -28,4 +31,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py b/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py new file mode 100755 index 00000000..bae7edde --- /dev/null +++ b/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class replica_bitcell_array_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(2, "Testing 4x4 array for cell_1rw_1r") + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0,1]) + self.local_check(a) + + debug.info(2, "Testing 4x4 array for cell_1rw_1r") + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0,1]) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/14_replica_bitcell_array_test.py b/compiler/tests/14_replica_bitcell_array_test.py new file mode 100755 index 00000000..2b446758 --- /dev/null +++ b/compiler/tests/14_replica_bitcell_array_test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class replica_bitcell_array_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + + debug.info(2, "Testing 4x4 array for 6t_cell") + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0]) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/14_replica_bitline_multiport_test.py b/compiler/tests/14_replica_bitline_multiport_test.py deleted file mode 100755 index f379e0d2..00000000 --- a/compiler/tests/14_replica_bitline_multiport_test.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a test on a multiport replica bitline -""" - -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 -from sram_factory import factory - -class replica_bitline_multiport_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import replica_bitline - - stages=4 - fanout=4 - rows=13 - - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - - factory.reset() - debug.info(2, "Testing 1rw 1r RBL with {0} FO4 stages, {1} rows".format(stages,rows)) - a = replica_bitline.replica_bitline(name="rbl1", delay_fanout_list=stages*[fanout], bitcell_loads=rows) - self.local_check(a) - - # check replica bitline in pbitcell multi-port - OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell = "replica_pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - factory.reset() - debug.info(2, "Testing RBL pbitcell 1rw with {0} FO4 stages, {1} rows".format(stages,rows)) - a = replica_bitline.replica_bitline(name="rbl2", delay_fanout_list=stages*[fanout], bitcell_loads=rows) - self.local_check(a) - - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 1 - OPTS.num_r_ports = 1 - - factory.reset() - debug.info(2, "Testing RBL pbitcell 1rw 1w 1r with {0} FO4 stages, {1} rows".format(stages,rows)) - a = replica_bitline.replica_bitline(name="rbl3", delay_fanout_list=stages*[fanout], bitcell_loads=rows) - self.local_check(a) - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/14_replica_bitline_test.py b/compiler/tests/14_replica_bitline_test.py deleted file mode 100755 index ca213d5c..00000000 --- a/compiler/tests/14_replica_bitline_test.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -""" -Run a test on a replica bitline -""" - -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 - -class replica_bitline_test(openram_test): - - def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import replica_bitline - - # check replica bitline in single port - stages=4 - fanout=4 - rows=13 - debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) - a = replica_bitline.replica_bitline(name="rbl1", delay_fanout_list=stages*[fanout], bitcell_loads=rows) - self.local_check(a) - #debug.error("Exiting...", 1) - - stages=8 - rows=100 - debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows)) - a = replica_bitline.replica_bitline(name="rbl2", delay_fanout_list=stages*[fanout], bitcell_loads=rows) - self.local_check(a) - - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/14_replica_column_test.py b/compiler/tests/14_replica_column_test.py new file mode 100755 index 00000000..c0db4d17 --- /dev/null +++ b/compiler/tests/14_replica_column_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class replica_column_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + + debug.info(2, "Testing replica column for 6t_cell") + a = factory.create(module_type="replica_column", rows=4, left_rbl=1, right_rbl=0, replica_bit=1) + self.local_check(a) + + debug.info(2, "Testing replica column for 6t_cell") + a = factory.create(module_type="replica_column", rows=4, left_rbl=1, right_rbl=1, replica_bit=6) + self.local_check(a) + + debug.info(2, "Testing replica column for 6t_cell") + a = factory.create(module_type="replica_column", rows=4, left_rbl=2, right_rbl=0, replica_bit=2) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/16_control_logic_multiport_test.py b/compiler/tests/16_control_logic_multiport_test.py new file mode 100755 index 00000000..66c34d24 --- /dev/null +++ b/compiler/tests/16_control_logic_multiport_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +#Copyright (c) 2016-2019 Regents of the University of California and The Board +#of Regents for the Oklahoma Agricultural and Mechanical College +#(acting for and on behalf of Oklahoma State University) +#All rights reserved. +# +""" +Run a regression test on a control_logic +""" + +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 +from sram_factory import factory +import debug + +class control_logic_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + import control_logic + import tech + + # check control logic for multi-port + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + + debug.info(1, "Testing sample for control_logic for multiport, only write control logic") + a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=8, port_type="rw") + self.local_check(a) + + # OPTS.num_rw_ports = 0 + # OPTS.num_w_ports = 1 + debug.info(1, "Testing sample for control_logic for multiport, only write control logic") + a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=8, port_type="w") + self.local_check(a) + + # OPTS.num_w_ports = 0 + # OPTS.num_r_ports = 1 + debug.info(1, "Testing sample for control_logic for multiport, only read control logic") + a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=8, port_type="r") + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/16_control_logic_test.py b/compiler/tests/16_control_logic_test.py index e0545af4..92d5c94b 100755 --- a/compiler/tests/16_control_logic_test.py +++ b/compiler/tests/16_control_logic_test.py @@ -1,65 +1,42 @@ #!/usr/bin/env python3 -""" -Run a regression test on a control_logic -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class control_logic_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) import control_logic import tech - # check control logic for single port - debug.info(1, "Testing sample for control_logic") - a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=32) - self.local_check(a) - - # check control logic for multi-port - OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell = "replica_pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - debug.info(1, "Testing sample for control_logic for multiport") - a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=8) - self.local_check(a) - - # Check port specific control logic - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - debug.info(1, "Testing sample for control_logic for multiport, only write control logic") - a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=8, port_type="rw") - self.local_check(a) - - OPTS.num_rw_ports = 0 - OPTS.num_w_ports = 1 - debug.info(1, "Testing sample for control_logic for multiport, only write control logic") - a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=8, port_type="w") - self.local_check(a) - - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 1 - debug.info(1, "Testing sample for control_logic for multiport, only read control logic") - a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=8, port_type="r") + debug.info(1, "Testing sample for control_logic_rw") + a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32) self.local_check(a) - globals.end_openram() + debug.info(1, "Testing sample for control_logic_r") + a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="r") + self.local_check(a) + + debug.info(1, "Testing sample for control_logic_w") + a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="w") + self.local_check(a) # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_address_test.py b/compiler/tests/18_port_address_test.py new file mode 100755 index 00000000..c8db6ec2 --- /dev/null +++ b/compiler/tests/18_port_address_test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class port_address_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + + debug.info(1, "Port address 16 rows") + a = factory.create("port_address", cols=16, rows=16) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_test.py b/compiler/tests/18_port_data_test.py new file mode 100755 index 00000000..e5f94329 --- /dev/null +++ b/compiler/tests/18_port_data_test.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class port_data_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from sram_config import sram_config + + c = sram_config(word_size=4, + num_words=16) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + OPTS.bitcell = "bitcell_1w_1r" + OPTS.num_rw_ports = 0 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + c.num_words=16 + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_wmask_test.py b/compiler/tests/18_port_data_wmask_test.py new file mode 100755 index 00000000..e9b70337 --- /dev/null +++ b/compiler/tests/18_port_data_wmask_test.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from sram_config import sram_config + + c = sram_config(word_size=16, + write_size=4, + num_words=16) + + c.words_per_row = 1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words = 32 + c.words_per_row = 2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words = 64 + c.words_per_row = 4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words = 128 + c.words_per_row = 8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + OPTS.bitcell = "bitcell_1w_1r" + OPTS.num_rw_ports = 0 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + c.num_words = 16 + c.words_per_row = 1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + # + c.num_words = 32 + c.words_per_row = 2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.num_words = 64 + c.words_per_row = 4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.word_size = 8 + c.num_words = 128 + c.words_per_row = 8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_bank_select_test.py b/compiler/tests/19_bank_select_test.py index 1245926b..e2f5a9a8 100755 --- a/compiler/tests/19_bank_select_test.py +++ b/compiler/tests/19_bank_select_test.py @@ -1,29 +1,32 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class bank_select_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import bank_select + globals.init_openram("config_{0}".format(OPTS.tech_name)) debug.info(1, "No column mux, rw control logic") - a = bank_select.bank_select(port="rw") + a = factory.create(module_type="bank_select", port="rw") self.local_check(a) OPTS.bitcell = "pbitcell" debug.info(1, "No column mux, rw control logic") - a = bank_select.bank_select(port="rw") + a = factory.create(module_type="bank_select", port="rw") self.local_check(a) OPTS.num_rw_ports = 0 @@ -31,11 +34,11 @@ class bank_select_test(openram_test): OPTS.num_r_ports = 1 debug.info(1, "No column mux, w control logic") - a = bank_select.bank_select(port="w") + a = factory.create(module_type="bank_select", port="w") self.local_check(a) debug.info(1, "No column mux, r control logic") - a = bank_select.bank_select(port="r") + a = factory.create(module_type="bank_select", port="r") self.local_check(a) globals.end_openram() @@ -45,4 +48,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index 0eff040d..1816cd61 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -1,22 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug @unittest.skip("SKIPPING 19_multi_bank_test") class multi_bank_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from bank import bank + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config c = sram_config(word_size=4, @@ -24,31 +27,35 @@ class multi_bank_test(openram_test): c.num_banks=2 c.words_per_row=1 + factory.reset() c.recompute_sizes() debug.info(1, "No column mux") - a = bank(c, name="bank1_multi") + a = factory.create("bank", sram_config=c) self.local_check(a) c.num_words=32 c.words_per_row=2 + factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") - a = bank(c, name="bank2_multi") + a = factory.create("bank", sram_config=c) self.local_check(a) c.num_words=64 c.words_per_row=4 + factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") - a = bank(c, name="bank3_multi") + a = factory.create("bank", sram_config=c) self.local_check(a) c.word_size=2 c.num_words=128 c.words_per_row=8 + factory.reset() c.recompute_sizes() debug.info(1, "Eight way column mux") - a = bank(c, name="bank4_multi") + a = factory.create("bank", sram_config=c) self.local_check(a) globals.end_openram() @@ -58,4 +65,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_pmulti_bank_test.py b/compiler/tests/19_pmulti_bank_test.py index 32d3917a..749460fa 100755 --- a/compiler/tests/19_pmulti_bank_test.py +++ b/compiler/tests/19_pmulti_bank_test.py @@ -1,22 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug @unittest.skip("SKIPPING 19_pmulti_bank_test") class multi_bank_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from bank import bank + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config OPTS.bitcell = "pbitcell" @@ -29,27 +32,35 @@ class multi_bank_test(openram_test): c.num_banks=2 c.words_per_row=1 + factory.reset() + c.recompute_sizes() debug.info(1, "No column mux") - a = bank(c, name="bank1_multi") + a = factory.create("bank", sram_config=c) self.local_check(a) c.num_words=32 c.words_per_row=2 + factory.reset() + c.recompute_sizes() debug.info(1, "Two way column mux") - a = bank(c, name="bank2_multi") + a = factory.create("bank", sram_config=c) self.local_check(a) c.num_words=64 c.words_per_row=4 + factory.reset() + c.recompute_sizes() debug.info(1, "Four way column mux") - a = bank(c, name="bank3_multi") + a = factory.create("bank", sram_config=c) self.local_check(a) c.word_size=2 c.num_words=128 c.words_per_row=8 + factory.reset() + c.recompute_sizes() debug.info(1, "Eight way column mux") - a = bank(c, name="bank4_multi") + a = factory.create("bank", sram_config=c) self.local_check(a) globals.end_openram() @@ -59,4 +70,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index 8e462318..90e886f4 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -1,30 +1,35 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -import debug from sram_factory import factory +import debug #@unittest.skip("SKIPPING 19_psingle_bank_test") class psingle_bank_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from bank import bank + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config - OPTS.bitcell = "pbitcell" - # testing layout of bank using pbitcell with 1 RW port (a 6T-cell equivalent) + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell="replica_pbitcell" + OPTS.dummy_bitcell="dummy_pbitcell" + OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 + c = sram_config(word_size=4, num_words=16) @@ -32,8 +37,7 @@ class psingle_bank_test(openram_test): factory.reset() c.recompute_sizes() debug.info(1, "No column mux") - name = "bank1_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) - a = bank(c, name=name) + a = factory.create(module_type="bank", sram_config=c) self.local_check(a) c.num_words=32 @@ -41,8 +45,7 @@ class psingle_bank_test(openram_test): factory.reset() c.recompute_sizes() debug.info(1, "Two way column mux") - name = "bank2_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) - a = bank(c, name=name) + a = factory.create(module_type="bank", sram_config=c) self.local_check(a) c.num_words=64 @@ -50,8 +53,7 @@ class psingle_bank_test(openram_test): factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") - name = "bank3_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) - a = bank(c, name=name) + a = factory.create(module_type="bank", sram_config=c) self.local_check(a) c.word_size=2 @@ -60,93 +62,9 @@ class psingle_bank_test(openram_test): factory.reset() c.recompute_sizes() debug.info(1, "Four way column mux") - name = "bank4_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) - a = bank(c, name=name) + a = factory.create(module_type="bank", sram_config=c) self.local_check(a) - - # testing bank using pbitcell in various port combinations - # layout for multiple ports does not work yet - """ - OPTS.netlist_only = True - - c.num_words=16 - c.words_per_row=1 - - OPTS.num_rw_ports = c.num_rw_ports = 2 - OPTS.num_w_ports = c.num_w_ports = 2 - OPTS.num_r_ports = c.num_r_ports = 2 - - debug.info(1, "No column mux") - name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) - a = bank(c, name=name) - self.local_check(a) - - OPTS.num_rw_ports = c.num_rw_ports = 0 - OPTS.num_w_ports = c.num_w_ports = 2 - OPTS.num_r_ports = c.num_r_ports = 2 - - debug.info(1, "No column mux") - name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) - a = bank(c, name=name) - self.local_check(a) - - OPTS.num_rw_ports = c.num_rw_ports = 2 - OPTS.num_w_ports = c.num_w_ports = 0 - OPTS.num_r_ports = c.num_r_ports = 2 - - debug.info(1, "No column mux") - name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) - a = bank(c, name=name) - self.local_check(a) - - OPTS.num_rw_ports = c.num_rw_ports = 2 - OPTS.num_w_ports = c.num_w_ports = 2 - OPTS.num_r_ports = c.num_r_ports = 0 - - debug.info(1, "No column mux") - name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) - a = bank(c, name=name) - self.local_check(a) - - OPTS.num_rw_ports = c.num_rw_ports = 2 - OPTS.num_w_ports = c.num_w_ports = 0 - OPTS.num_r_ports = c.num_r_ports = 0 - - debug.info(1, "No column mux") - name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) - a = bank(c, name=name) - self.local_check(a) - - # testing with various column muxes - OPTS.num_rw_ports = c.num_rw_ports = 2 - OPTS.num_w_ports = c.num_w_ports = 2 - OPTS.num_r_ports = c.num_r_ports = 2 - - c.num_words=32 - c.words_per_row=2 - debug.info(1, "Two way column mux") - name = "bank2_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) - a = bank(c, name=name) - self.local_check(a) - - c.num_words=64 - c.words_per_row=4 - debug.info(1, "Four way column mux") - name = "bank3_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) - a = bank(c, name=name) - self.local_check(a) - - # Eight way has a short circuit of one column mux select to gnd rail - c.word_size=2 - c.num_words=128 - c.words_per_row=8 - debug.info(1, "Eight way column mux") - name = "bank4_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) - a = bank(c, name=name) - self.local_check(a) - """ - globals.end_openram() # run the test from the command line @@ -154,4 +72,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_1rw_1r_test.py b/compiler/tests/19_single_bank_1rw_1r_test.py index 93bfd35a..ab5ce041 100755 --- a/compiler/tests/19_single_bank_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_1rw_1r_test.py @@ -1,53 +1,66 @@ #!/usr/bin/env python3 -""" -Run a regression test on 1rw 1r sram bank -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class single_bank_1rw_1r_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from bank import bank + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from sram_config import sram_config OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - from sram_config import sram_config c = sram_config(word_size=4, num_words=16) c.words_per_row=1 + factory.reset() + c.recompute_sizes() debug.info(1, "No column mux") - a = bank(c, name="bank1_single") + a = factory.create(module_type="bank", sram_config=c) self.local_check(a) c.num_words=32 c.words_per_row=2 + factory.reset() + c.recompute_sizes() debug.info(1, "Two way column mux") - a = bank(c, name="bank2_single") + a = factory.create(module_type="bank", sram_config=c) self.local_check(a) c.num_words=64 c.words_per_row=4 + factory.reset() + c.recompute_sizes() debug.info(1, "Four way column mux") - a = bank(c, name="bank3_single") + a = factory.create(module_type="bank", sram_config=c) self.local_check(a) c.word_size=2 c.num_words=128 c.words_per_row=8 + factory.reset() + c.recompute_sizes() debug.info(1, "Eight way column mux") - a = bank(c, name="bank4_single") + a = factory.create(module_type="bank", sram_config=c) self.local_check(a) globals.end_openram() @@ -57,4 +70,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_1w_1r_test.py b/compiler/tests/19_single_bank_1w_1r_test.py new file mode 100755 index 00000000..12b9f3a0 --- /dev/null +++ b/compiler/tests/19_single_bank_1w_1r_test.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class single_bank_1w_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1w_1r" + OPTS.replica_bitcell = "replica_bitcell_1w_1r" + OPTS.dummy_bitcell="dummy_bitcell_1w_1r" + + OPTS.num_rw_ports = 0 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + c = sram_config(word_size=4, + num_words=16) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create(module_type="bank", sram_config=c) + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create(module_type="bank", sram_config=c) + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create(module_type="bank", sram_config=c) + self.local_check(a) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create(module_type="bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index e7179d96..1d010db5 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -1,48 +1,59 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class single_bank_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from bank import bank + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config c = sram_config(word_size=4, num_words=16) c.words_per_row=1 + factory.reset() + c.recompute_sizes() debug.info(1, "No column mux") - a = bank(c, name="bank1_single") + a = factory.create("bank", sram_config=c) self.local_check(a) c.num_words=32 c.words_per_row=2 + factory.reset() + c.recompute_sizes() debug.info(1, "Two way column mux") - a = bank(c, name="bank2_single") + a = factory.create("bank", sram_config=c) self.local_check(a) c.num_words=64 c.words_per_row=4 + factory.reset() + c.recompute_sizes() debug.info(1, "Four way column mux") - a = bank(c, name="bank3_single") + a = factory.create("bank", sram_config=c) self.local_check(a) c.word_size=2 c.num_words=128 c.words_per_row=8 + factory.reset() + c.recompute_sizes() debug.info(1, "Eight way column mux") - a = bank(c, name="bank4_single") + a = factory.create("bank", sram_config=c) self.local_check(a) globals.end_openram() @@ -52,4 +63,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_wmask_test.py b/compiler/tests/19_single_bank_wmask_test.py new file mode 100755 index 00000000..439ffeba --- /dev/null +++ b/compiler/tests/19_single_bank_wmask_test.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class single_bank_wmask_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from sram_config import sram_config + + + c = sram_config(word_size=8, + write_size=4, + num_words=16, + num_banks=1) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py index f2f6386c..fdeae56f 100755 --- a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py @@ -1,26 +1,30 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete") class psram_1bank_2mux_1rw_1w_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config + OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" - + OPTS.dummy_bitcell="dummy_pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 1 OPTS.num_r_ports = 0 @@ -31,14 +35,16 @@ class psram_1bank_2mux_1rw_1w_test(openram_test): c.num_words=32 c.words_per_row=2 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w psram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -48,4 +54,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_psram_1bank_2mux_1rw_1w_wmask_test.py b/compiler/tests/20_psram_1bank_2mux_1rw_1w_wmask_test.py new file mode 100755 index 00000000..20fbd8e6 --- /dev/null +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_wmask_test.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +# @unittest.skip("SKIPPING psram_1bank_2mux_1rw_1w_wmask_test, multiport layout not complete") +class psram_1bank_2mux_1rw_1w_wmask_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from sram_config import sram_config + + OPTS.bitcell = "pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.dummy_bitcell = "dummy_pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 0 + + c = sram_config(word_size=8, + write_size=4, + num_words=32, + num_banks=1) + c.num_words = 32 + c.words_per_row = 2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w psram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) \ No newline at end of file diff --git a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py index 3d049aef..a5c01d8f 100755 --- a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py @@ -1,26 +1,30 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error") class psram_1bank_2mux_1w_1r_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config + OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" - + OPTS.dummy_bitcell="dummy_pbitcell" OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 @@ -38,7 +42,7 @@ class psram_1bank_2mux_1w_1r_test(openram_test): c.num_words, c.words_per_row, c.num_banks)) - a = sram(c, "sram") + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -48,4 +52,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_psram_1bank_2mux_test.py b/compiler/tests/20_psram_1bank_2mux_test.py index 3afd2c9b..64fa72ca 100755 --- a/compiler/tests/20_psram_1bank_2mux_test.py +++ b/compiler/tests/20_psram_1bank_2mux_test.py @@ -1,25 +1,29 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error") class psram_1bank_2mux_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" + OPTS.dummy_bitcell="dummy_pbitcell" # testing layout of sram using pbitcell with 1 RW port (a 6T-cell equivalent) OPTS.num_rw_ports = 1 @@ -32,14 +36,16 @@ class psram_1bank_2mux_test(openram_test): c.num_words=32 c.words_per_row=2 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w psram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -49,4 +55,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py index 1be26ca7..7779b794 100755 --- a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py +++ b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py @@ -1,25 +1,29 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class psram_1bank_4mux_1rw_1r_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config + OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" - + OPTS.dummy_bitcell="dummy_pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 @@ -30,14 +34,16 @@ class psram_1bank_4mux_1rw_1r_test(openram_test): c.num_words=64 c.words_per_row=4 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w psram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -47,4 +53,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py index ea5fba78..60192759 100755 --- a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py @@ -1,25 +1,29 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank, 2 port SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class sram_1bank_2mux_1rw_1r_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config OPTS.bitcell = "bitcell_1rw_1r" OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 @@ -30,14 +34,16 @@ class sram_1bank_2mux_1rw_1r_test(openram_test): c.words_per_row=2 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -47,4 +53,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py index 2954ffbc..2e1e848f 100755 --- a/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py @@ -1,26 +1,30 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error") class psram_1bank_2mux_1w_1r_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config + OPTS.bitcell = "bitcell_1w_1r" OPTS.replica_bitcell="replica_bitcell_1w_1r" - + OPTS.dummy_bitcell="dummy_bitcell_1w_1r" OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 @@ -31,14 +35,16 @@ class psram_1bank_2mux_1w_1r_test(openram_test): c.num_words=32 c.words_per_row=2 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -48,4 +54,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_2mux_test.py b/compiler/tests/20_sram_1bank_2mux_test.py index 26a7755f..7e5a4f3a 100755 --- a/compiler/tests/20_sram_1bank_2mux_test.py +++ b/compiler/tests/20_sram_1bank_2mux_test.py @@ -1,22 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory 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 + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config c = sram_config(word_size=4, num_words=32, @@ -24,14 +27,16 @@ class sram_1bank_2mux_test(openram_test): c.words_per_row=2 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -41,4 +46,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_2mux_wmask_test.py b/compiler/tests/20_sram_1bank_2mux_wmask_test.py new file mode 100755 index 00000000..65f025a7 --- /dev/null +++ b/compiler/tests/20_sram_1bank_2mux_wmask_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +# @unittest.skip("SKIPPING 20_sram_1bank_2mux_wmask_test") +class sram_1bank_2mux_wmask_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from sram_config import sram_config + c = sram_config(word_size=8, + write_size=4, + num_words=64, + num_banks=1) + + c.words_per_row = 2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} bit writes, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.write_size, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) \ No newline at end of file diff --git a/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py b/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py new file mode 100755 index 00000000..a5232267 --- /dev/null +++ b/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +@unittest.skip("SKIPPING sram_1bank_32b_1024_wmask_test") +class sram_1bank_32b_1024_wmask_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from sram_config import sram_config + c = sram_config(word_size=32, + write_size=8, + num_words=1024, + num_banks=1) + + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} bit writes, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.write_size, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) \ No newline at end of file diff --git a/compiler/tests/20_sram_1bank_4mux_test.py b/compiler/tests/20_sram_1bank_4mux_test.py index 16654be5..34af86a1 100755 --- a/compiler/tests/20_sram_1bank_4mux_test.py +++ b/compiler/tests/20_sram_1bank_4mux_test.py @@ -1,22 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 20_sram_1bank_4mux_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 + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config c = sram_config(word_size=4, num_words=64, @@ -24,14 +27,16 @@ class sram_1bank_4mux_test(openram_test): c.words_per_row=4 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -41,4 +46,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py index dfd8a6a1..48a42106 100755 --- a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py @@ -1,25 +1,29 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class sram_1bank_8mux_1rw_1r_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config OPTS.bitcell = "bitcell_1rw_1r" OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 @@ -30,14 +34,16 @@ class sram_1bank_8mux_1rw_1r_test(openram_test): c.words_per_row=8 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -47,4 +53,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_8mux_test.py b/compiler/tests/20_sram_1bank_8mux_test.py index dde1a448..c5eaea75 100755 --- a/compiler/tests/20_sram_1bank_8mux_test.py +++ b/compiler/tests/20_sram_1bank_8mux_test.py @@ -1,22 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory 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 + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config c = sram_config(word_size=2, num_words=128, @@ -24,14 +27,16 @@ class sram_1bank_8mux_test(openram_test): c.words_per_row=8 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -41,4 +46,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py index 02e82687..f6bccc13 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py @@ -1,25 +1,29 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank, 2 port SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class sram_1bank_nomux_1rw_1r_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config OPTS.bitcell = "bitcell_1rw_1r" OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.dummy_bitcell = "dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 @@ -30,14 +34,16 @@ class sram_1bank_nomux_1rw_1r_test(openram_test): c.words_per_row=1 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -47,4 +53,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_nomux_test.py b/compiler/tests/20_sram_1bank_nomux_test.py index 7a03ce1e..650d2ac2 100755 --- a/compiler/tests/20_sram_1bank_nomux_test.py +++ b/compiler/tests/20_sram_1bank_nomux_test.py @@ -1,22 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 20_sram_1bank_nomux_test") class sram_1bank_nomux_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config c = sram_config(word_size=4, num_words=16, @@ -24,14 +27,16 @@ class sram_1bank_nomux_test(openram_test): c.words_per_row=1 c.recompute_sizes() - debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - a = sram(c, "sram") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -41,4 +46,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_nomux_wmask_test.py b/compiler/tests/20_sram_1bank_nomux_wmask_test.py new file mode 100755 index 00000000..e0292e95 --- /dev/null +++ b/compiler/tests/20_sram_1bank_nomux_wmask_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +# @unittest.skip("SKIPPING 20_sram_1bank_nomux_wmask_test") +class sram_1bank_nomux_wmask_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from sram_config import sram_config + c = sram_config(word_size=8, + write_size=4, + num_words=16, + num_banks=1) + + c.words_per_row = 1 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} bit writes, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.write_size, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) \ No newline at end of file diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index 59db981d..c5d9d3d0 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -1,22 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on a 2 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug @unittest.skip("Multibank is not working yet.") class sram_2bank_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - from sram import sram + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram_config import sram_config c = sram_config(word_size=16, num_words=32, @@ -24,30 +27,66 @@ class sram_2bank_test(openram_test): c.words_per_row=1 c.recompute_sizes() - debug.info(1, "Two bank, no column mux with control logic") - a = sram(c, "sram1") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + factory.reset() + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) c.num_words=64 c.words_per_row=2 c.recompute_sizes() - debug.info(1, "Two bank two way column mux with control logic") - a = sram(c, "sram2") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + factory.reset() + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) c.num_words=128 c.words_per_row=4 c.recompute_sizes() - debug.info(1, "Two bank, four way column mux with control logic") - a = sram(c, "sram3") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + factory.reset() + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) c.word_size=2 c.num_words=256 c.words_per_row=8 c.recompute_sizes() - debug.info(1, "Two bank, eight way column mux with control logic") - a = sram(c, "sram4") + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + factory.reset() + a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) globals.end_openram() @@ -57,4 +96,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index fa7fc35f..e2222fcf 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -1,20 +1,24 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class timing_sram_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.spice_name="hspice" OPTS.analytical_delay = False OPTS.netlist_only = True @@ -24,16 +28,22 @@ class timing_sram_test(openram_test): import characterizer reload(characterizer) from characterizer import delay - from sram import sram from sram_config import sram_config c = sram_config(word_size=1, num_words=16, num_banks=1) c.words_per_row=1 + # c = sram_config(word_size=32, + # num_words=256, + # num_banks=1) + # c.words_per_row=2 + # OPTS.use_tech_delay_chain_size = True c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") - s = sram(c, name="sram1") - + s = factory.create(module_type="sram", sram_config=c) + #import sys + #sys.exit(1) + tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) @@ -44,43 +54,34 @@ class timing_sram_test(openram_test): corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) d = delay(s.s, tempspice, corner) import tech - loads = [tech.spice["msflop_in_cap"]*4] + loads = [tech.spice["dff_in_cap"]*4] slews = [tech.spice["rise_time"]*2] data, port_data = d.analyze(probe_address, probe_data, slews, loads) #Combine info about port into all data data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_bl': [0.1980959], - 'delay_br': [0.1946091], - 'delay_hl': [0.2121267], - 'delay_lh': [0.2121267], - 'leakage_power': 0.0023761999999999998, - 'min_period': 0.43, - 'read0_power': [0.5139368], - 'read1_power': [0.48940979999999995], - 'slew_hl': [0.0516745], - 'slew_lh': [0.0516745], - 'volt_bl': [0.5374525], - 'volt_br': [1.1058], - 'write0_power': [0.46267169999999996], - 'write1_power': [0.4670826]} + golden_data = {'delay_hl': [0.2181231], + 'delay_lh': [0.2181231], + 'leakage_power': 0.0025453999999999997, + 'min_period': 0.781, + 'read0_power': [0.34664159999999994], + 'read1_power': [0.32656349999999995], + 'slew_hl': [0.21136519999999998], + 'slew_lh': [0.21136519999999998], + 'write0_power': [0.37980179999999997], + 'write1_power': [0.3532026]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_bl': [1.1029], - 'delay_br': [0.9656455999999999], - 'delay_hl': [1.288], - 'delay_lh': [1.288], - 'leakage_power': 0.0273896, - 'min_period': 2.578, - 'read0_power': [16.9996], - 'read1_power': [16.2616], - 'slew_hl': [0.47891700000000004], - 'slew_lh': [0.47891700000000004], - 'volt_bl': [4.2155], - 'volt_br': [5.8142], - 'write0_power': [16.0656], - 'write1_power': [16.2616]} - + golden_data = {'delay_hl': [1.4082], + 'delay_lh': [1.4082], + 'leakage_power': 0.0267388, + 'min_period': 4.688, + 'read0_power': [11.5255], + 'read1_power': [10.9406], + 'slew_hl': [1.2979], + 'slew_lh': [1.2979], + 'write0_power': [12.9458], + 'write1_power': [11.7444]} else: self.assertTrue(False) # other techs fail # Check if no too many or too few results @@ -95,4 +96,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/21_hspice_setuphold_test.py b/compiler/tests/21_hspice_setuphold_test.py index c003f54a..ef5cf1a8 100755 --- a/compiler/tests/21_hspice_setuphold_test.py +++ b/compiler/tests/21_hspice_setuphold_test.py @@ -1,20 +1,24 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class timing_setup_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.spice_name="hspice" OPTS.analytical_delay = False OPTS.netlist_only = True @@ -57,4 +61,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/21_model_delay_test.py b/compiler/tests/21_model_delay_test.py new file mode 100755 index 00000000..a4de4c2a --- /dev/null +++ b/compiler/tests/21_model_delay_test.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +# @unittest.skip("SKIPPING 21_model_delay_test") +class model_delay_test(openram_test): + """ Compare the accuracy of the analytical model with a spice simulation. """ + + def runTest(self): + globals.init_openram("config_{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 delay + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=1, + num_words=16, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") + s = factory.create(module_type="sram", sram_config=c) + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + probe_address = "1" * s.s.addr_size + probe_data = s.s.word_size - 1 + debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data)) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + d = delay(s.s, tempspice, corner) + import tech + loads = [tech.spice["dff_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + + # Run a spice characterization + spice_data, port_data = d.analyze(probe_address, probe_data, slews, loads) + spice_data.update(port_data[0]) + + # Run analytical characterization + model_data, port_data = d.analytical_delay(slews, loads) + model_data.update(port_data[0]) + + # Only compare the delays + spice_delays = {key:value for key, value in spice_data.items() if 'delay' in key} + model_delays = {key:value for key, value in model_data.items() if 'delay' in key} + debug.info(1,"Spice Delays={}".format(spice_delays)) + debug.info(1,"Model Delays={}".format(model_delays)) + + if OPTS.tech_name == "freepdk45": + error_tolerance = 0.25 + elif OPTS.tech_name == "scn4m_subm": + error_tolerance = 0.25 + else: + self.assertTrue(False) # other techs fail + + # Check if no too many or too few results + self.assertTrue(len(spice_delays.keys())==len(model_delays.keys())) + + self.assertTrue(self.check_golden_data(spice_delays,model_delays,error_tolerance)) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 5af44e69..0fe5dfdd 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -1,20 +1,24 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class timing_sram_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.spice_name="ngspice" OPTS.analytical_delay = False OPTS.netlist_only = True @@ -24,7 +28,6 @@ class timing_sram_test(openram_test): import characterizer reload(characterizer) from characterizer import delay - from sram import sram from sram_config import sram_config c = sram_config(word_size=1, num_words=16, @@ -32,7 +35,7 @@ class timing_sram_test(openram_test): c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") - s = sram(c, name="sram1") + s = factory.create(module_type="sram", sram_config=c) tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) @@ -44,43 +47,34 @@ class timing_sram_test(openram_test): corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) d = delay(s.s, tempspice, corner) import tech - loads = [tech.spice["msflop_in_cap"]*4] + loads = [tech.spice["dff_in_cap"]*4] slews = [tech.spice["rise_time"]*2] data, port_data = d.analyze(probe_address, probe_data, slews, loads) #Combine info about port into all data data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_bl': [0.2003652], - 'delay_br': [0.198698], - 'delay_hl': [0.2108836], - 'delay_lh': [0.2108836], - 'leakage_power': 0.001564799, - 'min_period': 0.508, - 'read0_power': [0.43916689999999997], - 'read1_power': [0.4198608], - 'slew_hl': [0.0455126], - 'slew_lh': [0.0455126], - 'volt_bl': [0.6472883], - 'volt_br': [1.114024], - 'write0_power': [0.40681890000000004], - 'write1_power': [0.4198608]} + golden_data = {'delay_hl': [0.2264205], + 'delay_lh': [0.2264205], + 'leakage_power': 0.0021017429999999997, + 'min_period': 0.859, + 'read0_power': [0.3339161], + 'read1_power': [0.31329440000000003], + 'slew_hl': [0.2590786], + 'slew_lh': [0.2590786], + 'write0_power': [0.36360849999999995], + 'write1_power': [0.3486931]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_bl': [1.3937359999999999], - 'delay_br': [1.2596429999999998], - 'delay_hl': [1.5747600000000002], - 'delay_lh': [1.5747600000000002], - 'leakage_power': 0.00195795, - 'min_period': 3.281, - 'read0_power': [14.92874], - 'read1_power': [14.369810000000001], - 'slew_hl': [0.49631959999999997], - 'slew_lh': [0.49631959999999997], - 'volt_bl': [4.132618], - 'volt_br': [5.573099], - 'write0_power': [13.79953], - 'write1_power': [14.369810000000001]} - + golden_data = {'delay_hl': [1.7083549999999998], + 'delay_lh': [1.7083549999999998], + 'leakage_power': 0.001119657, + 'min_period': 7.812, + 'read0_power': [8.013845], + 'read1_power': [7.6889389999999995], + 'slew_hl': [1.31918], + 'slew_lh': [1.31918], + 'write0_power': [8.791557000000001], + 'write1_power': [8.70443]} else: self.assertTrue(False) # other techs fail @@ -96,4 +90,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/21_ngspice_setuphold_test.py b/compiler/tests/21_ngspice_setuphold_test.py index 924d05a5..4a289812 100755 --- a/compiler/tests/21_ngspice_setuphold_test.py +++ b/compiler/tests/21_ngspice_setuphold_test.py @@ -1,20 +1,24 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug class timing_setup_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.spice_name="ngspice" OPTS.analytical_delay = False OPTS.netlist_only = True @@ -58,4 +62,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_psram_1bank_2mux_func_test.py b/compiler/tests/22_psram_1bank_2mux_func_test.py index 87c8db48..f986c3e7 100755 --- a/compiler/tests/22_psram_1bank_2mux_func_test.py +++ b/compiler/tests/22_psram_1bank_2mux_func_test.py @@ -1,28 +1,34 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug -#@unittest.skip("SKIPPING 22_psram_1bank_2mux_1rw_1r_1w_func_test, third port reads are broken?") -class psram_1bank_2mux_1rw_1r_1w_func_test(openram_test): +class psram_1bank_2mux_func_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False + OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" + OPTS.dummy_bitcell="dummy_pbitcell" + OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 + OPTS.num_r_ports = 0 OPTS.num_w_ports = 1 # This is a hack to reload the characterizer __init__ with the spice version @@ -30,30 +36,26 @@ class psram_1bank_2mux_1rw_1r_1w_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional, delay - from sram import sram from sram_config import sram_config - c = sram_config(word_size=4, - num_words=64, + c = sram_config(word_size=2, + num_words=32, num_banks=1) c.words_per_row=2 c.recompute_sizes() - debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - s = sram(c, name="sram") - tempspice = OPTS.openram_temp + "temp.sp" + debug.info(1, "Functional test for {}rw,{}r,{}w psram with" + "{} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) f = functional(s.s, tempspice, corner) - d = delay(s.s, tempspice, corner) - feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) - - f.num_cycles = 10 (fail, error) = f.run() self.assertTrue(fail,error) @@ -64,4 +66,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_psram_1bank_4mux_func_test.py b/compiler/tests/22_psram_1bank_4mux_func_test.py index 16661483..c5fd8945 100755 --- a/compiler/tests/22_psram_1bank_4mux_func_test.py +++ b/compiler/tests/22_psram_1bank_4mux_func_test.py @@ -1,28 +1,35 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test, third port reads are broken?") class psram_1bank_4mux_func_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False + OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" + OPTS.dummy_bitcell="dummy_pbitcell" + OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 + OPTS.num_r_ports = 0 OPTS.num_w_ports = 1 # This is a hack to reload the characterizer __init__ with the spice version @@ -30,30 +37,26 @@ class psram_1bank_4mux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional, delay - from sram import sram from sram_config import sram_config - c = sram_config(word_size=4, + c = sram_config(word_size=2, num_words=256, num_banks=1) c.words_per_row=4 c.recompute_sizes() - debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - s = sram(c, name="sram") - tempspice = OPTS.openram_temp + "temp.sp" + debug.info(1, "Functional test for {}rw,{}r,{}w psram with" + "{} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) f = functional(s.s, tempspice, corner) - d = delay(s.s, tempspice, corner) - feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) - - f.num_cycles = 10 (fail, error) = f.run() self.assertTrue(fail,error) @@ -64,4 +67,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_psram_1bank_8mux_func_test.py b/compiler/tests/22_psram_1bank_8mux_func_test.py index 86cfd16f..acb168c0 100755 --- a/compiler/tests/22_psram_1bank_8mux_func_test.py +++ b/compiler/tests/22_psram_1bank_8mux_func_test.py @@ -1,28 +1,35 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory 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)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False + OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" + OPTS.dummy_bitcell="dummy_pbitcell" + OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 + OPTS.num_r_ports = 0 OPTS.num_w_ports = 1 # This is a hack to reload the characterizer __init__ with the spice version @@ -30,30 +37,26 @@ class psram_1bank_8mux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional, delay - 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=8 c.recompute_sizes() - debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - s = sram(c, name="sram") - tempspice = OPTS.openram_temp + "temp.sp" + debug.info(1, "Functional test for {}rw,{}r,{}w psram with" + "{} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) f = functional(s.s, tempspice, corner) - d = delay(s.s, tempspice, corner) - feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) - - f.num_cycles = 10 (fail, error) = f.run() self.assertTrue(fail,error) @@ -64,4 +67,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_psram_1bank_nomux_func_test.py b/compiler/tests/22_psram_1bank_nomux_func_test.py index 3133eb03..a2a2b41c 100755 --- a/compiler/tests/22_psram_1bank_nomux_func_test.py +++ b/compiler/tests/22_psram_1bank_nomux_func_test.py @@ -1,28 +1,35 @@ #!/usr/bin/env python3 -""" -Run a functioal test on 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory 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)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False + OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" + OPTS.dummy_bitcell="dummy_pbitcell" + OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 + OPTS.num_r_ports = 0 OPTS.num_w_ports = 1 # This is a hack to reload the characterizer __init__ with the spice version @@ -30,30 +37,26 @@ class psram_1bank_nomux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional, delay - from sram import sram from sram_config import sram_config - c = sram_config(word_size=4, + c = sram_config(word_size=2, num_words=32, num_banks=1) c.words_per_row=1 c.recompute_sizes() - debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - s = sram(c, name="sram") - tempspice = OPTS.openram_temp + "temp.sp" + debug.info(1, "Functional test for {}rw,{}r,{}w psram with" + "{} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) f = functional(s.s, tempspice, corner) - d = delay(s.s, tempspice, corner) - feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) - - f.num_cycles = 10 (fail, error) = f.run() self.assertTrue(fail,error) @@ -64,4 +67,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_sram_1bank_2mux_func_test.py b/compiler/tests/22_sram_1bank_2mux_func_test.py index f163216e..2037169e 100755 --- a/compiler/tests/22_sram_1bank_2mux_func_test.py +++ b/compiler/tests/22_sram_1bank_2mux_func_test.py @@ -1,21 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@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)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False @@ -25,28 +29,24 @@ class sram_1bank_2mux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional, delay - from sram import sram from sram_config import sram_config c = sram_config(word_size=4, - num_words=64, + num_words=32, num_banks=1) c.words_per_row=2 c.recompute_sizes() - 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" + 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 = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) f = functional(s.s, tempspice, corner) - d = delay(s.s, tempspice, corner) - feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) - - f.num_cycles = 10 - (fail, error) = f.run(feasible_period) + (fail, error) = f.run() self.assertTrue(fail,error) globals.end_openram() @@ -56,4 +56,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_sram_1bank_4mux_func_test.py b/compiler/tests/22_sram_1bank_4mux_func_test.py index 049d8459..178f955b 100755 --- a/compiler/tests/22_sram_1bank_4mux_func_test.py +++ b/compiler/tests/22_sram_1bank_4mux_func_test.py @@ -1,21 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory 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)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False @@ -25,27 +29,23 @@ class sram_1bank_4mux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional, delay - from sram import sram from sram_config import sram_config c = sram_config(word_size=4, - num_words=256, + num_words=128, num_banks=1) c.words_per_row=4 c.recompute_sizes() - 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" + 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 = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) f = functional(s.s, tempspice, corner) - d = delay(s.s, tempspice, corner) - feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) - - f.num_cycles = 10 (fail, error) = f.run() self.assertTrue(fail,error) @@ -56,4 +56,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_sram_1bank_8mux_func_test.py b/compiler/tests/22_sram_1bank_8mux_func_test.py index d159f265..d531163a 100755 --- a/compiler/tests/22_sram_1bank_8mux_func_test.py +++ b/compiler/tests/22_sram_1bank_8mux_func_test.py @@ -1,21 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory 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)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False @@ -28,27 +32,23 @@ class sram_1bank_8mux_func_test(openram_test): 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=256, + num_words=128, num_banks=1) c.words_per_row=8 c.recompute_sizes() - 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" + 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 = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) f = functional(s.s, tempspice, corner) - d = delay(s.s, tempspice, corner) - feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) - - f.num_cycles = 10 (fail, error) = f.run() self.assertTrue(fail,error) @@ -59,4 +59,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_sram_1bank_nomux_func_test.py b/compiler/tests/22_sram_1bank_nomux_func_test.py index 08a21d92..eb6d2412 100755 --- a/compiler/tests/22_sram_1bank_nomux_func_test.py +++ b/compiler/tests/22_sram_1bank_nomux_func_test.py @@ -1,21 +1,25 @@ #!/usr/bin/env python3 -""" -Run a functioal test on 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory 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)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.netlist_only = True @@ -24,25 +28,23 @@ class sram_1bank_nomux_func_test(openram_test): 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_words=16, num_banks=1) c.words_per_row=1 c.recompute_sizes() - 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" + 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 = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.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) @@ -53,4 +55,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py index a609460b..169e34d0 100755 --- a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py +++ b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py @@ -1,26 +1,31 @@ #!/usr/bin/env python3 -""" -Run a functioal test on 1 bank SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug #@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test") class psram_1bank_nomux_func_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False OPTS.bitcell = "bitcell_1rw_1r" OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 @@ -30,27 +35,23 @@ class psram_1bank_nomux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional, delay - 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 c.recompute_sizes() - debug.info(1, "Functional test for sram 1rw,1r with {} bit words, {} words, {} words per row, {} banks".format(c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) - s = sram(c, name="sram") - tempspice = OPTS.openram_temp + "temp.sp" + debug.info(1, "Functional test for sram 1rw,1r with " + "{} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" s.sp_write(tempspice) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) f = functional(s.s, tempspice, corner) - d = delay(s.s, tempspice, corner) - feasible_period = self.find_feasible_test_period(d, s.s, f.load, f.slew) - - f.num_cycles = 10 (fail, error) = f.run() self.assertTrue(fail,error) @@ -61,4 +62,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_sram_wmask_1w_1r_func_test.py b/compiler/tests/22_sram_wmask_1w_1r_func_test.py new file mode 100755 index 00000000..2ee79750 --- /dev/null +++ b/compiler/tests/22_sram_wmask_1w_1r_func_test.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +# @unittest.skip("SKIPPING sram_wmask_1w_1r_func_test") +class sram_wmask_1w_1r_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + OPTS.bitcell = "bitcell_1w_1r" + OPTS.replica_bitcell = "replica_bitcell_1w_1r" + OPTS.dummy_bitcell = "dummy_bitcell_1w_1r" + + OPTS.num_rw_ports = 0 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram_config import sram_config + c = sram_config(word_size=8, + num_words=16, + write_size=2, + num_banks=1) + c.words_per_row = 1 + c.recompute_sizes() + debug.info(1, + "Functional test for sram with {} bit words, {} words, {} words per row, {} bit writes, {} banks".format( + c.word_size, + c.num_words, + c.words_per_row, + c.write_size, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + (fail, error) = f.run() + self.assertTrue(fail, error) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_sram_wmask_func_test.py b/compiler/tests/22_sram_wmask_func_test.py new file mode 100755 index 00000000..c390f030 --- /dev/null +++ b/compiler/tests/22_sram_wmask_func_test.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING sram_wmask_func_test") +class sram_wmask_func_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + + # 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, delay + from sram_config import sram_config + c = sram_config(word_size=8, + num_words=16, + write_size=4, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Functional test for sram with " + "{} bit words, {} words, {} words per row, {} bit writes, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.write_size, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + (fail, error) = f.run() + self.assertTrue(fail, error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/23_lib_sram_model_corners_test.py b/compiler/tests/23_lib_sram_model_corners_test.py index 431555bc..5840c05d 100755 --- a/compiler/tests/23_lib_sram_model_corners_test.py +++ b/compiler/tests/23_lib_sram_model_corners_test.py @@ -1,20 +1,25 @@ #!/usr/bin/env python3 -""" -Check the .lib file for an SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os,re -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug -class model_corners_lib_test(openram_test): +#@unittest.skip("SKIPPING 23_lib_sram_model_corners_test") +class lib_model_corners_lib_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) + OPTS.netlist_only = True from characterizer import lib from sram import sram @@ -25,7 +30,10 @@ class model_corners_lib_test(openram_test): c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") - s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) + + # This doesn't have to use the factory since worst case + # it will just replaece the top-level module of the same name + s = sram(c, name="sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) @@ -60,7 +68,7 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index 8a996cb4..75e73f71 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -1,21 +1,26 @@ #!/usr/bin/env python3 -""" -Check the .lib file for an SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os,re -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug -class lib_test(openram_test): +#@unittest.skip("SKIPPING 23_lib_sram_model_test") +class lib_sram_model_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - + globals.init_openram("config_{0}".format(OPTS.tech_name)) + OPTS.netlist_only = True + from characterizer import lib from sram import sram from sram_config import sram_config @@ -25,6 +30,9 @@ class lib_test(openram_test): c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") + + # This doesn't have to use the factory since worst case + # it will just replaece the top-level module of the same name s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) @@ -50,7 +58,7 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index 1b93d1fd..1fc5a66b 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -1,20 +1,24 @@ #!/usr/bin/env python3 -""" -Check the .lib file for an SRAM with pruning -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os,re -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug -class lib_test(openram_test): +@unittest.skip("SKIPPING 23_lib_sram_prune_test") +class lib_sram_prune_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.trim_netlist = True @@ -34,6 +38,9 @@ class lib_test(openram_test): c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing pruned timing for sample 2 bit, 16 words SRAM with 1 bank") + + # This doesn't have to use the factory since worst case + # it will just replaece the top-level module of the same name s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" @@ -61,7 +68,7 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index 1c26fd45..0ababf32 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -1,12 +1,15 @@ #!/usr/bin/env python3 -""" -Check the .lib file for an SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os,re -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug @@ -14,7 +17,7 @@ import debug class lib_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.analytical_delay = False OPTS.trim_netlist = False @@ -34,6 +37,9 @@ class lib_test(openram_test): c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") + + # This doesn't have to use the factory since worst case + # it will just replaece the top-level module of the same name s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" @@ -60,7 +66,7 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/24_lef_sram_test.py b/compiler/tests/24_lef_sram_test.py index 983038a2..5f7fdc60 100755 --- a/compiler/tests/24_lef_sram_test.py +++ b/compiler/tests/24_lef_sram_test.py @@ -1,12 +1,15 @@ #!/usr/bin/env python3 -""" -Check the LEF file for an SRMA -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug @@ -15,7 +18,7 @@ import debug class lef_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram import sram from sram_config import sram_config @@ -25,6 +28,8 @@ class lef_test(openram_test): c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank") + # This doesn't have to use the factory since worst case + # it will just replaece the top-level module of the same name s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) gdsfile = s.name + ".gds" @@ -45,4 +50,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index f98475b4..da6a2682 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -1,12 +1,15 @@ #!/usr/bin/env python3 -""" -Check the .v file for an SRAM -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS import debug @@ -14,7 +17,7 @@ import debug class verilog_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) from sram import sram from sram_config import sram_config @@ -24,6 +27,8 @@ class verilog_test(openram_test): c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank") + # This doesn't have to use the factory since worst case + # it will just replaece the top-level module of the same name s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) vfile = s.name + ".v" @@ -42,4 +47,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/26_hspice_pex_pinv_test.py b/compiler/tests/26_hspice_pex_pinv_test.py new file mode 100755 index 00000000..f0cccba3 --- /dev/null +++ b/compiler/tests/26_hspice_pex_pinv_test.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +""" +Run regression tests/pex test on an extracted pinv to ensure pex functionality +with HSPICE. +""" +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 + + +class hspice_pex_pinv_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + import pinv + + # load the hspice + OPTS.spice_name="hspice" + OPTS.analytical_delay = False + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + + # generate the pinv + prev_purge_value = OPTS.purge_temp + OPTS.purge_temp = False # force set purge to false to save the sp file + debug.info(2, "Checking 1x size inverter") + tx = pinv.pinv(name="pinv", size=1) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp,tx.name) + tx.gds_write(tempgds) + tempsp = "{0}{1}.sp".format(OPTS.openram_temp,tx.name) + tx.sp_write(tempsp) + + # make sure that the library simulation is successful\ + sp_delay = self.simulate_delay(test_module = tempsp, + top_level_name = tx.name) + if sp_delay is "Failed": + self.fail('Library Spice module did not behave as expected') + + # now generate its pex file + pex_file = self.run_pex(tx) + OPTS.purge_temp = prev_purge_value # restore the old purge value + # generate simulation for pex, make sure the simulation is successful + pex_delay = self.simulate_delay(test_module = pex_file, + top_level_name = tx.name) + # make sure the extracted spice simulated + if pex_delay is "Failed": + self.fail('Pex file did not behave as expected') + + # if pex data is bigger than original spice file then result is ok + # However this may not always be true depending on the netlist provided + # comment out for now + #debug.info(2,"pex_delay: {0}".format(pex_delay)) + #debug.info(2,"sp_delay: {0}".format(sp_delay)) + + #assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\ + #.format(pex_delay,sp_delay) + + globals.end_openram() + + def simulate_delay(self, test_module, top_level_name): + from characterizer import charutils + from charutils import parse_spice_list + # setup simulation + sim_file = OPTS.openram_temp + "stim.sp" + log_file_name = "timing" + test_sim = self.write_simulation(sim_file, test_module, top_level_name) + test_sim.run_sim() + delay = parse_spice_list(log_file_name, "pinv_delay") + return delay + + def write_simulation(self, sim_file, cir_file, top_module_name): + """ write pex spice simulation for a pinv test""" + import tech + from characterizer import measurements, stimuli + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + sim_file = open(sim_file, "w") + simulation = stimuli(sim_file,corner) + + # library files + simulation.write_include(cir_file) + + # supply voltages + simulation.gen_constant(sig_name ="vdd", + v_val = tech.spice["nom_supply_voltage"]) + simulation.gen_constant(sig_name = "gnd", + v_val = "0v") + + run_time = tech.spice["feasible_period"] * 4 + # input voltage + clk_period = tech.spice["feasible_period"] + simulation.gen_pwl(sig_name ="input", + clk_times = [clk_period,clk_period], + data_values = [1,0], + period = clk_period, + slew = 0.001*tech.spice["feasible_period"], + setup = 0) + + # instantiation of simulated pinv + simulation.inst_model(pins = ["input", "output", "vdd", "gnd"], + model_name = top_module_name) + + # delay measurement + delay_measure = measurements.delay_measure(measure_name = "pinv_delay", + trig_name = "input", + targ_name = "output", + trig_dir_str = "FALL", + targ_dir_str = "RISE", + has_port = False) + trig_td = trag_td = 0.01 * run_time + rest_info = trig_td,trag_td,tech.spice["nom_supply_voltage"] + delay_measure.write_measure(simulation, rest_info) + + simulation.write_control(end_time = run_time) + sim_file.close() + return simulation + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/26_ngspice_pex_pinv_test.py b/compiler/tests/26_ngspice_pex_pinv_test.py new file mode 100755 index 00000000..2eb95948 --- /dev/null +++ b/compiler/tests/26_ngspice_pex_pinv_test.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +""" +Run regression tests/pex test on an extracted pinv to ensure pex functionality +with Ngspice. +""" +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 + + +class ngspice_pex_pinv_test(openram_test): + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + import pinv + + # load the ngspice + OPTS.spice_name="ngspice" + OPTS.analytical_delay = False + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + + # generate the pinv module + prev_purge_value = OPTS.purge_temp + OPTS.purge_temp = False # force set purge to false to save the sp file + debug.info(2, "Checking 1x size inverter") + tx = pinv.pinv(name="pinv", size=1) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp,tx.name) + tx.gds_write(tempgds) + tempsp = "{0}{1}.sp".format(OPTS.openram_temp,tx.name) + tx.sp_write(tempsp) + + # make sure that the library simulation is successful + sp_delay = self.simulate_delay(test_module = tempsp, + top_level_name = tx.name) + if sp_delay is "Failed": + self.fail('Library Spice module did not behave as expected') + + # now generate its pex file + pex_file = self.run_pex(tx) + OPTS.purge_temp = prev_purge_value # restore the old purge value + # generate simulation for pex, make sure the simulation is successful + pex_delay = self.simulate_delay(test_module = pex_file, + top_level_name = tx.name) + # make sure the extracted spice simulated + if pex_delay is "Failed": + self.fail('Pex file did not behave as expected') + + # if pex data is bigger than original spice file then result is ok + # However this may not always be true depending on the netlist provided + # comment out for now + #debug.info(2,"pex_delay: {0}".format(pex_delay)) + #debug.info(2,"sp_delay: {0}".format(sp_delay)) + + #assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\ + #.format(pex_delay,sp_delay) + + globals.end_openram() + + def simulate_delay(self, test_module, top_level_name): + from characterizer import charutils + from charutils import parse_spice_list + # setup simulation + sim_file = OPTS.openram_temp + "stim.sp" + log_file_name = "timing" + test_sim = self.write_simulation(sim_file, test_module, top_level_name) + test_sim.run_sim() + delay = parse_spice_list(log_file_name, "pinv_delay") + return delay + + def write_simulation(self, sim_file, cir_file, top_module_name): + """ write pex spice simulation for a pinv test""" + import tech + from characterizer import measurements, stimuli + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + sim_file = open(sim_file, "w") + simulation = stimuli(sim_file,corner) + + # library files + simulation.write_include(cir_file) + + # supply voltages + simulation.gen_constant(sig_name ="vdd", + v_val = tech.spice["nom_supply_voltage"]) + # The scn4m_subm and ngspice combination will have a gnd source error: + # "Fatal error: instance vgnd is a shorted VSRC" + # However, remove gnd power for all techa pass for this test + # simulation.gen_constant(sig_name = "gnd", + # v_val = "0v") + + + run_time = tech.spice["feasible_period"] * 4 + # input voltage + clk_period = tech.spice["feasible_period"] + simulation.gen_pwl(sig_name ="input", + clk_times = [clk_period,clk_period], + data_values = [1,0], + period = clk_period, + slew = 0.001*tech.spice["feasible_period"], + setup = 0) + + # instantiation of simulated pinv + simulation.inst_model(pins = ["input", "output", "vdd", "gnd"], + model_name = top_module_name) + + # delay measurement + delay_measure = measurements.delay_measure(measure_name = "pinv_delay", + trig_name = "input", + targ_name = "output", + trig_dir_str = "FALL", + targ_dir_str = "RISE", + has_port = False) + trig_td = trag_td = 0.01 * run_time + rest_info = trig_td,trag_td,tech.spice["nom_supply_voltage"] + delay_measure.write_measure(simulation, rest_info) + + simulation.write_control(end_time = run_time) + sim_file.close() + return simulation + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/26_pex_test.py b/compiler/tests/26_pex_test.py index d374e485..78409249 100755 --- a/compiler/tests/26_pex_test.py +++ b/compiler/tests/26_pex_test.py @@ -1,21 +1,25 @@ #!/usr/bin/env python3 -""" -Run a regression test on an extracted SRAM to ensure functionality. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug @unittest.skip("SKIPPING 26_pex_test") class sram_func_test(openram_test): def runTest(self): - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_{0}".format(OPTS.tech_name)) OPTS.use_pex = True @@ -311,4 +315,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/27_worst_case_delay_test.py b/compiler/tests/27_worst_case_delay_test.py deleted file mode 100755 index 834c6b69..00000000 --- a/compiler/tests/27_worst_case_delay_test.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/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.recompute_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() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/28_delay_model_test.py b/compiler/tests/28_delay_model_test.py deleted file mode 100755 index a02a42e9..00000000 --- a/compiler/tests/28_delay_model_test.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/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 - -class delay_model_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.trim_netlist = False - debug.info(1, "Trimming disabled for this test. Simulation could be slow.") - - # This is a hack to reload the characterizer __init__ with the spice version - from importlib import reload - import characterizer - reload(characterizer) - - from characterizer import model_check - from sram import sram - from sram_config import sram_config - c = sram_config(word_size=4, - num_words=16, - num_banks=1) - c.words_per_row=1 - c.recompute_sizes() - debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") - s = sram(c, name="sram1") - - tempspice = OPTS.openram_temp + "temp.sp" - s.sp_write(tempspice) - - probe_address = "1" * s.s.addr_size - probe_data = s.s.word_size - 1 - debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data)) - - corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - mc = model_check(s.s, tempspice, corner) - import tech - loads = [tech.spice["msflop_in_cap"]*4] - slews = [tech.spice["rise_time"]*2] - sram_data = mc.analyze(probe_address, probe_data, slews, loads) - #Combine info about port into all data - - #debug.info(1,"Data:\n{}".format(wl_data)) - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_back_end_test.py similarity index 73% rename from compiler/tests/30_openram_test.py rename to compiler/tests/30_openram_back_end_test.py index 55ab9457..de1bec05 100755 --- a/compiler/tests/30_openram_test.py +++ b/compiler/tests/30_openram_back_end_test.py @@ -1,28 +1,29 @@ #!/usr/bin/env python3 -""" -This tests the top-level executable. It checks that it generates the -appropriate files: .lef, .lib, .sp, .gds, .v. It DOES NOT, however, -check that these files are right. -""" - +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest -from testutils import header,openram_test +from testutils import * import sys,os,re,shutil -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS +from sram_factory import factory import debug import getpass -class openram_test(openram_test): +class openram_back_end_test(openram_test): def runTest(self): OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) - globals.init_openram("{0}/tests/config_20_{1}".format(OPENRAM_HOME,OPTS.tech_name)) - - debug.info(1, "Testing top-level openram.py with 2-bit, 16 word SRAM.") + globals.init_openram("{0}/tests/config_{1}".format(OPENRAM_HOME,OPTS.tech_name)) + + debug.info(1, "Testing top-level back-end openram.py with 2-bit, 16 word SRAM.") out_file = "testsram" - # make a temp directory for output out_path = "/tmp/testsram_{0}_{1}_{2}/".format(OPTS.tech_name,getpass.getuser(),os.getpid()) # make sure we start without the files existing @@ -37,10 +38,12 @@ class openram_test(openram_test): os.chmod(out_path, 0o0750) # specify the same verbosity for the system call - verbosity = "" + options = "" for i in range(OPTS.debug_level): - verbosity += " -v" + options += " -v" + if OPTS.spice_name: + options += " -s {}".format(OPTS.spice_name) # Always perform code coverage if OPTS.coverage == 0: @@ -48,11 +51,11 @@ class openram_test(openram_test): exe_name = "{0}/openram.py ".format(OPENRAM_HOME) else: exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) - config_name = "{0}config_20_{1}.py".format(OPENRAM_HOME + "/tests/",OPTS.tech_name) + config_name = "{0}config_{1}_back_end.py".format(OPENRAM_HOME + "/tests/",OPTS.tech_name) cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, out_file, out_path, - verbosity, + options, config_name, out_path) debug.info(1, cmd) @@ -83,10 +86,11 @@ class openram_test(openram_test): # now clean up the directory - if os.path.exists(out_path): - shutil.rmtree(out_path, ignore_errors=True) - self.assertEqual(os.path.exists(out_path),False) - + if OPTS.purge_temp: + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path),False) + globals.end_openram() # run the test from the command line @@ -94,4 +98,4 @@ if __name__ == "__main__": (OPTS, args) = globals.parse_args() del sys.argv[1:] header(__file__, OPTS.tech_name) - unittest.main() + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/30_openram_front_end_test.py b/compiler/tests/30_openram_front_end_test.py new file mode 100755 index 00000000..dbe7fcb9 --- /dev/null +++ b/compiler/tests/30_openram_front_end_test.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os,re,shutil +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug +import getpass + +#@unittest.skip("SKIPPING 30_openram_front_end_test") +class openram_front_end_test(openram_test): + + def runTest(self): + OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) + globals.init_openram("{0}/tests/config_{1}".format(OPENRAM_HOME,OPTS.tech_name)) + + debug.info(1, "Testing top-level front-end openram.py with 2-bit, 16 word SRAM.") + out_file = "testsram" + out_path = "/tmp/testsram_{0}_{1}_{2}".format(OPTS.tech_name,getpass.getuser(),os.getpid()) + + # make sure we start without the files existing + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path),False) + + try: + os.makedirs(out_path, 0o0750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(out_path, 0o0750) + + # specify the same verbosity for the system call + options = "" + for i in range(OPTS.debug_level): + options += " -v" + + if OPTS.spice_name: + options += " -s {}".format(OPTS.spice_name) + + # Always perform code coverage + if OPTS.coverage == 0: + debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") + exe_name = "{0}/openram.py ".format(OPENRAM_HOME) + else: + exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) + config_name = "{0}config_{1}_front_end.py".format(OPENRAM_HOME + "/tests/",OPTS.tech_name) + cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, + out_file, + out_path, + options, + config_name, + out_path) + debug.info(1, cmd) + os.system(cmd) + + # assert an error until we actually check a result + for extension in ["v", "lef", "sp", "gds"]: + filename = "{0}/{1}.{2}".format(out_path,out_file,extension) + debug.info(1,"Checking for file: " + filename) + self.assertEqual(os.path.exists(filename),True) + + # Make sure there is any .lib file + import glob + files = glob.glob('{0}/*.lib'.format(out_path)) + self.assertTrue(len(files)>0) + + # Make sure there is any .html file + if os.path.exists(out_path): + datasheets = glob.glob('{0}/*html'.format(out_path)) + self.assertTrue(len(datasheets)>0) + + # grep any errors from the output + output_log = open("{0}/output.log".format(out_path),"r") + output = output_log.read() + output_log.close() + self.assertEqual(len(re.findall('ERROR',output)),0) + self.assertEqual(len(re.findall('WARNING',output)),0) + + + # now clean up the directory + if OPTS.purge_temp: + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path),False) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/config_20_freepdk45.py b/compiler/tests/config_20_freepdk45.py deleted file mode 100755 index aaaa4c37..00000000 --- a/compiler/tests/config_20_freepdk45.py +++ /dev/null @@ -1,9 +0,0 @@ -word_size = 1 -num_words = 16 - -tech_name = "freepdk45" -process_corners = ["TT"] -supply_voltages = [1.0] -temperatures = [25] - - diff --git a/compiler/tests/config_20_scn3me_subm.py b/compiler/tests/config_20_scn3me_subm.py deleted file mode 100755 index 330d463b..00000000 --- a/compiler/tests/config_20_scn3me_subm.py +++ /dev/null @@ -1,8 +0,0 @@ -word_size = 1 -num_words = 16 - -tech_name = "scn3me_subm" -process_corners = ["TT"] -supply_voltages = [5.0] -temperatures = [25] - diff --git a/compiler/tests/config_20_scn4m_subm.py b/compiler/tests/config_20_scn4m_subm.py deleted file mode 100755 index e847745b..00000000 --- a/compiler/tests/config_20_scn4m_subm.py +++ /dev/null @@ -1,12 +0,0 @@ -word_size = 1 -num_words = 16 - -tech_name = "scn4m_subm" -process_corners = ["TT"] -supply_voltages = [5.0] -temperatures = [25] - -drc_name = "magic" -lvs_name = "netgen" -pex_name = "magic" - diff --git a/compiler/tests/config_freepdk45.py b/compiler/tests/config_freepdk45.py new file mode 100644 index 00000000..3103217f --- /dev/null +++ b/compiler/tests/config_freepdk45.py @@ -0,0 +1,18 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +word_size = 1 +num_words = 16 + +tech_name = "freepdk45" +process_corners = ["TT"] +supply_voltages = [1.0] +temperatures = [25] + +route_supplies = True +check_lvsdrc = True + diff --git a/compiler/tests/config_freepdk45_back_end.py b/compiler/tests/config_freepdk45_back_end.py new file mode 100644 index 00000000..68417a3b --- /dev/null +++ b/compiler/tests/config_freepdk45_back_end.py @@ -0,0 +1,20 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +word_size = 1 +num_words = 16 + +tech_name = "freepdk45" +process_corners = ["TT"] +supply_voltages = [1.0] +temperatures = [25] + +inline_lvsdrc = True +route_supplies = True +check_lvsdrc = True +analytical_delay = False + diff --git a/compiler/tests/config_freepdk45_front_end.py b/compiler/tests/config_freepdk45_front_end.py new file mode 100644 index 00000000..1886d808 --- /dev/null +++ b/compiler/tests/config_freepdk45_front_end.py @@ -0,0 +1,18 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +word_size = 1 +num_words = 16 + +tech_name = "freepdk45" +process_corners = ["TT"] +supply_voltages = [1.0] +temperatures = [25] + +analytical_delay = False + + diff --git a/compiler/tests/config_scn3me_subm.py b/compiler/tests/config_scn3me_subm.py new file mode 100644 index 00000000..7b5b5e15 --- /dev/null +++ b/compiler/tests/config_scn3me_subm.py @@ -0,0 +1,22 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +word_size = 1 +num_words = 16 + +tech_name = "scn3me_subm" +process_corners = ["TT"] +supply_voltages = [5.0] +temperatures = [25] + +route_supplies = True +check_lvsdrc = True + +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" + diff --git a/compiler/tests/config_scn3me_subm_back_end.py b/compiler/tests/config_scn3me_subm_back_end.py new file mode 100644 index 00000000..f9c23417 --- /dev/null +++ b/compiler/tests/config_scn3me_subm_back_end.py @@ -0,0 +1,23 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +word_size = 1 +num_words = 16 + +tech_name = "scn3me_subm" +process_corners = ["TT"] +supply_voltages = [5.0] +temperatures = [25] + +route_supplies = True +check_lvsdrc = True +inline_lvsdrc = True +analytical_delay = False + +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" diff --git a/compiler/tests/config_scn3me_subm_front_end.py b/compiler/tests/config_scn3me_subm_front_end.py new file mode 100644 index 00000000..40504a18 --- /dev/null +++ b/compiler/tests/config_scn3me_subm_front_end.py @@ -0,0 +1,21 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +word_size = 1 +num_words = 16 + +tech_name = "scn3me_subm" +process_corners = ["TT"] +supply_voltages = [5.0] +temperatures = [25] + +analytical_delay = False + +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" + diff --git a/compiler/tests/config_scn4m_subm.py b/compiler/tests/config_scn4m_subm.py new file mode 100644 index 00000000..abb31435 --- /dev/null +++ b/compiler/tests/config_scn4m_subm.py @@ -0,0 +1,22 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +word_size = 1 +num_words = 16 + +tech_name = "scn4m_subm" +process_corners = ["TT"] +supply_voltages = [5.0] +temperatures = [25] + +route_supplies = True +check_lvsdrc = True + +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" + diff --git a/compiler/tests/config_scn4m_subm_back_end.py b/compiler/tests/config_scn4m_subm_back_end.py new file mode 100644 index 00000000..35e4cd91 --- /dev/null +++ b/compiler/tests/config_scn4m_subm_back_end.py @@ -0,0 +1,23 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +word_size = 1 +num_words = 16 + +tech_name = "scn4m_subm" +process_corners = ["TT"] +supply_voltages = [5.0] +temperatures = [25] + +route_supplies = True +check_lvsdrc = True +inline_lvsdrc = True +analytical_delay = False + +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" diff --git a/compiler/tests/config_scn4m_subm_front_end.py b/compiler/tests/config_scn4m_subm_front_end.py new file mode 100644 index 00000000..142191a0 --- /dev/null +++ b/compiler/tests/config_scn4m_subm_front_end.py @@ -0,0 +1,19 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +word_size = 1 +num_words = 16 + +tech_name = "scn4m_subm" +process_corners = ["TT"] +supply_voltages = [5.0] +temperatures = [25] + +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" + diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.lef b/compiler/tests/golden/sram_2_16_1_freepdk45.lef index 5a80802d..15ad3a88 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45.lef +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.lef @@ -3,7 +3,7 @@ NAMESCASESENSITIVE ON ; BUSBITCHARS "[]" ; DIVIDERCHAR "/" ; UNITS - DATABASE MICRONS 1000 ; + dataBASE MICRONS 1000 ; END UNITS SITE MacroSite CLASS Core ; @@ -14,48 +14,48 @@ MACRO sram_2_16_1_freepdk45 SIZE 12145.0 BY 43967.5 ; SYMMETRY X Y R90 ; SITE MacroSite ; - PIN DATA[0] + PIN data[0] DIRECTION INOUT ; PORT LAYER metal2 ; RECT 10260.0 67.5 10330.0 207.5 ; END - END DATA[0] - PIN DATA[1] + END data[0] + PIN data[1] DIRECTION INOUT ; PORT LAYER metal2 ; RECT 10965.0 67.5 11035.0 207.5 ; END - END DATA[1] - PIN ADDR[0] + END data[1] + PIN addr[0] DIRECTION INPUT ; PORT LAYER metal3 ; RECT 67.5 8370.0 837.5 8440.0 ; END - END ADDR[0] - PIN ADDR[1] + END addr[0] + PIN addr[1] DIRECTION INPUT ; PORT LAYER metal3 ; RECT 67.5 7665.0 837.5 7735.0 ; END - END ADDR[1] - PIN ADDR[2] + END addr[1] + PIN addr[2] DIRECTION INPUT ; PORT LAYER metal3 ; RECT 67.5 6960.0 837.5 7030.0 ; END - END ADDR[2] - PIN ADDR[3] + END addr[2] + PIN addr[3] DIRECTION INPUT ; PORT LAYER metal3 ; RECT 67.5 6255.0 837.5 6325.0 ; END - END ADDR[3] + END addr[3] PIN CSb DIRECTION INPUT ; PORT diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.sp b/compiler/tests/golden/sram_2_16_1_freepdk45.sp index 90500f05..c499390e 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45.sp +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.sp @@ -136,10 +136,10 @@ Xpmos1 vdd A net1 vdd nor_2_pmos121 Xpmos2 net1 B Z vdd nor_2_pmos222 .ENDS nor2 -.SUBCKT msf_control DATA[0] DATA[1] DATA[2] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] data_in[2] data_in_bar[2] clk vdd gnd -XXdff0 DATA[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop -XXdff1 DATA[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop -XXdff2 DATA[2] data_in[2] data_in_bar[2] clk vdd gnd ms_flop +.SUBCKT msf_control data[0] data[1] data[2] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] data_in[2] data_in_bar[2] clk vdd gnd +XXdff0 data[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop +XXdff1 data[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop +XXdff2 data[2] data_in[2] data_in_bar[2] clk vdd gnd ms_flop .ENDS msf_control .SUBCKT replica_cell_6t bl br wl vdd gnd @@ -524,16 +524,16 @@ XINVERTER_[14] Z[14] decode_out[14] vdd gnd INVERTER XINVERTER_[15] Z[15] decode_out[15] vdd gnd INVERTER .ENDS hierarchical_decoder -.SUBCKT msf_address ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] addr_clk vdd gnd -XXdff0 ADDR[0] A[0] A_bar[0] addr_clk vdd gnd ms_flop -XXdff1 ADDR[1] A[1] A_bar[1] addr_clk vdd gnd ms_flop -XXdff2 ADDR[2] A[2] A_bar[2] addr_clk vdd gnd ms_flop -XXdff3 ADDR[3] A[3] A_bar[3] addr_clk vdd gnd ms_flop +.SUBCKT msf_address addr[0] addr[1] addr[2] addr[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] addr_clk vdd gnd +XXdff0 addr[0] A[0] A_bar[0] addr_clk vdd gnd ms_flop +XXdff1 addr[1] A[1] A_bar[1] addr_clk vdd gnd ms_flop +XXdff2 addr[2] A[2] A_bar[2] addr_clk vdd gnd ms_flop +XXdff3 addr[3] A[3] A_bar[3] addr_clk vdd gnd ms_flop .ENDS msf_address -.SUBCKT msf_data_in DATA[0] DATA[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk vdd gnd -XXdff0 DATA[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop -XXdff1 DATA[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop +.SUBCKT msf_data_in data[0] data[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk vdd gnd +XXdff0 data[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop +XXdff1 data[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop .ENDS msf_data_in .SUBCKT msf_data_out data_out[0] data_out[1] tri_in[0] tri_in_bar[0] tri_in[1] tri_in_bar[1] sclk vdd gnd @@ -551,9 +551,9 @@ M_6 in_inv in gnd gnd NMOS_VTG W=90.000000n L=50.000000n .ENDS -.SUBCKT tri_gate_array tri_in[0] tri_in[1] DATA[0] DATA[1] en en_bar vdd gnd -XXtri_gate0 tri_in[0] DATA[0] en en_bar vdd gnd tri_gate -XXtri_gate1 tri_in[1] DATA[1] en en_bar vdd gnd tri_gate +.SUBCKT tri_gate_array tri_in[0] tri_in[1] data[0] data[1] en en_bar vdd gnd +XXtri_gate0 tri_in[0] data[0] en en_bar vdd gnd tri_gate +XXtri_gate1 tri_in[1] data[1] en en_bar vdd gnd tri_gate .ENDS tri_gate_array .SUBCKT wordline_driver decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_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 vdd gnd @@ -643,19 +643,19 @@ Xpmos1 vdd A net1 vdd nor_2_pmos185 Xpmos2 net1 B Z vdd nor_2_pmos286 .ENDS NOR2 -.SUBCKT test_bank1 DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd +.SUBCKT test_bank1 data[0] data[1] addr[0] addr[1] addr[2] addr[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd Xbitcell_array bl[0] br[0] bl[1] br[1] 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] clk_bar vdd precharge_array Xsense_amp_array bl[0] br[0] bl[1] br[1] data_out[0] data_out[1] s_en vdd gnd sense_amp_array Xwrite_driver_array data_in[0] data_in[1] bl[0] br[0] bl[1] br[1] w_en vdd gnd write_driver_array -Xdata_in_flop_array DATA[0] DATA[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk_bar vdd gnd msf_data_in -Xtrigate_data_array data_out[0] data_out[1] DATA[0] DATA[1] tri_en tri_en_bar vdd gnd tri_gate_array +Xdata_in_flop_array data[0] data[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk_bar vdd gnd msf_data_in +Xtrigate_data_array data_out[0] data_out[1] data[0] data[1] tri_en tri_en_bar vdd gnd tri_gate_array Xaddress_decoder A[0] A[1] A[2] A[3] decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] vdd gnd hierarchical_decoder Xwordline_driver decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_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 vdd gnd wordline_driver -Xaddress_flop_array ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] clk vdd gnd msf_address +Xaddress_flop_array addr[0] addr[1] addr[2] addr[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] clk vdd gnd msf_address .ENDS test_bank1 -.SUBCKT testsram DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb OEb clk vdd gnd -Xbank0 DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd test_bank1 +.SUBCKT testsram data[0] data[1] addr[0] addr[1] addr[2] addr[3] CSb WEb OEb clk vdd gnd +Xbank0 data[0] data[1] addr[0] addr[1] addr[2] addr[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd test_bank1 Xcontrol CSb WEb OEb s_en w_en tri_en tri_en_bar clk_bar clk vdd gnd control_logic .ENDS testsram diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.v b/compiler/tests/golden/sram_2_16_1_freepdk45.v index 025350bc..5edd2fee 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45.v +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.v @@ -4,7 +4,7 @@ module sram_2_16_1_freepdk45( // Port 0: RW - clk0,csb0,web0,ADDR0,DIN0,DOUT0 + clk0,csb0,web0,addr0,din0,dout0 ); parameter DATA_WIDTH = 2 ; @@ -16,28 +16,28 @@ module sram_2_16_1_freepdk45( input clk0; // clock input csb0; // active low chip select input web0; // active low write control - input [ADDR_WIDTH-1:0] ADDR0; - input [DATA_WIDTH-1:0] DIN0; - output [DATA_WIDTH-1:0] DOUT0; + input [ADDR_WIDTH-1:0] addr0; + input [DATA_WIDTH-1:0] din0; + output [DATA_WIDTH-1:0] dout0; reg csb0_reg; reg web0_reg; - reg [ADDR_WIDTH-1:0] ADDR0_reg; - reg [DATA_WIDTH-1:0] DIN0_reg; - reg [DATA_WIDTH-1:0] DOUT0; + reg [ADDR_WIDTH-1:0] addr0_reg; + reg [DATA_WIDTH-1:0] din0_reg; + reg [DATA_WIDTH-1:0] dout0; // All inputs are registers always @(posedge clk0) begin csb0_reg = csb0; web0_reg = web0; - ADDR0_reg = ADDR0; - DIN0_reg = DIN0; - DOUT0 = 2'bx; + addr0_reg = addr0; + din0_reg = din0; + dout0 = 2'bx; if ( !csb0_reg && web0_reg ) - $display($time," Reading %m ADDR0=%b DOUT0=%b",ADDR0_reg,mem[ADDR0_reg]); + $display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]); if ( !csb0_reg && !web0_reg ) - $display($time," Writing %m ADDR0=%b DIN0=%b",ADDR0_reg,DIN0_reg); + $display($time," Writing %m addr0=%b din0=%b",addr0_reg,din0_reg); end reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; @@ -47,7 +47,7 @@ reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; always @ (negedge clk0) begin : MEM_WRITE0 if ( !csb0_reg && !web0_reg ) - mem[ADDR0_reg] = DIN0_reg; + mem[addr0_reg] = din0_reg; end // Memory Read Block Port 0 @@ -55,7 +55,7 @@ reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; always @ (negedge clk0) begin : MEM_READ0 if (!csb0_reg && web0_reg) - DOUT0 <= #(DELAY) mem[ADDR0_reg]; + dout0 <= #(DELAY) mem[addr0_reg]; end endmodule diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_FF_1p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_FF_1p0V_25C_analytical.lib index 88b54eca..a1c5a04b 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_FF_1p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_FF_1p0V_25C_analytical.lib @@ -1,10 +1,10 @@ library (sram_2_16_1_freepdk45_FF_1p0V_25C_lib){ delay_model : "table_lookup"; time_unit : "1ns" ; - voltage_unit : "1v" ; + voltage_unit : "1V" ; current_unit : "1mA" ; resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; + capacitive_load_unit(1, pF) ; leakage_power_unit : "1mW" ; pulling_resistance_unit :"1kohm" ; operating_conditions(OC){ @@ -52,7 +52,7 @@ library (sram_2_16_1_freepdk45_FF_1p0V_25C_lib){ default_operating_conditions : OC; - type (DATA){ + type (data){ base_type : array; data_type : bit; bit_width : 2; @@ -60,7 +60,7 @@ library (sram_2_16_1_freepdk45_FF_1p0V_25C_lib){ bit_to : 1; } - type (ADDR){ + type (addr){ base_type : array; data_type : bit; bit_width : 4; @@ -81,19 +81,19 @@ cell (sram_2_16_1_freepdk45){ area : 1124.88; leakage_power () { - when : "CSb0"; + when : "csb0"; value : 0.000167; } cell_leakage_power : 0; - bus(DIN0){ - bus_type : DATA; + bus(din0){ + bus_type : data; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR0; + address : addr0; clocked_on : clk0; } - pin(DIN0[1:0]){ + pin(din0[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -124,15 +124,15 @@ cell (sram_2_16_1_freepdk45){ } } } - bus(DOUT0){ - bus_type : DATA; + bus(dout0){ + bus_type : data; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR0; + address : addr0; } - pin(DOUT0[1:0]){ + pin(dout0[1:0]){ timing(){ timing_sense : non_unate; related_pin : "clk0"; @@ -161,12 +161,12 @@ cell (sram_2_16_1_freepdk45){ } } - bus(ADDR0){ - bus_type : ADDR; + bus(addr0){ + bus_type : addr; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR0[3:0]){ + pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -198,7 +198,7 @@ cell (sram_2_16_1_freepdk45){ } } - pin(CSb0){ + pin(csb0){ direction : input; capacitance : 0.2091; timing(){ @@ -231,7 +231,7 @@ cell (sram_2_16_1_freepdk45){ } } - pin(WEb0){ + pin(web0){ direction : input; capacitance : 0.2091; timing(){ @@ -269,7 +269,7 @@ cell (sram_2_16_1_freepdk45){ direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb0 & clk0 & !WEb0"; + when : "!csb0 & clk0 & !web0"; rise_power(scalar){ values("0.033101244168888884"); } @@ -278,7 +278,7 @@ cell (sram_2_16_1_freepdk45){ } } internal_power(){ - when : "!CSb0 & !clk0 & WEb0"; + when : "!csb0 & !clk0 & web0"; rise_power(scalar){ values("0.033101244168888884"); } @@ -287,7 +287,7 @@ cell (sram_2_16_1_freepdk45){ } } internal_power(){ - when : "CSb0"; + when : "csb0"; rise_power(scalar){ values("0"); } diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_SS_1p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_SS_1p0V_25C_analytical.lib index e78fe8d3..865daada 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_SS_1p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_SS_1p0V_25C_analytical.lib @@ -1,10 +1,10 @@ library (sram_2_16_1_freepdk45_SS_1p0V_25C_lib){ delay_model : "table_lookup"; time_unit : "1ns" ; - voltage_unit : "1v" ; + voltage_unit : "1V" ; current_unit : "1mA" ; resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; + capacitive_load_unit(1, pF) ; leakage_power_unit : "1mW" ; pulling_resistance_unit :"1kohm" ; operating_conditions(OC){ @@ -52,7 +52,7 @@ library (sram_2_16_1_freepdk45_SS_1p0V_25C_lib){ default_operating_conditions : OC; - type (DATA){ + type (data){ base_type : array; data_type : bit; bit_width : 2; @@ -60,7 +60,7 @@ library (sram_2_16_1_freepdk45_SS_1p0V_25C_lib){ bit_to : 1; } - type (ADDR){ + type (addr){ base_type : array; data_type : bit; bit_width : 4; @@ -81,19 +81,19 @@ cell (sram_2_16_1_freepdk45){ area : 1124.88; leakage_power () { - when : "CSb0"; + when : "csb0"; value : 0.000167; } cell_leakage_power : 0; - bus(DIN0){ - bus_type : DATA; + bus(din0){ + bus_type : data; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR0; + address : addr0; clocked_on : clk0; } - pin(DIN0[1:0]){ + pin(din0[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -124,15 +124,15 @@ cell (sram_2_16_1_freepdk45){ } } } - bus(DOUT0){ - bus_type : DATA; + bus(dout0){ + bus_type : data; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR0; + address : addr0; } - pin(DOUT0[1:0]){ + pin(dout0[1:0]){ timing(){ timing_sense : non_unate; related_pin : "clk0"; @@ -161,12 +161,12 @@ cell (sram_2_16_1_freepdk45){ } } - bus(ADDR0){ - bus_type : ADDR; + bus(addr0){ + bus_type : addr; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR0[3:0]){ + pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -198,7 +198,7 @@ cell (sram_2_16_1_freepdk45){ } } - pin(CSb0){ + pin(csb0){ direction : input; capacitance : 0.2091; timing(){ @@ -231,7 +231,7 @@ cell (sram_2_16_1_freepdk45){ } } - pin(WEb0){ + pin(web0){ direction : input; capacitance : 0.2091; timing(){ @@ -269,7 +269,7 @@ cell (sram_2_16_1_freepdk45){ direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb0 & clk0 & !WEb0"; + when : "!csb0 & clk0 & !web0"; rise_power(scalar){ values("0.033101244168888884"); } @@ -278,7 +278,7 @@ cell (sram_2_16_1_freepdk45){ } } internal_power(){ - when : "!CSb0 & !clk0 & WEb0"; + when : "!csb0 & !clk0 & web0"; rise_power(scalar){ values("0.033101244168888884"); } @@ -287,7 +287,7 @@ cell (sram_2_16_1_freepdk45){ } } internal_power(){ - when : "CSb0"; + when : "csb0"; rise_power(scalar){ values("0"); } 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 0939b4bb..72d01a0f 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 @@ -1,10 +1,10 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ delay_model : "table_lookup"; time_unit : "1ns" ; - voltage_unit : "1v" ; + voltage_unit : "1V" ; current_unit : "1mA" ; resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; + capacitive_load_unit(1, pF) ; leakage_power_unit : "1mW" ; pulling_resistance_unit :"1kohm" ; operating_conditions(OC){ @@ -52,7 +52,7 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ default_operating_conditions : OC; - type (DATA){ + type (data){ base_type : array; data_type : bit; bit_width : 2; @@ -60,7 +60,7 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ bit_to : 1; } - type (ADDR){ + type (addr){ base_type : array; data_type : bit; bit_width : 4; @@ -81,19 +81,19 @@ cell (sram_2_16_1_freepdk45){ area : 977.4951374999999; leakage_power () { - when : "CSb0"; + when : "csb0"; value : 0.0011164579999999999; } cell_leakage_power : 0; - bus(DIN0){ - bus_type : DATA; + bus(din0){ + bus_type : data; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR0; + address : addr0; clocked_on : clk0; } - pin(DIN0[1:0]){ + pin(din0[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -124,15 +124,15 @@ cell (sram_2_16_1_freepdk45){ } } } - bus(DOUT0){ - bus_type : DATA; + bus(dout0){ + bus_type : data; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR0; + address : addr0; } - pin(DOUT0[1:0]){ + pin(dout0[1:0]){ timing(){ timing_sense : non_unate; related_pin : "clk0"; @@ -161,12 +161,12 @@ cell (sram_2_16_1_freepdk45){ } } - bus(ADDR0){ - bus_type : ADDR; + bus(addr0){ + bus_type : addr; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR0[3:0]){ + pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -198,7 +198,7 @@ cell (sram_2_16_1_freepdk45){ } } - pin(CSb0){ + pin(csb0){ direction : input; capacitance : 0.2091; timing(){ @@ -231,7 +231,7 @@ cell (sram_2_16_1_freepdk45){ } } - pin(WEb0){ + pin(web0){ direction : input; capacitance : 0.2091; timing(){ @@ -269,7 +269,7 @@ cell (sram_2_16_1_freepdk45){ direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb0 & clk0 & !WEb0"; + when : "!csb0 & clk0 & !web0"; rise_power(scalar){ values("0.03599689694444445"); } @@ -278,7 +278,7 @@ cell (sram_2_16_1_freepdk45){ } } internal_power(){ - when : "!CSb0 & !clk0 & WEb0"; + when : "!csb0 & !clk0 & web0"; rise_power(scalar){ values("0.029906643888888886"); } @@ -287,7 +287,7 @@ cell (sram_2_16_1_freepdk45){ } } internal_power(){ - when : "CSb0"; + when : "csb0"; rise_power(scalar){ values("0"); } 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 a06ff5b5..33587063 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 @@ -1,10 +1,10 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ delay_model : "table_lookup"; time_unit : "1ns" ; - voltage_unit : "1v" ; + voltage_unit : "1V" ; current_unit : "1mA" ; resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; + capacitive_load_unit(1, pF) ; leakage_power_unit : "1mW" ; pulling_resistance_unit :"1kohm" ; operating_conditions(OC){ @@ -52,7 +52,7 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ default_operating_conditions : OC; - type (DATA){ + type (data){ base_type : array; data_type : bit; bit_width : 2; @@ -60,7 +60,7 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ bit_to : 1; } - type (ADDR){ + type (addr){ base_type : array; data_type : bit; bit_width : 4; @@ -81,19 +81,19 @@ cell (sram_2_16_1_freepdk45){ area : 977.4951374999999; leakage_power () { - when : "CSb0"; + when : "csb0"; value : 0.000179; } cell_leakage_power : 0; - bus(DIN0){ - bus_type : DATA; + bus(din0){ + bus_type : data; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR0; + address : addr0; clocked_on : clk0; } - pin(DIN0[1:0]){ + pin(din0[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -124,15 +124,15 @@ cell (sram_2_16_1_freepdk45){ } } } - bus(DOUT0){ - bus_type : DATA; + bus(dout0){ + bus_type : data; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR0; + address : addr0; } - pin(DOUT0[1:0]){ + pin(dout0[1:0]){ timing(){ timing_sense : non_unate; related_pin : "clk0"; @@ -161,12 +161,12 @@ cell (sram_2_16_1_freepdk45){ } } - bus(ADDR0){ - bus_type : ADDR; + bus(addr0){ + bus_type : addr; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR0[3:0]){ + pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -198,7 +198,7 @@ cell (sram_2_16_1_freepdk45){ } } - pin(CSb0){ + pin(csb0){ direction : input; capacitance : 0.2091; timing(){ @@ -231,7 +231,7 @@ cell (sram_2_16_1_freepdk45){ } } - pin(WEb0){ + pin(web0){ direction : input; capacitance : 0.2091; timing(){ @@ -269,7 +269,7 @@ cell (sram_2_16_1_freepdk45){ direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb0 & clk0 & !WEb0"; + when : "!csb0 & clk0 & !web0"; rise_power(scalar){ values("0.0747594982142222"); } @@ -278,7 +278,7 @@ cell (sram_2_16_1_freepdk45){ } } internal_power(){ - when : "!CSb0 & !clk0 & WEb0"; + when : "!csb0 & !clk0 & web0"; rise_power(scalar){ values("0.0747594982142222"); } @@ -287,7 +287,7 @@ cell (sram_2_16_1_freepdk45){ } } internal_power(){ - when : "CSb0"; + when : "csb0"; rise_power(scalar){ values("0"); } 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 d7d7de7e..5817211b 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 @@ -1,10 +1,10 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ delay_model : "table_lookup"; time_unit : "1ns" ; - voltage_unit : "1v" ; + voltage_unit : "1V" ; current_unit : "1mA" ; resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; + capacitive_load_unit(1, pF) ; leakage_power_unit : "1mW" ; pulling_resistance_unit :"1kohm" ; operating_conditions(OC){ @@ -52,7 +52,7 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ default_operating_conditions : OC; - type (DATA){ + type (data){ base_type : array; data_type : bit; bit_width : 2; @@ -60,7 +60,7 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ bit_to : 1; } - type (ADDR){ + type (addr){ base_type : array; data_type : bit; bit_width : 4; @@ -81,19 +81,19 @@ cell (sram_2_16_1_freepdk45){ area : 977.4951374999999; leakage_power () { - when : "CSb0"; + when : "csb0"; value : 0.0011164579999999999; } cell_leakage_power : 0; - bus(DIN0){ - bus_type : DATA; + bus(din0){ + bus_type : data; direction : input; capacitance : 0.2091; memory_write(){ - address : ADDR0; + address : addr0; clocked_on : clk0; } - pin(DIN0[1:0]){ + pin(din0[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -124,15 +124,15 @@ cell (sram_2_16_1_freepdk45){ } } } - bus(DOUT0){ - bus_type : DATA; + bus(dout0){ + bus_type : data; direction : output; max_capacitance : 1.6728; min_capacitance : 0.052275; memory_read(){ - address : ADDR0; + address : addr0; } - pin(DOUT0[1:0]){ + pin(dout0[1:0]){ timing(){ timing_sense : non_unate; related_pin : "clk0"; @@ -161,12 +161,12 @@ cell (sram_2_16_1_freepdk45){ } } - bus(ADDR0){ - bus_type : ADDR; + bus(addr0){ + bus_type : addr; direction : input; capacitance : 0.2091; max_transition : 0.04; - pin(ADDR0[3:0]){ + pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -198,7 +198,7 @@ cell (sram_2_16_1_freepdk45){ } } - pin(CSb0){ + pin(csb0){ direction : input; capacitance : 0.2091; timing(){ @@ -231,7 +231,7 @@ cell (sram_2_16_1_freepdk45){ } } - pin(WEb0){ + pin(web0){ direction : input; capacitance : 0.2091; timing(){ @@ -269,7 +269,7 @@ cell (sram_2_16_1_freepdk45){ direction : input; capacitance : 0.2091; internal_power(){ - when : "!CSb0 & clk0 & !WEb0"; + when : "!csb0 & clk0 & !web0"; rise_power(scalar){ values("0.03334771594444444"); } @@ -278,7 +278,7 @@ cell (sram_2_16_1_freepdk45){ } } internal_power(){ - when : "!CSb0 & !clk0 & WEb0"; + when : "!csb0 & !clk0 & web0"; rise_power(scalar){ values("0.028457026222222223"); } @@ -287,7 +287,7 @@ cell (sram_2_16_1_freepdk45){ } } internal_power(){ - when : "CSb0"; + when : "csb0"; rise_power(scalar){ values("0"); } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm.lef b/compiler/tests/golden/sram_2_16_1_scn4m_subm.lef index 9d784677..08ebcdb2 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm.lef +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm.lef @@ -3,7 +3,7 @@ NAMESCASESENSITIVE ON ; BUSBITCHARS "[]" ; DIVIDERCHAR "/" ; UNITS - DATABASE MICRONS 1000 ; + dataBASE MICRONS 1000 ; END UNITS SITE MacroSite CLASS Core ; @@ -14,48 +14,48 @@ MACRO sram_2_16_1_scn3me_subm SIZE 148050.0 BY 461850.0 ; SYMMETRY X Y R90 ; SITE MacroSite ; - PIN DATA[0] + PIN data[0] DIRECTION INOUT ; PORT LAYER metal2 ; RECT 120900.0 0.0 121800.0 1800.0 ; END - END DATA[0] - PIN DATA[1] + END data[0] + PIN data[1] DIRECTION INOUT ; PORT LAYER metal2 ; RECT 131100.0 0.0 132000.0 1800.0 ; END - END DATA[1] - PIN ADDR[0] + END data[1] + PIN addr[0] DIRECTION INPUT ; PORT LAYER metal3 ; RECT 0.0 87600.0 10800.0 89100.0 ; END - END ADDR[0] - PIN ADDR[1] + END addr[0] + PIN addr[1] DIRECTION INPUT ; PORT LAYER metal3 ; RECT 0.0 77400.0 10800.0 78900.0 ; END - END ADDR[1] - PIN ADDR[2] + END addr[1] + PIN addr[2] DIRECTION INPUT ; PORT LAYER metal3 ; RECT 0.0 67200.0 10800.0 68700.0 ; END - END ADDR[2] - PIN ADDR[3] + END addr[2] + PIN addr[3] DIRECTION INPUT ; PORT LAYER metal3 ; RECT 0.0 57000.0 10800.0 58500.0 ; END - END ADDR[3] + END addr[3] PIN CSb DIRECTION INPUT ; PORT diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm.sp b/compiler/tests/golden/sram_2_16_1_scn4m_subm.sp index 258b4464..1b8d4e07 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm.sp +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm.sp @@ -136,10 +136,10 @@ Xpmos1 vdd A net1 vdd nor_2_pmos125 Xpmos2 net1 B Z vdd nor_2_pmos226 .ENDS nor2 -.SUBCKT msf_control DATA[0] DATA[1] DATA[2] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] data_in[2] data_in_bar[2] clk vdd gnd -XXdff0 DATA[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop -XXdff1 DATA[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop -XXdff2 DATA[2] data_in[2] data_in_bar[2] clk vdd gnd ms_flop +.SUBCKT msf_control data[0] data[1] data[2] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] data_in[2] data_in_bar[2] clk vdd gnd +XXdff0 data[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop +XXdff1 data[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop +XXdff2 data[2] data_in[2] data_in_bar[2] clk vdd gnd ms_flop .ENDS msf_control *********************** "cell_6t" ****************************** @@ -541,16 +541,16 @@ XINVERTER_[14] Z[14] decode_out[14] vdd gnd INVERTER XINVERTER_[15] Z[15] decode_out[15] vdd gnd INVERTER .ENDS hierarchical_decoder -.SUBCKT msf_address ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] addr_clk vdd gnd -XXdff0 ADDR[0] A[0] A_bar[0] addr_clk vdd gnd ms_flop -XXdff1 ADDR[1] A[1] A_bar[1] addr_clk vdd gnd ms_flop -XXdff2 ADDR[2] A[2] A_bar[2] addr_clk vdd gnd ms_flop -XXdff3 ADDR[3] A[3] A_bar[3] addr_clk vdd gnd ms_flop +.SUBCKT msf_address addr[0] addr[1] addr[2] addr[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] addr_clk vdd gnd +XXdff0 addr[0] A[0] A_bar[0] addr_clk vdd gnd ms_flop +XXdff1 addr[1] A[1] A_bar[1] addr_clk vdd gnd ms_flop +XXdff2 addr[2] A[2] A_bar[2] addr_clk vdd gnd ms_flop +XXdff3 addr[3] A[3] A_bar[3] addr_clk vdd gnd ms_flop .ENDS msf_address -.SUBCKT msf_data_in DATA[0] DATA[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk vdd gnd -XXdff0 DATA[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop -XXdff1 DATA[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop +.SUBCKT msf_data_in data[0] data[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk vdd gnd +XXdff0 data[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop +XXdff1 data[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop .ENDS msf_data_in .SUBCKT msf_data_out data_out[0] data_out[1] tri_in[0] tri_in_bar[0] tri_in[1] tri_in_bar[1] sclk vdd gnd @@ -571,9 +571,9 @@ M_6 in_inv in gnd gnd n W='1.2*1u' L=0.6u .ENDS -.SUBCKT tri_gate_array tri_in[0] tri_in[1] DATA[0] DATA[1] en en_bar vdd gnd -XXtri_gate0 tri_in[0] DATA[0] en en_bar vdd gnd tri_gate -XXtri_gate1 tri_in[1] DATA[1] en en_bar vdd gnd tri_gate +.SUBCKT tri_gate_array tri_in[0] tri_in[1] data[0] data[1] en en_bar vdd gnd +XXtri_gate0 tri_in[0] data[0] en en_bar vdd gnd tri_gate +XXtri_gate1 tri_in[1] data[1] en en_bar vdd gnd tri_gate .ENDS tri_gate_array .SUBCKT wordline_driver decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_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 vdd gnd @@ -663,19 +663,19 @@ Xpmos1 vdd A net1 vdd nor_2_pmos197 Xpmos2 net1 B Z vdd nor_2_pmos298 .ENDS NOR2 -.SUBCKT test_bank1 DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd +.SUBCKT test_bank1 data[0] data[1] addr[0] addr[1] addr[2] addr[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd Xbitcell_array bl[0] br[0] bl[1] br[1] 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] clk_bar vdd precharge_array Xsense_amp_array bl[0] br[0] bl[1] br[1] data_out[0] data_out[1] s_en vdd gnd sense_amp_array Xwrite_driver_array data_in[0] data_in[1] bl[0] br[0] bl[1] br[1] w_en vdd gnd write_driver_array -Xdata_in_flop_array DATA[0] DATA[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk_bar vdd gnd msf_data_in -Xtrigate_data_array data_out[0] data_out[1] DATA[0] DATA[1] tri_en tri_en_bar vdd gnd tri_gate_array +Xdata_in_flop_array data[0] data[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk_bar vdd gnd msf_data_in +Xtrigate_data_array data_out[0] data_out[1] data[0] data[1] tri_en tri_en_bar vdd gnd tri_gate_array Xaddress_decoder A[0] A[1] A[2] A[3] decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] vdd gnd hierarchical_decoder Xwordline_driver decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_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 vdd gnd wordline_driver -Xaddress_flop_array ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] clk vdd gnd msf_address +Xaddress_flop_array addr[0] addr[1] addr[2] addr[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] clk vdd gnd msf_address .ENDS test_bank1 -.SUBCKT testsram DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb OEb clk vdd gnd -Xbank0 DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd test_bank1 +.SUBCKT testsram data[0] data[1] addr[0] addr[1] addr[2] addr[3] CSb WEb OEb clk vdd gnd +Xbank0 data[0] data[1] addr[0] addr[1] addr[2] addr[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd test_bank1 Xcontrol CSb WEb OEb s_en w_en tri_en tri_en_bar clk_bar clk vdd gnd control_logic .ENDS testsram diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm.v b/compiler/tests/golden/sram_2_16_1_scn4m_subm.v index 7017b8a7..cec47c19 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm.v +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm.v @@ -4,7 +4,7 @@ module sram_2_16_1_scn4m_subm( // Port 0: RW - clk0,csb0,web0,ADDR0,DIN0,DOUT0 + clk0,csb0,web0,addr0,din0,dout0 ); parameter DATA_WIDTH = 2 ; @@ -16,28 +16,28 @@ module sram_2_16_1_scn4m_subm( input clk0; // clock input csb0; // active low chip select input web0; // active low write control - input [ADDR_WIDTH-1:0] ADDR0; - input [DATA_WIDTH-1:0] DIN0; - output [DATA_WIDTH-1:0] DOUT0; + input [ADDR_WIDTH-1:0] addr0; + input [DATA_WIDTH-1:0] din0; + output [DATA_WIDTH-1:0] dout0; reg csb0_reg; reg web0_reg; - reg [ADDR_WIDTH-1:0] ADDR0_reg; - reg [DATA_WIDTH-1:0] DIN0_reg; - reg [DATA_WIDTH-1:0] DOUT0; + reg [ADDR_WIDTH-1:0] addr0_reg; + reg [DATA_WIDTH-1:0] din0_reg; + reg [DATA_WIDTH-1:0] dout0; // All inputs are registers always @(posedge clk0) begin csb0_reg = csb0; web0_reg = web0; - ADDR0_reg = ADDR0; - DIN0_reg = DIN0; - DOUT0 = 2'bx; + addr0_reg = addr0; + din0_reg = din0; + dout0 = 2'bx; if ( !csb0_reg && web0_reg ) - $display($time," Reading %m ADDR0=%b DOUT0=%b",ADDR0_reg,mem[ADDR0_reg]); + $display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]); if ( !csb0_reg && !web0_reg ) - $display($time," Writing %m ADDR0=%b DIN0=%b",ADDR0_reg,DIN0_reg); + $display($time," Writing %m addr0=%b din0=%b",addr0_reg,din0_reg); end reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; @@ -47,7 +47,7 @@ reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; always @ (negedge clk0) begin : MEM_WRITE0 if ( !csb0_reg && !web0_reg ) - mem[ADDR0_reg] = DIN0_reg; + mem[addr0_reg] = din0_reg; end // Memory Read Block Port 0 @@ -55,7 +55,7 @@ reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; always @ (negedge clk0) begin : MEM_READ0 if (!csb0_reg && web0_reg) - DOUT0 <= #(DELAY) mem[ADDR0_reg]; + dout0 <= #(DELAY) mem[addr0_reg]; end endmodule diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_FF_5p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_FF_5p0V_25C_analytical.lib index 5cbecd94..c370f993 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_FF_5p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_FF_5p0V_25C_analytical.lib @@ -1,10 +1,10 @@ library (sram_2_16_1_scn4m_subm_FF_5p0V_25C_lib){ delay_model : "table_lookup"; time_unit : "1ns" ; - voltage_unit : "1v" ; + voltage_unit : "1V" ; current_unit : "1mA" ; resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; + capacitive_load_unit(1, pF) ; leakage_power_unit : "1mW" ; pulling_resistance_unit :"1kohm" ; operating_conditions(OC){ @@ -52,7 +52,7 @@ library (sram_2_16_1_scn4m_subm_FF_5p0V_25C_lib){ default_operating_conditions : OC; - type (DATA){ + type (data){ base_type : array; data_type : bit; bit_width : 2; @@ -60,7 +60,7 @@ library (sram_2_16_1_scn4m_subm_FF_5p0V_25C_lib){ bit_to : 1; } - type (ADDR){ + type (addr){ base_type : array; data_type : bit; bit_width : 4; @@ -81,19 +81,19 @@ cell (sram_2_16_1_scn4m_subm){ area : 73068.14000000001; leakage_power () { - when : "CSb0"; + when : "csb0"; value : 0.000167; } cell_leakage_power : 0; - bus(DIN0){ - bus_type : DATA; + bus(din0){ + bus_type : data; direction : input; capacitance : 9.8242; memory_write(){ - address : ADDR0; + address : addr0; clocked_on : clk0; } - pin(DIN0[1:0]){ + pin(din0[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -124,15 +124,15 @@ cell (sram_2_16_1_scn4m_subm){ } } } - bus(DOUT0){ - bus_type : DATA; + bus(dout0){ + bus_type : data; direction : output; max_capacitance : 78.5936; min_capacitance : 2.45605; memory_read(){ - address : ADDR0; + address : addr0; } - pin(DOUT0[1:0]){ + pin(dout0[1:0]){ timing(){ timing_sense : non_unate; related_pin : "clk0"; @@ -161,12 +161,12 @@ cell (sram_2_16_1_scn4m_subm){ } } - bus(ADDR0){ - bus_type : ADDR; + bus(addr0){ + bus_type : addr; direction : input; capacitance : 9.8242; max_transition : 0.4; - pin(ADDR0[3:0]){ + pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -198,7 +198,7 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(CSb0){ + pin(csb0){ direction : input; capacitance : 9.8242; timing(){ @@ -231,7 +231,7 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(WEb0){ + pin(web0){ direction : input; capacitance : 9.8242; timing(){ @@ -269,7 +269,7 @@ cell (sram_2_16_1_scn4m_subm){ direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk0 & !WEb0"; + when : "!csb0 & clk0 & !web0"; rise_power(scalar){ values("4.99880645"); } @@ -278,7 +278,7 @@ cell (sram_2_16_1_scn4m_subm){ } } internal_power(){ - when : "!CSb0 & !clk0 & WEb0"; + when : "!csb0 & !clk0 & web0"; rise_power(scalar){ values("4.99880645"); } @@ -287,7 +287,7 @@ cell (sram_2_16_1_scn4m_subm){ } } internal_power(){ - when : "CSb0"; + when : "csb0"; rise_power(scalar){ values("0"); } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_SS_5p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_SS_5p0V_25C_analytical.lib index 3324f119..f52de676 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_SS_5p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_SS_5p0V_25C_analytical.lib @@ -1,10 +1,10 @@ library (sram_2_16_1_scn4m_subm_SS_5p0V_25C_lib){ delay_model : "table_lookup"; time_unit : "1ns" ; - voltage_unit : "1v" ; + voltage_unit : "1V" ; current_unit : "1mA" ; resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; + capacitive_load_unit(1, pF) ; leakage_power_unit : "1mW" ; pulling_resistance_unit :"1kohm" ; operating_conditions(OC){ @@ -52,7 +52,7 @@ library (sram_2_16_1_scn4m_subm_SS_5p0V_25C_lib){ default_operating_conditions : OC; - type (DATA){ + type (data){ base_type : array; data_type : bit; bit_width : 2; @@ -60,7 +60,7 @@ library (sram_2_16_1_scn4m_subm_SS_5p0V_25C_lib){ bit_to : 1; } - type (ADDR){ + type (addr){ base_type : array; data_type : bit; bit_width : 4; @@ -81,19 +81,19 @@ cell (sram_2_16_1_scn4m_subm){ area : 73068.14000000001; leakage_power () { - when : "CSb0"; + when : "csb0"; value : 0.000167; } cell_leakage_power : 0; - bus(DIN0){ - bus_type : DATA; + bus(din0){ + bus_type : data; direction : input; capacitance : 9.8242; memory_write(){ - address : ADDR0; + address : addr0; clocked_on : clk0; } - pin(DIN0[1:0]){ + pin(din0[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -124,15 +124,15 @@ cell (sram_2_16_1_scn4m_subm){ } } } - bus(DOUT0){ - bus_type : DATA; + bus(dout0){ + bus_type : data; direction : output; max_capacitance : 78.5936; min_capacitance : 2.45605; memory_read(){ - address : ADDR0; + address : addr0; } - pin(DOUT0[1:0]){ + pin(dout0[1:0]){ timing(){ timing_sense : non_unate; related_pin : "clk0"; @@ -161,12 +161,12 @@ cell (sram_2_16_1_scn4m_subm){ } } - bus(ADDR0){ - bus_type : ADDR; + bus(addr0){ + bus_type : addr; direction : input; capacitance : 9.8242; max_transition : 0.4; - pin(ADDR0[3:0]){ + pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -198,7 +198,7 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(CSb0){ + pin(csb0){ direction : input; capacitance : 9.8242; timing(){ @@ -231,7 +231,7 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(WEb0){ + pin(web0){ direction : input; capacitance : 9.8242; timing(){ @@ -269,7 +269,7 @@ cell (sram_2_16_1_scn4m_subm){ direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk0 & !WEb0"; + when : "!csb0 & clk0 & !web0"; rise_power(scalar){ values("4.99880645"); } @@ -278,7 +278,7 @@ cell (sram_2_16_1_scn4m_subm){ } } internal_power(){ - when : "!CSb0 & !clk0 & WEb0"; + when : "!csb0 & !clk0 & web0"; rise_power(scalar){ values("4.99880645"); } @@ -287,7 +287,7 @@ cell (sram_2_16_1_scn4m_subm){ } } internal_power(){ - when : "CSb0"; + when : "csb0"; rise_power(scalar){ values("0"); } 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 f8a337d3..7447a1e2 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 @@ -1,10 +1,10 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ delay_model : "table_lookup"; time_unit : "1ns" ; - voltage_unit : "1v" ; + voltage_unit : "1V" ; current_unit : "1mA" ; resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; + capacitive_load_unit(1, pF) ; leakage_power_unit : "1mW" ; pulling_resistance_unit :"1kohm" ; operating_conditions(OC){ @@ -52,7 +52,7 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ default_operating_conditions : OC; - type (DATA){ + type (data){ base_type : array; data_type : bit; bit_width : 2; @@ -60,7 +60,7 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ bit_to : 1; } - type (ADDR){ + type (addr){ base_type : array; data_type : bit; bit_width : 4; @@ -81,19 +81,19 @@ cell (sram_2_16_1_scn4m_subm){ area : 60774.3; leakage_power () { - when : "CSb0"; + when : "csb0"; value : 0.0009813788999999999; } cell_leakage_power : 0; - bus(DIN0){ - bus_type : DATA; + bus(din0){ + bus_type : data; direction : input; capacitance : 9.8242; memory_write(){ - address : ADDR0; + address : addr0; clocked_on : clk0; } - pin(DIN0[1:0]){ + pin(din0[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -124,15 +124,15 @@ cell (sram_2_16_1_scn4m_subm){ } } } - bus(DOUT0){ - bus_type : DATA; + bus(dout0){ + bus_type : data; direction : output; max_capacitance : 78.5936; min_capacitance : 2.45605; memory_read(){ - address : ADDR0; + address : addr0; } - pin(DOUT0[1:0]){ + pin(dout0[1:0]){ timing(){ timing_sense : non_unate; related_pin : "clk0"; @@ -161,12 +161,12 @@ cell (sram_2_16_1_scn4m_subm){ } } - bus(ADDR0){ - bus_type : ADDR; + bus(addr0){ + bus_type : addr; direction : input; capacitance : 9.8242; max_transition : 0.4; - pin(ADDR0[3:0]){ + pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -198,7 +198,7 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(CSb0){ + pin(csb0){ direction : input; capacitance : 9.8242; timing(){ @@ -231,7 +231,7 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(WEb0){ + pin(web0){ direction : input; capacitance : 9.8242; timing(){ @@ -269,7 +269,7 @@ cell (sram_2_16_1_scn4m_subm){ direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk0 & !WEb0"; + when : "!csb0 & clk0 & !web0"; rise_power(scalar){ values("9.972790277777777"); } @@ -278,7 +278,7 @@ cell (sram_2_16_1_scn4m_subm){ } } internal_power(){ - when : "!CSb0 & !clk0 & WEb0"; + when : "!csb0 & !clk0 & web0"; rise_power(scalar){ values("8.899322499999998"); } @@ -287,7 +287,7 @@ cell (sram_2_16_1_scn4m_subm){ } } internal_power(){ - when : "CSb0"; + when : "csb0"; rise_power(scalar){ values("0"); } 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 72a106ac..4248b986 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 @@ -1,10 +1,10 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ delay_model : "table_lookup"; time_unit : "1ns" ; - voltage_unit : "1v" ; + voltage_unit : "1V" ; current_unit : "1mA" ; resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; + capacitive_load_unit(1, pF) ; leakage_power_unit : "1mW" ; pulling_resistance_unit :"1kohm" ; operating_conditions(OC){ @@ -52,7 +52,7 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ default_operating_conditions : OC; - type (DATA){ + type (data){ base_type : array; data_type : bit; bit_width : 2; @@ -60,7 +60,7 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ bit_to : 1; } - type (ADDR){ + type (addr){ base_type : array; data_type : bit; bit_width : 4; @@ -81,19 +81,19 @@ cell (sram_2_16_1_scn4m_subm){ area : 60774.3; leakage_power () { - when : "CSb0"; + when : "csb0"; value : 0.000179; } cell_leakage_power : 0; - bus(DIN0){ - bus_type : DATA; + bus(din0){ + bus_type : data; direction : input; capacitance : 9.8242; memory_write(){ - address : ADDR0; + address : addr0; clocked_on : clk0; } - pin(DIN0[1:0]){ + pin(din0[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -124,15 +124,15 @@ cell (sram_2_16_1_scn4m_subm){ } } } - bus(DOUT0){ - bus_type : DATA; + bus(dout0){ + bus_type : data; direction : output; max_capacitance : 78.5936; min_capacitance : 2.45605; memory_read(){ - address : ADDR0; + address : addr0; } - pin(DOUT0[1:0]){ + pin(dout0[1:0]){ timing(){ timing_sense : non_unate; related_pin : "clk0"; @@ -161,12 +161,12 @@ cell (sram_2_16_1_scn4m_subm){ } } - bus(ADDR0){ - bus_type : ADDR; + bus(addr0){ + bus_type : addr; direction : input; capacitance : 9.8242; max_transition : 0.4; - pin(ADDR0[3:0]){ + pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -198,7 +198,7 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(CSb0){ + pin(csb0){ direction : input; capacitance : 9.8242; timing(){ @@ -231,7 +231,7 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(WEb0){ + pin(web0){ direction : input; capacitance : 9.8242; timing(){ @@ -269,7 +269,7 @@ cell (sram_2_16_1_scn4m_subm){ direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk0 & !WEb0"; + when : "!csb0 & clk0 & !web0"; rise_power(scalar){ values("11.3049604371"); } @@ -278,7 +278,7 @@ cell (sram_2_16_1_scn4m_subm){ } } internal_power(){ - when : "!CSb0 & !clk0 & WEb0"; + when : "!csb0 & !clk0 & web0"; rise_power(scalar){ values("11.3049604371"); } @@ -287,7 +287,7 @@ cell (sram_2_16_1_scn4m_subm){ } } internal_power(){ - when : "CSb0"; + when : "csb0"; rise_power(scalar){ values("0"); } 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 124f80ff..7b649d0d 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 @@ -1,10 +1,10 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ delay_model : "table_lookup"; time_unit : "1ns" ; - voltage_unit : "1v" ; + voltage_unit : "1V" ; current_unit : "1mA" ; resistance_unit : "1kohm" ; - capacitive_load_unit(1 ,fF) ; + capacitive_load_unit(1, pF) ; leakage_power_unit : "1mW" ; pulling_resistance_unit :"1kohm" ; operating_conditions(OC){ @@ -52,7 +52,7 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ default_operating_conditions : OC; - type (DATA){ + type (data){ base_type : array; data_type : bit; bit_width : 2; @@ -60,7 +60,7 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ bit_to : 1; } - type (ADDR){ + type (addr){ base_type : array; data_type : bit; bit_width : 4; @@ -81,19 +81,19 @@ cell (sram_2_16_1_scn4m_subm){ area : 60774.3; leakage_power () { - when : "CSb0"; + when : "csb0"; value : 0.0009813788999999999; } cell_leakage_power : 0; - bus(DIN0){ - bus_type : DATA; + bus(din0){ + bus_type : data; direction : input; capacitance : 9.8242; memory_write(){ - address : ADDR0; + address : addr0; clocked_on : clk0; } - pin(DIN0[1:0]){ + pin(din0[1:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -124,15 +124,15 @@ cell (sram_2_16_1_scn4m_subm){ } } } - bus(DOUT0){ - bus_type : DATA; + bus(dout0){ + bus_type : data; direction : output; max_capacitance : 78.5936; min_capacitance : 2.45605; memory_read(){ - address : ADDR0; + address : addr0; } - pin(DOUT0[1:0]){ + pin(dout0[1:0]){ timing(){ timing_sense : non_unate; related_pin : "clk0"; @@ -161,12 +161,12 @@ cell (sram_2_16_1_scn4m_subm){ } } - bus(ADDR0){ - bus_type : ADDR; + bus(addr0){ + bus_type : addr; direction : input; capacitance : 9.8242; max_transition : 0.4; - pin(ADDR0[3:0]){ + pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -198,7 +198,7 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(CSb0){ + pin(csb0){ direction : input; capacitance : 9.8242; timing(){ @@ -231,7 +231,7 @@ cell (sram_2_16_1_scn4m_subm){ } } - pin(WEb0){ + pin(web0){ direction : input; capacitance : 9.8242; timing(){ @@ -269,7 +269,7 @@ cell (sram_2_16_1_scn4m_subm){ direction : input; capacitance : 9.8242; internal_power(){ - when : "!CSb0 & clk0 & !WEb0"; + when : "!csb0 & clk0 & !web0"; rise_power(scalar){ values("9.602821763527778"); } @@ -278,7 +278,7 @@ cell (sram_2_16_1_scn4m_subm){ } } internal_power(){ - when : "!CSb0 & !clk0 & WEb0"; + when : "!csb0 & !clk0 & web0"; rise_power(scalar){ values("8.647938152416664"); } @@ -287,7 +287,7 @@ cell (sram_2_16_1_scn4m_subm){ } } internal_power(){ - when : "CSb0"; + when : "csb0"; rise_power(scalar){ values("0"); } diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py index d97def2b..e60b010d 100755 --- a/compiler/tests/regress.py +++ b/compiler/tests/regress.py @@ -1,15 +1,22 @@ #!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import re import unittest import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) import globals (OPTS, args) = globals.parse_args() del sys.argv[1:] -from testutils import header,openram_test +from testutils import * header(__file__, OPTS.tech_name) # get a list of all files in the tests directory diff --git a/compiler/tests/sram_1rw_1r_tb.v b/compiler/tests/sram_1rw_1r_tb.v index 9c212e0a..3a2194e1 100644 --- a/compiler/tests/sram_1rw_1r_tb.v +++ b/compiler/tests/sram_1rw_1r_tb.v @@ -19,14 +19,14 @@ module sram_1rw_1r_tb; reg csb1; wire [1:0] dout1; - sram_1rw_1r_2_16_scn4m_subm U0 (.DIN0(din0), - .DOUT0(dout0), - .ADDR0(addr0), + sram_1rw_1r_2_16_scn4m_subm U0 (.din0(din0), + .dout0(dout0), + .addr0(addr0), .csb0(csb0), .web0(web0), .clk0(clk), - .DOUT1(dout1), - .ADDR1(addr1), + .dout1(dout1), + .addr1(addr1), .csb1(csb1), .clk1(clk) ); diff --git a/compiler/tests/sram_1rw_tb.v b/compiler/tests/sram_1rw_tb.v index 31f120e8..f4f6cb33 100644 --- a/compiler/tests/sram_1rw_tb.v +++ b/compiler/tests/sram_1rw_tb.v @@ -13,9 +13,9 @@ module sram_1rw_tb; reg web0; wire [1:0] dout0; - sram_2_16_scn4m_subm U0 (.DIN0(din0), - .DOUT0(dout0), - .ADDR0(addr0), + sram_2_16_scn4m_subm U0 (.din0(din0), + .dout0(dout0), + .addr0(addr0), .csb0(csb0), .web0(web0), .clk0(clk) diff --git a/compiler/tests/sram_1rw_wmask_tb.v b/compiler/tests/sram_1rw_wmask_tb.v new file mode 100644 index 00000000..51d03d6b --- /dev/null +++ b/compiler/tests/sram_1rw_wmask_tb.v @@ -0,0 +1,110 @@ +`define assert(signal, value) \ +if (!(signal === value)) begin \ + $display("ASSERTION FAILED in %m: signal != value"); \ + $finish;\ +end + +module sram_1rw_wmask_tb; + reg clk; + + reg [3:0] addr0; + reg [1:0] din0; + reg csb0; + reg web0; + reg [1:0] wmask0; + wire [1:0] dout0; + + sram_2b_16_1rw_freepdk45 U0 (.din0(din0), + .dout0(dout0), + .addr0(addr0), + .csb0(csb0), + .web0(web0), + .wmask0(wmask0), + .clk0(clk) + ); + + + initial + begin + //$monitor("%g addr0=%b din0=%b dout0=%b", + // $time, addr0, din0, dout0); + + + clk = 1; + csb0 = 1; + web0 = 1; + wmask0 = 2'b01; + addr0 = 0; + din0 = 0; + + // write + #10 din0=2'b10; + addr0=4'h1; + web0 = 0; + csb0 = 0; + wmask0 = 2'b10; + + // read + #10 din0=2'b11; + addr0=4'h1; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'b1x) + + // write another + #10 din0=2'b01; + addr0=4'hC; + web0 = 0; + csb0 = 0; + wmask0 = 2'b01; + + // read undefined + #10 din0=2'b11; + addr0=4'h0; + web0 = 1; + csb0 = 0; + wmask0 = 2'b01; + + #10 `assert(dout0, 2'bxx) + + // read defined + din0=2'b11; + addr0=4'hC; + web0 = 1; + csb0 = 0; + wmask0 = 2'b01; + + #10 `assert(dout0, 2'bx1) + + // write another + din0=2'b01; + addr0=4'h1; + web0 = 0; + csb0 = 0; + + // read defined + #10 din0=2'b11; + addr0=4'h1; + web0 = 1; + csb0 = 0; + + + #10 `assert(dout0, 2'b11) + + // read undefined + din0=2'b11; + addr0=4'h0; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'bxx) + + #10 $finish; + + end + + always + #5 clk = !clk; + +endmodule \ No newline at end of file diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py old mode 100755 new mode 100644 index 799c223e..95675c38 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -1,14 +1,22 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import unittest,warnings +import pdb,traceback import sys,os,glob,copy import shutil -sys.path.append(os.path.join(sys.path[0],"..")) +sys.path.append(os.getenv("OPENRAM_HOME")) from globals import OPTS import debug class openram_test(unittest.TestCase): """ Base unit test that we have some shared classes in. """ - + def local_drc_check(self, w): self.reset() @@ -23,11 +31,11 @@ class openram_test(unittest.TestCase): if OPTS.purge_temp: self.cleanup() - + def local_check(self, a, final_verification=False): self.reset() - + tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name) tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name) @@ -44,7 +52,7 @@ class openram_test(unittest.TestCase): #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("DRC failed: {}".format(a.name)) - + result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification) if result != 0: #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) @@ -52,9 +60,22 @@ class openram_test(unittest.TestCase): #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("LVS mismatch: {}".format(a.name)) + # For debug... + #import pdb; pdb.set_trace() if OPTS.purge_temp: self.cleanup() - + + def run_pex(self, a, output=None): + if output == None: + output = OPTS.openram_temp + a.name + ".pex.netlist" + tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name) + + import verify + result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False) + if result != 0: + self.fail("PEX ERROR: {}".format(a.name)) + return output def find_feasible_test_period(self, delay_obj, sram, load, slew): """Creates a delay simulation to determine a feasible period for the functional tests to run. @@ -62,24 +83,21 @@ class openram_test(unittest.TestCase): """ debug.info(1, "Finding feasible period for current test.") delay_obj.set_load_slew(load, slew) - delay_obj.set_probe(probe_address="1"*sram.addr_size, probe_data=(sram.word_size-1)) test_port = delay_obj.read_ports[0] #Only test one port, assumes other ports have similar period. - delay_obj.create_signal_names() - delay_obj.create_measurement_names() - delay_obj.create_measurement_objects() - delay_obj.find_feasible_period_one_port(test_port) - return delay_obj.period - + delay_obj.analysis_init(probe_address="1"*sram.addr_size, probe_data=(sram.word_size-1)) + delay_obj.find_feasible_period_one_port(test_port) + return delay_obj.period + def cleanup(self): """ Reset the duplicate checker and cleanup files. """ files = glob.glob(OPTS.openram_temp + '*') for f in files: # Only remove the files if os.path.isfile(f): - os.remove(f) + os.remove(f) def reset(self): - """ + """ Reset everything after each test. """ # Reset the static duplicate name checker for unit tests. @@ -108,7 +126,7 @@ class openram_test(unittest.TestCase): data_string=pprint.pformat(data) debug.error("Results exceeded {:.1f}% tolerance compared to golden results:\n".format(error_tolerance*100)+data_string) return data_matches - + def isclose(self,key,value,actual_value,error_tolerance=1e-2): @@ -124,7 +142,7 @@ class openram_test(unittest.TestCase): return False def relative_diff(self, value1, value2): - """ Compute the relative difference of two values and normalize to the largest. + """ Compute the relative difference of two values and normalize to the largest. If largest value is 0, just return the difference.""" # Edge case to avoid divide by zero @@ -140,7 +158,7 @@ class openram_test(unittest.TestCase): # Edge case where greater is a zero if norm_value == 0: min_value = abs(min(value1, value2)) - + return abs(value1 - value2) / norm_value @@ -154,15 +172,15 @@ class openram_test(unittest.TestCase): """Compare two files. Arguments: - + filename1 -- First file name - + filename2 -- Second file name Return value: - + True if the files are the same, False otherwise. - + """ import re import debug @@ -195,7 +213,7 @@ class openram_test(unittest.TestCase): debug.info(3,"line1_floats: "+str(line1_floats)) debug.info(3,"line2_floats: "+str(line2_floats)) - + # 2. Remove the floats from the string for f in line1_floats: line1=line1.replace(f,"",1) @@ -207,10 +225,10 @@ class openram_test(unittest.TestCase): # 3. Convert to floats rather than strings line1_floats = [float(x) for x in line1_floats] line2_floats = [float(x) for x in line1_floats] - + # 4. Check if remaining string matches if line1 != line2: - #Uncomment if you want to see all the individual chars of the two lines + #Uncomment if you want to see all the individual chars of the two lines #print(str([i for i in line1])) #print(str([i for i in line2])) if mismatches==0: @@ -273,12 +291,13 @@ class openram_test(unittest.TestCase): debug.info(2,"MATCH {0} {1}".format(filename1,filename2)) return True + def header(filename, technology): # Skip the header for gitlab regression import getpass if getpass.getuser() == "gitlab-runner": return - + tst = "Running Test for:" print("\n") print(" ______________________________________________________________________________ ") @@ -289,3 +308,21 @@ def header(filename, technology): from globals import OPTS print("|=========" + OPTS.openram_temp.center(60) + "=========|") print("|==============================================================================|") + +def debugTestRunner(post_mortem=None): + """unittest runner doing post mortem debugging on failing tests""" + if post_mortem is None and not OPTS.purge_temp: + post_mortem = pdb.post_mortem + class DebugTestResult(unittest.TextTestResult): + def addError(self, test, err): + # called before tearDown() + traceback.print_exception(*err) + if post_mortem: + post_mortem(err[2]) + super(DebugTestResult, self).addError(test, err) + def addFailure(self, test, err): + traceback.print_exception(*err) + if post_mortem: + post_mortem(err[2]) + super(DebugTestResult, self).addFailure(test, err) + return unittest.TextTestRunner(resultclass=DebugTestResult) diff --git a/compiler/verify/__init__.py b/compiler/verify/__init__.py index 1f0ffaab..042fecff 100644 --- a/compiler/verify/__init__.py +++ b/compiler/verify/__init__.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ This is a module that will import the correct DRC/LVS/PEX module based on what tools are found. It is a layer of indirection @@ -26,9 +33,6 @@ else: OPTS.lvs_exe = get_tool("LVS", ["calibre","assura","netgen"], OPTS.lvs_name) OPTS.pex_exe = get_tool("PEX", ["calibre","magic"], OPTS.pex_name) -if OPTS.check_lvsdrc and OPTS.tech_name == "freepdk45": - debug.check(OPTS.drc_exe[0]!="magic","Magic does not support FreePDK45 for DRC.") - if OPTS.drc_exe == None: from .none import run_drc,print_drc_stats elif "calibre"==OPTS.drc_exe[0]: diff --git a/compiler/verify/assura.py b/compiler/verify/assura.py index af034730..d1d6146f 100644 --- a/compiler/verify/assura.py +++ b/compiler/verify/assura.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ This is a DRC/LVS interface for Assura. It implements completely two functions: run_drc and run_lvs, that perform these functions in batch diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 5f8d2c73..8abca448 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -1,82 +1,37 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ This is a DRC/LVS interface for calibre. It implements completely -independently two functions: run_drc and run_lvs, that perform these +independently three functions: run_drc, run_lvs, run_pex, that perform these functions in batch mode and will return true/false if the result passes. All of the setup (the rules, temp dirs, etc.) should be contained in this file. Replacing with another DRC/LVS tool involves rewriting this code to work properly. Porting to a new technology in Calibre means pointing the code to the proper DRC and LVS rule files. -A calibre DRC runset file contains, at the minimum, the following information: - -*drcRulesFile: /mada/software/techfiles/FreePDK45/ncsu_basekit/techfile/calibre/calibreDRC.rul -*drcRunDir: . -*drcLayoutPaths: ./cell_6t.gds -*drcLayoutPrimary: cell_6t -*drcLayoutSystem: GDSII -*drcResultsformat: ASCII -*drcResultsFile: cell_6t.drc.results -*drcSummaryFile: cell_6t.drc.summary -*cmnFDILayerMapFile: ./layer.map -*cmnFDIUseLayerMap: 1 - -This can be executed in "batch" mode with the following command: - -calibre -gui -drc example_drc_runset -batch - -To open the results, you can do this: - -calibredrv cell_6t.gds -Select Verification->Start RVE. -Select the cell_6t.drc.results file. -Click on the errors and they will highlight in the design layout viewer. - -For LVS: - -*lvsRulesFile: /mada/software/techfiles/FreePDK45/ncsu_basekit/techfile/calibre/calibreLVS.rul -*lvsRunDir: . -*lvsLayoutPaths: ./cell_6t.gds -*lvsLayoutPrimary: cell_6t -*lvsSourcePath: ./cell_6t.sp -*lvsSourcePrimary: cell_6t -*lvsSourceSystem: SPICE -*lvsSpiceFile: extracted.sp -*lvsPowerNames: vdd -*lvsGroundNames: vss -*lvsIgnorePorts: 1 -*lvsERCDatabase: cell_6t.erc.results -*lvsERCSummaryFile: cell_6t.erc.summary -*lvsReportFile: cell_6t.lvs.report -*lvsMaskDBFile: cell_6t.maskdb -*cmnFDILayerMapFile: ./layer.map -*cmnFDIUseLayerMap: 1 - -To run and see results: - -calibre -gui -lvs example_lvs_runset -batch -more cell_6t.lvs.report """ import os +import shutil import re import time import debug from globals import OPTS -import subprocess +from run_script import * # Keep track of statistics num_drc_runs = 0 num_lvs_runs = 0 num_pex_runs = 0 -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.""" - - global num_drc_runs - num_drc_runs += 1 - +def write_calibre_drc_script(cell_name, extract, final_verification): + """ Write a Calibre runset file and script to run DRC """ # the runset file contains all the options to run calibre from tech import drc drc_rules = drc["drc_rules"] @@ -84,12 +39,12 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False): drc_runset = { 'drcRulesFile': drc_rules, 'drcRunDir': OPTS.openram_temp, - 'drcLayoutPaths': gds_name, + 'drcLayoutPaths': cell_name + ".gds", 'drcLayoutPrimary': cell_name, 'drcLayoutSystem': 'GDSII', 'drcResultsformat': 'ASCII', - 'drcResultsFile': OPTS.openram_temp + cell_name + ".drc.results", - 'drcSummaryFile': OPTS.openram_temp + cell_name + ".drc.summary", + 'drcResultsFile': cell_name + ".drc.results", + 'drcSummaryFile': cell_name + ".drc.summary", 'cmnFDILayerMapFile': drc["layer_map"], 'cmnFDIUseLayerMap': 1 } @@ -100,26 +55,151 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False): f.write("*{0}: {1}\n".format(k, drc_runset[k])) f.close() - # run drc - cwd = os.getcwd() - os.chdir(OPTS.openram_temp) - errfile = "{0}{1}.drc.err".format(OPTS.openram_temp, cell_name) - outfile = "{0}{1}.drc.out".format(OPTS.openram_temp, cell_name) + # Create an auxiliary script to run calibre with the runset + run_file = OPTS.openram_temp + "run_drc.sh" + f = open(run_file, "w") + f.write("#!/bin/sh\n") + cmd = "{0} -gui -drc {1}drc_runset -batch".format(OPTS.drc_exe[1], + OPTS.openram_temp) + f.write(cmd) + f.write("\n") + f.close() + os.system("chmod u+x {}".format(run_file)) + return drc_runset - cmd = "{0} -gui -drc {1}drc_runset -batch 2> {2} 1> {3}".format(OPTS.drc_exe[1], - OPTS.openram_temp, - errfile, - outfile) - debug.info(2, cmd) - os.system(cmd) - os.chdir(cwd) +def write_calibre_lvs_script(cell_name, final_verification): + """ Write a Calibre runset file and script to run LVS """ + + from tech import drc + lvs_rules = drc["lvs_rules"] + lvs_runset = { + 'lvsRulesFile': lvs_rules, + 'lvsRunDir': OPTS.openram_temp, + 'lvsLayoutPaths': cell_name + ".gds", + 'lvsLayoutPrimary': cell_name, + 'lvsSourcePath': cell_name + ".sp", + 'lvsSourcePrimary': cell_name, + 'lvsSourceSystem': 'SPICE', + 'lvsSpiceFile': "extracted.sp", + 'lvsPowerNames': 'vdd', + 'lvsGroundNames': 'gnd', + 'lvsIncludeSVRFCmds': 1, + 'lvsIgnorePorts': 1, + 'lvsERCDatabase': cell_name + ".erc.results", + 'lvsERCSummaryFile': cell_name + ".erc.summary", + 'lvsReportFile': cell_name + ".lvs.report", + 'lvsMaskDBFile': cell_name + ".maskdb", + 'cmnFDILayerMapFile': drc["layer_map"], + '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 + + 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 + f = open(OPTS.openram_temp + "lvs_runset", "w") + for k in sorted(iter(lvs_runset.keys())): + f.write("*{0}: {1}\n".format(k, lvs_runset[k])) + f.close() + + # Create an auxiliary script to run calibre with the runset + run_file = OPTS.openram_temp + "run_lvs.sh" + f = open(run_file, "w") + f.write("#!/bin/sh\n") + PDK_DIR=os.environ.get("PDK_DIR") + f.write("export PDK_DIR={}\n".format(PDK_DIR)) + cmd = "{0} -gui -lvs {1}lvs_runset -batch".format(OPTS.lvs_exe[1], + OPTS.openram_temp) + f.write(cmd) + f.write("\n") + f.close() + os.system("chmod u+x {}".format(run_file)) + + return lvs_runset + +def write_calibre_pex_script(cell_name, extract, output, final_verification): + + if output == None: + output = name + ".pex.netlist" + + # check if lvs report has been done + # if not run drc and lvs + if not os.path.isfile(cell_name + ".lvs.report"): + gds_name = OPTS.openram_temp +"/"+ cell_name + ".gds" + sp_name = OPTS.openram_temp +"/"+ cell_name + ".sp" + run_drc(cell_name, gds_name) + run_lvs(cell_name, gds_name, sp_name) + + from tech import drc + pex_rules = drc["xrc_rules"] + pex_runset = { + 'pexRulesFile': pex_rules, + 'pexRunDir': OPTS.openram_temp, + 'pexLayoutPaths': cell_name + ".gds", + 'pexLayoutPrimary': cell_name, + #'pexSourcePath' : OPTS.openram_temp+"extracted.sp", + 'pexSourcePath': cell_name + ".sp", + 'pexSourcePrimary': cell_name, + 'pexReportFile': cell_name + ".lvs.report", + 'pexPexNetlistFile': cell_name + ".pex.netlist", + 'pexPexReportFile': cell_name + ".pex.report", + 'pexMaskDBFile': cell_name + ".maskdb", + 'cmnFDIDEFLayoutPath': cell_name + ".def", + } + + # write the runset file + f = open(OPTS.openram_temp + "pex_runset", "w") + for k in sorted(iter(pex_runset.keys())): + f.write("*{0}: {1}\n".format(k, pex_runset[k])) + f.close() + + # Create an auxiliary script to run calibre with the runset + run_file = OPTS.openram_temp + "run_pex.sh" + f = open(run_file, "w") + f.write("#!/bin/sh\n") + cmd = "{0} -gui -pex {1}pex_runset -batch".format(OPTS.pex_exe[1], + OPTS.openram_temp) + f.write(cmd) + f.write("\n") + f.close() + os.system("chmod u+x {}".format(run_file)) + + return pex_runset + +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.""" + + global num_drc_runs + num_drc_runs += 1 + + # Copy file to local dir if it isn't already + if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): + shutil.copy(gds_name, OPTS.openram_temp) + + drc_runset = write_calibre_drc_script(cell_name, extract, final_verification) + + (outfile, errfile, resultsfile) = run_script(cell_name, "drc") # check the result for these lines in the summary: # TOTAL Original Layer Geometries: 106 (157) # TOTAL DRC RuleChecks Executed: 156 # TOTAL DRC Results Generated: 0 (0) try: - f = open(drc_runset['drcSummaryFile'], "r") + f = open(OPTS.openram_temp + drc_runset['drcSummaryFile'], "r") except: debug.error("Unable to retrieve DRC results file. Is calibre set up?",1) results = f.readlines() @@ -132,12 +212,12 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False): # always display this summary if errors > 0: - debug.error("{0}\tGeometries: {1}\tChecks: {2}\tErrors: {3}".format(cell_name, + debug.error("{0}\tGeometries: {1}\tChecks: {2}\tErrors: {3}".format(cell_name, geometries, rulechecks, errors)) else: - debug.info(1, "{0}\tGeometries: {1}\tChecks: {2}\tErrors: {3}".format(cell_name, + debug.info(1, "{0}\tGeometries: {1}\tChecks: {2}\tErrors: {3}".format(cell_name, geometries, rulechecks, errors)) @@ -148,71 +228,22 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): """Run LVS check on a given top-level name which is implemented in gds_name and sp_name. Final verification will ensure that there are no remaining virtual conections. """ - + global num_lvs_runs num_lvs_runs += 1 - - from tech import drc - lvs_rules = drc["lvs_rules"] - lvs_runset = { - 'lvsRulesFile': lvs_rules, - 'lvsRunDir': OPTS.openram_temp, - 'lvsLayoutPaths': gds_name, - 'lvsLayoutPrimary': cell_name, - 'lvsSourcePath': sp_name, - 'lvsSourcePrimary': cell_name, - 'lvsSourceSystem': 'SPICE', - 'lvsSpiceFile': OPTS.openram_temp + "extracted.sp", - 'lvsPowerNames': 'vdd', - 'lvsGroundNames': 'gnd', - 'lvsIncludeSVRFCmds': 1, - 'lvsIgnorePorts': 1, - 'lvsERCDatabase': OPTS.openram_temp + cell_name + ".erc.results", - 'lvsERCSummaryFile': OPTS.openram_temp + cell_name + ".erc.summary", - 'lvsReportFile': OPTS.openram_temp + cell_name + ".lvs.report", - 'lvsMaskDBFile': OPTS.openram_temp + cell_name + ".maskdb", - 'cmnFDILayerMapFile': drc["layer_map"], - '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 - if not final_verification: - lvs_runset['cmnVConnectReport']=1 - lvs_runset['cmnVConnectNamesState']='SOME' - lvs_runset['cmnVConnectNames']='vdd gnd' - else: - lvs_runset['lvsAbortOnSupplyError']=1 - + lvs_runset = write_calibre_lvs_script(cell_name, final_verification) + # Copy file to local dir if it isn't already + if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): + shutil.copy(gds_name, OPTS.openram_temp) + if os.path.dirname(sp_name)!=OPTS.openram_temp.rstrip('/'): + shutil.copy(sp_name, OPTS.openram_temp) - # write the runset file - f = open(OPTS.openram_temp + "lvs_runset", "w") - for k in sorted(iter(lvs_runset.keys())): - f.write("*{0}: {1}\n".format(k, lvs_runset[k])) - f.close() - - # run LVS - cwd = os.getcwd() - os.chdir(OPTS.openram_temp) - errfile = "{0}{1}.lvs.err".format(OPTS.openram_temp, cell_name) - outfile = "{0}{1}.lvs.out".format(OPTS.openram_temp, cell_name) - - cmd = "{0} -gui -lvs {1}lvs_runset -batch 2> {2} 1> {3}".format(OPTS.lvs_exe[1], - OPTS.openram_temp, - errfile, - outfile) - debug.info(2, cmd) - os.system(cmd) - os.chdir(cwd) + (outfile, errfile, resultsfile) = run_script(cell_name, "lvs") # check the result for these lines in the summary: - f = open(lvs_runset['lvsReportFile'], "r") + f = open(OPTS.openram_temp + lvs_runset['lvsReportFile'], "r") results = f.readlines() f.close() @@ -235,7 +266,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): summary_errors = len(notcompared) + len(incorrect) + len(errors) # also check the extraction summary file - f = open(lvs_runset['lvsReportFile'] + ".ext", "r") + f = open(OPTS.openram_temp + lvs_runset['lvsReportFile'] + ".ext", "r") results = f.readlines() f.close() @@ -252,7 +283,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): # MRG - 9/26/17 - Change this to exclude warnings because of # multiple labels on different pins in column mux. ext_errors = len(exterrors) - ext_warnings = len(extwarnings) + ext_warnings = len(extwarnings) # also check the output file f = open(outfile, "r") @@ -269,16 +300,16 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): total_errors = summary_errors + out_errors + ext_errors if total_errors > 0: - debug.error("{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name, + debug.error("{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name, summary_errors, out_errors, ext_errors)) else: - debug.info(1, "{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name, + debug.info(1, "{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name, summary_errors, out_errors, ext_errors)) - + return total_errors @@ -288,52 +319,11 @@ def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False) global num_pex_runs num_pex_runs += 1 - - from tech import drc - if output == None: - output = name + ".pex.netlist" - # check if lvs report has been done - # if not run drc and lvs - if not os.path.isfile(cell_name + ".lvs.report"): - run_drc(cell_name, gds_name) - run_lvs(cell_name, gds_name, sp_name) + write_calibre_pex_script(cell_name,True,output,final_verification) - pex_rules = drc["xrc_rules"] - pex_runset = { - 'pexRulesFile': pex_rules, - 'pexRunDir': OPTS.openram_temp, - 'pexLayoutPaths': gds_name, - 'pexLayoutPrimary': cell_name, - #'pexSourcePath' : OPTS.openram_temp+"extracted.sp", - 'pexSourcePath': sp_name, - 'pexSourcePrimary': cell_name, - 'pexReportFile': cell_name + ".lvs.report", - 'pexPexNetlistFile': output, - 'pexPexReportFile': cell_name + ".pex.report", - 'pexMaskDBFile': cell_name + ".maskdb", - 'cmnFDIDEFLayoutPath': cell_name + ".def", - } + (outfile, errfile, resultsfile) = run_script(cell_name, "pex") - # write the runset file - f = open(OPTS.openram_temp + "pex_runset", "w") - for k in sorted(iter(pex_runset.keys())): - f.write("*{0}: {1}\n".format(k, pex_runset[k])) - f.close() - - # run pex - cwd = os.getcwd() - os.chdir(OPTS.openram_temp) - errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, cell_name) - outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, cell_name) - - cmd = "{0} -gui -pex {1}pex_runset -batch 2> {2} 1> {3}".format(OPTS.pex_exe[1], - OPTS.openram_temp, - errfile, - outfile) - debug.info(2, cmd) - os.system(cmd) - os.chdir(cwd) # also check the output file f = open(outfile, "r") diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 7db9a5c2..63aeaabe 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -1,9 +1,16 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ -This is a DRC/LVS/PEX interface file for magic + netgen. +This is a DRC/LVS/PEX interface file for magic + netgen. -We include the tech file for SCN3ME_SUBM in the tech directory, -that is included in OpenRAM during DRC. -You can use this interactively by appending the magic system path in +We include the tech file for SCN4M_SUBM in the tech directory, +that is included in OpenRAM during DRC. +You can use this interactively by appending the magic system path in your .magicrc file path sys /Users/mrg/openram/technology/scn3me_subm/tech @@ -19,14 +26,15 @@ import time import shutil import debug from globals import OPTS -import subprocess +from run_script import * # Keep track of statistics num_drc_runs = 0 num_lvs_runs = 0 num_pex_runs = 0 -def write_magic_script(cell_name, gds_name, extract=False, final_verification=False): + +def write_magic_script(cell_name, extract=False, final_verification=False): """ Write a magic script to perform DRC and optionally extraction. """ global OPTS @@ -37,7 +45,7 @@ def write_magic_script(cell_name, gds_name, extract=False, final_verification=Fa f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1])) f.write("gds polygon subcell true\n") f.write("gds warning default\n") - f.write("gds read {}\n".format(gds_name)) + f.write("gds read {}.gds\n".format(cell_name)) f.write("load {}\n".format(cell_name)) # Flatten the cell to get rid of DRCs spanning multiple layers # (e.g. with routes) @@ -53,6 +61,7 @@ def write_magic_script(cell_name, gds_name, extract=False, final_verification=Fa f.write("drc catchup\n") f.write("drc count total\n") f.write("drc count\n") + f.write("port makeall\n") if not extract: pre = "#" else: @@ -60,7 +69,7 @@ def write_magic_script(cell_name, gds_name, extract=False, final_verification=Fa if final_verification: f.write(pre+"extract unique all\n".format(cell_name)) f.write(pre+"extract\n".format(cell_name)) - #f.write(pre+"ext2spice hierarchy on\n") + #f.write(pre+"ext2spice hierarchy on\n") #f.write(pre+"ext2spice scale off\n") # lvs exists in 8.2.79, but be backword compatible for now #f.write(pre+"ext2spice lvs\n") @@ -73,26 +82,28 @@ def write_magic_script(cell_name, gds_name, extract=False, final_verification=Fa f.write(pre+"ext2spice blackbox on\n") f.write(pre+"ext2spice subcircuit top auto\n") f.write(pre+"ext2spice global 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".format(cell_name)) f.write("quit -noprompt\n") f.write("EOF\n") - + f.close() os.system("chmod u+x {}".format(run_file)) -def write_netgen_script(cell_name, sp_name): + +def write_netgen_script(cell_name): """ Write a netgen script to perform LVS. """ global OPTS - setup_file = OPTS.openram_tech + "mag_lib/setup.tcl" - if os.path.exists(setup_file): + setup_file = "setup.tcl" + full_setup_file = OPTS.openram_tech + "mag_lib/" + setup_file + if os.path.exists(full_setup_file): # Copy setup.tcl file into temp dir - shutil.copy(setup_file, OPTS.openram_temp) + shutil.copy(full_setup_file, OPTS.openram_temp) else: setup_file = 'nosetup' @@ -101,26 +112,24 @@ def write_netgen_script(cell_name, sp_name): f.write("#!/bin/sh\n") f.write("{} -noconsole << EOF\n".format(OPTS.lvs_exe[1])) f.write("readnet spice {0}.spice\n".format(cell_name)) - f.write("readnet spice {0}\n".format(sp_name)) - # Allow some flexibility in W size because magic will snap to a lambda grid - # This can also cause disconnects unfortunately! - # f.write("property {{{0}{1}.spice nfet}} tolerance {{w 0.1}}\n".format(OPTS.openram_temp, - # 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}}} {2} {0}.lvs.report\n".format(cell_name, sp_name, setup_file)) + f.write("readnet spice {0}.sp\n".format(cell_name)) + f.write("lvs {{{0}.spice {0}}} {{{0}.sp {0}}} {1} {0}.lvs.report\n".format(cell_name, setup_file)) f.write("quit\n") f.write("EOF\n") f.close() os.system("chmod u+x {}".format(run_file)) - + def run_drc(cell_name, gds_name, extract=True, final_verification=False): """Run DRC check on a cell which is implemented in gds_name.""" global num_drc_runs num_drc_runs += 1 + # Copy file to local dir if it isn't already + if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): + shutil.copy(gds_name, OPTS.openram_temp) + # Copy .magicrc file into temp dir magic_file = OPTS.openram_tech + "mag_lib/.magicrc" if os.path.exists(magic_file): @@ -128,20 +137,9 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False): else: debug.warning("Could not locate .magicrc file: {}".format(magic_file)) - write_magic_script(cell_name, gds_name, extract, final_verification) - - # run drc - cwd = os.getcwd() - os.chdir(OPTS.openram_temp) - errfile = "{0}{1}.drc.err".format(OPTS.openram_temp, cell_name) - outfile = "{0}{1}.drc.summary".format(OPTS.openram_temp, cell_name) + write_magic_script(cell_name, extract, final_verification) - cmd = "{0}run_drc.sh 2> {1} 1> {2}".format(OPTS.openram_temp, - errfile, - outfile) - debug.info(2, cmd) - os.system(cmd) - os.chdir(cwd) + (outfile, errfile, resultsfile) = run_script(cell_name, "drc") # Check the result for these lines in the summary: # Total DRC errors found: 0 @@ -153,7 +151,7 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False): f = open(outfile, "r") except FileNotFoundError: debug.error("Unable to load DRC results file from {}. Is magic set up?".format(outfile),1) - + results = f.readlines() f.close() errors=1 @@ -164,7 +162,7 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False): break else: debug.error("Unable to find the total error line in Magic output.",1) - + # always display this summary if errors > 0: @@ -185,31 +183,25 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): global num_lvs_runs num_lvs_runs += 1 - - write_netgen_script(cell_name, sp_name) - - # run LVS - cwd = os.getcwd() - os.chdir(OPTS.openram_temp) - errfile = "{0}{1}.lvs.err".format(OPTS.openram_temp, cell_name) - outfile = "{0}{1}.lvs.out".format(OPTS.openram_temp, cell_name) - resultsfile = "{0}{1}.lvs.report".format(OPTS.openram_temp, cell_name) - cmd = "{0}run_lvs.sh lvs 2> {1} 1> {2}".format(OPTS.openram_temp, - errfile, - outfile) - debug.info(2, cmd) - os.system(cmd) - os.chdir(cwd) + # Copy file to local dir if it isn't already + if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): + shutil.copy(gds_name, OPTS.openram_temp) + if os.path.dirname(sp_name)!=OPTS.openram_temp.rstrip('/'): + shutil.copy(sp_name, OPTS.openram_temp) + + write_netgen_script(cell_name) + + (outfile, errfile, resultsfile) = run_script(cell_name, "lvs") total_errors = 0 - + # check the result for these lines in the summary: try: f = open(resultsfile, "r") except FileNotFoundError: debug.error("Unable to load LVS results from {}".format(resultsfile),1) - + results = f.readlines() f.close() # Look for the results after the final "Subcircuit summary:" @@ -225,14 +217,14 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): test = re.compile("Property errors were found.") propertyerrors = list(filter(test.search, results)) total_errors += len(propertyerrors) - + # Require pins to match? # Cell pin lists for pnand2_1.spice and pnand2_1 altered to match. # test = re.compile(".*altered to match.") # pinerrors = list(filter(test.search, results)) # if len(pinerrors)>0: # debug.warning("Pins altered to match in {}.".format(cell_name)) - + #if len(propertyerrors)>0: # debug.warning("Property errors found, but not checking them.") @@ -240,7 +232,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): test = re.compile("Netlists do not match.") incorrect = list(filter(test.search, final_results)) total_errors += len(incorrect) - + # Netlists match uniquely. test = re.compile("match uniquely.") correct = list(filter(test.search, final_results)) @@ -252,7 +244,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): # Just print out the whole file, it is short. for e in results: debug.info(1,e.strip("\n")) - debug.error("{0}\tLVS mismatch (results in {1})".format(cell_name,resultsfile)) + debug.error("{0}\tLVS mismatch (results in {1})".format(cell_name,resultsfile)) else: debug.info(1, "{0}\tLVS matches".format(cell_name)) @@ -265,9 +257,9 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False): global num_pex_runs num_pex_runs += 1 - - debug.warning("PEX using magic not implemented.") - return 1 + #debug.warning("PEX using magic not implemented.") + #return 1 + os.chdir(OPTS.openram_temp) from tech import drc if output == None: @@ -279,25 +271,67 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False): run_drc(name, gds_name) run_lvs(name, gds_name, sp_name) - """ - 2. magic can perform extraction with the following: - #!/bin/sh - rm -f $1.ext - rm -f $1.spice - magic -dnull -noconsole << EOF - tech load SCN3ME_SUBM.30 - #scalegrid 1 2 - gds rescale no - gds polygon subcell true - gds warning default - gds read $1 - extract - ext2spice scale off - ext2spice - quit -noprompt - EOF - """ - + # pex_fix did run the pex using a script while dev orignial method + # use batch mode. + # the dev old code using batch mode does not run and is split into functions + #pex_runset = write_batch_pex_rule(gds_name,name,sp_name,output) + pex_runset = write_script_pex_rule(gds_name,name,output) + + errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name) + outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name) + + # bash mode command from dev branch + #batch_cmd = "{0} -gui -pex {1}pex_runset -batch 2> {2} 1> {3}".format(OPTS.pex_exe, + # OPTS.openram_temp, + # errfile, + # outfile) + script_cmd = "{0} 2> {1} 1> {2}".format(pex_runset, + errfile, + outfile) + cmd = script_cmd + debug.info(2, cmd) + os.system(cmd) + + # rename technology models + pex_nelist = open(output, 'r') + s = pex_nelist.read() + pex_nelist.close() + s = s.replace('pfet','p') + s = s.replace('nfet','n') + f = open(output, 'w') + f.write(s) + f.close() + + # also check the output file + f = open(outfile, "r") + results = f.readlines() + f.close() + out_errors = find_error(results) + debug.check(os.path.isfile(output),"Couldn't find PEX extracted output.") + + correct_port(name,output,sp_name) + return out_errors + +def write_batch_pex_rule(gds_name,name,sp_name,output): + """ + The dev branch old batch mode runset + 2. magic can perform extraction with the following: + #!/bin/sh + rm -f $1.ext + rm -f $1.spice + magic -dnull -noconsole << EOF + tech load SCN3ME_SUBM.30 + #scalegrid 1 2 + gds rescale no + gds polygon subcell true + gds warning default + gds read $1 + extract + ext2spice scale off + ext2spice + quit -noprompt + EOF + """ pex_rules = drc["xrc_rules"] pex_runset = { 'pexRulesFile': pex_rules, @@ -315,43 +349,89 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False): } # write the runset file - f = open(OPTS.openram_temp + "pex_runset", "w") - for k in sorted(pex_runset.iterkeys()): + file = OPTS.openram_temp + "pex_runset" + f = open(file, "w") + for k in sorted(pex_runset.keys()): f.write("*{0}: {1}\n".format(k, pex_runset[k])) f.close() + return file - # run pex - cwd = os.getcwd() - os.chdir(OPTS.openram_temp) - errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name) - outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name) +def write_script_pex_rule(gds_name,cell_name,output): + global OPTS + run_file = OPTS.openram_temp + "run_pex.sh" + f = open(run_file, "w") + f.write("#!/bin/sh\n") + f.write("{} -dnull -noconsole << eof\n".format(OPTS.drc_exe[1])) + f.write("gds polygon subcell true\n") + f.write("gds warning default\n") + f.write("gds read {}\n".format(gds_name)) + f.write("load {}\n".format(cell_name)) + f.write("select top cell\n") + f.write("expand\n") + f.write("port makeall\n") + extract = True + if not extract: + pre = "#" + else: + pre = "" + f.write(pre+"extract\n".format(cell_name)) + #f.write(pre+"ext2spice hierarchy on\n") + #f.write(pre+"ext2spice format ngspice\n") + #f.write(pre+"ext2spice renumber off\n") + #f.write(pre+"ext2spice scale off\n") + #f.write(pre+"ext2spice blackbox on\n") + f.write(pre+"ext2spice subcircuit top on\n") + #f.write(pre+"ext2spice global off\n") + f.write(pre+"ext2spice {}\n".format(cell_name)) + f.write("quit -noprompt\n") + f.write("eof\n") + f.write("mv {0}.spice {1}\n".format(cell_name,output)) - cmd = "{0} -gui -pex {1}pex_runset -batch 2> {2} 1> {3}".format(OPTS.pex_exe, - OPTS.openram_temp, - errfile, - outfile) - debug.info(2, cmd) - os.system(cmd) - os.chdir(cwd) - - # also check the output file - f = open(outfile, "r") - results = f.readlines() f.close() + os.system("chmod u+x {}".format(run_file)) + return run_file +def find_error(results): # Errors begin with "ERROR:" test = re.compile("ERROR:") stdouterrors = list(filter(test.search, results)) for e in stdouterrors: debug.error(e.strip("\n")) - out_errors = len(stdouterrors) - - assert(os.path.isfile(output)) - #correct_port(name, output, sp_name) - return out_errors +def correct_port(name, output_file_name, ref_file_name): + pex_file = open(output_file_name, "r") + contents = pex_file.read() + # locate the start of circuit definition line + match = re.search(".subckt " + str(name) + ".*", contents) + match_index_start = match.start() + pex_file.seek(match_index_start) + rest_text = pex_file.read() + # locate the end of circuit definition line + match = re.search(r'\n', rest_text) + match_index_end = match.start() + # store the unchanged part of pex file in memory + pex_file.seek(0) + part1 = pex_file.read(match_index_start) + pex_file.seek(match_index_start + match_index_end) + part2 = pex_file.read() + pex_file.close() + + # obtain the correct definition line from the original spice file + sp_file = open(ref_file_name, "r") + contents = sp_file.read() + circuit_title = re.search(".SUBCKT " + str(name) + ".*\n", contents) + circuit_title = circuit_title.group() + sp_file.close() + + # write the new pex file with info in the memory + output_file = open(output_file_name, "w") + output_file.write(part1) + output_file.write(circuit_title) + output_file.write(part2) + output_file.close() + def print_drc_stats(): debug.info(1,"DRC runs: {0}".format(num_drc_runs)) def print_lvs_stats(): diff --git a/compiler/verify/none.py b/compiler/verify/none.py index c69ed93b..9d7ac938 100644 --- a/compiler/verify/none.py +++ b/compiler/verify/none.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ This is a DRC/LVS/PEX interface file the case with no DRC/LVS tools. diff --git a/compiler/verify/run_script.py b/compiler/verify/run_script.py new file mode 100644 index 00000000..3bc8d2d8 --- /dev/null +++ b/compiler/verify/run_script.py @@ -0,0 +1,34 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +""" +Some baseline functions to run scripts. +""" + +import os +import debug +from globals import OPTS + +def run_script(cell_name, script="lvs"): + """ Run script and create output files. """ + + cwd = os.getcwd() + os.chdir(OPTS.openram_temp) + errfile = "{0}{1}.{2}.err".format(OPTS.openram_temp, cell_name, script) + outfile = "{0}{1}.{2}.out".format(OPTS.openram_temp, cell_name, script) + resultsfile = "{0}{1}.{2}.report".format(OPTS.openram_temp, cell_name, script) + + cmd = "{0}run_{1}.sh 2> {2} 1> {3}".format(OPTS.openram_temp, + script, + errfile, + outfile) + debug.info(2, cmd) + os.system(cmd) + os.chdir(cwd) + + return (outfile,errfile,resultsfile) + diff --git a/compiler/view_profile.py b/compiler/view_profile.py index aadcd459..cad8e9ae 100755 --- a/compiler/view_profile.py +++ b/compiler/view_profile.py @@ -1,4 +1,11 @@ #!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import pstats p = pstats.Stats("profile.dat") p.strip_dirs() diff --git a/technology/setup_scripts/setup_openram_freepdk45.py b/technology/freepdk45/__init__.py similarity index 76% rename from technology/setup_scripts/setup_openram_freepdk45.py rename to technology/freepdk45/__init__.py index 3ba0aa16..363df6f4 100644 --- a/technology/setup_scripts/setup_openram_freepdk45.py +++ b/technology/freepdk45/__init__.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/python """ This type of setup script should be placed in the setup_scripts directory in @@ -8,7 +15,6 @@ import sys import os TECHNOLOGY = "freepdk45" -LOCAL = "{0}/..".format(os.path.dirname(__file__)) ########################## # FreePDK45 paths @@ -33,8 +39,3 @@ os.environ["DRCLVS_HOME"] = DRCLVS_HOME # except: # Always use the one in the PDK dir for FreePDK45 os.environ["SPICE_MODEL_DIR"] = PDK_DIR+"/ncsu_basekit/models/hspice/tran_models" - -########################## -#Paths required for OPENRAM to function - -sys.path.append("{0}/{1}/tech".format(LOCAL,TECHNOLOGY)) diff --git a/technology/freepdk45/gds_lib/dummy_cell_1rw_1r.gds b/technology/freepdk45/gds_lib/dummy_cell_1rw_1r.gds new file mode 100644 index 00000000..2ac1a287 Binary files /dev/null and b/technology/freepdk45/gds_lib/dummy_cell_1rw_1r.gds differ diff --git a/technology/freepdk45/gds_lib/dummy_cell_1w_1r.gds b/technology/freepdk45/gds_lib/dummy_cell_1w_1r.gds new file mode 100644 index 00000000..fa89439e Binary files /dev/null and b/technology/freepdk45/gds_lib/dummy_cell_1w_1r.gds differ diff --git a/technology/freepdk45/gds_lib/dummy_cell_6t.gds b/technology/freepdk45/gds_lib/dummy_cell_6t.gds new file mode 100644 index 00000000..c6575122 Binary files /dev/null and b/technology/freepdk45/gds_lib/dummy_cell_6t.gds differ diff --git a/technology/freepdk45/gds_lib/write_driver.gds b/technology/freepdk45/gds_lib/write_driver.gds index 742e39d4..86015e7a 100644 Binary files a/technology/freepdk45/gds_lib/write_driver.gds and b/technology/freepdk45/gds_lib/write_driver.gds differ diff --git a/technology/freepdk45/lib/.cadence/cadence.signature.xml b/technology/freepdk45/lib/.cadence/cadence.signature.xml deleted file mode 100755 index 64bab7f4..00000000 --- a/technology/freepdk45/lib/.cadence/cadence.signature.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - Do not remove or change this .cadence directory signature file. diff --git a/technology/freepdk45/lib/.cadence/dfII/viva/viva.ini b/technology/freepdk45/lib/.cadence/dfII/viva/viva.ini deleted file mode 100755 index dfde08be..00000000 --- a/technology/freepdk45/lib/.cadence/dfII/viva/viva.ini +++ /dev/null @@ -1,43 +0,0 @@ -[browser] -orientation=horizontal -pathlist=/mada/users/cpeters/Working/simulations/latch.run1/si.raw, /mada/users/cpeters/Working/simulations/out_latch.run1/si.raw, /mada/users/cpeters/Working/simulations/ms_ff.run1/si.raw -pos=@Point(1118 92) -size=@Size(214 928) -splitter=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\xc0\x1\0\0\0\x4\x1\0\0\0\x1) - -[fullCalculator] -_bufferState=@Invalid() -_funcPanelName=undefined -_funcPanelSplitter=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x2\0\0\0\xb3\0\0\x1\xb4\0\0\0\0\x4\x1\0\0\0\x2) -_funcPanelType=FuncListType -_keyPadSplitter=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x2\0\0\x2\xf4\0\0\0\x64\x1\0\0\0\x4\x1\0\0\0\x1) -_resultsDir= -_signalSelectionHistory=@Invalid() -_splitter=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x2\0\0\0\xb3\0\0\0\0\x1\0\0\0\x4\x1\0\0\0\x2) -_testName= -clipSelectionMode=true -defaultCategory=Special Functions -displayContext=true -mathToolBar=false -plotStyle=Append -pos=@Point(582 186) -rpnMode=true -schematicAnalyses="tran,ac,dc,sweptDc,info,noise,rf" -schematicToolBar=true -showKeyPad=true -showStack=false -signalSelection=Off -size=@Size(866 834) -trigToolBar=false -userButton1=user 1, undefined -userButton10=user 1, undefined -userButton11=user 1, undefined -userButton12=user 1, undefined -userButton2=user 2, undefined -userButton3=user 3, undefined -userButton4=user 4, undefined -userButton5=user 5, undefined -userButton6=user 6, undefined -userButton7=user 7, undefined -userButton8=user 8, undefined -userButton9=user 9, undefined diff --git a/technology/freepdk45/lib/.cdsinit b/technology/freepdk45/lib/.cdsinit deleted file mode 100755 index 14e60079..00000000 --- a/technology/freepdk45/lib/.cdsinit +++ /dev/null @@ -1,47 +0,0 @@ - -envSetVal( "graphic" "drfPath" 'string - strcat( getShellEnvVar("PDK_DIR") "/ncsu_basekit/cdssetup/display.drf")) - - -loadi( strcat( getShellEnvVar("PDK_DIR") "/ncsu_basekit/cdssetup/common_bindkeys.il")) -if( getShellEnvVar("MGC_HOME") then - loadi( strcat( getShellEnvVar("MGC_HOME") "/shared/pkgs/icv/tools/queryskl/calibre.OA.skl")) -) ;if -procedure( prependNCSUCDKInstallPath( dir) - strcat( getShellEnvVar("PDK_DIR") "/ncsu_basekit/" dir)) -(envLoadVals -?envFile ( prependNCSUCDKInstallPath "cdssetup/cdsenv") -?tool "layout") - -printf( strcat( -"---------------------------------------------------------------------------\n" -"Welcome to the FreePDK 45nm Free, Open-Source Process Design Kit\n" -"\n" -"This initiative is brought to you by the Semiconductor Research\n" -"Corporation (SRC), the National Science Foundation (NSF), Silicon\n" -"Integration Initiative (Si2), Mentor Graphics, and Synopsys.\n" -"\n" -"This version of the kit was created by Rhett Davis, Paul Franzon,\n" -"Michael Bucher, and Sunil Basavarajaiah of North Carolina State University,\n" -"and James Stine and Ivan Castellanos of Oklahoma State University.\n" -"\n" -"Contributions and modifications to this kit are welcomed and encouraged.\n" -"\n" -"Copyright 2008 North Carolina State University (ncsu_basekit subtree)\n" -" and Oklahoma State University (osu_soc subtree)\n" -"\n" -"Licensed under the Apache License, Version 2.0 (the \"License\");\n" -"you may not use this file except in compliance with the License.\n" -"You may obtain a copy of the License at\n" -"\n" -" http://www.apache.org/licenses/LICENSE-2.0\n" -"\n" -"Unless required by applicable law or agreed to in writing, software\n" -"distributed under the License is distributed on an \"AS IS\" BASIS,\n" -"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -"See the License for the specific language governing permissions and\n" -"limitations under the License.\n" -"---------------------------------------------------------------------------\n" -"\n" -"Done loading FreePDK customizations.\n" -)) diff --git a/technology/freepdk45/lib/.runset.calibre.drc b/technology/freepdk45/lib/.runset.calibre.drc deleted file mode 100755 index 35186ce6..00000000 --- a/technology/freepdk45/lib/.runset.calibre.drc +++ /dev/null @@ -1,2 +0,0 @@ -*drcRulesFile: $PDK_DIR/ncsu_basekit/techfile/calibre/calibreDRC.rul - diff --git a/technology/freepdk45/lib/.runset.calibre.lfd b/technology/freepdk45/lib/.runset.calibre.lfd deleted file mode 100755 index f770b0a1..00000000 --- a/technology/freepdk45/lib/.runset.calibre.lfd +++ /dev/null @@ -1,2 +0,0 @@ -*drcRulesFile: $PDK_DIR/ncsu_basekit/techfile/calibre/calibreLFD.rul - diff --git a/technology/freepdk45/lib/.runset.calibre.lvs b/technology/freepdk45/lib/.runset.calibre.lvs deleted file mode 100755 index aa47aacb..00000000 --- a/technology/freepdk45/lib/.runset.calibre.lvs +++ /dev/null @@ -1,3 +0,0 @@ -*lvsRulesFile: $PDK_DIR/ncsu_basekit/techfile/calibre/calibreLVS.rul - - diff --git a/technology/freepdk45/lib/.runset.calibre.pex b/technology/freepdk45/lib/.runset.calibre.pex deleted file mode 100755 index a1f8b6d9..00000000 --- a/technology/freepdk45/lib/.runset.calibre.pex +++ /dev/null @@ -1,3 +0,0 @@ -*pexRulesFile: $PDK_DIR/ncsu_basekit/techfile/calibre/calibrexRC.rul - - diff --git a/technology/freepdk45/lib/cds.lib b/technology/freepdk45/lib/cds.lib deleted file mode 100644 index e3c63316..00000000 --- a/technology/freepdk45/lib/cds.lib +++ /dev/null @@ -1,8 +0,0 @@ -DEFINE analogLib $CDSHOME/tools/dfII/etc/cdslib/artist/analogLib -DEFINE US_8ths $CDSHOME/tools/dfII/etc/cdslib/sheets/US_8ths -DEFINE basic $CDSHOME/tools/dfII/etc/cdslib/basic -DEFINE cdsDefTechLib $CDSHOME/tools/dfII/etc/cdsDefTechLib -DEFINE NCSU_TechLib_FreePDK45 $PDK_DIR/ncsu_basekit/lib/NCSU_TechLib_FreePDK45 -DEFINE NCSU_Devices_FreePDK45 $PDK_DIR/ncsu_basekit/lib/NCSU_Devices_FreePDK45 -DEFINE sram $OPENRAM_HOME/lib/sram -DEFINE sub_sram $OPENRAM_HOME/lib/sub_sram diff --git a/technology/freepdk45/lib/lib.defs b/technology/freepdk45/lib/lib.defs deleted file mode 100644 index c2a6982c..00000000 --- a/technology/freepdk45/lib/lib.defs +++ /dev/null @@ -1,16 +0,0 @@ -DEFINE analogLib $CDSHOME/tools/dfII/etc/cdslib/artist/analogLib -ASSIGN analogLib libMode shared -DEFINE US_8ths $CDSHOME/tools/dfII/etc/cdslib/sheets/US_8ths -ASSIGN US_8ths libMode shared -DEFINE basic $CDSHOME/tools/dfII/etc/cdslib/basic -ASSIGN basic libMode shared -DEFINE cdsDefTechLib $CDSHOME/tools/dfII/etc/cdsDefTechLib -ASSIGN cdsDefTechLib libMode shared -DEFINE NCSU_TechLib_FreePDK45 $PDK_DIR/ncsu_basekit/lib/NCSU_TechLib_FreePDK45 -ASSIGN NCSU_TechLib_FreePDK45 libMode shared -DEFINE NCSU_Devices_FreePDK45 $PDK_DIR/ncsu_basekit/lib/NCSU_Devices_FreePDK45 -ASSIGN NCSU_Devices_FreePDK45 libMode shared -DEFINE sram $OPENRAM_HOME/lib/sram -ASSIGN sram libMode shared -DEFINE sub_sram $OPENRAM_HOME/lib/sub_sram -ASSIGN sub_sram libMode shared diff --git a/technology/freepdk45/lib/sram/.oalib b/technology/freepdk45/lib/sram/.oalib deleted file mode 100755 index 21ffef89..00000000 --- a/technology/freepdk45/lib/sram/.oalib +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/technology/freepdk45/lib/sram/addr_ff/layout/layout.oa b/technology/freepdk45/lib/sram/addr_ff/layout/layout.oa deleted file mode 100755 index 60a18e10..00000000 Binary files a/technology/freepdk45/lib/sram/addr_ff/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/addr_ff/layout/master.tag b/technology/freepdk45/lib/sram/addr_ff/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/addr_ff/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/addr_ff/schematic/data.dm b/technology/freepdk45/lib/sram/addr_ff/schematic/data.dm deleted file mode 100755 index 11925091..00000000 Binary files a/technology/freepdk45/lib/sram/addr_ff/schematic/data.dm and /dev/null differ diff --git a/technology/freepdk45/lib/sram/addr_ff/schematic/master.tag b/technology/freepdk45/lib/sram/addr_ff/schematic/master.tag deleted file mode 100755 index 26be1bef..00000000 --- a/technology/freepdk45/lib/sram/addr_ff/schematic/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -sch.oa diff --git a/technology/freepdk45/lib/sram/addr_ff/schematic/sch.oa b/technology/freepdk45/lib/sram/addr_ff/schematic/sch.oa deleted file mode 100755 index eabefc20..00000000 Binary files a/technology/freepdk45/lib/sram/addr_ff/schematic/sch.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/addr_latch/layout/layout.oa b/technology/freepdk45/lib/sram/addr_latch/layout/layout.oa deleted file mode 100755 index 3525ac02..00000000 Binary files a/technology/freepdk45/lib/sram/addr_latch/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/addr_latch/layout/master.tag b/technology/freepdk45/lib/sram/addr_latch/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/addr_latch/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/addr_latch/schematic/data.dm b/technology/freepdk45/lib/sram/addr_latch/schematic/data.dm deleted file mode 100755 index 6a4c9f97..00000000 Binary files a/technology/freepdk45/lib/sram/addr_latch/schematic/data.dm and /dev/null differ diff --git a/technology/freepdk45/lib/sram/addr_latch/schematic/master.tag b/technology/freepdk45/lib/sram/addr_latch/schematic/master.tag deleted file mode 100755 index 26be1bef..00000000 --- a/technology/freepdk45/lib/sram/addr_latch/schematic/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -sch.oa diff --git a/technology/freepdk45/lib/sram/addr_latch/schematic/sch.oa b/technology/freepdk45/lib/sram/addr_latch/schematic/sch.oa deleted file mode 100755 index 74b3999f..00000000 Binary files a/technology/freepdk45/lib/sram/addr_latch/schematic/sch.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/array/layout/layout.oa b/technology/freepdk45/lib/sram/array/layout/layout.oa deleted file mode 100755 index 8af1983b..00000000 Binary files a/technology/freepdk45/lib/sram/array/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/array/layout/master.tag b/technology/freepdk45/lib/sram/array/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/array/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/cdsinfo.tag b/technology/freepdk45/lib/sram/cdsinfo.tag deleted file mode 100644 index a8dcdcb4..00000000 --- a/technology/freepdk45/lib/sram/cdsinfo.tag +++ /dev/null @@ -1,40 +0,0 @@ -# -# This is a cdsinfo.tag file. -# -# See the "Cadence Application Infrastructure Reference Manual" for -# details on the format of this file, its semantics, and its use. -# -# The `#' character denotes a comment. Removing the leading `#' -# character from any of the entries below will activate them. -# -# CDSLIBRARY entry - add this entry if the directory containing -# this cdsinfo.tag file is the root of a Cadence library. -# CDSLIBRARY -# -# CDSLIBCHECK - set this entry to require that libraries have -# a cdsinfo.tag file with a CDSLIBRARY entry. Legal values are -# ON and OFF. By default (OFF), directories named in a cds.lib file -# do not have to have a cdsinfo.tag file with a CDSLIBRARY entry. -# CDSLIBCHECK ON -# -# DMTYPE - set this entry to define the DM system for Cadence's -# Generic DM facility. Values will be shifted to lower case. -# DMTYPE none -# DMTYPE crcs -# DMTYPE tdm -# DMTYPE sync -# -# NAMESPACE - set this entry to define the library namespace according -# to the type of machine on which the data is stored. Legal values are -# `LibraryNT' and -# `LibraryUnix'. -# NAMESPACE LibraryUnix -# -# Other entries may be added for use by specific applications as -# name-value pairs. Application documentation will describe the -# use and behaviour of these entries when appropriate. -# -# Current Settings: -# -CDSLIBRARY -NAMESPACE LibraryUnix diff --git a/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa b/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa deleted file mode 100755 index d19dccf7..00000000 Binary files a/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa- b/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa- deleted file mode 100755 index 7f81fccb..00000000 Binary files a/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa- and /dev/null differ diff --git a/technology/freepdk45/lib/sram/cell_10t/layout/master.tag b/technology/freepdk45/lib/sram/cell_10t/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/cell_10t/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/cell_10t/schematic/data.dm b/technology/freepdk45/lib/sram/cell_10t/schematic/data.dm deleted file mode 100755 index ff96d2ab..00000000 Binary files a/technology/freepdk45/lib/sram/cell_10t/schematic/data.dm and /dev/null differ diff --git a/technology/freepdk45/lib/sram/cell_10t/schematic/master.tag b/technology/freepdk45/lib/sram/cell_10t/schematic/master.tag deleted file mode 100755 index 26be1bef..00000000 --- a/technology/freepdk45/lib/sram/cell_10t/schematic/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -sch.oa diff --git a/technology/freepdk45/lib/sram/cell_10t/schematic/sch.oa b/technology/freepdk45/lib/sram/cell_10t/schematic/sch.oa deleted file mode 100755 index 8f2d4cd0..00000000 Binary files a/technology/freepdk45/lib/sram/cell_10t/schematic/sch.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa b/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa deleted file mode 100755 index 2c763698..00000000 Binary files a/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa- b/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa- deleted file mode 100755 index 43f013e8..00000000 Binary files a/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa- and /dev/null differ diff --git a/technology/freepdk45/lib/sram/cell_6t/layout/master.tag b/technology/freepdk45/lib/sram/cell_6t/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/cell_6t/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/cell_6t/schematic/data.dm b/technology/freepdk45/lib/sram/cell_6t/schematic/data.dm deleted file mode 100755 index c2d60e51..00000000 Binary files a/technology/freepdk45/lib/sram/cell_6t/schematic/data.dm and /dev/null differ diff --git a/technology/freepdk45/lib/sram/cell_6t/schematic/master.tag b/technology/freepdk45/lib/sram/cell_6t/schematic/master.tag deleted file mode 100755 index 26be1bef..00000000 --- a/technology/freepdk45/lib/sram/cell_6t/schematic/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -sch.oa diff --git a/technology/freepdk45/lib/sram/cell_6t/schematic/sch.oa b/technology/freepdk45/lib/sram/cell_6t/schematic/sch.oa deleted file mode 100755 index 51940d44..00000000 Binary files a/technology/freepdk45/lib/sram/cell_6t/schematic/sch.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/clock_nor/layout/layout.oa b/technology/freepdk45/lib/sram/clock_nor/layout/layout.oa deleted file mode 100755 index 7bf4d583..00000000 Binary files a/technology/freepdk45/lib/sram/clock_nor/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/clock_nor/layout/master.tag b/technology/freepdk45/lib/sram/clock_nor/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/clock_nor/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/data.dm b/technology/freepdk45/lib/sram/data.dm deleted file mode 100644 index 329ccd3c..00000000 Binary files a/technology/freepdk45/lib/sram/data.dm and /dev/null differ diff --git a/technology/freepdk45/lib/sram/delay_line/layout/layout.oa b/technology/freepdk45/lib/sram/delay_line/layout/layout.oa deleted file mode 100755 index 2a84a075..00000000 Binary files a/technology/freepdk45/lib/sram/delay_line/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/delay_line/layout/master.tag b/technology/freepdk45/lib/sram/delay_line/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/delay_line/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/dinv/layout/layout.oa b/technology/freepdk45/lib/sram/dinv/layout/layout.oa deleted file mode 100755 index a7735463..00000000 Binary files a/technology/freepdk45/lib/sram/dinv/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/dinv/layout/master.tag b/technology/freepdk45/lib/sram/dinv/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/dinv/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/dinv_mx/layout/layout.oa b/technology/freepdk45/lib/sram/dinv_mx/layout/layout.oa deleted file mode 100755 index ca7c13d2..00000000 Binary files a/technology/freepdk45/lib/sram/dinv_mx/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/dinv_mx/layout/master.tag b/technology/freepdk45/lib/sram/dinv_mx/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/dinv_mx/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/inv/layout/layout.oa b/technology/freepdk45/lib/sram/inv/layout/layout.oa deleted file mode 100755 index e98db560..00000000 Binary files a/technology/freepdk45/lib/sram/inv/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/inv/layout/master.tag b/technology/freepdk45/lib/sram/inv/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/inv/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/inv_clk/layout/layout.oa b/technology/freepdk45/lib/sram/inv_clk/layout/layout.oa deleted file mode 100755 index 20790f89..00000000 Binary files a/technology/freepdk45/lib/sram/inv_clk/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/inv_clk/layout/master.tag b/technology/freepdk45/lib/sram/inv_clk/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/inv_clk/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/inv_col/layout/layout.oa b/technology/freepdk45/lib/sram/inv_col/layout/layout.oa deleted file mode 100755 index 3335bece..00000000 Binary files a/technology/freepdk45/lib/sram/inv_col/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/inv_col/layout/master.tag b/technology/freepdk45/lib/sram/inv_col/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/inv_col/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/inv_dec/layout/layout.oa b/technology/freepdk45/lib/sram/inv_dec/layout/layout.oa deleted file mode 100755 index 3ec1f587..00000000 Binary files a/technology/freepdk45/lib/sram/inv_dec/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/inv_dec/layout/master.tag b/technology/freepdk45/lib/sram/inv_dec/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/inv_dec/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/inv_nor/layout/layout.oa b/technology/freepdk45/lib/sram/inv_nor/layout/layout.oa deleted file mode 100755 index 6c23bc4e..00000000 Binary files a/technology/freepdk45/lib/sram/inv_nor/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/inv_nor/layout/master.tag b/technology/freepdk45/lib/sram/inv_nor/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/inv_nor/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/mux_a/layout/layout.oa b/technology/freepdk45/lib/sram/mux_a/layout/layout.oa deleted file mode 100755 index 16e1369b..00000000 Binary files a/technology/freepdk45/lib/sram/mux_a/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/mux_a/layout/master.tag b/technology/freepdk45/lib/sram/mux_a/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/mux_a/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/mux_abar/layout/layout.oa b/technology/freepdk45/lib/sram/mux_abar/layout/layout.oa deleted file mode 100755 index 126897ec..00000000 Binary files a/technology/freepdk45/lib/sram/mux_abar/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/mux_abar/layout/master.tag b/technology/freepdk45/lib/sram/mux_abar/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/mux_abar/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/nor_1/layout/layout.oa b/technology/freepdk45/lib/sram/nor_1/layout/layout.oa deleted file mode 100755 index b0d79792..00000000 Binary files a/technology/freepdk45/lib/sram/nor_1/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/nor_1/layout/master.tag b/technology/freepdk45/lib/sram/nor_1/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/nor_1/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/nor_1_mx/layout/layout.oa b/technology/freepdk45/lib/sram/nor_1_mx/layout/layout.oa deleted file mode 100755 index 82764ea0..00000000 Binary files a/technology/freepdk45/lib/sram/nor_1_mx/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/nor_1_mx/layout/master.tag b/technology/freepdk45/lib/sram/nor_1_mx/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/nor_1_mx/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/ntap/layout/layout.oa b/technology/freepdk45/lib/sram/ntap/layout/layout.oa deleted file mode 100755 index aa2cdc19..00000000 Binary files a/technology/freepdk45/lib/sram/ntap/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/ntap/layout/master.tag b/technology/freepdk45/lib/sram/ntap/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/ntap/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa b/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa deleted file mode 100755 index 00d0b24e..00000000 Binary files a/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa- b/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa- deleted file mode 100755 index 211c52e7..00000000 Binary files a/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa- and /dev/null differ diff --git a/technology/freepdk45/lib/sram/out_inv_16/layout/master.tag b/technology/freepdk45/lib/sram/out_inv_16/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/out_inv_16/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa b/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa deleted file mode 100755 index c1480a3c..00000000 Binary files a/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa- b/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa- deleted file mode 100755 index 94f550c6..00000000 Binary files a/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa- and /dev/null differ diff --git a/technology/freepdk45/lib/sram/out_inv_2/layout/master.tag b/technology/freepdk45/lib/sram/out_inv_2/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/out_inv_2/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/out_inv_2/schematic/data.dm b/technology/freepdk45/lib/sram/out_inv_2/schematic/data.dm deleted file mode 100755 index 7cfdc2da..00000000 Binary files a/technology/freepdk45/lib/sram/out_inv_2/schematic/data.dm and /dev/null differ diff --git a/technology/freepdk45/lib/sram/out_inv_2/schematic/master.tag b/technology/freepdk45/lib/sram/out_inv_2/schematic/master.tag deleted file mode 100755 index 26be1bef..00000000 --- a/technology/freepdk45/lib/sram/out_inv_2/schematic/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -sch.oa diff --git a/technology/freepdk45/lib/sram/out_inv_2/schematic/sch.oa b/technology/freepdk45/lib/sram/out_inv_2/schematic/sch.oa deleted file mode 100755 index 1f45ca4a..00000000 Binary files a/technology/freepdk45/lib/sram/out_inv_2/schematic/sch.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa b/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa deleted file mode 100755 index e0690088..00000000 Binary files a/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa- b/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa- deleted file mode 100755 index e0690088..00000000 Binary files a/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa- and /dev/null differ diff --git a/technology/freepdk45/lib/sram/out_inv_4/layout/master.tag b/technology/freepdk45/lib/sram/out_inv_4/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/out_inv_4/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/output_latch/layout/layout.oa b/technology/freepdk45/lib/sram/output_latch/layout/layout.oa deleted file mode 100755 index ff29df6d..00000000 Binary files a/technology/freepdk45/lib/sram/output_latch/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/output_latch/layout/layout.oa- b/technology/freepdk45/lib/sram/output_latch/layout/layout.oa- deleted file mode 100755 index 6b39e820..00000000 Binary files a/technology/freepdk45/lib/sram/output_latch/layout/layout.oa- and /dev/null differ diff --git a/technology/freepdk45/lib/sram/output_latch/layout/master.tag b/technology/freepdk45/lib/sram/output_latch/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/output_latch/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/precharge/layout/layout.oa b/technology/freepdk45/lib/sram/precharge/layout/layout.oa deleted file mode 100755 index 8e1768fc..00000000 Binary files a/technology/freepdk45/lib/sram/precharge/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/precharge/layout/master.tag b/technology/freepdk45/lib/sram/precharge/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/precharge/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/ptap/layout/layout.oa b/technology/freepdk45/lib/sram/ptap/layout/layout.oa deleted file mode 100755 index f55a28cd..00000000 Binary files a/technology/freepdk45/lib/sram/ptap/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/ptap/layout/master.tag b/technology/freepdk45/lib/sram/ptap/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/ptap/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/sense_amp/layout.oa.backup b/technology/freepdk45/lib/sram/sense_amp/layout.oa.backup deleted file mode 100755 index 99f09b81..00000000 Binary files a/technology/freepdk45/lib/sram/sense_amp/layout.oa.backup and /dev/null differ diff --git a/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa b/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa deleted file mode 100755 index 33c3ce0e..00000000 Binary files a/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa- b/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa- deleted file mode 100755 index b69654d6..00000000 Binary files a/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa- and /dev/null differ diff --git a/technology/freepdk45/lib/sram/sense_amp/layout/master.tag b/technology/freepdk45/lib/sram/sense_amp/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/sense_amp/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/tgate/layout/layout.oa b/technology/freepdk45/lib/sram/tgate/layout/layout.oa deleted file mode 100755 index df7fd9be..00000000 Binary files a/technology/freepdk45/lib/sram/tgate/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/tgate/layout/layout.oa- b/technology/freepdk45/lib/sram/tgate/layout/layout.oa- deleted file mode 100755 index df7fd9be..00000000 Binary files a/technology/freepdk45/lib/sram/tgate/layout/layout.oa- and /dev/null differ diff --git a/technology/freepdk45/lib/sram/tgate/layout/master.tag b/technology/freepdk45/lib/sram/tgate/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/tgate/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/sram/write_driver/layout/layout.oa b/technology/freepdk45/lib/sram/write_driver/layout/layout.oa deleted file mode 100755 index 79c3ed15..00000000 Binary files a/technology/freepdk45/lib/sram/write_driver/layout/layout.oa and /dev/null differ diff --git a/technology/freepdk45/lib/sram/write_driver/layout/layout.oa- b/technology/freepdk45/lib/sram/write_driver/layout/layout.oa- deleted file mode 100755 index e5f3cdf7..00000000 Binary files a/technology/freepdk45/lib/sram/write_driver/layout/layout.oa- and /dev/null differ diff --git a/technology/freepdk45/lib/sram/write_driver/layout/master.tag b/technology/freepdk45/lib/sram/write_driver/layout/master.tag deleted file mode 100755 index 431d8f04..00000000 --- a/technology/freepdk45/lib/sram/write_driver/layout/master.tag +++ /dev/null @@ -1,2 +0,0 @@ --- Master.tag File, Rev:1.0 -layout.oa diff --git a/technology/freepdk45/lib/stream_all_gds.sh b/technology/freepdk45/lib/stream_all_gds.sh deleted file mode 100644 index 8a2c1048..00000000 --- a/technology/freepdk45/lib/stream_all_gds.sh +++ /dev/null @@ -1,8 +0,0 @@ - -for i in addr_ff clock_nor dinv inv_clk inv_nor nor_1 out_inv_16 output_latch sense_amp addr_latch cell_10t dinv_mx inv_col mux_a nor_1_mx out_inv_2 precharge tgate cell_6t inv inv_dec mux_abar out_inv_4 write_driver -do - echo $i - strmout -layerMap ../sram_lib/layers.map -library sram -topCell $i -view layout -strmFile ../sram_lib/$i.gds -done - - diff --git a/technology/freepdk45/sp_lib/dummy_cell_1rw_1r.sp b/technology/freepdk45/sp_lib/dummy_cell_1rw_1r.sp new file mode 100644 index 00000000..c3c082ee --- /dev/null +++ b/technology/freepdk45/sp_lib/dummy_cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT dummy_cell_1rw_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1_noconn 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_noconn gnd NMOS_VTG W=180.0n L=50n m=1 +MM5 Q wl0 bl0_noconn gnd NMOS_VTG W=135.00n L=50n m=1 +MM4 Q_bar wl0 br0_noconn gnd NMOS_VTG W=135.00n L=50n m=1 +MM1 Q Q_bar gnd gnd NMOS_VTG W=205.0n L=50n m=1 +MM0 Q_bar Q gnd gnd NMOS_VTG W=205.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/sp_lib/dummy_cell_1w_1r.sp b/technology/freepdk45/sp_lib/dummy_cell_1w_1r.sp new file mode 100644 index 00000000..72d7553f --- /dev/null +++ b/technology/freepdk45/sp_lib/dummy_cell_1w_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT dummy_cell_1w_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1_noconn 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_noconn gnd NMOS_VTG W=180.0n L=50n m=1 +MM5 Q wl0 bl0_noconn gnd NMOS_VTG W=135.00n L=50n m=1 +MM4 Q_bar wl0 br0_noconn gnd NMOS_VTG W=135.00n L=50n m=1 +MM1 Q Q_bar gnd gnd NMOS_VTG W=205.0n L=50n m=1 +MM0 Q_bar Q gnd gnd NMOS_VTG W=205.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/sp_lib/dummy_cell_6t.sp b/technology/freepdk45/sp_lib/dummy_cell_6t.sp new file mode 100644 index 00000000..ab862ec5 --- /dev/null +++ b/technology/freepdk45/sp_lib/dummy_cell_6t.sp @@ -0,0 +1,15 @@ + +.SUBCKT dummy_cell_6t bl br wl vdd gnd +* Inverter 1 +MM0 Qbar Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 Qbar Q vdd vdd PMOS_VTG W=90n L=50n + +* Inverer 2 +MM1 Q Qbar gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q Qbar vdd vdd PMOS_VTG W=90n L=50n + +* Access transistors +MM3 bl_noconn wl Q gnd NMOS_VTG W=135.00n L=50n +MM2 br_noconn wl Qbar gnd NMOS_VTG W=135.00n L=50n +.ENDS cell_6t + diff --git a/technology/freepdk45/tech/__init__.py b/technology/freepdk45/tech/__init__.py index 6b2d03b3..662a09b6 100644 --- a/technology/freepdk45/tech/__init__.py +++ b/technology/freepdk45/tech/__init__.py @@ -1,6 +1,13 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ Import tech specific modules. """ -from tech import * +from .tech import * diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 760d2a5a..caa3c748 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import os from design_rules import * @@ -7,7 +14,16 @@ File containing the process technology parameters for FreePDK 45nm. #GDS file info GDS = {} -# gds units +# gds units +# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first +#is the size of a database unit in user units. The second is the size +#of a database unit in meters. For example, if your library was +#created with the default units (user unit = 1 m and 1000 database +#units per user unit), then the first number would be 0.001 and the +#second number would be 10-9. Typically, the first number is less than +#1, since you use more than 1 database unit per user unit. To +#calculate the size of a user unit in meters, divide the second number +#by the first." GDS["unit"] = (0.0005,1e-9) # default label zoom GDS["zoom"] = 0.05 @@ -287,37 +303,16 @@ spice["fall_time"] = 0.005 # fall time in [Nano-seconds] spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) spice["nom_temperature"] = 25 # Nominal temperature (celcius) - -#sram signal names -#FIXME: We don't use these everywhere... -spice["vdd_name"] = "vdd" -spice["gnd_name"] = "gnd" -spice["control_signals"] = ["CSB", "WEB"] -spice["data_name"] = "DATA" -spice["addr_name"] = "ADDR" -spice["minwidth_tx"] = drc["minwidth_tx"] -spice["channel"] = drc["minlength_channel"] -spice["clk"] = "clk" - # analytical delay parameters -spice["vdd_nominal"] = 1.0 # Typical Threshold voltage in Volts -spice["temp_nominal"] = 25.0 # Typical Threshold voltage in Volts -spice["v_threshold_typical"] = 0.4 # Typical Threshold voltage in Volts +spice["nom_threshold"] = 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 spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff spice["min_tx_gate_c"] = 0.2 # Minimum transistor gate capacitance in ff -spice["msflop_setup"] = 9 # DFF setup time in ps -spice["msflop_hold"] = 1 # DFF hold time in ps -spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps -spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load -spice["msflop_in_cap"] = 0.2091 # Input capacitance of ms_flop (Din) [Femto-farad] spice["dff_setup"] = 9 # DFF setup time in ps spice["dff_hold"] = 1 # DFF hold time in ps -spice["dff_delay"] = 20.5 # DFF Clk-to-q delay in ps -spice["dff_slew"] = 13.1 # DFF output slew in ps w/ no load -spice["dff_in_cap"] = 0.2091 # Input capacitance of ms_flop (Din) [Femto-farad] +spice["dff_in_cap"] = 0.2091 # Input capacitance (D) [Femto-farad] +spice["dff_out_cap"] = 2 # Output capacitance (Q) [Femto-farad] # analytical power parameters, many values are temporary spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW @@ -325,26 +320,21 @@ spice["inv_leakage"] = 1 # Leakage power of inverter in nW spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW -spice["msflop_leakage"] = 1 # Leakage power of flop in nW -spice["flop_para_cap"] = 2 # Parasitic Output capacitance in fF +spice["dff_leakage"] = 1 # Leakage power of flop in nW -spice["default_event_rate"] = 100 # Default event activity of every gate. MHz -spice["flop_transition_prob"] = .5 # Transition probability of inverter. -spice["inv_transition_prob"] = .5 # Transition probability of inverter. -spice["nand2_transition_prob"] = .1875 # Transition probability of 2-input nand. -spice["nand3_transition_prob"] = .1094 # Transition probability of 3-input nand. -spice["nor2_transition_prob"] = .1875 # Transition probability of 2-input nor. +spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz #Parameters related to sense amp enable timing and delay chain/RBL sizing -parameter["static_delay_stages"] = 4 -parameter["static_fanout_per_stage"] = 3 -parameter["static_fanout_list"] = parameter["static_delay_stages"]*[parameter["static_fanout_per_stage"]] +parameter["le_tau"] = 2.25 #In pico-seconds. +parameter["cap_relative_per_ff"] = 7.5 #Units of Relative Capacitance/ Femto-Farad parameter["dff_clk_cin"] = 30.6 #relative capacitance parameter["6tcell_wl_cin"] = 3 #relative capacitance parameter["min_inv_para_delay"] = 2.4 #Tau delay units -parameter["sa_en_pmos_size"] = .72 #micro-meters -parameter["sa_en_nmos_size"] = .27 #micro-meters -parameter["rbl_height_percentage"] = .5 #Height of RBL compared to bitcell array +parameter["sa_en_pmos_size"] = 0.72 #micro-meters +parameter["sa_en_nmos_size"] = 0.27 #micro-meters +parameter["sa_inv_pmos_size"] = 0.54 #micro-meters +parameter["sa_inv_nmos_size"] = 0.27 #micro-meters +parameter["bitcell_drain_cap"] = 0.1 #In Femto-Farad, approximation of drain capacitance ################################################### ##END Spice Simulation Parameters diff --git a/technology/freepdk45/tf/FreePDK45.lyp b/technology/freepdk45/tf/FreePDK45.lyp new file mode 100644 index 00000000..29e8e32f --- /dev/null +++ b/technology/freepdk45/tf/FreePDK45.lyp @@ -0,0 +1,1749 @@ + + + + #ff8000 + #ff8000 + 0 + 0 + C36 + C0 + true + true + false + 1 + false + false + 0 + pwell.drawing - 2/0 + 2/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + C36 + C0 + true + true + false + 1 + false + false + 0 + nwell.drawing - 3/0 + 3/0@1 + + + #0000ff + #0000ff + 0 + 0 + C41 + C1 + true + true + false + 1 + false + false + 0 + vtg.drawing - 6/0 + 6/0@1 + + + #0000ff + #0000ff + 0 + 0 + C42 + C1 + true + true + false + 1 + false + false + 0 + vth.drawing - 7/0 + 7/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + C34 + C0 + true + true + false + 1 + false + false + 0 + active.drawing - 1/0 + 1/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + C20 + C0 + true + true + false + 1 + false + false + 0 + nimplant.drawing - 4/0 + 4/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C20 + C0 + true + true + false + 1 + false + false + 0 + pimplant.drawing - 5/0 + 5/0@1 + + + #ff0000 + #ff0000 + 0 + 0 + C37 + C0 + true + true + false + 1 + false + false + 0 + poly.drawing - 9/0 + 9/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C43 + C0 + true + true + false + 1 + false + false + 0 + thkox.drawing - 8/0 + 8/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + C1 + C0 + true + true + false + 1 + false + false + 0 + contact.drawing - 10/0 + 10/0@1 + + + #0000ff + #0000ff + 0 + 0 + C12 + C0 + true + true + false + 1 + false + false + 0 + metal1.drawing - 11/0 + 11/0@1 + + + #333399 + #ff00ff + 0 + 0 + C39 + C0 + true + true + false + 1 + false + false + 0 + via1.drawing - 12/0 + 12/0@1 + + + #ff00ff + #ff00ff + 0 + 0 + C2 + C0 + true + true + false + 1 + false + false + 0 + metal2.drawing - 13/0 + 13/0@1 + + + #39bfff + #39bfff + 0 + 0 + C39 + C0 + true + true + false + 1 + false + false + 0 + via2.drawing - 14/0 + 14/0@1 + + + #00ffff + #00ffff + 0 + 0 + C11 + C0 + true + true + false + 1 + false + false + 0 + metal3.drawing - 15/0 + 15/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C39 + C0 + true + true + false + 1 + false + false + 0 + via3.drawing - 16/0 + 16/0@1 + + + #ffffcc + #ffffcc + 0 + 0 + C25 + C0 + true + true + false + 1 + false + false + 0 + metal4.drawing - 17/0 + 17/0@1 + + + #0000ff + #0000ff + 0 + 0 + C39 + C0 + true + true + false + 1 + false + false + 0 + via4.drawing - 18/0 + 18/0@1 + + + #39bfff + #39bfff + 0 + 0 + C29 + C0 + true + true + false + 1 + false + false + 0 + metal5.drawing - 19/0 + 19/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C39 + C0 + true + true + false + 1 + false + false + 0 + via5.drawing - 20/0 + 20/0@1 + + + #d9cc00 + #d9cc00 + 0 + 0 + C8 + C0 + true + true + false + 1 + false + false + 0 + metal6.drawing - 21/0 + 21/0@1 + + + #ff00ff + #ff00ff + 0 + 0 + C39 + C0 + true + true + false + 1 + false + false + 0 + via6.drawing - 22/0 + 22/0@1 + + + #00ff00 + #00ff00 + 0 + 0 + C11 + C0 + true + true + false + 1 + false + false + 0 + metal7.drawing - 23/0 + 23/0@1 + + + #39bfff + #39bfff + 0 + 0 + C39 + C0 + true + true + false + 1 + false + false + 0 + via7.drawing - 24/0 + 24/0@1 + + + #ffffff + #ffffff + 0 + 0 + C4 + C0 + true + true + false + 1 + false + false + 0 + metal8.drawing - 25/0 + 25/0@1 + + + #ffffcc + #ffffcc + 0 + 0 + C39 + C0 + true + true + false + 1 + false + false + 0 + via8.drawing - 26/0 + 26/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C6 + C0 + true + true + false + 1 + false + false + 0 + metal9.drawing - 27/0 + 27/0@1 + + + #0000ff + #0000ff + 0 + 0 + C39 + C0 + true + true + false + 1 + false + false + 0 + via9.drawing - 28/0 + 28/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C29 + C0 + true + true + false + 1 + false + false + 0 + metal10.drawing - 29/0 + 29/0@1 + + + #9900e6 + #9900e6 + 0 + 0 + C0 + C0 + true + true + false + 1 + false + false + 0 + comment.drawing - 239/0 + 239/0@1 + + + + + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + + 1 + blank + + + + **************** + **************** + **************** + **************** + **************** + **************** + **************** + **************** + **************** + **************** + **************** + **************** + **************** + **************** + **************** + **************** + + 2 + solid + + + + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + + 3 + dots + + + + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + + 4 + hLine + + + + ................ + ................ + ................ + ................ + ................ + ................ + ................ + **************** + ................ + ................ + ................ + ................ + ................ + ................ + ................ + **************** + + 5 + hLine2 + + + + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + + 6 + vLine + + + + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + *......*.......* + + 7 + vLine2 + + + + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + + 8 + cross + + + + *...*...*...*... + .*.......*...... + ................ + ................ + *....*..*....*.. + ....*.......*... + ................ + ................ + *...*...*...*... + .*.......*...... + ................ + ................ + *....*..*....*.. + ....*.......*... + ................ + ................ + + 9 + miniHatch + + + + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + + 10 + grid + + + + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + + 11 + slash + + + + ...*.......*.... + ..*.......*..... + .*.......*...... + *.......*....... + .......*.......* + ......*.......*. + .....*.......*.. + ....*.......*... + ...*.......*.... + ..*.......*..... + .*.......*...... + *.......*....... + .......*.......* + ......*.......*. + .....*.......*.. + ....*.......*... + + 12 + halfslash + + + + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + + 13 + backSlash + + + + **......**...... + ..*.......*..... + ...**......**... + .....*.......*.. + ......**......** + *.......*....... + .**......**..... + ...*.......*.... + ....**......**.. + ......*.......*. + *......**......* + .*.......*...... + ..**......**.... + ....*.......*... + .....**......**. + .......*.......* + + 14 + hZigZag + + + + *....*....*..... + *.....*....*.... + .*....*.....*... + ..*....*....*... + ..*.....*....*.. + ...*....*.....*. + ....*....*....*. + ....*.....*....* + *....*....*..... + *.....*....*.... + .*....*.....*... + ..*....*....*... + ..*.....*....*.. + ...*....*.....*. + ....*....*....*. + ....*.....*....* + + 15 + vZigZag + + + + .....*....*....* + ....*....*.....* + ...*.....*....*. + ...*....*....*.. + ..*....*.....*.. + .*.....*....*... + .*....*....*.... + *....*.....*.... + .....*....*....* + ....*....*.....* + ...*.....*....*. + ...*....*....*.. + ..*....*.....*.. + .*.....*....*... + .*....*....*.... + *....*.....*.... + + 16 + rvZigZag + + + + ................ + ................ + ...*****...***** + ...*...*...*...* + ...*...*...*...* + ****...*****...* + ................ + ................ + ................ + ................ + ...*****...***** + ...*...*...*...* + ...*...*...*...* + ****...*****...* + ................ + ................ + + 17 + hCurb + + + + .....*.......*.. + .....*.......*.. + .....*.......*.. + ..****....****.. + ..*.......*..... + ..*.......*..... + ..*.......*..... + ..****....****.. + .....*.......*.. + .....*.......*.. + .....*.......*.. + ..****....****.. + ..*.......*..... + ..*.......*..... + ..*.......*..... + ..****....****.. + + 18 + vCurb + + + + **************** + ..*.......*..... + ..*.......*..... + ..*.......*..... + **************** + ......*.......*. + ......*.......*. + ......*.......*. + **************** + ..*.......*..... + ..*.......*..... + ..*.......*..... + **************** + ......*.......*. + ......*.......*. + ......*.......*. + + 19 + brick + + + + ................ + ..*.......*..... + ..*.......*..... + ..*.......*..... + *****...*****... + ..*.......*..... + ..*.......*..... + ..*.......*..... + ................ + .....*.......*.. + .....*.......*.. + .....*.......*.. + ...*****...***** + .....*.......*.. + .....*.......*.. + .....*.......*.. + + 20 + dagger + + + + ................ + ....*........... + ...*.*.......... + ..*...*......... + .*.....*........ + *********....... + ................ + ................ + ................ + ...........*.... + ..........*.*... + .........*...*.. + ........*.....*. + .......********* + ................ + ................ + + 21 + triangle + + + + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + + 22 + x + + + + *............... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + + 23 + dot1 + + + + ................ + ................ + ................ + ...***.......... + ...***.......... + ...***.......... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + + 24 + dot2 + + + + ................ + ................ + ..*.....*.....*. + ................ + ................ + .....*.....*.... + ................ + ................ + ..*.....*.....*. + ................ + ................ + .....*.....*.... + ................ + ................ + ..*.....*.....*. + ................ + + 25 + dot3 + + + + ................ + ................ + .*...*.....*.... + ................ + ................ + ........*.....*. + ................ + ................ + .*...*.....*.... + ................ + ................ + ........*.....*. + ................ + ................ + .*...*.....*.... + ................ + + 26 + dot4 + + + + ********........ + ********........ + ********........ + ********........ + ********........ + ********........ + ********........ + ********........ + ........******** + ........******** + ........******** + ........******** + ........******** + ........******** + ........******** + ........******** + + 27 + checker + + + + ..*...*...*..... + ................ + *...*...*...*... + ................ + ..*...*...*...*. + ................ + *...*...*...*... + ................ + ..*...*...*...*. + ................ + *...*...*...*... + ................ + ..*...*...*...*. + ................ + ....*...*...*... + ...............* + + 28 + viap + + + + *............... + **.............. + .**............. + ..**............ + ...**........... + ....**.......... + .....**......... + ......**........ + .......**....... + ........**...... + .........**..... + ..........**.... + ...........**... + ............**.. + .............**. + ..............** + + 29 + metal1S + + + + *..............* + ................ + ................ + ................ + ...........*.... + .....*.......... + ................ + ................ + ................ + ..*............. + ................ + ..........*..... + ................ + ................ + .....*.......... + *..............* + + 30 + metal2S + + + + *.....***.....** + *.......*....... + ***.....***..... + ..*.......*..... + ..***.....***... + ....*.......*... + ....***.....***. + ......*.......*. + *.....***.....** + *.......*....... + ***.....***..... + ..*.......*..... + ..***.....***... + ....*.......*... + ....***.....***. + ......*.......*. + + 31 + gnd2S + + + + ...**......**... + ....*.......*... + .....**......**. + ......*.......*. + *......**......* + *.......*....... + .**......**..... + ..*.......*..... + ...**......**... + ....*.......*... + .....**......**. + ......*.......*. + *......**......* + *.......*....... + .**......**..... + ..*.......*..... + + 32 + vcc2S + + + + *..**...*..**... + .*..*....*..*... + ..*..**...*..**. + ...*..*....*..*. + *...*..**...*..* + *....*..*....*.. + .**...*..**...*. + ..*....*..*....* + *..**...*..**... + .*..*....*..*... + ..*..**...*..**. + ...*..*....*..*. + *...*..**...*..* + *....*..*....*.. + .**...*..**...*. + ..*....*..*....* + + 33 + vcc1S + + + + ................ + ................ + ................ + ..***.....**.... + ..*..*...*..*... + ..*..*......*... + ..***......*.... + ..*.......*..... + ..*......*...... + ..*......****... + ................ + ................ + ................ + ................ + ................ + ................ + + 34 + poly2p + + + + **......**...... + **......**...... + ................ + ................ + ....**......**.. + ....**......**.. + ................ + ................ + **......**...... + **......**...... + ................ + ................ + ....**......**.. + ....**......**.. + ................ + ................ + + 35 + contp + + + + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ...............*................ + ...............*................ + .............*****.............. + ...............*................ + ...............*................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + + 36 + pplusp + + + + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ..........*...........*......... + ..........*.....*.....*......... + ...........*...*.*...*.......... + ...........*...*.*...*.......... + ............*.*...*.*........... + ............*.*...*.*........... + .............*.....*............ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + + 37 + wellp + + + + .*.*.*.*.*.*.*.* + *.*.*.*.*.*.*.*. + .*.*.*.*.*.*.*.* + *.*.*.*.*.*.*.*. + .*.*.*.*.*.*.*.* + *.*.*.*.*.*.*.*. + .*.*.*.*.*.*.*.* + *.*.*.*.*.*.*.*. + .*.*.*.*.*.*.*.* + *.*.*.*.*.*.*.*. + .*.*.*.*.*.*.*.* + *.*.*.*.*.*.*.*. + .*.*.*.*.*.*.*.* + *.*.*.*.*.*.*.*. + .*.*.*.*.*.*.*.* + *.*.*.*.*.*.*.*. + + 38 + checker1 + + + + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + + 39 + checker2 + + + + **.***.***.***.* + *.*.*.*.*.*.*.*. + .***.***.***.*** + *.*.*.*.*.*.*.*. + **.***.***.***.* + *.*.*.*.*.*.*.*. + .***.***.***.*** + *.*.*.*.*.*.*.*. + **.***.***.***.* + *.*.*.*.*.*.*.*. + .***.***.***.*** + *.*.*.*.*.*.*.*. + **.***.***.***.* + *.*.*.*.*.*.*.*. + .***.***.***.*** + *.*.*.*.*.*.*.*. + + 40 + invCross + + + + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ........***....***...***........ + .........**....***...**......... + .........**....***...**......... + .........**....*.*...**......... + .........***..**.**..**......... + ..........**..**.**.**.......... + ..........**..**.**.**.......... + ..........**..**.**.**.......... + ..........**..*...*.**.......... + ...........***....***........... + ...........***....***........... + ...........***....***........... + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + + 41 + wellBp + + + + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ...........**********........... + ...........*.........*.......... + ...........*.................... + ...........*.................... + ...........*.................... + ...........*.................... + ...........*....*****........... + ...........*........*........... + ...........*........*........... + ...........*........*........... + ...........**********........... + + 42 + wellvtg + + + + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ...........*.................... + ...........*.......*............ + ...........*.......*............ + ...........*.......*............ + ...........*.......*............ + ...........*********............ + ...........*.......*............ + ...........*.......*............ + ...........*.......*............ + ...........*.......*............ + ...........*.......*............ + + 43 + wellvth + + + + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ...........*********............ + ...........*...*...*............ + ...............*................ + ...............*................ + ...............*................ + ...............*................ + ...............*................ + ...............*................ + ...............*................ + ...............*................ + ...............*................ + + 44 + thickox + + + + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ..............****.............. + ............********............ + ............**...***............ + ...........**.....***........... + ...........**................... + ...........**................... + ...........**................... + ...........**................... + ...........**.....***........... + ............**...***............ + ............*******............. + ..............****.............. + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + ................................ + + 45 + cwellBp + + + + ................ + ................ + .......**....... + .......**....... + .......**....... + .......**....... + ...**********... + ................ + ................ + .....******..... + ....*..**..*.... + ...*...**...*... + .......**....... + .......**....... + ................ + ................ + + 46 + capID + + + + ................ + ................ + ................ + ................ + ...*............ + ...*............ + ..*.*........... + ..*.*........... + ***.*..***...... + .....*.*........ + .....*.*........ + ......*......... + ......*......... + ................ + ................ + ................ + + 47 + resID + + + + ................ + ................ + ................ + ................ + ......*....*.... + ......**...*.... + ......*.*..*.... + ......*..*.*.... + ...****...****.. + ......*..*.*.... + ......*.*..*.... + ......**...*.... + ......*....*.... + ................ + ................ + ................ + + 48 + diodeID + + + + **************** + **************** + **.*.*.***.*.*.* + **************** + **.*.*.***.*.*.* + **************** + **.*.*.***.*.*.* + **************** + **************** + **************** + **.*.*.***.*.*.* + **************** + **.*.*.***.*.*.* + **************** + **.*.*.***.*.*.* + **************** + + 49 + sgrid + + + *** + 1 + solid + + + ****.. + 2 + dashed + + + *.. + 3 + dots + + + ***..*.. + 4 + dashDot + + + **.. + 5 + shortDash + + + ****..**.. + 6 + doubleDash + + + *... + 7 + hidden + + + *** + 8 + thickLine + + + *** + 9 + mLine + + diff --git a/technology/freepdk45/tf/README.txt b/technology/freepdk45/tf/README.txt index 5393b0b3..662dbb7c 100644 --- a/technology/freepdk45/tf/README.txt +++ b/technology/freepdk45/tf/README.txt @@ -44,4 +44,14 @@ Contributions and modifications to this kit are welcomed and encouraged. ncsu_basekit/ Base kit for custom design osu_soc/ Standard-cell kit for synthesis, place, & route +FreePDK45.lyp is converted automatically from the .tf using: +https://github.com/klayoutmatthias/tf_import +Command line: +klayout -z -rd tf_file=FreePDK45.tf -rd lyp_file=FreePDK45.lyp +You can then view layouts with: +klayout file.gds -l mosis.lyp +glade_freepdk45.py is a script for Glade: +https://peardrop.co.uk/ +to load the .tf using: +glade -script ~/openram/technology/freepdk45/tf/glade_freepdk45.py -gds file.gds diff --git a/technology/freepdk45/tf/SVRF_EULA_06Feb09.txt b/technology/freepdk45/tf/SVRF_EULA_06Feb09.txt new file mode 100644 index 00000000..637a7627 --- /dev/null +++ b/technology/freepdk45/tf/SVRF_EULA_06Feb09.txt @@ -0,0 +1,84 @@ + SVRF(TM) TECHNOLOGY LICENSE AGREEMENT + February 6, 2009 + +This license is a legal "Agreement" concerning the use of SVRF +Technology between you, the end user, either individually or as an +authorized representative of the school or company acquiring the +license ("You"), and Mentor Graphics Corporation and Mentor Graphics +(Ireland) Limited acting directly or through their subsidiaries or +authorized distributors (collectively "Mentor Graphics"). "SVRF +Technology" shall mean Mentor Graphics' syntax for expressing process +rules and controlling physical verification, principally the syntax +expressed in Mentor Graphics. SVRF User Manual. USE OF SVRF TECHNOLOGY +INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE OF THE TERMS AND +CONDITIONS SET FORTH IN THIS AGREEMENT. If you do not agree to these +terms and conditions, promptly certify destruction of the SVRF +Technology and all accompanying items within five days after receipt +of SVRF Technology. + +1. GRANT OF LICENSE. This file includes SVRF Technology under license + by Mentor Graphics Corporation. Mentor Graphics grants to You a + nontransferable, nonexclusive license to use SVRF Technology solely + for use with Mentor Graphics' Calibre(R) tools and either: (a) in an + educational classroom or laboratory environment; or (b) for your + internal business purpose as allowed by an existing license + agreement between You and Mentor Graphics. + +2. RESTRICTIONS ON USE. All SVRF Technology constitutes or contains + trade secrets and confidential information of Mentor Graphics or + its licensors. You shall not make SVRF Technology available in any + form to any person other those persons whose performance requires + such access and who are under obligations of confidentiality. You + shall take appropriate action to protect the confidentiality of + SVRF Technology and ensure that persons permitted access to SVRF + Technology do not disclose it or use it except as permitted by this + Agreement. The provisions of this Section 2 shall survive the + termination or expiration of this Agreement. + +3. NO WARRANTY. Mentor Graphics expressly disclaims any warranty for + SVRF Technology. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, + SVRF TECHNOLOGY AND ANY RELATED DOCUMENTATION ARE PROVIDED "AS IS" + AND WITH ALL FAULTS AND WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, THE + IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE, OR NONINFRINGEMENT. THE ENTIRE RISK ARISING OUT + OF USE OR PERFORMANCE OF SVRF TECHNOLOGY REMAINS WITH YOU. + +4. LIMITATION OF LIABILITY. EXCEPT WHERE THIS EXCLUSION OR RESTRICTION + OF LIABILITY WOULD BE VOID OR INEFFECTIVE UNDER APPLICABLE LAW, IN + NO EVENT WILL MENTOR GRAPHICS OR ITS LICENSORS BE LIABLE FOR + INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES (INCLUDING + LOST PROFITS OR SAVINGS) WHETHER BASED ON CONTRACT, TORT OR ANY + OTHER LEGAL THEORY, EVEN IF MENTOR GRAPHICS OR ITS LICENSORS HAVE + BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +5. TERM. This Agreement remains effective until termination. This + Agreement will immediately terminate upon notice if you exceed the + scope of license granted or otherwise fail to comply with the + provisions of Sections 1 or 2. For any other material breach under + this Agreement, Mentor Graphics may terminate this Agreement upon + 30 days written notice if you are in material breach and fail to + cure such breach within the 30-day notice period. Upon any + termination, you agree to cease all use of SVRF Technology and + certify destruction of SVRF Technology, including all copies, to + Mentor Graphics. reasonable satisfaction. + +6. GOVERNING LAW. This License shall be governed by and construed + under the laws of the State of Oregon, USA, if You are located in + North or South America, and the laws of Ireland if You are located + outside of North or South America. + +7. SEVERABILITY. If any provision of this Agreement is held by a court + of competent jurisdiction to be void, invalid, unenforceable or + illegal, such provision shall be severed from this Agreement and + the remaining provisions will remain in full force and effect. + +8. MISCELLANEOUS. This Agreement contains the parties' entire + understanding relating to its subject matter and supersedes all + prior or contemporaneous agreements, except valid license + agreements related to the subject matter of this Agreement (which + are physically signed by you and an authorized agent of Mentor + Graphics). This Agreement may only be modified in writing by + authorized representatives of the parties. Waiver of terms or + excuse of breach must be in writing and shall not constitute + subsequent consent, waiver or excuse. diff --git a/technology/freepdk45/tf/display.drf b/technology/freepdk45/tf/display.drf index 7923ae62..2b6304ec 100644 --- a/technology/freepdk45/tf/display.drf +++ b/technology/freepdk45/tf/display.drf @@ -1097,7 +1097,7 @@ drDefinePacket( ( display metal1Pin X solid blue blue ) ( display metal1Lbl blank solid blue blue ) ( display metal1Bnd blank solid blue blue ) - ( display contact X solid black lime ) + ( display contact solid solid green green ) ( display contactNe blank solid brown brown ) ( display contactPin X solid black black ) ( display contactLbl blank solid black black ) @@ -1223,6 +1223,7 @@ drDefinePacket( ( display prBoundary blank solid purple purple ) ( display prBoundaryBnd blank solid cyan cyan ) ( display prBoundaryLbl blank solid purple purple ) + ( display comment blank solid purple purple ) ( display align blank solid tan tan ) ( display hardFence blank solid red red ) ( display softFence blank solid yellow yellow ) diff --git a/technology/setup_scripts/setup_openram_scn3me_subm.py b/technology/scn3me_subm/__init__.py similarity index 77% rename from technology/setup_scripts/setup_openram_scn3me_subm.py rename to technology/scn3me_subm/__init__.py index 1508b2a8..87b26056 100644 --- a/technology/setup_scripts/setup_openram_scn3me_subm.py +++ b/technology/scn3me_subm/__init__.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +#Copyright (c) 2016-2019 Regents of the University of California and The Board +#of Regents for the Oklahoma Agricultural and Mechanical College +#(acting for and on behalf of Oklahoma State University) +#All rights reserved. +# #!/usr/bin/python """ This type of setup script should be placed in the setup_scripts directory in the trunk @@ -34,8 +41,3 @@ os.environ["DRCLVS_HOME"] = DRCLVS_HOME OPENRAM_TECH=os.path.abspath(os.environ.get("OPENRAM_TECH")) os.environ["SPICE_MODEL_DIR"] = "{0}/{1}/models".format(OPENRAM_TECH, TECHNOLOGY) -########################## -# Paths required for OPENRAM to function - -LOCAL = "{0}/..".format(os.path.dirname(__file__)) -sys.path.append("{0}/{1}/tech".format(LOCAL,TECHNOLOGY)) diff --git a/technology/scn3me_subm/gds_lib/cell_1rw_1r.gds b/technology/scn3me_subm/gds_lib/cell_1rw_1r.gds new file mode 100644 index 00000000..f9a19873 Binary files /dev/null and b/technology/scn3me_subm/gds_lib/cell_1rw_1r.gds differ diff --git a/technology/scn3me_subm/gds_lib/cell_1w_1r.gds b/technology/scn3me_subm/gds_lib/cell_1w_1r.gds new file mode 100644 index 00000000..41cff8af Binary files /dev/null and b/technology/scn3me_subm/gds_lib/cell_1w_1r.gds differ diff --git a/technology/scn3me_subm/gds_lib/cell_6t.gds b/technology/scn3me_subm/gds_lib/cell_6t.gds index 20e5367e..673962cc 100644 Binary files a/technology/scn3me_subm/gds_lib/cell_6t.gds and b/technology/scn3me_subm/gds_lib/cell_6t.gds differ diff --git a/technology/scn3me_subm/gds_lib/dff.gds b/technology/scn3me_subm/gds_lib/dff.gds index 07c37298..6d089c49 100644 Binary files a/technology/scn3me_subm/gds_lib/dff.gds and b/technology/scn3me_subm/gds_lib/dff.gds differ diff --git a/technology/scn3me_subm/gds_lib/replica_cell_1rw_1r.gds b/technology/scn3me_subm/gds_lib/replica_cell_1rw_1r.gds new file mode 100644 index 00000000..90002b77 Binary files /dev/null and b/technology/scn3me_subm/gds_lib/replica_cell_1rw_1r.gds differ diff --git a/technology/scn3me_subm/gds_lib/replica_cell_1w_1r.gds b/technology/scn3me_subm/gds_lib/replica_cell_1w_1r.gds new file mode 100644 index 00000000..2adb9582 Binary files /dev/null and b/technology/scn3me_subm/gds_lib/replica_cell_1w_1r.gds differ diff --git a/technology/scn3me_subm/gds_lib/replica_cell_6t.gds b/technology/scn3me_subm/gds_lib/replica_cell_6t.gds index 12d97796..b26d63d4 100644 Binary files a/technology/scn3me_subm/gds_lib/replica_cell_6t.gds and b/technology/scn3me_subm/gds_lib/replica_cell_6t.gds differ diff --git a/technology/scn3me_subm/gds_lib/sense_amp.gds b/technology/scn3me_subm/gds_lib/sense_amp.gds index 0fc7eb56..11081bd3 100644 Binary files a/technology/scn3me_subm/gds_lib/sense_amp.gds and b/technology/scn3me_subm/gds_lib/sense_amp.gds differ diff --git a/technology/scn3me_subm/gds_lib/tri_gate.gds b/technology/scn3me_subm/gds_lib/tri_gate.gds index a3f25a39..c48fe3a1 100644 Binary files a/technology/scn3me_subm/gds_lib/tri_gate.gds and b/technology/scn3me_subm/gds_lib/tri_gate.gds differ diff --git a/technology/scn3me_subm/gds_lib/write_driver.gds b/technology/scn3me_subm/gds_lib/write_driver.gds index fdd3ad88..0bef7b79 100644 Binary files a/technology/scn3me_subm/gds_lib/write_driver.gds and b/technology/scn3me_subm/gds_lib/write_driver.gds differ diff --git a/technology/scn3me_subm/mag_lib/cell_1rw_1r.mag b/technology/scn3me_subm/mag_lib/cell_1rw_1r.mag new file mode 100644 index 00000000..6a6fbc86 --- /dev/null +++ b/technology/scn3me_subm/mag_lib/cell_1rw_1r.mag @@ -0,0 +1,156 @@ +magic +tech scmos +timestamp 1558933786 +<< nwell >> +rect 0 48 54 77 +<< pwell >> +rect 0 0 54 48 +<< ntransistor >> +rect 14 34 16 38 +rect 22 30 24 38 +rect 30 30 32 38 +rect 38 34 40 38 +rect 14 17 16 23 +rect 22 17 24 23 +rect 30 17 32 23 +rect 38 17 40 23 +<< ptransistor >> +rect 22 56 24 59 +rect 30 56 32 59 +<< ndiffusion >> +rect 13 34 14 38 +rect 16 34 17 38 +rect 21 34 22 38 +rect 17 30 22 34 +rect 24 36 30 38 +rect 24 32 25 36 +rect 29 32 30 36 +rect 24 30 30 32 +rect 32 34 33 38 +rect 37 34 38 38 +rect 40 34 41 38 +rect 32 30 37 34 +rect 9 21 14 23 +rect 13 17 14 21 +rect 16 17 22 23 +rect 24 22 30 23 +rect 24 18 25 22 +rect 29 18 30 22 +rect 24 17 30 18 +rect 32 17 38 23 +rect 40 21 45 23 +rect 40 17 41 21 +<< pdiffusion >> +rect 21 56 22 59 +rect 24 56 25 59 +rect 29 56 30 59 +rect 32 56 33 59 +<< ndcontact >> +rect 9 34 13 38 +rect 17 34 21 38 +rect 25 32 29 36 +rect 33 34 37 38 +rect 41 34 45 38 +rect 9 17 13 21 +rect 25 18 29 22 +rect 41 17 45 21 +<< pdcontact >> +rect 17 56 21 60 +rect 25 56 29 60 +rect 33 56 37 60 +<< psubstratepcontact >> +rect 25 9 29 13 +<< nsubstratencontact >> +rect 37 70 41 74 +<< polysilicon >> +rect 22 59 24 62 +rect 30 59 32 62 +rect 22 45 24 56 +rect 30 53 32 56 +rect 13 41 16 43 +rect 14 38 16 41 +rect 22 38 24 41 +rect 30 38 32 49 +rect 38 41 41 43 +rect 38 38 40 41 +rect 14 32 16 34 +rect 38 32 40 34 +rect 14 23 16 24 +rect 22 23 24 30 +rect 30 23 32 30 +rect 38 23 40 24 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 28 49 32 53 +rect 9 41 13 45 +rect 22 41 26 45 +rect 41 41 45 45 +rect 12 24 16 28 +rect 38 24 42 28 +<< metal1 >> +rect 0 70 25 74 +rect 29 70 37 74 +rect 41 70 54 74 +rect 0 63 54 67 +rect 9 45 13 63 +rect 16 56 17 60 +rect 37 56 38 60 +rect 16 53 20 56 +rect 16 49 28 53 +rect 16 38 19 49 +rect 35 45 38 56 +rect 26 41 38 45 +rect 41 45 45 63 +rect 35 38 38 41 +rect 6 34 9 38 +rect 16 34 17 38 +rect 25 36 29 38 +rect 37 34 38 38 +rect 45 34 48 38 +rect 25 22 29 32 +rect 25 13 29 18 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 25 70 29 74 +rect 25 56 29 60 +rect 2 34 6 38 +rect 48 34 52 38 +rect 16 24 20 28 +rect 34 24 38 28 +rect 9 17 13 21 +rect 41 17 45 21 +rect 16 2 20 6 +rect 34 2 38 6 +<< metal2 >> +rect 2 38 6 74 +rect 2 0 6 34 +rect 9 21 13 74 +rect 25 60 29 70 +rect 9 0 13 17 +rect 16 6 20 24 +rect 34 6 38 24 +rect 41 21 45 74 +rect 41 0 45 17 +rect 48 38 52 74 +rect 48 0 52 34 +<< comment >> +rect 0 0 54 72 +<< labels >> +rlabel metal1 27 4 27 4 4 wl1 +rlabel psubstratepcontact 27 11 27 11 4 gnd +rlabel metal2 4 7 4 7 4 bl0 +rlabel metal2 50 7 50 7 4 br0 +rlabel metal1 19 65 19 65 4 wl0 +rlabel metal2 11 7 11 7 4 bl1 +rlabel metal2 43 7 43 7 4 br1 +rlabel metal1 18 72 18 72 1 vdd +<< properties >> +string path 0.000 0.000 243.000 0.000 243.000 324.000 0.000 324.000 0.000 0.000 +<< end >> diff --git a/technology/scn3me_subm/mag_lib/cell_1w_1r.mag b/technology/scn3me_subm/mag_lib/cell_1w_1r.mag new file mode 100644 index 00000000..6a6fbc86 --- /dev/null +++ b/technology/scn3me_subm/mag_lib/cell_1w_1r.mag @@ -0,0 +1,156 @@ +magic +tech scmos +timestamp 1558933786 +<< nwell >> +rect 0 48 54 77 +<< pwell >> +rect 0 0 54 48 +<< ntransistor >> +rect 14 34 16 38 +rect 22 30 24 38 +rect 30 30 32 38 +rect 38 34 40 38 +rect 14 17 16 23 +rect 22 17 24 23 +rect 30 17 32 23 +rect 38 17 40 23 +<< ptransistor >> +rect 22 56 24 59 +rect 30 56 32 59 +<< ndiffusion >> +rect 13 34 14 38 +rect 16 34 17 38 +rect 21 34 22 38 +rect 17 30 22 34 +rect 24 36 30 38 +rect 24 32 25 36 +rect 29 32 30 36 +rect 24 30 30 32 +rect 32 34 33 38 +rect 37 34 38 38 +rect 40 34 41 38 +rect 32 30 37 34 +rect 9 21 14 23 +rect 13 17 14 21 +rect 16 17 22 23 +rect 24 22 30 23 +rect 24 18 25 22 +rect 29 18 30 22 +rect 24 17 30 18 +rect 32 17 38 23 +rect 40 21 45 23 +rect 40 17 41 21 +<< pdiffusion >> +rect 21 56 22 59 +rect 24 56 25 59 +rect 29 56 30 59 +rect 32 56 33 59 +<< ndcontact >> +rect 9 34 13 38 +rect 17 34 21 38 +rect 25 32 29 36 +rect 33 34 37 38 +rect 41 34 45 38 +rect 9 17 13 21 +rect 25 18 29 22 +rect 41 17 45 21 +<< pdcontact >> +rect 17 56 21 60 +rect 25 56 29 60 +rect 33 56 37 60 +<< psubstratepcontact >> +rect 25 9 29 13 +<< nsubstratencontact >> +rect 37 70 41 74 +<< polysilicon >> +rect 22 59 24 62 +rect 30 59 32 62 +rect 22 45 24 56 +rect 30 53 32 56 +rect 13 41 16 43 +rect 14 38 16 41 +rect 22 38 24 41 +rect 30 38 32 49 +rect 38 41 41 43 +rect 38 38 40 41 +rect 14 32 16 34 +rect 38 32 40 34 +rect 14 23 16 24 +rect 22 23 24 30 +rect 30 23 32 30 +rect 38 23 40 24 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 28 49 32 53 +rect 9 41 13 45 +rect 22 41 26 45 +rect 41 41 45 45 +rect 12 24 16 28 +rect 38 24 42 28 +<< metal1 >> +rect 0 70 25 74 +rect 29 70 37 74 +rect 41 70 54 74 +rect 0 63 54 67 +rect 9 45 13 63 +rect 16 56 17 60 +rect 37 56 38 60 +rect 16 53 20 56 +rect 16 49 28 53 +rect 16 38 19 49 +rect 35 45 38 56 +rect 26 41 38 45 +rect 41 45 45 63 +rect 35 38 38 41 +rect 6 34 9 38 +rect 16 34 17 38 +rect 25 36 29 38 +rect 37 34 38 38 +rect 45 34 48 38 +rect 25 22 29 32 +rect 25 13 29 18 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 25 70 29 74 +rect 25 56 29 60 +rect 2 34 6 38 +rect 48 34 52 38 +rect 16 24 20 28 +rect 34 24 38 28 +rect 9 17 13 21 +rect 41 17 45 21 +rect 16 2 20 6 +rect 34 2 38 6 +<< metal2 >> +rect 2 38 6 74 +rect 2 0 6 34 +rect 9 21 13 74 +rect 25 60 29 70 +rect 9 0 13 17 +rect 16 6 20 24 +rect 34 6 38 24 +rect 41 21 45 74 +rect 41 0 45 17 +rect 48 38 52 74 +rect 48 0 52 34 +<< comment >> +rect 0 0 54 72 +<< labels >> +rlabel metal1 27 4 27 4 4 wl1 +rlabel psubstratepcontact 27 11 27 11 4 gnd +rlabel metal2 4 7 4 7 4 bl0 +rlabel metal2 50 7 50 7 4 br0 +rlabel metal1 19 65 19 65 4 wl0 +rlabel metal2 11 7 11 7 4 bl1 +rlabel metal2 43 7 43 7 4 br1 +rlabel metal1 18 72 18 72 1 vdd +<< properties >> +string path 0.000 0.000 243.000 0.000 243.000 324.000 0.000 324.000 0.000 0.000 +<< end >> diff --git a/technology/scn3me_subm/mag_lib/convertall.sh b/technology/scn3me_subm/mag_lib/convertall.sh index f5e2482c..43de584f 100755 --- a/technology/scn3me_subm/mag_lib/convertall.sh +++ b/technology/scn3me_subm/mag_lib/convertall.sh @@ -11,4 +11,12 @@ load tri_gate gds write tri_gate.gds load write_driver gds write write_driver.gds +load replica_cell_1w_1r +gds write replica_cell_1w_1r +load replica_cell_1rw_1r +gds write replica_cell_1rw_1r +load cell_1rw_1r +gds write cell_1rw_1r +load cell_1w_1r +gds write cell_1w_1r EOF diff --git a/technology/scn3me_subm/mag_lib/replica_cell_1rw_1r.mag b/technology/scn3me_subm/mag_lib/replica_cell_1rw_1r.mag new file mode 100644 index 00000000..a7387b7e --- /dev/null +++ b/technology/scn3me_subm/mag_lib/replica_cell_1rw_1r.mag @@ -0,0 +1,157 @@ +magic +tech scmos +timestamp 1558933826 +<< nwell >> +rect 0 48 54 77 +<< pwell >> +rect 0 0 54 48 +<< ntransistor >> +rect 14 34 16 38 +rect 22 30 24 38 +rect 30 30 32 38 +rect 38 34 40 38 +rect 14 17 16 23 +rect 22 17 24 23 +rect 30 17 32 23 +rect 38 17 40 23 +<< ptransistor >> +rect 22 56 24 59 +rect 30 56 32 59 +<< ndiffusion >> +rect 13 34 14 38 +rect 16 34 17 38 +rect 21 34 22 38 +rect 17 30 22 34 +rect 24 36 30 38 +rect 24 32 25 36 +rect 29 32 30 36 +rect 24 30 30 32 +rect 32 34 33 38 +rect 37 34 38 38 +rect 40 34 41 38 +rect 32 30 37 34 +rect 9 21 14 23 +rect 13 17 14 21 +rect 16 17 22 23 +rect 24 22 30 23 +rect 24 18 25 22 +rect 29 18 30 22 +rect 24 17 30 18 +rect 32 17 38 23 +rect 40 21 45 23 +rect 40 17 41 21 +<< pdiffusion >> +rect 21 56 22 59 +rect 24 56 25 59 +rect 29 56 30 59 +rect 32 56 33 59 +<< ndcontact >> +rect 9 34 13 38 +rect 17 34 21 38 +rect 25 32 29 36 +rect 33 34 37 38 +rect 41 34 45 38 +rect 9 17 13 21 +rect 25 18 29 22 +rect 41 17 45 21 +<< pdcontact >> +rect 17 56 21 60 +rect 25 56 29 60 +rect 33 56 37 60 +<< psubstratepcontact >> +rect 25 9 29 13 +<< nsubstratencontact >> +rect 37 70 41 74 +<< polysilicon >> +rect 22 59 24 62 +rect 30 59 32 62 +rect 22 45 24 56 +rect 30 53 32 56 +rect 13 41 16 43 +rect 14 38 16 41 +rect 22 38 24 41 +rect 30 38 32 49 +rect 38 41 41 43 +rect 38 38 40 41 +rect 14 32 16 34 +rect 38 32 40 34 +rect 14 23 16 24 +rect 22 23 24 30 +rect 30 23 32 30 +rect 38 23 40 24 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 28 49 32 53 +rect 9 41 13 45 +rect 22 41 26 45 +rect 41 41 45 45 +rect 12 24 16 28 +rect 38 24 42 28 +<< metal1 >> +rect 0 70 25 74 +rect 29 70 37 74 +rect 41 70 54 74 +rect 0 63 54 67 +rect 9 45 13 63 +rect 16 56 17 60 +rect 29 56 33 60 +rect 37 56 38 60 +rect 16 53 20 56 +rect 16 49 28 53 +rect 16 38 19 49 +rect 35 45 38 56 +rect 26 41 38 45 +rect 41 45 45 63 +rect 35 38 38 41 +rect 6 34 9 38 +rect 16 34 17 38 +rect 25 36 29 38 +rect 37 34 38 38 +rect 45 34 48 38 +rect 25 22 29 32 +rect 25 13 29 18 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 25 70 29 74 +rect 25 56 29 60 +rect 2 34 6 38 +rect 48 34 52 38 +rect 16 24 20 28 +rect 34 24 38 28 +rect 9 17 13 21 +rect 41 17 45 21 +rect 16 2 20 6 +rect 34 2 38 6 +<< metal2 >> +rect 2 38 6 74 +rect 2 0 6 34 +rect 9 21 13 74 +rect 25 60 29 70 +rect 9 0 13 17 +rect 16 6 20 24 +rect 34 6 38 24 +rect 41 21 45 74 +rect 41 0 45 17 +rect 48 38 52 74 +rect 48 0 52 34 +<< comment >> +rect 0 0 54 72 +<< labels >> +rlabel metal1 27 4 27 4 4 wl1 +rlabel psubstratepcontact 27 11 27 11 4 gnd +rlabel metal2 4 7 4 7 4 bl0 +rlabel metal2 50 7 50 7 4 br0 +rlabel metal1 19 65 19 65 4 wl0 +rlabel metal2 11 7 11 7 4 bl1 +rlabel metal2 43 7 43 7 4 br1 +rlabel metal1 18 72 18 72 1 vdd +<< properties >> +string path 0.000 0.000 243.000 0.000 243.000 324.000 0.000 324.000 0.000 0.000 +<< end >> diff --git a/technology/scn3me_subm/mag_lib/replica_cell_1w_1r.mag b/technology/scn3me_subm/mag_lib/replica_cell_1w_1r.mag new file mode 100644 index 00000000..a7387b7e --- /dev/null +++ b/technology/scn3me_subm/mag_lib/replica_cell_1w_1r.mag @@ -0,0 +1,157 @@ +magic +tech scmos +timestamp 1558933826 +<< nwell >> +rect 0 48 54 77 +<< pwell >> +rect 0 0 54 48 +<< ntransistor >> +rect 14 34 16 38 +rect 22 30 24 38 +rect 30 30 32 38 +rect 38 34 40 38 +rect 14 17 16 23 +rect 22 17 24 23 +rect 30 17 32 23 +rect 38 17 40 23 +<< ptransistor >> +rect 22 56 24 59 +rect 30 56 32 59 +<< ndiffusion >> +rect 13 34 14 38 +rect 16 34 17 38 +rect 21 34 22 38 +rect 17 30 22 34 +rect 24 36 30 38 +rect 24 32 25 36 +rect 29 32 30 36 +rect 24 30 30 32 +rect 32 34 33 38 +rect 37 34 38 38 +rect 40 34 41 38 +rect 32 30 37 34 +rect 9 21 14 23 +rect 13 17 14 21 +rect 16 17 22 23 +rect 24 22 30 23 +rect 24 18 25 22 +rect 29 18 30 22 +rect 24 17 30 18 +rect 32 17 38 23 +rect 40 21 45 23 +rect 40 17 41 21 +<< pdiffusion >> +rect 21 56 22 59 +rect 24 56 25 59 +rect 29 56 30 59 +rect 32 56 33 59 +<< ndcontact >> +rect 9 34 13 38 +rect 17 34 21 38 +rect 25 32 29 36 +rect 33 34 37 38 +rect 41 34 45 38 +rect 9 17 13 21 +rect 25 18 29 22 +rect 41 17 45 21 +<< pdcontact >> +rect 17 56 21 60 +rect 25 56 29 60 +rect 33 56 37 60 +<< psubstratepcontact >> +rect 25 9 29 13 +<< nsubstratencontact >> +rect 37 70 41 74 +<< polysilicon >> +rect 22 59 24 62 +rect 30 59 32 62 +rect 22 45 24 56 +rect 30 53 32 56 +rect 13 41 16 43 +rect 14 38 16 41 +rect 22 38 24 41 +rect 30 38 32 49 +rect 38 41 41 43 +rect 38 38 40 41 +rect 14 32 16 34 +rect 38 32 40 34 +rect 14 23 16 24 +rect 22 23 24 30 +rect 30 23 32 30 +rect 38 23 40 24 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 28 49 32 53 +rect 9 41 13 45 +rect 22 41 26 45 +rect 41 41 45 45 +rect 12 24 16 28 +rect 38 24 42 28 +<< metal1 >> +rect 0 70 25 74 +rect 29 70 37 74 +rect 41 70 54 74 +rect 0 63 54 67 +rect 9 45 13 63 +rect 16 56 17 60 +rect 29 56 33 60 +rect 37 56 38 60 +rect 16 53 20 56 +rect 16 49 28 53 +rect 16 38 19 49 +rect 35 45 38 56 +rect 26 41 38 45 +rect 41 45 45 63 +rect 35 38 38 41 +rect 6 34 9 38 +rect 16 34 17 38 +rect 25 36 29 38 +rect 37 34 38 38 +rect 45 34 48 38 +rect 25 22 29 32 +rect 25 13 29 18 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 25 70 29 74 +rect 25 56 29 60 +rect 2 34 6 38 +rect 48 34 52 38 +rect 16 24 20 28 +rect 34 24 38 28 +rect 9 17 13 21 +rect 41 17 45 21 +rect 16 2 20 6 +rect 34 2 38 6 +<< metal2 >> +rect 2 38 6 74 +rect 2 0 6 34 +rect 9 21 13 74 +rect 25 60 29 70 +rect 9 0 13 17 +rect 16 6 20 24 +rect 34 6 38 24 +rect 41 21 45 74 +rect 41 0 45 17 +rect 48 38 52 74 +rect 48 0 52 34 +<< comment >> +rect 0 0 54 72 +<< labels >> +rlabel metal1 27 4 27 4 4 wl1 +rlabel psubstratepcontact 27 11 27 11 4 gnd +rlabel metal2 4 7 4 7 4 bl0 +rlabel metal2 50 7 50 7 4 br0 +rlabel metal1 19 65 19 65 4 wl0 +rlabel metal2 11 7 11 7 4 bl1 +rlabel metal2 43 7 43 7 4 br1 +rlabel metal1 18 72 18 72 1 vdd +<< properties >> +string path 0.000 0.000 243.000 0.000 243.000 324.000 0.000 324.000 0.000 0.000 +<< end >> diff --git a/technology/scn3me_subm/mag_lib/setup.tcl b/technology/scn3me_subm/mag_lib/setup.tcl index af55a416..01639fe2 100644 --- a/technology/scn3me_subm/mag_lib/setup.tcl +++ b/technology/scn3me_subm/mag_lib/setup.tcl @@ -4,10 +4,12 @@ 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} +flatten class {-circuit1 bitcell_array_0} +flatten class {-circuit1 bitcell_array_1} +#flatten class {-circuit1 precharge_array_0} +#flatten class {-circuit1 precharge_array_1} +#flatten class {-circuit1 precharge_array_2} +#flatten class {-circuit1 precharge_array_3} property {-circuit1 nfet} remove as ad ps pd property {-circuit1 pfet} remove as ad ps pd property {-circuit2 n} remove as ad ps pd diff --git a/technology/scn3me_subm/sp_lib/cell_1rw_1r.sp b/technology/scn3me_subm/sp_lib/cell_1rw_1r.sp new file mode 100644 index 00000000..f58867a7 --- /dev/null +++ b/technology/scn3me_subm/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 n w=1.8u l=0.6u +MM8 RA_to_R_right Q gnd gnd n w=1.8u l=0.6u +MM7 RA_to_R_left Q_bar gnd gnd n w=1.8u l=0.6u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.8u l=0.6u +MM5 Q wl0 bl0 gnd n w=1.2u l=0.6u +MM4 Q_bar wl0 br0 gnd n w=1.2u l=0.6u +MM1 Q Q_bar gnd gnd n w=2.4u l=0.6u +MM0 Q_bar Q gnd gnd n w=2.4u l=0.6u +MM3 Q Q_bar vdd vdd p w=0.9u l=0.6u +MM2 Q_bar Q vdd vdd p w=0.9u l=0.6u +.ENDS + diff --git a/technology/scn3me_subm/sp_lib/cell_1w_1r.sp b/technology/scn3me_subm/sp_lib/cell_1w_1r.sp new file mode 100644 index 00000000..fe981d6d --- /dev/null +++ b/technology/scn3me_subm/sp_lib/cell_1w_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT cell_1w_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd n w=1.8u l=0.6u +MM8 RA_to_R_right Q gnd gnd n w=1.8u l=0.6u +MM7 RA_to_R_left Q_bar gnd gnd n w=1.8u l=0.6u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.8u l=0.6u +MM5 Q wl0 bl0 gnd n w=1.2u l=0.6u +MM4 Q_bar wl0 br0 gnd n w=1.2u l=0.6u +MM1 Q Q_bar gnd gnd n w=2.4u l=0.6u +MM0 Q_bar Q gnd gnd n w=2.4u l=0.6u +MM3 Q Q_bar vdd vdd p w=0.9u l=0.6u +MM2 Q_bar Q vdd vdd p w=0.9u l=0.6u +.ENDS + diff --git a/technology/scn3me_subm/sp_lib/incorrect/cell_1rw_1r.sp b/technology/scn3me_subm/sp_lib/incorrect/cell_1rw_1r.sp new file mode 100644 index 00000000..1c4c1bc3 --- /dev/null +++ b/technology/scn3me_subm/sp_lib/incorrect/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 n w=1.8u l=0.6u +MM8 RA_to_R_right Q gnd gnd n w=1.8u l=0.6u +MM7 RA_to_R_left Q_bar gnd gnd n w=1.8u l=0.6u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.8u l=0.6u +MM5 Q wl0 bl0 gnd n w=1.2u l=0.6u +MM4 Q_bar wl0 br0 gnd n w=1.2u l=0.6u +MM1 Q Q_bar gnd gnd n w=2.4u l=0.6u +MM0 Q_bar Q gnd gnd n w=2.4u l=0.6u +MM3 Q Q_bar vdd vdd p w=1.2u l=0.6u +MM2 Q_bar Q vdd vdd p w=1.2u l=0.6u +.ENDS + diff --git a/technology/scn3me_subm/sp_lib/incorrect/cell_1w_1r.sp b/technology/scn3me_subm/sp_lib/incorrect/cell_1w_1r.sp new file mode 100644 index 00000000..aed7466b --- /dev/null +++ b/technology/scn3me_subm/sp_lib/incorrect/cell_1w_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT cell_1w_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd n w=1.8u l=0.6u +MM8 RA_to_R_right Q gnd gnd n w=1.8u l=0.6u +MM7 RA_to_R_left Q_bar gnd gnd n w=1.8u l=0.6u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.8u l=0.6u +MM5 Q wl0 bl0 gnd n w=1.2u l=0.6u +MM4 Q_bar wl0 br0 gnd n w=1.2u l=0.6u +MM1 Q Q_bar gnd gnd n w=2.4u l=0.6u +MM0 Q_bar Q gnd gnd n w=2.4u l=0.6u +MM3 Q Q_bar vdd vdd p w=1.2u l=0.6u +MM2 Q_bar Q vdd vdd p w=1.2u l=0.6u +.ENDS + diff --git a/technology/scn3me_subm/sp_lib/incorrect/replica_cell_1rw_1r.sp b/technology/scn3me_subm/sp_lib/incorrect/replica_cell_1rw_1r.sp new file mode 100644 index 00000000..e90dd033 --- /dev/null +++ b/technology/scn3me_subm/sp_lib/incorrect/replica_cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT replica_cell_1rw_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd n w=1.8u l=0.6u +MM8 RA_to_R_right Q gnd gnd n w=1.8u l=0.6u +MM7 RA_to_R_left vdd gnd gnd n w=1.8u l=0.6u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.8u l=0.6u +MM5 Q wl0 bl0 gnd n w=1.2u l=0.6u +MM4 vdd wl0 br0 gnd n w=1.2u l=0.6u +MM1 Q vdd gnd gnd n w=2.4u l=0.6u +MM0 vdd Q gnd gnd n w=2.4u l=0.6u +MM3 Q vdd vdd vdd p w=1.2u l=0.6u +MM2 vdd Q vdd vdd p w=1.2u l=0.6u +.ENDS + diff --git a/technology/scn3me_subm/sp_lib/incorrect/replica_cell_1w_1r.sp b/technology/scn3me_subm/sp_lib/incorrect/replica_cell_1w_1r.sp new file mode 100644 index 00000000..bd2e5eb5 --- /dev/null +++ b/technology/scn3me_subm/sp_lib/incorrect/replica_cell_1w_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT replica_cell_1w_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd n w=1.8u l=0.6u +MM8 RA_to_R_right Q gnd gnd n w=1.8u l=0.6u +MM7 RA_to_R_left vdd gnd gnd n w=1.8u l=0.6u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.8u l=0.6u +MM5 Q wl0 bl0 gnd n w=1.2u l=0.6u +MM4 vdd wl0 br0 gnd n w=0.8u l=0.6u +MM1 Q vdd gnd gnd n w=2.4u l=0.6u +MM0 vdd Q gnd gnd n w=2.4u l=0.6u +MM3 Q vdd vdd vdd p w=0.6u l=0.6u +MM2 vdd Q vdd vdd p w=0.6u l=0.6u +.ENDS + diff --git a/technology/scn3me_subm/sp_lib/replica_cell_1rw_1r.sp b/technology/scn3me_subm/sp_lib/replica_cell_1rw_1r.sp new file mode 100644 index 00000000..a8654c83 --- /dev/null +++ b/technology/scn3me_subm/sp_lib/replica_cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT replica_cell_1rw_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd n w=1.8u l=0.6u +MM8 RA_to_R_right Q gnd gnd n w=1.8u l=0.6u +MM7 RA_to_R_left vdd gnd gnd n w=1.8u l=0.6u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.8u l=0.6u +MM5 Q wl0 bl0 gnd n w=1.2u l=0.6u +MM4 vdd wl0 br0 gnd n w=1.2u l=0.6u +MM1 Q vdd gnd gnd n w=2.4u l=0.6u +MM0 vdd Q gnd gnd n w=2.4u l=0.6u +MM3 Q vdd vdd vdd p w=0.9u l=0.6u +MM2 vdd Q vdd vdd p w=0.9u l=0.6u +.ENDS + diff --git a/technology/scn3me_subm/sp_lib/replica_cell_1w_1r.sp b/technology/scn3me_subm/sp_lib/replica_cell_1w_1r.sp new file mode 100644 index 00000000..5b9c9b18 --- /dev/null +++ b/technology/scn3me_subm/sp_lib/replica_cell_1w_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT replica_cell_1w_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1 gnd n w=1.8u l=0.6u +MM8 RA_to_R_right Q gnd gnd n w=1.8u l=0.6u +MM7 RA_to_R_left vdd gnd gnd n w=1.8u l=0.6u +MM6 RA_to_R_left wl1 bl1 gnd n w=1.8u l=0.6u +MM5 Q wl0 bl0 gnd n w=1.2u l=0.6u +MM4 vdd wl0 br0 gnd n w=1.2u l=0.6u +MM1 Q vdd gnd gnd n w=2.4u l=0.6u +MM0 vdd Q gnd gnd n w=2.4u l=0.6u +MM3 Q vdd vdd vdd p w=0.9u l=0.6u +MM2 vdd Q vdd vdd p w=0.9u l=0.6u +.ENDS + diff --git a/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech b/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech index be511001..bc447205 100644 --- a/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech +++ b/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech @@ -1,10 +1,10 @@ tech - format 32 + format 29 scmos end version - version 2002a + version 2001a description "SCMOS: Submit as technology.lambda: SCN3ME_SUBM.30 [to process: AMIc5]" end @@ -34,8 +34,7 @@ types select nselect,ns select pselect,ps cap electrode,poly2,el,p2 - cap electrodecontact,poly2contact,poly2c,p2c,elc - cap p2m12contact,p2m12c + metal1 electrodecontact,poly2contact,poly2c,p2c,elc cap electrodecap,ecap,poly2cap,p2cap,pcap contact genericpoly2contact,gc2 active ntransistor,nfet @@ -104,12 +103,7 @@ contact psc psd metal1 m2c metal1 metal2 m3c metal2 metal3 - stackable pc m2c pm12contact,pm12c - stackable pdc m2c pdm12contact,pdm12c - stackable psc m2c psm12contact,psm12c,pom12c,pwm12c - stackable ndc m2c ndm12contact,ndm12c - stackable nsc m2c nsm12contact,nsm12c,nom12c,nwm12c - stackable m2c m3c m123contact,m123c + stackable end styles @@ -147,7 +141,6 @@ styles fapm 1 20 21 34 gv1 55 m2contact 20 21 55 - p2m12contact 14 20 21 32 55 metal2 21 rm2 21 48 prm2 48 @@ -218,10 +211,6 @@ compose paint nsc pwell psc paint poly2 poly ecap erase ecap poly poly2 - paint p2c poly2 p2c - paint p2c ecap p2c - paint p2m12c poly2 p2m12c - paint p2m12c ecap p2m12c paint pad m3 pad compose phr poly2 hr paint hr poly2 phr @@ -246,18 +235,15 @@ end connect nwell,nsc/a,nsd nwell,nsc/a,nsd pwell,psc/a,psd pwell,psc/a,psd - m1,fm1,fapm,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 m1,fm1,fapm,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 + m1,fm1,fapm,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 m1,fm1,fapm,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 m2,fm2,fapm,m2c/m2,m3c/m2,m3c/m2 m2,fm2,fapm,m2c/m2,m3c/m2,m3c/m2 m3,fm3,fapm,m3c/m3 m3,fm3,fapm,m3c/m3 ndiff,ndc/a,pdiff,pdc/a ndiff,ndc/a,pdiff,pdc/a poly,fp,nfet,pfet,fet,fapm,pc/a poly,fp,nfet,pfet,fet,fapm,pc/a + poly2,ecap,p2c poly2,ecap,p2c gc2 poly2,ecap,metal1 - p2c poly2,ecap,m1,fm1,fapm,m2c/m1 - p2m12c poly2,ecap,m1,fm1,fapm,m2c/m1,m2,fm2,fapm,m2c/m2,m3c/m2 - poly2,ecap,p2c,p2m12c poly2,ecap,p2c,p2m12c - gc2 poly2,ecap,m1,fm1,fapm,m2c/m1 gc poly,fp,ndiff,pdiff,nsd,psd,m1,fm1,fapm,m2c/m1 - gv1 m1,fm1,fapm,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2,fm2,fapm,m3c/m2 + gv1 m1,fm1,fapm,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2,fm2,fapm,m3c/m2 gv2 m2,fm2,fapm,m2c/m2,m3c/m2,m3,fm3,fapm pad m1,fm1,m2,fm2,m3,fm3 rm1 prm1 @@ -275,12 +261,8 @@ cifoutput style lambda=0.30(p) scalefactor 30 15 + options calma-permissive-labels - # This is a custom section to add bounding boxes in OpenRAM - layer BB bb - labels bb - calma 63 0 - layer CWN nwell,rnw bloat-or pdiff,rpd,pdc/a,pfet * 180 bloat-or nsd,nsc/a * 90 @@ -377,7 +359,7 @@ style lambda=0.30(p) squares 0 60 90 calma 55 0 - layer CCE p2c,p2m12c + layer CCE p2c squares 30 60 90 calma 55 0 @@ -389,7 +371,7 @@ style lambda=0.30(p) squares 0 60 90 calma 25 0 - layer CV1 m2c/m1,p2m12c + layer CV1 m2c/m1 squares 30 60 90 calma 50 0 @@ -431,12 +413,12 @@ style lambda=0.30(p) layer CM1 pad calma 49 0 - layer CM1 m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1,p2c,p2m12c - labels m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1,p2c,p2m12c + layer CM1 m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 + labels m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 calma 49 0 - layer CM2 m2,rm2,m2c/m2,m3c/m2,m3c/m2,p2m12c - labels m2,rm2,m2c/m2,m3c/m2,m3c/m2,p2m12c + layer CM2 m2,rm2,m2c/m2,m3c/m2,m3c/m2 + labels m2,rm2,m2c/m2,m3c/m2,m3c/m2 calma 51 0 layer CMFP m1p @@ -527,8 +509,8 @@ style lambda=0.30(p) labels hr,phr calma 34 0 - layer CEL poly2,ecap,phr,p2c,p2m12c - labels poly2,ecap,phr,p2c,p2m12c + layer CEL poly2,ecap,phr,p2c + labels poly2,ecap,phr,p2c calma 56 0 #CRE/CRM @@ -559,8 +541,8 @@ style lambda=0.30(p) #CRE/CRM layer CRM rm1,prm1,rm2,prm2,rm3,prm3 #CRE/CRM calma 70 0 - layer CX comment - labels comment + layer CX comment,bb + labels comment,bb calma 63 0 layer XP pad,xp @@ -606,9 +588,9 @@ style fapm-boxes scalefactor 30 15 - templayer CRIT fapm,fn,diff,ndiff,rnd,nfet,nsd,pdiff,rpd,pfet,psd,ndc/a,nsc/a,pdc/a,psc/a,pfet,pfet,fet,poly,rp,nfet,pfet,fet,pc/a,poly2,ecap,phr,p2c,p2m12c - or fm1,m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1,p2c,p2m12c - or fm2,m2,rm2,m2c/m2,m3c/m2,m3c/m2,p2m12c + templayer CRIT fapm,fn,diff,ndiff,rnd,nfet,nsd,pdiff,rpd,pfet,psd,ndc/a,nsc/a,pdc/a,psc/a,pfet,pfet,fet,poly,rp,nfet,pfet,fet,pc/a,poly2,ecap,phr,p2c + or fm1,m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 + or fm2,m2,rm2,m2c/m2,m3c/m2,m3c/m2 or fm3,m3,rm3,m3c/m3 or glass,pad grow 510 @@ -631,9 +613,9 @@ style fapm-stripes # and then *replacing* the left side (1-lambda wide) stripe of each 'fa' box # to be a 1-lambda wide layer 'fb' box -- else you won't get strips! - templayer CRIT fapm,fn,diff,ndiff,rnd,nfet,nsd,pdiff,rpd,pfet,psd,ndc/a,nsc/a,pdc/a,psc/a,pfet,pfet,fet,poly,rp,nfet,pfet,fet,pc/a,poly2,ecap,phr,p2c,p2m12c - or fm1,m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1,p2c,p2m12c - or fm2,m2,rm2,m2c/m2,m3c/m2,m3c/m2,p2m12c + templayer CRIT fapm,fn,diff,ndiff,rnd,nfet,nsd,pdiff,rpd,pfet,psd,ndc/a,nsc/a,pdc/a,psc/a,pfet,pfet,fet,poly,rp,nfet,pfet,fet,pc/a,poly2,ecap,phr,p2c + or fm1,m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 + or fm2,m2,rm2,m2c/m2,m3c/m2,m3c/m2 or fm3,m3,rm3,m3c/m3 or glass,pad grow 510 @@ -754,7 +736,7 @@ style lambda=0.30(cp) squares 0 60 90 calma 25 0 - layer CCC p2c,p2m12c + layer CCC p2c squares 30 60 90 calma 25 0 @@ -766,7 +748,7 @@ style lambda=0.30(cp) squares 0 60 90 calma 25 0 - layer CV1 m2c/m1,p2m12c + layer CV1 m2c/m1 squares 30 60 90 calma 50 0 @@ -808,12 +790,12 @@ style lambda=0.30(cp) layer CM1 pad calma 49 0 - layer CM1 m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1,p2c,p2m12c - labels m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1,p2c,p2m12c + layer CM1 m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 + labels m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 calma 49 0 - layer CM2 m2,rm2,m2c/m2,m3c/m2,m3c/m2,p2m12c - labels m2,rm2,m2c/m2,m3c/m2,m3c/m2,p2m12c + layer CM2 m2,rm2,m2c/m2,m3c/m2,m3c/m2 + labels m2,rm2,m2c/m2,m3c/m2,m3c/m2 calma 51 0 layer CMFP m1p @@ -904,8 +886,8 @@ style lambda=0.30(cp) labels hr,phr calma 34 0 - layer CEL poly2,ecap,phr,p2c,p2m12c - labels poly2,ecap,phr,p2c,p2m12c + layer CEL poly2,ecap,phr,p2c + labels poly2,ecap,phr,p2c calma 56 0 #CRE/CRM @@ -936,8 +918,8 @@ style lambda=0.30(cp) #CRE/CRM layer CRM rm1,prm1,rm2,prm2,rm3,prm3 #CRE/CRM calma 70 0 - layer CX comment - labels comment + layer CX comment,bb + labels comment,bb calma 63 0 layer XP pad,xp @@ -1034,7 +1016,7 @@ style lambda=0.30(c) squares 0 60 90 calma 25 0 - layer CCC p2c,p2m12c + layer CCC p2c squares 30 60 90 calma 25 0 @@ -1046,7 +1028,7 @@ style lambda=0.30(c) squares 0 60 90 calma 25 0 - layer CV1 m2c/m1,p2m12c + layer CV1 m2c/m1 squares 30 60 90 calma 50 0 @@ -1088,12 +1070,12 @@ style lambda=0.30(c) layer CM1 pad calma 49 0 - layer CM1 m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1,p2c,p2m12c - labels m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1,p2c,p2m12c + layer CM1 m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 + labels m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 calma 49 0 - layer CM2 m2,rm2,m2c/m2,m3c/m2,m3c/m2,p2m12c - labels m2,rm2,m2c/m2,m3c/m2,m3c/m2,p2m12c + layer CM2 m2,rm2,m2c/m2,m3c/m2,m3c/m2 + labels m2,rm2,m2c/m2,m3c/m2,m3c/m2 calma 51 0 layer CMFP m1p @@ -1184,8 +1166,8 @@ style lambda=0.30(c) labels hr,phr calma 34 0 - layer CEL poly2,ecap,phr,p2c,p2m12c - labels poly2,ecap,phr,p2c,p2m12c + layer CEL poly2,ecap,phr,p2c + labels poly2,ecap,phr,p2c calma 56 0 #CRE/CRM @@ -1216,8 +1198,8 @@ style lambda=0.30(c) #CRE/CRM layer CRM rm1,prm1,rm2,prm2,rm3,prm3 #CRE/CRM calma 70 0 - layer CX comment - labels comment + layer CX comment,bb + labels comment,bb calma 63 0 layer XP pad,xp @@ -1314,7 +1296,7 @@ style lambda=0.30() squares 0 60 90 calma 55 0 - layer CCE p2c,p2m12c + layer CCE p2c squares 30 60 90 calma 55 0 @@ -1326,7 +1308,7 @@ style lambda=0.30() squares 0 60 90 calma 25 0 - layer CV1 m2c/m1,p2m12c + layer CV1 m2c/m1 squares 30 60 90 calma 50 0 @@ -1368,12 +1350,12 @@ style lambda=0.30() layer CM1 pad calma 49 0 - layer CM1 m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1,p2c,p2m12c - labels m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1,p2c,p2m12c + layer CM1 m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 + labels m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 calma 49 0 - layer CM2 m2,rm2,m2c/m2,m3c/m2,m3c/m2,p2m12c - labels m2,rm2,m2c/m2,m3c/m2,m3c/m2,p2m12c + layer CM2 m2,rm2,m2c/m2,m3c/m2,m3c/m2 + labels m2,rm2,m2c/m2,m3c/m2,m3c/m2 calma 51 0 layer CMFP m1p @@ -1464,8 +1446,8 @@ style lambda=0.30() labels hr,phr calma 34 0 - layer CEL poly2,ecap,phr,p2c,p2m12c - labels poly2,ecap,phr,p2c,p2m12c + layer CEL poly2,ecap,phr,p2c + labels poly2,ecap,phr,p2c calma 56 0 #CRE/CRM @@ -1496,8 +1478,8 @@ style lambda=0.30() #CRE/CRM layer CRM rm1,prm1,rm2,prm2,rm3,prm3 #CRE/CRM calma 70 0 - layer CX comment - labels comment + layer CX comment,bb + labels comment,bb calma 63 0 layer XP pad,xp @@ -1514,7 +1496,7 @@ style lambda=0.30(p) # This is a custom section to add bounding boxes in OpenRAM layer bb BB labels BB - calma 63 0 + calma BB 63 0 layer nwell CWN and-not CWNR @@ -1721,7 +1703,6 @@ layer nwell CWN layer ndc CAA and CSN and CCA - and-not CV1 and-not CWNR and-not CTA @@ -1735,7 +1716,6 @@ layer nwell CWN layer ndc CAA and CSN and CCC - and-not CV1 and-not CWNR and-not CTA @@ -1749,7 +1729,6 @@ layer nwell CWN layer nsc CAA and CSN and CCA - and-not CV1 and-not CWNR and-not CTA @@ -1763,7 +1742,6 @@ layer nwell CWN layer nsc CAA and CSN and CCC - and-not CV1 and-not CWNR and-not CTA @@ -1777,7 +1755,6 @@ layer nwell CWN layer pdc CAA and CSP and CCA - and-not CV1 and-not CTA and-not CPS @@ -1791,7 +1768,6 @@ layer nwell CWN layer pdc CAA and CSP and CCC - and-not CV1 and-not CTA and-not CPS @@ -1805,7 +1781,6 @@ layer nwell CWN layer psc CAA and CSP and CCA - and-not CV1 and-not CTA and-not CPS @@ -1819,128 +1794,6 @@ layer nwell CWN layer psc CAA and CSP and CCC - and-not CV1 - and-not CWNR - and-not CTA - - and-not CPS - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer ndc CAA - and CSN - and CCA - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer ndc CAA - and CSN - and CCC - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer nsc CAA - and CSN - and CCA - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer nsc CAA - and CSN - and CCC - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer pdc CAA - and CSP - and CCA - and CV1 - and CV2 - and-not CTA - - and-not CPS - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer pdc CAA - and CSP - and CCC - and CV1 - and CV2 - and-not CTA - - and-not CPS - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer psc CAA - and CSP - and CCA - and CV1 - and CV2 - and-not CTA - - and-not CPS - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer psc CAA - and CSP - and CCC - and CV1 - and CV2 and-not CWNR and-not CTA @@ -1971,7 +1824,6 @@ layer nwell CWN calma CRG 67 * layer pc CCP - and-not CV1 and CPG and-not CPC and-not CEL @@ -1983,7 +1835,6 @@ layer nwell CWN calma CCP 47 * layer pc CCC - and-not CV1 and CPG and-not CPC and-not CEL @@ -1994,82 +1845,6 @@ layer nwell CWN shrink 15 calma CCC 25 * - layer pc CCP - and CV1 - and CV2 - and CPG - and-not CPC - and-not CEL - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCP 47 * - - layer pc CCC - and CV1 - and CV2 - and CPG - and-not CPC - and-not CEL - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - - layer p2c CCE - and-not CV1 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCE 55 * - - layer p2c CCC - and-not CV1 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - - layer p2c CCE - and CV1 - and CV2 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCE 55 * - - layer p2c CCC - and CV1 - and CV2 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - layer gc CCP and-not CPG and-not CPC @@ -2134,11 +1909,6 @@ layer nwell CWN calma CV2 61 * layer m2c CV1 - and-not CV2 - and-not CCC - and-not CCE - and-not CCP - and-not CCA and-not XP grow 30 and CM2 @@ -2147,32 +1917,6 @@ layer nwell CWN shrink 15 calma CV1 50 * - - - layer p2m12c CV1 - and-not CV2 - and CCE - grow 30 - and CM2 - and CM1 - and CPG - and CEL - grow 15 - shrink 15 - calma CV1 50 * - - layer p2m12c CV1 - and-not CV2 - and CCC - grow 30 - and CM2 - and CM1 - and CPG - and CEL - grow 15 - shrink 15 - calma CV1 50 * - layer m1 CM1 and-not CRM and-not CRF @@ -2219,14 +1963,6 @@ layer nwell CWN labels CMSP calma CMSP 82 * - - - - - - - - layer fp 100 calma 100 100 * @@ -2249,8 +1985,6 @@ layer nwell CWN calma 110 110 * layer m3c CV2 - - and-not CV1 and-not XP grow 30 and CM3 @@ -2259,7 +1993,6 @@ layer nwell CWN shrink 15 calma CV2 61 * - layer m3 CM3 and-not CRM and-not CRT @@ -2329,6 +2062,20 @@ layer nwell CWN and-not CRE calma CRG2 68 * + layer elc CCE + grow 30 + and CM1 + and CEL + labels CM1 + calma CCE 55 * + + layer elc CCC + grow 30 + and CM1 + and CEL + labels CM1 + calma CCC 25 * + layer comment CX labels CX calma CX 63 * @@ -2348,6 +2095,11 @@ layer nwell CWN style lambda=0.30(s) scalefactor 30 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -2554,7 +2306,6 @@ style lambda=0.30(s) layer ndc CAA and CSN and CCA - and-not CV1 and-not CWNR and-not CTA @@ -2568,7 +2319,6 @@ style lambda=0.30(s) layer ndc CAA and CSN and CCC - and-not CV1 and-not CWNR and-not CTA @@ -2582,7 +2332,6 @@ style lambda=0.30(s) layer nsc CAA and CSN and CCA - and-not CV1 and-not CWNR and-not CTA @@ -2596,7 +2345,6 @@ style lambda=0.30(s) layer nsc CAA and CSN and CCC - and-not CV1 and-not CWNR and-not CTA @@ -2610,7 +2358,6 @@ style lambda=0.30(s) layer pdc CAA and CSP and CCA - and-not CV1 and-not CTA and-not CPS @@ -2624,7 +2371,6 @@ style lambda=0.30(s) layer pdc CAA and CSP and CCC - and-not CV1 and-not CTA and-not CPS @@ -2638,7 +2384,6 @@ style lambda=0.30(s) layer psc CAA and CSP and CCA - and-not CV1 and-not CTA and-not CPS @@ -2652,128 +2397,6 @@ style lambda=0.30(s) layer psc CAA and CSP and CCC - and-not CV1 - and-not CWNR - and-not CTA - - and-not CPS - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer ndc CAA - and CSN - and CCA - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer ndc CAA - and CSN - and CCC - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer nsc CAA - and CSN - and CCA - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer nsc CAA - and CSN - and CCC - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer pdc CAA - and CSP - and CCA - and CV1 - and CV2 - and-not CTA - - and-not CPS - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer pdc CAA - and CSP - and CCC - and CV1 - and CV2 - and-not CTA - - and-not CPS - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer psc CAA - and CSP - and CCA - and CV1 - and CV2 - and-not CTA - - and-not CPS - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer psc CAA - and CSP - and CCC - and CV1 - and CV2 and-not CWNR and-not CTA @@ -2804,7 +2427,6 @@ style lambda=0.30(s) calma CRG 67 * layer pc CCP - and-not CV1 and CPG and-not CPC and-not CEL @@ -2816,7 +2438,6 @@ style lambda=0.30(s) calma CCP 47 * layer pc CCC - and-not CV1 and CPG and-not CPC and-not CEL @@ -2827,82 +2448,6 @@ style lambda=0.30(s) shrink 15 calma CCC 25 * - layer pc CCP - and CV1 - and CV2 - and CPG - and-not CPC - and-not CEL - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCP 47 * - - layer pc CCC - and CV1 - and CV2 - and CPG - and-not CPC - and-not CEL - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - - layer p2c CCE - and-not CV1 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCE 55 * - - layer p2c CCC - and-not CV1 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - - layer p2c CCE - and CV1 - and CV2 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCE 55 * - - layer p2c CCC - and CV1 - and CV2 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - layer gc CCP and-not CPG and-not CPC @@ -2967,11 +2512,6 @@ style lambda=0.30(s) calma CV2 61 * layer m2c CV1 - and-not CV2 - and-not CCC - and-not CCE - and-not CCP - and-not CCA and-not XP grow 30 and CM2 @@ -2980,32 +2520,6 @@ style lambda=0.30(s) shrink 15 calma CV1 50 * - - - layer p2m12c CV1 - and-not CV2 - and CCE - grow 30 - and CM2 - and CM1 - and CPG - and CEL - grow 15 - shrink 15 - calma CV1 50 * - - layer p2m12c CV1 - and-not CV2 - and CCC - grow 30 - and CM2 - and CM1 - and CPG - and CEL - grow 15 - shrink 15 - calma CV1 50 * - layer m1 CM1 and-not CRM and-not CRF @@ -3052,14 +2566,6 @@ style lambda=0.30(s) labels CMSP calma CMSP 82 * - - - - - - - - layer fp 100 calma 100 100 * @@ -3082,8 +2588,6 @@ style lambda=0.30(s) calma 110 110 * layer m3c CV2 - - and-not CV1 and-not XP grow 30 and CM3 @@ -3092,7 +2596,6 @@ style lambda=0.30(s) shrink 15 calma CV2 61 * - layer m3 CM3 and-not CRM and-not CRT @@ -3162,6 +2665,20 @@ style lambda=0.30(s) and-not CRE calma CRG2 68 * + layer elc CCE + grow 30 + and CM1 + and CEL + labels CM1 + calma CCE 55 * + + layer elc CCC + grow 30 + and CM1 + and CEL + labels CM1 + calma CCC 25 * + layer comment CX labels CX calma CX 63 * @@ -3181,6 +2698,11 @@ style lambda=0.30(s) style lambda=0.30(ps) scalefactor 30 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -3388,7 +2910,6 @@ style lambda=0.30(ps) layer ndc CAA and CSN and CCA - and-not CV1 and-not CWNR and-not CTA @@ -3402,7 +2923,6 @@ style lambda=0.30(ps) layer ndc CAA and CSN and CCC - and-not CV1 and-not CWNR and-not CTA @@ -3416,7 +2936,6 @@ style lambda=0.30(ps) layer nsc CAA and CSN and CCA - and-not CV1 and-not CWNR and-not CTA @@ -3430,7 +2949,6 @@ style lambda=0.30(ps) layer nsc CAA and CSN and CCC - and-not CV1 and-not CWNR and-not CTA @@ -3444,7 +2962,6 @@ style lambda=0.30(ps) layer pdc CAA and CSP and CCA - and-not CV1 and-not CTA and-not CPS @@ -3458,7 +2975,6 @@ style lambda=0.30(ps) layer pdc CAA and CSP and CCC - and-not CV1 and-not CTA and-not CPS @@ -3472,7 +2988,6 @@ style lambda=0.30(ps) layer psc CAA and CSP and CCA - and-not CV1 and-not CTA and-not CPS @@ -3486,128 +3001,6 @@ style lambda=0.30(ps) layer psc CAA and CSP and CCC - and-not CV1 - and-not CWNR - and-not CTA - - and-not CPS - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer ndc CAA - and CSN - and CCA - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer ndc CAA - and CSN - and CCC - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer nsc CAA - and CSN - and CCA - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer nsc CAA - and CSN - and CCC - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer pdc CAA - and CSP - and CCA - and CV1 - and CV2 - and-not CTA - - and-not CPS - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer pdc CAA - and CSP - and CCC - and CV1 - and CV2 - and-not CTA - - and-not CPS - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer psc CAA - and CSP - and CCA - and CV1 - and CV2 - and-not CTA - - and-not CPS - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer psc CAA - and CSP - and CCC - and CV1 - and CV2 and-not CWNR and-not CTA @@ -3638,7 +3031,6 @@ style lambda=0.30(ps) calma CRG 67 * layer pc CCP - and-not CV1 and CPG and-not CPC and-not CEL @@ -3650,7 +3042,6 @@ style lambda=0.30(ps) calma CCP 47 * layer pc CCC - and-not CV1 and CPG and-not CPC and-not CEL @@ -3661,82 +3052,6 @@ style lambda=0.30(ps) shrink 15 calma CCC 25 * - layer pc CCP - and CV1 - and CV2 - and CPG - and-not CPC - and-not CEL - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCP 47 * - - layer pc CCC - and CV1 - and CV2 - and CPG - and-not CPC - and-not CEL - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - - layer p2c CCE - and-not CV1 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCE 55 * - - layer p2c CCC - and-not CV1 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - - layer p2c CCE - and CV1 - and CV2 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCE 55 * - - layer p2c CCC - and CV1 - and CV2 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - layer gc CCP and-not CPG and-not CPC @@ -3801,11 +3116,6 @@ style lambda=0.30(ps) calma CV2 61 * layer m2c CV1 - and-not CV2 - and-not CCC - and-not CCE - and-not CCP - and-not CCA and-not XP grow 30 and CM2 @@ -3814,32 +3124,6 @@ style lambda=0.30(ps) shrink 15 calma CV1 50 * - - - layer p2m12c CV1 - and-not CV2 - and CCE - grow 30 - and CM2 - and CM1 - and CPG - and CEL - grow 15 - shrink 15 - calma CV1 50 * - - layer p2m12c CV1 - and-not CV2 - and CCC - grow 30 - and CM2 - and CM1 - and CPG - and CEL - grow 15 - shrink 15 - calma CV1 50 * - layer m1 CM1 and-not CRM and-not CRF @@ -3886,14 +3170,6 @@ style lambda=0.30(ps) labels CMSP calma CMSP 82 * - - - - - - - - layer fp 100 calma 100 100 * @@ -3916,8 +3192,6 @@ style lambda=0.30(ps) calma 110 110 * layer m3c CV2 - - and-not CV1 and-not XP grow 30 and CM3 @@ -3926,7 +3200,6 @@ style lambda=0.30(ps) shrink 15 calma CV2 61 * - layer m3 CM3 and-not CRM and-not CRT @@ -3996,6 +3269,20 @@ style lambda=0.30(ps) and-not CRE calma CRG2 68 * + layer elc CCE + grow 30 + and CM1 + and CEL + labels CM1 + calma CCE 55 * + + layer elc CCC + grow 30 + and CM1 + and CEL + labels CM1 + calma CCC 25 * + layer comment CX labels CX calma CX 63 * @@ -4015,6 +3302,11 @@ style lambda=0.30(ps) style lambda=0.30() scalefactor 30 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -4219,7 +3511,6 @@ style lambda=0.30() layer ndc CAA and CSN and CCA - and-not CV1 and-not CWNR and-not CTA @@ -4233,7 +3524,6 @@ style lambda=0.30() layer ndc CAA and CSN and CCC - and-not CV1 and-not CWNR and-not CTA @@ -4247,7 +3537,6 @@ style lambda=0.30() layer nsc CAA and CSN and CCA - and-not CV1 and-not CWNR and-not CTA @@ -4261,7 +3550,6 @@ style lambda=0.30() layer nsc CAA and CSN and CCC - and-not CV1 and-not CWNR and-not CTA @@ -4275,7 +3563,6 @@ style lambda=0.30() layer pdc CAA and CSP and CCA - and-not CV1 and-not CTA and-not CPS @@ -4289,7 +3576,6 @@ style lambda=0.30() layer pdc CAA and CSP and CCC - and-not CV1 and-not CTA and-not CPS @@ -4303,7 +3589,6 @@ style lambda=0.30() layer psc CAA and CSP and CCA - and-not CV1 and-not CTA and-not CPS @@ -4317,128 +3602,6 @@ style lambda=0.30() layer psc CAA and CSP and CCC - and-not CV1 - and-not CWNR - and-not CTA - - and-not CPS - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer ndc CAA - and CSN - and CCA - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer ndc CAA - and CSN - and CCC - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer nsc CAA - and CSN - and CCA - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer nsc CAA - and CSN - and CCC - and CV1 - and CV2 - and-not CWNR - and-not CTA - - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer pdc CAA - and CSP - and CCA - and CV1 - and CV2 - and-not CTA - - and-not CPS - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer pdc CAA - and CSP - and CCC - and CV1 - and CV2 - and-not CTA - - and-not CPS - and CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCC 25 * - - layer psc CAA - and CSP - and CCA - and CV1 - and CV2 - and-not CTA - - and-not CPS - and-not CWN - and CM1 - grow 30 - grow 15 - shrink 15 - calma CCA 48 * - - layer psc CAA - and CSP - and CCC - and CV1 - and CV2 and-not CWNR and-not CTA @@ -4469,7 +3632,6 @@ style lambda=0.30() calma CRG 67 * layer pc CCP - and-not CV1 and CPG and-not CPC and-not CEL @@ -4481,7 +3643,6 @@ style lambda=0.30() calma CCP 47 * layer pc CCC - and-not CV1 and CPG and-not CPC and-not CEL @@ -4492,82 +3653,6 @@ style lambda=0.30() shrink 15 calma CCC 25 * - layer pc CCP - and CV1 - and CV2 - and CPG - and-not CPC - and-not CEL - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCP 47 * - - layer pc CCC - and CV1 - and CV2 - and CPG - and-not CPC - and-not CEL - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - - layer p2c CCE - and-not CV1 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCE 55 * - - layer p2c CCC - and-not CV1 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - - layer p2c CCE - and CV1 - and CV2 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCE 55 * - - layer p2c CCC - and CV1 - and CV2 - and CPG - and CEL - and-not CPC - and-not CAA - grow 30 - and CM1 - grow 15 - shrink 15 - calma CCC 25 * - layer gc CCP and-not CPG and-not CPC @@ -4632,11 +3717,6 @@ style lambda=0.30() calma CV2 61 * layer m2c CV1 - and-not CV2 - and-not CCC - and-not CCE - and-not CCP - and-not CCA and-not XP grow 30 and CM2 @@ -4645,32 +3725,6 @@ style lambda=0.30() shrink 15 calma CV1 50 * - - - layer p2m12c CV1 - and-not CV2 - and CCE - grow 30 - and CM2 - and CM1 - and CPG - and CEL - grow 15 - shrink 15 - calma CV1 50 * - - layer p2m12c CV1 - and-not CV2 - and CCC - grow 30 - and CM2 - and CM1 - and CPG - and CEL - grow 15 - shrink 15 - calma CV1 50 * - layer m1 CM1 and-not CRM and-not CRF @@ -4717,14 +3771,6 @@ style lambda=0.30() labels CMSP calma CMSP 82 * - - - - - - - - layer fp 100 calma 100 100 * @@ -4747,8 +3793,6 @@ style lambda=0.30() calma 110 110 * layer m3c CV2 - - and-not CV1 and-not XP grow 30 and CM3 @@ -4757,7 +3801,6 @@ style lambda=0.30() shrink 15 calma CV2 61 * - layer m3 CM3 and-not CRM and-not CRT @@ -4827,6 +3870,20 @@ style lambda=0.30() and-not CRE calma CRG2 68 * + layer elc CCE + grow 30 + and CM1 + and CEL + labels CM1 + calma CCE 55 * + + layer elc CCC + grow 30 + and CM1 + and CEL + labels CM1 + calma CCC 25 * + layer comment CX labels CX calma CX 63 * @@ -4846,6 +3903,11 @@ style lambda=0.30() style lambda=0.30(c) scalefactor 30 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -5301,6 +4363,11 @@ style lambda=0.30(c) style lambda=0.30(cs) scalefactor 30 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -5758,6 +4825,11 @@ style lambda=0.30(cs) style lambda=0.30(cps) scalefactor 30 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -6216,6 +5288,11 @@ style lambda=0.30(cps) style lambda=0.30(cp) scalefactor 30 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -6740,27 +5817,22 @@ drc width pc/m1 4 \ "Poly contact width < 4 (Mosis #5.1)" - width gc 2 \ "GC contact width < 2 (Mosis #6.1)" width ndc/m1 4 \ "Diffusion contact width < 4 (Mosis #6.1)" - width nsc/m1 4 \ "Diffusion contact width < 4 (Mosis #6.1)" - width pdc/m1 4 \ "Diffusion contact width < 4 (Mosis #6.1)" - width psc/m1 4 \ "Diffusion contact width < 4 (Mosis #6.1)" - - width m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 3 \ + width m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 3 \ "Metal1 width < 3 (Mosis #7.1)" width gv1 2 \ @@ -6769,22 +5841,13 @@ drc width m2c/m1 4 \ "Metal2 contact width < 4 (Mosis #8.1)" - - - - - - - width p2m12c 4 \ - "Metal2 contact width < 4 (Mosis #8.1)" - - width p2m12c 4 \ - "stacked p2m12c width < 4 (Mosis #8.1)" - width m2,fm2,rm2,m2c/m2,m3c/m2 3 \ "Metal2 width < 3 (Mosis #9.1)" - width poly2,ecap,phr,p2c,p2m12c,p2c,p2m12c 5 \ + width ecap 8 \ + "Ecap width < 8 (Mosis #11.1)" + + width poly2,ecap,phr 5 \ "Poly2 width < 5 (Mosis #11.1)" width gc2 2 \ @@ -6793,16 +5856,12 @@ drc width p2c 4 \ "Poly2 contact width < 4 (Mosis #13.1)" - width p2m12c 4 \ - "Poly2 contact width < 4 (Mosis #13.1)" - width gv2 2 \ "GV2 via width < 2 (Mosis #14.1)" width m3c/m2 4 \ "Metal3 contact width < 4 (Mosis #14.1)" - width m3,fm3,rm3,m3c/m3,pad 5 \ "Metal3 width < 5 (Mosis #15.1)" @@ -6890,7 +5949,7 @@ drc edge4way nfet,pfet,fet space/active,ndiff,rnd,ndc/a,pdiff,rpd,pdc/a 3 ndiff,rnd,ndc/a,pdiff,rpd,pdc/a,nfet,pfet,fet 0 0 \ "N-Diffusion,P-Diffusion overhang of Transistor < 3 (Mosis #3.4)" active - edge4way poly,fp,rp,pc/a ~(poly,fp,rp,pc/a,nfet,pfet,fet,prp)/active 1 space space 1 \ + edge4way poly,fp,rp,pc/a ~(poly,fp,rp,pc/a,nfet,pfet,fet,prp)/active 1 space/a space/a 1 \ "Poly spacing to Diffusion < 1 (Mosis #3.5)" edge4way nfet ~(nfet)/active 3 ~(pselect)/select ~(nfet)/active 3 \ @@ -6977,12 +6036,9 @@ drc spacing gc gc 3 touching_ok \ "Generic contact spacing < 3 (Mosis #5.3)" - edge4way ~(gc)/contact gc 1 ~(ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1)/metal1 0 0 \ + edge4way ~(gc)/contact gc 1 ~(ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c)/metal1 0 0 \ "GC contact cannot touch Metal1 contacts (Mosis #0)" metal1 - edge4way ~(gc2)/contact gc2 1 ~(ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1)/metal1 0 0 \ - "Generic contact2 cannot touch Metal1 contacts (Mosis #0)" metal1 - spacing gv1 m2c/m2 2 touching_illegal \ "GV1 via spacing to Metal2 contacts < 2 (Mosis #14.2)" @@ -7001,12 +6057,6 @@ drc spacing psc/m1 ndc/m1 1 touching_illegal \ "psc spacing to ndc < 1 (Mosis #6.3)" - - - - - - spacing nfet,pfet ndc/a,pdc/a,psc/a,nsc/a 1 touching_illegal \ "N-Transistor,P-Transistor spacing to Diffusion contact < 1 (Mosis #6.4)" @@ -7020,35 +6070,44 @@ drc "Diffusion spacing to Diffusion contact < 4 (Mosis #6.5.b)" spacing pc/a ndc/a,pdc/a,psc/a,nsc/a 2 touching_illegal \ - "pc/a,pm12c/a spacing to ndc/a,pdc/a,psc/a,nsc/a < 2 (Mosis #6.7)" + "pc/a spacing to ndc/a,pdc/a,psc/a,nsc/a < 2 (Mosis #6.7)" - spacing m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 3 touching_ok \ + spacing m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 3 touching_ok \ "Metal1 spacing < 3 (Mosis #7.2)" - spacing m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 fm1,fapm 3 touching_illegal \ + spacing m1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 fm1,fapm 3 touching_illegal \ "Metal1 spacing to fill layer (fm1) < 3 (Mosis #7.2)" spacing fm1 fm1 4 touching_ok \ "Metal1 fill layer (fm1) spacing < 4 (Mosis #0)" - edge4way gc space 1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 space 1 \ + edge4way gc space 1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 space 1 \ "Metal1 must overlap GC contact by 1 (Mosis #7.3,7.4)" metal1 - edge4way ~(m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1)/metal1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 1 ~(gc)/contact 0 0 \ + edge4way ~(m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1)/metal1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 1 ~(gc)/contact 0 0 \ "Metal1(edge) cannot touch GC contact (Mosis #7.3+7.4)" contact spacing gv1 gv1 3 touching_ok \ "GV1 via spacing < 3 (Mosis #8.2)" - edge4way gv1 ~(gv1)/via1 1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 ~(gv1)/via1 1 \ + edge4way gv1 ~(gv1)/via1 1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 ~(gv1)/via1 1 \ "Metal1 overlap of GV1 via < 1 (Mosis #8.3)" metal1 - edge4way gv1 space 1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 space 1 \ + edge4way gv1 space 1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 space 1 \ "Metal1 must overlap GV1 via by 1 (Mosis #8.3)" metal1 - edge4way ~(m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1)/metal1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 1 ~(gv1)/via1 0 0 \ + edge4way ~(m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1)/metal1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 1 ~(gv1)/via1 0 0 \ "Metal1(edge) cannot touch GV1 via (Mosis #8.3)" via1 + spacing gc gv1 2 touching_illegal \ + "GC contact spacing to GV1 via < 2 (Mosis #8.4)" + + spacing gc m2c/m2 2 touching_illegal \ + " spacing to Metal2 contacts < 2 (Mosis #14.2)" + + spacing gc m2c/m2 2 touching_illegal \ + "GC contact spacing to Metal2 contacts < 2 (Mosis #8.4)" + spacing m2,rm2,m2c/m2,m3c/m2 m2,rm2,m2c/m2,m3c/m2 3 touching_ok \ "Metal2 spacing < 3 (Mosis #9.2)" @@ -7079,7 +6138,7 @@ drc edge4way ~(ecap)/cap ecap 1 poly,fp,rp,pc/a 0 0 \ "Ecap must touch Poly (Mosis #11.3x)" active - edge4way poly2,phr,p2c,p2m12c space 5 ~(poly,fp,rp,pc/a)/active space 5 \ + edge4way poly2,phr space 5 ~(poly,fp,rp,pc/a)/active space 5 \ "Poly2 spacing to Poly < 5 (Mosis #11.3c)" active spacing ecap pc/a 2 touching_illegal \ @@ -7088,13 +6147,13 @@ drc spacing ecap gc 3 touching_illegal \ "Ecap spacing to Generic contact < 3 (Mosis #11.5)" - spacing poly2,ecap,phr,p2c,p2m12c poly2,ecap,phr,p2c,p2m12c 3 touching_ok \ + spacing poly2,ecap,phr poly2,ecap,phr 3 touching_ok \ "Poly2 spacing < 3 (Mosis #11.2)" - spacing poly2,ecap,phr,p2c,p2m12c pc/a,ndc/a,pdc/a,psc/a,nsc/a 2 touching_illegal \ + spacing poly2,ecap,phr pc/a,ndc/a,pdc/a,psc/a,nsc/a 2 touching_illegal \ "Poly2 spacing to Poly contact,Diffusion contact < 2 (Mosis #11.5)" - spacing poly2,ecap,phr,p2c,p2m12c gc,gc 3 touching_illegal \ + spacing poly2,ecap,phr gc,gc 3 touching_illegal \ "Poly2 spacing to GC contact < 3 (Mosis #11.5)" spacing gc2 gc2 3 touching_ok \ @@ -7103,17 +6162,17 @@ drc edge4way ~(ecap)/cap ecap 3 ~(gc2)/contact ecap 3 \ "Ecap overlap of Generic contact2 < 3 (Mosis #13.3)" contact - edge4way ~(ecap)/cap ecap 2 ~(p2c,p2m12c)/cap ecap 2 \ - "Ecap overlap of Poly2 contact < 2 (Mosis #13.3)" cap + edge4way ~(ecap)/cap ecap 2 ~(p2c)/metal1 ecap 2 \ + "Ecap overlap of Poly2 contact < 2 (Mosis #13.3)" metal1 - edge4way gc2 space 1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 space 1 \ + edge4way gc2 space 1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 space 1 \ "Metal1 must overlap Generic contact2 by 1 (Mosis #13.4)" metal1 - edge4way ~(m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1)/metal1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 1 ~(gc2)/contact 0 0 \ + edge4way ~(m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1)/metal1 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 1 ~(gc2)/contact 0 0 \ "Metal1(edge) cannot touch Generic contact2 (Mosis #13.4)" contact - edge4way ~(poly2,ecap,phr,p2c,p2m12c)/cap poly2,ecap,phr,p2c,p2m12c 1 ~(p2c,p2m12c)/cap poly2,ecap,phr,p2c,p2m12c 1 \ - "Poly2 overlap of Poly2 contact < 1 (Mosis #13.4)" cap + edge4way ~(poly2,ecap,phr)/cap poly2,ecap,phr 1 ~(p2c)/metal1 poly2,ecap,phr 1 \ + "Poly2 overlap of Poly2 contact < 1 (Mosis #13.4)" metal1 spacing gv2 gv2 3 touching_ok \ "GV2 via spacing < 3 (Mosis #14.2)" @@ -7127,6 +6186,12 @@ drc edge4way ~(m2,fm2,rm2,m2c/m2,m3c/m2)/metal2 m2,fm2,rm2,m2c/m2,m3c/m2 1 ~(gv2)/via2 0 0 \ "Metal2(edge) cannot touch GV2 via (Mosis #14.3)" via2 + spacing gv1 gv2 2 touching_illegal \ + "GV1 via spacing to GV2 via < 2 (Mosis #14.4)" + + spacing gv1 m3c/m2 1 touching_illegal \ + "GV1 via spacing to Metal3 contact < 1 (Mosis #14.4)" + spacing m3,rm3,m3c/m3,pad m3,rm3,m3c/m3,pad 3 touching_ok \ "Metal3 spacing < 3 (Mosis #15.2)" @@ -7148,7 +6213,7 @@ drc spacing hr,phr hr,phr 4 touching_ok \ "High-Resist spacing < 4 (Mosis #27.2)" - spacing hr,phr,phr p2c,p2m12c 2 touching_illegal \ + spacing hr,phr,phr p2c 2 touching_illegal \ "High-Resist spacing to Poly2 contact < 2 (Mosis #27.3)" spacing hr,phr,phr gc 2 touching_illegal \ @@ -7157,10 +6222,10 @@ drc edge4way hr,phr space 2 ~(ndiff,rnd,ndc/a,pdiff,rpd,pdc/a)/active 0 2 \ "High-Resist space to Diffusion < 2 (Mosis #27.4)" active - spacing hr,phr,phr poly2,ecap,phr,p2c,p2m12c 2 touching_ok \ + spacing hr,phr,phr poly2,ecap,phr 2 touching_ok \ "High-Resist spacing to other Poly2 < 2 (Mosis #27.5)" - edge4way hr,phr space 2 ~(poly2,ecap,phr,p2c,p2m12c)/contact hr,phr 2 \ + edge4way hr,phr space 2 ~(poly2,ecap,phr)/contact hr,phr 2 \ "High-Resist space to Poly2 < 2 (Mosis #27.5x)" contact spacing nwell phr 4 touching_illegal \ @@ -7271,7 +6336,7 @@ drc edge4way gc2 ~(gc2)/contact 1 ~(gc2)/contact (~(gc2),gc2)/contact 1 \ "Contact not rectangular (Magic rule)" - edge4way p2c,p2m12c ~(p2c,p2m12c)/cap 1 ~(p2c,p2m12c)/cap (~(p2c,p2m12c),p2c,p2m12c)/cap 1 \ + edge4way p2c ~(p2c)/metal1 1 ~(p2c)/metal1 (~(p2c),p2c)/metal1 1 \ "Contact not rectangular (Magic rule)" edge4way gc ~(gc)/contact 1 ~(gc)/contact (~(gc),gc)/contact 1 \ @@ -7291,12 +6356,12 @@ drc exact_overlap gc,ndc/a,pdc/a,psc/a,nsc/a,gc,pc/a,gc - exact_overlap gc2,p2c,p2m12c + exact_overlap gc2,p2c edge4way pad ~(pad)/m3 1 ~(pad)/m3 (~(pad),pad)/m3 1 \ "Contact not rectangular (Magic rule)" - exact_overlap ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1 + exact_overlap ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c exact_overlap m2c/m2 @@ -7356,17 +6421,17 @@ lef ignore PC ignore CA -routing m1 M1 m1 met1 -routing m2 M2 m2 met2 -routing m3 M3 m3 met3 +routing m1 metal1 M1 m1 met1 +routing m2 metal2 M2 m2 met2 +routing m3 metal3 M3 m3 met3 -contact m2c via via1 V1 v1 +contact m2c via1 via V1 v1 contact m3c via2 V2 v2 end extract - style AMI0.5um(amic5)from:T24H + style AMI0.5um(amic5)from:T11Z cscale 1 lambda 30 step 100 @@ -7387,162 +6452,164 @@ extract planeorder via2 13 planeorder fill 14 - resist (ndiff,rnd,ndc,nsd,nsc)/active 82200 - resist (pdiff,rpd,pdc,psd,psc)/active 105200 - resist (nwell)/well 808000 - resist (rnw)/active 808000 - resist (pwell)/well 1 - resist (poly,fp,rp,pc,pc,nfet,pfet,fet)/active 22000 - resist (poly2,ecap,p2c,p2m12c,p2c,p2m12c)/cap 40300 - resist (phr)/cap 40300 - resist (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c,m2c)/metal1 90 - resist (m2,fm2,rm2,m2c,m3c,m3c)/metal2 90 - resist (m3,fm3,rm3,m3c,pad)/metal3 50 + substrate *psd,space/w,pwell well - contact ndc 4 62700 - contact pdc 4 160000 - contact pc 4 15600 - contact p2c 4 26100 - contact m2c 4 910 - contact m3c 4 830 + resist (ndiff,rnd,ndc,nsd,nsc)/active 82400 + resist (pdiff,rpd,pdc,psd,psc)/active 102700 + resist (nwell)/well 816000 + resist (rnw)/active 816000 0.5 + resist (pwell)/well 1 + resist (poly,fp,rp,pc,pc,nfet,pfet,fet)/active 22300 + resist (poly2,ecap,p2c)/cap 41300 + resist (phr)/cap 41300 0.5 + resist (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c,m2c)/metal1 90 + resist (m2,fm2,rm2,m2c,m3c,m3c)/metal2 90 + resist (m3,fm3,rm3,m3c,pad)/metal3 60 + + contact ndc 4 58700 + contact pdc 4 141000 + contact pc 4 15800 + contact p2c 4 26800 + contact m2c 4 760 + contact m3c 4 730 #poly2 - overlap (poly,fp,rp,pc,pc)/active (poly2,ecap,phr,p2c,p2m12c,p2c,p2m12c)/cap 84.960 + overlap (poly,fp,rp,pc,pc)/active (poly2,ecap,phr,p2c)/cap 82.260 #nwell,cwell,pwell - areacap (nwell)/well 3.600 + areacap (nwell)/well 3.510 #rnw - areacap (rnw)/active 3.600 + areacap (rnw)/active 3.510 #ndiff -# MODEL HANDLES THIS: areacap (ndiff,ndc)/active 38.430 -# MODEL HANDLES THIS: overlap (ndiff,ndc)/active ~space/w 38.430 -# MODEL HANDLES THIS: perimc (ndiff,ndc)/active ~(ndiff,ndc,nfet,pfet,fet)/active 94.800 -# MODEL HANDLES THIS: sideoverlap (ndiff,ndc)/active ~(ndiff,ndc,nfet,pfet,fet)/active ~space/w 94.800 +# MODEL HANDLES THIS: areacap (ndiff,ndc)/active 38.070 +# MODEL HANDLES THIS: overlap (ndiff,ndc)/active ~space/w 38.070 +# MODEL HANDLES THIS: perimc (ndiff,ndc)/active ~(ndiff,ndc,nfet,pfet,fet)/active 95.700 +# MODEL HANDLES THIS: sideoverlap (ndiff,ndc)/active ~(ndiff,ndc,nfet,pfet,fet)/active ~space/w 95.700 - areacap (rnd)/active 38.430 - overlap (rnd)/active ~space/w 38.430 - perimc (rnd)/active ~(rnd)/active 94.800 - sideoverlap (rnd)/active ~(rnd)/active ~space/w 94.800 + areacap (rnd)/active 38.070 + overlap (rnd)/active ~space/w 38.070 + perimc (rnd)/active ~(rnd)/active 95.700 + sideoverlap (rnd)/active ~(rnd)/active ~space/w 95.700 #pdiff -# MODEL HANDLES THIS: areacap (pdiff,pdc)/active 65.880 -# MODEL HANDLES THIS: overlap (pdiff,pdc)/active ~space/w 65.880 -# MODEL HANDLES THIS: perimc (pdiff,pdc)/active ~(pdiff,pdc,nfet,pfet,fet)/active 75.300 -# MODEL HANDLES THIS: sideoverlap (pdiff,pdc)/active ~(pdiff,pdc,nfet,pfet,fet)/active ~space/w 75.300 +# MODEL HANDLES THIS: areacap (pdiff,pdc)/active 65.610 +# MODEL HANDLES THIS: overlap (pdiff,pdc)/active ~space/w 65.610 +# MODEL HANDLES THIS: perimc (pdiff,pdc)/active ~(pdiff,pdc,nfet,pfet,fet)/active 73.800 +# MODEL HANDLES THIS: sideoverlap (pdiff,pdc)/active ~(pdiff,pdc,nfet,pfet,fet)/active ~space/w 73.800 - areacap (rpd)/active 65.880 - overlap (rpd)/active ~space/w 65.880 - perimc (rpd)/active ~(rpd)/active 75.300 - sideoverlap (rpd)/active ~(rpd)/active ~space/w 75.300 + areacap (rpd)/active 65.610 + overlap (rpd)/active ~space/w 65.610 + perimc (rpd)/active ~(rpd)/active 73.800 + sideoverlap (rpd)/active ~(rpd)/active ~space/w 73.800 #rnw #poly -# MODEL HANDLES THIS: overlap (nfet)/active (ndiff,rnd,ndc)/active 221.670 -# MODEL HANDLES THIS: sideoverlap (nfet)/active ~(nfet)/active (ndiff,rnd,ndc)/active 58.500 -# MODEL HANDLES THIS: overlap (pfet)/active (pdiff,rpd,pdc)/active 213.480 -# MODEL HANDLES THIS: sideoverlap (pfet)/active ~(pfet)/active (pdiff,rpd,pdc)/active 82.800 +# MODEL HANDLES THIS: overlap (nfet)/active (ndiff,rnd,ndc)/active 225.450 +# MODEL HANDLES THIS: sideoverlap (nfet)/active ~(nfet)/active (ndiff,rnd,ndc)/active 65.700 +# MODEL HANDLES THIS: overlap (pfet)/active (pdiff,rpd,pdc)/active 219.060 +# MODEL HANDLES THIS: sideoverlap (pfet)/active ~(pfet)/active (pdiff,rpd,pdc)/active 69 - areacap (poly,fp,rp,pc)/active 7.740 - overlap (poly,fp,rp,pc)/active ~space/w 7.740 + areacap (poly,fp,rp,pc)/active 7.920 + overlap (poly,fp,rp,pc)/active ~space/w 7.920 #poly2 #rnw #metal1 - areacap (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 2.700 + areacap (m1,fm1,rm1,ndc,pdc,pc,p2c,m2c)/metal1 2.790 #metal1-sub blocked by ~space/a,~space/c - overlap (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 ~space/w 2.700 ~space/a,~space/c - perimc (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 20.700 - sideoverlap (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 ~space/w 20.700 ~space/a,~space/c + overlap (m1,fm1,rm1,ndc,pdc,pc,p2c,m2c)/metal1 ~space/w 2.790 ~space/a,~space/c + perimc (m1,fm1,rm1,ndc,pdc,pc,p2c,m2c)/metal1 ~(m1,fm1,rm1,ndc,pdc,pc,p2c,m2c)/metal1 22.200 + sideoverlap (m1,fm1,rm1,ndc,pdc,pc,p2c,m2c)/metal1 ~(m1,fm1,rm1,ndc,pdc,pc,p2c,m2c)/metal1 ~space/w 22.200 ~space/a,~space/c #rnw - overlap (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 rnw/active 2.700 ~space/c - sideoverlap (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 rnw/active 20.700 ~space/c + overlap (m1,fm1,rm1,ndc,pdc,pc,p2c,m2c)/metal1 rnw/active 2.790 ~space/c + sideoverlap (m1,fm1,rm1,ndc,pdc,pc,p2c,m2c)/metal1 ~(m1,fm1,rm1,ndc,pdc,pc,p2c,m2c)/metal1 rnw/active 22.200 ~space/c #metal1-diff blocked by ~space/c - overlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 (ndiff,rnd,ndc)/active 3.150 ~space/c - sideoverlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 (ndiff,rnd,ndc)/active 20.700 ~space/c - overlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 (pdiff,rpd,pdc)/active 3.150 ~space/c - sideoverlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 (pdiff,rpd,pdc)/active 20.700 ~space/c + overlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 (ndiff,rnd,ndc)/active 3.150 ~space/c + sideoverlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 (ndiff,rnd,ndc)/active 22.200 ~space/c + overlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 (pdiff,rpd,pdc)/active 3.150 ~space/c + sideoverlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 (pdiff,rpd,pdc)/active 22.200 ~space/c #metal1-poly blocked by ~space/c - overlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 (poly,fp,rp,pc,nfet,pfet,fet)/active 4.590 ~space/c - sideoverlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 (poly,fp,rp,pc,nfet,pfet,fet)/active 15.900 ~space/c - sideoverlap (poly,fp,rp,pc,nfet,pfet,fet)/active ~(poly,fp,rp,pc,nfet,pfet,fet)/active (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 15.900 ~space/c + overlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 (poly,fp,rp,pc,nfet,pfet,fet)/active 4.680 ~space/c + sideoverlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 (poly,fp,rp,pc,nfet,pfet,fet)/active 18 ~space/c + sideoverlap (poly,fp,rp,pc,nfet,pfet,fet)/active ~(poly,fp,rp,pc,nfet,pfet,fet)/active (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 18 ~space/c #metal1-poly2 not blocked - overlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 (poly2,ecap,phr,p2c,p2m12c)/cap 3.960 + overlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 (poly2,ecap,phr)/cap 4.320 #metal2 - areacap (m2,fm2,rm2,m3c)/metal2 1.350 + areacap (m2,fm2,rm2,m3c)/metal2 1.440 #metal2-sub blocked by - overlap (m2,fm2,rm2,m3c)/metal2 ~space/w 1.350 ~space/a,~space/m1,~space/c - perimc (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 15.900 - sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 ~space/w 15.900 ~space/a,~space/m1,~space/c - overlap (m2,fm2,rm2,m3c)/metal2 rnw/active 1.350 ~space/m1,~space/c - sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 rnw/active 15.900 ~space/m1,~space/c + overlap (m2,fm2,rm2,m3c)/metal2 ~space/w 1.440 ~space/a,~space/m1,~space/c + perimc (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 17.400 + sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 ~space/w 17.400 ~space/a,~space/m1,~space/c + overlap (m2,fm2,rm2,m3c)/metal2 rnw/active 1.440 ~space/m1,~space/c + sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 rnw/active 17.400 ~space/m1,~space/c #metal2-*diff blocked by ~space/m1,~space/c - overlap (m2,fm2,rm2,m3c)/metal2 (ndiff,rnd,ndc)/active 1.350 ~space/m1,~space/c - sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (ndiff,rnd,ndc)/active 15.900 ~space/m1,~space/c - overlap (m2,fm2,rm2,m2c,m3c)/metal2 (pdiff,rpd,pdc)/active 1.350 ~space/m1,~space/c - sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (pdiff,rpd,pdc)/active 15.900 ~space/m1,~space/c + overlap (m2,fm2,rm2,m3c)/metal2 (ndiff,rnd,ndc)/active 1.440 ~space/m1,~space/c + sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (ndiff,rnd,ndc)/active 17.400 ~space/m1,~space/c + overlap (m2,fm2,rm2,m2c,m3c)/metal2 (pdiff,rpd,pdc)/active 1.440 ~space/m1,~space/c + sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (pdiff,rpd,pdc)/active 17.400 ~space/m1,~space/c #metal2-poly blocked by ~space/m1,~space/c overlap (m2,fm2,rm2,m3c)/metal2 (poly,fp,rp,pc,nfet,pfet,fet)/active 1.350 ~space/m1,~space/c - sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (poly,fp,rp,pc,nfet,pfet,fet)/active 10.800 ~space/m1,~space/c - sideoverlap (poly,fp,rp,pc,nfet,pfet,fet)/active ~(poly,fp,rp,pc,nfet,pfet,fet)/active (m2,fm2,rm2,m2c,m3c)/metal2 10.800 ~space/m1,~space/c + sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (poly,fp,rp,pc,nfet,pfet,fet)/active 11.400 ~space/m1,~space/c + sideoverlap (poly,fp,rp,pc,nfet,pfet,fet)/active ~(poly,fp,rp,pc,nfet,pfet,fet)/active (m2,fm2,rm2,m2c,m3c)/metal2 11.400 ~space/m1,~space/c #metal2-poly2 blocked by ~space/m1 #M2->M1 - overlap (m2,fm2,rm2,m3c)/metal2 (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 2.520 - sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 14.700 - sideoverlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 (m2,fm2,rm2,m2c,m3c)/metal2 14.700 + overlap (m2,fm2,rm2,m3c)/metal2 (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 2.700 + sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 15 + sideoverlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 (m2,fm2,rm2,m2c,m3c)/metal2 15 #metal3 areacap (m3,fm3,rm3,pad)/metal3 0.900 #metal3-sub blocked by ~space/a,~space/m1,~space/m2,~space/c overlap (m3,fm3,rm3,pad)/metal3 ~space/w 0.900 ~space/a,~space/m1,~space/m2,~space/c - perimc (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 10.500 - sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 ~space/w 10.500 ~space/a,~space/m1,~space/m2,~space/c + perimc (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 11.400 + sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 ~space/w 11.400 ~space/a,~space/m1,~space/m2,~space/c #rnw overlap (m3,fm3,rm3,pad)/metal3 rnw/active 0.900 ~space/m1,~space/m2,~space/c - sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 rnw/active 10.500 ~space/m1,~space/m2,~space/c + sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 rnw/active 11.400 ~space/m1,~space/m2,~space/c #metal3-*diff blocked by ~space/m1,~space/m2,~space/c overlap (m3,fm3,rm3,pad)/metal3 (ndiff,rnd,ndc)/active 0.990 ~space/m1,~space/m2,~space/c - sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 (ndiff,rnd,ndc)/active 10.500 ~space/m1,~space/m2,~space/c + sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 (ndiff,rnd,ndc)/active 11.400 ~space/m1,~space/m2,~space/c overlap (m3,fm3,rm3,pad)/metal3 (pdiff,rpd,pdc)/active 0.990 ~space/m1,~space/m2,~space/c - sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 (pdiff,rpd,pdc)/active 10.500 ~space/m1,~space/m2,~space/c + sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 (pdiff,rpd,pdc)/active 11.400 ~space/m1,~space/m2,~space/c #metal3-poly blocked by ~space/m1,~space/m2,~space/c overlap (m3,fm3,rm3,pad)/metal3 (poly,fp,rp,pc,nfet,pfet,fet)/active 0.810 ~space/m1,~space/m2,~space/c - sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 (poly,fp,rp,pc,nfet,pfet,fet)/active 8.100 ~space/m1,~space/m2,~space/c - sideoverlap (poly,fp,rp,pc,nfet,pfet,fet)/active ~(poly,fp,rp,pc,nfet,pfet,fet)/active (m3,fm3,rm3,m3c,pad)/metal3 8.100 ~space/m1,~space/m2,~space/c + sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 (poly,fp,rp,pc,nfet,pfet,fet)/active 8.400 ~space/m1,~space/m2,~space/c + sideoverlap (poly,fp,rp,pc,nfet,pfet,fet)/active ~(poly,fp,rp,pc,nfet,pfet,fet)/active (m3,fm3,rm3,m3c,pad)/metal3 8.400 ~space/m1,~space/m2,~space/c #metal3-poly2 blocked by ~space/m1,~space/m2 #M3->M1 - overlap (m3,fm3,rm3,pad)/metal3 (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 1.080 ~space/m2 + overlap (m3,fm3,rm3,pad)/metal3 (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 1.080 ~space/m2 #metal3-metal1 blocked by ~space/m2 - sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 9.900 ~space/m2 - sideoverlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,pdc,psc,pc,m2c)/metal1 (m3,fm3,rm3,m3c,pad)/metal3 9.900 ~space/m2 + sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 9.600 ~space/m2 + sideoverlap (m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,pdc,psc,pc,p2c,m2c)/metal1 (m3,fm3,rm3,m3c,pad)/metal3 9.600 ~space/m2 #M3->M2 - overlap (m3,fm3,rm3,pad)/metal3 (m2,fm2,rm2,m2c,m3c)/metal2 3.060 - sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 (m2,fm2,rm2,m2c,m3c)/metal2 16.800 - sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (m3,fm3,rm3,m3c,pad)/metal3 16.800 + overlap (m3,fm3,rm3,pad)/metal3 (m2,fm2,rm2,m2c,m3c)/metal2 2.880 + sideoverlap (m3,fm3,rm3,m3c,pad)/metal3 ~(m3,fm3,rm3,m3c,pad)/metal3 (m2,fm2,rm2,m2c,m3c)/metal2 15.600 + sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (m3,fm3,rm3,m3c,pad)/metal3 15.600 #metal4 @@ -7554,44 +6621,28 @@ extract #metali -#fets +#devices -# fet pfet pdiff,pdc 2 pfet Vdd! nwell 83 213 -# fet pfet pdiff,pdc 1 pfet Vdd! nwell 83 213 + device mosfet pfet pfet pdiff,pdc nwell ERROR 69 219 + device mosfet nfet nfet ndiff,ndc pwell,space/w ERROR 66 225 - device mosfet pfet pfet pdiff,pdc nwell $VDD 83 213 + fetresis pfet linear 21001 + fetresis pfet saturation 21001 + fetresis nfet linear 6020 + fetresis nfet saturation 6020 -# fet nfet ndiff,ndc 2 nfet Gnd! pwell 59 222 -# fet nfet ndiff,ndc 1 nfet Gnd! pwell 59 222 + device resistor nwellResistor rnwell *nsd + device resistor polyResistor rpoly *poly + device resistor poly2Resistor rpoly2 *poly2 + device resistor ndiffResistor rndiff *ndiff + device resistor pdiffResistor rpdiff *pdiff - device mosfet nfet nfet ndiff,ndc pwell $GND 59 222 + device resistor metal1Resistor rmetal1 *metal1 + device resistor metal2Resistor rmetal2 *metal2 + device resistor metal3Resistor rmetal3 *metal3 - fetresis pfet linear 20996 - fetresis pfet saturation 20996 - fetresis nfet linear 6144 - fetresis nfet saturation 6144 + device resistor presResistor phr *poly2 -# fet rnwell nsd,nsc 2 nwellResistor Gnd! nwell,pwell 0 0 -# fet rpoly poly,pc 2 polyResistor Gnd! nwell,pwell 0 0 -# fet rpoly2 poly2,p2c 2 poly2Resistor Gnd! nwell,pwell 0 0 -# fet rndiff ndiff,ndc 2 ndiffResistor Gnd! nwell,pwell 0 0 -# fet rpdiff pdiff,pdc 2 pdiffResistor Gnd! nwell,pwell 0 0 - - device resistor None rnwell nsd,nsc - device resistor None rpoly poly,pc - device resistor None rpoly2 poly2,p2c - device resistor None rndiff ndiff,ndc - device resistor None rpdiff pdiff,pdc - -# fet rmetal1 metal1 2 metal1Resistor Gnd! nwell,pwell 0 0 -# fet rmetal2 metal2 2 metal2Resistor Gnd! nwell,pwell 0 0 -# fet rmetal3 metal3 2 metal3Resistor Gnd! nwell,pwell 0 0 -# fet phr poly2,p2c 2 phrResistor Gnd! nwell,pwell 0 0 - - device resistor None rmetal1 *metal1 - device resistor None rmetal2 *metal2 - device resistor None rmetal3 *metal3 - device resistor None phr poly2,p2c end @@ -7606,7 +6657,7 @@ end router layer2 metal2 3 m2,fm2,rm2,m2c/m2,m3c/m2,m3c/m2 4 poly,fp,rp,ndiff,rnd,nsd,pdiff,rpd,psd,m1,fm1,rm1 1 - layer1 metal1 3 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 3 + layer1 metal1 3 m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 3 contacts m2contact 4 gridspacing 8 @@ -7706,12 +6757,12 @@ style colorversatec 4949 A0A0 5252 2828 \ 9494 0A0A 2525 8282 \ 4949 A0A0 5252 2828 - poly2,ecap,phr,p2c,p2m12c yellow \ + poly2,ecap,phr yellow \ FFFF FFFF FFFF FFFF \ FFFF FFFF FFFF FFFF \ FFFF FFFF FFFF FFFF \ FFFF FFFF FFFF FFFF - m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 cyan \ + m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 cyan \ AAAA 0000 AAAA 0000 \ AAAA 0000 AAAA 0000 \ AAAA 0000 AAAA 0000 \ @@ -7834,7 +6885,7 @@ style versatec 8080 4000 2020 1010 \ 0808 0004 0202 0101 \ 8080 0040 2020 1010 - m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,m2c/m1 \ + m1,fm1,rm1,ndc/m1,nsc/m1,pdc/m1,psc/m1,pc/m1,p2c,m2c/m1 \ 8080 0000 0000 0000 \ 0808 0000 0000 0000 \ 8080 0000 0000 0000 \ diff --git a/technology/scn3me_subm/tech/__init__.py b/technology/scn3me_subm/tech/__init__.py index 6b2d03b3..2573d2c2 100755 --- a/technology/scn3me_subm/tech/__init__.py +++ b/technology/scn3me_subm/tech/__init__.py @@ -2,5 +2,5 @@ Import tech specific modules. """ -from tech import * +from .tech import * diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index e088eff9..074009f2 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -8,6 +8,16 @@ File containing the process technology parameters for SCMOS 3me, subm, 180nm. #GDS file info GDS={} # gds units +# gds units +# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first +#is the size of a database unit in user units. The second is the size +#of a database unit in meters. For example, if your library was +#created with the default units (user unit = 1 m and 1000 database +#units per user unit), then the first number would be 0.001 and the +#second number would be 10-9. Typically, the first number is less than +#1, since you use more than 1 database unit per user unit. To +#calculate the size of a user unit in meters, divide the second number +#by the first." GDS["unit"]=(0.001,1e-6) # default label zoom GDS["zoom"] = 0.5 @@ -209,17 +219,20 @@ spice["nmos"]="n" spice["pmos"]="p" # This is a map of corners to model files SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR") -# FIXME: Uncomment when we have the new spice models -#spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"] } spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"], "FF" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"], "FS" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"], "SF" : [SPICE_MODEL_DIR+"/ss/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"], - "SS" : [SPICE_MODEL_DIR+"/ss/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"] } + "SS" : [SPICE_MODEL_DIR+"/ss/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"], + "ST" : [SPICE_MODEL_DIR+"/ss/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"], + "TS" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"], + "FT" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"], + "TF" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"], + } #spice stimulus related variables -spice["feasible_period"] = 5 # estimated feasible period in ns +spice["feasible_period"] = 10 # estimated feasible period in ns spice["supply_voltages"] = [4.5, 5.0, 5.5] # Supply voltage corners in [Volts] spice["nom_supply_voltage"] = 5.0 # Nominal supply voltage in [Volts] spice["rise_time"] = 0.05 # rise time in [Nano-seconds] @@ -227,37 +240,17 @@ spice["fall_time"] = 0.05 # fall time in [Nano-seconds] spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) spice["nom_temperature"] = 25 # Nominal temperature (celcius) -#sram signal names -#FIXME: We don't use these everywhere... -spice["vdd_name"] = "vdd" -spice["gnd_name"] = "gnd" -spice["control_signals"] = ["CSB", "WEB"] -spice["data_name"] = "DATA" -spice["addr_name"] = "ADDR" -spice["minwidth_tx"] = drc["minwidth_tx"] -spice["channel"] = drc["minlength_channel"] -spice["clk"] = "clk" - # analytical delay parameters +spice["nom_threshold"] = 1.3 # Typical Threshold voltage in Volts # FIXME: These need to be updated for SCMOS, they are copied from FreePDK45. -spice["vdd_nominal"] = 5.0 # Typical Threshold voltage in Volts -spice["temp_nominal"] = 25.0 # Typical Threshold voltage in Volts -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 spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff spice["min_tx_gate_c"] = 0.1 # Minimum transistor gate capacitance in ff -spice["msflop_setup"] = 9 # DFF setup time in ps -spice["msflop_hold"] = 1 # DFF hold time in ps -spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps -spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load -spice["msflop_in_cap"] = 9.8242 # Input capacitance of ms_flop (Din) [Femto-farad] spice["dff_setup"] = 9 # DFF setup time in ps spice["dff_hold"] = 1 # DFF hold time in ps -spice["dff_delay"] = 20.5 # DFF Clk-to-q delay in ps -spice["dff_slew"] = 13.1 # DFF output slew in ps w/ no load -spice["dff_in_cap"] = 9.8242 # Input capacitance of ms_flop (Din) [Femto-farad] +spice["dff_in_cap"] = 9.8242 # Input capacitance (D) [Femto-farad] +spice["dff_out_cap"] = 2 # Output capacitance (Q) [Femto-farad] # analytical power parameters, many values are temporary spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW @@ -265,15 +258,22 @@ spice["inv_leakage"] = 1 # Leakage power of inverter in nW spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW -spice["msflop_leakage"] = 1 # Leakage power of flop in nW -spice["flop_para_cap"] = 2 # Parasitic Output capacitance in fF +spice["dff_leakage"] = 1 # Leakage power of flop in nW + +spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz + +#Logical Effort relative values for the Handmade cells +parameter["le_tau"] = 23 #In pico-seconds. +parameter["min_inv_para_delay"] = 0.73 #In relative delay units +parameter["cap_relative_per_ff"] = 0.91 #Units of Relative Capacitance/ Femto-Farad +parameter["dff_clk_cin"] = 27.5 #In relative capacitance units +parameter["6tcell_wl_cin"] = 2 #In relative capacitance units +parameter["sa_en_pmos_size"] = 24*_lambda_ +parameter["sa_en_nmos_size"] = 9*_lambda_ +parameter["sa_inv_pmos_size"] = 18*_lambda_ +parameter["sa_inv_nmos_size"] = 9*_lambda_ +parameter["bitcell_drain_cap"] = 0.2 #In Femto-Farad, approximation of drain capacitance -spice["default_event_rate"] = 100 # Default event activity of every gate. MHz -spice["flop_transition_prob"] = .5 # Transition probability of inverter. -spice["inv_transition_prob"] = .5 # Transition probability of inverter. -spice["nand2_transition_prob"] = .1875 # Transition probability of 2-input nand. -spice["nand3_transition_prob"] = .1094 # Transition probability of 3-input nand. -spice["nor2_transition_prob"] = .1875 # Transition probability of 2-input nor. ################################################### ##END Spice Simulation Parameters ################################################### diff --git a/technology/setup_scripts/setup_openram_scn4m_subm.py b/technology/scn4m_subm/__init__.py similarity index 76% rename from technology/setup_scripts/setup_openram_scn4m_subm.py rename to technology/scn4m_subm/__init__.py index 19a4960c..c7a863f0 100644 --- a/technology/setup_scripts/setup_openram_scn4m_subm.py +++ b/technology/scn4m_subm/__init__.py @@ -1,3 +1,10 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# #!/usr/bin/python """ This type of setup script should be placed in the setup_scripts directory in the trunk @@ -34,8 +41,3 @@ os.environ["DRCLVS_HOME"] = DRCLVS_HOME OPENRAM_TECH=os.path.abspath(os.environ.get("OPENRAM_TECH")) os.environ["SPICE_MODEL_DIR"] = "{0}/{1}/models".format(OPENRAM_TECH, TECHNOLOGY) -########################## -# Paths required for OPENRAM to function - -LOCAL = "{0}/..".format(os.path.dirname(__file__)) -sys.path.append("{0}/{1}/tech".format(LOCAL,TECHNOLOGY)) diff --git a/technology/scn4m_subm/gds_lib/cell_6t.gds b/technology/scn4m_subm/gds_lib/cell_6t.gds index df644048..14d6ab7e 100644 Binary files a/technology/scn4m_subm/gds_lib/cell_6t.gds and b/technology/scn4m_subm/gds_lib/cell_6t.gds differ diff --git a/technology/scn4m_subm/gds_lib/dummy_cell_1rw_1r.gds b/technology/scn4m_subm/gds_lib/dummy_cell_1rw_1r.gds new file mode 100644 index 00000000..42727119 Binary files /dev/null and b/technology/scn4m_subm/gds_lib/dummy_cell_1rw_1r.gds differ diff --git a/technology/scn4m_subm/gds_lib/dummy_cell_1w_1r.gds b/technology/scn4m_subm/gds_lib/dummy_cell_1w_1r.gds new file mode 100644 index 00000000..34d692e8 Binary files /dev/null and b/technology/scn4m_subm/gds_lib/dummy_cell_1w_1r.gds differ diff --git a/technology/scn4m_subm/gds_lib/dummy_cell_6t.gds b/technology/scn4m_subm/gds_lib/dummy_cell_6t.gds new file mode 100644 index 00000000..4a950a68 Binary files /dev/null and b/technology/scn4m_subm/gds_lib/dummy_cell_6t.gds differ diff --git a/technology/scn4m_subm/gds_lib/replica_cell_6t.gds b/technology/scn4m_subm/gds_lib/replica_cell_6t.gds index f16f7b13..191f1206 100644 Binary files a/technology/scn4m_subm/gds_lib/replica_cell_6t.gds and b/technology/scn4m_subm/gds_lib/replica_cell_6t.gds differ diff --git a/technology/scn4m_subm/gds_lib/write_driver.gds b/technology/scn4m_subm/gds_lib/write_driver.gds index 44dabaf1..8223c795 100644 Binary files a/technology/scn4m_subm/gds_lib/write_driver.gds and b/technology/scn4m_subm/gds_lib/write_driver.gds differ diff --git a/technology/scn4m_subm/mag_lib/cell_1w_1r.mag b/technology/scn4m_subm/mag_lib/cell_1w_1r.mag new file mode 100644 index 00000000..9aec1c5d --- /dev/null +++ b/technology/scn4m_subm/mag_lib/cell_1w_1r.mag @@ -0,0 +1,142 @@ +magic +tech scmos +timestamp 1542220294 +<< nwell >> +rect 0 46 54 75 +<< pwell >> +rect 0 0 54 46 +<< ntransistor >> +rect 14 33 16 37 +rect 22 29 24 37 +rect 30 29 32 37 +rect 38 33 40 37 +rect 14 17 16 23 +rect 22 17 24 23 +rect 30 17 32 23 +rect 38 17 40 23 +<< ptransistor >> +rect 22 54 24 57 +rect 30 54 32 57 +<< ndiffusion >> +rect 13 33 14 37 +rect 16 33 17 37 +rect 21 33 22 37 +rect 17 29 22 33 +rect 24 29 25 37 +rect 29 29 30 37 +rect 32 33 33 37 +rect 37 33 38 37 +rect 40 33 41 37 +rect 32 29 37 33 +rect 9 21 14 23 +rect 13 17 14 21 +rect 16 17 22 23 +rect 24 17 25 23 +rect 29 17 30 23 +rect 32 17 38 23 +rect 40 21 45 23 +rect 40 17 41 21 +<< pdiffusion >> +rect 21 54 22 57 +rect 24 54 25 57 +rect 29 54 30 57 +rect 32 54 33 57 +<< ndcontact >> +rect 9 33 13 37 +rect 17 33 21 37 +rect 25 29 29 37 +rect 33 33 37 37 +rect 41 33 45 37 +rect 25 17 29 23 +<< pdcontact >> +rect 17 54 21 58 +rect 33 54 37 58 +<< psubstratepcontact >> +rect 25 9 29 13 +<< polysilicon >> +rect 22 57 24 60 +rect 30 57 32 60 +rect 22 44 24 54 +rect 30 51 32 54 +rect 31 47 32 51 +rect 14 37 16 44 +rect 22 40 23 44 +rect 22 37 24 40 +rect 30 37 32 47 +rect 38 37 40 44 +rect 14 31 16 33 +rect 38 31 40 33 +rect 14 23 16 24 +rect 22 23 24 29 +rect 30 23 32 29 +rect 38 23 40 24 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 27 47 31 51 +rect 10 40 14 44 +rect 23 40 27 44 +rect 40 40 44 44 +rect 12 24 16 28 +rect 38 24 42 28 +<< metal1 >> +rect 0 68 25 72 +rect 29 68 54 72 +rect 0 61 54 65 +rect 10 44 14 61 +rect 17 51 20 54 +rect 17 47 27 51 +rect 17 37 20 47 +rect 34 44 37 54 +rect 27 40 37 44 +rect 40 44 44 61 +rect 34 37 37 40 +rect 6 33 9 37 +rect 45 33 48 37 +rect 25 23 29 29 +rect 25 13 29 17 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 2 33 6 37 +rect 48 33 52 37 +rect 16 24 20 28 +rect 34 24 38 28 +rect 16 2 20 6 +rect 34 2 38 6 +<< pdm12contact >> +rect 25 54 29 58 +<< ndm12contact >> +rect 9 17 13 21 +rect 41 17 45 21 +<< nsm12contact >> +rect 25 68 29 72 +<< metal2 >> +rect 2 37 6 72 +rect 2 0 6 33 +rect 9 21 13 72 +rect 25 58 29 68 +rect 9 0 13 17 +rect 16 6 20 24 +rect 34 6 38 24 +rect 41 21 45 72 +rect 41 0 45 17 +rect 48 37 52 72 +rect 48 0 52 33 +<< comment >> +rect 0 0 54 70 +<< labels >> +rlabel metal1 19 63 19 63 1 wl0 +rlabel metal1 19 70 19 70 5 vdd +rlabel metal1 27 4 27 4 1 wl1 +rlabel psubstratepcontact 27 11 27 11 1 gnd +rlabel metal2 4 7 4 7 2 bl0 +rlabel metal2 11 7 11 7 1 bl1 +rlabel metal2 43 7 43 7 1 br1 +rlabel metal2 50 7 50 7 8 br0 +<< end >> diff --git a/technology/scn4m_subm/mag_lib/cell_6t.mag b/technology/scn4m_subm/mag_lib/cell_6t.mag index f2e9906a..bb9d943d 100644 --- a/technology/scn4m_subm/mag_lib/cell_6t.mag +++ b/technology/scn4m_subm/mag_lib/cell_6t.mag @@ -1,117 +1,118 @@ magic tech scmos -timestamp 1536091415 +timestamp 1560809302 << nwell >> -rect -8 29 42 51 +rect -8 35 42 57 << pwell >> -rect -8 -8 42 29 +rect -8 -2 42 35 << ntransistor >> -rect 7 10 9 18 -rect 29 10 31 18 -rect 10 3 14 5 -rect 24 3 28 5 +rect 7 16 9 24 +rect 29 16 31 24 +rect 10 9 14 11 +rect 24 9 28 11 << ptransistor >> -rect 7 37 11 40 -rect 27 37 31 40 +rect 7 43 11 46 +rect 27 43 31 46 << ndiffusion >> +rect -2 22 7 24 +rect 2 18 7 22 rect -2 16 7 18 -rect 2 12 7 16 -rect -2 10 7 12 -rect 9 14 10 18 -rect 9 10 14 14 -rect 28 14 29 18 -rect 24 10 29 14 +rect 9 20 10 24 +rect 9 16 14 20 +rect 28 20 29 24 +rect 24 16 29 20 +rect 31 22 36 24 +rect 31 18 32 22 rect 31 16 36 18 -rect 31 12 32 16 -rect 31 10 36 12 -rect 10 5 14 10 -rect 24 5 28 10 -rect 10 2 14 3 -rect 24 2 28 3 +rect 10 11 14 16 +rect 24 11 28 16 +rect 10 8 14 9 +rect 24 8 28 9 << pdiffusion >> -rect 2 37 7 40 -rect 11 37 12 40 -rect 26 37 27 40 -rect 31 37 32 40 +rect 2 43 7 46 +rect 11 43 12 46 +rect 26 43 27 46 +rect 31 43 32 46 << ndcontact >> -rect -2 12 2 16 -rect 10 14 14 18 -rect 24 14 28 18 -rect 32 12 36 16 -rect 10 -2 14 2 -rect 24 -2 28 2 +rect -2 18 2 22 +rect 10 20 14 24 +rect 24 20 28 24 +rect 32 18 36 22 +rect 10 4 14 8 +rect 24 4 28 8 << pdcontact >> -rect -2 36 2 40 -rect 12 36 16 40 -rect 22 36 26 40 -rect 32 36 36 40 +rect -2 42 2 46 +rect 12 42 16 46 +rect 22 42 26 46 +rect 32 42 36 46 << psubstratepcontact >> -rect -2 22 2 26 -rect 32 22 36 26 +rect -2 28 2 32 +rect 32 28 36 32 << nsubstratencontact >> -rect 32 44 36 48 +rect 32 50 36 54 << polysilicon >> -rect 7 40 11 42 -rect 27 40 31 42 -rect 7 35 11 37 -rect 7 21 9 35 -rect 27 34 31 37 -rect 15 33 31 34 -rect 19 32 31 33 -rect 7 20 21 21 -rect 7 19 24 20 -rect 7 18 9 19 -rect 29 18 31 32 -rect 7 8 9 10 -rect 17 5 21 6 -rect 29 8 31 10 -rect -2 3 10 5 -rect 14 3 24 5 -rect 28 3 36 5 +rect 7 46 11 48 +rect 27 46 31 48 +rect 7 41 11 43 +rect 7 27 9 41 +rect 27 40 31 43 +rect 15 39 31 40 +rect 19 38 31 39 +rect 7 26 21 27 +rect 7 25 24 26 +rect 7 24 9 25 +rect 29 24 31 38 +rect 7 14 9 16 +rect 17 11 21 12 +rect 29 14 31 16 +rect -2 9 10 11 +rect 14 9 24 11 +rect 28 9 36 11 << polycontact >> -rect 15 29 19 33 -rect 21 20 25 24 -rect 17 6 21 10 +rect 15 35 19 39 +rect 21 26 25 30 +rect 17 12 21 16 << metal1 >> -rect -2 44 15 48 -rect 19 44 32 48 -rect -2 40 2 44 -rect 32 40 36 44 -rect 11 36 12 40 -rect 26 36 27 40 -rect -2 26 2 29 -rect -2 16 2 22 -rect 11 18 15 36 -rect 23 24 27 36 -rect 25 20 27 24 -rect 14 14 15 18 -rect 23 18 27 20 -rect 32 26 36 29 -rect 23 14 24 18 -rect 32 16 36 22 -rect -2 6 17 9 -rect 21 6 36 9 -rect -2 5 36 6 +rect -2 50 15 54 +rect 19 50 32 54 +rect -2 46 2 50 +rect 32 46 36 50 +rect 11 42 12 46 +rect 26 42 27 46 +rect -2 32 2 35 +rect -2 22 2 28 +rect 11 24 15 42 +rect 23 30 27 42 +rect 25 26 27 30 +rect 14 20 15 24 +rect 23 24 27 26 +rect 32 32 36 35 +rect 23 20 24 24 +rect 32 22 36 28 +rect -2 12 17 15 +rect 21 12 36 15 +rect -2 11 36 12 << m2contact >> -rect 15 44 19 48 -rect -2 29 2 33 -rect 32 29 36 33 -rect 6 -2 10 2 -rect 20 -2 24 2 +rect 15 50 19 54 +rect -2 35 2 39 +rect 32 35 36 39 +rect 6 4 10 8 +rect 20 4 24 8 << metal2 >> -rect -2 33 2 48 -rect -2 -2 2 29 -rect 6 2 10 48 -rect 24 -2 28 48 -rect 32 33 36 48 -rect 32 -2 36 29 +rect -2 39 2 54 +rect -2 0 2 35 +rect 6 8 10 54 +rect 6 0 10 4 +rect 24 0 28 54 +rect 32 39 36 54 +rect 32 0 36 35 << bb >> -rect 0 0 34 46 +rect 0 0 34 52 << labels >> -rlabel metal2 0 0 0 0 1 gnd -rlabel metal2 34 0 34 0 1 gnd -rlabel m2contact 17 46 17 46 5 vdd -rlabel metal2 8 43 8 43 1 bl -rlabel metal2 26 43 26 43 1 br -rlabel metal1 4 7 4 7 1 wl +rlabel metal2 0 6 0 6 1 gnd +rlabel metal2 34 6 34 6 1 gnd +rlabel m2contact 17 52 17 52 5 vdd +rlabel metal2 8 49 8 49 1 bl +rlabel metal2 26 49 26 49 1 br +rlabel metal1 4 13 4 13 1 wl << end >> diff --git a/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag b/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag new file mode 100644 index 00000000..60e24aca --- /dev/null +++ b/technology/scn4m_subm/mag_lib/dummy_cell_1rw_1r.mag @@ -0,0 +1,136 @@ +magic +tech scmos +timestamp 1562188987 +<< nwell >> +rect 0 46 54 75 +<< pwell >> +rect 0 0 54 46 +<< ntransistor >> +rect 14 33 16 37 +rect 22 29 24 37 +rect 30 29 32 37 +rect 38 33 40 37 +rect 14 17 16 23 +rect 22 17 24 23 +rect 30 17 32 23 +rect 38 17 40 23 +<< ptransistor >> +rect 22 54 24 57 +rect 30 54 32 57 +<< ndiffusion >> +rect 13 33 14 37 +rect 16 33 17 37 +rect 21 33 22 37 +rect 17 29 22 33 +rect 24 29 25 37 +rect 29 29 30 37 +rect 32 33 33 37 +rect 37 33 38 37 +rect 40 33 41 37 +rect 32 29 37 33 +rect 9 21 14 23 +rect 13 17 14 21 +rect 16 17 22 23 +rect 24 17 25 23 +rect 29 17 30 23 +rect 32 17 38 23 +rect 40 21 45 23 +rect 40 17 41 21 +<< pdiffusion >> +rect 21 54 22 57 +rect 24 54 25 57 +rect 29 54 30 57 +rect 32 54 33 57 +<< ndcontact >> +rect 9 33 13 37 +rect 17 33 21 37 +rect 25 29 29 37 +rect 33 33 37 37 +rect 41 33 45 37 +rect 9 17 13 21 +rect 25 17 29 23 +rect 41 17 45 21 +<< pdcontact >> +rect 17 54 21 58 +rect 25 54 29 58 +rect 33 54 37 58 +<< psubstratepcontact >> +rect 25 9 29 13 +<< nsubstratencontact >> +rect 25 68 29 72 +<< polysilicon >> +rect 22 57 24 60 +rect 30 57 32 60 +rect 22 44 24 54 +rect 30 51 32 54 +rect 31 47 32 51 +rect 14 37 16 44 +rect 22 40 23 44 +rect 22 37 24 40 +rect 30 37 32 47 +rect 38 37 40 44 +rect 14 31 16 33 +rect 38 31 40 33 +rect 14 23 16 24 +rect 22 23 24 29 +rect 30 23 32 29 +rect 38 23 40 24 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 27 47 31 51 +rect 10 40 14 44 +rect 23 40 27 44 +rect 40 40 44 44 +rect 12 24 16 28 +rect 38 24 42 28 +<< metal1 >> +rect 0 68 25 72 +rect 29 68 54 72 +rect 0 61 54 65 +rect 10 44 14 61 +rect 17 51 20 54 +rect 17 47 27 51 +rect 17 37 20 47 +rect 34 44 37 54 +rect 27 40 37 44 +rect 40 44 44 61 +rect 34 37 37 40 +rect 2 33 9 37 +rect 45 33 52 37 +rect 25 23 29 29 +rect 25 13 29 17 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 25 68 29 72 +rect 25 54 29 58 +rect 16 24 20 28 +rect 34 24 38 28 +rect 16 2 20 6 +rect 34 2 38 6 +<< metal2 >> +rect 2 0 6 72 +rect 9 0 13 72 +rect 25 58 29 68 +rect 16 6 20 24 +rect 34 6 38 24 +rect 41 0 45 72 +rect 48 0 52 72 +<< comment >> +rect 0 0 54 70 +<< labels >> +rlabel metal1 19 63 19 63 1 wl0 +rlabel metal1 19 70 19 70 5 vdd +rlabel metal1 27 4 27 4 1 wl1 +rlabel psubstratepcontact 27 11 27 11 1 gnd +rlabel metal2 4 7 4 7 2 bl0 +rlabel metal2 11 7 11 7 1 bl1 +rlabel metal2 43 7 43 7 1 br1 +rlabel metal2 50 7 50 7 8 br0 +<< end >> diff --git a/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag b/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag new file mode 100644 index 00000000..03e49f03 --- /dev/null +++ b/technology/scn4m_subm/mag_lib/dummy_cell_1w_1r.mag @@ -0,0 +1,136 @@ +magic +tech scmos +timestamp 1562189027 +<< nwell >> +rect 0 46 54 75 +<< pwell >> +rect 0 0 54 46 +<< ntransistor >> +rect 14 33 16 37 +rect 22 29 24 37 +rect 30 29 32 37 +rect 38 33 40 37 +rect 14 17 16 23 +rect 22 17 24 23 +rect 30 17 32 23 +rect 38 17 40 23 +<< ptransistor >> +rect 22 54 24 57 +rect 30 54 32 57 +<< ndiffusion >> +rect 13 33 14 37 +rect 16 33 17 37 +rect 21 33 22 37 +rect 17 29 22 33 +rect 24 29 25 37 +rect 29 29 30 37 +rect 32 33 33 37 +rect 37 33 38 37 +rect 40 33 41 37 +rect 32 29 37 33 +rect 9 21 14 23 +rect 13 17 14 21 +rect 16 17 22 23 +rect 24 17 25 23 +rect 29 17 30 23 +rect 32 17 38 23 +rect 40 21 45 23 +rect 40 17 41 21 +<< pdiffusion >> +rect 21 54 22 57 +rect 24 54 25 57 +rect 29 54 30 57 +rect 32 54 33 57 +<< ndcontact >> +rect 9 33 13 37 +rect 17 33 21 37 +rect 25 29 29 37 +rect 33 33 37 37 +rect 41 33 45 37 +rect 9 17 13 21 +rect 25 17 29 23 +rect 41 17 45 21 +<< pdcontact >> +rect 17 54 21 58 +rect 25 54 29 58 +rect 33 54 37 58 +<< psubstratepcontact >> +rect 25 9 29 13 +<< nsubstratencontact >> +rect 25 68 29 72 +<< polysilicon >> +rect 22 57 24 60 +rect 30 57 32 60 +rect 22 44 24 54 +rect 30 51 32 54 +rect 31 47 32 51 +rect 14 37 16 44 +rect 22 40 23 44 +rect 22 37 24 40 +rect 30 37 32 47 +rect 38 37 40 44 +rect 14 31 16 33 +rect 38 31 40 33 +rect 14 23 16 24 +rect 22 23 24 29 +rect 30 23 32 29 +rect 38 23 40 24 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 27 47 31 51 +rect 10 40 14 44 +rect 23 40 27 44 +rect 40 40 44 44 +rect 12 24 16 28 +rect 38 24 42 28 +<< metal1 >> +rect 0 68 25 72 +rect 29 68 54 72 +rect 0 61 54 65 +rect 10 44 14 61 +rect 17 51 20 54 +rect 17 47 27 51 +rect 17 37 20 47 +rect 34 44 37 54 +rect 27 40 37 44 +rect 40 44 44 61 +rect 34 37 37 40 +rect 2 33 9 37 +rect 45 33 52 37 +rect 25 23 29 29 +rect 25 13 29 17 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 25 68 29 72 +rect 25 54 29 58 +rect 16 24 20 28 +rect 34 24 38 28 +rect 16 2 20 6 +rect 34 2 38 6 +<< metal2 >> +rect 2 0 6 72 +rect 9 0 13 72 +rect 25 58 29 68 +rect 16 6 20 24 +rect 34 6 38 24 +rect 41 0 45 72 +rect 48 0 52 72 +<< comment >> +rect 0 0 54 70 +<< labels >> +rlabel metal1 19 63 19 63 1 wl0 +rlabel metal1 19 70 19 70 5 vdd +rlabel metal1 27 4 27 4 1 wl1 +rlabel psubstratepcontact 27 11 27 11 1 gnd +rlabel metal2 4 7 4 7 2 bl0 +rlabel metal2 11 7 11 7 1 bl1 +rlabel metal2 43 7 43 7 1 br1 +rlabel metal2 50 7 50 7 8 br0 +<< end >> diff --git a/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag b/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag new file mode 100644 index 00000000..74562f15 --- /dev/null +++ b/technology/scn4m_subm/mag_lib/dummy_cell_6t.mag @@ -0,0 +1,115 @@ +magic +tech scmos +timestamp 1560809362 +<< nwell >> +rect -8 35 42 57 +<< pwell >> +rect -8 -2 42 35 +<< ntransistor >> +rect 7 16 9 24 +rect 29 16 31 24 +rect 10 9 14 11 +rect 24 9 28 11 +<< ptransistor >> +rect 7 43 11 46 +rect 27 43 31 46 +<< ndiffusion >> +rect -2 22 7 24 +rect 2 18 7 22 +rect -2 16 7 18 +rect 9 20 10 24 +rect 9 16 14 20 +rect 28 20 29 24 +rect 24 16 29 20 +rect 31 22 36 24 +rect 31 18 32 22 +rect 31 16 36 18 +rect 10 11 14 16 +rect 24 11 28 16 +rect 10 8 14 9 +rect 24 8 28 9 +<< pdiffusion >> +rect 2 43 7 46 +rect 11 43 12 46 +rect 26 43 27 46 +rect 31 43 32 46 +<< ndcontact >> +rect -2 18 2 22 +rect 10 20 14 24 +rect 24 20 28 24 +rect 32 18 36 22 +rect 10 4 14 8 +rect 24 4 28 8 +<< pdcontact >> +rect -2 42 2 46 +rect 12 42 16 46 +rect 22 42 26 46 +rect 32 42 36 46 +<< psubstratepcontact >> +rect -2 28 2 32 +rect 32 28 36 32 +<< nsubstratencontact >> +rect 32 50 36 54 +<< polysilicon >> +rect 7 46 11 48 +rect 27 46 31 48 +rect 7 41 11 43 +rect 7 27 9 41 +rect 27 40 31 43 +rect 15 39 31 40 +rect 19 38 31 39 +rect 7 26 21 27 +rect 7 25 24 26 +rect 7 24 9 25 +rect 29 24 31 38 +rect 7 14 9 16 +rect 17 11 21 12 +rect 29 14 31 16 +rect -2 9 10 11 +rect 14 9 24 11 +rect 28 9 36 11 +<< polycontact >> +rect 15 35 19 39 +rect 21 26 25 30 +rect 17 12 21 16 +<< metal1 >> +rect -2 50 15 54 +rect 19 50 32 54 +rect -2 46 2 50 +rect 32 46 36 50 +rect 11 42 12 46 +rect 26 42 27 46 +rect -2 32 2 35 +rect -2 22 2 28 +rect 11 24 15 42 +rect 23 30 27 42 +rect 25 26 27 30 +rect 14 20 15 24 +rect 23 24 27 26 +rect 32 32 36 35 +rect 23 20 24 24 +rect 32 22 36 28 +rect -2 12 17 15 +rect 21 12 36 15 +rect -2 11 36 12 +<< m2contact >> +rect 15 50 19 54 +rect -2 35 2 39 +rect 32 35 36 39 +<< metal2 >> +rect -2 39 2 54 +rect -2 0 2 35 +rect 6 0 10 54 +rect 24 0 28 54 +rect 32 39 36 54 +rect 32 0 36 35 +<< bb >> +rect 0 0 34 52 +<< labels >> +rlabel metal2 0 6 0 6 1 gnd +rlabel metal2 34 6 34 6 1 gnd +rlabel m2contact 17 52 17 52 5 vdd +rlabel metal2 8 49 8 49 1 bl +rlabel metal2 26 49 26 49 1 br +rlabel metal1 4 13 4 13 1 wl +<< end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag b/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag new file mode 100644 index 00000000..f215ff04 --- /dev/null +++ b/technology/scn4m_subm/mag_lib/replica_cell_1w_1r.mag @@ -0,0 +1,145 @@ +magic +tech scmos +timestamp 1542221056 +<< nwell >> +rect 0 46 54 75 +<< pwell >> +rect 0 0 54 46 +<< ntransistor >> +rect 14 33 16 37 +rect 22 29 24 37 +rect 30 29 32 37 +rect 38 33 40 37 +rect 14 17 16 23 +rect 22 17 24 23 +rect 30 17 32 23 +rect 38 17 40 23 +<< ptransistor >> +rect 22 54 24 57 +rect 30 54 32 57 +<< ndiffusion >> +rect 13 33 14 37 +rect 16 33 17 37 +rect 21 33 22 37 +rect 17 29 22 33 +rect 24 29 25 37 +rect 29 29 30 37 +rect 32 33 33 37 +rect 37 33 38 37 +rect 40 33 41 37 +rect 32 29 37 33 +rect 9 21 14 23 +rect 13 17 14 21 +rect 16 17 22 23 +rect 24 17 25 23 +rect 29 17 30 23 +rect 32 17 38 23 +rect 40 21 45 23 +rect 40 17 41 21 +<< pdiffusion >> +rect 21 54 22 57 +rect 24 54 25 57 +rect 29 54 30 57 +rect 32 54 33 57 +<< ndcontact >> +rect 9 33 13 37 +rect 17 33 21 37 +rect 25 29 29 37 +rect 33 33 37 37 +rect 41 33 45 37 +rect 9 17 13 21 +rect 25 17 29 23 +rect 41 17 45 21 +<< pdcontact >> +rect 17 54 21 58 +rect 25 54 29 58 +rect 33 54 37 58 +<< psubstratepcontact >> +rect 25 9 29 13 +<< nsubstratencontact >> +rect 25 68 29 72 +<< polysilicon >> +rect 22 57 24 60 +rect 30 57 32 60 +rect 22 44 24 54 +rect 30 51 32 54 +rect 31 47 32 51 +rect 14 37 16 44 +rect 22 40 23 44 +rect 22 37 24 40 +rect 30 37 32 47 +rect 38 37 40 44 +rect 14 31 16 33 +rect 38 31 40 33 +rect 14 23 16 24 +rect 22 23 24 29 +rect 30 23 32 29 +rect 38 23 40 24 +rect 14 15 16 17 +rect 22 15 24 17 +rect 30 15 32 17 +rect 38 15 40 17 +<< polycontact >> +rect 27 47 31 51 +rect 10 40 14 44 +rect 23 40 27 44 +rect 40 40 44 44 +rect 12 24 16 28 +rect 38 24 42 28 +<< metal1 >> +rect 0 68 25 72 +rect 29 68 54 72 +rect 0 61 54 65 +rect 10 44 14 61 +rect 29 54 33 58 +rect 17 51 20 54 +rect 17 47 27 51 +rect 17 37 20 47 +rect 34 44 37 54 +rect 27 40 37 44 +rect 40 44 44 61 +rect 34 37 37 40 +rect 6 33 9 37 +rect 45 33 48 37 +rect 25 23 29 29 +rect 25 13 29 17 +rect 0 9 25 13 +rect 29 9 54 13 +rect 0 2 16 6 +rect 20 2 34 6 +rect 38 2 54 6 +<< m2contact >> +rect 25 68 29 72 +rect 25 54 29 58 +rect 2 33 6 37 +rect 48 33 52 37 +rect 16 24 20 28 +rect 34 24 38 28 +rect 9 17 13 21 +rect 41 17 45 21 +rect 16 2 20 6 +rect 34 2 38 6 +<< metal2 >> +rect 2 37 6 72 +rect 2 0 6 33 +rect 9 21 13 72 +rect 25 58 29 68 +rect 9 0 13 17 +rect 16 6 20 24 +rect 34 6 38 24 +rect 41 21 45 72 +rect 41 0 45 17 +rect 48 37 52 72 +rect 48 0 52 33 +<< comment >> +rect 0 0 54 70 +<< labels >> +rlabel metal1 19 63 19 63 1 wl0 +rlabel metal1 19 70 19 70 5 vdd +rlabel metal1 27 4 27 4 1 wl1 +rlabel psubstratepcontact 27 11 27 11 1 gnd +rlabel metal2 4 7 4 7 2 bl0 +rlabel metal2 11 7 11 7 1 bl1 +rlabel metal2 43 7 43 7 1 br1 +rlabel metal2 50 7 50 7 8 br0 +<< end >> diff --git a/technology/scn4m_subm/mag_lib/replica_cell_6t.mag b/technology/scn4m_subm/mag_lib/replica_cell_6t.mag index c28cb2c6..b5a5f7b8 100644 --- a/technology/scn4m_subm/mag_lib/replica_cell_6t.mag +++ b/technology/scn4m_subm/mag_lib/replica_cell_6t.mag @@ -1,118 +1,119 @@ magic tech scmos -timestamp 1541443051 +timestamp 1560809329 << nwell >> -rect -8 29 42 51 +rect -8 35 42 57 << pwell >> -rect -8 -8 42 29 +rect -8 -2 42 35 << ntransistor >> -rect 7 10 9 18 -rect 29 10 31 18 -rect 10 3 14 5 -rect 24 3 28 5 +rect 7 16 9 24 +rect 29 16 31 24 +rect 10 9 14 11 +rect 24 9 28 11 << ptransistor >> -rect 7 37 11 40 -rect 27 37 31 40 +rect 7 43 11 46 +rect 27 43 31 46 << ndiffusion >> +rect -2 22 7 24 +rect 2 18 7 22 rect -2 16 7 18 -rect 2 12 7 16 -rect -2 10 7 12 -rect 9 14 10 18 -rect 9 10 14 14 -rect 28 14 29 18 -rect 24 10 29 14 +rect 9 20 10 24 +rect 9 16 14 20 +rect 28 20 29 24 +rect 24 16 29 20 +rect 31 22 36 24 +rect 31 18 32 22 rect 31 16 36 18 -rect 31 12 32 16 -rect 31 10 36 12 -rect 10 5 14 10 -rect 24 5 28 10 -rect 10 2 14 3 -rect 24 2 28 3 +rect 10 11 14 16 +rect 24 11 28 16 +rect 10 8 14 9 +rect 24 8 28 9 << pdiffusion >> -rect 2 37 7 40 -rect 11 37 12 40 -rect 26 37 27 40 -rect 31 37 32 40 +rect 2 43 7 46 +rect 11 43 12 46 +rect 26 43 27 46 +rect 31 43 32 46 << ndcontact >> -rect -2 12 2 16 -rect 10 14 14 18 -rect 24 14 28 18 -rect 32 12 36 16 -rect 10 -2 14 2 -rect 24 -2 28 2 +rect -2 18 2 22 +rect 10 20 14 24 +rect 24 20 28 24 +rect 32 18 36 22 +rect 10 4 14 8 +rect 24 4 28 8 << pdcontact >> -rect -2 36 2 40 -rect 12 36 16 40 -rect 22 36 26 40 -rect 32 36 36 40 +rect -2 42 2 46 +rect 12 42 16 46 +rect 22 42 26 46 +rect 32 42 36 46 << psubstratepcontact >> -rect -2 22 2 26 -rect 32 22 36 26 +rect -2 28 2 32 +rect 32 28 36 32 << nsubstratencontact >> -rect 32 44 36 48 +rect 32 50 36 54 << polysilicon >> -rect 7 40 11 42 -rect 27 40 31 42 -rect 7 35 11 37 -rect 7 21 9 35 -rect 27 34 31 37 -rect 15 33 31 34 -rect 19 32 31 33 -rect 7 20 21 21 -rect 7 19 24 20 -rect 7 18 9 19 -rect 29 18 31 32 -rect 7 8 9 10 -rect 17 5 21 6 -rect 29 8 31 10 -rect -2 3 10 5 -rect 14 3 24 5 -rect 28 3 36 5 +rect 7 46 11 48 +rect 27 46 31 48 +rect 7 41 11 43 +rect 7 27 9 41 +rect 27 40 31 43 +rect 15 39 31 40 +rect 19 38 31 39 +rect 7 26 21 27 +rect 7 25 24 26 +rect 7 24 9 25 +rect 29 24 31 38 +rect 7 14 9 16 +rect 17 11 21 12 +rect 29 14 31 16 +rect -2 9 10 11 +rect 14 9 24 11 +rect 28 9 36 11 << polycontact >> -rect 15 29 19 33 -rect 21 20 25 24 -rect 17 6 21 10 +rect 15 35 19 39 +rect 21 26 25 30 +rect 17 12 21 16 << metal1 >> -rect -2 44 15 48 -rect 19 44 32 48 -rect -2 40 2 44 -rect 22 40 26 44 -rect 32 40 36 44 -rect 11 36 12 40 -rect 26 36 27 40 -rect -2 26 2 29 -rect -2 16 2 22 -rect 11 18 15 36 -rect 23 24 27 36 -rect 25 20 27 24 -rect 14 14 15 18 -rect 23 18 27 20 -rect 32 26 36 29 -rect 23 14 24 18 -rect 32 16 36 22 -rect -2 6 17 9 -rect 21 6 36 9 -rect -2 5 36 6 +rect -2 50 15 54 +rect 19 50 32 54 +rect -2 46 2 50 +rect 22 46 26 50 +rect 32 46 36 50 +rect 11 42 12 46 +rect 26 42 27 46 +rect -2 32 2 35 +rect -2 22 2 28 +rect 11 24 15 42 +rect 23 30 27 42 +rect 25 26 27 30 +rect 14 20 15 24 +rect 23 24 27 26 +rect 32 32 36 35 +rect 23 20 24 24 +rect 32 22 36 28 +rect -2 12 17 15 +rect 21 12 36 15 +rect -2 11 36 12 << m2contact >> -rect 15 44 19 48 -rect -2 29 2 33 -rect 32 29 36 33 -rect 6 -2 10 2 -rect 20 -2 24 2 +rect 15 50 19 54 +rect -2 35 2 39 +rect 32 35 36 39 +rect 6 4 10 8 +rect 20 4 24 8 << metal2 >> -rect -2 33 2 48 -rect -2 -2 2 29 -rect 6 2 10 48 -rect 24 -2 28 48 -rect 32 33 36 48 -rect 32 -2 36 29 +rect -2 39 2 54 +rect -2 0 2 35 +rect 6 8 10 54 +rect 6 0 10 4 +rect 24 0 28 54 +rect 32 39 36 54 +rect 32 0 36 35 << bb >> -rect 0 0 34 46 +rect 0 0 34 52 << labels >> -rlabel metal2 0 0 0 0 1 gnd -rlabel metal2 34 0 34 0 1 gnd -rlabel m2contact 17 46 17 46 5 vdd -rlabel metal2 8 43 8 43 1 bl -rlabel metal2 26 43 26 43 1 br -rlabel metal1 4 7 4 7 1 wl +rlabel metal2 0 6 0 6 1 gnd +rlabel metal2 34 6 34 6 1 gnd +rlabel m2contact 17 52 17 52 5 vdd +rlabel metal2 8 49 8 49 1 bl +rlabel metal2 26 49 26 49 1 br +rlabel metal1 4 13 4 13 1 wl << end >> diff --git a/technology/scn4m_subm/mag_lib/setup.tcl b/technology/scn4m_subm/mag_lib/setup.tcl index 084428b5..95e7dbea 100644 --- a/technology/scn4m_subm/mag_lib/setup.tcl +++ b/technology/scn4m_subm/mag_lib/setup.tcl @@ -2,14 +2,13 @@ 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 +# We must flatten these because the ports are disconnected +flatten class {-circuit1 dummy_cell_6t} +flatten class {-circuit1 dummy_cell_1rw_1r} +flatten class {-circuit1 dummy_cell_1w_1r} flatten class {-circuit1 bitcell_array_0} -flatten class {-circuit1 bitcell_array_1} -flatten class {-circuit1 precharge_array_0} -flatten class {-circuit1 precharge_array_1} -flatten class {-circuit1 precharge_array_2} -flatten class {-circuit1 precharge_array_3} +flatten class {-circuit1 pbitcell_0} +flatten class {-circuit1 pbitcell_1} property {-circuit1 nfet} remove as ad ps pd property {-circuit1 pfet} remove as ad ps pd property {-circuit2 n} remove as ad ps pd diff --git a/technology/scn4m_subm/mag_lib/write_driver.mag b/technology/scn4m_subm/mag_lib/write_driver.mag index ab2014aa..0aa48197 100644 --- a/technology/scn4m_subm/mag_lib/write_driver.mag +++ b/technology/scn4m_subm/mag_lib/write_driver.mag @@ -1,16 +1,15 @@ magic tech scmos -timestamp 1536089714 +timestamp 1565304081 << nwell >> rect -3 101 37 138 rect -3 0 37 51 << pwell >> -rect -3 138 37 202 +rect -3 138 37 203 rect -3 51 37 101 << ntransistor >> -rect 9 177 11 189 -rect 17 177 19 189 -rect 15 162 27 164 +rect 9 178 11 190 +rect 17 178 19 190 rect 9 144 11 148 rect 17 144 19 148 rect 10 82 12 89 @@ -27,14 +26,10 @@ rect 8 38 10 45 rect 16 38 18 45 rect 24 38 26 45 << ndiffusion >> -rect 8 177 9 189 -rect 11 177 12 189 -rect 16 177 17 189 -rect 19 177 20 189 -rect 15 164 27 165 -rect 15 161 27 162 -rect 12 157 15 160 -rect 12 156 16 157 +rect 8 178 9 190 +rect 11 178 12 190 +rect 16 178 17 190 +rect 19 178 20 190 rect 8 144 9 148 rect 11 144 12 148 rect 16 144 17 148 @@ -43,7 +38,7 @@ rect 9 82 10 89 rect 12 82 13 89 rect 17 82 18 89 rect 20 82 21 89 -rect 25 82 26 86 +rect 25 82 26 89 rect 7 57 8 64 rect 10 57 11 64 rect 15 57 16 64 @@ -68,11 +63,9 @@ rect 23 38 24 45 rect 26 38 27 45 rect 3 35 7 38 << ndcontact >> -rect 4 177 8 189 -rect 12 177 16 189 -rect 20 177 24 189 -rect 15 165 27 169 -rect 15 157 27 161 +rect 4 178 8 190 +rect 12 178 16 190 +rect 20 178 24 190 rect 4 144 8 148 rect 12 144 16 148 rect 20 144 24 148 @@ -95,22 +88,19 @@ rect 11 38 15 45 rect 19 38 23 45 rect 27 38 31 45 << psubstratepcontact >> -rect 12 152 16 156 -rect 26 82 30 86 +rect 26 82 30 89 << nsubstratencontact >> rect 12 118 16 122 rect 3 31 7 35 << polysilicon >> -rect 9 194 30 196 -rect 9 189 11 194 -rect 17 189 19 191 -rect 28 185 30 194 -rect 9 175 11 177 -rect 17 172 19 177 -rect 6 170 19 172 -rect 6 167 8 170 -rect 13 162 15 164 -rect 27 162 33 164 +rect 9 195 30 197 +rect 9 190 11 195 +rect 17 190 19 192 +rect 28 186 30 195 +rect 9 176 11 178 +rect 17 173 19 178 +rect 6 171 19 173 +rect 6 168 8 171 rect 9 148 11 150 rect 17 148 19 150 rect 9 132 11 144 @@ -133,12 +123,9 @@ rect 18 89 20 90 rect 10 81 12 82 rect 10 79 13 81 rect 2 71 3 75 -rect 11 71 13 79 +rect 11 67 13 79 rect 18 79 20 82 rect 18 77 23 79 -rect 31 71 33 162 -rect 11 69 33 71 -rect 11 67 13 69 rect 8 65 13 67 rect 8 64 10 65 rect 16 64 18 66 @@ -153,26 +140,23 @@ rect 8 28 10 38 rect 16 14 18 38 rect 24 36 26 38 << polycontact >> -rect 28 181 32 185 -rect 4 163 8 167 +rect 28 182 32 186 +rect 4 164 8 168 rect 23 96 27 100 rect 3 71 7 75 rect 23 75 27 79 rect 7 24 11 28 rect 15 10 19 14 << metal1 >> -rect 5 192 10 196 -rect 5 189 8 192 -rect 32 181 33 185 -rect 13 169 16 177 -rect 13 165 15 169 -rect 4 148 8 163 -rect 12 157 15 161 -rect 12 156 16 157 -rect 12 148 16 152 +rect 5 193 10 197 +rect 5 190 8 193 +rect 32 182 33 186 +rect 4 148 8 164 +rect 12 163 16 178 +rect 12 148 16 159 rect 4 132 8 144 rect 20 142 24 144 -rect 30 142 33 181 +rect 30 142 33 182 rect 20 138 33 142 rect 20 132 24 138 rect 12 122 16 125 @@ -193,32 +177,33 @@ rect 27 45 31 60 rect 3 35 7 38 rect 19 35 23 38 rect 7 31 19 35 -rect 0 24 7 28 -rect 11 24 36 28 +rect 3 24 7 28 +rect 11 24 31 28 << m2contact >> -rect 10 192 14 196 -rect 20 189 24 193 -rect 23 153 27 157 +rect 10 193 14 197 +rect 20 190 24 194 +rect 12 159 16 163 rect 16 118 20 122 -rect 26 86 30 90 +rect 26 89 30 90 +rect 26 86 30 89 rect 19 64 23 68 rect 19 31 23 35 rect 15 6 19 10 << metal2 >> -rect 10 196 14 202 -rect 20 193 24 202 -rect 20 177 24 189 +rect 10 197 14 203 +rect 20 194 24 203 +rect 20 178 24 190 rect 15 0 19 6 << bb >> -rect 0 0 34 202 +rect 0 0 34 203 << labels >> rlabel metal2 15 1 15 1 1 din -rlabel metal1 2 25 2 25 3 en -rlabel metal2 12 200 12 200 5 bl -rlabel metal2 22 200 22 200 5 br rlabel m2contact 21 66 21 66 1 gnd rlabel m2contact 28 88 28 88 1 gnd rlabel m2contact 21 33 21 33 1 vdd rlabel m2contact 18 120 18 120 1 vdd -rlabel m2contact 25 155 25 155 1 gnd +rlabel metal2 12 201 12 201 5 bl +rlabel metal2 22 201 22 201 5 br +rlabel m2contact 14 161 14 161 1 gnd +rlabel metal1 17 26 17 26 1 en << end >> diff --git a/technology/scn4m_subm/sp_lib/dummy_cell_1rw_1r.sp b/technology/scn4m_subm/sp_lib/dummy_cell_1rw_1r.sp new file mode 100644 index 00000000..9766a840 --- /dev/null +++ b/technology/scn4m_subm/sp_lib/dummy_cell_1rw_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT dummy_cell_1rw_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1_noconn gnd n w=1.2u l=0.4u +MM8 RA_to_R_right Q gnd gnd n w=1.2u l=0.4u +MM7 RA_to_R_left Q_bar gnd gnd n w=1.2u l=0.4u +MM6 RA_to_R_left wl1 bl1_noconn gnd n w=1.2u l=0.4u +MM5 Q wl0 bl0_noconn gnd n w=0.8u l=0.4u +MM4 Q_bar wl0 br0_noconn gnd n w=0.8u l=0.4u +MM1 Q Q_bar gnd gnd n w=1.6u l=0.4u +MM0 Q_bar Q gnd gnd n w=1.6u l=0.4u +MM3 Q Q_bar vdd vdd p w=0.6u l=0.4u +MM2 Q_bar Q vdd vdd p w=0.6u l=0.4u +.ENDS + diff --git a/technology/scn4m_subm/sp_lib/dummy_cell_1w_1r.sp b/technology/scn4m_subm/sp_lib/dummy_cell_1w_1r.sp new file mode 100644 index 00000000..f5424998 --- /dev/null +++ b/technology/scn4m_subm/sp_lib/dummy_cell_1w_1r.sp @@ -0,0 +1,14 @@ + +.SUBCKT dummy_cell_1w_1r bl0 br0 bl1 br1 wl0 wl1 vdd gnd +MM9 RA_to_R_right wl1 br1_noconn gnd n w=1.2u l=0.4u +MM8 RA_to_R_right Q gnd gnd n w=1.2u l=0.4u +MM7 RA_to_R_left Q_bar gnd gnd n w=1.2u l=0.4u +MM6 RA_to_R_left wl1 bl1_noconn gnd n w=1.2u l=0.4u +MM5 Q wl0 bl0_noconn gnd n w=0.8u l=0.4u +MM4 Q_bar wl0 br0_noconn gnd n w=0.8u l=0.4u +MM1 Q Q_bar gnd gnd n w=1.6u l=0.4u +MM0 Q_bar Q gnd gnd n w=1.6u l=0.4u +MM3 Q Q_bar vdd vdd p w=0.6u l=0.4u +MM2 Q_bar Q vdd vdd p w=0.6u l=0.4u +.ENDS + diff --git a/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp b/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp new file mode 100644 index 00000000..3b0584df --- /dev/null +++ b/technology/scn4m_subm/sp_lib/dummy_cell_6t.sp @@ -0,0 +1,17 @@ + +*********************** "dummy_cell_6t" ****************************** +.SUBCKT dummy_cell_6t bl br wl vdd gnd + +* Inverter 1 +M1000 Q Qbar vdd vdd p w=0.6u l=0.8u +M1002 Q Qbar gnd gnd n w=1.6u l=0.4u + +* Inverter 2 +M1001 vdd Q Qbar vdd p w=0.6u l=0.8u +M1003 gnd Q Qbar gnd n w=1.6u l=0.4u + +* Access transistors +M1004 Q wl bl_noconn gnd n w=0.8u l=0.4u +M1005 Qbar wl br_noconn gnd n w=0.8u l=0.4u + +.ENDS diff --git a/technology/scn4m_subm/sp_lib/write_driver.sp b/technology/scn4m_subm/sp_lib/write_driver.sp index d1dbf9b2..e86da288 100644 --- a/technology/scn4m_subm/sp_lib/write_driver.sp +++ b/technology/scn4m_subm/sp_lib/write_driver.sp @@ -28,9 +28,8 @@ M_14 din_gated_bar din_gated gnd gnd n W=0.8u L=0.4u ************************************************ * pull down with en enable -M_15 bl din_gated_bar net_5 gnd n W=2.4u L=0.4u -M_16 br din_bar_gated_bar net_5 gnd n W=2.4u L=0.4u -M_17 net_5 en gnd gnd n W=2.4u L=0.4u +M_15 bl din_gated_bar gnd gnd n W=2.4u L=0.4u +M_16 br din_bar_gated_bar gnd gnd n W=2.4u L=0.4u diff --git a/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech b/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech index f39aa84f..7207f681 100644 --- a/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech +++ b/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech @@ -636,8 +636,8 @@ style lambda=0.20(p) #CRE/CRM layer CRM rm1,prm1,rm2,prm2,rm3,prm3,rm4,prm4 #CRE/CRM calma 70 0 - layer CX comment - labels comment + layer CX comment,bb + labels comment,bb calma 63 0 layer XP pad,xp @@ -1075,8 +1075,8 @@ style lambda=0.20(cp) #CRE/CRM layer CRM rm1,prm1,rm2,prm2,rm3,prm3,rm4,prm4 #CRE/CRM calma 70 0 - layer CX comment - labels comment + layer CX comment,bb + labels comment,bb calma 63 0 layer XP pad,xp @@ -1412,8 +1412,8 @@ style lambda=0.20(c) #CRE/CRM layer CRM rm1,prm1,rm2,prm2,rm3,prm3,rm4,prm4 #CRE/CRM calma 70 0 - layer CX comment - labels comment + layer CX comment,bb + labels comment,bb calma 63 0 layer XP pad,xp @@ -1749,8 +1749,8 @@ style lambda=0.20() #CRE/CRM layer CRM rm1,prm1,rm2,prm2,rm3,prm3,rm4,prm4 #CRE/CRM calma 70 0 - layer CX comment - labels comment + layer CX comment,bb + labels comment,bb calma 63 0 layer XP pad,xp @@ -1764,6 +1764,11 @@ cifinput style lambda=0.20(p) scalefactor 20 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -2450,10 +2455,6 @@ style lambda=0.20(p) and-not CBA calma CAA 43 * - layer comment CX - labels CX - calma CX 63 * - calma CTA 60 * calma CRW 65 * @@ -2470,6 +2471,11 @@ style lambda=0.20(p) style lambda=0.20(s) scalefactor 20 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -3158,10 +3164,6 @@ style lambda=0.20(s) and-not CBA calma CAA 43 * - layer comment CX - labels CX - calma CX 63 * - calma CTA 60 * calma CRW 65 * @@ -3178,6 +3180,11 @@ style lambda=0.20(s) style lambda=0.20(ps) scalefactor 20 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -3810,10 +3817,6 @@ style lambda=0.20(ps) and-not CBA calma CAA 43 * - layer comment CX - labels CX - calma CX 63 * - calma CTA 60 * calma CRW 65 * @@ -3830,6 +3833,11 @@ style lambda=0.20(ps) style lambda=0.20() scalefactor 20 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -4516,10 +4524,6 @@ style lambda=0.20() and-not CBA calma CAA 43 * - layer comment CX - labels CX - calma CX 63 * - calma CTA 60 * calma CRW 65 * @@ -4536,6 +4540,11 @@ style lambda=0.20() style lambda=0.20(c) scalefactor 20 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -4989,10 +4998,6 @@ style lambda=0.20(c) and-not CBA calma CAA 43 * - layer comment CX - labels CX - calma CX 63 * - calma CTA 60 * calma CRW 65 * @@ -5009,6 +5014,11 @@ style lambda=0.20(c) style lambda=0.20(cs) scalefactor 20 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -5464,10 +5474,6 @@ style lambda=0.20(cs) and-not CBA calma CAA 43 * - layer comment CX - labels CX - calma CX 63 * - calma CTA 60 * calma CRW 65 * @@ -5484,6 +5490,11 @@ style lambda=0.20(cs) style lambda=0.20(cps) scalefactor 20 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -5940,10 +5951,6 @@ style lambda=0.20(cps) and-not CBA calma CAA 43 * - layer comment CX - labels CX - calma CX 63 * - calma CTA 60 * calma CRW 65 * @@ -5960,6 +5967,11 @@ style lambda=0.20(cps) style lambda=0.20(cp) scalefactor 20 + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma BB 63 0 + layer nwell CWN and-not CWNR and-not CTA @@ -6414,10 +6426,6 @@ style lambda=0.20(cp) and-not CBA calma CAA 43 * - layer comment CX - labels CX - calma CX 63 * - calma CTA 60 * calma CRW 65 * diff --git a/technology/scn4m_subm/tech/__init__.py b/technology/scn4m_subm/tech/__init__.py old mode 100755 new mode 100644 index 6b2d03b3..662a09b6 --- a/technology/scn4m_subm/tech/__init__.py +++ b/technology/scn4m_subm/tech/__init__.py @@ -1,6 +1,13 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# """ Import tech specific modules. """ -from tech import * +from .tech import * diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py old mode 100755 new mode 100644 index 78222fd6..68066c09 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -1,13 +1,29 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# import os from design_rules import * """ -File containing the process technology parameters for SCMOS 3me, subm, 180nm. +File containing the process technology parameters for SCMOS 4m, 0.35um """ #GDS file info GDS={} # gds units +# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first +#is the size of a database unit in user units. The second is the size +#of a database unit in meters. For example, if your library was +#created with the default units (user unit = 1 m and 1000 database +#units per user unit), then the first number would be 0.001 and the +#second number would be 10-9. Typically, the first number is less than +#1, since you use more than 1 database unit per user unit. To +#calculate the size of a user unit in meters, divide the second number +#by the first." GDS["unit"]=(0.001,1e-6) # default label zoom GDS["zoom"] = 0.5 @@ -54,6 +70,7 @@ parameter={} parameter["min_tx_size"] = 4*_lambda_ parameter["beta"] = 2 +# These 6T sizes are used in the parameterized bitcell. parameter["6T_inv_nmos_size"] = 8*_lambda_ parameter["6T_inv_pmos_size"] = 3*_lambda_ parameter["6T_access_size"] = 4*_lambda_ @@ -70,8 +87,8 @@ drc["has_nwell"] = True 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["drc_rules"]=None #drclvs_home+"/calibreDRC_scn3me_subm.rul" +drc["lvs_rules"]=None #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) @@ -230,8 +247,6 @@ spice["nmos"]="n" spice["pmos"]="p" # This is a map of corners to model files SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR") -# FIXME: Uncomment when we have the new spice models -#spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"] } spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"], "FF" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"], "FS" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"], @@ -253,37 +268,17 @@ spice["fall_time"] = 0.05 # fall time in [Nano-seconds] spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) spice["nom_temperature"] = 25 # Nominal temperature (celcius) -#sram signal names -#FIXME: We don't use these everywhere... -spice["vdd_name"] = "vdd" -spice["gnd_name"] = "gnd" -spice["control_signals"] = ["CSB", "WEB"] -spice["data_name"] = "DATA" -spice["addr_name"] = "ADDR" -spice["minwidth_tx"] = drc["minwidth_tx"] -spice["channel"] = drc["minlength_channel"] -spice["clk"] = "clk" - # analytical delay parameters +spice["nom_threshold"] = 1.3 # Nominal Threshold voltage in Volts # FIXME: These need to be updated for SCMOS, they are copied from FreePDK45. -spice["vdd_nominal"] = 5.0 # Typical Threshold voltage in Volts -spice["temp_nominal"] = 25.0 # Typical Threshold voltage in Volts -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 spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff spice["min_tx_gate_c"] = 0.1 # Minimum transistor gate capacitance in ff -spice["msflop_setup"] = 9 # DFF setup time in ps -spice["msflop_hold"] = 1 # DFF hold time in ps -spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps -spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load -spice["msflop_in_cap"] = 9.8242 # Input capacitance of ms_flop (Din) [Femto-farad] spice["dff_setup"] = 9 # DFF setup time in ps spice["dff_hold"] = 1 # DFF hold time in ps -spice["dff_delay"] = 20.5 # DFF Clk-to-q delay in ps -spice["dff_slew"] = 13.1 # DFF output slew in ps w/ no load -spice["dff_in_cap"] = 9.8242 # Input capacitance of ms_flop (Din) [Femto-farad] +spice["dff_in_cap"] = 9.8242 # Input capacitance (D) [Femto-farad] +spice["dff_out_cap"] = 2 # Output capacitance (Q) [Femto-farad] # analytical power parameters, many values are temporary spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW @@ -291,26 +286,21 @@ spice["inv_leakage"] = 1 # Leakage power of inverter in nW spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW -spice["msflop_leakage"] = 1 # Leakage power of flop in nW -spice["flop_para_cap"] = 2 # Parasitic Output capacitance in fF +spice["dff_leakage"] = 1 # Leakage power of flop in nW -spice["default_event_rate"] = 100 # Default event activity of every gate. MHz -spice["flop_transition_prob"] = .5 # Transition probability of inverter. -spice["inv_transition_prob"] = .5 # Transition probability of inverter. -spice["nand2_transition_prob"] = .1875 # Transition probability of 2-input nand. -spice["nand3_transition_prob"] = .1094 # Transition probability of 3-input nand. -spice["nor2_transition_prob"] = .1875 # Transition probability of 2-input nor. +spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz #Logical Effort relative values for the Handmade cells -parameter["static_delay_stages"] = 4 -parameter["static_fanout_per_stage"] = 3 -parameter["static_fanout_list"] = parameter["static_delay_stages"]*[parameter["static_fanout_per_stage"]] -parameter["dff_clk_cin"] = 27.5 -parameter["6tcell_wl_cin"] = 2 -parameter["min_inv_para_delay"] = .5 +parameter["le_tau"] = 23 #In pico-seconds. +parameter["min_inv_para_delay"] = .73 #In relative delay units +parameter["cap_relative_per_ff"] = .91 #Units of Relative Capacitance/ Femto-Farad +parameter["dff_clk_cin"] = 27.5 #In relative capacitance units +parameter["6tcell_wl_cin"] = 2 #In relative capacitance units parameter["sa_en_pmos_size"] = 24*_lambda_ parameter["sa_en_nmos_size"] = 9*_lambda_ -parameter["rbl_height_percentage"] = .5 #Height of RBL compared to bitcell array +parameter["sa_inv_pmos_size"] = 18*_lambda_ +parameter["sa_inv_nmos_size"] = 9*_lambda_ +parameter["bitcell_drain_cap"] = 0.2 #In Femto-Farad, approximation of drain capacitance ################################################### ##END Spice Simulation Parameters diff --git a/technology/scn4m_subm/tf/README b/technology/scn4m_subm/tf/README.txt similarity index 63% rename from technology/scn4m_subm/tf/README rename to technology/scn4m_subm/tf/README.txt index d2531fe1..a89a588e 100644 --- a/technology/scn4m_subm/tf/README +++ b/technology/scn4m_subm/tf/README.txt @@ -18,4 +18,16 @@ be found in the file cdb2oa/OA_Conversion.txt. This kit is not yet fully supported. Please post problems and solutions at http://www.chiptalk.org -> Forums -> NCSU CDK -> NCSU CDK 1.6.0.beta for Virtuoso 6.1 -Modified 2018 by MRG to contain SCN4ME Via3/Metal4 layers. \ No newline at end of file +Modified 2018 by MRG to contain SCN4ME Via3/Metal4 layers. + +mosis.lyp is converted automatically from the .tf using: +https://github.com/klayoutmatthias/tf_import +Command line: +klayout -z -rd tf_file=FreePDK45.tf -rd lyp_file=FreePDK45.ly +You can then view layouts with: +klayout file.gds -l mosis.lyp + +glade_scn4m_subm.py is a script for Glade: +https://peardrop.co.uk/ +to load the .tf using: +glade -script ~/openram/technology/scn3me_subm/tf/glade_scn3me_subm.py -gds file.gds diff --git a/technology/scn4m_subm/tf/mosis.lyp b/technology/scn4m_subm/tf/mosis.lyp new file mode 100644 index 00000000..69480a0d --- /dev/null +++ b/technology/scn4m_subm/tf/mosis.lyp @@ -0,0 +1,2557 @@ + + + + #000000 + #000000 + 0 + 0 + C17 + C8 + false + true + false + 1 + false + false + 0 + background.drawing - 0/0 + 0/0@1 + + + #8c8ca6 + #8c8ca6 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + grid.drawing - 0/0 + 0/0@1 + + + #ccccd9 + #ccccd9 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + grid.drawing1 - 0/0 + 0/0@1 + + + #8c8ca6 + #8c8ca6 + 0 + 0 + C30 + C7 + true + true + false + 3 + false + false + 0 + Nwell.drawing - 42/0 + 42/0@1 + + + #8c8ca6 + #8c8ca6 + 0 + 0 + C25 + C8 + false + true + false + 1 + false + false + 0 + Pwell.drawing - 41/0 + 41/0@1 + + + #8c8ca6 + #8c8ca6 + 0 + 0 + C13 + C0 + true + true + false + 1 + false + false + 0 + CapWell.drawing - 0/0 + 0/0@1 + + + #bf4026 + #bf4026 + 0 + 0 + C1 + C0 + true + true + false + 1 + false + false + 0 + Pselect.drawing - 44/0 + 44/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + C29 + C0 + true + true + false + 1 + false + false + 0 + Nselect.drawing - 45/0 + 45/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C28 + C8 + true + true + false + 1 + false + false + 0 + Active.drawing - 43/0 + 43/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + I1 + C0 + true + true + false + 1 + false + false + 0 + ActX.drawing - 48/0 + 48/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + I1 + C1 + true + true + false + 1 + false + false + 0 + SiBlock.drawing - 0/0 + 0/0@1 + + + #737373 + #802626 + 0 + 0 + C15 + C0 + true + true + false + 1 + false + false + 0 + HR.drawing - 0/0 + 0/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + C0 + C8 + true + true + false + 1 + false + false + 0 + Poly1.drawing - 46/0 + 46/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + I1 + C0 + true + true + false + 1 + false + false + 0 + P1Con.drawing - 47/0 + 47/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C1 + C8 + true + true + false + 1 + false + false + 0 + Poly2.drawing - 0/0 + 0/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + I1 + C8 + true + true + false + 1 + false + false + 0 + P2Con.drawing - 0/0 + 0/0@1 + + + #39bfff + #39bfff + 0 + 0 + C22 + C8 + true + true + false + 1 + false + false + 0 + Metal1.drawing - 49/0 + 49/0@1 + + + #ff00ff + #ff00ff + 0 + 0 + I1 + C0 + true + true + false + 1 + false + false + 0 + Via.drawing - 50/0 + 50/0@1 + + + #ff00ff + #ff00ff + 0 + 0 + C21 + C8 + true + true + false + 1 + false + false + 0 + Metal2.drawing - 51/0 + 51/0@1 + + + #333399 + #333399 + 0 + 0 + I1 + C0 + true + true + false + 1 + false + false + 0 + Via2.drawing - 61/0 + 61/0@1 + + + #5e00e6 + #333399 + 0 + 0 + C5 + C0 + true + true + false + 1 + false + false + 0 + Metal3.drawing - 62/0 + 62/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + I1 + C0 + true + true + false + 1 + false + false + 0 + Via3.drawing - 30/0 + 30/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C5 + C0 + true + true + false + 1 + false + false + 0 + Metal4.drawing - 31/0 + 31/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + annotate.drawing - 0/0 + 0/0@1 + + + #ffbff2 + #ffbff2 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + annotate.drawing1 - 0/0 + 0/0@1 + + + #00ff00 + #00ff00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + annotate.drawing2 - 0/0 + 0/0@1 + + + #00ffff + #00ffff + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + annotate.drawing3 - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + annotate.drawing4 - 0/0 + 0/0@1 + + + #ffffff + #ffffff + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + annotate.drawing5 - 0/0 + 0/0@1 + + + #d9e6ff + #d9e6ff + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + annotate.drawing6 - 0/0 + 0/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + annotate.drawing7 - 0/0 + 0/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + annotate.drawing8 - 0/0 + 0/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + annotate.drawing9 - 0/0 + 0/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + C16 + C8 + true + true + false + 1 + false + false + 0 + Poly1.pin - 0/0 + 0/0@1 + + + #334dff + #334dff + 0 + 0 + C16 + C8 + true + true + false + 1 + false + false + 0 + Metal1.pin - 0/0 + 0/0@1 + + + #ff00ff + #ff00ff + 0 + 0 + C16 + C8 + true + true + false + 1 + false + false + 0 + Metal2.pin - 0/0 + 0/0@1 + + + #ffffff + #ffffff + 0 + 0 + I1 + C8 + true + true + false + 1 + false + true + 0 + Glass.drawing - 52/0 + 52/0@1 + + + #d9cc00 + #e0e0e0 + 0 + 0 + I1 + C8 + true + true + false + 1 + false + false + 0 + XP.drawing - 60/0 + 60/0@1 + + + #9900e6 + #9900e6 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + prBoundary.drawing - 0/0 + 0/0@1 + + + #00ffff + #00ffff + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + prBoundary.boundary - 0/0 + 0/0@1 + + + #ff3333 + #e0e0e0 + 0 + 0 + I1 + C0 + true + true + false + 1 + false + false + 0 + instance.drawing - 246/0 + 246/0@1 + + + #d9cc00 + #d9cc00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + instance.label - 0/0 + 0/0@1 + + + #8c8ca6 + #8c8ca6 + 0 + 0 + C29 + C7 + false + true + false + 3 + false + false + 0 + Nwell.net - 0/0 + 0/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + align.drawing - 0/0 + 0/0@1 + + + #8c8ca6 + #8c8ca6 + 0 + 0 + C29 + C8 + false + true + false + 1 + false + false + 0 + Pwell.net - 0/0 + 0/0@1 + + + #8c8ca6 + #8c8ca6 + 0 + 0 + C12 + C0 + false + true + false + 1 + false + false + 0 + CapWell.net - 0/0 + 0/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + hardFence.drawing - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C29 + C8 + false + true + false + 1 + false + false + 0 + Active.net - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + softFence.drawing - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C15 + C0 + false + true + false + 1 + false + false + 0 + ActX.net - 0/0 + 0/0@1 + + + #e0e0e0 + #e0e0e0 + 0 + 0 + C16 + C8 + false + true + false + 1 + false + false + 0 + A2.drawing - 5/0 + 5/0@1 + + + #e0e0e0 + #e0e0e0 + 0 + 0 + C16 + C8 + false + true + false + 1 + false + false + 0 + A1.drawing - 2/0 + 2/0@1 + + + #e0e0e0 + #e0e0e0 + 0 + 0 + C16 + C8 + false + true + false + 1 + false + false + 0 + comment.drawing - 0/0 + 0/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + border.drawing - 0/0 + 0/0@1 + + + #bf4026 + #bf4026 + 0 + 0 + C29 + C0 + false + true + false + 1 + false + false + 0 + Pselect.net - 0/0 + 0/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + C29 + C0 + false + true + false + 1 + false + false + 0 + Nselect.net - 0/0 + 0/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C15 + C1 + false + true + false + 1 + false + false + 0 + SiBlock.net - 0/0 + 0/0@1 + + + #737373 + #802626 + 0 + 0 + C15 + C0 + false + true + false + 1 + false + false + 0 + HR.net - 0/0 + 0/0@1 + + + #00ffff + #00ffff + 0 + 0 + I1 + C8 + false + true + false + 1 + false + false + 0 + wire.drawing - 0/0 + 0/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + C29 + C8 + false + true + false + 1 + false + false + 0 + Poly1.net - 0/0 + 0/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + C15 + C0 + false + true + false + 1 + false + false + 0 + P1Con.net - 0/0 + 0/0@1 + + + #334dff + #334dff + 0 + 0 + C29 + C8 + false + true + false + 1 + false + false + 0 + Metal1.net - 0/0 + 0/0@1 + + + #ff00ff + #ff00ff + 0 + 0 + C29 + C8 + false + true + false + 1 + false + false + 0 + Metal2.net - 0/0 + 0/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + device.label - 0/0 + 0/0@1 + + + #ff00ff + #ff00ff + 0 + 0 + C15 + C0 + false + true + false + 1 + false + false + 0 + Via.net - 0/0 + 0/0@1 + + + #333399 + #333399 + 0 + 0 + C29 + C0 + false + true + false + 1 + false + false + 0 + Metal3.net - 0/0 + 0/0@1 + + + #333399 + #333399 + 0 + 0 + C4 + C0 + false + true + false + 1 + false + false + 0 + Via2.net - 0/0 + 0/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C29 + C0 + false + true + false + 1 + false + false + 0 + Metal4.net - 0/0 + 0/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + pin.label - 0/0 + 0/0@1 + + + #ffffff + #ffffff + 0 + 0 + I1 + C0 + true + true + false + 1 + false + false + 0 + text.drawing - 63/0 + 63/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + I1 + C8 + false + true + false + 1 + false + false + 0 + pin.drawing - 0/0 + 0/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + I1 + C0 + false + true + false + 1 + false + false + 0 + device.drawing - 0/0 + 0/0@1 + + + #8c8ca6 + #8c8ca6 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + axis.drawing - 0/0 + 0/0@1 + + + #ccccd9 + #ccccd9 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + edgeLayer.drawing - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + edgeLayer.pin - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + snap.drawing - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + stretch.drawing - 0/0 + 0/0@1 + + + #ccccd9 + #ccccd9 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + y0.drawing - 0/0 + 0/0@1 + + + #bf4026 + #bf4026 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + y1.drawing - 0/0 + 0/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + y2.drawing - 0/0 + 0/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + y3.drawing - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + y4.drawing - 0/0 + 0/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + y5.drawing - 0/0 + 0/0@1 + + + #334dff + #334dff + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + y6.drawing - 0/0 + 0/0@1 + + + #9900e6 + #9900e6 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + y7.drawing - 0/0 + 0/0@1 + + + #d9cc00 + #d9cc00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + y8.drawing - 0/0 + 0/0@1 + + + #d9e6ff + #d9e6ff + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + y9.drawing - 0/0 + 0/0@1 + + + #ffffff + #ffffff + 0 + 0 + I1 + C0 + false + true + false + 1 + false + false + 0 + hilite.drawing - 0/0 + 0/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + hilite.drawing2 - 0/0 + 0/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + select.drawing - 0/0 + 0/0@1 + + + #334dff + #334dff + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + drive.drawing - 0/0 + 0/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + hiz.drawing - 0/0 + 0/0@1 + + + #00ffff + #00ffff + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + resist.drawing - 0/0 + 0/0@1 + + + #9900e6 + #9900e6 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + spike.drawing - 0/0 + 0/0@1 + + + #00ff00 + #00ff00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + supply.drawing - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + unknown.drawing - 0/0 + 0/0@1 + + + #268c6b + #268c6b + 0 + 0 + C16 + C0 + false + true + false + 1 + false + false + 0 + unset.drawing - 0/0 + 0/0@1 + + + #ff3333 + #ff3333 + 0 + 0 + C16 + C0 + false + false + false + 1 + false + false + 0 + changedLayer.tool0 - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C26 + C8 + true + true + false + 1 + false + false + 0 + Pbase.drawing - 0/0 + 0/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + C29 + C0 + false + true + false + 1 + false + false + 0 + Pbase.net - 0/0 + 0/0@1 + + + #00ffff + #00ffff + 0 + 0 + C29 + C0 + false + true + false + 1 + false + false + 0 + Resistor.net - 0/0 + 0/0@1 + + + #00ffff + #00ffff + 0 + 0 + C28 + C8 + true + true + false + 1 + false + false + 0 + Resistor.drawing - 0/0 + 0/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C29 + C8 + false + true + false + 1 + false + false + 0 + Capacitor.net - 0/0 + 0/0@1 + + + #ffe6bf + #ffe6bf + 0 + 0 + C23 + C8 + true + true + false + 1 + false + false + 0 + Capacitor.drawing - 0/0 + 0/0@1 + + + #ffffcc + #ffffcc + 0 + 0 + C29 + C8 + false + true + false + 1 + false + false + 0 + Diode.net - 0/0 + 0/0@1 + + + #ffffcc + #ffffcc + 0 + 0 + C23 + C8 + true + true + false + 1 + false + false + 0 + Diode.drawing - 0/0 + 0/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C29 + C8 + false + true + false + 1 + false + false + 0 + Poly2.net - 0/0 + 0/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C15 + C8 + false + true + false + 1 + false + false + 0 + P2Con.net - 0/0 + 0/0@1 + + + + + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + + 1 + dots + + + + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + + 2 + dots1 + + + + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + + 3 + hLine + + + + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + + 4 + vLine + + + + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + + 5 + cross + + + + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + + 6 + grid + + + + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + + 7 + slash + + + + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + + 8 + backSlash + + + + **......**...... + ..*.......*..... + ...**......**... + .....*.......*.. + ......**......** + *.......*....... + .**......**..... + ...*.......*.... + ....**......**.. + ......*.......*. + *......**......* + .*.......*...... + ..**......**.... + ....*.......*... + .....**......**. + .......*.......* + + 9 + hZigZag + + + + *....*....*..... + *.....*....*.... + .*....*.....*... + ..*....*....*... + ..*.....*....*.. + ...*....*.....*. + ....*....*....*. + ....*.....*....* + *....*....*..... + *.....*....*.... + .*....*.....*... + ..*....*....*... + ..*.....*....*.. + ...*....*.....*. + ....*....*....*. + ....*.....*....* + + 10 + vZigZag + + + + ................ + ................ + ...*****...***** + ...*...*...*...* + ...*...*...*...* + ****...*****...* + ................ + ................ + ................ + ................ + ...*****...***** + ...*...*...*...* + ...*...*...*...* + ****...*****...* + ................ + ................ + + 11 + hCurb + + + + .....*.......*.. + .....*.......*.. + .....*.......*.. + ..****....****.. + ..*.......*..... + ..*.......*..... + ..*.......*..... + ..****....****.. + .....*.......*.. + .....*.......*.. + .....*.......*.. + ..****....****.. + ..*.......*..... + ..*.......*..... + ..*.......*..... + ..****....****.. + + 12 + vCurb + + + + **************** + ..*.......*..... + ..*.......*..... + ..*.......*..... + **************** + ......*.......*. + ......*.......*. + ......*.......*. + **************** + ..*.......*..... + ..*.......*..... + ..*.......*..... + **************** + ......*.......*. + ......*.......*. + ......*.......*. + + 13 + brick + + + + ................ + ..*.......*..... + ..*.......*..... + ..*.......*..... + *****...*****... + ..*.......*..... + ..*.......*..... + ..*.......*..... + ................ + .....*.......*.. + .....*.......*.. + .....*.......*.. + ...*****...***** + .....*.......*.. + .....*.......*.. + .....*.......*.. + + 14 + dagger + + + + ................ + ....*........... + ...*.*.......... + ..*...*......... + .*.....*........ + *********....... + ................ + ................ + ................ + ...........*.... + ..........*.*... + .........*...*.. + ........*.....*. + .......********* + ................ + ................ + + 15 + triangle + + + + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + + 16 + x + + + + * + + 17 + stipple0 + + + + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + + 18 + stipple1 + + + + ****....****.... + ****....****.... + ****....****.... + ****....****.... + ....****....**** + ....****....**** + ....****....**** + ....****....**** + ****....****.... + ****....****.... + ****....****.... + ****....****.... + ....****....**** + ....****....**** + ....****....**** + ....****....**** + + 19 + stipple2 + + + + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + + 20 + stipple3 + + + + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + + 21 + stipple4 + + + + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + + 22 + stipple5 + + + + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + + 23 + stipple6 + + + + ..****....****.. + .*....*..*....*. + *......**......* + *......**......* + *......**......* + *......**......* + .*....*..*....*. + ..****....****.. + ..****....****.. + .*....*..*....*. + *......**......* + *......**......* + *......**......* + *......**......* + .*....*..*....*. + ..****....****.. + + 24 + stipple7 + + + + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + + 25 + stipple8 + + + + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + + 26 + stipple9 + + + + *.......*....... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + *.......*....... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + + 27 + stipple10 + + + + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + + 28 + stipple11 + + + + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + + 29 + dots2 + + + + *.....*.....*... + ................ + ................ + ...*.....*.....* + ................ + ................ + *.....*.....*... + ................ + ................ + ...*.....*.....* + ................ + ................ + *.....*.....*... + ................ + ................ + ...*.....*.....* + + 30 + dots4 + + + + ................ + .*........*..... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ...*........*... + ................ + ................ + ................ + ................ + + 31 + dats5 + + + * + 1 + solid + + + ***..*** + 2 + dashed + + + *.. + 3 + dots + + + ***..*.. + 4 + dashDot + + + **.. + 5 + shortDash + + + ****..**.. + 6 + doubleDash + + + *... + 7 + hidden + + + *** + 8 + thickLine + + + * + 9 + lineStyle0 + + + ***.***.***.**.* + 10 + lineStyle1 + + diff --git a/technology/scn4m_subm/tf/mosis.tf b/technology/scn4m_subm/tf/mosis.tf index bae7f07a..a8bf4ca6 100644 --- a/technology/scn4m_subm/tf/mosis.tf +++ b/technology/scn4m_subm/tf/mosis.tf @@ -552,6 +552,8 @@ layerRules( ( ("P1Con" "drawing") 47 0 t ) ( ("Metal1" "drawing") 49 0 t ) ( ("Metal2" "drawing") 51 0 t ) + ( ("Metal3" "drawing") 62 0 t ) + ( ("Metal4" "drawing") 31 0 t ) ( ("annotate" "drawing") 0 0 nil ) ( ("annotate" "drawing1") 0 0 nil ) ( ("annotate" "drawing2") 0 0 nil ) @@ -562,7 +564,6 @@ layerRules( ( ("annotate" "drawing7") 0 0 nil ) ( ("annotate" "drawing8") 0 0 nil ) ( ("annotate" "drawing9") 0 0 nil ) - ( ("Via" "drawing") 50 0 t ) ( ("Glass" "drawing") 52 0 t ) ( ("XP" "drawing") 60 0 t ) ( ("Metal2" "pin") 0 0 nil ) @@ -590,8 +591,9 @@ layerRules( ( ("P1Con" "net") 0 0 nil ) ( ("Metal1" "net") 0 0 nil ) ( ("Metal2" "net") 0 0 nil ) + ( ("Metal3" "net") 0 0 nil ) + ( ("Metal4" "net") 0 0 nil ) ( ("device" "label") 0 0 nil ) - ( ("Via" "net") 0 0 nil ) ( ("pin" "label") 0 0 nil ) ( ("text" "drawing") 63 0 t ) ( ("pin" "drawing") 0 0 nil ) @@ -649,11 +651,12 @@ layerRules( ( Metal2 0 0 nil ) ( Glass 0 0 nil ) ( XP 0 0 nil ) - ( ("Via2" "drawing") 50 0 t ) + ( ("Via" "drawing") 50 0 t ) + ( ("Via" "net") 0 0 nil ) + ( ("Via2" "drawing") 61 0 t ) ( ("Via2" "net") 0 0 nil ) - ( ("Metal3" "drawing") 50 0 t ) - ( ("Metal3" "net") 0 0 nil ) - ( ("Metal3" "pin") 0 0 nil ) + ( ("Via3" "drawing") 30 0 t ) + ( ("Via3" "net") 0 0 nil ) ( ("CapWell" "drawing") 0 0 nil ) ( ("CapWell" "net") 0 0 nil ) ( ("SiBlock" "drawing") 0 0 nil ) @@ -665,6 +668,7 @@ layerRules( viaLayers( ;( layer1 viaLayer layer2 ) ;( ------ -------- ------ ) + ( Metal3 Via3 Metal4 ) ( Metal2 Via2 Metal3 ) ( Metal1 Via Metal2 ) ( Active ActX Poly1 ) @@ -798,6 +802,10 @@ electricalRules( ( currentDensity "Metal1" 1.0 ) ( currentDensity "Via" 1.0 ) ( currentDensity "Metal2" 1.0 ) + ( currentDensity "Via2" 1.0 ) + ( currentDensity "Metal3" 1.0 ) + ( currentDensity "Via3" 1.0 ) + ( currentDensity "Metal4" 1.0 ) ) ;characterizationRules ) ;electricalRules @@ -824,10 +832,13 @@ leRules( ( Metal2 drawing ) ( Via2 drawing ) ( Metal3 drawing ) + ( Via3 drawing ) + ( Metal4 drawing ) ( Poly1 pin ) ( Metal1 pin ) ( Metal2 pin ) ( Metal3 pin ) + ( Metal4 pin ) ( Poly2 drawing ) ( P2Con drawing ) ( instance drawing ) @@ -853,7 +864,7 @@ leRules( lxRules( lxExtractLayers( - (Metal1 Metal2 Metal3) + (Metal1 Metal2 Metal3 Metal4) ) ;lxExtractLayers ) ;lxRules diff --git a/technology/setup_scripts/README b/technology/setup_scripts/README deleted file mode 100644 index 628cec7e..00000000 --- a/technology/setup_scripts/README +++ /dev/null @@ -1,4 +0,0 @@ -THIS DIRECTORY SHOULD ONLY CONTAIN SETUP SCRIPTS FOR TECHNOLOGIES. - -These scripts will be called automatically by the import_tech functions in -globals.py in the compiler directory.