diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index 5ec955c1..5e86692e 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -17,7 +17,7 @@ class channel_net(): self.name = net_name self.pins = pins self.vertical = vertical - + # Keep track of the internval if vertical: self.min_value = min(i.by() for i in pins) @@ -25,34 +25,34 @@ class channel_net(): else: self.min_value = min(i.lx() for i in pins) self.max_value = max(i.rx() for i in pins) - + # Keep track of the conflicts self.conflicts = [] def __str__(self): return self.name - + def __repr__(self): return self.name - + def __lt__(self, other): return self.min_value < other.min_value def pin_overlap(self, pin1, pin2, 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 extra comparison *shouldn't* matter. - + # Pin 1 must be in the "BOTTOM" set x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch - + # Pin 1 must be in the "LEFT" set y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap) return overlaps - + def pins_overlap(self, other, pitch): """ Check all the pin pairs on two nets and return a pin @@ -73,8 +73,8 @@ class channel_net(): min_overlap = self.min_value >= other.min_value and self.min_value <= other.max_value max_overlap = self.max_value >= other.min_value and self.max_value <= other.max_value return min_overlap or max_overlap - - + + class channel_route(design.design): unique_id = 0 @@ -98,7 +98,7 @@ class channel_route(design.design): name = "cr_{0}".format(channel_route.unique_id) channel_route.unique_id += 1 super().__init__(name) - + self.netlist = netlist self.offset = offset self.layer_stack = layer_stack @@ -106,7 +106,7 @@ class channel_route(design.design): self.vertical = vertical # For debugging... self.parent = parent - + if not directions or directions == "pref": # Use the preferred layer directions if self.get_preferred_direction(layer_stack[0]) == "V": @@ -154,7 +154,7 @@ class channel_route(design.design): if pin in conflicts: g[other_pin].remove(pin) return g - + def route(self): # Create names for the nets for the graphs nets = [] @@ -180,7 +180,7 @@ class channel_route(design.design): except KeyError: hcg[net2.name] = set([net1.name]) - + # Initialize the vertical conflict graph (vcg) # and make a list of all pins vcg = collections.OrderedDict() @@ -204,12 +204,12 @@ class channel_route(design.design): # Skip yourself if net1.name == net2.name: continue - + if net1.pins_overlap(net2, pitch): vcg[net2.name].add(net1.name) # Check if there are any cycles net1 <---> net2 in the VCG - + # Some of the pins may be to the left/below the channel offset, # so adjust if this is the case @@ -226,7 +226,7 @@ class channel_route(design.design): while len(nets) > 0: current_offset_value = current_offset.y if self.vertical else current_offset.x - + # from pprint import pformat # print("VCG:\n", pformat(vcg)) # for name,net in vcg.items(): @@ -253,7 +253,7 @@ class channel_route(design.design): # Remove the net from other constriants in the VCG vcg = self.remove_net_from_graph(net.name, vcg) nets.remove(net) - + break else: # If we made a full pass and the offset didn't change... @@ -276,7 +276,7 @@ class channel_route(design.design): current_offset = vector(current_offset.x + self.horizontal_nonpref_pitch, real_channel_offset.y) else: current_offset = vector(real_channel_offset.x, current_offset.y + self.vertical_nonpref_pitch) - + # Return the size of the channel if self.vertical: self.width = current_offset.x + self.horizontal_nonpref_pitch - self.offset.x @@ -284,7 +284,7 @@ class channel_route(design.design): else: self.width = self.max_value + self.horizontal_nonpref_pitch - self.offset.x self.height = current_offset.y + self.vertical_nonpref_pitch - self.offset.y - + def get_layer_pitch(self, layer): """ Return the track pitch on a given layer """ try: @@ -307,10 +307,10 @@ class channel_route(design.design): """ max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) - + # if we are less than a pitch, just create a non-preferred layer jog non_preferred_route = max_x - min_x <= pitch - + if non_preferred_route: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] # Add the horizontal trunk on the vertical layer! @@ -324,7 +324,7 @@ class channel_route(design.design): pin_pos = pin.uc() else: pin_pos = pin.bc() - + # No bend needed here mid = vector(pin_pos.x, trunk_offset.y) self.add_path(self.vertical_layer, [pin_pos, mid]) @@ -361,10 +361,10 @@ class channel_route(design.design): """ max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) - + # if we are less than a pitch, just create a non-preferred layer jog non_preferred_route = max_y - min_y <= pitch - + if non_preferred_route: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] # Add the vertical trunk on the horizontal layer! diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 60d3c840..1f38fc38 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -33,8 +33,8 @@ class contact(hierarchy_design.hierarchy_design): implant_type=None, well_type=None, name=""): # This will ignore the name parameter since # we can guarantee a unique name here - - super().__init__(name) + + super().__init__(name, name) debug.info(4, "create contact object {0}".format(name)) self.add_comment("layers: {0}".format(layer_stack)) @@ -80,7 +80,7 @@ class contact(hierarchy_design.hierarchy_design): self.create_first_layer_enclosure() self.create_second_layer_enclosure() self.create_nitride_cut_enclosure() - + self.height = max(self.first_layer_position.y + self.first_layer_height, self.second_layer_position.y + self.second_layer_height) self.width = max(self.first_layer_position.x + self.first_layer_width, @@ -99,7 +99,7 @@ class contact(hierarchy_design.hierarchy_design): (first_layer, via_layer, second_layer) = self.layer_stack self.first_layer_name = first_layer self.second_layer_name = second_layer - + # Contacts will have unique per first layer if via_layer in tech.layer: self.via_layer_name = via_layer @@ -115,7 +115,7 @@ class contact(hierarchy_design.hierarchy_design): 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 @@ -126,7 +126,7 @@ class contact(hierarchy_design.hierarchy_design): # DRC rules # The extend rule applies to asymmetric enclosures in one direction. # The enclosure rule applies to symmetric enclosure component. - + self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name)) # If there's a different rule for active @@ -171,7 +171,7 @@ class contact(hierarchy_design.hierarchy_design): (self.second_layer_minwidth - self.contact_array_width) / 2) else: debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1) - + def create_contact_array(self): """ Create the contact array at the origin""" # offset for the via array @@ -210,7 +210,7 @@ class contact(hierarchy_design.hierarchy_design): offset=self.second_layer_position - npc_enclose_offset, width=self.second_layer_width + 2 * npc_enclose_poly, height=self.second_layer_height + 2 * npc_enclose_poly) - + def create_first_layer_enclosure(self): # this is if the first and second layers are different self.first_layer_position = vector( @@ -269,12 +269,12 @@ class contact(hierarchy_design.hierarchy_design): offset=well_position, width=self.well_width, height=self.well_height) - + def analytical_power(self, corner, load): """ Get total power of a module """ return self.return_power() - + # Set up a static for each layer to be used for measurements for layer_stack in tech.layer_stacks: (layer1, via, layer2) = layer_stack @@ -295,7 +295,7 @@ if "nwell" in tech.layer: well_type="n") module = sys.modules[__name__] setattr(module, "nwell_contact", cont) - + if "pwell" in tech.layer: cont = factory.create(module_type="contact", layer_stack=tech.active_stack, diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index 43c84206..fc2445ab 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -15,7 +15,7 @@ class _pins: for k, v in pin_dict.items(): self.__dict__[k] = v - + class _cell: def __init__(self, pin_dict): pin_dict.update(self._default_power_pins()) @@ -29,24 +29,24 @@ class _cell: return {'vdd': 'vdd', 'gnd': 'gnd'} - + class _mirror_axis: def __init__(self, x, y): self.x = x self.y = y - + class _ptx: def __init__(self, model_is_subckt, bin_spice_models): self.model_is_subckt = model_is_subckt self.bin_spice_models = bin_spice_models - + class _pgate: def __init__(self, add_implants): self.add_implants = add_implants - + class _bitcell: def __init__(self, mirror, cell_s8_6t, cell_6t, cell_1rw1r, cell_1w1r): self.mirror = mirror @@ -110,25 +110,25 @@ class _dff: self.custom_type_list = custom_type_list self.clk_pin = clk_pin - + class _dff_buff: def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts): self.use_custom_ports = use_custom_ports self.buf_ports = custom_buff_ports self.add_body_contacts = add_body_contacts - + class _dff_buff_array: def __init__(self, use_custom_ports, add_body_contacts): self.use_custom_ports = use_custom_ports self.add_body_contacts = add_body_contacts - + class _bitcell_array: def __init__(self, use_custom_cell_arrangement): self.use_custom_cell_arrangement = use_custom_cell_arrangement - + class cell_properties(): """ This contains meta information about the custom designed cells. For @@ -142,14 +142,14 @@ class cell_properties(): self._ptx = _ptx(model_is_subckt=False, bin_spice_models=False) - + self._pgate = _pgate(add_implants=False) - + self._dff = _dff(use_custom_ports=False, custom_port_list=["D", "Q", "clk", "vdd", "gnd"], custom_type_list=["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"], clk_pin="clk") - + self._dff_buff = _dff_buff(use_custom_ports=False, custom_buff_ports=["D", "qint", "clk", "vdd", "gnd"], add_body_contacts=False) @@ -176,7 +176,7 @@ class cell_properties(): @property def ptx(self): return self._ptx - + @property def pgate(self): return self._pgate @@ -184,7 +184,7 @@ class cell_properties(): @property def dff(self): return self._dff - + @property def dff_buff(self): return self._dff_buff @@ -200,7 +200,7 @@ class cell_properties(): @property def sense_amp(self): return self._sense_amp - + @property def bitcell_array(self): return self._bitcell_array diff --git a/compiler/base/custom_layer_properties.py b/compiler/base/custom_layer_properties.py index 36e044c5..7f8e5993 100644 --- a/compiler/base/custom_layer_properties.py +++ b/compiler/base/custom_layer_properties.py @@ -6,7 +6,7 @@ # All rights reserved. # - + class _bank: def __init__(self, stack, pitch): # bank @@ -15,8 +15,8 @@ class _bank: # m2_stack, m3_pitch (sky130) self.stack = stack self.pitch = pitch - - + + class _hierarchical_decoder: def __init__(self, bus_layer, @@ -60,7 +60,7 @@ class _hierarchical_predecode: self.output_layer = output_layer self.vertical_supply = vertical_supply - + class _column_mux_array: def __init__(self, select_layer, @@ -74,7 +74,7 @@ class _column_mux_array: self.select_pitch= select_pitch self.bitline_layer = bitline_layer - + class _port_address: def __init__(self, supply_offset): @@ -82,7 +82,7 @@ class _port_address: # special supply offset self.supply_offset = supply_offset - + class _port_data: def __init__(self, channel_route_bitlines, @@ -94,7 +94,7 @@ class _port_data: # en_layer # m1 # m3 (sky130) - + # precharge_array # en_bar_layer # m1 @@ -110,7 +110,7 @@ class _replica_column: # even row check (sky130) self.even_rows = even_rows - + class _wordline_driver: def __init__(self, vertical_supply): @@ -122,14 +122,14 @@ class _wordline_driver: # vertical vdd/gnd (sky130) self.vertical_supply = vertical_supply - + class layer_properties(): """ This contains meta information about the module routing layers. These can be overriden in the tech.py file. """ def __init__(self): - + self._bank = _bank(stack="m1_stack", pitch="m2_pitch") @@ -138,7 +138,7 @@ class layer_properties(): input_layer="m1", output_layer="m3", vertical_supply=False) - + self._hierarchical_predecode = _hierarchical_predecode(bus_layer="m2", bus_directions="pref", bus_space_factor=1, @@ -156,13 +156,13 @@ class layer_properties(): enable_layer="m1") self._replica_column = _replica_column(even_rows=False) - + self._wordline_driver = _wordline_driver(vertical_supply=False) @property def bank(self): return self._bank - + @property def column_mux_array(self): return self._column_mux_array @@ -174,7 +174,7 @@ class layer_properties(): @property def hierarchical_predecode(self): return self._hierarchical_predecode - + @property def port_address(self): return self._port_address @@ -190,4 +190,4 @@ class layer_properties(): @property def wordline_driver(self): return self._wordline_driver - + diff --git a/compiler/base/delay_data.py b/compiler/base/delay_data.py index 97fe9867..5157de76 100644 --- a/compiler/base/delay_data.py +++ b/compiler/base/delay_data.py @@ -38,7 +38,7 @@ class delay_data(): 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 90364658..93169f6a 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -9,22 +9,30 @@ from hierarchy_design import hierarchy_design from utils import round_to_grid import contact from tech import preferred_directions +from tech import cell_properties as props from globals import OPTS import re +import debug class design(hierarchy_design): """ This is the same as the hierarchy_design class except it contains some DRC/layer constants and analytical models for other modules to reuse. - """ - - def __init__(self, name): - super().__init__(name) + + def __init__(self, name, cell_name=None): + # This allows us to use different GDS/spice circuits for hard cells instead of the default ones + # Except bitcell names are generated automatically by the globals.py setup_bitcells routines + # depending on the number of ports. + if name in props.names: + cell_name = props.names[name] + elif not cell_name: + cell_name = name + super().__init__(name, cell_name) self.setup_multiport_constants() - + def check_pins(self): for pin_name in self.pins: pins = self.get_pins(pin_name) @@ -52,7 +60,7 @@ class design(hierarchy_design): match = re.search(r"minarea_(.*)", rule) if match: setattr(design, match.group(0), drc(match.group(0))) - + # Single layer spacing rules match = re.search(r"(.*)_to_(.*)", rule) if match and match.group(1) == match.group(2): @@ -63,7 +71,7 @@ class design(hierarchy_design): drc(match.group(0))) else: setattr(design, match.group(0), drc(match.group(0))) - + match = re.search(r"(.*)_enclose_(.*)", rule) if match: setattr(design, match.group(0), drc(match.group(0))) @@ -94,7 +102,7 @@ class design(hierarchy_design): design.well_enclose_active = max(design.pwell_enclose_active, design.nwell_enclose_active, design.active_space) - + # These are for debugging previous manual rules if False: print("poly_width", design.poly_width) @@ -127,7 +135,7 @@ class design(hierarchy_design): These are some layer constants used in many places in the compiler. """ - + from tech import layer_indices import tech for layer in layer_indices: @@ -143,17 +151,17 @@ class design(hierarchy_design): # Skip computing the pitch for active if layer == "active": continue - + # Add the pitch setattr(design, "{}_pitch".format(layer), design.compute_pitch(layer, True)) - + # Add the non-preferrd pitch (which has vias in the "wrong" way) setattr(design, "{}_nonpref_pitch".format(layer), design.compute_pitch(layer, False)) - + if False: from tech import preferred_directions print(preferred_directions) @@ -173,9 +181,9 @@ class design(hierarchy_design): import sys sys.exit(1) - @staticmethod + @staticmethod def compute_pitch(layer, preferred=True): - + """ This is the preferred direction pitch i.e. we take the minimum or maximum contact dimension @@ -195,7 +203,7 @@ class design(hierarchy_design): @staticmethod def get_preferred_direction(layer): return preferred_directions[layer] - + @staticmethod def compute_layer_pitch(layer_stack, preferred): @@ -228,7 +236,7 @@ class design(hierarchy_design): def setup_multiport_constants(self): - """ + """ These are contants and lists that aid multiport design. Ports are always in the order RW, W, R. Port indices start from 0 and increment. @@ -266,14 +274,14 @@ class design(hierarchy_design): self.read_ports.append(port_number) self.readonly_ports.append(port_number) port_number += 1 - + def analytical_power(self, corner, load): """ Get total power of a module """ total_module_power = self.return_power() for inst in self.insts: total_module_power += inst.mod.analytical_power(corner, load) return total_module_power - + design.setup_drc_constants() design.setup_layer_constants() - + diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 0ba8e858..c8f6036f 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -153,7 +153,7 @@ class geometry: def center(self): """ Return the center coordinate """ return vector(self.cx(), self.cy()) - + class instance(geometry): """ @@ -227,7 +227,7 @@ class instance(geometry): self.mod.gds_write_file(self.gds) # now write an instance of my module/structure new_layout.addInstance(self.gds, - self.mod.name, + self.mod.cell_name, offsetInMicrons=self.offset, mirror=self.mirror, rotate=self.rotate) @@ -271,9 +271,9 @@ class instance(geometry): p.transform(self.offset, self.mirror, self.rotate) new_pins.append(p) return new_pins - + def calculate_transform(self, node): - #set up the rotation matrix + #set up the rotation matrix angle = math.radians(float(node.rotate)) mRotate = np.array([[math.cos(angle),-math.sin(angle),0.0], [math.sin(angle),math.cos(angle),0.0], @@ -285,7 +285,7 @@ class instance(geometry): mTranslate = np.array([[1.0,0.0,translateX], [0.0,1.0,translateY], [0.0,0.0,1.0]]) - + #set up the scale matrix (handles mirror X) scaleX = 1.0 if(node.mirror == 'MX'): @@ -295,7 +295,7 @@ class instance(geometry): mScale = np.array([[scaleX,0.0,0.0], [0.0,scaleY,0.0], [0.0,0.0,1.0]]) - + return (mRotate, mScale, mTranslate) def apply_transform(self, mtransforms, uVector, vVector, origin): @@ -312,13 +312,13 @@ class instance(geometry): def apply_path_transform(self, path): uVector = np.array([[1.0],[0.0],[0.0]]) vVector = np.array([[0.0],[1.0],[0.0]]) - origin = np.array([[0.0],[0.0],[1.0]]) + origin = np.array([[0.0],[0.0],[1.0]]) while(path): instance = path.pop(-1) mtransforms = self.calculate_transform(instance) (uVector, vVector, origin) = self.apply_transform(mtransforms, uVector, vVector, origin) - + return (uVector, vVector, origin) def reverse_transformation_bitcell(self, cell_name): @@ -339,7 +339,7 @@ class instance(geometry): cell_paths.append(copy.copy(path)) inst_name = path[-1].name - + # get the row and col names from the path row = int(path[-1].name.split('_')[-2][1:]) col = int(path[-1].name.split('_')[-1][1:]) @@ -349,7 +349,7 @@ class instance(geometry): normalized_storage_nets = node.mod.get_normalized_storage_nets_offset() (normalized_bl_offsets, normalized_br_offsets, bl_names, br_names) = node.mod.get_normalized_bitline_offset() - + for offset in range(len(normalized_bl_offsets)): for port in range(len(bl_names)): cell_bl_meta.append([bl_names[offset], row, col, port]) @@ -369,18 +369,18 @@ class instance(geometry): Q_bar_y = -1 * Q_bar_y for pair in range(len(normalized_bl_offsets)): - normalized_bl_offsets[pair] = (normalized_bl_offsets[pair][0], + normalized_bl_offsets[pair] = (normalized_bl_offsets[pair][0], -1 * normalized_bl_offsets[pair][1]) for pair in range(len(normalized_br_offsets)): - normalized_br_offsets[pair] = (normalized_br_offsets[pair][0], + normalized_br_offsets[pair] = (normalized_br_offsets[pair][0], -1 * normalized_br_offsets[pair][1]) - - Q_offsets.append([Q_x, Q_y]) + + Q_offsets.append([Q_x, Q_y]) Q_bar_offsets.append([Q_bar_x, Q_bar_y]) - - + + bl_offsets.append(normalized_bl_offsets) br_offsets.append(normalized_br_offsets) @@ -402,13 +402,13 @@ class instance(geometry): def __str__(self): """ override print function output """ - return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")" + return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.cell_name + " " + self.mirror + " R=" + str(self.rotate) + ")" def __repr__(self): """ override print function output """ - return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")" + return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.cell_name + " " + self.mirror + " R=" + str(self.rotate) + ")" + - class path(geometry): """Represents a Path""" diff --git a/compiler/base/graph_util.py b/compiler/base/graph_util.py index 6d4ee5da..d7afbf26 100644 --- a/compiler/base/graph_util.py +++ b/compiler/base/graph_util.py @@ -2,13 +2,13 @@ import copy from collections import defaultdict import debug - + class timing_graph(): """ Implements a directed graph Nodes are currently just Strings. """ - + def __init__(self): self.graph = defaultdict(set) self.all_paths = [] @@ -17,7 +17,7 @@ class timing_graph(): 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) @@ -25,99 +25,99 @@ class timing_graph(): def add_node(self, node): """Add node to graph with no edges""" - + node = node.lower() if node not 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 + + # Mark all the vertices as not visited visited = set() - - # Create an array to store paths + + # Create an array to store paths path = [] self.all_paths = [] - - # Call the recursive helper function to print 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 + + # 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 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 + # 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 + + # 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 at the last output, include the final output load if i == len(path) - 2: cout += load - + delays.append(path_edge_mod.analytical_delay(corner, cur_slew, cout)) cur_slew = delays[-1].slew - + return delays - + def __str__(self): """ override print function output """ @@ -132,4 +132,4 @@ class timing_graph(): """ override print function output """ return str(self) - + diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index b4b0ef72..28954834 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -20,9 +20,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): """ name_map = [] - def __init__(self, name): - self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds" - self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp" + def __init__(self, name, cell_name): + self.gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds" + self.sp_file = OPTS.openram_tech + "sp_lib/" + cell_name + ".sp" # If we have a separate lvs directory, then all the lvs files # should be in there (all or nothing!) @@ -33,7 +33,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): lvs_dir = OPTS.openram_tech + lvs_subdir + "/" if os.path.exists(lvs_dir): - self.lvs_file = lvs_dir + name + ".sp" + self.lvs_file = lvs_dir + cell_name + ".sp" else: self.lvs_file = self.sp_file @@ -41,8 +41,9 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.lvs_errors = "skipped" self.name = name - hierarchy_spice.spice.__init__(self, name) - hierarchy_layout.layout.__init__(self, name) + self.cell_name = cell_name + hierarchy_spice.spice.__init__(self, name, cell_name) + hierarchy_layout.layout.__init__(self, name, cell_name) self.init_graph_params() def get_layout_pins(self, inst): @@ -55,7 +56,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): debug.error("Couldn't find instance {0}".format(inst.name), -1) inst_map = inst.mod.pin_map return inst_map - + def DRC_LVS(self, final_verification=False, force_check=False): """Checks both DRC and LVS for a module""" import verify @@ -76,23 +77,23 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.lvs_write(tempspice) self.gds_write(tempgds) # Final verification option does not allow nets to be connected by label. - self.drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) - self.lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) + self.drc_errors = verify.run_drc(self.cell_name, tempgds, extract=True, final_verification=final_verification) + self.lvs_errors = verify.run_lvs(self.cell_name, tempgds, tempspice, final_verification=final_verification) # force_check is used to determine decoder height and other things, so we shouldn't fail # if that flag is set if OPTS.inline_lvsdrc and not force_check: debug.check(self.drc_errors == 0, - "DRC failed for {0} with {1} error(s)".format(self.name, + "DRC failed for {0} with {1} error(s)".format(self.cell_name, self.drc_errors)) debug.check(self.lvs_errors == 0, - "LVS failed for {0} with {1} errors(s)".format(self.name, + "LVS failed for {0} with {1} errors(s)".format(self.cell_name, self.lvs_errors)) if OPTS.purge_temp: os.remove(tempspice) os.remove(tempgds) - + def DRC(self, final_verification=False): """Checks DRC for a module""" import verify @@ -104,11 +105,11 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if OPTS.netlist_only: return elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): - tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) + tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.cell_name) self.gds_write(tempgds) - num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification) + num_errors = verify.run_drc(self.cell_name, tempgds, final_verification=final_verification) debug.check(num_errors == 0, - "DRC failed for {0} with {1} error(s)".format(self.name, + "DRC failed for {0} with {1} error(s)".format(self.cell_name, num_errors)) if OPTS.purge_temp: @@ -125,30 +126,30 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if OPTS.netlist_only: return elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): - tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) + tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.cell_name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.lvs_write(tempspice) self.gds_write(tempgds) num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) debug.check(num_errors == 0, - "LVS failed for {0} with {1} error(s)".format(self.name, + "LVS failed for {0} with {1} error(s)".format(self.cell_name, num_errors)) if OPTS.purge_temp: os.remove(tempspice) 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, @@ -162,7 +163,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): 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. @@ -195,7 +196,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): 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. @@ -211,13 +212,13 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): 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 """ 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 = "********** design {0} **********".format(self.cell_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)) @@ -231,4 +232,4 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): for i in self.insts: text+=str(i) + ",\n" return text - + diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 7fe838cb..67482f61 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -13,6 +13,7 @@ from tech import drc, GDS from tech import layer as techlayer from tech import layer_indices from tech import layer_stacks +from tech import preferred_directions import os from globals import OPTS from vector import vector @@ -30,8 +31,9 @@ class layout(): layout/netlist and perform LVS/DRC. """ - def __init__(self, name): + def __init__(self, name, cell_name): self.name = name + self.cell_name = cell_name self.width = None self.height = None self.bounding_box = None @@ -66,13 +68,13 @@ class layout(): def offset_x_coordinates(self): """ This function is called after everything is placed to - shift the origin to the furthest left point. + shift the origin to the furthest left point. Y offset is unchanged. """ offset = self.find_lowest_coords() self.translate_all(offset.scale(1, 0)) return offset - + def get_gate_offset(self, x_offset, height, inv_num): """ Gets the base offset and y orientation of stacked rows of gates @@ -214,7 +216,7 @@ class layout(): # Contacts are not really instances, so skip them if "contact" not in mod.name: # Check that the instance name is unique - debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.name, name)) + debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.cell_name, name)) self.inst_names.add(name) self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) @@ -299,9 +301,9 @@ class layout(): tx_list.append(i) except AttributeError: pass - + return tx_list - + def get_pin(self, text): """ Return the pin or list of pins @@ -315,7 +317,7 @@ class layout(): return any_pin except Exception: self.gds_write("missing_pin.gds") - debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.name), -1) + debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.cell_name), -1) def get_pins(self, text): """ @@ -612,24 +614,24 @@ class layout(): next_id = 0 curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None) - + via = self.add_via_center(layers=curr_stack, size=size, offset=offset, directions=directions, implant_type=implant_type, well_type=well_type) - + if cur_layer != from_layer: self.add_min_area_rect_center(cur_layer, offset, via.mod.first_layer_width, via.mod.first_layer_height) - + cur_layer = curr_stack[next_id] return via - + def add_min_area_rect_center(self, layer, offset, @@ -643,14 +645,14 @@ class layout(): min_area = drc("minarea_{}".format(layer)) if min_area == 0: return - + min_width = drc("minwidth_{}".format(layer)) - + if preferred_directions[layer] == "V": height = max(min_area / width, min_width) else: width = max(min_area / height, min_width) - + self.add_rect_center(layer=layer, offset=offset, width=width, @@ -734,7 +736,7 @@ class layout(): height = boundary[1][1] - boundary[0][1] width = boundary[1][0] - boundary[0][0] - + for boundary_layer in boundary_layers: (layer_number, layer_purpose) = techlayer[boundary_layer] gds_layout.addBox(layerNumber=layer_number, @@ -886,7 +888,7 @@ class layout(): new_pin = pin_layout(names[i], [rect.ll(), rect.ur()], layer) - + pins[names[i]] = new_pin else: for i in range(len(names)): @@ -904,7 +906,7 @@ class layout(): new_pin = pin_layout(names[i], [rect.ll(), rect.ur()], layer) - + pins[names[i]] = new_pin return pins @@ -1042,7 +1044,7 @@ class layout(): cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self) self.add_inst(cr.name, cr) self.connect_inst([]) - + def add_boundary(self, ll=vector(0, 0), ur=None): """ Add boundary for debugging dimensions """ if OPTS.netlist_only: @@ -1107,7 +1109,7 @@ class layout(): width=xmax - xmin, height=ymax - ymin) return rect - + def copy_power_pins(self, inst, name, add_vias=True): """ This will copy a power pin if it is on the lowest power_grid layer. @@ -1166,7 +1168,7 @@ class layout(): bottom = ll.y right = ur.x top = ur.y - + pin_loc = pin.center() if side == "left": peri_pin_loc = vector(left, pin_loc.y) @@ -1184,14 +1186,14 @@ class layout(): self.add_via_stack_center(from_layer=pin.layer, to_layer=layer, offset=pin_loc) - + self.add_path(layer, [pin_loc, peri_pin_loc]) return self.add_layout_pin_rect_center(text=name, layer=layer, offset=peri_pin_loc) - + def add_power_ring(self, bbox): """ Create vdd and gnd power rings around an area of the bounding box diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 08e2b474..d971492e 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -10,6 +10,7 @@ import re import os import math import tech +from pprint import pformat from delay_data import delay_data from wire_spice_model import wire_spice_model from power_data import power_data @@ -26,8 +27,9 @@ class spice(): Class consisting of a set of modules and instances of these modules """ - def __init__(self, name): + def __init__(self, name, cell_name): self.name = name + self.cell_name = cell_name self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"] # Holds subckts/mods for this module @@ -63,7 +65,7 @@ class spice(): 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) @@ -82,7 +84,7 @@ class spice(): "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, @@ -104,7 +106,7 @@ class spice(): \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. """ pin_type = self.pin_type[name] @@ -118,7 +120,7 @@ class spice(): return "INOUT" else: return self.pin_type[name] - + def get_inputs(self): """ These use pin types to determine pin lists. These may be over-ridden by submodules that didn't use pin directions yet.""" @@ -164,7 +166,6 @@ class spice(): num_pins = len(self.insts[-1].mod.pins) num_args = len(args) if (check and num_pins != num_args): - from pprint import pformat if num_pins < num_args: mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins) arg_pins = args @@ -181,10 +182,9 @@ class spice(): self.conns.append(args) if check and (len(self.insts)!=len(self.conns)): - from pprint import pformat insts_string=pformat(self.insts) conns_string=pformat(self.conns) - + debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, len(self.insts), len(self.conns))) @@ -214,7 +214,7 @@ class spice(): f.close() # find the correct subckt line in the file - subckt = re.compile("^.subckt {}".format(self.name), re.IGNORECASE) + subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE) subckt_line = list(filter(subckt.search, self.spice))[0] # parses line into ports and remove subckt self.pins = subckt_line.split(" ")[2:] @@ -234,12 +234,12 @@ class spice(): # pins and subckt should be the same # find the correct subckt line in the file - subckt = re.compile("^.subckt {}".format(self.name), re.IGNORECASE) + subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE) subckt_line = list(filter(subckt.search, self.lvs))[0] # parses line into ports and remove subckt lvs_pins = subckt_line.split(" ")[2:] debug.check(lvs_pins == self.pins, "LVS and spice file pin mismatch.") - + 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. @@ -255,14 +255,14 @@ class spice(): 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: @@ -279,7 +279,7 @@ class spice(): return elif not self.spice: # If spice isn't defined, we dynamically generate one. - + # recursively write the modules for i in self.mods: if self.contains(i, usedMODS): @@ -293,18 +293,18 @@ class spice(): return # write out the first spice line (the subcircuit) - sp.write("\n.SUBCKT {0} {1}\n".format(self.name, + sp.write("\n.SUBCKT {0} {1}\n".format(self.cell_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, + debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.cell_name, len(self.insts), len(self.conns))) debug.error("Instances: \n" + str(self.insts)) @@ -330,9 +330,9 @@ class spice(): else: sp.write("X{0} {1} {2}\n".format(self.insts[i].name, " ".join(self.conns[i]), - self.insts[i].mod.name)) + self.insts[i].mod.cell_name)) - sp.write(".ENDS {0}\n".format(self.name)) + sp.write(".ENDS {0}\n".format(self.cell_name)) else: # If spice is a hard module, output the spice file contents. @@ -343,7 +343,7 @@ class spice(): sp.write("\n".join(self.lvs)) else: sp.write("\n".join(self.spice)) - + sp.write("\n") def sp_write(self, spname): @@ -365,19 +365,19 @@ class spice(): self.sp_write_file(spfile, usedMODS, True) del usedMODS spfile.close() - + def analytical_delay(self, corner, slew, load=0.0): """Inform users undefined delay module while building new modules""" - + # 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 not stage_effort: 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 @@ -390,27 +390,27 @@ class spice(): .format(self.__class__.__name__)) debug.warning("Class {0} name {1}" .format(self.__class__.__name__, - self.name)) + self.cell_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)) + self.cell_name)) return 0 - + def cal_delay_with_rc(self, corner, r, c, slew, swing=0.5): """ Calculate the delay of a mosfet by @@ -420,7 +420,7 @@ class spice(): delay = swing_factor * r * c # c is in ff and delay is in fs 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 # as 0.005* slew. @@ -439,7 +439,7 @@ class spice(): 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.""" @@ -452,13 +452,13 @@ class spice(): elif mos_proc == 'S': proc_factors.append(1.1) return proc_factors - + def get_voltage_delay_factor(self, voltage): """Returns delay increase due to voltage. Implemented as linear factor based off nominal voltage. """ return tech.spice["nom_supply_voltage"] / voltage - + def get_temp_delay_factor(self, temp): """Returns delay increase due to temperature (in C). Determines effect on threshold voltage and then linear factor is estimated. @@ -478,7 +478,7 @@ class spice(): def generate_rc_net(self, lump_num, wire_length, wire_width): return wire_spice_model(lump_num, wire_length, wire_width) - + def calc_dynamic_power(self, corner, c, freq, swing=1.0): """ Calculate dynamic power using effective capacitance, frequency, and corner (PVT) @@ -486,16 +486,16 @@ class spice(): proc, vdd, temp = corner net_vswing = vdd * swing power_dyn = c * vdd * net_vswing * freq - + # A pply process and temperature factors. # Roughly, process and Vdd affect the delay which affects the power. # No other estimations are currently used. Increased delay->slower freq.->less power proc_div = max(self.get_process_delay_factor(proc)) temp_div = self.get_temp_delay_factor(temp) power_dyn = power_dyn / (proc_div * temp_div) - + return power_dyn - + def return_power(self, dynamic=0.0, leakage=0.0): return power_data(dynamic, leakage) @@ -519,7 +519,7 @@ class spice(): 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). @@ -541,7 +541,7 @@ class spice(): 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. diff --git a/compiler/base/lef.py b/compiler/base/lef.py index af539742..7cd11a93 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -41,7 +41,7 @@ class lef: self.lef_write_obstructions() self.lef_write_footer() self.lef.close() - + def lef_write_header(self): """ Header of LEF file """ self.lef.write("VERSION 5.4 ;\n") @@ -51,7 +51,7 @@ 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("{0}MACRO {1}\n".format(self.indent,self.name)) self.indent += " " self.lef.write("{0}CLASS BLOCK ;\n".format(self.indent)) @@ -59,25 +59,25 @@ class lef: round(self.width,self.round_grid), round(self.height,self.round_grid))) self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent)) - + def lef_write_footer(self): self.lef.write("{0}END {1}\n".format(self.indent,self.name)) self.indent = self.indent[:-3] self.lef.write("END LIBRARY\n") - - + + def lef_write_pin(self, name): pin_dir = self.get_pin_dir(name) pin_type = self.get_pin_type(name) self.lef.write("{0}PIN {1}\n".format(self.indent,name)) self.indent += " " - + self.lef.write("{0}DIRECTION {1} ;\n".format(self.indent,pin_dir)) - + if pin_type in ["POWER","GROUND"]: self.lef.write("{0}USE {1} ; \n".format(self.indent,pin_type)) self.lef.write("{0}SHAPE ABUTMENT ; \n".format(self.indent)) - + self.lef.write("{0}PORT\n".format(self.indent)) self.indent += " " @@ -86,7 +86,7 @@ class lef: for pin in pin_list: self.lef.write("{0}LAYER {1} ;\n".format(self.indent,pin.layer)) self.lef_write_shape(pin.rect) - + # End the PORT self.indent = self.indent[:-3] self.lef.write("{0}END\n".format(self.indent)) @@ -94,7 +94,7 @@ class lef: # End the PIN self.indent = self.indent[:-3] self.lef.write("{0}END {1}\n".format(self.indent,name)) - + def lef_write_obstructions(self): """ Write all the obstructions on each layer """ self.lef.write("{0}OBS\n".format(self.indent)) @@ -111,16 +111,16 @@ class lef: self.lef.write("{0}END\n".format(self.indent)) def lef_write_shape(self, rect): - if len(rect) == 2: + if len(rect) == 2: """ Write a LEF rectangle """ - self.lef.write("{0}RECT ".format(self.indent)) + 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: + else: """ Write a LEF polygon """ - self.lef.write("{0}POLYGON ".format(self.indent)) + 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)): diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index dac4e525..31a47671 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -33,7 +33,7 @@ class pin_layout: # These are the valid pin layers valid_layers = { x: layer[x] for x in layer_indices.keys()} - + # if it's a string, use the name if type(layer_name_pp) == str: self._layer = layer_name_pp @@ -378,7 +378,7 @@ class pin_layout: from tech import label_purpose except ImportError: label_purpose = purpose - + newLayout.addBox(layerNumber=layer_num, purposeNumber=pin_purpose, offsetInMicrons=self.ll(), diff --git a/compiler/base/route.py b/compiler/base/route.py index 4e3d8a60..d1caf612 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -14,12 +14,12 @@ from vector3d import vector3d from sram_factory import factory class route(design): - """ + """ Object route (used by the router module) Add a route of minimium metal width between a set of points. - The widths are the layer widths of the layer stack. + The widths are the layer widths of the layer stack. (Vias are in numer of vias.) - The wire must be completely rectilinear and the + The wire must be completely rectilinear and the z-dimension of the points refers to the layers. The points are the center of the wire. This can have non-preferred direction routing. @@ -45,12 +45,12 @@ class route(design): def setup_layers(self): (self.horiz_layer_name, self.via_layer, self.vert_layer_name) = self.layer_stack (self.horiz_layer_width, self.num_vias, self.vert_layer_width) = self.layer_widths - + if not self.vert_layer_width: self.vert_layer_width = drc("minwidth_{0}".format(self.vert_layer_name)) if not self.horiz_layer_width: self.horiz_layer_width = drc("minwidth_{0}".format(self.horiz_layer_name)) - + # offset this by 1/2 the via size self.c=factory.create(module_type="contact", layer_stack=self.layer_stack, @@ -58,7 +58,7 @@ class route(design): def create_wires(self): - """ + """ Add the wire segments of the route. """ @@ -67,7 +67,7 @@ class route(design): a, b = tee(iterable) next(b, None) return zip(a, b) - + plist = list(pairwise(self.path)) for p0,p1 in plist: if p0.z != p1.z: # via @@ -87,10 +87,10 @@ class route(design): self.draw_corner_wire(plist[0][0]) self.draw_corner_wire(plist[-1][1]) - + def get_layer_width(self, layer_zindex): """ - Return the layer width + Return the layer width """ if layer_zindex==0: return self.horiz_layer_width @@ -109,11 +109,11 @@ class route(design): return self.vert_layer_name else: debug.error("Incorrect layer zindex.",-1) - + def draw_wire(self, p0, p1): """ - This draws a straight wire with layer_minwidth + This draws a straight wire with layer_minwidth """ layer_width = self.get_layer_width(p0.z) @@ -145,8 +145,8 @@ class route(design): offset=vector(offset.x,offset.y), width=width, height=height) - - + + def draw_corner_wire(self, p0): """ This function adds the corner squares since the center line convention only draws to the center of the corner.""" diff --git a/compiler/base/utils.py b/compiler/base/utils.py index b476e778..ed016964 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -4,10 +4,12 @@ # of Regents for the Oklahoma Agricultural and Mechanical College # (acting for and on behalf of Oklahoma State University) # All rights reserved. -# + +import os +import math + import gdsMill import tech -import math import globals import debug from vector import vector @@ -57,10 +59,11 @@ def auto_measure_libcell(pin_list, name, units, lpp): Return these as a set of properties including the cell width/height too. """ cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" - cell_vlsi = gdsMill.VlsiLayout(units=units) - reader = gdsMill.Gds2reader(cell_vlsi) - reader.loadFromFile(cell_gds) + cell_vlsi = _get_gds_reader(units, cell_gds) + + # FIXME: This duplicates a lot of functionality of get_gds_size and + # get_gds_pins, it should probably just call those functions? cell = {} measure_result = cell_vlsi.getLayoutBorder(lpp[0]) if measure_result: @@ -73,22 +76,47 @@ def auto_measure_libcell(pin_list, name, units, lpp): return cell +_GDS_READER_CACHE = {} + + +def _get_gds_reader(units, gds_filename): + gds_absname = os.path.realpath(gds_filename) + k = (units, gds_absname) + try: + return _GDS_READER_CACHE[k] + except KeyError: + debug.info(4, "Creating VLSI layout from {}".format(gds_absname)) + cell_vlsi = gdsMill.VlsiLayout(units=units) + reader = gdsMill.Gds2reader(cell_vlsi) + reader.loadFromFile(gds_absname) + + _GDS_READER_CACHE[k] = cell_vlsi + return cell_vlsi + + +_GDS_SIZE_CACHE = {} + + def get_gds_size(name, gds_filename, units, lpp): """ Open a GDS file and return the size from either the bounding box or a border layer. """ - debug.info(4, "Creating VLSI layout for {}".format(name)) - cell_vlsi = gdsMill.VlsiLayout(units=units) - reader = gdsMill.Gds2reader(cell_vlsi) - reader.loadFromFile(gds_filename) + k = (name, os.path.realpath(gds_filename), units, lpp) + try: + return _GDS_SIZE_CACHE[k] + except KeyError: + cell_vlsi = _get_gds_reader(units, gds_filename) - measure_result = cell_vlsi.getLayoutBorder(lpp) - if not measure_result: - debug.info(2, "Layout border failed. Trying to measure size for {}".format(name)) - measure_result = cell_vlsi.measureSize(name) - # returns width,height - return measure_result + measure_result = cell_vlsi.getLayoutBorder(lpp) + if not measure_result: + debug.info(2, "Layout border failed. Trying to measure size for {}".format(name)) + measure_result = cell_vlsi.measureSize(name) + + _GDS_SIZE_CACHE[k] = measure_result + + # returns width,height + return measure_result def get_libcell_size(name, units, lpp): @@ -101,27 +129,34 @@ def get_libcell_size(name, units, lpp): return(get_gds_size(name, cell_gds, units, lpp)) +_GDS_PINS_CACHE = {} + + def get_gds_pins(pin_names, name, gds_filename, units): """ Open a GDS file and find the pins in pin_names as text on a given layer. Return these as a rectangle layer pair for each pin. """ - cell_vlsi = gdsMill.VlsiLayout(units=units) - reader = gdsMill.Gds2reader(cell_vlsi) - reader.loadFromFile(gds_filename) + k = (tuple(pin_names), name, os.path.realpath(gds_filename), units) + try: + return dict(_GDS_PINS_CACHE[k]) + except KeyError: + cell_vlsi = _get_gds_reader(units, gds_filename) - cell = {} - for pin_name in pin_names: - cell[str(pin_name)] = [] - pin_list = cell_vlsi.getPinShape(str(pin_name)) - for pin_shape in pin_list: - (lpp, boundary) = pin_shape - rect = [vector(boundary[0], boundary[1]), - vector(boundary[2], boundary[3])] - # this is a list because other cells/designs - # may have must-connect pins - cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp)) - return cell + cell = {} + for pin_name in pin_names: + cell[str(pin_name)] = [] + pin_list = cell_vlsi.getPinShape(str(pin_name)) + for pin_shape in pin_list: + (lpp, boundary) = pin_shape + rect = [vector(boundary[0], boundary[1]), + vector(boundary[2], boundary[3])] + # this is a list because other cells/designs + # may have must-connect pins + cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp)) + + _GDS_PINS_CACHE[k] = cell + return dict(cell) def get_libcell_pins(pin_list, name, units): @@ -132,7 +167,3 @@ def get_libcell_pins(pin_list, name, units): cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" return(get_gds_pins(pin_list, name, cell_gds, units)) - - - - diff --git a/compiler/base/vector.py b/compiler/base/vector.py index 582b9391..356ef42b 100644 --- a/compiler/base/vector.py +++ b/compiler/base/vector.py @@ -39,8 +39,8 @@ class vector(): return "v["+str(self.x)+","+str(self.y)+"]" def __setitem__(self, index, value): - """ - override setitem function + """ + override setitem function can set value by vector[index]=value """ if index==0: @@ -50,10 +50,10 @@ class vector(): else: self.x=float(value[0]) self.y=float(value[1]) - + def __getitem__(self, index): """ - override getitem function + override getitem function can get value by value=vector[index] """ if index==0: @@ -61,7 +61,7 @@ class vector(): elif index==1: return self.y else: - return self + return self def __add__(self, other): """ @@ -109,7 +109,7 @@ class vector(): """ Changes the coodrinate to match the grid settings """ - grid = tech.drc["grid"] + grid = tech.drc["grid"] # this gets the nearest integer value off_in_grid = int(round(round((offset / grid), 2), 0)) offset = off_in_grid * grid @@ -150,8 +150,8 @@ class vector(): Override round function """ return vector(int(round(self.x)),int(round(self.y))) - - + + def __eq__(self, other): """Override the default Equals behavior""" if isinstance(other, self.__class__): diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index aadf602c..636e3bd6 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -9,13 +9,13 @@ import debug import math class verilog: - """ + """ Create a behavioral Verilog file for simulation. This is inherited by the sram_base class. """ def __init__(self): pass - + def verilog_write(self,verilog_name): """ Write a behavioral Verilog model. """ self.vf = open(verilog_name, "w") @@ -67,7 +67,7 @@ class verilog: self.add_inputs_outputs(port) self.vf.write("\n") - + for port in self.all_ports: self.register_inputs(port) @@ -79,8 +79,8 @@ class verilog: self.add_write_block(port) if port in self.read_ports: self.add_read_block(port) - - self.vf.write("\n") + + self.vf.write("\n") self.vf.write("endmodule\n") self.vf.close() @@ -91,9 +91,9 @@ class verilog: """ self.add_regs(port) self.add_flops(port) - + def add_regs(self, port): - """ + """ Create the input regs for the given port. """ self.vf.write(" reg csb{0}_reg;\n".format(port)) @@ -107,7 +107,7 @@ class verilog: 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)) - + def add_flops(self, port): """ Add the flop behavior logic for a port. @@ -125,7 +125,7 @@ class verilog: self.vf.write(" addr{0}_reg = addr{0};\n".format(port)) if port in self.read_ports: self.add_write_read_checks(port) - + if port in self.write_ports: self.vf.write(" din{0}_reg = din{0};\n".format(port)) if port in self.read_ports: @@ -150,7 +150,7 @@ class verilog: 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") - + def add_inputs_outputs(self, port): """ @@ -203,7 +203,7 @@ class verilog: 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): """ Add a read port block. @@ -231,12 +231,12 @@ class verilog: wport_control = "!csb{0} && !web{0}".format(wport) else: wport_control = "!csb{0}".format(wport) - + self.vf.write(" if ({1} && {3} && (addr{0} == addr{2}))\n".format(wport,wport_control,rport,rport_control)) self.vf.write(" $display($time,\" WARNING: Writing and reading addr{0}=%b and addr{1}=%b simultaneously!\",addr{0},addr{1});\n".format(wport,rport)) def add_write_read_checks(self, rport): - """ + """ Add a warning if we read from an address that we are currently writing. Can be fixed if we appropriately size the write drivers to do this . """ diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 9c8dc4e6..67026a5b 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -58,13 +58,13 @@ class wire(wire_path): via_connect.first_layer_width) self.horiz_layer_contact_width = max(via_connect.second_layer_height, via_connect.first_layer_height) - + self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width, drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height] self.pitch = self.compute_pitch(self.layer_stack) def compute_pitch(self, layer_stack): - + """ This is contact direction independent pitch, i.e. we take the maximum contact dimension @@ -79,13 +79,13 @@ class wire(wire_path): except AttributeError: contact1 = getattr(contact, layer2 + "_via") max_contact = max(contact1.width, contact1.height) - + layer1_space = drc("{0}_to_{0}".format(layer1)) layer2_space = drc("{0}_to_{0}".format(layer2)) pitch = max_contact + max(layer1_space, layer2_space) return pitch - + # create a 1x1 contact def create_vias(self): """ Add a via and corner square at every corner of the path.""" diff --git a/compiler/base/wire_path.py b/compiler/base/wire_path.py index 31d0ae78..b4f81363 100644 --- a/compiler/base/wire_path.py +++ b/compiler/base/wire_path.py @@ -15,7 +15,7 @@ def create_rectilinear_route(my_list): """ Add intermediate nodes if it isn't rectilinear. Also skip repeated nodes. Also, convert to vector if the aren't.""" pl = [snap_to_grid(x) for x in my_list] - + my_list = [] for index in range(len(pl) - 1): if pl[index] != pl[index + 1]: @@ -121,7 +121,7 @@ class wire_path(): """ width = layer_width - height = length + height = length if orientation == "horizontal": width = length diff --git a/compiler/base/wire_spice_model.py b/compiler/base/wire_spice_model.py index 5624b575..9a73134b 100644 --- a/compiler/base/wire_spice_model.py +++ b/compiler/base/wire_spice_model.py @@ -12,7 +12,7 @@ class wire_spice_model(): """ 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_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): @@ -36,7 +36,7 @@ class wire_spice_model(): 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 + 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 5a987ca4..fb5e4929 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -6,9 +6,8 @@ # All rights reserved. # import debug -import utils -from tech import GDS, layer from tech import cell_properties as props +from globals import OPTS import bitcell_base @@ -20,8 +19,6 @@ class bitcell(bitcell_base.bitcell_base): library. """ - # If we have a split WL bitcell, if not be backwards - # compatible in the tech file pin_names = [props.bitcell.cell_6t.pin.bl, props.bitcell.cell_6t.pin.br, props.bitcell.cell_6t.pin.wl, @@ -30,20 +27,12 @@ class bitcell(bitcell_base.bitcell_base): type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] storage_nets = ['Q', 'Q_bar'] - (width, height) = utils.get_libcell_size("cell_6t", - GDS["unit"], - layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) - - def __init__(self, name=""): - # Ignore the name argument - bitcell_base.bitcell_base.__init__(self, "cell_6t") + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = OPTS.bitcell_name + super().__init__(name, cell_name) debug.info(2, "Create bitcell") - 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 get_all_wl_names(self): diff --git a/compiler/bitcells/bitcell_1rw_1r.py b/compiler/bitcells/bitcell_1rw_1r.py index 820adcce..a22e511b 100644 --- a/compiler/bitcells/bitcell_1rw_1r.py +++ b/compiler/bitcells/bitcell_1rw_1r.py @@ -6,11 +6,9 @@ # All rights reserved. # import debug -import utils -from tech import GDS, layer, parameter, drc from tech import cell_properties as props -import logical_effort import bitcell_base +from globals import OPTS class bitcell_1rw_1r(bitcell_base.bitcell_base): @@ -29,33 +27,25 @@ class bitcell_1rw_1r(bitcell_base.bitcell_base): props.bitcell.cell_1rw1r.pin.wl1, props.bitcell.cell_1rw1r.pin.vdd, props.bitcell.cell_1rw1r.pin.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"]) - def __init__(self, name=""): - # Ignore the name argument - bitcell_base.bitcell_base.__init__(self, "cell_1rw_1r") + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = OPTS.bitcell_name + super().__init__(name, cell_name) debug.info(2, "Create bitcell with 1RW and 1R Port") - self.width = bitcell_1rw_1r.width - self.height = bitcell_1rw_1r.height - self.pin_map = bitcell_1rw_1r.pin_map - self.add_pin_types(self.type_list) self.nets_match = self.do_nets_exist(self.storage_nets) - pin_names = bitcell_1rw_1r.pin_names + pin_names = self.pin_names self.bl_names = [pin_names[0], pin_names[2]] self.br_names = [pin_names[1], pin_names[3]] self.wl_names = [pin_names[4], pin_names[5]] 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 """ diff --git a/compiler/bitcells/bitcell_1w_1r.py b/compiler/bitcells/bitcell_1w_1r.py index 56bd0b45..02b6e768 100644 --- a/compiler/bitcells/bitcell_1w_1r.py +++ b/compiler/bitcells/bitcell_1w_1r.py @@ -6,10 +6,9 @@ # All rights reserved. # import debug -import utils -from tech import GDS, layer from tech import cell_properties as props import bitcell_base +from globals import OPTS class bitcell_1w_1r(bitcell_base.bitcell_base): @@ -31,28 +30,20 @@ class bitcell_1w_1r(bitcell_base.bitcell_base): 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"]) - def __init__(self, name=""): - # Ignore the name argument - bitcell_base.bitcell_base.__init__(self, "cell_1w_1r") + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = OPTS.bitcell_name + super().__init__(name, cell_name) debug.info(2, "Create bitcell with 1W and 1R Port") - 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) - pin_names = bitcell_1w_1r.pin_names + pin_names = self.pin_names self.bl_names = [pin_names[0], pin_names[2]] self.br_names = [pin_names[1], pin_names[3]] self.wl_names = [pin_names[4], pin_names[5]] - def get_bitcell_pins(self, col, row): """ Creates a list of connections in the bitcell, diff --git a/compiler/bitcells/bitcell_base.py b/compiler/bitcells/bitcell_base.py index 690e98fa..f61805da 100644 --- a/compiler/bitcells/bitcell_base.py +++ b/compiler/bitcells/bitcell_base.py @@ -8,17 +8,29 @@ import debug import design +import utils from globals import OPTS import logical_effort -from tech import parameter, drc, layer +from tech import GDS, parameter, drc, layer class bitcell_base(design.design): """ Base bitcell parameters to be over-riden. """ - def __init__(self, name): - design.design.__init__(self, name) + cell_size_layer = "boundary" + + def __init__(self, name, cell_name, hard_cell=True): + design.design.__init__(self, name, cell_name) + + if hard_cell: + (self.width, self.height) = utils.get_libcell_size(cell_name, + GDS["unit"], + layer[self.cell_size_layer]) + self.pin_map = utils.get_libcell_pins(self.pin_names, + cell_name, + GDS["unit"]) + self.add_pin_types(self.type_list) def get_stage_effort(self, load): parasitic_delay = 1 @@ -49,13 +61,13 @@ class bitcell_base(design.design): def input_load(self): """ Return the relative capacitance of the access transistor gates """ - + # 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_wl_cin(self): """Return the relative capacitance of the access transistor gates""" # This is a handmade cell so the value must be entered @@ -82,7 +94,7 @@ class bitcell_base(design.design): def get_storage_net_offset(self): """ - Gets the location of the storage net labels to add top level + Gets the location of the storage net labels to add top level labels for pex simulation. """ # If we generated the bitcell, we already know where Q and Q_bar are @@ -92,7 +104,7 @@ class bitcell_base(design.design): for text in self.gds.getTexts(layer["m1"]): if self.storage_nets[i] == text.textString.rstrip('\x00'): self.storage_net_offsets.append(text.coordinates[0]) - + for i in range(len(self.storage_net_offsets)): self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]]) @@ -116,7 +128,7 @@ class bitcell_base(design.design): if bl_names[i] == text.textString.rstrip('\x00'): self.bl_offsets.append(text.coordinates[0]) found_bl.append(bl_names[i]) - + continue for i in range(len(br_names)): @@ -131,16 +143,16 @@ class bitcell_base(design.design): self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]]) for i in range(len(self.br_offsets)): - self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]]) + self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]]) return(self.bl_offsets, self.br_offsets, found_bl, found_br) - def get_normalized_storage_nets_offset(self): + def get_normalized_storage_nets_offset(self): """ Convert storage net offset to be relative to the bottom left corner - of the bitcell. This is useful for making sense of offsets outside + of the bitcell. This is useful for making sense of offsets outside of the bitcell. - """ + """ if OPTS.bitcell is not "pbitcell": normalized_storage_net_offset = self.get_storage_net_offset() diff --git a/compiler/bitcells/col_cap_bitcell_1rw_1r.py b/compiler/bitcells/col_cap_bitcell_1rw_1r.py index 01818a12..1f2a30d0 100644 --- a/compiler/bitcells/col_cap_bitcell_1rw_1r.py +++ b/compiler/bitcells/col_cap_bitcell_1rw_1r.py @@ -6,8 +6,6 @@ # All rights reserved. # import debug -import utils -from tech import GDS, layer from tech import cell_properties as props import bitcell_base @@ -21,24 +19,14 @@ class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base): props.bitcell.cell_1rw1r.pin.bl1, props.bitcell.cell_1rw1r.pin.br1, props.bitcell.cell_1rw1r.pin.vdd] - type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "POWER", "GROUND"] - (width, height) = utils.get_libcell_size("col_cap_cell_1rw_1r", - GDS["unit"], - layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, - "col_cap_cell_1rw_1r", - GDS["unit"]) - - def __init__(self, name=""): + def __init__(self, name="col_cap_cell_1rw_1r", cell_name=None): + if not cell_name: + cell_name = name # Ignore the name argument - bitcell_base.bitcell_base.__init__(self, "col_cap_cell_1rw_1r") + bitcell_base.bitcell_base.__init__(self, name, cell_name) debug.info(2, "Create col_cap bitcell 1rw+1r object") - self.width = col_cap_bitcell_1rw_1r.width - self.height = col_cap_bitcell_1rw_1r.height - self.pin_map = col_cap_bitcell_1rw_1r.pin_map - self.add_pin_types(self.type_list) self.no_instances = True diff --git a/compiler/bitcells/dummy_bitcell.py b/compiler/bitcells/dummy_bitcell.py index 116ea3ed..2a3d74ae 100644 --- a/compiler/bitcells/dummy_bitcell.py +++ b/compiler/bitcells/dummy_bitcell.py @@ -6,10 +6,9 @@ # All rights reserved. # import debug -import utils -from tech import GDS, layer from tech import cell_properties as props import bitcell_base +from globals import OPTS class dummy_bitcell(bitcell_base.bitcell_base): @@ -24,19 +23,12 @@ class dummy_bitcell(bitcell_base.bitcell_base): props.bitcell.cell_6t.pin.wl, props.bitcell.cell_6t.pin.vdd, props.bitcell.cell_6t.pin.gnd] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - (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 - bitcell_base.bitcell_base.__init__(self, "dummy_cell_6t") + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = OPTS.dummy_bitcell_name + super().__init__(name, cell_name) debug.info(2, "Create dummy bitcell") - self.width = dummy_bitcell.width - self.height = dummy_bitcell.height - self.pin_map = dummy_bitcell.pin_map - diff --git a/compiler/bitcells/dummy_bitcell_1rw_1r.py b/compiler/bitcells/dummy_bitcell_1rw_1r.py index d29c804f..0fb92103 100644 --- a/compiler/bitcells/dummy_bitcell_1rw_1r.py +++ b/compiler/bitcells/dummy_bitcell_1rw_1r.py @@ -6,10 +6,9 @@ # All rights reserved. # import debug -import utils -from tech import GDS, layer from tech import cell_properties as props import bitcell_base +from globals import OPTS class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base): @@ -27,23 +26,13 @@ class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base): props.bitcell.cell_1rw1r.pin.wl1, props.bitcell.cell_1rw1r.pin.vdd, props.bitcell.cell_1rw1r.pin.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 - bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r") + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = OPTS.dummy_bitcell_name + super().__init__(name, cell_name) 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) diff --git a/compiler/bitcells/dummy_bitcell_1w_1r.py b/compiler/bitcells/dummy_bitcell_1w_1r.py index 758a5b16..1f3fb754 100644 --- a/compiler/bitcells/dummy_bitcell_1w_1r.py +++ b/compiler/bitcells/dummy_bitcell_1w_1r.py @@ -6,10 +6,9 @@ # All rights reserved. # import debug -import utils -from tech import GDS, layer from tech import cell_properties as props import bitcell_base +from globals import OPTS class dummy_bitcell_1w_1r(bitcell_base.bitcell_base): @@ -29,21 +28,13 @@ class dummy_bitcell_1w_1r(bitcell_base.bitcell_base): props.bitcell.cell_1w1r.pin.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 - bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r") + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = OPTS.dummy_bitcell_name + super().__init__(name, cell_name) 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) + diff --git a/compiler/bitcells/dummy_pbitcell.py b/compiler/bitcells/dummy_pbitcell.py index ead3d7f3..4ece44c4 100644 --- a/compiler/bitcells/dummy_pbitcell.py +++ b/compiler/bitcells/dummy_pbitcell.py @@ -7,85 +7,88 @@ # 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): + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = 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) + + design.design.__init__(self, name, cell_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() self.add_boundary() - + 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.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("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): + 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, "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 diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 25868df5..e2cdd032 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -21,16 +21,18 @@ class pbitcell(bitcell_base.bitcell_base): with a variable number of read/write, write, and read ports """ - def __init__(self, name, replica_bitcell=False, dummy_bitcell=False): + def __init__(self, name, cell_name=None, replica_bitcell=False, dummy_bitcell=False): + if not cell_name: + cell_name = 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 - + self.replica_bitcell = replica_bitcell self.dummy_bitcell = dummy_bitcell - bitcell_base.bitcell_base.__init__(self, name) + bitcell_base.bitcell_base.__init__(self, name, cell_name, hard_cell=False) fmt_str = "{0} rw ports, {1} w ports and {2} r ports" info_string = fmt_str.format(self.num_rw_ports, self.num_w_ports, @@ -295,7 +297,7 @@ class pbitcell(bitcell_base.bitcell_base): 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 @@ -322,7 +324,7 @@ class pbitcell(bitcell_base.bitcell_base): 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 """ @@ -406,7 +408,7 @@ class pbitcell(bitcell_base.bitcell_base): contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \ + 0.5 * contact.poly.height, self.cross_couple_upper_ypos) - + contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \ - 0.5*contact.poly.height, self.cross_couple_lower_ypos) @@ -421,8 +423,8 @@ class pbitcell(bitcell_base.bitcell_base): offset=self.gnd_position, width=self.width) self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H")) - - + + vdd_ypos = self.inverter_nmos_ypos \ + self.inverter_nmos.active_height \ + self.inverter_gap \ @@ -433,7 +435,7 @@ class pbitcell(bitcell_base.bitcell_base): offset=self.vdd_position, width=self.width) self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H")) - + def create_readwrite_ports(self): """ Creates read/write ports to the bit cell. A differential @@ -461,7 +463,7 @@ class pbitcell(bitcell_base.bitcell_base): 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) @@ -662,7 +664,7 @@ class pbitcell(bitcell_base.bitcell_base): 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) @@ -897,7 +899,7 @@ class pbitcell(bitcell_base.bitcell_base): [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) def route_readwrite_access(self): - """ + """ Routes read/write transistors to the storage component of the bitcell """ @@ -917,7 +919,7 @@ class pbitcell(bitcell_base.bitcell_base): [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 """ @@ -937,7 +939,7 @@ class pbitcell(bitcell_base.bitcell_base): [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 """ @@ -1016,7 +1018,7 @@ class pbitcell(bitcell_base.bitcell_base): offset=offset, width=well_width, height=well_height) - + # extend nwell to encompass inverter_pmos # calculate offset of the left pmos well if "nwell" in layer: @@ -1024,7 +1026,7 @@ class pbitcell(bitcell_base.bitcell_base): - self.nwell_enclose_active inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ + self.inverter_gap - self.nwell_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) \ @@ -1099,18 +1101,18 @@ class pbitcell(bitcell_base.bitcell_base): Q_bar_pos = self.inverter_pmos_right.get_pin("S").center() vdd_pos = self.inverter_pmos_right.get_pin("D").center() self.add_path("m1", [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) @@ -1119,7 +1121,7 @@ class pbitcell(bitcell_base.bitcell_base): """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 # This accounts for bitline being drained thought the access @@ -1128,7 +1130,7 @@ class pbitcell(bitcell_base.bitcell_base): # Assumes always a minimum sizes inverter. Could be # specified in the tech.py file. cin = 3 - + # 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 @@ -1144,10 +1146,10 @@ class pbitcell(bitcell_base.bitcell_base): load + read_port_load, parasitic_delay, False) - + def input_load(self): """ Return the relative capacitance of the access transistor gates """ - + # 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() @@ -1155,10 +1157,10 @@ class pbitcell(bitcell_base.bitcell_base): 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) diff --git a/compiler/bitcells/replica_bitcell.py b/compiler/bitcells/replica_bitcell.py index 597cf5b4..53bcd16f 100644 --- a/compiler/bitcells/replica_bitcell.py +++ b/compiler/bitcells/replica_bitcell.py @@ -5,15 +5,15 @@ # (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,cell_properties +import bitcell_base +from tech import GDS, layer from tech import cell_properties as props - from globals import OPTS -class replica_bitcell(design.design): + +class replica_bitcell(bitcell_base.bitcell_base): """ A single bit cell (6T, 8T, etc.) This module implements the single memory cell used in the design. It @@ -26,38 +26,28 @@ class replica_bitcell(design.design): props.bitcell.cell_6t.pin.vdd, props.bitcell.cell_6t.pin.gnd] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - - if not OPTS.netlist_only: - (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"]) - else: - (width,height) = (0,0) - pin_map = [] - def __init__(self, name=""): + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = OPTS.replica_bitcell_name # Ignore the name argument - design.design.__init__(self, "replica_cell_6t") + super().__init__(name, cell_name) debug.info(2, "Create replica bitcell object") - self.width = replica_bitcell.width - self.height = replica_bitcell.height - self.pin_map = replica_bitcell.pin_map - 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""" - + # 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 @@ -66,6 +56,6 @@ class replica_bitcell(design.design): total_power = self.return_power(dynamic, leakage) return total_power - def build_graph(self, graph, inst_name, port_nets): + 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/replica_bitcell_1rw_1r.py b/compiler/bitcells/replica_bitcell_1rw_1r.py index 79f16a47..f2696e19 100644 --- a/compiler/bitcells/replica_bitcell_1rw_1r.py +++ b/compiler/bitcells/replica_bitcell_1rw_1r.py @@ -5,13 +5,13 @@ # (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 +import bitcell_base from tech import cell_properties as props +from globals import OPTS -class replica_bitcell_1rw_1r(design.design): + +class replica_bitcell_1rw_1r(bitcell_base.bitcell_base): """ A single bit cell which is forced to store a 0. This module implements the single memory cell used in the design. It @@ -26,31 +26,24 @@ class replica_bitcell_1rw_1r(design.design): props.bitcell.cell_1rw1r.pin.wl1, props.bitcell.cell_1rw1r.pin.vdd, props.bitcell.cell_1rw1r.pin.gnd] + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] - 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"]) - - def __init__(self, name=""): - # Ignore the name argument - design.design.__init__(self, "replica_cell_1rw_1r") + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = OPTS.replica_bitcell_name + super().__init__(name, cell_name) debug.info(2, "Create replica bitcell 1rw+1r object") - self.width = replica_bitcell_1rw_1r.width - self.height = replica_bitcell_1rw_1r.height - self.pin_map = replica_bitcell_1rw_1r.pin_map - 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""" - + # 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"] diff --git a/compiler/bitcells/replica_bitcell_1w_1r.py b/compiler/bitcells/replica_bitcell_1w_1r.py index 52bea519..248292ed 100644 --- a/compiler/bitcells/replica_bitcell_1w_1r.py +++ b/compiler/bitcells/replica_bitcell_1w_1r.py @@ -5,13 +5,15 @@ # (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 +import bitcell_base from tech import cell_properties as props +from globals import OPTS +from tech import GDS, layer +import utils -class replica_bitcell_1w_1r(design.design): + +class replica_bitcell_1w_1r(bitcell_base.bitcell_base): """ A single bit cell which is forced to store a 0. This module implements the single memory cell used in the design. It @@ -26,20 +28,14 @@ class replica_bitcell_1w_1r(design.design): props.bitcell.cell_1w1r.pin.wl1, props.bitcell.cell_1w1r.pin.vdd, props.bitcell.cell_1w1r.pin.gnd] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] - 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"]) - - def __init__(self, name=""): - # Ignore the name argument - design.design.__init__(self, "replica_cell_1w_1r") + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = OPTS.replica_bitcell_name + super().__init__(name, cell_name) debug.info(2, "Create replica bitcell 1w+1r object") - 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_stage_effort(self, load): parasitic_delay = 1 @@ -47,10 +43,10 @@ class replica_bitcell_1w_1r(design.design): 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""" - + # 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"] diff --git a/compiler/bitcells/replica_pbitcell.py b/compiler/bitcells/replica_pbitcell.py index 5a588c1e..710cf81d 100644 --- a/compiler/bitcells/replica_pbitcell.py +++ b/compiler/bitcells/replica_pbitcell.py @@ -7,82 +7,85 @@ # import debug import design -from tech import drc, spice,parameter from vector import vector from globals import OPTS from sram_factory import factory + class replica_pbitcell(design.design): """ Creates a replica bitcell using pbitcell """ - def __init__(self, name): + def __init__(self, name, cell_name=None): + if not cell_name: + cell_name = 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) + + design.design.__init__(self, name, cell_name) debug.info(1, "create a replica 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() self.add_boundary() - + 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",replica_bitcell=True) + self.prbc = factory.create(module_type="pbitcell", + replica_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("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): + 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, "wl{}".format(port)) self.copy_layout_pin(self.prbc_inst, "vdd") self.copy_layout_pin(self.prbc_inst, "gnd") - + diff --git a/compiler/bitcells/row_cap_bitcell_1rw_1r.py b/compiler/bitcells/row_cap_bitcell_1rw_1r.py index f7a3a687..17de5d34 100644 --- a/compiler/bitcells/row_cap_bitcell_1rw_1r.py +++ b/compiler/bitcells/row_cap_bitcell_1rw_1r.py @@ -6,8 +6,6 @@ # All rights reserved. # import debug -import utils -from tech import GDS, layer from tech import cell_properties as props import bitcell_base @@ -22,23 +20,12 @@ class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base): pin_names = [props.bitcell.cell_1rw1r.pin.wl0, props.bitcell.cell_1rw1r.pin.wl1, props.bitcell.cell_1rw1r.pin.gnd] - type_list = ["INPUT", "INPUT", "GROUND"] - - (width, height) = utils.get_libcell_size("row_cap_cell_1rw_1r", - GDS["unit"], - layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, - "row_cap_cell_1rw_1r", - GDS["unit"]) - def __init__(self, name=""): - # Ignore the name argument - bitcell_base.bitcell_base.__init__(self, "row_cap_cell_1rw_1r") + def __init__(self, name="row_cap_cell_1rw_1r", cell_name=None): + if not cell_name: + cell_name = name + bitcell_base.bitcell_base.__init__(self, name, cell_name) debug.info(2, "Create row_cap bitcell 1rw+1r object") - self.width = row_cap_bitcell_1rw_1r.width - self.height = row_cap_bitcell_1rw_1r.height - self.pin_map = row_cap_bitcell_1rw_1r.pin_map - self.add_pin_types(self.type_list) self.no_instances = True diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index 93dd5bcb..d1f45575 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -30,10 +30,10 @@ if not OPTS.analytical_delay: else: (OPTS.spice_name,OPTS.spice_exe) = get_tool("spice",["hspice", "ngspice", "ngspice.exe", "xa"]) - # set the input dir for spice files if using ngspice + # set the input dir for spice files if using ngspice if OPTS.spice_name == "ngspice": os.environ["NGSPICE_INPUT_DIR"] = "{0}".format(OPTS.openram_temp) - + if OPTS.spice_exe == "": debug.error("No recognizable spice version found. Unable to perform characterization.",1) else: diff --git a/compiler/characterizer/bit_polarity.py b/compiler/characterizer/bit_polarity.py index c14c167e..2cd8186a 100644 --- a/compiler/characterizer/bit_polarity.py +++ b/compiler/characterizer/bit_polarity.py @@ -11,4 +11,4 @@ from enum import Enum class bit_polarity(Enum): NONINVERTING = 0 INVERTING = 1 - + diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py index fa49b1ed..e3813b36 100644 --- a/compiler/characterizer/charutils.py +++ b/compiler/characterizer/charutils.py @@ -9,7 +9,7 @@ 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) @@ -37,7 +37,7 @@ def parse_spice_list(filename, key): return convert_to_float(val.group(1)) else: return "Failed" - + def round_time(time,time_precision=3): # times are in ns, so this is how many digits of precision # 3 digits = 1ps @@ -58,10 +58,10 @@ def convert_to_float(number): """Converts a string into a (float) number; also converts units(m,u,n,p)""" if number == "Failed": return False - + # start out with a binary value float_value = False - try: + try: # checks if string is a float without letter units float_value = float(number) except ValueError: @@ -69,7 +69,7 @@ def convert_to_float(number): unit = re.search(r"(-?\d+\.?\d*)e(\-?\+?\d+)", number) if unit != None: float_value=float(unit.group(1)) * (10 ^ float(unit.group(2))) - + # see if it is in spice notation unit = re.search(r"(-?\d+\.?\d*)(m?u?n?p?f?)", number) if unit != None: diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 6323a351..dfd845e9 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -51,7 +51,7 @@ class delay(simulation): 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 """ @@ -66,14 +66,14 @@ class delay(simulation): "disabled_write1_power"] # 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.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. @@ -86,7 +86,7 @@ class delay(simulation): 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""" @@ -99,19 +99,19 @@ class delay(simulation): 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 - + 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 - + 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 - + self.read_lib_meas.append(power_measure("disabled_read1_power", "RISE", measure_scale=1e3)) self.read_lib_meas[-1].meta_str = "disabled_read1" self.read_lib_meas.append(power_measure("disabled_read0_power", "FALL", measure_scale=1e3)) @@ -121,7 +121,7 @@ class delay(simulation): 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 @@ -130,14 +130,14 @@ class delay(simulation): read_measures.append(self.create_read_bit_measures()) return read_measures - + 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_volt_meas = [] self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO", self.bl_name)) @@ -145,7 +145,7 @@ class delay(simulation): 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 @@ -153,17 +153,17 @@ class delay(simulation): 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_lib_meas = [] 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 - + self.write_lib_meas.append(power_measure("disabled_write1_power", "RISE", measure_scale=1e3)) self.write_lib_meas[-1].meta_str = "disabled_write1" self.write_lib_meas.append(power_measure("disabled_write0_power", "FALL", measure_scale=1e3)) @@ -173,17 +173,17 @@ class delay(simulation): 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 - + if not OPTS.use_pex: self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name + "{}", "FALL", "RISE", measure_scale=1e9) else: @@ -193,10 +193,10 @@ class delay(simulation): self.sen_meas.meta_add_delay = True return self.dout_volt_meas + [self.sen_meas] - + def create_read_bit_measures(self): """ Adds bit measurements for read0 and read1 cycles """ - + self.read_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []} meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE) for cycle in meas_cycles: @@ -210,7 +210,7 @@ class delay(simulation): def create_write_bit_measures(self): """ Adds bit measurements for write0 and write1 cycles """ - + self.write_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []} meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE) for cycle in meas_cycles: @@ -221,13 +221,13 @@ class delay(simulation): self.write_bit_meas[polarity].append(meas) # Dictionary values are lists, reduce to a single list of measurements return [meas for meas_list in self.write_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) @@ -241,24 +241,24 @@ class delay(simulation): bank_num = self.sram.get_bank_num(self.sram.name, bit_row, bit_col) q_name = "bitcell_Q_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col) qbar_name = "bitcell_Q_bar_b{0}_r{1}_c{2}".format(bank_num, bit_row, bit_col) - + # Bit measures, measurements times to be defined later. The measurement names must be unique # but they is enforced externally. {} added to names to differentiate between ports allow the # measurements are independent of the ports q_meas = voltage_at_measure("v_q_{}".format(meas_tag), q_name) qbar_meas = voltage_at_measure("v_qbar_{}".format(meas_tag), qbar_name) - + 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 check_arguments(self): """Checks if arguments given for write_stimulus() meets requirements""" - + try: int(self.probe_address, 2) except ValueError: @@ -269,7 +269,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 if len(self.read_ports) == 0: debug.error("Characterizer does not currently support SRAMs without read ports.", 1) @@ -292,14 +292,14 @@ class delay(simulation): for port in self.read_ports: for i in range(self.word_size): self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port, i, self.dout_name, self.load)) - + def write_delay_stimulus(self): - """ + """ 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 @@ -316,7 +316,7 @@ class delay(simulation): self.stim.write_include(self.trim_sp_file) self.write_generic_stimulus() - + # generate data and addr signals self.sf.write("\n* Generation of data and address signals\n") self.gen_data() @@ -335,7 +335,7 @@ class delay(simulation): period=self.period, t_rise=self.slew, t_fall=self.slew) - + self.write_delay_measures() # run until the end of the cycle time @@ -344,7 +344,7 @@ class delay(simulation): self.sf.close() def write_power_stimulus(self, trim): - """ Creates a stimulus file to measure leakage power only. + """ Creates a stimulus file to measure leakage power only. This works on the *untrimmed netlist*. """ self.check_arguments() @@ -354,15 +354,15 @@ class delay(simulation): self.sf = open(temp_stim, "w") self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period)) self.stim = stimuli(self.sf, self.corner) - + # include UNTRIMMED files in stimulus file if trim: self.stim.write_include(self.trim_sp_file) else: self.stim.write_include(self.sim_sp_file) - + self.write_generic_stimulus() - + # generate data and addr signals self.sf.write("\n* Generation of data and address signals\n") for write_port in self.write_ports: @@ -384,20 +384,20 @@ class delay(simulation): self.sf.write("\n* Generation of global clock signal\n") for port in self.all_ports: self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0) - + self.write_power_measures() # run until the end of the cycle time self.stim.write_control(2 * self.period) self.sf.close() - + 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: variant_tuple = self.get_delay_measure_variants(port, measure_obj) @@ -410,18 +410,18 @@ class delay(simulation): 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 + # 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 == sram_op.READ_ZERO: @@ -431,50 +431,50 @@ class delay(simulation): 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: + + # 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 - + 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][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_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 = self.cycle_times[self.measure_cycles[port][sram_op.READ_ZERO]] return (t_trig, self.vdd_voltage, port) - + def write_delay_measures_read_port(self, port): """ Write the measure statements to quantify the delay and power results for a read port. """ - + # add measure statements for delays/slews for meas_list in self.read_meas_lists: for measure in meas_list: @@ -485,7 +485,7 @@ class delay(simulation): """ Write the measure statements to quantify the power results for a write port. """ - + # add measure statements for power for meas_list in self.write_meas_lists: for measure in meas_list: @@ -496,7 +496,7 @@ class delay(simulation): """ 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 @@ -508,14 +508,14 @@ class delay(simulation): for read_port in self.targ_read_ports: 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.sf.write("* Write ports {}\n".format(write_port)) self.write_delay_measures_write_port(write_port) - + def write_power_measures(self): """ - Write the measure statements to quantify the leakage power only. + Write the measure statements to quantify the leakage power only. """ self.sf.write("\n* Measure statements for idle leakage power\n") @@ -526,37 +526,37 @@ class delay(simulation): self.stim.gen_meas_power(meas_name="leakage_power", t_initial=t_initial, t_final=t_final) - + def find_feasible_period_one_port(self, port): """ Uses an initial period and finds a feasible period before we run the binary search algorithm to find min period. We check if the given clock period is valid and if it's not, we continue to double the period until we find a valid period to use as a - starting point. + starting point. """ debug.check(port in self.read_ports, "Characterizer requires a read port to determine a period.") - + feasible_period = float(tech.spice["feasible_period"]) time_out = 9 while True: time_out -= 1 if (time_out <= 0): debug.error("Timed out, could not find a feasible period.", 2) - + # Write ports are assumed non-critical to timing, so the first available is used self.targ_write_ports = [self.write_ports[0]] # Set target read port for simulation self.targ_read_ports = [port] - + 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 self.targ_write_ports = [] self.targ_read_ports = [] - + if not success: feasible_period = 2 * feasible_period continue @@ -570,7 +570,7 @@ class delay(simulation): 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 @@ -579,15 +579,15 @@ class delay(simulation): def find_feasible_period(self): """ - Loops through all read ports determining the feasible period and collecting + Loops through all read ports determining the feasible period and collecting delay information from each port. """ feasible_delays = [{} for i in self.all_ports] - + # 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. i = 1 @@ -595,7 +595,7 @@ class delay(simulation): port = self.read_ports[i] # Only extract port values from the specified port, not the entire results. feasible_delays[port].update(self.find_feasible_period_one_port(port)) - # Function sets the period. Restart the entire process if period changes to collect accurate delays + # Function sets the period. Restart the entire process if period changes to collect accurate delays if self.period > previous_period: i = 0 else: @@ -603,7 +603,7 @@ class delay(simulation): previous_period = self.period debug.info(1, "Found feasible_period: {0}ns".format(self.period)) return feasible_delays - + def run_delay_simulation(self): """ This tries to simulate a period and checks if the result works. If @@ -611,9 +611,9 @@ class delay(simulation): works on the trimmed netlist by default, so powers do not include leakage of all cells. """ - + debug.check(self.period > 0, "Target simulation period non-positive") - + self.write_delay_stimulus() self.stim.run_sim() @@ -622,14 +622,14 @@ class delay(simulation): def check_measurements(self): """ Check the write and read measurements """ - + # Loop through all targeted ports and collect delays and powers. result = [{} for i in self.all_ports] for port in self.targ_write_ports: if not self.check_bit_measures(self.write_bit_meas, port): return(False, {}) - + debug.info(2, "Checking write values for port {}".format(port)) write_port_dict = {} for measure in self.write_lib_meas: @@ -638,38 +638,38 @@ class delay(simulation): 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) result[port].update(write_port_dict) - + for port in self.targ_read_ports: # First, check that the memory has the right values at the right times if not self.check_bit_measures(self.read_bit_meas, port): return(False, {}) - + 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 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: @@ -677,13 +677,13 @@ class delay(simulation): 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 + # 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: @@ -710,14 +710,14 @@ class delay(simulation): 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 + + # 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, bit_measures, port): """ Checks the measurements which represent the internal storage voltages @@ -748,28 +748,28 @@ class delay(simulation): 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 + # 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() @@ -784,21 +784,21 @@ class delay(simulation): trim_leakage_power=parse_spice_list("timing", "leakage_power") debug.check(trim_leakage_power!="Failed", "Could not measure leakage power.") 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") 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 delay_hl = result_dict["delay_hl"] delay_lh = result_dict["delay_lh"] slew_hl = result_dict["slew_hl"] slew_lh = result_dict["slew_lh"] period_load_slew_str = "period {0} load {1} slew {2}".format(self.period, self.load, self.slew) - + # if it failed or the read was longer than a period if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float: delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh) @@ -807,11 +807,11 @@ class delay(simulation): delays_str, slews_str)) return False - + 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) # high-to-low delays start at neg. clk edge, so they need to be less than half_period - half_period = self.period / 2 + half_period = self.period / 2 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, @@ -824,16 +824,16 @@ class delay(simulation): slews_str)) return True - + def find_min_period(self, feasible_delays): """ Determine a single minimum period for all ports. """ - + feasible_period = ub_period = self.period 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. for port in self.read_ports: @@ -841,15 +841,15 @@ class delay(simulation): # 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 self.targ_read_ports = [] self.targ_write_ports = [] return target_period - + def find_min_period_one_port(self, feasible_delays, port, lb_period, ub_period, target_period): """ - Searches for the smallest period with output delays being within 5% of + Searches for the smallest period with output delays being within 5% of long period. For the current logic to characterize multiport, bounds are required as an input. """ @@ -857,7 +857,7 @@ class delay(simulation): # 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 # Write ports are assumed non-critical to timing, so the first available is used @@ -882,27 +882,27 @@ class delay(simulation): if relative_compare(ub_period, lb_period, error_tolerance=0.05): # ub_period is always feasible. return ub_period - + # Update target target_period = 0.5 * (ub_period + lb_period) # key=input("press return to continue") def try_period(self, feasible_delays): - """ + """ 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: - # check that the delays and slews do not degrade with tested period. + # 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. @@ -914,28 +914,28 @@ class delay(simulation): return False # key=raw_input("press return to continue") - + 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, port)) 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. """ - + self.probe_address = probe_address self.probe_data = probe_data self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) self.wordline_row = self.get_address_row_number(probe_address) self.prepare_netlist() - + 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: @@ -950,7 +950,7 @@ class delay(simulation): def prepare_netlist(self): """ Prepare a trimmed netlist and regular netlist. """ - + # Set up to trim the netlist here if that is enabled if OPTS.trim_netlist: self.trim_sp_file = "{}reduced.sp".format(OPTS.openram_temp) @@ -964,36 +964,36 @@ class delay(simulation): else: # The non-reduced netlist file when it is disabled self.trim_sp_file = "{}sram.sp".format(OPTS.openram_temp) - - # The non-reduced netlist file for power simulation + + # The non-reduced netlist file for power simulation self.sim_sp_file = "{}sram.sp".format(OPTS.openram_temp) # Make a copy in temp for debugging shutil.copy(self.sp_file, self.sim_sp_file) 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_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) - + # 1) Find a feasible period and it's corresponding delays using the trimmed array. feasible_delays = self.find_feasible_period() - + # 2) Finds the minimum period without degrading the delays by X% self.set_load_slew(max(loads), max(slews)) min_period = self.find_min_period(feasible_delays) @@ -1008,25 +1008,25 @@ class delay(simulation): # 4) At the minimum period, measure the delay, slew and power for all slew/load pairs. 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. 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. 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: @@ -1045,12 +1045,12 @@ class delay(simulation): else: measure_data[port][mname].append(value) return measure_data - + 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. + # 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 @@ -1073,10 +1073,10 @@ class delay(simulation): data_ones = "1" * self.word_size data_zeros = "0" * self.word_size wmask_ones = "1" * self.num_wmasks - + if self.t_current == 0: self.add_noop_all_ports("Idle cycle (no positive clock edge)") - + self.add_write("W data 1 address {}".format(inverse_address), inverse_address, data_ones, @@ -1089,7 +1089,7 @@ class delay(simulation): wmask_ones, write_port) self.measure_cycles[write_port][sram_op.WRITE_ZERO] = len(self.cycle_times) - 1 - + self.add_noop_clock_one_port(write_port) self.measure_cycles[write_port]["disabled_write0"] = len(self.cycle_times) - 1 @@ -1102,7 +1102,7 @@ class delay(simulation): self.probe_address, read_port) self.measure_cycles[read_port][sram_op.READ_ZERO] = len(self.cycle_times) - 1 - + self.add_noop_clock_one_port(read_port) self.measure_cycles[read_port]["disabled_read0"] = len(self.cycle_times) - 1 @@ -1131,39 +1131,39 @@ class delay(simulation): self.add_read("R data 0 address {} to clear dout caps".format(inverse_address), inverse_address, read_port) - + self.add_read("R data 1 address {} to check W1 worked".format(self.probe_address), self.probe_address, 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))") - + def get_available_port(self, get_read_port): - - """Returns the first accessible read or write port. """ + + """Returns the first accessible read or write port. """ if get_read_port and len(self.read_ports) > 0: return self.read_ports[0] elif not get_read_port and len(self.write_ports) > 0: return self.write_ports[0] return None - + def set_stimulus_variables(self): simulation.set_stimulus_variables(self) 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) 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. if len(self.targ_write_ports) == 0 or len(self.targ_read_ports) == 0: debug.error("Write and read port must be specified for characterization.", 1) self.set_stimulus_variables() - + # Get any available read/write port in case only a single write or read ports is being characterized. cur_read_port = self.get_available_port(get_read_port=True) cur_write_port = self.get_available_port(get_read_port=False) @@ -1171,7 +1171,7 @@ class delay(simulation): "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. write_pos = 0 read_pos = 0 @@ -1179,7 +1179,7 @@ class delay(simulation): # 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. if write_pos < len(self.targ_write_ports): cur_write_port = self.targ_write_ports[write_pos] @@ -1187,39 +1187,39 @@ class delay(simulation): if read_pos < len(self.targ_read_ports): 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. 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("In analytical mode, all ports have the timing of the first read port.") - + # 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() - + 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) @@ -1229,7 +1229,7 @@ class delay(simulation): for load in loads: # 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, @@ -1237,7 +1237,7 @@ class delay(simulation): 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: @@ -1249,20 +1249,20 @@ class delay(simulation): 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 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) def analytical_power(self, slews, loads): """Get the dynamic and leakage power from the SRAM""" - + # slews unused, only last load is used load = loads[-1] power = self.sram.analytical_power(self.corner, load) @@ -1272,21 +1272,21 @@ class delay(simulation): debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic)) debug.info(1, "Leakage Power: {0} mW".format(power.leakage)) return power - + 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) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05) def gen_addr(self): - """ - Generates the address inputs for a simulation timing test. + """ + 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) @@ -1294,15 +1294,15 @@ 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: self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) - + 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 # 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] diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 44c6d278..4ec218fc 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -23,7 +23,7 @@ class functional(simulation): def __init__(self, sram, spfile, corner, cycles=15): super().__init__(sram, spfile, corner) - + # Seed the characterizer with a constant seed for unit tests if OPTS.is_unit_test: random.seed(12345) @@ -50,7 +50,7 @@ class functional(simulation): self.set_internal_spice_names() self.q_name, self.qbar_name = self.get_bit_name() debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name)) - + # Number of checks can be changed self.num_cycles = cycles # This is to have ordered keys for random selection @@ -63,16 +63,16 @@ class functional(simulation): self.period = feasible_period # Generate a random sequence of reads and writes self.create_random_memory_sequence() - + # Run SPICE 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. (success, error) = self.read_stim_results() if not success: return (0, error) - + # Check read values with written values. If the values do not match, return an error. return self.check_stim_results() @@ -94,7 +94,7 @@ class functional(simulation): len(val), port, name)) - + def create_random_memory_sequence(self): if self.write_size: rw_ops = ["noop", "write", "partial_write", "read"] @@ -123,7 +123,7 @@ class functional(simulation): self.cycle_times.append(self.t_current) self.t_current += self.period self.check_lengths() - + # 2. 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 @@ -138,7 +138,7 @@ class functional(simulation): self.cycle_times.append(self.t_current) self.t_current += self.period self.check_lengths() - + # 3. Perform a random sequence of writes and reads on random # ports, using random addresses and random words and random # write masks (if applicable) @@ -151,7 +151,7 @@ class functional(simulation): op = random.choice(w_ops) else: op = random.choice(r_ops) - + if op == "noop": self.add_noop_one_port(port) elif op == "write": @@ -191,10 +191,10 @@ class functional(simulation): comment = self.gen_cycle_comment("read", word, addr, "0" * self.num_wmasks, port, self.t_current) self.add_read_one_port(comment, addr, port) self.add_read_check(word, port) - + self.cycle_times.append(self.t_current) 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.num_wmasks, 0, self.t_current) self.add_noop_all_ports(comment) @@ -203,7 +203,7 @@ class functional(simulation): """ Create the masked data word """ # Start with the new word new_word = word - + # 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 for bit in range(len(wmask)): @@ -211,9 +211,9 @@ class functional(simulation): 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:] - + return new_word - + def add_read_check(self, word, port): """ Add to the check array to ensure a read works. """ try: @@ -222,7 +222,7 @@ class functional(simulation): self.check = 0 self.read_check.append([word, "{0}{1}".format(self.dout_name, port), self.t_current + self.period, self.check]) self.check += 1 - + def read_stim_results(self): # Extract dout values from spice timing.lis for (word, dout_port, eo_period, check) in self.read_check: @@ -247,12 +247,12 @@ class functional(simulation): bit, value, eo_period) - + return (0, error) - - self.read_results.append([sp_read_value, dout_port, eo_period, check]) + + self.read_results.append([sp_read_value, dout_port, eo_period, check]) return (1, "SUCCESS") - + def check_stim_results(self): for i in range(len(self.read_check)): if self.read_check[i][0] != self.read_results[i][0]: @@ -307,14 +307,14 @@ class functional(simulation): random_value = random.randint(0, ((2 ** (self.addr_size - 1) - 1)) + (self.num_spare_rows * self.words_per_row)) addr_bits = self.convert_to_bin(random_value, True) return addr_bits - + def get_data(self): """ Gets an available address and corresponding word. """ # 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) - + def convert_to_bin(self, value, is_addr): """ Converts addr & word to usable binary values. """ new_value = str.replace(bin(value), "0b", "") @@ -324,10 +324,10 @@ class functional(simulation): expected_value = self.word_size + self.num_spare_cols 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 write_functional_stimulus(self): """ Writes SPICE stimulus. """ temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) @@ -341,7 +341,7 @@ class functional(simulation): # Write Vdd/Gnd statements self.sf.write("\n* Global Power Supplies\n") self.stim.write_supply() - + # Instantiate the SRAM self.sf.write("\n* Instantiation of the SRAM\n") self.stim.inst_model(pins=self.pins, @@ -361,19 +361,19 @@ class functional(simulation): 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") for comment in self.fn_cycle_comments: self.sf.write("*{}\n".format(comment)) - + # Generate data input bits self.sf.write("\n* Generation of data and address signals\n") for port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): sig_name="{0}{1}_{2} ".format(self.din_name, port, bit) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05) - + # Generate address bits for port in self.all_ports: for bit in range(self.addr_size): @@ -384,7 +384,7 @@ class functional(simulation): self.sf.write("\n * Generation of control signals\n") for port in self.all_ports: self.stim.gen_pwl("CSB{}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) - + 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) @@ -417,7 +417,7 @@ class functional(simulation): period=self.period, t_rise=self.slew, t_fall=self.slew) - + # Generate dout value measurements self.sf.write("\n * Generation of dout measurements\n") for (word, dout_port, eo_period, check) in self.read_check: @@ -428,10 +428,10 @@ class functional(simulation): dout="{0}_{1}".format(dout_port, bit), t_intital=t_intital, t_final=t_final) - + self.stim.write_control(self.cycle_times[-1] + self.period) self.sf.close() - + #FIXME: Similar function to delay.py, refactor this def get_bit_name(self): """ Get a bit cell name """ @@ -444,4 +444,4 @@ class functional(simulation): return (q_name, qbar_name) - + diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 96c464b7..e5628c5d 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -18,21 +18,21 @@ from globals import OPTS class lib: """ lib file generation.""" - + def __init__(self, out_dir, sram, sp_file, use_model=OPTS.analytical_delay): - + self.out_dir = out_dir self.sram = sram - self.sp_file = sp_file + self.sp_file = sp_file self.use_model = use_model self.set_port_indices() - + self.prepare_tables() - + self.create_corners() - + self.characterize_corners() - + def set_port_indices(self): """Copies port information set in the SRAM instance""" self.total_port_num = len(self.sram.all_ports) @@ -40,7 +40,7 @@ class lib: self.readwrite_ports = self.sram.readwrite_ports self.read_ports = self.sram.read_ports self.write_ports = self.sram.write_ports - + def prepare_tables(self): """ Determine the load/slews if they aren't specified in the config file. """ # These are the parameters to determine the table sizes @@ -48,12 +48,12 @@ class lib: self.load = tech.spice["dff_in_cap"] self.loads = self.load_scales * self.load debug.info(1, "Loads: {0}".format(self.loads)) - + self.slew_scales = np.array(OPTS.slew_scales) self.slew = tech.spice["rise_time"] self.slews = self.slew_scales * self.slew debug.info(1, "Slews: {0}".format(self.slews)) - + def create_corners(self): """ Create corners for characterization. """ # Get the corners from the options file @@ -71,7 +71,7 @@ class lib: min_process = "FF" nom_process = "TT" max_process = "SS" - + self.corners = [] self.lib_files = [] @@ -103,15 +103,15 @@ class lib: temp) self.corner_name = self.corner_name.replace(".","p") # Remove decimals lib_name = self.out_dir+"{}.lib".format(self.corner_name) - + # A corner is a tuple of PVT self.corners.append((proc, volt, temp)) self.lib_files.append(lib_name) - - + + def characterize_corners(self): """ Characterize the list of corners. """ - debug.info(1,"Characterizing corners: " + str(self.corners)) + debug.info(1,"Characterizing corners: " + str(self.corners)) for (self.corner,lib_name) in zip(self.corners,self.lib_files): debug.info(1,"Corner: " + str(self.corner)) (self.process, self.voltage, self.temperature) = self.corner @@ -121,7 +121,7 @@ class lib: self.characterize() self.lib.close() self.parse_info(self.corner,lib_name) - + def characterize(self): """ Characterize the current corner. """ @@ -130,8 +130,8 @@ class lib: self.compute_setup_hold() self.write_header() - - # Loop over all ports. + + # Loop over all ports. for port in self.all_ports: # set the read and write port as inputs. self.write_data_bus(port) @@ -143,7 +143,7 @@ class lib: self.write_clk_timing_power(port) self.write_footer() - + def write_footer(self): """ Write the footer """ self.lib.write(" }\n") #Closing brace for the cell @@ -154,13 +154,13 @@ class lib: self.lib.write("library ({0}_lib)".format(self.corner_name)) self.lib.write("{\n") self.lib.write(" delay_model : \"table_lookup\";\n") - + self.write_units() self.write_defaults() self.write_LUT_templates() self.lib.write(" default_operating_conditions : OC; \n") - + self.write_bus() self.lib.write("cell ({0})".format(self.sram.name)) @@ -182,7 +182,7 @@ class lib: control_str = 'csb0' #assume at least 1 port for i in range(1, self.total_port_num): control_str += ' & csb{0}'.format(i) - + # Leakage is included in dynamic when macro is enabled self.lib.write(" leakage_power () {\n") # 'when' condition unnecessary when cs pin does not turn power to devices @@ -190,15 +190,15 @@ class lib: self.lib.write(" value : {};\n".format(self.char_sram_results["leakage_power"])) self.lib.write(" }\n") self.lib.write(" cell_leakage_power : {};\n".format(self.char_sram_results["leakage_power"])) - - + + def write_units(self): """ Adds default units for time, voltage, current,... - Valid values are 1mV, 10mV, 100mV, and 1V. + 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), + 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(" current_unit : \"1mA\" ;\n") @@ -214,7 +214,7 @@ class lib: def write_defaults(self): """ Adds default values for slew and capacitance.""" - + self.lib.write(" input_threshold_pct_fall : 50.0 ;\n") self.lib.write(" output_threshold_pct_fall : 50.0 ;\n") self.lib.write(" input_threshold_pct_rise : 50.0 ;\n") @@ -255,7 +255,7 @@ class lib: formatted_rows = list(map(self.create_list,split_values)) formatted_array = ",\\\n".join(formatted_rows) return formatted_array - + def write_index(self, number, values): """ Write the index """ quoted_string = self.create_list(values) @@ -267,10 +267,10 @@ class lib: # indent each newline plus extra spaces for word values indented_string = quoted_string.replace('\n', '\n' + indent +" ") self.lib.write("{0}values({1});\n".format(indent,indented_string)) - + def write_LUT_templates(self): """ Adds lookup_table format (A 1x1 lookup_table).""" - + Tran = ["CELL_TABLE"] for i in Tran: self.lib.write(" lu_table_template({0})".format(i)) @@ -278,8 +278,8 @@ 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) - # Dividing by 1000 to all cap values since output of .sp is in fF, - # and it needs to be in pF for Innovus. + # 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") @@ -292,12 +292,12 @@ class lib: self.write_index(1,self.slews) self.write_index(2,self.slews) self.lib.write(" }\n\n") - + # self.lib.write(" lu_table_template(CLK_TRAN) {\n") # self.lib.write(" variable_1 : constrained_pin_transition;\n") # self.write_index(1,self.slews) # self.lib.write(" }\n\n") - + # self.lib.write(" lu_table_template(TRAN) {\n") # self.lib.write(" variable_1 : total_output_net_capacitance;\n") # self.write_index(1,self.slews) @@ -311,10 +311,10 @@ class lib: # #self.write_index(1,self.slews) # self.write_index(1,[self.slews[0]]) # self.lib.write(" }\n\n") - + def write_bus(self): """ Adds format of data and addr bus.""" - + self.lib.write("\n\n") self.lib.write(" type (data){\n") self.lib.write(" base_type : array;\n") @@ -378,11 +378,11 @@ class lib: 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)/1000)) - self.lib.write(" min_capacitance : {0}; \n".format(min(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(" }\n") - + self.lib.write(" pin(dout{0}[{1}:0]){{\n".format(read_port,self.sram.word_size-1)) self.lib.write(" timing(){ \n") @@ -402,7 +402,7 @@ class lib: self.write_values(self.char_port_results[read_port]["slew_hl"],len(self.loads)," ") self.lib.write(" }\n") # fall trans self.lib.write(" }\n") # timing - self.lib.write(" }\n") # pin + self.lib.write(" }\n") # pin self.lib.write(" }\n\n") # bus def write_data_bus_input(self, write_port): @@ -416,10 +416,10 @@ class lib: self.lib.write(" memory_write(){ \n") 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(" }\n") 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") # pin self.lib.write(" }\n") #bus def write_data_bus(self, port): @@ -431,7 +431,7 @@ 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(" direction : input; \n") @@ -439,9 +439,9 @@ class lib: 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("{\n") - + self.write_FF_setuphold(port) - self.lib.write(" }\n") + self.lib.write(" }\n") self.lib.write(" }\n\n") def write_wmask_bus(self, port): @@ -465,7 +465,7 @@ class lib: ctrl_pin_names = ["csb{0}".format(port)] if port in self.readwrite_ports: 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") @@ -508,12 +508,12 @@ class lib: self.lib.write(" }\n") self.lib.write(" }\n") self.lib.write(" }\n\n") - + def add_clk_control_power(self, port): """Writes powers under the clock pin group for a specified port""" #Web added to read/write ports. Likely to change when control logic finished. web_name = "" - + if port in self.write_ports: if port in self.read_ports: web_name = " & !web{0}".format(port) @@ -556,7 +556,7 @@ class lib: self.lib.write(" values(\"{0:.6e}\");\n".format(read0_power)) self.lib.write(" }\n") self.lib.write(" }\n") - + # Disabled power. disabled_read1_power = np.mean(self.char_port_results[port]["disabled_read1_power"]) disabled_read0_power = np.mean(self.char_port_results[port]["disabled_read0_power"]) @@ -585,7 +585,7 @@ class lib: self.d = delay(self.sram, self.sp_file, self.corner) if self.use_model: char_results = self.d.analytical_delay(self.slews,self.loads) - self.char_sram_results, self.char_port_results = char_results + self.char_sram_results, self.char_port_results = char_results else: if (self.sram.num_spare_rows == 0): probe_address = "1" * self.sram.addr_size @@ -593,8 +593,8 @@ class lib: probe_address = "0" + "1" * (self.sram.addr_size - 1) probe_data = self.sram.word_size - 1 char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads) - self.char_sram_results, self.char_port_results = char_results - + self.char_sram_results, self.char_port_results = char_results + def compute_setup_hold(self): """ Do the analysis if we haven't characterized a FF yet """ # Do the analysis if we haven't characterized a FF yet @@ -604,8 +604,8 @@ class lib: self.times = self.sh.analytical_setuphold(self.slews,self.loads) else: self.times = self.sh.analyze(self.slews,self.slews) - - + + def parse_info(self,corner,lib_name): """ Copies important characterization data to datasheet.info to be added to datasheet """ if OPTS.is_unit_test: @@ -617,9 +617,9 @@ class lib: proc = subprocess.Popen(['git','rev-parse','HEAD'], cwd=os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/', stdout=subprocess.PIPE) git_id = str(proc.stdout.read()) - + try: - git_id = git_id[2:-3] + git_id = git_id[2:-3] except: pass # check if git id is valid @@ -628,7 +628,7 @@ class lib: git_id = 'Failed to retruieve' datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+') - + current_time = datetime.date.today() # write static information to be parser later datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},".format( @@ -654,10 +654,10 @@ class lib: # information of checks # run it only the first time datasheet.write("{0},{1},".format(self.sram.drc_errors, self.sram.lvs_errors)) - + # write area datasheet.write(str(self.sram.width * self.sram.height) + ',') - + # write timing information for all ports for port in self.all_ports: #din timings @@ -675,7 +675,7 @@ class lib: min(list(map(round_time,self.times["hold_times_HL"]))), max(list(map(round_time,self.times["hold_times_HL"]))) - + )) for port in self.all_ports: @@ -695,7 +695,7 @@ class lib: min(list(map(round_time,self.char_port_results[port]["slew_hl"]))), max(list(map(round_time,self.char_port_results[port]["slew_hl"]))) - + )) for port in self.all_ports: @@ -791,9 +791,9 @@ class lib: control_str = 'csb0' for i in range(1, self.total_port_num): control_str += ' & csb{0}'.format(i) - + datasheet.write("{0},{1},{2},".format('leak', control_str, self.char_sram_results["leakage_power"])) - + datasheet.write("END\n") datasheet.close() diff --git a/compiler/characterizer/logical_effort.py b/compiler/characterizer/logical_effort.py index 42ac8861..6656e5fd 100644 --- a/compiler/characterizer/logical_effort.py +++ b/compiler/characterizer/logical_effort.py @@ -17,7 +17,7 @@ class logical_effort(): 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 @@ -26,31 +26,31 @@ class logical_effort(): 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.electrical_effort, self.parasitic_scale, self.is_rise - ) + ) def get_stage_effort(self): return self.logical_effort*self.electrical_effort - + def get_parasitic_delay(self): return logical_effort.pinv*self.parasitic_scale - + def get_stage_delay(self): return self.get_stage_effort()+self.get_parasitic_delay() 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() for stage in stage_effort_list] - + 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) @@ -62,7 +62,7 @@ def calculate_absolute_delay(stage_effort_list): for stage in stage_effort_list: total_delay+=stage.get_absolute_delay() return total_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") @@ -74,11 +74,11 @@ def calculate_relative_rise_fall_delays(stage_effort_list): else: total_fall_delay += stage.get_stage_delay() return total_rise_delay, total_fall_delay - + 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 + 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 31da8ac5..7900dd2b 100644 --- a/compiler/characterizer/measurements.py +++ b/compiler/characterizer/measurements.py @@ -19,63 +19,63 @@ class spice_measurement(ABC): 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_str = None self.meta_add_delay = False @abstractmethod def get_measure_function(self): - return None - + return None + @abstractmethod def get_measure_values(self): return None - + def write_measure(self, stim_obj, input_tuple): measure_func = self.get_measure_function() if measure_func == None: debug.error("Did not set measure function",1) measure_vals = self.get_measure_values(*input_tuple) measure_func(stim_obj, *measure_vals) - + 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)) + 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: + 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, 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): return stimuli.gen_meas_delay - + def set_meas_constants(self, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd): """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.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 - + #Time delays and ports are variant and needed as inputs when writing the measurement - - def get_measure_values(self, trig_td, targ_td, vdd_voltage, port=None): + + 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 @@ -90,74 +90,74 @@ 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) + 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): -class slew_measure(delay_measure): - 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): """Set the values needed to generate a Spice measurement statement based on the name of the measurement.""" self.trig_dir_str = slew_dir_str self.targ_dir_str = slew_dir_str - + if slew_dir_str == "RISE": - self.trig_val_of_vdd = 0.1 + self.trig_val_of_vdd = 0.1 self.targ_val_of_vdd = 0.9 elif slew_dir_str == "FALL": - self.trig_val_of_vdd = 0.9 + self.trig_val_of_vdd = 0.9 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 - - #Time delays and ports are variant and needed as inputs when writing the measurement - + + #Time delays and ports are variant and needed as inputs when writing the measurement + 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, has_port=True): spice_measurement.__init__(self, measure_name, measure_scale, has_port) self.set_meas_constants(power_type) - + def get_measure_function(self): return stimuli.gen_meas_power - + def set_meas_constants(self, power_type): """Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)""" #Not needed for power simulation self.power_type = power_type #Expected to be "RISE"/"FALL" - - def get_measure_values(self, t_initial, t_final, port=None): + + 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: meas_name = self.name - return (meas_name,t_initial,t_final) - -class voltage_when_measure(spice_measurement): + return (meas_name,t_initial,t_final) + +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, 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): return stimuli.gen_meas_find_voltage - + def set_meas_constants(self, trig_name, targ_name, trig_dir_str, trig_vdd): """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_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): + + 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: @@ -169,25 +169,25 @@ 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 + 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): + +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): + + 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: @@ -196,6 +196,6 @@ class voltage_at_measure(spice_measurement): 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) - + targ_name = self.targ_name_no_port + return (meas_name,targ_name,time_at) + diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index 52100001..794b3988 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -30,14 +30,14 @@ class model_check(delay): 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, port): """Create measurement names. The names themselves currently define the type of measurement""" #Create delay measurement names @@ -73,10 +73,10 @@ class model_check(delay): 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"] - + 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 @@ -90,7 +90,7 @@ class model_check(delay): 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())] + 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: @@ -103,21 +103,21 @@ class model_check(delay): 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.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_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_meas_objs() @@ -125,101 +125,101 @@ class model_check(delay): 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_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" targ_dir = "FALL" - + for i in range(1, len(self.wl_signal_names)): - self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1], - self.wl_signal_names[i-1], - self.wl_signal_names[i], - trig_dir, - targ_dir, + self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1], + self.wl_signal_names[i-1], + self.wl_signal_names[i], + trig_dir, + targ_dir, measure_scale=1e9)) - self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1], - self.wl_signal_names[i-1], - trig_dir, + self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1], + self.wl_signal_names[i-1], + trig_dir, measure_scale=1e9)) temp_dir = trig_dir trig_dir = targ_dir 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_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, + 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 = [] trig_dir = "RISE" targ_dir = "FALL" #Add measurements from gated_clk_bar to RBL for i in range(1, len(self.rbl_en_signal_names)): - self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1], - self.rbl_en_signal_names[i-1], - self.rbl_en_signal_names[i], - trig_dir, - targ_dir, + self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1], + self.rbl_en_signal_names[i-1], + self.rbl_en_signal_names[i], + trig_dir, + targ_dir, measure_scale=1e9)) - self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1], - self.rbl_en_signal_names[i-1], - trig_dir, + self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1], + self.rbl_en_signal_names[i-1], + trig_dir, measure_scale=1e9)) 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", + 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, + self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[-1], + self.rbl_en_signal_names[-1], + trig_dir, measure_scale=1e9)) - + #Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL. trig_dir = "FALL" targ_dir = "RISE" 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], - self.sae_signal_names[i], - trig_dir, - targ_dir, + self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1], + self.sae_signal_names[i-1], + self.sae_signal_names[i], + trig_dir, + targ_dir, measure_scale=1e9)) - self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1], - self.sae_signal_names[i-1], - trig_dir, + self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1], + self.sae_signal_names[i-1], + trig_dir, measure_scale=1e9)) temp_dir = trig_dir trig_dir = targ_dir targ_dir = temp_dir - self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[-1], - self.sae_signal_names[-1], - trig_dir, + self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[-1], + self.sae_signal_names[-1], + trig_dir, measure_scale=1e9)) - + def write_delay_measures(self): """ Write the measure statements to quantify the delay and power results for all targeted ports. @@ -229,12 +229,12 @@ class model_check(delay): # Output some comments to aid where cycles start and what is happening for comment in self.cycle_comments: self.sf.write("* {}\n".format(comment)) - + for read_port in self.targ_read_ports: self.write_measures_read_port(read_port) def get_delay_measure_variants(self, port, measure_obj): - """Get the measurement values that can either vary from simulation to simulation (vdd, address) + """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 #Assuming only read 0 for now @@ -246,15 +246,15 @@ class model_check(delay): return self.get_power_measure_variants(port, measure_obj, "read") else: debug.error("Measurement not recognized by the model checker.",1) - + 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): """ Write the measure statements for all nodes along the wordline path. @@ -263,16 +263,16 @@ class model_check(delay): for measure in self.all_measures: measure_variant_inp_tuple = self.get_delay_measure_variants(port, measure) measure.write_measure(self.stim, measure_variant_inp_tuple) - + 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 = [] + delay_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: - debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, measure_value),1) + debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, measure_value),1) if type(measure) is delay_measure: delay_meas_list.append(measure_value) elif type(measure)is slew_measure: @@ -282,7 +282,7 @@ class model_check(delay): else: debug.error("Measurement object not recognized.",1) return delay_meas_list, slew_meas_list,power_meas_list - + def run_delay_simulation(self): """ This tries to simulate a period and checks if the result works. If @@ -291,8 +291,8 @@ class model_check(delay): include leakage of all cells. """ #Sanity Check - debug.check(self.period > 0, "Target simulation period non-positive") - + debug.check(self.period > 0, "Target simulation period non-positive") + wl_delay_result = [[] for i in self.all_ports] wl_slew_result = [[] for i in self.all_ports] sae_delay_result = [[] for i in self.all_ports] @@ -304,44 +304,44 @@ class model_check(delay): self.write_delay_stimulus() self.stim.run_sim() #running sim prodoces spice output file. - + #Retrieve the results from the output file - for port in self.targ_read_ports: + 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) 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.""" return self.sram.control_logic_rw.get_wl_sen_delays() - + def get_num_delay_stages(self): """Gets the number of stages in the delay chain from the control logic""" return len(self.sram.control_logic_rw.replica_bitline.delay_fanout_list) - + def get_num_delay_fanout_list(self): """Gets the number of stages in the delay chain from the control logic""" return self.sram.control_logic_rw.replica_bitline.delay_fanout_list - + def get_num_delay_stage_fanout(self): """Gets fanout in each stage in the delay chain. Assumes each stage is the same""" return self.sram.control_logic_rw.replica_bitline.delay_fanout_list[0] - + def get_num_wl_en_driver_stages(self): """Gets the number of stages in the wl_en driver from the control logic""" return self.sram.control_logic_rw.wl_en_driver.num_stages - + def get_num_sen_driver_stages(self): """Gets the number of stages in the sen driver from the control logic""" return self.sram.control_logic_rw.s_en_driver.num_stages - + def get_num_wl_driver_stages(self): """Gets the number of stages in the wordline driver from the control logic""" - return self.sram.bank.wordline_driver.inv.num_stages - + return self.sram.bank.wordline_driver.inv.num_stages + def scale_delays(self, delay_list): """Takes in a list of measured delays and convert it to simple units to easily compare to model values.""" converted_values = [] @@ -350,12 +350,12 @@ class model_check(delay): for meas_value in delay_list: total+=meas_value average = total/len(delay_list) - + #Convert values for meas_value in delay_list: converted_values.append(meas_value/average) return converted_values - + def min_max_normalization(self, value_list): """Re-scales input values on a range from 0-1 where min(list)=0, max(list)=1""" scaled_values = [] @@ -364,14 +364,14 @@ class model_check(delay): for value in value_list: scaled_values.append((value-average)/(min_max_diff)) return scaled_values - - def calculate_error_l2_norm(self, list_a, list_b): + + def calculate_error_l2_norm(self, list_a, list_b): """Calculates error between two lists using the l2 norm""" error_list = [] for val_a, val_b in zip(list_a, list_b): error_list.append((val_a-val_b)**2) return error_list - + def compare_measured_and_model(self, measured_vals, model_vals): """First scales both inputs into similar ranges and then compares the error between both.""" scaled_meas = self.min_max_normalization(measured_vals) @@ -380,7 +380,7 @@ class model_check(delay): debug.info(1, "Scaled model:\n{}".format(scaled_model)) 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, port): """Measures entire delay path along the wordline and sense amp enable and compare it to the model delays.""" self.load=max(loads) @@ -390,7 +390,7 @@ class model_check(delay): 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] @@ -398,33 +398,33 @@ class model_check(delay): debug.info(1,"Model test: corner {}".format(self.corner)) (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)) - + debug.info(1,"Measured Wordline delays (ns):\n\t {}".format(wl_delays[read_port])) 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,"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.sae_meas_name] = sae_delays[read_port] 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.") # self.compare_measured_and_model(wl_delays[read_port], wl_model_delays) # debug.info(1, "Comparing SAE measurements and model") # self.compare_measured_and_model(sae_delays[read_port], sae_model_delays) - + return data_dict def get_all_signal_names(self): @@ -438,12 +438,12 @@ class model_check(delay): 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 8def076d..377ead67 100644 --- a/compiler/characterizer/setup_hold.py +++ b/compiler/characterizer/setup_hold.py @@ -31,7 +31,7 @@ class setup_hold(): self.set_corner(corner) - + def set_corner(self,corner): """ Set the corner values """ self.corner = corner @@ -60,9 +60,9 @@ class setup_hold(): self.write_clock() - self.write_measures(mode=mode, + self.write_measures(mode=mode, correct_value=correct_value) - + self.stim.write_control(4*self.period) @@ -102,14 +102,14 @@ class setup_hold(): data_values=[init_value, start_value, end_value], period=target_time, slew=self.constrained_input_slew, - setup=0) + setup=0) def write_clock(self): """ Create the clock signal for setup/hold analysis. First period initializes the FF while the second is used for characterization.""" - + self.stim.gen_pwl(sig_name="clk", - # initial clk edge is right after the 0 time to initialize a flop + # initial clk edge is right after the 0 time to initialize a flop # without using .IC on an internal node. # Return input to value after one period. # The second pulse is the characterization one at 2*period @@ -117,7 +117,7 @@ class setup_hold(): data_values=[0, 1, 0, 1], period=2*self.period, slew=self.constrained_input_slew, - setup=0) + setup=0) @@ -154,7 +154,7 @@ class setup_hold(): targ_dir=dout_rise_or_fall, trig_td=1.9*self.period, targ_td=1.9*self.period) - + targ_name = "data" # Start triggers right after initialize value is returned to normal # at one period @@ -167,14 +167,14 @@ class setup_hold(): targ_dir=din_rise_or_fall, trig_td=1.2*self.period, targ_td=1.2*self.period) - + def bidir_search(self, correct_value, mode): """ This will perform a bidirectional search for either setup or hold times. It starts with the feasible priod and looks a half period beyond or before it - depending on whether we are doing setup or hold. + depending on whether we are doing setup or hold. """ # NOTE: The feasible bound is always feasible. This is why they are different for setup and hold. @@ -189,8 +189,8 @@ class setup_hold(): feasible_bound = 2.75*self.period # Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search! - self.write_stimulus(mode=mode, - target_time=feasible_bound, + self.write_stimulus(mode=mode, + target_time=feasible_bound, correct_value=correct_value) self.stim.run_sim() ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay")) @@ -204,18 +204,18 @@ class setup_hold(): setuphold_time *= -1e9 else: setuphold_time *= 1e9 - + passing_setuphold_time = setuphold_time debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode, setuphold_time, feasible_bound, 2*self.period)) #raw_input("Press Enter to continue...") - + while True: target_time = (feasible_bound + infeasible_bound)/2 - self.write_stimulus(mode=mode, - target_time=target_time, + self.write_stimulus(mode=mode, + target_time=target_time, correct_value=correct_value) debug.info(2,"{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode, @@ -245,7 +245,7 @@ class setup_hold(): if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001): debug.info(3,"CONVERGE {0} vs {1}".format(feasible_bound,infeasible_bound)) break - + debug.info(2,"Converged on {0} time {1}.".format(mode,passing_setuphold_time)) return passing_setuphold_time @@ -261,7 +261,7 @@ class setup_hold(): """Calculates the setup time for high-to-low transition for a DFF """ return self.bidir_search(0, "SETUP") - + def hold_LH_time(self): """Calculates the hold time for low-to-high transition for a DFF """ @@ -283,7 +283,7 @@ class setup_hold(): HL_setup = [] LH_hold = [] HL_hold = [] - + #For debugging, skips characterization and returns dummy values. # i = 1.0 # for self.related_input_slew in related_slews: @@ -293,15 +293,15 @@ class setup_hold(): # LH_hold.append(i+2.0) # HL_hold.append(i+3.0) # i+=4.0 - + # times = {"setup_times_LH": LH_setup, # "setup_times_HL": HL_setup, # "hold_times_LH": LH_hold, # "hold_times_HL": HL_hold # } # return times - - + + for self.related_input_slew in related_slews: for self.constrained_input_slew in constrained_slews: debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,self.constrained_input_slew)) @@ -317,7 +317,7 @@ class setup_hold(): HL_setup.append(HL_setup_time) LH_hold.append(LH_hold_time) HL_hold.append(HL_hold_time) - + times = {"setup_times_LH": LH_setup, "setup_times_HL": HL_setup, "hold_times_LH": LH_hold, @@ -332,7 +332,7 @@ class setup_hold(): HL_setup = [] LH_hold = [] HL_hold = [] - + for self.related_input_slew in related_slews: for self.constrained_input_slew in constrained_slews: # convert from ps to ns @@ -340,7 +340,7 @@ class setup_hold(): 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, "hold_times_LH": LH_hold, diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 2edb42bf..af30df9b 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -17,7 +17,7 @@ class simulation(): def __init__(self, sram, spfile, corner): self.sram = sram - + self.name = self.sram.name self.word_size = self.sram.word_size self.addr_size = self.sram.addr_size @@ -28,7 +28,7 @@ class simulation(): else: self.num_spare_cols = self.sram.num_spare_cols 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 @@ -53,7 +53,7 @@ class simulation(): 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" @@ -66,12 +66,12 @@ class simulation(): "Number of pins generated for characterization \ do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins, self.pins)) - + def set_stimulus_variables(self): # Clock signals self.cycle_times = [] self.t_current = 0 - + # control signals: only one cs_b for entire multiported sram, one we_b for each write port self.csb_values = {port: [] for port in self.all_ports} self.web_values = {port: [] for port in self.readwrite_ports} @@ -81,13 +81,13 @@ class simulation(): self.data_value = {port: [] for port in self.write_ports} self.wmask_value = {port: [] for port in self.write_ports} self.spare_wen_value = {port: [] for port in self.write_ports} - + # Three dimensional list to handle each addr and data bits for each port over the number of checks self.addr_values = {port: [[] for bit in range(self.addr_size)] for port in self.all_ports} self.data_values = {port: [[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports} self.wmask_values = {port: [[] for bit in range(self.num_wmasks)] for port in self.write_ports} self.spare_wen_values = {port: [[] for bit in range(self.num_spare_cols)] for port in self.write_ports} - + # For generating comments in SPICE stimulus self.cycle_comments = [] self.fn_cycle_comments = [] @@ -104,13 +104,13 @@ class simulation(): web_val = 0 elif op != "noop": debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port, op), 1) - + # Append the values depending on the type of port self.csb_values[port].append(csb_val) # If port is in both lists, add rw control signal. Condition indicates its a RW port. if port in self.readwrite_ports: self.web_values[port].append(web_val) - + def add_data(self, data, port): """ Add the array of data values """ debug.check(len(data)==(self.word_size + self.num_spare_cols), "Invalid data word size.") @@ -129,7 +129,7 @@ class simulation(): def add_address(self, address, port): """ Add the array of address values """ debug.check(len(address)==self.addr_size, "Invalid address size.") - + self.addr_value[port].append(address) bit = self.addr_size - 1 for c in address: @@ -170,7 +170,7 @@ class simulation(): else: debug.error("Non-binary spare enable signal string", 1) bit -= 1 - + def add_write(self, comment, address, data, wmask, port): """ Add the control values for a write cycle. """ debug.check(port in self.write_ports, @@ -182,18 +182,18 @@ class simulation(): self.cycle_times.append(self.t_current) self.t_current += self.period - + self.add_control_one_port(port, "write") self.add_data(data, port) self.add_address(address, port) - self.add_wmask(wmask, port) + self.add_wmask(wmask, port) self.add_spare_wen("1" * self.num_spare_cols, port) - + #Add noops to all other ports. for unselected_port in self.all_ports: if unselected_port != port: self.add_noop_one_port(unselected_port) - + def add_read(self, comment, address, port): """ Add the control values for a read cycle. """ debug.check(port in self.read_ports, @@ -206,8 +206,8 @@ class simulation(): self.cycle_times.append(self.t_current) self.t_current += self.period self.add_control_one_port(port, "read") - self.add_address(address, port) - + self.add_address(address, port) + # If the port is also a readwrite then add # the same value as previous cycle if port in self.write_ports: @@ -220,7 +220,7 @@ class simulation(): except: self.add_wmask("0" * self.num_wmasks, port) self.add_spare_wen("0" * self.num_spare_cols, port) - + #Add noops to all other ports. for unselected_port in self.all_ports: if unselected_port != port: @@ -234,7 +234,7 @@ class simulation(): self.cycle_times.append(self.t_current) self.t_current += self.period - + for port in self.all_ports: self.add_noop_one_port(port) @@ -251,7 +251,7 @@ class simulation(): self.add_address(address, port) self.add_wmask(wmask, port) self.add_spare_wen("1" * self.num_spare_cols, port) - + def add_read_one_port(self, comment, address, port): """ Add the control values for a read cycle. Does not increment the period. """ debug.check(port in self.read_ports, @@ -259,7 +259,7 @@ class simulation(): self.read_ports)) debug.info(2, comment) self.fn_cycle_comments.append(comment) - + self.add_control_one_port(port, "read") self.add_address(address, port) @@ -275,16 +275,16 @@ class simulation(): except: self.add_wmask("0" * self.num_wmasks, port) self.add_spare_wen("0" * self.num_spare_cols, port) - + def add_noop_one_port(self, port): - """ Add the control values for a noop to a single port. Does not increment the period. """ + """ Add the control values for a noop to a single port. Does not increment the period. """ self.add_control_one_port(port, "noop") - + try: self.add_address(self.addr_value[port][-1], port) except: self.add_address("0" * self.addr_size, port) - + # If the port is also a readwrite then add # the same value as previous cycle if port in self.write_ports: @@ -297,7 +297,7 @@ class simulation(): except: self.add_wmask("0" * self.num_wmasks, port) self.add_spare_wen("0" * self.num_spare_cols, port) - + def add_noop_clock_one_port(self, port): """ Add the control values for a noop to a single port. Increments the period. """ debug.info(2, 'Clock only on port {}'.format(port)) @@ -324,7 +324,7 @@ class simulation(): time, time_spacing, comment)) - + def gen_cycle_comment(self, op, word, addr, wmask, port, t_current): if op == "noop": str = "\tIdle during cycle {0} ({1}ns - {2}ns)" @@ -355,22 +355,22 @@ class simulation(): 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 + # 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)) @@ -389,25 +389,25 @@ class simulation(): for port in write_index: for bit in range(self.num_wmasks): pin_names.append("WMASK{0}_{1}".format(port, bit)) - + if self.num_spare_cols: for port in write_index: for bit in range(self.num_spare_cols): pin_names.append("SPARE_WEN{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 - + 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() @@ -415,29 +415,29 @@ class simulation(): self.sram.graph_exclude_data_dff() self.sram.graph_exclude_ctrl_dffs() self.sram.bank.bitcell_array.graph_exclude_replica_col_bits() - + 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] if not OPTS.use_pex: self.graph.get_all_paths('{}{}'.format("clk", port), '{}{}_{}'.format(self.dout_name, port, self.probe_data)) - + sen_with_port = self.get_sen_name(self.graph.all_paths) if sen_with_port.endswith(str(port)): self.sen_name = sen_with_port[:-len(str(port))] else: self.sen_name = sen_with_port debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.") - + debug.info(2, "s_en name = {}".format(self.sen_name)) - + bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port) port_pos = -1 - len(str(self.probe_data)) - len(str(port)) - + if bl_name_port.endswith(str(port) + "_" + str(self.probe_data)): self.bl_name = bl_name_port[:port_pos] + "{}" + bl_name_port[port_pos + len(str(port)):] elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 @@ -445,7 +445,7 @@ class simulation(): else: self.bl_name = bl_name_port debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.") - + if br_name_port.endswith(str(port) + "_" + str(self.probe_data)): self.br_name = br_name_port[:port_pos] + "{}" + br_name_port[port_pos + len(str(port)):] elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0 @@ -457,22 +457,22 @@ class simulation(): else: 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 = "bl{0}_{1}".format(port, OPTS.word_size - 1) self.br_name = "br{0}_{1}".format(port, OPTS.word_size - 1) debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name)) - + def get_sen_name(self, paths, assumed_port=None): """ 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 + # 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() @@ -480,15 +480,15 @@ class simulation(): if OPTS.use_pex: sen_name = sen_name.split('.')[-1] return sen_name - + def create_graph(self): """ Creates timing graph to generate the timing paths for the SRAM output. """ - + self.sram.clear_exclude_bits() # Removes previous bit exclusions self.sram.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_instance_name = "X{}".format(self.sram.name) @@ -498,14 +498,14 @@ class simulation(): """ 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, internal_net, mod, exclusion_set=None): """ - Finds a single alias for the internal_net in given paths. + Finds a single alias for the internal_net in given paths. More or less hits cause an error """ net_found = False @@ -520,7 +520,7 @@ class simulation(): net_found = True if not net_found: debug.error("Could not find {} net in timing paths.".format(internal_net), 1) - + return path_net_name def get_bl_name(self, paths, port): @@ -530,7 +530,7 @@ class simulation(): 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_names = [] exclude_set = self.get_bl_name_search_exclusions() for int_net in [cell_bl, cell_br]: @@ -540,5 +540,5 @@ class simulation(): bl_names[i] = bl_names[i].split('.')[-1] return bl_names[0], bl_names[1] - - + + diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 0c528365..a312fc6d 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -31,7 +31,7 @@ class stimuli(): self.tx_length = tech.drc["minlength_channel"] self.sf = stim_file - + (self.process, self.voltage, self.temperature) = corner found = False self.device_libraries = [] @@ -48,10 +48,10 @@ class stimuli(): pass if not found: debug.error("Must define either fet_libraries or fet_models.", -1) - + def inst_model(self, pins, model_name): """ Function to instantiate a generic model with a set of pins """ - + if OPTS.use_pex: self.inst_pex_model(pins, model_name) else: @@ -59,7 +59,7 @@ class stimuli(): for pin in pins: self.sf.write("{0} ".format(pin)) self.sf.write("{0}\n".format(model_name)) - + def inst_pex_model(self, pins, model_name): self.sf.write("X{0} ".format(model_name)) for pin in pins: @@ -99,7 +99,7 @@ class stimuli(): def create_buffer(self, buffer_name, size=[1, 3], beta=2.5): """ Generates buffer for top level signals (only for sim - purposes). Size is pair for PMOS, NMOS width multiple. + purposes). Size is pair for PMOS, NMOS width multiple. """ self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name, @@ -124,23 +124,23 @@ class stimuli(): self.sf.write(".ENDS test_{0}\n\n".format(buffer_name)) def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall): - """ + """ Generates a periodic signal with 50% duty cycle and slew rates. Period is measured from 50% to 50%. """ self.sf.write("* PULSE: period={0}\n".format(period)) pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n" - self.sf.write(pulse_string.format(sig_name, + self.sf.write(pulse_string.format(sig_name, v1, v2, offset, t_rise, - t_fall, + t_fall, 0.5*period-0.5*t_rise-0.5*t_fall, period)) def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup): - """ + """ Generate a PWL stimulus given a signal name and data values at each period. Automatically creates slews and ensures each data occurs a setup before the clock edge. The first clk_time should be 0 and is the initial time that corresponds @@ -152,7 +152,7 @@ class stimuli(): str.format(len(clk_times), len(data_values), sig_name)) - + # shift signal times earlier for setup time times = np.array(clk_times) - setup * period values = np.array(data_values) * self.voltage @@ -185,7 +185,7 @@ class stimuli(): return 1 else: debug.error("Invalid value to get an inverse of: {0}".format(value)) - + def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td): """ Creates the .meas statement for the measurement of delay """ measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n" @@ -198,7 +198,7 @@ class stimuli(): targ_val, targ_dir, targ_td)) - + def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td): """ Creates the .meas statement for the measurement of delay """ measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n" @@ -208,7 +208,7 @@ class stimuli(): trig_val, 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" @@ -227,15 +227,15 @@ class stimuli(): power_exp, t_initial, t_final)) - + def gen_meas_value(self, meas_name, dout, t_intital, t_final): measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final) self.sf.write(measure_string) def write_control(self, end_time, runlvl=4): """ Write the control cards to run and end the simulation """ - - # These are guesses... + + # These are guesses... if runlvl==1: reltol = 0.02 # 2% elif runlvl==2: @@ -245,7 +245,7 @@ class stimuli(): else: reltol = 0.001 # 0.1% timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests. - + # UIC is needed for ngspice to converge self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time)) self.sf.write(".TEMP {}\n".format(self.temperature)) @@ -281,9 +281,9 @@ class stimuli(): self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1])) else: debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0])) - + includes = self.device_models + [circuit] - + for item in list(includes): if os.path.isfile(item): self.sf.write(".include \"{0}\"\n".format(item)) @@ -305,7 +305,7 @@ class stimuli(): import datetime start_time = datetime.datetime.now() debug.check(OPTS.spice_exe != "", "No spice simulator has been found.") - + if OPTS.spice_name == "xa": # Output the xa configurations here. FIXME: Move this to write it once. xa_cfg = open("{}xa.cfg".format(OPTS.openram_temp), "w") @@ -340,13 +340,13 @@ class stimuli(): spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w') spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w') - + debug.info(3, cmd) retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True) spice_stdout.close() spice_stderr.close() - + if (retcode > valid_retcode): debug.error("Spice simulation error: " + cmd, -1) else: @@ -354,4 +354,4 @@ class stimuli(): delta_time = round((end_time - start_time).total_seconds(), 1) debug.info(2, "*** Spice: {} seconds".format(delta_time)) - + diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index d20dfe42..11c1216f 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -11,33 +11,33 @@ import re class trim_spice(): """ - A utility to trim redundant parts of an SRAM spice netlist. + A utility to trim redundant parts of an SRAM spice netlist. Input is an SRAM spice file. Output is an equivalent netlist that works for a single address and range of data bits. """ def __init__(self, spfile, reduced_spfile): self.sp_file = spfile - self.reduced_spfile = reduced_spfile + self.reduced_spfile = reduced_spfile debug.info(1,"Trimming non-critical cells to speed-up characterization: {}.".format(reduced_spfile)) - + # Load the file into a buffer for performance sp = open(self.sp_file, "r") self.spice = sp.readlines() sp.close() for i in range(len(self.spice)): self.spice[i] = self.spice[i].rstrip(" \n") - + self.sp_buffer = self.spice def set_configuration(self, banks, rows, columns, word_size): """ Set the configuration of SRAM sizes that we are simulating. - Need the: number of banks, number of rows in each bank, number of + Need the: number of banks, number of rows in each bank, number of columns in each bank, and data word size.""" self.num_banks = banks - self.num_rows = rows + self.num_rows = rows self.num_columns = columns self.word_size = word_size @@ -80,8 +80,8 @@ class trim_spice(): debug.info(1,wl_msg) self.sp_buffer.insert(0, "* It should NOT be used for LVS!!") self.sp_buffer.insert(0, "* WARNING: This is a TRIMMED NETLIST.") - - + + wl_regex = r"wl\d*_{}".format(wl_address) bl_regex = r"bl\d*_{}".format(int(self.words_per_row*data_bit + col_address)) self.remove_insts("bitcell_array",[wl_regex,bl_regex]) @@ -92,7 +92,7 @@ class trim_spice(): # 3. Keep column muxes basd on BL self.remove_insts("column_mux_array",[bl_regex]) - + # 4. Keep write driver based on DATA data_regex = r"data_{}".format(data_bit) self.remove_insts("write_driver_array",[data_regex]) @@ -100,18 +100,18 @@ class trim_spice(): # 5. Keep wordline driver based on WL # Need to keep the gater too #self.remove_insts("wordline_driver",wl_regex) - + # 6. Keep precharges based on BL self.remove_insts("precharge_array",[bl_regex]) - + # Everything else isn't worth removing. :) - + # Finally, write out the buffer as the new reduced file sp = open(self.reduced_spfile, "w") sp.write("\n".join(self.sp_buffer)) sp.close() - + def remove_insts(self, subckt_name, keep_inst_list): """This will remove all of the instances in the list from the named subckt that DO NOT contain a term in the list. It just does a @@ -121,7 +121,7 @@ class trim_spice(): removed_insts = 0 #Expects keep_inst_list are regex patterns. Compile them here. compiled_patterns = [re.compile(pattern) for pattern in keep_inst_list] - + start_name = ".SUBCKT {}".format(subckt_name) end_name = ".ENDS {}".format(subckt_name) diff --git a/compiler/custom/dff.py b/compiler/custom/dff.py index cb703707..38e3bd75 100644 --- a/compiler/custom/dff.py +++ b/compiler/custom/dff.py @@ -6,7 +6,7 @@ # All rights reserved. # import design -from tech import GDS, layer, spice, parameter +from tech import GDS, layer, spice from tech import cell_properties as props import utils @@ -23,39 +23,42 @@ class dff(design.design): pin_names = props.dff.custom_port_list type_list = props.dff.custom_type_list clk_pin = props.dff.clk_pin + cell_size_layer = "boundary" - (width, height) = utils.get_libcell_size("dff", - GDS["unit"], - layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"]) - def __init__(self, name="dff"): - design.design.__init__(self, name) + super().__init__(name) - self.width = dff.width - self.height = dff.height - self.pin_map = dff.pin_map + (width, height) = utils.get_libcell_size(self.cell_name, + GDS["unit"], + layer[self.cell_size_layer]) + + pin_map = utils.get_libcell_pins(self.pin_names, + self.cell_name, + GDS["unit"]) + + self.width = width + self.height = height + self.pin_map = 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_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["dff_leakage"] - + total_power = self.return_power(power_dyn, power_leak) return total_power - + def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" - from tech import parameter c_load = load c_para = spice["dff_out_cap"]#ff transition_prob = 0.5 - return transition_prob*(c_load + c_para) + 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/custom/inv_dec.py b/compiler/custom/inv_dec.py index 80fdb74e..b150a38e 100644 --- a/compiler/custom/inv_dec.py +++ b/compiler/custom/inv_dec.py @@ -9,28 +9,31 @@ import design from tech import GDS, layer, spice, parameter import logical_effort import utils -import debug class inv_dec(design.design): """ INV for address decoders. """ - + pin_names = ["A", "Z", "vdd", "gnd"] type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"] - - (width, height) = utils.get_libcell_size("inv_dec", - GDS["unit"], - layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "inv_dec", GDS["unit"]) - - def __init__(self, name="inv_dec", height=None): - design.design.__init__(self, name) + cell_size_layer = "boundary" - self.width = inv_dec.width - self.height = inv_dec.height - self.pin_map = inv_dec.pin_map + def __init__(self, name="inv_dec", height=None): + super().__init__(name) + + (width, height) = utils.get_libcell_size(self.cell_name, + GDS["unit"], + layer[self.cell_size_layer]) + + pin_map = utils.get_libcell_pins(self.pin_names, + self.cell_name, + GDS["unit"]) + + self.width = width + self.height = height + self.pin_map = pin_map self.add_pin_types(self.type_list) def analytical_power(self, corner, load): @@ -39,10 +42,10 @@ class inv_dec(design.design): freq = spice["default_event_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["inv_leakage"] - + total_power = self.return_power(power_dyn, power_leak) return total_power - + def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load @@ -57,7 +60,7 @@ class inv_dec(design.design): 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. diff --git a/compiler/custom/nand2_dec.py b/compiler/custom/nand2_dec.py index c806bf5a..efec63b6 100644 --- a/compiler/custom/nand2_dec.py +++ b/compiler/custom/nand2_dec.py @@ -15,21 +15,25 @@ class nand2_dec(design.design): """ 2-input NAND decoder for address decoders. """ - + pin_names = ["A", "B", "Z", "vdd", "gnd"] type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] - - (width, height) = utils.get_libcell_size("nand2_dec", - GDS["unit"], - layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "nand2_dec", GDS["unit"]) - - def __init__(self, name="nand2_dec", height=None): - design.design.__init__(self, name) + cell_size_layer = "boundary" - self.width = nand2_dec.width - self.height = nand2_dec.height - self.pin_map = nand2_dec.pin_map + def __init__(self, name="nand2_dec", height=None): + super().__init__(name) + + (width, height) = utils.get_libcell_size(self.cell_name, + GDS["unit"], + layer[self.cell_size_layer]) + + pin_map = utils.get_libcell_pins(self.pin_names, + self.cell_name, + GDS["unit"]) + + self.width = width + self.height = height + self.pin_map = pin_map self.add_pin_types(self.type_list) # FIXME: For now... @@ -39,17 +43,17 @@ class nand2_dec(design.design): self.pmos_size = parameter["beta"] * size self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") - + 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_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["nand2_leakage"] - + total_power = self.return_power(power_dyn, power_leak) return total_power - + def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load @@ -61,7 +65,7 @@ class nand2_dec(design.design): 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. @@ -82,4 +86,4 @@ class nand2_dec(design.design): Overrides base class function. """ self.add_graph_edges(graph, port_nets) - + diff --git a/compiler/custom/nand3_dec.py b/compiler/custom/nand3_dec.py index 5eea68de..3fc2b949 100644 --- a/compiler/custom/nand3_dec.py +++ b/compiler/custom/nand3_dec.py @@ -15,21 +15,25 @@ class nand3_dec(design.design): """ 3-input NAND decoder for address decoders. """ - + pin_names = ["A", "B", "C", "Z", "vdd", "gnd"] type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] - - (width, height) = utils.get_libcell_size("nand3_dec", - GDS["unit"], - layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "nand3_dec", GDS["unit"]) - - def __init__(self, name="nand3_dec", height=None): - design.design.__init__(self, name) + cell_size_layer = "boundary" - self.width = nand3_dec.width - self.height = nand3_dec.height - self.pin_map = nand3_dec.pin_map + def __init__(self, name="nand3_dec", height=None): + super().__init__(name) + + (width, height) = utils.get_libcell_size(self.cell_name, + GDS["unit"], + layer[self.cell_size_layer]) + + pin_map = utils.get_libcell_pins(self.pin_names, + self.cell_name, + GDS["unit"]) + + self.width = width + self.height = height + self.pin_map = pin_map self.add_pin_types(self.type_list) # FIXME: For now... @@ -39,17 +43,17 @@ class nand3_dec(design.design): self.pmos_size = parameter["beta"] * size self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") - + 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_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["nand3_leakage"] - + total_power = self.return_power(power_dyn, power_leak) return total_power - + def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load @@ -61,7 +65,7 @@ class nand3_dec(design.design): 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. @@ -82,4 +86,4 @@ class nand3_dec(design.design): Overrides base class function. """ self.add_graph_edges(graph, port_nets) - + diff --git a/compiler/custom/nand4_dec.py b/compiler/custom/nand4_dec.py index df3eee14..abcfd2f3 100644 --- a/compiler/custom/nand4_dec.py +++ b/compiler/custom/nand4_dec.py @@ -15,21 +15,25 @@ class nand4_dec(design.design): """ 2-input NAND decoder for address decoders. """ - + pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"] type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] - - (width, height) = utils.get_libcell_size("nand4_dec", - GDS["unit"], - layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "nand4_dec", GDS["unit"]) - - def __init__(self, name="nand4_dec", height=None): - design.design.__init__(self, name) + cell_size_layer = "boundary" - self.width = nand4_dec.width - self.height = nand4_dec.height - self.pin_map = nand4_dec.pin_map + def __init__(self, name="nand4_dec", height=None): + super().__init__(name) + + (width, height) = utils.get_libcell_size(self.cell_name, + GDS["unit"], + layer[self.cell_size_layer]) + + pin_map = utils.get_libcell_pins(self.pin_names, + self.cell_name, + GDS["unit"]) + + self.width = width + self.height = height + self.pin_map = pin_map self.add_pin_types(self.type_list) # FIXME: For now... @@ -39,17 +43,17 @@ class nand4_dec(design.design): self.pmos_size = parameter["beta"] * size self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") - + 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_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["nand4_leakage"] - + total_power = self.return_power(power_dyn, power_leak) return total_power - + def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load @@ -61,7 +65,7 @@ class nand4_dec(design.design): 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. @@ -82,4 +86,4 @@ class nand4_dec(design.design): Overrides base class function. """ self.add_graph_edges(graph, port_nets) - + diff --git a/compiler/modules/sense_amp.py b/compiler/custom/sense_amp.py similarity index 85% rename from compiler/modules/sense_amp.py rename to compiler/custom/sense_amp.py index f1d5de92..4e84b7fe 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/custom/sense_amp.py @@ -10,7 +10,6 @@ import debug import utils from tech import GDS, layer, parameter, drc from tech import cell_properties as props -from globals import OPTS import logical_effort @@ -28,12 +27,24 @@ class sense_amp(design.design): props.sense_amp.pin.vdd, props.sense_amp.pin.gnd] type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - if not OPTS.netlist_only: - (width, height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"]) - else: - (width, height) = (0, 0) - pin_map = [] + cell_size_layer = "boundary" + + def __init__(self, name="sense_amp"): + super().__init__(name) + debug.info(2, "Create sense_amp") + + (width, height) = utils.get_libcell_size(self.cell_name, + GDS["unit"], + layer[self.cell_size_layer]) + + pin_map = utils.get_libcell_pins(self.pin_names, + self.cell_name, + GDS["unit"]) + + self.width = width + self.height = height + self.pin_map = pin_map + self.add_pin_types(self.type_list) def get_bl_names(self): return props.sense_amp.pin.bl @@ -49,26 +60,17 @@ class sense_amp(design.design): def en_name(self): return props.sense_amp.pin.en - def __init__(self, name): - super().__init__(name) - debug.info(2, "Create sense_amp") - - self.width = sense_amp.width - self.height = sense_amp.height - self.pin_map = sense_amp.pin_map - self.add_pin_types(self.type_list) - def get_cin(self): - + # FIXME: This input load will be applied to both the s_en timing and bitline timing. - + # Input load for the bitlines which are connected to the source/drain of a TX. Not the selects. from tech import spice # Default is 8x. Per Samira and Hodges-Jackson book: # "Column-mux transistors driven by the decoder must be sized for optimal speed" bitline_pmos_size = 8 # FIXME: This should be set somewhere and referenced. Probably in tech file. return spice["min_tx_drain_c"] * bitline_pmos_size # ff - + def get_stage_effort(self, load): # Delay of the sense amp will depend on the size of the amp and the output load. parasitic_delay = 1 @@ -82,14 +84,14 @@ class sense_amp(design.design): # Power in this module currently not defined. Returns 0 nW (leakage and dynamic). total_power = self.return_power() return total_power - + def get_enable_name(self): """Returns name used for enable net""" # FIXME: A better programmatic solution to designate pins enable_name = self.en_name debug.check(enable_name in self.pin_names, "Enable name {} not found in pin list".format(enable_name)) return enable_name - + 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/custom/tri_gate.py b/compiler/custom/tri_gate.py index 998562b1..476cc886 100644 --- a/compiler/custom/tri_gate.py +++ b/compiler/custom/tri_gate.py @@ -8,43 +8,51 @@ import debug import design import utils -from tech import GDS,layer +from tech import GDS, layer + class tri_gate(design.design): """ This module implements the tri gate cell used in the design forS bit-line isolation. It is a hand-made cell, so the layout and - netlist should be available in the technology library. + netlist should be available in the technology library. """ pin_names = ["in", "out", "en", "en_bar", "vdd", "gnd"] type_list = ["INPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] - (width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"]) + cell_size_layer = "boundary" unique_id = 1 - + def __init__(self, name=""): if name=="": name = "tri{0}".format(tri_gate.unique_id) tri_gate.unique_id += 1 - design.design.__init__(self, name) + super().__init__(self, name) debug.info(2, "Create tri_gate") - self.width = tri_gate.width - self.height = tri_gate.height - self.pin_map = tri_gate.pin_map + (width, height) = utils.get_libcell_size(self.cell_name, + GDS["unit"], + layer[self.cell_size_layer]) + + pin_map = utils.get_libcell_pins(self.pin_names, + self.cell_name, + GDS["unit"]) + + self.width = width + self.height = height + self.pin_map = pin_map self.add_pin_types(self.type_list) 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() + total_power = self.return_power() return total_power def get_cin(self): return 9*spice["min_tx_gate_c"] - def build_graph(self, graph, inst_name, port_nets): + 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 + self.add_graph_edges(graph, port_nets) diff --git a/compiler/custom/write_driver.py b/compiler/custom/write_driver.py index 9afac81b..41375ab3 100644 --- a/compiler/custom/write_driver.py +++ b/compiler/custom/write_driver.py @@ -8,13 +8,13 @@ import debug import design import utils -from globals import OPTS -from tech import GDS,layer +from tech import GDS, layer from tech import cell_properties as props + class write_driver(design.design): """ - Tristate write driver to be active during write operations only. + Tristate write driver to be active during write operations only. This module implements the write driver cell used in the design. It is a hand-made cell, so the layout and netlist should be available in the technology library. @@ -28,20 +28,23 @@ class write_driver(design.design): props.write_driver.pin.gnd] type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - if not OPTS.netlist_only: - (width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"]) - else: - (width,height) = (0,0) - pin_map = [] + cell_size_layer = "boundary" def __init__(self, name): - design.design.__init__(self, name) + super().__init__(name) debug.info(2, "Create write_driver") - self.width = write_driver.width - self.height = write_driver.height - self.pin_map = write_driver.pin_map + (width, height) = utils.get_libcell_size(self.cell_name, + GDS["unit"], + layer[self.cell_size_layer]) + + pin_map = utils.get_libcell_pins(self.pin_names, + self.cell_name, + GDS["unit"]) + + self.width = width + self.height = height + self.pin_map = pin_map self.add_pin_types(self.type_list) def get_bl_names(self): @@ -63,6 +66,6 @@ class write_driver(design.design): # This is approximated from SCMOS. It has roughly 5 3x transistor gates. return 5*3 - def build_graph(self, graph, inst_name, port_nets): + 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 + self.add_graph_edges(graph, port_nets) diff --git a/compiler/datasheet/add_db.py b/compiler/datasheet/add_db.py index 9514cf1b..2ad546a9 100644 --- a/compiler/datasheet/add_db.py +++ b/compiler/datasheet/add_db.py @@ -25,16 +25,16 @@ def parse_html(file, comment): end_tag = comment+'-->' 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: diff --git a/compiler/datasheet/datasheet_gen.py b/compiler/datasheet/datasheet_gen.py index 02ecc5d4..becb11d9 100644 --- a/compiler/datasheet/datasheet_gen.py +++ b/compiler/datasheet/datasheet_gen.py @@ -112,7 +112,7 @@ def parse_characterizer_csv(f, pages): DATETIME = row[col] col += 1 - + ANALYTICAL_MODEL = row[col] col += 1 @@ -121,7 +121,7 @@ def parse_characterizer_csv(f, pages): LVS = row[col] col += 1 - + AREA = row[col] col += 1 @@ -565,7 +565,7 @@ def parse_characterizer_csv(f, pages): for element in row[col_start:col-1]: sheet.description.append(str(element)) break - # parse initial power and leakage information + # parse initial power and leakage information while(True): start = col if(row[col].startswith('power')): diff --git a/compiler/drc/design_rules.py b/compiler/drc/design_rules.py index b194d082..afbf3c96 100644 --- a/compiler/drc/design_rules.py +++ b/compiler/drc/design_rules.py @@ -46,7 +46,7 @@ class design_rules(dict): def keys(self): return self.rules.keys() - + def add_layer(self, name, width, spacing, area=0): # Minimum width self.add("minwidth_{}".format(name), width) @@ -54,7 +54,7 @@ class design_rules(dict): self.add("{0}_to_{0}".format(name), spacing) # Minimum area self.add("minarea_{}".format(name), area) - + def add_enclosure(self, name, layer, enclosure, extension=None): self.add("{0}_enclose_{1}".format(name, layer), enclosure) # Reserved for asymmetric enclosures @@ -62,4 +62,4 @@ class design_rules(dict): self.add("{0}_extend_{1}".format(name, layer), extension) else: self.add("{0}_extend_{1}".format(name, layer), enclosure) - + diff --git a/compiler/drc/drc_lut.py b/compiler/drc/drc_lut.py index 8a7b49d2..086e355e 100644 --- a/compiler/drc/drc_lut.py +++ b/compiler/drc/drc_lut.py @@ -44,7 +44,7 @@ class drc_lut(): if k1 < k2: return False return True - - - + + + diff --git a/compiler/drc/drc_value.py b/compiler/drc/drc_value.py index aa957d1b..07425ec5 100644 --- a/compiler/drc/drc_value.py +++ b/compiler/drc/drc_value.py @@ -19,7 +19,7 @@ class drc_value(): Return the value. """ return self.value - - - + + + diff --git a/compiler/gdsMill/gdsMill/gdsStreamer.py b/compiler/gdsMill/gdsMill/gdsStreamer.py index 7c9daacc..3d1e98d5 100644 --- a/compiler/gdsMill/gdsMill/gdsStreamer.py +++ b/compiler/gdsMill/gdsMill/gdsStreamer.py @@ -6,7 +6,7 @@ class GdsStreamer: """ def __init__(self, workingDirectory = "."): self.workingDirectory = os.path.abspath(workingDirectory) - + def createStreamOutTemplate(self, sourceLibraryName, sourceCellName, gdsDestinationPath): templateFile = open(self.workingDirectory+"/partStreamOut.tmpl","w") templateFile.write("streamOutKeys = list(nil\n") @@ -70,7 +70,7 @@ class GdsStreamer: templateFile = open(self.workingDirectory+"/partStreamIn.tmpl","w") templateFile.write("streamInKeys = list(nil\n") templateFile.write("'runDir \".\"\n") - templateFile.write("'inFile \""+inputGdsPath+"\"\n") + templateFile.write("'inFile \""+inputGdsPath+"\"\n") templateFile.write("'primaryCell \"\"\n") templateFile.write("'libName \""+sourceLibraryName+"\"\n") templateFile.write("'techFileName \"\"\n") @@ -88,7 +88,7 @@ class GdsStreamer: templateFile.write("'convertNode \"ignore\"\n") templateFile.write("'keepPcell nil\n") templateFile.write("'replaceBusBitChar nil\n") - templateFile.write("'skipUndefinedLPP nil\n") + templateFile.write("'skipUndefinedLPP nil\n") templateFile.write("'ignoreBox nil\n") templateFile.write("'mergeUndefPurposToDrawing nil\n") templateFile.write("'reportPrecision nil\n") @@ -109,10 +109,10 @@ class GdsStreamer: templateFile.write("'propSeparator \",\"\n") templateFile.write("'userSkillFile \"\"\n") templateFile.write("'rodDir \"\"\n") - templateFile.write("'refLibOrder \"\"\n") + templateFile.write("'refLibOrder \"\"\n") templateFile.write(")\n") templateFile.close() - + def streamFromCadence(self, cadenceLibraryContainerPath, libraryName, cellName, outputPath): #change into the cadence directory outputPath = os.path.abspath(outputPath) @@ -132,7 +132,7 @@ class GdsStreamer: os.remove(self.workingDirectory+"/partStreamOut.tmpl") #and go back to whever it was we started from os.chdir(currentPath) - + def streamToCadence(self,cadenceLibraryContainerPath, libraryName, inputPath): #change into the cadence directory inputPath = os.path.abspath(inputPath) diff --git a/compiler/gdsMill/gdsMill/pdfLayout.py b/compiler/gdsMill/gdsMill/pdfLayout.py index 9e5ba3e8..a8996f38 100644 --- a/compiler/gdsMill/gdsMill/pdfLayout.py +++ b/compiler/gdsMill/gdsMill/pdfLayout.py @@ -11,19 +11,19 @@ class pdfLayout: self.layout = theLayout self.layerColors=dict() self.scale = 1.0 - + def setScale(self,newScale): self.scale = float(newScale) - + def hexToRgb(self,hexColor): """ Takes a hexadecimal color string i.e. "#219E1C" and converts it to an rgb float triplet ranging 0->1 """ red = int(hexColor[1:3],16) green = int(hexColor[3:5],16) - blue = int(hexColor[5:7],16) + blue = int(hexColor[5:7],16) return (float(red)/255,float(green)/255,float(blue)/255) - + def randomHexColor(self): """ Generates a random color in hex using the format #ABC123 @@ -50,26 +50,26 @@ class pdfLayout: xyPoint = tMatrix * xyPoint xyCoordinates += [(xyPoint[0],xyPoint[1])] return xyCoordinates - + def drawBoundary(self,boundary,origin,uVector,vVector): #get the coordinates in the correct coordinate space coordinates = self.transformCoordinates(boundary.coordinates,origin,uVector,vVector) - #method to draw a boundary with an XY offset + #method to draw a boundary with an XY offset x=(coordinates[0][0])/self.scale y=(coordinates[0][1])/self.scale shape = pyx.path.path(pyx.path.moveto(x, y)) for index in range(1,len(coordinates)): x=(coordinates[index][0])/self.scale - y=(coordinates[index][1])/self.scale - shape.append(pyx.path.lineto(x,y)) + y=(coordinates[index][1])/self.scale + shape.append(pyx.path.lineto(x,y)) self.canvas.stroke(shape, [pyx.style.linewidth.thick]) if(boundary.drawingLayer in self.layerColors): layerColor = self.hexToRgb(self.layerColors[boundary.drawingLayer]) self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)]) - - def drawPath(self,path,origin,uVector,vVector): - #method to draw a path with an XY offset - boundaryCoordinates = self.transformCoordinates(path.equivalentBoundaryCoordinates(),origin,uVector,vVector) + + def drawPath(self,path,origin,uVector,vVector): + #method to draw a path with an XY offset + boundaryCoordinates = self.transformCoordinates(path.equivalentBoundaryCoordinates(),origin,uVector,vVector) shape = pyx.path.path(pyx.path.moveto((boundaryCoordinates[0][0])/self.scale,(boundaryCoordinates[0][1])/self.scale)) for coordinate in boundaryCoordinates[1::]: shape.append(pyx.path.lineto((coordinate[0])/self.scale,(coordinate[1])/self.scale)) @@ -77,7 +77,7 @@ class pdfLayout: if(path.drawingLayer in self.layerColors): layerColor = self.hexToRgb(self.layerColors[path.drawingLayer]) self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)]) - + def drawLayout(self): #use the layout xyTree and structureList #to draw ONLY the geometry in each structure @@ -89,6 +89,6 @@ class pdfLayout: self.drawBoundary(boundary,element[1],element[2], element[3]) for path in structureToDraw.paths: self.drawPath(path,element[1],element[2], element[3]) - + def writeToFile(self,filename): self.canvas.writePDFfile(filename) diff --git a/compiler/gdsMill/pyx/bbox.py b/compiler/gdsMill/pyx/bbox.py index d00383d8..089823c7 100644 --- a/compiler/gdsMill/pyx/bbox.py +++ b/compiler/gdsMill/pyx/bbox.py @@ -28,7 +28,7 @@ import unit # class bbox_pt: - + """class for bounding boxes This variant requires points in the constructor, and is used for internal diff --git a/compiler/gdsMill/pyx/canvas.py b/compiler/gdsMill/pyx/canvas.py index 6110af17..bdd89d78 100644 --- a/compiler/gdsMill/pyx/canvas.py +++ b/compiler/gdsMill/pyx/canvas.py @@ -47,7 +47,7 @@ class canvasitem: - the PS code corresponding to the canvasitem has to be written in the stream file, which provides a write(string) method - writer is the PSwriter used for the output - - context is an instance of pswriter.context which is used for keeping + - context is an instance of pswriter.context which is used for keeping track of the graphics state (current linewidth, colorspace and font)) - registry is used for tracking resources needed by the canvasitem - bbox has to be updated to include the bounding box of the canvasitem @@ -63,7 +63,7 @@ class canvasitem: - writer is the PDFwriter used for the output, which contains properties like whether streamcompression is used - context is an instance of pdfwriter.context which is used for keeping - track of the graphics state, in particular for the emulation of PS + track of the graphics state, in particular for the emulation of PS behaviour regarding fill and stroke styles, for keeping track of the currently selected font as well as of text regions. - registry is used for tracking resources needed by the canvasitem @@ -145,8 +145,8 @@ class _canvas(canvasitem): attr.checkattrs(attrs, [trafo.trafo_pt, clip, style.strokestyle, style.fillstyle]) # We have to reverse the trafos such that the PostScript concat operators # are in the right order. Correspondingly, we below multiply the current self.trafo - # from the right. - # Note that while for the stroke and fill styles the order doesn't matter at all, + # from the right. + # Note that while for the stroke and fill styles the order doesn't matter at all, # this is not true for the clip operation. attrs = attrs[:] attrs.reverse() diff --git a/compiler/gdsMill/pyx/deformer.py b/compiler/gdsMill/pyx/deformer.py index fdde6b67..21289617 100644 --- a/compiler/gdsMill/pyx/deformer.py +++ b/compiler/gdsMill/pyx/deformer.py @@ -1188,7 +1188,7 @@ class parallel(deformer): # <<< intsparams = np[nsp_i][nspitem_i].intersect(np[nsp_j][nspitem_j], epsilon) if intsparams: for intsparam_i, intsparam_j in intsparams: - if ( (abs(intsparam_i) < epsilon and abs(1-intsparam_j) < epsilon) or + if ( (abs(intsparam_i) < epsilon and abs(1-intsparam_j) < epsilon) or (abs(intsparam_j) < epsilon and abs(1-intsparam_i) < epsilon) ): continue npp_i = normpath.normpathparam(np, nsp_i, float(nspitem_i)+intsparam_i) diff --git a/compiler/gdsMill/pyx/dvifile.py b/compiler/gdsMill/pyx/dvifile.py index 06248c89..578a8a0e 100644 --- a/compiler/gdsMill/pyx/dvifile.py +++ b/compiler/gdsMill/pyx/dvifile.py @@ -467,7 +467,7 @@ class font: return fontinfo def __str__(self): - return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name, + return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name, 16.0*self.d/16777216L, 16.0*self.q/16777216L) __repr__ = __str__ @@ -510,7 +510,7 @@ class font: def _convert_tfm_to_ds(self, length): return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv * 1000 / self.getsize_pt() - + def _convert_tfm_to_pt(self, length): return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv @@ -528,7 +528,7 @@ class font: def getitalic_dvi(self, charcode): return self._convert_tfm_to_dvi(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index]) - # routines returning lengths as integers in design size (AFM) units + # routines returning lengths as integers in design size (AFM) units def getwidth_ds(self, charcode): return self._convert_tfm_to_ds(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index]) @@ -767,7 +767,7 @@ class dvifile: if fontslant is not None: fontslant = float(fontslant) - # XXX we currently misuse use self.activefont as metric + # XXX we currently misuse use self.activefont as metric font = type1font.font(fontbasefontname, fontfilename, fontencoding, fontslant, self.activefont) self.activetext = type1font.text_pt(self.pos[_POS_H] * self.pyxconv, -self.pos[_POS_V] * self.pyxconv, font) @@ -973,14 +973,14 @@ class dvifile: den = afile.readuint32() self.mag = afile.readuint32() - # For the interpretation of the lengths in dvi and tfm files, + # For the interpretation of the lengths in dvi and tfm files, # three conversion factors are relevant: # - self.tfmconv: tfm units -> dvi units # - self.pyxconv: dvi units -> (PostScript) points # - self.conv: dvi units -> pixels self.tfmconv = (25400000.0/num)*(den/473628672.0)/16.0 - # calculate conv as described in the DVIType docu using + # calculate conv as described in the DVIType docu using # a given resolution in dpi self.resolution = 300.0 self.conv = (num/254000.0)*(self.resolution/den) diff --git a/compiler/gdsMill/pyx/epsfile.py b/compiler/gdsMill/pyx/epsfile.py index b008d68f..4e74d891 100644 --- a/compiler/gdsMill/pyx/epsfile.py +++ b/compiler/gdsMill/pyx/epsfile.py @@ -319,7 +319,7 @@ class epsfile(canvas.canvasitem): try: epsfile=open(self.filename,"rb") except: - raise IOError, "cannot open EPS file '%s'" % self.filename + raise IOError, "cannot open EPS file '%s'" % self.filename file.write("BeginEPSF\n") @@ -330,7 +330,7 @@ class epsfile(canvas.canvasitem): self.trafo.processPS(file, writer, context, registry, bbox) file.write("%%%%BeginDocument: %s\n" % self.filename) - file.write(epsfile.read()) + file.write(epsfile.read()) file.write("%%EndDocument\n") file.write("EndEPSF\n") diff --git a/compiler/gdsMill/pyx/mathutils.py b/compiler/gdsMill/pyx/mathutils.py index 1b47af12..9250ddf1 100644 --- a/compiler/gdsMill/pyx/mathutils.py +++ b/compiler/gdsMill/pyx/mathutils.py @@ -166,4 +166,4 @@ def realpolyroots(*cs): # else: # rs.append(r) # return rs -# +# diff --git a/compiler/gdsMill/pyx/path.py b/compiler/gdsMill/pyx/path.py index 6352ac59..e12b1aaf 100644 --- a/compiler/gdsMill/pyx/path.py +++ b/compiler/gdsMill/pyx/path.py @@ -710,9 +710,9 @@ class arct_pt(pathitem): # Negative (positive) angles alpha corresponds to a turn to the right (left) # as seen from currentpoint. if dx1*dy2-dy1*dx2 > 0: - alpha = acos(dx1*dx2+dy1*dy2) + alpha = acos(dx1*dx2+dy1*dy2) else: - alpha = -acos(dx1*dx2+dy1*dy2) + alpha = -acos(dx1*dx2+dy1*dy2) try: # two tangent points @@ -744,7 +744,7 @@ class arct_pt(pathitem): return [line, arcn_pt(mx_pt, my_pt, self.r_pt, phi-deltaphi, phi+deltaphi)] except ZeroDivisionError: - # in the degenerate case, we just return a line as specified by the PS + # in the degenerate case, we just return a line as specified by the PS # language reference return [lineto_pt(self.x1_pt, self.y1_pt)] diff --git a/compiler/gdsMill/pyx/pattern.py b/compiler/gdsMill/pyx/pattern.py index a2b0cc1a..e4bed253 100644 --- a/compiler/gdsMill/pyx/pattern.py +++ b/compiler/gdsMill/pyx/pattern.py @@ -48,7 +48,7 @@ class pattern(canvas._canvas, attr.exclusiveattr, style.fillstyle): self.patternbbox = bbox self.patterntrafo = trafo - def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker, + def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker, bbox=_marker, trafo=_marker): if painttype is _marker: painttype = self.painttype diff --git a/compiler/gdsMill/pyx/trafo.py b/compiler/gdsMill/pyx/trafo.py index b9509502..d68177a7 100644 --- a/compiler/gdsMill/pyx/trafo.py +++ b/compiler/gdsMill/pyx/trafo.py @@ -38,7 +38,7 @@ def set(epsilon=None): def _rmatrix(angle): phi = math.pi*angle/180.0 - return ((math.cos(phi), -math.sin(phi)), + return ((math.cos(phi), -math.sin(phi)), (math.sin(phi), math.cos(phi))) def _rvector(angle, x, y): @@ -198,7 +198,7 @@ class trafo(trafo_pt): epsilon=epsilon) # -# some standard transformations +# some standard transformations # class mirror(trafo): @@ -219,7 +219,7 @@ class rotate_pt(trafo_pt): class rotate(trafo_pt): def __init__(self, angle, x=None, y=None, epsilon=_marker): - vector = 0, 0 + vector = 0, 0 if x is not None or y is not None: if x is None or y is None: raise TrafoException("either specify both x and y or none of them") diff --git a/compiler/gdsMill/pyx/unit.py b/compiler/gdsMill/pyx/unit.py index 932eaabb..d1b937c3 100644 --- a/compiler/gdsMill/pyx/unit.py +++ b/compiler/gdsMill/pyx/unit.py @@ -26,7 +26,7 @@ scale = { 't':1, 'u':1, 'v':1, 'w':1, 'x':1 } _default_unit = "cm" -_m = { +_m = { 'm' : 1, 'cm': 0.01, 'mm': 0.001, @@ -51,7 +51,7 @@ def set(uscale=None, vscale=None, wscale=None, xscale=None, defaultunit=None): def _convert_to(l, dest_unit="m"): if type(l) in (types.IntType, types.LongType, types.FloatType): return l * _m[_default_unit] * scale['u'] / _m[dest_unit] - elif not isinstance(l, length): + elif not isinstance(l, length): l = length(l) # convert to length instance if necessary return (l.t + l.u*scale['u'] + l.v*scale['v'] + l.w*scale['w'] + l.x*scale['x']) / _m[dest_unit] diff --git a/compiler/globals.py b/compiler/globals.py index 3ebb2c11..9e05efcb 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -144,18 +144,18 @@ def check_versions(): minor_required = 5 if not (major_python_version == major_required and minor_python_version >= minor_required): debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1) - + # FIXME: Check versions of other tools here?? # or, this could be done in each module (e.g. verify, characterizer, etc.) global OPTS - + try: import coverage OPTS.coverage = 1 except: OPTS.coverage = 0 - + def init_openram(config_file, is_unit_test=True): """ Initialize the technology, paths, simulators, etc. """ @@ -164,7 +164,7 @@ def init_openram(config_file, is_unit_test=True): debug.info(1, "Initializing OpenRAM...") setup_paths() - + read_config(config_file, is_unit_test) import_tech() @@ -188,7 +188,10 @@ def init_openram(config_file, is_unit_test=True): if is_unit_test and CHECKPOINT_OPTS: OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy() return - + + # Setup correct bitcell names + setup_bitcell() + # Import these to find the executables for checkpointing import characterizer import verify @@ -197,22 +200,19 @@ def init_openram(config_file, is_unit_test=True): if not CHECKPOINT_OPTS: CHECKPOINT_OPTS = copy.copy(OPTS) - + def setup_bitcell(): """ Determine the correct custom or parameterized bitcell for the design. """ - global OPTS - # 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"): - + 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" + OPTS.bitcell_name = "cell_6t" else: ports = "" if OPTS.num_rw_ports > 0: @@ -225,6 +225,20 @@ def setup_bitcell(): if ports != "": OPTS.bitcell_suffix = "_" + ports OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix + OPTS.bitcell_name = "cell" + OPTS.bitcell_suffix + + OPTS.dummy_bitcell = "dummy_" + OPTS.bitcell + OPTS.dummy_bitcell_name = "dummy_" + OPTS.bitcell_name + + OPTS.replica_bitcell = "replica_" + OPTS.bitcell + OPTS.replica_bitcell_name = "replica_" + OPTS.bitcell_name + elif (OPTS.bitcell == "pbitcell"): + OPTS.bitcell = "pbitcell" + OPTS.bitcell_name = "pbitcell" + OPTS.dummy_bitcell = "dummy_pbitcell" + OPTS.dummy_bitcell_name = "dummy_pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.replica_bitcell_name = "replica_pbitcell" # See if bitcell exists try: @@ -234,6 +248,11 @@ def setup_bitcell(): # or its custom replica bitcell # Use the pbitcell (and give a warning if not in unit test mode) OPTS.bitcell = "pbitcell" + OPTS.bitcell_name = "pbitcell" + OPTS.dummy_bitcell = "dummy_pbitcell" + OPTS.dummy_bitcell_name = "dummy_pbitcell" + OPTS.replica_bitcell = "replica_pbitcell" + OPTS.replica_bitcell_name = "replica_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)) @@ -269,7 +288,7 @@ def get_tool(tool_type, preferences, default_name=None): else: return(None, "") - + def read_config(config_file, is_unit_test=True): """ Read the configuration file that defines a few parameters. The @@ -282,14 +301,14 @@ def read_config(config_file, is_unit_test=True): # it is already not an abs path, make it one if not os.path.isabs(config_file): config_file = os.getcwd() + "/" + config_file - + # Make it a python file if the base name was only given config_file = re.sub(r'\.py$', "", config_file) - - + + # Expand the user if it is used config_file = os.path.expanduser(config_file) - + OPTS.config_file = config_file + ".py" # Add the path to the system path # so we can import things in the other directory @@ -328,7 +347,7 @@ def read_config(config_file, is_unit_test=True): # If we are only generating a netlist, we can't do DRC/LVS if OPTS.netlist_only: OPTS.check_lvsdrc = False - + # If config didn't set output name, make a reasonable default. if (OPTS.output_name == ""): ports = "" @@ -343,7 +362,7 @@ def read_config(config_file, is_unit_test=True): ports, OPTS.tech_name) - + def end_openram(): """ Clean up openram for a proper exit """ cleanup_paths() @@ -353,8 +372,8 @@ def end_openram(): verify.print_drc_stats() verify.print_lvs_stats() verify.print_pex_stats() - - + + def cleanup_paths(): """ We should clean up the temp directory after execution. @@ -376,8 +395,8 @@ def cleanup_paths(): os.remove(i) else: shutil.rmtree(i) - - + + def setup_paths(): """ Set up the non-tech related paths. """ debug.info(2, "Setting up paths...") @@ -400,7 +419,7 @@ def setup_paths(): debug.check(os.path.isdir(full_path), "$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path)) if "__pycache__" not in full_path: - sys.path.append("{0}".format(full_path)) + sys.path.append("{0}".format(full_path)) if not OPTS.openram_temp.endswith('/'): OPTS.openram_temp += "/" @@ -413,9 +432,9 @@ def is_exe(fpath): def find_exe(check_exe): - """ + """ Check if the binary exists in any path dir - and return the full path. + and return the full path. """ # Check if the preferred spice option exists in the path for path in os.environ["PATH"].split(os.pathsep): @@ -437,7 +456,7 @@ def init_paths(): except OSError as e: if e.errno == 17: # errno.EEXIST os.chmod(OPTS.openram_temp, 0o750) - + # Don't delete the output dir, it may have other files! # make the directory if it doesn't exist try: @@ -448,7 +467,7 @@ def init_paths(): except: debug.error("Unable to make output directory.", -1) - + def set_default_corner(): """ Set the default corner. """ @@ -459,13 +478,13 @@ def set_default_corner(): OPTS.process_corners = ["TT"] else: OPTS.process_corners = tech.spice["fet_models"].keys() - + if (OPTS.supply_voltages == ""): if OPTS.nominal_corner_only: OPTS.supply_voltages = [tech.spice["supply_voltages"][1]] else: OPTS.supply_voltages = tech.spice["supply_voltages"] - + if (OPTS.temperatures == ""): if OPTS.nominal_corner_only: OPTS.temperatures = [tech.spice["temperatures"][1]] @@ -479,8 +498,8 @@ def set_default_corner(): # Load scales are fanout multiples of the default spice input slew if (OPTS.slew_scales == ""): OPTS.slew_scales = [0.25, 1, 8] - - + + def import_tech(): """ Dynamically adds the tech directory to the path and imports it. """ global OPTS @@ -501,7 +520,7 @@ def import_tech(): sys.path.append(tech_path) debug.info(1, "Adding technology path: {}".format(tech_path)) - # Import the tech + # Import the tech try: tech_mod = __import__(OPTS.tech_name) except ImportError: @@ -526,7 +545,7 @@ def import_tech(): def print_time(name, now_time, last_time=None, indentation=2): """ Print a statement about the time delta. """ global OPTS - + # Don't print during testing if not OPTS.is_unit_test or OPTS.debug_level > 0: if last_time: @@ -537,12 +556,12 @@ def print_time(name, now_time, last_time=None, indentation=2): def report_status(): - """ + """ Check for valid arguments and report the - info about the SRAM being generated + info about the SRAM being generated """ global OPTS - + # Check if all arguments are integers for bits, size, banks if type(OPTS.word_size) != int: debug.error("{0} is not an integer in config file.".format(OPTS.word_size)) @@ -550,7 +569,7 @@ def report_status(): 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: @@ -582,10 +601,10 @@ def report_status(): if OPTS.netlist_only: 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 to save run-time (inline_lvsdrc=True to do inline checking).") diff --git a/compiler/custom/and2_dec.py b/compiler/modules/and2_dec.py similarity index 97% rename from compiler/custom/and2_dec.py rename to compiler/modules/and2_dec.py index e6f314c4..258ad0c7 100644 --- a/compiler/custom/and2_dec.py +++ b/compiler/modules/and2_dec.py @@ -18,18 +18,18 @@ class and2_dec(design.design): This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - + design.design.__init__(self, name) - + debug.info(1, "Creating and2_dec {}".format(name)) self.add_comment("size: {}".format(size)) self.size = size self.height = height - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() - + def create_netlist(self): self.add_pins() self.create_modules() @@ -38,14 +38,14 @@ class and2_dec(design.design): def create_modules(self): self.nand = factory.create(module_type="nand2_dec", height=self.height) - + self.inv = factory.create(module_type="inv_dec", height=self.height, size=self.size) - + self.add_mod(self.nand) self.add_mod(self.inv) - + def create_layout(self): if "li" in layer: @@ -54,14 +54,14 @@ class and2_dec(design.design): self.route_layer = "m1" self.width = self.nand.width + self.inv.width self.height = self.nand.height - + self.place_insts() self.add_wires() self.add_layout_pins() self.route_supply_rails() self.add_boundary() self.DRC_LVS() - + def add_pins(self): self.add_pin("A", "INPUT") self.add_pin("B", "INPUT") @@ -73,7 +73,7 @@ class and2_dec(design.design): self.nand_inst = self.add_inst(name="pand2_dec_nand", mod=self.nand) self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) - + self.inv_inst = self.add_inst(name="pand2_dec_inv", mod=self.inv) self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) @@ -100,7 +100,7 @@ class and2_dec(design.design): layer=self.route_layer, offset=vector(0.5 * self.width, self.height), width=self.width) - + def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") @@ -111,7 +111,7 @@ class and2_dec(design.design): mid1_point = vector(z1_pin.cx(), a2_pin.cy()) self.add_path(self.route_layer, [z1_pin.center(), mid1_point, a2_pin.center()]) - + def add_layout_pins(self): pin = self.inv_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Z", @@ -127,7 +127,7 @@ class and2_dec(design.design): offset=pin.center(), width=pin.width(), height=pin.height()) - + 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 = [] @@ -135,13 +135,13 @@ class and2_dec(design.design): 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/custom/and3_dec.py b/compiler/modules/and3_dec.py similarity index 98% rename from compiler/custom/and3_dec.py rename to compiler/modules/and3_dec.py index 207d545b..af8a1419 100644 --- a/compiler/custom/and3_dec.py +++ b/compiler/modules/and3_dec.py @@ -23,7 +23,7 @@ class and3_dec(design.design): self.add_comment("size: {}".format(size)) self.size = size self.height = height - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -52,14 +52,14 @@ class and3_dec(design.design): self.width = self.nand.width + self.inv.width self.height = self.nand.height - + self.place_insts() self.add_wires() self.add_layout_pins() self.route_supply_rails() self.add_boundary() self.DRC_LVS() - + def add_pins(self): self.add_pin("A", "INPUT") self.add_pin("B", "INPUT") @@ -72,7 +72,7 @@ class and3_dec(design.design): self.nand_inst = self.add_inst(name="pand3_dec_nand", mod=self.nand) self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"]) - + self.inv_inst = self.add_inst(name="pand3_dec_inv", mod=self.inv) self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) @@ -99,7 +99,7 @@ class and3_dec(design.design): layer=self.route_layer, offset=vector(0.5 * self.width, self.height), width=self.width) - + def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") @@ -136,7 +136,7 @@ class and3_dec(design.design): 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 = [] @@ -144,13 +144,13 @@ class and3_dec(design.design): 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/custom/and4_dec.py b/compiler/modules/and4_dec.py similarity index 98% rename from compiler/custom/and4_dec.py rename to compiler/modules/and4_dec.py index 211e4ce4..e7a8446e 100644 --- a/compiler/custom/and4_dec.py +++ b/compiler/modules/and4_dec.py @@ -18,14 +18,14 @@ class and4_dec(design.design): This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - + design.design.__init__(self, name) - + debug.info(1, "Creating and4_dec {}".format(name)) self.add_comment("size: {}".format(size)) self.size = size self.height = height - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -61,7 +61,7 @@ class and4_dec(design.design): self.route_supply_rails() self.add_boundary() self.DRC_LVS() - + def add_pins(self): self.add_pin("A", "INPUT") self.add_pin("B", "INPUT") @@ -75,7 +75,7 @@ class and4_dec(design.design): self.nand_inst = self.add_inst(name="pand4_dec_nand", mod=self.nand) self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"]) - + self.inv_inst = self.add_inst(name="pand4_dec_inv", mod=self.inv) self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) @@ -102,7 +102,7 @@ class and4_dec(design.design): layer=self.route_layer, offset=vector(0.5 * self.width, self.height), width=self.width) - + def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") @@ -139,7 +139,7 @@ class and4_dec(design.design): 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 = [] @@ -147,13 +147,13 @@ class and4_dec(design.design): 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/modules/bank.py b/compiler/modules/bank.py index bede24a1..3bd3a2e9 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -31,16 +31,16 @@ class bank(design.design): self.num_wmasks = int(ceil(self.word_size / self.write_size)) else: self.num_wmasks = 0 - + if not self.num_spare_cols: self.num_spare_cols = 0 - + if name == "": name = "bank_{0}_{1}".format(self.word_size, self.num_words) super().__init__(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. @@ -62,12 +62,12 @@ class bank(design.design): self.add_modules() self.add_pins() # Must create the replica bitcell array first self.create_instances() - + def create_layout(self): self.place_instances() self.setup_routing_constraints() self.route_layout() - + # Can remove the following, but it helps for debug! # self.add_lvs_correspondence_points() @@ -110,13 +110,13 @@ class bank(design.design): self.add_pin("wl_en{0}".format(port), "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - + def route_layout(self): """ Create routing amoung the modules """ self.route_central_bus() self.route_unused_wordlines() - + for port in self.all_ports: self.route_bitlines(port) self.route_rbl(port) @@ -125,7 +125,7 @@ class bank(design.design): self.route_control_lines(port) if self.num_banks > 1: self.route_bank_select(port) - + self.route_supplies() def route_rbl(self, port): @@ -149,7 +149,7 @@ class bank(design.design): layer="m3", start=left_right_offset, end=pin_offset) - + def route_bitlines(self, port): """ Route the bitlines depending on the port type rw, w, or r. """ @@ -158,7 +158,7 @@ class bank(design.design): if port in self.read_ports: self.route_port_data_out(port) self.route_port_data_to_bitcell_array(port) - + def create_instances(self): """ Create the instances of the netlist. """ @@ -185,7 +185,7 @@ class bank(design.design): # 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.get_main_array_top() # Just past the dummy column @@ -213,7 +213,7 @@ class bank(design.design): """ port = 0 - + # UPPER RIGHT QUADRANT # Bitcell array is placed at (0,0) self.bitcell_array_offset = vector(0, 0) @@ -258,14 +258,14 @@ class bank(design.design): """ port=1 - + # LOWER LEFT QUADRANT # Bitcell array is placed at (0,0) # UPPER LEFT QUADRANT # Above the bitcell array self.port_data_offsets[port] = vector(0, self.bitcell_array_top) - + # LOWER RIGHT QUADRANT # To the right of the bitcell array x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap @@ -294,12 +294,12 @@ class bank(design.design): else: y_offset = self.port_address_offsets[port].y self.bank_select_offsets[port] = vector(x_offset, y_offset) - + def place_instances(self): """ Place the instances. """ self.compute_instance_offsets() - + self.place_bitcell_array(self.bitcell_array_offset) self.place_port_data(self.port_data_offsets) @@ -308,7 +308,7 @@ class bank(design.design): self.place_column_decoder(self.column_decoder_offsets) self.place_bank_select(self.bank_select_offsets) - + def compute_sizes(self): """ Computes the required sizes to create the bank """ @@ -351,7 +351,7 @@ class bank(design.design): self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]]) else: self.control_signals.append(self.input_control_signals[port]) - + # The central bus is the column address (one hot) and row address (binary) if self.col_addr_size>0: @@ -379,7 +379,7 @@ class bank(design.design): local_array_size = OPTS.local_array_size except AttributeError: local_array_size = 0 - + if local_array_size > 0: # Find the even multiple that satisfies the fanout with equal sized local arrays total_cols = self.num_cols + self.num_spare_cols @@ -406,11 +406,11 @@ class bank(design.design): bit_offsets=self.bit_offsets) self.port_data.append(temp_pre) self.add_mod(self.port_data[port]) - + if(self.num_banks > 1): self.bank_select = factory.create(module_type="bank_select") self.add_mod(self.bank_select) - + def create_bitcell_array(self): """ Creating Bitcell Array """ self.bitcell_array_inst=self.add_inst(name="bitcell_array", @@ -426,12 +426,12 @@ class bank(design.design): temp.extend(self.bitcell_array.get_wordline_names()) if len(self.all_ports) > 1: temp.append("rbl_wl1") - + temp.append("vdd") temp.append("gnd") self.connect_inst(temp) - + def place_bitcell_array(self, offset): """ Placing Bitcell Array """ self.bitcell_array_inst.place(offset) @@ -470,7 +470,7 @@ class bank(design.design): 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: @@ -481,7 +481,7 @@ class bank(design.design): def create_port_address(self): """ Create the hierarchical row decoder """ - + self.port_address_inst = [None] * len(self.all_ports) for port in self.all_ports: self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port), @@ -496,32 +496,32 @@ class bank(design.design): temp.append("rbl_wl{}".format(port)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - + 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.") - + # The address and control bus will be in between decoder and the main memory array # This bus will route address bits to the decoder input and column mux inputs. # The wires are actually routed after we placed the stuff on both sides. # The predecoder is below the x-axis and the main decoder is above the x-axis # The address flop and decoder are aligned in the x coord. - + for port in self.all_ports: if port % 2: mirror = "MY" else: mirror = "R0" self.port_address_inst[port].place(offset=offsets[port], mirror=mirror) - + def create_column_decoder(self): """ Create a 2:4 or 3:8 column address decoder. """ self.dff =factory.create(module_type="dff") - + if self.col_addr_size == 0: return elif self.col_addr_size == 1: @@ -541,7 +541,7 @@ class bank(design.design): # No error checking before? debug.error("Invalid column decoder?", -1) self.add_mod(self.column_decoder) - + self.column_decoder_inst = [None] * len(self.all_ports) for port in self.all_ports: self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port), @@ -554,14 +554,14 @@ class bank(design.design): temp.append("sel{0}_{1}".format(port, bit)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - + def place_column_decoder(self, offsets): """ Place a 2:4 or 3:8 column address decoder. """ if self.col_addr_size == 0: return - + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.") @@ -571,7 +571,7 @@ class bank(design.design): else: mirror = "R0" self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror) - + def create_bank_select(self): """ Create the bank select logic. """ @@ -582,7 +582,7 @@ class bank(design.design): for port in self.all_ports: self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port), mod=self.bank_select) - + temp = [] temp.extend(self.input_control_signals[port]) temp.append("bank_sel{}".format(port)) @@ -601,7 +601,7 @@ class bank(design.design): for port in self.all_ports: self.bank_select_inst[port].place(offsets[port]) - + def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ # Copy only the power pins already on the power layer @@ -624,7 +624,7 @@ class bank(design.design): def route_bank_select(self, port): """ Route the bank select logic. """ - + if self.port_id[port] == "rw": 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"] @@ -634,11 +634,11 @@ class bank(design.design): 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"] - + copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)] for signal in range(len(copy_control_signals)): self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal]) - + for signal in range(len(gated_bank_sel_signals)): # Connect the inverter output to the central bus out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc() @@ -651,7 +651,7 @@ class bank(design.design): offset=out_pos) self.add_via_center(layers=self.m2_stack, offset=out_pos) - + def setup_routing_constraints(self): """ After the modules are instantiated, find the dimensions for the @@ -660,7 +660,7 @@ class bank(design.design): self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width self.min_y_offset = min([x.by() for x in self.insts]) - + self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width self.min_x_offset = min([x.lx() for x in self.insts]) @@ -668,7 +668,7 @@ class bank(design.design): ur = vector(self.max_x_offset, self.max_y_offset) ll = vector(self.min_x_offset, self.min_y_offset) self.core_bbox = [ll, ur] - + self.height = ur.y - ll.y self.width = ur.x - ll.x @@ -692,7 +692,7 @@ class bank(design.design): vertical=True, make_pins=(self.num_banks==1), pitch=self.m3_pitch) - + # Port 1 if len(self.all_ports)==2: # The other control bus is routed up to two pitches above the bitcell array @@ -731,12 +731,12 @@ class bank(design.design): inst1_br_name=inst1_br_name, inst2_bl_name=inst2_bl_name, inst2_br_name=inst2_br_name) - + # Connect the replica bitlines for (array_name, data_name) in zip(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)], ["rbl_bl", "rbl_br"]): self.connect_bitline(inst1, inst2, array_name, data_name) - + def route_port_data_out(self, port): """ Add pins for the port data out """ @@ -747,7 +747,7 @@ class bank(design.design): offset=data_pin.center(), height=data_pin.height(), width=data_pin.width()) - + def route_port_address_in(self, port): """ Routes the row decoder inputs and supplies """ @@ -771,19 +771,19 @@ class bank(design.design): 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) - + for col in range(self.num_spare_cols): sparecol_name = "bank_spare_wen{}".format(col) bank_sparecol_name = "bank_spare_wen{0}_{1}".format(port, col) self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name) - + def channel_route_bitlines(self, inst1, inst2, num_bits, inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst2_bl_name="bl_{}", inst2_br_name="br_{}"): """ 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(): @@ -801,14 +801,14 @@ class bank(design.design): top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))] route_map = list(zip(bottom_names, top_names)) self.create_horizontal_channel_route(route_map, offset, self.m1_stack) - + def connect_bitline(self, inst1, inst2, inst1_name, inst2_name): """ 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). """ - + # determine top and bottom automatically. # since they don't overlap, we can just check the bottom y coordinate. if inst1.by() < inst2.by(): @@ -821,17 +821,17 @@ class bank(design.design): 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 connect_bitlines(self, inst1, inst2, inst1_bl_name, inst1_br_name, inst2_bl_name, inst2_br_name): @@ -847,12 +847,12 @@ class bank(design.design): """ Connect Wordline driver to bitcell array wordline """ self.route_port_address_in(port) - + if port % 2: self.route_port_address_out(port, "right") else: self.route_port_address_out(port, "left") - + def route_port_address_out(self, port, side="left"): """ Connecting Wordline driver output to Bitcell WL connection """ @@ -866,7 +866,7 @@ class bank(design.design): else: driver_wl_pos = driver_wl_pin.lc() bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name) - + if side == "left": bitcell_wl_pos = bitcell_wl_pin.lc() port_address_pos = self.port_address_inst[port].rx() @@ -875,7 +875,7 @@ class bank(design.design): bitcell_wl_pos = bitcell_wl_pin.rc() port_address_pos = self.port_address_inst[port].lx() bitcell_array_pos = self.bitcell_array_inst.rx() - + mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0) mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1) if driver_wl_pin.layer != bitcell_wl_pin.layer: @@ -886,7 +886,7 @@ class bank(design.design): self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos]) else: self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) - + def route_port_address_right(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ @@ -906,7 +906,7 @@ class bank(design.design): to_layer=bitcell_wl_pin.layer, offset=mid2) self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos]) - + def route_column_address_lines(self, port): """ Connecting the select lines of column mux to the address bus """ if not self.col_addr_size>0: @@ -914,15 +914,15 @@ class bank(design.design): stack = getattr(self, layer_props.bank.stack) pitch = getattr(self, layer_props.bank.pitch) - + if self.col_addr_size == 1: - + # Connect to sel[0] and sel[1] decode_names = ["Zb", "Z"] - + # The Address LSB self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) - + elif self.col_addr_size > 1: decode_names = [] for i in range(self.num_col_addr_lines): @@ -942,7 +942,7 @@ class bank(design.design): sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] 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, @@ -964,7 +964,7 @@ class bank(design.design): # self.add_label(text=wl_name, # layer="m1", # offset=wl_pin.center()) - + # # Add the bitline names # for i in range(self.num_cols): # bl_name = "bl_{}".format(i) @@ -1025,10 +1025,10 @@ class bank(design.design): # Add a path to connect to the array self.add_path(pin_layer, [left_loc, left_pin_loc]) self.add_path(pin_layer, [right_loc, right_pin_loc]) - + def route_control_lines(self, port): """ Route the control lines of the entire bank """ - + # Make a list of tuples that we will connect. # From control signal to the module pin # Connection from the central bus to the main control block crosses @@ -1040,7 +1040,7 @@ class bank(design.design): if port in self.write_ports: connection.append((self.prefix + "w_en{}".format(port), self.port_data_inst[port].get_pin("w_en"))) - + if port in self.read_ports: connection.append((self.prefix + "s_en{}".format(port), self.port_data_inst[port].get_pin("s_en"))) @@ -1057,7 +1057,7 @@ class bank(design.design): self.add_via_stack_center(from_layer=pin.layer, to_layer="m2", offset=control_pos) - + # clk to wordline_driver control_signal = self.prefix + "wl_en{}".format(port) if port % 2: @@ -1081,7 +1081,7 @@ class bank(design.design): 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. @@ -1097,8 +1097,8 @@ class bank(design.design): self.bitcell_array.graph_exclude_bits(targ_row, targ_col) def clear_exclude_bits(self): - """ + """ Clears the bit exclusions """ self.bitcell_array.clear_exclude_bits() - + diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 8a776d30..aa80a875 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -27,7 +27,7 @@ class bank_select(design.design): super().__init__(name) self.port = port - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -36,7 +36,7 @@ class bank_select(design.design): self.add_pins() self.add_modules() self.create_instances() - + def create_layout(self): self.calculate_module_offsets() self.place_instances() @@ -44,12 +44,12 @@ class bank_select(design.design): self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width self.width = max([x.rx() for x in self.inv_inst]) - + self.add_boundary() self.DRC_LVS() def add_pins(self): - + # Number of control lines in the bus if self.port == "rw": self.num_control_lines = 4 @@ -86,7 +86,7 @@ class bank_select(design.design): self.nor2 = factory.create(module_type="pnor2", height=height) self.add_mod(self.nor2) - + self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4) self.add_mod(self.inv4x_nor) @@ -94,15 +94,15 @@ class bank_select(design.design): self.add_mod(self.nand2) def calculate_module_offsets(self): - + self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell") self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell") self.xoffset_bank_sel_inv = 0 self.xoffset_inputs = 0 self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height - + def create_instances(self): - + self.bank_sel_inv=self.add_inst(name="bank_sel_inv", mod=self.inv_sel) self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) @@ -119,7 +119,7 @@ class bank_select(design.design): # These require OR (nor2+inv) gates since they are active low. # (writes occur on clk low) if input_name in ("clk_buf"): - + self.logic_inst.append(self.add_inst(name=name_nor, mod=self.nor2)) self.connect_inst([input_name, @@ -127,7 +127,7 @@ class bank_select(design.design): gated_name + "_temp_bar", "vdd", "gnd"]) - + # They all get inverters on the output self.inv_inst.append(self.add_inst(name=name_inv, mod=self.inv4x_nor)) @@ -135,7 +135,7 @@ class bank_select(design.design): gated_name, "vdd", "gnd"]) - + # the rest are AND (nand2+inv) gates else: self.logic_inst.append(self.add_inst(name=name_nand, @@ -155,7 +155,7 @@ class bank_select(design.design): "gnd"]) def place_instances(self): - + # bank select inverter self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0) @@ -166,27 +166,27 @@ class bank_select(design.design): logic_inst = self.logic_inst[i] inv_inst = self.inv_inst[i] - + input_name = self.input_control_signals[i] if i == 0: y_offset = 0 else: y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1) - + if i % 2: y_offset += self.inv4x.height mirror = "MX" else: mirror = "" - + # These require OR (nor2+inv) gates since they are active low. # (writes occur on clk low) if input_name in ("clk_buf"): - + logic_inst.place(offset=[self.xoffset_nor, y_offset], mirror=mirror) - + # the rest are AND (nand2+inv) gates else: logic_inst.place(offset=[self.xoffset_nand, y_offset], @@ -197,7 +197,7 @@ class bank_select(design.design): mirror=mirror) def route_instances(self): - + # bank_sel is vertical wire bank_sel_inv_pin = self.bank_sel_inv.get_pin("A") xoffset_bank_sel = bank_sel_inv_pin.lx() @@ -227,19 +227,19 @@ class bank_select(design.design): height=self.inv4x.height) self.add_via_center(layers=self.m1_stack, offset=bank_sel_bar_pin.rc()) - + for i in range(self.num_control_lines): logic_inst = self.logic_inst[i] inv_inst = self.inv_inst[i] - + input_name = self.input_control_signals[i] gated_name = self.control_signals[i] if input_name in ("clk_buf"): xoffset_bank_signal = xoffset_bank_sel_bar else: xoffset_bank_signal = xoffset_bank_sel - + # Connect the logic output to inverter input out_pin = logic_inst.get_pin("Z") out_pos = out_pin.center() @@ -248,7 +248,7 @@ class bank_select(design.design): mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y) mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y) self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos]) - + # Connect the logic B input to bank_sel / bank_sel_bar logic_pin = logic_inst.get_pin("B") logic_pos = logic_pin.center() @@ -304,7 +304,7 @@ class bank_select(design.design): self.add_layout_pin_rect_center(text=n, layer="m3", offset=pin_pos) - + # Add vdd/gnd supply rails gnd_pin = self.inv_inst[num].get_pin("gnd") left_gnd_pos = vector(0, gnd_pin.cy()) @@ -312,7 +312,7 @@ class bank_select(design.design): layer="m1", start=left_gnd_pos, end=gnd_pin.rc()) - + vdd_pin = self.inv_inst[num].get_pin("vdd") left_vdd_pos = vector(0, vdd_pin.cy()) self.add_layout_pin_segment_center(text="vdd", diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 65470726..f586e9ed 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -25,11 +25,11 @@ class bitcell_array(bitcell_base_array): # This will create a default set of bitline/wordline names self.create_all_bitline_names() self.create_all_wordline_names() - + 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() diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 6ceff7ca..86ee5d43 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -47,11 +47,11 @@ class bitcell_base_array(design.design): "br_{0}_{1}".format(port, col)]) # Make a flat list too self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl] - + def create_all_wordline_names(self, row_size=None): if row_size == None: row_size = self.row_size - + for row in range(row_size): for port in self.all_ports: self.wordline_names[port].append("wl_{0}_{1}".format(port, row)) @@ -69,7 +69,7 @@ class bitcell_base_array(design.design): def get_bitcell_pins(self, row, col): """ Creates a list of connections in the bitcell, - indexed by column and row, for instance use in bitcell_array + indexed by column and row, for instance use in bitcell_array """ bitcell_pins = [] for port in self.all_ports: @@ -81,7 +81,7 @@ class bitcell_base_array(design.design): return bitcell_pins def get_rbl_wordline_names(self, port=None): - """ + """ Return the WL for the given RBL port. """ if port == None: @@ -102,7 +102,7 @@ class bitcell_base_array(design.design): return self.all_bitline_names else: return self.bitline_names[port] - + def get_all_bitline_names(self, port=None): """ Return ALL the bitline names (including rbl) """ temp = [] @@ -121,7 +121,7 @@ class bitcell_base_array(design.design): return self.all_wordline_names else: return self.wordline_names[port] - + def get_all_wordline_names(self, port=None): """ Return all the wordline names """ temp = [] @@ -133,7 +133,7 @@ class bitcell_base_array(design.design): if len(self.all_ports) > 1: temp.extend(self.get_rbl_wordline_names(1)) return temp - + def add_layout_pins(self): """ Add the layout pins """ bitline_names = self.cell.get_all_bitline_names() @@ -161,7 +161,7 @@ class bitcell_base_array(design.design): offset=wl_pin.ll().scale(0, 1), width=self.width, height=wl_pin.height()) - + # Copy a vdd/gnd layout pin from every cell for row in range(self.row_size): for col in range(self.column_size): diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 24ecc640..8aaff142 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -34,7 +34,7 @@ class col_cap_array(bitcell_base_array): if not end_caps_enabled: self.create_all_wordline_names() self.create_all_bitline_names() - + self.add_modules() self.add_pins() self.create_instances() diff --git a/compiler/modules/column_mux_array.py b/compiler/modules/column_mux_array.py index 61c71672..2f1ee56e 100644 --- a/compiler/modules/column_mux_array.py +++ b/compiler/modules/column_mux_array.py @@ -46,7 +46,7 @@ class column_mux_array(design.design): # self.sel_layer = "m1" # self.sel_pitch = self.m2_pitch # self.bitline_layer = "m2" - + if preferred_directions[self.sel_layer] == "V": self.via_directions = ("H", "H") else: @@ -125,7 +125,7 @@ class column_mux_array(design.design): # Default to single spaced columns if not self.offsets: self.offsets = [n * self.mux.width for n in range(self.columns)] - + # For every column, add a pass gate for col_num, xoffset in enumerate(self.offsets[0:self.columns]): if cell_props.bitcell.mirror.y and (col_num + self.column_offset) % 2: @@ -209,7 +209,7 @@ class column_mux_array(design.design): br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc() bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.sel_pitch) br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.sel_pitch) - + self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end]) self.add_path(self.sel_layer, [br_out_offset_begin, br_out_offset_end]) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 7f9035d9..b0620a38 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -28,7 +28,7 @@ class control_logic(design.design): self.add_comment("num_rows: {0}".format(num_rows)) self.add_comment("words_per_row: {0}".format(words_per_row)) self.add_comment("word_size {0}".format(word_size)) - + self.sram=sram self.num_rows = num_rows self.words_per_row = words_per_row @@ -42,21 +42,21 @@ class control_logic(design.design): self.num_cols = word_size * words_per_row + self.num_spare_cols self.num_words = num_rows * words_per_row - + 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. # FIXME: This should be made a parameter self.wl_timing_tolerance = 1 self.wl_stage_efforts = None self.sen_stage_efforts = None - + if self.port_type == "rw": self.num_control_signals = 2 else: self.num_control_signals = 1 - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -66,7 +66,7 @@ class control_logic(design.design): self.add_pins() self.add_modules() self.create_instances() - + def create_layout(self): """ Create layout and route between modules """ self.place_instances() @@ -84,16 +84,16 @@ class control_logic(design.design): def add_modules(self): """ Add all the required modules """ - + self.dff = factory.create(module_type="dff_buf") dff_height = self.dff.height - + self.ctrl_dff_array = factory.create(module_type="dff_buf_array", rows=self.num_control_signals, columns=1) - + self.add_mod(self.ctrl_dff_array) - + self.and2 = factory.create(module_type="pand2", size=12, height=dff_height) @@ -103,7 +103,7 @@ class control_logic(design.design): size=self.num_cols, height=dff_height) self.add_mod(self.rbl_driver) - + # 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 @@ -114,13 +114,13 @@ class control_logic(design.design): 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, @@ -144,7 +144,7 @@ class control_logic(design.design): size=1, height=dff_height) self.add_mod(self.inv) - + # 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 @@ -183,14 +183,14 @@ class control_logic(design.design): # Fanout can be varied as well but is a little more complicated but potentially optimal. debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay)) return (delay_stages, delay_fanout) - + 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_per_stage = previous_fanout + 1 + self.inv_parasitic_delay previous_delay_chain_delay = previous_delay_per_stage * 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 # The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each # inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value @@ -201,7 +201,7 @@ class control_logic(design.design): debug.info(2, "Required delays from chain: fall={}, rise={}".format(required_delay_fall, required_delay_rise)) - + # If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic. WARNING_FANOUT_DIFF = 5 stages_close = False @@ -218,7 +218,7 @@ class control_logic(design.design): stages_close = True safe_fanout_rise = fanout_rise safe_fanout_fall = fanout_fall - + if stages_fall == stages_rise: break elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise): @@ -232,14 +232,14 @@ class control_logic(design.design): fanout_fall+=1 else: fanout_rise+=1 - + total_stages = max(stages_fall, stages_rise) * 2 debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall)) - + # Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage. stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)] return stage_list - + 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 @@ -249,7 +249,7 @@ class control_logic(design.design): delay_per_stage = fanout + 1 + self.inv_parasitic_delay delay_stages = ceil(required_delay / delay_per_stage) return delay_stages - + def setup_signal_busses(self): """ Setup bus names, determine the size of the busses etc """ @@ -265,7 +265,7 @@ class control_logic(design.design): self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"] else: self.dff_output_list = ["cs_bar", "cs"] - + # list of output control signals (for making a vertical bus) if self.port_type == "rw": self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "we_bar", "clk_buf", "cs"] @@ -275,7 +275,7 @@ class control_logic(design.design): 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"] @@ -286,14 +286,14 @@ class control_logic(design.design): self.output_list.append("p_en_bar") self.output_list.append("wl_en") self.output_list.append("clk_buf") - + self.supply_list = ["vdd", "gnd"] def route_rails(self): """ Add the input signal inverted tracks """ height = self.control_logic_center.y - self.m2_pitch offset = vector(self.ctrl_dff_array.width, 0) - + self.input_bus = self.create_vertical_bus("m2", offset, self.internal_bus_list, @@ -325,7 +325,7 @@ class control_logic(design.design): # All of the control logic is placed to the right of the DFFs and bus self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width - + row = 0 # Add the logic on the right of the bus self.place_clk_buf_row(row) @@ -396,7 +396,7 @@ class control_logic(design.design): # Add to the right of the control rows and routing channel 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() @@ -408,21 +408,21 @@ class control_logic(design.design): self.add_wire(self.m1_stack, [out_pos, mid1, in_pos]) self.add_via_center(layers=self.m1_stack, 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): """ Create the multistage and gated clock buffer """ self.clk_buf_inst = self.add_inst(name="clkbuf", mod=self.clk_buf_driver) self.connect_inst(["clk", "clk_buf", "vdd", "gnd"]) - + def place_clk_buf_row(self, row): x_offset = self.control_x_offset - + x_offset = self.place_util(self.clk_buf_inst, x_offset, row) - + self.row_end_inst.append(self.clk_buf_inst) def route_clk_buf(self): @@ -443,17 +443,17 @@ class control_logic(design.design): self.clk_bar_inst = self.add_inst(name="inv_clk_bar", mod=self.inv) self.connect_inst(["clk_buf", "clk_bar", "vdd", "gnd"]) - + self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar", mod=self.and2) self.connect_inst(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"]) def place_gated_clk_bar_row(self, row): x_offset = self.control_x_offset - + 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) def route_gated_clk_bar(self): @@ -468,7 +468,7 @@ class control_logic(design.design): self.add_via_stack_center(from_layer=out_pin.layer, to_layer=in_pin.layer, offset=in_pos) - + # This is the second gate over, so it needs to be on M3 clkbuf_map = zip(["B"], ["cs"]) @@ -495,9 +495,9 @@ class control_logic(design.design): 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) - + def route_gated_clk_buf(self): clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"]) self.connect_vertical_bus(clkbuf_map, @@ -514,7 +514,7 @@ class control_logic(design.design): self.add_via_stack_center(from_layer=z_pin.layer, to_layer="m2", offset=z_pin.center()) - + def create_wlen_row(self): # input pre_p_en, output: wl_en self.wl_en_inst=self.add_inst(name="buf_wl_en", @@ -531,7 +531,7 @@ class control_logic(design.design): def route_wlen(self): wlen_map = zip(["A"], ["gated_clk_bar"]) self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.input_bus) - + self.connect_output(self.wl_en_inst, "Z", "wl_en") def create_pen_row(self): @@ -544,7 +544,7 @@ class control_logic(design.design): 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_offset = self.control_x_offset @@ -568,7 +568,7 @@ class control_logic(design.design): offset=in_pin.center()) self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar") - + def create_sen_row(self): """ Create the sense enable buffer. """ if self.port_type=="rw": @@ -582,24 +582,24 @@ class control_logic(design.design): # we also must wait until the bitline has been discharged enough for proper sensing # hence we use rbl_bl_delay as well. self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"]) - + def place_sen_row(self, row): x_offset = self.control_x_offset x_offset = self.place_util(self.s_en_gate_inst, x_offset, row) - + self.row_end_inst.append(self.s_en_gate_inst) - + def route_sen(self): if self.port_type=="rw": input_name = "we_bar" else: input_name = "cs" - + 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.input_bus) - + self.connect_output(self.s_en_gate_inst, "Z", "s_en") def create_rbl_delay_row(self): @@ -612,15 +612,15 @@ class control_logic(design.design): 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 self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar") - + rbl_map = zip(["A"], ["rbl_bl_delay"]) self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.input_bus) @@ -638,26 +638,26 @@ class control_logic(design.design): mod=self.wen_and) # Only drive the writes in the second half of the clock cycle during a write operation. self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"]) - + def place_wen_row(self, row): 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_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", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"]) self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus) 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", mod=self.ctrl_dff_array) @@ -669,7 +669,7 @@ class control_logic(design.design): def place_dffs(self): self.ctrl_dff_inst.place(vector(0, 0)) - + 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"]) @@ -678,7 +678,7 @@ class control_logic(design.design): else: dff_out_map = zip(["dout_bar_0"], ["cs"]) self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.input_bus, self.m2_stack[::-1]) - + # Connect the clock rail to the other clock rail # by routing in the supply rail track to avoid channel conflicts in_pos = self.ctrl_dff_inst.get_pin("clk").uc() @@ -691,7 +691,7 @@ class control_logic(design.design): self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb") if (self.port_type == "rw"): self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web") - + def get_offset(self, row): """ Compute the y-offset and mirroring """ y_off = row * self.and2.height @@ -702,14 +702,14 @@ class control_logic(design.design): mirror="R0" return (y_off, mirror) - + def connect_output(self, inst, pin_name, out_name): """ Create an output pin on the right side from the pin of a given instance. """ - + out_pin = inst.get_pin(pin_name) out_pos = out_pin.center() right_pos = out_pos + vector(self.width - out_pin.cx(), 0) - + self.add_via_stack_center(from_layer=out_pin.layer, to_layer="m2", offset=out_pos) @@ -722,7 +722,7 @@ class control_logic(design.design): """ Add vdd and gnd to the instance cells """ supply_layer = self.dff.get_pin("vdd").layer - + max_row_x_loc = max([inst.rx() for inst in self.row_end_inst]) for inst in self.row_end_inst: pins = inst.get_pins("vdd") @@ -740,13 +740,13 @@ class control_logic(design.design): pin_loc = vector(max_row_x_loc, pin.rc().y) self.add_power_pin("gnd", pin_loc, start_layer=pin.layer) self.add_path(supply_layer, [row_loc, pin_loc]) - + 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") - + 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 @@ -772,10 +772,10 @@ class control_logic(design.design): offset=pin.ll(), height=pin.height(), width=pin.width()) - + 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) @@ -799,4 +799,4 @@ class control_logic(design.design): self.add_via_stack_center(from_layer=out_pin.layer, to_layer="m2", offset=out_pos) - + diff --git a/compiler/modules/custom_cell.py b/compiler/modules/custom_cell.py deleted file mode 100644 index 2591de76..00000000 --- a/compiler/modules/custom_cell.py +++ /dev/null @@ -1,271 +0,0 @@ -# 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 -from sram_factory import factory -from vector import vector -from globals import OPTS - - -class custom_cell(design.design): - """ - Array of tristate drivers to write to the bitlines through the column mux. - Dynamically generated write driver array of all bitlines. - """ - - def __init__(self, name, pins, mod): - - 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.columns = columns - self.word_size = word_size - self.write_size = write_size - self.column_offset = column_offset - self.words_per_row = int(columns / word_size) - if not num_spare_cols: - self.num_spare_cols = 0 - else: - self.num_spare_cols = num_spare_cols - - 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() - - def get_bl_name(self): - bl_name = "bl" - return bl_name - - def get_br_name(self): - br_name = "br" - return br_name - - @property - def data_name(self): - return "data" - - @property - def en_name(self): - return "en" - - def create_netlist(self): - self.add_modules() - self.add_pins() - self.create_write_array() - - def create_layout(self): - - if self.bitcell.width > self.driver.width: - self.width = (self.columns + self.num_spare_cols) * self.bitcell.width - self.width_regular_cols = self.columns * self.bitcell.width - self.single_col_width = self.bitcell.width - else: - self.width = (self.columns + self.num_spare_cols) * self.driver.width - self.width_regular_cols = self.columns * self.driver.width - self.single_col_width = 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.num_spare_cols): - self.add_pin(self.data_name + "_{0}".format(i), "INPUT") - for i in range(self.word_size + self.num_spare_cols): - self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT") - self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT") - if self.write_size: - for i in range(self.num_wmasks + self.num_spare_cols): - self.add_pin(self.en_name + "_{0}".format(i), "INPUT") - elif self.num_spare_cols and not self.write_size: - for i in range(self.num_spare_cols + 1): - self.add_pin(self.en_name + "_{0}".format(i), "INPUT") - else: - self.add_pin(self.en_name, "INPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - - def add_modules(self): - self.driver = factory.create(module_type="write_driver") - self.add_mod(self.driver) - - # This is just used for measurements, - # so don't add the module - self.bitcell = factory.create(module_type="bitcell") - - 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) - - if self.write_size: - self.connect_inst([self.data_name + "_{0}".format(index), - self.get_bl_name() + "_{0}".format(index), - self.get_br_name() + "_{0}".format(index), - self.en_name + "_{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 - - elif self.num_spare_cols and not self.write_size: - self.connect_inst([self.data_name + "_{0}".format(index), - self.get_bl_name() + "_{0}".format(index), - self.get_br_name() + "_{0}".format(index), - self.en_name + "_{0}".format(0), "vdd", "gnd"]) - - else: - self.connect_inst([self.data_name + "_{0}".format(index), - self.get_bl_name() + "_{0}".format(index), - self.get_br_name() + "_{0}".format(index), - self.en_name, "vdd", "gnd"]) - - for i in range(self.num_spare_cols): - index = self.word_size + i - if self.write_size: - offset = self.num_wmasks - else: - offset = 1 - name = "write_driver{}".format(self.columns + i) - self.driver_insts[index]=self.add_inst(name=name, - mod=self.driver) - - self.connect_inst([self.data_name + "_{0}".format(index), - self.get_bl_name() + "_{0}".format(index), - self.get_br_name() + "_{0}".format(index), - self.en_name + "_{0}".format(i + offset), "vdd", "gnd"]) - - def place_write_array(self): - from tech import cell_properties - if self.bitcell.width > self.driver.width: - self.driver_spacing = self.bitcell.width - else: - self.driver_spacing = self.driver.width - for i in range(0, self.columns, self.words_per_row): - index = int(i / self.words_per_row) - xoffset = i * self.driver_spacing - - if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: - mirror = "MY" - xoffset = xoffset + self.driver.width - else: - mirror = "" - - base = vector(xoffset, 0) - self.driver_insts[index].place(offset=base, mirror=mirror) - - # place spare write drivers (if spare columns are specified) - for i in range(self.num_spare_cols): - index = self.word_size + i - xoffset = (self.columns + i) * self.driver_spacing - - if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: - mirror = "MY" - xoffset = xoffset + self.driver.width - else: - mirror = "" - - base = vector(xoffset, 0) - self.driver_insts[index].place(offset=base, mirror=mirror) - - def add_layout_pins(self): - for i in range(self.word_size + self.num_spare_cols): - inst = self.driver_insts[i] - din_pin = inst.get_pin(inst.mod.din_name) - self.add_layout_pin(text=self.data_name + "_{0}".format(i), - layer=din_pin.layer, - offset=din_pin.ll(), - width=din_pin.width(), - height=din_pin.height()) - bl_pin = inst.get_pin(inst.mod.get_bl_names()) - self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i), - layer=bl_pin.layer, - offset=bl_pin.ll(), - width=bl_pin.width(), - height=bl_pin.height()) - - br_pin = inst.get_pin(inst.mod.get_br_names()) - self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i), - layer=br_pin.layer, - offset=br_pin.ll(), - width=br_pin.width(), - height=br_pin.height()) - - for n in ["vdd", "gnd"]: - pin_list = self.driver_insts[i].get_pins(n) - for pin in pin_list: - self.add_power_pin(name=n, - loc=pin.center(), - directions=("V", "V"), - start_layer=pin.layer) - if self.write_size: - for bit in range(self.num_wmasks): - inst = self.driver_insts[bit * self.write_size] - en_pin = inst.get_pin(inst.mod.en_name) - # 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=self.en_name + "_{0}".format(bit), - layer=en_pin.layer, - offset=en_pin.ll(), - width=wmask_en_len - en_gap, - height=en_pin.height()) - - for i in range(self.num_spare_cols): - inst = self.driver_insts[self.word_size + i] - en_pin = inst.get_pin(inst.mod.en_name) - self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks), - layer="m1", - offset=en_pin.lr() + vector(-drc("minwidth_m1"),0)) - - elif self.num_spare_cols and not self.write_size: - # shorten enable rail to accomodate those for spare write drivers - inst = self.driver_insts[0] - en_pin = inst.get_pin(inst.mod.en_name) - self.add_layout_pin(text=self.en_name + "_{0}".format(0), - layer="m1", - offset=en_pin.ll(), - width=self.width_regular_cols - self.words_per_row * en_pin.width()) - - # individual enables for every spare write driver - for i in range(self.num_spare_cols): - inst = self.driver_insts[self.word_size + i] - en_pin = inst.get_pin(inst.mod.en_name) - self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1), - layer="m1", - offset=en_pin.lr() + vector(-drc("minwidth_m1"),0)) - - else: - inst = self.driver_insts[0] - self.add_layout_pin(text=self.en_name, - layer="m1", - offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1), - width=self.width) - - def get_w_en_cin(self): - """Get the relative capacitance of all the enable connections in the bank""" - # The enable is connected to a nand2 for every row. - return self.driver.get_w_en_cin() * len(self.driver_insts) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index 30126b63..fa6df322 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -24,14 +24,14 @@ class delay_chain(design.design): super().__init__(name) debug.info(1, "creating delay chain {0}".format(str(fanout_list))) self.add_comment("fanouts: {0}".format(str(fanout_list))) - + # Two fanouts are needed so that we can route the vdd/gnd connections for f in fanout_list: debug.check(f>=2, "Must have >=2 fanouts for each stage.") # number of inverters including any fanout loads. self.fanout_list = fanout_list - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -40,7 +40,7 @@ class delay_chain(design.design): self.add_modules() self.add_pins() self.create_inverters() - + def create_layout(self): # Each stage is a a row self.height = len(self.fanout_list) * self.inv.height @@ -53,7 +53,7 @@ class delay_chain(design.design): 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", "INPUT") @@ -86,7 +86,7 @@ class delay_chain(design.design): else: stagein_name = "dout_{}".format(stage_num) self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"]) - + # Now add the dummy loads to the right self.load_inst_map[cur_driver]=[] for i in range(fanout_size): @@ -95,7 +95,7 @@ class delay_chain(design.design): # Fanout stage is always driven by driver and output is disconnected disconnect_name = "n_{0}_{1}".format(stage_num, i) self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"]) - + # Keep track of all the loads to connect their inputs as a load self.load_inst_map[cur_driver].append(cur_load) @@ -108,19 +108,19 @@ class delay_chain(design.design): else: inv_mirror = "R0" inv_offset = vector(0, stage_num * self.inv.height) - + # Add the inverter cur_driver=self.driver_inst_list[stage_num] cur_driver.place(offset=inv_offset, mirror=inv_mirror) - + # Now add the dummy loads to the right load_list = self.load_inst_map[cur_driver] for i in range(fanout_size): inv_offset += vector(self.inv.width, 0) load_list[i].place(offset=inv_offset, mirror=inv_mirror) - + def add_route(self, pin1, pin2): """ This guarantees that we route from the top to bottom row correctly. """ pin1_pos = pin1.center() @@ -131,7 +131,7 @@ class delay_chain(design.design): mid_point = vector(pin2_pos.x, 0.5 * (pin1_pos.y + pin2_pos.y)) # Written this way to guarantee it goes right first if we are switching rows self.add_path("m2", [pin1_pos, vector(pin1_pos.x, mid_point.y), mid_point, vector(mid_point.x, pin2_pos.y), pin2_pos]) - + def route_inverters(self): """ Add metal routing for each of the fanout stages """ @@ -180,12 +180,12 @@ class delay_chain(design.design): self.add_power_pin(pin_name, pin.rc() - vector(self.m1_pitch, 0), start_layer=pin.layer) - + pin = load_list[-2].get_pin(pin_name) self.add_power_pin(pin_name, pin.rc() - vector(self.m1_pitch, 0), start_layer=pin.layer) - + def add_layout_pins(self): # input is A pin of first inverter diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index cb82443b..0cbe8fe5 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -27,7 +27,7 @@ class dff_array(design.design): super().__init__(name) debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns)) self.add_comment("rows: {0} cols: {1}".format(rows, columns)) - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -36,11 +36,11 @@ class dff_array(design.design): self.add_modules() self.add_pins() self.create_dff_array() - + def create_layout(self): self.width = self.columns * self.dff.width self.height = self.rows * self.dff.height - + self.place_dff_array() self.add_layout_pins() self.add_boundary() @@ -49,7 +49,7 @@ class dff_array(design.design): def add_modules(self): self.dff = factory.create(module_type="dff") self.add_mod(self.dff) - + def add_pins(self): for row in range(self.rows): for col in range(self.columns): @@ -86,7 +86,7 @@ class dff_array(design.design): mirror = "MX" self.dff_insts[row, col].place(offset=base, mirror=mirror) - + def get_din_name(self, row, col): if self.columns == 1: din_name = "din_{0}".format(row) @@ -96,7 +96,7 @@ class dff_array(design.design): din_name = "din_{0}_{1}".format(row, col) return din_name - + def get_dout_name(self, row, col): if self.columns == 1: dout_name = "dout_{0}".format(row) @@ -106,7 +106,7 @@ class dff_array(design.design): dout_name = "dout_{0}_{1}".format(row, col) return dout_name - + def add_layout_pins(self): for row in range(self.rows): for col in range(self.columns): @@ -117,7 +117,7 @@ class dff_array(design.design): # Continous gnd rail along with label. gnd_pin=self.dff_insts[row, col].get_pin("gnd") self.add_power_pin("gnd", gnd_pin.center(), start_layer=gnd_pin.layer) - + for row in range(self.rows): for col in range(self.columns): din_pin = self.dff_insts[row, col].get_pin("D") diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 1290bf12..b218a9bb 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -7,7 +7,7 @@ # import debug import design -from tech import parameter, layer +from tech import layer from tech import cell_properties as props from vector import vector from globals import OPTS @@ -21,16 +21,15 @@ class dff_buf(design.design): and qbar. This is to enable driving large fanout loads. """ unique_id = 1 - - def __init__(self, inv1_size=2, inv2_size=4, name=""): + def __init__(self, inv1_size=2, inv2_size=4, name=""): if name=="": name = "dff_buf_{0}".format(dff_buf.unique_id) dff_buf.unique_id += 1 super().__init__(name) debug.info(1, "Creating {}".format(self.name)) self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size)) - + # This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width. # This causes a DRC in the pinv which assumes min width rails. This ensures the output # contact does not violate spacing to the rail in the NMOS. @@ -39,7 +38,7 @@ class dff_buf(design.design): self.inv1_size=inv1_size self.inv2_size=inv2_size - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -57,7 +56,7 @@ class dff_buf(design.design): self.add_layout_pins() self.add_boundary() self.DRC_LVS() - + def add_modules(self): self.dff = factory.create(module_type="dff") self.add_mod(self.dff) @@ -71,7 +70,7 @@ class dff_buf(design.design): size=self.inv2_size, height=self.dff.height) self.add_mod(self.inv2) - + def add_pins(self): self.add_pin("D", "INPUT") self.add_pin("Q", "OUTPUT") @@ -93,7 +92,7 @@ class dff_buf(design.design): self.inv1_inst=self.add_inst(name="dff_buf_inv1", mod=self.inv1) self.connect_inst(["qint", "Qb", "vdd", "gnd"]) - + self.inv2_inst=self.add_inst(name="dff_buf_inv2", mod=self.inv2) self.connect_inst(["Qb", "Q", "vdd", "gnd"]) @@ -119,16 +118,16 @@ class dff_buf(design.design): except AttributeError: pass self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0)) - + # Add INV2 to the right self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) - + def route_wires(self): if "li" in layer: self.route_layer = "li" else: self.route_layer = "m1" - + # Route dff q to inv1 a q_pin = self.dff_inst.get_pin("Q") a1_pin = self.inv1_inst.get_pin("A") @@ -143,7 +142,7 @@ class dff_buf(design.design): a2_pin = self.inv2_inst.get_pin("A") self.mid_qb_pos = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy()) self.add_zjog(z1_pin.layer, z1_pin.center(), a2_pin.center()) - + def add_layout_pins(self): # Continous vdd rail along with label. @@ -161,7 +160,7 @@ class dff_buf(design.design): offset=gnd_pin.ll(), width=self.width, height=vdd_pin.height()) - + clk_pin = self.dff_inst.get_pin("clk") self.add_layout_pin(text="clk", layer=clk_pin.layer, diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index d6382973..2d885ccc 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -19,7 +19,7 @@ class dff_buf_array(design.design): Unlike the data flops, these are never spaced out. """ unique_id = 1 - + def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""): self.rows = rows self.columns = columns @@ -31,10 +31,10 @@ class dff_buf_array(design.design): debug.info(1, "Creating {}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, columns)) self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size)) - + self.inv1_size = inv1_size self.inv2_size = inv2_size - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -110,7 +110,7 @@ class dff_buf_array(design.design): pass dff_pitch = self.dff.width + well_spacing + self.well_extend_active - + for row in range(self.rows): for col in range(self.columns): # name = "Xdff_r{0}_c{1}".format(row, col) @@ -122,7 +122,7 @@ class dff_buf_array(design.design): mirror = "MX" self.dff_insts[row, col].place(offset=base, mirror=mirror) - + def get_din_name(self, row, col): if self.columns == 1: din_name = "din_{0}".format(row) @@ -132,7 +132,7 @@ class dff_buf_array(design.design): din_name = "din_{0}_{1}".format(row, col) return din_name - + def get_dout_name(self, row, col): if self.columns == 1: dout_name = "dout_{0}".format(row) @@ -142,7 +142,7 @@ class dff_buf_array(design.design): dout_name = "dout_{0}_{1}".format(row, col) return dout_name - + def get_dout_bar_name(self, row, col): if self.columns == 1: dout_bar_name = "dout_bar_{0}".format(row) @@ -158,11 +158,11 @@ class dff_buf_array(design.design): vdd0_pin=self.dff_insts[row, 0].get_pin("vdd") vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd") self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height()) - + gnd0_pin=self.dff_insts[row, 0].get_pin("gnd") gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd") self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height()) - + for row in range(self.rows): for col in range(self.columns): # Continous vdd rail along with label. @@ -172,9 +172,9 @@ class dff_buf_array(design.design): # Continous gnd rail along with label. gnd_pin=self.dff_insts[row, col].get_pin("gnd") self.add_power_pin("gnd", gnd_pin.lc(), start_layer=gnd_pin.layer) - + def add_layout_pins(self): - + for row in range(self.rows): for col in range(self.columns): din_pin = self.dff_insts[row, col].get_pin("D") @@ -200,7 +200,7 @@ class dff_buf_array(design.design): offset=dout_bar_pin.ll(), width=dout_bar_pin.width(), height=dout_bar_pin.height()) - + # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0, 0].get_pin("clk") clk_ypos = 2 * self.m3_pitch + self.m3_width diff --git a/compiler/modules/dff_inv.py b/compiler/modules/dff_inv.py index 8f50f9e8..ffd02025 100644 --- a/compiler/modules/dff_inv.py +++ b/compiler/modules/dff_inv.py @@ -19,7 +19,7 @@ class dff_inv(design.design): do not have Qbar, so this will create it. """ unique_id = 1 - + def __init__(self, inv_size=2, name=""): if name=="": @@ -30,7 +30,7 @@ class dff_inv(design.design): self.add_comment("inv: {0}".format(inv_size)) self.inv_size = inv_size - + # This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width. # This causes a DRC in the pinv which assumes min width rails. This ensures the output # contact does not violate spacing to the rail in the NMOS. @@ -44,7 +44,7 @@ class dff_inv(design.design): self.add_pins() self.add_modules() self.create_modules() - + def create_layout(self): self.width = self.dff.width + self.inv1.width self.height = self.dff.height @@ -52,10 +52,10 @@ class dff_inv(design.design): self.place_modules() self.add_wires() self.add_layout_pins() - + self.add_boundary() self.DRC_LVS() - + def add_pins(self): self.add_pin("D") self.add_pin("Q") @@ -67,7 +67,7 @@ class dff_inv(design.design): def add_modules(self): self.dff = dff_inv.dff_inv(self.inv_size) self.add_mod(self.dff) - + self.inv1 = factory.create(module_type="pinv", size=self.inv_size, height=self.dff.height) @@ -88,8 +88,8 @@ class dff_inv(design.design): # Place the INV1 to the right self.inv1_inst.place(vector(self.dff_inst.rx(),0)) - - + + def add_wires(self): # Route dff q to inv1 a q_pin = self.dff_inst.get_pin("Q") @@ -106,7 +106,7 @@ class dff_inv(design.design): self.add_via_center(layers=self.m1_stack, offset=a1_pin.center()) - + def add_layout_pins(self): # Continous vdd rail along with label. @@ -124,7 +124,7 @@ class dff_inv(design.design): offset=gnd_pin.ll(), width=self.width, height=vdd_pin.height()) - + clk_pin = self.dff_inst.get_pin("clk") self.add_layout_pin(text="clk", layer=clk_pin.layer, diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index 1687e043..59fdfade 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -19,7 +19,7 @@ class dff_inv_array(design.design): Unlike the data flops, these are never spaced out. """ unique_id = 1 - + def __init__(self, rows, columns, inv_size=2, name=""): self.rows = rows self.columns = columns @@ -31,9 +31,9 @@ class dff_inv_array(design.design): debug.info(1, "Creating {}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, columns)) self.add_comment("inv1: {0}".format(inv1_size)) - + self.inv_size = inv_size - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -42,7 +42,7 @@ class dff_inv_array(design.design): self.add_pins() self.add_modules() self.create_dff_array() - + def create_layout(self): self.width = self.columns * self.dff.width self.height = self.rows * self.dff.height @@ -53,14 +53,14 @@ class dff_inv_array(design.design): self.DRC_LVS() def add_modules(self): - self.dff = factory.create(module_type="dff") + self.dff = factory.create(module_type="dff") self.add_mod(self.dff) def add_pins(self): - for row in range(self.rows): + for row in range(self.rows): for col in range(self.columns): self.add_pin(self.get_din_name(row,col), "INPUT") - for row in range(self.rows): + for row in range(self.rows): for col in range(self.columns): self.add_pin(self.get_dout_name(row,col), "OUTPUT") self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT") @@ -70,20 +70,20 @@ class dff_inv_array(design.design): def create_dff_array(self): self.dff_insts={} - for row in range(self.rows): + for row in range(self.rows): for col in range(self.columns): name = "Xdff_r{0}_c{1}".format(row,col) self.dff_insts[row,col]=self.add_inst(name=name, mod=self.dff) self.connect_inst([self.get_din_name(row,col), self.get_dout_name(row,col), - self.get_dout_bar_name(row,col), + self.get_dout_bar_name(row,col), "clk", "vdd", "gnd"]) def place_dff_array(self): - for row in range(self.rows): + for row in range(self.rows): for col in range(self.columns): name = "Xdff_r{0}_c{1}".format(row,col) if (row % 2 == 0): @@ -92,9 +92,9 @@ class dff_inv_array(design.design): else: base = vector(col*self.dff.width,(row+1)*self.dff.height) mirror = "MX" - self.dff_insts[row,col].place(offset=base, + self.dff_insts[row,col].place(offset=base, mirror=mirror) - + def get_din_name(self, row, col): if self.columns == 1: din_name = "din_{0}".format(row) @@ -104,7 +104,7 @@ class dff_inv_array(design.design): din_name = "din_{0}_{1}".format(row,col) return din_name - + def get_dout_name(self, row, col): if self.columns == 1: dout_name = "dout_{0}".format(row) @@ -114,7 +114,7 @@ class dff_inv_array(design.design): dout_name = "dout_{0}_{1}".format(row,col) return dout_name - + def get_dout_bar_name(self, row, col): if self.columns == 1: dout_bar_name = "dout_bar_{0}".format(row) @@ -124,10 +124,10 @@ class dff_inv_array(design.design): dout_bar_name = "dout_bar_{0}_{1}".format(row,col) return dout_bar_name - + def add_layout_pins(self): for row in range(self.rows): - for col in range(self.columns): + for col in range(self.columns): # Adds power pin on left of row vdd_pin=self.dff_insts[row,col].get_pin("vdd") self.add_power_pin("vdd", vdd_pin.lc()) @@ -135,10 +135,10 @@ class dff_inv_array(design.design): # Adds gnd pin on left of row gnd_pin=self.dff_insts[row,col].get_pin("gnd") self.add_power_pin("gnd", gnd_pin.lc()) - - for row in range(self.rows): - for col in range(self.columns): + + for row in range(self.rows): + for col in range(self.columns): din_pin = self.dff_insts[row,col].get_pin("D") debug.check(din_pin.layer=="m2","DFF D pin not on metal2") self.add_layout_pin(text=self.get_din_name(row,col), @@ -163,7 +163,7 @@ class dff_inv_array(design.design): width=dout_bar_pin.width(), height=dout_bar_pin.height()) - + # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") clk_ypos = 2*self.m3_pitch+self.m3_width @@ -188,4 +188,4 @@ class dff_inv_array(design.design): height=self.height) # Drop a via to the M3 pin self.add_via_center(layers=self.m2_stack, - offset=vector(clk_pin.cx(),clk_ypos)) + offset=vector(clk_pin.cx(),clk_ypos)) diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 465fb9d8..6368f9c8 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -1,6 +1,6 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California +# Copyright (c) 2016-2019 Regents of the University of California # All rights reserved. # from bitcell_base_array import bitcell_base_array @@ -20,13 +20,13 @@ class dummy_array(bitcell_base_array): self.create_netlist() if not OPTS.netlist_only: self.create_layout() - + def create_netlist(self): """ Create and connect the netlist """ # This will create a default set of bitline/wordline names self.create_all_bitline_names() self.create_all_wordline_names() - + self.add_modules() self.add_pins() self.create_instances() @@ -38,16 +38,16 @@ class dummy_array(bitcell_base_array): self.add_layout_pins() self.add_boundary() - + self.DRC_LVS() def add_modules(self): """ Add the modules used in this design """ - + self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell)) self.cell = factory.create(module_type="bitcell") self.add_mod(self.dummy_cell) - + def create_instances(self): """ Create the module instances used in this design """ self.cell_inst = {} @@ -67,7 +67,7 @@ class dummy_array(bitcell_base_array): self.add_pin(wl_name, "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - + def add_layout_pins(self): """ Add the layout pins """ @@ -86,7 +86,7 @@ class dummy_array(bitcell_base_array): offset=br_pin.ll().scale(1, 0), width=br_pin.width(), height=self.height) - + wl_names = self.cell.get_all_wl_names() for row in range(self.row_size): for port in self.all_ports: @@ -104,7 +104,7 @@ class dummy_array(bitcell_base_array): inst = self.cell_inst[row, col] for pin_name in ["vdd", "gnd"]: self.copy_layout_pin(inst, pin_name) - + def input_load(self): # FIXME: This appears to be old code from previous characterization. Needs to be updated. wl_wire = self.gen_wl_wire() diff --git a/compiler/modules/global_bitcell_array.py b/compiler/modules/global_bitcell_array.py index 4cb384b6..2ff8bb77 100644 --- a/compiler/modules/global_bitcell_array.py +++ b/compiler/modules/global_bitcell_array.py @@ -27,11 +27,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): debug.check(len(self.all_ports)<=2, "Only support dual port or less in global bitcell array.") self.rbl = [1, 1 if len(self.all_ports)>1 else 0] - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() - + def create_netlist(self): """ Create and connect the netlist """ self.add_modules() @@ -43,11 +43,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): self.place() self.route() - + self.add_layout_pins() self.add_boundary() - + self.DRC_LVS() def add_modules(self): @@ -88,7 +88,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): rows=self.row_size, cols=cols, rbl=self.rbl) - + self.add_mod(la) self.local_mods.append(la) @@ -108,7 +108,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): self.rbl_bitline_names[0].append("rbl_bl_{}_0".format(port)) for port in self.all_ports: self.rbl_bitline_names[0].append("rbl_br_{}_0".format(port)) - + for col in range(self.column_size): for port in self.all_ports: self.bitline_names[port].append("bl_{0}_{1}".format(port, col)) @@ -120,7 +120,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): self.rbl_bitline_names[1].append("rbl_bl_{}_1".format(port)) for port in self.all_ports: self.rbl_bitline_names[1].append("rbl_br_{}_1".format(port)) - + # Make a flat list too self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl] # Make a flat list too @@ -130,19 +130,19 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): self.add_pin_list(self.all_bitline_names, "INOUT") if len(self.all_ports) > 1: self.add_pin_list(self.rbl_bitline_names[1], "INOUT") - + def add_wordline_pins(self): self.rbl_wordline_names = [[] for x in self.all_ports] - + self.wordline_names = [[] for x in self.all_ports] for bit in self.all_ports: for port in self.all_ports: self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit)) - + self.all_rbl_wordline_names = [x for sl in zip(*self.rbl_wordline_names) for x in sl] - + # Regular WLs for row in range(self.row_size): for port in self.all_ports: @@ -163,11 +163,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): name = "la_{0}".format(col) self.local_insts.append(self.add_inst(name=name, mod=mod)) - + temp = [] if col == 0: temp.extend(self.get_rbl_bitline_names(0)) - + port_inouts = [x for x in mod.get_inouts() if x.startswith("bl") or x.startswith("br")] for pin_name in port_inouts: # Offset of the last underscore that defines the bit number @@ -180,18 +180,18 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): # Strip the bit and add the new one new_name = "{0}_{1}".format(base_name, col + col_value) temp.append(new_name) - + if len(self.all_ports) > 1 and mod == self.local_mods[-1]: temp.extend(self.get_rbl_bitline_names(1)) for port in self.all_ports: port_inputs = [x for x in mod.get_inputs() if "wl_{}".format(port) in x] temp.extend(port_inputs) - + temp.append("vdd") temp.append("gnd") self.connect_inst(temp) - + def place(self): offset = vector(0, 0) for inst in self.local_insts: @@ -204,7 +204,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): def route(self): pass - + def add_layout_pins(self): # Regular bitlines @@ -230,11 +230,11 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): layer=left_pin.layer, start=left_pin.lc(), end=right_pin.rc()) - + # Replica bitlines self.copy_layout_pin(self.local_insts[0], "rbl_bl_0_0") self.copy_layout_pin(self.local_insts[0], "rbl_br_0_0") - + if len(self.all_ports) > 1: self.copy_layout_pin(self.local_insts[0], "rbl_bl_1_0") self.copy_layout_pin(self.local_insts[0], "rbl_br_1_0") @@ -269,10 +269,10 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): return offsets def graph_exclude_bits(self, targ_row, targ_col): - """ - Excludes bits in column from being added to graph except target """ - + Excludes bits in column from being added to graph except target + """ + # This must find which local array includes the specified column # Find the summation of columns that is large and take the one before for i, col in enumerate(self.col_offsets): @@ -303,7 +303,7 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" - + # This must find which local array includes the specified column # Find the summation of columns that is large and take the one before for i, local_col in enumerate(self.col_offsets): @@ -321,9 +321,9 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): local_col = col - self.col_offsets[i - 1] return local_array.get_cell_name(inst_name + '.x' + local_inst.name, row, local_col) - + def clear_exclude_bits(self): - """ + """ Clears the bit exclusions """ for mod in self.local_mods: @@ -331,6 +331,6 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): 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) - + diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index a5a63fc8..c121f53a 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -22,14 +22,14 @@ class hierarchical_decoder(design.design): super().__init__(name) self.AND_FORMAT = "DEC_AND_{0}" - + self.pre2x4_inst = [] self.pre3x8_inst = [] self.pre4x16_inst = [] b = factory.create(module_type="bitcell") self.cell_height = b.height - + self.num_outputs = num_outputs self.num_inputs = math.ceil(math.log(self.num_outputs, 2)) (self.no_of_pre2x4, self.no_of_pre3x8, self.no_of_pre4x16)=self.determine_predecodes(self.num_inputs) @@ -37,7 +37,7 @@ class hierarchical_decoder(design.design): self.create_netlist() if not OPTS.netlist_only: self.create_layout() - + def create_netlist(self): self.add_modules() self.setup_netlist_constants() @@ -49,33 +49,33 @@ class hierarchical_decoder(design.design): self.setup_layout_constants() self.place_pre_decoder() self.place_row_decoder() - + self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space - + self.route_inputs() self.route_outputs() self.route_decoder_bus() self.route_vdd_gnd() - + self.offset_x_coordinates() - + self.width = self.and_inst[0].rx() + 0.5 * self.m1_width - + self.add_boundary() self.DRC_LVS() - + def add_modules(self): self.and2 = factory.create(module_type="and2_dec", height=self.cell_height) self.add_mod(self.and2) - + self.and3 = factory.create(module_type="and3_dec", height=self.cell_height) self.add_mod(self.and3) # TBD # self.and4 = factory.create(module_type="and4_dec") # self.add_mod(self.and4) - + self.add_decoders() def add_decoders(self): @@ -83,7 +83,7 @@ class hierarchical_decoder(design.design): self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4", height=self.cell_height) self.add_mod(self.pre2_4) - + self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8", height=self.cell_height) self.add_mod(self.pre3_8) @@ -91,11 +91,11 @@ class hierarchical_decoder(design.design): self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16", height=self.cell_height) self.add_mod(self.pre4_16) - + def determine_predecodes(self, num_inputs): - """ + """ Determines the number of 2:4, 3:8 and 4:16 pre-decoders - needed based on the number of inputs + needed based on the number of inputs """ if (num_inputs == 2): return (1, 0, 0) @@ -151,7 +151,7 @@ class hierarchical_decoder(design.design): lines.append(index) index = index + 1 self.predec_groups.append(lines) - + def setup_layout_constants(self): """ Calculate the overall dimensions of the hierarchical decoder """ @@ -190,7 +190,7 @@ class hierarchical_decoder(design.design): self.input_layer = layer_props.hierarchical_decoder.input_layer self.output_layer = layer_props.hierarchical_decoder.output_layer self.output_layer_pitch = getattr(self, self.output_layer + "_pitch") - + # Two extra pitches between modules on left and right self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch self.row_decoder_height = self.and2.height * self.num_outputs @@ -215,7 +215,7 @@ class hierarchical_decoder(design.design): offset=input_offset, names=input_bus_names, length=self.predecoder_height) - + self.route_input_to_predecodes() def route_input_to_predecodes(self): @@ -231,13 +231,13 @@ class hierarchical_decoder(design.design): decoder_offset = decoder_pin.center() input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) - + self.route_input_bus(decoder_offset, input_offset) - + for pre_num in range(self.no_of_pre3x8): for i in range(3): index = pre_num * 3 + i + self.no_of_pre2x4 * 2 - + input_pos = self.input_bus["addr_{}".format(index)].center() in_name = "in_{}".format(i) @@ -245,13 +245,13 @@ class hierarchical_decoder(design.design): decoder_offset = decoder_pin.center() input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) - + self.route_input_bus(decoder_offset, input_offset) for pre_num in range(self.no_of_pre4x16): for i in range(4): index = pre_num * 4 + i + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2 - + input_pos = self.input_bus["addr_{}".format(index)].center() in_name = "in_{}".format(i) @@ -259,15 +259,15 @@ class hierarchical_decoder(design.design): decoder_offset = decoder_pin.center() input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) - + self.route_input_bus(decoder_offset, input_offset) - + def route_input_bus(self, input_offset, output_offset): """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ - + self.add_via_stack_center(from_layer=self.bus_layer, to_layer=self.input_layer, offset=input_offset) @@ -276,10 +276,10 @@ class hierarchical_decoder(design.design): offset=output_offset, directions=self.bus_directions) self.add_path(self.input_layer, [input_offset, output_offset]) - + def add_pins(self): """ Add the module pins """ - + for i in range(self.num_inputs): self.add_pin("addr_{0}".format(i), "INPUT") @@ -290,10 +290,10 @@ class hierarchical_decoder(design.design): def create_pre_decoder(self): """ Creates pre-decoder and places labels input address [A] """ - + for i in range(self.no_of_pre2x4): self.create_pre2x4(i) - + for i in range(self.no_of_pre3x8): self.create_pre3x8(i) @@ -302,7 +302,7 @@ class hierarchical_decoder(design.design): def create_pre2x4(self, num): """ Add a 2x4 predecoder to the left of the origin """ - + if (self.num_inputs == 2): index_off1 = index_off2 = 0 else: @@ -355,19 +355,19 @@ class hierarchical_decoder(design.design): self.pre4x16_inst.append(self.add_inst(name="pre4x16_{0}".format(num), mod=self.pre4_16)) self.connect_inst(pins) - + def place_pre_decoder(self): """ Creates pre-decoder and places labels input address [A] """ - + for i in range(self.no_of_pre2x4): self.place_pre2x4(i) - + for i in range(self.no_of_pre3x8): self.place_pre3x8(i) for i in range(self.no_of_pre4x16): self.place_pre4x16(i) - + self.predecode_height = 0 if self.no_of_pre2x4 > 0: self.predecode_height = self.pre2x4_inst[-1].uy() @@ -378,10 +378,10 @@ class hierarchical_decoder(design.design): def place_pre2x4(self, num): """ Place 2x4 predecoder to the left of the origin """ - + base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing)) self.pre2x4_inst[num].place(base) - + def place_pre3x8(self, num): """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \ @@ -396,7 +396,7 @@ class hierarchical_decoder(design.design): + num * (self.pre4_16.height + self.predecoder_spacing) offset = vector(-self.pre4_16.width, height) self.pre4x16_inst[num].place(offset) - + def create_row_decoder(self): """ Create the row-decoder by placing AND2/AND3 and Inverters and add the primary decoder output pins. """ @@ -407,7 +407,7 @@ class hierarchical_decoder(design.design): """ Add a column of AND gates for final decode """ self.and_inst = [] - + # Row Decoder AND GATE array for address inputs <5. if (self.num_inputs == 4 or self.num_inputs == 5): for i in range(len(self.predec_groups[0])): @@ -435,7 +435,7 @@ class hierarchical_decoder(design.design): name = self.AND_FORMAT.format(output) self.and_inst.append(self.add_inst(name=name, mod=self.and3)) - + pins = ["out_{0}".format(i), "out_{0}".format(j + len(self.predec_groups[0])), "out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), @@ -456,7 +456,7 @@ class hierarchical_decoder(design.design): Add a column of AND gates for final decode. This may have more than one decoder per row to match the bitcell height. """ - + # Row Decoder AND GATE array for address inputs <5. if (self.num_inputs == 4 or self.num_inputs == 5): self.place_and_array(and_mod=self.and2) @@ -489,7 +489,7 @@ class hierarchical_decoder(design.design): for row in range(self.num_outputs): and_inst = self.and_inst[row] self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row)) - + def route_decoder_bus(self): """ Creates vertical metal 2 bus to connect predecoder and decoder stages. @@ -522,7 +522,7 @@ class hierarchical_decoder(design.design): x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) - + # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre3x8): for i in range(8): @@ -542,7 +542,7 @@ class hierarchical_decoder(design.design): x_offset = self.pre4x16_inst[pre_num].rx() + self.output_layer_pitch y_offset = self.pre4x16_inst[pre_num].by() + i * self.cell_height self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) - + def route_bus_to_decoder(self): """ Use the self.predec_groups to determine the connections to the decoder AND gates. @@ -555,7 +555,7 @@ class hierarchical_decoder(design.design): and the 128th AND3 is connected to [3,7,15] """ output_index = 0 - + if (self.num_inputs == 4 or self.num_inputs == 5): for index_B in self.predec_groups[1]: for index_A in self.predec_groups[0]: @@ -596,7 +596,7 @@ class hierarchical_decoder(design.design): Add a pin for each row of vdd/gnd which are must-connects next level up. """ - + if layer_props.hierarchical_decoder.vertical_supply: for n in ["vdd", "gnd"]: pins = self.and_inst[0].get_pins(n) @@ -636,7 +636,7 @@ class hierarchical_decoder(design.design): for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst: for pin_name in ["vdd", "gnd"]: self.copy_layout_pin(pre, pin_name) - + def route_predecode_bus_outputs(self, rail_name, pin, row): """ Connect the routing rail to the given metal1 pin @@ -646,17 +646,17 @@ class hierarchical_decoder(design.design): pin_pos = pin.center() rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y) self.add_path(self.input_layer, [rail_pos, pin_pos]) - + self.add_via_stack_center(from_layer=self.bus_layer, to_layer=self.input_layer, offset=rail_pos, directions=self.bus_directions) - + self.add_via_stack_center(from_layer=pin.layer, to_layer=self.input_layer, offset=pin_pos, directions=("H", "H")) - + def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset): """ Connect the routing rail to the given metal1 pin using a jog diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 3c19910f..ea1f1215 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -31,10 +31,10 @@ class hierarchical_predecode(design.design): # If we are pitch matched to the bitcell, it's a predecoder # otherwise it's a column decoder (out of pgates) self.column_decoder = (height != b.height) - + self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) super().__init__(name) - + def add_pins(self): for k in range(self.number_of_inputs): self.add_pin("in_{0}".format(k), "INPUT") @@ -48,7 +48,7 @@ class hierarchical_predecode(design.design): debug.check(self.number_of_inputs <= 4, "Invalid number of predecode inputs: {}".format(self.number_of_inputs)) - + if self.column_decoder: and_type = "pand{}".format(self.number_of_inputs) inv_type = "pinv" @@ -79,7 +79,7 @@ class hierarchical_predecode(design.design): self.route() self.add_boundary() self.DRC_LVS() - + def setup_layout_constraints(self): # Inputs to cells are on input layer @@ -92,7 +92,7 @@ class hierarchical_predecode(design.design): self.input_layer = layer_props.hierarchical_predecode.input_layer self.output_layer = layer_props.hierarchical_predecode.output_layer self.output_layer_pitch = getattr(self, self.output_layer + "_pitch") - + self.height = self.number_of_outputs * self.and_mod.height # x offset for input inverters @@ -139,7 +139,7 @@ class hierarchical_predecode(design.design): def place_input_inverters(self): """ Place the input inverters to invert input signals for the decode stage. """ for inv_num in range(self.number_of_inputs): - + if (inv_num % 2 == 0): y_off = inv_num * (self.inv.height) mirror = "R0" @@ -149,7 +149,7 @@ class hierarchical_predecode(design.design): offset = vector(self.x_off_inv_1, y_off) self.inv_inst[inv_num].place(offset=offset, mirror=mirror) - + def create_and_array(self, connections): """ Create the AND stage for the decodes """ self.and_inst = [] @@ -196,7 +196,7 @@ class hierarchical_predecode(design.design): pin = top_and_gate.get_pin("D") else: debug.error("Too many inputs for predecoder.", -1) - + y_offset = pin.cy() in_pin = "in_{}".format(num) a_pin = "A_{}".format(num) @@ -222,7 +222,7 @@ class hierarchical_predecode(design.design): offset=z_pin.ll(), height=z_pin.height(), width=z_pin.width()) - + def route_input_inverters(self): """ Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd] @@ -232,7 +232,7 @@ class hierarchical_predecode(design.design): in_pin = "in_{}".format(inv_num) inv_out_pin = self.inv_inst[inv_num].get_pin("Z") - + # add output so that it is just below the vdd or gnd rail # since this is where the p/n devices are and there are no # pins in the and gates. @@ -241,7 +241,7 @@ class hierarchical_predecode(design.design): right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0) rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset) self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) - + self.add_via_stack_center(from_layer=inv_out_pin.layer, to_layer=self.output_layer, offset=inv_out_pos) @@ -249,7 +249,7 @@ class hierarchical_predecode(design.design): to_layer=self.bus_layer, offset=rail_pos, directions=self.bus_directions) - + # route input pin = self.inv_inst[inv_num].get_pin("A") inv_in_pos = pin.center() @@ -267,11 +267,11 @@ class hierarchical_predecode(design.design): offset=in_pos, height=via.mod.second_layer_height, width=via.mod.second_layer_width) - + if layer_props.hierarchical_predecode.vertical_supply: below_rail = vector(self.decode_rails[out_pin].cx(), y_offset - (self.cell_height / 2)) self.add_path(self.bus_layer, [rail_pos, below_rail], width=self.li_width + self.m1_enclose_mcon * 2) - + def route_and_to_rails(self): # This 2D array defines the connection mapping and_input_line_combination = self.get_and_input_line_combination() @@ -302,7 +302,7 @@ class hierarchical_predecode(design.design): direction = None else: direction = ("H", "H") - + self.add_via_stack_center(from_layer=pin.layer, to_layer=self.input_layer, offset=pin_pos, @@ -334,7 +334,7 @@ class hierarchical_predecode(design.design): self.add_power_pin(name=n, loc=pin.uc(), start_layer=pin.layer) - + # In other techs, we are using standard cell decoder cells with horizontal power else: for num in range(0, self.number_of_outputs): @@ -354,6 +354,6 @@ class hierarchical_predecode(design.design): self.add_power_pin(name=n, loc=pin_pos, start_layer=and_pin.layer) - + diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index 7513ed7c..0bbfd1e0 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -25,9 +25,9 @@ class hierarchical_predecode3x8(hierarchical_predecode): self.add_modules() self.create_input_inverters() connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"], - ["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"], ["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"], - ["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"], ["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"], ["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"], ["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"], @@ -41,7 +41,7 @@ class hierarchical_predecode3x8(hierarchical_predecode): ["Abar_0", "A_1", "Abar_2"], ["A_0", "A_1", "Abar_2"], ["Abar_0", "Abar_1", "A_2"], - ["A_0", "Abar_1", "A_2"], - ["Abar_0", "A_1", "A_2"], + ["A_0", "Abar_1", "A_2"], + ["Abar_0", "A_1", "A_2"], ["A_0", "A_1", "A_2"]] return combination diff --git a/compiler/modules/hierarchical_predecode4x16.py b/compiler/modules/hierarchical_predecode4x16.py index 93dbc4ea..24a1b79f 100644 --- a/compiler/modules/hierarchical_predecode4x16.py +++ b/compiler/modules/hierarchical_predecode4x16.py @@ -25,17 +25,17 @@ class hierarchical_predecode4x16(hierarchical_predecode): self.add_modules() self.create_input_inverters() connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"], - ["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"], ["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"], - ["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"], ["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"], ["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"], ["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"], ["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"], ["inbar_0", "inbar_1", "inbar_2", "in_3", "out_8", "vdd", "gnd"], - ["in_0", "inbar_1", "inbar_2", "in_3", "out_9", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "in_3", "out_9", "vdd", "gnd"], ["inbar_0", "in_1", "inbar_2", "in_3", "out_10", "vdd", "gnd"], - ["in_0", "in_1", "inbar_2", "in_3", "out_11", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "in_3", "out_11", "vdd", "gnd"], ["inbar_0", "inbar_1", "in_2", "in_3", "out_12", "vdd", "gnd"], ["in_0", "inbar_1", "in_2", "in_3", "out_13", "vdd", "gnd"], ["inbar_0", "in_1", "in_2", "in_3", "out_14", "vdd", "gnd"], @@ -50,15 +50,15 @@ class hierarchical_predecode4x16(hierarchical_predecode): ["Abar_0", "A_1", "Abar_2", "Abar_3"], ["A_0", "A_1", "Abar_2", "Abar_3"], ["Abar_0", "Abar_1", "A_2" , "Abar_3"], - ["A_0", "Abar_1", "A_2" , "Abar_3"], - ["Abar_0", "A_1", "A_2" , "Abar_3"], + ["A_0", "Abar_1", "A_2" , "Abar_3"], + ["Abar_0", "A_1", "A_2" , "Abar_3"], ["A_0", "A_1", "A_2" , "Abar_3"], ["Abar_0", "Abar_1", "Abar_2", "A_3"], ["A_0", "Abar_1", "Abar_2", "A_3"], ["Abar_0", "A_1", "Abar_2", "A_3"], ["A_0", "A_1", "Abar_2", "A_3"], ["Abar_0", "Abar_1", "A_2", "A_3"], - ["A_0", "Abar_1", "A_2", "A_3"], - ["Abar_0", "A_1", "A_2", "A_3"], + ["A_0", "Abar_1", "A_2", "A_3"], + ["Abar_0", "A_1", "A_2", "A_3"], ["A_0", "A_1", "A_2", "A_3"]] return combination diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index c07b4f1b..b4702b2c 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -32,9 +32,9 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): self.rbl = rbl self.left_rbl = left_rbl self.right_rbl = right_rbl - + debug.check(len(self.all_ports) < 3, "Local bitcell array only supports dual port or less.") - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -42,7 +42,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): # 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() @@ -56,7 +56,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): self.add_layout_pins() self.route() - + self.add_boundary() self.DRC_LVS() @@ -78,7 +78,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): rows=self.rows + 1, cols=self.cols) self.add_mod(self.wl_array) - + def add_pins(self): # Outputs from the wordline driver (by port) self.driver_wordline_outputs = [] @@ -87,18 +87,18 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): self.wordline_names = self.bitcell_array.wordline_names self.all_wordline_names = self.bitcell_array.all_wordline_names - + self.bitline_names = self.bitcell_array.bitline_names self.all_bitline_names = self.bitcell_array.all_bitline_names self.rbl_wordline_names = self.bitcell_array.rbl_wordline_names self.all_rbl_wordline_names = self.bitcell_array.all_rbl_wordline_names - + self.rbl_bitline_names = self.bitcell_array.rbl_bitline_names self.all_rbl_bitline_names = self.bitcell_array.all_rbl_bitline_names - + self.all_array_wordline_inputs = [x + "i" for x in self.bitcell_array.get_all_wordline_names()] - + # Arrays are always: # bit lines (left to right) # word lines (bottom to top) @@ -133,10 +133,10 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): else: temp += self.get_wordline_names(port)[::-1] self.driver_wordline_outputs.append([x + "i" for x in temp]) - + temp += self.driver_wordline_outputs[-1] temp += ["vdd", "gnd"] - + self.connect_inst(temp) self.bitcell_array_inst = self.add_inst(name="array", @@ -180,7 +180,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): for x in self.get_inouts(): self.copy_layout_pin(self.bitcell_array_inst, x) - + supply_insts = [*self.wl_insts, self.bitcell_array_inst] for pin_name in ["vdd", "gnd"]: for inst in supply_insts: @@ -189,7 +189,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): self.add_power_pin(name=pin_name, loc=pin.center(), start_layer=pin.layer) - + def route(self): # Route the global wordlines @@ -198,27 +198,27 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port) else: wordline_names = [self.get_rbl_wordline_names(port)[port]] + self.get_wordline_names(port)[::-1] - + wordline_pins = self.wl_array.get_inputs() for (wl_name, in_pin_name) in zip(wordline_names, wordline_pins): # wl_pin = self.bitcell_array_inst.get_pin(wl_name) in_pin = self.wl_insts[port].get_pin(in_pin_name) - + y_offset = in_pin.cy() if port == 0: y_offset -= 2 * self.m3_pitch else: y_offset += 2 * self.m3_pitch - + self.add_layout_pin_segment_center(text=wl_name, layer="m3", start=vector(self.wl_insts[port].lx(), y_offset), end=vector(self.wl_insts[port].lx() + self.wl_array.width, y_offset)) - + mid = vector(in_pin.cx(), y_offset) self.add_path("m2", [in_pin.center(), mid]) - + self.add_via_stack_center(from_layer=in_pin.layer, to_layer="m2", offset=in_pin.center()) @@ -228,7 +228,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): # Route the buffers for port in self.all_ports: driver_outputs = self.driver_wordline_outputs[port] - + for (driver_name, net_name) in zip(self.wl_insts[port].mod.get_outputs(), driver_outputs): array_name = net_name[:-1] out_pin = self.wl_insts[port].get_pin(driver_name) @@ -242,7 +242,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): mid_loc = vector(self.wl_insts[port].lx() - 1.5 * self.m3_pitch, out_loc.y) in_loc = in_pin.rc() self.add_path(out_pin.layer, [out_loc, mid_loc, in_loc]) - + def get_main_array_top(self): return self.bitcell_array_inst.by() + self.bitcell_array.get_main_array_top() @@ -262,13 +262,13 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): # must add the offset of the instance offsets = [self.bitcell_array_inst.lx() + x for x in self.bitcell_array.get_column_offsets()] return offsets - + def graph_exclude_bits(self, targ_row=None, targ_col=None): """ 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 but replica in the local array. @@ -281,8 +281,8 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col) def clear_exclude_bits(self): - """ + """ Clears the bit exclusions """ self.bitcell_array.clear_exclude_bits() - + diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index 48bf5f1f..20012427 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -19,9 +19,9 @@ from globals import OPTS class multibank(design.design): """ Dynamically generated a single bank including bitcell array, - hierarchical_decoder, precharge, (optional column_mux and column decoder), + hierarchical_decoder, precharge, (optional column_mux and column decoder), write driver and sense amplifiers. - This module includes the tristate and bank select logic. + This module includes the tristate and bank select logic. """ def __init__(self, name, word_size, num_words, words_per_row, num_banks=1): @@ -41,7 +41,7 @@ class multibank(design.design): self.prefix="gated_" else: self.prefix="" - + self.compute_sizes() self.add_pins() self.add_modules() @@ -50,16 +50,16 @@ class multibank(design.design): # FIXME: Move this to the add modules function self.add_bank_select() - + self.route_layout() - + # Can remove the following, but it helps for debug! - self.add_lvs_correspondence_points() + self.add_lvs_correspondence_points() # Remember the bank center for further placement self.bank_center=self.offset_all_coordinates().scale(-1,-1) - + self.DRC_LVS() def add_pins(self): @@ -91,23 +91,23 @@ class multibank(design.design): #self.route_tri_gate_out() self.route_sense_amp_out() self.route_wordline_driver() - self.route_write_driver() + self.route_write_driver() self.route_row_decoder() self.route_column_address_lines() self.route_control_lines() self.add_control_pins() if self.num_banks > 1: - self.route_bank_select() - + self.route_bank_select() + self.route_supplies() - + def create_instances(self): """ Add modules. The order should not matter! """ # Above the bitcell array self.add_bitcell_array() self.add_precharge_array() - + # Below the bitcell array self.add_column_mux_array() self.add_sense_amp_array() @@ -120,7 +120,7 @@ class multibank(design.design): self.add_wordline_driver() self.add_column_decoder() - + def compute_sizes(self): """ Computes the required sizes to create the bank """ @@ -139,7 +139,7 @@ class multibank(design.design): 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 - + # Number of control lines in the bus self.num_control_lines = 6 # The order of the control signals on the control bus: @@ -153,7 +153,7 @@ class multibank(design.design): if self.col_addr_size>0: self.num_col_addr_lines = 2**self.col_addr_size else: - self.num_col_addr_lines = 0 + self.num_col_addr_lines = 0 # The width of this bus is needed to place other modules (e.g. decoder) # A width on each side too @@ -169,7 +169,7 @@ class multibank(design.design): """ Add all the modules using the class loader """ self.tri = self.mod_tri_gate() self.bitcell = self.mod_bitcell() - + self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols, rows=self.num_rows) self.add_mod(self.bitcell_array) @@ -178,12 +178,12 @@ class multibank(design.design): self.add_mod(self.precharge_array) if self.col_addr_size > 0: - self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols, + self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols, word_size=self.word_size) self.add_mod(self.column_mux_array) - self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size, + self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size, words_per_row=self.words_per_row) self.add_mod(self.sense_amp_array) @@ -199,8 +199,8 @@ class multibank(design.design): self.row_decoder = self.mod_decoder(rows=self.num_rows) self.add_mod(self.row_decoder) - - self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols, + + self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols, word_size=self.word_size) self.add_mod(self.tri_gate_array) @@ -213,12 +213,12 @@ class multibank(design.design): if(self.num_banks > 1): self.bank_select = self.mod_bank_select() self.add_mod(self.bank_select) - + def add_bitcell_array(self): """ Adding Bitcell Array """ - self.bitcell_array_inst=self.add_inst(name="bitcell_array", + self.bitcell_array_inst=self.add_inst(name="bitcell_array", mod=self.bitcell_array, offset=vector(0,0)) temp = [] @@ -238,7 +238,7 @@ class multibank(design.design): # The enclosure is for the well and the spacing is to the bitcell wells y_offset = self.bitcell_array.height + self.m2_gap self.precharge_array_inst=self.add_inst(name="precharge_array", - mod=self.precharge_array, + mod=self.precharge_array, offset=vector(0,y_offset)) temp = [] for i in range(self.num_cols): @@ -255,7 +255,7 @@ class multibank(design.design): self.column_mux_height = 0 return - y_offset = self.column_mux_height + y_offset = self.column_mux_height self.col_mux_array_inst=self.add_inst(name="column_mux_array", mod=self.column_mux_array, offset=vector(0,y_offset).scale(-1,-1)) @@ -287,7 +287,7 @@ class multibank(design.design): else: temp.append("bl_out_{0}".format(i)) temp.append("br_out_{0}".format(i)) - + temp.extend([self.prefix+"s_en", "vdd", "gnd"]) self.connect_inst(temp) @@ -295,16 +295,16 @@ class multibank(design.design): """ Adding Write Driver """ y_offset = self.sense_amp_array.height + self.column_mux_height \ - + self.m2_gap + self.write_driver_array.height - self.write_driver_array_inst=self.add_inst(name="write_driver_array", - mod=self.write_driver_array, + + self.m2_gap + self.write_driver_array.height + self.write_driver_array_inst=self.add_inst(name="write_driver_array", + mod=self.write_driver_array, offset=vector(0,y_offset).scale(-1,-1)) temp = [] for i in range(self.word_size): temp.append("bank_din_{0}".format(i)) - for i in range(self.word_size): - if (self.words_per_row == 1): + for i in range(self.word_size): + if (self.words_per_row == 1): temp.append("bl_{0}".format(i)) temp.append("br_{0}".format(i)) else: @@ -317,10 +317,10 @@ class multibank(design.design): """ data tri gate to drive the data bus """ y_offset = self.sense_amp_array.height+self.column_mux_height \ + self.m2_gap + self.tri_gate_array.height - self.tri_gate_array_inst=self.add_inst(name="tri_gate_array", - mod=self.tri_gate_array, + self.tri_gate_array_inst=self.add_inst(name="tri_gate_array", + mod=self.tri_gate_array, offset=vector(0,y_offset).scale(-1,-1)) - + temp = [] for i in range(self.word_size): temp.append("sa_out_{0}".format(i)) @@ -332,16 +332,16 @@ class multibank(design.design): def add_row_decoder(self): """ Add the hierarchical row decoder """ - - # The address and control bus will be in between decoder and the main memory array - # This bus will route address bits to the decoder input and column mux inputs. + + # The address and control bus will be in between decoder and the main memory array + # This bus will route address bits to the decoder input and column mux inputs. # The wires are actually routed after we placed the stuff on both sides. # The predecoder is below the x-axis and the main decoder is above the x-axis # The address flop and decoder are aligned in the x coord. - + x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - self.row_decoder_inst=self.add_inst(name="row_decoder", - mod=self.row_decoder, + self.row_decoder_inst=self.add_inst(name="row_decoder", + mod=self.row_decoder, offset=vector(x_offset,0)) temp = [] @@ -357,8 +357,8 @@ class multibank(design.design): # The wordline driver is placed to the right of the main decoder width. x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch - self.wordline_driver_inst=self.add_inst(name="wordline_driver", - mod=self.wordline_driver, + self.wordline_driver_inst=self.add_inst(name="wordline_driver", + mod=self.wordline_driver, offset=vector(x_offset,0)) temp = [] @@ -371,16 +371,16 @@ class multibank(design.design): temp.append("gnd") self.connect_inst(temp) - + def add_column_decoder_module(self): - """ + """ Create a 2:4 or 3:8 column address decoder. """ # Place the col decoder right aligned with row decoder x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) y_off = -(self.col_decoder.height + 2*drc("well_to_well")) - self.col_decoder_inst=self.add_inst(name="col_address_decoder", - mod=self.col_decoder, + self.col_decoder_inst=self.add_inst(name="col_address_decoder", + mod=self.col_decoder, offset=vector(x_off,y_off)) temp = [] @@ -390,9 +390,9 @@ class multibank(design.design): temp.append("sel_{0}".format(j)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - + def add_column_decoder(self): - """ + """ Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2, 2:4 decoder, or 3:8 decoder. """ @@ -408,7 +408,7 @@ class multibank(design.design): else: # No error checking before? debug.error("Invalid column decoder?",-1) - + self.add_column_decoder_module() @@ -417,7 +417,7 @@ class multibank(design.design): if not self.num_banks > 1: return - + x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) if self.col_addr_size > 0: y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by()) @@ -441,7 +441,7 @@ class multibank(design.design): for inst in self.insts: self.copy_power_pins(inst,"vdd") self.copy_power_pins(inst,"gnd") - + def route_bank_select(self): """ Route the bank select logic. """ for input_name in self.input_control_signals+["bank_sel"]: @@ -461,8 +461,8 @@ class multibank(design.design): self.add_via_center(layers=self.m2_stack, offset=out_pos, rotate=90) - - + + def setup_layout_constraints(self): """ After the modules are instantiated, find the dimensions for the control bus, power ring, etc. """ @@ -472,36 +472,36 @@ class multibank(design.design): #driver. # Leave room for the output below the tri gate. #tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch - write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch + write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch row_decoder_min_y_offset = self.row_decoder_inst.by() if self.col_addr_size > 0: col_decoder_min_y_offset = self.col_decoder_inst.by() else: col_decoder_min_y_offset = row_decoder_min_y_offset - + if self.num_banks>1: # The control gating logic is below the decoder # Min of the control gating logic and tri gate. self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset) else: - # Just the min of the decoder logic logic and tri gate. + # Just the min of the decoder logic logic and tri gate. self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset) # The max point is always the top of the precharge bitlines # Add a vdd and gnd power rail above the array self.max_y_offset = self.precharge_array_inst.uy() + 3*self.m1_width self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width - self.min_x_offset = self.row_decoder_inst.lx() - + self.min_x_offset = self.row_decoder_inst.lx() + # # Create the core bbox for the power rings ur = vector(self.max_x_offset, self.max_y_offset) ll = vector(self.min_x_offset, self.min_y_offset) self.core_bbox = [ll, ur] - + self.height = ur.y - ll.y self.width = ur.x - ll.x - - + + def route_central_bus(self): """ Create the address, supply, and control signal central bus lines. """ @@ -509,7 +509,7 @@ class multibank(design.design): # Overall central bus width. It includes all the column mux lines, # and control lines. # 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 + # 2 pitches on the right for vias/jogs to access the inputs control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0) control_bus_length = self.bitcell_array_inst.uy() self.bus_xoffset = self.create_vertical_bus(layer="m2", @@ -542,7 +542,7 @@ class multibank(design.design): # Only do this if we have a column mux! if self.col_addr_size==0: return - + for i in range(self.num_cols): col_mux_bl = self.col_mux_array_inst.get_pin("bl_{}".format(i)).uc() col_mux_br = self.col_mux_array_inst.get_pin("br_{}".format(i)).uc() @@ -554,7 +554,7 @@ class multibank(design.design): vector(bitcell_bl.x,yoffset), bitcell_bl]) self.add_path("m2",[col_mux_br, vector(col_mux_br.x,yoffset), vector(bitcell_br.x,yoffset), bitcell_br]) - + def route_sense_amp_to_col_mux_or_bitcell_array(self): """ Routing of BL and BR between sense_amp and column mux or bitcell array """ @@ -570,19 +570,19 @@ class multibank(design.design): # Sense amp is directly connected to the bitcell array connect_bl = self.bitcell_array_inst.get_pin("bl_{}".format(i)).bc() connect_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).bc() - - + + yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) self.add_path("m2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset), vector(connect_bl.x,yoffset), connect_bl]) self.add_path("m2",[sense_amp_br, vector(sense_amp_br.x,yoffset), vector(connect_br.x,yoffset), connect_br]) - + def route_sense_amp_to_trigate(self): """ Routing of sense amp output to tri_gate input """ for i in range(self.word_size): - # Connection of data_out of sense amp to data_in + # Connection of data_out of sense amp to data_in tri_gate_in = self.tri_gate_array_inst.get_pin("in_{}".format(i)).lc() sa_data_out = self.sense_amp_array_inst.get_pin("data_{}".format(i)).bc() @@ -597,17 +597,17 @@ class multibank(design.design): 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), - layer=data_pin.layer, + layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), width=data_pin.width()), - + def route_tri_gate_out(self): """ 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), - layer=data_pin.layer, + layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), width=data_pin.width()), @@ -622,21 +622,21 @@ class multibank(design.design): decoder_name = "a_{}".format(i) addr_name = "a_{}".format(addr_idx) self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name) - - + + def route_write_driver(self): """ Connecting write driver """ - + for i in range(self.word_size): - data_name = "data_{}".format(i) + data_name = "data_{}".format(i) din_name = "bank_din_{}".format(i) self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) - - + + def route_wordline_driver(self): """ Connecting Wordline driver output to Bitcell WL connection """ - + # we don't care about bends after connecting to the input pin, so let the path code decide. for i in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs @@ -653,23 +653,23 @@ class multibank(design.design): mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) - + def route_column_address_lines(self): """ Connecting the select lines of column mux to the address bus """ if not self.col_addr_size>0: return - + if self.col_addr_size == 1: - + # Connect to sel[0] and sel[1] 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 = [] for i in range(self.num_col_addr_lines): @@ -679,7 +679,7 @@ class multibank(design.design): decoder_name = "in_{}".format(i) addr_name = "a_{}".format(i) self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name) - + # This will do a quick "river route" on two layers. # When above the top select line it will offset "inward" again to prevent conflicts. @@ -688,7 +688,7 @@ class multibank(design.design): for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)): mux_name = "sel_{}".format(i) mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc() - + decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center() # To get to the edge of the decoder and one track out @@ -700,13 +700,13 @@ class multibank(design.design): mid2_pos = vector(mid1_pos.x,mux_addr_pos.y) #self.add_wire(self.m1_stack,[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) self.add_path("m1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) - - + + def add_lvs_correspondence_points(self): - """ This adds some points for easier debugging if LVS goes wrong. + """ 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. """ @@ -715,9 +715,9 @@ class multibank(design.design): wl_name = "wl_{}".format(i) wl_pin = self.bitcell_array_inst.get_pin(wl_name) self.add_label(text=wl_name, - layer="m1", + layer="m1", offset=wl_pin.center()) - + # Add the bitline names for i in range(self.num_cols): bl_name = "bl_{}".format(i) @@ -725,35 +725,35 @@ class multibank(design.design): bl_pin = self.bitcell_array_inst.get_pin(bl_name) br_pin = self.bitcell_array_inst.get_pin(br_name) self.add_label(text=bl_name, - layer="m2", + layer="m2", offset=bl_pin.center()) self.add_label(text=br_name, - layer="m2", + layer="m2", offset=br_pin.center()) - # # Add the data output names to the sense amp output + # # Add the data output names to the sense amp output # for i in range(self.word_size): # data_name = "data_{}".format(i) # data_pin = self.sense_amp_array_inst.get_pin(data_name) # self.add_label(text="sa_out_{}".format(i), - # layer="m2", + # layer="m2", # offset=data_pin.center()) # Add labels on the decoder for i in range(self.word_size): data_name = "dec_out_{}".format(i) - pin_name = "in_{}".format(i) + pin_name = "in_{}".format(i) data_pin = self.wordline_driver_inst.get_pin(pin_name) self.add_label(text=data_name, - layer="m1", + layer="m1", offset=data_pin.center()) - - + + def route_control_lines(self): """ Route the control lines of the entire bank """ - + # Make a list of tuples that we will connect. - # From control signal to the module pin + # From control signal to the module pin # Connection from the central bus to the main control block crosses # pre-decoder and this connection is in metal3 connection = [] @@ -762,7 +762,7 @@ class multibank(design.design): connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc())) connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc())) connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc())) - + for (control_signal, pin_pos) in connection: control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) self.add_path("m1", [control_pos, pin_pos]) @@ -781,7 +781,7 @@ class multibank(design.design): self.add_via_center(layers=self.m1_stack, offset=control_via_pos, rotate=90) - + def add_control_pins(self): """ Add the control signal input pins """ @@ -791,15 +791,15 @@ class multibank(design.design): if self.num_banks > 1: # it's not an input pin if we have multiple banks self.add_label_pin(text=ctrl, - layer="m2", - offset=vector(x_offset, self.min_y_offset), - width=self.m2_width, + layer="m2", + offset=vector(x_offset, self.min_y_offset), + width=self.m2_width, height=self.max_y_offset-self.min_y_offset) else: self.add_layout_pin(text=ctrl, - layer="m2", - offset=vector(x_offset, self.min_y_offset), - width=self.m2_width, + layer="m2", + offset=vector(x_offset, self.min_y_offset), + width=self.m2_width, height=self.max_y_offset-self.min_y_offset) @@ -815,8 +815,8 @@ class multibank(design.design): self.add_via(layers=self.m2_stack, offset=in_pin + self.m2m3_via_offset, rotate=90) - - + + def connect_rail_from_left(self,inst, pin, rail): """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ in_pin = inst.get_pin(pin).rc() diff --git a/compiler/modules/orig_bitcell_array.py b/compiler/modules/orig_bitcell_array.py index 97b681e9..cf982ce5 100644 --- a/compiler/modules/orig_bitcell_array.py +++ b/compiler/modules/orig_bitcell_array.py @@ -27,7 +27,7 @@ class bitcell_array(bitcell_base_array): # 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() @@ -41,7 +41,7 @@ class bitcell_array(bitcell_base_array): self.add_layout_pins() self.add_boundary() - + self.DRC_LVS() def add_modules(self): @@ -58,20 +58,20 @@ class bitcell_array(bitcell_base_array): self.cell_inst[row, col]=self.add_inst(name=name, mod=self.cell) self.connect_inst(self.get_bitcell_pins(col, row)) - + def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" - + # 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) @@ -102,7 +102,7 @@ class bitcell_array(bitcell_base_array): 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 @@ -111,7 +111,7 @@ class bitcell_array(bitcell_base_array): 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/port_address.py b/compiler/modules/port_address.py index 14d0b833..48140995 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -19,12 +19,12 @@ class port_address(design.design): """ def __init__(self, cols, rows, port, name=""): - + self.num_cols = cols self.num_rows = rows self.port = port self.addr_size = ceil(log(self.num_rows, 2)) - + if name == "": name = "port_address_{0}_{1}".format(cols, rows) super().__init__(name) @@ -42,7 +42,7 @@ class port_address(design.design): self.create_row_decoder() self.create_wordline_driver() self.create_rbl_driver() - + def create_layout(self): if "li" in layer: self.route_layer = "li" @@ -57,23 +57,23 @@ class port_address(design.design): 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("rbl_wl", "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.wordline_driver_array_inst, self.row_decoder_inst]: @@ -85,7 +85,7 @@ class port_address(design.design): self.add_power_pin("vdd", rbl_vdd_pin.center()) else: self.add_power_pin("vdd", rbl_vdd_pin.lc()) - + def route_pins(self): for row in range(self.addr_size): decoder_name = "addr_{}".format(row) @@ -96,7 +96,7 @@ class port_address(design.design): self.copy_layout_pin(self.wordline_driver_array_inst, driver_name) self.copy_layout_pin(self.rbl_driver_inst, "Z", "rbl_wl") - + 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 @@ -133,13 +133,13 @@ class port_address(design.design): self.add_layout_pin_rect_center(text="wl_en", layer=en_pin.layer, offset=rbl_in_pos) - + def add_modules(self): self.row_decoder = factory.create(module_type="decoder", num_outputs=self.num_rows) self.add_mod(self.row_decoder) - + self.wordline_driver_array = factory.create(module_type="wordline_driver_array", rows=self.num_rows, cols=self.num_cols) @@ -165,12 +165,12 @@ class port_address(design.design): self.rbl_driver = factory.create(module_type="buf_dec", size=driver_size, height=b.height) - + self.add_mod(self.rbl_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) @@ -187,7 +187,7 @@ class port_address(design.design): self.rbl_driver_inst = self.add_inst(name="rbl_driver", mod=self.rbl_driver) - + temp = [] temp.append("wl_en") temp.append("rbl_wl") @@ -197,7 +197,7 @@ class port_address(design.design): def create_wordline_driver(self): """ Create the Wordline Driver """ - + self.wordline_driver_array_inst = self.add_inst(name="wordline_driver", mod=self.wordline_driver_array) @@ -231,7 +231,7 @@ class port_address(design.design): rbl_driver_offset = vector(x_offset, self.wordline_driver_array.height) self.rbl_driver_inst.place(rbl_driver_offset) - + # Pass this up self.predecoder_height = self.row_decoder.predecoder_height diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 636f4b44..9848341e 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -39,7 +39,7 @@ class port_data(design.design): self.bit_offsets.append(i * bitcell.width) else: self.bit_offsets = bit_offsets - + if name == "": name = "port_data_{0}".format(self.port) super().__init__(name) @@ -189,7 +189,7 @@ class port_data(design.design): # Precharge will be shifted left if needed # Column offset is set to port so extra column can be on left or right # and mirroring happens correctly - + # Used for names/dimensions only self.cell = factory.create(module_type="bitcell") @@ -295,7 +295,7 @@ class port_data(design.design): for bit in range(self.num_cols): temp.append("bl_{0}".format(bit)) temp.append("br_{0}".format(bit)) - + for bit in range(self.num_spare_cols): temp.append("sparebl_{0}".format(bit)) temp.append("sparebr_{0}".format(bit)) @@ -352,12 +352,12 @@ class port_data(design.design): else: temp.append("bl_out_{0}".format(bit)) temp.append("br_out_{0}".format(bit)) - + for bit in range(self.num_spare_cols): temp.append("dout_{}".format(self.word_size + bit)) temp.append("sparebl_{0}".format(bit)) - temp.append("sparebr_{0}".format(bit)) - + temp.append("sparebr_{0}".format(bit)) + temp.append("s_en") temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -561,26 +561,26 @@ class port_data(design.design): else: start_bit=0 - # spare cols connected to precharge array since they are read independently + # spare cols connected to precharge array since they are read independently if self.num_spare_cols and self.col_addr_size>0: if self.port==0: off = 1 else: off = 0 - + self.channel_route_bitlines(inst1=self.column_mux_array_inst, inst1_bls_template="{inst}_out_{bit}", inst2=inst2, num_bits=self.word_size, inst1_start_bit=start_bit) - + self.channel_route_bitlines(inst1=self.precharge_array_inst, inst1_bls_template="{inst}_{bit}", inst2=inst2, num_bits=self.num_spare_cols, inst1_start_bit=self.num_cols + off, inst2_start_bit=self.word_size) - + elif layer_props.port_data.channel_route_bitlines: self.channel_route_bitlines(inst1=inst1, inst1_bls_template=inst1_bls_templ, @@ -595,7 +595,7 @@ class port_data(design.design): inst2=inst2, num_bits=self.word_size, 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 @@ -619,19 +619,19 @@ class port_data(design.design): else: off = 0 - # Channel route spare columns' bitlines + # Channel route spare columns' bitlines if self.num_spare_cols and self.col_addr_size>0: if self.port==0: off = 1 else: off = 0 - + self.channel_route_bitlines(inst1=self.column_mux_array_inst, inst1_bls_template="{inst}_out_{bit}", inst2=inst2, num_bits=self.word_size, inst1_start_bit=start_bit) - + self.channel_route_bitlines(inst1=self.precharge_array_inst, inst1_bls_template="{inst}_{bit}", inst2=inst2, @@ -689,7 +689,7 @@ class port_data(design.design): "br_{}".format(bit)) else: debug.error("Didn't find precharge array.") - + # Copy bitlines of spare columns for bit in range(self.num_spare_cols): if self.precharge_array_inst: diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 7f101ac9..a9cf2824 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -23,7 +23,7 @@ class precharge_array(design.design): super().__init__(name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br)) - + self.columns = columns self.offsets = offsets self.size = size @@ -35,7 +35,7 @@ class precharge_array(design.design): self.en_bar_layer = "m3" else: self.en_bar_layer = "m1" - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -61,10 +61,10 @@ class precharge_array(design.design): self.add_modules() self.add_pins() self.create_insts() - + def create_layout(self): self.place_insts() - + self.width = self.offsets[-1] + self.pc_cell.width self.height = self.pc_cell.height @@ -94,12 +94,12 @@ class precharge_array(design.design): to_layer=self.en_bar_layer, offset=inst.get_pin("en_bar").center()) self.copy_layout_pin(inst, "vdd") - + for i in range(len(self.local_insts)): inst = self.local_insts[i] self.copy_layout_pin(inst, "bl", "bl_{0}".format(i)) self.copy_layout_pin(inst, "br", "br_{0}".format(i)) - + def create_insts(self): """Creates a precharge array by horizontally tiling the precharge cell""" self.local_insts = [] @@ -114,7 +114,7 @@ class precharge_array(design.design): def place_insts(self): """ Places precharge array by horizontally tiling the precharge cell""" - + # Default to single spaced columns if not self.offsets: self.offsets = [n * self.pc_cell.width for n in range(self.columns)] @@ -130,4 +130,4 @@ class precharge_array(design.design): offset = vector(tempx, 0) self.local_insts[i].place(offset=offset, mirror=mirror) - + diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 12c1706e..5c354c97 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -52,7 +52,7 @@ class replica_bitcell_array(bitcell_base_array): else: self.right_rbl=[1] if len(self.all_ports) > 1 else [] self.rbls = self.left_rbl + self.right_rbl - + debug.check(sum(self.rbl) == len(self.all_ports), "Invalid number of RBLs for port configuration.") debug.check(sum(self.rbl) >= len(self.left_rbl) + len(self.right_rbl), @@ -80,7 +80,7 @@ class replica_bitcell_array(bitcell_base_array): # 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() @@ -119,7 +119,7 @@ class replica_bitcell_array(bitcell_base_array): # Replica bitlines self.replica_columns = {} - + for port in self.all_ports: if port in self.left_rbl: # We will always have self.rbl[0] rows of replica wordlines below @@ -127,17 +127,17 @@ class replica_bitcell_array(bitcell_base_array): # These go from the top (where the bitcell array starts ) down replica_bit = self.rbl[0] - port elif port in self.right_rbl: - + # We will always have self.rbl[0] rows of replica wordlines below # the array. # These go from the bottom up replica_bit = self.rbl[0] + self.row_size + port else: continue - + # If we have an odd numer on the bottom column_offset = self.rbl[0] + 1 - + self.replica_columns[port] = factory.create(module_type="replica_column", rows=self.row_size, rbl=self.rbl, @@ -213,7 +213,7 @@ class replica_bitcell_array(bitcell_base_array): # # vdd # gnd - + self.add_bitline_pins() self.add_wordline_pins() self.add_pin("vdd", "POWER") @@ -238,13 +238,13 @@ class replica_bitcell_array(bitcell_base_array): self.add_pin_list(self.all_bitline_names, "INOUT") for port in self.right_rbl: self.add_pin_list(self.rbl_bitline_names[port], "INOUT") - + def add_wordline_pins(self): try: end_caps_enabled = cell_properties.bitcell.end_caps except AttributeError: end_caps_enabled = False - + # Wordlines to ground self.gnd_wordline_names = [] @@ -259,7 +259,7 @@ class replica_bitcell_array(bitcell_base_array): self.wordline_names = self.bitcell_array.wordline_names self.all_wordline_names = self.bitcell_array.all_wordline_names - + # All wordlines including dummy and RBL self.replica_array_wordline_names = [] self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) @@ -297,7 +297,7 @@ class replica_bitcell_array(bitcell_base_array): self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies) else: self.replica_col_insts.append(None) - + # Dummy rows under the bitcell array (connected with with the replica cell wl) self.dummy_row_replica_insts = [] # Note, this is the number of left and right even if we aren't adding the columns to this bitcell array! @@ -347,7 +347,7 @@ class replica_bitcell_array(bitcell_base_array): self.bitcell_array_inst.place(offset=self.unused_offset) self.add_replica_columns() - + self.add_end_caps() @@ -359,7 +359,7 @@ class replica_bitcell_array(bitcell_base_array): self.add_layout_pins() self.route_unused_wordlines() - + self.add_boundary() self.DRC_LVS() @@ -419,7 +419,7 @@ class replica_bitcell_array(bitcell_base_array): dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul() self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset, mirror="MX" if bit % 2 else "R0") - + def add_end_caps(self): """ Add dummy cells or end caps around the array """ try: @@ -465,7 +465,7 @@ class replica_bitcell_array(bitcell_base_array): def add_layout_pins(self): """ Add the layout pins """ - + #All wordlines #Main array wl and bl/br @@ -514,7 +514,7 @@ class replica_bitcell_array(bitcell_base_array): # vdd/gnd are only connected in the perimeter cells # replica column should only have a vdd/gnd in the dummy cell on top/bottom supply_insts = self.dummy_col_insts + self.dummy_row_insts - + for pin_name in self.supplies: for inst in supply_insts: pin_list = inst.get_pins(pin_name) @@ -560,7 +560,7 @@ class replica_bitcell_array(bitcell_base_array): def ground_pin(self, inst, name): pin = inst.get_pin(name) pin_layer = pin.layer - + left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy()) right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy()) @@ -573,7 +573,7 @@ class replica_bitcell_array(bitcell_base_array): # Add a path to connect to the array self.add_path(pin_layer, [left_loc, left_pin_loc]) self.add_path(pin_layer, [right_loc, right_pin_loc]) - + def gen_bl_wire(self): if OPTS.netlist_only: height = 0 @@ -605,7 +605,7 @@ class replica_bitcell_array(bitcell_base_array): return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col) def clear_exclude_bits(self): - """ + """ Clears the bit exclusions """ self.bitcell_array.init_graph_params() diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 0ce472c3..3788909c 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -35,7 +35,7 @@ class replica_column(bitcell_base_array): self.total_size += 2 except AttributeError: self.total_size += 2 - + self.column_offset = column_offset debug.check(replica_bit != 0 and replica_bit != rows, @@ -77,9 +77,9 @@ class replica_column(bitcell_base_array): self.add_pin("gnd", "GROUND") def add_modules(self): - self.replica_cell = factory.create(module_type="replica_{}".format(OPTS.bitcell)) + self.replica_cell = factory.create(module_type=OPTS.replica_bitcell) self.add_mod(self.replica_cell) - self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell)) + self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell) self.add_mod(self.dummy_cell) try: edge_module_type = ("col_cap" if cell_properties.bitcell.end_caps else "dummy") @@ -96,7 +96,7 @@ class replica_column(bitcell_base_array): end_caps_enabled = cell_properties.bitcell.end_caps except AttributeError: end_caps_enabled = False - + for row in range(self.total_size): name="rbc_{0}".format(row) # Top/bottom cell are always dummy cells. @@ -197,7 +197,7 @@ class replica_column(bitcell_base_array): self.copy_power_pins(inst, pin_name) else: self.copy_layout_pin(inst, pin_name) - + def get_bitline_names(self, port=None): if port == None: return self.all_bitline_names @@ -205,9 +205,9 @@ class replica_column(bitcell_base_array): return self.bitline_names[port] def get_bitcell_pins(self, row, col): - """ + """ Creates a list of connections in the bitcell, - indexed by column and row, for instance use in bitcell_array + indexed by column and row, for instance use in bitcell_array """ bitcell_pins = [] for port in self.all_ports: @@ -219,7 +219,7 @@ class replica_column(bitcell_base_array): return bitcell_pins def get_bitcell_pins_col_cap(self, row, col): - """ + """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ @@ -242,4 +242,4 @@ class replica_column(bitcell_base_array): if row != self.replica_bit: self.graph_inst_exclude.add(cell) - + diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index a46ccebf..f7f08bf2 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -34,7 +34,7 @@ class sense_amp_array(design.design): self.num_spare_cols = 0 else: self.num_spare_cols = num_spare_cols - + self.column_offset = column_offset self.row_size = self.word_size * self.words_per_row @@ -71,13 +71,13 @@ class sense_amp_array(design.design): def create_layout(self): self.place_sense_amp_array() - + self.height = self.amp.height self.width = self.local_insts[-1].rx() self.add_layout_pins() self.route_rails() - + self.add_boundary() self.DRC_LVS() @@ -115,7 +115,7 @@ class sense_amp_array(design.design): self.amp_spacing = self.bitcell.width else: self.amp_spacing = self.amp.width - + if not self.offsets: self.offsets = [] for i in range(self.num_cols + self.num_spare_cols): @@ -130,7 +130,7 @@ class sense_amp_array(design.design): amp_position = vector(xoffset, 0) self.local_insts[i].place(offset=amp_position, mirror=mirror) - + # place spare sense amps (will share the same enable as regular sense amps) for i, xoffset in enumerate(self.offsets[self.num_cols:]): index = self.word_size + i diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index c329a04b..432d3a44 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -25,7 +25,7 @@ class tri_gate_array(design.design): self.columns = columns self.word_size = word_size self.words_per_row = int(self.columns / self.word_size) - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -34,7 +34,7 @@ class tri_gate_array(design.design): self.add_modules() self.add_pins() self.create_array() - + def create_layout(self): self.width = (self.columns / self.words_per_row) * self.tri.width self.height = self.tri.height @@ -47,7 +47,7 @@ class tri_gate_array(design.design): def add_modules(self): self.tri = factory.create(module_type="tri_gate") self.add_mod(self.tri) - + def add_pins(self): """create the name of pins depend on the word size""" for i in range(self.word_size): @@ -74,10 +74,10 @@ class tri_gate_array(design.design): for i in range(0,self.columns,self.words_per_row): base = vector(i*self.tri.width, 0) self.tri_inst[i].place(base) - + def add_layout_pins(self): - + for i in range(0,self.columns,self.words_per_row): index = int(i/self.words_per_row) @@ -105,7 +105,7 @@ class tri_gate_array(design.design): self.add_layout_pin_rect_center(text=n, layer="m3", offset=pin_pos) - + width = self.tri.width * self.columns - (self.words_per_row - 1) * self.tri.width en_pin = self.tri_inst[0].get_pin("en") @@ -114,7 +114,7 @@ class tri_gate_array(design.design): offset=en_pin.ll().scale(0, 1), width=width, height=drc("minwidth_m1")) - + enbar_pin = self.tri_inst[0].get_pin("en_bar") self.add_layout_pin(text="en_bar", layer="m1", diff --git a/compiler/modules/wordline_buffer_array.py b/compiler/modules/wordline_buffer_array.py index 3b83b5eb..90703bc1 100644 --- a/compiler/modules/wordline_buffer_array.py +++ b/compiler/modules/wordline_buffer_array.py @@ -23,7 +23,7 @@ class wordline_buffer_array(design.design): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) - + self.rows = rows self.cols = cols @@ -35,7 +35,7 @@ class wordline_buffer_array(design.design): self.add_modules() self.add_pins() self.create_drivers() - + def create_layout(self): if "li" in layer: self.route_layer = "li" @@ -47,7 +47,7 @@ class wordline_buffer_array(design.design): self.offset_all_coordinates() self.add_boundary() self.DRC_LVS() - + def add_pins(self): # inputs to wordline_driver. for i in range(self.rows): @@ -60,12 +60,12 @@ class wordline_buffer_array(design.design): def add_modules(self): b = factory.create(module_type="bitcell") - + self.wl_driver = factory.create(module_type="inv_dec", size=self.cols, height=b.height) self.add_mod(self.wl_driver) - + def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which @@ -84,7 +84,7 @@ class wordline_buffer_array(design.design): xoffset_list = [self.wld_inst[0].rx()] for num in range(self.rows): # this will result in duplicate polygons for rails, but who cares - + # use the inverter offset even though it will be the and's too (gate_offset, y_dir) = self.get_gate_offset(0, self.wl_driver.height, @@ -92,12 +92,12 @@ class wordline_buffer_array(design.design): # Route both supplies for name in ["vdd", "gnd"]: supply_pin = self.wld_inst[num].get_pin(name) - + # Add pins in two locations for xoffset in xoffset_list: pin_pos = vector(xoffset, supply_pin.cy()) self.add_power_pin(name, pin_pos) - + def create_drivers(self): self.wld_inst = [] for row in range(self.rows): @@ -119,7 +119,7 @@ class wordline_buffer_array(design.design): inst_mirror = "MX" offset = [0, y_offset] - + self.wld_inst[row].place(offset=offset, mirror=inst_mirror) diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index c8d22260..a310a26f 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -24,7 +24,7 @@ class wordline_driver_array(design.design): super().__init__(name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) - + self.rows = rows self.cols = cols @@ -36,7 +36,7 @@ class wordline_driver_array(design.design): self.add_modules() self.add_pins() self.create_drivers() - + def create_layout(self): if "li" in layer: self.route_layer = "li" @@ -48,7 +48,7 @@ class wordline_driver_array(design.design): self.offset_x_coordinates() self.add_boundary() self.DRC_LVS() - + def add_pins(self): # inputs to wordline_driver. for i in range(self.rows): @@ -61,12 +61,12 @@ class wordline_driver_array(design.design): self.add_pin("gnd", "GROUND") def add_modules(self): - + self.wl_driver = factory.create(module_type="wordline_driver", cols=self.cols) - + self.add_mod(self.wl_driver) - + def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which @@ -85,7 +85,7 @@ class wordline_driver_array(design.design): xoffset_list = [self.wld_inst[0].rx()] for num in range(self.rows): # this will result in duplicate polygons for rails, but who cares - + # use the inverter offset even though it will be the and's too (gate_offset, y_dir) = self.get_gate_offset(0, self.wl_driver.height, @@ -93,12 +93,12 @@ class wordline_driver_array(design.design): # Route both supplies for name in ["vdd", "gnd"]: supply_pin = self.wld_inst[num].get_pin(name) - + # Add pins in two locations for xoffset in xoffset_list: pin_pos = vector(xoffset, supply_pin.cy()) self.add_power_pin(name, pin_pos) - + def create_drivers(self): self.wld_inst = [] for row in range(self.rows): @@ -123,7 +123,7 @@ class wordline_driver_array(design.design): inst_mirror = "R0" and2_offset = [self.wl_driver.width, y_offset] - + # add and2 self.wld_inst[row].place(offset=and2_offset, mirror=inst_mirror) @@ -143,7 +143,7 @@ class wordline_driver_array(design.design): layer="m2", offset=en_bottom_pos, height=self.height) - + for row in range(self.rows): and_inst = self.wld_inst[row] @@ -152,7 +152,7 @@ class wordline_driver_array(design.design): self.add_via_stack_center(from_layer=b_pin.layer, to_layer="m2", offset=b_pin.center()) - + # connect the decoder input pin to and2 A self.copy_layout_pin(and_inst, "A", "in_{0}".format(row)) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index a2458f42..6806f034 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -121,13 +121,13 @@ class write_driver_array(design.design): if w == self.write_size: w = 0 windex+=1 - + elif self.num_spare_cols and not self.write_size: self.connect_inst([self.data_name + "_{0}".format(index), self.get_bl_name() + "_{0}".format(index), self.get_br_name() + "_{0}".format(index), self.en_name + "_{0}".format(0), "vdd", "gnd"]) - + else: self.connect_inst([self.data_name + "_{0}".format(index), self.get_bl_name() + "_{0}".format(index), @@ -173,13 +173,13 @@ class write_driver_array(design.design): # place spare write drivers (if spare columns are specified) for i, xoffset in enumerate(self.offsets[self.columns:]): index = self.word_size + i - + if cell_properties.bitcell.mirror.y and (index + self.column_offset) % 2: mirror = "MY" xoffset = xoffset + self.driver.width else: mirror = "" - + base = vector(xoffset, 0) self.driver_insts[index].place(offset=base, mirror=mirror) @@ -229,14 +229,14 @@ class write_driver_array(design.design): offset=en_pin.ll(), width=wmask_en_len - en_gap, height=en_pin.height()) - + for i in range(self.num_spare_cols): inst = self.driver_insts[self.word_size + i] en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks), layer="m1", offset=en_pin.lr() + vector(-drc("minwidth_m1"),0)) - + elif self.num_spare_cols and not self.write_size: # shorten enable rail to accomodate those for spare write drivers left_inst = self.driver_insts[0] diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 94446755..db8c4e1d 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -96,7 +96,7 @@ class write_mask_and_array(design.design): self.offsets = [] for i in range(self.columns): self.offsets.append(i * self.driver_spacing) - + self.width = self.offsets[-1] + self.driver_spacing self.height = self.and2.height @@ -128,7 +128,7 @@ class write_mask_and_array(design.design): layer="m2", offset=in_pos) self.add_path(a_pin.layer, [in_pos, a_pos]) - + # Copy remaining layout pins self.copy_layout_pin(self.and2_insts[i], "Z", "wmask_out_{0}".format(i)) diff --git a/compiler/openram.py b/compiler/openram.py index f046fdb1..25587f6c 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -64,7 +64,7 @@ output_extensions = ["lvs", "sp", "v", "lib", "py", "html", "log"] # Only output lef/gds if back-end if not OPTS.netlist_only: output_extensions.extend(["lef", "gds"]) - + output_files = ["{0}{1}.{2}".format(OPTS.output_path, OPTS.output_name, x) for x in output_extensions] diff --git a/compiler/options.py b/compiler/options.py index 0f94be2b..1a0d1804 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -33,7 +33,7 @@ class options(optparse.Values): # By default, use local arrays with a max fanout of 16 #local_array_size = 16 - + # Write mask size, default will be overwritten with word_size if not user specified write_size = None @@ -140,7 +140,7 @@ class options(optparse.Values): # Route the input/output pins to the perimeter perimeter_pins = False - + purge_temp = True # These are the default modules that can be over-riden diff --git a/compiler/pgates/column_mux.py b/compiler/pgates/column_mux.py index 18092b4d..844fd00a 100644 --- a/compiler/pgates/column_mux.py +++ b/compiler/pgates/column_mux.py @@ -110,7 +110,7 @@ class column_mux(pgate.pgate): layer=self.pin_layer, offset=br_pos, height=self.pin_height) - + def place_ptx(self): """ Create the two pass gate NMOS transistors to switch the bitlines""" @@ -126,7 +126,7 @@ class column_mux(pgate.pgate): if cell_props.pgate.add_implants: self.extend_implants() - + def connect_poly(self): """ Connect the poly gate of the two pass transistors """ @@ -197,7 +197,7 @@ class column_mux(pgate.pgate): + nmos_lower_d_pin.uc().scale(1, 0.5) self.add_path(self.col_mux_stack[2], [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()]) - + def extend_implants(self): """ Add top-to-bottom implants for adjacency issues in s8. @@ -210,7 +210,7 @@ class column_mux(pgate.pgate): ll, ur.x - ll.x, ur.y - ll.y) - + def add_pn_wells(self): """ Add a well and implant over the whole cell. Also, add the diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 21241056..a6efc93b 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -38,7 +38,7 @@ class pand2(pgate.pgate): size_list=[self.size], height=self.height, add_wells=self.add_wells) - + self.add_mod(self.nand) self.add_mod(self.inv) @@ -48,14 +48,14 @@ class pand2(pgate.pgate): self.width = max(self.nand.width, self.inv.width) else: self.width = self.nand.width + self.inv.width - + self.place_insts() self.add_wires() self.add_layout_pins() self.route_supply_rails() self.add_boundary() self.DRC_LVS() - + def add_pins(self): self.add_pin("A", "INPUT") self.add_pin("B", "INPUT") @@ -67,7 +67,7 @@ class pand2(pgate.pgate): self.nand_inst = self.add_inst(name="pand2_nand", mod=self.nand) self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) - + self.inv_inst = self.add_inst(name="pand2_inv", mod=self.inv) self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) @@ -98,7 +98,7 @@ class pand2(pgate.pgate): layer=self.route_layer, offset=vector(0.5 * self.width, self.height), width=self.width) - + if self.vertical: # Shared between two gates y_offset = 0.5 * self.height @@ -108,7 +108,7 @@ class pand2(pgate.pgate): layer=self.route_layer, offset=vector(0.5 * self.width, y_offset), width=self.width) - + def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") @@ -130,7 +130,7 @@ class pand2(pgate.pgate): mid1_point = vector(z1_pin.cx(), a2_pin.cy()) self.add_path(route_layer, [z1_pin.center(), mid1_point, a2_pin.center()]) - + def add_layout_pins(self): pin = self.inv_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Z", @@ -146,4 +146,3 @@ class pand2(pgate.pgate): offset=pin.center(), width=pin.width(), height=pin.height()) - \ No newline at end of file diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index 63d1cd0f..c99a303d 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -21,7 +21,7 @@ class pand3(pgate.pgate): self.vertical = vertical self.size = size - + # Creates the netlist and layout super().__init__(name, height, add_wells) @@ -59,7 +59,7 @@ class pand3(pgate.pgate): self.route_supply_rails() self.add_boundary() self.DRC_LVS() - + def add_pins(self): self.add_pin("A", "INPUT") self.add_pin("B", "INPUT") @@ -72,7 +72,7 @@ class pand3(pgate.pgate): 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"]) @@ -103,7 +103,7 @@ class pand3(pgate.pgate): layer=self.route_layer, offset=vector(0.5 * self.width, self.height), width=self.width) - + if self.vertical: # Shared between two gates y_offset = 0.5 * self.height @@ -113,7 +113,7 @@ class pand3(pgate.pgate): layer=self.route_layer, offset=vector(0.5 * self.width, y_offset), width=self.width) - + def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") @@ -161,4 +161,3 @@ class pand3(pgate.pgate): slew=nand_delay.slew, load=load) return nand_delay + inv_delay - \ No newline at end of file diff --git a/compiler/pgates/pand4.py b/compiler/pgates/pand4.py index 021ccf6c..e353f017 100644 --- a/compiler/pgates/pand4.py +++ b/compiler/pgates/pand4.py @@ -21,7 +21,7 @@ class pand4(pgate.pgate): self.vertical = vertical self.size = size - + # Creates the netlist and layout super().__init__(name, height, add_wells) @@ -59,7 +59,7 @@ class pand4(pgate.pgate): self.route_supply_rails() self.add_boundary() self.DRC_LVS() - + def add_pins(self): self.add_pin("A", "INPUT") self.add_pin("B", "INPUT") @@ -73,7 +73,7 @@ class pand4(pgate.pgate): self.nand_inst = self.add_inst(name="pand4_nand", mod=self.nand) self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"]) - + self.inv_inst = self.add_inst(name="pand4_inv", mod=self.inv) self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) @@ -104,7 +104,7 @@ class pand4(pgate.pgate): layer=self.route_layer, offset=vector(0.5 * self.width, self.height), width=self.width) - + if self.vertical: # Shared between two gates y_offset = 0.5 * self.height @@ -114,7 +114,7 @@ class pand4(pgate.pgate): layer=self.route_layer, offset=vector(0.5 * self.width, y_offset), width=self.width) - + def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") @@ -162,4 +162,4 @@ class pand4(pgate.pgate): slew=nand_delay.slew, load=load) return nand_delay + inv_delay - + diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index c2398d5f..4b6eee99 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -16,7 +16,7 @@ class pbuf(pgate.pgate): This is a simple buffer used for driving loads. """ 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)) @@ -39,7 +39,7 @@ class pbuf(pgate.pgate): self.add_layout_pins() self.route_supply_rails() self.add_boundary() - + def add_pins(self): self.add_pin("A", "INPUT") self.add_pin("Z", "OUTPUT") @@ -53,7 +53,7 @@ class pbuf(pgate.pgate): size=input_size, height=self.height) self.add_mod(self.inv1) - + self.inv2 = factory.create(module_type="pinv", size=self.size, height=self.height, @@ -64,7 +64,7 @@ class pbuf(pgate.pgate): self.inv1_inst = self.add_inst(name="buf_inv1", mod=self.inv1) self.connect_inst(["A", "zb_int", "vdd", "gnd"]) - + self.inv2_inst = self.add_inst(name="buf_inv2", mod=self.inv2) self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) @@ -75,13 +75,13 @@ class pbuf(pgate.pgate): # Add INV2 to the right self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) - + def add_wires(self): # inv1 Z to inv2 A z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") self.add_zjog(self.route_layer, z1_pin.center(), a2_pin.center()) - + def add_layout_pins(self): z_pin = self.inv2_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Z", @@ -96,4 +96,4 @@ class pbuf(pgate.pgate): offset=a_pin.center(), width=a_pin.width(), height=a_pin.height()) - + diff --git a/compiler/pgates/pbuf_dec.py b/compiler/pgates/pbuf_dec.py index e5018b62..310f8c05 100644 --- a/compiler/pgates/pbuf_dec.py +++ b/compiler/pgates/pbuf_dec.py @@ -16,7 +16,7 @@ class pbuf_dec(pgate.pgate): This is a simple buffer used for driving wordlines. """ 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)) @@ -39,7 +39,7 @@ class pbuf_dec(pgate.pgate): self.add_layout_pins() self.route_supply_rails() self.add_boundary() - + def add_pins(self): self.add_pin("A", "INPUT") self.add_pin("Z", "OUTPUT") @@ -53,7 +53,7 @@ class pbuf_dec(pgate.pgate): size=input_size, height=self.height) self.add_mod(self.inv1) - + self.inv2 = factory.create(module_type="pinv_dec", size=self.size, height=self.height) @@ -63,7 +63,7 @@ class pbuf_dec(pgate.pgate): self.inv1_inst = self.add_inst(name="buf_inv1", mod=self.inv1) self.connect_inst(["A", "zb_int", "vdd", "gnd"]) - + self.inv2_inst = self.add_inst(name="buf_inv2", mod=self.inv2) self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) @@ -74,7 +74,7 @@ class pbuf_dec(pgate.pgate): # Add INV2 to the right self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) - + def add_wires(self): # inv1 Z to inv2 A z1_pin = self.inv1_inst.get_pin("Z") @@ -105,7 +105,7 @@ class pbuf_dec(pgate.pgate): offset=a_pin.center(), width=a_pin.width(), height=a_pin.height()) - + def get_stage_efforts(self, external_cout, inp_is_rise=False): """Get the stage efforts of the A -> Z path""" stage_effort_list = [] @@ -113,10 +113,10 @@ class pbuf_dec(pgate.pgate): stage1 = self.inv1.get_stage_effort(stage1_cout, inp_is_rise) stage_effort_list.append(stage1) last_stage_is_rise = stage1.is_rise - + stage2 = self.inv2.get_stage_effort(external_cout, last_stage_is_rise) stage_effort_list.append(stage2) - + return stage_effort_list def get_cin(self): diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index e48f9f6c..2c74be56 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -16,7 +16,7 @@ class pdriver(pgate.pgate): This instantiates an even or odd number of inverters sized for driving a load. """ - + def __init__(self, name, inverting=False, fanout=0, size_list=None, height=None, add_wells=True): debug.info(1, "creating pdriver {}".format(name)) @@ -33,10 +33,10 @@ class pdriver(pgate.pgate): debug.error("Cannot specify both size_list and fanout.", -1) if self.size_list and self.inverting: debug.error("Cannot specify both size_list and inverting.", -1) - + # Creates the netlist and layout super().__init__(name, height, add_wells) - + def compute_sizes(self): # size_list specified if self.size_list: @@ -78,7 +78,7 @@ class pdriver(pgate.pgate): self.extend_wells() self.route_supply_rails() self.add_boundary() - + def add_pins(self): self.add_pin("A", "INPUT") self.add_pin("Z", "OUTPUT") @@ -96,7 +96,7 @@ class pdriver(pgate.pgate): add_well=False self.inv_list.append(temp_inv) self.add_mod(temp_inv) - + def create_insts(self): self.inv_inst_list = [] for x in range(1, self.num_stages + 1): @@ -110,7 +110,7 @@ class pdriver(pgate.pgate): self.connect_inst(["A", "Z", "vdd", "gnd"]) else: self.connect_inst(["A", zbx_int, "vdd", "gnd"]) - + # Create last inverter elif x == self.num_stages: zbn_int = "Zb{}_int".format(x - 1) @@ -127,7 +127,7 @@ class pdriver(pgate.pgate): mod=self.inv_list[x - 1]) self.inv_inst_list.append(inst) self.connect_inst([zbx_int, zbn_int, "vdd", "gnd"]) - + def place_modules(self): # Add the first inverter at the origin self.inv_inst_list[0].place(vector(0, 0)) @@ -136,7 +136,7 @@ class pdriver(pgate.pgate): for x in range(1, len(self.inv_inst_list)): loc = vector(self.inv_inst_list[x - 1].rx(), 0) self.inv_inst_list[x].place(loc) - + def route_wires(self): z_inst_list = [] a_inst_list = [] @@ -168,4 +168,3 @@ class pdriver(pgate.pgate): def get_sizes(self): """ Return the relative sizes of the buffers """ return self.size_list - \ No newline at end of file diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 468f367e..bb057726 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -17,7 +17,7 @@ from tech import cell_properties as cell_props if cell_props.ptx.bin_spice_models: from tech import nmos_bins, pmos_bins - + class pgate(design.design): """ This is a module that implements some shared @@ -34,7 +34,7 @@ class pgate(design.design): # By default, something simple self.height = 14 * self.m1_pitch self.add_wells = add_wells - + if "li" in layer: self.route_layer = "li" else: @@ -50,10 +50,10 @@ class pgate(design.design): contact_to_vdd_rail_space = 0.5 * self.route_layer_width + self.route_layer_space # This is a poly-to-poly of a flipped cell poly_to_poly_gate_space = self.poly_extend_active + 0.5 * self.poly_space - + self.top_bottom_space = max(contact_to_vdd_rail_space, poly_to_poly_gate_space) - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -63,7 +63,7 @@ class pgate(design.design): 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) @@ -71,10 +71,10 @@ class pgate(design.design): def connect_pin_to_rail(self, inst, pin_name, supply_name): """ Connects a ptx pin to a supply rail. """ supply_pin = self.get_pin(supply_name) - + source_pins = inst.get_pins(pin_name) for source_pin in source_pins: - + if supply_name == "gnd": height = supply_pin.by() - source_pin.by() elif supply_name == "vdd": @@ -87,7 +87,7 @@ class pgate(design.design): offset=source_pin.ll(), height=height, width=source_pin.width()) - + def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None): """ Route the input gate to the left side of the cell for access. @@ -103,7 +103,7 @@ class pgate(design.design): self.gds_write("unaliged_gates.gds") debug.check(nmos_gate_pin.ll().x == pmos_gate_pin.ll().x, "Connecting unaligned gates not supported. See unaligned_gates.gds.") - + # Pick point on the left of NMOS and up to PMOS nmos_gate_pos = nmos_gate_pin.ul() + vector(0.5 * self.poly_width, 0) pmos_gate_pos = vector(nmos_gate_pos.x, pmos_gate_pin.bc().y) @@ -114,7 +114,7 @@ class pgate(design.design): # Center is completely symmetric. contact_width = contact.poly_contact.width - + if position == "center": contact_offset = left_gate_offset \ + vector(0.5 * self.poly_width, 0) @@ -158,7 +158,7 @@ class pgate(design.design): self.nwell_yoffset = 0.48 * self.height full_height = self.height + 0.5 * self.m1_width - + # FIXME: float rounding problem if "nwell" in layer: # Add a rail width to extend the well to the top of the rail @@ -197,13 +197,13 @@ class pgate(design.design): def add_nwell_contact(self, pmos, pmos_pos): """ Add an nwell contact next to the given pmos device. """ - + layer_stack = self.active_stack - + # To the right a spacing away from the pmos right active edge contact_xoffset = pmos_pos.x + pmos.active_width \ + self.active_space - + # Must be at least an well enclosure of active down # from the top of the well # OR align the active with the top of PMOS active. @@ -219,12 +219,12 @@ class pgate(design.design): implant_type="n", well_type="n", directions=("V", "V")) - + self.add_rect_center(layer=self.route_layer, offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)), width=self.nwell_contact.mod.second_layer_width, height=self.height - contact_offset.y) - + # Now add the full active and implant for the PMOS # active_offset = pmos_pos + vector(pmos.active_width,0) # This might be needed if the spacing between the actives @@ -257,7 +257,7 @@ class pgate(design.design): rightx = None else: rightx = self.width - + nmos_insts = self.get_tx_insts("nmos") if len(nmos_insts) > 0: self.add_enclosure(nmos_insts, @@ -266,7 +266,7 @@ class pgate(design.design): leftx=0, rightx=rightx, boty=0) - + pmos_insts = self.get_tx_insts("pmos") if len(pmos_insts) > 0: self.add_enclosure(pmos_insts, @@ -275,7 +275,7 @@ class pgate(design.design): leftx=0, rightx=rightx, topy=self.height) - + try: ntap_insts = [self.nwell_contact] self.add_enclosure(ntap_insts, @@ -294,7 +294,7 @@ class pgate(design.design): boty=0) except AttributeError: pass - + def add_pwell_contact(self, nmos, nmos_pos): """ Add an pwell contact next to the given nmos device. """ @@ -318,7 +318,7 @@ class pgate(design.design): implant_type="p", well_type="p", directions=("V", "V")) - + self.add_rect_center(layer=self.route_layer, offset=contact_offset.scale(1, 0.5), width=self.pwell_contact.mod.second_layer_width, @@ -343,14 +343,14 @@ class pgate(design.design): # offset=implant_offset, # width=implant_width, # height=implant_height) - + def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", layer=self.route_layer, offset=vector(0.5 * self.width, 0), width=self.width) - + self.add_layout_pin_rect_center(text="vdd", layer=self.route_layer, offset=vector(0.5 * self.width, self.height), @@ -368,7 +368,7 @@ class pgate(design.design): max_active_xoffset = self.find_highest_layer_coords("active").x max_route_xoffset = self.find_highest_layer_coords(self.route_layer).x + 0.5 * self.m1_space width = max(max_active_xoffset, max_route_xoffset) - + self.width = width @staticmethod @@ -379,7 +379,7 @@ class pgate(design.design): # Find all of the relavent scaled bins and multiples scaled_bins = pgate.scaled_bins(tx_type, target_width) - + for (scaled_width, multiple) in scaled_bins: if abs(target_width - scaled_width) / target_width <= 1 - OPTS.accuracy_requirement: break diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 2546f833..eff1337b 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -44,7 +44,7 @@ class pinv(pgate.pgate): self.nmos_size = size self.pmos_size = beta * size self.beta = beta - + super().__init__(name, height, add_wells) def create_netlist(self): @@ -53,7 +53,7 @@ class pinv(pgate.pgate): self.determine_tx_mults() self.add_ptx() self.create_ptx() - + def create_layout(self): """ Calls all functions related to the generation of the layout """ self.place_ptx() @@ -70,7 +70,7 @@ class pinv(pgate.pgate): self.route_supply_rails() self.connect_rails() self.add_boundary() - + def add_pins(self): """ Adds pins for spice netlist """ pin_list = ["A", "Z", "vdd", "gnd"] @@ -92,7 +92,7 @@ class pinv(pgate.pgate): (self.nmos_width, self.tx_mults) = pgate.pgate.best_bin("nmos", self.nmos_width) (self.pmos_width, self.tx_mults) = pgate.pgate.best_bin("pmos", self.pmos_width) return - + # Do a quick sanity check and bail if unlikely feasible height # Sanity check. can we make an inverter in the height # with minimum tx sizes? @@ -108,7 +108,7 @@ class pinv(pgate.pgate): # rotated m1 pitch or poly to active spacing min_channel = max(contact.poly_contact.width + self.m1_space, contact.poly_contact.width + 2 * self.poly_to_active) - + total_height = tx_height + min_channel + 2 * self.top_bottom_space # debug.check(self.height > total_height, # "Cell height {0} too small for simple min height {1}.".format(self.height, @@ -208,7 +208,7 @@ class pinv(pgate.pgate): connect_poly=True, connect_drain_active=True) self.add_mod(self.nmos) - + self.pmos = factory.create(module_type="ptx", width=self.pmos_width, mults=self.tx_mults, @@ -218,12 +218,12 @@ class pinv(pgate.pgate): connect_poly=True, connect_drain_active=True) self.add_mod(self.pmos) - + def create_ptx(self): """ Create the PMOS and NMOS netlist. """ - + self.pmos_inst = self.add_inst(name="pinv_pmos", mod=self.pmos) self.connect_inst(["Z", "A", "vdd", "vdd"]) @@ -237,7 +237,7 @@ class pinv(pgate.pgate): Place PMOS and NMOS to the layout at the upper-most and lowest position to provide maximum routing in channel """ - + # place PMOS so it is half a poly spacing down from the top self.pmos_pos = self.pmos.active_offset.scale(1, 0) \ + vector(0, @@ -259,7 +259,7 @@ class pinv(pgate.pgate): Route the output (drains) together. Optionally, routes output to edge. """ - + # Get the drain pins nmos_drain_pin = self.nmos_inst.get_pin("D") pmos_drain_pin = self.pmos_inst.get_pin("D") @@ -291,17 +291,17 @@ class pinv(pgate.pgate): self.connect_pin_to_rail(self.nmos_inst, "S", "gnd") self.connect_pin_to_rail(self.pmos_inst, "S", "vdd") - + 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_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["inv_leakage"] - + total_power = self.return_power(power_dyn, power_leak) return total_power - + def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load @@ -316,7 +316,7 @@ class pinv(pgate.pgate): 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. diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py index b8fc8fe0..1a078a3c 100644 --- a/compiler/pgates/pinv_dec.py +++ b/compiler/pgates/pinv_dec.py @@ -38,7 +38,7 @@ class pinv_dec(pinv.pinv): self.supply_layer = "m1" else: self.supply_layer = "m2" - + super().__init__(name, size, beta, self.cell_height, add_wells) def determine_tx_mults(self): @@ -54,7 +54,7 @@ class pinv_dec(pinv.pinv): if cell_props.ptx.bin_spice_models: self.nmos_width = self.nearest_bin("nmos", self.nmos_width) self.pmos_width = self.nearest_bin("pmos", self.pmos_width) - + # Over-ride the route input gate to call the horizontal version. # Other top-level netlist and layout functions are not changed. def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None): @@ -71,7 +71,7 @@ class pinv_dec(pinv.pinv): self.gds_write("unaliged_gates.gds") debug.check(nmos_gate_pin.ll().y == pmos_gate_pin.ll().y, "Connecting unaligned gates not supported. See unaligned_gates.gds.") - + # Pick point on the left of NMOS and up to PMOS nmos_gate_pos = nmos_gate_pin.rc() pmos_gate_pos = pmos_gate_pin.lc() @@ -86,7 +86,7 @@ class pinv_dec(pinv.pinv): offset=contact_offset, directions=directions) self.add_path("poly", [contact_offset, nmos_gate_pin.lc()]) - + self.add_layout_pin_rect_center(text=name, layer=self.route_layer, offset=contact_offset, @@ -143,7 +143,7 @@ class pinv_dec(pinv.pinv): if cell_props.pgate.add_implants: self.extend_implants() - + def extend_implants(self): """ Add top-to-bottom implants for adjacency issues in s8. @@ -165,13 +165,13 @@ class pinv_dec(pinv.pinv): ll, ur.x - ll.x, ur.y - ll.y) - + def route_outputs(self): """ Route the output (drains) together. Optionally, routes output to edge. """ - + # Get the drain pin nmos_drain_pin = self.nmos_inst.get_pin("D") @@ -225,7 +225,7 @@ class pinv_dec(pinv.pinv): self.supply_layer, start=bottom_pos, end=top_pos) - + def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ @@ -238,4 +238,4 @@ class pinv_dec(pinv.pinv): self.add_via_stack_center(offset=source_pos, from_layer=self.route_layer, to_layer=self.supply_layer) - + diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 497bd3df..7d0deb16 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -24,7 +24,7 @@ class pinvbuf(pgate.pgate): self.stage_effort = 4 self.row_height = height # FIXME: Change the number of stages to support high drives. - + # stage effort of 4 or less # The pinvbuf has a FO of 2 for the first stage, so the second stage # should be sized "half" to prevent loading of the first stage @@ -43,14 +43,14 @@ class pinvbuf(pgate.pgate): self.width = 2 * self.inv1.width + self.inv2.width self.height = 2 * self.inv1.height - + self.place_modules() self.route_wires() self.add_layout_pins() self.add_boundary() - + self.offset_all_coordinates() - + def add_pins(self): self.add_pin("A") self.add_pin("Zb") @@ -59,14 +59,14 @@ class pinvbuf(pgate.pgate): self.add_pin("gnd") def add_modules(self): - + # Shield the cap, but have at least a stage effort of 4 input_size = max(1, int(self.predriver_size / self.stage_effort)) self.inv = factory.create(module_type="pinv", size=input_size, height=self.row_height) self.add_mod(self.inv) - + self.inv1 = factory.create(module_type="pinv", size=self.predriver_size, height=self.row_height) @@ -82,11 +82,11 @@ class pinvbuf(pgate.pgate): self.inv1_inst = self.add_inst(name="buf_inv1", mod=self.inv) self.connect_inst(["A", "zb_int", "vdd", "gnd"]) - + self.inv2_inst = self.add_inst(name="buf_inv2", mod=self.inv1) self.connect_inst(["zb_int", "z_int", "vdd", "gnd"]) - + self.inv3_inst = self.add_inst(name="buf_inv3", mod=self.inv2) self.connect_inst(["z_int", "Zb", "vdd", "gnd"]) @@ -101,7 +101,7 @@ class pinvbuf(pgate.pgate): # Add INV2 to the right of INV1 self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) - + # Add INV3 to the right of INV2 self.inv3_inst.place(vector(self.inv2_inst.rx(), 0)) @@ -109,13 +109,13 @@ class pinvbuf(pgate.pgate): self.inv4_inst.place(offset=vector(self.inv2_inst.rx(), 2 * self.inv2.height), mirror="MX") - + def route_wires(self): if "li" in layer: route_stack = self.li_stack else: route_stack = self.m1_stack - + # inv1 Z to inv2 A z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") @@ -124,7 +124,7 @@ class pinvbuf(pgate.pgate): self.add_via_stack_center(from_layer=z1_pin.layer, to_layer=a2_pin.layer, offset=a2_pin.center()) - + # inv2 Z to inv3 A z2_pin = self.inv2_inst.get_pin("Z") a3_pin = self.inv3_inst.get_pin("A") @@ -143,7 +143,7 @@ class pinvbuf(pgate.pgate): self.add_via_stack_center(from_layer=z1_pin.layer, to_layer=route_stack[2], offset=z1_pin.center()) - + def add_layout_pins(self): # Continous vdd rail along with label. @@ -161,7 +161,7 @@ class pinvbuf(pgate.pgate): offset=gnd_pin.ll().scale(0, 1), width=self.width, height=gnd_pin.height()) - + # Continous gnd rail along with label. gnd_pin = self.inv1_inst.get_pin("gnd") self.add_layout_pin(text="gnd", @@ -169,7 +169,7 @@ class pinvbuf(pgate.pgate): offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) - + z_pin = self.inv4_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Z", layer=z_pin.layer, @@ -179,7 +179,7 @@ class pinvbuf(pgate.pgate): self.add_layout_pin_rect_center(text="Zb", layer=zb_pin.layer, offset=zb_pin.center()) - + a_pin = self.inv1_inst.get_pin("A") self.add_layout_pin_rect_center(text="A", layer=a_pin.layer, diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index b2dead7e..b2fe7bae 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -41,15 +41,15 @@ class pnand2(pgate.pgate): if cell_props.ptx.bin_spice_models: self.nmos_width = self.nearest_bin("nmos", self.nmos_width) self.pmos_width = self.nearest_bin("pmos", self.pmos_width) - + # Creates the netlist and layout super().__init__(name, height, add_wells) - + 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 """ @@ -64,7 +64,7 @@ class pnand2(pgate.pgate): self.extend_wells() self.route_inputs() self.add_boundary() - + def add_pins(self): """ Adds pins for spice netlist """ pin_list = ["A", "B", "Z", "vdd", "gnd"] @@ -80,7 +80,7 @@ class pnand2(pgate.pgate): add_source_contact=self.route_layer, add_drain_contact="active") self.add_mod(self.nmos_left) - + self.nmos_right = factory.create(module_type="ptx", width=self.nmos_width, mults=self.tx_mults, @@ -104,7 +104,7 @@ class pnand2(pgate.pgate): add_source_contact=self.route_layer, add_drain_contact=self.route_layer) self.add_mod(self.pmos_right) - + def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ @@ -163,7 +163,7 @@ class pnand2(pgate.pgate): self.add_nwell_contact(self.pmos_right, self.pmos2_pos) self.add_pwell_contact(self.nmos_left, self.nmos2_pos) - + def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ @@ -188,13 +188,13 @@ class pnand2(pgate.pgate): self.inputA_yoffset = max(active_contact_to_poly_contact, active_to_poly_contact, active_to_poly_contact2) - + apin = self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A", position="center") - + self.inputB_yoffset = self.inputA_yoffset + 2 * self.m3_pitch # # active contact metal to poly contact metal spacing # active_contact_to_poly_contact = self.output_yoffset - self.route_layer_space - 0.5 * contact.poly_contact.second_layer_height @@ -217,7 +217,7 @@ class pnand2(pgate.pgate): def route_output(self): """ Route the Z output """ - + # One routing track layer below the PMOS contacts route_layer_offset = 0.5 * contact.poly_contact.second_layer_height + self.route_layer_space self.output_yoffset = self.pmos1_inst.get_pin("D").by() - route_layer_offset @@ -229,7 +229,7 @@ class pnand2(pgate.pgate): # NMOS2 drain nmos_pin = self.nmos2_inst.get_pin("D") bottom_pin_offset = nmos_pin.uc() - + # Output pin out_offset = vector(nmos_pin.cx() + self.route_layer_pitch, self.output_yoffset) @@ -249,7 +249,7 @@ class pnand2(pgate.pgate): # offset=nmos_pin.center()) # self.add_via_center(layers=self.m1_stack, # offset=out_offset) - + # # PMOS1 to mid-drain to NMOS2 drain # self.add_path("m2", # [top_pin_offset, mid1_offset, out_offset, @@ -266,7 +266,7 @@ class pnand2(pgate.pgate): # Bottom transistors self.add_path(self.route_layer, [out_offset, bottom_mid_offset, bottom_pin_offset]) - + # This extends the output to the edge of the cell self.add_layout_pin_rect_center(text="Z", layer=self.route_layer, @@ -278,10 +278,10 @@ class pnand2(pgate.pgate): freq = spice["default_event_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["nand2_leakage"] - + total_power = self.return_power(power_dyn, power_leak) return total_power - + def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load @@ -293,7 +293,7 @@ class pnand2(pgate.pgate): 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. diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index de7e3e68..24396cd4 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -47,7 +47,7 @@ class pnand3(pgate.pgate): # Creates the netlist and layout super().__init__(name, height, add_wells) - + def add_pins(self): """ Adds pins for spice netlist """ pin_list = ["A", "B", "C", "Z", "vdd", "gnd"] @@ -58,7 +58,7 @@ class pnand3(pgate.pgate): self.add_pins() self.add_ptx() self.create_ptx() - + def create_layout(self): """ Calls all functions related to the generation of the layout """ @@ -73,7 +73,7 @@ class pnand3(pgate.pgate): self.connect_rails() self.extend_wells() self.add_boundary() - + def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos_center = factory.create(module_type="ptx", @@ -99,7 +99,7 @@ class pnand3(pgate.pgate): add_source_contact=self.route_layer, add_drain_contact="active") self.add_mod(self.nmos_left) - + self.pmos_left = factory.create(module_type="ptx", width=self.pmos_width, mults=self.tx_mults, @@ -123,13 +123,13 @@ class pnand3(pgate.pgate): add_source_contact=self.route_layer, add_drain_contact=self.route_layer) self.add_mod(self.pmos_right) - + def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ - + # Compute the overlap of the source and drain pins self.ptx_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center() - + # This is the extra space needed to ensure DRC rules # to the active contacts nmos = factory.create(module_type="ptx", tx_type="nmos") @@ -150,7 +150,7 @@ class pnand3(pgate.pgate): self.pmos3_inst = self.add_inst(name="pnand3_pmos3", mod=self.pmos_right) self.connect_inst(["Z", "C", "vdd", "vdd"]) - + self.nmos1_inst = self.add_inst(name="pnand3_nmos1", mod=self.nmos_left) self.connect_inst(["Z", "C", "net1", "gnd"]) @@ -158,7 +158,7 @@ class pnand3(pgate.pgate): self.nmos2_inst = self.add_inst(name="pnand3_nmos2", mod=self.nmos_center) self.connect_inst(["net1", "B", "net2", "gnd"]) - + self.nmos3_inst = self.add_inst(name="pnand3_nmos3", mod=self.nmos_right) self.connect_inst(["net2", "A", "gnd", "gnd"]) @@ -178,14 +178,14 @@ class pnand3(pgate.pgate): self.pmos3_pos = pmos2_pos + self.ptx_offset self.pmos3_inst.place(self.pmos3_pos) - + nmos1_pos = vector(self.pmos_left.active_offset.x, self.top_bottom_space) self.nmos1_inst.place(nmos1_pos) nmos2_pos = nmos1_pos + self.ptx_offset self.nmos2_inst.place(nmos2_pos) - + self.nmos3_pos = nmos2_pos + self.ptx_offset self.nmos3_inst.place(self.nmos3_pos) @@ -225,7 +225,7 @@ class pnand3(pgate.pgate): self.inputA_yoffset = max(active_contact_to_poly_contact, active_to_poly_contact, active_to_poly_contact2) - + apin = self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, @@ -248,7 +248,7 @@ class pnand3(pgate.pgate): if cell_props.pgate.add_implants: self.add_enclosure([apin, bpin, cpin], "npc", drc("npc_enclose_poly")) - + def route_output(self): """ Route the Z output """ @@ -272,7 +272,7 @@ class pnand3(pgate.pgate): # self.add_via_center(layers=self.m1_stack, # offset=nmos3_pin.center(), # directions=("V", "V")) - + # # Route in the A input track (top track) # mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) # self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) @@ -284,7 +284,7 @@ class pnand3(pgate.pgate): top_left_pin_offset = pmos1_pin.center() top_right_pin_offset = pmos3_pin.center() bottom_pin_offset = nmos3_pin.center() - + # PMOS1 to output self.add_path(self.route_layer, [top_left_pin_offset, vector(top_left_pin_offset.x, out_offset.y), @@ -300,7 +300,7 @@ class pnand3(pgate.pgate): width=nmos3_pin.height()) mid3_offset = vector(out_offset.x, nmos3_pin.by()) self.add_path(self.route_layer, [mid3_offset, out_offset]) - + self.add_layout_pin_rect_center(text="Z", layer=self.route_layer, offset=out_offset) @@ -311,10 +311,10 @@ class pnand3(pgate.pgate): freq = spice["default_event_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["nand3_leakage"] - + total_power = self.return_power(power_dyn, power_leak) return total_power - + def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load @@ -326,7 +326,7 @@ class pnand3(pgate.pgate): 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. diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py index a71f2f52..b9fe71e3 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/pgates/pnand4.py @@ -47,7 +47,7 @@ class pnand4(pgate.pgate): # Creates the netlist and layout super().__init__(name, height, add_wells) - + def add_pins(self): """ Adds pins for spice netlist """ pin_list = ["A", "B", "C", "D", "Z", "vdd", "gnd"] @@ -58,7 +58,7 @@ class pnand4(pgate.pgate): self.add_pins() self.add_ptx() self.create_ptx() - + def create_layout(self): """ Calls all functions related to the generation of the layout """ @@ -73,7 +73,7 @@ class pnand4(pgate.pgate): self.connect_rails() self.extend_wells() self.add_boundary() - + def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos_center = factory.create(module_type="ptx", @@ -99,7 +99,7 @@ class pnand4(pgate.pgate): add_source_contact=self.route_layer, add_drain_contact="active") self.add_mod(self.nmos_left) - + self.pmos_left = factory.create(module_type="ptx", width=self.pmos_width, mults=self.tx_mults, @@ -123,13 +123,13 @@ class pnand4(pgate.pgate): add_source_contact=self.route_layer, add_drain_contact=self.route_layer) self.add_mod(self.pmos_right) - + def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ - + # Compute the overlap of the source and drain pins self.ptx_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center() - + # This is the extra space needed to ensure DRC rules # to the active contacts nmos = factory.create(module_type="ptx", tx_type="nmos") @@ -154,7 +154,7 @@ class pnand4(pgate.pgate): self.pmos4_inst = self.add_inst(name="pnand4_pmos4", mod=self.pmos_right) self.connect_inst(["Z", "D", "vdd", "vdd"]) - + self.nmos1_inst = self.add_inst(name="pnand4_nmos1", mod=self.nmos_left) self.connect_inst(["Z", "D", "net1", "gnd"]) @@ -162,7 +162,7 @@ class pnand4(pgate.pgate): self.nmos2_inst = self.add_inst(name="pnand4_nmos2", mod=self.nmos_center) self.connect_inst(["net1", "C", "net2", "gnd"]) - + self.nmos3_inst = self.add_inst(name="pnand4_nmos3", mod=self.nmos_center) self.connect_inst(["net2", "B", "net3", "gnd"]) @@ -170,7 +170,7 @@ class pnand4(pgate.pgate): self.nmos4_inst = self.add_inst(name="pnand4_nmos4", mod=self.nmos_right) self.connect_inst(["net3", "A", "gnd", "gnd"]) - + def place_ptx(self): """ Place the PMOS and NMOS in the layout at the upper-most @@ -186,7 +186,7 @@ class pnand4(pgate.pgate): pmos3_pos = pmos2_pos + self.ptx_offset self.pmos3_inst.place(pmos3_pos) - + self.pmos4_pos = pmos3_pos + self.ptx_offset self.pmos4_inst.place(self.pmos4_pos) @@ -196,13 +196,13 @@ class pnand4(pgate.pgate): nmos2_pos = nmos1_pos + self.ptx_offset self.nmos2_inst.place(nmos2_pos) - + nmos3_pos = nmos2_pos + self.ptx_offset self.nmos3_inst.place(nmos3_pos) self.nmos4_pos = nmos3_pos + self.ptx_offset self.nmos4_inst.place(self.nmos4_pos) - + def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ @@ -221,7 +221,7 @@ class pnand4(pgate.pgate): self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd") self.connect_pin_to_rail(self.pmos4_inst, "D", "vdd") - + def route_inputs(self): """ Route the A and B and C inputs """ @@ -240,7 +240,7 @@ class pnand4(pgate.pgate): self.inputA_yoffset = max(active_contact_to_poly_contact, active_to_poly_contact, active_to_poly_contact2) - + apin = self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, @@ -267,10 +267,10 @@ class pnand4(pgate.pgate): self.inputD_yoffset, "D", position="right") - + if cell_props.pgate.add_implants: self.add_enclosure([apin, bpin, cpin, dpin], "npc", drc("npc_enclose_poly")) - + def route_output(self): """ Route the Z output """ @@ -294,7 +294,7 @@ class pnand4(pgate.pgate): # self.add_via_center(layers=self.m1_stack, # offset=nmos3_pin.center(), # directions=("V", "V")) - + # # Route in the A input track (top track) # mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) # self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) @@ -306,7 +306,7 @@ class pnand4(pgate.pgate): top_left_pin_offset = pmos1_pin.center() top_right_pin_offset = pmos3_pin.center() bottom_pin_offset = nmos4_pin.center() - + # PMOS1 to output self.add_path(self.route_layer, [top_left_pin_offset, vector(top_left_pin_offset.x, out_offset.y), @@ -322,7 +322,7 @@ class pnand4(pgate.pgate): width=nmos4_pin.height()) mid3_offset = vector(out_offset.x, nmos4_pin.by()) self.add_path(self.route_layer, [mid3_offset, out_offset]) - + self.add_layout_pin_rect_center(text="Z", layer=self.route_layer, offset=out_offset) @@ -333,10 +333,10 @@ class pnand4(pgate.pgate): freq = spice["default_event_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["nand4_leakage"] - + total_power = self.return_power(power_dyn, power_leak) return total_power - + def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load @@ -348,7 +348,7 @@ class pnand4(pgate.pgate): 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. diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 3a17be30..70617d0a 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -42,15 +42,15 @@ class pnor2(pgate.pgate): # Creates the netlist and layout super().__init__(name, height, add_wells) - + 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.setup_layout_constants() self.place_ptx() if self.add_wells: @@ -86,7 +86,7 @@ class pnor2(pgate.pgate): add_source_contact=self.route_layer, add_drain_contact=self.route_layer) self.add_mod(self.nmos_right) - + self.pmos_left = factory.create(module_type="ptx", width=self.pmos_width, mults=self.tx_mults, @@ -102,7 +102,7 @@ class pnor2(pgate.pgate): add_source_contact="active", add_drain_contact=self.route_layer) self.add_mod(self.pmos_right) - + def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ @@ -118,7 +118,7 @@ class pnor2(pgate.pgate): + 0.5 * self.nwell_enclose_active self.well_width = self.width + 2 * self.nwell_enclose_active # Height is an input parameter, so it is not recomputed. - + def create_ptx(self): """ Add PMOS and NMOS to the layout at the upper-most and lowest position @@ -140,7 +140,7 @@ class pnor2(pgate.pgate): self.nmos2_inst = self.add_inst(name="pnor2_nmos2", mod=self.nmos_right) 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 @@ -163,10 +163,10 @@ class pnor2(pgate.pgate): self.pmos2_pos = pmos1_pos + self.overlap_offset self.pmos2_inst.place(self.pmos2_pos) - + nmos1_pos = vector(self.pmos_right.active_offset.x, self.top_bottom_space) self.nmos1_inst.place(nmos1_pos) - + self.nmos2_pos = nmos1_pos + self.overlap_offset self.nmos2_inst.place(self.nmos2_pos) @@ -175,7 +175,7 @@ class pnor2(pgate.pgate): self.add_nwell_contact(self.pmos_right, self.pmos2_pos) self.add_pwell_contact(self.nmos_right, self.nmos2_pos) - + def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ @@ -193,14 +193,14 @@ class pnor2(pgate.pgate): bottom_pin_offset = nmos_pin.uy() self.inputB_yoffset = bottom_pin_offset + self.m1_nonpref_pitch self.inputA_yoffset = self.inputB_yoffset + self.m1_nonpref_pitch - + bpin = self.route_input_gate(self.pmos2_inst, self.nmos2_inst, self.inputB_yoffset, "B", position="right", directions=("V", "V")) - + # This will help with the wells and the input/output placement apin = self.route_input_gate(self.pmos1_inst, self.nmos1_inst, @@ -227,7 +227,7 @@ class pnor2(pgate.pgate): # offset=pmos_pin.center()) # m1m2_contact = self.add_via_center(layers=self.m1_stack, # offset=nmos_pin.center()) - + mid1_offset = vector(nmos_pin.center().x, self.output_yoffset) mid2_offset = vector(pmos_pin.center().x, self.output_yoffset) @@ -244,10 +244,10 @@ class pnor2(pgate.pgate): freq = spice["default_event_frequency"] power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_leak = spice["nor2_leakage"] - + total_power = self.return_power(power_dyn, power_leak) return total_power - + def calculate_effective_capacitance(self, load): """Computes effective capacitance. Results in fF""" c_load = load @@ -255,7 +255,7 @@ class pnor2(pgate.pgate): c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) 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 feb9eca6..d3599902 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -42,7 +42,7 @@ class precharge(design.design): else: self.bitline_layer = "m2" self.en_layer = "m1" - + # Creates the netlist and layout # Since it has variable height, it is not a pgate. self.create_netlist() @@ -60,7 +60,7 @@ class precharge(design.design): self.add_pins() self.add_ptx() self.create_ptx() - + def create_layout(self): self.place_ptx() @@ -71,7 +71,7 @@ class precharge(design.design): self.route_bitlines() self.connect_to_bitlines() self.add_boundary() - + def add_pins(self): self.add_pin_list(["bl", "br", "en_bar", "vdd"], ["OUTPUT", "OUTPUT", "INPUT", "POWER"]) @@ -92,7 +92,7 @@ class precharge(design.design): """ Adds a vdd rail at the top of the cell """ - + # Adds the rail across the width of the cell vdd_position = vector(0.5 * self.width, self.height) layer_width = drc("minwidth_" + self.en_layer) @@ -102,11 +102,11 @@ class precharge(design.design): height=layer_width) pmos_pin = self.upper_pmos2_inst.get_pin("S") - + # center of vdd rail pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos]) - + self.add_power_pin("vdd", self.well_contact_pos, directions=("V", "V")) @@ -115,7 +115,7 @@ class precharge(design.design): to_layer=self.en_layer, offset=pmos_pin.center(), directions=("V", "V")) - + def create_ptx(self): """ Create both the upper_pmos and lower_pmos to the module @@ -132,7 +132,7 @@ class precharge(design.design): self.upper_pmos2_inst = self.add_inst(name="upper_pmos2", mod=self.pmos) self.connect_inst(["br", "en_bar", "vdd", "vdd"]) - + def place_ptx(self): """ Place both the upper_pmos and lower_pmos to the module @@ -143,7 +143,7 @@ class precharge(design.design): # Compute the other pmos2 location, # but determining offset to overlap the source and drain pins overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() - + # adds the lower pmos to layout self.lower_pmos_position = vector(self.well_enclose_active + 0.5 * self.m1_width, self.initial_yoffset) @@ -157,7 +157,7 @@ class precharge(design.design): # Second pmos to the right of the first upper_pmos2_pos = self.upper_pmos1_pos + overlap_offset self.upper_pmos2_inst.place(upper_pmos2_pos) - + def connect_poly(self): """ Connects the upper and lower pmos together @@ -202,12 +202,12 @@ class precharge(design.design): layer=self.en_layer, start=offset.scale(0, 1), end=offset.scale(0, 1) + vector(self.width, 0)) - + def place_nwell_and_contact(self): """ Adds a nwell tap to connect to the vdd rail """ - + # adds the contact from active to metal1 offset_height = self.upper_pmos1_inst.uy() + \ contact.active_contact.height + \ @@ -245,7 +245,7 @@ class precharge(design.design): layer=self.bitline_layer, start=pin_pos, end=top_pos) - + # adds the BR self.br_xoffset = self.width - layer_pitch top_pos = vector(self.br_xoffset, self.height) @@ -265,7 +265,7 @@ class precharge(design.design): self.bl_xoffset) self.connect_pmos(self.lower_pmos_inst.get_pin("D"), self.br_xoffset) - + self.connect_pmos(self.upper_pmos1_inst.get_pin("S"), self.bl_xoffset) self.connect_pmos(self.upper_pmos2_inst.get_pin("D"), @@ -301,4 +301,4 @@ class precharge(design.design): self.add_path(self.bitline_layer, [left_pos, right_pos], width=pmos_pin.height()) - + diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py index affc157e..d3b096eb 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/pgates/ptristate_inv.py @@ -36,7 +36,7 @@ class ptristate_inv(pgate.pgate): self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") - + # Creates the netlist and layout super().__init__(name, height) @@ -45,7 +45,7 @@ class ptristate_inv(pgate.pgate): 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() @@ -57,7 +57,7 @@ class ptristate_inv(pgate.pgate): self.route_inputs() self.route_outputs() self.add_boundary() - + def add_pins(self): """ Adds pins for spice netlist """ self.add_pin_list(["in", "out", "en", "en_bar", "vdd", "gnd"]) @@ -78,7 +78,7 @@ class ptristate_inv(pgate.pgate): # 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. - + def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos = factory.create(module_type="ptx", @@ -86,13 +86,13 @@ class ptristate_inv(pgate.pgate): 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", @@ -151,13 +151,13 @@ class ptristate_inv(pgate.pgate): # 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, @@ -181,7 +181,7 @@ class ptristate_inv(pgate.pgate): 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 """ @@ -206,7 +206,7 @@ class ptristate_inv(pgate.pgate): 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. diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index e445cbcd..6b828cf9 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -28,7 +28,7 @@ class ptx(design.design): ll, ur, width and height refer to the active area. Wells and poly may extend beyond this. - + """ def __init__(self, name="", @@ -91,7 +91,7 @@ class ptx(design.design): self.route_layer_width = drc("minwidth_{}".format(self.route_layer)) self.route_layer_space = drc("{0}_to_{0}".format(self.route_layer)) - + # Since it has variable height, it is not a pgate. self.create_netlist() # We must always create ptx layout for pbitcell @@ -104,7 +104,7 @@ class ptx(design.design): # (0,0) will be the corner of the active area (not the larger well) self.translate_all(self.active_offset) - + def create_layout(self): """Calls all functions related to the generation of the layout""" self.setup_layout_constants() @@ -166,7 +166,7 @@ class ptx(design.design): self.mults, self.tx_width, drc("minwidth_poly")) - + def setup_layout_constants(self): """ Pre-compute some handy layout parameters. @@ -184,7 +184,7 @@ class ptx(design.design): self.well_type = "n" else: self.error("Invalid transitor type.", -1) - + # This is not actually instantiated but used for calculations self.active_contact = factory.create(module_type="contact", layer_stack=self.active_stack, @@ -194,7 +194,7 @@ class ptx(design.design): # This is the extra poly spacing due to the poly contact to poly contact pitch # of contacted gates extra_poly_contact_width = contact.poly_contact.width - self.poly_width - + # This is the spacing between S/D contacts # This is the spacing between the poly gates self.min_poly_pitch = self.poly_space + self.poly_width @@ -205,7 +205,7 @@ class ptx(design.design): self.contact_pitch) self.end_to_contact = 0.5 * self.active_contact.width - + # Active width is determined by enclosure on both ends and contacted pitch, # at least one poly and n-1 poly pitches self.active_width = 2 * self.end_to_contact + self.active_contact.width \ @@ -216,7 +216,7 @@ class ptx(design.design): # Poly height must include poly extension over active self.poly_height = self.tx_width + 2 * self.poly_extend_active - + self.active_offset = vector([self.well_enclose_active] * 2) # Well enclosure of active, ensure minwidth as well @@ -230,7 +230,7 @@ class ptx(design.design): else: self.well_height = self.height self.well_width = self.width - + # We are going to shift the 0,0, so include that in the width and height self.height = self.active_height self.width = self.active_width @@ -238,7 +238,7 @@ class ptx(design.design): # This is the center of the first active contact offset (centered vertically) self.contact_offset = self.active_offset + vector(0.5 * self.active_contact.width, 0.5 * self.active_height) - + # Min area results are just flagged for now. debug.check(self.active_width * self.active_height >= self.minarea_active, "Minimum active area violated.") @@ -286,7 +286,7 @@ class ptx(design.design): """ Connect each contact up/down to a source or drain pin """ - + if len(positions) <= 1: return @@ -325,11 +325,11 @@ class ptx(design.design): # poly is one contacted spacing from the end and down an extension poly_offset = self.contact_offset \ + vector(0.5 * self.active_contact.width + 0.5 * self.poly_width + self.active_contact_to_gate, 0) - + # poly_positions are the bottom center of the poly gates self.poly_positions = [] self.poly_gates = [] - + # It is important that these are from left to right, # so that the pins are in the right # order for the accessors @@ -347,12 +347,12 @@ class ptx(design.design): width=self.poly_width) self.poly_positions.append(poly_offset) self.poly_gates.append(gate) - + poly_offset = poly_offset + vector(self.poly_pitch, 0) if self.connect_poly: self.connect_fingered_poly(self.poly_positions) - + def add_active(self): """ Adding the diffusion (active region = diffusion region) @@ -388,7 +388,7 @@ class ptx(design.design): width=self.well_width, height=self.well_height) setattr(self, well_name, well) - + if "vtg" in layer: self.add_rect(layer="vtg", offset=well_ll, @@ -412,7 +412,7 @@ class ptx(design.design): # Keep a list of the source/drain contacts self.source_contacts = [] self.drain_contacts = [] - + # First one is always a SOURCE label = "S" pos = self.contact_offset @@ -437,7 +437,7 @@ class ptx(design.design): else: label = "S" source_positions.append(pos) - + if (label=="S" and self.add_source_contact): contact = self.add_diff_contact(label, pos) self.source_contacts.append(contact) @@ -469,16 +469,16 @@ class ptx(design.design): self.add_layout_pin_rect_center(text=label, layer="active", offset=pos) - + if self.connect_source_active: self.connect_fingered_active(source_positions, "S", top=(self.tx_type=="pmos")) if self.connect_drain_active: self.connect_fingered_active(drain_positions, "D", top=(self.tx_type=="nmos")) - + def get_stage_effort(self, cout): """Returns an object representing the parameters for delay in tau units.""" - + # FIXME: Using the same definition as the pinv.py. parasitic_delay = 1 size = self.mults * self.tx_width / drc("minwidth_tx") @@ -487,12 +487,12 @@ class ptx(design.design): self.input_load(), cout, parasitic_delay) - + def input_load(self): """ Returns the relative gate cin of the tx """ - + # FIXME: this will be applied for the loads of the drain/source return self.mults * self.tx_width / drc("minwidth_tx") @@ -518,10 +518,10 @@ class ptx(design.design): pin_width = via.mod.second_layer_width else: via = None - + pin_height = None pin_width = None - + # Source drain vias are all vertical self.add_layout_pin_rect_center(text=label, layer=layer, @@ -530,7 +530,7 @@ class ptx(design.design): height=pin_height) return(via) - + def get_cin(self): """Returns the relative gate cin of the tx""" return self.tx_width / drc("minwidth_tx") @@ -541,4 +541,4 @@ class ptx(design.design): Overrides base class function. """ self.add_graph_edges(graph, port_nets) - + diff --git a/compiler/pgates/pwrite_driver.py b/compiler/pgates/pwrite_driver.py index 6ae448f9..26c49d2f 100644 --- a/compiler/pgates/pwrite_driver.py +++ b/compiler/pgates/pwrite_driver.py @@ -16,7 +16,7 @@ from sram_factory import factory class pwrite_driver(design.design): """ The pwrite_driver is two tristate inverters that drive the bitlines. - The data input is first inverted before one tristate. + The data input is first inverted before one tristate. The inverted enable is also generated to control one tristate. """ def __init__(self, name, size=0): @@ -53,7 +53,7 @@ class pwrite_driver(design.design): self.route_wires() self.route_supplies() self.add_boundary() - + def add_pins(self): self.add_pin("din", "INPUT") self.add_pin("bl", "OUTPUT") @@ -75,7 +75,7 @@ class pwrite_driver(design.design): #self.add_mod(self.tbuf) #debug.check(self.tbuf.width 0: return self.min_cost - - + + def get_type(self): type_string = "" - + if self.blocked: type_string += "X" @@ -52,5 +52,5 @@ class grid_cell: if type_string != "": return type_string - + return None diff --git a/compiler/router/grid_path.py b/compiler/router/grid_path.py index e4e0995d..6232b411 100644 --- a/compiler/router/grid_path.py +++ b/compiler/router/grid_path.py @@ -14,13 +14,13 @@ from direction import direction class grid_path: """ - A grid path is a list of lists of grid cells. + A grid path is a list of lists of grid cells. It can have a width that is more than one cell. All of the sublists will be the same dimension. Cells should be continguous. It can have a name to define pin shapes as well. """ - + def __init__(self, items=[], name=""): self.name = name if items: @@ -36,22 +36,22 @@ class grid_path: return p def __setitem__(self, index, value): - """ - override setitem function + """ + override setitem function can set value by pathinstance[index]=value """ self.pathlist[index]=value def __getitem__(self, index): """ - override getitem function + override getitem function can get value by value=pathinstance[index] """ return self.pathlist[index] def __contains__(self, key): - """ - Determine if cell exists in this path + """ + Determine if cell exists in this path """ # FIXME: Could maintain a hash to make in O(1) for sublist in self.pathlist: @@ -63,13 +63,13 @@ class grid_path: def __add__(self, items): """ - Override add to do append + Override add to do append """ return self.pathlist.extend(items) def __len__(self): return len(self.pathlist) - + def trim_last(self): """ Drop the last item @@ -83,7 +83,7 @@ class grid_path: """ if len(self.pathlist)>0: self.pathlist.pop(0) - + def append(self,item): """ Append the list of items to the cells @@ -95,7 +95,7 @@ class grid_path: Extend the list of items to the cells """ self.pathlist.extend(item) - + def set_path(self,value=True): for sublist in self.pathlist: for p in sublist: @@ -124,9 +124,9 @@ class grid_path: for sublist in self.pathlist: newset.update(sublist[start_index:end_index]) return newset - + def cost(self): - """ + """ The cost of the path is the length plus a penalty for the number of vias. We assume that non-preferred direction is penalized. This cost only works with 1 wide tracks. @@ -151,9 +151,9 @@ class grid_path: elif p0.x != p1.x and p0.z==1: # horizontal on vertical layer cost += grid.NONPREFERRED_COST elif p0.y != p1.y and p0.z==0: # vertical on horizontal layer - cost += grid.NONPREFERRED_COST + cost += grid.NONPREFERRED_COST else: - cost += grid.PREFERRED_COST + cost += grid.PREFERRED_COST return cost @@ -173,7 +173,7 @@ class grid_path: n = self.neighbor(d) if n: neighbors.append(n) - + return neighbors def neighbor(self, d): @@ -185,20 +185,20 @@ class grid_path: return None elif newwave[0].z>1 or newwave[0].z<0: return None - + return newwave - - + + def set_layer(self, zindex): new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave] self.pathlist = new_pathlist - - + + def overlap(self, other): """ Return the overlap waves ignoring different layers """ - + my_zindex = self.pathlist[0][0].z other_flat_cells = [vector3d(item.x,item.y,my_zindex) for wave in other.pathlist for item in wave] # This keeps the wave structure of the self layer @@ -216,4 +216,4 @@ class grid_path: ur = shared_waves[-1][-1] return [ll,ur] return None - + diff --git a/compiler/router/grid_utils.py b/compiler/router/grid_utils.py index a9a4c08d..30518793 100644 --- a/compiler/router/grid_utils.py +++ b/compiler/router/grid_utils.py @@ -20,22 +20,22 @@ def increment_set(curset, direct): Return the cells incremented in given direction """ offset = direction.get_offset(direct) - + newset = set() for c in curset: newc = c+offset newset.add(newc) - + return newset def remove_border(curset, direct): - """ + """ Remove the cells on a given border. """ border = get_border(curset, direct) curset.difference_update(border) - + def get_upper_right(curset): ur = None @@ -55,7 +55,7 @@ def get_border( curset, direct): """ Return the furthest cell(s) in a given direction. """ - + # find direction-most cell(s) maxc = [] if direct==direction.NORTH: @@ -94,7 +94,7 @@ def expand_border(curset, direct): border_set = get_border(curset, direct) next_border_set = increment_set(border_set, direct) return next_border_set - + def expand_borders(curset): """ Return the expansions in planar directions. @@ -103,7 +103,7 @@ def expand_borders(curset): south_set=expand_border(curset,direction.SOUTH) east_set=expand_border(curset,direction.EAST) west_set=expand_border(curset,direction.WEST) - + return(north_set, east_set, south_set, west_set) def inflate_cell(cell, distance): @@ -111,7 +111,7 @@ def inflate_cell(cell, distance): Expand the current cell in all directions and return the set. """ newset = set(cell) - + if distance==0: return(newset) @@ -119,7 +119,7 @@ def inflate_cell(cell, distance): for offset in direction.all_offsets(): # FIXME: If distance is large this will be inefficient, but it is like 1 or 2 newset.update(inflate_cell(cell+offset,distance-1)) - + return newset def inflate_set(curset, distance): @@ -128,7 +128,7 @@ def inflate_set(curset, distance): """ if distance<=0: return curset - + newset = curset.copy() # Add all my neighbors for c in curset: @@ -149,7 +149,7 @@ def flatten_set(curset): return newset - + def distance_set(coord, curset): """ Return the distance from a coordinate to any item in the set @@ -159,4 +159,4 @@ def distance_set(coord, curset): min_dist = min(coord.euclidean_distance(c), min_dist) return min_dist - + diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 7f30226a..5ba87dc9 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -153,7 +153,7 @@ class pin_group: debug.check(len(new_pin_list) > 0, "Did not find any enclosures.") - + return new_pin_list def compute_connector(self, pin, enclosure): @@ -198,7 +198,7 @@ class pin_group: return p def find_above_connector(self, pin, enclosures): - """ + """ Find the enclosure that is to above the pin and make a connector to it's upper edge. """ @@ -662,7 +662,7 @@ class pin_group: if len(self.grids) < 0: debug.error("Did not find any unblocked grids: {}".format(str(self.pins))) self.router.write_debug_gds("blocked_pin.gds") - + # Remember the secondary grids for removing adjacent pins self.secondary_grids = partial_set diff --git a/compiler/router/router.py b/compiler/router/router.py index ba678215..a6df4f0b 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -34,7 +34,7 @@ class router(router_tech): considered. """ router_tech.__init__(self, layers, rail_track_width) - + self.cell = design # If didn't specify a gds blockage file, write it out to read the gds @@ -50,7 +50,7 @@ class router(router_tech): self.reader.loadFromFile(gds_filename) self.top_name = self.layout.rootStructureName # print_time("GDS read",datetime.now(), start_time) - + # The pin data structures # A map of pin names to a set of pin_layout structures # (i.e. pins with a given label) @@ -60,18 +60,18 @@ class router(router_tech): # (They will be blocked when we are routing other # nets based on their name.) self.all_pins = set() - + # The labeled pins above categorized into pin groups # that are touching/connected. self.pin_groups = {} - + # The blockage data structures # A list of metal shapes (using the same pin_layout structure) # that are not pins but blockages. self.blockages = [] # The corresponding set of blocked grids for above pin shapes self.blocked_grids = set() - + # The routed data structures # A list of paths that have been "routed" self.paths = [] @@ -95,18 +95,18 @@ class router(router_tech): self.pin_groups = {} # DO NOT clear the blockages as these don't change self.rg.reinit() - + def set_top(self, top_name): """ If we want to route something besides the top-level cell.""" self.top_name = top_name - + def is_wave(self, path): """ Determines if this is a multi-track width wave (True) # or a normal route (False) """ return len(path[0]) > 1 - + def retrieve_pins(self, pin_name): """ Retrieve the pin shapes on metal 3 from the layout. @@ -142,7 +142,7 @@ class router(router_tech): debug.info(1, "Finding blockages.") for lpp in [self.vert_lpp, self.horiz_lpp]: self.retrieve_blockages(lpp) - + def find_pins_and_blockages(self, pin_list): """ Find the pins and blockages in the design @@ -155,7 +155,7 @@ class router(router_tech): for pin_name in pin_list: self.retrieve_pins(pin_name) print_time("Retrieving pins", datetime.now(), start_time, 4) - + start_time = datetime.now() for pin_name in pin_list: self.analyze_pins(pin_name) @@ -171,7 +171,7 @@ class router(router_tech): start_time = datetime.now() self.convert_blockages() print_time("Converting blockages", datetime.now(), start_time, 4) - + # This will convert the pins to grid units # It must be done after blockages to ensure no DRCs # between expanded pins and blocked grids @@ -195,7 +195,7 @@ class router(router_tech): start_time = datetime.now() self.separate_adjacent_pins(0) print_time("Separating adjacent pins", datetime.now(), start_time, 4) - + # Enclose the continguous grid units in a metal # rectangle to fix some DRCs start_time = datetime.now() @@ -208,15 +208,15 @@ class router(router_tech): # def combine_adjacent_pins(self, pin_name): # """ # Find pins that have adjacent routing tracks and merge them into a - # single pin_group. The pins themselves may not be touching, but + # single pin_group. The pins themselves may not be touching, but # enclose_pins in the next step will ensure they are touching. - # """ - # debug.info(1,"Combining adjacent pins for {}.".format(pin_name)) + # """ + # debug.info(1,"Combining adjacent pins for {}.".format(pin_name)) # # Find all adjacencies # adjacent_pins = {} # for index1,pg1 in enumerate(self.pin_groups[pin_name]): # for index2,pg2 in enumerate(self.pin_groups[pin_name]): - # # Cannot combine with yourself, also don't repeat + # # Cannot combine with yourself, also don't repeat # if index1<=index2: # continue # # Combine if at least 1 grid cell is adjacent @@ -228,7 +228,7 @@ class router(router_tech): # # Make a list of indices to ensure every group gets in the new set # all_indices = set([x for x in range(len(self.pin_groups[pin_name]))]) - + # # Now reconstruct the new groups # new_pin_groups = [] # for index1,index2_set in adjacent_pins.items(): @@ -251,18 +251,18 @@ class router(router_tech): # for index in all_indices: # new_pin_groups.append(self.pin_groups[pin_name][index]) - # old_size = len(self.pin_groups[pin_name]) + # 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 - + def separate_adjacent_pins(self, separation): """ - This will try to separate all grid pins by the supplied + This will try to separate all grid pins by the supplied number of separation tracks (default is to prevent adjacency). """ # Commented out to debug with SCMOS @@ -277,7 +277,7 @@ class router(router_tech): if i > j: return self.separate_adjacent_pin(pin_name1, pin_name2, separation) - + def separate_adjacent_pin(self, pin_name1, pin_name2, separation): """ Go through all of the pin groups and check if any other pin group is @@ -300,7 +300,7 @@ class router(router_tech): index2, adj_grids)) self.remove_adjacent_grid(pg1, pg2, adj_grids) - + debug.info(1, "Removed {} adjacent grids.".format(removed_grids)) def remove_adjacent_grid(self, pg1, pg2, adj_grids): @@ -316,9 +316,9 @@ class router(router_tech): else: bigger = pg2 smaller = pg1 - + for adj in adj_grids: - + # If the adjacent grids are a subset of the secondary # grids (i.e. not necessary) remove them from each @@ -353,7 +353,7 @@ class router(router_tech): 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 @@ -363,7 +363,7 @@ class router(router_tech): # Block all of the supply rails # (some will be unblocked if they're a target) self.set_supply_rail_blocked(True) - + # Block all of the pin components # (some will be unblocked if they're a source/target) # Also block the previous routes @@ -382,7 +382,7 @@ class router(router_tech): # 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 convert_shape_to_units(self, shape): """ Scale a shape (two vector list) to user units @@ -391,7 +391,7 @@ class router(router_tech): ll = shape[0].scale(unit_factor) ur = shape[1].scale(unit_factor) return [ll, ur] - + def min_max_coord(self, coord): """ Find the lowest and highest corner of a Rectangle @@ -424,7 +424,7 @@ class router(router_tech): """ debug.info(3, "Clearing all blockages") self.rg.clear_blockages() - + def set_blockages(self, blockages, value=True): """ Flag the blockages in the grid """ self.rg.set_blocked(blockages, value) @@ -448,7 +448,7 @@ class router(router_tech): zlayer = self.get_zindex(blockage.lpp) blockage_tracks = self.get_blockage_tracks(ll, ur, zlayer) return blockage_tracks - + def convert_blockages(self): """ Convert blockages to grid tracks. """ debug.info(1, "Converting blockages.") @@ -456,7 +456,7 @@ class router(router_tech): debug.info(3, "Converting blockage {}".format(str(blockage))) blockage_list = self.convert_blockage(blockage) self.blocked_grids.update(blockage_list) - + def retrieve_blockages(self, lpp): """ Recursive find boundaries as blockages to the routing grid. @@ -470,14 +470,14 @@ class router(router_tech): new_pin = pin_layout("blockage{}".format(len(self.blockages)), rect, lpp) - + # If there is a rectangle that is the same in the pins, # it isn't a blockage! if new_pin not in self.all_pins: self.blockages.append(new_pin) def convert_point_to_units(self, p): - """ + """ Convert a path set of tracks to center line path. """ pt = vector3d(p) @@ -489,7 +489,7 @@ class router(router_tech): Convert a wave to a set of center points """ return [self.convert_point_to_units(i) for i in wave] - + def convert_blockage_to_tracks(self, shape): """ Convert a rectangular blockage shape into track units. @@ -518,7 +518,7 @@ class router(router_tech): """ (ll, ur) = pin.rect debug.info(3, "Converting pin [ {0} , {1} ]".format(ll, ur)) - + # scale the size bigger to include neaby tracks ll = ll.scale(self.track_factor).floor() ur = ur.scale(self.track_factor).ceil() @@ -561,7 +561,7 @@ class router(router_tech): max_overlap = max(overlap_rect) if max_overlap > 0: any_overlap.update([coord]) - + return any_overlap def get_best_offgrid_pin(self, pin, insufficient_list): @@ -580,14 +580,14 @@ class router(router_tech): if min_overlap > best_overlap: best_overlap = min_overlap best_coord = coord - + return set([best_coord]) - + def get_furthest_offgrid_pin(self, pin, insufficient_list): """ Get a grid cell that is the furthest from the blocked grids. """ - + # Find the coordinate with the most overlap best_coord = None best_dist = math.inf @@ -596,7 +596,7 @@ class router(router_tech): if min_dist < best_dist: best_dist = min_dist best_coord = coord - + return set([best_coord]) def get_nearest_offgrid_pin(self, pin, insufficient_list): @@ -613,9 +613,9 @@ class router(router_tech): if min_dist < best_dist: best_dist = min_dist best_coord = coord - + return set([best_coord]) - + def convert_pin_coord_to_tracks(self, pin, coord): """ Return all tracks that an inflated pin overlaps @@ -629,7 +629,7 @@ class router(router_tech): inflated_pin = pin_layout(pin.name, pin.inflate(0.5 * self.track_space), pin.layer) - + overlap_length = pin.overlap_length(track_pin) debug.info(4,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, @@ -647,7 +647,7 @@ class router(router_tech): return (coord, None) # If it overlaps with the inflated pin, it is partial elif inflated_overlap_length == math.inf or inflated_overlap_length > 0: - debug.info(4," Partial overlap: {0} >? {1}".format(inflated_overlap_length, 0)) + debug.info(4," Partial overlap: {0} >? {1}".format(inflated_overlap_length, 0)) return (None, coord) else: debug.info(4, " No overlap: {0} {1}".format(overlap_length, 0)) @@ -662,7 +662,7 @@ class router(router_tech): x = track.x * self.track_width - 0.5 * self.track_width + 0.5 * self.track_space y = track.y * self.track_width - 0.5 * self.track_width + 0.5 * self.track_space ll = snap_to_grid(vector(x,y)) - + # calculate upper right x = track.x * self.track_width + 0.5 * self.track_width - 0.5 * self.track_space y = track.y * self.track_width + 0.5 * self.track_width - 0.5 * self.track_space @@ -702,7 +702,7 @@ class router(router_tech): ur = snap_to_grid(ll + vector(self.track_width, self.track_width)) return [ll, ur] - + def convert_track_to_inflated_pin(self, track): """ Convert a grid point into a rectangle shape @@ -712,7 +712,7 @@ class router(router_tech): x = track.x*self.track_width - 0.5*self.track_width - 0.5*self.track_space y = track.y*self.track_width - 0.5*self.track_width - 0.5*self.track_space ll = snap_to_grid(vector(x,y)) - + # calculate upper right x = track.x*self.track_width + 0.5*self.track_width + 0.5*self.track_space y = track.y*self.track_width + 0.5*self.track_width + 0.5*self.track_space @@ -720,13 +720,13 @@ class router(router_tech): p = pin_layout("", [ll, ur], self.get_layer(track[2])) return p - + 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)) + debug.info(2, "Analyzing pin groups for {}.".format(pin_name)) pin_set = self.pins[pin_name] # This will be a list of pin tuples that overlap @@ -773,13 +773,13 @@ class router(router_tech): for pin in pin_list: group_id[pin] = gid gid += 1 - + for p in overlap_list: (p1, p2) = p for pin in pin_list: if group_id[pin] == group_id[p2]: group_id[pin] = group_id[p1] - + # For each pin add it to it's group group_map = {} for pin in pin_list: @@ -792,7 +792,7 @@ class router(router_tech): group_map[gid].pins.add(pin) self.pin_groups[pin_name] = list(group_map.values()) - + def convert_pins(self, pin_name): """ Convert the pin groups into pin tracks and blockage tracks. @@ -800,7 +800,7 @@ class router(router_tech): debug.info(1, "Converting pins for {}.".format(pin_name)) for pg in self.pin_groups[pin_name]: pg.convert_pin() - + def enclose_pins(self): """ This will find the biggest rectangle enclosing some grid squares and @@ -828,22 +828,22 @@ class router(router_tech): """ for i in range(self.num_pin_components(pin_name)): self.add_pin_component_target(pin_name, i) - + def num_pin_components(self, pin_name): """ This returns how many disconnected pin components there are. """ return len(self.pin_groups[pin_name]) - + def add_pin_component_source(self, pin_name, index): """ - This will mark only the pin tracks + This will mark only the pin tracks from the indexed pin component as a source. It also unsets it as a blockage. """ debug.check(index 1: newpath.append(path[-1]) return newpath - + def run_router(self, detour_scale): """ This assumes the blockages, source, and target are all set up. @@ -1002,7 +1002,7 @@ class router(router_tech): 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: @@ -1010,7 +1010,7 @@ class router(router_tech): self.paths.append(path) self.add_route(path) - + path_set = grid_utils.flatten_set(path) self.path_blockages.append(path_set) else: @@ -1067,7 +1067,7 @@ class router(router_tech): width=shape[1].x-shape[0].x, height=shape[1].y-shape[0].y) t = self.rg.map[g].get_type() - + # midpoint offset off = vector((shape[1].x+shape[0].x)/2, (shape[1].y+shape[0].y)/2) @@ -1094,7 +1094,7 @@ class router(router_tech): self.cell.add_label(text=str(t), layer="text", offset=type_off) - + self.cell.add_label(text="{0},{1}".format(g[0], g[1]), layer="text", offset=shape[0], @@ -1107,7 +1107,7 @@ class router(router_tech): 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 @@ -1120,12 +1120,12 @@ class router(router_tech): show_blockage_grids = False show_enclosures = False show_all_grids = True - + if show_all_grids: self.rg.add_all_grids() for g in self.rg.map: self.annotate_grid(g) - + if show_blockages: # Display the inflated blockage for blockage in self.blockages: @@ -1156,7 +1156,7 @@ class router(router_tech): width=pin.width(), height=pin.height()) - + # FIXME: This should be replaced with vector.snap_to_grid at some point def snap_to_grid(offset): """ diff --git a/compiler/router/router_tech.py b/compiler/router/router_tech.py index 27156eeb..2cf31020 100644 --- a/compiler/router/router_tech.py +++ b/compiler/router/router_tech.py @@ -27,10 +27,10 @@ class router_tech: if len(self.layers) == 1: self.horiz_layer_name = self.vert_layer_name = self.layers[0] self.horiz_lpp = self.vert_lpp = 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: @@ -40,7 +40,7 @@ class router_tech: # routing self.horiz_layer_name = None self.vert_layer_name = None - + if preferred_directions[try_horiz_layer] == "H": self.horiz_layer_name = try_horiz_layer else: @@ -59,17 +59,17 @@ class router_tech: self.horiz_lpp = layer[self.horiz_layer_name] self.vert_lpp = 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) - + self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing self.vert_track_width = self.vert_layer_minwidth + 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)) @@ -77,14 +77,14 @@ class router_tech: debug.info(1,"Track space: {:.3f}".format(self.track_space)) self.track_wire = self.track_width - self.track_space debug.info(1,"Track wire width: {:.3f}".format(self.track_wire)) - + self.track_widths = vector([self.track_width] * 2) self.track_factor = vector([1/self.track_width] * 2) debug.info(2,"Track factor: {}".format(self.track_factor)) - + # When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side) self.layer_widths = [self.track_wire, 1, self.track_wire] - + def same_lpp(self, lpp1, lpp2): """ Check if the layers and purposes are the same. @@ -92,7 +92,7 @@ class router_tech: """ if lpp1[1] == None or lpp2[1] == None: return lpp1[0] == lpp2[0] - + return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1] def get_zindex(self, lpp): @@ -120,12 +120,12 @@ class router_tech: layer_name = self.horiz_layer_name else: debug.error("Invalid zindex for track", -1) - + min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf) - + min_width = drc("minwidth_{0}".format(layer_name), self.rail_track_width*min_wire_width, math.inf) min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.rail_track_width*min_wire_width, math.inf) return (min_width,min_spacing) - + diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py index cb012a5e..4cddaa5f 100644 --- a/compiler/router/signal_grid.py +++ b/compiler/router/signal_grid.py @@ -22,7 +22,7 @@ class signal_grid(grid): def __init__(self, ll, ur, track_factor): """ Create a routing map of width x height cells and 2 in the z-axis. """ grid.__init__(self, ll, ur, track_factor) - + # priority queue for the maze routing self.q = [] @@ -32,12 +32,12 @@ class signal_grid(grid): # Reset all the cells in the map for p in self.map.values(): p.reset() - + # clear source and target pins self.source=[] self.target=[] - - # Clear the queue + + # Clear the queue while len(self.q)>0: heappop(self.q) self.counter = 0 @@ -60,14 +60,14 @@ class signal_grid(grid): heappush(self.q,(cost,self.counter,grid_path([vector3d(s)]))) self.counter+=1 - + def route(self,detour_scale): """ This does the A* maze routing with preferred direction routing. This only works for 1 track wide routes! """ - - # We set a cost bound of the HPWL for run-time. This can be + + # We set a cost bound of the HPWL for run-time. This can be # over-ridden if the route fails due to pruning a feasible solution. any_source_element = next(iter(self.source)) cost_bound = detour_scale*self.cost_to_target(any_source_element)*grid.PREFERRED_COST @@ -76,28 +76,28 @@ class signal_grid(grid): for s in self.source: if self.is_target(s): return((grid_path([vector3d(s)]),0)) - + # Make sure the queue is empty if we run another route while len(self.q)>0: heappop(self.q) - + # Put the source items into the queue self.init_queue() cheapest_path = None cheapest_cost = None - + # Keep expanding and adding to the priority queue until we are done while len(self.q)>0: # should we keep the path in the queue as well or just the final node? (cost,count,curpath) = heappop(self.q) debug.info(3,"Queue size: size=" + str(len(self.q)) + " " + str(cost)) debug.info(4,"Expanding: cost=" + str(cost) + " " + str(curpath)) - + # expand the last element neighbors = self.expand_dirs(curpath) debug.info(4,"Neighbors: " + str(neighbors)) - + for n in neighbors: # make a new copy of the path to not update the old ones newpath = deepcopy(curpath) @@ -134,10 +134,10 @@ class signal_grid(grid): but not expanding to blocked cells. Expands in all directions regardless of preferred directions. """ - - # Expand all directions. + + # Expand all directions. neighbors = curpath.expand_dirs() - + # Filter the blocked ones unblocked_neighbors = [x for x in neighbors if not self.is_blocked(x)] @@ -145,9 +145,9 @@ class signal_grid(grid): def hpwl(self, src, dest): - """ + """ Return half perimeter wire length from point to another. - Either point can have positive or negative coordinates. + Either point can have positive or negative coordinates. Include the via penalty if there is one. """ hpwl = abs(src.x-dest.x) @@ -155,10 +155,10 @@ class signal_grid(grid): if src.x!=dest.x and src.y!=dest.y: hpwl += grid.VIA_COST return hpwl - + def cost_to_target(self,source): """ - Find the cheapest HPWL distance to any target point ignoring + Find the cheapest HPWL distance to any target point ignoring blockages for A* search. """ any_target_element = next(iter(self.target)) @@ -170,8 +170,8 @@ class signal_grid(grid): def get_inertia(self,p0,p1): - """ - Sets the direction based on the previous direction we came from. + """ + Sets the direction based on the previous direction we came from. """ # direction (index) of movement if p0.x==p1.x: @@ -182,5 +182,5 @@ class signal_grid(grid): # z direction return 2 - - + + diff --git a/compiler/router/signal_router.py b/compiler/router/signal_router.py index 9390deaf..5d9b52bc 100644 --- a/compiler/router/signal_router.py +++ b/compiler/router/signal_router.py @@ -29,7 +29,7 @@ class signal_router(router): def create_routing_grid(self): - """ + """ Create a sprase routing grid with A* expansion functions. """ # We will add a halo around the boundary @@ -39,19 +39,19 @@ class signal_router(router): import signal_grid self.rg = signal_grid.signal_grid(self.ll, self.ur, self.track_width) - + def route(self, src, dest, detour_scale=5): - """ + """ Route a single source-destination net and return - the simplified rectilinear path. Cost factor is how sub-optimal to explore for a feasible route. + the simplified rectilinear path. Cost factor is how sub-optimal to explore for a feasible route. This is used to speed up the routing when there is not much detouring needed. """ debug.info(1,"Running signal router from {0} to {1}...".format(src,dest)) self.pins[src] = [] self.pins[dest] = [] - + # Clear the pins if we have previously routed if (hasattr(self,'rg')): self.clear_pins() @@ -63,13 +63,13 @@ class signal_router(router): # Get the pin shapes self.find_pins_and_blockages([src, dest]) - + # Block everything self.prepare_blockages() # Clear the pins we are routing self.set_blockages(self.pin_components[src],False) - self.set_blockages(self.pin_components[dest],False) - + self.set_blockages(self.pin_components[dest],False) + # Now add the src/tgt if they are not blocked by other shapes self.add_source(src) self.add_target(dest) @@ -77,12 +77,12 @@ class signal_router(router): if not self.run_router(detour_scale=detour_scale): self.write_debug_gds(stop_program=False) return False - - self.write_debug_gds(stop_program=False) + + self.write_debug_gds(stop_program=False) return True - - + + diff --git a/compiler/router/supply_grid.py b/compiler/router/supply_grid.py index 98c6f031..f4c31bff 100644 --- a/compiler/router/supply_grid.py +++ b/compiler/router/supply_grid.py @@ -24,7 +24,7 @@ class supply_grid(signal_grid): def __init__(self, ll, ur, track_width): """ Create a routing map of width x height cells and 2 in the z-axis. """ signal_grid.__init__(self, ll, ur, track_width) - + def reinit(self): """ Reinitialize everything for a new route. """ self.source = set() @@ -32,10 +32,10 @@ class supply_grid(signal_grid): # Reset all the cells in the map for p in self.map.values(): p.reset() - + 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. """ @@ -54,10 +54,10 @@ class supply_grid(signal_grid): # Return a start if it isn't blocked if not self.is_wave_blocked(wave): return wave - + return wave - - + + def is_wave_blocked(self, wave): """ Checks if any of the locations are blocked @@ -68,7 +68,7 @@ class supply_grid(signal_grid): else: return False - + def probe(self, wave, direct): """ Expand the wave until there is a blockage and return @@ -83,4 +83,4 @@ class supply_grid(signal_grid): return wave_path - + diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index cd7b6b7b..21369003 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -29,7 +29,7 @@ class supply_grid_router(router): # Power rail width in minimum wire widths self.rail_track_width = 3 - + router.__init__(self, layers, design, gds_filename, self.rail_track_width) # The list of supply rails (grid sets) that may be routed @@ -38,9 +38,9 @@ class supply_grid_router(router): self.supply_rail_tracks = {} print_time("Init supply router", datetime.now(), start_time, 3) - + def create_routing_grid(self): - """ + """ Create a sprase routing grid with A* expansion functions. """ size = self.ur - self.ll @@ -48,7 +48,7 @@ class supply_grid_router(router): import supply_grid self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width) - + def route(self, vdd_name="vdd", gnd_name="gnd"): """ Add power supply rails and connect all pins to these rails. @@ -76,18 +76,18 @@ class supply_grid_router(router): self.prepare_blockages(self.gnd_name) # Determine the rail locations self.route_supply_rails(self.gnd_name, 0) - + # Block everything self.prepare_blockages(self.vdd_name) # Determine the rail locations self.route_supply_rails(self.vdd_name, 1) 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) print_time("Simple overlap routing", datetime.now(), start_time, 3) - + # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter start_time = datetime.now() @@ -101,7 +101,7 @@ class supply_grid_router(router): return False if not self.check_all_routed(gnd_name): return False - + return True def check_all_routed(self, pin_name): @@ -111,7 +111,7 @@ class supply_grid_router(router): for pg in self.pin_groups[pin_name]: if not pg.is_routed(): return False - + def route_simple_overlaps(self, pin_name): """ This checks for simple cases where a pin component already overlaps a supply rail. @@ -125,20 +125,20 @@ class supply_grid_router(router): for pg in self.pin_groups[pin_name]: if pg.is_routed(): continue - + # First, check if we just overlap, if so, we are done. overlap_grids = wire_tracks & pg.grids if len(overlap_grids)>0: routed_count += 1 pg.set_routed() continue - + # Else, if we overlap some of the space track, we can patch it with an enclosure # pg.create_simple_overlap_enclosure(pg.grids) # pg.add_enclosure(self.cell) debug.info(1, "Routed {} simple overlap pins".format(routed_count)) - + def finalize_supply_rails(self, name): """ Determine which supply rails overlap and can accomodate a via. @@ -164,7 +164,7 @@ class supply_grid_router(router): # Never compare to yourself if i1==i2: continue - + # Only consider r2 vertical rails e = next(iter(r2)) if e.z==0: @@ -179,7 +179,7 @@ class supply_grid_router(router): debug.info(3, "Via overlap {0} {1}".format(len(overlap),overlap)) connections.update([i1, i2]) via_areas.append(overlap) - + # Go through and add the vias at the center of the intersection for area in via_areas: ll = grid_utils.get_lower_left(area) @@ -190,7 +190,7 @@ class supply_grid_router(router): # Determien which indices were not connected to anything above missing_indices = set([x for x in range(len(self.supply_rails[name]))]) missing_indices.difference_update(connections) - + # Go through and remove those disconnected indices # (No via was added, so that doesn't need to be removed) for rail_index in sorted(missing_indices, reverse=True): @@ -202,7 +202,7 @@ class supply_grid_router(router): # Make the supply rails into a big giant set of grids for easy blockages. # Must be done after we determine which ones are connected. self.create_supply_track_set(name) - + def add_supply_rails(self, name): """ Add the shapes that represent the routed supply rails. @@ -211,7 +211,7 @@ class supply_grid_router(router): """ for rail in self.supply_rails[name]: ll = grid_utils.get_lower_left(rail) - ur = grid_utils.get_upper_right(rail) + ur = grid_utils.get_upper_right(rail) z = ll.z pin = self.compute_pin_enclosure(ll, ur, z, name) debug.info(3, "Adding supply rail {0} {1}->{2} {3}".format(name, ll, ur, pin)) @@ -229,12 +229,12 @@ class supply_grid_router(router): """ self.supply_rails[name]=[] - + max_yoffset = self.rg.ur.y max_xoffset = self.rg.ur.x min_yoffset = self.rg.ll.y min_xoffset = self.rg.ll.x - + # Horizontal supply rails start_offset = min_yoffset + supply_number for offset in range(start_offset, max_yoffset, 2): @@ -272,19 +272,19 @@ class supply_grid_router(router): """ # 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: return None wave_path = self.probe_supply_rail(name, start_wave, direct) - + self.approve_supply_rail(name, wave_path) # Return the rail whether we approved it or not, # as it will be used to find the next start location return wave_path - + def probe_supply_rail(self, name, start_wave, direct): """ This finds the first valid starting location and routes a supply rail @@ -308,7 +308,7 @@ class supply_grid_router(router): wave_path.trim_first() wave_path.trim_last() - + return wave_path def approve_supply_rail(self, name, wave_path): @@ -321,7 +321,7 @@ class supply_grid_router(router): grid_set = wave_path.get_grids() self.supply_rails[name].append(grid_set) return True - + return False def route_supply_rails(self, name, supply_number): @@ -333,7 +333,7 @@ class supply_grid_router(router): # Compute the grid locations of the supply rails self.compute_supply_rails(name, supply_number) - + # Add the supply rail vias (and prune disconnected rails) self.finalize_supply_rails(name) @@ -348,7 +348,7 @@ class supply_grid_router(router): for rail in self.supply_rails[pin_name]: rail_set.update(rail) self.supply_rail_tracks[pin_name] = rail_set - + def route_pins_to_rails(self, pin_name): """ This will route each of the remaining pin components to the supply rails. @@ -362,7 +362,7 @@ class supply_grid_router(router): for index, pg in enumerate(self.pin_groups[pin_name]): if pg.is_routed(): continue - + debug.info(3, "Routing component {0} {1}".format(pin_name, index)) # Clear everything in the routing grid. @@ -371,7 +371,7 @@ class supply_grid_router(router): # 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) @@ -383,7 +383,7 @@ class supply_grid_router(router): # Actually run the A* router if not self.run_router(detour_scale=5): self.write_debug_gds("debug_route.gds", False) - + # if index==3 and pin_name=="vdd": # self.write_debug_gds("route.gds",False) @@ -396,7 +396,7 @@ class supply_grid_router(router): self.rg.set_target(self.supply_rail_tracks[pin_name]) # But unblock all the rail tracks including the space self.rg.set_blocked(self.supply_rail_tracks[pin_name], False) - + def set_supply_rail_blocked(self, value=True): """ Add the supply rails of given name as a routing target. @@ -404,4 +404,4 @@ class supply_grid_router(router): debug.info(4, "Blocking supply rail") for rail_name in self.supply_rail_tracks: self.rg.set_blocked(self.supply_rail_tracks[rail_name]) - + diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 7def03e0..9d98c2b0 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -13,7 +13,7 @@ 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 vector3d import vector3d from router import router from direction import direction from datetime import datetime @@ -33,12 +33,12 @@ class supply_tree_router(router): """ # 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 @@ -46,9 +46,9 @@ class supply_tree_router(router): 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)) @@ -82,19 +82,19 @@ class supply_tree_router(router): self.route_pins(gnd_name) print_time("Maze routing supplies",datetime.now(), start_time, 3) - #self.write_debug_gds("final.gds",False) + #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]: @@ -107,7 +107,7 @@ class supply_tree_router(router): 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 @@ -131,9 +131,9 @@ class supply_tree_router(router): # 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. @@ -147,7 +147,7 @@ class supply_tree_router(router): 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. @@ -156,11 +156,11 @@ class supply_tree_router(router): # 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 @@ -179,15 +179,15 @@ class supply_tree_router(router): 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 index 4a673c24..1210c064 100644 --- a/compiler/router/tests/01_no_blockages_test.py +++ b/compiler/router/tests/01_no_blockages_test.py @@ -43,14 +43,14 @@ class no_blockages_test(openram_test): mod=cell, offset=[0,0]) self.connect_inst([]) - + layer_stack =("metal1","via1","metal2") r=router(layer_stack,self,gds_file) self.assertTrue(r.route(src="A",dest="B")) r=routing("01_no_blockages_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) - + # fails if there are any DRC errors on any cells globals.end_openram() diff --git a/compiler/router/tests/02_blockages_test.py b/compiler/router/tests/02_blockages_test.py index e317b642..056cb3f7 100644 --- a/compiler/router/tests/02_blockages_test.py +++ b/compiler/router/tests/02_blockages_test.py @@ -43,19 +43,19 @@ class blockages_test(openram_test): mod=cell, offset=[0,0]) self.connect_inst([]) - + layer_stack =("metal1","via1","metal2") r=router(layer_stack,self,gds_file) self.assertTrue(r.route(src="A",dest="B")) r=routing("02_blockages_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) - + # fails if there are any DRC errors on any cells globals.end_openram() - + # instantiate a copy of the class to actually run the test diff --git a/compiler/router/tests/03_same_layer_pins_test.py b/compiler/router/tests/03_same_layer_pins_test.py index 10a3544a..a79112d8 100644 --- a/compiler/router/tests/03_same_layer_pins_test.py +++ b/compiler/router/tests/03_same_layer_pins_test.py @@ -42,14 +42,14 @@ class same_layer_pins_test(openram_test): mod=cell, offset=[0,0]) self.connect_inst([]) - + layer_stack =("metal1","via1","metal2") r=router(layer_stack,self,gds_file) self.assertTrue(r.route(src="A",dest="B")) r = routing("03_same_layer_pins_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) - + # fails if there are any DRC errors on any cells globals.end_openram() diff --git a/compiler/router/tests/04_diff_layer_pins_test.py b/compiler/router/tests/04_diff_layer_pins_test.py index 8966d2e4..a0eba961 100644 --- a/compiler/router/tests/04_diff_layer_pins_test.py +++ b/compiler/router/tests/04_diff_layer_pins_test.py @@ -44,14 +44,14 @@ class diff_layer_pins_test(openram_test): mod=cell, offset=[0,0]) self.connect_inst([]) - + layer_stack =("metal1","via1","metal2") r=router(layer_stack,self,gds_file) self.assertTrue(r.route(src="A",dest="B")) r = routing("04_diff_layer_pins_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) - + # fails if there are any DRC errors on any cells globals.end_openram() diff --git a/compiler/router/tests/05_two_nets_test.py b/compiler/router/tests/05_two_nets_test.py index ebf84745..962af07e 100644 --- a/compiler/router/tests/05_two_nets_test.py +++ b/compiler/router/tests/05_two_nets_test.py @@ -44,7 +44,7 @@ class two_nets_test(openram_test): mod=cell, offset=[0,0]) self.connect_inst([]) - + layer_stack =("metal1","via1","metal2") r=router(layer_stack,self,gds_file) self.assertTrue(r.route(src="A",dest="B")) @@ -52,7 +52,7 @@ class two_nets_test(openram_test): r = routing("05_two_nets_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) - + # fails if there are any DRC errors on any cells globals.end_openram() diff --git a/compiler/router/tests/06_pin_location_test.py b/compiler/router/tests/06_pin_location_test.py index a035cc59..cb8d56d1 100644 --- a/compiler/router/tests/06_pin_location_test.py +++ b/compiler/router/tests/06_pin_location_test.py @@ -43,7 +43,7 @@ class pin_location_test(openram_test): mod=cell, offset=[0,0]) self.connect_inst([]) - + layer_stack =("metal1","via1","metal2") r=router(layer_stack,self,gds_file) # these are user coordinates and layers @@ -61,7 +61,7 @@ class pin_location_test(openram_test): # fails if there are any DRC errors on any cells globals.end_openram() - + # instantiate a copy of the class to actually run the test diff --git a/compiler/router/tests/07_big_test.py b/compiler/router/tests/07_big_test.py index 5410dcbf..d6da2488 100644 --- a/compiler/router/tests/07_big_test.py +++ b/compiler/router/tests/07_big_test.py @@ -43,7 +43,7 @@ class big_test(openram_test): mod=cell, offset=[0,0]) self.connect_inst([]) - + layer_stack =("metal1","via1","metal2") r=router(layer_stack,self,gds_file) connections=[('out_0_2', 'a_0_0'), @@ -76,10 +76,10 @@ class big_test(openram_test): self.local_drc_check(r) else: debug.warning("This test does not support technology {0}".format(OPTS.tech_name)) - + # fails if there are any DRC errors on any cells globals.end_openram() - + # instantiate a copy of the class to actually run the test diff --git a/compiler/router/tests/08_expand_region_test.py b/compiler/router/tests/08_expand_region_test.py index ab4bce59..3f2975f9 100644 --- a/compiler/router/tests/08_expand_region_test.py +++ b/compiler/router/tests/08_expand_region_test.py @@ -43,17 +43,17 @@ class expand_region_test(openram_test): mod=cell, offset=[0,0]) self.connect_inst([]) - + layer_stack =("metal1","via1","metal2") r=router(layer_stack,self,gds_file) - # This should be infeasible because it is blocked without a detour. + # This should be infeasible because it is blocked without a detour. self.assertFalse(r.route(src="A",dest="B",detour_scale=1)) # This should be feasible because we allow it to detour self.assertTrue(r.route(src="A",dest="B",detour_scale=3)) r = routing("08_expand_region_test_{0}".format(OPTS.tech_name)) self.local_drc_check(r) - + # fails if there are any DRC errors on any cells globals.end_openram() diff --git a/compiler/router/tests/10_supply_grid_test.py b/compiler/router/tests/10_supply_grid_test.py index 73e41b86..5824a7f4 100644 --- a/compiler/router/tests/10_supply_grid_test.py +++ b/compiler/router/tests/10_supply_grid_test.py @@ -38,13 +38,13 @@ class no_blockages_test(openram_test): c = sram_config(word_size=4, num_words=32, num_banks=1) - + c.words_per_row=1 sram = sram(c, "sram1") cell = sram.s self.local_check(cell,True) - + # fails if there are any DRC errors on any cells globals.end_openram() diff --git a/compiler/router/tests/gds_cell.py b/compiler/router/tests/gds_cell.py index b93b512a..07eac4bc 100644 --- a/compiler/router/tests/gds_cell.py +++ b/compiler/router/tests/gds_cell.py @@ -14,7 +14,7 @@ class gds_cell(design): self.name = name self.gds_file = gds_file self.sp_file = None - + design.__init__(self, name) # The dimensions will not be defined, so do this... diff --git a/compiler/router/tests/regress.py b/compiler/router/tests/regress.py index db21bbd9..63b4dd0d 100644 --- a/compiler/router/tests/regress.py +++ b/compiler/router/tests/regress.py @@ -41,6 +41,6 @@ test_result = test_runner.run(suite) import verify verify.print_drc_stats() verify.print_lvs_stats() -verify.print_pex_stats() +verify.print_pex_stats() sys.exit(not test_result.wasSuccessful()) diff --git a/compiler/router/tests/testutils.py b/compiler/router/tests/testutils.py index c90046f3..c6e91459 100644 --- a/compiler/router/tests/testutils.py +++ b/compiler/router/tests/testutils.py @@ -13,7 +13,7 @@ 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() @@ -28,11 +28,11 @@ class openram_test(unittest.TestCase): if OPTS.purge_temp: self.cleanup() - + def local_check(self, a, final_verification=False): self.reset() - + tempspice = OPTS.openram_temp + "temp.sp" tempgds = OPTS.openram_temp + "temp.gds" @@ -44,7 +44,7 @@ class openram_test(unittest.TestCase): if result != 0: self.fail("DRC failed: {}".format(a.name)) - + result=verify.run_lvs(a.name, tempgds, tempspice, final_verification) if result != 0: self.fail("LVS mismatch: {}".format(a.name)) @@ -58,10 +58,10 @@ class openram_test(unittest.TestCase): 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. @@ -89,7 +89,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): @@ -105,7 +105,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 @@ -121,7 +121,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 @@ -135,15 +135,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 @@ -176,7 +176,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) @@ -188,7 +188,7 @@ 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: if mismatches==0: @@ -252,7 +252,7 @@ def header(filename, technology): import getpass if getpass.getuser() == "gitlab-runner": return - + tst = "Running Test for:" print("\n") print(" ______________________________________________________________________________ ") diff --git a/compiler/router/vector3d.py b/compiler/router/vector3d.py index 066f843f..46279099 100644 --- a/compiler/router/vector3d.py +++ b/compiler/router/vector3d.py @@ -28,7 +28,7 @@ class vector3d(): self.y = y self.z = z self._hash = hash((self.x,self.y,self.z)) - + def __str__(self): """ override print function output """ return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" @@ -38,8 +38,8 @@ class vector3d(): return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" def __setitem__(self, index, value): - """ - override setitem function + """ + override setitem function can set value by vector3d[index]=value """ if index==0: @@ -51,11 +51,11 @@ class vector3d(): else: self.x=value[0] self.y=value[1] - self.z=value[2] + self.z=value[2] def __getitem__(self, index): """ - override getitem function + override getitem function can get value by value=vector3d[index] """ if index==0: @@ -65,7 +65,7 @@ class vector3d(): elif index==2: return self.z else: - return self + return self def __add__(self, other): """ @@ -142,7 +142,7 @@ class vector3d(): Override round function """ return vector3d(int(round(self.x)),int(round(self.y)), self.z) - + def __eq__(self, other): """Override the default Equals behavior""" if isinstance(other, self.__class__): @@ -157,7 +157,7 @@ class vector3d(): if self.x==other.x and self.y 1: port = 1 - + # Add the col address flops below the bank to the right of the control logic x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width # Place it a data bus below the x-axis, but at least as high as the control logic to not block @@ -215,7 +215,7 @@ class sram_1bank(sram_base): x_offset = self.col_addr_dff_insts[port].lx() else: self.col_addr_pos[port] = vector(x_offset, y_offset) - + if port in self.write_ports: # Add spare write enable flops to the right of the data flops since the spare # columns will be on the left @@ -248,7 +248,7 @@ class sram_1bank(sram_base): highest_coord = self.find_highest_coords() lowest_coord = self.find_lowest_coords() bbox = [lowest_coord, highest_coord] - + for port in self.all_ports: # Depending on the port, use the bottom/top or left/right sides # Port 0 is left/bottom @@ -296,7 +296,7 @@ class sram_1bank(sram_base): self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "din{0}[{1}]".format(port, bit)) - + # Data output pins go to BOTTOM/TOP if port in self.readwrite_ports and OPTS.perimeter_pins: for bit in range(self.word_size + self.num_spare_cols): @@ -319,8 +319,8 @@ class sram_1bank(sram_base): self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port, bit), "dout{0}[{1}]".format(port, bit)) - - + + # Lower address bits go to BOTTOM/TOP for bit in range(self.col_addr_size): @@ -333,7 +333,7 @@ class sram_1bank(sram_base): self.copy_layout_pin(self.col_addr_dff_insts[port], "din_{}".format(bit), "addr{0}[{1}]".format(port, bit)) - + # Upper address bits go to LEFT/RIGHT for bit in range(self.row_addr_size): if OPTS.perimeter_pins: @@ -345,7 +345,7 @@ class sram_1bank(sram_base): self.copy_layout_pin(self.row_addr_dff_insts[port], "din_{}".format(bit), "addr{0}[{1}]".format(port, bit + self.col_addr_size)) - + # Write mask pins go to BOTTOM/TOP if port in self.write_ports: if self.write_size: @@ -379,20 +379,20 @@ class sram_1bank(sram_base): self.add_layout_pins() self.route_clk() - + self.route_control_logic() - + self.route_row_addr_dff() def route_dffs(self, add_routes=True): - + for port in self.all_ports: self.route_dff(port, add_routes) def route_dff(self, port, add_routes): route_map = [] - + # column mux dff is routed on it's own since it is to the far end # decoder inputs are min pitch M2, so need to use lower layer stack if self.col_addr_size > 0: @@ -408,7 +408,7 @@ class sram_1bank(sram_base): else: offset = vector(0, self.bank.height + 2 * self.m1_space) - + cr = channel_route.channel_route(netlist=route_map, offset=offset, layer_stack=self.m1_stack, @@ -420,7 +420,7 @@ class sram_1bank(sram_base): self.col_addr_bus_size[port] = cr.height route_map = [] - + # wmask dff if self.num_wmasks > 0 and port in self.write_ports: dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)] @@ -436,7 +436,7 @@ class sram_1bank(sram_base): bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, dff_pins))) - + if port in self.readwrite_ports and OPTS.perimeter_pins: # outputs from sense amp # These are the output pins which had their pin placed on the perimeter, so route from the @@ -454,14 +454,14 @@ class sram_1bank(sram_base): bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, dff_pins))) - + if len(route_map) > 0: - + if self.num_wmasks > 0 and port in self.write_ports: layer_stack = self.m3_stack else: layer_stack = self.m1_stack - + if port == 0: offset = vector(self.control_logic_insts[port].rx() + self.dff.width, - self.data_bus_size[port] + 2 * self.m1_pitch) @@ -486,7 +486,7 @@ class sram_1bank(sram_base): self.connect_inst([]) else: self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap - + def route_clk(self): """ Route the clock network """ @@ -497,7 +497,7 @@ class sram_1bank(sram_base): # 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") @@ -518,11 +518,11 @@ class sram_1bank(sram_base): self.add_via_stack_center(from_layer=control_clk_buf_pin.layer, to_layer="m2", offset=clk_steiner_pos) - + # Note, the via to the control logic is taken care of above self.add_wire(self.m2_stack[::-1], [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() @@ -567,9 +567,9 @@ class sram_1bank(sram_base): self.add_via_stack_center(from_layer=dest_pin.layer, to_layer="m2", offset=dest_pin.center()) - + def route_row_addr_dff(self): - """ + """ Connect the output of the row flops to the bank pins """ for port in self.all_ports: @@ -602,7 +602,7 @@ class sram_1bank(sram_base): 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. @@ -616,7 +616,7 @@ class sram_1bank(sram_base): if self.num_spare_cols: for inst in self.spare_wen_dff_insts: self.graph_inst_exclude.add(inst) - + def graph_exclude_addr_dff(self): """ Removes data dff from search graph. @@ -624,7 +624,7 @@ class sram_1bank(sram_base): # 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) @@ -636,7 +636,7 @@ class sram_1bank(sram_base): # Insts located in control logic, exclusion function called here for inst in self.control_logic_insts: inst.mod.graph_exclude_dffs() - + def get_cell_name(self, inst_name, row, col): """ Gets the spice name of the target bitcell. @@ -645,6 +645,6 @@ class sram_1bank(sram_base): 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) - + def get_bank_num(self, inst_name, row, col): return 0 diff --git a/compiler/sram/sram_2bank.py b/compiler/sram/sram_2bank.py index dace1ed9..c23008b1 100644 --- a/compiler/sram/sram_2bank.py +++ b/compiler/sram/sram_2bank.py @@ -32,11 +32,11 @@ class sram_2bank(sram_base): # In 2 bank SRAM, the height is determined by the control bus which is higher than the msb address self.vertical_bus_height = self.bank.height + 2*self.bank_to_bus_distance + self.data_bus_height + self.control_bus_height # The address bus extends down through the power rails, but control and bank_sel bus don't - self.addr_bus_height = self.vertical_bus_height - + self.addr_bus_height = self.vertical_bus_height + self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0) self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance) - self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height) + self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height) self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height) self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0) self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0) @@ -49,14 +49,14 @@ class sram_2bank(sram_base): self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch, self.supply_bus_offset.y + self.supply_bus_height \ + 2*self.m1_pitch + self.msb_address.width) - + def add_modules(self): """ Adds the modules and the buses to the top level """ self.compute_bus_sizes() self.add_banks() - + self.compute_bank_offsets() self.add_busses() @@ -90,7 +90,7 @@ class sram_2bank(sram_base): rotate=270) 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"]) - + def route_shared_banks(self): """ Route the shared signals for two and four bank configurations. """ @@ -98,7 +98,7 @@ class sram_2bank(sram_base): # create the input control pins for n in self.control_logic_inputs + ["clk"]: self.copy_layout_pin(self.control_logic_inst, n) - + # connect the control logic to the control bus for n in self.control_logic_outputs + ["vdd", "gnd"]: pins = self.control_logic_inst.get_pins(n) @@ -109,7 +109,7 @@ class sram_2bank(sram_base): rail_pos = vector(pin_pos.x,self.horz_control_bus_positions[n].y) self.add_path("m2",[pin_pos,rail_pos]) self.add_via_center(self.m1_stack,rail_pos) - + # connect the control logic cross bar for n in self.control_logic_outputs: cross_pos = vector(self.vert_control_bus_positions[n].x,self.horz_control_bus_positions[n].y) @@ -125,7 +125,7 @@ class sram_2bank(sram_base): def route_single_msb_address(self): """ Route one MSB address bit for 2-bank SRAM """ - + # connect the bank MSB flop supplies vdd_pins = self.msb_address_inst.get_pins("vdd") for vdd_pin in vdd_pins: @@ -133,11 +133,11 @@ class sram_2bank(sram_base): vdd_pos = vdd_pin.bc() down_pos = vdd_pos - vector(0,self.m1_pitch) rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y) - self.add_path("m1",[vdd_pos,down_pos]) - self.add_via_center(self.m1_stack,down_pos,rotate=90) + self.add_path("m1",[vdd_pos,down_pos]) + self.add_via_center(self.m1_stack,down_pos,rotate=90) self.add_path("m2",[down_pos,rail_pos]) self.add_via_center(self.m1_stack,rail_pos) - + gnd_pins = self.msb_address_inst.get_pins("gnd") # Only add the ground connection to the lowest metal2 rail in the flop array # FIXME: SCMOS doesn't have a vertical rail in the cell, or we could use those @@ -149,9 +149,9 @@ class sram_2bank(sram_base): gnd_pos = gnd_pin.ur() rail_pos = vector(gnd_pos.x,self.horz_control_bus_positions["gnd"].y) self.add_path("m2",[gnd_pos,rail_pos]) - self.add_via_center(self.m1_stack,rail_pos) - - # connect the MSB flop to the address input bus + self.add_via_center(self.m1_stack,rail_pos) + + # connect the MSB flop to the address input bus msb_pins = self.msb_address_inst.get_pins("din[0]") for msb_pin in msb_pins: if msb_pin.layer == "m3": @@ -170,7 +170,7 @@ class sram_2bank(sram_base): self.add_path("m2",[msb_out_pos,out_extend_right_pos,out_extend_up_pos]) self.add_wire(("m3","via2","m2"),[out_extend_right_pos,out_extend_up_pos,rail_pos]) self.add_via_center(self.m2_stack,rail_pos) - + # Connect the output to select 1 msb_out_pin = self.msb_address_inst.get_pin("dout[0]") msb_out_pos = msb_out_pin.rc() @@ -180,21 +180,21 @@ class sram_2bank(sram_base): self.add_path("m2",[msb_out_pos,out_extend_right_pos,out_extend_down_pos]) self.add_wire(("m3","via2","m2"),[out_extend_right_pos,out_extend_down_pos,rail_pos]) self.add_via_center(self.m2_stack,rail_pos) - + # Connect clk clk_pin = self.msb_address_inst.get_pin("clk") clk_pos = clk_pin.bc() rail_pos = self.horz_control_bus_positions["clk_buf"] bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y) self.add_path("m1",[clk_pos,bend_pos,rail_pos]) - - + + def route(self): """ Route all of the signals for the two bank SRAM. """ self.route_shared_banks() - + # connect the horizontal control bus to the vertical bus # connect the data output to the data bus for n in self.data_bus_names: @@ -220,21 +220,21 @@ class sram_2bank(sram_base): self.add_via_center(self.m2_stack,rail_pos) - + def add_lvs_correspondence_points(self): - """ - This adds some points for easier debugging if LVS goes wrong. + """ + 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. """ - + if self.num_banks==1: return - + for n in self.control_bus_names: self.add_label(text=n, - layer="m2", + layer="m2", offset=self.vert_control_bus_positions[n]) for n in self.bank_sel_bus_names: self.add_label(text=n, - layer="m2", + layer="m2", offset=self.vert_control_bus_positions[n]) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 351619bf..455d2b94 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -26,7 +26,7 @@ class sram_base(design, verilog, lef): design.__init__(self, name) lef.__init__(self, ["m1", "m2", "m3", "m4"]) verilog.__init__(self) - + self.sram_config = sram_config sram_config.set_local_config(self) @@ -46,7 +46,7 @@ class sram_base(design, verilog, lef): for port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): 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") @@ -64,7 +64,7 @@ class sram_base(design, verilog, lef): else: self.control_logic_inputs.append(self.control_logic_r.get_inputs()) self.control_logic_outputs.append(self.control_logic_r.get_outputs()) - + for port in self.all_ports: self.add_pin("csb{}".format(port), "INPUT") for port in self.readwrite_ports: @@ -80,7 +80,7 @@ class sram_base(design, verilog, lef): for port in self.read_ports: for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT") - + self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -89,7 +89,7 @@ class sram_base(design, verilog, lef): Add pex labels at the sram level for spice analysis """ - + # add pex labels for bitcells for bank_num in range(len(self.bank_insts)): @@ -103,13 +103,13 @@ class sram_base(design, verilog, lef): br_offsets = pex_data[4] bl_meta = pex_data[5] br_meta = pex_data[6] - + bl = [] br = [] storage_layer_name = "m1" bitline_layer_name = self.bitcell.get_pin("bl").layer - + for cell in range(len(bank_offset)): Q = [bank_offset[cell][0] + Q_offset[cell][0], bank_offset[cell][1] + Q_offset[cell][1]] @@ -166,21 +166,21 @@ class sram_base(design, verilog, lef): def create_netlist(self): """ Netlist creation """ - + start_time = datetime.datetime.now() - + # Must create the control logic before pins to get the pins self.add_modules() self.add_pins() self.create_modules() - + # This is for the lib file if we don't create layout self.width=0 self.height=0 - + if not OPTS.is_unit_test: print_time("Submodules", datetime.datetime.now(), start_time) - + def create_layout(self): """ Layout creation """ start_time = datetime.datetime.now() @@ -195,7 +195,7 @@ class sram_base(design, verilog, lef): print_time("Routing", datetime.datetime.now(), start_time) self.add_lvs_correspondence_points() - + self.offset_all_coordinates() highest_coord = self.find_highest_coords() @@ -214,7 +214,7 @@ class sram_base(design, verilog, lef): def create_modules(self): debug.error("Must override pure virtual function.", -1) - + def route_supplies(self): """ Route the supply grid and connect the pins to them. """ @@ -244,10 +244,10 @@ class sram_base(design, verilog, lef): from supply_grid_router import supply_grid_router as router rtr=router(grid_stack, self) rtr.route() - + def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """ - + # address size + control signals + one-hot bank select signals self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks, 2) + 1 # data bus size @@ -255,20 +255,20 @@ class sram_base(design, verilog, lef): self.vertical_bus_width = self.m2_pitch * self.num_vertical_line # vertical bus height depends on 2 or 4 banks - + self.data_bus_height = self.m3_pitch * self.num_horizontal_line self.data_bus_width = 2 * (self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width - + self.control_bus_height = self.m1_pitch * (self.control_size + 2) self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width - + self.supply_bus_height = self.m1_pitch * 2 # 2 for vdd/gnd placed with control bus self.supply_bus_width = self.data_bus_width # Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really) debug.check(self.bank.width + self.vertical_bus_width > 0.9 * self.control_logic.width, "Bank is too small compared to control logic.") - + def add_busses(self): """ Add the horizontal and vertical busses """ # Vertical bus @@ -304,7 +304,7 @@ class sram_base(design, verilog, lef): offset=self.bank_sel_bus_offset, names=self.bank_sel_bus_names, length=self.vertical_bus_height)) - + # Horizontal data bus self.data_bus_names = ["DATA{0}[{1}]".format(port, i) for i in range(self.word_size)] self.data_bus_positions = self.create_horizontal_pin_bus(layer="m3", @@ -333,7 +333,7 @@ class sram_base(design, verilog, lef): offset=self.control_bus_offset, names=self.control_bus_names[port], length=self.control_bus_width)) - + def add_multi_bank_modules(self): """ Create the multibank address flops and bank decoder """ from dff_buf_array import dff_buf_array @@ -345,7 +345,7 @@ class sram_base(design, verilog, lef): if self.num_banks>2: self.msb_decoder = self.bank.decoder.pre2_4 self.add_mod(self.msb_decoder) - + def add_modules(self): self.bitcell = factory.create(module_type=OPTS.bitcell) self.dff = factory.create(module_type="dff") @@ -370,7 +370,7 @@ class sram_base(design, verilog, lef): if self.num_spare_cols: self.spare_wen_dff = factory.create("dff_array", module_name="spare_wen_dff", rows=1, columns=self.num_spare_cols) self.add_mod(self.spare_wen_dff) - + # Create the bank module (up to four are instantiated) self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank") self.add_mod(self.bank) @@ -383,7 +383,7 @@ class sram_base(design, verilog, lef): c = reload(__import__(OPTS.control_logic)) self.mod_control_logic = getattr(c, OPTS.control_logic) - + # Create the control logic module for each port type if len(self.readwrite_ports)>0: self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows, @@ -470,7 +470,7 @@ class sram_base(design, verilog, lef): bank_mirror = "MY" else: bank_mirror = "R0" - + bank_inst.place(offset=position, mirror=bank_mirror, rotate=bank_rotation) @@ -483,7 +483,7 @@ class sram_base(design, verilog, lef): for port in self.all_ports: insts.append(self.add_inst(name="row_address{}".format(port), mod=self.row_addr_dff)) - + # inputs, outputs/output/bar inputs = [] outputs = [] @@ -492,16 +492,16 @@ class sram_base(design, verilog, lef): outputs.append("a{}[{}]".format(port, bit + self.col_addr_size)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) - + return insts - + def create_col_addr_dff(self): """ Add and place all address flops for the column decoder """ insts = [] for port in self.all_ports: insts.append(self.add_inst(name="col_address{}".format(port), mod=self.col_addr_dff)) - + # inputs, outputs/output/bar inputs = [] outputs = [] @@ -510,7 +510,7 @@ class sram_base(design, verilog, lef): outputs.append("a{}[{}]".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) - + return insts def create_data_dff(self): @@ -523,7 +523,7 @@ class sram_base(design, verilog, lef): else: insts.append(None) continue - + # inputs, outputs/output/bar inputs = [] outputs = [] @@ -532,7 +532,7 @@ class sram_base(design, verilog, lef): 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): @@ -556,7 +556,7 @@ class sram_base(design, verilog, lef): self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) return insts - + def create_spare_wen_dff(self): """ Add all spare write enable flops """ insts = [] @@ -578,7 +578,7 @@ class sram_base(design, verilog, lef): self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) return insts - + def create_control_logic(self): """ Add control logic instances """ @@ -590,7 +590,7 @@ class sram_base(design, verilog, lef): mod = self.control_logic_w else: mod = self.control_logic_r - + insts.append(self.add_inst(name="control{}".format(port), mod=mod)) # Inputs @@ -608,9 +608,9 @@ class sram_base(design, verilog, lef): 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 sp_write(self, sp_name, lvs_netlist=False): # Write the entire spice of the object to the file ############################################################ @@ -626,7 +626,7 @@ class sram_base(design, verilog, lef): sp.write("* Column mux: {}:1\n".format(self.words_per_row)) sp.write("**************************************************\n") # This causes unit test mismatch - + # sp.write("* Created: {0}\n".format(datetime.datetime.now())) # sp.write("* User: {0}\n".format(getpass.getuser())) # sp.write(".global {0} {1}\n".format(spice["vdd_name"], @@ -644,9 +644,9 @@ class sram_base(design, verilog, lef): Excludes bits in column from being added to graph except target """ self.bank.graph_exclude_bits(targ_row, targ_col) - + def clear_exclude_bits(self): - """ + """ Clears the bit exclusions """ self.bank.clear_exclude_bits() diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index 573e7514..28bdfe8d 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -13,7 +13,7 @@ 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, write_size=None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=0): self.word_size = word_size self.num_words = num_words @@ -29,7 +29,7 @@ class sram_config: def set_local_config(self, module): """ Copy all of the member variables to the given module for convenience """ - + members = [attr for attr in dir(self) if not callable(getattr(self, attr)) and not attr.startswith("__")] # Copy all the variables to the local module @@ -39,14 +39,14 @@ class sram_config: def compute_sizes(self): """ Computes the organization of the memory using bitcell size by trying to make it square.""" - bitcell = factory.create(module_type="bitcell") - + bitcell = factory.create(module_type=OPTS.bitcell, cell_name=OPTS.bitcell_name) + debug.check(self.num_banks in [1, 2, 4], "Valid number of banks are 1 , 2 and 4.") self.num_words_per_bank = self.num_words / self.num_banks self.num_bits_per_bank = self.word_size * self.num_words_per_bank - + # 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) @@ -64,18 +64,18 @@ class sram_config: self.recompute_sizes() def recompute_sizes(self): - """ - Calculate the auxiliary values assuming fixed number of words per row. - This can be called multiple times from the unit test when we reconfigure an + """ + Calculate the auxiliary values assuming fixed number of words per row. + This can be called multiple times from the unit test when we reconfigure an 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 - + # Fix the number of columns and rows self.num_cols = int(self.words_per_row * self.word_size) self.num_rows_temp = int(self.num_words_per_bank / self.words_per_row) @@ -116,11 +116,11 @@ class sram_config: debug.check(tentative_num_rows * words_per_row <= 4096, "Number of words exceeds 2048") return int(words_per_row * tentative_num_rows / 512) - + # Recompute the words per row given a hard min if (not OPTS.is_unit_test and tentative_num_rows < 16): debug.check(tentative_num_rows * words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows)) return int(words_per_row * tentative_num_rows / 16) - + return words_per_row diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 0e9721c7..3680e5d5 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -79,7 +79,7 @@ class sram_factory: user_module_type, um_overridden = self.get_usermodule_type(module_type) # print(module_type, tech_module_type, tm_overridden) # print(module_type, user_module_type, um_overridden) - + # overridden user modules have priority if um_overridden: real_module_type = user_module_type @@ -102,7 +102,7 @@ class sram_factory: self.modules[real_module_type] = mod self.module_indices[real_module_type] = 0 self.objects[real_module_type] = [] - + # Either retreive a previous object or create a new one for obj in self.objects[real_module_type]: (obj_kwargs, obj_item) = obj @@ -125,7 +125,7 @@ class sram_factory: module_name = real_module_type else: if self.is_duplicate_name(module_name): - raise ValueError("Modules with duplicate name are not allowed." \ + raise ValueError("Modules with duplicate name are not allowed." " '{}'".format(module_name)) # type_str = "type={}".format(real_module_type) @@ -150,6 +150,6 @@ class sram_factory: 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 571f4be7..834cbace 100755 --- a/compiler/tests/00_code_format_check_test.py +++ b/compiler/tests/00_code_format_check_test.py @@ -27,7 +27,7 @@ class code_format_test(openram_test): if re.search("gdsMill", code): continue errors += check_file_format_tab(code) - errors += check_file_format_carriage(code) + errors += check_file_format_carriage(code) for code in source_codes: if re.search("gdsMill", code): @@ -79,7 +79,7 @@ def check_file_format_tab(file_name): def check_file_format_carriage(file_name): """ - Check if file contains carriage returns at the end of lines + Check if file contains carriage returns at the end of lines and return the number of carriage return lines. """ diff --git a/compiler/tests/01_library_drc_test.py b/compiler/tests/01_library_drc_test.py index 8b254bbc..dbdb1cd2 100755 --- a/compiler/tests/01_library_drc_test.py +++ b/compiler/tests/01_library_drc_test.py @@ -25,7 +25,7 @@ class library_drc_test(openram_test): drc_errors = 0 debug.info(1, "\nPerforming DRC on: " + ", ".join(allnames)) for f in allnames: - gds_name = "{0}/{1}.gds".format(gds_dir, f) + gds_name = "{0}/{1}.gds".format(gds_dir, f) if not os.path.isfile(gds_name): drc_errors += 1 debug.error("Missing GDS file: {}".format(gds_name)) @@ -43,7 +43,7 @@ def setup_files(): gds_files = list(filter(nametest.search, files)) tempnames = gds_files - + # remove the .gds and .sp suffixes for i in range(len(tempnames)): gds_files[i] = re.sub('\.gds$', '', tempnames[i]) diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index d2f0c7f6..7a43d0e4 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -32,7 +32,7 @@ class contact_test(openram_test): 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=("H","V")) @@ -51,7 +51,7 @@ class contact_test(openram_test): # Only do multiple contacts for BEOL for layer_stack in beol_stacks: stack_name = ":".join(map(str, layer_stack)) - + # check vertical array with one in the middle and two ends debug.info(2, "1 x 3 {} test".format(stack_name)) c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 3)) @@ -71,7 +71,7 @@ class contact_test(openram_test): # check vertical array with one in the middle and two ends layer_stack = active_stack stack_name = ":".join(map(str, layer_stack)) - + debug.info(2, "1 x 1 {} nwell".format(stack_name)) c = factory.create(module_type="contact", layer_stack=layer_stack, @@ -85,9 +85,9 @@ class contact_test(openram_test): implant_type="p", well_type="p") self.local_drc_check(c) - + globals.end_openram() - + # run the test from the command line diff --git a/compiler/tests/03_path_test.py b/compiler/tests/03_path_test.py index 94e7821a..306bf4e1 100755 --- a/compiler/tests/03_path_test.py +++ b/compiler/tests/03_path_test.py @@ -87,7 +87,7 @@ class path_test(openram_test): self.local_drc_check(w) globals.end_openram() - + # run the test from the command line diff --git a/compiler/tests/03_ptx_3finger_pmos_test.py b/compiler/tests/03_ptx_3finger_pmos_test.py index ccb8b586..0f0edb9c 100755 --- a/compiler/tests/03_ptx_3finger_pmos_test.py +++ b/compiler/tests/03_ptx_3finger_pmos_test.py @@ -33,7 +33,7 @@ class ptx_3finger_pmos_test(openram_test): self.local_drc_check(fet) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": diff --git a/compiler/tests/03_ptx_4finger_pmos_test.py b/compiler/tests/03_ptx_4finger_pmos_test.py index 0bcdf909..9fae372e 100755 --- a/compiler/tests/03_ptx_4finger_pmos_test.py +++ b/compiler/tests/03_ptx_4finger_pmos_test.py @@ -33,7 +33,7 @@ class ptx_test(openram_test): self.local_drc_check(fet) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": diff --git a/compiler/tests/03_ptx_no_contacts_test.py b/compiler/tests/03_ptx_no_contacts_test.py index ff39da79..58611ca8 100755 --- a/compiler/tests/03_ptx_no_contacts_test.py +++ b/compiler/tests/03_ptx_no_contacts_test.py @@ -48,7 +48,7 @@ class ptx_no_contacts_test(openram_test): series_devices=True, tx_type="nmos") self.local_drc_check(fet) - + globals.end_openram() diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py index 61e21985..6e036c08 100755 --- a/compiler/tests/03_wire_test.py +++ b/compiler/tests/03_wire_test.py @@ -51,7 +51,7 @@ class wire_test(openram_test): self.local_drc_check(w) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": diff --git a/compiler/tests/04_and2_dec_test.py b/compiler/tests/04_and2_dec_test.py index 97ac5749..de215ffe 100755 --- a/compiler/tests/04_and2_dec_test.py +++ b/compiler/tests/04_and2_dec_test.py @@ -27,7 +27,7 @@ class and2_dec_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(2, "Testing and2_dec gate") a = factory.create(module_type="and2_dec") self.local_check(a) diff --git a/compiler/tests/04_and3_dec_test.py b/compiler/tests/04_and3_dec_test.py index ec83335b..51a310fd 100755 --- a/compiler/tests/04_and3_dec_test.py +++ b/compiler/tests/04_and3_dec_test.py @@ -27,7 +27,7 @@ class and3_dec_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(2, "Testing and3_dec gate") a = factory.create(module_type="and3_dec") self.local_check(a) diff --git a/compiler/tests/04_and4_dec_test.py b/compiler/tests/04_and4_dec_test.py index aa163160..496cf69f 100755 --- a/compiler/tests/04_and4_dec_test.py +++ b/compiler/tests/04_and4_dec_test.py @@ -29,7 +29,7 @@ class and4_dec_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(2, "Testing and4_dec gate") a = factory.create(module_type="and4_dec") self.local_check(a) diff --git a/compiler/tests/04_column_mux_1rw_1r_test.py b/compiler/tests/04_column_mux_1rw_1r_test.py index 7825e081..1e066181 100755 --- a/compiler/tests/04_column_mux_1rw_1r_test.py +++ b/compiler/tests/04_column_mux_1rw_1r_test.py @@ -26,7 +26,7 @@ class column_mux_1rw_1r_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(2, "Checking column mux port 0") tx = factory.create(module_type="column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) @@ -34,7 +34,7 @@ class column_mux_1rw_1r_test(openram_test): debug.info(2, "Checking column mux port 1") tx = factory.create(module_type="column_mux", tx_size=8, bitcell_bl="bl1", bitcell_br="br1") self.local_check(tx) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/04_dff_buf_test.py b/compiler/tests/04_dff_buf_test.py index 070cdb56..748e38a0 100755 --- a/compiler/tests/04_dff_buf_test.py +++ b/compiler/tests/04_dff_buf_test.py @@ -8,19 +8,19 @@ # import unittest from testutils import * -import sys,os +import sys, os 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): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(2, "Testing dff_buf 4x 8x") a = factory.create(module_type="dff_buf", inv1_size=4, inv2_size=8) self.local_check(a) diff --git a/compiler/tests/04_dummy_pbitcell_test.py b/compiler/tests/04_dummy_pbitcell_test.py index b39e48ea..f92d2c91 100755 --- a/compiler/tests/04_dummy_pbitcell_test.py +++ b/compiler/tests/04_dummy_pbitcell_test.py @@ -21,7 +21,7 @@ class replica_pbitcell_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) import dummy_pbitcell - + OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 0 @@ -31,7 +31,7 @@ class replica_pbitcell_test(openram_test): 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 @@ -42,7 +42,7 @@ class replica_pbitcell_test(openram_test): self.local_check(tx) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 450c0263..74880a2f 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -37,7 +37,7 @@ class pbitcell_test(openram_test): debug.info(2, "Bitcell with 0 read/write ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) - + OPTS.num_rw_ports=1 OPTS.num_w_ports=0 OPTS.num_r_ports=1 @@ -45,7 +45,7 @@ class pbitcell_test(openram_test): debug.info(2, "Bitcell with 0 write ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) - + OPTS.num_rw_ports=1 OPTS.num_w_ports=1 OPTS.num_r_ports=0 @@ -53,7 +53,7 @@ class pbitcell_test(openram_test): debug.info(2, "Bitcell with 0 read ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) - + OPTS.num_rw_ports=1 OPTS.num_w_ports=0 OPTS.num_r_ports=0 @@ -69,7 +69,7 @@ class pbitcell_test(openram_test): debug.info(2, "Bitcell with 2 of each port: read/write, write, and read") tx = factory.create(module_type="pbitcell") self.local_check(tx) - + OPTS.num_rw_ports=0 OPTS.num_w_ports=2 OPTS.num_r_ports=2 @@ -77,7 +77,7 @@ class pbitcell_test(openram_test): debug.info(2, "Bitcell with 0 read/write ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) - + OPTS.num_rw_ports=2 OPTS.num_w_ports=0 OPTS.num_r_ports=2 @@ -85,7 +85,7 @@ class pbitcell_test(openram_test): debug.info(2, "Bitcell with 0 write ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) - + OPTS.num_rw_ports=2 OPTS.num_w_ports=2 OPTS.num_r_ports=0 @@ -93,7 +93,7 @@ class pbitcell_test(openram_test): debug.info(2, "Bitcell with 0 read ports") tx = factory.create(module_type="pbitcell") self.local_check(tx) - + OPTS.num_rw_ports=2 OPTS.num_w_ports=0 OPTS.num_r_ports=0 diff --git a/compiler/tests/04_pbuf_dec_8x_test.py b/compiler/tests/04_pbuf_dec_8x_test.py index ad1665fe..6a7bd62a 100755 --- a/compiler/tests/04_pbuf_dec_8x_test.py +++ b/compiler/tests/04_pbuf_dec_8x_test.py @@ -25,7 +25,7 @@ class pbuf_dec_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(2, "Checking 8x size decoder buffer") a = factory.create(module_type="pbuf_dec", size=8) self.local_check(a) diff --git a/compiler/tests/04_pdriver_test.py b/compiler/tests/04_pdriver_test.py index 41af86f6..d1d3dad4 100755 --- a/compiler/tests/04_pdriver_test.py +++ b/compiler/tests/04_pdriver_test.py @@ -25,19 +25,19 @@ class pdriver_test(openram_test): # 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 = factory.create(module_type="pdriver", size_list = [1,2,4,8]) self.local_check(b) - + c = factory.create(module_type="pdriver", fanout = 50) self.local_check(c) - + d = factory.create(module_type="pdriver", fanout = 50, inverting = True) self.local_check(d) - + e = factory.create(module_type="pdriver", fanout = 64) self.local_check(e) - + f = factory.create(module_type="pdriver", fanout = 64, inverting = True) self.local_check(f) diff --git a/compiler/tests/04_pinv_10x_test.py b/compiler/tests/04_pinv_10x_test.py index ae3f480b..f8452460 100755 --- a/compiler/tests/04_pinv_10x_test.py +++ b/compiler/tests/04_pinv_10x_test.py @@ -25,7 +25,7 @@ class pinv_test(openram_test): tx = factory.create(module_type="pinv", size=8) self.local_check(tx) - globals.end_openram() + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/04_pinv_1x_beta_test.py b/compiler/tests/04_pinv_1x_beta_test.py index 0b8c055a..45a6d37c 100755 --- a/compiler/tests/04_pinv_1x_beta_test.py +++ b/compiler/tests/04_pinv_1x_beta_test.py @@ -25,7 +25,7 @@ class pinv_test(openram_test): tx = factory.create(module_type="pinv", size=1, beta=3) self.local_check(tx) - globals.end_openram() + globals.end_openram() # run the test from the command line if __name__ == "__main__": diff --git a/compiler/tests/04_pinv_2x_test.py b/compiler/tests/04_pinv_2x_test.py index 0a0e63ec..374279a1 100755 --- a/compiler/tests/04_pinv_2x_test.py +++ b/compiler/tests/04_pinv_2x_test.py @@ -25,7 +25,7 @@ class pinv_test(openram_test): tx = factory.create(module_type="pinv", size=2) self.local_check(tx) - globals.end_openram() + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/04_pinv_dec_1x_test.py b/compiler/tests/04_pinv_dec_1x_test.py index 92772616..18f5e907 100755 --- a/compiler/tests/04_pinv_dec_1x_test.py +++ b/compiler/tests/04_pinv_dec_1x_test.py @@ -25,7 +25,7 @@ class pinv_dec_1x_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(2, "Checking 1x size decoder inverter") tx = factory.create(module_type="pinv_dec", size=1) self.local_check(tx) diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py index ae0668ae..04106f03 100755 --- a/compiler/tests/04_pnand2_test.py +++ b/compiler/tests/04_pnand2_test.py @@ -29,9 +29,9 @@ class pnand2_test(openram_test): # tx = factory.create(module_type="pnand2", size=1, add_wells=False) # # Only DRC because well contacts will fail LVS # self.local_drc_check(tx) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": diff --git a/compiler/tests/04_pnand3_test.py b/compiler/tests/04_pnand3_test.py index c03e32d6..0a327e86 100755 --- a/compiler/tests/04_pnand3_test.py +++ b/compiler/tests/04_pnand3_test.py @@ -31,7 +31,7 @@ class pnand3_test(openram_test): # self.local_drc_check(tx) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": diff --git a/compiler/tests/04_pnand4_test.py b/compiler/tests/04_pnand4_test.py index 8eb1b4ba..f90a4ecc 100755 --- a/compiler/tests/04_pnand4_test.py +++ b/compiler/tests/04_pnand4_test.py @@ -27,7 +27,7 @@ class pnand4_test(openram_test): self.local_check(tx) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py index ea0d6dbc..fd6ecab2 100755 --- a/compiler/tests/04_pnor2_test.py +++ b/compiler/tests/04_pnor2_test.py @@ -26,7 +26,7 @@ class pnor2_test(openram_test): self.local_check(tx) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/04_precharge_1rw_1r_test.py b/compiler/tests/04_precharge_1rw_1r_test.py index 4765c77c..ea452e38 100755 --- a/compiler/tests/04_precharge_1rw_1r_test.py +++ b/compiler/tests/04_precharge_1rw_1r_test.py @@ -21,7 +21,7 @@ class precharge_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - + # check precharge array in multi-port OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 @@ -38,7 +38,7 @@ class precharge_test(openram_test): self.local_check(tx) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index 76d433a8..e66329b5 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -21,14 +21,14 @@ class precharge_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - + # check precharge in single port debug.info(2, "Checking precharge for handmade bitcell") tx = factory.create(module_type="precharge", size=1) self.local_check(tx) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/04_pwrite_driver_test.py b/compiler/tests/04_pwrite_driver_test.py index 5e1bf621..3ebca3ab 100755 --- a/compiler/tests/04_pwrite_driver_test.py +++ b/compiler/tests/04_pwrite_driver_test.py @@ -26,7 +26,7 @@ class pwrite_driver_test(openram_test): tx = factory.create(module_type="pwrite_driver", size=1) self.local_check(tx) - globals.end_openram() + globals.end_openram() # run the test from the command line if __name__ == "__main__": diff --git a/compiler/tests/04_replica_pbitcell_test.py b/compiler/tests/04_replica_pbitcell_test.py index 77336c61..64159906 100755 --- a/compiler/tests/04_replica_pbitcell_test.py +++ b/compiler/tests/04_replica_pbitcell_test.py @@ -21,7 +21,7 @@ class replica_pbitcell_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) import replica_pbitcell - + OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 0 @@ -31,7 +31,7 @@ class replica_pbitcell_test(openram_test): debug.info(2, "Checking replica bitcell using pbitcell (small cell)") tx = replica_pbitcell.replica_pbitcell(name="rpbc") self.local_check(tx) - + OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 1 @@ -42,7 +42,7 @@ class replica_pbitcell_test(openram_test): self.local_check(tx) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/05_bitcell_array_1rw_1r_test.py b/compiler/tests/05_bitcell_array_1rw_1r_test.py index 0e7d5665..a1b7a3eb 100755 --- a/compiler/tests/05_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/05_bitcell_array_1rw_1r_test.py @@ -22,16 +22,16 @@ class bitcell_array_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - + OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + 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 diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index ed4b8298..9726a4ea 100755 --- a/compiler/tests/05_bitcell_array_test.py +++ b/compiler/tests/05_bitcell_array_test.py @@ -26,7 +26,7 @@ class array_test(openram_test): debug.info(2, "Testing 8x8 array for 6t_cell") a = factory.create(module_type="bitcell_array", cols=8, rows=8) self.local_check(a) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/05_dummy_array_test.py b/compiler/tests/05_dummy_array_test.py index 97ec8db5..512b6ce0 100755 --- a/compiler/tests/05_dummy_array_test.py +++ b/compiler/tests/05_dummy_array_test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California +# Copyright (c) 2016-2019 Regents of the University of California # All rights reserved. # import unittest @@ -26,7 +26,7 @@ class dummy_row_test(openram_test): 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 diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 6c5e4729..797f27b1 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -29,7 +29,7 @@ class pbitcell_array_test(openram_test): OPTS.num_w_ports = 2 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") OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 2 @@ -37,7 +37,7 @@ class pbitcell_array_test(openram_test): OPTS.num_w_ports = 2 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") OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 2 diff --git a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py index 844160a6..d7e268f8 100755 --- a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py @@ -27,7 +27,7 @@ class hierarchical_decoder_1rw_1r_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + # Checks 2x4 and 2-input NAND decoder debug.info(1, "Testing 16 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) @@ -57,9 +57,9 @@ class hierarchical_decoder_1rw_1r_test(openram_test): debug.info(1, "Testing 512 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=512) self.local_check(a) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py index e1605067..efa05708 100755 --- a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py @@ -25,7 +25,7 @@ class hierarchical_decoder_pbitcell_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 globals.setup_bitcell() - + factory.reset() debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 42bc38cd..083e5f3f 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -22,11 +22,10 @@ class hierarchical_decoder_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() # Checks 2x4 and 2-input NAND decoder debug.info(1, "Testing 16 row sample for hierarchical_decoder") @@ -62,9 +61,9 @@ class hierarchical_decoder_test(openram_test): debug.info(1, "Testing 4096 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=4096) self.local_check(a) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py index a3ae2ed2..fbcf15f4 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py @@ -26,7 +26,7 @@ class hierarchical_predecode2x4_1rw_1r_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(1, "Testing sample for hierarchy_predecode2x4") a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py index 0f70ddb1..f8825c23 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py @@ -26,7 +26,7 @@ class hierarchical_predecode2x4_pbitcell_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 globals.setup_bitcell() - + debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)") a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py index c0653243..2e7361a8 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py @@ -27,7 +27,7 @@ class hierarchical_predecode3x8_1rw_1r_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(1, "Testing sample for hierarchy_predecode3x8") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py index 9170d5c0..87f845ae 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py @@ -26,7 +26,7 @@ class hierarchical_predecode3x8_pbitcell_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 globals.setup_bitcell() - + debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode4x16_test.py b/compiler/tests/06_hierarchical_predecode4x16_test.py index 7ded6144..3ea60c5d 100755 --- a/compiler/tests/06_hierarchical_predecode4x16_test.py +++ b/compiler/tests/06_hierarchical_predecode4x16_test.py @@ -24,10 +24,10 @@ class hierarchical_predecode4x16_test(openram_test): globals.init_openram(config_file) # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() debug.info(1, "Testing sample for hierarchy_predecode4x16") a = factory.create(module_type="hierarchical_predecode4x16") diff --git a/compiler/tests/07_column_mux_array_1rw_1r_test.py b/compiler/tests/07_column_mux_array_1rw_1r_test.py index 10c96092..5e23d7ce 100755 --- a/compiler/tests/07_column_mux_array_1rw_1r_test.py +++ b/compiler/tests/07_column_mux_array_1rw_1r_test.py @@ -25,7 +25,7 @@ class column_mux_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(1, "Testing sample for 2-way column_mux_array port 0") a = factory.create(module_type="column_mux_array", columns=8, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) @@ -49,7 +49,7 @@ class column_mux_test(openram_test): debug.info(1, "Testing sample for 8-way column_mux_array port 1") a = factory.create(module_type="column_mux_array", columns=16, word_size=2, bitcell_bl="bl1", bitcell_br="br1") self.local_check(a) - + globals.end_openram() diff --git a/compiler/tests/08_precharge_array_1rw_1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py index c5efb59b..d76e0ee7 100755 --- a/compiler/tests/08_precharge_array_1rw_1r_test.py +++ b/compiler/tests/08_precharge_array_1rw_1r_test.py @@ -20,7 +20,7 @@ class precharge_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - + # check precharge array in multi-port OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 @@ -35,7 +35,7 @@ class precharge_1rw_1r_test(openram_test): debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 1)") pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0", column_offset=1) self.local_check(pc) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/09_sense_amp_array_spare_cols_test.py b/compiler/tests/09_sense_amp_array_spare_cols_test.py index d8da1dc5..052aea1e 100755 --- a/compiler/tests/09_sense_amp_array_spare_cols_test.py +++ b/compiler/tests/09_sense_amp_array_spare_cols_test.py @@ -29,7 +29,7 @@ class sense_amp_array_spare_cols_test(openram_test): debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 and num_spare_cols=2") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4, num_spare_cols=2) self.local_check(a) - + # check sense amp array for multi-port OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 @@ -44,9 +44,9 @@ class sense_amp_array_spare_cols_test(openram_test): debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4, num_spare_cols=3 (multi-port case)") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4, num_spare_cols=3) self.local_check(a) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/10_write_driver_array_spare_cols_test.py b/compiler/tests/10_write_driver_array_spare_cols_test.py index 8b1256d0..fde478ee 100755 --- a/compiler/tests/10_write_driver_array_spare_cols_test.py +++ b/compiler/tests/10_write_driver_array_spare_cols_test.py @@ -29,7 +29,7 @@ class write_driver_array_spare_cols_test(openram_test): debug.info(2, "Testing write_driver_array for columns=16, word_size=8 and num_spare_cols=3") a = factory.create(module_type="write_driver_array", columns=16, word_size=8, num_spare_cols=3) self.local_check(a) - + # check write driver array for multi-port OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 @@ -44,7 +44,7 @@ class write_driver_array_spare_cols_test(openram_test): debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case and num_spare_cols=3") a = factory.create(module_type="write_driver_array", columns=16, word_size=8, num_spare_cols=3) self.local_check(a) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/10_write_mask_and_array_1rw_1r_test.py b/compiler/tests/10_write_mask_and_array_1rw_1r_test.py index 73988db9..1331747d 100755 --- a/compiler/tests/10_write_mask_and_array_1rw_1r_test.py +++ b/compiler/tests/10_write_mask_and_array_1rw_1r_test.py @@ -27,7 +27,7 @@ class write_mask_and_array_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + 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) diff --git a/compiler/tests/12_tri_gate_array_test.py b/compiler/tests/12_tri_gate_array_test.py index 813feda0..24c00ecb 100755 --- a/compiler/tests/12_tri_gate_array_test.py +++ b/compiler/tests/12_tri_gate_array_test.py @@ -24,13 +24,13 @@ class tri_gate_array_test(openram_test): debug.info(1, "Testing tri_gate_array for 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 = factory.create(module_type="tri_gate_array", columns=16, word_size=8) self.local_check(a) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/13_delay_chain_test.py b/compiler/tests/13_delay_chain_test.py index 4b893a8a..6930a077 100755 --- a/compiler/tests/13_delay_chain_test.py +++ b/compiler/tests/13_delay_chain_test.py @@ -26,7 +26,7 @@ class delay_chain_test(openram_test): self.local_check(a) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py index 65bc7848..54181058 100755 --- a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py @@ -23,7 +23,7 @@ class replica_bitcell_array_1rw_1r_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(2, "Testing 4x4 non-replica array for cell_1rw_1r") a = factory.create(module_type="replica_bitcell_array", cols=4, @@ -38,7 +38,7 @@ class replica_bitcell_array_1rw_1r_test(openram_test): rbl=[1, 1], left_rbl=[0]) self.local_check(a) - + debug.info(2, "Testing 4x4 array left and right replica for cell_1rw_1r") a = factory.create(module_type="replica_bitcell_array", cols=4, @@ -48,7 +48,7 @@ class replica_bitcell_array_1rw_1r_test(openram_test): right_rbl=[1]) self.local_check(a) - + # Sky 130 has restrictions on the symmetries if OPTS.tech_name != "sky130": debug.info(2, "Testing 4x4 array right only replica for cell_1rw_1r") diff --git a/compiler/tests/14_replica_bitcell_array_test.py b/compiler/tests/14_replica_bitcell_array_test.py index 22b3a926..d9cf7ac6 100755 --- a/compiler/tests/14_replica_bitcell_array_test.py +++ b/compiler/tests/14_replica_bitcell_array_test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California +# Copyright (c) 2016-2019 Regents of the University of California # All rights reserved. # import unittest @@ -27,7 +27,7 @@ class replica_bitcell_array_test(openram_test): debug.info(2, "Testing 4x4 array for bitcell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 0]) self.local_check(a) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/14_replica_column_1rw_1r_test.py b/compiler/tests/14_replica_column_1rw_1r_test.py index 335666d7..f98543a3 100755 --- a/compiler/tests/14_replica_column_1rw_1r_test.py +++ b/compiler/tests/14_replica_column_1rw_1r_test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California +# Copyright (c) 2016-2019 Regents of the University of California # All rights reserved. # import unittest @@ -31,11 +31,11 @@ class replica_column_test(openram_test): debug.info(2, "Testing replica column for 6t_cell") a = factory.create(module_type="replica_column", rows=4, rbl=[1, 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, rbl=[2, 0], replica_bit=2) self.local_check(a) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/14_replica_column_test.py b/compiler/tests/14_replica_column_test.py index eb1e96f7..8ac5eea9 100755 --- a/compiler/tests/14_replica_column_test.py +++ b/compiler/tests/14_replica_column_test.py @@ -1,36 +1,39 @@ #!/usr/bin/env python3 # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California +# Copyright (c) 2016-2019 Regents of the University of California # All rights reserved. # import unittest from testutils import * -import sys,os +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): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(2, "Testing replica column for 6t_cell") + debug.info(2, "Testing replica column for cell_6t") a = factory.create(module_type="replica_column", rows=4, rbl=[1, 0], replica_bit=1) self.local_check(a) - debug.info(2, "Testing replica column for 6t_cell") + debug.info(2, "Testing replica column for cell_1rw_1r") + globals.setup_bitcell() a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=6) self.local_check(a) - - debug.info(2, "Testing replica column for 6t_cell") + + debug.info(2, "Testing replica column for cell_1rw_1r") + globals.setup_bitcell() a = factory.create(module_type="replica_column", rows=4, rbl=[2, 0], replica_bit=2) self.local_check(a) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/15_global_bitcell_array_1rw_1r_test.py b/compiler/tests/15_global_bitcell_array_1rw_1r_test.py index dd17d82e..15fc4821 100755 --- a/compiler/tests/15_global_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/15_global_bitcell_array_1rw_1r_test.py @@ -34,7 +34,7 @@ class global_bitcell_array_test(openram_test): # debug.info(2, "Testing 4x4 local bitcell array for 6t_cell with replica column") # a = factory.create(module_type="local_bitcell_array", cols=4, left_rbl=1, rows=4, ports=[0]) # self.local_check(a) - + globals.end_openram() diff --git a/compiler/tests/15_global_bitcell_array_test.py b/compiler/tests/15_global_bitcell_array_test.py index ed7e282a..e53e98ee 100755 --- a/compiler/tests/15_global_bitcell_array_test.py +++ b/compiler/tests/15_global_bitcell_array_test.py @@ -29,7 +29,7 @@ class global_bitcell_array_test(openram_test): debug.info(2, "Testing 2 x 4x4 global bitcell array for 6t_cell") a = factory.create(module_type="global_bitcell_array", cols=[10, 6], rows=4) self.local_check(a) - + globals.end_openram() diff --git a/compiler/tests/15_local_bitcell_array_1rw_1r_test.py b/compiler/tests/15_local_bitcell_array_1rw_1r_test.py index 01a5c8ee..4d3469b3 100755 --- a/compiler/tests/15_local_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/15_local_bitcell_array_1rw_1r_test.py @@ -34,7 +34,7 @@ class local_bitcell_array_1rw_1r_test(openram_test): debug.info(2, "Testing 4x4 local bitcell array for cell_1rw_1r with replica column") a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 1], right_rbl=[1]) self.local_check(a) - + debug.info(2, "Testing 4x4 local bitcell array for cell_1rw_1r with replica column") a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, rbl=[1, 1], left_rbl=[0]) self.local_check(a) diff --git a/compiler/tests/16_control_logic_multiport_test.py b/compiler/tests/16_control_logic_multiport_test.py index 919e2335..a660ff55 100755 --- a/compiler/tests/16_control_logic_multiport_test.py +++ b/compiler/tests/16_control_logic_multiport_test.py @@ -37,13 +37,13 @@ class control_logic_test(openram_test): 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") @@ -51,7 +51,7 @@ class control_logic_test(openram_test): self.local_check(a) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/16_control_logic_r_test.py b/compiler/tests/16_control_logic_r_test.py index b695f9c9..95d10a2d 100755 --- a/compiler/tests/16_control_logic_r_test.py +++ b/compiler/tests/16_control_logic_r_test.py @@ -27,7 +27,7 @@ class control_logic_test(openram_test): self.local_check(a) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/16_control_logic_rw_test.py b/compiler/tests/16_control_logic_rw_test.py index 8d12085f..a77f55b8 100755 --- a/compiler/tests/16_control_logic_rw_test.py +++ b/compiler/tests/16_control_logic_rw_test.py @@ -25,9 +25,9 @@ class control_logic_test(openram_test): 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() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/16_control_logic_w_test.py b/compiler/tests/16_control_logic_w_test.py index 8407d1e8..3bc9f545 100755 --- a/compiler/tests/16_control_logic_w_test.py +++ b/compiler/tests/16_control_logic_w_test.py @@ -26,7 +26,7 @@ class control_logic_test(openram_test): self.local_check(a) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_1rw_1r_test.py index 42a46614..3b8da0b7 100755 --- a/compiler/tests/18_port_address_1rw_1r_test.py +++ b/compiler/tests/18_port_address_1rw_1r_test.py @@ -25,7 +25,7 @@ class port_address_1rw_1r_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + debug.info(1, "Port address 16 rows") a = factory.create("port_address", cols=16, rows=16, port=0) self.local_check(a) @@ -33,9 +33,9 @@ class port_address_1rw_1r_test(openram_test): debug.info(1, "Port address 256 rows") a = factory.create("port_address", cols=256, rows=256, port=1) self.local_check(a) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/18_port_address_test.py b/compiler/tests/18_port_address_test.py index 94feedb2..a3cb9bd9 100755 --- a/compiler/tests/18_port_address_test.py +++ b/compiler/tests/18_port_address_test.py @@ -23,13 +23,13 @@ class port_address_test(openram_test): debug.info(1, "Port address 16 rows") a = factory.create("port_address", cols=16, rows=16, port=0) self.local_check(a) - + debug.info(1, "Port address 512 rows") a = factory.create("port_address", cols=256, rows=512, port=0) self.local_check(a) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index 3a7687d6..e8205c1a 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -25,7 +25,7 @@ class port_data_1rw_1r_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=16) @@ -68,9 +68,9 @@ class port_data_1rw_1r_test(openram_test): 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() diff --git a/compiler/tests/18_port_data_spare_cols_test.py b/compiler/tests/18_port_data_spare_cols_test.py index 8c08ad0a..0ec87d5f 100755 --- a/compiler/tests/18_port_data_spare_cols_test.py +++ b/compiler/tests/18_port_data_spare_cols_test.py @@ -6,13 +6,14 @@ # import unittest from testutils import * -import sys,os +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_spare_cols_test(openram_test): def runTest(self): @@ -57,11 +58,11 @@ class port_data_spare_cols_test(openram_test): 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 + globals.setup_bitcell() c.num_words=16 c.words_per_row=1 @@ -72,7 +73,7 @@ class port_data_spare_cols_test(openram_test): 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() @@ -103,9 +104,9 @@ class port_data_spare_cols_test(openram_test): 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() diff --git a/compiler/tests/18_port_data_test.py b/compiler/tests/18_port_data_test.py index 57e19846..ae305cea 100755 --- a/compiler/tests/18_port_data_test.py +++ b/compiler/tests/18_port_data_test.py @@ -54,9 +54,9 @@ class port_data_test(openram_test): debug.info(1, "Eight way column mux") a = factory.create("port_data", sram_config=c, port=0) self.local_check(a) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/18_port_data_wmask_1rw_1r_test.py b/compiler/tests/18_port_data_wmask_1rw_1r_test.py index 74aa7fc0..1f56bd62 100755 --- a/compiler/tests/18_port_data_wmask_1rw_1r_test.py +++ b/compiler/tests/18_port_data_wmask_1rw_1r_test.py @@ -62,10 +62,10 @@ class port_data_wmask_1rw_1r_test(openram_test): 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 + globals.setup_bitcell() c.num_words = 16 c.words_per_row = 1 diff --git a/compiler/tests/18_port_data_wmask_test.py b/compiler/tests/18_port_data_wmask_test.py index 1c650c74..a7fecb8a 100755 --- a/compiler/tests/18_port_data_wmask_test.py +++ b/compiler/tests/18_port_data_wmask_test.py @@ -57,10 +57,10 @@ class port_data_wmask_test(openram_test): 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 + globals.setup_bitcell() c.num_words = 16 c.words_per_row = 1 diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index f4c622c7..c2782954 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -58,9 +58,9 @@ class multi_bank_test(openram_test): 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() diff --git a/compiler/tests/19_pmulti_bank_test.py b/compiler/tests/19_pmulti_bank_test.py index 7c3da444..0e736973 100755 --- a/compiler/tests/19_pmulti_bank_test.py +++ b/compiler/tests/19_pmulti_bank_test.py @@ -63,9 +63,9 @@ class multi_bank_test(openram_test): 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() diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index 8c6ddb12..5c6bc418 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -8,39 +8,40 @@ # import unittest from testutils import * -import sys,os +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 19_psingle_bank_test") + class psingle_bank_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) 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 = 0 + globals.setup_bitcell() 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() @@ -48,7 +49,7 @@ class psingle_bank_test(openram_test): 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() @@ -56,7 +57,7 @@ class psingle_bank_test(openram_test): 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 @@ -65,9 +66,9 @@ class psingle_bank_test(openram_test): debug.info(1, "Four 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() diff --git a/compiler/tests/19_single_bank_1rw_1r_test.py b/compiler/tests/19_single_bank_1rw_1r_test.py index b60e7c98..2390482a 100755 --- a/compiler/tests/19_single_bank_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_1rw_1r_test.py @@ -27,7 +27,7 @@ class single_bank_1rw_1r_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=16) @@ -62,9 +62,9 @@ class single_bank_1rw_1r_test(openram_test): 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() diff --git a/compiler/tests/19_single_bank_1w_1r_test.py b/compiler/tests/19_single_bank_1w_1r_test.py index e3a2d886..1eef7c93 100755 --- a/compiler/tests/19_single_bank_1w_1r_test.py +++ b/compiler/tests/19_single_bank_1w_1r_test.py @@ -8,13 +8,14 @@ # import unittest from testutils import * -import sys,os +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): @@ -26,7 +27,7 @@ class single_bank_1w_1r_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 1 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=16) @@ -61,9 +62,9 @@ class single_bank_1w_1r_test(openram_test): 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() diff --git a/compiler/tests/19_single_bank_global_bitline.py b/compiler/tests/19_single_bank_global_bitline.py index ffaea6e6..60568fda 100755 --- a/compiler/tests/19_single_bank_global_bitline.py +++ b/compiler/tests/19_single_bank_global_bitline.py @@ -63,9 +63,9 @@ class single_bank_1rw_1r_test(openram_test): 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() diff --git a/compiler/tests/19_single_bank_spare_cols_test.py b/compiler/tests/19_single_bank_spare_cols_test.py index 52eeea52..78c8f497 100755 --- a/compiler/tests/19_single_bank_spare_cols_test.py +++ b/compiler/tests/19_single_bank_spare_cols_test.py @@ -57,9 +57,9 @@ class single_bank_spare_cols_test(openram_test): 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() diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index 6cff481b..db6776e3 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -8,13 +8,14 @@ # import unittest from testutils import * -import sys,os +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_test(openram_test): def runTest(self): @@ -56,9 +57,9 @@ class single_bank_test(openram_test): 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() diff --git a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py index ddb97905..0137ad61 100755 --- a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py @@ -62,9 +62,9 @@ class single_bank_wmask_1rw_1r_test(openram_test): 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() diff --git a/compiler/tests/19_single_bank_wmask_test.py b/compiler/tests/19_single_bank_wmask_test.py index 50567ebb..e36bd2df 100755 --- a/compiler/tests/19_single_bank_wmask_test.py +++ b/compiler/tests/19_single_bank_wmask_test.py @@ -58,9 +58,9 @@ class single_bank_wmask_test(openram_test): 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() 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 f521851b..1ba44598 100755 --- a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py @@ -8,14 +8,14 @@ # import unittest from testutils import * -import sys,os +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_psram_1bank_test, multiport layout not complete") + class psram_1bank_2mux_1rw_1w_test(openram_test): def runTest(self): @@ -28,7 +28,7 @@ class psram_1bank_2mux_1rw_1w_test(openram_test): OPTS.num_w_ports = 1 OPTS.num_r_ports = 0 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=32, num_banks=1) @@ -46,9 +46,9 @@ class psram_1bank_2mux_1rw_1w_test(openram_test): 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() 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 index 0d236bc2..23b2d490 100755 --- a/compiler/tests/20_psram_1bank_2mux_1rw_1w_wmask_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_wmask_test.py @@ -9,7 +9,6 @@ import unittest from testutils import * import sys, os - sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS @@ -26,12 +25,11 @@ class psram_1bank_2mux_1rw_1w_wmask_test(openram_test): 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 - + globals.setup_bitcell() + c = sram_config(word_size=8, write_size=4, num_words=32, 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 35912823..1e82ea4f 100755 --- a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py @@ -8,14 +8,14 @@ # import unittest from testutils import * -import sys,os +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_psram_1bank_2mux_1w_1r_test, odd supply routing error") + class psram_1bank_2mux_1w_1r_test(openram_test): def runTest(self): @@ -28,7 +28,7 @@ class psram_1bank_2mux_1w_1r_test(openram_test): OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=32, num_banks=1) @@ -44,9 +44,9 @@ class psram_1bank_2mux_1w_1r_test(openram_test): 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() diff --git a/compiler/tests/20_psram_1bank_2mux_test.py b/compiler/tests/20_psram_1bank_2mux_test.py index 92403cd1..7ce30ecf 100755 --- a/compiler/tests/20_psram_1bank_2mux_test.py +++ b/compiler/tests/20_psram_1bank_2mux_test.py @@ -8,27 +8,27 @@ # import unittest from testutils import * -import sys,os +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_psram_1bank_2mux_test, wide metal supply routing error") + class psram_1bank_2mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - + OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=32, num_banks=1) @@ -46,9 +46,9 @@ class psram_1bank_2mux_test(openram_test): 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() 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 ecbd0863..45de93e5 100755 --- a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py +++ b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py @@ -8,13 +8,14 @@ # import unittest from testutils import * -import sys,os +import sys, os 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): @@ -27,7 +28,7 @@ class psram_1bank_4mux_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=64, num_banks=1) @@ -45,9 +46,9 @@ class psram_1bank_4mux_1rw_1r_test(openram_test): 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() diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py index 6adb3e8e..8887ca5b 100755 --- a/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py @@ -8,25 +8,26 @@ # import unittest from testutils import * -import sys,os +import sys, os 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_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - + OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=32, num_spare_cols=3, @@ -48,7 +49,7 @@ class sram_1bank_2mux_1rw_1r_spare_cols_test(openram_test): 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() 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 0a2b7d32..bac2fd5f 100755 --- a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py @@ -8,25 +8,26 @@ # import unittest from testutils import * -import sys,os +import sys, os 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): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - + OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=32, num_banks=1) @@ -46,7 +47,7 @@ class sram_1bank_2mux_1rw_1r_test(openram_test): 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() diff --git a/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py index 987a297e..83c8cc75 100755 --- a/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py @@ -8,26 +8,26 @@ # import unittest from testutils import * -import sys,os +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_1w_1r_spare_cols_test, odd supply routing error") + class sram_1bank_2mux_1w_1r_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - + OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=32, num_spare_cols=3, @@ -47,9 +47,9 @@ class sram_1bank_2mux_1w_1r_spare_cols_test(openram_test): 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() 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 2c4e29e6..53dc4a8d 100755 --- a/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py @@ -8,26 +8,26 @@ # import unittest from testutils import * -import sys,os +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_psram_1bank_2mux_1w_1r_test, odd supply routing error") + class psram_1bank_2mux_1w_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - + OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=32, num_banks=1) @@ -45,9 +45,9 @@ class psram_1bank_2mux_1w_1r_test(openram_test): 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() diff --git a/compiler/tests/20_sram_1bank_2mux_global_test.py b/compiler/tests/20_sram_1bank_2mux_global_test.py index 4020dff9..93caeb86 100755 --- a/compiler/tests/20_sram_1bank_2mux_global_test.py +++ b/compiler/tests/20_sram_1bank_2mux_global_test.py @@ -16,7 +16,6 @@ from sram_factory import factory import debug -#@unittest.skip("SKIPPING 20_sram_1bank_4mux_test") class sram_1bank_2mux_global_test(openram_test): def runTest(self): @@ -44,7 +43,7 @@ class sram_1bank_2mux_global_test(openram_test): 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() diff --git a/compiler/tests/20_sram_1bank_2mux_test.py b/compiler/tests/20_sram_1bank_2mux_test.py index 582f5217..2ce4704b 100755 --- a/compiler/tests/20_sram_1bank_2mux_test.py +++ b/compiler/tests/20_sram_1bank_2mux_test.py @@ -8,14 +8,14 @@ # import unittest from testutils import * -import sys,os +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_test") + class sram_1bank_2mux_test(openram_test): def runTest(self): @@ -39,9 +39,9 @@ class sram_1bank_2mux_test(openram_test): 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() diff --git a/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py index 0488b93e..6a513288 100755 --- a/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py @@ -9,14 +9,13 @@ 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_spare_cols_test") + class sram_1bank_2mux_wmask_spare_cols_test(openram_test): def runTest(self): diff --git a/compiler/tests/20_sram_1bank_2mux_wmask_test.py b/compiler/tests/20_sram_1bank_2mux_wmask_test.py index 50bd41dc..fc7f2a8d 100755 --- a/compiler/tests/20_sram_1bank_2mux_wmask_test.py +++ b/compiler/tests/20_sram_1bank_2mux_wmask_test.py @@ -9,7 +9,6 @@ import unittest from testutils import * import sys, os - sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS @@ -17,7 +16,6 @@ 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): diff --git a/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py b/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py index 05cb7c0d..7717242f 100755 --- a/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py +++ b/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py @@ -9,7 +9,6 @@ import unittest from testutils import * import sys, os - sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS diff --git a/compiler/tests/20_sram_1bank_4mux_test.py b/compiler/tests/20_sram_1bank_4mux_test.py index e38ef7c4..f4791105 100755 --- a/compiler/tests/20_sram_1bank_4mux_test.py +++ b/compiler/tests/20_sram_1bank_4mux_test.py @@ -8,14 +8,14 @@ # import unittest from testutils import * -import sys,os +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_4mux_test") + class sram_1bank_4mux_test(openram_test): def runTest(self): @@ -41,7 +41,7 @@ class sram_1bank_4mux_test(openram_test): 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() 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 1e4df34d..167602d7 100755 --- a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py @@ -8,13 +8,14 @@ # import unittest from testutils import * -import sys,os +import sys, os 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): @@ -26,7 +27,7 @@ class sram_1bank_8mux_1rw_1r_test(openram_test): OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + c = sram_config(word_size=2, num_words=128, num_banks=1) @@ -46,7 +47,7 @@ class sram_1bank_8mux_1rw_1r_test(openram_test): 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() diff --git a/compiler/tests/20_sram_1bank_8mux_test.py b/compiler/tests/20_sram_1bank_8mux_test.py index dfdcd5b7..5d65e02a 100755 --- a/compiler/tests/20_sram_1bank_8mux_test.py +++ b/compiler/tests/20_sram_1bank_8mux_test.py @@ -8,14 +8,14 @@ # import unittest from testutils import * -import sys,os +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_8mux_test") + class sram_1bank_8mux_test(openram_test): def runTest(self): @@ -41,7 +41,7 @@ class sram_1bank_8mux_test(openram_test): 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() diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py index dbeca8aa..4175f01d 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py @@ -8,25 +8,26 @@ # import unittest from testutils import * -import sys,os +import sys, os 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_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - + OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=16, num_spare_cols=4, @@ -48,7 +49,7 @@ class sram_1bank_nomux_1rw_1r_spare_cols_test(openram_test): 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() 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 a516b4f0..81b13c0e 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py @@ -8,25 +8,26 @@ # import unittest from testutils import * -import sys,os +import sys, os 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): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - + OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 globals.setup_bitcell() - + c = sram_config(word_size=4, num_words=16, num_banks=1) @@ -46,7 +47,7 @@ class sram_1bank_nomux_1rw_1r_test(openram_test): 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() diff --git a/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py index 135518e3..410fedea 100755 --- a/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py @@ -9,7 +9,6 @@ import unittest from testutils import * import sys, os - sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS @@ -17,7 +16,6 @@ from sram_factory import factory import debug -# @unittest.skip("SKIPPING 20_sram_1bank_nomux_spare_cols_test") class sram_1bank_nomux_spare_cols_test(openram_test): def runTest(self): diff --git a/compiler/tests/20_sram_1bank_nomux_test.py b/compiler/tests/20_sram_1bank_nomux_test.py index c79d8552..524172bd 100755 --- a/compiler/tests/20_sram_1bank_nomux_test.py +++ b/compiler/tests/20_sram_1bank_nomux_test.py @@ -8,14 +8,14 @@ # import unittest from testutils import * -import sys,os +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_test") + class sram_1bank_nomux_test(openram_test): def runTest(self): @@ -41,7 +41,7 @@ class sram_1bank_nomux_test(openram_test): 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() diff --git a/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py b/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py index e22122bd..4d2e2c4a 100755 --- a/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py @@ -9,7 +9,6 @@ import unittest from testutils import * import sys, os - sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS diff --git a/compiler/tests/20_sram_1bank_nomux_wmask_test.py b/compiler/tests/20_sram_1bank_nomux_wmask_test.py index 24d7c97d..01282521 100755 --- a/compiler/tests/20_sram_1bank_nomux_wmask_test.py +++ b/compiler/tests/20_sram_1bank_nomux_wmask_test.py @@ -9,7 +9,6 @@ import unittest from testutils import * import sys, os - sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS @@ -17,7 +16,6 @@ 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): diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index 5fd4bf29..a6cb3e1a 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -8,13 +8,14 @@ # import unittest from testutils import * -import sys,os +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("Multibank is not working yet.") class sram_2bank_test(openram_test): @@ -91,7 +92,7 @@ class sram_2bank_test(openram_test): 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() diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index a602f9d2..779118cf 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -23,7 +23,7 @@ class timing_sram_test(openram_test): OPTS.spice_name="hspice" OPTS.analytical_delay = False OPTS.netlist_only = True - + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer @@ -44,7 +44,7 @@ class timing_sram_test(openram_test): s = factory.create(module_type="sram", sram_config=c) #import sys #sys.exit(1) - + tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) @@ -96,9 +96,9 @@ class timing_sram_test(openram_test): self.assertTrue(len(data.keys())==len(golden_data.keys())) self.assertTrue(self.check_golden_data(data,golden_data,0.25)) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/21_hspice_setuphold_test.py b/compiler/tests/21_hspice_setuphold_test.py index 1844fd39..f07b70f1 100755 --- a/compiler/tests/21_hspice_setuphold_test.py +++ b/compiler/tests/21_hspice_setuphold_test.py @@ -23,7 +23,7 @@ class timing_setup_test(openram_test): OPTS.spice_name="hspice" OPTS.analytical_delay = False OPTS.netlist_only = True - + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer @@ -32,7 +32,7 @@ class timing_setup_test(openram_test): import sram import tech slews = [tech.spice["rise_time"]*2] - + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) sh = setup_hold(corner) data = sh.analyze(slews,slews) @@ -56,7 +56,7 @@ class timing_setup_test(openram_test): self.assertTrue(self.check_golden_data(data,golden_data,0.25)) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/21_model_delay_test.py b/compiler/tests/21_model_delay_test.py index e21d658e..a570c3ed 100755 --- a/compiler/tests/21_model_delay_test.py +++ b/compiler/tests/21_model_delay_test.py @@ -18,13 +18,13 @@ 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): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) 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 @@ -52,35 +52,35 @@ class model_delay_test(openram_test): 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() diff --git a/compiler/tests/21_ngspice_delay_extra_rows_test.py b/compiler/tests/21_ngspice_delay_extra_rows_test.py index 22812d52..43b64154 100755 --- a/compiler/tests/21_ngspice_delay_extra_rows_test.py +++ b/compiler/tests/21_ngspice_delay_extra_rows_test.py @@ -90,7 +90,7 @@ class timing_sram_test(openram_test): # Check if no too many or too few results self.assertTrue(len(data.keys())==len(golden_data.keys())) - + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) globals.end_openram() diff --git a/compiler/tests/21_ngspice_delay_global_test.py b/compiler/tests/21_ngspice_delay_global_test.py index ac463883..d4cbfafa 100755 --- a/compiler/tests/21_ngspice_delay_global_test.py +++ b/compiler/tests/21_ngspice_delay_global_test.py @@ -97,7 +97,7 @@ class timing_sram_test(openram_test): # Check if no too many or too few results self.assertTrue(len(data.keys())==len(golden_data.keys())) - + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) globals.end_openram() diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 92b8eac0..81388ccd 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -89,7 +89,7 @@ class timing_sram_test(openram_test): # Check if no too many or too few results self.assertTrue(len(data.keys())==len(golden_data.keys())) - + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) globals.end_openram() diff --git a/compiler/tests/21_ngspice_setuphold_test.py b/compiler/tests/21_ngspice_setuphold_test.py index 246bdff9..84ce882b 100755 --- a/compiler/tests/21_ngspice_setuphold_test.py +++ b/compiler/tests/21_ngspice_setuphold_test.py @@ -23,7 +23,7 @@ class timing_setup_test(openram_test): OPTS.spice_name="ngspice" OPTS.analytical_delay = False OPTS.netlist_only = True - + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer @@ -32,7 +32,7 @@ class timing_setup_test(openram_test): import sram import tech slews = [tech.spice["rise_time"]*2] - + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) sh = setup_hold(corner) data = sh.analyze(slews,slews) @@ -54,7 +54,7 @@ class timing_setup_test(openram_test): self.assertTrue(len(data.keys())==len(golden_data.keys())) self.assertTrue(self.check_golden_data(data,golden_data,0.25)) - + reload(characterizer) globals.end_openram() diff --git a/compiler/tests/22_psram_1bank_2mux_func_test.py b/compiler/tests/22_psram_1bank_2mux_func_test.py index 5e4040d6..3876e69b 100755 --- a/compiler/tests/22_psram_1bank_2mux_func_test.py +++ b/compiler/tests/22_psram_1bank_2mux_func_test.py @@ -32,7 +32,7 @@ class psram_1bank_2mux_func_test(openram_test): OPTS.num_rw_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 from importlib import reload import characterizer @@ -55,14 +55,14 @@ class psram_1bank_2mux_func_test(openram_test): 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() diff --git a/compiler/tests/22_psram_1bank_4mux_func_test.py b/compiler/tests/22_psram_1bank_4mux_func_test.py index 2c51f2f3..5f745114 100755 --- a/compiler/tests/22_psram_1bank_4mux_func_test.py +++ b/compiler/tests/22_psram_1bank_4mux_func_test.py @@ -33,7 +33,7 @@ class psram_1bank_4mux_func_test(openram_test): OPTS.num_rw_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 from importlib import reload import characterizer @@ -56,14 +56,14 @@ class psram_1bank_4mux_func_test(openram_test): 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() diff --git a/compiler/tests/22_psram_1bank_8mux_func_test.py b/compiler/tests/22_psram_1bank_8mux_func_test.py index 447b7512..345e50e0 100755 --- a/compiler/tests/22_psram_1bank_8mux_func_test.py +++ b/compiler/tests/22_psram_1bank_8mux_func_test.py @@ -33,7 +33,7 @@ class psram_1bank_8mux_func_test(openram_test): OPTS.num_rw_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 from importlib import reload import characterizer @@ -56,14 +56,14 @@ class psram_1bank_8mux_func_test(openram_test): 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() diff --git a/compiler/tests/22_psram_1bank_nomux_func_test.py b/compiler/tests/22_psram_1bank_nomux_func_test.py index 5f2e81c8..3fe522ea 100755 --- a/compiler/tests/22_psram_1bank_nomux_func_test.py +++ b/compiler/tests/22_psram_1bank_nomux_func_test.py @@ -32,7 +32,7 @@ class psram_1bank_nomux_func_test(openram_test): OPTS.num_rw_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 from importlib import reload import characterizer @@ -55,14 +55,14 @@ class psram_1bank_nomux_func_test(openram_test): 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() diff --git a/compiler/tests/22_sram_1bank_2mux_func_test.py b/compiler/tests/22_sram_1bank_2mux_func_test.py index 20ac52f0..3c48787f 100755 --- a/compiler/tests/22_sram_1bank_2mux_func_test.py +++ b/compiler/tests/22_sram_1bank_2mux_func_test.py @@ -25,7 +25,7 @@ class sram_1bank_2mux_func_test(openram_test): 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 @@ -45,14 +45,14 @@ class sram_1bank_2mux_func_test(openram_test): 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() diff --git a/compiler/tests/22_sram_1bank_2mux_global_func_test.py b/compiler/tests/22_sram_1bank_2mux_global_func_test.py index e5b8853a..e012bf23 100755 --- a/compiler/tests/22_sram_1bank_2mux_global_func_test.py +++ b/compiler/tests/22_sram_1bank_2mux_global_func_test.py @@ -25,7 +25,7 @@ class sram_1bank_2mux_func_test(openram_test): 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 @@ -47,14 +47,14 @@ class sram_1bank_2mux_func_test(openram_test): 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() diff --git a/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py b/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py index a09ab17e..1ce68a54 100755 --- a/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py +++ b/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py @@ -25,7 +25,7 @@ class sram_1bank_2mux_sparecols_func_test(openram_test): 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 @@ -47,14 +47,14 @@ class sram_1bank_2mux_sparecols_func_test(openram_test): 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() diff --git a/compiler/tests/22_sram_1bank_4mux_func_test.py b/compiler/tests/22_sram_1bank_4mux_func_test.py index baa25906..ee7795c0 100755 --- a/compiler/tests/22_sram_1bank_4mux_func_test.py +++ b/compiler/tests/22_sram_1bank_4mux_func_test.py @@ -25,7 +25,7 @@ class sram_1bank_4mux_func_test(openram_test): 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 @@ -45,14 +45,14 @@ class sram_1bank_4mux_func_test(openram_test): 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() diff --git a/compiler/tests/22_sram_1bank_8mux_func_test.py b/compiler/tests/22_sram_1bank_8mux_func_test.py index d9cd794a..7600b7c0 100755 --- a/compiler/tests/22_sram_1bank_8mux_func_test.py +++ b/compiler/tests/22_sram_1bank_8mux_func_test.py @@ -25,7 +25,7 @@ class sram_1bank_8mux_func_test(openram_test): 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 @@ -48,14 +48,14 @@ class sram_1bank_8mux_func_test(openram_test): 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() diff --git a/compiler/tests/22_sram_1bank_nomux_1rw_1r_func_test.py b/compiler/tests/22_sram_1bank_nomux_1rw_1r_func_test.py index 50906e62..513f74b2 100755 --- a/compiler/tests/22_sram_1bank_nomux_1rw_1r_func_test.py +++ b/compiler/tests/22_sram_1bank_nomux_1rw_1r_func_test.py @@ -15,7 +15,7 @@ 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): @@ -29,7 +29,7 @@ class psram_1bank_nomux_func_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 globals.setup_bitcell() - + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer @@ -49,14 +49,14 @@ class psram_1bank_nomux_func_test(openram_test): 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() - + # instantiate a copy of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/22_sram_1bank_nomux_func_test.py b/compiler/tests/22_sram_1bank_nomux_func_test.py index 0af55ec2..27dfc4e5 100755 --- a/compiler/tests/22_sram_1bank_nomux_func_test.py +++ b/compiler/tests/22_sram_1bank_nomux_func_test.py @@ -24,7 +24,7 @@ class sram_1bank_nomux_func_test(openram_test): globals.init_openram(config_file) 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 @@ -44,14 +44,14 @@ class sram_1bank_nomux_func_test(openram_test): 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() diff --git a/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py b/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py index dbb9d7b2..0df39226 100755 --- a/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py +++ b/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py @@ -24,7 +24,7 @@ class sram_1bank_nomux_sparecols_func_test(openram_test): globals.init_openram(config_file) 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 @@ -45,14 +45,14 @@ class sram_1bank_nomux_sparecols_func_test(openram_test): 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() diff --git a/compiler/tests/22_sram_1bank_wmask_1rw_1r_func_test.py b/compiler/tests/22_sram_1bank_wmask_1rw_1r_func_test.py index 7ab35824..806c7b46 100755 --- a/compiler/tests/22_sram_1bank_wmask_1rw_1r_func_test.py +++ b/compiler/tests/22_sram_1bank_wmask_1rw_1r_func_test.py @@ -30,7 +30,7 @@ class sram_wmask_1w_1r_func_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 globals.setup_bitcell() - + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer diff --git a/compiler/tests/22_sram_wmask_func_test.py b/compiler/tests/22_sram_wmask_func_test.py index b59c3b95..dd3ac8d7 100755 --- a/compiler/tests/22_sram_wmask_func_test.py +++ b/compiler/tests/22_sram_wmask_func_test.py @@ -25,7 +25,7 @@ class sram_wmask_func_test(openram_test): 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 @@ -54,7 +54,7 @@ class sram_wmask_func_test(openram_test): self.assertTrue(fail, error) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/23_lib_sram_model_corners_test.py b/compiler/tests/23_lib_sram_model_corners_test.py index 84de2b3f..2b5d9234 100755 --- a/compiler/tests/23_lib_sram_model_corners_test.py +++ b/compiler/tests/23_lib_sram_model_corners_test.py @@ -48,7 +48,7 @@ class lib_model_corners_lib_test(openram_test): OPTS.process_corners = ["TT", "SS", "FF"] OPTS.supply_voltages = [1.0] OPTS.temperatures = [25] - + lib(out_dir=OPTS.openram_temp, sram=s.s, sp_file=tempspice, use_model=True) # get all of the .lib files generated @@ -62,7 +62,7 @@ class lib_model_corners_lib_test(openram_test): libname = "{0}/{1}".format(OPTS.openram_temp,filename) golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname) self.assertTrue(self.isapproxdiff(libname,golden,0.15)) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index 1ea8fc9e..c502b892 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -22,7 +22,7 @@ class lib_sram_model_test(openram_test): globals.init_openram(config_file) OPTS.nominal_corner_only = False OPTS.netlist_only = True - + from characterizer import lib from sram import sram from sram_config import sram_config @@ -52,7 +52,7 @@ class lib_sram_model_test(openram_test): libname = "{0}/{1}".format(OPTS.openram_temp,filename) golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname) self.assertTrue(self.isapproxdiff(libname,golden,0.15)) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index e9c727c6..6021147c 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -64,7 +64,7 @@ class lib_sram_prune_test(openram_test): reload(characterizer) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index 91f410c3..b370db8f 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -62,7 +62,7 @@ class lib_test(openram_test): reload(characterizer) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index 120974ff..0d3be92a 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -44,7 +44,7 @@ class verilog_test(openram_test): self.assertTrue(self.isdiff(vname,golden)) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/26_hspice_pex_pinv_test.py b/compiler/tests/26_hspice_pex_pinv_test.py index 96b2d108..660469b1 100755 --- a/compiler/tests/26_hspice_pex_pinv_test.py +++ b/compiler/tests/26_hspice_pex_pinv_test.py @@ -36,7 +36,7 @@ class hspice_pex_pinv_test(openram_test): # generate the pinv prev_purge_value = OPTS.purge_temp # force set purge to false to save the sp file - OPTS.purge_temp = False + OPTS.purge_temp = False debug.info(2, "Checking 1x size inverter") tx = pinv.pinv(name="pinv", size=1) tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name) diff --git a/compiler/tests/26_ngspice_pex_pinv_test.py b/compiler/tests/26_ngspice_pex_pinv_test.py index e5dbe5db..67413bae 100755 --- a/compiler/tests/26_ngspice_pex_pinv_test.py +++ b/compiler/tests/26_ngspice_pex_pinv_test.py @@ -50,7 +50,7 @@ class ngspice_pex_pinv_test(openram_test): # now generate its pex file pex_file = self.run_pex(tx) - # restore the old purge value + # restore the old purge value OPTS.purge_temp = prev_purge_value # generate simulation for pex, make sure the simulation is successful pex_delay = self.simulate_delay(test_module=pex_file, diff --git a/compiler/tests/26_sram_pex_test.py b/compiler/tests/26_sram_pex_test.py index 5a3253de..9ba50148 100755 --- a/compiler/tests/26_sram_pex_test.py +++ b/compiler/tests/26_sram_pex_test.py @@ -24,7 +24,7 @@ class sram_pex_test(openram_test): globals.init_openram(config_file) OPTS.analytical_delay = False OPTS.use_pex = True - + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer @@ -43,14 +43,14 @@ class sram_pex_test(openram_test): c.num_banks)) s = factory.create(module_type="sram", sram_config=c) tempspice = self.run_pex(s) - + 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() diff --git a/compiler/tests/30_openram_back_end_test.py b/compiler/tests/30_openram_back_end_test.py index 591d03ac..85d52750 100755 --- a/compiler/tests/30_openram_back_end_test.py +++ b/compiler/tests/30_openram_back_end_test.py @@ -45,7 +45,7 @@ class openram_back_end_test(openram_test): 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") @@ -61,7 +61,7 @@ class openram_back_end_test(openram_test): out_path) debug.info(1, cmd) os.system(cmd) - + # assert an error until we actually check a resul for extension in ["gds", "v", "lef", "sp"]: filename = "{0}/{1}.{2}".format(out_path, out_file, extension) @@ -72,12 +72,12 @@ class openram_back_end_test(openram_test): 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() @@ -90,7 +90,7 @@ class openram_back_end_test(openram_test): 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 diff --git a/compiler/tests/30_openram_front_end_test.py b/compiler/tests/30_openram_front_end_test.py index 127df6c2..9a412ada 100755 --- a/compiler/tests/30_openram_front_end_test.py +++ b/compiler/tests/30_openram_front_end_test.py @@ -45,7 +45,7 @@ class openram_front_end_test(openram_test): 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") @@ -61,7 +61,7 @@ class openram_front_end_test(openram_test): 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) @@ -72,12 +72,12 @@ class openram_front_end_test(openram_test): 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() diff --git a/compiler/tests/50_riscv_func_test.py b/compiler/tests/50_riscv_func_test.py index 0d9825e6..26af9106 100755 --- a/compiler/tests/50_riscv_func_test.py +++ b/compiler/tests/50_riscv_func_test.py @@ -30,7 +30,7 @@ class riscv_func_test(openram_test): OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 globals.setup_bitcell() - + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer @@ -51,14 +51,14 @@ class riscv_func_test(openram_test): 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() - + # instantiate a copy of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/50_riscv_phys_test.py b/compiler/tests/50_riscv_phys_test.py index 724131dd..5f3d7d59 100755 --- a/compiler/tests/50_riscv_phys_test.py +++ b/compiler/tests/50_riscv_phys_test.py @@ -23,7 +23,7 @@ class riscv_phys_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - + OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 @@ -31,7 +31,7 @@ class riscv_phys_test(openram_test): globals.setup_bitcell() OPTS.route_supplies = False OPTS.perimeter_pins = False - + c = sram_config(word_size=32, write_size=8, num_words=256, @@ -50,9 +50,9 @@ class riscv_phys_test(openram_test): c.num_banks)) a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) - + globals.end_openram() - + # instantiate a copy of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 9188a4f9..0ab7b8a8 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -50,7 +50,7 @@ class openram_test(unittest.TestCase): # We can still run LVS even if DRC fails in Magic OR Calibre lvs_result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification) - + # Only allow DRC to fail and LVS to pass if we are using magic if lvs_result == 0 and drc_result != 0: # import shutil @@ -64,7 +64,7 @@ class openram_test(unittest.TestCase): # debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) # shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("DRC failed: {}".format(a.name)) - + if lvs_result != 0: # import shutil # zip_file = "/tmp/{0}_{1}".format(a.name, os.getpid()) @@ -82,7 +82,7 @@ class openram_test(unittest.TestCase): 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) - + a.gds_write(tempgds) import verify @@ -300,7 +300,7 @@ class openram_test(unittest.TestCase): def dbg(): import pdb; pdb.set_trace() - + def header(filename, technology): # Skip the header for gitlab regression import getpass @@ -318,14 +318,14 @@ def header(filename, technology): print("|=========" + OPTS.openram_temp.center(60) + "=========|") print("|==============================================================================|") - + def debugTestRunner(post_mortem=None): """unittest runner doing post mortem debugging on failing tests""" import pdb import traceback 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() @@ -333,7 +333,7 @@ def debugTestRunner(post_mortem=None): 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: diff --git a/compiler/verify/__init__.py b/compiler/verify/__init__.py index 4d9eb151..4bcf93cc 100644 --- a/compiler/verify/__init__.py +++ b/compiler/verify/__init__.py @@ -6,7 +6,7 @@ # All rights reserved. # """ -This is a module that will import the correct DRC/LVS/PEX +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 to enable multiple verification tool support. @@ -77,4 +77,4 @@ if OPTS.tech_name == "sky130": from .magic import filter_gds else: debug.warning("Did not find Magic.") - + diff --git a/compiler/verify/assura.py b/compiler/verify/assura.py index d1d6146f..439f833d 100644 --- a/compiler/verify/assura.py +++ b/compiler/verify/assura.py @@ -43,7 +43,7 @@ def run_drc(name, gds_name, final_verification=False): global num_drc_runs num_drc_runs += 1 - + from tech import drc drc_rules = drc["drc_rules"] drc_runset = OPTS.openram_temp + name + ".rsf" @@ -106,7 +106,7 @@ def run_lvs(name, gds_name, sp_name, final_verification=False): global num_lvs_runs num_lvs_runs += 1 - + from tech import drc lvs_rules = drc["lvs_rules"] lvs_runset = OPTS.openram_temp + name + ".rsf" @@ -192,7 +192,7 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False): global num_pex_runs num_pex_runs += 1 - + def print_drc_stats(): debug.info(1,"DRC runs: {0}".format(num_drc_runs)) def print_lvs_stats(): diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index d51e71b6..6970b271 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -202,7 +202,7 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False): if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)): shutil.copy(gds_name, OPTS.openram_temp + os.path.basename(gds_name)) - + (outfile, errfile, resultsfile) = run_script(cell_name, "drc") # check the result for these lines in the summary: @@ -335,7 +335,7 @@ def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False) shutil.copy(gds_name, OPTS.openram_temp) if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)): shutil.copy(sp_name, OPTS.openram_temp) - + (outfile, errfile, resultsfile) = run_script(cell_name, "pex") diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index ece741cd..6a559b3d 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -43,7 +43,7 @@ def filter_gds(cell_name, input_gds, output_gds): shutil.copy(magic_file, OPTS.openram_temp) else: debug.warning("Could not locate .magicrc file: {}".format(magic_file)) - + run_file = OPTS.openram_temp + "run_filter.sh" f = open(run_file, "w") @@ -65,7 +65,7 @@ def filter_gds(cell_name, input_gds, output_gds): (outfile, errfile, resultsfile) = run_script(cell_name, "filter") - + def write_magic_script(cell_name, extract=False, final_verification=False): """ Write a magic script to perform DRC and optionally extraction. """ @@ -488,14 +488,14 @@ def correct_port(name, output_file_name, ref_file_name): 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(): debug.info(1, "LVS runs: {0}".format(num_lvs_runs)) - + def print_pex_stats(): debug.info(1, "PEX runs: {0}".format(num_pex_runs)) diff --git a/technology/freepdk45/__init__.py b/technology/freepdk45/__init__.py index 363df6f4..7e6943b4 100644 --- a/technology/freepdk45/__init__.py +++ b/technology/freepdk45/__init__.py @@ -20,10 +20,10 @@ TECHNOLOGY = "freepdk45" # FreePDK45 paths PDK_DIR=os.path.abspath(os.environ.get("FREEPDK45")) -os.environ["PDK_DIR"] = PDK_DIR -os.environ["SYSTEM_CDS_LIB_DIR"] = "{0}/ncsu_basekit/cdssetup".format(PDK_DIR) -os.environ["CDS_SITE"] = PDK_DIR -os.environ["MGC_TMPDIR"] = "/tmp" +os.environ["PDK_DIR"] = PDK_DIR +os.environ["SYSTEM_CDS_LIB_DIR"] = "{0}/ncsu_basekit/cdssetup".format(PDK_DIR) +os.environ["CDS_SITE"] = PDK_DIR +os.environ["MGC_TMPDIR"] = "/tmp" ########################### #OpenRAM Paths diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 7aeb3e21..0feb1cd9 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -45,7 +45,7 @@ layer_properties = layer_properties() ################################################### 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 diff --git a/technology/scn3me_subm/__init__.py b/technology/scn3me_subm/__init__.py index 6b08d0b9..e80abc5d 100644 --- a/technology/scn3me_subm/__init__.py +++ b/technology/scn3me_subm/__init__.py @@ -20,14 +20,14 @@ TECHNOLOGY = "scn3me_subm" # CDK paths # os.environ["CDK_DIR"] = CDK_DIR #PDK path -# os.environ["SYSTEM_CDS_LIB_DIR"] = "{0}/cdssetup".format(CDK_DIR) -# os.environ["CDS_SITE"] = CDK_DIR -os.environ["MGC_TMPDIR"] = "/tmp" +# os.environ["SYSTEM_CDS_LIB_DIR"] = "{0}/cdssetup".format(CDK_DIR) +# os.environ["CDS_SITE"] = CDK_DIR +os.environ["MGC_TMPDIR"] = "/tmp" ########################### # OpenRAM Paths - + try: DRCLVS_HOME = os.path.abspath(os.environ.get("DRCLVS_HOME")) except: diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index 9e6f370b..0bd12162 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -33,7 +33,7 @@ GDS={} #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) +GDS["unit"]=(0.001,1e-6) # default label zoom GDS["zoom"] = 0.5 @@ -43,7 +43,7 @@ GDS["zoom"] = 0.5 ################################################### # create the GDS layer map -layer={} +layer={} layer["vtg"] = (-1, 0) layer["vth"] = (-1, 0) layer["contact"] = (47, 0) @@ -75,7 +75,7 @@ _lambda_ = 0.3 #technology parameter parameter={} parameter["min_tx_size"] = 4*_lambda_ -parameter["beta"] = 2 +parameter["beta"] = 2 parameter["6T_inv_nmos_size"] = 8*_lambda_ parameter["6T_inv_pmos_size"] = 3*_lambda_ @@ -97,29 +97,29 @@ drc["drc_rules"]=drclvs_home+"/calibreDRC_scn3me_subm.rul" drc["lvs_rules"]=drclvs_home+"/calibreLVS_scn3me_subm.rul" drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/scn3me_subm/layers.map" - + # minwidth_tx with contact (no dog bone transistors) drc["minwidth_tx"] = 4*_lambda_ drc["minlength_channel"] = 2*_lambda_ -# 1.3 Minimum spacing between wells of same type (if both are drawn) +# 1.3 Minimum spacing between wells of same type (if both are drawn) drc["well_to_well"] = 6*_lambda_ -# 1.4 Minimum spacing between wells of different type (if both are drawn) +# 1.4 Minimum spacing between wells of different type (if both are drawn) drc["pwell_to_nwell"] = 0 -# 1.1 Minimum width +# 1.1 Minimum width drc["minwidth_well"] = 12*_lambda_ -# 3.1 Minimum width +# 3.1 Minimum width drc["minwidth_poly"] = 2*_lambda_ # 3.2 Minimum spacing over active drc["poly_to_poly"] = 3*_lambda_ -# 3.3 Minimum gate extension of active +# 3.3 Minimum gate extension of active drc["poly_extend_active"] = 2*_lambda_ # 5.5.b Minimum spacing between poly contact and other poly (alternative rules) drc["poly_to_polycontact"] = 4*_lambda_ # ?? drc["active_enclosure_gate"] = 0.0 -# 3.5 Minimum field poly to active +# 3.5 Minimum field poly to active drc["poly_to_active"] = _lambda_ # 3.2.a Minimum spacing over field poly drc["poly_to_field_poly"] = 3*_lambda_ @@ -128,22 +128,22 @@ drc["minarea_poly"] = 0.0 # ?? drc["active_to_body_active"] = 4*_lambda_ # Fix me -# 2.1 Minimum width +# 2.1 Minimum width drc["minwidth_active"] = 3*_lambda_ # 2.2 Minimum spacing drc["active_to_active"] = 3*_lambda_ -# 2.3 Source/drain active to well edge +# 2.3 Source/drain active to well edge drc["well_enclosure_active"] = 6*_lambda_ # Reserved for asymmetric enclosures drc["well_extend_active"] = 6*_lambda_ # Not a rule drc["minarea_active"] = 0.0 -# 4.1 Minimum select spacing to channel of transistor to ensure adequate source/drain width +# 4.1 Minimum select spacing to channel of transistor to ensure adequate source/drain width drc["implant_to_channel"] = 3*_lambda_ # 4.2 Minimum select overlap of active drc["implant_enclosure_active"] = 2*_lambda_ -# 4.3 Minimum select overlap of contact +# 4.3 Minimum select overlap of contact drc["implant_enclosure_contact"] = _lambda_ # Not a rule drc["implant_to_contact"] = 0 @@ -155,12 +155,12 @@ drc["minwidth_implant"] = 0 # 6.1 Exact contact size drc["minwidth_contact"] = 2*_lambda_ # 5.3 Minimum contact spacing -drc["contact_to_contact"] = 3*_lambda_ -# 6.2.b Minimum active overlap +drc["contact_to_contact"] = 3*_lambda_ +# 6.2.b Minimum active overlap drc["active_enclosure_contact"] = _lambda_ # Reserved for asymmetric enclosure drc["active_extend_contact"] = _lambda_ -# 5.2.b Minimum poly overlap +# 5.2.b Minimum poly overlap drc["poly_enclosure_contact"] = _lambda_ # Reserved for asymmetric enclosures drc["poly_extend_contact"] = _lambda_ @@ -168,32 +168,32 @@ drc["poly_extend_contact"] = _lambda_ drc["contact_to_gate"] = 2*_lambda_ # 5.4 Minimum spacing to gate of transistor drc["contact_to_poly"] = 2*_lambda_ - -# 7.1 Minimum width + +# 7.1 Minimum width drc["minwidth_metal1"] = 3*_lambda_ -# 7.2 Minimum spacing +# 7.2 Minimum spacing drc["metal1_to_metal1"] = 3*_lambda_ -# 7.3 Minimum overlap of any contact +# 7.3 Minimum overlap of any contact drc["metal1_enclosure_contact"] = _lambda_ # Reserved for asymmetric enclosure drc["metal1_extend_contact"] = _lambda_ -# 8.3 Minimum overlap by metal1 -drc["metal1_enclosure_via1"] = _lambda_ +# 8.3 Minimum overlap by metal1 +drc["metal1_enclosure_via1"] = _lambda_ # Reserve for asymmetric enclosures drc["metal1_extend_via1"] = _lambda_ # Not a rule drc["minarea_metal1"] = 0 -# 8.1 Exact size +# 8.1 Exact size drc["minwidth_via1"] = 2*_lambda_ -# 8.2 Minimum via1 spacing +# 8.2 Minimum via1 spacing drc["via1_to_via1"] = 3*_lambda_ # 9.1 Minimum width drc["minwidth_metal2"] = 3*_lambda_ -# 9.2 Minimum spacing +# 9.2 Minimum spacing drc["metal2_to_metal2"] = 3*_lambda_ -# 9.3 Minimum overlap of via1 +# 9.3 Minimum overlap of via1 drc["metal2_extend_via1"] = _lambda_ # Reserved for asymmetric enclosures drc["metal2_enclosure_via1"] = _lambda_ @@ -237,14 +237,14 @@ SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR") 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"], + "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"], "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"] = 10 # estimated feasible period in ns @@ -278,7 +278,7 @@ 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["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 diff --git a/technology/scn4m_subm/__init__.py b/technology/scn4m_subm/__init__.py index 5fbd8c08..bebe9702 100644 --- a/technology/scn4m_subm/__init__.py +++ b/technology/scn4m_subm/__init__.py @@ -20,14 +20,14 @@ TECHNOLOGY = "scn4m_subm" # CDK paths # os.environ["CDK_DIR"] = CDK_DIR #PDK path -# os.environ["SYSTEM_CDS_LIB_DIR"] = "{0}/cdssetup".format(CDK_DIR) -# os.environ["CDS_SITE"] = CDK_DIR -os.environ["MGC_TMPDIR"] = "/tmp" +# os.environ["SYSTEM_CDS_LIB_DIR"] = "{0}/cdssetup".format(CDK_DIR) +# os.environ["CDS_SITE"] = CDK_DIR +os.environ["MGC_TMPDIR"] = "/tmp" ########################### # OpenRAM Paths - + try: DRCLVS_HOME = os.path.abspath(os.environ.get("DRCLVS_HOME")) except: diff --git a/technology/scn4m_subm/tech/setup.tcl b/technology/scn4m_subm/tech/setup.tcl index 40de5ac1..968c5c01 100644 --- a/technology/scn4m_subm/tech/setup.tcl +++ b/technology/scn4m_subm/tech/setup.tcl @@ -9,9 +9,13 @@ flatten class {-circuit1 dummy_cell_1w_1r} flatten class {-circuit1 dummy_pbitcell} flatten class {-circuit1 dummy_pbitcell_0} flatten class {-circuit1 dummy_pbitcell_1} +flatten class {-circuit1 dummy_pbitcell_2} +flatten class {-circuit1 dummy_pbitcell_3} flatten class {-circuit1 pbitcell} flatten class {-circuit1 pbitcell_0} flatten class {-circuit1 pbitcell_1} +flatten class {-circuit1 pbitcell_2} +flatten class {-circuit1 pbitcell_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/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index f9a99a86..0bea4035 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -52,7 +52,7 @@ GDS={} #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) +GDS["unit"]=(0.001,1e-6) # default label zoom GDS["zoom"] = 0.5 @@ -129,7 +129,7 @@ _lambda_ = 0.2 #technology parameter parameter={} parameter["min_tx_size"] = 4*_lambda_ -parameter["beta"] = 2 +parameter["beta"] = 2 # These 6T sizes are used in the parameterized bitcell. parameter["6T_inv_nmos_size"] = 8*_lambda_ @@ -147,7 +147,7 @@ drc["grid"]=0.5*_lambda_ 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) drc["minwidth_tx"] = 4*_lambda_ drc["minlength_channel"] = 2*_lambda_ @@ -163,29 +163,29 @@ drc.add_layer("pwell", width = 12*_lambda_, spacing = 6*_lambda_) -# 3.1 Minimum width +# 3.1 Minimum width # 3.2 Minimum spacing over active drc.add_layer("poly", width = 2*_lambda_, spacing = 3*_lambda_) -# 3.3 Minimum gate extension of active +# 3.3 Minimum gate extension of active drc["poly_extend_active"] = 2*_lambda_ # 5.5.b Minimum spacing between poly contact and other poly (alternative rules) drc["poly_to_contact"] = 4*_lambda_ # ?? drc["active_enclose_gate"] = 0.0 -# 3.5 Minimum field poly to active +# 3.5 Minimum field poly to active drc["poly_to_active"] = _lambda_ # 3.2.a Minimum spacing over field poly drc["poly_to_field_poly"] = 3*_lambda_ -# 2.1 Minimum width +# 2.1 Minimum width # 2.2 Minimum spacing drc.add_layer("active", width = 3*_lambda_, spacing = 4*_lambda_) -# 2.3 Source/drain active to well edge +# 2.3 Source/drain active to well edge drc.add_enclosure("nwell", layer = "active", enclosure = 6*_lambda_) @@ -193,13 +193,13 @@ drc.add_enclosure("pwell", layer = "active", enclosure = 6*_lambda_) -# 4.1 Minimum select spacing to channel of transistor to ensure adequate source/drain width +# 4.1 Minimum select spacing to channel of transistor to ensure adequate source/drain width drc["implant_to_channel"] = 3*_lambda_ # 4.2 Minimum select overlap of active drc.add_enclosure("implant", layer = "active", enclosure = 2*_lambda_) -# 4.3 Minimum select overlap of contact +# 4.3 Minimum select overlap of contact drc.add_enclosure("implant", layer = "contact", enclosure = _lambda_) @@ -232,7 +232,7 @@ drc["poly_contact_to_gate"] = 2*_lambda_ drc.add_layer("poly_contact", width = 2*_lambda_, spacing = 3*_lambda_) -# 5.2.b Minimum poly overlap +# 5.2.b Minimum poly overlap drc.add_enclosure("poly", layer = "poly_contact", enclosure = _lambda_) @@ -241,12 +241,12 @@ drc["poly_contact_to_gate"] = 2*_lambda_ # 5.4 Minimum spacing to gate of transistor drc["poly_contact_to_poly"] = 2*_lambda_ -# 7.1 Minimum width -# 7.2 Minimum spacing +# 7.1 Minimum width +# 7.2 Minimum spacing drc.add_layer("m1", width = 3*_lambda_, spacing = 3*_lambda_) -# 7.3 Minimum overlap of any contact +# 7.3 Minimum overlap of any contact drc.add_enclosure("m1", layer = "poly_contact", enclosure = _lambda_) @@ -258,18 +258,18 @@ drc.add_enclosure("m1", layer = "via1", enclosure = _lambda_) -# 8.1 Exact size -# 8.2 Minimum via1 spacing +# 8.1 Exact size +# 8.2 Minimum via1 spacing drc.add_layer("via1", width = 2*_lambda_, spacing = 3*_lambda_) # 9.1 Minimum width -# 9.2 Minimum spacing +# 9.2 Minimum spacing drc.add_layer("m2", width = 3*_lambda_, spacing = 3*_lambda_) -# 9.3 Minimum overlap of via1 +# 9.3 Minimum overlap of via1 drc.add_enclosure("m2", layer = "via1", enclosure = _lambda_) @@ -337,7 +337,7 @@ spice["fet_models"] = {"TT": [SPICE_MODEL_DIR + "/nom/pmos.sp", SPICE_MODEL_DIR "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"] = 10 # estimated feasible period in ns @@ -371,7 +371,7 @@ 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"] = 18.17 #In pico-seconds. +parameter["le_tau"] = 18.17 #In pico-seconds. parameter["min_inv_para_delay"] = 2.07 #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