From 4e232c49ad27047c308d1a958499ec7dea74d360 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 7 Nov 2018 14:46:51 -0800 Subject: [PATCH 01/31] Update precharge cell for multiport. Comment out pbitcell tests. Add bitcell_1rw_1r test. Move bitcell horizontal routing to metal1. Extend precharge height for stacking. --- compiler/pgates/precharge.py | 48 ++++++++++------------- compiler/tests/08_precharge_array_test.py | 16 +++++--- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index ea67eeb5..0bb6142f 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -170,7 +170,7 @@ class precharge(pgate.pgate): well_type="n") - self.height = well_contact_pos.y + contact.well.height + self.height = well_contact_pos.y + contact.well.height + self.m1_pitch self.add_rect(layer="nwell", offset=vector(0,0), @@ -182,19 +182,19 @@ class precharge(pgate.pgate): """Adds both bit-line and bit-line-bar to the module""" # adds the BL on metal 2 offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0) - self.add_layout_pin(text="bl", - layer="metal2", - offset=offset, - width=drc("minwidth_metal2"), - height=self.height) + self.bl_pin = self.add_layout_pin(text="bl", + layer="metal2", + offset=offset, + width=drc("minwidth_metal2"), + height=self.height) # adds the BR on metal 2 offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0) - self.add_layout_pin(text="br", - layer="metal2", - offset=offset, - width=drc("minwidth_metal2"), - height=self.height) + self.br_pin = self.add_layout_pin(text="br", + layer="metal2", + offset=offset, + width=drc("minwidth_metal2"), + height=self.height) def connect_to_bitlines(self): self.add_bitline_contacts() @@ -208,29 +208,23 @@ class precharge(pgate.pgate): """Adds contacts/via from metal1 to metal2 for bit-lines""" stack=("metal1", "via1", "metal2") - pos = self.lower_pmos_inst.get_pin("S").center() + upper_y = self.upper_pmos1_inst.get_pin("S").cy() + lower_y = self.lower_pmos_inst.get_pin("S").cy() + self.add_contact_center(layers=stack, - offset=pos) - pos = self.lower_pmos_inst.get_pin("D").center() + offset = vector(self.bl_pin.cx(), upper_y)) self.add_contact_center(layers=stack, - offset=pos) - pos = self.upper_pmos1_inst.get_pin("S").center() + offset = vector(self.br_pin.cx(), upper_y)) self.add_contact_center(layers=stack, - offset=pos) - pos = self.upper_pmos2_inst.get_pin("D").center() + offset = vector(self.bl_pin.cx(), lower_y)) self.add_contact_center(layers=stack, - offset=pos) + offset = vector(self.br_pin.cx(), lower_y)) def connect_pmos(self, pmos_pin, bit_pin): """ Connect pmos pin to bitline pin """ - ll_pos = vector(min(pmos_pin.lx(),bit_pin.lx()), pmos_pin.by()) - ur_pos = vector(max(pmos_pin.rx(),bit_pin.rx()), pmos_pin.uy()) + left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) + right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) - width = ur_pos.x-ll_pos.x - height = ur_pos.y-ll_pos.y - self.add_rect(layer="metal2", - offset=ll_pos, - width=width, - height=height) + self.add_path("metal1", [ left_pos, right_pos] ) diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index 5ea9931b..cdf6100e 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -24,18 +24,22 @@ class precharge_test(openram_test): self.local_check(pc) # check precharge array in multi-port - OPTS.bitcell = "pbitcell" + OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 + OPTS.num_w_ports = 0 - debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") + debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl0", bitcell_br="br0") self.local_check(pc) - debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)") - pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl2", bitcell_br="br2") - self.local_check(pc) + # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") + # pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl0", bitcell_br="br0") + # self.local_check(pc) + + # debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)") + # pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl2", bitcell_br="br2") + # self.local_check(pc) globals.end_openram() From ad7fe1be51d9060bd55308a2296a198639375803 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 7 Nov 2018 14:52:03 -0800 Subject: [PATCH 02/31] Clean up code formatting. --- compiler/pgates/precharge.py | 51 +++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 0bb6142f..d5016219 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -54,7 +54,9 @@ class precharge(pgate.pgate): self.add_pin_list(["bl", "br", "en", "vdd"]) def add_ptx(self): - """Initializes the upper and lower pmos""" + """ + Initializes the upper and lower pmos + """ self.pmos = ptx(width=self.ptx_width, tx_type="pmos") self.add_mod(self.pmos) @@ -63,8 +65,10 @@ class precharge(pgate.pgate): def route_vdd_rail(self): + """ + Adds a vdd rail at the top of the cell + """ - """Adds a vdd rail at the top of the cell""" # adds the rail across the width of the cell vdd_position = vector(0, self.height - self.m1_width) self.add_rect(layer="metal1", @@ -77,7 +81,7 @@ class precharge(pgate.pgate): vdd_pos = vector(pmos_pin.cx(), vdd_position.y + 0.5*self.m1_width) self.add_path("metal1", [pmos_pin.uc(), vdd_pos]) - # Add the M1->M2->M3 stack at the left edge + # Add the M1->M2->M3 stack vdd_contact_pos = vector(0.5*self.width, vdd_position.y + 0.5*self.m1_width) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=vdd_contact_pos) @@ -89,7 +93,9 @@ class precharge(pgate.pgate): def create_ptx(self): - """Create both the upper_pmos and lower_pmos to the module""" + """ + Create both the upper_pmos and lower_pmos to the module + """ self.lower_pmos_inst=self.add_inst(name="lower_pmos", mod=self.pmos) @@ -105,7 +111,9 @@ class precharge(pgate.pgate): def place_ptx(self): - """Place both the upper_pmos and lower_pmos to the module""" + """ + Place both the upper_pmos and lower_pmos to the module + """ # Compute the other pmos2 location, but determining offset to overlap the # source and drain pins @@ -126,7 +134,9 @@ class precharge(pgate.pgate): self.upper_pmos2_inst.place(upper_pmos2_pos) def connect_poly(self): - """Connects the upper and lower pmos together""" + """ + Connects the upper and lower pmos together + """ offset = self.lower_pmos_inst.get_pin("G").ll() # connects the top and bottom pmos' gates together @@ -145,7 +155,10 @@ class precharge(pgate.pgate): height=self.poly_width) def route_en(self): - """Adds the en input rail, en contact/vias, and connects to the pmos""" + """ + Adds the en input rail, en contact/vias, and connects to the pmos + """ + # adds the en contact to connect the gates to the en rail on metal1 offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space) self.add_contact_center(layers=("poly", "contact", "metal1"), @@ -160,7 +173,10 @@ class precharge(pgate.pgate): def place_nwell_and_contact(self): - """Adds a nwell tap to connect to the vdd rail""" + """ + Adds a nwell tap to connect to the vdd rail + """ + # adds the contact from active to metal1 well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \ + vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active")) @@ -169,9 +185,10 @@ class precharge(pgate.pgate): implant_type="n", well_type="n") - + # leave an extra pitch for the height self.height = well_contact_pos.y + contact.well.height + self.m1_pitch + # nwell should span the whole design since it is pmos only self.add_rect(layer="nwell", offset=vector(0,0), width=self.width, @@ -179,7 +196,10 @@ class precharge(pgate.pgate): def route_bitlines(self): - """Adds both bit-line and bit-line-bar to the module""" + """ + Adds both bit-line and bit-line-bar to the module + """ + # adds the BL on metal 2 offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0) self.bl_pin = self.add_layout_pin(text="bl", @@ -197,6 +217,9 @@ class precharge(pgate.pgate): height=self.height) def connect_to_bitlines(self): + """ + Connect the bitlines to the devices + """ self.add_bitline_contacts() self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl")) self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin("br")) @@ -205,7 +228,9 @@ class precharge(pgate.pgate): def add_bitline_contacts(self): - """Adds contacts/via from metal1 to metal2 for bit-lines""" + """ + Adds contacts/via from metal1 to metal2 for bit-lines + """ stack=("metal1", "via1", "metal2") upper_y = self.upper_pmos1_inst.get_pin("S").cy() @@ -221,7 +246,9 @@ class precharge(pgate.pgate): offset = vector(self.br_pin.cx(), lower_y)) def connect_pmos(self, pmos_pin, bit_pin): - """ Connect pmos pin to bitline pin """ + """ + Connect a pmos pin to bitline pin + """ left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) From 3d2abc08730bf0c6b7a3c8c8bd3ebd64abd12c52 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 7 Nov 2018 15:43:08 -0800 Subject: [PATCH 03/31] Change default col mux size to 2. Add some comments. --- compiler/modules/single_level_column_mux_array.py | 3 +-- compiler/pgates/single_level_column_mux.py | 11 +++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index d78e0fdc..699e1d85 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -61,8 +61,7 @@ class single_level_column_mux_array(design.design): def add_modules(self): - # FIXME: Why is this 8x? - self.mux = single_level_column_mux(tx_size=8, bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) + self.mux = single_level_column_mux(bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) self.add_mod(self.mux) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index ddfca30c..bc2b1741 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -9,13 +9,16 @@ from globals import OPTS class single_level_column_mux(design.design): """ This module implements the columnmux bitline cell used in the design. - Creates a single columnmux cell. + Creates a single columnmux cell with the given integer size relative + to minimum size. Default is 2x. """ + # This is needed for different bitline spacings unique_id = 1 - def __init__(self, tx_size, bitcell_bl="bl", bitcell_br="br"): - name="single_level_column_mux_{}_no{}".format(tx_size,single_level_column_mux.unique_id) + def __init__(self, tx_size=2, bitcell_bl="bl", bitcell_br="br"): + self.tx_size = int(tx_size) + name="single_level_column_mux_{}_{}".format(self.tx_size,single_level_column_mux.unique_id) single_level_column_mux.unique_id += 1 design.design.__init__(self, name) debug.info(2, "create single column mux cell: {0}".format(name)) @@ -52,7 +55,7 @@ class single_level_column_mux(design.design): self.bitcell = self.mod_bitcell() # Adds nmos_lower,nmos_upper to the module - self.ptx_width = self.tx_size * drc("minwidth_tx") + self.ptx_width = self.tx_size*drc("minwidth_tx") self.nmos = ptx(width=self.ptx_width) self.add_mod(self.nmos) From 5dfba21acc97e417f010dcd5cd99d5f67a6f6d61 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 7 Nov 2018 16:03:48 -0800 Subject: [PATCH 04/31] Change tx mux size back to 8. Document why it was chosen. --- compiler/pgates/single_level_column_mux.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index bc2b1741..cb9c9909 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -10,20 +10,20 @@ class single_level_column_mux(design.design): """ This module implements the columnmux bitline cell used in the design. Creates a single columnmux cell with the given integer size relative - to minimum size. Default is 2x. + to minimum size. Default is 8x. Per Samira and Hodges-Jackson book: + Column-mux transistors driven by the decoder must be sized for optimal speed """ # This is needed for different bitline spacings unique_id = 1 - def __init__(self, tx_size=2, bitcell_bl="bl", bitcell_br="br"): + def __init__(self, tx_size=8, bitcell_bl="bl", bitcell_br="br"): self.tx_size = int(tx_size) name="single_level_column_mux_{}_{}".format(self.tx_size,single_level_column_mux.unique_id) single_level_column_mux.unique_id += 1 design.design.__init__(self, name) debug.info(2, "create single column mux cell: {0}".format(name)) - self.tx_size = tx_size self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br From 929eae4a23bbc056365040b05f091e4eec77a266 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 7 Nov 2018 16:09:50 -0800 Subject: [PATCH 05/31] Document why sense amp is 8x isolation transistor --- compiler/modules/sense_amp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index e2a0e131..6ba5a812 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -26,6 +26,8 @@ class sense_amp(design.design): def input_load(self): #Input load for the bitlines which are connected to the source/drain of a TX. Not the selects. from tech import spice, parameter + # Default is 8x. Per Samira and Hodges-Jackson book: + # "Column-mux transistors driven by the decoder must be sized for optimal speed" bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file. return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff From dd5b2a5b59cd25a8a449761fa7ead84bfaf33a8b Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 12:16:59 -0800 Subject: [PATCH 06/31] Fix missing fail when non-list item doesn't match. --- compiler/tests/testutils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 78db41cb..39376202 100755 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -77,7 +77,8 @@ class openram_test(unittest.TestCase): if not self.isclose(k,data[k][i],golden_data[k][i],error_tolerance): data_matches = False else: - self.isclose(k,data[k],golden_data[k],error_tolerance) + if not self.isclose(k,data[k],golden_data[k],error_tolerance): + data_matches = False if not data_matches: import pprint data_string=pprint.pformat(data) From b25650eb0736adc81ef92d8a6df12d263b3e0eb2 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 12:19:06 -0800 Subject: [PATCH 07/31] Netlist only mode for ngspice delay test --- compiler/tests/21_ngspice_delay_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 3ef27fc5..e449fce0 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -17,7 +17,7 @@ class timing_sram_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) OPTS.spice_name="ngspice" OPTS.analytical_delay = False - OPTS.trim_netlist = False + OPTS.netlist_only = True # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload From 7b10e3bfec99ec2f36d96174b14e86d0cc525bd8 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 12:19:40 -0800 Subject: [PATCH 08/31] Convert port index lists to three simple lists. --- compiler/base/design.py | 41 +-- compiler/characterizer/delay.py | 62 ++--- compiler/characterizer/functional.py | 28 +- compiler/characterizer/simulation.py | 41 ++- compiler/modules/bank.py | 380 +++++++++++++++------------ compiler/modules/replica_bitline.py | 16 +- compiler/sram_1bank.py | 24 +- compiler/sram_base.py | 58 ++-- 8 files changed, 350 insertions(+), 300 deletions(-) diff --git a/compiler/base/design.py b/compiler/base/design.py index 211133a1..a90fa70a 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -50,31 +50,36 @@ class design(hierarchy_design): self.implant_space = drc("implant_to_implant") def setup_multiport_constants(self): - """ These are contants and lists that aid multiport design """ - self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports - self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports - self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports - self.num_rw_ports = OPTS.num_rw_ports + """ + These are contants and lists that aid multiport design. + Ports are always in the order RW, W, R. + Port indices start from 0 and increment. + A first RW port will have clk0, csb0, web0, addr0, data0 + A first W port (with no RW ports) will be: clk0, csb0, addr0, data0 + + """ + total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + + # These are the read/write port indices. + self.readwrite_ports = [] + # These are the read/write and write-only port indices + self.write_ports = [] + # These are teh read/write and read-only port indice + self.read_ports = [] + # These are all the ports + self.all_ports = list(range(total_ports)) - # Port indices used for data, address, and control signals - # Port IDs used to identify port type - self.write_index = [] - self.read_index = [] - self.port_id = [] port_number = 0 - for port in range(OPTS.num_rw_ports): - self.write_index.append(port_number) - self.read_index.append(port_number) - self.port_id.append("rw") + self.readwrite_ports.append(port_number) + self.write_ports.append(port_number) + self.read_ports.append(port_number) port_number += 1 for port in range(OPTS.num_w_ports): - self.write_index.append(port_number) - self.port_id.append("w") + self.write_ports.append(port_number) port_number += 1 for port in range(OPTS.num_r_ports): - self.read_index.append(port_number) - self.port_id.append("r") + self.read_ports.append(port_number) port_number += 1 def analytical_power(self, proc, vdd, temp, load): diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 5fdc1fef..6b97fa52 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -73,9 +73,9 @@ class delay(simulation): debug.error("Given probe_data is not an integer to specify a data bit",1) #Adding port options here which the characterizer cannot handle. Some may be added later like ROM - if len(self.read_index) == 0: + if len(self.read_ports) == 0: debug.error("Characterizer does not currently support SRAMs without read ports.",1) - if len(self.write_index) == 0: + if len(self.write_ports) == 0: debug.error("Characterizer does not currently support SRAMs without write ports.",1) def write_generic_stimulus(self): @@ -89,12 +89,12 @@ class delay(simulation): self.sf.write("\n* Instantiation of the SRAM\n") self.stim.inst_sram(sram=self.sram, port_signal_names=(self.addr_name,self.din_name,self.dout_name), - port_info=(self.total_ports,self.write_index,self.read_index), + port_info=(len(self.all_ports),self.write_ports,self.read_ports), abits=self.addr_size, dbits=self.word_size, sram_name=self.name) self.sf.write("\n* SRAM output loads\n") - for port in self.read_index: + for port in self.read_ports: for i in range(self.word_size): self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port,i,self.dout_name,self.load)) @@ -132,7 +132,7 @@ class delay(simulation): self.gen_control() self.sf.write("\n* Generation of Port clock signal\n") - for port in range(self.total_ports): + for port in self.all_ports: self.stim.gen_pulse(sig_name="CLK{0}".format(port), v1=0, v2=self.vdd_voltage, @@ -171,24 +171,24 @@ class delay(simulation): # generate data and addr signals self.sf.write("\n* Generation of data and address signals\n") - for write_port in self.write_index: + for write_port in self.write_ports: for i in range(self.word_size): self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i), v_val=0) - for port in range(self.total_ports): + for port in self.all_ports: for i in range(self.addr_size): self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name,port, i), v_val=0) # generate control signals self.sf.write("\n* Generation of control signals\n") - for port in range(self.total_ports): + for port in self.all_ports: self.stim.gen_constant(sig_name="CSB{0}".format(port), v_val=self.vdd_voltage) - if port in self.write_index and port in self.read_index: + if port in self.readwrite_ports: self.stim.gen_constant(sig_name="WEB{0}".format(port), v_val=self.vdd_voltage) self.sf.write("\n* Generation of global clock signal\n") - for port in range(self.total_ports): + for port in self.all_ports: self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0) self.write_power_measures() @@ -317,7 +317,7 @@ class delay(simulation): double the period until we find a valid period to use as a starting point. """ - debug.check(port in self.read_index, "Characterizer requires a read port to determine a period.") + debug.check(port in self.read_ports, "Characterizer requires a read port to determine a period.") feasible_period = float(tech.spice["feasible_period"]) time_out = 9 @@ -362,18 +362,18 @@ class delay(simulation): Loops through all read ports determining the feasible period and collecting delay information from each port. """ - feasible_delays = [{} for i in range(self.total_ports)] + feasible_delays = [{} for i in self.all_ports] #Get initial feasible delays from first port - feasible_delays[self.read_index[0]] = self.find_feasible_period_one_port(self.read_index[0]) + feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[0]) previous_period = self.period #Loops through all the ports checks if the feasible period works. Everything restarts it if does not. #Write ports do not produce delays which is why they are not included here. i = 1 - while i < len(self.read_index): - port = self.read_index[i] + while i < len(self.read_ports): + port = self.read_ports[i] #Only extract port values from the specified port, not the entire results. feasible_delays[port].update(self.find_feasible_period_one_port(port)) #Function sets the period. Restart the entire process if period changes to collect accurate delays @@ -416,7 +416,7 @@ class delay(simulation): #Sanity Check debug.check(self.period > 0, "Target simulation period non-positive") - result = [{} for i in range(self.total_ports)] + result = [{} for i in self.all_ports] # Checking from not data_value to data_value self.write_delay_stimulus() @@ -518,7 +518,7 @@ class delay(simulation): #Find the minimum period for all ports. Start at one port and perform binary search then use that delay as a starting position. #For testing purposes, only checks read ports. - for port in self.read_index: + for port in self.read_ports: target_period = self.find_min_period_one_port(feasible_delays, port, lb_period, ub_period, target_period) #The min period of one port becomes the new lower bound. Reset the upper_bound. lb_period = target_period @@ -683,8 +683,8 @@ class delay(simulation): """Simulate all specified output loads and input slews pairs of all ports""" measure_data = self.get_empty_measure_data_dict() #Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways. - self.targ_read_ports = self.read_index - self.targ_write_ports = self.write_index + self.targ_read_ports = self.read_ports + self.targ_write_ports = self.write_ports for slew in slews: for load in loads: self.set_load_slew(load,slew) @@ -693,7 +693,7 @@ class delay(simulation): debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load)) debug.info(1, "Simulation Passed: Port {0} slew={1} load={2}".format("All", self.slew,self.load)) #The results has a dict for every port but dicts can be empty (e.g. ports were not targeted). - for port in range(self.total_ports): + for port in self.all_ports: for mname,value in delay_results[port].items(): if "power" in mname: # Subtract partial array leakage and add full array leakage for the power measures @@ -763,15 +763,15 @@ class delay(simulation): def get_available_port(self,get_read_port): """Returns the first accessible read or write port. """ - if get_read_port and len(self.read_index) > 0: - return self.read_index[0] - elif not get_read_port and len(self.write_index) > 0: - return self.write_index[0] + if get_read_port and len(self.read_ports) > 0: + return self.read_ports[0] + elif not get_read_port and len(self.write_ports) > 0: + return self.write_ports[0] return None def set_stimulus_variables(self): simulation.set_stimulus_variables(self) - self.measure_cycles = [{} for port in range(self.total_ports)] + self.measure_cycles = [{} for port in self.all_ports] def create_test_cycles(self): """Returns a list of key time-points [ns] of the waveform (each rising edge) @@ -819,7 +819,7 @@ class delay(simulation): for load in loads: self.set_load_slew(load,slew) bank_delay = self.sram.analytical_delay(self.vdd_voltage, self.slew,self.load) - for port in range(self.total_ports): + for port in self.all_ports: for mname in self.delay_meas_names+self.power_meas_names: if "power" in mname: port_data[port][mname].append(power.dynamic) @@ -877,7 +877,7 @@ class delay(simulation): def gen_data(self): """ Generates the PWL data inputs for a simulation timing test. """ - for write_port in self.write_index: + for write_port in self.write_ports: for i in range(self.word_size): sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05) @@ -887,16 +887,16 @@ class delay(simulation): Generates the address inputs for a simulation timing test. This alternates between all 1's and all 0's for the address. """ - for port in range(self.total_ports): + for port in self.all_ports: for i in range(self.addr_size): sig_name = "{0}{1}_{2}".format(self.addr_name,port,i) self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05) def gen_control(self): """ Generates the control signals """ - for port in range(self.total_ports): + for port in self.all_ports: self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) - if port in self.read_index and port in self.write_index: + if port in self.readwrite_ports: self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) @@ -904,5 +904,5 @@ class delay(simulation): """Make a dict of lists for each type of delay and power measurement to append results to""" measure_names = self.delay_meas_names + self.power_meas_names #Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. - measure_data = [{mname:[] for mname in measure_names} for i in range(self.total_ports)] + measure_data = [{mname:[] for mname in measure_names} for i in self.all_ports] return measure_data diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index b69ab2e8..b99e644c 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -80,8 +80,8 @@ class functional(simulation): # Read at least once. For multiport, it is important that one read cycle uses all RW and R port to read from the same address simultaniously. # This will test the viablilty of the transistor sizing in the bitcell. - for port in range(self.total_ports): - if self.port_id[port] == "w": + for port in self.all_ports: + if port in self.write_ports: self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) else: comment = self.gen_cycle_comment("read", word, addr, port, self.t_current) @@ -94,10 +94,10 @@ class functional(simulation): # Perform a random sequence of writes and reads on random ports, using random addresses and random words for i in range(self.num_cycles): w_addrs = [] - for port in range(self.total_ports): - if self.port_id[port] == "rw": + for port in self.all_ports: + if port in self.readwrite_ports: op = random.choice(rw_ops) - elif self.port_id[port] == "w": + elif port in self.write_ports: op = random.choice(w_ops) else: op = random.choice(r_ops) @@ -225,17 +225,17 @@ class functional(simulation): self.sf.write("\n* Instantiation of the SRAM\n") self.stim.inst_sram(sram=self.sram, port_signal_names=(self.addr_name,self.din_name,self.dout_name), - port_info=(self.total_ports, self.write_index, self.read_index), + port_info=(len(self.all_ports), self.write_ports, self.read_ports), abits=self.addr_size, dbits=self.word_size, sram_name=self.name) # Add load capacitance to each of the read ports self.sf.write("\n* SRAM output loads\n") - for port in range(self.total_read): + for port in self.read_ports: for bit in range(self.word_size): - sig_name="{0}{1}_{2} ".format(self.dout_name, self.read_index[port], bit) - self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(self.read_index[port], bit, sig_name, self.load)) + sig_name="{0}{1}_{2} ".format(self.dout_name, port, bit) + self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(port, bit, sig_name, self.load)) # Write debug comments to stim file self.sf.write("\n\n * Sequence of operations\n") @@ -244,27 +244,27 @@ class functional(simulation): # Generate data input bits self.sf.write("\n* Generation of data and address signals\n") - for port in range(self.total_write): + for port in self.write_ports: for bit in range(self.word_size): sig_name="{0}{1}_{2} ".format(self.din_name, port, bit) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05) # Generate address bits - for port in range(self.total_ports): + for port in self.all_ports: for bit in range(self.addr_size): sig_name="{0}{1}_{2} ".format(self.addr_name, port, bit) self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][bit], self.period, self.slew, 0.05) # Generate control signals self.sf.write("\n * Generation of control signals\n") - for port in range(self.total_ports): + for port in self.all_ports: self.stim.gen_pwl("CSB{}".format(port), self.cycle_times , self.csb_values[port], self.period, self.slew, 0.05) - for port in range(self.num_rw_ports): + for port in self.readwrite_ports: self.stim.gen_pwl("WEB{}".format(port), self.cycle_times , self.web_values[port], self.period, self.slew, 0.05) # Generate CLK signals - for port in range(self.total_ports): + for port in self.all_ports: self.stim.gen_pulse(sig_name="{0}{1}".format(tech.spice["clk"], port), v1=self.gnd_voltage, v2=self.vdd_voltage, diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index e624250a..46bd6c57 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -22,13 +22,10 @@ class simulation(): self.num_banks = self.sram.num_banks self.sp_file = spfile - self.total_ports = self.sram.total_ports - self.total_write = self.sram.total_write - self.total_read = self.sram.total_read - self.read_index = self.sram.read_index - self.write_index = self.sram.write_index - self.num_rw_ports = self.sram.num_rw_ports - self.port_id = self.sram.port_id + self.all_ports = self.sram.all_ports + self.readwrite_ports = self.sram.readwrite_ports + self.read_ports = self.sram.read_ports + self.write_ports = self.sram.write_ports def set_corner(self,corner): """ Set the corner values """ @@ -48,12 +45,12 @@ class simulation(): self.t_current = 0 # control signals: only one cs_b for entire multiported sram, one we_b for each write port - self.csb_values = [[] for port in range(self.total_ports)] - self.web_values = [[] for port in range(self.num_rw_ports)] + self.csb_values = [[] for port in self.all_ports] + self.web_values = [[] for port in self.readwrite_ports] # Three dimensional list to handle each addr and data bits for wach port over the number of checks - self.addr_values = [[[] for bit in range(self.addr_size)] for port in range(self.total_ports)] - self.data_values = [[[] for bit in range(self.word_size)] for port in range(self.total_write)] + self.addr_values = [[[] for bit in range(self.addr_size)] for port in self.all_ports] + self.data_values = [[[] for bit in range(self.word_size)] for port in self.write_ports] # For generating comments in SPICE stimulus self.cycle_comments = [] @@ -75,7 +72,7 @@ class simulation(): # Append the values depending on the type of port self.csb_values[port].append(csb_val) # If port is in both lists, add rw control signal. Condition indicates its a RW port. - if port < self.num_rw_ports: + if port in self.readwrite_ports: self.web_values[port].append(web_val) def add_data(self, data, port): @@ -108,7 +105,7 @@ class simulation(): def add_write(self, comment, address, data, port): """ Add the control values for a write cycle. """ - debug.check(port in self.write_index, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_index)) + debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports)) debug.info(2, comment) self.fn_cycle_comments.append(comment) self.append_cycle_comment(port, comment) @@ -123,13 +120,13 @@ class simulation(): #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port noop_data = "0"*self.word_size #Add noops to all other ports. - for unselected_port in range(self.total_ports): + for unselected_port in self.all_ports: if unselected_port != port: self.add_noop_one_port(address, noop_data, unselected_port) def add_read(self, comment, address, din_data, port): """ Add the control values for a read cycle. """ - debug.check(port in self.read_index, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_index)) + debug.check(port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports)) debug.info(2, comment) self.fn_cycle_comments.append(comment) self.append_cycle_comment(port, comment) @@ -139,14 +136,14 @@ class simulation(): self.add_control_one_port(port, "read") #If the port is also a readwrite then add data. - if port in self.write_index: + if port in self.write_ports: self.add_data(din_data,port) self.add_address(address, port) #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port noop_data = "0"*self.word_size #Add noops to all other ports. - for unselected_port in range(self.total_ports): + for unselected_port in self.all_ports: if unselected_port != port: self.add_noop_one_port(address, noop_data, unselected_port) @@ -159,12 +156,12 @@ class simulation(): self.cycle_times.append(self.t_current) self.t_current += self.period - for port in range(self.total_ports): + for port in self.all_ports: self.add_noop_one_port(address, data, port) def add_write_one_port(self, comment, address, data, port): """ Add the control values for a write cycle. Does not increment the period. """ - debug.check(port in self.write_index, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_index)) + debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports)) debug.info(2, comment) self.fn_cycle_comments.append(comment) @@ -174,20 +171,20 @@ class simulation(): def add_read_one_port(self, comment, address, din_data, port): """ Add the control values for a read cycle. Does not increment the period. """ - debug.check(port in self.read_index, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_index)) + debug.check(port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports)) debug.info(2, comment) self.fn_cycle_comments.append(comment) self.add_control_one_port(port, "read") #If the port is also a readwrite then add data. - if port in self.write_index: + if port in self.write_ports: self.add_data(din_data,port) self.add_address(address, port) def add_noop_one_port(self, address, data, port): """ Add the control values for a noop to a single port. Does not increment the period. """ self.add_control_one_port(port, "noop") - if port in self.write_index: + if port in self.write_ports: self.add_data(data,port) self.add_address(address, port) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 1e9401b8..8ccb71b0 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -5,6 +5,7 @@ import design import math from math import log,sqrt,ceil import contact +import pgates from pinv import pinv from pnand2 import pnand2 from pnor2 import pnor2 @@ -66,26 +67,26 @@ class bank(design.design): def add_pins(self): """ Adding pins for Bank module""" - for port in range(self.total_read): + for port in self.read_ports: for bit in range(self.word_size): - self.add_pin("dout{0}_{1}".format(self.read_index[port],bit),"OUT") - for port in range(self.total_write): + self.add_pin("dout{0}_{1}".format(port,bit),"OUT") + for port in self.write_ports: for bit in range(self.word_size): self.add_pin("din{0}_{1}".format(port,bit),"IN") - for port in range(self.total_ports): + for port in self.all_ports: for bit in range(self.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_*. if self.num_banks > 1: - for port in range(self.total_ports): + for port in self.all_ports: self.add_pin("bank_sel{}".format(port),"INPUT") - for port in range(self.total_read): - self.add_pin("s_en{0}".format(self.read_index[port]), "INPUT") - for port in range(self.total_write): + for port in self.read_ports: + self.add_pin("s_en{0}".format(port), "INPUT") + for port in self.write_ports: self.add_pin("w_en{0}".format(port), "INPUT") - for port in range(self.total_ports): + for port in self.all_ports: self.add_pin("clk_buf_bar{0}".format(port),"INPUT") self.add_pin("clk_buf{0}".format(port),"INPUT") self.add_pin("vdd","POWER") @@ -96,8 +97,8 @@ class bank(design.design): """ Create routing amoung the modules """ self.route_central_bus() self.route_precharge_to_bitcell_array() - self.route_col_mux_to_bitcell_array() - self.route_sense_amp_to_col_mux_or_bitcell_array() + self.route_col_mux_to_precharge_array() + self.route_sense_amp_to_col_mux_or_precharge_array() self.route_sense_amp_out() self.route_wordline_driver() self.route_write_driver() @@ -127,26 +128,76 @@ class bank(design.design): self.create_column_decoder() self.create_bank_select() - - def place_modules(self): - """ Add modules. The order should not matter! """ - - # Above the bitcell array - self.place_bitcell_array() - self.place_precharge_array() + def compute_module_offsets(self): + """ + Compute the module offsets. + """ + # UPPER RIGHT QUADRANT + # Bitcell array is placed at (0,0) + self.bitcell_array_offset = vector(0,0) + + # LOWER RIGHT QUADRANT # Below the bitcell array - self.place_column_mux_array() - self.place_sense_amp_array() - self.place_write_driver_array() + y_offset = self.precharge_array[0].height + self.m2_gap + self.precharge_offset = vector(0,-y_offset) + if self.col_addr_size > 0: + y_offset += self.column_mux_array[0].height + self.column_mux_offset = vector(0,-y_offset) + y_offset += self.sense_amp_array.height + self.sense_amp_offset = vector(0,-y_offset) + y_offset += self.write_driver_array.height + self.write_driver_offset = vector(0,-y_offset) + # UPPER LEFT QUADRANT # To the left of the bitcell array - self.place_row_decoder() - self.place_wordline_driver() - self.place_column_decoder() + # The wordline driver is placed to the right of the main decoder width. + x_offset = self.central_bus_width + self.wordline_driver.width - self.m2_pitch + self.wordline_driver_offset = vector(-x_offset,0) + x_offset += self.row_decoder.width + self.m2_pitch + self.row_decoder_offset = vector(-x_offset,0) - self.place_bank_select() + # LOWER LEFT QUADRANT + # Place the col decoder right aligned with row decoder (x_offset doesn't change) + # Below the bitcell array + if self.col_addr_size > 0: + y_offset = self.col_decoder.height + else: + y_offset = 0 + y_offset += 2*drc("well_to_well") + self.column_decoder_offset = vector(-x_offset,-y_offset) + + # Bank select gets placed below the column decoder (x_offset doesn't change) + if self.col_addr_size > 0: + y_offset = min(self.column_decoder_offset.y, self.column_mux_offset.y) + else: + y_offset = self.row_decoder_offset.y + if self.num_banks > 1: + y_offset += self.bank_select.height + drc("well_to_well") + self.bank_select_offset = vector(-x_offset,-y_offset) + + def place_modules(self): + """ Place the modules. """ + + self.compute_module_offsets() + + # UPPER RIGHT QUADRANT + self.place_bitcell_array(self.bitcell_array_offset) + + # LOWER RIGHT QUADRANT + self.place_precharge_array([self.precharge_offset]*len(self.read_ports)) + self.place_column_mux_array([self.column_mux_offset]*len(self.all_ports)) + self.place_sense_amp_array([self.sense_amp_offset]*len(self.read_ports)) + self.place_write_driver_array([self.write_driver_offset]*len(self.write_ports)) + + # UPPER LEFT QUADRANT + self.place_row_decoder([self.row_decoder_offset]*len(self.all_ports)) + self.place_wordline_driver([self.wordline_driver_offset]*len(self.all_ports)) + + # LOWER LEFT QUADRANT + self.place_column_decoder([self.column_decoder_offset]*len(self.all_ports)) + self.place_bank_select([self.bank_select_offset]*len(self.all_ports)) def compute_sizes(self): @@ -184,7 +235,7 @@ class bank(design.design): # These will be outputs of the gaters if this is multibank, if not, normal signals. self.control_signals = [] - for port in range(self.total_ports): + for port in self.all_ports: if self.num_banks > 1: self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]]) else: @@ -226,30 +277,33 @@ class bank(design.design): self.add_mod(self.bitcell_array) # create arrays of bitline and bitline_bar names for read, write, or all ports - self.read_bl_list = self.bitcell.list_read_bl_names() - self.read_br_list = self.bitcell.list_read_br_names() + self.read_bl_names = self.bitcell.list_read_bl_names() + self.read_br_names = self.bitcell.list_read_br_names() - self.write_bl_list = self.bitcell.list_write_bl_names() - self.write_br_list = self.bitcell.list_write_br_names() + self.write_bl_names = self.bitcell.list_write_bl_names() + self.write_br_names = self.bitcell.list_write_br_names() - self.total_bl_list = self.bitcell.list_all_bl_names() - self.total_br_list = self.bitcell.list_all_br_names() + self.total_bl_names = self.bitcell.list_all_bl_names() + self.total_br_names = self.bitcell.list_all_br_names() - self.total_wl_list = self.bitcell.list_all_wl_names() - self.total_bitline_list = self.bitcell.list_all_bitline_names() + self.total_wl_names = self.bitcell.list_all_wl_names() + self.total_bitline_names = self.bitcell.list_all_bitline_names() self.precharge_array = [] - for port in range(self.total_read): - self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.read_bl_list[port], bitcell_br=self.read_br_list[port])) - self.add_mod(self.precharge_array[port]) + for port in self.all_ports: + if port in self.read_ports: + self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.total_bl_names[port], bitcell_br=self.total_br_names[port])) + self.add_mod(self.precharge_array[port]) + else: + self.precharge_array.append(None) if self.col_addr_size > 0: self.column_mux_array = [] - for port in range(self.total_ports): + for port in self.all_ports: self.column_mux_array.append(self.mod_column_mux_array(columns=self.num_cols, word_size=self.word_size, - bitcell_bl=self.total_bl_list[port], - bitcell_br=self.total_br_list[port])) + bitcell_bl=self.total_bl_names[port], + bitcell_br=self.total_br_names[port])) self.add_mod(self.column_mux_array[port]) @@ -284,151 +338,153 @@ class bank(design.design): temp = [] for col in range(self.num_cols): - for bitline in self.total_bitline_list: + for bitline in self.total_bitline_names: temp.append(bitline+"_{0}".format(col)) for row in range(self.num_rows): - for wordline in self.total_wl_list: + for wordline in self.total_wl_names: temp.append(wordline+"_{0}".format(row)) temp.append("vdd") temp.append("gnd") self.connect_inst(temp) - def place_bitcell_array(self): + def place_bitcell_array(self, offset): """ Placing Bitcell Array """ - self.bitcell_array_inst.place(vector(0,0)) + self.bitcell_array_inst.place(offset) def create_precharge_array(self): """ Creating Precharge """ self.precharge_array_inst = [] - for port in range(self.total_read): + for port in self.read_ports: self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(port), mod=self.precharge_array[port])) temp = [] for i in range(self.num_cols): - temp.append(self.read_bl_list[port]+"_{0}".format(i)) - temp.append(self.read_br_list[port]+"_{0}".format(i)) - temp.extend([self.prefix+"clk_buf_bar{0}".format(self.read_index[port]), "vdd"]) + temp.append(self.total_bl_names[port]+"_{0}".format(i)) + temp.append(self.total_br_names[port]+"_{0}".format(i)) + temp.extend([self.prefix+"clk_buf_bar{0}".format(port), "vdd"]) self.connect_inst(temp) - def place_precharge_array(self): + def place_precharge_array(self, offsets): """ Placing Precharge """ + + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place precharge array.") # FIXME: place for multiport - for port in range(self.total_read): - # The wells must be far enough apart - # The enclosure is for the well and the spacing is to the bitcell wells - y_offset = self.bitcell_array.height + self.m2_gap - self.precharge_array_inst[port].place(vector(0,y_offset)) + for port in self.read_ports: + self.precharge_array_inst[port].place(offsets[port]) def create_column_mux_array(self): """ Creating Column Mux when words_per_row > 1 . """ + self.col_mux_array_inst = [] + if self.col_addr_size == 0: return - self.col_mux_array_inst = [] - for port in range(self.total_ports): + for port in self.all_ports: self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(port), mod=self.column_mux_array[port])) temp = [] for col in range(self.num_cols): - temp.append(self.total_bl_list[port]+"_{0}".format(col)) - temp.append(self.total_br_list[port]+"_{0}".format(col)) + temp.append(self.total_bl_names[port]+"_{0}".format(col)) + temp.append(self.total_br_names[port]+"_{0}".format(col)) for word in range(self.words_per_row): temp.append("sel{0}_{1}".format(port,word)) for bit in range(self.word_size): - temp.append(self.total_bl_list[port]+"_out_{0}".format(bit)) - temp.append(self.total_br_list[port]+"_out_{0}".format(bit)) + temp.append(self.total_bl_names[port]+"_out_{0}".format(bit)) + temp.append(self.total_br_names[port]+"_out_{0}".format(bit)) temp.append("gnd") self.connect_inst(temp) + - def place_column_mux_array(self): + def place_column_mux_array(self, offsets): """ Placing Column Mux when words_per_row > 1 . """ - if self.col_addr_size > 0: - self.column_mux_height = self.column_mux_array[0].height + self.m2_gap - else: - self.column_mux_height = 0 + if self.col_addr_size == 0: return - for port in range(self.total_ports): - y_offset = self.column_mux_height - self.col_mux_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column mux array.") + + for port in self.all_ports: + self.col_mux_array_inst[port].place(offsets[port]) def create_sense_amp_array(self): """ Creating Sense amp """ self.sense_amp_array_inst = [] - for port in range(self.total_read): + for port in self.read_ports: self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(port), mod=self.sense_amp_array)) temp = [] for bit in range(self.word_size): - temp.append("dout{0}_{1}".format(self.read_index[port],bit)) + temp.append("dout{0}_{1}".format(port,bit)) if self.words_per_row == 1: - temp.append(self.read_bl_list[port]+"_{0}".format(bit)) - temp.append(self.read_br_list[port]+"_{0}".format(bit)) + temp.append(self.total_bl_names[port]+"_{0}".format(bit)) + temp.append(self.total_br_names[port]+"_{0}".format(bit)) else: - temp.append(self.read_bl_list[port]+"_out_{0}".format(bit)) - temp.append(self.read_br_list[port]+"_out_{0}".format(bit)) + temp.append(self.total_bl_names[port]+"_out_{0}".format(bit)) + temp.append(self.total_br_names[port]+"_out_{0}".format(bit)) - temp.extend([self.prefix+"s_en{}".format(self.read_index[port]), "vdd", "gnd"]) + temp.extend([self.prefix+"s_en{}".format(port), "vdd", "gnd"]) self.connect_inst(temp) - def place_sense_amp_array(self): + def place_sense_amp_array(self, offsets): """ Placing Sense amp """ + + debug.check(len(offsets)>=len(self.read_ports), "Insufficient offsets to place sense amp array.") # FIXME: place for multiport - for port in range(self.total_read): - y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap - self.sense_amp_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) + for port in self.read_ports: + self.sense_amp_array_inst[port].place(offsets[port]) def create_write_driver_array(self): """ Creating Write Driver """ self.write_driver_array_inst = [] - for port in range(self.total_write): - self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port), - mod=self.write_driver_array)) + for port in self.all_ports: + if port in self.write_ports: + self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port), + mod=self.write_driver_array)) + else: + self.write_driver_array_inst.append(None) temp = [] for bit in range(self.word_size): temp.append("din{0}_{1}".format(port,bit)) for bit in range(self.word_size): if (self.words_per_row == 1): - temp.append(self.write_bl_list[port]+"_{0}".format(bit)) - temp.append(self.write_br_list[port]+"_{0}".format(bit)) + temp.append(self.total_bl_names[port]+"_{0}".format(bit)) + temp.append(self.total_br_names[port]+"_{0}".format(bit)) else: - temp.append(self.write_bl_list[port]+"_out_{0}".format(bit)) - temp.append(self.write_br_list[port]+"_out_{0}".format(bit)) + temp.append(self.total_bl_names[port]+"_out_{0}".format(bit)) + temp.append(self.total_br_names[port]+"_out_{0}".format(bit)) temp.extend([self.prefix+"w_en{0}".format(port), "vdd", "gnd"]) self.connect_inst(temp) - def place_write_driver_array(self): + def place_write_driver_array(self, offsets): """ Placing Write Driver """ - # FIXME: place for multiport - for port in range(self.total_write): - y_offset = self.sense_amp_array.height + self.column_mux_height \ - + self.m2_gap + self.write_driver_array.height - self.write_driver_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) + debug.check(len(offsets)>=len(self.write_ports), "Insufficient offsets to place write driver array.") + + for port in self.write_ports: + self.write_driver_array_inst[port].place(offsets[port]) def create_row_decoder(self): """ Create the hierarchical row decoder """ self.row_decoder_inst = [] - for port in range(self.total_ports): + for port in self.all_ports: self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(port), mod=self.row_decoder)) @@ -441,9 +497,11 @@ class bank(design.design): self.connect_inst(temp) - def place_row_decoder(self): + def place_row_decoder(self, offsets): """ Place the hierarchical row decoder """ + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.") + # The address and control bus will be in between decoder and the main memory array # This bus will route address bits to the decoder input and column mux inputs. # The wires are actually routed after we placed the stuff on both sides. @@ -451,16 +509,15 @@ class bank(design.design): # The address flop and decoder are aligned in the x coord. # FIXME: place for multiport - for port in range(self.total_ports): - x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - self.row_decoder_inst[port].place(vector(x_offset,0)) + for port in self.all_ports: + self.row_decoder_inst[port].place(offsets[port]) def create_wordline_driver(self): """ Create the Wordline Driver """ self.wordline_driver_inst = [] - for port in range(self.total_ports): + for port in self.all_ports: self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(port), mod=self.wordline_driver)) @@ -468,21 +525,20 @@ class bank(design.design): for row in range(self.num_rows): temp.append("dec_out{0}_{1}".format(port,row)) for row in range(self.num_rows): - temp.append(self.total_wl_list[port]+"_{0}".format(row)) + temp.append(self.total_wl_names[port]+"_{0}".format(row)) temp.append(self.prefix+"clk_buf{0}".format(port)) temp.append("vdd") temp.append("gnd") self.connect_inst(temp) - def place_wordline_driver(self): + def place_wordline_driver(self, offsets): """ Place the Wordline Driver """ - # FIXME: place for multiport - for port in range(self.total_ports): - # The wordline driver is placed to the right of the main decoder width. - x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch - self.wordline_driver_inst[port].place(vector(x_offset,0)) + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place wordline driver array.") + + for port in self.all_ports: + self.wordline_driver_inst[port].place(offsets[port]) def create_column_decoder(self): @@ -504,7 +560,7 @@ class bank(design.design): debug.error("Invalid column decoder?",-1) self.col_decoder_inst = [] - for port in range(self.total_ports): + for port in self.all_ports: self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(port), mod=self.col_decoder)) @@ -517,21 +573,17 @@ class bank(design.design): self.connect_inst(temp) - def place_column_decoder(self): + def place_column_decoder(self, offsets): """ Place a 2:4 or 3:8 column address decoder. """ if self.col_addr_size == 0: return - # FIXME: place for multiport - for port in range(self.total_ports): - col_decoder_inst = self.col_decoder_inst[port] - - # Place the col decoder right aligned with row decoder - x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) - y_off = -(self.col_decoder.height + 2*drc("well_to_well")) - col_decoder_inst.place(vector(x_off,y_off)) + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.") + + for port in self.all_ports: + col_decoder_inst.place(offsets[port]) @@ -542,7 +594,7 @@ class bank(design.design): return self.bank_select_inst = [] - for port in range(self.total_ports): + for port in self.all_ports: self.bank_select_inst.append(self.add_inst(name="bank_select{}".format(port), mod=self.bank_select)) @@ -554,22 +606,16 @@ class bank(design.design): self.connect_inst(temp) - def place_bank_select(self): + def place_bank_select(self, offsets): """ Place the bank select logic. """ if not self.num_banks > 1: return - - # FIXME: place for multiport - for port in range(self.total_ports): - x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - if self.col_addr_size > 0: - y_off = min(self.col_decoder_inst[port].by(), self.col_mux_array_inst[port].by()) - else: - y_off = self.row_decoder_inst[port].by() - y_off -= (self.bank_select.height + drc("well_to_well")) - self.bank_select_pos = vector(x_off,y_off) - self.bank_select_inst[port].place(self.bank_select_pos) + + debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place bank select logic.") + + for port in self.all_ports: + self.bank_select_inst[port].place(offsets[port]) def route_supplies(self): @@ -581,7 +627,7 @@ class bank(design.design): def route_bank_select(self): """ Route the bank select logic. """ - for port in range(self.total_ports): + for port in self.all_ports: if self.port_id[port] == "rw": bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"] gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"] @@ -641,7 +687,7 @@ class bank(design.design): # The max point is always the top of the precharge bitlines # Add a vdd and gnd power rail above the array # FIXME: Update multiport - self.max_y_offset = self.precharge_array_inst[0].uy() + 3*self.m1_width + self.max_y_offset = self.bitcell_array_inst.ur().y + 3*self.m1_width self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width self.min_x_offset = self.row_decoder_inst[0].lx() @@ -661,7 +707,7 @@ class bank(design.design): # and control lines. # The bank is at (0,0), so this is to the left of the y-axis. # 2 pitches on the right for vias/jogs to access the inputs - for port in range(self.total_ports): + for port in self.all_ports: control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset) control_bus_length = self.max_y_offset - self.min_y_offset self.bus_xoffset = self.create_bus(layer="metal2", @@ -677,12 +723,12 @@ class bank(design.design): """ Routing of BL and BR between pre-charge and bitcell array """ # FIXME: Update for multiport - for port in range(self.total_read): + for port in self.read_ports: for col in range(self.num_cols): - precharge_bl = self.precharge_array_inst[port].get_pin("bl_{}".format(col)).bc() - precharge_br = self.precharge_array_inst[port].get_pin("br_{}".format(col)).bc() - bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"_{}".format(col)).uc() - bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"_{}".format(col)).uc() + precharge_bl = self.precharge_array_inst[port].get_pin("bl_{}".format(col)).uc() + precharge_br = self.precharge_array_inst[port].get_pin("br_{}".format(col)).uc() + bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_names[port]+"_{}".format(col)).bc() + bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_names[port]+"_{}".format(col)).bc() yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), @@ -691,32 +737,32 @@ class bank(design.design): vector(bitcell_br.x,yoffset), bitcell_br]) - def route_col_mux_to_bitcell_array(self): - """ Routing of BL and BR between col mux and bitcell array """ + def route_col_mux_to_precharge_array(self): + """ Routing of BL and BR between col mux and precharge array """ # Only do this if we have a column mux! if self.col_addr_size==0: return # FIXME: Update for multiport - for port in range(self.total_ports): + for port in self.all_ports: for col in range(self.num_cols): col_mux_bl = self.col_mux_array_inst[port].get_pin("bl_{}".format(col)).uc() col_mux_br = self.col_mux_array_inst[port].get_pin("br_{}".format(col)).uc() - bitcell_bl = self.bitcell_array_inst.get_pin(self.total_bl_list[port]+"_{}".format(col)).bc() - bitcell_br = self.bitcell_array_inst.get_pin(self.total_br_list[port]+"_{}".format(col)).bc() + precharge_bl = self.precharge_array_inst[port].get_pin(self.total_bl_names[port]+"_{}".format(col)).bc() + precharge_br = self.precharge_array_inst[port].get_pin(self.total_br_names[port]+"_{}".format(col)).bc() - yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y) + yoffset = 0.5*(col_mux_bl.y+precharge_bl.y) self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), - vector(bitcell_bl.x,yoffset), bitcell_bl]) + vector(precharge_bl.x,yoffset), precharge_bl]) self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset), - vector(bitcell_br.x,yoffset), bitcell_br]) + vector(precharge_br.x,yoffset), precharge_br]) - def route_sense_amp_to_col_mux_or_bitcell_array(self): - """ Routing of BL and BR between sense_amp and column mux or bitcell array """ + def route_sense_amp_to_col_mux_or_precharge_array(self): + """ Routing of BL and BR between sense_amp and column mux or precharge array """ - for port in range(self.total_read): + for port in self.read_ports: for bit in range(self.word_size): sense_amp_bl = self.sense_amp_array_inst[port].get_pin("bl_{}".format(bit)).uc() sense_amp_br = self.sense_amp_array_inst[port].get_pin("br_{}".format(bit)).uc() @@ -726,9 +772,9 @@ class bank(design.design): connect_bl = self.col_mux_array_inst[port].get_pin("bl_out_{}".format(bit)).bc() connect_br = self.col_mux_array_inst[port].get_pin("br_out_{}".format(bit)).bc() else: - # Sense amp is directly connected to the bitcell array - connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"_{}".format(bit)).bc() - connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"_{}".format(bit)).bc() + # Sense amp is directly connected to the precharge array + connect_bl = self.precharge_array_inst[port].get_pin(self.read_bl_names[port]+"_{}".format(bit)).bc() + connect_br = self.precharge_array_inst[port].get_pin(self.read_br_names[port]+"_{}".format(bit)).bc() yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) @@ -742,10 +788,10 @@ class bank(design.design): """ Add pins for the sense amp output """ # FIXME: Update for multiport - for port in range(self.total_read): + for port in self.read_ports: for bit in range(self.word_size): data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit)) - self.add_layout_pin_rect_center(text="dout{0}_{1}".format(self.read_index[port],bit), + self.add_layout_pin_rect_center(text="dout{0}_{1}".format(self.read_ports[port],bit), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), @@ -757,7 +803,7 @@ class bank(design.design): # FIXME: Update for multiport # Create inputs for the row address lines - for port in range(self.total_ports): + for port in self.all_ports: for row in range(self.row_addr_size): addr_idx = row + self.col_addr_size decoder_name = "addr_{}".format(row) @@ -767,7 +813,7 @@ class bank(design.design): def route_write_driver(self): """ Connecting write driver """ - for port in range(self.total_ports): + for port in self.all_ports: for row in range(self.word_size): data_name = "data_{}".format(row) din_name = "din{0}_{1}".format(port,row) @@ -776,7 +822,7 @@ class bank(design.design): def route_wordline_driver(self): """ Connecting Wordline driver output to Bitcell WL connection """ - for port in range(self.total_ports): + for port in self.all_ports: for row in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs decoder_out_pos = self.row_decoder_inst[port].get_pin("decode_{}".format(row)).rc() @@ -787,7 +833,7 @@ class bank(design.design): # The mid guarantees we exit the input cell to the right. driver_wl_pos = self.wordline_driver_inst[port].get_pin("wl_{}".format(row)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[port]+"_{}".format(row)).lc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_names[port]+"_{}".format(row)).lc() mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0) mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) @@ -798,7 +844,7 @@ class bank(design.design): if not self.col_addr_size>0: return - for port in range(self.total_ports): + for port in self.all_ports: if self.col_addr_size == 1: # Connect to sel[0] and sel[1] @@ -894,16 +940,16 @@ class bank(design.design): read_inst = 0 # Control lines for RW ports - for port in range(self.total_ports): + for port in self.all_ports: connection = [] - if (self.port_id[port] == "rw") or (self.port_id[port] == "r"): - connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[read_inst].get_pin("en").lc())) - if (self.port_id[port] == "rw") or (self.port_id[port] == "w"): - connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[write_inst].get_pin("en").lc())) - write_inst += 1 - if (self.port_id[port] == "rw") or (self.port_id[port] == "r"): - connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[read_inst].get_pin("en").lc())) - read_inst += 1 + if port in self.read_ports: + connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc())) + + if port in self.write_ports: + connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc())) + + if port in self.read_ports: + connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[port].get_pin("en").lc())) for (control_signal, pin_pos) in connection: control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) @@ -937,7 +983,7 @@ class bank(design.design): bitcell_array_delay = self.bitcell_array.analytical_delay(word_driver_delay.slew) #This also essentially creates the same delay for each port. Good structure, no substance - for port in range(self.total_ports): + for port in self.all_ports: if self.words_per_row > 1: column_mux_delay = self.column_mux_array[port].analytical_delay(vdd, bitcell_array_delay.slew, self.sense_amp_array.input_load()) diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index 84aaa63d..e9ebfb0c 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -125,10 +125,10 @@ class replica_bitline(design.design): self.rbc_inst=self.add_inst(name="bitcell", mod=self.replica_bitcell) temp = [] - for port in range(self.total_ports): + for port in self.all_ports: temp.append("bl{}_0".format(port)) temp.append("br{}_0".format(port)) - for port in range(self.total_ports): + for port in self.all_ports: temp.append("delayed_en") temp.append("vdd") temp.append("gnd") @@ -139,11 +139,11 @@ class replica_bitline(design.design): mod=self.rbl) temp = [] - for port in range(self.total_ports): + for port in self.all_ports: temp.append("bl{}_0".format(port)) temp.append("br{}_0".format(port)) for wl in range(self.bitcell_loads): - for port in range(self.total_ports): + for port in self.all_ports: temp.append("gnd") temp.append("vdd") temp.append("gnd") @@ -195,7 +195,7 @@ class replica_bitline(design.design): self.add_power_pin("gnd", pin_extension) # for multiport, need to short wordlines to each other so they all connect to gnd. - wl_last = self.wl_list[self.total_ports-1]+"_{}".format(row) + wl_last = self.wl_list[-1]+"_{}".format(row) pin_last = self.rbl_inst.get_pin(wl_last) self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0)) @@ -203,7 +203,7 @@ class replica_bitline(design.design): """Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins.""" #Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord. #This is my (Hunter) first time editing layout in openram so this function is likely not optimal. - if self.total_ports > 1: + if len(self.all_ports) > 1: #1. Create vertical metal for all the bitlines to connect to #m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped correct_y = vector(0, 0.5*drc("minwidth_metal1")) @@ -234,7 +234,7 @@ class replica_bitline(design.design): debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1) #2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this. - for port in range(self.total_ports): + for port in self.all_ports: if is_replica_cell: wl = self.wl_list[port] pin = self.rbc_inst.get_pin(wl) @@ -319,7 +319,7 @@ class replica_bitline(design.design): # 4. Short wodlines if multiport wl = self.wl_list[0] - wl_last = self.wl_list[self.total_ports-1] + wl_last = self.wl_list[-1] pin = self.rbc_inst.get_pin(wl) pin_last = self.rbc_inst.get_pin(wl_last) x_offset = self.short_wordlines(pin, pin_last, "left", True) diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 3886a4df..260b26fc 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -57,7 +57,7 @@ class sram_1bank(sram_base): # 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. - for port in range(self.total_ports): + for port in self.all_ports: control_pos = vector(-self.control_logic.width - 2*self.m2_pitch, self.bank.bank_center.y - self.control_logic.control_logic_center.y) self.control_logic_inst[port].place(control_pos) @@ -94,13 +94,14 @@ class sram_1bank(sram_base): """ Add the top-level pins for a single bank SRAM with control. """ - for port in range(self.total_ports): + for port in self.all_ports: # Connect the control pins as inputs for signal in self.control_logic_inputs[port] + ["clk"]: self.copy_layout_pin(self.control_logic_inst[port], signal, signal+"{}".format(port)) - for bit in range(self.word_size): - self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "DOUT{0}[{1}]".format(port,bit)) + if port in self.read_ports: + for bit in range(self.word_size): + self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "DOUT{0}[{1}]".format(port,bit)) # Lower address bits for bit in range(self.col_addr_size): @@ -109,8 +110,9 @@ class sram_1bank(sram_base): for bit in range(self.row_addr_size): self.copy_layout_pin(self.row_addr_dff_inst[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit+self.col_addr_size)) - for bit in range(self.word_size): - self.copy_layout_pin(self.data_dff_inst[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit)) + if port in self.write_ports: + for bit in range(self.word_size): + self.copy_layout_pin(self.data_dff_inst[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit)) def route(self): """ Route a single bank SRAM """ @@ -132,7 +134,7 @@ class sram_1bank(sram_base): """ Route the clock network """ # This is the actual input to the SRAM - for port in range(self.total_ports): + for port in self.all_ports: self.copy_layout_pin(self.control_logic_inst[port], "clk", "clk{}".format(port)) # Connect all of these clock pins to the clock in the central bus @@ -172,7 +174,7 @@ class sram_1bank(sram_base): def route_control_logic(self): """ Route the outputs from the control logic module """ - for port in range(self.total_ports): + for port in self.all_ports: for signal in self.control_logic_outputs[port]: src_pin = self.control_logic_inst[port].get_pin(signal) dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) @@ -184,7 +186,7 @@ class sram_1bank(sram_base): def route_row_addr_dff(self): """ Connect the output of the row flops to the bank pins """ - for port in range(self.total_ports): + 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) @@ -200,7 +202,7 @@ class sram_1bank(sram_base): def route_col_addr_dff(self): """ Connect the output of the row flops to the bank pins """ - for port in range(self.total_ports): + for port in self.all_ports: bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)] col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1", pitch=self.m1_pitch, @@ -220,7 +222,7 @@ class sram_1bank(sram_base): def route_data_dff(self): """ Connect the output of the data flops to the write driver """ # This is where the channel will start (y-dimension at least) - for port in range(self.total_write): + for port in self.write_ports: offset = self.data_dff_inst[port].ul() + vector(0, self.m1_pitch) dff_names = ["dout_{}".format(x) for x in range(self.word_size)] diff --git a/compiler/sram_base.py b/compiler/sram_base.py index a1be1f30..7135b9f2 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -24,38 +24,38 @@ class sram_base(design): def add_pins(self): """ Add pins for entire SRAM. """ - for port in range(self.total_write): + for port in self.write_ports: for bit in range(self.word_size): self.add_pin("DIN{0}[{1}]".format(port,bit),"INPUT") - for port in range(self.total_ports): + for port in self.all_ports: for bit in range(self.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 range(self.total_ports): - if self.port_id[port] == "rw": + 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 self.port_id[port] == "w": + 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 range(self.total_ports): + for port in self.all_ports: self.add_pin("csb{}".format(port),"INPUT") - for port in range(self.num_rw_ports): + for port in self.readwrite_ports: self.add_pin("web{}".format(port),"INPUT") - for port in range(self.total_ports): + for port in self.all_ports: self.add_pin("clk{}".format(port),"INPUT") - for port in range(self.total_read): + for port in self.read_ports: for bit in range(self.word_size): - self.add_pin("DOUT{0}[{1}]".format(self.read_index[port],bit),"OUTPUT") + self.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") @@ -138,7 +138,7 @@ class sram_base(design): # Vertical bus # The order of the control signals on the control bus: self.control_bus_names = [] - for port in range(self.total_ports): + for port in self.all_ports: self.control_bus_names[port] = ["clk_buf{}".format(port), "clk_buf_bar{}".format(port)] if (self.port_id[port] == "rw") or (self.port_id[port] == "w"): self.control_bus_names[port].append("w_en{}".format(port)) @@ -268,23 +268,23 @@ class sram_base(design): mod=self.bank)) temp = [] - for port in range(self.total_read): + for port in self.read_ports: for bit in range(self.word_size): - temp.append("DOUT{0}[{1}]".format(self.read_index[port],bit)) - for port in range(self.total_write): + temp.append("DOUT{0}[{1}]".format(port,bit)) + for port in self.write_ports: for bit in range(self.word_size): temp.append("BANK_DIN{0}[{1}]".format(port,bit)) - for port in range(self.total_ports): + for port in self.all_ports: for bit in range(self.bank_addr_size): temp.append("A{0}[{1}]".format(port,bit)) if(self.num_banks > 1): - for port in range(self.total_ports): + for port in self.all_ports: temp.append("bank_sel{0}[{1}]".format(port,bank_num)) - for port in range(self.total_read): - temp.append("s_en{0}".format(self.read_index[port])) - for port in range(self.total_write): + for port in self.read_ports: + temp.append("s_en{0}".format(port)) + for port in self.readwrite_ports: temp.append("w_en{0}".format(port)) - for port in range(self.total_ports): + for port in self.all_ports: temp.append("clk_buf_bar{0}".format(port)) temp.append("clk_buf{0}".format(port)) temp.extend(["vdd", "gnd"]) @@ -327,7 +327,7 @@ class sram_base(design): def create_row_addr_dff(self): """ Add all address flops for the main decoder """ insts = [] - for port in range(self.total_ports): + for port in self.all_ports: insts.append(self.add_inst(name="row_address{}".format(port), mod=self.row_addr_dff)) @@ -346,7 +346,7 @@ class sram_base(design): def create_col_addr_dff(self): """ Add and place all address flops for the column decoder """ insts = [] - for port in range(self.total_ports): + for port in self.all_ports: insts.append(self.add_inst(name="col_address{}".format(port), mod=self.col_addr_dff)) @@ -365,7 +365,7 @@ class sram_base(design): def create_data_dff(self): """ Add and place all data flops """ insts = [] - for port in range(self.total_write): + for port in self.write_ports: insts.append(self.add_inst(name="data_dff{}".format(port), mod=self.data_dff)) @@ -384,10 +384,10 @@ class sram_base(design): def create_control_logic(self): """ Add and place control logic """ insts = [] - for port in range(self.total_ports): - if self.port_id[port] == "rw": + for port in self.all_ports: + if port in self.readwrite_ports: mod = self.control_logic_rw - elif self.port_id[port] == "w": + elif port in self.write_ports: mod = self.control_logic_w else: mod = self.control_logic_r @@ -396,12 +396,12 @@ class sram_base(design): mod=mod)) temp = ["csb{}".format(port)] - if self.port_id[port] == "rw": + if port in self.readwrite_ports: temp.append("web{}".format(port)) temp.append("clk{}".format(port)) - if (self.port_id[port] == "rw") or (self.port_id[port] == "r"): + if port in self.read_ports: temp.append("s_en{}".format(port)) - if (self.port_id[port] == "rw") or (self.port_id[port] == "w"): + if port in self.write_ports: temp.append("w_en{}".format(port)) temp.extend(["clk_buf_bar{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"]) self.connect_inst(temp) From 5d733154e9a7a9a5b40beca427ae45ee7bcf6882 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 15:18:51 -0800 Subject: [PATCH 09/31] Refactor bank to allow easier multiport. --- compiler/modules/bank.py | 86 +++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 8ccb71b0..62dbc85e 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -99,6 +99,7 @@ class bank(design.design): self.route_precharge_to_bitcell_array() self.route_col_mux_to_precharge_array() self.route_sense_amp_to_col_mux_or_precharge_array() + self.route_write_driver_to_sense_amp() self.route_sense_amp_out() self.route_wordline_driver() self.route_write_driver() @@ -143,11 +144,11 @@ class bank(design.design): y_offset = self.precharge_array[0].height + self.m2_gap self.precharge_offset = vector(0,-y_offset) if self.col_addr_size > 0: - y_offset += self.column_mux_array[0].height + y_offset += self.column_mux_array[0].height + self.m2_gap self.column_mux_offset = vector(0,-y_offset) - y_offset += self.sense_amp_array.height + y_offset += self.sense_amp_array.height + self.m2_gap self.sense_amp_offset = vector(0,-y_offset) - y_offset += self.write_driver_array.height + y_offset += self.write_driver_array.height + self.m2_gap self.write_driver_offset = vector(0,-y_offset) # UPPER LEFT QUADRANT @@ -583,7 +584,7 @@ class bank(design.design): debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.") for port in self.all_ports: - col_decoder_inst.place(offsets[port]) + self.col_decoder_inst[port].place(offsets[port]) @@ -746,43 +747,43 @@ class bank(design.design): # FIXME: Update for multiport for port in self.all_ports: - for col in range(self.num_cols): - col_mux_bl = self.col_mux_array_inst[port].get_pin("bl_{}".format(col)).uc() - col_mux_br = self.col_mux_array_inst[port].get_pin("br_{}".format(col)).uc() - precharge_bl = self.precharge_array_inst[port].get_pin(self.total_bl_names[port]+"_{}".format(col)).bc() - precharge_br = self.precharge_array_inst[port].get_pin(self.total_br_names[port]+"_{}".format(col)).bc() - - yoffset = 0.5*(col_mux_bl.y+precharge_bl.y) - self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), - vector(precharge_bl.x,yoffset), precharge_bl]) - self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset), - vector(precharge_br.x,yoffset), precharge_br]) + bottom_inst = self.col_mux_array_inst[port] + top_inst = self.precharge_array_inst[port] + top_bl = self.total_bl_names[port]+"_{}" + top_br = self.total_br_names[port]+"_{}" + self.connect_bitlines(top_inst, bottom_inst, self.num_cols, + top_bl_name=top_bl, top_br_name=top_br) def route_sense_amp_to_col_mux_or_precharge_array(self): """ Routing of BL and BR between sense_amp and column mux or precharge array """ for port in self.read_ports: - for bit in range(self.word_size): - sense_amp_bl = self.sense_amp_array_inst[port].get_pin("bl_{}".format(bit)).uc() - sense_amp_br = self.sense_amp_array_inst[port].get_pin("br_{}".format(bit)).uc() - - if self.col_addr_size>0: - # Sense amp is connected to the col mux - connect_bl = self.col_mux_array_inst[port].get_pin("bl_out_{}".format(bit)).bc() - connect_br = self.col_mux_array_inst[port].get_pin("br_out_{}".format(bit)).bc() - else: - # Sense amp is directly connected to the precharge array - connect_bl = self.precharge_array_inst[port].get_pin(self.read_bl_names[port]+"_{}".format(bit)).bc() - connect_br = self.precharge_array_inst[port].get_pin(self.read_br_names[port]+"_{}".format(bit)).bc() + bottom_inst = self.sense_amp_array_inst[port] + + if self.col_addr_size>0: + # Sense amp is connected to the col mux + top_inst = self.col_mux_array_inst[port] + top_bl = "bl_out_{}" + top_br = "br_out_{}" + else: + # Sense amp is directly connected to the precharge array + top_inst = self.precharge_array_inst[port] + top_bl = self.total_bl_names[port]+"_{}" + top_br = self.total_br_names[port]+"_{}" - - yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) - self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset), - vector(connect_bl.x,yoffset), connect_bl]) - self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset), - vector(connect_br.x,yoffset), connect_br]) + self.connect_bitlines(top_inst, bottom_inst, self.word_size, + top_bl_name=top_bl, top_br_name=top_br) + def route_write_driver_to_sense_amp(self): + """ Routing of BL and BR between write driver and sense amp """ + + for port in self.write_ports: + bottom_inst = self.write_driver_array_inst[port] + top_inst = self.sense_amp_array_inst[port] + self.connect_bitlines(top_inst, bottom_inst, self.word_size) + + def route_sense_amp_out(self): """ Add pins for the sense amp output """ @@ -819,6 +820,25 @@ class bank(design.design): din_name = "din{0}_{1}".format(port,row) self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name) + def connect_bitlines(self, top_inst, bottom_inst, num_items, + top_bl_name="bl_{}", top_br_name="br_{}", bottom_bl_name="bl_{}", bottom_br_name="br_{}"): + """ + Connect the bl and br of two modules. + This assumes that they have sufficient space to create a jog + in the middle between the two modules (if needed) + """ + for col in range(num_items): + bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col)).uc() + bottom_br = bottom_inst.get_pin(bottom_br_name.format(col)).uc() + top_bl = top_inst.get_pin(top_bl_name.format(col)).bc() + top_br = top_inst.get_pin(top_br_name.format(col)).bc() + + yoffset = 0.5*(top_bl.y+bottom_bl.y) + self.add_path("metal2",[bottom_bl, vector(bottom_bl.x,yoffset), + vector(top_bl.x,yoffset), top_bl]) + self.add_path("metal2",[bottom_br, vector(bottom_br.x,yoffset), + vector(top_br.x,yoffset), top_br]) + def route_wordline_driver(self): """ Connecting Wordline driver output to Bitcell WL connection """ From ef2ed9a92c699915c59c2b8986316ee2ef1aa122 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 15:48:49 -0800 Subject: [PATCH 10/31] Simplify bl and br name lists. --- compiler/bitcells/bitcell.py | 20 ---------- compiler/bitcells/pbitcell.py | 22 +---------- compiler/modules/bank.py | 70 ++++++++++++++++------------------- 3 files changed, 32 insertions(+), 80 deletions(-) diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index 5df86c87..710ce2d7 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -65,26 +65,6 @@ class bitcell(design.design): column_pins = ["br"] return column_pins - def list_read_bl_names(self): - """ Creates a list of bl pin names associated with read ports """ - column_pins = ["bl"] - return column_pins - - def list_read_br_names(self): - """ Creates a list of br pin names associated with read ports """ - column_pins = ["br"] - return column_pins - - def list_write_bl_names(self): - """ Creates a list of bl pin names associated with write ports """ - column_pins = ["bl"] - return column_pins - - def list_write_br_names(self): - """ Creates a list of br pin names asscociated with write ports""" - column_pins = ["br"] - return column_pins - def analytical_power(self, proc, vdd, temp, load): """Bitcell power in nW. Only characterizes leakage.""" from tech import spice diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 0242f2ce..51d1ce25 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -859,26 +859,6 @@ class pbitcell(design.design): br_pins = self.rw_br_names + self.w_br_names + self.r_br_names return br_pins - def list_read_bl_names(self): - """ Creates a list of bl pin names associated with read ports """ - bl_pins = self.rw_bl_names + self.r_bl_names - return bl_pins - - def list_read_br_names(self): - """ Creates a list of br pin names associated with read ports """ - br_pins = self.rw_br_names + self.r_br_names - return br_pins - - def list_write_bl_names(self): - """ Creates a list of bl pin names associated with write ports """ - bl_pins = self.rw_bl_names + self.w_bl_names - return bl_pins - - def list_write_br_names(self): - """ Creates a list of br pin names asscociated with write ports""" - br_pins = self.rw_br_names + self.w_br_names - return br_pins - def route_rbc_short(self): """ route the short from Q_bar to gnd necessary for the replica bitcell """ Q_bar_pos = self.inverter_pmos_right.get_pin("S").center() @@ -899,4 +879,4 @@ class pbitcell(design.design): leakage = spice["bitcell_leakage"] dynamic = 0 #temporary total_power = self.return_power(dynamic, leakage) - return total_power \ No newline at end of file + return total_power diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 62dbc85e..4c545545 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -278,22 +278,16 @@ class bank(design.design): self.add_mod(self.bitcell_array) # create arrays of bitline and bitline_bar names for read, write, or all ports - self.read_bl_names = self.bitcell.list_read_bl_names() - self.read_br_names = self.bitcell.list_read_br_names() + self.bl_names = self.bitcell.list_all_bl_names() + self.br_names = self.bitcell.list_all_br_names() - self.write_bl_names = self.bitcell.list_write_bl_names() - self.write_br_names = self.bitcell.list_write_br_names() - - self.total_bl_names = self.bitcell.list_all_bl_names() - self.total_br_names = self.bitcell.list_all_br_names() - - self.total_wl_names = self.bitcell.list_all_wl_names() - self.total_bitline_names = self.bitcell.list_all_bitline_names() + self.wl_names = self.bitcell.list_all_wl_names() + self.bitline_names = self.bitcell.list_all_bitline_names() self.precharge_array = [] for port in self.all_ports: if port in self.read_ports: - self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.total_bl_names[port], bitcell_br=self.total_br_names[port])) + self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.bl_names[port], bitcell_br=self.br_names[port])) self.add_mod(self.precharge_array[port]) else: self.precharge_array.append(None) @@ -303,8 +297,8 @@ class bank(design.design): for port in self.all_ports: self.column_mux_array.append(self.mod_column_mux_array(columns=self.num_cols, word_size=self.word_size, - bitcell_bl=self.total_bl_names[port], - bitcell_br=self.total_br_names[port])) + bitcell_bl=self.bl_names[port], + bitcell_br=self.br_names[port])) self.add_mod(self.column_mux_array[port]) @@ -339,10 +333,10 @@ class bank(design.design): temp = [] for col in range(self.num_cols): - for bitline in self.total_bitline_names: + for bitline in self.bitline_names: temp.append(bitline+"_{0}".format(col)) for row in range(self.num_rows): - for wordline in self.total_wl_names: + for wordline in self.wl_names: temp.append(wordline+"_{0}".format(row)) temp.append("vdd") temp.append("gnd") @@ -363,8 +357,8 @@ class bank(design.design): mod=self.precharge_array[port])) temp = [] for i in range(self.num_cols): - temp.append(self.total_bl_names[port]+"_{0}".format(i)) - temp.append(self.total_br_names[port]+"_{0}".format(i)) + temp.append(self.bl_names[port]+"_{0}".format(i)) + temp.append(self.br_names[port]+"_{0}".format(i)) temp.extend([self.prefix+"clk_buf_bar{0}".format(port), "vdd"]) self.connect_inst(temp) @@ -392,13 +386,13 @@ class bank(design.design): temp = [] for col in range(self.num_cols): - temp.append(self.total_bl_names[port]+"_{0}".format(col)) - temp.append(self.total_br_names[port]+"_{0}".format(col)) + temp.append(self.bl_names[port]+"_{0}".format(col)) + temp.append(self.br_names[port]+"_{0}".format(col)) for word in range(self.words_per_row): temp.append("sel{0}_{1}".format(port,word)) for bit in range(self.word_size): - temp.append(self.total_bl_names[port]+"_out_{0}".format(bit)) - temp.append(self.total_br_names[port]+"_out_{0}".format(bit)) + temp.append(self.bl_names[port]+"_out_{0}".format(bit)) + temp.append(self.br_names[port]+"_out_{0}".format(bit)) temp.append("gnd") self.connect_inst(temp) @@ -427,11 +421,11 @@ class bank(design.design): for bit in range(self.word_size): temp.append("dout{0}_{1}".format(port,bit)) if self.words_per_row == 1: - temp.append(self.total_bl_names[port]+"_{0}".format(bit)) - temp.append(self.total_br_names[port]+"_{0}".format(bit)) + temp.append(self.bl_names[port]+"_{0}".format(bit)) + temp.append(self.br_names[port]+"_{0}".format(bit)) else: - temp.append(self.total_bl_names[port]+"_out_{0}".format(bit)) - temp.append(self.total_br_names[port]+"_out_{0}".format(bit)) + temp.append(self.bl_names[port]+"_out_{0}".format(bit)) + temp.append(self.br_names[port]+"_out_{0}".format(bit)) temp.extend([self.prefix+"s_en{}".format(port), "vdd", "gnd"]) self.connect_inst(temp) @@ -463,11 +457,11 @@ class bank(design.design): temp.append("din{0}_{1}".format(port,bit)) for bit in range(self.word_size): if (self.words_per_row == 1): - temp.append(self.total_bl_names[port]+"_{0}".format(bit)) - temp.append(self.total_br_names[port]+"_{0}".format(bit)) + temp.append(self.bl_names[port]+"_{0}".format(bit)) + temp.append(self.br_names[port]+"_{0}".format(bit)) else: - temp.append(self.total_bl_names[port]+"_out_{0}".format(bit)) - temp.append(self.total_br_names[port]+"_out_{0}".format(bit)) + temp.append(self.bl_names[port]+"_out_{0}".format(bit)) + temp.append(self.br_names[port]+"_out_{0}".format(bit)) temp.extend([self.prefix+"w_en{0}".format(port), "vdd", "gnd"]) self.connect_inst(temp) @@ -526,7 +520,7 @@ class bank(design.design): for row in range(self.num_rows): temp.append("dec_out{0}_{1}".format(port,row)) for row in range(self.num_rows): - temp.append(self.total_wl_names[port]+"_{0}".format(row)) + temp.append(self.wl_names[port]+"_{0}".format(row)) temp.append(self.prefix+"clk_buf{0}".format(port)) temp.append("vdd") temp.append("gnd") @@ -728,8 +722,8 @@ class bank(design.design): for col in range(self.num_cols): precharge_bl = self.precharge_array_inst[port].get_pin("bl_{}".format(col)).uc() precharge_br = self.precharge_array_inst[port].get_pin("br_{}".format(col)).uc() - bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_names[port]+"_{}".format(col)).bc() - bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_names[port]+"_{}".format(col)).bc() + bitcell_bl = self.bitcell_array_inst.get_pin(self.bl_names[port]+"_{}".format(col)).bc() + bitcell_br = self.bitcell_array_inst.get_pin(self.br_names[port]+"_{}".format(col)).bc() yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), @@ -749,10 +743,8 @@ class bank(design.design): for port in self.all_ports: bottom_inst = self.col_mux_array_inst[port] top_inst = self.precharge_array_inst[port] - top_bl = self.total_bl_names[port]+"_{}" - top_br = self.total_br_names[port]+"_{}" - self.connect_bitlines(top_inst, bottom_inst, self.num_cols, - top_bl_name=top_bl, top_br_name=top_br) + self.connect_bitlines(top_inst, bottom_inst, self.num_cols) + def route_sense_amp_to_col_mux_or_precharge_array(self): @@ -769,8 +761,8 @@ class bank(design.design): else: # Sense amp is directly connected to the precharge array top_inst = self.precharge_array_inst[port] - top_bl = self.total_bl_names[port]+"_{}" - top_br = self.total_br_names[port]+"_{}" + top_bl = "bl_{}" + top_br = "br_{}" self.connect_bitlines(top_inst, bottom_inst, self.word_size, top_bl_name=top_bl, top_br_name=top_br) @@ -853,7 +845,7 @@ class bank(design.design): # The mid guarantees we exit the input cell to the right. driver_wl_pos = self.wordline_driver_inst[port].get_pin("wl_{}".format(row)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_names[port]+"_{}".format(row)).lc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).lc() mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0) mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) From e28978180fa931b45e0c9bf3ea599f996a51ddb5 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 16:49:02 -0800 Subject: [PATCH 11/31] Vertical channel routes go from left right. Horizontal go bottom up. --- compiler/base/hierarchy_layout.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 32af57c1..a469e50d 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -856,13 +856,13 @@ class layout(lef.lef): # Remove the net from other constriants in the VCG vcg=remove_net_from_graph(net_name, vcg) - # Add the trunk routes from the bottom up or right to left + # Add the trunk routes from the bottom up or the left to right if vertical: self.add_vertical_trunk_route(pin_list, offset, layer_stack, pitch) - offset -= vector(pitch,0) + offset += vector(pitch,0) else: self.add_horizontal_trunk_route(pin_list, offset, layer_stack, pitch) - offset -= vector(0,pitch) + offset += vector(0,pitch) def create_vertical_channel_route(self, netlist, pins, offset, From 18fbf30b46b5a237697682f03cc27445e56988df Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 16:53:58 -0800 Subject: [PATCH 12/31] Convert col decoder select routing to channel route. --- compiler/modules/bank.py | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 4c545545..0fae5f4d 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -874,27 +874,17 @@ class bank(design.design): decoder_name = "in_{}".format(i) addr_name = "addr{0}_{1}".format(port,i) self.copy_layout_pin(self.col_decoder_inst[port], decoder_name, addr_name) - - # This will do a quick "river route" on two layers. - # When above the top select line it will offset "inward" again to prevent conflicts. - # This could be done on a single layer, but we follow preferred direction rules for later routing. - top_y_offset = self.col_mux_array_inst[port].get_pin("sel_{}".format(self.num_col_addr_lines-1)).cy() - for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)): - mux_name = "sel_{}".format(i) - mux_addr_pos = self.col_mux_array_inst[port].get_pin(mux_name).lc() - - decode_out_pos = self.col_decoder_inst[port].get_pin(decode_name).center() + offset = self.col_decoder_inst[port].lr() + vector(self.m2_pitch, 0) - # To get to the edge of the decoder and one track out - delta_offset = self.col_decoder_inst[port].rx() - decode_out_pos.x + self.m2_pitch - if decode_out_pos.y > top_y_offset: - mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y) - else: - mid1_pos = vector(decode_out_pos.x + delta_offset + (self.num_col_addr_lines-i)*self.m2_pitch,decode_out_pos.y) - mid2_pos = vector(mid1_pos.x,mux_addr_pos.y) - #self.add_wire(("metal1","via1","metal2"),[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) - self.add_path("metal1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) + sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] + + route_map = list(zip(decode_names, sel_names)) + decode_pins = {key: self.col_decoder_inst[port].get_pin(key) for key in decode_names } + col_mux_pins = {key: self.col_mux_array_inst[port].get_pin(key) for key in sel_names } + # Combine the dff and bank pins into a single dictionary of pin name to pin. + all_pins = {**decode_pins, **col_mux_pins} + self.create_vertical_channel_route(route_map, all_pins, offset) def add_lvs_correspondence_points(self): From fd5cd675ac18fb7b01ea09f5476f08c1c88c0ec3 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 17:01:57 -0800 Subject: [PATCH 13/31] Horizontal increments top down. --- compiler/base/hierarchy_layout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index a469e50d..fdadcec9 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -862,7 +862,7 @@ class layout(lef.lef): offset += vector(pitch,0) else: self.add_horizontal_trunk_route(pin_list, offset, layer_stack, pitch) - offset += vector(0,pitch) + offset -= vector(0,pitch) def create_vertical_channel_route(self, netlist, pins, offset, From d03c9d5294364679b6240b85beb2d96629c559b8 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 17:02:20 -0800 Subject: [PATCH 14/31] Fix write bl name list in replica bitline --- compiler/modules/replica_bitline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index e9ebfb0c..32e87c34 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -150,7 +150,7 @@ class replica_bitline(design.design): self.connect_inst(temp) self.wl_list = self.rbl.cell.list_all_wl_names() - self.bl_list = self.rbl.cell.list_write_bl_names() + self.bl_list = self.rbl.cell.list_all_bl_names() def place_modules(self): """ Add all of the module instances in the logical netlist """ From 71177d0b706cce31204b672acae58b47449834d4 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 17:40:22 -0800 Subject: [PATCH 15/31] Fixed small bugs with new port index stuff and layout. --- compiler/characterizer/lib.py | 12 +++++++----- compiler/modules/bank.py | 3 ++- compiler/sram_base.py | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 6cc177f6..20f79a69 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -27,9 +27,11 @@ class lib: def set_port_indices(self): """Copies port information set in the SRAM instance""" - self.total_port_num = self.sram.total_ports - self.read_ports = self.sram.read_index - self.write_ports = self.sram.write_index + self.total_port_num = len(self.sram.all_ports) + self.all_ports = self.sram.all_ports + self.readwrite_ports = self.sram.readwrite_ports + self.read_ports = self.sram.read_ports + self.write_ports = self.sram.write_ports def prepare_tables(self): """ Determine the load/slews if they aren't specified in the config file. """ @@ -93,7 +95,7 @@ class lib: self.write_header() #Loop over all ports. - for port in range(self.total_port_num): + for port in self.all_ports: #set the read and write port as inputs. self.write_data_bus(port) self.write_addr_bus(port) @@ -387,7 +389,7 @@ class lib: """ Adds control pins timing results.""" #The control pins are still to be determined. This is a placeholder for what could be. ctrl_pin_names = ["CSb{0}".format(port)] - if port in self.write_ports and port in self.read_ports: + if port in self.readwrite_ports: ctrl_pin_names.append("WEb{0}".format(port)) for i in ctrl_pin_names: diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 0fae5f4d..cd1b83e4 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -253,7 +253,7 @@ class bank(design.design): # A space for wells or jogging m2 self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), - 2*self.m2_pitch) + 3*self.m2_pitch) def add_modules(self): @@ -451,6 +451,7 @@ class bank(design.design): mod=self.write_driver_array)) else: self.write_driver_array_inst.append(None) + continue temp = [] for bit in range(self.word_size): diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 7135b9f2..2166aaba 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -282,7 +282,7 @@ class sram_base(design): temp.append("bank_sel{0}[{1}]".format(port,bank_num)) for port in self.read_ports: temp.append("s_en{0}".format(port)) - for port in self.readwrite_ports: + for port in self.write_ports: temp.append("w_en{0}".format(port)) for port in self.all_ports: temp.append("clk_buf_bar{0}".format(port)) From 5d684b02e06af3f1503257ddfb2672aa13a55a33 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 18:00:09 -0800 Subject: [PATCH 16/31] Leakage changed in ngspice test. --- compiler/tests/21_ngspice_delay_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index e449fce0..e203b878 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -63,7 +63,7 @@ class timing_sram_test(openram_test): elif OPTS.tech_name == "scn4m_subm": golden_data = {'delay_hl': [3.644147], 'delay_lh': [1.629815], - 'leakage_power': 0.0009299118999999999, + 'leakage_power': 0.001542964, 'min_period': 4.688, 'read0_power': [16.28732], 'read1_power': [15.75155], From 9c8d5395ff6f5bc2126533b8f99c286fa1aa9416 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 8 Nov 2018 18:16:01 -0800 Subject: [PATCH 17/31] Update leakage data for scn4m --- compiler/tests/21_ngspice_delay_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index e449fce0..e203b878 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -63,7 +63,7 @@ class timing_sram_test(openram_test): elif OPTS.tech_name == "scn4m_subm": golden_data = {'delay_hl': [3.644147], 'delay_lh': [1.629815], - 'leakage_power': 0.0009299118999999999, + 'leakage_power': 0.001542964, 'min_period': 4.688, 'read0_power': [16.28732], 'read1_power': [15.75155], From 8f3fa0e2f688763e8f4c140aec5e6f4f1dbd69a7 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 9 Nov 2018 08:52:05 -0800 Subject: [PATCH 18/31] Fix blocked pin debug output. --- compiler/router/pin_group.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index e564d500..598805e7 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -587,8 +587,8 @@ class pin_group: # At least one of the groups must have some valid tracks if (len(pin_set)==0 and len(blockage_set)==0): - self.write_debug_gds("blocked_pin.gds") - debug.error("Unable to find unblocked pin on grid.") + debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins)) + self.router.write_debug_gds("blocked_pin.gds") # We need to route each of the components, so don't combine the groups self.grids = pin_set | blockage_set From 21f5fb0870538e82986202ce2c2d64b4a725368a Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 9 Nov 2018 09:11:00 -0800 Subject: [PATCH 19/31] precharge bl is on metal2 only. simplify via position code. --- compiler/pgates/precharge.py | 66 ++++++++++++++++------------- compiler/tests/04_precharge_test.py | 4 ++ 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index d5016219..651a64f3 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -69,27 +69,20 @@ class precharge(pgate.pgate): Adds a vdd rail at the top of the cell """ - # adds the rail across the width of the cell - vdd_position = vector(0, self.height - self.m1_width) - self.add_rect(layer="metal1", - offset=vdd_position, - width=self.width, - height=self.m1_width) + # Adds the rail across the width of the cell + vdd_position = vector(0.5*self.width, self.height) + self.add_rect_center(layer="metal1", + offset=vdd_position, + width=self.width, + height=self.m1_width) pmos_pin = self.upper_pmos2_inst.get_pin("S") # center of vdd rail - vdd_pos = vector(pmos_pin.cx(), vdd_position.y + 0.5*self.m1_width) - self.add_path("metal1", [pmos_pin.uc(), vdd_pos]) + pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) + self.add_path("metal1", [pmos_pin.uc(), pmos_vdd_pos]) - # Add the M1->M2->M3 stack - vdd_contact_pos = vector(0.5*self.width, vdd_position.y + 0.5*self.m1_width) - self.add_via_center(layers=("metal1", "via1", "metal2"), - offset=vdd_contact_pos) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=vdd_contact_pos) - self.add_layout_pin_rect_center(text="vdd", - layer="metal3", - offset=vdd_contact_pos) + # Add layout pin + self.add_power_pin("vdd", vdd_position) def create_ptx(self): @@ -221,10 +214,10 @@ class precharge(pgate.pgate): Connect the bitlines to the devices """ self.add_bitline_contacts() - self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl")) - self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin("br")) - self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl")) - self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br")) + self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl")) + self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl")) + self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),self.get_pin("br")) + self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br")) def add_bitline_contacts(self): @@ -233,19 +226,22 @@ class precharge(pgate.pgate): """ stack=("metal1", "via1", "metal2") - upper_y = self.upper_pmos1_inst.get_pin("S").cy() - lower_y = self.lower_pmos_inst.get_pin("S").cy() + upper_pin = self.upper_pmos1_inst.get_pin("S") + lower_pin = self.lower_pmos_inst.get_pin("S") + + # BL goes up to M2 at the transistor + self.bl_contact=self.add_contact_center(layers=stack, + offset=upper_pin.center()) + self.add_contact_center(layers=stack, + offset=lower_pin.center()) + # BR routes over on M1 first self.add_contact_center(layers=stack, - offset = vector(self.bl_pin.cx(), upper_y)) + offset = vector(self.br_pin.cx(), upper_pin.cy())) self.add_contact_center(layers=stack, - offset = vector(self.br_pin.cx(), upper_y)) - self.add_contact_center(layers=stack, - offset = vector(self.bl_pin.cx(), lower_y)) - self.add_contact_center(layers=stack, - offset = vector(self.br_pin.cx(), lower_y)) + offset = vector(self.br_pin.cx(), lower_pin.cy())) - def connect_pmos(self, pmos_pin, bit_pin): + def connect_pmos_m1(self, pmos_pin, bit_pin): """ Connect a pmos pin to bitline pin """ @@ -254,4 +250,14 @@ class precharge(pgate.pgate): right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) self.add_path("metal1", [ left_pos, right_pos] ) + + def connect_pmos_m2(self, pmos_pin, bit_pin): + """ + Connect a pmos pin to bitline pin + """ + + left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) + right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) + + self.add_path("metal2", [ left_pos, right_pos], self.bl_contact.height) diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index e5419dab..6c0cfe56 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -32,6 +32,10 @@ class precharge_test(openram_test): debug.info(2, "Checking precharge for pbitcell (innermost connections)") tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) + + debug.info(2, "Checking precharge for pbitcell (innermost connections)") + tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl1", bitcell_br="br1") + self.local_check(tx) debug.info(2, "Checking precharge for pbitcell (outermost connections)") tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl2", bitcell_br="br2") From cc619084c7ffa7b2a1774b966ca7e6e346351ba6 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 9 Nov 2018 09:34:34 -0800 Subject: [PATCH 20/31] Clean up psingle_bank_test --- compiler/tests/19_psingle_bank_test.py | 14 ++++++++++---- compiler/tests/19_single_bank_test.py | 1 - 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index dee59175..6496c16f 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -29,27 +29,33 @@ class psingle_bank_test(openram_test): OPTS.num_r_ports = 0 c = sram_config(word_size=4, num_words=16) + c.words_per_row=1 debug.info(1, "No column mux") - a = bank(c, name="bank1_1rw_0w_0r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) + a = bank(c, name=name) self.local_check(a) c.num_words=32 c.words_per_row=2 debug.info(1, "Two way column mux") - a = bank(c, name="bank1_1rw_0w_0r_single") + name = "bank2_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) + a = bank(c, name=name) self.local_check(a) c.num_words=64 c.words_per_row=4 debug.info(1, "Four way column mux") - a = bank(c, name="bank1_1rw_0w_0r_single") + name = "bank3_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) + a = bank(c, name=name) self.local_check(a) + c.word_size=2 c.num_words=128 c.words_per_row=8 debug.info(1, "Four way column mux") - a = bank(c, name="bank1_1rw_0w_0r_single") + name = "bank4_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports) + a = bank(c, name=name) self.local_check(a) diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index da411d1f..e7179d96 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -38,7 +38,6 @@ class single_bank_test(openram_test): a = bank(c, name="bank3_single") self.local_check(a) - # Eight way has a short circuit of one column mux select to gnd rail c.word_size=2 c.num_words=128 c.words_per_row=8 From ac7229f8d3eb11ec92a1daf4caaea553605c4802 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 9 Nov 2018 10:11:24 -0800 Subject: [PATCH 21/31] Move vdd pin in precharge inside cell --- compiler/pgates/precharge.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 651a64f3..2bff825e 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -81,8 +81,8 @@ class precharge(pgate.pgate): pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) self.add_path("metal1", [pmos_pin.uc(), pmos_vdd_pos]) - # Add layout pin - self.add_power_pin("vdd", vdd_position) + # Add vdd pin above the transistor + self.add_power_pin("vdd", pmos_pin.center(), rotate=0) def create_ptx(self): From c01effc819c70ecaa469416a20ec5aee93024dd0 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 9 Nov 2018 10:26:15 -0800 Subject: [PATCH 22/31] Adjust ptx positions in precharge to be under the bl rail --- compiler/pgates/precharge.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 2bff825e..5f9c1e5b 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -110,11 +110,13 @@ class precharge(pgate.pgate): # Compute the other pmos2 location, but determining offset to overlap the # source and drain pins - self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() + overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() + # This is how much the contact is placed inside the ptx active + contact_xdiff = self.pmos.get_pin("S").lx() # adds the lower pmos to layout - #base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0) - self.lower_pmos_position = vector(max(self.bitcell.get_pin(self.bitcell_bl).lx(), self.well_enclose_active), + bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx() + self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, self.well_enclose_active), self.pmos.active_offset.y) self.lower_pmos_inst.place(self.lower_pmos_position) @@ -123,7 +125,7 @@ class precharge(pgate.pgate): self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff) self.upper_pmos1_inst.place(self.upper_pmos1_pos) - upper_pmos2_pos = self.upper_pmos1_pos + self.overlap_offset + upper_pmos2_pos = self.upper_pmos1_pos + overlap_offset self.upper_pmos2_inst.place(upper_pmos2_pos) def connect_poly(self): From 9fe64b486c15f79f723ea6b78d04cc4ed88f3371 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 9 Nov 2018 11:02:19 -0800 Subject: [PATCH 23/31] Remove layer 230 labels from library cells --- technology/freepdk45/gds_lib/sense_amp.gds | Bin 16384 -> 16384 bytes technology/freepdk45/gds_lib/write_driver.gds | Bin 22528 -> 20480 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/sense_amp.gds b/technology/freepdk45/gds_lib/sense_amp.gds index c215f793c7811fe0bbf3a9733e6327cbfaaba047..fecbfcb8cdc6af32968849ba447d240a27c92d8a 100644 GIT binary patch delta 97 zcmZo@U~Fh$oFM7S&A`dP#bC|A#=yhGmXey5SejG9z#ziRYNv2y_R`0}-z=44#4q@+ sdB-5b%EZ9Wz{J4Hz`>} zQA^m6`6vT}oGt^8BwLPxt%8A`fhhxvAOiyn0bTVB4Dx$Hy2`=2jLZq^5;5>3-6|0S zX3}i}`U>a;#RH`IO2pta1A}q^8J-fiB-JLMrzEc|pQWw`(V diff --git a/technology/freepdk45/gds_lib/write_driver.gds b/technology/freepdk45/gds_lib/write_driver.gds index 2c5556853f2b7ae21457e9b15ede648aba035a2d..742e39d40ba6e581ac7a4effccb6d226bc0b9930 100644 GIT binary patch delta 1322 zcmXYvKWti87{wp(u(83|A=rTb;=iOZ1q`^r10I*hh45dgt6Ci@OO?`29XhmX#1K)7 zN}f`?r>!EjLx;3ehis`5DT8$*F+@qEN>%43Yottd#1Qp3?eNq2&i9?~a^K_i@7Vf3 z+m1t3w>F_oYI)7jJod3`AAj=E7oUEvX?};(467f1dG_8vxwQ`iPdmT8(iWX|&8xY! z5H~JqY0by_|J{>=2&(oZ&TK85+5`@57NpID-6l>>YbQr>WORWu2d9n-zIK!#9c4Hj zMV#7ea5)WFwq2Y#-^I`=aCGjS=Y4^pF5axqaebESvs|Ao!ajS5fxi#w7tsA}JiVBP zyx52G9pgJ+0@BAm8C>W?Cu)ZEOcjlpJk&JzrjOyDPC$9tC%X45Je=aSrkI}+P@WD> zJSpfN?z#n}n|-R9WS;1g>`$;i!9G>F+BkNlp}W{0XMdc1sv2W{EQPxPfiDAHR0F(w zz=E2SaE0+@xnc+$dnM!93B4TIYP7 z^L6_5G9Ip#Fj(W9=xfZcGQUc{%JW&PFn=%H#?>(I%v!DR{wsWkE6lGjzs&qH^UKUr zhfDlhmg=~=#5`-Y$oyg!^+f>}87?rt!2AO9)Zsky^UTjP&sv3;4@HoPc=1hU8@-et z-Hd>jJbp*uCg~C{dAv(cy2QI%#*=gf-=+<;snh8q9;GbYOmR-UDHD&9^hp!#WDGBN z4xo0TFn0YI?3l22Ea=qJ1V73IKgt9@N~((U`H3WYob#B4L5z8-9^-xA=A1~P6BRXa z80CJH&llzU+os>9-{zdC%L2D@7x4Y%I3#PGsJD0qkwhoz5+vJ*A#->@s-$e_-oCkM`VeTDZt{8(V?BR!k zz1@vn4tY23P zxL4)8Dq*r7N#>=*4OxJ}dQ@S)!hD5!)~_vr2jm7ZE#}M2mzk%UWqLtRdLo4v-5h@H y?%{e@AltPt>X`VwW1-jKS!C3vZ_`t=)KQDOEuPgfkZsX7Id5{_OkrvNkADFfC(`Z! delta 2278 zcma);Uuaup7{6{2t=)Q>_0QU-X_~gnX_}mTIXOwQF3C3BjUWSI^J*6r zlu8CAjtqv#XnpSVMno?s6GTS`yC`RnixDb#<&~g_2pdJjL6zZ?c5cWt*~O3deZTiP zPu};-32pfkNBOp6b0DLLcF`sRLI|(ZbLsM%Z@hEmZ6W+Fx1re|fBxgEe=G%F@7pch za*83hQ>auAP=8V^2%mWN?AFBzWSkK^cAQ7m5yLfygy}G$IE<|wxpi$3QAOghi(MDH zt~4^PBotQ)_niq`bF%MY-_5>T;s(jNV{p2&sPefRj#)%|+3zX9?vb#1%1{_9{xU4T zgyPR*w|fCrH_y9y?&G5NS`0W_(zx&8 zew=-$XA$dt=aK7+L+LZH)0@V1PXy5c?$yt|`{Ste)0fzud3-Z2u|ED9&W+QPxjjbz zF&()viB?h_rGAw9Q46h%)f2p@6FHnaK|OOjKz)Gv0QICgLj4H!Bh)juHSVG1aZaP2 zxjjt%Fz;}f_cLsvdYt;>)E}pwxqXa!VjWAteJlxki1m<#+z|I+ZV$31)*$tR)E{Mi zl>MXZGq<^CWndob6J7W)dJ?4>KZ;QamDEm2+$2S!lG-9`QY0#=EwCmaC zsYMKY5HV4VSnx&SsLipSV?D<{sfDR0MWTicJeW0cbJjqfQJrN!#6Bs840uB(YBPL( zMj}7MKJy2`5Y~bhVFiN+b1Ol9f72!^(*{gNWt#gKhGnMuA^ zlkA^lpIAg8_5^EU5oIEXaVg=^b@-(Zn|cqvOimxfw~|SOlM;J|fiDabYm6Zyi{BG* z+)hZWC0Hj+?8VuSvmciTlf7lu%d8ng%My=bA#6u=RAb|KWT;q+X&6fS=-GnF*fM@v zGVtkA8kd)n^m1czX_+1he$nHMUfk9fa7hnx#?^y4e~bDtzPf^krwiCVEm0z|(-xlO zB<|(nC^5!ztXEi*4Pvasv7eRL&RW>W#t~z_pRsT+Lw$yN=1;6Rwk`Tu77XV5Y3kF| zr>SSYZ?YyO5;IfyGnK%dR2o+q^^}Qy-s84R;i`<_VNv2%v4B$1fL>&;VBk(6i;V)k z3iQge&YLJP>Wuqm_$(=%VSR@6D*LMv@2-Ygy+U}7IK|IUb#1iMC4{S2h@rc}JK(vZ zCA4rTyeO0|A(R({&Rrn}uh)g^2f{i1e}s;YUeeKVp{Zk7bErb78x7~9h)x|}HJmN1 zIp2Q?r}~ynoSxrXzoX$?hr+sJZ{PFT=1(=ax0iCPZ`;K2@ZP%hhVvcXTTh_jY=`&O z)TzE@6Q{%LsJ|VaUmMQ#RLCve{A>Qt`}V@Ug`|d%-l@61ZsPb%_*YZM0RMrgPuTuh qg9$_OK*ssJ-#?%4KYkD4gV`E-se0Jw;#~iBf_C-?l+Tu*()l+}99>%g From 05c25eb5060769fe71615cb652d98b8a97c5b4cc Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 9 Nov 2018 11:08:20 -0800 Subject: [PATCH 24/31] Remove layer 230 labels from library cells --- technology/freepdk45/gds_lib/cell_6t.gds | Bin 20480 -> 20480 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/cell_6t.gds b/technology/freepdk45/gds_lib/cell_6t.gds index bde8742aa0bb9db6a3713b09ad30979434591af0..17b6202840bbfeed1fc7d699d9377cf6f057322c 100644 GIT binary patch delta 885 zcmYLFOHUJF6rHb>M;|;2ebAOiQw>DuyCvu_{pRaXabb&qERBhYvQk2VCZ;rHAg&M- zn6q?4LUiTI*$_hNXjo$6PcU(3+>sc9X5s}+T->?m-h1x3XFh$8>3eJ^eA6SjB#+b~ zxh1co?ct-P2hScql_Y;lD~}z&e)H|_&l~Z3p)b{U@1=oOhtx&fHsS`PoYYJB-|p`H z7;H8HlU1R!5=u%y`oI0rM*v3gq4!m^&r_OmCI|W`i6$obu%vnUUW%`0{ z_6V%@R3Ru^-8-1;UWVPJ!Ro5wRaX{vr;Y{%@6?RWGLF5Z;MHMybm$%q?h&xv0u8r; z9`_0?7e~XTLAXe+gXB6^j1GeB0(!eJ+IO(iwgSEN6!~8A>6^tzUl9$TX82ZdN-eXO ztoP=z8WG4x=HZMeu)?$*E~6Y)41!jF1>5}vh<;%ZJP8$`hXh2Z0xw}JNbFz*m0-aL zmaq|6Hv(kXFATp0tB-~hqK{5Q!`MoVBAdF6-$@P4BuAaH74_jxG7USyag@-Es25v_ z9F&9xdz{WkocJ`Zn9W#&p&p~XcnRy*)ct0R!5*dO8r3f`AK~WF)lFtLMi|5T6%}@z z9x`tIU*$1+uIMtx1{a_Vll~A%4X9{PR)XU7vM^adtx@9JFFB#6V8V>Vfzc%TBJju<2iA}aI=NM`QK?BQ_ zbM!-J*)oo@D%L3EEXP@fwlXEGWvI?jou)dSz*1U}bOMWM1^%=jZz-Co1V%&IZLcah2 delta 1502 zcmbVLO=}xh6n$?rk}FyMkSt4)CDlsphFX^8u|7awo_RA-FgSLSQZGwrp$)}2E4MUg zF@E+sE%uyM=_cTIQ?kl*5nLfA#o4)#MgKt|i*602m>_c1Les`nrX9H4d*9r1&pB@x z`GCs@d_Q^3&qi5@60M^Pscd6$G&jJ_VH{~gB%qQ6yAu07}>}8hz2h#Aj3L(!+Vg!`?xh+ zKrbZFrO-or@PZODNVs4NYOo3UhK=5kM0ZHQ9=eJ3p%T1+4L#ssJV5(Mk@S~v8j)}# z>yQy0dgLm0gGHQ1Nqv;gq9tr6*CD2aGexVDo|7chCFxv}@De6=69zjz8KWIBk{4^jo1p257FrXF*v>?-S9k{x(^;$*im-D5 zD1|1(+!pc+x6sQMu$9|@%#;0jvO2HBp10u5nc(vVyc{XXkrK*0XE=GfwK?I;k)dhJ z^)fbxedR~z3Tb{@xZ+LDU2|rKJ2Sf5o%M0-Ufyvo%R?{2;Z2jQ>AwRr7DPrk8FG{+ z+BCJ4dnuw%(QJxlQ2@s1 z(OK!Dy<#0UP42#Ek)WfN?mo7NzHQMutL1jYCQpVL3ydi2ISa%UMsJPoX$^BqeC@%rjnR?m) From c5b408ae2d37925f8f8c44fe899d1c1a8bfe6be0 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 9 Nov 2018 11:10:40 -0800 Subject: [PATCH 25/31] Add router output message --- compiler/router/router.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/router/router.py b/compiler/router/router.py index 7f88934a..afe06588 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -966,6 +966,7 @@ class router(router_tech): """ Write out a GDS file with the routing grid and search information annotated on it. """ + debug.info(0,"Writing annotated router gds file to {}".format(gds_name)) self.add_router_info() self.cell.gds_write(gds_name) From 83aadc47c909af69770645b4a3528081d5a1e9f9 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 9 Nov 2018 11:12:31 -0800 Subject: [PATCH 26/31] Remove layer 230 labels from library cells --- .../freepdk45/gds_lib/replica_cell_6t.gds | Bin 20480 -> 20480 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/replica_cell_6t.gds b/technology/freepdk45/gds_lib/replica_cell_6t.gds index 38c69cf99f024f9850c9981060b2a56793640b03..f2fe91974439e0cf98c77571fd57bb8fa9f15c3c 100644 GIT binary patch delta 869 zcmYjNO-~b16rDG;!;}_Cp-`Y8AWHZs1^QvyQf6KsBOR~>qnNlLCMIf(VWWW+2~4_Z z1`|!;urY2(h=~hVjyn=VuxWORU->N z$u9+@b}1k$j~+jHxV5n-r21QO`o4v*o)1k8b{<*$yCotEB+w$yqTyM=xn~{~&jOY_#P%5Y zO1z>xecWhg*lvFXwY`e(Z8NajI7}+H)q>etMNjJt+yI9i(4Yj$u>Dos_s5|4IqEGO zi!EiiJ`1*wbUp(CUkF0eY9>#?9xx#2wa!aB@w5utDrZa4kwE@G`a z36*-ctBAv{B+M?-Q~xbQ?-3%O5M>PI;C2$f(?GFv8r#tzUdG1osecG}VsUt40_OMz zl2f;#OvSJ{u?9Oz_ebg0Nez`r0e3tP-=q$Af^tky4niNH8l`s|r=8<;)1YwNfgOg* z=o!>e3VoeB<`u@ArIB3*bwqRO*P0mihIgS3TgUDIgF8sM2LIO%P?!NSq;mUdc|XmB z3ZWYza)ihc0hO@cm%{H@E6Q_Ge2hi0KG%WE>{I;6cH>2sBc5G@lGRYl2vjn86f!!L z49#hp(=?~^jyg+$l^dt1r?^v@rm%$sg;f%okIMkf}e_1j?EQPAF>}XY(9oDtX>QIcGuSsL$o`WiF1?GTDe#rzR|hTH!Ow r1#xWTnd7-hfzAxgITf_NEXYn6PK`fj*jmmwZ7Y||nOV8``>C=&pT{O% delta 1636 zcmbtT-)mcC82-MKld~jE+NMkM>ttI<$2K+1uO^I|5t$yjprQ3L}&lkE4PY`ws;F02L7?;+t4#H*N68#mo1+-}}7p z_qOc=1nYxsCP4Q1R!(_^CIG2rl& zvFD}{ZWGp+iPo5g%9wyc(R8&Dbgja2YG^tQRGl=>LGv7|a5xAb6KGL5j&0zMvWk}d zC*-F{s&^URc#CLyX}%uxtb&Be7dPfbRoj8k~ zum_cd203A%KdWJVmV8jXcx^Tbh4NmUG^%d2;~A6*D{+DTbE4Cau5qkKsVS!8<};-0`Hc&B-WbKIOML75>-W8H(+N|bz4SFo)LWYuXbo+8pI8W$;{ zc~ZyX$r5Ra4{Kkapt}7B!ef z14dD6)3IBiazTSA7?9}f=5w8#{vmJcB*YM%^~&!YMup^-2Xb|iyI@e|LC4T(C*43p z%;DO_d54=Z#UrsF@S7ToF=lr$=3Hd%ka8tjjHVK)9Fx6_$%kom%6p8tW(KtP{-n)6 zNn?s%Vl+9x>0?yF++@tH+Dm(T-_kANh~3y8BEpk4Lhp}$_KA55|775U(E@4RXzFPd z@){Z78@YV6{+A}Q!uHjH6`AzEvtqD5mq$N*X#M)%`Z@HFR{p<2_CWonl^gjz>#$JKREK)5kiXl|JOs79O($Z{IrnH7Z-o!e*lY2kAeUI From de616309620455ed35c0d9eba8080d45824faeb5 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 9 Nov 2018 14:25:10 -0800 Subject: [PATCH 27/31] Expand blocked pins to neighbor grid cells. --- compiler/base/pin_layout.py | 36 ++++++++++++++++++++++++++++++++++++ compiler/router/pin_group.py | 22 +++++++++++++++------- compiler/router/router.py | 34 +++++++++++++++++++++++++++++----- 3 files changed, 80 insertions(+), 12 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index c5434246..c1e6d79a 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -316,7 +316,43 @@ class pin_layout: return [dx,dy] else: return [0,0] + + def distance(self, other): + """ + Calculate the distance to another pin layout. + """ + (r1_ll,r1_ur) = self.rect + (r2_ll,r2_ur) = other.rect + + def dist(x1, y1, x2, y2): + return sqrt((x2-x1)**2 + (y2-y1)**2) + left = r2_ur.x < r1_ll.x + right = r1_ur.x < r2_ll.x + bottom = r2_ur.y < r1_ll.y + top = r1_ur.y < r2_ll.y + + if top and left: + return dist(r1_ll.x, r1_ur.y, r2_ur.x, r2_ll.y) + elif left and bottom: + return dist(r1_ll.x, r1_ll.y, r2_ur.x, r2_ur.y) + elif bottom and right: + return dist(r1_ur.x, r1_ll.y, r2_ll.x, r2_ur.y) + elif right and top: + return dist(r1_ur.x, r1_ur.y, r2_ll.x, r2_ll.y) + elif left: + return r1_ll.x - r2_ur.x + elif right: + return r2_ll.x - r1.ur.x + elif bottom: + return r1_ll.y - r2_ur.y + elif top: + return r2_ll.y - r1_ur.y + else: + # rectangles intersect + return 0 + + def overlap_length(self, other): """ Calculate the intersection segment and determine its length diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 598805e7..d71c7073 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -566,11 +566,10 @@ class pin_group: debug.info(2," Converting {0}".format(pin)) # Determine which tracks the pin overlaps pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin) - pin_set.update(pin_in_tracks) + # Blockages will be a super-set of pins since it uses the inflated pin shape. blockage_in_tracks = self.router.convert_blockage(pin) - blockage_set.update(blockage_in_tracks) # If we have a blockage, we must remove the grids @@ -578,17 +577,26 @@ class pin_group: shared_set = pin_set & self.router.blocked_grids if len(shared_set)>0: debug.info(2,"Removing pins {}".format(shared_set)) - pin_set.difference_update(self.router.blocked_grids) - + pin_set.difference_update(shared_set) shared_set = blockage_set & self.router.blocked_grids if len(shared_set)>0: debug.info(2,"Removing blocks {}".format(shared_set)) - blockage_set.difference_update(self.router.blocked_grids) + blockage_set.difference_update(shared_set) # At least one of the groups must have some valid tracks if (len(pin_set)==0 and len(blockage_set)==0): - debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins)) - self.router.write_debug_gds("blocked_pin.gds") + debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins)) + + for pin_list in self.pins: + for pin in pin_list: + debug.info(2," Converting {0}".format(pin)) + # Determine which tracks the pin overlaps + pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin, expansion=1) + pin_set.update(pin_in_tracks) + + if len(pin_set)==0: + debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins)) + self.router.write_debug_gds("blocked_pin.gds") # We need to route each of the components, so don't combine the groups self.grids = pin_set | blockage_set diff --git a/compiler/router/router.py b/compiler/router/router.py index afe06588..76f2e1cc 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -495,10 +495,11 @@ class router(router_tech): # debug.info(0,"Pin {}".format(pin)) return [ll,ur] - def convert_pin_to_tracks(self, pin_name, pin): + def convert_pin_to_tracks(self, pin_name, pin, expansion=0): """ Convert a rectangular pin shape into a list of track locations,layers. If no pins are "on-grid" (i.e. sufficient overlap) it makes the one with most overlap if it is not blocked. + If expansion>0, expamine areas beyond the current pin when it is blocked. """ (ll,ur) = pin.rect debug.info(3,"Converting pin [ {0} , {1} ]".format(ll,ur)) @@ -512,8 +513,8 @@ class router(router_tech): insufficient_list = set() zindex=self.get_zindex(pin.layer_num) - for x in range(int(ll[0]),int(ur[0])+1): - for y in range(int(ll[1]),int(ur[1])+1): + for x in range(int(ll[0])+expansion,int(ur[0])+1+expansion): + for y in range(int(ll[1]+expansion),int(ur[1])+1+expansion): debug.info(4,"Converting [ {0} , {1} ]".format(x,y)) (full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex)) if full_overlap: @@ -523,9 +524,14 @@ class router(router_tech): if len(sufficient_list)>0: return sufficient_list - elif len(insufficient_list)>0: - # If there wasn't a sufficient grid, find the best and patch it to be on grid. + elif expansion==0 and len(insufficient_list)>0: + #Remove blockages and return the best to be patched + insufficient_list.difference_update(self.blocked_grids) return self.get_best_offgrid_pin(pin, insufficient_list) + elif expansion>0: + #Remove blockages and return the nearest + insufficient_list.difference_update(self.blocked_grids) + return self.get_nearest_offgrid_pin(pin, insufficient_list) else: debug.error("Unable to find any overlapping grids.", -1) @@ -555,6 +561,24 @@ class router(router_tech): return set([best_coord]) + def get_nearest_offgrid_pin(self, pin, insufficient_list): + """ + Given a pin and a list of grid cells (probably non-overlapping), + return the nearest grid cell (center to center). + """ + #print("INSUFFICIENT LIST",insufficient_list) + # Find the coordinate with the most overlap + best_coord = None + best_dist = math.inf + for coord in insufficient_list: + track_pin = self.convert_track_to_pin(coord) + min_dist = pin.distance(track_pin) + if min_dist Date: Sat, 10 Nov 2018 10:05:27 -0800 Subject: [PATCH 28/31] Save gds file in testutils when fail to figure out randomness in regression CI --- compiler/tests/testutils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 39376202..a22416e2 100755 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -36,6 +36,9 @@ class openram_test(unittest.TestCase): import verify result=verify.run_drc(a.name, tempgds) if result != 0: + new_file = "/tmp/"+a.name+".gds" + debug.info(0,"Copying failed file to {}".format(new_file)) + os.copy(tempgds, newfile) self.fail("DRC failed: {}".format(a.name)) From 65b6bfd5e740c232ffef510ed8a2bcdbd2665cc6 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Sat, 10 Nov 2018 10:06:33 -0800 Subject: [PATCH 29/31] Change os to shutils --- compiler/tests/testutils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index a22416e2..6310673e 100755 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -38,7 +38,8 @@ class openram_test(unittest.TestCase): if result != 0: new_file = "/tmp/"+a.name+".gds" debug.info(0,"Copying failed file to {}".format(new_file)) - os.copy(tempgds, newfile) + import shutil + shutil.copy(tempgds, newfile) self.fail("DRC failed: {}".format(a.name)) From 6c17734712a6b96dd3bbf0fc447626fbd8ca53f9 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Sat, 10 Nov 2018 11:54:28 -0800 Subject: [PATCH 30/31] Add testutil archive on failed tests for debug --- compiler/tests/testutils.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 6310673e..0f7e4ee6 100755 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -1,12 +1,14 @@ import unittest,warnings import sys,os,glob,copy +import shutil sys.path.append(os.path.join(sys.path[0],"..")) from globals import OPTS import debug class openram_test(unittest.TestCase): """ Base unit test that we have some shared classes in. """ - + + def local_drc_check(self, w): self.reset() @@ -36,15 +38,17 @@ class openram_test(unittest.TestCase): import verify result=verify.run_drc(a.name, tempgds) if result != 0: - new_file = "/tmp/"+a.name+".gds" - debug.info(0,"Copying failed file to {}".format(new_file)) - import shutil - shutil.copy(tempgds, newfile) + zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) + debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("DRC failed: {}".format(a.name)) result=verify.run_lvs(a.name, tempgds, tempspice, final_verification) if result != 0: + zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) + debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("LVS mismatch: {}".format(a.name)) if OPTS.purge_temp: From 5cbbd5e4cac7ae9ea8f191545ba911bd8df032e2 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Sat, 10 Nov 2018 13:44:36 -0800 Subject: [PATCH 31/31] Comment out regress CI debug code --- compiler/tests/testutils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 0f7e4ee6..a0f2670d 100755 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -38,17 +38,17 @@ class openram_test(unittest.TestCase): import verify result=verify.run_drc(a.name, tempgds) if result != 0: - zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) - debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) - shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) + #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("DRC failed: {}".format(a.name)) result=verify.run_lvs(a.name, tempgds, tempspice, final_verification) if result != 0: - zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) - debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) - shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) + #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("LVS mismatch: {}".format(a.name)) if OPTS.purge_temp: