diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index b87aea77..5dec6a80 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -742,6 +742,9 @@ class VlsiLayout: Search for a pin label and return ALL the enclosing rectangles on the same layer as the pin label. """ + #debug + for pin in self.pins: + print(pin) shape_list = [] pin_map = self.pins[pin_name] for pin_list in pin_map: diff --git a/compiler/modules/sram_part.py b/compiler/modules/sram_part.py new file mode 100644 index 00000000..5d374940 --- /dev/null +++ b/compiler/modules/sram_part.py @@ -0,0 +1,2589 @@ +# 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): + # do not now why need this + """ 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}_{0}".format(port), "OUTPUT") + 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") + + # 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): + 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): + # do not know why we need this + 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") + #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): + # do not know why we need this + 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): + # do not know why we need this + 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): + # do not know why we need this + 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): + # do not know why we need this + for port in self.all_ports: + for bit in range(self.num_spare_cols): + # 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): + #Do not know why we need this + 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.bank_inst = self.create_col_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 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.bank_inst = 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_pos[port] = vector(0,0) + self.control_logic_insts[port].place(self.control_pos[port]) + + 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_pos[port] = vector(0,0) + self.row_addr_dff_insts[port].place(self.row_addr_pos[port]) + + 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_pos[port] = vector(0,0) + self.col_addr_dff_insts[port].place(self.col_addr_pos[port]) + + 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_pos[port] = vector(0,0) + self.data_dff_insts[port].place(self.data_pos[port]) + + 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_pos[port] = vector(0,0) + self.wmask_dff_insts[port].place(self.wmask_pos[port]) + + 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_pos[port] = vector(0,0) + self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port]) + + 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_layout_spare_wen_dff_only_pins(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): + # 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_pins_to_route_spare_wen_dff(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): + # input + self.add_io_pin(self.spare_wen_dff_insts[port], + "spare_wen{}[{}]".format(port, 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], + "bank_spare_wen{}_{}".format(port, 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_buf{}".format(port), + "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], + "wmask{}[{}]".format(port, bit), + "wmask{}[{}]".format(port, bit), + start_layer=pin_layer) + # output + self.add_io_pin(self.wmask_dff_insts[port], + "bank_wmask{}_{}".format(port, 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_buf{}".format(port), + "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(port, bit), + "din{}[{}]".format(port, bit), + start_layer=pin_layer) + # output + self.add_io_pin(self.data_dff_insts[port], + "bank_din{}_{}".format(port, 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_buf{}".format(port), + "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], + "addr{}[{}]".format(port, bit), # old name + "addr{}[{}]".format(port, bit), # new name + start_layer=pin_layer) + #output + self.add_io_pin(self.col_addr_dff_insts[port], + "a{}_{}".format(port, bit), + "a{}_{}".format(port, bit), + start_layer=pin_layer) + #clk_buf, regard as input + self.add_pin(self.col_addr_dff_insts[port], + "clk_buf{}".format(port), + "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 + self.add_io_pin(self.row_addr_dff_insts[port], + "addr{}[{}]".format(port, 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], + "a{}_{}".format(port, 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_buf{}".format(port), + "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_insts[port], + "csb{}".format(port), #old name + "csb{}".format(port), #new name + start_layer=pin_layer) + if port in self.readwrite_ports: + self.add_io_pin(self.control_insts[port], + "web{}".format(port), + "web{}".format(port), + start_layer=pin_layer) + self.add_io_pin(self.control_insts[port], + "clk{}".format(port), + "clk{}".format(port), + start_layer=pin_layer) + if self.has_rbl: + self.add_io_pin(self.control_insts[port], + "rbl_bl{}".format(port), + "rbl_bl{}".format(port), + start_layer=pin_layer) + # Outputs + if port in self.read_ports: + self.add_io_pin(self.control_insts[port], + "s_en{}".format(port), + "s_en{}".format(port), + start_layer=pin_layer) + if port in self.write_ports: + self.add_io_pin(self.control_insts[port], + "w_en{}".format(port), + "w_en{}".format(port), + start_layer=pin_layer) + self.add_io_pin(self.control_insts[port], + "p_en_bar{}".format(port), + "p_en_bar{}".format(port), + start_layer=pin_layer) + self.add_io_pin(self.control_insts[port], + "wl_en{}".format(port), + "wl_en{}".format(port), + start_layer=pin_layer) + self.add_io_pin(self.control_insts[port], + "clk_buf{}".format(port), + "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): + 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): + 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/signal_escape_router.py b/compiler/router/signal_escape_router.py index fe0cca10..6f79509a 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -10,7 +10,7 @@ from openram import OPTS from .graph import graph from .graph_shape import graph_shape from .router import router - +import re class signal_escape_router(router): """ @@ -76,6 +76,8 @@ class signal_escape_router(router): self.find_vias(new_vias) routed_count += 1 debug.info(2, "Routed {} of {} signal pins".format(routed_count, routed_max)) + print("route pins:") + print(source) self.replace_layout_pins() @@ -175,14 +177,18 @@ class signal_escape_router(router): rect=rect, layer_name_pp=layer) self.fake_pins.append(pin) - + print("this is add_per") + print(pin.name) + print(pin.center) def create_fake_pin(self, pin): """ Create a fake pin on the perimeter orthogonal to the given pin. """ ll, ur = self.bbox c = pin.center() - + print("inside pin name") + print("----------------------------------------------------------") + print(pin.name) # Find the closest edge edge, vertical = self.get_closest_edge(c) @@ -196,16 +202,62 @@ class signal_escape_router(router): fake_center = vector(ur.x + self.track_wire * 2, c.y) if edge == "top": fake_center = vector(c.x, ur.y + self.track_wire * 2) + #fake_center = vector(ll.x - self.track_wire * 2, c.y) # test if here we could change the pin position at the layout + # relocate the pin position + """ + pattern = r'^addr0_1' + if re.match(pattern, pin.name): + vertical = True + fake_center = vector(ll.x - self.track_wire * 2, c.y + self.track_wire * 4)# fix still do not know how to control the distance between every fake pin + #do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y? + pattern = r'^addr0_2' + if re.match(pattern, pin.name): + vertical = True + fake_center = vector(ll.x - self.track_wire * 2, c.y + self.track_wire * 8)# fix still do not know how to control the distance between every fake pin + #do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y? + pattern = r'^addr0_3' + if re.match(pattern, pin.name): + vertical = True + fake_center = vector(ll.x - self.track_wire * 2, c.y + self.track_wire*12)# fix still do not know how to control the distance between every fake pin + #do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y? + pattern = r'^addr0_4' + if re.match(pattern, pin.name): + vertical = True + fake_center = vector(ll.x - self.track_wire * 2, c.y + self.track_wire*16)# fix still do not know how to control the distance between every fake pin + #do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y? + pattern = r'^p_en_bar0' + if re.match(pattern, pin.name): + vertical = True + fake_center = vector(ll.x - self.track_wire * 2, c.y)# fix still do not know how to control the distance between every fake pin + #do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y? + pattern = r'^s_en0' + if re.match(pattern, pin.name): + vertical = True + fake_center = vector(ll.x - self.track_wire * 2, c.y + self.track_width *3)# fix still do not know how to control the distance between every fake pin + #do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y? + pattern = r'^w_en0' + if re.match(pattern, pin.name): + vertical = True + fake_center = vector(ll.x - self.track_wire * 2, c.y + self.track_width * 6)# fix still do not know how to control the distance between every fake pin + #do not know why after this, all fake out pins are put at the same position -> because the originl inside pin has same y? + """ # Create the fake pin shape layer = self.get_layer(int(not vertical)) half_wire_vector = vector([self.half_wire] * 2) nll = fake_center - half_wire_vector nur = fake_center + half_wire_vector + #not test jet + #half_wire_vector = vector([self.half_wire] * 2)# out *2 means vector([self.half_wire, self.half_wire]) + #nll = fake_center - half_wire_vector - half_wire_vector + #nur = fake_center + half_wire_vector + half_wire_vector rect = [nll, nur] pin = graph_shape(name="fake", rect=rect, layer_name_pp=layer) + print("this create_fake_pin") + print(pin.name) + print(pin.center) return pin @@ -214,6 +266,8 @@ class signal_escape_router(router): to_route = [] for name in pin_names: + print("==============the pin names===================") + print(name) pin = next(iter(self.pins[name])) fake = self.create_fake_pin(pin) to_route.append((pin, fake, pin.distance(fake))) diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index fc61f4e6..e868c8b2 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -76,6 +76,8 @@ class supply_router(router): # Create the graph g = graph(self) g.create_graph(source, target) + # debug + debug.warning("graph creat success!") # Find the shortest path from source to target path = g.find_shortest_path() # If no path is found, throw an error diff --git a/compiler/sram.py b/compiler/sram.py index ea71cad4..36ec253c 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -50,7 +50,7 @@ class sram(): self.name = name from openram.modules.sram_1bank import sram_1bank as sram - + #from openram.modules.sram_new import sram_1bank as sram self.s = sram(name, sram_config) self.s.create_netlist() diff --git a/compiler/sram_road.py b/compiler/sram_road.py new file mode 100644 index 00000000..85592d4a --- /dev/null +++ b/compiler/sram_road.py @@ -0,0 +1,253 @@ +# 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): + + # 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_1bank import sram_1bank as sram + from openram.modules.sram_part import sram_1bank 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, module_name): + """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 + module_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_{1}.sp".format(OPTS.output_path, module_name) + 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 + module_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 + module_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 + module_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 + module_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 + module_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 + module_name + ".pex.sp" + spname = OPTS.output_path + self.s.name + module_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): + """ Save all the output files while reporting time to do it as well. """ + for i in range(7): + if i == 0: + self.s.create_netlist_bank() + if not OPTS.netlist_only: + self.s.create_layout_bank_only() + self.generate_files("bank") + elif i == 1: + self.s.create_netlist_control() + if not OPTS.netlist_only: + for port in self.s.all_ports: + self.s.create_layout_control_only(self, instance_index=port) + self.generate_files("control_" + port) + else: + for port in self.s.all_ports: + self.generate_files("control_" + port) + elif i == 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(self, instance_index=port) + self.generate_files("row_addr_dff_" + port) + else: + for port in self.s.all_ports: + self.generate_files("row_addr_dff_" + port) + elif i == 3: + if self.s.create_netlist_col_addr_dff() == False: + 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(self, instance_index=port) + self.generate_files("col_addr_dff_" + port) + else: + self.generate_files("col_addr_dff_" + port) + elif i == 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(self, instance_index=port) + self.generate_files("data_dff_" + port) + else: + for port in self.s.all_ports: + self.generate_files("data_dff_" + port) + elif i == 5: + if self.s.create_netlist_wmask_dff() == False: + 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(self, instance_index=port) + self.generate_files("wmask_dff_" + port) + else: + for port in self.s.all_ports: + self.generate_files("wmask_dff_" + port) + elif i == 6: + if self.s.create_netlist_spare_wen_dff() == False: + 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(self, instance_index=port) + self.generate_files("spare_wen_dff_" + port) + else: + for port in self.s.all_ports: + self.generate_files("spare_wen_dff_" + port) + + + diff --git a/sram_compiler.py b/sram_compiler.py index 34b398bc..31435695 100755 --- a/sram_compiler.py +++ b/sram_compiler.py @@ -69,9 +69,10 @@ for path in output_files: debug.print_raw(path) # Create an SRAM (we can also pass sram_config, see documentation/tutorials for details) -from openram import sram -s = sram() - +#from openram import sram +#s = sram() +from openram import sram_road +s = sram_road.sram() # Output the files for the resulting SRAM s.save()