From f56460bb9404cb46dc5981130bb89a5d5174999f Mon Sep 17 00:00:00 2001 From: FriedrichWu Date: Tue, 12 Nov 2024 17:02:02 +0100 Subject: [PATCH] make code clean --- compiler/base/channel_route.py | 26 +- compiler/modules/sram_1bank.py | 63 +- compiler/modules/sram_for_road.py | 2378 -------------------- compiler/modules/sram_openroad.py | 1485 ------------- compiler/modules/sram_part.py | 2623 ----------------------- compiler/router/io_pin_placer.py | 239 +-- compiler/router/signal_escape_router.py | 26 +- compiler/sram.py | 121 +- compiler/sram_openroad_test.py | 247 --- compiler/sram_road.py | 268 --- sram_compiler.py | 32 +- 11 files changed, 196 insertions(+), 7312 deletions(-) delete mode 100644 compiler/modules/sram_for_road.py delete mode 100644 compiler/modules/sram_openroad.py delete mode 100644 compiler/modules/sram_part.py delete mode 100644 compiler/sram_openroad_test.py delete mode 100644 compiler/sram_road.py diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index 6c8a874b..e1f9bedc 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -144,16 +144,16 @@ 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("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): @@ -233,7 +233,7 @@ 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) - + if self.dff_area == False: current_offset = real_channel_offset else: # special handle for dff area @@ -241,7 +241,7 @@ class channel_route(design): if self.layer_stack == self.m2_stack: self.vertical_nonpref_pitch = self.horizontal_pitch + 0.1 # 0.1 make sure even if via at same col, fulfill m3-m3 spacing - + 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 @@ -326,8 +326,8 @@ class channel_route(design): except AttributeError: debug.error("Cannot find layer pitch.", -1) return (nonpref_pitch, pitch, pitch - space, space) - - def add_horizontal_trunk_with_jog(self, + + def add_horizontal_trunk_with_jog(self, pins, trunk_offset, pitch): @@ -346,7 +346,7 @@ class channel_route(design): non_preferred_route = max_x - min_x <= pitch half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] if port == 0: # bottom need shift - if non_preferred_route: + if non_preferred_route: # Add the horizontal trunk on the vertical layer! self.add_path(self.vertical_layer, [vector(min_x - half_layer_width, trunk_offset.y), @@ -368,7 +368,7 @@ class channel_route(design): self.add_path(self.vertical_layer, [pin_pos, mid]) self.add_via_stack_center(from_layer=pin.layer, to_layer=self.vertical_layer, - offset=pin.bc()) + offset=pin.bc()) else: # Add the horizontal trunk self.add_path(self.horizontal_layer, @@ -399,7 +399,7 @@ class channel_route(design): to_layer=self.vertical_layer, offset=pin.bc()) else: # port 1, situation different, top need shift - if non_preferred_route: + if non_preferred_route: # Add the horizontal trunk on the vertical layer! self.add_path(self.vertical_layer, [vector(min_x - half_layer_width, trunk_offset.y), @@ -411,7 +411,7 @@ class channel_route(design): pin_pos = pin.uc() # No bend needed here mid = vector(pin_pos.x, trunk_offset.y) - self.add_path(self.vertical_layer, [pin_pos, mid]) + self.add_path(self.vertical_layer, [pin_pos, mid]) self.add_via_stack_center(from_layer=pin.layer, to_layer=self.vertical_layer, offset=pin.uc()) @@ -440,7 +440,7 @@ class channel_route(design): directions=self.directions) self.add_via_stack_center(from_layer=pin.layer, to_layer=self.vertical_layer, - offset=pin.uc()) + offset=pin.uc()) else: pin_pos = pin.center() mid = vector(pin_pos.x - 0.1, trunk_offset.y) @@ -451,7 +451,7 @@ class channel_route(design): self.add_via_stack_center(from_layer=pin.layer, to_layer=self.vertical_layer, offset=pin_pos) - + def add_horizontal_trunk_route(self, pins, trunk_offset, diff --git a/compiler/modules/sram_1bank.py b/compiler/modules/sram_1bank.py index 1d76e07c..3fada4d5 100644 --- a/compiler/modules/sram_1bank.py +++ b/compiler/modules/sram_1bank.py @@ -18,6 +18,7 @@ from openram.base import lef from openram.sram_factory import factory from openram.tech import spice from openram import OPTS, print_time +import re class sram_1bank(design, verilog, lef): @@ -206,8 +207,8 @@ class sram_1bank(design, verilog, lef): if not OPTS.is_unit_test: print_time("Submodules", datetime.datetime.now(), start_time) - - def create_layout(self, position_add=0, mod=0): + + def create_layout(self, position_add=0, mod=0, route_option="classic"): """ Layout creation """ start_time = datetime.datetime.now() self.place_instances_changeable(position_add=position_add) @@ -215,7 +216,7 @@ class sram_1bank(design, verilog, lef): print_time("Placement", datetime.datetime.now(), start_time) start_time = datetime.datetime.now() - self.route_layout(mod=mod) + self.route_layout(mod=mod, route_option=route_option) if not OPTS.is_unit_test: print_time("Routing", datetime.datetime.now(), start_time) @@ -239,7 +240,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) @@ -319,7 +320,7 @@ class sram_1bank(design, verilog, lef): # Grid is left with many top level pins pass - def route_escape_pins(self, bbox=None, mod=0): + def route_escape_pins(self, bbox=None, mod=0, route_option="classic"): """ Add the top-level pins for a single bank SRAM with control. """ @@ -361,17 +362,29 @@ 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, - mod=mod) - rtr.route(pins_to_route) + + if route_option == "classic": + 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, + mod=mod) + rtr.route(pins_to_route) + elif route_option == "fast": + # use io_pin_placer + # put the IO pins at the edge + from openram.router.io_pin_placer import io_pin_placer as placer + pl = placer(layers=self.m3_stack, + bbox=bbox, + design=self) + for name in pins_to_route: + debug.warning("pins_to_route pins -> {0}".format(name)) + pl.add_io_pins_connected(pins_to_route) + #pl.add_io_pins(pins_to_route) def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """ @@ -784,7 +797,7 @@ 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_changeable(self, position_add=0): """ This places the instances for a single bank SRAM with control @@ -849,11 +862,10 @@ 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 - + self.data_bus_size[port] += position_add # Re-place with the new channel size self.place_dffs() @@ -866,7 +878,7 @@ class sram_1bank(design, verilog, lef): 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) - + y_offset = y_offset + 0.4 # fix, maigc number self.row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_dff_insts[port].place(self.row_addr_pos[port]) @@ -875,7 +887,7 @@ class sram_1bank(design, verilog, lef): # 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 + y_offset = min(self.control_logic_insts[port].by(), 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], mirror="XY") @@ -1061,7 +1073,7 @@ class sram_1bank(design, verilog, lef): "spare_wen{0}[{1}]".format(port, bit), start_layer=pin_layer) - def route_layout(self, mod=0): + def route_layout(self, mod=0, route_option="classic"): """ Route a single bank SRAM """ self.route_clk() @@ -1085,12 +1097,11 @@ 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(bbox=init_bbox, mod=mod) - + self.route_escape_pins(bbox=init_bbox, mod=mod, route_option=route_option) + if OPTS.route_supplies: self.route_supplies(init_bbox) - def route_dffs(self, add_routes=True): for port in self.all_ports: diff --git a/compiler/modules/sram_for_road.py b/compiler/modules/sram_for_road.py deleted file mode 100644 index 62d08847..00000000 --- a/compiler/modules/sram_for_road.py +++ /dev/null @@ -1,2378 +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 file aims to use openroad to do the routing -# No more rings for each moudle -# Provide methods for generating module verilog files & top verilog file -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 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_for_road(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_pin_bank(self): - # lef file use this pin name, should be same with new pin - """ Adding pins for Bank module""" - 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") - for port in self.all_ports: - if self.has_rbl: - self.add_pin("rbl_bl{0}".format(port), "OUTPUT") - for port in self.write_ports: - for bit in range(self.word_size + self.num_spare_cols): - self.add_pin("bank_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") - - # For more than one bank, we have a bank select and name - # the signals gated_*. - for port in self.read_ports: - self.add_pin("s_en{0}".format(port), "INPUT") - for port in self.all_ports: - self.add_pin("p_en_bar{0}".format(port), "INPUT") - for port in self.write_ports: - self.add_pin("w_en{0}".format(port), "INPUT") - for bit in range(self.num_wmasks): - self.add_pin("bank_wmask{0}[{1}]".format(port, bit), "INPUT") - for bit in range(self.num_spare_cols): - if self.num_spare_cols == 1: - self.add_pin("bank_spare_wen{0}".format(port), "INPUT") - else: - self.add_pin("bank_spare_wen{0}[{1}]".format(port, bit), "INPUT") - for port in self.all_ports: - self.add_pin("wl_en{0}".format(port), "INPUT") - - # 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_pin_row_addr_dff(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - for bit in range(self.row_addr_size): - #input - self.add_pin("addr{}[{}]".format(port, bit + self.col_addr_size), "INPUT") - print("addr{}[{}]".format(port, bit), "INPUT") - #output - self.add_pin("a{}[{}]".format(port, bit + self.col_addr_size), "OUTPUT") - #clk_buf, regard as input - self.add_pin("clk_buf{}".format(port), "INPUT") - # 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_pin_col_addr_dff(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - for bit in range(self.col_addr_size): - #input - self.add_pin("addr{}[{}]".format(port, bit), "INPUT") - #output - self.add_pin("a{}[{}]".format(port, bit), "OUTPUT") - #clk_buf, regard as input - self.add_pin("clk_buf{}".format(port), "INPUT") - # 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_pin_data_dff(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - for bit in range(self.word_size + self.num_spare_cols): - # input - self.add_pin("din{}[{}]".format(port, bit), "INPUT") - # output - self.add_pin("bank_din{}[{}]".format(port, bit), "OUTPUT") - #clk_buf, regard as input - self.add_pin("clk_buf{}".format(port), "INPUT") - # 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_pin_wmask_dff(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - for bit in range(self.num_wmasks): - # input - self.add_pin("wmask{}[{}]".format(port, bit), "INPUT") - # output - self.add_pin("bank_wmask{}[{}]".format(port, bit), "OUTPUT") - #clk_buf, regard as input - self.add_pin("clk_buf{}".format(port), "INPUT") - # 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_pin_spare_wen_dff(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - for bit in range(self.num_spare_cols): - if self.num_spare_cols == 1: - # input - self.add_pin("spare_wen{}".format(port), "INPUT") - # output - self.add_pin("bank_spare_wen{}".format(port), "OUTPUT") - else: - # input - self.add_pin("spare_wen{}[{}]".format(port, bit), "INPUT") - # output - self.add_pin("bank_spare_wen{}[{}]".format(port, bit), "OUTPUT") - #clk_buf, regard as input - self.add_pin("clk_buf{}".format(port), "INPUT") - # 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_pin_control(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - # Inputs - self.add_pin("csb{}".format(port), "INPUT") - if port in self.readwrite_ports: - self.add_pin("web{}".format(port), "INPUT") - self.add_pin("clk{}".format(port), "INPUT") - if self.has_rbl: - self.add_pin("rbl_bl{}".format(port), "INPUT") - # Outputs - if port in self.read_ports: - self.add_pin("s_en{}".format(port), "OUTPUT") - if port in self.write_ports: - self.add_pin("w_en{}".format(port), "OUTPUT") - self.add_pin("p_en_bar{}".format(port), "OUTPUT") - self.add_pin("wl_en{}".format(port), "OUTPUT") - self.add_pin("clk_buf{}".format(port), "OUTPUT") - - # Standard supply and ground names - # not sure if this part should stay inside the for loop - 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_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_netlist_bank(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - self.add_pin_bank() - self.bank_inst = self.create_bank(0) - - # 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 Bank", datetime.datetime.now(), start_time) - - def create_netlist_control(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - self.add_pin_control() - self.control_logic_insts = self.create_control_logic() # be aware, multi-insts possible - - # 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 Control", datetime.datetime.now(), start_time) - - def create_netlist_row_addr_dff(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - self.add_pin_row_addr_dff() - self.row_addr_dff_insts = self.create_row_addr_dff() # be aware, multi-insts possible - - # 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 row_addr_dff", datetime.datetime.now(), start_time) - - def create_netlist_col_addr_dff(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - if self.col_addr_dff: - self.add_pin_col_addr_dff() - self.col_addr_dff_insts = self.create_col_addr_dff() # be aware, multi-insts possible, or none - - # 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 col_addr_dff", datetime.datetime.now(), start_time) - else: - return False # no col_addr_dff will be generated - - def create_netlist_data_dff(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - self.add_pin_data_dff() - self.data_dff_insts = self.create_data_dff() # be aware, multi-insts possible - - # 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 data dff", datetime.datetime.now(), start_time) - - def create_netlist_wmask_dff(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - if self.write_size != self.word_size: - self.add_pin_wmask_dff() - self.wmask_dff_insts = self.create_wmask_dff() # be aware, multi-insts possible, or none - - # 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 wmask dff", datetime.datetime.now(), start_time) - else: - return False # no wmask_dff will be generated - - def create_netlist_spare_wen_dff(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - if self.num_spare_cols: - self.add_pin_spare_wen_dff() - self.spare_wen_dff_insts = self.create_spare_wen_dff() # be aware, multi-insts possible, or none - - # 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 spare wen dff", datetime.datetime.now(), start_time) - else: - self.num_spare_cols = 0 - return False # no spare_col will be generated - - 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_layout_bank_only(self): - start_time = datetime.datetime.now() - #Placement - self.place_bank(self.bank_inst, [0, 0], 1, 1) - if not OPTS.is_unit_test: - print_time("Bank Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_bank_only() - - 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_control_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.control_logic_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("Control Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_control_only(instance_index=instance_index) - - 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_row_addr_dff_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.row_addr_dff_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("row_addr_dff Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_row_addr_dff_only(instance_index=instance_index) - - 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_col_addr_dff_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.col_addr_dff_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("col_addr_dff Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_col_addr_dff_only(instance_index=instance_index) - - 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_data_dff_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.data_dff_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("data_dff Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_data_dff_only(instance_index=instance_index) - - 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_wmask_dff_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.wmask_dff_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("wmask_dff Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_wmask_dff_only(instance_index=instance_index) - - 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_spare_wen_dff_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.spare_wen_dff_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("spare_wen_dff Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_spare_wen_dff_only(instance_index=instance_index) - - 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 route_bank_only(self): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_bank_only_pins() - print("====================================================") - for pin in self.pins: - print(pin) - - # Some technologies have an isolation - self.add_dnwell(inflate=2.5) - - def route_control_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_control_only_pins(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # Some technologies have an isolation - self.add_dnwell(inflate=2.5) - - def route_row_addr_dff_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_row_addr_dff_only_pins(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # Some technologies have an isolation - self.add_dnwell(inflate=2.5) - - def route_col_addr_dff_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_col_addr_dff_only_pins(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # Some technologies have an isolation - self.add_dnwell(inflate=2.5) - - def route_data_dff_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_data_dff_only_pins(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # Some technologies have an isolation - self.add_dnwell(inflate=2.5) - - def route_wmask_dff_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_wmask_dff_only_pins(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # Some technologies have an isolation - self.add_dnwell(inflate=2.5) - - def route_spare_wen_dff_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_spare_wen_dff_only_pins(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # Some technologies have an isolation - self.add_dnwell(inflate=2.5) - - def add_layout_spare_wen_dff_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - for bit in range(self.num_spare_cols): - if self.num_spare_cols == 1: - # input - self.add_io_pin(self.spare_wen_dff_insts[port], - "din_{}".format(bit), # old name - "spare_wen{}".format(port), # new name - start_layer=pin_layer) - # output - self.add_io_pin(self.spare_wen_dff_insts[port], - "dout_{}".format(bit), - "bank_spare_wen{}".format(port), - start_layer=pin_layer) - else: - # input - self.add_io_pin(self.spare_wen_dff_insts[port], - "din_{}".format(bit), # old name - "spare_wen{}[{}]".format(port, bit), # new name - start_layer=pin_layer) - # output - self.add_io_pin(self.spare_wen_dff_insts[port], - "dout_{}".format(bit), - "bank_spare_wen{}[{}]".format(port, bit), - start_layer=pin_layer) - #clk_buf, regard as input - self.add_io_pin(self.spare_wen_dff_insts[port], - "clk", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_layout_wmask_dff_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - for bit in range(self.num_wmasks): - # input - self.add_io_pin(self.wmask_dff_insts[port], - "din_{}".format(bit), - "wmask{}[{}]".format(port, bit), - start_layer=pin_layer) - # output - self.add_io_pin(self.wmask_dff_insts[port], - "dout_{}".format(bit), - "bank_wmask{}[{}]".format(port, bit), - start_layer=pin_layer) - #clk_buf, regard as input - self.add_io_pin(self.wmask_dff_insts[port], - "clk", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_layout_data_dff_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - for bit in range(self.word_size + self.num_spare_cols): - # input - self.add_io_pin(self.data_dff_insts[port], - "din_{}".format(bit), - "din{}[{}]".format(port, bit), - start_layer=pin_layer) - # output - self.add_io_pin(self.data_dff_insts[port], - "dout_{}".format(bit), - "bank_din{}[{}]".format(port, bit), - start_layer=pin_layer) - #clk_buf, regard as input - self.add_io_pin(self.data_dff_insts[port], - "clk", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_layout_col_addr_dff_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - for bit in range(self.col_addr_size): - #input - self.add_io_pin(self.col_addr_dff_insts[port], - "din_{}".format(bit), # old name - "addr{}[{}]".format(port, bit), # new name - start_layer=pin_layer) - #output - self.add_io_pin(self.col_addr_dff_insts[port], - "dout_{}".format(bit), - "a{}[{}]".format(port, bit), - start_layer=pin_layer) - #clk_buf, regard as input - self.add_pin(self.col_addr_dff_insts[port], - "clk", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_layout_row_addr_dff_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - for bit in range(self.row_addr_size): - #input - #debug - print("this is port number:{0}".format(port)) - print("insts:{0}".format(self.row_addr_dff_insts[port])) - self.add_io_pin(self.row_addr_dff_insts[port], - "din_{}".format(bit + self.col_addr_size), # old name - "addr{}[{}]".format(port, bit + self.col_addr_size), # new name - start_layer=pin_layer) - #output - self.add_io_pin(self.row_addr_dff_insts[port], - "dout_{}".format(bit + self.col_addr_size), - "a{}[{}]".format(port, bit + self.col_addr_size), - start_layer=pin_layer) - #clk_buf, regard as input - self.add_io_pin(self.row_addr_dff_insts[port], - "clk", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_layout_control_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - # Inputs - self.add_io_pin(self.control_logic_insts[port], - "csb", #old name - "csb{}".format(port), #new name - start_layer=pin_layer) - if port in self.readwrite_ports: - self.add_io_pin(self.control_logic_insts[port], - "web", - "web{}".format(port), - start_layer=pin_layer) - self.add_io_pin(self.control_logic_insts[port], - "clk", - "clk{}".format(port), - start_layer=pin_layer) - if self.has_rbl: - self.add_io_pin(self.control_logic_insts[port], - "rbl_bl", - "rbl_bl{}".format(port), - start_layer=pin_layer) - # Outputs - if port in self.read_ports: - self.add_io_pin(self.control_logic_insts[port], - "s_en", - "s_en{}".format(port), - start_layer=pin_layer) - if port in self.write_ports: - self.add_io_pin(self.control_logic_insts[port], - "w_en", - "w_en{}".format(port), - start_layer=pin_layer) - self.add_io_pin(self.control_logic_insts[port], - "p_en_bar", - "p_en_bar{}".format(port), - start_layer=pin_layer) - self.add_io_pin(self.control_logic_insts[port], - "wl_en", - "wl_en{}".format(port), - start_layer=pin_layer) - self.add_io_pin(self.control_logic_insts[port], - "clk_buf", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_layout_bank_only_pins(self, add_vias=True): - """ - Add the top-level pins for a single bank SRAM with control. - """ - # 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] - - for 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),#old name - "dout{0}[{1}]".format(port, bit),#new name - start_layer=pin_layer) - """ Adding pins for Bank module""" - for port in self.all_ports: - if self.has_rbl: - self.add_io_pin(self.bank_inst, - "rbl_bl_{0}_{0}".format(port), - "rbl_bl{0}".format(port), - start_layer=pin_layer) - for port in self.write_ports: - for bit in range(self.word_size + self.num_spare_cols): - self.add_io_pin(self.bank_inst, - "din{0}_{1}".format(port, bit), - "bank_din{0}[{1}]".format(port, bit), - start_layer=pin_layer) - # manuel change position, so not at same y - for port in self.all_ports: - for bit in range(self.bank_addr_size): - self.change_layout_pin_position(self.bank_inst, - "addr{0}_{1}".format(port, bit), - "addr{0}[{1}]".format(port, bit), - start_layer=pin_layer, - distance=bit) - for port in self.read_ports: - self.change_layout_pin_position(self.bank_inst, - "s_en{0}".format(port), - "s_en{0}".format(port), - start_layer=pin_layer, - distance=4) - for port in self.all_ports: - self.change_layout_pin_position(self.bank_inst, - "p_en_bar{0}".format(port), - "p_en_bar{0}".format(port), - start_layer=pin_layer, - distance=2) - for port in self.write_ports: - self.change_layout_pin_position(self.bank_inst, - "w_en{0}".format(port), - "w_en{0}".format(port), - start_layer=pin_layer) - - for bit in range(self.num_wmasks): - self.add_io_pin(self.bank_inst, - "bank_wmask{0}_{1}".format(port, bit), - "bank_wmask{0}[{1}]".format(port, bit), - start_layer=pin_layer) - for bit in range(self.num_spare_cols): - if self.num_spare_cols == 1: - self.add_io_pin(self.bank_inst, - "bank_spare_wen{0}_{1}".format(port, bit), - "bank_spare_wen{0}".format(port), - start_layer=pin_layer) - else: - self.add_io_pin(self.bank_inst, - "bank_spare_wen{0}_{1}".format(port, bit), - "bank_spare_wen{0}[{1}]".format(port, bit), - start_layer=pin_layer) - if port in self.all_ports: - self.add_io_pin(self.bank_inst, - "wl_en{0}".format(port), - "wl_en{0}".format(port), - start_layer=pin_layer) - - def change_layout_pin_position(self, instance, pin_name, new_name, start_layer=None, directions=None, distance=1): - """ - Add a signle input or output pin up to metal 3. - This additonal operation make sure pins are not at the same y - """ - pin = instance.get_pin(pin_name) - - if not start_layer: - start_layer = pin.layer - # Just use the power pin function for now to save code - self.add_power_pin(new_name, vector(pin.center()[0], pin.center()[1] + self.m3_pitch * distance * 2), start_layer=start_layer, directions=directions) - - 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): - """ - 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)) - - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=bbox, - design=self) - 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(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): - """ 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 - # change the order - - 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) - #if OPTS.route_supplies: - # self.route_supplies(init_bbox) - """ - if OPTS.route_supplies: - self.route_supplies(init_bbox) - 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) - """ - - - 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.m3_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/modules/sram_openroad.py b/compiler/modules/sram_openroad.py deleted file mode 100644 index 514a042b..00000000 --- a/compiler/modules/sram_openroad.py +++ /dev/null @@ -1,1485 +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 file aiming to create the sram module without routing -# provide methods of seperated lef file generation -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 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 place_io_pins(self, bbox): - """Place IO pins""" - # 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)) - - from openram.router.io_pin_placer import io_pin_placer as placer - pl = placer(layers=self.m3_stack, - bbox=bbox, - design=self) - pl.add_io_pins(pins_to_route) - #pl.design.remove_layout_pin("wmask0[0]") # this could use for deleting pins - - def get_closest_edge(self, point, bbox): - """ Return a point's the closest edge and the edge's axis direction. """ - - ll, ur = 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 create_layout_openroad(self): - # only placement of submoduels & io pins - # not routed inner-signal/escape-signal/pdn - start_time = datetime.datetime.now() - self.place_instances() - if not OPTS.is_unit_test: - print_time("Placement", datetime.datetime.now(), start_time) - - # Some technologies have an isolation - self.add_dnwell(inflate=2.5) - # add IO pins - init_bbox = self.get_bbox() - - self.add_layout_pins() - self.place_io_pins(bbox=init_bbox) - - # add power rings - # use current supply router - if OPTS.route_supplies: - self.route_supplies(init_bbox) - - 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): - """ - 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)) - - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=bbox, - design=self) - 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(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): - """ 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(init_bbox) - 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.m3_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/modules/sram_part.py b/compiler/modules/sram_part.py deleted file mode 100644 index 10b99145..00000000 --- a/compiler/modules/sram_part.py +++ /dev/null @@ -1,2623 +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 file aims to use openroad to do the routing -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 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_pin_bank(self): - # lef file use this pin name, should be same with new pin - """ Adding pins for Bank module""" - 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") - for port in self.all_ports: - if self.has_rbl: - self.add_pin("rbl_bl{0}".format(port), "OUTPUT") - for port in self.write_ports: - for bit in range(self.word_size + self.num_spare_cols): - self.add_pin("bank_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") - - # For more than one bank, we have a bank select and name - # the signals gated_*. - for port in self.read_ports: - self.add_pin("s_en{0}".format(port), "INPUT") - for port in self.all_ports: - self.add_pin("p_en_bar{0}".format(port), "INPUT") - for port in self.write_ports: - self.add_pin("w_en{0}".format(port), "INPUT") - for bit in range(self.num_wmasks): - self.add_pin("bank_wmask{0}[{1}]".format(port, bit), "INPUT") - for bit in range(self.num_spare_cols): - if self.num_spare_cols ==1: - self.add_pin("bank_spare_wen{0}".format(port), "INPUT") - else: - self.add_pin("bank_spare_wen{0}[{1}]".format(port, bit), "INPUT") - for port in self.all_ports: - self.add_pin("wl_en{0}".format(port), "INPUT") - - # 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_pin_row_addr_dff(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - for bit in range(self.row_addr_size): - #input - self.add_pin("addr{}[{}]".format(port, bit + self.col_addr_size), "INPUT") - print("addr{}[{}]".format(port, bit), "INPUT") - #output - self.add_pin("a{}[{}]".format(port, bit + self.col_addr_size), "OUTPUT") - #clk_buf, regard as input - self.add_pin("clk_buf{}".format(port), "INPUT") - # 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_pin_col_addr_dff(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - for bit in range(self.col_addr_size): - #input - self.add_pin("addr{}[{}]".format(port, bit), "INPUT") - #output - self.add_pin("a{}[{}]".format(port, bit), "OUTPUT") - #clk_buf, regard as input - self.add_pin("clk_buf{}".format(port), "INPUT") - # 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_pin_data_dff(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - for bit in range(self.word_size + self.num_spare_cols): - # input - self.add_pin("din{}[{}]".format(port, bit), "INPUT") - # output - self.add_pin("bank_din{}[{}]".format(port, bit), "OUTPUT") - #clk_buf, regard as input - self.add_pin("clk_buf{}".format(port), "INPUT") - # 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_pin_wmask_dff(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - for bit in range(self.num_wmasks): - # input - self.add_pin("wmask{}[{}]".format(port, bit), "INPUT") - # output - self.add_pin("bank_wmask{}[{}]".format(port, bit), "OUTPUT") - #clk_buf, regard as input - self.add_pin("clk_buf{}".format(port), "INPUT") - # 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_pin_spare_wen_dff(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - for bit in range(self.num_spare_cols): - if self.num_spare_cols == 1: - # input - self.add_pin("spare_wen{}".format(port), "INPUT") - # output - self.add_pin("bank_spare_wen{}".format(port), "OUTPUT") - else: - # input - self.add_pin("spare_wen{}[{}]".format(port, bit), "INPUT") - # output - self.add_pin("bank_spare_wen{}[{}]".format(port, bit), "OUTPUT") - #clk_buf, regard as input - self.add_pin("clk_buf{}".format(port), "INPUT") - # 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_pin_control(self): - # lef file use this pin name, should be same with new pin - for port in self.all_ports: - # Inputs - self.add_pin("csb{}".format(port), "INPUT") - if port in self.readwrite_ports: - self.add_pin("web{}".format(port), "INPUT") - self.add_pin("clk{}".format(port), "INPUT") - if self.has_rbl: - self.add_pin("rbl_bl{}".format(port), "INPUT") - # Outputs - if port in self.read_ports: - self.add_pin("s_en{}".format(port), "OUTPUT") - if port in self.write_ports: - self.add_pin("w_en{}".format(port), "OUTPUT") - self.add_pin("p_en_bar{}".format(port), "OUTPUT") - self.add_pin("wl_en{}".format(port), "OUTPUT") - self.add_pin("clk_buf{}".format(port), "OUTPUT") - - # Standard supply and ground names - # not sure if this part should stay inside the for loop - 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_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_netlist_bank(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - self.add_pin_bank() - self.bank_inst = self.create_bank(0) - - # 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 Bank", datetime.datetime.now(), start_time) - - def create_netlist_control(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - self.add_pin_control() - self.control_logic_insts = self.create_control_logic() # be aware, multi-insts possible - - # 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 Control", datetime.datetime.now(), start_time) - - def create_netlist_row_addr_dff(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - self.add_pin_row_addr_dff() - self.row_addr_dff_insts = self.create_row_addr_dff() # be aware, multi-insts possible - - # 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 row_addr_dff", datetime.datetime.now(), start_time) - - def create_netlist_col_addr_dff(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - if self.col_addr_dff: - self.add_pin_col_addr_dff() - self.col_addr_dff_insts = self.create_col_addr_dff() # be aware, multi-insts possible, or none - - # 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 col_addr_dff", datetime.datetime.now(), start_time) - else: - return False # no col_addr_dff will be generated - - def create_netlist_data_dff(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - self.add_pin_data_dff() - self.data_dff_insts = self.create_data_dff() # be aware, multi-insts possible - - # 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 data dff", datetime.datetime.now(), start_time) - - def create_netlist_wmask_dff(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - if self.write_size != self.word_size: - self.add_pin_wmask_dff() - self.wmask_dff_insts = self.create_wmask_dff() # be aware, multi-insts possible, or none - - # 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 wmask dff", datetime.datetime.now(), start_time) - else: - return False # no wmask_dff will be generated - - def create_netlist_spare_wen_dff(self): - """ Netlist creation """ - - start_time = datetime.datetime.now() - - # Must create the control logic before pins to get the pins - self.add_modules() - if self.num_spare_cols: - self.add_pin_spare_wen_dff() - self.spare_wen_dff_insts = self.create_spare_wen_dff() # be aware, multi-insts possible, or none - - # 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 spare wen dff", datetime.datetime.now(), start_time) - else: - self.num_spare_cols = 0 - return False # no spare_col will be generated - - 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_layout_bank_only(self): - start_time = datetime.datetime.now() - #Placement - self.place_bank(self.bank_inst, [0, 0], 1, 1) - if not OPTS.is_unit_test: - print_time("Bank Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_bank_only() - - 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_control_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.control_logic_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("Control Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_control_only(instance_index=instance_index) - - 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_row_addr_dff_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.row_addr_dff_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("row_addr_dff Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_row_addr_dff_only(instance_index=instance_index) - - 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_col_addr_dff_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.col_addr_dff_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("col_addr_dff Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_col_addr_dff_only(instance_index=instance_index) - - 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_data_dff_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.data_dff_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("data_dff Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_data_dff_only(instance_index=instance_index) - - 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_wmask_dff_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.wmask_dff_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("wmask_dff Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_wmask_dff_only(instance_index=instance_index) - - 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_spare_wen_dff_only(self, instance_index=0): - start_time = datetime.datetime.now() - # Placement - # We will generate different layout for different port(if there are multi-port) - port = instance_index - self.spare_wen_dff_insts[port].place(vector(0,0)) - - if not OPTS.is_unit_test: - print_time("spare_wen_dff Placement", datetime.datetime.now(), start_time) - start_time = datetime.datetime.now() - self.route_spare_wen_dff_only(instance_index=instance_index) - - 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 route_bank_only(self): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_bank_only_pins() - pins_to_route_bank = self.add_pins_to_route_bank() - print("====================================================") - for pin in self.pins: - print(pin) - - # 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 - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=init_bbox, - design=self) - rtr.route(pins_to_route_bank) - if OPTS.route_supplies: - self.route_supplies(init_bbox) - - def route_control_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_control_only_pins(instance_index=instance_index) - pins_to_route_control = self.add_pins_to_route_control(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # 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 - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=init_bbox, - design=self) - rtr.route(pins_to_route_control) - if OPTS.route_supplies: - self.route_supplies(init_bbox) - - def route_row_addr_dff_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_row_addr_dff_only_pins(instance_index=instance_index) - pins_to_route_row_addr_dff = self.add_pins_to_route_row_addr_dff(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # 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 - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=init_bbox, - design=self) - rtr.route(pins_to_route_row_addr_dff) - if OPTS.route_supplies: - self.route_supplies(init_bbox) - - def route_col_addr_dff_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_col_addr_dff_only_pins(instance_index=instance_index) - pins_to_route_col_addr_dff = self.add_pins_to_route_col_addr_dff(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # 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 - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=init_bbox, - design=self) - rtr.route(pins_to_route_col_addr_dff) - if OPTS.route_supplies: - self.route_supplies(init_bbox) - - def route_data_dff_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_data_dff_only_pins(instance_index=instance_index) - pins_to_route_data_dff = self.add_pins_to_route_data_dff(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # 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 - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=init_bbox, - design=self) - rtr.route(pins_to_route_data_dff) - if OPTS.route_supplies: - self.route_supplies(init_bbox) - - def route_wmask_dff_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_wmask_dff_only_pins(instance_index=instance_index) - pins_to_route_wmask_dff = self.add_pins_to_route_wmask_dff(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # 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 - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=init_bbox, - design=self) - rtr.route(pins_to_route_wmask_dff) - if OPTS.route_supplies: - self.route_supplies(init_bbox) - - def route_spare_wen_dff_only(self, instance_index=0): - # We add the vias to M3 before routing supplies because - # they might create some blockages - self.add_layout_spare_wen_dff_only_pins(instance_index=instance_index) - pins_to_route_spare_wen_dff = self.add_pins_to_route_spare_wen_dff(instance_index=instance_index) - print("====================================================") - for pin in self.pins: - print(pin) - - # 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 - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=init_bbox, - design=self) - rtr.route(pins_to_route_spare_wen_dff) - if OPTS.route_supplies: - self.route_supplies(init_bbox) - - def add_pins_to_route_spare_wen_dff(self, instance_index=0): - #only contains signal pins, no power pins - """ Adding to route pins for spare wen dff """ - pins_to_route = [] - port = instance_index - - for bit in range(self.num_spare_cols): - if self.num_spare_cols == 1: - # input - pins_to_route.append("spare_wen{}".format(port)) - # output - pins_to_route.append("bank_spare_wen{}".format(port)) - else: - # input - pins_to_route.append("spare_wen{}[{}]".format(port, bit)) - # output - pins_to_route.append("bank_spare_wen{}[{}]".format(port, bit)) - #clk_buf, regard as input - pins_to_route.append("clk_buf{}".format(port)) - - return pins_to_route - - def add_layout_spare_wen_dff_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - for bit in range(self.num_spare_cols): - if self.num_spare_cols == 1: - # input - self.add_io_pin(self.spare_wen_dff_insts[port], - "din_{}".format(bit), # old name - "spare_wen{}".format(port), # new name - start_layer=pin_layer) - # output - self.add_io_pin(self.spare_wen_dff_insts[port], - "dout_{}".format(bit), - "bank_spare_wen{}".format(port), - start_layer=pin_layer) - else: - # input - self.add_io_pin(self.spare_wen_dff_insts[port], - "din_{}".format(bit), # old name - "spare_wen{}[{}]".format(port, bit), # new name - start_layer=pin_layer) - # output - self.add_io_pin(self.spare_wen_dff_insts[port], - "dout_{}".format(bit), - "bank_spare_wen{}[{}]".format(port, bit), - start_layer=pin_layer) - #clk_buf, regard as input - self.add_io_pin(self.spare_wen_dff_insts[port], - "clk", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_pins_to_route_wmask_dff(self, instance_index=0): - #only contains signal pins, no power pins - """ Adding to route pins for wmask addr dff """ - pins_to_route = [] - port = instance_index - for bit in range(self.num_wmasks): - # input - pins_to_route.append("wmask{}[{}]".format(port, bit)) - # output - pins_to_route.append("bank_wmask{}[{}]".format(port, bit)) - #clk_buf, regard as input - pins_to_route.append("clk_buf{}".format(port)) - - return pins_to_route - - def add_layout_wmask_dff_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - for bit in range(self.num_wmasks): - # input - self.add_io_pin(self.wmask_dff_insts[port], - "din_{}".format(bit), - "wmask{}[{}]".format(port, bit), - start_layer=pin_layer) - # output - self.add_io_pin(self.wmask_dff_insts[port], - "dout_{}".format(bit), - "bank_wmask{}[{}]".format(port, bit), - start_layer=pin_layer) - #clk_buf, regard as input - self.add_io_pin(self.wmask_dff_insts[port], - "clk", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_pins_to_route_data_dff(self, instance_index=0): - #only contains signal pins, no power pins - """ Adding to route pins for col addr dff """ - pins_to_route = [] - port = instance_index - for bit in range(self.word_size + self.num_spare_cols): - # input - pins_to_route.append("din{}[{}]".format(port, bit)) - # output - pins_to_route.append("bank_din{}[{}]".format(port, bit)) - #clk_buf, regard as input - pins_to_route.append("clk_buf{}".format(port)) - - return pins_to_route - - def add_layout_data_dff_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - for bit in range(self.word_size + self.num_spare_cols): - # input - self.add_io_pin(self.data_dff_insts[port], - "din_{}".format(bit), - "din{}[{}]".format(port, bit), - start_layer=pin_layer) - # output - self.add_io_pin(self.data_dff_insts[port], - "dout_{}".format(bit), - "bank_din{}[{}]".format(port, bit), - start_layer=pin_layer) - #clk_buf, regard as input - self.add_io_pin(self.data_dff_insts[port], - "clk", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_pins_to_route_col_addr_dff(self, instance_index=0): - #only contains signal pins, no power pins - """ Adding to route pins for col addr dff """ - pins_to_route = [] - port = instance_index - for bit in range(self.col_addr_size): - #input - pins_to_route.append("addr{}[{}]".format(port, bit)) - #output - pins_to_route.append("a{}[{}]".format(port, bit)) - #clk_buf, regard as input - pins_to_route.append("clk_buf{}".format(port)) - - return pins_to_route - - def add_layout_col_addr_dff_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - for bit in range(self.col_addr_size): - #input - self.add_io_pin(self.col_addr_dff_insts[port], - "din_{}".format(bit), # old name - "addr{}[{}]".format(port, bit), # new name - start_layer=pin_layer) - #output - self.add_io_pin(self.col_addr_dff_insts[port], - "dout_{}".format(bit), - "a{}[{}]".format(port, bit), - start_layer=pin_layer) - #clk_buf, regard as input - self.add_pin(self.col_addr_dff_insts[port], - "clk", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_pins_to_route_row_addr_dff(self, instance_index=0): - #only contains signal pins, no power pins - """ Adding to route pins for row addr dff """ - pins_to_route = [] - port = instance_index - for bit in range(self.row_addr_size): - #input - pins_to_route.append("addr{}[{}]".format(port, bit + self.col_addr_size)) - #output - pins_to_route.append("a{}[{}]".format(port, bit + self.col_addr_size)) - #clk_buf, regard as input - pins_to_route.append("clk_buf{}".format(port)) - - return pins_to_route - - def add_layout_row_addr_dff_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - for bit in range(self.row_addr_size): - #input - #debug - print("this is port number:{0}".format(port)) - print("insts:{0}".format(self.row_addr_dff_insts[port])) - self.add_io_pin(self.row_addr_dff_insts[port], - "din_{}".format(bit + self.col_addr_size), # old name - "addr{}[{}]".format(port, bit + self.col_addr_size), # new name - start_layer=pin_layer) - #output - self.add_io_pin(self.row_addr_dff_insts[port], - "dout_{}".format(bit + self.col_addr_size), - "a{}[{}]".format(port, bit + self.col_addr_size), - start_layer=pin_layer) - #clk_buf, regard as input - self.add_io_pin(self.row_addr_dff_insts[port], - "clk", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_pins_to_route_control(self, instance_index=0): - #only contains signal pins, no power pins - """ Adding to route pins for control """ - pins_to_route = [] - port = instance_index - # Inputs - pins_to_route.append("csb{}".format(port)) - if port in self.readwrite_ports: - pins_to_route.append("web{}".format(port)), - pins_to_route.append("clk{}".format(port)) - if self.has_rbl: - pins_to_route.append("rbl_bl{}".format(port)) - # Outputs - if port in self.read_ports: - pins_to_route.append("s_en{}".format(port)) - if port in self.write_ports: - pins_to_route.append("w_en{}".format(port)) - pins_to_route.append("p_en_bar{}".format(port)) - pins_to_route.append("wl_en{}".format(port)) - pins_to_route.append("clk_buf{}".format(port)) - - return pins_to_route - - def add_layout_control_only_pins(self, add_vias=True, instance_index=0): - """ - Add the top-level pins for a single bank SRAM with control. - """ - port = instance_index - # 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] - - # Inputs - self.add_io_pin(self.control_logic_insts[port], - "csb", #old name - "csb{}".format(port), #new name - start_layer=pin_layer) - if port in self.readwrite_ports: - self.add_io_pin(self.control_logic_insts[port], - "web", - "web{}".format(port), - start_layer=pin_layer) - self.add_io_pin(self.control_logic_insts[port], - "clk", - "clk{}".format(port), - start_layer=pin_layer) - if self.has_rbl: - self.add_io_pin(self.control_logic_insts[port], - "rbl_bl", - "rbl_bl{}".format(port), - start_layer=pin_layer) - # Outputs - if port in self.read_ports: - self.add_io_pin(self.control_logic_insts[port], - "s_en", - "s_en{}".format(port), - start_layer=pin_layer) - if port in self.write_ports: - self.add_io_pin(self.control_logic_insts[port], - "w_en", - "w_en{}".format(port), - start_layer=pin_layer) - self.add_io_pin(self.control_logic_insts[port], - "p_en_bar", - "p_en_bar{}".format(port), - start_layer=pin_layer) - self.add_io_pin(self.control_logic_insts[port], - "wl_en", - "wl_en{}".format(port), - start_layer=pin_layer) - self.add_io_pin(self.control_logic_insts[port], - "clk_buf", - "clk_buf{}".format(port), - start_layer=pin_layer) - - def add_pins_to_route_bank(self): - #only contains signal pins, no power pins - """ Adding to route pins for Bank """ - pins_to_route = [] - for 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 port in self.all_ports: - if self.has_rbl: - pins_to_route.append("rbl_bl{0}".format(port)) - for port in self.write_ports: - for bit in range(self.word_size + self.num_spare_cols): - pins_to_route.append("bank_din{0}[{1}]".format(port, bit)) - for port in self.all_ports: - for bit in range(self.bank_addr_size): - pins_to_route.append("addr{0}[{1}]".format(port, bit)) - - # For more than one bank, we have a bank select and name - # the signals gated_*. - for port in self.read_ports: - pins_to_route.append("s_en{0}".format(port)) - for port in self.all_ports: - pins_to_route.append("p_en_bar{0}".format(port)) - for port in self.write_ports: - pins_to_route.append("w_en{0}".format(port)) - for bit in range(self.num_wmasks): - pins_to_route.append("bank_wmask{0}[{1}]".format(port, bit)) - for bit in range(self.num_spare_cols): - if self.num_spare_cols == 1: - pins_to_route.append("bank_spare_wen{0}".format(port, bit)) - else: - pins_to_route.append("bank_spare_wen{0}[{1}]".format(port, bit)) - for port in self.all_ports: - pins_to_route.append("wl_en{0}".format(port)) - - return pins_to_route - - def add_layout_bank_only_pins(self, add_vias=True): - """ - Add the top-level pins for a single bank SRAM with control. - """ - # 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] - - for 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),#old name - "dout{0}[{1}]".format(port, bit),#new name - start_layer=pin_layer) - """ Adding pins for Bank module""" - for port in self.all_ports: - if self.has_rbl: - self.add_io_pin(self.bank_inst, - "rbl_bl_{0}_{0}".format(port), - "rbl_bl{0}".format(port), - start_layer=pin_layer) - for port in self.write_ports: - for bit in range(self.word_size + self.num_spare_cols): - self.add_io_pin(self.bank_inst, - "din{0}_{1}".format(port, bit), - "bank_din{0}[{1}]".format(port, bit), - start_layer=pin_layer) - # manuel change position, so not at same y - for port in self.all_ports: - for bit in range(self.bank_addr_size): - self.change_layout_pin_position(self.bank_inst, - "addr{0}_{1}".format(port, bit), - "addr{0}[{1}]".format(port, bit), - start_layer=pin_layer, - distance=bit) - for port in self.read_ports: - self.change_layout_pin_position(self.bank_inst, - "s_en{0}".format(port), - "s_en{0}".format(port), - start_layer=pin_layer, - distance=4) - for port in self.all_ports: - self.change_layout_pin_position(self.bank_inst, - "p_en_bar{0}".format(port), - "p_en_bar{0}".format(port), - start_layer=pin_layer, - distance=2) - for port in self.write_ports: - self.change_layout_pin_position(self.bank_inst, - "w_en{0}".format(port), - "w_en{0}".format(port), - start_layer=pin_layer) - - for bit in range(self.num_wmasks): - self.add_io_pin(self.bank_inst, - "bank_wmask{0}_{1}".format(port, bit), - "bank_wmask{0}[{1}]".format(port, bit), - start_layer=pin_layer) - for bit in range(self.num_spare_cols): - if self.num_spare_cols == 1: - self.add_io_pin(self.bank_inst, - "bank_spare_wen{0}_{1}".format(port, bit), - "bank_spare_wen{0}".format(port), - start_layer=pin_layer) - else: - self.add_io_pin(self.bank_inst, - "bank_spare_wen{0}_{1}".format(port, bit), - "bank_spare_wen{0}[{1}]".format(port, bit), - start_layer=pin_layer) - if port in self.all_ports: - self.add_io_pin(self.bank_inst, - "wl_en{0}".format(port), - "wl_en{0}".format(port), - start_layer=pin_layer) - - def change_layout_pin_position(self, instance, pin_name, new_name, start_layer=None, directions=None, distance=1): - """ - Add a signle input or output pin up to metal 3. - This additonal operation make sure pins are not at the same y - """ - pin = instance.get_pin(pin_name) - - if not start_layer: - start_layer = pin.layer - # Just use the power pin function for now to save code - self.add_power_pin(new_name, vector(pin.center()[0], pin.center()[1] + self.m3_pitch * distance * 2), start_layer=start_layer, directions=directions) - - 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): - """ - 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)) - - from openram.router import signal_escape_router as router - rtr = router(layers=self.m3_stack, - bbox=bbox, - design=self) - 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(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): - """ 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 - # change the order - - 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) - #if OPTS.route_supplies: - # self.route_supplies(init_bbox) - """ - if OPTS.route_supplies: - self.route_supplies(init_bbox) - 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) - """ - - - 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.m3_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/io_pin_placer.py b/compiler/router/io_pin_placer.py index ae6677b9..f3a416ab 100644 --- a/compiler/router/io_pin_placer.py +++ b/compiler/router/io_pin_placer.py @@ -21,7 +21,7 @@ class io_pin_placer(router): # New pins are the side supply pins self.new_pins = {} - + # added_io_pins self.io_pins_added_left = [] self.io_pins_added_right = [] @@ -30,7 +30,7 @@ class io_pin_placer(router): # fake_pins, use for rename self.io_pins_fake =[] - + def get_closest_edge(self, point): """ Return a point's the closest edge and the edge's axis direction. """ @@ -52,9 +52,9 @@ class io_pin_placer(router): return "right", True return "top", False - - def initial_position(self, pins): # pins is list [] - """ Set the IO pin center at the perimeter """ + + def initial_position(self, pins): # pins is list [] + """ Set the IO pin center at the perimeter """ pattern_clk = r'^clk' pattern_addr0 = r'^addr0' pattern_addr1 = r'^addr1' @@ -82,14 +82,14 @@ class io_pin_placer(router): vertical = True elif edge == "bottom": # but for big sram, addr0[] may have pins at bottom, which is allowed vertical = False - self.store_position(pin, edge, vertical) + self.store_position(pin, edge, vertical) if re.match(pattern_addr1, pin.name): # all the addr1[] should be placed at right edge if edge == "bottom" or edge == "right": edge = "right" vertical = True elif edge == "top": # but for big sram, addr1[] may have pins at top, which is allowed vertical = False - self.store_position(pin, edge, vertical) + self.store_position(pin, edge, vertical) if re.match(pattern_din, pin.name): # din self.store_position(pin, edge, vertical) if re.match(pattern_wmask, pin.name): # wmask @@ -100,7 +100,7 @@ class io_pin_placer(router): self.store_position(pin, edge, vertical) if re.match(pattern_web, pin.name): # web self.store_position(pin, edge, vertical) - # special handle the dout pins, if r/rw ports + # special handle the dout pins, if r/rw ports for pin in pins: if re.match(pattern_dout0, pin.name): edge = "bottom" @@ -115,7 +115,7 @@ class io_pin_placer(router): def check_overlap(self, pin_position, edge): """ Return the suggested position to aviod overlap """ ll, ur = self.bbox - c = pin_position # original source pin center + c = pin_position # original source pin center offset = 0.95 + 0.19 # FIX: this is the magic number to overcome the ovetflow problem at the boundary, may need a method add_distance = 0 @@ -124,26 +124,26 @@ class io_pin_placer(router): pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down) via_to_close = False # if cannot direct place below the source pin, need move towards right, and ensure the min. distance between vias - while pin_to_close or via_to_close: - debug.warning("overlap, changing position") - add_distance = add_distance + 0.1 + while pin_to_close or via_to_close: + debug.warning("overlap, changing position") + add_distance = add_distance + 0.1 fake_center = vector(c.x + add_distance, ll.y - self.track_wire * 2 + offset) - pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down) - via_to_close = abs(fake_center.x - c.x) < 0.6 + pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down) + via_to_close = abs(fake_center.x - c.x) < 0.6 if edge == "top": fake_center = vector(c.x, ur.y + self.track_wire * 2 - offset) pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up) - via_to_close = False - # if cannot direct place below the source pin, need move towards right, and ensure the min. distance between vias - while pin_to_close or via_to_close: + via_to_close = False + # if cannot direct place below the source pin, need move towards right, and ensure the min. distance between vias + while pin_to_close or via_to_close: debug.warning("overlap, changing position") add_distance = add_distance + 0.1 fake_center = vector(c.x + add_distance, ur.y + self.track_wire * 2 - offset) - pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up) - via_to_close = abs(fake_center.x - c.x) < 0.6 + pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up) + via_to_close = abs(fake_center.x - c.x) < 0.6 + + return fake_center - return fake_center - def store_dout_position(self, pin, edge, vertical): pin_position = pin.center() @@ -151,14 +151,14 @@ class io_pin_placer(router): # store the center position, rect, layer of fake pin, here make sure the pin in the gds will be big enough layer = self.get_layer(int(not vertical)) half_wire_vector = vector([self.half_wire] * 2) - nll = pin_position - half_wire_vector - half_wire_vector + nll = pin_position - half_wire_vector - half_wire_vector nur = pin_position + half_wire_vector + half_wire_vector rect = [nll, nur] #fake_pin = [pin.name() + "_" + "fake", pin_position, rect, layer] fake_pin = graph_shape(name=pin.name + "_" + "fake", rect=rect, layer_name_pp=layer) - + if edge == "left": self.io_pins_added_left.append(fake_pin) elif edge == "bottom": @@ -168,10 +168,10 @@ class io_pin_placer(router): elif edge == "top": self.io_pins_added_up.append(fake_pin) - self.io_pins_fake.append(fake_pin) + self.io_pins_fake.append(fake_pin) debug.warning("pin added: {0}".format(fake_pin)) - - + + def store_position(self, pin, edge, vertical): # also need to store the source pin ll, ur = self.bbox c = pin.center() @@ -197,14 +197,14 @@ class io_pin_placer(router): # store the center position, rect, layer of fake pin, here make sure the pin in the gds will be big enough layer = self.get_layer(int(not vertical)) half_wire_vector = vector([self.half_wire] * 2) - nll = fake_center - half_wire_vector - half_wire_vector + nll = fake_center - half_wire_vector - half_wire_vector nur = fake_center + half_wire_vector + half_wire_vector rect = [nll, nur] #fake_pin = [pin.name() + "_" + "fake", fake_center, rect, layer] fake_pin = graph_shape(name=pin.name + "_" + "fake", rect=rect, layer_name_pp=layer) - + if edge == "left": self.io_pins_added_left.append(fake_pin) elif edge == "bottom": @@ -213,111 +213,9 @@ class io_pin_placer(router): self.io_pins_added_right.append(fake_pin) elif edge == "top": self.io_pins_added_up.append(fake_pin) - + self.io_pins_fake.append(fake_pin) debug.warning("pin added: {0}".format(fake_pin)) - - - 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) - # Relocate the pin position of addr/dout - pattern_addr = r'^addr' - pattern_dout = r'^dout' - if re.match(pattern_addr, pin.name):# all the addr[] should be placed at vertical edge - if edge == "top" or edge == "left": - edge = "left" - vertical = True - elif edge == "bottom" or edge == "right": - edge = "right" - vertical = True - - if re.match(pattern_dout, pin.name):# all the dout[] should be placed at horizontal edge - if edge == "bottom" or edge == "right": - edge = "bottom" - vertical = False - elif edge == "top" or edge == "left": - edge = "top" - vertical = False - - offset = 0.95 + 0.19 # FIX: this is the magic number to overcome the ovetflow problem at the boundary, may need a method - add_distance = 0 - # 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 + offset, c.y) - is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_left) - while is_too_close: - debug.warning("overlap, changing position") - add_distance = add_distance + 0.1#+ 0.4 + self.half_wire * 4 - fake_center = vector(ll.x - self.track_wire * 2 + offset, c.y + add_distance) - is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_left) - - if edge == "bottom": - fake_center = vector(c.x, ll.y - self.track_wire * 2 + offset) - is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down) - while is_too_close: - debug.warning("overlap, changing position") - add_distance = add_distance + 0.1#0.4 + self.half_wire * 4 - fake_center = vector(c.x + add_distance, ll.y - self.track_wire * 2 + offset) - is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down) - - if edge == "right": - fake_center = vector(ur.x + self.track_wire * 2 - offset, c.y) - is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_right) - while is_too_close: - debug.warning("overlap, changing position") - add_distance = add_distance + 0.1#0.4 + self.half_wire * 4 - fake_center = vector(ur.x + self.track_wire * 2 - offset, c.y + add_distance) - # debug - for pin_added in self.io_pins_added_right: - dis = abs(pin_added.center().y - fake_center.y) - debug.warning("current position is {0}".format(fake_center)) - debug.warning("distance from {0} is {1}".format(pin_added, dis)) - debug.warning("must disrance is {0}".format(0.4 + self.half_wire * 4)) - is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_right) - - if edge == "top": - fake_center = vector(c.x, ur.y + self.track_wire * 2 - offset) - is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up) - while is_too_close: - debug.warning("overlap, changing position") - add_distance = add_distance + 0.1#0.4 + self.half_wire * 4 - fake_center = vector(c.x + add_distance, ur.y + self.track_wire * 2 - offset) - is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up) - - # Create the fake pin shape, here make sure the pin in the gds will be big enough - layer = self.get_layer(int(not vertical)) - half_wire_vector = vector([self.half_wire] * 2) - 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=pin.name + "_" + "fake", - rect=rect, - layer_name_pp=layer) - print("this create_fake_pin") - print(pin.name) - print(pin.center) - - if edge == "left": - self.io_pins_added_left.append(pin) - elif edge == "bottom": - self.io_pins_added_down.append(pin) - elif edge == "right": - self.io_pins_added_right.append(pin) - elif edge == "top": - self.io_pins_added_up.append(pin) - debug.warning("pin added: {0}".format(pin)) - - return pin, vertical, add_distance def add_io_pins(self, pin_names): @@ -336,13 +234,13 @@ class io_pin_placer(router): for name in self.pins: pin = next(iter(self.pins[name])) pin_list.append(pin) - - self.initial_position(pin_list) + + self.initial_position(pin_list) # Change IO pin names, which means "internal name" will be used, and internal io pins will not have label such as "dout0[0]" self.replace_layout_pins(pin_names) - - + + def add_io_pins_connected(self, pin_names): """ Add IO pins on the edges WITH routing them. """ debug.info(1, "Adding IO pins on the perimeter...") @@ -361,9 +259,9 @@ class io_pin_placer(router): pin = next(iter(self.pins[name])) pin_list.append(pin) debug.warning("the pins in self.pins -> {0}".format(name)) - - self.initial_position(pin_list) - + + self.initial_position(pin_list) + # add fake io pins at the perimeter, which will be used for routing for fake_pin in self.io_pins_fake: self.design.add_layout_pin(text=fake_pin.name, @@ -372,7 +270,7 @@ class io_pin_placer(router): width=fake_pin.width(), height=fake_pin.height()) - # connect the source_pin and io_pin(target) + # connect the source_pin and io_pin(target) self.connect_pins(pin_names) # remove the fake pin before change the name, in order to avoid possible problem @@ -381,8 +279,8 @@ class io_pin_placer(router): # Change IO pin names, which means "internal name" will be used, and internal io pins will not have label such as "dout0[0]" self.replace_layout_pins(pin_names) - - + + def connect_pins(self, pin_names): # pin_names should be a list """ Add IO pins on the edges, and connect them to the internal one, not-graph like process """ debug.info(1, "connecting to io pins...") @@ -392,18 +290,18 @@ class io_pin_placer(router): source_pin = next(iter(self.pins[pin_name])) for fake_pin in self.io_pins_fake: if pin_name + "_" + "fake" == fake_pin.name: - target_pin = fake_pin - break + target_pin = fake_pin + break # special hanlde dout pins if re.match(pattern_dout, pin_name): number_str = re.findall(r'\[(\d+)\]', pin_name) if number_str: number = int(number_str[0]) if number % 2 == 0: - is_up = True + is_up = True else: is_up = False - point_list = self.decide_point(source_pin, target_pin, is_up) + point_list = self.decide_point(source_pin, target_pin, is_up) self.add_wire(point_list) # other pins else: @@ -414,7 +312,7 @@ class io_pin_placer(router): self.add_wire(point_list) - def add_wire(self, point_list): + def add_wire(self, point_list): if len(point_list) == 2: # direct connect self.add_line(point_list[0], point_list[1]) @@ -425,32 +323,32 @@ class io_pin_placer(router): self.add_line(point_list[1], point_list[2]) self.add_via(point_list[2]) self.add_line(point_list[2], point_list[3]) - + def add_line(self, point_1, point_2): if round(point_1.y, 3) == round(point_2.y, 3): - # horizontal m3 + # horizontal m3 self.design.add_path(self.get_layer(False), [point_1, point_2]) else: # vertical m4 self.design.add_path(self.get_layer(True), [point_1, point_2]) - + def add_via(self, point): # currently only m3-m4 vias are supported in this method # usd in order to make "z" shape routing only - self.design.add_via_stack_center(from_layer=self.get_layer(False), - to_layer=self.get_layer(True), + self.design.add_via_stack_center(from_layer=self.get_layer(False), + to_layer=self.get_layer(True), offset=point) - + def add_start_via(self, point): # currently only m3-m4 vias are supported in this method # used in source_pin only - self.design.add_via_stack_center(from_layer=self.get_layer(False), - to_layer=self.get_layer(True), + self.design.add_via_stack_center(from_layer=self.get_layer(False), + to_layer=self.get_layer(True), offset=point) - + def add_big_plate(self, layer, offset, width, height): # add rectagle at internal source pin, which avoid jog/non-preferred routing, but could implement shift-routing @@ -460,19 +358,19 @@ class io_pin_placer(router): offset=offset, width=width, height=height) - + def decide_point(self, source_pin, target_pin, is_up=False): ll, ur = self.bbox offset = 0.95 + 0.19 # FIX: this is the magic number to overcome the ovetflow problem at the boundary, may need a method pattern_clk = r'^clk' pattern_csb = r'^csb' - # internal -> left + # internal -> left if round(target_pin.center().x, 3) == round(ll.x - self.track_wire * 2 + offset, 3): # special handle clk0 if re.match(pattern_clk, source_pin.name): return [vector(source_pin.rc().x, source_pin.rc().y + 0.32), target_pin.lc()]# 0.32 should be same in initial_position - # special handel csb0 + # special handel csb0 elif re.match(pattern_csb, source_pin.name): return [vector(source_pin.rc().x, source_pin.rc().y - 0.32), target_pin.lc()]# 0.32 should be same in initial_position else: @@ -482,11 +380,11 @@ class io_pin_placer(router): if round(target_pin.center().x, 3) == round(ur.x + self.track_wire * 2 - offset, 3): # special handel clk1 if re.match(pattern_clk, source_pin.name): - return [vector(source_pin.lc().x, source_pin.lc().y - 0.32), target_pin.rc()]# 0.32 should be same in initial_position - # special handle csb0 + return [vector(source_pin.lc().x, source_pin.lc().y - 0.32), target_pin.rc()]# 0.32 should be same in initial_position + # special handle csb0 elif re.match(pattern_csb, source_pin.name): - return [vector(source_pin.lc().x, source_pin.lc().y + 0.32), target_pin.rc()]# 0.32 should be same in initial_position - else: + return [vector(source_pin.lc().x, source_pin.lc().y + 0.32), target_pin.rc()]# 0.32 should be same in initial_position + else: # direct connect possible return [source_pin.lc(), target_pin.rc()] # internal -> top, need to add start_via m3->m4 @@ -499,13 +397,13 @@ class io_pin_placer(router): # need intermediate point via_basic_y = self.design.bank.height + 3 # 3 is magic number, make sure out of bank area is_up = not is_up# Be attention, for channel at the top, the is_up should be inverted! Otherwise will cause overlap! - if is_up: + if is_up: via_basic_y = via_basic_y + 0.5 else: via_basic_y = via_basic_y - 0.5 point_1 = vector(source_pin.center().x, via_basic_y) point_2 = vector(target_pin.center().x, via_basic_y) - return [source_pin.bc(), point_1, point_2, target_pin.uc()] + return [source_pin.bc(), point_1, point_2, target_pin.uc()] # internal -> bottom, need to add start_via m3->m4 if round(target_pin.center().y, 3) == round(ll.y - self.track_wire * 2 + offset, 3): self.add_start_via(source_pin.center()) @@ -521,14 +419,14 @@ class io_pin_placer(router): via_basic_y = via_basic_y - 0.5 point_1 = vector(source_pin.center().x, via_basic_y) point_2 = vector(target_pin.center().x, via_basic_y) - return [source_pin.uc(), point_1, point_2, target_pin.bc()] - - + return [source_pin.uc(), point_1, point_2, target_pin.bc()] + + def remove_io_pins(self, pin_name): # remove io pin in gds, so we could reroute self.design.remove_layout_pin(pin_name) - - + + def replace_layout_pins(self, pin_names): """ Change the IO pin names with new ones around the perimeter. """ for pin_name in pin_names: @@ -537,8 +435,7 @@ class io_pin_placer(router): if pin.name == perimeter_pin_name: perimeter_pin = pin self.design.replace_layout_pin(pin_name, perimeter_pin) - break - + break diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 764001d1..b965980c 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -24,13 +24,13 @@ class signal_escape_router(router): # New pins are the side supply pins self.new_pins = {} - - # Use for add distance of dout pins at the perimeter + + # 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 + + # Use for control which edge/position the pins(dout) will be placed # 0 -> default # 1 -> all top/bottom # 2 -> all left/right @@ -206,16 +206,16 @@ 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) - + # 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": + if edge == "right": vertical = False fake_center = vector(c.x, ll.y - self.track_wire * 2) self.distance_right += 1 @@ -223,8 +223,8 @@ class signal_escape_router(router): if edge == "left": vertical = False fake_center = vector(c.x, ll.y + self.track_wire * 2) - self.distance_left += 1 - + self.distance_left += 1 + elif self.state_mod == 2: # all left/right if (edge == "bottom") or (edge == "right"):# change to the east vertical = True @@ -234,10 +234,10 @@ class signal_escape_router(router): 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 + self.distance_left += 1 else: - debug.error("wrong state mod!", -1) - + 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) diff --git a/compiler/sram.py b/compiler/sram.py index fcce2acd..997f5746 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -20,7 +20,7 @@ class sram(): results. We can later add visualizer and other high-level functions as needed. """ - def __init__(self, sram_config=None, name=None): + def __init__(self, sram_config=None, name=None, route_option="classic"): # Create default configs if custom config isn't provided if sram_config is None: @@ -48,67 +48,74 @@ class sram(): start_time = datetime.datetime.now() self.name = name + self.route_option = route_option # "classic" or "fast" from openram.modules.sram_1bank 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): - - 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 - + # choose the routung method, maze router or constructive + if self.route_option == "classic": + cur_state = "IDLE" + if not OPTS.netlist_only: + 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 + elif self.route_option == "fast": + if not OPTS.netlist_only: + self.s.create_layout(route_option=route_option) + if not OPTS.is_unit_test: print_time("SRAM creation", datetime.datetime.now(), start_time) @@ -162,7 +169,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 + #''' #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, @@ -184,7 +191,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/lvs, bevause magic_ext stuck + #''' #comment the above when generating big sram, and then disable drc/lvs, 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_openroad_test.py b/compiler/sram_openroad_test.py deleted file mode 100644 index 98ee1583..00000000 --- a/compiler/sram_openroad_test.py +++ /dev/null @@ -1,247 +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 -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_openroad import sram_1bank as sram - #from openram.modules.sram_new import sram_1bank as sram - self.s = sram(name, sram_config) - - self.s.create_netlist() - if not OPTS.netlist_only: - self.s.create_layout_openroad() - - 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_only(self): - 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) - - 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/compiler/sram_road.py b/compiler/sram_road.py deleted file mode 100644 index 87e133de..00000000 --- a/compiler/sram_road.py +++ /dev/null @@ -1,268 +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 file generate sram part, aiming at using openroad to do the P&R -import os -import shutil -import datetime -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, mod=0): - - # 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) - # maybe this part should be put after set_local_config, so we could calculate the number of port and define file name - if name is None: - #name = OPTS.output_name - if mod == 0: - name = OPTS.output_name + "_bank_" + "0" - elif mod == 1:# not consider multi-port yet!!!!!! - name = OPTS.output_name + "_control_" + "0" - elif mod == 2: - name = OPTS.output_name + "_row_addr_dff_" + "0" - elif mod == 3: - name = OPTS.output_name + "_col_addr_dff_" + "0" - elif mod == 4: - name = OPTS.output_name + "_data_dff_" + "0" - elif mod == 5: - name = OPTS.output_name + "_wmask_dff_" + "0" - elif mod == 6: - name = OPTS.output_name + "_spare_wen_dff_" + "0" - - - 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_1bank import sram_1bank as sram - from openram.modules.sram_for_road import sram_for_road as sram - - self.s = sram(name, sram_config) - - 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 generate_files(self): - """use to generate gds, lef files for one certain layout""" - # 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) - - print_time("Spice writing", 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 - - # 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 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) - - - def save(self, mod=0): - """ Save all the output files while reporting time to do it as well. """ - if mod == 0: - self.s.create_netlist_bank() - if not OPTS.netlist_only: - self.s.create_layout_bank_only() - self.generate_files() - elif mod == 1: - self.s.create_netlist_control() - if not OPTS.netlist_only: - for port in self.s.all_ports: - self.s.create_layout_control_only(instance_index=port) - self.generate_files() - else: - for port in self.s.all_ports: - self.generate_files() - elif mod == 2: - self.s.create_netlist_row_addr_dff() - if not OPTS.netlist_only: - for port in self.s.all_ports: - self.s.create_layout_row_addr_dff_only(instance_index=port) - self.generate_files() - else: - for port in self.s.all_ports: - self.generate_files() - elif mod == 3: - if self.s.create_netlist_col_addr_dff() == False: - pass#continue # do not need col addr dff - elif not OPTS.netlist_only: - for port in self.s.all_ports: - self.create_layout_col_addr_dff_only(instance_index=port) - self.generate_files() - else: - self.generate_files() - elif mod == 4: - self.s.create_netlist_data_dff() - if not OPTS.netlist_only: - for port in self.s.all_ports: - self.s.create_layout_data_dff_only(instance_index=port) - self.generate_files() - else: - for port in self.s.all_ports: - self.generate_files() - elif mod == 5: - if self.s.create_netlist_wmask_dff() == False: - pass#continue # do not need wmask dff - elif not OPTS.netlist_only: - for port in self.s.all_ports: - self.s.create_layout_wmask_dff_only(instance_index=port) - self.generate_files() - else: - for port in self.s.all_ports: - self.generate_files() - elif mod == 6: - if self.s.create_netlist_spare_wen_dff() == False: - pass#continue # do not need spare wen dff - elif not OPTS.netlist_only: - for port in self.s.all_ports: - self.s.create_layout_spare_wen_dff_only(instance_index=port) - self.generate_files() - else: - for port in self.s.all_ports: - self.generate_files() - - - diff --git a/sram_compiler.py b/sram_compiler.py index e1404745..9d5fc4b3 100755 --- a/sram_compiler.py +++ b/sram_compiler.py @@ -70,40 +70,10 @@ for path in output_files: # Create an SRAM (we can also pass sram_config, see documentation/tutorials for details) from openram import sram -s = sram() -#from openram import sram_openroad_test -#s = sram_openroad_test.sram() +s = sram(route_option="classic")# "classic" or "fast" # Output the files for the resulting SRAM s.save() -#s.save_only() -''' -from openram import sram_road -s = sram_road.sram(mod=0) -s.save(mod=0) -del s -s = sram_road.sram(mod=1) -s.save(mod=1) -del s -s = sram_road.sram(mod=2) -s.save(mod=2) - -del s -s = sram_road.sram(mod=3) -s.save(mod=3) - -del s -s = sram_road.sram(mod=4) -s.save(mod=4) - -del s -s = sram_road.sram(mod=5) -s.save(mod=5) - -del s -s = sram_road.sram(mod=6) -s.save(mod=6) -''' # Delete temp files etc. openram.end_openram() openram.print_time("End", datetime.datetime.now(), start_time)