From 6743049d44989aa4807e4a1659e00df1e13ba36c Mon Sep 17 00:00:00 2001 From: FriedrichWu Date: Thu, 5 Sep 2024 10:08:59 +0200 Subject: [PATCH] clean version --- compiler/base/channel_route.py | 31 +- compiler/base/channel_route_new.py | 427 ----- compiler/modules/sram_1bank.py | 53 +- compiler/modules/sram_new.py | 1494 ----------------- compiler/router/signal_escape_router.py | 71 +- .../router/signal_escape_router_change.py | 311 ---- compiler/router/supply_router.py | 2 - compiler/sram.py | 64 +- compiler/sram_new_test.py | 338 ---- sram_compiler.py | 9 +- 10 files changed, 171 insertions(+), 2629 deletions(-) delete mode 100644 compiler/base/channel_route_new.py delete mode 100644 compiler/modules/sram_new.py delete mode 100644 compiler/router/signal_escape_router_change.py delete mode 100644 compiler/sram_new_test.py diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index 5eb4da63..9b4c545c 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -5,6 +5,8 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # +# This version aims to keep the track out/vertical to the dff pins +# Now only consider the channel at the south, but works fine with channel at the north import collections from openram import debug from openram.tech import drc @@ -85,7 +87,8 @@ class channel_route(design): layer_stack, directions=None, vertical=False, - parent=None): + parent=None, + dff_area=False): """ The net list is a list of the nets with each net being a list of pins to be connected. The offset is the lower-left of where the @@ -106,6 +109,7 @@ class channel_route(design): self.vertical = vertical # For debugging... self.parent = parent + self.dff_area = dff_area # this is a special value to handle dff areas, should be true when routing col_dff/dffs if not directions or directions == "pref": # Use the preferred layer directions @@ -139,7 +143,17 @@ class channel_route(design): layer_stuff = self.get_layer_pitch(self.horizontal_layer) (self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff - + # For debug + + debug.warning("layer horizontal: {0}".format(self.horizontal_layer)) + debug.warning("horizontal_nonpref_pitch: {0}".format(self.horizontal_nonpref_pitch)) + debug.warning("horizontal_pitch: {0}".format(self.horizontal_pitch)) + debug.warning("horizontal_space: {0}".format(self.horizontal_space)) + debug.warning("layer vertical: {0}".format(self.vertical_layer)) + debug.warning("vertiacl_nonpref_pitch: {0}".format(self.vertical_pitch)) + debug.warning("vertical_pitch: {0}".format(self.vertical_pitch)) + debug.warning("vertical_space: {0}".format(self.vertical_space)) + self.route() def remove_net_from_graph(self, pin, g): @@ -219,8 +233,19 @@ class channel_route(design): real_channel_offset = vector(self.offset.x, min(self.min_value, self.offset.y)) else: real_channel_offset = vector(min(self.min_value, self.offset.x), self.offset.y) - current_offset = real_channel_offset + + if self.dff_area == False: + current_offset = real_channel_offset + else: # special handle for dff area + current_offset = vector(real_channel_offset.x, real_channel_offset.y + 5) # make route out of dffs area + if self.layer_stack == self.m2_stack: + self.vertical_nonpref_pitch = self.horizontal_pitch + + if self.layer_stack == self.m1_stack: + current_offset = vector(real_channel_offset.x, current_offset.y + 14) # make sure no overlap between col_dffs & data_dffs + if real_channel_offset.y > 0: # which means this is channnel router for coldff at the top + current_offset = real_channel_offset # no offset to avoid overlap problem at the top # Sort nets by left edge value nets.sort() while len(nets) > 0: diff --git a/compiler/base/channel_route_new.py b/compiler/base/channel_route_new.py deleted file mode 100644 index a1178bd1..00000000 --- a/compiler/base/channel_route_new.py +++ /dev/null @@ -1,427 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2024 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -# This version aims to keep the track out/vertical to the dff pins -# Now only consider the channel at the south -import collections -from openram import debug -from openram.tech import drc -from .vector import vector -from .design import design - - -class channel_net(): - def __init__(self, net_name, pins, vertical): - 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) - self.max_value = max(i.uy() for i in pins) - 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 - overlap if any pin overlaps. - """ - - for pin1 in self.pins: - for pin2 in other.pins: - if self.pin_overlap(pin1, pin2, pitch): - return True - - return False - - def segment_overlap(self, other): - """ - Check if the horizontal span of the two nets overlaps eachother. - """ - 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): - - unique_id = 0 - - def __init__(self, - netlist, - offset, - layer_stack, - directions=None, - vertical=False, - parent=None): - """ - The net list is a list of the nets with each net being a list of pins - to be connected. The offset is the lower-left of where the - routing channel will start. This does NOT try to minimize the - number of tracks -- instead, it picks an order to avoid the - vertical conflicts between pins. The track size must be the number of - nets times the *nonpreferred* routing of the non-track layer pitch. - - """ - 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 - self.directions = directions - 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": - self.vertical_layer = layer_stack[0] - self.horizontal_layer = layer_stack[2] - else: - self.vertical_layer = layer_stack[2] - self.horizontal_layer = layer_stack[0] - elif directions == "nonpref": - # Use the preferred layer directions - if self.get_preferred_direction(layer_stack[0]) == "V": - self.vertical_layer = layer_stack[2] - self.horizontal_layer = layer_stack[0] - else: - self.vertical_layer = layer_stack[0] - self.horizontal_layer = layer_stack[2] - else: - # Use the layer directions specified to the router rather than - # the preferred directions - debug.check(directions[0] != directions[1], "Must have unique layer directions.") - if directions[0] == "V": - self.vertical_layer = layer_stack[0] - self.horizontal_layer = layer_stack[2] - else: - self.horizontal_layer = layer_stack[0] - self.vertical_layer = layer_stack[2] - - layer_stuff = self.get_layer_pitch(self.vertical_layer) - (self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff - - layer_stuff = self.get_layer_pitch(self.horizontal_layer) - (self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff - # For debug - - debug.warning("layer horizontal: {0}".format(self.horizontal_layer)) - debug.warning("horizontal_nonpref_pitch: {0}".format(self.horizontal_nonpref_pitch)) - debug.warning("horizontal_pitch: {0}".format(self.horizontal_pitch)) - debug.warning("horizontal_space: {0}".format(self.horizontal_space)) - debug.warning("layer vertical: {0}".format(self.vertical_layer)) - debug.warning("vertiacl_nonpref_pitch: {0}".format(self.vertical_pitch)) - debug.warning("vertical_pitch: {0}".format(self.vertical_pitch)) - debug.warning("vertical_space: {0}".format(self.vertical_space)) - - self.route() - - def remove_net_from_graph(self, pin, g): - """ - Remove the pin from the graph and all conflicts - """ - g.pop(pin, None) - - # Remove the pin from all conflicts - # FIXME: This is O(n^2), so maybe optimize it. - for other_pin, conflicts in g.items(): - if pin in conflicts: - g[other_pin].remove(pin) - return g - - def route(self): - # Create names for the nets for the graphs - nets = [] - index = 0 - # print(self.netlist) - for pin_list in self.netlist: - nets.append(channel_net("n{}".format(index), pin_list, self.vertical)) - index += 1 - - # Create the (undirected) horizontal constraint graph - hcg = collections.OrderedDict() - for net1 in nets: - for net2 in nets: - if net1.name == net2.name: - continue - if net1.segment_overlap(net2): - try: - hcg[net1.name].add(net2.name) - except KeyError: - hcg[net1.name] = set([net2.name]) - try: - hcg[net2.name].add(net1.name) - except KeyError: - hcg[net2.name] = set([net1.name]) - - - # Initialize the vertical conflict graph (vcg) - # and make a list of all pins - vcg = collections.OrderedDict() - - # print("Nets:") - # for net_name in nets: - # print(net_name, [x.name for x in nets[net_name]]) - - # Find the vertical pin conflicts - # FIXME: O(n^2) but who cares for now - if self.vertical: - pitch = self.horizontal_nonpref_pitch - else: - pitch = self.vertical_nonpref_pitch - - for net in nets: - vcg[net.name] = set() - - for net1 in nets: - for net2 in nets: - # 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 - self.min_value = min([n.min_value for n in nets]) - self.max_value = min([n.max_value for n in nets]) - if self.vertical: - real_channel_offset = vector(self.offset.x, min(self.min_value, self.offset.y)) - else: - real_channel_offset = vector(min(self.min_value, self.offset.x), self.offset.y) - current_offset = real_channel_offset - current_offset = vector(real_channel_offset.x, current_offset.y + 5) # make route out of dffs area - - if self.layer_stack == self.m2_stack: - self.vertical_nonpref_pitch = self.horizontal_pitch - - if self.layer_stack == self.m1_stack: - current_offset = vector(real_channel_offset.x, current_offset.y + 14) # make sure no overlap between col_dffs & data_dffs - if real_channel_offset.y > 0: # which means this is channnel router for coldff at the top - current_offset = real_channel_offset # no offset to avoid overlap problem at the top - # Sort nets by left edge value - nets.sort() - 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(): - # print(name, net.min_value, net.max_value, net.conflicts) - # print(current_offset) - # get a route from conflict graph with empty fanout set - for net in nets: - # If it has no conflicts and the interval is to the right of the current offset in the track - if net.min_value >= current_offset_value and len(vcg[net.name]) == 0: - # print("Routing {}".format(net.name)) - # Add the trunk routes from the bottom up for - # horizontal or the left to right for vertical - if self.vertical: - self.add_vertical_trunk_route(net.pins, - current_offset, - self.horizontal_pitch) - current_offset = vector(current_offset.x, net.max_value + self.horizontal_nonpref_pitch) - else: - self.add_horizontal_trunk_route(net.pins, - current_offset, - self.vertical_pitch) - current_offset = vector(net.max_value + self.vertical_nonpref_pitch, current_offset.y) - - # 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... - current_offset_value = current_offset.y if self.vertical else current_offset.x - initial_offset_value = real_channel_offset.y if self.vertical else real_channel_offset.x - if current_offset_value == initial_offset_value: - debug.info(0, "Channel offset: {}".format(real_channel_offset)) - debug.info(0, "Current offset: {}".format(current_offset)) - debug.info(0, "VCG {}".format(str(vcg))) - debug.info(0, "HCG {}".format(str(hcg))) - for net in nets: - debug.info(0, "{0} pin: {1}".format(net.name, str(net.pins))) - if self.parent: - debug.info(0, "Saving vcg.gds") - self.parent.gds_write("vcg.gds") - debug.error("Cyclic VCG in channel router.", -1) - - # Increment the track and reset the offset to the start (like a typewriter) - if self.vertical: - 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 - self.height = self.max_value + self.vertical_nonpref_pitch - self.offset.y - 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: - # FIXME: Using non-pref pitch here due to overlap bug in VCG constraints. - # It should just result in inefficient channel width but will work. - pitch = getattr(self, "{}_pitch".format(layer)) - nonpref_pitch = getattr(self, "{}_nonpref_pitch".format(layer)) - space = getattr(self, "{}_space".format(layer)) - except AttributeError: - debug.error("Cannot find layer pitch.", -1) - return (nonpref_pitch, pitch, pitch - space, space) - - def add_horizontal_trunk_route(self, - pins, - trunk_offset, - pitch): - """ - Create a trunk route for all pins with - the trunk located at the given y offset. - """ - 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! - self.add_path(self.vertical_layer, - [vector(min_x - half_layer_width, trunk_offset.y), - vector(max_x + half_layer_width, trunk_offset.y)]) - - # Route each pin to the trunk - for pin in pins: - if pin.cy() < trunk_offset.y: - 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]) - else: - # Add the horizontal trunk - self.add_path(self.horizontal_layer, - [vector(min_x, trunk_offset.y), - vector(max_x, trunk_offset.y)]) - - # Route each pin to the trunk - for pin in pins: - # Find the correct side of the pin - if pin.cy() < trunk_offset.y: - pin_pos = pin.uc() - else: - pin_pos = pin.bc() - mid = vector(pin_pos.x, trunk_offset.y) - self.add_path(self.vertical_layer, [pin_pos, mid]) - if not non_preferred_route: - self.add_via_center(layers=self.layer_stack, - offset=mid, - directions=self.directions) - self.add_via_stack_center(from_layer=pin.layer, - to_layer=self.vertical_layer, - offset=pin_pos) - - def add_vertical_trunk_route(self, - pins, - trunk_offset, - pitch): - """ - Create a trunk route for all pins with the - trunk located at the given x offset. - """ - 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! - self.add_path(self.horizontal_layer, - [vector(trunk_offset.x, min_y - half_layer_width), - vector(trunk_offset.x, max_y + half_layer_width)]) - - # Route each pin to the trunk - for pin in pins: - # Find the correct side of the pin - if pin.cx() < trunk_offset.x: - pin_pos = pin.rc() - else: - pin_pos = pin.lc() - # No bend needed here - mid = vector(trunk_offset.x, pin_pos.y) - self.add_path(self.horizontal_layer, [pin_pos, mid]) - else: - # Add the vertical trunk - self.add_path(self.vertical_layer, - [vector(trunk_offset.x, min_y), - vector(trunk_offset.x, max_y)]) - - # Route each pin to the trunk - for pin in pins: - # Find the correct side of the pin - if pin.cx() < trunk_offset.x: - pin_pos = pin.rc() - else: - pin_pos = pin.lc() - mid = vector(trunk_offset.x, pin_pos.y) - self.add_path(self.horizontal_layer, [pin_pos, mid]) - if not non_preferred_route: - self.add_via_center(layers=self.layer_stack, - offset=mid, - directions=self.directions) - self.add_via_stack_center(from_layer=pin.layer, - to_layer=self.horizontal_layer, - offset=pin_pos) - diff --git a/compiler/modules/sram_1bank.py b/compiler/modules/sram_1bank.py index 7d1aec3f..1d76e07c 100644 --- a/compiler/modules/sram_1bank.py +++ b/compiler/modules/sram_1bank.py @@ -5,6 +5,7 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # +# This aim to change the position of the submodule import datetime from math import ceil from importlib import import_module, reload @@ -205,16 +206,16 @@ class sram_1bank(design, verilog, lef): if not OPTS.is_unit_test: print_time("Submodules", datetime.datetime.now(), start_time) - - def create_layout(self): + + def create_layout(self, position_add=0, mod=0): """ Layout creation """ start_time = datetime.datetime.now() - self.place_instances() + self.place_instances_changeable(position_add=position_add) if not OPTS.is_unit_test: print_time("Placement", datetime.datetime.now(), start_time) start_time = datetime.datetime.now() - self.route_layout() + self.route_layout(mod=mod) if not OPTS.is_unit_test: print_time("Routing", datetime.datetime.now(), start_time) @@ -238,7 +239,7 @@ class sram_1bank(design, verilog, lef): # Only run this if not a unit test, because unit test will also verify it. self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc) print_time("Verification", datetime.datetime.now(), start_time) - + def create_modules(self): debug.error("Must override pure virtual function.", -1) @@ -318,7 +319,7 @@ class sram_1bank(design, verilog, lef): # Grid is left with many top level pins pass - def route_escape_pins(self, bbox=None): + def route_escape_pins(self, bbox=None, mod=0): """ Add the top-level pins for a single bank SRAM with control. """ @@ -360,11 +361,16 @@ class sram_1bank(design, verilog, lef): else: for bit in range(self.num_spare_cols): pins_to_route.append("spare_wen{0}[{1}]".format(port, bit)) - + from openram.router import signal_escape_router as router + # mod Use for control which edge/position the pins(dout) will be placed + # 0 -> default + # 1 -> all top/bottom + # 2 -> all left/right rtr = router(layers=self.m3_stack, bbox=bbox, - design=self) + design=self, + mod=mod) rtr.route(pins_to_route) def compute_bus_sizes(self): @@ -778,11 +784,11 @@ class sram_1bank(design, verilog, lef): self.spare_wen_dff_insts = self.create_spare_wen_dff() else: self.num_spare_cols = 0 - - def place_instances(self): + + def place_instances_changeable(self, position_add=0): """ This places the instances for a single bank SRAM with control - logic and up to 2 ports. + logic and up to 2 ports, but be able to change the io the dff position """ # No orientation or offset @@ -843,7 +849,11 @@ class sram_1bank(design, verilog, lef): self.add_layout_pins(add_vias=False) self.route_dffs(add_routes=False) self.remove_layout_pins() - + + for port in self.all_ports: + # Add the extra position + self.data_bus_size[port] += position_add + # Re-place with the new channel size self.place_dffs() @@ -1051,7 +1061,7 @@ class sram_1bank(design, verilog, lef): "spare_wen{0}[{1}]".format(port, bit), start_layer=pin_layer) - def route_layout(self): + def route_layout(self, mod=0): """ Route a single bank SRAM """ self.route_clk() @@ -1075,7 +1085,8 @@ class sram_1bank(design, verilog, lef): if OPTS.perimeter_pins: # We now route the escape routes far enough out so that they will # reach past the power ring or stripes on the sides - self.route_escape_pins(init_bbox) + self.route_escape_pins(bbox=init_bbox, mod=mod) + if OPTS.route_supplies: self.route_supplies(init_bbox) @@ -1118,7 +1129,8 @@ class sram_1bank(design, verilog, lef): cr = channel_route(netlist=route_map, offset=offset, layer_stack=layer_stack, - parent=self) + parent=self, + dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs # This causes problem in magic since it sometimes cannot extract connectivity of instances # with no active devices. self.add_inst(cr.name, cr) @@ -1130,7 +1142,8 @@ class sram_1bank(design, verilog, lef): cr = channel_route(netlist=route_map, offset=offset, layer_stack=layer_stack, - parent=self) + parent=self, + dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs # This causes problem in magic since it sometimes cannot extract connectivity of instances # with no active devices. self.add_inst(cr.name, cr) @@ -1167,7 +1180,7 @@ class sram_1bank(design, verilog, lef): if len(route_map) > 0: # This layer stack must be different than the column addr dff layer stack - layer_stack = self.m3_stack + layer_stack = self.m2_stack if port == 0: # This is relative to the bank at 0,0 or the s_en which is routed on M3 also if "s_en" in self.control_logic_insts[port].mod.pin_map: @@ -1181,7 +1194,8 @@ class sram_1bank(design, verilog, lef): cr = channel_route(netlist=route_map, offset=offset, layer_stack=layer_stack, - parent=self) + parent=self, + dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs if add_routes: # This causes problem in magic since it sometimes cannot extract connectivity of instances # with no active devices. @@ -1201,7 +1215,8 @@ class sram_1bank(design, verilog, lef): cr = channel_route(netlist=route_map, offset=offset, layer_stack=layer_stack, - parent=self) + parent=self, + dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs if add_routes: # This causes problem in magic since it sometimes cannot extract connectivity of instances # with no active devices. diff --git a/compiler/modules/sram_new.py b/compiler/modules/sram_new.py deleted file mode 100644 index 2454dcb6..00000000 --- a/compiler/modules/sram_new.py +++ /dev/null @@ -1,1494 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2024 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -# This aim to change the position of the submodule -import datetime -from math import ceil -from importlib import import_module, reload -from openram import debug -from openram.base import vector -#from openram.base import channel_route -from openram.base.channel_route_new import channel_route as channel_route -from openram.base import design -from openram.base import verilog -from openram.base import lef -from openram.sram_factory import factory -from openram.tech import spice -from openram import OPTS, print_time - - -class sram_1bank(design, verilog, lef): - """ - Procedures specific to a one bank SRAM. - """ - def __init__(self, name, sram_config): - 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) - - self.bank_insts = [] - - if self.write_size != self.word_size: - 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 - - try: - from openram.tech import power_grid - self.supply_stack = power_grid - except ImportError: - # if no power_grid is specified by tech we use sensible defaults - # Route a M3/M4 grid - self.supply_stack = self.m3_stack - - # delay control logic does not have RBLs - self.has_rbl = OPTS.control_logic != "control_logic_delay" - - def add_pins(self): - """ Add pins for entire SRAM. """ - - 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.bank_addr_size): - self.add_pin("addr{0}[{1}]".format(port, bit), "INPUT") - - # These are used to create the physical pins - self.control_logic_inputs = [] - self.control_logic_outputs = [] - for port in self.all_ports: - if port in self.readwrite_ports: - self.control_logic_inputs.append(self.control_logic_rw.get_inputs()) - self.control_logic_outputs.append(self.control_logic_rw.get_outputs()) - elif port in self.write_ports: - self.control_logic_inputs.append(self.control_logic_w.get_inputs()) - self.control_logic_outputs.append(self.control_logic_w.get_outputs()) - 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: - self.add_pin("web{}".format(port), "INPUT") - for port in self.all_ports: - self.add_pin("clk{}".format(port), "INPUT") - # add the optional write mask pins - for port in self.write_ports: - for bit in range(self.num_wmasks): - self.add_pin("wmask{0}[{1}]".format(port, bit), "INPUT") - if self.num_spare_cols == 1: - self.add_pin("spare_wen{0}".format(port), "INPUT") - else: - for bit in range(self.num_spare_cols): - self.add_pin("spare_wen{0}[{1}]".format(port, bit), "INPUT") - 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") - - # Standard supply and ground names - try: - self.vdd_name = spice["power"] - except KeyError: - self.vdd_name = "vdd" - try: - self.gnd_name = spice["ground"] - except KeyError: - self.gnd_name = "gnd" - - self.add_pin(self.vdd_name, "POWER") - self.add_pin(self.gnd_name, "GROUND") - self.ext_supplies = [self.vdd_name, self.gnd_name] - self.ext_supply = {"vdd" : self.vdd_name, "gnd" : self.gnd_name} - - def add_global_pex_labels(self): - """ - Add pex labels at the sram level for spice analysis - """ - - - - # add pex labels for bitcells - for bank_num in range(len(self.bank_insts)): - bank = self.bank_insts[bank_num] - pex_data = bank.reverse_transformation_bitcell(self.bitcell.name) - - bank_offset = pex_data[0] # offset bank relative to sram - Q_offset = pex_data[1] # offset of storage relative to bank - Q_bar_offset = pex_data[2] # offset of storage relative to bank - bl_offsets = pex_data[3] - 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]] - Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], - bank_offset[cell][1] + Q_bar_offset[cell][1]] - OPTS.words_per_row = self.words_per_row - row = int(cell % (OPTS.num_words / self.words_per_row)) - col = int(cell / (OPTS.num_words)) - self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num, - row, - col), - storage_layer_name, - Q) - self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num, - row, - col), - storage_layer_name, - Q_bar) - - for cell in range(len(bl_offsets)): - col = bl_meta[cell][0][2] - for bitline in range(len(bl_offsets[cell])): - bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0], - float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]] - bl.append([bitline_location, bl_meta[cell][bitline][3], col]) - - for cell in range(len(br_offsets)): - col = br_meta[cell][0][2] - for bitline in range(len(br_offsets[cell])): - bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0], - float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]] - br.append([bitline_location, br_meta[cell][bitline][3], col]) - - for i in range(len(bl)): - self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]), - bitline_layer_name, bl[i][0]) - - for i in range(len(br)): - self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]), - bitline_layer_name, br[i][0]) - - # add pex labels for control logic - for i in range(len(self.control_logic_insts)): - instance = self.control_logic_insts[i] - control_logic_offset = instance.offset - for output in instance.mod.output_list: - pin = instance.mod.get_pin(output) - pin.transform([0, 0], instance.mirror, instance.rotate) - offset = [control_logic_offset[0] + pin.center()[0], - control_logic_offset[1] + pin.center()[1]] - self.add_layout_pin_rect_center("{0}{1}".format(pin.name, i), - storage_layer_name, - offset) - - 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_recrusive(self, position_add=0, mod=0): - """ Layout creation """ - start_time = datetime.datetime.now() - self.place_instances_changeable(position_add=position_add) - if not OPTS.is_unit_test: - print_time("Placement", datetime.datetime.now(), start_time) - - start_time = datetime.datetime.now() - - if position_add == 0: - change = False - else: - change = True - self.route_layout(change=change, mod=mod) - - if not OPTS.is_unit_test: - print_time("Routing", datetime.datetime.now(), start_time) - - self.add_lvs_correspondence_points() - - self.offset_all_coordinates() - - highest_coord = self.find_highest_coords() - self.width = highest_coord[0] - self.height = highest_coord[1] - if OPTS.use_pex and OPTS.pex_exe[0] != "calibre": - debug.info(2, "adding global pex labels") - self.add_global_pex_labels() - self.add_boundary(ll=vector(0, 0), - ur=vector(self.width, self.height)) - - start_time = datetime.datetime.now() - if not OPTS.is_unit_test: - # We only enable final verification if we have routed the design - # Only run this if not a unit test, because unit test will also verify it. - self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc) - print_time("Verification", datetime.datetime.now(), start_time) - - def create_layout(self): - """ Layout creation """ - start_time = datetime.datetime.now() - self.place_instances() - if not OPTS.is_unit_test: - print_time("Placement", datetime.datetime.now(), start_time) - - start_time = datetime.datetime.now() - self.route_layout() - - if not OPTS.is_unit_test: - print_time("Routing", datetime.datetime.now(), start_time) - - self.add_lvs_correspondence_points() - - self.offset_all_coordinates() - - highest_coord = self.find_highest_coords() - self.width = highest_coord[0] - self.height = highest_coord[1] - if OPTS.use_pex and OPTS.pex_exe[0] != "calibre": - debug.info(2, "adding global pex labels") - self.add_global_pex_labels() - self.add_boundary(ll=vector(0, 0), - ur=vector(self.width, self.height)) - - start_time = datetime.datetime.now() - if not OPTS.is_unit_test: - # We only enable final verification if we have routed the design - # Only run this if not a unit test, because unit test will also verify it. - self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc) - print_time("Verification", datetime.datetime.now(), start_time) - - def create_modules(self): - debug.error("Must override pure virtual function.", -1) - - def route_supplies(self, bbox=None): - """ Route the supply grid and connect the pins to them. """ - - # Copy the pins to the top level - # This will either be used to route or left unconnected. - for pin_name in ["vdd", "gnd"]: - for inst in self.insts: - self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name]) - - from openram.router import supply_router as router - rtr = router(layers=self.supply_stack, - design=self, - bbox=bbox, - pin_type=OPTS.supply_pin_type) - rtr.route() - - if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]: - # Find the lowest leftest pin for vdd and gnd - for pin_name in ["vdd", "gnd"]: - # Copy the pin shape(s) to rectangles - for pin in self.get_pins(pin_name): - self.add_rect(pin.layer, - pin.ll(), - pin.width(), - pin.height()) - - # Remove the pin shape(s) - self.remove_layout_pin(pin_name) - - # Get new pins - pins = rtr.get_new_pins(pin_name) - for pin in pins: - self.add_layout_pin(self.ext_supply[pin_name], - pin.layer, - pin.ll(), - pin.width(), - pin.height()) - - elif OPTS.supply_pin_type == "single": - # Update these as we may have routed outside the region (perimeter pins) - lowest_coord = self.find_lowest_coords() - - # Find the lowest leftest pin for vdd and gnd - for pin_name in ["vdd", "gnd"]: - # Copy the pin shape(s) to rectangles - for pin in self.get_pins(pin_name): - self.add_rect(pin.layer, - pin.ll(), - pin.width(), - pin.height()) - - # Remove the pin shape(s) - self.remove_layout_pin(pin_name) - - # Get the lowest, leftest pin - pin = rtr.get_ll_pin(pin_name) - - pin_width = 2 * getattr(self, "{}_width".format(pin.layer)) - - # Add it as an IO pin to the perimeter - route_width = pin.rx() - lowest_coord.x - pin_offset = vector(lowest_coord.x, pin.by()) - self.add_rect(pin.layer, - pin_offset, - route_width, - pin.height()) - - self.add_layout_pin(self.ext_supply[pin_name], - pin.layer, - pin_offset, - pin_width, - pin.height()) - else: - # Grid is left with many top level pins - pass - - def route_escape_pins(self, bbox=None, change=False, mod=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - - # List of pin to new pin name - pins_to_route = [] - for port in self.all_ports: - # Connect the control pins as inputs - for signal in self.control_logic_inputs[port]: - if signal.startswith("rbl"): - continue - if signal=="clk": - pins_to_route.append("{0}{1}".format(signal, port)) - else: - pins_to_route.append("{0}{1}".format(signal, port)) - - if port in self.write_ports: - for bit in range(self.word_size + self.num_spare_cols): - pins_to_route.append("din{0}[{1}]".format(port, bit)) - - if port in self.readwrite_ports or port in self.read_ports: - for bit in range(self.word_size + self.num_spare_cols): - pins_to_route.append("dout{0}[{1}]".format(port, bit)) - - for bit in range(self.col_addr_size): - pins_to_route.append("addr{0}[{1}]".format(port, bit)) - - for bit in range(self.row_addr_size): - pins_to_route.append("addr{0}[{1}]".format(port, bit + self.col_addr_size)) - - if port in self.write_ports: - if self.write_size != self.word_size: - for bit in range(self.num_wmasks): - pins_to_route.append("wmask{0}[{1}]".format(port, bit)) - - if port in self.write_ports: - if self.num_spare_cols == 1: - pins_to_route.append("spare_wen{0}".format(port)) - else: - for bit in range(self.num_spare_cols): - pins_to_route.append("spare_wen{0}[{1}]".format(port, bit)) - - if change == False: - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=bbox, - design=self) - else: - from openram.router.signal_escape_router_change import signal_escape_router_change as router - rtr = router(layers=self.m3_stack, - bbox=bbox, - design=self, - mod=mod) - rtr.route(pins_to_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.bank_addr_size + self.control_size + 1# + log(self.num_banks, 2) + 1 - # data bus size - self.num_horizontal_line = self.word_size - - 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 - # The order of the control signals on the control bus: - self.control_bus_names = [] - for port in self.all_ports: - self.control_bus_names[port] = ["clk_buf{}".format(port)] - wen = "w_en{}".format(port) - sen = "s_en{}".format(port) - pen = "p_en_bar{}".format(port) - if self.port_id[port] == "r": - self.control_bus_names[port].extend([sen, pen]) - elif self.port_id[port] == "w": - self.control_bus_names[port].extend([wen, pen]) - else: - self.control_bus_names[port].extend([sen, wen, pen]) - self.vert_control_bus_positions = self.create_vertical_bus(layer="m2", - pitch=self.m2_pitch, - offset=self.vertical_bus_offset, - names=self.control_bus_names[port], - length=self.vertical_bus_height) - - self.addr_bus_names=["A{0}[{1}]".format(port, i) for i in range(self.bank_addr_size)] - self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2", - pitch=self.m2_pitch, - offset=self.addr_bus_offset, - names=self.addr_bus_names, - length=self.addr_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", - pitch=self.m3_pitch, - offset=self.data_bus_offset, - names=self.data_bus_names, - length=self.data_bus_width) - - # Horizontal control logic bus - # vdd/gnd in bus go along whole SRAM - # FIXME: Fatten these wires? - self.horz_control_bus_positions = self.create_horizontal_bus(layer="m1", - pitch=self.m1_pitch, - offset=self.supply_bus_offset, - names=["vdd"], - length=self.supply_bus_width) - # The gnd rail must not be the entire width since we protrude the right-most vdd rail up for - # the decoder in 4-bank SRAMs - self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1", - pitch=self.m1_pitch, - offset=self.supply_bus_offset + vector(0, self.m1_pitch), - names=["gnd"], - length=self.supply_bus_width)) - self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1", - pitch=self.m1_pitch, - offset=self.control_bus_offset, - names=self.control_bus_names[port], - length=self.control_bus_width)) - - def add_modules(self): - self.bitcell = factory.create(module_type=OPTS.bitcell) - self.dff = factory.create(module_type="dff") - - # Create the bank module (up to four are instantiated) - self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank") - - self.num_spare_cols = self.bank.num_spare_cols - - # Create the address and control flops (but not the clk) - self.row_addr_dff = factory.create("dff_array", module_name="row_addr_dff", rows=self.row_addr_size, columns=1) - - if self.col_addr_size > 0: - self.col_addr_dff = factory.create("dff_array", module_name="col_addr_dff", rows=1, columns=self.col_addr_size) - else: - self.col_addr_dff = None - - self.data_dff = factory.create("dff_array", module_name="data_dff", rows=1, columns=self.word_size + self.num_spare_cols) - - if self.write_size != self.word_size: - self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks) - - 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.bank_count = 0 - - c = reload(import_module("." + OPTS.control_logic, "openram.modules")) - 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, - words_per_row=self.words_per_row, - word_size=self.word_size, - spare_columns=self.num_spare_cols, - sram=self, - port_type="rw") - if len(self.writeonly_ports) > 0: - self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, - words_per_row=self.words_per_row, - word_size=self.word_size, - spare_columns=self.num_spare_cols, - sram=self, - port_type="w") - if len(self.readonly_ports) > 0: - self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, - words_per_row=self.words_per_row, - word_size=self.word_size, - spare_columns=self.num_spare_cols, - sram=self, - port_type="r") - - def create_bank(self, bank_num): - """ Create a bank """ - self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num), - mod=self.bank)) - - temp = [] - for port in self.read_ports: - for bit in range(self.word_size + self.num_spare_cols): - temp.append("dout{0}[{1}]".format(port, bit)) - if self.has_rbl: - for port in self.all_ports: - temp.append("rbl_bl{0}".format(port)) - for port in self.write_ports: - for bit in range(self.word_size + self.num_spare_cols): - temp.append("bank_din{0}_{1}".format(port, bit)) - for port in self.all_ports: - for bit in range(self.bank_addr_size): - temp.append("a{0}_{1}".format(port, bit)) - for port in self.read_ports: - temp.append("s_en{0}".format(port)) - for port in self.all_ports: - temp.append("p_en_bar{0}".format(port)) - for port in self.write_ports: - temp.append("w_en{0}".format(port)) - for bit in range(self.num_wmasks): - temp.append("bank_wmask{0}_{1}".format(port, bit)) - for bit in range(self.num_spare_cols): - temp.append("bank_spare_wen{0}_{1}".format(port, bit)) - for port in self.all_ports: - temp.append("wl_en{0}".format(port)) - temp.extend(self.ext_supplies) - self.connect_inst(temp) - - return self.bank_insts[-1] - - def place_bank(self, bank_inst, position, x_flip, y_flip): - """ Place a bank at the given position with orientations """ - - # x_flip == 1 --> no flip in x_axis - # x_flip == -1 --> flip in x_axis - # y_flip == 1 --> no flip in y_axis - # y_flip == -1 --> flip in y_axis - - # x_flip and y_flip are used for position translation - - if x_flip == -1 and y_flip == -1: - bank_rotation = 180 - else: - bank_rotation = 0 - - if x_flip == y_flip: - bank_mirror = "R0" - elif x_flip == -1: - bank_mirror = "MX" - elif y_flip == -1: - bank_mirror = "MY" - else: - bank_mirror = "R0" - - bank_inst.place(offset=position, - mirror=bank_mirror, - rotate=bank_rotation) - - return bank_inst - - def create_row_addr_dff(self): - """ Add all address flops for the main decoder """ - insts = [] - 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 = [] - for bit in range(self.row_addr_size): - inputs.append("addr{}[{}]".format(port, bit + self.col_addr_size)) - outputs.append("a{}_{}".format(port, bit + self.col_addr_size)) - - self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) - - 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 = [] - for bit in range(self.col_addr_size): - inputs.append("addr{}[{}]".format(port, bit)) - outputs.append("a{}_{}".format(port, bit)) - - self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) - - return insts - - def create_data_dff(self): - """ Add and place all data flops """ - insts = [] - for port in self.all_ports: - if port in self.write_ports: - insts.append(self.add_inst(name="data_dff{}".format(port), - mod=self.data_dff)) - else: - insts.append(None) - continue - - # inputs, outputs/output/bar - inputs = [] - outputs = [] - for bit in range(self.word_size + self.num_spare_cols): - inputs.append("din{}[{}]".format(port, bit)) - outputs.append("bank_din{}_{}".format(port, bit)) - - self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) - - return insts - - def create_wmask_dff(self): - """ Add and place all wmask flops """ - insts = [] - for port in self.all_ports: - if port in self.write_ports: - insts.append(self.add_inst(name="wmask_dff{}".format(port), - mod=self.wmask_dff)) - else: - insts.append(None) - continue - - # inputs, outputs/output/bar - inputs = [] - outputs = [] - for bit in range(self.num_wmasks): - inputs.append("wmask{}[{}]".format(port, bit)) - outputs.append("bank_wmask{}_{}".format(port, bit)) - - self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) - - return insts - - def create_spare_wen_dff(self): - """ Add all spare write enable flops """ - insts = [] - for port in self.all_ports: - if port in self.write_ports: - insts.append(self.add_inst(name="spare_wen_dff{}".format(port), - mod=self.spare_wen_dff)) - else: - insts.append(None) - continue - - # inputs, outputs/output/bar - inputs = [] - outputs = [] - for bit in range(self.num_spare_cols): - inputs.append("spare_wen{}[{}]".format(port, bit)) - outputs.append("bank_spare_wen{}_{}".format(port, bit)) - - self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) - - return insts - - def create_control_logic(self): - """ Add control logic instances """ - - insts = [] - for port in self.all_ports: - if port in self.readwrite_ports: - mod = self.control_logic_rw - elif port in self.write_ports: - mod = self.control_logic_w - else: - mod = self.control_logic_r - - insts.append(self.add_inst(name="control{}".format(port), mod=mod)) - - # Inputs - temp = ["csb{}".format(port)] - if port in self.readwrite_ports: - temp.append("web{}".format(port)) - temp.append("clk{}".format(port)) - if self.has_rbl: - temp.append("rbl_bl{}".format(port)) - - # Outputs - if port in self.read_ports: - temp.append("s_en{}".format(port)) - if port in self.write_ports: - temp.append("w_en{}".format(port)) - temp.append("p_en_bar{}".format(port)) - temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port)] + self.ext_supplies) - self.connect_inst(temp) - - return insts - - def sp_write(self, sp_name, lvs=False, trim=False): - # Write the entire spice of the object to the file - ############################################################ - # Spice circuit - ############################################################ - sp = open(sp_name, 'w') - - sp.write("**************************************************\n") - sp.write("* OpenRAM generated memory.\n") - sp.write("* Words: {}\n".format(self.num_words)) - sp.write("* Data bits: {}\n".format(self.word_size)) - sp.write("* Banks: {}\n".format(self.num_banks)) - sp.write("* Column mux: {}:1\n".format(self.words_per_row)) - sp.write("* Trimmed: {}\n".format(trim)) - sp.write("* LVS: {}\n".format(lvs)) - 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"], - # spice["gnd_name"])) - usedMODS = list() - self.sp_write_file(sp, usedMODS, lvs=lvs, trim=trim) - del usedMODS - sp.close() - - def graph_exclude_bits(self, targ_row, targ_col): - """ - 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() - - def graph_exclude_column_mux(self, column_include_num, port): - """ - Excludes all columns muxes unrelated to the target bit being simulated. - """ - self.bank.graph_exclude_column_mux(column_include_num, port) - - def graph_clear_column_mux(self, port): - """ - Clear mux exclusions to allow different bit tests. - """ - self.bank.graph_clear_column_mux(port) - - def create_modules(self): - """ - This adds the modules for a single bank SRAM with control - logic. - """ - - self.bank_inst=self.create_bank(0) - - self.control_logic_insts = self.create_control_logic() - - self.row_addr_dff_insts = self.create_row_addr_dff() - - if self.col_addr_dff: - self.col_addr_dff_insts = self.create_col_addr_dff() - - if self.write_size != self.word_size: - self.wmask_dff_insts = self.create_wmask_dff() - self.data_dff_insts = self.create_data_dff() - else: - self.data_dff_insts = self.create_data_dff() - - if self.num_spare_cols: - self.spare_wen_dff_insts = self.create_spare_wen_dff() - else: - self.num_spare_cols = 0 - - def place_instances_changeable(self, position_add=0): - """ - This places the instances for a single bank SRAM with control - logic and up to 2 ports, but recrusive edition - """ - - # No orientation or offset - self.place_bank(self.bank_inst, [0, 0], 1, 1) - - # The control logic is placed such that the vertical center (between the delay/RBL and - # the actual control logic is aligned with the vertical center of the bank (between - # the sense amps/column mux and cell array) - # The x-coordinate is placed to allow a single clock wire (plus an extra pitch) - # up to the row address DFFs. - self.control_pos = [None] * len(self.all_ports) - self.row_addr_pos = [None] * len(self.all_ports) - - # DFFs are placd on their own - self.col_addr_pos = [None] * len(self.all_ports) - self.wmask_pos = [None] * len(self.all_ports) - self.spare_wen_pos = [None] * len(self.all_ports) - self.data_pos = [None] * len(self.all_ports) - - # These positions utilize the channel route sizes. - # FIXME: Auto-compute these rather than manual computation. - # If a horizontal channel, they rely on the vertical channel non-preferred (contacted) pitch. - # If a vertical channel, they rely on the horizontal channel non-preferred (contacted) pitch. - # So, m3 non-pref pitch means that this is routed on the m2 layer. - self.data_bus_gap = self.m4_nonpref_pitch * 2 - - # Spare wen are on a separate layer so not included - # Start with 1 track minimum - self.data_bus_size = [1] * len(self.all_ports) - self.col_addr_bus_size = [1] * len(self.all_ports) - for port in self.all_ports: - # The column address wires are routed separately from the data bus and will always be smaller. - # All ports need the col addr flops - self.col_addr_bus_size[port] = self.col_addr_size * self.m4_nonpref_pitch - # Write ports need the data input flops and write mask flops - if port in self.write_ports: - self.data_bus_size[port] += self.num_wmasks + self.word_size - # This is for the din pins that get routed in the same channel - # when we have dout and din together - if port in self.readwrite_ports: - self.data_bus_size[port] += self.word_size - # Convert to length - self.data_bus_size[port] *= self.m4_nonpref_pitch - # Add the gap in unit length - self.data_bus_size[port] += self.data_bus_gap - - # The control and row addr flops are independent of any bus widths. - self.place_control() - self.place_row_addr_dffs() - - # Place with an initial wide channel (from above) - self.place_dffs() - - # Route the channel and set to the new data bus size - # We need to temporarily add some pins for the x offsets - # but we'll remove them so that they have the right y - # offsets after the DFF placement. - self.add_layout_pins(add_vias=False) - self.route_dffs(add_routes=False) - self.remove_layout_pins() - - for port in self.all_ports: - # Add the extra position - self.data_bus_size[port] += position_add - - # Re-place with the new channel size - self.place_dffs() - - def place_instances(self): - """ - This places the instances for a single bank SRAM with control - logic and up to 2 ports. - """ - - # No orientation or offset - self.place_bank(self.bank_inst, [0, 0], 1, 1) - - # The control logic is placed such that the vertical center (between the delay/RBL and - # the actual control logic is aligned with the vertical center of the bank (between - # the sense amps/column mux and cell array) - # The x-coordinate is placed to allow a single clock wire (plus an extra pitch) - # up to the row address DFFs. - self.control_pos = [None] * len(self.all_ports) - self.row_addr_pos = [None] * len(self.all_ports) - - # DFFs are placd on their own - self.col_addr_pos = [None] * len(self.all_ports) - self.wmask_pos = [None] * len(self.all_ports) - self.spare_wen_pos = [None] * len(self.all_ports) - self.data_pos = [None] * len(self.all_ports) - - # These positions utilize the channel route sizes. - # FIXME: Auto-compute these rather than manual computation. - # If a horizontal channel, they rely on the vertical channel non-preferred (contacted) pitch. - # If a vertical channel, they rely on the horizontal channel non-preferred (contacted) pitch. - # So, m3 non-pref pitch means that this is routed on the m2 layer. - self.data_bus_gap = self.m4_nonpref_pitch * 2 - - # Spare wen are on a separate layer so not included - # Start with 1 track minimum - self.data_bus_size = [1] * len(self.all_ports) - self.col_addr_bus_size = [1] * len(self.all_ports) - for port in self.all_ports: - # The column address wires are routed separately from the data bus and will always be smaller. - # All ports need the col addr flops - self.col_addr_bus_size[port] = self.col_addr_size * self.m4_nonpref_pitch - # Write ports need the data input flops and write mask flops - if port in self.write_ports: - self.data_bus_size[port] += self.num_wmasks + self.word_size - # This is for the din pins that get routed in the same channel - # when we have dout and din together - if port in self.readwrite_ports: - self.data_bus_size[port] += self.word_size - # Convert to length - self.data_bus_size[port] *= self.m4_nonpref_pitch - # Add the gap in unit length - self.data_bus_size[port] += self.data_bus_gap - - # The control and row addr flops are independent of any bus widths. - self.place_control() - self.place_row_addr_dffs() - - # Place with an initial wide channel (from above) - self.place_dffs() - - # Route the channel and set to the new data bus size - # We need to temporarily add some pins for the x offsets - # but we'll remove them so that they have the right y - # offsets after the DFF placement. - self.add_layout_pins(add_vias=False) - self.route_dffs(add_routes=False) - self.remove_layout_pins() - - # Re-place with the new channel size - self.place_dffs() - - def place_row_addr_dffs(self): - """ - Must be run after place control logic. - """ - port = 0 - # The row address bits are placed above the control logic aligned on the right. - x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width - # It is above the control logic and the predecoder array - y_offset = max(self.control_logic_insts[port].uy(), self.bank.predecoder_top) - - self.row_addr_pos[port] = vector(x_offset, y_offset) - self.row_addr_dff_insts[port].place(self.row_addr_pos[port]) - - if len(self.all_ports)>1: - port = 1 - # The row address bits are placed above the control logic aligned on the left. - x_offset = self.control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width - # If it can be placed above the predecoder and below the control logic, do it - y_offset = self.bank.predecoder_bottom - self.row_addr_pos[port] = vector(x_offset, y_offset) - self.row_addr_dff_insts[port].place(self.row_addr_pos[port], mirror="XY") - - def place_control(self): - port = 0 - - # This includes 2 M2 pitches for the row addr clock line. - # The delay line is aligned with the bitcell array while the control logic is aligned with the port_data - # using the control_logic_center value. - self.control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch, - self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y) - self.control_logic_insts[port].place(self.control_pos[port]) - if len(self.all_ports) > 1: - port = 1 - # This includes 2 M2 pitches for the row addr clock line - # The delay line is aligned with the bitcell array while the control logic is aligned with the port_data - # using the control_logic_center value. - self.control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch, - self.bank.bank_array_ur.y - + self.control_logic_insts[port].height - - self.control_logic_insts[port].height - + self.control_logic_insts[port].mod.control_logic_center.y) - self.control_logic_insts[port].place(self.control_pos[port], mirror="XY") - - def place_dffs(self): - """ - Place the col addr, data, wmask, and spare data DFFs. - This can be run more than once after we recompute the channel width. - """ - - port = 0 - # Add the col address flops below the bank to the right of the control logic - x_offset = self.control_logic_insts[port].rx() + self.dff.width - # Place it a data bus below the x-axis, but at least as low as the control logic to not block - # the control logic signals - y_offset = min(-self.data_bus_size[port] - self.dff.height, - self.control_logic_insts[port].by()) - if self.col_addr_dff: - self.col_addr_pos[port] = vector(x_offset, - y_offset) - self.col_addr_dff_insts[port].place(self.col_addr_pos[port]) - x_offset = self.col_addr_dff_insts[port].rx() - else: - self.col_addr_pos[port] = vector(x_offset, 0) - - if port in self.write_ports: - if self.write_size != self.word_size: - # Add the write mask flops below the write mask AND array. - self.wmask_pos[port] = vector(x_offset, - y_offset) - self.wmask_dff_insts[port].place(self.wmask_pos[port]) - x_offset = self.wmask_dff_insts[port].rx() - - # Add the data flops below the write mask flops. - self.data_pos[port] = vector(x_offset, - y_offset) - self.data_dff_insts[port].place(self.data_pos[port]) - x_offset = self.data_dff_insts[port].rx() - - # Add spare write enable flops to the right of data flops since the spare columns - # will be on the right - if self.num_spare_cols: - self.spare_wen_pos[port] = vector(x_offset, - y_offset) - self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port]) - x_offset = self.spare_wen_dff_insts[port].rx() - - else: - self.wmask_pos[port] = vector(x_offset, y_offset) - self.data_pos[port] = vector(x_offset, y_offset) - self.spare_wen_pos[port] = vector(x_offset, y_offset) - - if len(self.all_ports) > 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 - # the control logic signals - y_offset = max(self.bank.height + self.data_bus_size[port] + self.dff.height, - self.control_logic_insts[port].uy() - self.dff.height) - if self.col_addr_dff: - self.col_addr_pos[port] = vector(x_offset, - y_offset) - self.col_addr_dff_insts[port].place(self.col_addr_pos[port], mirror="XY") - 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 - if self.num_spare_cols: - self.spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width, - y_offset) - self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port], mirror="MX") - x_offset = self.spare_wen_dff_insts[port].lx() - - if self.write_size != self.word_size: - # Add the write mask flops below the write mask AND array. - self.wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width, - y_offset) - self.wmask_dff_insts[port].place(self.wmask_pos[port], mirror="MX") - x_offset = self.wmask_dff_insts[port].lx() - - # Add the data flops below the write mask flops. - self.data_pos[port] = vector(x_offset - self.data_dff_insts[port].width, - y_offset) - self.data_dff_insts[port].place(self.data_pos[port], mirror="MX") - else: - self.wmask_pos[port] = vector(x_offset, y_offset) - self.data_pos[port] = vector(x_offset, y_offset) - self.spare_wen_pos[port] = vector(x_offset, y_offset) - - def add_layout_pins(self, add_vias=True): - """ - Add the top-level pins for a single bank SRAM with control. - """ - for port in self.all_ports: - # Hack: If we are escape routing, set the pin layer to - # None so that we will start from the pin layer - # Otherwise, set it as the pin layer so that no vias are added. - # Otherwise, when we remove pins to move the dff array dynamically, - # we will leave some remaining vias when the pin locations change. - if add_vias: - pin_layer = None - else: - pin_layer = self.pwr_grid_layers[0] - - # Connect the control pins as inputs - for signal in self.control_logic_inputs[port]: - if signal.startswith("rbl"): - continue - self.add_io_pin(self.control_logic_insts[port], - signal, - signal + "{}".format(port), - start_layer=pin_layer) - - if port in self.write_ports: - for bit in range(self.word_size + self.num_spare_cols): - self.add_io_pin(self.data_dff_insts[port], - "din_{}".format(bit), - "din{0}[{1}]".format(port, bit), - start_layer=pin_layer) - - if port in self.readwrite_ports or port in self.read_ports: - for bit in range(self.word_size + self.num_spare_cols): - self.add_io_pin(self.bank_inst, - "dout{0}_{1}".format(port, bit), - "dout{0}[{1}]".format(port, bit), - start_layer=pin_layer) - - for bit in range(self.col_addr_size): - self.add_io_pin(self.col_addr_dff_insts[port], - "din_{}".format(bit), - "addr{0}[{1}]".format(port, bit), - start_layer=pin_layer) - - for bit in range(self.row_addr_size): - self.add_io_pin(self.row_addr_dff_insts[port], - "din_{}".format(bit), - "addr{0}[{1}]".format(port, bit + self.col_addr_size), - start_layer=pin_layer) - - if port in self.write_ports: - if self.write_size != self.word_size: - for bit in range(self.num_wmasks): - self.add_io_pin(self.wmask_dff_insts[port], - "din_{}".format(bit), - "wmask{0}[{1}]".format(port, bit), - start_layer=pin_layer) - - if port in self.write_ports: - if self.num_spare_cols == 1: - self.add_io_pin(self.spare_wen_dff_insts[port], - "din_{}".format(0), - "spare_wen{0}".format(port), - start_layer=pin_layer) - else: - for bit in range(self.num_spare_cols): - self.add_io_pin(self.spare_wen_dff_insts[port], - "din_{}".format(bit), - "spare_wen{0}[{1}]".format(port, bit), - start_layer=pin_layer) - - def route_layout(self, change=False, mod=0): - """ Route a single bank SRAM """ - - self.route_clk() - - self.route_control_logic() - - self.route_row_addr_dff() - - self.route_dffs() - - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_pins() - - # Some technologies have an isolation - self.add_dnwell(inflate=2.5) - - init_bbox = self.get_bbox() - # Route the supplies together and/or to the ring/stripes. - # Route the pins to the perimeter - if OPTS.perimeter_pins: - # We now route the escape routes far enough out so that they will - # reach past the power ring or stripes on the sides - self.route_escape_pins(bbox=init_bbox, change=change, mod=mod) - if OPTS.route_supplies: - self.route_supplies(init_bbox) - - - 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): - - # This is only done when we add_routes because the data channel will be larger - # so that can be used for area estimation. - if add_routes: - self.route_col_addr_dffs(port) - - self.route_data_dffs(port, add_routes) - - def route_col_addr_dffs(self, port): - - 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: - dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] - dff_pins = [self.col_addr_dff_insts[port].get_pin(x) for x in dff_names] - bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)] - 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: - - # This layer stack must be different than the data dff layer stack - 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.m3_pitch) - cr = channel_route(netlist=route_map, - offset=offset, - layer_stack=layer_stack, - parent=self) - # This causes problem in magic since it sometimes cannot extract connectivity of instances - # with no active devices. - self.add_inst(cr.name, cr) - self.connect_inst([]) - # self.add_flat_inst(cr.name, cr) - else: - offset = vector(0, - self.bank.height + self.m3_pitch) - cr = channel_route(netlist=route_map, - offset=offset, - layer_stack=layer_stack, - parent=self) - # This causes problem in magic since it sometimes cannot extract connectivity of instances - # with no active devices. - self.add_inst(cr.name, cr) - self.connect_inst([]) - # self.add_flat_inst(cr.name, cr) - - def route_data_dffs(self, port, add_routes): - 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)] - dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names] - bank_names = ["bank_wmask{0}_{1}".format(port, x) for x in range(self.num_wmasks)] - bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] - route_map.extend(list(zip(bank_pins, dff_pins))) - - if port in self.write_ports: - # synchronized inputs from data dff - dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)] - dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] - bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + 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))) - - # spare wen dff - if self.num_spare_cols > 0 and port in self.write_ports: - dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] - dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] - 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: - - # This layer stack must be different than the column addr dff layer stack - layer_stack = self.m2_stack - if port == 0: - # This is relative to the bank at 0,0 or the s_en which is routed on M3 also - if "s_en" in self.control_logic_insts[port].mod.pin_map: - y_bottom = min(0, self.control_logic_insts[port].get_pin("s_en").by()) - else: - y_bottom = 0 - - y_offset = y_bottom - self.data_bus_size[port] + 2 * self.m3_pitch - offset = vector(self.control_logic_insts[port].rx() + self.dff.width, - y_offset) - cr = channel_route(netlist=route_map, - offset=offset, - layer_stack=layer_stack, - parent=self) - if add_routes: - # This causes problem in magic since it sometimes cannot extract connectivity of instances - # with no active devices. - self.add_inst(cr.name, cr) - self.connect_inst([]) - # self.add_flat_inst(cr.name, cr) - else: - self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap - else: - if "s_en" in self.control_logic_insts[port].mod.pin_map: - y_top = max(self.bank.height, self.control_logic_insts[port].get_pin("s_en").uy()) - else: - y_top = self.bank.height - y_offset = y_top + self.m3_pitch - offset = vector(0, - y_offset) - cr = channel_route(netlist=route_map, - offset=offset, - layer_stack=layer_stack, - parent=self) - if add_routes: - # This causes problem in magic since it sometimes cannot extract connectivity of instances - # with no active devices. - self.add_inst(cr.name, cr) - self.connect_inst([]) - # self.add_flat_inst(cr.name, cr) - 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 """ - - # This is the actual input to the SRAM - for port in self.all_ports: - # Connect all of these clock pins to the clock in the central bus - # This is something like a "spine" clock distribution. The two spines - # are clk_buf and clk_buf_bar - control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf") - control_clk_buf_pos = control_clk_buf_pin.center() - - # This uses a metal2 track to the right (for port0) of the control/row addr DFF - # to route vertically. For port1, it is to the left. - row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk") - if port % 2: - control_clk_buf_pos = control_clk_buf_pin.lc() - row_addr_clk_pos = row_addr_clk_pin.lc() - mid1_pos = vector(self.row_addr_dff_insts[port].lx() - self.m2_pitch, - row_addr_clk_pos.y) - else: - control_clk_buf_pos = control_clk_buf_pin.rc() - row_addr_clk_pos = row_addr_clk_pin.rc() - mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch, - row_addr_clk_pos.y) - - # This is the steiner point where the net branches out - clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y) - self.add_path(control_clk_buf_pin.layer, [control_clk_buf_pos, clk_steiner_pos]) - 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() - mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) - self.add_wire(self.m2_stack[::-1], - [dff_clk_pos, mid_pos, clk_steiner_pos]) - elif port in self.write_ports: - data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") - data_dff_clk_pos = data_dff_clk_pin.center() - mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y) - # In some designs, the steiner via will be too close to the mid_pos via - # so make the wire as wide as the contacts - self.add_path("m2", - [mid_pos, clk_steiner_pos], - width=max(self.m2_via.width, self.m2_via.height)) - self.add_wire(self.m2_stack[::-1], - [data_dff_clk_pos, mid_pos, clk_steiner_pos]) - - def route_control_logic(self): - """ - Route the control logic pins that are not inputs - """ - - for port in self.all_ports: - for signal in self.control_logic_outputs[port]: - # The clock gets routed separately and is not a part of the bank - if "clk" in signal: - continue - src_pin = self.control_logic_insts[port].get_pin(signal) - dest_pin = self.bank_inst.get_pin(signal + "{}".format(port)) - self.connect_vbus(src_pin, dest_pin) - - if self.has_rbl: - for port in self.all_ports: - # Only input (besides pins) is the replica bitline - src_pin = self.control_logic_insts[port].get_pin("rbl_bl") - dest_pin = self.bank_inst.get_pin("rbl_bl_{0}_{0}".format(port)) - self.add_wire(self.m3_stack, - [src_pin.center(), vector(src_pin.cx(), dest_pin.cy()), dest_pin.rc()]) - self.add_via_stack_center(from_layer=src_pin.layer, - to_layer="m4", - offset=src_pin.center()) - self.add_via_stack_center(from_layer=dest_pin.layer, - to_layer="m3", - 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: - for bit in range(self.row_addr_size): - flop_name = "dout_{}".format(bit) - bank_name = "addr{0}_{1}".format(port, bit + self.col_addr_size) - flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name) - bank_pin = self.bank_inst.get_pin(bank_name) - flop_pos = flop_pin.center() - bank_pos = bank_pin.center() - mid_pos = vector(bank_pos.x, flop_pos.y) - self.add_via_stack_center(from_layer=flop_pin.layer, - to_layer="m3", - offset=flop_pos) - self.add_path("m3", [flop_pos, mid_pos]) - self.add_via_stack_center(from_layer=bank_pin.layer, - to_layer="m3", - offset=mid_pos) - self.add_path(bank_pin.layer, [mid_pos, bank_pos]) - - def add_lvs_correspondence_points(self): - """ - This adds some points for easier debugging if LVS goes wrong. - These should probably be turned off by default though, since extraction - will show these as ports in the extracted netlist. - """ - return - for n in self.control_logic_outputs[0]: - pin = self.control_logic_insts[0].get_pin(n) - self.add_label(text=n, - layer=pin.layer, - offset=pin.center()) - - def graph_exclude_data_dff(self): - """ - Removes data dff and wmask dff (if applicable) from search graph. - """ - # Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay. - for inst in self.data_dff_insts: - self.graph_inst_exclude.add(inst) - if self.write_size != self.word_size: - for inst in self.wmask_dff_insts: - self.graph_inst_exclude.add(inst) - 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. - """ - # Address is considered not part of the critical path, subjectively removed - for inst in self.row_addr_dff_insts: - self.graph_inst_exclude.add(inst) - - if self.col_addr_dff: - for inst in self.col_addr_dff_insts: - self.graph_inst_exclude.add(inst) - - def graph_exclude_ctrl_dffs(self): - """ - Exclude dffs for CSB, WEB, etc from graph - """ - # Insts located in control logic, exclusion function called here - for inst in self.control_logic_insts: - inst.mod.graph_exclude_dffs() - - def get_cell_name(self, inst_name, row, col): - """ - Gets the spice name of the target bitcell. - """ - # Sanity check in case it was forgotten - if inst_name.find("x") != 0: - inst_name = "x" + inst_name - return self.bank_inst.mod.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.bank_inst.name, row, col) - - def get_bank_num(self, inst_name, row, col): - return 0 diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 96de12d1..764001d1 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -17,13 +17,24 @@ class signal_escape_router(router): This is the signal escape router that uses the Hanan grid graph method. """ - def __init__(self, layers, design, bbox=None): + def __init__(self, layers, design, bbox=None, mod=0): # `router` is the base router class router.__init__(self, layers, design, bbox) # New pins are the side supply pins self.new_pins = {} + + # Use for add distance of dout pins at the perimeter + self.distance_right = 0 + + self.distance_left = 0 + + # Use for control which edge/position the pins(dout) will be placed + # 0 -> default + # 1 -> all top/bottom + # 2 -> all left/right + self.state_mod = mod def route(self, pin_names): @@ -76,8 +87,6 @@ class signal_escape_router(router): self.find_vias(new_vias) routed_count += 1 debug.info(2, "Routed {} of {} signal pins".format(routed_count, routed_max)) - print("route pins:") - print(source) self.replace_layout_pins() @@ -177,18 +186,13 @@ class signal_escape_router(router): rect=rect, layer_name_pp=layer) self.fake_pins.append(pin) - print("this is add_per") - print(pin.name) - print(pin.center) def create_fake_pin(self, pin): """ Create a fake pin on the perimeter orthogonal to the given pin. """ ll, ur = self.bbox c = pin.center() - print("inside pin name") - print("----------------------------------------------------------") - print(pin.name) + # Find the closest edge edge, vertical = self.get_closest_edge(c) @@ -202,30 +206,49 @@ class signal_escape_router(router): fake_center = vector(ur.x + self.track_wire * 2, c.y) if edge == "top": fake_center = vector(c.x, ur.y + self.track_wire * 2) - #fake_center = vector(ll.x - self.track_wire * 2, c.y) # test if here we could change the pin position at the layout - """ - pattern = r'^addr0_1' + + # relocate the pin position + pattern = r'^dout' if re.match(pattern, pin.name): - vertical = True - fake_center = vector(ll.x - self.track_wire * 2, c.y + self.track_wire * 4)# fix still do not know how to control the distance between every fake pin - #do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y? - """ + + if self.state_mod == 0: + pass# do not change, default + + elif self.state_mod == 1: # all top/bottom + if edge == "right": + vertical = False + fake_center = vector(c.x, ll.y - self.track_wire * 2) + self.distance_right += 1 + else: + if edge == "left": + vertical = False + fake_center = vector(c.x, ll.y + self.track_wire * 2) + self.distance_left += 1 + + elif self.state_mod == 2: # all left/right + if (edge == "bottom") or (edge == "right"):# change to the east + vertical = True + fake_center = vector(ur.x + self.track_wire * 2, ll.y + 30 + self.distance_right) + self.distance_right += 1 + else: + if (edge == "top") or (edge == "left"):# change to the west + vertical = True + fake_center = vector(ll.x - self.track_wire * 2, ur.y - 30 - self.distance_left) + self.distance_left += 1 + else: + debug.error("wrong state mod!", -1) + # Create the fake pin shape layer = self.get_layer(int(not vertical)) half_wire_vector = vector([self.half_wire] * 2) nll = fake_center - half_wire_vector nur = fake_center + half_wire_vector - #not test jet - #half_wire_vector = vector([self.half_wire] * 2)# out *2 means vector([self.half_wire, self.half_wire]) - #nll = fake_center - half_wire_vector - half_wire_vector - #nur = fake_center + half_wire_vector + half_wire_vector + rect = [nll, nur] pin = graph_shape(name="fake", rect=rect, layer_name_pp=layer) - print("this create_fake_pin") - print(pin.name) - print(pin.center) + return pin @@ -234,8 +257,6 @@ class signal_escape_router(router): to_route = [] for name in pin_names: - print("==============the pin names===================") - print(name) pin = next(iter(self.pins[name])) fake = self.create_fake_pin(pin) to_route.append((pin, fake, pin.distance(fake))) diff --git a/compiler/router/signal_escape_router_change.py b/compiler/router/signal_escape_router_change.py deleted file mode 100644 index 89eab572..00000000 --- a/compiler/router/signal_escape_router_change.py +++ /dev/null @@ -1,311 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz -# All rights reserved. -# -from openram import debug -from openram.base.vector import vector -from openram.base.vector3d import vector3d -from openram import OPTS -from .graph import graph -from .graph_shape import graph_shape -from .router import router -import re - -class signal_escape_router_change(router): - """ - This is the signal escape router that uses the Hanan grid graph method. - """ - - def __init__(self, layers, design, bbox=None, mod=0): - - # `router` is the base router class - router.__init__(self, layers, design, bbox) - - # New pins are the side supply pins - self.new_pins = {} - - # Use for add distance of dout pins at the perimeter - self.distance_right = 0 - - self.distance_left = 0 - - # Use for control which edge/position the pins(dout) will be placed - # 0 -> default - # 1 -> all top/bottom - # 2 -> all left/right - self.state_mod = mod - - - def route(self, pin_names): - """ Route the given pins to the perimeter. """ - debug.info(1, "Running signal escape router...") - - # Prepare gdsMill to find pins and blockages - self.prepare_gds_reader() - - # Find pins to be routed - for name in pin_names: - self.find_pins(name) - - # Find blockages and vias - self.find_blockages() - self.find_vias() - - # Convert blockages and vias if they overlap a pin - self.convert_vias() - self.convert_blockages() - - # Add fake pins on the perimeter to do the escape routing on - self.add_perimeter_fake_pins() - - # Add vdd and gnd pins as blockages as well - # NOTE: This is done to make vdd and gnd pins DRC-safe - for pin in self.all_pins: - self.blockages.append(self.inflate_shape(pin)) - - # Route vdd and gnd - routed_count = 0 - routed_max = len(pin_names) - for source, target, _ in self.get_route_pairs(pin_names): - # Change fake pin's name so the graph will treat it as routable - target.name = source.name - # Create the graph - g = graph(self) - g.create_graph(source, target) - # Find the shortest path from source to target - path = g.find_shortest_path() - # If no path is found, throw an error - if path is None: - self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target) - debug.error("Couldn't route from {} to {}.".format(source, target), -1) - # Create the path shapes on layout - new_wires, new_vias = self.add_path(path) - self.new_pins[source.name] = new_wires[-1] - # Find the recently added shapes - self.find_blockages(name, new_wires) - self.find_vias(new_vias) - routed_count += 1 - debug.info(2, "Routed {} of {} signal pins".format(routed_count, routed_max)) - print("route pins:") - print(source) - self.replace_layout_pins() - - - def get_closest_edge(self, point): - """ Return a point's the closest edge and the edge's axis direction. """ - - ll, ur = self.bbox - - # Snap the pin to the perimeter and break the iteration - ll_diff_x = abs(point.x - ll.x) - ll_diff_y = abs(point.y - ll.y) - ur_diff_x = abs(point.x - ur.x) - ur_diff_y = abs(point.y - ur.y) - min_diff = min(ll_diff_x, ll_diff_y, ur_diff_x, ur_diff_y) - - if min_diff == ll_diff_x: - return "left", True - if min_diff == ll_diff_y: - return "bottom", False - if min_diff == ur_diff_x: - return "right", True - return "top", False - - - def prepare_path(self, path): - """ - Override the `prepare_path` method from the `router` class to prevent - overflows from the SRAM layout area. - """ - - ll, ur = self.bbox - nodes = super().prepare_path(path) - new_nodes = [] - for i in range(len(nodes)): - node = nodes[i] - c = node.center - # Haven't overflown yet - if ll.x < c.x and c.x < ur.x and ll.y < c.y and c.y < ur.y: - new_nodes.append(node) - continue - # Snap the pin to the perimeter and break the iteration - edge, _ = self.get_closest_edge(c) - if edge == "left": - fake_center = vector3d(ll.x + self.half_wire, c.y, c.z) - if edge == "bottom": - fake_center = vector3d(c.x, ll.y + self.half_wire, c.z) - if edge == "right": - fake_center = vector3d(ur.x - self.half_wire, c.y, c.z) - if edge == "top": - fake_center = vector3d(c.x, ur.y - self.half_wire, c.z) - node.center = fake_center - new_nodes.append(node) - break - return new_nodes - - - def add_perimeter_fake_pins(self): - """ - Add the fake pins on the perimeter to where the signals will be routed. - These perimeter fake pins are only used to replace layout pins at the - end of routing. - """ - - ll, ur = self.bbox - wide = self.track_wire - - for side in ["top", "bottom", "left", "right"]: - vertical = side in ["left", "right"] - - # Calculate the lower left coordinate - if side == "top": - offset = vector(ll.x, ur.y - wide) - elif side == "bottom": - offset = vector(ll.x, ll.y) - elif side == "left": - offset = vector(ll.x, ll.y) - elif side == "right": - offset = vector(ur.x - wide, ll.y) - - # Calculate width and height - shape = ur - ll - if vertical: - shape_width = wide - shape_height = shape.y - else: - shape_width = shape.x - shape_height = wide - - # Add this new pin - # They must lie on the non-preferred direction since the side supply - # pins will lie on the preferred direction - layer = self.get_layer(int(not vertical)) - nll = vector(offset.x, offset.y) - nur = vector(offset.x + shape_width, offset.y + shape_height) - rect = [nll, nur] - pin = graph_shape(name="fake", - rect=rect, - layer_name_pp=layer) - self.fake_pins.append(pin) - print("this is add_per") - print(pin.name) - print(pin.center) - - def create_fake_pin(self, pin): - """ Create a fake pin on the perimeter orthogonal to the given pin. """ - - ll, ur = self.bbox - c = pin.center() - print("inside pin name") - print("----------------------------------------------------------") - print(pin.name) - # Find the closest edge - edge, vertical = self.get_closest_edge(c) - - # Keep the fake pin out of the SRAM layout are so that they won't be - # blocked by previous signals if they're on the same orthogonal line - if edge == "left": - fake_center = vector(ll.x - self.track_wire * 2, c.y) - if edge == "bottom": - fake_center = vector(c.x, ll.y - self.track_wire * 2) - if edge == "right": - fake_center = vector(ur.x + self.track_wire * 2, c.y) - if edge == "top": - fake_center = vector(c.x, ur.y + self.track_wire * 2) - #fake_center = vector(ll.x - self.track_wire * 2, c.y) # test if here we could change the pin position at the layout - - # relocate the pin position - pattern = r'^dout' - if re.match(pattern, pin.name): - - if self.state_mod == 0: - pass# do not change, default - - elif self.state_mod == 1: # all top/bottom - if edge == "right": - vertical = False - fake_center = vector(c.x, ll.y - self.track_wire * 2) - self.distance_right += 1 - else: - if edge == "left": - vertical = False - fake_center = vector(c.x, ll.y + self.track_wire * 2) - self.distance_left += 1 - - elif self.state_mod == 2: # all left/right - if (edge == "bottom") or (edge == "right"):# change to the east - vertical = True - fake_center = vector(ur.x + self.track_wire * 2, ll.y + 30 + self.distance_right) - self.distance_right += 1 - else: - if (edge == "top") or (edge == "left"):# change to the west - vertical = True - fake_center = vector(ll.x - self.track_wire * 2, ur.y - 30 - self.distance_left) - self.distance_left += 1 - else: - debug.error("wrong state mod!", -1) - ''' # old version - if edge == "bottom":# change to the east - vertical = True - fake_center = vector(ur.x + self.track_wire * 2, ll.y + 30 + self.distance_right) - self.distance_right += 1 - else: - if edge == "top":# change to the west - vertical = True - fake_center = vector(ll.x - self.track_wire * 2, ur.y - 30 - self.distance_left) - self.distance_left += 1 - ''' - """ - pattern = r'^addr0_1' - if re.match(pattern, pin.name): - vertical = True - fake_center = vector(ll.x - self.track_wire * 2, c.y + self.track_wire * 4)# fix still do not know how to control the distance between every fake pin - #do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y? - """ - # Create the fake pin shape - layer = self.get_layer(int(not vertical)) - half_wire_vector = vector([self.half_wire] * 2) - nll = fake_center - half_wire_vector - nur = fake_center + half_wire_vector - #not test jet - #half_wire_vector = vector([self.half_wire] * 2)# out *2 means vector([self.half_wire, self.half_wire]) - #nll = fake_center - half_wire_vector - half_wire_vector - #nur = fake_center + half_wire_vector + half_wire_vector - rect = [nll, nur] - pin = graph_shape(name="fake", - rect=rect, - layer_name_pp=layer) - print("this create_fake_pin") - print(pin.name) - print(pin.center) - return pin - - - def get_route_pairs(self, pin_names): - """ Return the pairs to be routed. """ - - to_route = [] - for name in pin_names: - print("==============the pin names===================") - print(name) - pin = next(iter(self.pins[name])) - fake = self.create_fake_pin(pin) - to_route.append((pin, fake, pin.distance(fake))) - return sorted(to_route, key=lambda x: x[2]) - - - def replace_layout_pins(self): - """ Replace the old layout pins with new ones around the perimeter. """ - - for name, pin in self.new_pins.items(): - print("name:{0}".format(name)) - print("replace_pin->{0}".format(pin)) - pin = graph_shape(pin.name, pin.boundary, pin.lpp) - # Find the intersection of this pin on the perimeter - for fake in self.fake_pins: - edge = pin.intersection(fake) - if edge: - break - self.design.replace_layout_pin(name, edge) - print("pass!!!!!!!") diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index e868c8b2..fc61f4e6 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -76,8 +76,6 @@ class supply_router(router): # Create the graph g = graph(self) g.create_graph(source, target) - # debug - debug.warning("graph creat success!") # Find the shortest path from source to target path = g.find_shortest_path() # If no path is found, throw an error diff --git a/compiler/sram.py b/compiler/sram.py index 36ec253c..6af99cb9 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -50,13 +50,65 @@ class sram(): self.name = name from openram.modules.sram_1bank import sram_1bank as sram - #from openram.modules.sram_new import sram_1bank as sram + + num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports self.s = sram(name, sram_config) - - self.s.create_netlist() + self.s.create_netlist()# not placed & routed jet + cur_state = "IDLE" if not OPTS.netlist_only: - self.s.create_layout() + i = 0 + while i < (OPTS.word_size + 100): + + debug.warning("current state: state = {0}".format(cur_state)) + + if cur_state == "IDLE":# default, fisrt try + try: + self.s.create_layout(position_add=i) + except AssertionError as e: + cur_state = "ALL_TOP_BOTTOM" + del self.s + self.s = sram(name, sram_config) + self.s.create_netlist() + if num_ports > 1: + i = 16 + else: + i = 0 + continue + cur_state = "FINISH" + break + elif cur_state == "ALL_TOP_BOTTOM": + try: + self.s.create_layout(position_add=i, mod=1) + except AssertionError as e: + cur_state = "ALL_LEFT_RIGHT" + i = OPTS.word_size + 24 + del self.s + self.s = sram(name, sram_config) + self.s.create_netlist() + continue + cur_state = "FINISH" + break + + elif cur_state == "ALL_LEFT_RIGHT": + try: + self.s.create_layout(position_add=i, mod=2) + except AssertionError as e: + cur_state = "ALL_LEFT_RIGHT" + i = i + 1 + if i == (99 + OPTS.word_size):# failed in rounting + debug.error("Failed in rounting", -1) + break + del self.s + self.s = sram(name, sram_config) + self.s.create_netlist() + continue + cur_state = "FINISH" + break + else: + cur_state = "FINISH" + break + if not OPTS.is_unit_test: print_time("SRAM creation", datetime.datetime.now(), start_time) @@ -110,7 +162,7 @@ class sram(): spname = OPTS.output_path + self.s.name + ".sp" debug.print_raw("SP: Writing to {0}".format(spname)) self.sp_write(spname) - + #''' #comment the following state when generating big sram, and then disable drc/lvs, because maigc_ext stuck # Save a functional simulation file with default period functional(self.s, spname, @@ -132,7 +184,7 @@ class sram(): d.targ_write_ports = [self.s.write_ports[0]] d.write_delay_stimulus() print_time("DELAY", datetime.datetime.now(), start_time) - + #''' #comment the above when generating big sram, and then disable drc/;vs, bevause magic_ext stuck # Save trimmed spice file temp_trim_sp = "{0}trimmed.sp".format(OPTS.output_path) self.sp_write(temp_trim_sp, lvs=False, trim=True) diff --git a/compiler/sram_new_test.py b/compiler/sram_new_test.py deleted file mode 100644 index cbce8582..00000000 --- a/compiler/sram_new_test.py +++ /dev/null @@ -1,338 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2024 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -import os -import shutil -import datetime -import copy -from openram import debug -from openram import sram_config as config -from openram import OPTS, print_time - - -class sram(): - """ - This is not a design module, but contains an SRAM design instance. - It could later try options of number of banks and organization to compare - results. - We can later add visualizer and other high-level functions as needed. - """ - def __init__(self, sram_config=None, name=None): - - # Create default configs if custom config isn't provided - if sram_config is None: - sram_config = config(word_size=OPTS.word_size, - num_words=OPTS.num_words, - write_size=OPTS.write_size, - num_banks=OPTS.num_banks, - words_per_row=OPTS.words_per_row, - num_spare_rows=OPTS.num_spare_rows, - num_spare_cols=OPTS.num_spare_cols) - - if name is None: - name = OPTS.output_name - - sram_config.set_local_config(self) - - # reset the static duplicate name checker for unit tests - # in case we create more than one SRAM - from openram.base import design - design.name_map=[] - - debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size, - self.num_words, - self.num_banks)) - start_time = datetime.datetime.now() - - self.name = name - - from openram.modules.sram_new import sram_1bank as sram - - num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports - self.s = sram(name, sram_config) - self.s.create_netlist()# not placed & routed jet - cur_state = "IDLE" - if not OPTS.netlist_only: - i = 0 - while i < (OPTS.word_size + 100): - - print("current iteration: i = {0}".format(i)) - debug.warning("current state: state = {0}".format(cur_state)) - - if cur_state == "IDLE":# default, fisrt try - try: - self.s.create_layout_recrusive(position_add=i) - except AssertionError as e: - cur_state = "ALL_TOP_BOTTOM" - del self.s - self.s = sram(name, sram_config) - self.s.create_netlist() - if num_ports > 1: - i = 16 - else: - i = 0 - continue - cur_state = "FINISH" - break - - elif cur_state == "ALL_TOP_BOTTOM": - try: - self.s.create_layout_recrusive(position_add=i, mod=1) - except AssertionError as e: - cur_state = "ALL_LEFT_RIGHT" - i = OPTS.word_size + 24 - del self.s - self.s = sram(name, sram_config) - self.s.create_netlist() - continue - cur_state = "FINISH" - break - - elif cur_state == "ALL_LEFT_RIGHT": - try: - self.s.create_layout_recrusive(position_add=i, mod=2) - except AssertionError as e: - cur_state = "ALL_LEFT_RIGHT" - i = i + 1 - if i == (99 + OPTS.word_size):# failed in rounting - debug.error("Failed in rounting", -1) - break - del self.s - self.s = sram(name, sram_config) - self.s.create_netlist() - continue - cur_state = "FINISH" - break - else: - cur_state = "FINISH" - break - - ''' # 2nd version - self.s = sram(name, sram_config) - self.s.create_netlist()# not placed & routed jet - if not OPTS.netlist_only: - i = 0 - supply_route_not_found = False - while i < (OPTS.word_size + 100): - print("current iteration: i = {0}".format(i)) - try: - self.s.create_layout_recrusive(position_add=i) - except AssertionError as e: - supply_route_not_found = True - if i == (99 + OPTS.word_size):# failed in rounting - debug.error("Failed in rounting", -1) - break - - - if (supply_route_not_found): - del self.s - self.s = sram(name, sram_config) - self.s.create_netlist() - if i == 0:# after first try - i = OPTS.word_size + 20 - else: - i = i + 1 - supply_route_not_found = False - else:# successfully routed - break - ''' - '''#old version - self.s = sram(name, sram_config) - self.s.create_netlist()# not placed & routed jet - #self.s_tmp = copy.deepcopy(self.s) # need deepcopy - if not OPTS.netlist_only: - i = 98 - supply_route_not_found = False - #self.s_tmp = copy.deepcopy(self.s) # need deepcopy - while i < 100: - print("current iteration: {0}".format(i)) - try: - self.s.create_layout_recrusive(position_add=i) - except AssertionError as e: - supply_route_not_found = True - if i == 99:# failed in rounting - debug.error("Failed in rounting", -1) - break - - - if (supply_route_not_found): - #self.s_tmp = copy.deepcopy(self.s) # need deepcopy - del self.s - self.s = sram(name, sram_config) - self.s.create_netlist() - i = i + 1 - supply_route_not_found = False - else:# successfully routed - #self.s = copy.deepcopy(self.s_tmp) # need deepcopy - break - ''' - if not OPTS.is_unit_test: - print_time("SRAM creation", datetime.datetime.now(), start_time) - - def get_sp_name(self): - if OPTS.use_pex: - # Use the extracted spice file - return self.pex_name - else: - # Use generated spice file for characterization - return self.sp_name - - def sp_write(self, name, lvs=False, trim=False): - self.s.sp_write(name, lvs, trim) - - def lef_write(self, name): - self.s.lef_write(name) - - def gds_write(self, name): - self.s.gds_write(name) - - def verilog_write(self, name): - self.s.verilog_write(name) - if self.num_banks != 1: - from openram.modules.sram_multibank import sram_multibank - mb = sram_multibank(self.s) - mb.verilog_write(name[:-2] + '_top.v') - - def extended_config_write(self, name): - """Dump config file with all options. - Include defaults and anything changed by input config.""" - f = open(name, "w") - var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name))) - for var_name, var_value in var_dict.items(): - if isinstance(var_value, str): - f.write(str(var_name) + " = " + "\"" + str(var_value) + "\"\n") - else: - f.write(str(var_name) + " = " + str(var_value)+ "\n") - f.close() - - def save(self): - """ Save all the output files while reporting time to do it as well. """ - - # Import this at the last minute so that the proper tech file - # is loaded and the right tools are selected - from openram import verify - from openram.characterizer import functional - from openram.characterizer import delay - - # Save the spice file - start_time = datetime.datetime.now() - spname = OPTS.output_path + self.s.name + ".sp" - debug.print_raw("SP: Writing to {0}".format(spname)) - self.sp_write(spname) - ''' - # Save a functional simulation file with default period - functional(self.s, - spname, - cycles=200, - output_path=OPTS.output_path) - print_time("Spice writing", datetime.datetime.now(), start_time) - - # Save stimulus and measurement file - start_time = datetime.datetime.now() - debug.print_raw("DELAY: Writing stimulus...") - d = delay(self.s, spname, ("TT", 5, 25), output_path=OPTS.output_path) - if (self.s.num_spare_rows == 0): - probe_address = "1" * self.s.addr_size - else: - probe_address = "0" + "1" * (self.s.addr_size - 1) - probe_data = self.s.word_size - 1 - d.analysis_init(probe_address, probe_data) - d.targ_read_ports.extend(self.s.read_ports) - d.targ_write_ports = [self.s.write_ports[0]] - d.write_delay_stimulus() - print_time("DELAY", datetime.datetime.now(), start_time) - ''' - # Save trimmed spice file - temp_trim_sp = "{0}trimmed.sp".format(OPTS.output_path) - self.sp_write(temp_trim_sp, lvs=False, trim=True) - - if not OPTS.netlist_only: - # Write the layout - start_time = datetime.datetime.now() - gdsname = OPTS.output_path + self.s.name + ".gds" - debug.print_raw("GDS: Writing to {0}".format(gdsname)) - self.gds_write(gdsname) - if OPTS.check_lvsdrc: - verify.write_drc_script(cell_name=self.s.name, - gds_name=os.path.basename(gdsname), - extract=True, - final_verification=True, - output_path=OPTS.output_path) - print_time("GDS", datetime.datetime.now(), start_time) - - # Create a LEF physical model - start_time = datetime.datetime.now() - lefname = OPTS.output_path + self.s.name + ".lef" - debug.print_raw("LEF: Writing to {0}".format(lefname)) - self.lef_write(lefname) - print_time("LEF", datetime.datetime.now(), start_time) - - # Save the LVS file - start_time = datetime.datetime.now() - lvsname = OPTS.output_path + self.s.name + ".lvs.sp" - debug.print_raw("LVS: Writing to {0}".format(lvsname)) - self.sp_write(lvsname, lvs=True) - if not OPTS.netlist_only and OPTS.check_lvsdrc: - verify.write_lvs_script(cell_name=self.s.name, - gds_name=os.path.basename(gdsname), - sp_name=os.path.basename(lvsname), - final_verification=True, - output_path=OPTS.output_path) - print_time("LVS writing", datetime.datetime.now(), start_time) - - # Save the extracted spice file - if OPTS.use_pex: - start_time = datetime.datetime.now() - # Output the extracted design if requested - pexname = OPTS.output_path + self.s.name + ".pex.sp" - spname = OPTS.output_path + self.s.name + ".sp" - verify.run_pex(self.s.name, gdsname, spname, output=pexname) - sp_file = pexname - print_time("Extraction", datetime.datetime.now(), start_time) - else: - # Use generated spice file for characterization - sp_file = spname - - # Characterize the design - start_time = datetime.datetime.now() - from openram.characterizer import lib - debug.print_raw("LIB: Characterizing... ") - lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) - print_time("Characterization", datetime.datetime.now(), start_time) - - # Write the config file - start_time = datetime.datetime.now() - try: - from shutil import copyfile - copyfile(OPTS.config_file, OPTS.output_path + OPTS.output_name + '.py') - except shutil.SameFileError: - pass - debug.print_raw("Config: Writing to {0}".format(OPTS.output_path + OPTS.output_name + '.py')) - print_time("Config", datetime.datetime.now(), start_time) - - # Write the datasheet - start_time = datetime.datetime.now() - from openram.datasheet import datasheet_gen - dname = OPTS.output_path + self.s.name + ".html" - debug.print_raw("Datasheet: Writing to {0}".format(dname)) - datasheet_gen.datasheet_write(dname) - print_time("Datasheet", datetime.datetime.now(), start_time) - - # Write a verilog model - start_time = datetime.datetime.now() - vname = OPTS.output_path + self.s.name + '.v' - debug.print_raw("Verilog: Writing to {0}".format(vname)) - self.verilog_write(vname) - print_time("Verilog", datetime.datetime.now(), start_time) - - # Write out options if specified - if OPTS.output_extended_config: - start_time = datetime.datetime.now() - oname = OPTS.output_path + OPTS.output_name + "_extended.py" - debug.print_raw("Extended Config: Writing to {0}".format(oname)) - self.extended_config_write(oname) - print_time("Extended Config", datetime.datetime.now(), start_time) diff --git a/sram_compiler.py b/sram_compiler.py index fc2f7f51..40480e30 100755 --- a/sram_compiler.py +++ b/sram_compiler.py @@ -69,12 +69,13 @@ for path in output_files: debug.print_raw(path) # Create an SRAM (we can also pass sram_config, see documentation/tutorials for details) -#from openram import sram -#s = sram() -from openram import sram_new_test -s = sram_new_test.sram() +from openram import sram +s = sram() +#from openram import sram_openroad_test +#s = sram_openroad_test.sram() # Output the files for the resulting SRAM s.save() +#s.save_only() ''' del s s = sram_road.sram(mod=1)