From 5b3846e1e52ecfe4fcaeac8c98d52b6e67b705a8 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Sun, 8 Dec 2019 13:24:39 +0000 Subject: [PATCH 001/206] Changed replica bitcell array to work with bank tests for non power of two rows --- compiler/modules/bank.py | 7 ++++--- compiler/modules/port_address.py | 4 ++-- compiler/modules/replica_bitcell_array.py | 9 +++++---- compiler/sram/sram_config.py | 8 +++++--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 3e105d09..18ced346 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -289,13 +289,14 @@ class bank(design.design): """ Computes the required sizes to create the bank """ self.num_cols = int(self.words_per_row*self.word_size) - self.num_rows = int(self.num_words / self.words_per_row) + self.num_rows_temp = int(self.num_words / self.words_per_row) + self.num_rows = self.num_rows_temp + self.num_spare_rows - self.row_addr_size = int(log(self.num_rows, 2)) + self.row_addr_size = ceil(log(self.num_rows, 2)) self.col_addr_size = int(log(self.words_per_row, 2)) self.addr_size = self.col_addr_size + self.row_addr_size - debug.check(self.num_rows*self.num_cols==self.word_size*self.num_words,"Invalid bank sizes.") + debug.check(self.num_rows_temp*self.num_cols==self.word_size*self.num_words,"Invalid bank sizes.") debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.") # The order of the control signals on the control bus: diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 0a624c60..11035f30 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -5,7 +5,7 @@ # import sys from tech import drc, parameter -from math import log +from math import log, ceil import debug import design from sram_factory import factory @@ -22,7 +22,7 @@ class port_address(design.design): self.num_cols = cols self.num_rows = rows - self.addr_size = int(log(self.num_rows, 2)) + self.addr_size = ceil(log(self.num_rows, 2)) if name == "": name = "port_address_{0}_{1}".format(cols,rows) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 639d0714..0f922eb0 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -264,8 +264,9 @@ class replica_bitcell_array(design.design): # Far top dummy row (first row above array is NOT flipped) flip_dummy = self.right_rbl%2 - self.dummy_row_top_inst.place(offset=offset.scale(0,self.right_rbl+flip_dummy)+self.bitcell_array_inst.ul(), - mirror="MX" if flip_dummy else "R0") + odd_rows = self.row_size%2 + self.dummy_row_top_inst.place(offset=offset.scale(0,self.right_rbl+(flip_dummy ^ odd_rows))+self.bitcell_array_inst.ul(), + mirror="MX" if (flip_dummy ^ odd_rows) else "R0") # Far bottom dummy row (first row below array IS flipped) flip_dummy = (self.left_rbl+1)%2 self.dummy_row_bot_inst.place(offset=offset.scale(0,-self.left_rbl-1+flip_dummy), @@ -280,8 +281,8 @@ class replica_bitcell_array(design.design): self.dummy_row_replica_inst[bit].place(offset=offset.scale(0,-bit-bit%2), mirror="R0" if bit%2 else "MX") for bit in range(self.right_rbl): - self.dummy_row_replica_inst[self.left_rbl+bit].place(offset=offset.scale(0,bit+bit%2)+self.bitcell_array_inst.ul(), - mirror="MX" if bit%2 else "R0") + self.dummy_row_replica_inst[self.left_rbl+bit].place(offset=offset.scale(0,bit+bit%2+odd_rows)+self.bitcell_array_inst.ul(), + mirror="MX" if (bit%2 or odd_rows) else "R0") self.translate_all(offset.scale(-1-self.left_rbl,-1-self.left_rbl)) diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index 376bf42b..265cdba8 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -14,11 +14,12 @@ from sram_factory import factory class sram_config: """ This is a structure that is used to hold the SRAM configuration options. """ - def __init__(self, word_size, num_words, write_size = None, num_banks=1, words_per_row=None): + def __init__(self, word_size, num_words, write_size = None, num_banks=1, words_per_row=None, num_spare_rows=0): self.word_size = word_size self.num_words = num_words self.write_size = write_size self.num_banks = num_banks + self.num_spare_rows = num_spare_rows # This will get over-written when we determine the organization self.words_per_row = words_per_row @@ -78,11 +79,12 @@ class sram_config: # Fix the number of columns and rows self.num_cols = int(self.words_per_row*self.word_size) - self.num_rows = int(self.num_words_per_bank/self.words_per_row) + self.num_rows_temp = int(self.num_words_per_bank/self.words_per_row) + self.num_rows = self.num_rows_temp + self.num_spare_rows debug.info(1,"Rows: {} Cols: {}".format(self.num_rows,self.num_cols)) # Compute the address and bank sizes - self.row_addr_size = int(log(self.num_rows, 2)) + self.row_addr_size = ceil(log(self.num_rows, 2)) self.col_addr_size = int(log(self.words_per_row, 2)) self.bank_addr_size = self.col_addr_size + self.row_addr_size self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) From 88bc1f09cb29bff19a3c92855942c520a6852c03 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Thu, 20 Feb 2020 17:01:52 +0000 Subject: [PATCH 002/206] Characterization for extra rows --- compiler/characterizer/delay.py | 3 +- compiler/characterizer/functional.py | 5 +- compiler/characterizer/simulation.py | 7 +- compiler/characterizer/trim_spice.py | 4 +- compiler/gen_stimulus.py | 3 +- compiler/globals.py | 2 + compiler/openram.py | 3 +- .../tests/21_ngspice_delay_extra_rows_test.py | 95 +++++++++++++++++++ 8 files changed, 115 insertions(+), 7 deletions(-) create mode 100755 compiler/tests/21_ngspice_delay_extra_rows_test.py diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 2a8d5293..9b59c9e6 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -1063,7 +1063,8 @@ class delay(simulation): self.trimsp.set_configuration(self.num_banks, self.num_rows, self.num_cols, - self.word_size) + self.word_size, + self.num_spare_rows) self.trimsp.trim(self.probe_address,self.probe_data) else: # The non-reduced netlist file when it is disabled diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 6d827aa3..cbd9b8d5 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -287,7 +287,10 @@ class functional(simulation): def gen_addr(self): """ Generates a random address value to write to. """ - random_value = random.randint(0,(2**self.addr_size)-1) + if (self.num_spare_rows == 0): + random_value = random.randint(0,(2**self.addr_size)-1) + else: + random_value = random.randint(0,((2**(self.addr_size-1)-1))+(self.num_spare_rows * self.words_per_row)) addr_bits = self.convert_to_bin(random_value,True) return addr_bits diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index adbe5f5f..b21c0bd0 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -25,12 +25,14 @@ class simulation(): self.word_size = self.sram.word_size self.addr_size = self.sram.addr_size self.write_size = self.sram.write_size + self.num_spare_rows = self.sram.num_spare_rows self.sp_file = spfile self.all_ports = self.sram.all_ports self.readwrite_ports = self.sram.readwrite_ports self.read_ports = self.sram.read_ports self.write_ports = self.sram.write_ports + self.words_per_row = self.sram.words_per_row if self.write_size: self.num_wmasks = int(self.word_size/self.write_size) else: @@ -135,7 +137,10 @@ class simulation(): if c=="0": self.addr_values[port][bit].append(0) elif c=="1": - self.addr_values[port][bit].append(1) + if((self.num_spare_rows != 0) and (bit == (self.addr_size - 1))): + self.addr_values[port][bit].append(0) + else: + self.addr_values[port][bit].append(1) else: debug.error("Non-binary address string",1) bit -= 1 diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index ffc45a9c..d20dfe42 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -6,7 +6,7 @@ # All rights reserved. # import debug -from math import log +from math import log,ceil import re class trim_spice(): @@ -42,7 +42,7 @@ class trim_spice(): self.word_size = word_size self.words_per_row = self.num_columns / self.word_size - self.row_addr_size = int(log(self.num_rows, 2)) + self.row_addr_size = ceil(log(self.num_rows, 2)) self.col_addr_size = int(log(self.words_per_row, 2)) self.bank_addr_size = self.col_addr_size + self.row_addr_size self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) diff --git a/compiler/gen_stimulus.py b/compiler/gen_stimulus.py index 0c5d988c..5e351577 100755 --- a/compiler/gen_stimulus.py +++ b/compiler/gen_stimulus.py @@ -52,11 +52,12 @@ import sram class fake_sram(sram.sram): """ This is an SRAM that doesn't actually create itself, just computes the sizes. """ - def __init__(self, word_size, num_words, num_banks, name): + def __init__(self, word_size, num_words, num_banks, name, num_spare_rows): self.name = name self.word_size = word_size self.num_words = num_words self.num_banks = num_banks + self.num_spare_rows = num_spare_rows c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() diff --git a/compiler/globals.py b/compiler/globals.py index e9b77d66..69ead34f 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -532,6 +532,8 @@ def report_status(): debug.error("{0} is not an integer in config file.".format(OPTS.sram_size)) if type(OPTS.write_size) is not int and OPTS.write_size is not None: debug.error("{0} is not an integer in config file.".format(OPTS.write_size)) + if type(OPTS.num_spare_rows) is not int and OPT.num_spare_rows is not None: + debug.error("{0} is not an integer in config file.".format(OPTS.num_spare_rows)) # If a write mask is specified by the user, the mask write size should be the same as # the word size so that an entire word is written at once. diff --git a/compiler/openram.py b/compiler/openram.py index 63a8cfd6..cf18f97b 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -53,7 +53,8 @@ from sram_config import sram_config # Configure the SRAM organization c = sram_config(word_size=OPTS.word_size, num_words=OPTS.num_words, - write_size=OPTS.write_size) + write_size=OPTS.write_size, + num_spare_rows=OPTS.num_spare_rows) debug.print_raw("Words per row: {}".format(c.words_per_row)) output_extensions = ["sp", "v", "lib", "py", "html", "log"] diff --git a/compiler/tests/21_ngspice_delay_extra_rows_test.py b/compiler/tests/21_ngspice_delay_extra_rows_test.py new file mode 100755 index 00000000..328dc630 --- /dev/null +++ b/compiler/tests/21_ngspice_delay_extra_rows_test.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class timing_sram_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.spice_name="ngspice" + OPTS.analytical_delay = False + OPTS.netlist_only = True + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import delay + from sram_config import sram_config + c = sram_config(word_size=1, + num_words=16, + num_banks=1, + num_spare_rows=5) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") + s = factory.create(module_type="sram", sram_config=c) + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + probe_address = "0" + ("1" * (s.s.addr_size - 1)) + probe_data = s.s.word_size - 1 + debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data)) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + d = delay(s.s, tempspice, corner) + import tech + loads = [tech.spice["dff_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + data, port_data = d.analyze(probe_address, probe_data, slews, loads) + #Combine info about port into all data + data.update(port_data[0]) + + if OPTS.tech_name == "freepdk45": + golden_data = {'delay_hl': [0.2264205], + 'delay_lh': [0.2264205], + 'leakage_power': 0.0021017429999999997, + 'min_period': 0.859, + 'read0_power': [0.3339161], + 'read1_power': [0.31329440000000003], + 'slew_hl': [0.2590786], + 'slew_lh': [0.2590786], + 'write0_power': [0.36360849999999995], + 'write1_power': [0.3486931]} + elif OPTS.tech_name == "scn4m_subm": + golden_data = {'delay_hl': [1.85985], + 'delay_lh': [1.85985], + 'leakage_power': 0.006418553, + 'min_period': 6.875, + 'read0_power': [12.656310000000001], + 'read1_power': [12.11682], + 'slew_hl': [1.868942], + 'slew_lh': [1.868942], + 'write0_power': [13.978110000000001], + 'write1_power': [11.437930000000001]} + else: + self.assertTrue(False) # other techs fail + + # Check if no too many or too few results + self.assertTrue(len(data.keys())==len(golden_data.keys())) + + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 694ea5c20ea58d831068fde3c596548420f5f756 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Thu, 20 Feb 2020 17:31:58 +0000 Subject: [PATCH 003/206] Characterization for extra rows --- compiler/modules/bank.py | 2 +- compiler/tests/21_ngspice_delay_extra_rows_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index aea4e909..e2f0186a 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -8,7 +8,7 @@ import debug import design from sram_factory import factory -from math import log +from math import log, ceil from tech import drc from vector import vector from globals import OPTS diff --git a/compiler/tests/21_ngspice_delay_extra_rows_test.py b/compiler/tests/21_ngspice_delay_extra_rows_test.py index 328dc630..66827ef5 100755 --- a/compiler/tests/21_ngspice_delay_extra_rows_test.py +++ b/compiler/tests/21_ngspice_delay_extra_rows_test.py @@ -33,7 +33,7 @@ class timing_sram_test(openram_test): c = sram_config(word_size=1, num_words=16, num_banks=1, - num_spare_rows=5) + num_spare_rows=3) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") From a5afbfe0aa825847633c456ac24ebae1b17bc0d4 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Sun, 22 Mar 2020 20:54:49 +0000 Subject: [PATCH 004/206] Fixed errors in extra rows characterization --- compiler/characterizer/lib.py | 5 ++++- compiler/gen_stimulus.py | 5 ++++- compiler/globals.py | 4 +--- compiler/options.py | 1 + compiler/sram/sram_config.py | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 6d6c6ce5..e3b1230d 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -559,7 +559,10 @@ class lib: char_results = self.d.analytical_delay(self.slews,self.loads) self.char_sram_results, self.char_port_results = char_results else: - probe_address = "1" * self.sram.addr_size + if (self.sram.num_spare_rows == 0): + probe_address = "1" * self.sram.addr_size + else: + probe_address = "0" + "1" * (self.sram.addr_size - 1) probe_data = self.sram.word_size - 1 char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads) self.char_sram_results, self.char_port_results = char_results diff --git a/compiler/gen_stimulus.py b/compiler/gen_stimulus.py index 5e351577..cfea17d4 100755 --- a/compiler/gen_stimulus.py +++ b/compiler/gen_stimulus.py @@ -76,7 +76,10 @@ d.period = period # Set the load of outputs and slew of inputs d.set_load_slew(load,slew) # Set the probe address/bit -probe_address = "1" * sram.addr_size +if (self.num_spare_rows == 0): + probe_address = "1" * sram.addr_size +else: + probe_address = "0" + ("1" * sram.addr_size - 1) probe_data = sram.word_size - 1 d.set_probe(probe_address, probe_data) diff --git a/compiler/globals.py b/compiler/globals.py index c864f53b..f883a648 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -539,9 +539,7 @@ def report_status(): debug.error("{0} is not an integer in config file.".format(OPTS.sram_size)) if type(OPTS.write_size) is not int and OPTS.write_size is not None: debug.error("{0} is not an integer in config file.".format(OPTS.write_size)) - if type(OPTS.num_spare_rows) is not int and OPT.num_spare_rows is not None: - debug.error("{0} is not an integer in config file.".format(OPTS.num_spare_rows)) - + # If a write mask is specified by the user, the mask write size should be the same as # the word size so that an entire word is written at once. if OPTS.write_size is not None: diff --git a/compiler/options.py b/compiler/options.py index d891ebd9..1c5d3e6a 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -44,6 +44,7 @@ class options(optparse.Values): # word_size = 0 # You can manually specify banks, but it is better to auto-detect it. num_banks = 1 + num_spare_rows = 0 ################### # Optimization options diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index cc1b44b5..e5d9c8dc 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -81,7 +81,7 @@ class sram_config: self.num_cols = int(self.words_per_row*self.word_size) self.num_rows_temp = int(self.num_words_per_bank/self.words_per_row) self.num_rows = self.num_rows_temp + self.num_spare_rows - debug.info(1,"Rows: {} Cols: {}".format(self.num_rows,self.num_cols)) + debug.info(1,"Rows: {} Cols: {}".format(self.num_rows_temp,self.num_cols)) # Compute the address and bank sizes self.row_addr_size = ceil(log(self.num_rows, 2)) From 2661a427260ab00be56d446eb10759519069fc26 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Tue, 14 Apr 2020 03:09:10 +0000 Subject: [PATCH 005/206] changes to support spare columns --- compiler/modules/port_data.py | 140 ++++++++++++++---- compiler/modules/sense_amp_array.py | 43 ++++-- compiler/modules/write_driver_array.py | 74 ++++++++- compiler/options.py | 1 + compiler/sram/sram_config.py | 7 +- .../09_sense_amp_array_spare_cols_test.py | 55 +++++++ .../10_write_driver_array_spare_cols_test.py | 55 +++++++ .../tests/18_port_data_spare_cols_test.py | 115 ++++++++++++++ 8 files changed, 439 insertions(+), 51 deletions(-) create mode 100755 compiler/tests/09_sense_amp_array_spare_cols_test.py create mode 100755 compiler/tests/10_write_driver_array_spare_cols_test.py create mode 100755 compiler/tests/18_port_data_spare_cols_test.py diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index fbf5223a..5fad35ae 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -27,6 +27,10 @@ class port_data(design.design): else: self.num_wmasks = 0 + if self.num_spare_cols is None: + self.num_spare_cols = 0 + + if name == "": name = "port_data_{0}".format(self.port) design.design.__init__(self, name) @@ -102,7 +106,7 @@ class port_data(design.design): self.DRC_LVS() def add_pins(self): - """ Adding pins for port address module""" + """ Adding pins for port data module""" self.add_pin("rbl_bl", "INOUT") self.add_pin("rbl_br", "INOUT") @@ -111,11 +115,18 @@ class port_data(design.design): br_name = self.get_br_name(self.port) self.add_pin("{0}_{1}".format(bl_name, bit), "INOUT") self.add_pin("{0}_{1}".format(br_name, bit), "INOUT") + + for bit in range(self.num_spare_cols): + bl_name = self.get_bl_name(self.port) + br_name = self.get_br_name(self.port) + self.add_pin("spare{0}_{1}".format(bl_name, bit), "INOUT") + self.add_pin("spare{0}_{1}".format(br_name, bit), "INOUT") + if self.port in self.read_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout_{}".format(bit), "OUTPUT") if self.port in self.write_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): self.add_pin("din_{}".format(bit), "INPUT") # Will be empty if no col addr lines sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] @@ -128,6 +139,8 @@ class port_data(design.design): self.add_pin("w_en", "INPUT") for bit in range(self.num_wmasks): self.add_pin("bank_wmask_{}".format(bit), "INPUT") + for bit in range(self.num_spare_cols): + self.add_pin("spare_wen{}".format(bit), "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -179,7 +192,7 @@ class port_data(design.design): # Extra column +1 is for RBL # Precharge will be shifted left if needed self.precharge_array = factory.create(module_type="precharge_array", - columns=self.num_cols + 1, + columns=self.num_cols + self.num_spare_cols + 1, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) self.add_mod(self.precharge_array) @@ -187,7 +200,8 @@ class port_data(design.design): if self.port in self.read_ports: self.sense_amp_array = factory.create(module_type="sense_amp_array", word_size=self.word_size, - words_per_row=self.words_per_row) + words_per_row=self.words_per_row, + num_spare_cols=self.num_spare_cols) self.add_mod(self.sense_amp_array) else: self.sense_amp_array = None @@ -206,7 +220,8 @@ class port_data(design.design): self.write_driver_array = factory.create(module_type="write_driver_array", columns=self.num_cols, word_size=self.word_size, - write_size=self.write_size) + write_size=self.write_size, + num_spare_cols=self.num_spare_cols) self.add_mod(self.write_driver_array) if self.write_size is not None: self.write_mask_and_array = factory.create(module_type="write_mask_and_array", @@ -249,7 +264,7 @@ class port_data(design.design): # We create a dummy here to get bl/br names to add those pins to this # module, which happens before we create the real precharge_array self.precharge_array = factory.create(module_type="precharge_array", - columns=self.num_cols + 1, + columns=self.num_cols + self.num_spare_cols + 1, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) @@ -272,6 +287,10 @@ class port_data(design.design): for bit in range(self.num_cols): temp.append("{0}_{1}".format(bl_name, bit)) temp.append("{0}_{1}".format(br_name, bit)) + + for bit in range(self.num_spare_cols): + temp.append("sparebl{0}_{1}".format(self.port, bit)) + temp.append("sparebr{0}_{1}".format(self.port, bit)) # Use right BLs for RBL if self.port==1: @@ -297,7 +316,6 @@ class port_data(design.design): for col in range(self.num_cols): temp.append("{0}_{1}".format(bl_name, col)) temp.append("{0}_{1}".format(br_name, col)) - for word in range(self.words_per_row): temp.append("sel_{}".format(word)) for bit in range(self.word_size): @@ -330,8 +348,14 @@ class port_data(design.design): else: temp.append("{0}_out_{1}".format(bl_name, bit)) temp.append("{0}_out_{1}".format(br_name, bit)) - - temp.extend(["s_en", "vdd", "gnd"]) + + for bit in range(self.num_spare_cols): + temp.append("dout_{}".format(self.word_size + bit)) + temp.append("sparebl{0}_{1}".format(self.port, bit)) + temp.append("sparebr{0}_{1}".format(self.port, bit)) + + temp.append("s_en") + temp.extend(["vdd", "gnd"]) self.connect_inst(temp) def place_sense_amp_array(self, offset): @@ -346,7 +370,7 @@ class port_data(design.design): br_name = self.get_br_name(self.port) temp = [] - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): temp.append("din_{}".format(bit)) for bit in range(self.word_size): @@ -355,11 +379,20 @@ class port_data(design.design): temp.append("{0}_{1}".format(br_name, bit)) else: temp.append("{0}_out_{1}".format(bl_name, bit)) - temp.append("{0}_out_{1}".format(br_name, bit)) + temp.append("{0}_out_{1}".format(br_name, bit)) + + for bit in range(self.num_spare_cols): + temp.append("sparebl{0}_{1}".format(self.port, bit)) + temp.append("sparebr{0}_{1}".format(self.port, bit)) if self.write_size is not None: for i in range(self.num_wmasks): temp.append("wdriver_sel_{}".format(i)) + + elif self.num_spare_cols: + temp.append("w_en") + for i in range(self.num_spare_cols): + temp.append("spare_wen{}".format(i)) else: temp.append("w_en") temp.extend(["vdd", "gnd"]) @@ -445,7 +478,7 @@ class port_data(design.design): def route_sense_amp_out(self, port): """ Add pins for the sense amp output """ - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(bit)) self.add_layout_pin_rect_center(text="dout_{0}".format(bit), layer=data_pin.layer, @@ -456,7 +489,7 @@ class port_data(design.design): def route_write_driver_in(self, port): """ Connecting write driver """ - for row in range(self.word_size): + for row in range(self.word_size + self.num_spare_cols): data_name = "data_{}".format(row) din_name = "din_{}".format(row) self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) @@ -545,12 +578,36 @@ class port_data(design.design): else: start_bit=0 - self.channel_route_bitlines(inst1=inst1, - inst1_bls_template=inst1_bls_templ, - inst2=inst2, - num_bits=self.word_size, - inst1_start_bit=start_bit) + if self.port==0: + off = 1 + else: + off = 0 + + if self.num_spare_cols != 0 and self.col_addr_size>0: + self.channel_route_bitlines(inst1=self.column_mux_array_inst, + inst1_bls_template="{inst}_out_{bit}", + inst2=inst2, + num_bits=self.word_size, + inst1_start_bit=start_bit) + + self.channel_route_bitlines(inst1=self.precharge_array_inst, + inst1_bls_template="{inst}_{bit}", + inst2=inst2, + num_bits=self.num_spare_cols, + inst1_start_bit=self.num_cols + off, + inst2_start_bit=self.word_size) + + else: + self.channel_route_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + num_bits=self.word_size + self.num_spare_cols, + inst1_start_bit=start_bit) + + + # spare cols connected to precharge array since they are read independently + def route_write_driver_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ inst2 = self.write_driver_array_inst @@ -569,10 +626,33 @@ class port_data(design.design): else: start_bit=0 - self.channel_route_bitlines(inst1=inst1, inst2=inst2, - num_bits=self.word_size, - inst1_bls_template=inst1_bls_templ, - inst1_start_bit=start_bit) + if self.port==0: + off=1 + else: + off=0 + + + if self.num_spare_cols != 0 and self.col_addr_size>0: + self.channel_route_bitlines(inst1=self.column_mux_array_inst, + inst1_bls_template="{inst}_out_{bit}", + inst2=inst2, + num_bits=self.word_size, + inst1_start_bit=start_bit) + + self.channel_route_bitlines(inst1=self.precharge_array_inst, + inst1_bls_template="{inst}_{bit}", + inst2=inst2, + num_bits=self.num_spare_cols, + inst1_start_bit=self.num_cols + off, + inst2_start_bit=self.word_size) + + else: + self.channel_route_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + num_bits=self.word_size + self.num_spare_cols, + inst1_start_bit=start_bit) + def route_write_driver_to_sense_amp(self, port): """ Routing of BL and BR between write driver and sense amp """ @@ -584,7 +664,7 @@ class port_data(design.design): # but just in case, do a channel route. self.channel_route_bitlines(inst1=inst1, inst2=inst2, - num_bits=self.word_size) + num_bits=self.word_size + self.num_spare_cols) def route_bitline_pins(self): """ Add the bitline pins for the given port """ @@ -595,13 +675,13 @@ class port_data(design.design): self.copy_layout_pin(self.precharge_array_inst, "br_0", "rbl_br") bit_offset=1 elif self.port==1: - self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(self.num_cols), "rbl_bl") - self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(self.num_cols), "rbl_br") + self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(self.num_cols + self.num_spare_cols), "rbl_bl") + self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(self.num_cols + self.num_spare_cols), "rbl_br") bit_offset=0 else: bit_offset=0 - for bit in range(self.num_cols): + for bit in range(self.num_cols + self.num_spare_cols): if self.precharge_array_inst: self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit + bit_offset), @@ -621,12 +701,16 @@ class port_data(design.design): for pin_name in sel_names: self.copy_layout_pin(self.column_mux_array_inst, pin_name) if self.sense_amp_array_inst: - self.copy_layout_pin(self.sense_amp_array_inst, "en", "s_en") + self.copy_layout_pin(self.sense_amp_array_inst, "en", "s_en") if self.write_driver_array_inst: if self.write_mask_and_array_inst: for bit in range(self.num_wmasks): # Add write driver's en_{} pins self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit), "wdriver_sel_{}".format(bit)) + elif self.num_spare_cols: + self.copy_layout_pin(self.write_driver_array_inst, "en_0", "w_en") + for bit in range(self.num_spare_cols): + self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + 1), "spare_wen{}".format(bit)) else: self.copy_layout_pin(self.write_driver_array_inst, "en", "w_en") if self.write_mask_and_array_inst: diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 322cc1b3..8640e033 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -19,7 +19,7 @@ class sense_amp_array(design.design): Dynamically generated sense amp array for all bitlines. """ - def __init__(self, name, word_size, words_per_row): + def __init__(self, name, word_size, words_per_row, num_spare_cols=None): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("word_size {0}".format(word_size)) @@ -27,8 +27,12 @@ class sense_amp_array(design.design): self.word_size = word_size self.words_per_row = words_per_row - self.row_size = self.word_size * self.words_per_row - + + if not num_spare_cols: + self.num_spare_cols = 0 + else: + self.num_spare_cols = num_spare_cols + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -58,9 +62,9 @@ class sense_amp_array(design.design): self.height = self.amp.height if self.bitcell.width > self.amp.width: - self.width = self.bitcell.width * self.word_size * self.words_per_row + self.width = self.bitcell.width * (self.word_size * self.words_per_row + self.num_spare_cols) else: - self.width = self.amp.width * self.word_size * self.words_per_row + self.width = self.amp.width * (self.word_size * self.words_per_row + self.num_spare_cols) self.place_sense_amp_array() self.add_layout_pins() @@ -69,7 +73,7 @@ class sense_amp_array(design.design): self.DRC_LVS() def add_pins(self): - for i in range(0,self.word_size): + for i in range(0,self.word_size + self.num_spare_cols): self.add_pin(self.data_name + "_{0}".format(i), "OUTPUT") self.add_pin(self.get_bl_name() + "_{0}".format(i), "INPUT") self.add_pin(self.get_br_name() + "_{0}".format(i), "INPUT") @@ -88,8 +92,7 @@ class sense_amp_array(design.design): def create_sense_amp_array(self): self.local_insts = [] - for i in range(0,self.word_size): - + for i in range(0,self.word_size + self.num_spare_cols): name = "sa_d{0}".format(i) self.local_insts.append(self.add_inst(name=name, mod=self.amp)) @@ -102,13 +105,14 @@ class sense_amp_array(design.design): from tech import cell_properties if self.bitcell.width > self.amp.width: amp_spacing = self.bitcell.width * self.words_per_row + spare_cols_spacing = self.bitcell.width else: amp_spacing = self.amp.width * self.words_per_row + spare_cols_spacing = self.amp.width for i in range(0,self.word_size): xoffset = amp_spacing * i - - # align the xoffset to the grid of bitcells. This way we + # align the xoffset to the grid of bitcells. This way we # know when to do the mirroring. grid_x = int(xoffset / self.amp.width) @@ -120,6 +124,23 @@ class sense_amp_array(design.design): amp_position = vector(xoffset, 0) self.local_insts[i].place(offset=amp_position,mirror=mirror) + + # place spare sense amps (will share the same enable as regular sense amps) + for i in range(0,self.num_spare_cols): + index = self.word_size + i + xoffset = ((self.word_size * self.words_per_row) + i) * spare_cols_spacing + # align the xoffset to the grid of bitcells. This way we + # know when to do the mirroring. + grid_x = int(xoffset / self.amp.width) + + if cell_properties.bitcell.mirror.y and grid_x % 2: + mirror = "MY" + xoffset = xoffset + self.amp.width + else: + mirror = "" + + amp_position = vector(xoffset, 0) + self.local_insts[index].place(offset=amp_position,mirror=mirror) def add_layout_pins(self): @@ -173,7 +194,7 @@ class sense_amp_array(design.design): def get_en_cin(self): """Get the relative capacitance of all the sense amp enable connections in the array""" sense_amp_en_cin = self.amp.get_en_cin() - return sense_amp_en_cin * self.word_size + return sense_amp_en_cin * (self.word_size + self.num_spare_cols) def get_drain_cin(self): """Get the relative capacitance of the drain of the PMOS isolation TX""" diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 08a2e007..f56c5e49 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -19,7 +19,7 @@ class write_driver_array(design.design): Dynamically generated write driver array of all bitlines. """ - def __init__(self, name, columns, word_size,write_size=None): + def __init__(self, name, columns, word_size, num_spare_cols=None, write_size=None): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) @@ -29,6 +29,10 @@ class write_driver_array(design.design): self.word_size = word_size self.write_size = write_size self.words_per_row = int(columns / word_size) + if not num_spare_cols: + self.num_spare_cols = 0 + else: + self.num_spare_cols = num_spare_cols if self.write_size: self.num_wmasks = int(self.word_size/self.write_size) @@ -61,9 +65,13 @@ class write_driver_array(design.design): def create_layout(self): if self.bitcell.width > self.driver.width: - self.width = self.columns * self.bitcell.width + self.width = (self.columns + self.num_spare_cols) * self.bitcell.width + self.width_regular_cols = self.columns * self.bitcell.width + self.single_col_width = self.bitcell.width else: - self.width = self.columns * self.driver.width + self.width = (self.columns + self.num_spare_cols) * self.driver.width + self.width_regular_cols = self.columns * self.driver.width + self.single_col_width = self.driver.width self.height = self.driver.height self.place_write_array() @@ -72,14 +80,17 @@ class write_driver_array(design.design): self.DRC_LVS() def add_pins(self): - for i in range(self.word_size): + for i in range(self.word_size + self.num_spare_cols): self.add_pin(self.data_name + "_{0}".format(i), "INPUT") - for i in range(self.word_size): + for i in range(self.word_size + self.num_spare_cols): self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT") self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT") if self.write_size: for i in range(self.num_wmasks): self.add_pin(self.en_name + "_{0}".format(i), "INPUT") + elif self.num_spare_cols: + for i in range(self.num_spare_cols + 1): + self.add_pin(self.en_name + "_{0}".format(i), "INPUT") else: self.add_pin(self.en_name, "INPUT") self.add_pin("vdd", "POWER") @@ -113,12 +124,30 @@ class write_driver_array(design.design): if w == self.write_size: w = 0 windex+=1 + + elif self.num_spare_cols: + self.connect_inst([self.data_name + "_{0}".format(index), + self.get_bl_name() + "_{0}".format(index), + self.get_br_name() + "_{0}".format(index), + self.en_name + "_{0}".format(0), "vdd", "gnd"]) + else: self.connect_inst([self.data_name + "_{0}".format(index), self.get_bl_name() + "_{0}".format(index), self.get_br_name() + "_{0}".format(index), self.en_name, "vdd", "gnd"]) + for i in range(self.num_spare_cols): + index = self.word_size + i + name = "write_driver{}".format(index) + self.driver_insts[index]=self.add_inst(name=name, + mod=self.driver) + + self.connect_inst([self.data_name + "_{0}".format(index), + self.get_bl_name() + "_{0}".format(index), + self.get_br_name() + "_{0}".format(index), + self.en_name + "_{0}".format(i + 1), "vdd", "gnd"]) + def place_write_array(self): from tech import cell_properties @@ -139,9 +168,23 @@ class write_driver_array(design.design): base = vector(xoffset, 0) self.driver_insts[index].place(offset=base, mirror=mirror) + # place spare write drivers (if spare columns are specified) + for i in range(self.num_spare_cols): + index = self.word_size + i + xoffset = (self.columns + i) * self.driver_spacing + + if cell_properties.bitcell.mirror.y and i % 2: + mirror = "MY" + xoffset = xoffset + self.driver.width + else: + mirror = "" + + base = vector(xoffset, 0) + self.driver_insts[index].place(offset=base, mirror=mirror) + def add_layout_pins(self): - for i in range(self.word_size): + for i in range(self.word_size + self.num_spare_cols): inst = self.driver_insts[i] din_pin = inst.get_pin(inst.mod.din_name) self.add_layout_pin(text=self.data_name + "_{0}".format(i), @@ -186,14 +229,29 @@ class write_driver_array(design.design): offset=en_pin.ll(), width=wmask_en_len-en_gap, height=en_pin.height()) + + elif self.num_spare_cols: + # shorten enable rail to accomodate those for spare write drivers + inst = self.driver_insts[0] + self.add_layout_pin(text=self.en_name + "_{0}".format(0), + layer="m1", + offset=inst.get_pin(inst.mod.en_name).ll(), + width=self.width_regular_cols - (self.words_per_row * inst.get_pin(inst.mod.en_name).width())) + + # individual enables for every spare write driver + for i in range(self.num_spare_cols): + inst = self.driver_insts[self.word_size + i] + self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1), + layer="m1", + offset=inst.get_pin(inst.mod.en_name).ll(), + width=self.single_col_width - inst.get_pin(inst.mod.en_name).width()) + else: inst = self.driver_insts[0] self.add_layout_pin(text=self.en_name, layer="m1", offset=inst.get_pin(inst.mod.en_name).ll().scale(0,1), width=self.width) - - def get_w_en_cin(self): diff --git a/compiler/options.py b/compiler/options.py index 1c5d3e6a..5a35b173 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -45,6 +45,7 @@ class options(optparse.Values): # You can manually specify banks, but it is better to auto-detect it. num_banks = 1 num_spare_rows = 0 + # num_spare_cols = 0 ################### # Optimization options diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index e5d9c8dc..3af2fd9c 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -14,19 +14,18 @@ from sram_factory import factory class sram_config: """ This is a structure that is used to hold the SRAM configuration options. """ - def __init__(self, word_size, num_words, write_size = None, num_banks=1, words_per_row=None, num_spare_rows=0): + def __init__(self, word_size, num_words, write_size = None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=None): self.word_size = word_size self.num_words = num_words self.write_size = write_size self.num_banks = num_banks self.num_spare_rows = num_spare_rows + self.num_spare_cols = num_spare_cols # This will get over-written when we determine the organization self.words_per_row = words_per_row - self.compute_sizes() - - + self.compute_sizes() def set_local_config(self, module): """ Copy all of the member variables to the given module for convenience """ diff --git a/compiler/tests/09_sense_amp_array_spare_cols_test.py b/compiler/tests/09_sense_amp_array_spare_cols_test.py new file mode 100755 index 00000000..1eabe196 --- /dev/null +++ b/compiler/tests/09_sense_amp_array_spare_cols_test.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class sense_amp_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check sense amp array for single port + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=1, num_spare_cols=3) + self.local_check(a) + + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4") + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4, num_spare_cols=2) + self.local_check(a) + + # check sense amp array for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)") + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2, num_spare_cols=2) + self.local_check(a) + + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)") + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4, num_spare_cols=3) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/10_write_driver_array_spare_cols_test.py b/compiler/tests/10_write_driver_array_spare_cols_test.py new file mode 100755 index 00000000..c5e7132f --- /dev/null +++ b/compiler/tests/10_write_driver_array_spare_cols_test.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class write_driver_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check write driver array for single port + debug.info(2, "Testing write_driver_array for columns=8, word_size=8") + a = factory.create(module_type="write_driver_array", columns=8, word_size=8, num_spare_cols=3) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=8") + a = factory.create(module_type="write_driver_array", columns=16, word_size=8, num_spare_cols=3) + self.local_check(a) + + # check write driver array for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)") + a = factory.create(module_type="write_driver_array", columns=8, word_size=8, num_spare_cols=3) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case)") + a = factory.create(module_type="write_driver_array", columns=16, word_size=8, num_spare_cols=3) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_spare_cols_test.py b/compiler/tests/18_port_data_spare_cols_test.py new file mode 100755 index 00000000..1695f861 --- /dev/null +++ b/compiler/tests/18_port_data_spare_cols_test.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class port_data_spare_cols_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + + c = sram_config(word_size=8, + num_words=16, + num_spare_rows=1, + num_spare_cols=1) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + c.num_spare_cols=3 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + c.num_spare_cols=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + OPTS.bitcell = "bitcell_1w_1r" + OPTS.num_rw_ports = 0 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + c.num_words=16 + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 1b6634bb9742ebc3be700e93d1fe6d50033f2d21 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 29 Apr 2020 15:48:15 -0700 Subject: [PATCH 006/206] port data routing fix --- compiler/base/hierarchy_layout.py | 19 +++++++++++++++---- compiler/modules/port_data.py | 19 +++++++++---------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index e6f9ac9f..03d0116e 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -905,8 +905,11 @@ class layout(): max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) + max_x_lc = max([pin.lc().x for pin in pins]) + min_x_rc = min([pin.rc().x for pin in pins]) + # if we are less than a pitch, just create a non-preferred layer jog - if max_x-min_x <= pitch: + if max_x - min_x <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] # Add the horizontal trunk on the vertical layer! @@ -927,7 +930,15 @@ class layout(): # Route each pin to the trunk for pin in pins: - mid = vector(pin.center().x, trunk_offset.y) + # If there is sufficient space, Route from the edge of the pins + # Otherwise, route from the center of the pins + if max_x_lc - min_x_rc > pitch: + if pin.center().x == max_x: + mid = vector(pin.lc().x, trunk_offset.y) + else: + mid = vector(pin.rc().x, trunk_offset.y) + else: + mid = vector(pin.center().x, trunk_offset.y) self.add_path(self.vertical_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid) @@ -1014,7 +1025,7 @@ class layout(): def vcg_pin_overlap(pin1, pin2, vertical, pitch): """ Check for vertical or horizontal overlap of the two pins """ - + # FIXME: If the pins are not in a row, this may break. # However, a top pin shouldn't overlap another top pin, # for example, so the @@ -1095,7 +1106,7 @@ class layout(): # list of routes to do while vcg: # from pprint import pformat - # print("VCG:\n",pformat(vcg)) + # print("VCG:\n", pformat(vcg)) # get a route from conflict graph with empty fanout set net_name = None for net_name, conflicts in vcg.items(): diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index bdf10f39..641a1349 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -19,7 +19,7 @@ class port_data(design.design): """ def __init__(self, sram_config, port, name=""): - + sram_config.set_local_config(self) self.port = port if self.write_size is not None: @@ -444,7 +444,7 @@ class port_data(design.design): def route_sense_amp_out(self, port): """ Add pins for the sense amp output """ - + for bit in range(self.word_size): data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(bit)) self.add_layout_pin_rect_center(text="dout_{0}".format(bit), @@ -521,10 +521,10 @@ class port_data(design.design): insn2_start_bit = 1 if self.port == 0 else 0 - self.connect_bitlines(inst1=inst1, - inst2=inst2, - num_bits=self.num_cols, - inst2_start_bit=insn2_start_bit) + self.channel_route_bitlines(inst1=inst1, + inst2=inst2, + num_bits=self.num_cols, + inst2_start_bit=insn2_start_bit) def route_sense_amp_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ @@ -703,7 +703,7 @@ class port_data(design.design): bitline_dirs = ("H", "V") elif bottom_names[0].layer == "m1": bitline_dirs = ("V", "H") - + route_map = list(zip(bottom_names, top_names)) self.create_horizontal_channel_route(route_map, offset, self.m1_stack, bitline_dirs) @@ -717,7 +717,7 @@ class port_data(design.design): This assumes that they have sufficient space to create a jog in the middle between the two modules (if needed). """ - + bot_inst_group, top_inst_group = self._group_bitline_instances( inst1, inst2, num_bits, inst1_bls_template, inst1_start_bit, @@ -738,9 +738,8 @@ class port_data(design.design): vector(bot_br.x, yoffset), vector(top_br.x, yoffset), top_br]) - + def graph_exclude_precharge(self): """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" if self.precharge_array_inst: self.graph_inst_exclude.add(self.precharge_array_inst) - From 49918b0716d8d403e1b47e412a356db3c9c51a90 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Sat, 2 May 2020 09:44:56 +0000 Subject: [PATCH 007/206] New lib syntax for golden results --- .../tests/21_ngspice_delay_extra_rows_test.py | 46 +++++++++++-------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/compiler/tests/21_ngspice_delay_extra_rows_test.py b/compiler/tests/21_ngspice_delay_extra_rows_test.py index 66827ef5..22812d52 100755 --- a/compiler/tests/21_ngspice_delay_extra_rows_test.py +++ b/compiler/tests/21_ngspice_delay_extra_rows_test.py @@ -56,27 +56,35 @@ class timing_sram_test(openram_test): data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_hl': [0.2264205], - 'delay_lh': [0.2264205], - 'leakage_power': 0.0021017429999999997, - 'min_period': 0.859, - 'read0_power': [0.3339161], - 'read1_power': [0.31329440000000003], - 'slew_hl': [0.2590786], - 'slew_lh': [0.2590786], - 'write0_power': [0.36360849999999995], - 'write1_power': [0.3486931]} + golden_data = {'slew_lh': [0.2592187], + 'slew_hl': [0.2592187], + 'delay_lh': [0.2465583], + 'disabled_write0_power': [0.1924678], + 'disabled_read0_power': [0.152483], + 'write0_power': [0.3409064], + 'disabled_read1_power': [0.1737818], + 'read0_power': [0.3096708], + 'read1_power': [0.3107916], + 'delay_hl': [0.2465583], + 'write1_power': [0.26915849999999997], + 'leakage_power': 0.002044307, + 'min_period': 0.898, + 'disabled_write1_power': [0.201411]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl': [1.85985], - 'delay_lh': [1.85985], - 'leakage_power': 0.006418553, + golden_data = {'read1_power': [12.11658], + 'write1_power': [10.52653], + 'read0_power': [11.956710000000001], + 'disabled_write0_power': [7.673665], + 'disabled_write1_power': [7.981922000000001], + 'slew_lh': [1.868836], + 'slew_hl': [1.868836], + 'delay_hl': [1.8598510000000001], + 'delay_lh': [1.8598510000000001], + 'leakage_power': 0.005457728, + 'disabled_read0_power': [5.904712], 'min_period': 6.875, - 'read0_power': [12.656310000000001], - 'read1_power': [12.11682], - 'slew_hl': [1.868942], - 'slew_lh': [1.868942], - 'write0_power': [13.978110000000001], - 'write1_power': [11.437930000000001]} + 'disabled_read1_power': [7.132159], + 'write0_power': [13.406400000000001]} else: self.assertTrue(False) # other techs fail From e30938fb66db62ed06340f45e235659f610471c8 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Sun, 3 May 2020 15:23:30 +0000 Subject: [PATCH 008/206] Spare columns working at bank level --- compiler/modules/bank.py | 34 +++++++--- compiler/modules/port_data.py | 8 +-- .../tests/19_single_bank_spare_cols_test.py | 68 +++++++++++++++++++ 3 files changed, 92 insertions(+), 18 deletions(-) create mode 100755 compiler/tests/19_single_bank_spare_cols_test.py diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 252f0945..379e03d7 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -31,6 +31,9 @@ class bank(design.design): else: self.num_wmasks = 0 + if not self.num_spare_cols: + self.num_spare_cols = 0 + if name == "": name = "bank_{0}_{1}".format(self.word_size, self.num_words) design.design.__init__(self, name) @@ -77,12 +80,12 @@ class bank(design.design): def add_pins(self): """ Adding pins for Bank module""" for port in self.read_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout{0}_{1}".format(port, bit), "OUTPUT") for port in self.all_ports: self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT") for port in self.write_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): self.add_pin("din{0}_{1}".format(port,bit), "INPUT") for port in self.all_ports: for bit in range(self.addr_size): @@ -101,6 +104,8 @@ class bank(design.design): self.add_pin("w_en{0}".format(port), "INPUT") for bit in range(self.num_wmasks): self.add_pin("bank_wmask{0}_{1}".format(port, bit), "INPUT") + for bit in range(self.num_spare_cols): + self.add_pin("spare_wen{0}_{1}".format(port, bit), "INPUT") for port in self.all_ports: self.add_pin("wl_en{0}".format(port), "INPUT") self.add_pin("vdd", "POWER") @@ -359,7 +364,7 @@ class bank(design.design): self.add_mod(self.port_data[port]) self.port_address = factory.create(module_type="port_address", - cols=self.num_cols, + cols=self.num_cols + self.num_spare_cols, rows=self.num_rows) self.add_mod(self.port_address) @@ -367,7 +372,7 @@ class bank(design.design): self.num_rbl = len(self.all_ports) self.bitcell_array = factory.create(module_type="replica_bitcell_array", - cols=self.num_cols, + cols=self.num_cols + self.num_spare_cols, rows=self.num_rows, left_rbl=1, right_rbl=1 if len(self.all_ports)>1 else 0, @@ -385,7 +390,7 @@ class bank(design.design): mod=self.bitcell_array) temp = [] - for col in range(self.num_cols): + for col in range(self.num_cols + self.num_spare_cols): for bitline in self.bitline_names: temp.append("{0}_{1}".format(bitline, col)) for rbl in range(self.num_rbl): @@ -419,14 +424,14 @@ class bank(design.design): rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port]) temp.append(rbl_bl_name) temp.append(rbl_br_name) - for col in range(self.num_cols): + for col in range(self.num_cols + self.num_spare_cols): temp.append("{0}_{1}".format(self.bl_names[port], col)) temp.append("{0}_{1}".format(self.br_names[port], col)) if port in self.read_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): temp.append("dout{0}_{1}".format(port, bit)) if port in self.write_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): temp.append("din{0}_{1}".format(port, bit)) # Will be empty if no col addr lines sel_names = ["sel{0}_{1}".format(port, x) for x in range(self.num_col_addr_lines)] @@ -438,6 +443,8 @@ class bank(design.design): temp.append("w_en{0}".format(port)) for bit in range(self.num_wmasks): temp.append("bank_wmask{0}_{1}".format(port, bit)) + for bit in range(self.num_spare_cols): + temp.append("spare_wen{0}_{1}".format(port, bit)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -676,7 +683,7 @@ class bank(design.design): self.connect_bitlines(inst1=inst1, inst2=inst2, - num_bits=self.num_cols, + num_bits=self.num_cols + self.num_spare_cols, inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst2_bl_name=inst2_bl_name, @@ -691,7 +698,7 @@ class bank(design.design): def route_port_data_out(self, port): """ Add pins for the port data out """ - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): data_pin = self.port_data_inst[port].get_pin("dout_{0}".format(bit)) self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port, bit), layer=data_pin.layer, @@ -712,7 +719,7 @@ class bank(design.design): def route_port_data_in(self, port): """ Connecting port data in """ - for row in range(self.word_size): + for row in range(self.word_size + self.num_spare_cols): data_name = "din_{}".format(row) din_name = "din{0}_{1}".format(port, row) self.copy_layout_pin(self.port_data_inst[port], data_name, din_name) @@ -722,6 +729,11 @@ class bank(design.design): wmask_name = "bank_wmask_{}".format(row) bank_wmask_name = "bank_wmask{0}_{1}".format(port, row) self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name) + + for col in range(self.num_spare_cols): + sparecol_name = "spare_wen{}".format(col) + bank_sparecol_name = "spare_wen{0}_{1}".format(port, col) + self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name) def channel_route_bitlines(self, inst1, inst2, num_bits, inst1_bl_name="bl_{}", inst1_br_name="br_{}", diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index c33b7639..dc551f7e 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -110,18 +110,12 @@ class port_data(design.design): self.add_pin("rbl_bl", "INOUT") self.add_pin("rbl_br", "INOUT") - for bit in range(self.num_cols): + for bit in range(self.num_cols + self.num_spare_cols): bl_name = self.get_bl_name(self.port) br_name = self.get_br_name(self.port) self.add_pin("{0}_{1}".format(bl_name, bit), "INOUT") self.add_pin("{0}_{1}".format(br_name, bit), "INOUT") - for bit in range(self.num_spare_cols): - bl_name = self.get_bl_name(self.port) - br_name = self.get_br_name(self.port) - self.add_pin("spare{0}_{1}".format(bl_name, bit), "INOUT") - self.add_pin("spare{0}_{1}".format(br_name, bit), "INOUT") - if self.port in self.read_ports: for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout_{}".format(bit), "OUTPUT") diff --git a/compiler/tests/19_single_bank_spare_cols_test.py b/compiler/tests/19_single_bank_spare_cols_test.py new file mode 100755 index 00000000..fc247bcd --- /dev/null +++ b/compiler/tests/19_single_bank_spare_cols_test.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class single_bank_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=3) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 91dbbed9ba404bc951711d51f59023821e034508 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Tue, 5 May 2020 12:18:26 -0700 Subject: [PATCH 009/206] added horizontal trunk route edit to vertical trunk route --- compiler/base/hierarchy_layout.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 03d0116e..4f74185b 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -955,6 +955,9 @@ class layout(): max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) + max_y_uc = max([pin.uc().y for pin in pins]) + min_y_bc = min([pin.bc().y for pin in pins]) + # if we are less than a pitch, just create a non-preferred layer jog if max_y - min_y <= pitch: @@ -978,7 +981,15 @@ class layout(): # Route each pin to the trunk for pin in pins: - mid = vector(trunk_offset.x, pin.center().y) + # If there is sufficient space, Route from the edge of the pins + # Otherwise, route from the center of the pins + if max_y_uc - min_y_bc > pitch: + if pin.center().y == max_y: + mid = vector(trunk_offset.x, pin.bc().y) + else: + mid = vector(trunk_offset.x, pin.uc().y) + else: + mid = vector(trunk_offset.x, pin.center().y) self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid) From e642b8521b01df689d9a55a5e6de4e72f4820d7a Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 6 May 2020 13:02:33 -0700 Subject: [PATCH 010/206] increase col_mux bitline spacing to fix cyclic vcg --- compiler/base/hierarchy_layout.py | 14 +++++++++----- compiler/pgates/single_level_column_mux.py | 15 +++++++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 4f74185b..ffb35151 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -905,11 +905,13 @@ class layout(): max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) + # max_x_lc & min_x_rc are for routing to/from the edge of the pins + # to increase spacing between contacts of different nets max_x_lc = max([pin.lc().x for pin in pins]) min_x_rc = min([pin.rc().x for pin in pins]) # if we are less than a pitch, just create a non-preferred layer jog - if max_x - min_x <= pitch: + if max_x_lc - min_x_rc <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] # Add the horizontal trunk on the vertical layer! @@ -955,11 +957,13 @@ class layout(): max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) - max_y_uc = max([pin.uc().y for pin in pins]) - min_y_bc = min([pin.bc().y for pin in pins]) + # max_y_bc & min_y_uc are for routing to/from the edge of the pins + # to reduce spacing between contacts of different nets + max_y_bc = max([pin.bc().y for pin in pins]) + min_y_uc = min([pin.uc().y for pin in pins]) # if we are less than a pitch, just create a non-preferred layer jog - if max_y - min_y <= pitch: + if max_y_bc - min_y_uc <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] @@ -983,7 +987,7 @@ class layout(): for pin in pins: # If there is sufficient space, Route from the edge of the pins # Otherwise, route from the center of the pins - if max_y_uc - min_y_bc > pitch: + if max_y_bc - min_y_uc > pitch: if pin.center().y == max_y: mid = vector(trunk_offset.x, pin.bc().y) else: diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 9e38287f..0629a96c 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -11,7 +11,7 @@ from tech import drc, layer from vector import vector from sram_factory import factory import logical_effort - +from utils import round_to_grid class single_level_column_mux(pgate.pgate): """ @@ -75,6 +75,17 @@ class single_level_column_mux(pgate.pgate): bl_pos = vector(bl_pin.lx(), 0) br_pos = vector(br_pin.lx(), 0) + # The bitline input/output pins must be a least as wide as the metal pitch + # so that there is enough space to route to/from the pins. + # FIXME: bitline_metal_pitch should be greater than the horizontal metal pitch used in port_data + bitline_metal_pitch = self.width / 2 + bitline_width = br_pos.x - bl_pos.x + if bitline_width < bitline_metal_pitch: + bitline_width_increase_bl = round_to_grid((bitline_metal_pitch - bitline_width) / 2) + bitline_width_increase_br = round_to_grid((bitline_metal_pitch - bitline_width) - bitline_width_increase_bl) + bl_pos = bl_pos + vector(-bitline_width_increase_bl, 0) + br_pos = br_pos + vector( bitline_width_increase_br, 0) + # bl and br self.add_layout_pin(text="bl", layer=bl_pin.layer, @@ -133,7 +144,7 @@ class single_level_column_mux(pgate.pgate): def connect_bitlines(self): """ Connect the bitlines to the mux transistors """ - + # If li exists, use li and m1 for the mux, otherwise use m1 and m2 if "li" in layer: self.col_mux_stack = self.li_stack From 5c50cf234bade52c321bcfb502f0de6654ef2dcf Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Sat, 9 May 2020 07:56:19 +0000 Subject: [PATCH 011/206] Fixed lvs errors for spare columns --- compiler/modules/bank.py | 7 ++++++- compiler/modules/port_data.py | 37 +++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 379e03d7..597d2fc0 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -683,11 +683,16 @@ class bank(design.design): self.connect_bitlines(inst1=inst1, inst2=inst2, - num_bits=self.num_cols + self.num_spare_cols, + num_bits=self.num_cols, inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst2_bl_name=inst2_bl_name, inst2_br_name=inst2_br_name) + + # connect spare bitlines + for i in range(self.num_spare_cols): + self.connect_bitline(inst1, inst2, inst1_bl_name.format(self.num_cols+i), "spare" + inst2_bl_name.format(i)) + self.connect_bitline(inst1, inst2, inst1_br_name.format(self.num_cols+i), "spare" + inst2_br_name.format(i)) # Connect the replica bitlines rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index dc551f7e..1d434929 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -30,7 +30,6 @@ class port_data(design.design): if self.num_spare_cols is None: self.num_spare_cols = 0 - if name == "": name = "port_data_{0}".format(self.port) design.design.__init__(self, name) @@ -110,12 +109,17 @@ class port_data(design.design): self.add_pin("rbl_bl", "INOUT") self.add_pin("rbl_br", "INOUT") - for bit in range(self.num_cols + self.num_spare_cols): + for bit in range(self.num_cols): bl_name = self.get_bl_name(self.port) br_name = self.get_br_name(self.port) self.add_pin("{0}_{1}".format(bl_name, bit), "INOUT") self.add_pin("{0}_{1}".format(br_name, bit), "INOUT") - + for bit in range(self.num_spare_cols): + bl_name = self.get_bl_name(self.port) + br_name = self.get_br_name(self.port) + self.add_pin("spare{0}_{1}".format(bl_name, bit), "INOUT") + self.add_pin("spare{0}_{1}".format(br_name, bit), "INOUT") + if self.port in self.read_ports: for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout_{}".format(bit), "OUTPUT") @@ -283,8 +287,8 @@ class port_data(design.design): temp.append("{0}_{1}".format(br_name, bit)) for bit in range(self.num_spare_cols): - temp.append("sparebl{0}_{1}".format(self.port, bit)) - temp.append("sparebr{0}_{1}".format(self.port, bit)) + temp.append("spare{0}_{1}".format(bl_name, bit)) + temp.append("spare{0}_{1}".format(br_name, bit)) # Use right BLs for RBL if self.port==1: @@ -345,8 +349,8 @@ class port_data(design.design): for bit in range(self.num_spare_cols): temp.append("dout_{}".format(self.word_size + bit)) - temp.append("sparebl{0}_{1}".format(self.port, bit)) - temp.append("sparebr{0}_{1}".format(self.port, bit)) + temp.append("spare{0}_{1}".format(bl_name, bit)) + temp.append("spare{0}_{1}".format(br_name, bit)) temp.append("s_en") temp.extend(["vdd", "gnd"]) @@ -376,8 +380,8 @@ class port_data(design.design): temp.append("{0}_out_{1}".format(br_name, bit)) for bit in range(self.num_spare_cols): - temp.append("sparebl{0}_{1}".format(self.port, bit)) - temp.append("sparebr{0}_{1}".format(self.port, bit)) + temp.append("spare{0}_{1}".format(bl_name, bit)) + temp.append("spare{0}_{1}".format(br_name, bit)) if self.write_size is not None: for i in range(self.num_wmasks): @@ -675,7 +679,7 @@ class port_data(design.design): else: bit_offset=0 - for bit in range(self.num_cols + self.num_spare_cols): + for bit in range(self.num_cols): if self.precharge_array_inst: self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit + bit_offset), @@ -685,6 +689,19 @@ class port_data(design.design): "br_{}".format(bit)) else: debug.error("Didn't find precharge array.") + + # Copy layout pins of spare columns + for bit in range(self.num_spare_cols): + if self.precharge_array_inst: + self.copy_layout_pin(self.precharge_array_inst, + "bl_{}".format(self.num_cols + bit + bit_offset), + "sparebl_{}".format(bit)) + self.copy_layout_pin(self.precharge_array_inst, + "br_{}".format(self.num_cols + bit + bit_offset), + "sparebr_{}".format(bit)) + else: + debug.error("Didn't find precharge array.") + def route_control_pins(self): """ Add the control pins: s_en, p_en_bar, w_en """ From c96a6d0b9d9d7d5d5461b11592fac0ad2a2feaee Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 11 May 2020 16:22:08 -0700 Subject: [PATCH 012/206] Add no well option. Add stack gates vertical option. --- compiler/pgates/pand2.py | 80 +++++++++++++++++++++++++------- compiler/pgates/pand3.py | 76 ++++++++++++++++++++++++------ compiler/pgates/pbuf.py | 3 +- compiler/pgates/pdriver.py | 5 +- compiler/pgates/pgate.py | 18 +++++-- compiler/pgates/pinv.py | 7 +-- compiler/pgates/pnand2.py | 9 ++-- compiler/pgates/pnand3.py | 7 +-- compiler/pgates/pnor2.py | 11 +++-- compiler/tests/04_pand2_test.py | 4 ++ compiler/tests/04_pand3_test.py | 4 ++ compiler/tests/04_pnand2_test.py | 5 ++ compiler/tests/04_pnand3_test.py | 5 ++ compiler/tests/04_pnor2_test.py | 4 ++ 14 files changed, 187 insertions(+), 51 deletions(-) diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 69af1e62..6df877fd 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -15,14 +15,14 @@ class pand2(pgate.pgate): """ This is a simple buffer used for driving loads. """ - def __init__(self, name, size=1, height=None): - debug.info(1, "Creating pnand2 {}".format(name)) + def __init__(self, name, size=1, height=None, vertical=False): + debug.info(1, "Creating pand2 {}".format(name)) self.add_comment("size: {}".format(size)) - + + self.vertical = vertical self.size = size - - # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + + pgate.pgate.__init__(self, name) def create_netlist(self): self.add_pins() @@ -30,17 +30,22 @@ class pand2(pgate.pgate): self.create_insts() def create_modules(self): - self.nand = factory.create(module_type="pnand2", height=self.height) + self.nand = factory.create(module_type="pnand2", + height=self.height) self.add_mod(self.nand) self.inv = factory.create(module_type="pdriver", - neg_polarity=True, - fanout=self.size, + size_list=[self.size], height=self.height) self.add_mod(self.inv) def create_layout(self): - self.width = self.nand.width + self.inv.width + if self.vertical: + self.height = 2 * self.nand.height + self.width = max(self.nand.width, self.inv.width) + else: + self.width = self.nand.width + self.inv.width + self.place_insts() self.add_wires() self.add_layout_pins() @@ -68,17 +73,60 @@ class pand2(pgate.pgate): # Add NAND to the right self.nand_inst.place(offset=vector(0, 0)) - # Add INV to the right - self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + if self.vertical: + # Add INV above + self.inv_inst.place(offset=vector(self.inv.width, + 2 * self.nand.height), + mirror="XY") + else: + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + + # Second gnd of the inverter gate + if self.vertical: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + if self.vertical: + # Shared between two gates + y_offset = 0.5 * self.height + else: + y_offset = self.height + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, y_offset), + width=self.width) + def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") a2_pin = self.inv_inst.get_pin("A") - mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy()) - mid2_point = vector(mid1_point, a2_pin.cy()) - self.add_path(self.route_layer, - [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) + if self.vertical: + route_layer = "m2" + self.add_via_stack_center(offset=z1_pin.center(), + from_layer=z1_pin.layer, + to_layer=route_layer) + self.add_zjog(route_layer, + z1_pin.uc(), + a2_pin.bc(), + "V") + self.add_via_stack_center(offset=a2_pin.center(), + from_layer=a2_pin.layer, + to_layer=route_layer) + else: + route_layer = self.route_layer + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) def add_layout_pins(self): pin = self.inv_inst.get_pin("Z") diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index 841ac69d..5e2e83f8 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -15,10 +15,11 @@ class pand3(pgate.pgate): """ This is a simple buffer used for driving loads. """ - def __init__(self, name, size=1, height=None): + def __init__(self, name, size=1, height=None, vertical=False): debug.info(1, "Creating pand3 {}".format(name)) self.add_comment("size: {}".format(size)) - + + self.vertical = vertical self.size = size # Creates the netlist and layout @@ -31,16 +32,22 @@ class pand3(pgate.pgate): def create_modules(self): # Shield the cap, but have at least a stage effort of 4 - self.nand = factory.create(module_type="pnand3", height=self.height) + self.nand = factory.create(module_type="pnand3", + height=self.height) self.add_mod(self.nand) - self.inv = factory.create(module_type="pinv", - size=self.size, + self.inv = factory.create(module_type="pdriver", + size_list=[self.size], height=self.height) self.add_mod(self.inv) def create_layout(self): - self.width = self.nand.width + self.inv.width + if self.vertical: + self.height = 2 * self.nand.height + self.width = max(self.nand.width, self.inv.width) + else: + self.width = self.nand.width + self.inv.width + self.place_insts() self.add_wires() self.add_layout_pins() @@ -69,18 +76,61 @@ class pand3(pgate.pgate): # Add NAND to the right self.nand_inst.place(offset=vector(0, 0)) - # Add INV to the right - self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + if self.vertical: + # Add INV above + self.inv_inst.place(offset=vector(self.inv.width, + 2 * self.nand.height), + mirror="XY") + else: + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + + # Second gnd of the inverter gate + if self.vertical: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + if self.vertical: + # Shared between two gates + y_offset = 0.5 * self.height + else: + y_offset = self.height + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, y_offset), + width=self.width) + def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") a2_pin = self.inv_inst.get_pin("A") - mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) - mid2_point = vector(mid1_point, a2_pin.cy()) - self.add_path(z1_pin.layer, - [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) - + if self.vertical: + route_layer = "m2" + self.add_via_stack_center(offset=z1_pin.center(), + from_layer=z1_pin.layer, + to_layer=route_layer) + self.add_zjog(route_layer, + z1_pin.uc(), + a2_pin.bc(), + "V") + self.add_via_stack_center(offset=a2_pin.center(), + from_layer=a2_pin.layer, + to_layer=route_layer) + else: + route_layer = self.route_layer + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + def add_layout_pins(self): pin = self.inv_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Z", diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index 4d90286d..8b9c4eab 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -56,7 +56,8 @@ class pbuf(pgate.pgate): self.inv2 = factory.create(module_type="pinv", size=self.size, - height=self.height) + height=self.height, + add_wells=False) self.add_mod(self.inv2) def create_insts(self): diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 9bba7aee..b36d3e95 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -87,10 +87,13 @@ class pdriver(pgate.pgate): def add_modules(self): self.inv_list = [] + add_well = True for size in self.size_list: temp_inv = factory.create(module_type="pinv", size=size, - height=self.height) + height=self.height, + add_wells=add_well) + add_well=False self.inv_list.append(temp_inv) self.add_mod(temp_inv) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 5351be07..be317f48 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -24,7 +24,7 @@ class pgate(design.design): functions for parameterized gates. """ - def __init__(self, name, height=None): + def __init__(self, name, height=None, add_wells=True): """ Creates a generic cell """ design.design.__init__(self, name) @@ -33,7 +33,8 @@ class pgate(design.design): elif not height: # By default, something simple self.height = 14 * self.m1_pitch - + self.add_wells = add_wells + if "li" in layer: self.route_layer = "li" else: @@ -150,7 +151,7 @@ class pgate(design.design): # This should match the cells in the cell library self.nwell_y_offset = 0.48 * self.height - full_height = self.height + 0.5* self.m1_width + full_height = self.height + 0.5 * self.m1_width # FIXME: float rounding problem if "nwell" in layer: @@ -302,10 +303,17 @@ class pgate(design.design): def determine_width(self): """ Determine the width based on the well contacts (assumed to be on the right side) """ + # Width is determined by well contact and spacing and allowing a supply via between each cell - self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width + if self.add_wells: + self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width + # Height is an input parameter, so it is not recomputed. + else: + max_active_xoffset = self.find_highest_layer_coords("active").x + max_route_xoffset = self.find_highest_layer_coords(self.route_layer).x + 0.5 * self.m1_space + self.width = max(max_active_xoffset, max_route_xoffset) + self.well_width = self.width + 2 * self.nwell_enclose_active - # Height is an input parameter, so it is not recomputed. @staticmethod def bin_width(tx_type, target_width): diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index e0e1ce81..214e66dc 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -32,7 +32,7 @@ class pinv(pgate.pgate): from center of rail to rail. """ - def __init__(self, name, size=1, beta=parameter["beta"], height=None): + def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True): debug.info(2, "creating pinv structure {0} with size of {1}".format(name, @@ -44,7 +44,7 @@ class pinv(pgate.pgate): self.pmos_size = beta * size self.beta = beta - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): """ Calls all functions related to the generation of the netlist """ @@ -56,7 +56,8 @@ class pinv(pgate.pgate): def create_layout(self): """ Calls all functions related to the generation of the layout """ self.place_ptx() - self.add_well_contacts() + if self.add_wells: + self.add_well_contacts() self.determine_width() self.extend_wells() self.route_supply_rails() diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 5d20201e..59995133 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -20,7 +20,7 @@ class pnand2(pgate.pgate): This module generates gds of a parametrically sized 2-input nand. This model use ptx to generate a 2-input nand within a cetrain height. """ - def __init__(self, name, size=1, height=None): + def __init__(self, name, size=1, height=None, add_wells=True): """ Creates a cell for a simple 2 input nand """ debug.info(2, @@ -43,7 +43,7 @@ class pnand2(pgate.pgate): (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): self.add_pins() @@ -55,13 +55,14 @@ class pnand2(pgate.pgate): self.setup_layout_constants() self.place_ptx() - self.add_well_contacts() + if self.add_wells: + self.add_well_contacts() + self.route_output() self.determine_width() self.route_supply_rails() self.connect_rails() self.extend_wells() self.route_inputs() - self.route_output() self.add_boundary() def add_pins(self): diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 454b75dd..446b8212 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -19,7 +19,7 @@ class pnand3(pgate.pgate): This module generates gds of a parametrically sized 2-input nand. This model use ptx to generate a 2-input nand within a cetrain height. """ - def __init__(self, name, size=1, height=None): + def __init__(self, name, size=1, height=None, add_wells=True): """ Creates a cell for a simple 3 input nand """ debug.info(2, @@ -45,7 +45,7 @@ class pnand3(pgate.pgate): (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def add_pins(self): """ Adds pins for spice netlist """ @@ -63,7 +63,8 @@ class pnand3(pgate.pgate): self.setup_layout_constants() self.place_ptx() - self.add_well_contacts() + if self.add_wells: + self.add_well_contacts() self.determine_width() self.route_supply_rails() self.connect_rails() diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index f522578e..2126e86c 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -19,7 +19,7 @@ class pnor2(pgate.pgate): This module generates gds of a parametrically sized 2-input nor. This model use ptx to generate a 2-input nor within a cetrain height. """ - def __init__(self, name, size=1, height=None): + def __init__(self, name, size=1, height=None, add_wells=True): """ Creates a cell for a simple 2 input nor """ debug.info(2, @@ -42,7 +42,7 @@ class pnor2(pgate.pgate): (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): self.add_pins() @@ -54,13 +54,14 @@ class pnor2(pgate.pgate): self.setup_layout_constants() self.place_ptx() - self.add_well_contacts() + if self.add_wells: + self.add_well_contacts() + self.route_inputs() + self.route_output() self.determine_width() self.route_supply_rails() self.connect_rails() self.extend_wells() - self.route_inputs() - self.route_output() self.add_boundary() def add_pins(self): diff --git a/compiler/tests/04_pand2_test.py b/compiler/tests/04_pand2_test.py index f7e5f304..21a0a38e 100755 --- a/compiler/tests/04_pand2_test.py +++ b/compiler/tests/04_pand2_test.py @@ -29,6 +29,10 @@ class pand2_test(openram_test): a = pand2.pand2(name="pand2x4", size=4) self.local_check(a) + debug.info(2, "Testing vertical pand2 gate 4x") + a = pand2.pand2(name="pand2x4", size=4, vertical=True) + self.local_check(a) + globals.end_openram() # instantiate a copdsay of the class to actually run the test diff --git a/compiler/tests/04_pand3_test.py b/compiler/tests/04_pand3_test.py index e58f1ee9..f851077b 100755 --- a/compiler/tests/04_pand3_test.py +++ b/compiler/tests/04_pand3_test.py @@ -29,6 +29,10 @@ class pand3_test(openram_test): a = pand3.pand3(name="pand3x4", size=4) self.local_check(a) + debug.info(2, "Testing vertical pand3 gate 4x") + a = pand3.pand3(name="pand3x4", size=4, vertical=True) + self.local_check(a) + globals.end_openram() # instantiate a copdsay of the class to actually run the test diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py index f939738a..77a6932c 100755 --- a/compiler/tests/04_pnand2_test.py +++ b/compiler/tests/04_pnand2_test.py @@ -25,6 +25,11 @@ class pnand2_test(openram_test): tx = factory.create(module_type="pnand2", size=1) self.local_check(tx) + debug.info(2, "Checking 2-input nand gate") + tx = factory.create(module_type="pnand2", size=1, add_wells=False) + # Only DRC because well contacts will fail LVS + self.local_drc_check(tx) + globals.end_openram() diff --git a/compiler/tests/04_pnand3_test.py b/compiler/tests/04_pnand3_test.py index f1af19ac..82bf1846 100755 --- a/compiler/tests/04_pnand3_test.py +++ b/compiler/tests/04_pnand3_test.py @@ -25,6 +25,11 @@ class pnand3_test(openram_test): tx = factory.create(module_type="pnand3", size=1) self.local_check(tx) + debug.info(2, "Checking 3-input nand gate") + tx = factory.create(module_type="pnand3", size=1, add_wells=False) + # Only DRC because well contacts will fail LVS + self.local_drc_check(tx) + globals.end_openram() diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py index ea0d6dbc..1e8abaa4 100755 --- a/compiler/tests/04_pnor2_test.py +++ b/compiler/tests/04_pnor2_test.py @@ -25,6 +25,10 @@ class pnor2_test(openram_test): tx = factory.create(module_type="pnor2", size=1) self.local_check(tx) + debug.info(2, "Checking 2-input nor gate") + tx = factory.create(module_type="pnor2", size=1, add_wells=False) + self.local_drc_check(tx) + globals.end_openram() # run the test from the command line From 848241a3ad501ef78df9ac05a99e3ba4ef411d67 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 11 May 2020 16:22:17 -0700 Subject: [PATCH 013/206] PEP8 cleanup --- compiler/base/geometry.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index cfcfa73a..96da3d99 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -66,14 +66,17 @@ class geometry: self.compute_boundary(self.offset, self.mirror, self.rotate) def compute_boundary(self, offset=vector(0, 0), mirror="", rotate=0): - """ Transform with offset, mirror and rotation to get the absolute pin location. - We must then re-find the ll and ur. The master is the cell instance. """ + """ + Transform with offset, mirror and rotation to get the absolute pin location. + We must then re-find the ll and ur. The master is the cell instance. + """ if OPTS.netlist_only: - self.boundary = [vector(0,0), vector(0,0)] + self.boundary = [vector(0, 0), vector(0, 0)] return (ll, ur) = [vector(0, 0), vector(self.width, self.height)] + # Mirroring is performed before rotation if mirror == "MX": ll = ll.scale(1, -1) ur = ur.scale(1, -1) @@ -195,14 +198,13 @@ class instance(geometry): blockages = [] blockages = self.mod.gds.getBlockages(lpp) for b in blockages: - new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) + new_blockages.append(self.transform_coords(b, self.offset, mirr, angle)) else: blockages = self.mod.get_blockages(lpp) for b in blockages: - new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) + new_blockages.append(self.transform_coords(b, self.offset, mirr, angle)) return new_blockages - def gds_write_file(self, new_layout): """Recursively writes all the sub-modules in this instance""" debug.info(4, "writing instance: " + self.name) @@ -225,26 +227,25 @@ class instance(geometry): self.update_boundary() debug.info(3, "placing instance {}".format(self)) - - def get_pin(self,name,index=-1): + def get_pin(self, name, index=-1): """ Return an absolute pin that is offset and transformed based on this instance location. Index will return one of several pins.""" import copy if index == -1: pin = copy.deepcopy(self.mod.get_pin(name)) - pin.transform(self.offset,self.mirror,self.rotate) + pin.transform(self.offset, self.mirror, self.rotate) return pin else: pins = copy.deepcopy(self.mod.get_pin(name)) - pin.transform(self.offset,self.mirror,self.rotate) + pins.transform(self.offset, self.mirror, self.rotate) return pin[index] def get_num_pins(self, name): """ Return the number of pins of a given name """ return len(self.mod.get_pins(name)) - def get_pins(self,name): + def get_pins(self, name): """ Return an absolute pin that is offset and transformed based on this instance location. """ @@ -253,7 +254,7 @@ class instance(geometry): new_pins = [] for p in pin: - p.transform(self.offset,self.mirror,self.rotate) + p.transform(self.offset, self.mirror, self.rotate) new_pins.append(p) return new_pins @@ -265,6 +266,7 @@ class instance(geometry): """ override print function output """ return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")" + class path(geometry): """Represents a Path""" @@ -322,7 +324,7 @@ class label(geometry): self.size = 0 - debug.info(4,"creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset)) + debug.info(4, "creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset)) def gds_write_file(self, new_layout): """Writes the text label to GDS""" @@ -340,7 +342,7 @@ class label(geometry): def __str__(self): """ override print function output """ - return "label: " + self.text + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose) + return "label: " + self.text + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose) def __repr__(self): """ override print function output """ From 617bf302d18f1863da212073d8e80d71097d7dae Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 13 May 2020 14:46:42 -0700 Subject: [PATCH 014/206] Add option to remove wells. Save area in pgates with redundant wells. --- compiler/pgates/pand2.py | 13 ++++++++----- compiler/pgates/pand3.py | 15 ++++++++++----- compiler/pgates/pdriver.py | 8 ++++---- compiler/pgates/pgate.py | 17 +++++++++-------- compiler/pgates/pinv.py | 4 ++-- compiler/pgates/pnand2.py | 1 - compiler/pgates/single_level_column_mux.py | 4 ++-- compiler/tests/04_pnor2_test.py | 4 ---- .../04_single_level_column_mux_pbitcell_test.py | 0 compiler/tests/05_replica_bitcell_array_test.py | 0 ...ngle_level_column_mux_array_pbitcell_test.py | 0 .../tests/08_wordline_driver_pbitcell_test.py | 0 .../tests/09_sense_amp_array_test_pbitcell.py | 0 .../10_write_driver_array_pbitcell_test.py | 0 ...10_write_driver_array_wmask_pbitcell_test.py | 0 .../10_write_mask_and_array_pbitcell_test.py | 0 compiler/tests/19_bank_select_pbitcell_test.py | 0 17 files changed, 35 insertions(+), 31 deletions(-) mode change 100644 => 100755 compiler/tests/04_single_level_column_mux_pbitcell_test.py mode change 100644 => 100755 compiler/tests/05_replica_bitcell_array_test.py mode change 100644 => 100755 compiler/tests/07_single_level_column_mux_array_pbitcell_test.py mode change 100644 => 100755 compiler/tests/08_wordline_driver_pbitcell_test.py mode change 100644 => 100755 compiler/tests/09_sense_amp_array_test_pbitcell.py mode change 100644 => 100755 compiler/tests/10_write_driver_array_pbitcell_test.py mode change 100644 => 100755 compiler/tests/10_write_driver_array_wmask_pbitcell_test.py mode change 100644 => 100755 compiler/tests/10_write_mask_and_array_pbitcell_test.py mode change 100644 => 100755 compiler/tests/19_bank_select_pbitcell_test.py diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 6df877fd..dfbdc3e7 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -15,14 +15,14 @@ class pand2(pgate.pgate): """ This is a simple buffer used for driving loads. """ - def __init__(self, name, size=1, height=None, vertical=False): + def __init__(self, name, size=1, height=None, vertical=False, add_wells=True): debug.info(1, "Creating pand2 {}".format(name)) self.add_comment("size: {}".format(size)) self.vertical = vertical self.size = size - pgate.pgate.__init__(self, name) + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): self.add_pins() @@ -31,12 +31,15 @@ class pand2(pgate.pgate): def create_modules(self): self.nand = factory.create(module_type="pnand2", - height=self.height) - self.add_mod(self.nand) + height=self.height, + add_wells=False) self.inv = factory.create(module_type="pdriver", size_list=[self.size], - height=self.height) + height=self.height, + add_wells=self.add_wells) + + self.add_mod(self.nand) self.add_mod(self.inv) def create_layout(self): diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index 5e2e83f8..d7f0b00e 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -15,7 +15,7 @@ class pand3(pgate.pgate): """ This is a simple buffer used for driving loads. """ - def __init__(self, name, size=1, height=None, vertical=False): + def __init__(self, name, size=1, height=None, vertical=False, add_wells=True): debug.info(1, "Creating pand3 {}".format(name)) self.add_comment("size: {}".format(size)) @@ -23,7 +23,7 @@ class pand3(pgate.pgate): self.size = size # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): self.add_pins() @@ -33,12 +33,17 @@ class pand3(pgate.pgate): def create_modules(self): # Shield the cap, but have at least a stage effort of 4 self.nand = factory.create(module_type="pnand3", - height=self.height) - self.add_mod(self.nand) + height=self.height, + add_wells=False) + # Add the well tap to the inverter because when stacked + # vertically it is sometimes narrower self.inv = factory.create(module_type="pdriver", size_list=[self.size], - height=self.height) + height=self.height, + add_wells=self.add_wells) + + self.add_mod(self.nand) self.add_mod(self.inv) def create_layout(self): diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index b36d3e95..266400c9 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -17,7 +17,7 @@ class pdriver(pgate.pgate): sized for driving a load. """ - def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None): + def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None, add_wells=True): debug.info(1, "creating pdriver {}".format(name)) @@ -35,7 +35,7 @@ class pdriver(pgate.pgate): debug.error("Cannot specify both size_list and neg_polarity.", -1) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def compute_sizes(self): # size_list specified @@ -73,9 +73,9 @@ class pdriver(pgate.pgate): self.place_modules() self.route_wires() self.add_layout_pins() - self.width = self.inv_inst_list[-1].rx() self.height = self.inv_inst_list[0].height + self.extend_wells() self.route_supply_rails() self.add_boundary() @@ -87,7 +87,7 @@ class pdriver(pgate.pgate): def add_modules(self): self.inv_list = [] - add_well = True + add_well = self.add_wells for size in self.size_list: temp_inv = factory.create(module_type="pinv", size=size, diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index be317f48..cdb89fcb 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -162,12 +162,12 @@ class pgate(design.design): nwell_height = nwell_max_offset - self.nwell_y_offset self.add_rect(layer="nwell", offset=nwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=nwell_height) if "vtg" in layer: self.add_rect(layer="vtg", offset=nwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=nwell_height) # Start this half a rail width below the cell @@ -178,12 +178,12 @@ class pgate(design.design): pwell_height = self.nwell_y_offset - pwell_position.y self.add_rect(layer="pwell", offset=pwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=pwell_height) if "vtg" in layer: self.add_rect(layer="vtg", offset=pwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=pwell_height) def add_nwell_contact(self, pmos, pmos_pos): @@ -304,16 +304,17 @@ class pgate(design.design): def determine_width(self): """ Determine the width based on the well contacts (assumed to be on the right side) """ + # It was already set or is left as default (minimum) # Width is determined by well contact and spacing and allowing a supply via between each cell if self.add_wells: - self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width + width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width # Height is an input parameter, so it is not recomputed. else: max_active_xoffset = self.find_highest_layer_coords("active").x max_route_xoffset = self.find_highest_layer_coords(self.route_layer).x + 0.5 * self.m1_space - self.width = max(max_active_xoffset, max_route_xoffset) - - self.well_width = self.width + 2 * self.nwell_enclose_active + width = max(max_active_xoffset, max_route_xoffset) + + self.width = width @staticmethod def bin_width(tx_type, target_width): diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 214e66dc..465f7a58 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -60,14 +60,14 @@ class pinv(pgate.pgate): self.add_well_contacts() self.determine_width() self.extend_wells() - self.route_supply_rails() - self.connect_rails() self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", position="farleft") self.route_outputs() + self.route_supply_rails() + self.connect_rails() self.add_boundary() def add_pins(self): diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 59995133..51581e61 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -5,7 +5,6 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import contact import pgate import debug from tech import drc, parameter, spice diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 9e38287f..e1d19534 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -50,7 +50,7 @@ class single_level_column_mux(pgate.pgate): self.connect_poly() self.add_bitline_pins() self.connect_bitlines() - self.add_wells() + self.add_pn_wells() def add_modules(self): self.bitcell = factory.create(module_type="bitcell") @@ -218,7 +218,7 @@ class single_level_column_mux(pgate.pgate): self.add_path(self.col_mux_stack[2], [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()]) - def add_wells(self): + def add_pn_wells(self): """ Add a well and implant over the whole cell. Also, add the pwell contact (if it exists) diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py index 1e8abaa4..ea0d6dbc 100755 --- a/compiler/tests/04_pnor2_test.py +++ b/compiler/tests/04_pnor2_test.py @@ -25,10 +25,6 @@ class pnor2_test(openram_test): tx = factory.create(module_type="pnor2", size=1) self.local_check(tx) - debug.info(2, "Checking 2-input nor gate") - tx = factory.create(module_type="pnor2", size=1, add_wells=False) - self.local_drc_check(tx) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/04_single_level_column_mux_pbitcell_test.py b/compiler/tests/04_single_level_column_mux_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/05_replica_bitcell_array_test.py b/compiler/tests/05_replica_bitcell_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/08_wordline_driver_pbitcell_test.py b/compiler/tests/08_wordline_driver_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/09_sense_amp_array_test_pbitcell.py b/compiler/tests/09_sense_amp_array_test_pbitcell.py old mode 100644 new mode 100755 diff --git a/compiler/tests/10_write_driver_array_pbitcell_test.py b/compiler/tests/10_write_driver_array_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/10_write_mask_and_array_pbitcell_test.py b/compiler/tests/10_write_mask_and_array_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/19_bank_select_pbitcell_test.py b/compiler/tests/19_bank_select_pbitcell_test.py old mode 100644 new mode 100755 From f8bcc543385ff8da82cf2d04c477714bcb8fc85e Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 13 May 2020 16:04:38 -0700 Subject: [PATCH 015/206] Determine width after routing with no well contacts. --- compiler/pgates/pnand3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 446b8212..85b3f0b6 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -65,12 +65,12 @@ class pnand3(pgate.pgate): self.place_ptx() if self.add_wells: self.add_well_contacts() + self.route_inputs() + self.route_output() self.determine_width() self.route_supply_rails() self.connect_rails() self.extend_wells() - self.route_inputs() - self.route_output() self.add_boundary() def add_ptx(self): From 4b526f0d5fd970a87628aadf85183045ab06cbe0 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 13 May 2020 16:54:26 -0700 Subject: [PATCH 016/206] Check min size inverter. --- compiler/modules/write_mask_and_array.py | 2 +- compiler/pgates/pinv.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 4d4e0c50..e660d519 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -60,7 +60,7 @@ class write_mask_and_array(design.design): # Size the AND gate for the number of write drivers it drives, which is equal to the write size. # Assume stage effort of 3 to compute the size self.and2 = factory.create(module_type="pand2", - size=self.write_size / 4.0) + size=max(self.write_size / 4.0, 1)) self.add_mod(self.and2) def create_and2_array(self): diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 465f7a58..5e1aab7b 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -40,6 +40,7 @@ class pinv(pgate.pgate): self.add_comment("size: {}".format(size)) self.size = size + debug.check(self.size >= 1, "Must have a size greater than or equal to 1.") self.nmos_size = size self.pmos_size = beta * size self.beta = beta From a305d788d7635b4536ea43e0b2486660a59e5682 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 13 May 2020 16:54:35 -0700 Subject: [PATCH 017/206] Vertical gates need both well contacts. --- compiler/pgates/pand2.py | 2 +- compiler/pgates/pand3.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index dfbdc3e7..41189876 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -32,7 +32,7 @@ class pand2(pgate.pgate): def create_modules(self): self.nand = factory.create(module_type="pnand2", height=self.height, - add_wells=False) + add_wells=self.vertical) self.inv = factory.create(module_type="pdriver", size_list=[self.size], diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index d7f0b00e..92429921 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -34,7 +34,7 @@ class pand3(pgate.pgate): # Shield the cap, but have at least a stage effort of 4 self.nand = factory.create(module_type="pnand3", height=self.height, - add_wells=False) + add_wells=self.vertical) # Add the well tap to the inverter because when stacked # vertically it is sometimes narrower From 8bd1052fc2777fdcb228e55bd47b87ace6912b56 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Thu, 14 May 2020 10:30:29 +0000 Subject: [PATCH 018/206] Spare columns in full sram layout --- compiler/modules/control_logic.py | 9 ++- compiler/sram/sram_1bank.py | 14 +++-- compiler/sram/sram_base.py | 22 +++++-- ..._sram_1bank_2mux_1rw_1r_spare_cols_test.py | 58 ++++++++++++++++++ ...0_sram_1bank_2mux_1w_1r_spare_cols_test.py | 59 +++++++++++++++++++ ...sram_1bank_nomux_1rw_1r_spare_cols_test.py | 59 +++++++++++++++++++ 6 files changed, 208 insertions(+), 13 deletions(-) create mode 100755 compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py create mode 100755 compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py create mode 100755 compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 078dc3ae..c8805049 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -20,7 +20,7 @@ class control_logic(design.design): Dynamically generated Control logic for the total SRAM circuit. """ - def __init__(self, num_rows, words_per_row, word_size, sram=None, port_type="rw", name=""): + def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""): """ Constructor """ name = "control_logic_" + port_type design.design.__init__(self, name) @@ -35,7 +35,12 @@ class control_logic(design.design): self.word_size = word_size self.port_type = port_type - self.num_cols = word_size * words_per_row + if not spare_columns: + self.num_spare_cols = spare_columns + else: + self.num_spare_cols = 0 + + self.num_cols = word_size * words_per_row + self.num_spare_cols self.num_words = num_rows * words_per_row self.enable_delay_chain_resizing = False diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 8dbcfaf4..ac4b7e33 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -71,7 +71,7 @@ class sram_1bank(sram_base): self.wmask_bus_size = self.m2_nonpref_pitch * (max(self.num_wmasks + 1, self.col_addr_size + 1)) + self.wmask_bus_gap else: self.data_bus_gap = self.m3_nonpref_pitch * 2 - self.data_bus_size = self.m3_nonpref_pitch * (max(self.word_size + 1, self.col_addr_size + 1)) + self.data_bus_gap + self.data_bus_size = self.m3_nonpref_pitch * (max(self.word_size + self.num_spare_cols + 1, self.col_addr_size + 1)) + self.data_bus_gap self.col_addr_bus_gap = self.m2_nonpref_pitch * 2 self.col_addr_bus_size = self.m2_nonpref_pitch * (self.col_addr_size) + self.col_addr_bus_gap @@ -191,7 +191,7 @@ class sram_1bank(sram_base): signal + "{}".format(port)) if port in self.read_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port, bit), "dout{0}[{1}]".format(port, bit)) @@ -208,7 +208,7 @@ class sram_1bank(sram_base): "addr{0}[{1}]".format(port, bit + self.col_addr_size)) if port in self.write_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "din{0}[{1}]".format(port, bit)) @@ -218,6 +218,10 @@ class sram_1bank(sram_base): self.copy_layout_pin(self.wmask_dff_insts[port], "din_{}".format(bit), "wmask{0}[{1}]".format(port, bit)) + for bit in range(self.num_spare_cols): + self.copy_layout_pin(self.bank_inst, + "spare_wen{0}_{1}".format(port, bit), + "spare_wen{0}[{1}]".format(port, bit)) def route_layout(self): """ Route a single bank SRAM """ @@ -380,7 +384,7 @@ class sram_1bank(sram_base): else: offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap) - dff_names = ["dout_{}".format(x) for x in range(self.word_size)] + dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)] dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] if self.write_size: for x in dff_names: @@ -393,7 +397,7 @@ class sram_1bank(sram_base): to_layer="m4", offset=pin_offset) - bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size)] + bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] if self.write_size: for x in bank_names: diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index bcb85308..0ca569af 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -37,6 +37,9 @@ class sram_base(design, verilog, lef): else: self.num_wmasks = 0 + if not self.num_spare_cols: + self.num_spare_cols = 0 + # For logical effort delay calculations. self.all_mods_except_control_done = False @@ -44,7 +47,7 @@ class sram_base(design, verilog, lef): """ Add pins for entire SRAM. """ for port in self.write_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): self.add_pin("din{0}[{1}]".format(port, bit), "INPUT") for port in self.all_ports: @@ -75,8 +78,10 @@ class sram_base(design, verilog, lef): for port in self.write_ports: for bit in range(self.num_wmasks): self.add_pin("wmask{0}[{1}]".format(port, bit), "INPUT") + for bit in range(self.num_spare_cols): + self.add_pin("spare_wen{0}[{1}]".format(port, bit), "INPUT") for port in self.read_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT") self.add_pin("vdd", "POWER") @@ -274,7 +279,7 @@ class sram_base(design, verilog, lef): else: self.col_addr_dff = None - self.data_dff = factory.create("dff_array", module_name="data_dff", rows=1, columns=self.word_size) + self.data_dff = factory.create("dff_array", module_name="data_dff", rows=1, columns=self.word_size + self.num_spare_cols) self.add_mod(self.data_dff) if self.write_size: @@ -303,6 +308,7 @@ class sram_base(design, verilog, lef): self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, + spare_columns=self.num_spare_cols, sram=self, port_type="rw") self.add_mod(self.control_logic_rw) @@ -310,6 +316,7 @@ class sram_base(design, verilog, lef): self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, + spare_columns=self.num_spare_cols, sram=self, port_type="w") self.add_mod(self.control_logic_w) @@ -317,6 +324,7 @@ class sram_base(design, verilog, lef): self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, + spare_columns=self.num_spare_cols, sram=self, port_type="r") self.add_mod(self.control_logic_r) @@ -328,12 +336,12 @@ class sram_base(design, verilog, lef): temp = [] for port in self.read_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): temp.append("dout{0}[{1}]".format(port, bit)) for port in self.all_ports: temp.append("rbl_bl{0}".format(port)) for port in self.write_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): temp.append("bank_din{0}[{1}]".format(port, bit)) for port in self.all_ports: for bit in range(self.bank_addr_size): @@ -349,6 +357,8 @@ class sram_base(design, verilog, lef): temp.append("w_en{0}".format(port)) for bit in range(self.num_wmasks): temp.append("bank_wmask{}[{}]".format(port, bit)) + for bit in range(self.num_spare_cols): + temp.append("spare_wen{0}[{1}]".format(port, bit)) for port in self.all_ports: temp.append("wl_en{0}".format(port)) temp.extend(["vdd", "gnd"]) @@ -436,7 +446,7 @@ class sram_base(design, verilog, lef): # inputs, outputs/output/bar inputs = [] outputs = [] - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): inputs.append("din{}[{}]".format(port, bit)) outputs.append("bank_din{}[{}]".format(port, bit)) diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py new file mode 100755 index 00000000..d087a47b --- /dev/null +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class sram_1bank_2mux_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + c = sram_config(word_size=4, + num_words=32, + num_spare_cols=3, + num_banks=1) + + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py new file mode 100755 index 00000000..349f5374 --- /dev/null +++ b/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error") +class psram_1bank_2mux_1w_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1w_1r" + OPTS.replica_bitcell="replica_bitcell_1w_1r" + OPTS.dummy_bitcell="dummy_bitcell_1w_1r" + OPTS.num_rw_ports = 0 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + + c = sram_config(word_size=4, + num_words=32, + num_spare_cols=3, + num_banks=1) + c.num_words=32 + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py new file mode 100755 index 00000000..d2df2aef --- /dev/null +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class sram_1bank_nomux_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_bitcell_1rw_1r" + OPTS.dummy_bitcell = "dummy_bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=4, + num_banks=1) + + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} spare columns, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_spare_cols, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From c14190c5aa20b9bf6f4a743a385949e41d149396 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Thu, 14 May 2020 10:41:54 +0000 Subject: [PATCH 019/206] Changes in control logic for spare columns --- compiler/modules/control_logic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index c8805049..e09a6615 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -36,9 +36,9 @@ class control_logic(design.design): self.port_type = port_type if not spare_columns: - self.num_spare_cols = spare_columns - else: self.num_spare_cols = 0 + else: + self.num_spare_cols = spare_columns self.num_cols = word_size * words_per_row + self.num_spare_cols self.num_words = num_rows * words_per_row From c7d86b21ae12eea5038618dca5e4e67b2385230a Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Sat, 16 May 2020 10:09:03 +0000 Subject: [PATCH 020/206] Spare cols with wmask enabled --- compiler/modules/port_data.py | 11 +++- compiler/modules/write_driver_array.py | 22 ++++++-- compiler/sram/sram_1bank.py | 2 +- ...rite_driver_array_wmask_spare_cols_test.py | 47 ++++++++++++++++ ...0_sram_1bank_2mux_wmask_spare_cols_test.py | 56 +++++++++++++++++++ ..._sram_1bank_nomux_wmask_spare_cols_test.py | 56 +++++++++++++++++++ 6 files changed, 185 insertions(+), 9 deletions(-) create mode 100755 compiler/tests/10_write_driver_array_wmask_spare_cols_test.py create mode 100755 compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py create mode 100755 compiler/tests/20_sram_1bank_nomux_wmask_spare_cols_test.py diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 9ff99bdd..968b4605 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -386,8 +386,10 @@ class port_data(design.design): if self.write_size is not None: for i in range(self.num_wmasks): temp.append("wdriver_sel_{}".format(i)) + for i in range(self.num_spare_cols): + temp.append("spare_wen{}".format(i)) - elif self.num_spare_cols: + elif self.num_spare_cols and not self.write_size: temp.append("w_en") for i in range(self.num_spare_cols): temp.append("spare_wen{}".format(i)) @@ -690,7 +692,7 @@ class port_data(design.design): else: debug.error("Didn't find precharge array.") - # Copy layout pins of spare columns + # Copy bitlines of spare columns for bit in range(self.num_spare_cols): if self.precharge_array_inst: self.copy_layout_pin(self.precharge_array_inst, @@ -718,7 +720,10 @@ class port_data(design.design): for bit in range(self.num_wmasks): # Add write driver's en_{} pins self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit), "wdriver_sel_{}".format(bit)) - elif self.num_spare_cols: + for bit in range(self.num_spare_cols): + # Add spare columns' en_{} pins + self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + self.num_wmasks), "spare_wen{}".format(bit)) + elif self.num_spare_cols and not self.write_mask_and_array_inst: self.copy_layout_pin(self.write_driver_array_inst, "en_0", "w_en") for bit in range(self.num_spare_cols): self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + 1), "spare_wen{}".format(bit)) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 640db97e..48564e86 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -85,9 +85,9 @@ class write_driver_array(design.design): self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT") self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT") if self.write_size: - for i in range(self.num_wmasks): + for i in range(self.num_wmasks + self.num_spare_cols): self.add_pin(self.en_name + "_{0}".format(i), "INPUT") - elif self.num_spare_cols: + elif self.num_spare_cols and not self.write_size: for i in range(self.num_spare_cols + 1): self.add_pin(self.en_name + "_{0}".format(i), "INPUT") else: @@ -124,7 +124,7 @@ class write_driver_array(design.design): w = 0 windex+=1 - elif self.num_spare_cols: + elif self.num_spare_cols and not self.write_size: self.connect_inst([self.data_name + "_{0}".format(index), self.get_bl_name() + "_{0}".format(index), self.get_br_name() + "_{0}".format(index), @@ -138,6 +138,10 @@ class write_driver_array(design.design): for i in range(self.num_spare_cols): index = self.word_size + i + if self.write_size: + offset = self.num_wmasks + else: + offset = 1 name = "write_driver{}".format(index) self.driver_insts[index]=self.add_inst(name=name, mod=self.driver) @@ -145,7 +149,7 @@ class write_driver_array(design.design): self.connect_inst([self.data_name + "_{0}".format(index), self.get_bl_name() + "_{0}".format(index), self.get_br_name() + "_{0}".format(index), - self.en_name + "_{0}".format(i + 1), "vdd", "gnd"]) + self.en_name + "_{0}".format(i + offset), "vdd", "gnd"]) def place_write_array(self): @@ -227,8 +231,16 @@ class write_driver_array(design.design): offset=en_pin.ll(), width=wmask_en_len - en_gap, height=en_pin.height()) + + for i in range(self.num_spare_cols): + inst = self.driver_insts[self.word_size + i] + self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks), + layer="m1", + offset=inst.get_pin(inst.mod.en_name).ll(), + width=self.single_col_width - inst.get_pin(inst.mod.en_name).width()) + - elif self.num_spare_cols: + elif self.num_spare_cols and not self.write_size: # shorten enable rail to accomodate those for spare write drivers inst = self.driver_insts[0] self.add_layout_pin(text=self.en_name + "_{0}".format(0), diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index ac4b7e33..4af19f98 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -66,7 +66,7 @@ class sram_1bank(sram_base): # So, m3 non-pref pitch means that this is routed on the m2 layer. if self.write_size: self.data_bus_gap = self.m4_nonpref_pitch * 2 - self.data_bus_size = self.m4_nonpref_pitch * (self.word_size) + self.data_bus_gap + self.data_bus_size = self.m4_nonpref_pitch * (self.word_size + self.num_spare_cols) + self.data_bus_gap self.wmask_bus_gap = self.m2_nonpref_pitch * 2 self.wmask_bus_size = self.m2_nonpref_pitch * (max(self.num_wmasks + 1, self.col_addr_size + 1)) + self.wmask_bus_gap else: diff --git a/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py b/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py new file mode 100755 index 00000000..be85665e --- /dev/null +++ b/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class write_driver_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check write driver array for single port + debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4") + a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4, num_spare_cols=3) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=16, write_size=2") + a = factory.create(module_type="write_driver_array", columns=16, word_size=16, write_size=2, num_spare_cols=2) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=8, write_size=4") + a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py new file mode 100755 index 00000000..0e9c74c3 --- /dev/null +++ b/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +# @unittest.skip("SKIPPING 20_sram_1bank_2mux_wmask_test") +class sram_1bank_2mux_wmask_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + c = sram_config(word_size=8, + write_size=4, + num_spare_cols=3, + num_words=64, + num_banks=1) + + c.words_per_row = 2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} bit writes, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.write_size, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_nomux_wmask_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_wmask_spare_cols_test.py new file mode 100755 index 00000000..dba96ca2 --- /dev/null +++ b/compiler/tests/20_sram_1bank_nomux_wmask_spare_cols_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +# @unittest.skip("SKIPPING 20_sram_1bank_nomux_wmask_test") +class sram_1bank_nomux_wmask_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + c = sram_config(word_size=8, + write_size=4, + num_spare_cols=3, + num_words=16, + num_banks=1) + + c.words_per_row = 1 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} bit writes, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.write_size, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 7505fa5aef116220975e3670091e927cdd93ca37 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 27 May 2020 20:03:11 -0700 Subject: [PATCH 021/206] update for end caps --- compiler/bitcells/col_cap_bitcell_1rw_1r.py | 43 ++++++ compiler/bitcells/row_cap_bitcell_1rw_1r.py | 43 ++++++ compiler/modules/bitcell_base_array.py | 15 +- compiler/modules/col_cap_array.py | 103 +++++++++++++ compiler/modules/replica_bitcell_array.py | 138 ++++++++++-------- compiler/modules/replica_column.py | 84 ++++++++--- compiler/modules/row_cap_array.py | 128 ++++++++++++++++ compiler/options.py | 21 +-- .../14_replica_bitcell_1rw_1r_array_test.py | 12 +- 9 files changed, 486 insertions(+), 101 deletions(-) create mode 100644 compiler/bitcells/col_cap_bitcell_1rw_1r.py create mode 100644 compiler/bitcells/row_cap_bitcell_1rw_1r.py create mode 100644 compiler/modules/col_cap_array.py create mode 100644 compiler/modules/row_cap_array.py diff --git a/compiler/bitcells/col_cap_bitcell_1rw_1r.py b/compiler/bitcells/col_cap_bitcell_1rw_1r.py new file mode 100644 index 00000000..315ad23f --- /dev/null +++ b/compiler/bitcells/col_cap_bitcell_1rw_1r.py @@ -0,0 +1,43 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +import utils +from tech import GDS, layer +from tech import cell_properties as props +import bitcell_base + + +class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base): + """ + todo""" + + pin_names = [props.bitcell.cell_1rw1r.pin.bl0, + props.bitcell.cell_1rw1r.pin.br0, + props.bitcell.cell_1rw1r.pin.bl1, + props.bitcell.cell_1rw1r.pin.br1, + props.bitcell.cell_1rw1r.pin.vdd] + + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", + "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("col_cap_cell_1rw_1r", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, + "col_cap_cell_1rw_1r", + GDS["unit"]) + + def __init__(self, name=""): + # Ignore the name argument + bitcell_base.bitcell_base.__init__(self, "col_cap_cell_1rw_1r") + debug.info(2, "Create col_cap bitcell 1rw+1r object") + + self.width = col_cap_bitcell_1rw_1r.width + self.height = col_cap_bitcell_1rw_1r.height + self.pin_map = col_cap_bitcell_1rw_1r.pin_map + self.add_pin_types(self.type_list) diff --git a/compiler/bitcells/row_cap_bitcell_1rw_1r.py b/compiler/bitcells/row_cap_bitcell_1rw_1r.py new file mode 100644 index 00000000..b50629f0 --- /dev/null +++ b/compiler/bitcells/row_cap_bitcell_1rw_1r.py @@ -0,0 +1,43 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +import utils +from tech import GDS, layer +from tech import cell_properties as props +import bitcell_base + + +class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base): + """ + A single bit cell which is forced to store a 0. + This module implements the single memory cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. """ + + pin_names = [props.bitcell.cell_1rw1r.pin.wl0, + props.bitcell.cell_1rw1r.pin.wl1, + props.bitcell.cell_1rw1r.pin.gnd] + + type_list = ["INPUT", "INPUT", "GROUND"] + + (width, height) = utils.get_libcell_size("row_cap_cell_1rw_1r", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, + "row_cap_cell_1rw_1r", + GDS["unit"]) + + def __init__(self, name=""): + # Ignore the name argument + bitcell_base.bitcell_base.__init__(self, "row_cap_cell_1rw_1r") + debug.info(2, "Create row_cap bitcell 1rw+1r object") + + self.width = row_cap_bitcell_1rw_1r.width + self.height = row_cap_bitcell_1rw_1r.height + self.pin_map = row_cap_bitcell_1rw_1r.pin_map + self.add_pin_types(self.type_list) diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index a8829fc3..9b46a192 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -108,16 +108,23 @@ class bitcell_base_array(design.design): except AttributeError: bitcell_power_pin_directions = None + # For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps. + try: + bitcell_no_vdd_pin = cell_properties.bitcell.no_vdd_via + except AttributeError: + bitcell_no_vdd_pin = False + # Add vdd/gnd via stacks for row in range(self.row_size): for col in range(self.column_size): inst = self.cell_inst[row,col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): - self.add_power_pin(name=pin_name, - loc=pin.center(), - directions=bitcell_power_pin_directions, - start_layer=pin.layer) + if not (pin_name == "vdd" and bitcell_no_vdd_pin): + self.add_power_pin(name=pin_name, + loc=pin.center(), + directions=bitcell_power_pin_directions, + start_layer=pin.layer) def _adjust_x_offset(self, xoffset, col, col_offset): tempx = xoffset diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py new file mode 100644 index 00000000..3119f4e5 --- /dev/null +++ b/compiler/modules/col_cap_array.py @@ -0,0 +1,103 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +from bitcell_base_array import bitcell_base_array +from sram_factory import factory +from globals import OPTS +from tech import cell_properties + +class col_cap_array(bitcell_base_array): + """ + Generate a dummy row/column for the replica array. + """ + def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): + super().__init__(cols, rows, name, column_offset) + self.mirror = mirror + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + """ Create and connect the netlist """ + self.add_modules() + self.add_pins() + self.create_instances() + + def create_layout(self): + + self.place_array("dummy_r{0}_c{1}", self.mirror) + self.add_layout_pins() + self.add_boundary() + self.DRC_LVS() + + def add_modules(self): + """ Add the modules used in this design """ + # self.dummy_cell = factory.create(module_type="col_cap_bitcell_1rw_1r") # TODO: make module_type generic + self.dummy_cell = factory.create(module_type="col_cap_bitcell") + self.add_mod(self.dummy_cell) + + self.cell = factory.create(module_type="bitcell") + + def create_instances(self): + """ Create the module instances used in this design """ + self.cell_inst = {} + for col in range(self.column_size): + for row in range(self.row_size): + name = "bit_r{0}_c{1}".format(row, col) + self.cell_inst[row,col]=self.add_inst(name=name, + mod=self.dummy_cell) + self.connect_inst(self.get_bitcell_pins(col, row)) + + def get_bitcell_pins(self, col, row): + """ + Creates a list of connections in the bitcell, + indexed by column and row, for instance use in bitcell_array + """ + + pin_name = cell_properties.bitcell.cell_1rw1r.pin + bitcell_pins = ["{0}_{1}".format(pin_name.bl0, col), + "{0}_{1}".format(pin_name.br0, col), + "{0}_{1}".format(pin_name.bl1, col), + "{0}_{1}".format(pin_name.br1, col), + "vdd"] + + return bitcell_pins + + def add_layout_pins(self): + """ Add the layout pins """ + + column_list = self.cell.get_all_bitline_names() + + for col in range(self.column_size): + for cell_column in column_list: + bl_pin = self.cell_inst[0,col].get_pin(cell_column) + self.add_layout_pin(text=cell_column+"_{0}".format(col), + layer=bl_pin.layer, + offset=bl_pin.ll().scale(1,0), + width=bl_pin.width(), + height=self.height) + + # Add vdd/gnd via stacks + for row in range(self.row_size): + for col in range(self.column_size): + inst = self.cell_inst[row,col] + for pin_name in ["vdd", "gnd"]: + for pin in inst.get_pins(pin_name): + self.add_power_pin(name=pin.name, + loc=pin.center(), + start_layer=pin.layer) + + + # def input_load(self): + # wl_wire = self.gen_wl_wire() + # return wl_wire.return_input_cap() + # + # def get_wordline_cin(self): + # """Get the relative input capacitance from the wordline connections in all the bitcell""" + # #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns + # bitcell_wl_cin = self.cell.get_wl_cin() + # total_cin = bitcell_wl_cin * self.column_size + # return total_cin diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index c88dbe6d..ff91f7fd 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -1,12 +1,12 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California +# Copyright (c) 2016-2019 Regents of the University of California # All rights reserved. # import debug import design -from tech import drc, spice +from tech import drc, spice, cell_properties from vector import vector from globals import OPTS from sram_factory import factory @@ -34,11 +34,11 @@ class replica_bitcell_array(design.design): debug.check(left_rbl+right_rbl==len(self.all_ports),"Invalid number of RBLs for port configuration.") debug.check(left_rbl+right_rbl==len(self.bitcell_ports),"Bitcell ports must match total RBLs.") - + # Two dummy rows/cols plus replica for each port self.extra_rows = 2 + left_rbl + right_rbl self.extra_cols = 2 + left_rbl + right_rbl - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -46,8 +46,8 @@ class replica_bitcell_array(design.design): # We don't offset this because we need to align # the replica bitcell in the control logic #self.offset_all_coordinates() - - + + def create_netlist(self): """ Create and connect the netlist """ self.add_modules() @@ -55,15 +55,15 @@ class replica_bitcell_array(design.design): self.create_instances() def add_modules(self): - """ Array and dummy/replica columns + """ Array and dummy/replica columns d or D = dummy cell (caps to distinguish grouping) r or R = replica cell (caps to distinguish grouping) - b or B = bitcell - replica columns 1 + b or B = bitcell + replica columns 1 v v - bdDDDDDDDDDDDDDDdb <- Dummy row - bdDDDDDDDDDDDDDDrb <- Dummy row + bdDDDDDDDDDDDDDDdb <- Dummy row + bdDDDDDDDDDDDDDDrb <- Dummy row br--------------rb br| Array |rb br| row x col |rb @@ -106,7 +106,7 @@ class replica_bitcell_array(design.design): column_offset=column_offset, replica_bit=replica_bit) self.add_mod(self.replica_columns[bit]) - + # Dummy row self.dummy_row = factory.create(module_type="dummy_array", cols=self.column_size, @@ -116,15 +116,35 @@ class replica_bitcell_array(design.design): mirror=0) self.add_mod(self.dummy_row) - # Dummy col (mirror starting at first if odd replica+dummy rows) - self.dummy_col_left = factory.create(module_type="dummy_array", + + # If there are bitcell end caps, replace the dummy cells on the edge of the bitcell array with end caps. + try: + end_caps_enabled = cell_properties.bitcell.end_caps + except AttributeError: + end_caps_enabled = False + + # Dummy Row or Col Cap, depending on bitcell array properties + edge_row_module_type = ("col_cap_array" if end_caps_enabled else "dummy_array") + + self.edge_row = factory.create(module_type=edge_row_module_type, + cols=self.column_size, + rows=1, + # dummy column + left replica column + column_offset=1 + self.left_rbl, + mirror=0) + self.add_mod(self.edge_row) + + # Dummy Col or Row Cap, depending on bitcell array properties + edge_col_module_type = ("row_cap_array" if end_caps_enabled else "dummy_array") + + self.edge_col_left = factory.create(module_type=edge_col_module_type, cols=1, column_offset=0, rows=self.row_size + self.extra_rows, mirror=(self.left_rbl+1)%2) - self.add_mod(self.dummy_col_left) + self.add_mod(self.edge_col_left) - self.dummy_col_right = factory.create(module_type="dummy_array", + self.edge_col_right = factory.create(module_type=edge_col_module_type, cols=1, # dummy column # + left replica column @@ -133,9 +153,7 @@ class replica_bitcell_array(design.design): column_offset=1 + self.left_rbl + self.column_size + self.right_rbl, rows=self.row_size + self.extra_rows, mirror=(self.left_rbl+1)%2) - self.add_mod(self.dummy_col_right) - - + self.add_mod(self.edge_col_right) def add_pins(self): self.bitcell_array_wl_names = self.bitcell_array.get_all_wordline_names() @@ -150,7 +168,7 @@ class replica_bitcell_array(design.design): self.rbl_bl_names = {} self.rbl_br_names = {} self.rbl_wl_names = {} - + # Create the full WL names include dummy, replica, and regular bit cells self.replica_col_wl_names = [] self.replica_col_wl_names.extend(["{0}_bot".format(x) for x in self.dummy_cell_wl_names]) @@ -193,7 +211,7 @@ class replica_bitcell_array(design.design): wl_names = ["rbl_{0}_{1}".format(x,port) for x in self.cell.get_all_wl_names()] #wl_names[port] = "rbl_wl{}".format(port) self.replica_wl_names[port] = wl_names - + # External pins self.add_pin_list(self.bitcell_array_bl_names, "INOUT") @@ -204,22 +222,22 @@ class replica_bitcell_array(design.design): self.add_pin(bl_name,"OUTPUT") self.add_pin(br_name,"OUTPUT") self.add_pin_list(self.bitcell_array_wl_names, "INPUT") - # Need to sort by port order since dictionary values may not be in order + # Need to sort by port order since dictionary values may not be in order wl_names = [self.rbl_wl_names[x] for x in sorted(self.rbl_wl_names.keys())] for pin_name in wl_names: self.add_pin(pin_name,"INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - + def create_instances(self): """ Create the module instances used in this design """ supplies = ["vdd", "gnd"] - + # Used for names/dimensions only self.cell = factory.create(module_type="bitcell") - + # Main array self.bitcell_array_inst=self.add_inst(name="bitcell_array", mod=self.bitcell_array) @@ -231,35 +249,33 @@ class replica_bitcell_array(design.design): self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port), mod=self.replica_columns[port]) self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies) - - + + # Dummy rows under the bitcell array (connected with with the replica cell wl) self.dummy_row_replica_inst = {} for port in range(self.left_rbl+self.right_rbl): self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port), mod=self.dummy_row) self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names[port] + supplies) - - - # Top/bottom dummy rows + + + # Top/bottom dummy rows or col caps self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot", - mod=self.dummy_row) + mod=self.edge_row) self.connect_inst(self.dummy_row_bl_names + [x+"_bot" for x in self.dummy_cell_wl_names] + supplies) self.dummy_row_top_inst=self.add_inst(name="dummy_row_top", - mod=self.dummy_row) + mod=self.edge_row) self.connect_inst(self.dummy_row_bl_names + [x+"_top" for x in self.dummy_cell_wl_names] + supplies) # Left/right Dummy columns self.dummy_col_left_inst=self.add_inst(name="dummy_col_left", - mod=self.dummy_col_left) + mod=self.edge_col_left) self.connect_inst([x+"_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) self.dummy_col_right_inst=self.add_inst(name="dummy_col_right", - mod=self.dummy_col_right) + mod=self.edge_col_right) self.connect_inst([x+"_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) - - def create_layout(self): self.height = (self.row_size+self.extra_rows)*self.dummy_row.height @@ -267,7 +283,7 @@ class replica_bitcell_array(design.design): # This is a bitcell x bitcell offset to scale offset = vector(self.cell.width, self.cell.height) - + self.bitcell_array_inst.place(offset=[0,0]) # To the left of the bitcell array @@ -276,7 +292,6 @@ class replica_bitcell_array(design.design): # To the right of the bitcell array for bit in range(self.right_rbl): self.replica_col_inst[self.left_rbl+bit].place(offset=offset.scale(bit,-self.left_rbl-1)+self.bitcell_array_inst.lr()) - # Far top dummy row (first row above array is NOT flipped) flip_dummy = self.right_rbl%2 @@ -298,17 +313,17 @@ class replica_bitcell_array(design.design): for bit in range(self.right_rbl): self.dummy_row_replica_inst[self.left_rbl+bit].place(offset=offset.scale(0,bit+bit%2)+self.bitcell_array_inst.ul(), mirror="MX" if bit%2 else "R0") - + self.translate_all(offset.scale(-1-self.left_rbl,-1-self.left_rbl)) - + self.add_layout_pins() - + self.add_boundary() - + self.DRC_LVS() - + def add_layout_pins(self): """ Add the layout pins """ @@ -341,10 +356,10 @@ class replica_bitcell_array(design.design): for (pin_name,wl_name) in zip(self.cell.get_all_wl_names(),self.replica_wl_names[port]): # +1 for dummy row pin_bit = port+1 - # +row_size if above the array + # +row_size if above the array if port>=self.left_rbl: pin_bit += self.row_size - + pin_name += "_{}".format(pin_bit) pin = inst.get_pin(pin_name) if wl_name in self.rbl_wl_names.values(): @@ -368,20 +383,27 @@ class replica_bitcell_array(design.design): offset=pin.ll().scale(1, 0), width=pin.width(), height=self.height) - + + # For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps. + try: + bitcell_no_vdd_pin = cell_properties.bitcell.no_vdd_via + except AttributeError: + bitcell_no_vdd_pin = False + for pin_name in ["vdd", "gnd"]: for inst in self.insts: pin_list = inst.get_pins(pin_name) for pin in pin_list: - self.add_power_pin(name=pin_name, - loc=pin.center(), - directions=("V", "V"), - start_layer=pin.layer) + if not (pin_name == "vdd" and bitcell_no_vdd_pin): + self.add_power_pin(name=pin_name, + loc=pin.center(), + directions=("V", "V"), + start_layer=pin.layer) def get_rbl_wl_name(self, port): """ Return the WL for the given RBL port """ return self.rbl_wl_names[port] - + def get_rbl_bl_name(self, port): """ Return the BL for the given RBL port """ return self.rbl_bl_names[port] @@ -393,17 +415,17 @@ class replica_bitcell_array(design.design): def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" from tech import drc, parameter - + # Dynamic Power from Bitline bl_wire = self.gen_bl_wire() cell_load = 2 * bl_wire.return_input_cap() bl_swing = OPTS.rbl_delay_percentage freq = spice["default_event_frequency"] bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) - - #Calculate the bitcell power which currently only includes leakage + + #Calculate the bitcell power which currently only includes leakage cell_power = self.cell.analytical_power(corner, load) - + #Leakage power grows with entire array and bitlines. total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, cell_power.leakage * self.column_size * self.row_size) @@ -429,13 +451,13 @@ class replica_bitcell_array(design.design): def graph_exclude_bits(self, targ_row, targ_col): """Excludes bits in column from being added to graph except target""" self.bitcell_array.graph_exclude_bits(targ_row, targ_col) - + def graph_exclude_replica_col_bits(self): """Exclude all replica/dummy cells in the replica columns except the replica bit.""" - + for port in range(self.left_rbl+self.right_rbl): self.replica_columns[port].exclude_all_but_replica() def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" - return self.bitcell_array.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col) + return self.bitcell_array.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index d874ba8c..2e3d207e 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -1,11 +1,11 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California +# Copyright (c) 2016-2019 Regents of the University of California # All rights reserved. # import debug import design -from tech import drc +from tech import drc, cell_properties import contact from sram_factory import factory from vector import vector @@ -16,7 +16,7 @@ class replica_column(design.design): Generate a replica bitline column for the replica array. Rows is the total number of rows i the main array. Left_rbl and right_rbl are the number of left and right replica bitlines. - Replica bit specifies which replica column this is (to determine where to put the + Replica bit specifies which replica column this is (to determine where to put the replica cell. """ @@ -31,15 +31,15 @@ class replica_column(design.design): # left, right, regular rows plus top/bottom dummy cells self.total_size = self.left_rbl+rows+self.right_rbl+2 self.column_offset = column_offset - + debug.check(replica_bit!=0 and replica_bit!=rows,"Replica bit cannot be the dummy row.") debug.check(replica_bit<=left_rbl or replica_bit>=self.total_size-right_rbl-1, - "Replica bit cannot be in the regular array.") + "Replica bit cannot be in the regular array.") self.create_netlist() if not OPTS.netlist_only: self.create_layout() - + def create_netlist(self): self.add_modules() self.add_pins() @@ -47,7 +47,7 @@ class replica_column(design.design): def create_layout(self): self.height = self.total_size*self.cell.height - self.width = self.cell.width + self.width = self.cell.width self.place_instances() self.add_layout_pins() @@ -55,7 +55,7 @@ class replica_column(design.design): self.DRC_LVS() def add_pins(self): - + for bl_name in self.cell.get_all_bitline_names(): # In the replica column, these are only outputs! self.add_pin("{0}_{1}".format(bl_name,0), "OUTPUT") @@ -63,7 +63,7 @@ class replica_column(design.design): for row in range(self.total_size): for wl_name in self.cell.get_all_wl_names(): self.add_pin("{0}_{1}".format(wl_name,row), "INPUT") - + self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -72,27 +72,49 @@ class replica_column(design.design): self.add_mod(self.replica_cell) self.dummy_cell = factory.create(module_type="dummy_bitcell") self.add_mod(self.dummy_cell) + try: + edge_module_type = ("col_cap_bitcell" if cell_properties.bitcell.end_caps else "dummy_bitcell") + except AttributeError: + edge_module_type = "dummy_bitcell" + self.edge_cell = factory.create(module_type=edge_module_type) + self.add_mod(self.edge_cell) # Used for pin names only self.cell = factory.create(module_type="bitcell") - + def create_instances(self): + + try: + end_caps_enabled = cell_properties.bitcell.end_caps + except AttributeError: + end_caps_enabled = False + self.cell_inst = {} for row in range(self.total_size): name="rbc_{0}".format(row) # Top/bottom cell are always dummy cells. # Regular array cells are replica cells (>left_rbl and self.left_rbl and row Date: Thu, 28 May 2020 20:31:21 -0700 Subject: [PATCH 022/206] fix for replica column mirroring over y --- compiler/modules/replica_bitcell_array.py | 6 ++++-- compiler/tests/14_replica_bitcell_1rw_1r_array_test.py | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index ff91f7fd..ff2260d3 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -91,14 +91,16 @@ class replica_bitcell_array(design.design): # Replica bitlines self.replica_columns = {} for bit in range(self.left_rbl+self.right_rbl): + # Creating left_rbl if bit Date: Thu, 14 May 2020 11:20:37 -0700 Subject: [PATCH 023/206] Thin-cell decoder changes. Add hard decoder gates (nand, inv, pnan) Add conditions for routing using LI layer in s8. Generalize bus layers for decoders. Move custom cells to own directory. Fixed via directions, etc. Add 4x16 hierarchical decoder and test case --- .gitlab-ci.yml | 11 +- compiler/base/contact.py | 8 +- compiler/base/geometry.py | 14 +- compiler/base/hierarchy_layout.py | 58 +-- compiler/base/hierarchy_spice.py | 1 + compiler/bitcells/bitcell.py | 2 +- compiler/{modules => custom}/dff.py | 4 +- compiler/custom/inv_dec.py | 80 ++++ compiler/custom/nand2_dec.py | 77 ++++ compiler/custom/nand3_dec.py | 77 ++++ compiler/custom/nand4_dec.py | 77 ++++ compiler/custom/pand2_dec.py | 138 +++++++ compiler/custom/pand3_dec.py | 149 +++++++ compiler/custom/pand4_dec.py | 149 +++++++ compiler/{modules => custom}/tri_gate.py | 0 compiler/{modules => custom}/write_driver.py | 0 compiler/modules/hierarchical_decoder.py | 373 ++++++++---------- compiler/modules/hierarchical_predecode.py | 260 ++++++++---- compiler/modules/hierarchical_predecode2x4.py | 20 +- compiler/modules/hierarchical_predecode3x8.py | 21 +- .../modules/hierarchical_predecode4x16.py | 64 +++ compiler/modules/port_address.py | 27 +- compiler/modules/port_data.py | 59 ++- ...ine_driver.py => wordline_driver_array.py} | 153 +++---- compiler/options.py | 4 + compiler/pgates/pand2.py | 2 +- compiler/pgates/pdriver.py | 12 +- compiler/pgates/pgate.py | 1 - compiler/pgates/pinv_dec.py | 217 ++++++++++ compiler/pgates/pnand3.py | 3 + compiler/pgates/ptx.py | 10 +- compiler/pgates/wordline_driver.py | 155 ++++++++ compiler/sram_factory.py | 4 +- compiler/tests/02_library_lvs_test.py | 2 +- compiler/tests/04_pand2_dec_test.py | 39 ++ compiler/tests/04_pand3_dec_test.py | 39 ++ compiler/tests/04_pand4_dec_test.py | 39 ++ compiler/tests/04_pdriver_test.py | 4 +- compiler/tests/04_pinv_dec_1x_test.py | 35 ++ ...ver_test.py => 04_wordline_driver_test.py} | 2 +- .../tests/06_hierarchical_decoder_test.py | 8 +- .../06_hierarchical_predecode2x4_test.py | 7 +- .../06_hierarchical_predecode3x8_test.py | 7 +- .../06_hierarchical_predecode4x16_test.py | 42 ++ ...08_wordline_driver_array_pbitcell_test.py} | 5 +- .../tests/08_wordline_driver_array_test.py | 37 ++ compiler/tests/18_port_data_1rw_1r_test.py | 6 +- compiler/tests/19_single_bank_test.py | 40 +- compiler/tests/testutils.py | 1 + 49 files changed, 1991 insertions(+), 552 deletions(-) rename compiler/{modules => custom}/dff.py (95%) create mode 100644 compiler/custom/inv_dec.py create mode 100644 compiler/custom/nand2_dec.py create mode 100644 compiler/custom/nand3_dec.py create mode 100644 compiler/custom/nand4_dec.py create mode 100644 compiler/custom/pand2_dec.py create mode 100644 compiler/custom/pand3_dec.py create mode 100644 compiler/custom/pand4_dec.py rename compiler/{modules => custom}/tri_gate.py (100%) rename compiler/{modules => custom}/write_driver.py (100%) create mode 100644 compiler/modules/hierarchical_predecode4x16.py rename compiler/modules/{wordline_driver.py => wordline_driver_array.py} (50%) create mode 100644 compiler/pgates/pinv_dec.py create mode 100644 compiler/pgates/wordline_driver.py create mode 100755 compiler/tests/04_pand2_dec_test.py create mode 100755 compiler/tests/04_pand3_dec_test.py create mode 100755 compiler/tests/04_pand4_dec_test.py create mode 100755 compiler/tests/04_pinv_dec_1x_test.py rename compiler/tests/{08_wordline_driver_test.py => 04_wordline_driver_test.py} (93%) create mode 100755 compiler/tests/06_hierarchical_predecode4x16_test.py rename compiler/tests/{08_wordline_driver_pbitcell_test.py => 08_wordline_driver_array_pbitcell_test.py} (87%) create mode 100755 compiler/tests/08_wordline_driver_array_test.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 27c341aa..27431cb2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ before_script: - . /home/gitlab-runner/setup-paths.sh - export OPENRAM_HOME="`pwd`/compiler" - - export OPENRAM_TECH="`pwd`/technology" + - export OPENRAM_TECH="`pwd`/technology:/home/PDKs/skywater-tech" stages: - test @@ -25,6 +25,15 @@ scn4m_subm: - .coverage.* expire_in: 1 week +# s8: +# stage: test +# script: +# - coverage run -p $OPENRAM_HOME/tests/regress.py -t s8 +# artifacts: +# paths: +# - .coverage.* +# expire_in: 1 week + coverage: stage: coverage script: diff --git a/compiler/base/contact.py b/compiler/base/contact.py index f4fda552..14dbf76e 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -53,6 +53,10 @@ class contact(hierarchy_design.hierarchy_design): first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V" second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V" self.directions = (first_dir, second_dir) + # Preferred directions + elif directions == "pref": + self.directions = (tech.preferred_directions[layer_stack[0]], + tech.preferred_directions[layer_stack[2]]) # User directions elif directions: self.directions = directions @@ -149,7 +153,7 @@ class contact(hierarchy_design.hierarchy_design): self.first_layer_vertical_enclosure = max(self.first_layer_enclosure, (self.first_layer_minwidth - self.contact_array_height) / 2) else: - debug.error("Invalid first layer direction.", -1) + debug.error("Invalid first layer direction: ".format(self.directions[0]), -1) # In some technologies, the minimum width may be larger # than the overlap requirement around the via, so @@ -165,7 +169,7 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_vertical_enclosure = max(self.second_layer_enclosure, (self.second_layer_minwidth - self.contact_array_width) / 2) else: - debug.error("Invalid second layer direction.", -1) + debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1) def create_contact_array(self): """ Create the contact array at the origin""" diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 96da3d99..32af7ee9 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -86,8 +86,14 @@ class geometry: elif mirror == "XY": ll = ll.scale(-1, -1) ur = ur.scale(-1, -1) + elif mirror == "" or mirror == "R0": + pass + else: + debug.error("Invalid mirroring: {}".format(mirror), -1) - if rotate == 90: + if rotate == 0: + pass + elif rotate == 90: ll = ll.rotate_scale(-1, 1) ur = ur.rotate_scale(-1, 1) elif rotate == 180: @@ -96,6 +102,8 @@ class geometry: elif rotate == 270: ll = ll.rotate_scale(1, -1) ur = ur.rotate_scale(1, -1) + else: + debug.error("Invalid rotation: {}".format(rotate), -1) self.boundary = [offset + ll, offset + ur] self.normalize() @@ -139,6 +147,10 @@ class geometry: def cy(self): """ Return the center y """ return 0.5 * (self.boundary[0].y + self.boundary[1].y) + + def center(self): + """ Return the center coordinate """ + return vector(self.cx(), self.cy()) class instance(geometry): diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index df7095df..4d0345a3 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -244,25 +244,27 @@ class layout(): height)) return self.objs[-1] - def add_segment_center(self, layer, start, end): + def add_segment_center(self, layer, start, end, width=None): """ Add a min-width rectanglular segment using center line on the start to end point """ - minwidth_layer = drc["minwidth_{}".format(layer)] + if not width: + width = drc["minwidth_{}".format(layer)] + if start.x != end.x and start.y != end.y: debug.error("Nonrectilinear center rect!", -1) elif start.x != end.x: - offset = vector(0, 0.5 * minwidth_layer) + offset = vector(0, 0.5 * width) return self.add_rect(layer, start - offset, end.x - start.x, - minwidth_layer) + width) else: - offset = vector(0.5 * minwidth_layer, 0) + offset = vector(0.5 * width, 0) return self.add_rect(layer, start - offset, - minwidth_layer, + width, end.y - start.y) def get_pin(self, text): @@ -322,7 +324,7 @@ class layout(): for pin_name in self.pin_map.keys(): self.copy_layout_pin(instance, pin_name, prefix + pin_name) - def add_layout_pin_segment_center(self, text, layer, start, end): + def add_layout_pin_segment_center(self, text, layer, start, end, width=None): """ Creates a path like pin with center-line convention """ @@ -331,27 +333,27 @@ class layout(): self.gds_write(file_name) debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1) - minwidth_layer = drc["minwidth_{}".format(layer)] + if not width: + layer_width = drc["minwidth_{}".format(layer)] + else: + layer_width = width # one of these will be zero - width = max(start.x, end.x) - min(start.x, end.x) - height = max(start.y, end.y) - min(start.y, end.y) + bbox_width = max(start.x, end.x) - min(start.x, end.x) + bbox_height = max(start.y, end.y) - min(start.y, end.y) ll_offset = vector(min(start.x, end.x), min(start.y, end.y)) # Shift it down 1/2 a width in the 0 dimension - if height == 0: - ll_offset -= vector(0, 0.5 * minwidth_layer) - if width == 0: - ll_offset -= vector(0.5 * minwidth_layer, 0) - # This makes sure it is long enough, but also it is not 0 width! - height = max(minwidth_layer, height) - width = max(minwidth_layer, width) + if bbox_height == 0: + ll_offset -= vector(0, 0.5 * layer_width) + if bbox_width == 0: + ll_offset -= vector(0.5 * layer_width, 0) - return self.add_layout_pin(text, - layer, - ll_offset, - width, - height) + return self.add_layout_pin(text=text, + layer=layer, + offset=ll_offset, + width=bbox_width, + height=bbox_height) def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None): """ Creates a path like pin with center-line convention """ @@ -692,6 +694,8 @@ class layout(): boundary_layer = "stdc" boundary = [self.find_lowest_coords(), self.find_highest_coords()] + debug.check(boundary[0] and boundary[1], "No shapes to make a boundary.") + height = boundary[1][1] - boundary[0][1] width = boundary[1][0] - boundary[0][0] (layer_number, layer_purpose) = techlayer[boundary_layer] @@ -1306,16 +1310,16 @@ class layout(): which vias are needed. """ - via = self.add_via_stack_center(from_layer=start_layer, - to_layer=self.pwr_grid_layer, - size=size, - offset=loc, - directions=directions) if start_layer == self.pwr_grid_layer: self.add_layout_pin_rect_center(text=name, layer=self.pwr_grid_layer, offset=loc) else: + via = self.add_via_stack_center(from_layer=start_layer, + to_layer=self.pwr_grid_layer, + size=size, + offset=loc, + directions=directions) # Hack for min area if OPTS.tech_name == "s8": width = round_to_grid(sqrt(drc["minarea_m3"])) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 8091af63..00d7ad44 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -208,6 +208,7 @@ class spice(): # parses line into ports and remove subckt self.pins = subckt_line.split(" ")[2:] else: + debug.info(4, "no spfile {0}".format(self.sp_file)) self.spice = [] # We don't define self.lvs and will use self.spice if dynamically created diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index b0e79208..4ed2d053 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -7,7 +7,7 @@ # import debug import utils -from tech import GDS, layer, parameter +from tech import GDS, layer from tech import cell_properties as props import bitcell_base diff --git a/compiler/modules/dff.py b/compiler/custom/dff.py similarity index 95% rename from compiler/modules/dff.py rename to compiler/custom/dff.py index e319459e..c8fdb4b0 100644 --- a/compiler/modules/dff.py +++ b/compiler/custom/dff.py @@ -61,7 +61,7 @@ class dff(design.design): #Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width. return parameter["dff_clk_cin"] - def build_graph(self, graph, inst_name, port_nets): + def build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" - self.add_graph_edges(graph, port_nets) + self.add_graph_edges(graph, port_nets) diff --git a/compiler/custom/inv_dec.py b/compiler/custom/inv_dec.py new file mode 100644 index 00000000..80fdb74e --- /dev/null +++ b/compiler/custom/inv_dec.py @@ -0,0 +1,80 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils +import debug + + +class inv_dec(design.design): + """ + INV for address decoders. + """ + + pin_names = ["A", "Z", "vdd", "gnd"] + type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("inv_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "inv_dec", GDS["unit"]) + + def __init__(self, name="inv_dec", height=None): + design.design.__init__(self, name) + + self.width = inv_dec.width + self.height = inv_dec.height + self.pin_map = inv_dec.pin_map + self.add_pin_types(self.type_list) + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + c_eff = self.calculate_effective_capacitance(load) + freq = spice["default_event_frequency"] + power_dyn = self.calc_dynamic_power(corner, c_eff, freq) + power_leak = spice["inv_leakage"] + + total_power = self.return_power(power_dyn, power_leak) + return total_power + + def calculate_effective_capacitance(self, load): + """Computes effective capacitance. Results in fF""" + c_load = load + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) + + return transition_prob * (c_load + c_para) + + def input_load(self): + """ + Return the capacitance of the gate connection in generic capacitive + units relative to the minimum width of a transistor + """ + return self.nmos_size + self.pmos_size + + def get_stage_effort(self, cout, inp_is_rise=True): + """ + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 1 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) diff --git a/compiler/custom/nand2_dec.py b/compiler/custom/nand2_dec.py new file mode 100644 index 00000000..30a018a1 --- /dev/null +++ b/compiler/custom/nand2_dec.py @@ -0,0 +1,77 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils + + +class nand2_dec(design.design): + """ + 2-input NAND decoder for address decoders. + """ + + pin_names = ["A", "B", "Z", "vdd", "gnd"] + type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("nand2_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "nand2_dec", GDS["unit"]) + + def __init__(self, name="nand2_dec", height=None): + design.design.__init__(self, name) + + self.width = nand2_dec.width + self.height = nand2_dec.height + self.pin_map = nand2_dec.pin_map + self.add_pin_types(self.type_list) + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + c_eff = self.calculate_effective_capacitance(load) + freq = spice["default_event_frequency"] + power_dyn = self.calc_dynamic_power(corner, c_eff, freq) + power_leak = spice["nand2_leakage"] + + total_power = self.return_power(power_dyn, power_leak) + return total_power + + def calculate_effective_capacitance(self, load): + """Computes effective capacitance. Results in fF""" + c_load = load + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) + transition_prob = 0.1875 + return transition_prob * (c_load + c_para) + + def input_load(self): + """Return the relative input capacitance of a single input""" + return self.nmos_size + self.pmos_size + + def get_stage_effort(self, cout, inp_is_rise=True): + """ + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 2 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) + diff --git a/compiler/custom/nand3_dec.py b/compiler/custom/nand3_dec.py new file mode 100644 index 00000000..d700062c --- /dev/null +++ b/compiler/custom/nand3_dec.py @@ -0,0 +1,77 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils + + +class nand3_dec(design.design): + """ + 3-input NAND decoder for address decoders. + """ + + pin_names = ["A", "B", "C", "Z", "vdd", "gnd"] + type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("nand3_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "nand3_dec", GDS["unit"]) + + def __init__(self, name="nand3_dec", height=None): + design.design.__init__(self, name) + + self.width = nand3_dec.width + self.height = nand3_dec.height + self.pin_map = nand3_dec.pin_map + self.add_pin_types(self.type_list) + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + c_eff = self.calculate_effective_capacitance(load) + freq = spice["default_event_frequency"] + power_dyn = self.calc_dynamic_power(corner, c_eff, freq) + power_leak = spice["nand3_leakage"] + + total_power = self.return_power(power_dyn, power_leak) + return total_power + + def calculate_effective_capacitance(self, load): + """Computes effective capacitance. Results in fF""" + c_load = load + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) + transition_prob = 0.1875 + return transition_prob * (c_load + c_para) + + def input_load(self): + """Return the relative input capacitance of a single input""" + return self.nmos_size + self.pmos_size + + def get_stage_effort(self, cout, inp_is_rise=True): + """ + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 2 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) + diff --git a/compiler/custom/nand4_dec.py b/compiler/custom/nand4_dec.py new file mode 100644 index 00000000..5c2bb882 --- /dev/null +++ b/compiler/custom/nand4_dec.py @@ -0,0 +1,77 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils + + +class nand4_dec(design.design): + """ + 2-input NAND decoder for address decoders. + """ + + pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"] + type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("nand4_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "nand4_dec", GDS["unit"]) + + def __init__(self, name="nand4_dec", height=None): + design.design.__init__(self, name) + + self.width = nand4_dec.width + self.height = nand4_dec.height + self.pin_map = nand4_dec.pin_map + self.add_pin_types(self.type_list) + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + c_eff = self.calculate_effective_capacitance(load) + freq = spice["default_event_frequency"] + power_dyn = self.calc_dynamic_power(corner, c_eff, freq) + power_leak = spice["nand4_leakage"] + + total_power = self.return_power(power_dyn, power_leak) + return total_power + + def calculate_effective_capacitance(self, load): + """Computes effective capacitance. Results in fF""" + c_load = load + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) + transition_prob = 0.1875 + return transition_prob * (c_load + c_para) + + def input_load(self): + """Return the relative input capacitance of a single input""" + return self.nmos_size + self.pmos_size + + def get_stage_effort(self, cout, inp_is_rise=True): + """ + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 2 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) + diff --git a/compiler/custom/pand2_dec.py b/compiler/custom/pand2_dec.py new file mode 100644 index 00000000..0db490ff --- /dev/null +++ b/compiler/custom/pand2_dec.py @@ -0,0 +1,138 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from vector import vector +import pgate +from sram_factory import factory +from globals import OPTS + + +class pand2_dec(pgate.pgate): + """ + This is an AND with configurable drive strength. + """ + def __init__(self, name, size=1, height=None, add_wells=True): + debug.info(1, "Creating pand2_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + pgate.pgate.__init__(self, name, height, add_wells) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand2_dec") + else: + self.nand = factory.create(module_type="nand2_dec", + height=self.height) + + self.inv = factory.create(module_type="inv_dec", + height=self.height, + size=self.size) + + self.add_mod(self.nand) + self.add_mod(self.inv) + + def create_layout(self): + self.width = self.nand.width + self.inv.width + + self.place_insts() + self.add_wires() + self.add_layout_pins() + self.route_supply_rails() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="pand2_dec_nand", + mod=self.nand) + self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) + + self.inv_inst = self.add_inst(name="pand2_dec_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) + + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.inv_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + if OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + + def add_layout_pins(self): + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A", "B"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/custom/pand3_dec.py b/compiler/custom/pand3_dec.py new file mode 100644 index 00000000..3ec1eb45 --- /dev/null +++ b/compiler/custom/pand3_dec.py @@ -0,0 +1,149 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from vector import vector +import pgate +from sram_factory import factory +from globals import OPTS + + +class pand3_dec(pgate.pgate): + """ + This is an AND with configurable drive strength. + """ + def __init__(self, name, size=1, height=None, add_wells=True): + debug.info(1, "Creating pand3_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + pgate.pgate.__init__(self, name, height, add_wells) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand3_dec") + else: + self.nand = factory.create(module_type="nand3_dec", + height=self.height) + + self.inv = factory.create(module_type="inv_dec", + height=self.height, + size=self.size) + + self.add_mod(self.nand) + self.add_mod(self.inv) + + def create_layout(self): + self.width = self.nand.width + self.inv.width + + self.place_insts() + self.add_wires() + self.add_layout_pins() + self.route_supply_rails() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("C", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="pand3_dec_nand", + mod=self.nand) + self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"]) + + self.inv_inst = self.add_inst(name="pand3_dec_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) + + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.inv_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + if OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + + def add_layout_pins(self): + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A", "B", "C"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + def analytical_delay(self, corner, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + nand_delay = self.nand.analytical_delay(corner, + slew=slew, + load=self.inv.input_load()) + inv_delay = self.inv.analytical_delay(corner, + slew=nand_delay.slew, + load=load) + return nand_delay + inv_delay + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/custom/pand4_dec.py b/compiler/custom/pand4_dec.py new file mode 100644 index 00000000..ee65a349 --- /dev/null +++ b/compiler/custom/pand4_dec.py @@ -0,0 +1,149 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from vector import vector +import pgate +from sram_factory import factory +from globals import OPTS + + +class pand4_dec(pgate.pgate): + """ + This is an AND with configurable drive strength. + """ + def __init__(self, name, size=1, height=None, add_wells=True): + debug.info(1, "Creating pand4_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + pgate.pgate.__init__(self, name, height, add_wells) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand4_dec") + else: + self.nand = factory.create(module_type="nand4_dec", + height=self.height) + + self.inv = factory.create(module_type="inv_dec", + size=self.size) + + self.add_mod(self.nand) + self.add_mod(self.inv) + + def create_layout(self): + self.width = self.nand.width + self.inv.width + + self.place_insts() + self.add_wires() + self.add_layout_pins() + self.route_supply_rails() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("C", "INPUT") + self.add_pin("D", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="pand4_dec_nand", + mod=self.nand) + self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"]) + + self.inv_inst = self.add_inst(name="pand4_dec_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) + + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.inv_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + if OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + + def add_layout_pins(self): + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A", "B", "C"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + def analytical_delay(self, corner, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + nand_delay = self.nand.analytical_delay(corner, + slew=slew, + load=self.inv.input_load()) + inv_delay = self.inv.analytical_delay(corner, + slew=nand_delay.slew, + load=load) + return nand_delay + inv_delay + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/modules/tri_gate.py b/compiler/custom/tri_gate.py similarity index 100% rename from compiler/modules/tri_gate.py rename to compiler/custom/tri_gate.py diff --git a/compiler/modules/write_driver.py b/compiler/custom/write_driver.py similarity index 100% rename from compiler/modules/write_driver.py rename to compiler/custom/write_driver.py diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index c1593932..3cb5d35c 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -12,7 +12,6 @@ from sram_factory import factory from vector import vector from globals import OPTS from errors import drc_error -from tech import cell_properties, layer class hierarchical_decoder(design.design): @@ -28,12 +27,8 @@ class hierarchical_decoder(design.design): self.pre3x8_inst = [] b = factory.create(module_type="bitcell") - try: - self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple - except AttributeError: - self.cell_multiple = 1 - self.cell_height = self.cell_multiple * b.height - + self.cell_height = b.height + self.num_outputs = num_outputs self.num_inputs = math.ceil(math.log(self.num_outputs, 2)) (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) @@ -41,41 +36,6 @@ class hierarchical_decoder(design.design): self.create_netlist() if not OPTS.netlist_only: self.create_layout() - - def find_decoder_height(self): - """ - Dead code. This would dynamically determine the bitcell multiple, - but I just decided to hard code it in the tech file if it is not 1 - because a DRC tool would be required even to run in front-end mode. - """ - b = factory.create(module_type="bitcell") - - # Old behavior - if OPTS.netlist_only: - return (b.height, 1) - - # Search for the smallest multiple that works - cell_multiple = 1 - while cell_multiple < 5: - cell_height = cell_multiple * b.height - # debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height)) - try: - and3 = factory.create(module_type="pand3", - height=cell_height) - except drc_error: - # debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height)) - pass - else: - (drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) - total_errors = drc_errors + lvs_errors - if total_errors == 0: - debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple)) - return (cell_height, cell_multiple) - - cell_multiple += 1 - - else: - debug.error("Couldn't find a valid decoder height multiple.", -1) def create_netlist(self): self.add_modules() @@ -97,15 +57,22 @@ class hierarchical_decoder(design.design): self.DRC_LVS() def add_modules(self): - self.inv = factory.create(module_type="pinv", - height=self.cell_height) - self.add_mod(self.inv) - self.and2 = factory.create(module_type="pand2", - height=self.cell_height) + if OPTS.tech_name == "s8": + self.and2 = factory.create(module_type="pand2_dec") + else: + self.and2 = factory.create(module_type="pand2_dec", + height=self.cell_height) self.add_mod(self.and2) - self.and3 = factory.create(module_type="pand3", - height=self.cell_height) + if OPTS.tech_name == "s8": + self.and3 = factory.create(module_type="pand3_dec") + else: + self.and3 = factory.create(module_type="pand3_dec", + height=self.cell_height) + self.add_mod(self.and3) + # TBD + # self.and4 = factory.create(module_type="pand4_dec") + # self.add_mod(self.and4) self.add_decoders() @@ -176,56 +143,63 @@ class hierarchical_decoder(design.design): -1) # Calculates height and width of pre-decoder, - if self.no_of_pre3x8 > 0: + # FIXME: Update with 4x16 + if self.no_of_pre3x8 > 0 and self.no_of_pre2x4 > 0: + self.predecoder_width = max(self.pre3_8.width, self.pre2_4.width) + elif self.no_of_pre3x8 > 0: self.predecoder_width = self.pre3_8.width else: self.predecoder_width = self.pre2_4.width - - self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 - # We may have more than one bitcell per decoder row - self.num_rows = math.ceil(self.num_outputs / self.cell_multiple) - # We will place this many final decoders per row - self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows) - # We will need to use M2 and M3 in the vertical bus if we have multiple decoders per row - if self.decoders_per_row == 1: - self.decoder_bus_pitch = self.m2_pitch - elif self.decoders_per_row == 2: - self.decoder_bus_pitch = self.m3_pitch + # How much space between each predecoder + self.predecoder_spacing = self.and2.height + self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 \ + + (self.no_of_pre2x4 + self.no_of_pre3x8 - 1) * self.predecoder_spacing + + # Inputs to cells are on input layer + # Outputs from cells are on output layer + if OPTS.tech_name == "s8": + self.bus_layer = "m1" + self.bus_directions = "nonpref" + self.bus_pitch = self.m1_pitch + self.bus_space = self.m2_space + self.input_layer = "m2" + self.output_layer = "li" + self.output_layer_pitch = self.li_pitch else: - debug.error("Insufficient layers for multi-bit height decoder.", -1) + self.bus_layer = "m2" + self.bus_directions = "pref" + self.bus_pitch = self.m2_pitch + self.bus_space = self.m2_space + # These two layers being the same requires a special jog + # to ensure to conflicts with the output layers + self.input_layer = "m1" + self.output_layer = "m3" + self.output_layer_pitch = self.m3_pitch + + # Two extra pitches between modules on left and right + self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch + self.row_decoder_height = self.and2.height * self.num_outputs + # Extra bus space for supply contacts + self.input_routing_width = self.num_inputs * self.bus_pitch + self.bus_space # Calculates height and width of row-decoder - if (self.num_inputs == 4 or self.num_inputs == 5): - nand_width = self.and2.width - nand_inputs = 2 - else: - nand_width = self.and3.width - nand_inputs = 3 - self.internal_routing_width = self.decoder_bus_pitch * (self.total_number_of_predecoder_outputs + 1) - self.row_decoder_height = self.inv.height * self.num_rows - - decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m2_pitch - # print(self.decoders_per_row, nand_inputs) - # print(decoder_input_wire_height, self.cell_height) - if decoder_input_wire_height > self.cell_height: - debug.warning("Cannot fit multi-bit decoder routes per row.") - # debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.") - - self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch - # Calculates height and width of hierarchical decoder # Add extra pitch for good measure - self.height = max(self.predecoder_height, self.row_decoder_height) + self.m2_pitch - self.width = self.input_routing_width + self.predecoder_width \ + self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space + if (self.num_inputs == 4 or self.num_inputs == 5): + self.nand_width = self.and2.width + else: + self.nand_width = self.and3.width + + self.width = self.input_routing_width \ + + self.predecoder_width \ + self.internal_routing_width \ - + self.decoders_per_row * nand_width + self.inv.width - + + self.nand_width \ + + self.m1_space + def route_inputs(self): """ Create input bus for the predecoders """ - # inputs should be as high as the decoders - input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height - # Find the left-most predecoder min_x = 0 if self.no_of_pre2x4 > 0: @@ -235,10 +209,10 @@ class hierarchical_decoder(design.design): input_offset=vector(min_x - self.input_routing_width, 0) input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] - self.input_bus = self.create_vertical_pin_bus(layer="m2", + self.input_bus = self.create_vertical_pin_bus(layer=self.bus_layer, offset=input_offset, names=input_bus_names, - length=input_height) + length=self.predecoder_height) self.route_input_to_predecodes() @@ -253,9 +227,7 @@ class hierarchical_decoder(design.design): in_name = "in_{}".format(i) decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) - # To prevent conflicts, we will offset each input connect so - # that it aligns with the vdd/gnd rails - decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch)) + decoder_offset = decoder_pin.center() input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) self.route_input_bus(decoder_offset, input_offset) @@ -269,9 +241,7 @@ class hierarchical_decoder(design.design): in_name = "in_{}".format(i) decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) - # To prevent conflicts, we will offset each input connect so - # that it aligns with the vdd/gnd rails - decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch)) + decoder_offset = decoder_pin.center() input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) self.route_input_bus(decoder_offset, input_offset) @@ -282,13 +252,14 @@ class hierarchical_decoder(design.design): vertical M2 coordinate to the predecode inputs """ - self.add_via_stack_center(from_layer="m2", - to_layer="m3", + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.input_layer, offset=input_offset) - self.add_via_stack_center(from_layer="m2", - to_layer="m3", - offset=output_offset) - self.add_path("m3", [input_offset, output_offset]) + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.input_layer, + offset=output_offset, + directions=self.bus_directions) + self.add_path(self.input_layer, [input_offset, output_offset]) def add_pins(self): """ Add the module pins """ @@ -363,19 +334,19 @@ class hierarchical_decoder(design.design): if (self.num_inputs == 2): base = vector(-self.pre2_4.width, 0) else: - base= vector(-self.pre2_4.width, num * self.pre2_4.height) + base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing)) - self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0)) + self.pre2x4_inst[num].place(base) def place_pre3x8(self, num): """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ if (self.num_inputs == 3): offset = vector(-self.pre_3_8.width, 0) else: - height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height + height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) + num * (self.pre3_8.height + self.predecoder_spacing) offset = vector(-self.pre3_8.width, height) - self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0)) + self.pre3x8_inst[num].place(offset) def create_row_decoder(self): """ Create the row-decoder by placing AND2/AND3 and Inverters @@ -431,7 +402,6 @@ class hierarchical_decoder(design.design): if (self.num_inputs >= 4): self.place_decoder_and_array() - def place_decoder_and_array(self): """ Add a column of AND gates for final decode. @@ -452,9 +422,7 @@ class hierarchical_decoder(design.design): Add a column of AND gates for the decoder above the predecoders. """ - for inst_index in range(self.num_outputs): - row = math.floor(inst_index / self.decoders_per_row) - dec = inst_index % self.decoders_per_row + for row in range(self.num_outputs): if ((row % 2) == 0): y_off = and_mod.height * row mirror = "R0" @@ -462,32 +430,16 @@ class hierarchical_decoder(design.design): y_off = and_mod.height * (row + 1) mirror = "MX" - x_off = self.internal_routing_width + dec * and_mod.width - self.and_inst[inst_index].place(offset=vector(x_off, y_off), - mirror=mirror) + x_off = self.internal_routing_width + self.and_inst[row].place(offset=vector(x_off, y_off), + mirror=mirror) def route_outputs(self): """ Add the pins. """ - max_xoffset = max(x.rx() for x in self.and_inst) - - for output_index in range(self.num_outputs): - row_remainder = (output_index % self.decoders_per_row) - - and_inst = self.and_inst[output_index] - z_pin = and_inst.get_pin("Z") - if row_remainder == 0 and self.decoders_per_row > 1: - layer = "m3" - self.add_via_stack_center(from_layer=z_pin.layer, - to_layer="m3", - offset=z_pin.center()) - else: - layer = z_pin.layer - - self.add_layout_pin_segment_center(text="decode_{0}".format(output_index), - layer=layer, - start=z_pin.center(), - end=vector(max_xoffset, z_pin.cy())) + for row in range(self.num_outputs): + and_inst = self.and_inst[row] + self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row)) def route_decoder_bus(self): """ @@ -498,9 +450,9 @@ class hierarchical_decoder(design.design): if (self.num_inputs >= 4): # This leaves an offset for the predecoder output jogs input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)] - self.predecode_bus = self.create_vertical_pin_bus(layer="m2", - pitch=self.decoder_bus_pitch, - offset=vector(0, 0), + self.predecode_bus = self.create_vertical_pin_bus(layer=self.bus_layer, + pitch=self.bus_pitch, + offset=vector(self.bus_pitch, 0), names=input_bus_names, length=self.height) @@ -518,8 +470,9 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(pre_num * 4 + i) out_name = "out_{}".format(i) pin = self.pre2x4_inst[pre_num].get_pin(out_name) - x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch - self.route_predecode_bus_inputs(predecode_name, pin, x_offset) + x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch + y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height + self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre3x8): @@ -527,8 +480,9 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4) out_name = "out_{}".format(i) pin = self.pre3x8_inst[pre_num].get_pin(out_name) - x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch - self.route_predecode_bus_inputs(predecode_name, pin, x_offset) + x_offset = self.pre3x8_inst[pre_num].rx() + self.output_layer_pitch + y_offset = self.pre3x8_inst[pre_num].by() + i * self.cell_height + self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) def route_bus_to_decoder(self): """ @@ -542,12 +496,6 @@ class hierarchical_decoder(design.design): and the 128th AND3 is connected to [3,7,15] """ output_index = 0 - - if "li" in layer: - self.decoder_layers = [self.m1_stack, self.m2_stack[::-1]] - else: - self.decoder_layers = [self.m2_stack[::-1]] - debug.check(self.decoders_per_row <= len(self.decoder_layers), "Must have more layers for multi-height decoder.") if (self.num_inputs == 4 or self.num_inputs == 5): for index_B in self.predec_groups[1]: @@ -557,13 +505,11 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(index_A) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("A"), - output_index, - 0) + output_index) predecode_name = "predecode_{}".format(index_B) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("B"), - output_index, - 1) + output_index) output_index = output_index + 1 elif (self.num_inputs > 5): @@ -575,18 +521,15 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(index_A) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("A"), - output_index, - 0) + output_index) predecode_name = "predecode_{}".format(index_B) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("B"), - output_index, - 1) + output_index) predecode_name = "predecode_{}".format(index_C) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("C"), - output_index, - 2) + output_index) output_index = output_index + 1 def route_vdd_gnd(self): @@ -594,90 +537,90 @@ class hierarchical_decoder(design.design): Add a pin for each row of vdd/gnd which are must-connects next level up. """ + + if OPTS.tech_name == "s8": + for n in ["vdd", "gnd"]: + pins = self.and_inst[0].get_pins(n) + for pin in pins: + self.add_rect(layer=pin.layer, + offset=pin.ll() + vector(0, self.bus_space), + width=pin.width(), + height=self.height - 2 * self.bus_space) - # The vias will be placed at the right of the cells. - xoffset = max(x.rx() for x in self.and_inst) - for num in range(0, self.num_outputs): - # Only add the power pin for the 1st in each row - if num % self.decoders_per_row: - continue - - for pin_name in ["vdd", "gnd"]: - # The nand and inv are the same height rows... - supply_pin = self.and_inst[num].get_pin(pin_name) - pin_pos = vector(xoffset, supply_pin.cy()) - self.add_path(supply_pin.layer, - [supply_pin.lc(), vector(xoffset, supply_pin.cy())]) - self.add_power_pin(name=pin_name, - loc=pin_pos, - start_layer=supply_pin.layer) - - # Copy the pins from the predecoders - for pre in self.pre2x4_inst + self.pre3x8_inst: - self.copy_layout_pin(pre, "vdd") - self.copy_layout_pin(pre, "gnd") + # This adds power vias at the top of each cell + # (except the last to keep them inside the boundary) + for i in self.and_inst[:-1]: + pins = i.get_pins(n) + for pin in pins: + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + + for i in self.pre2x4_inst + self.pre3x8_inst: + self.copy_layout_pin(i, n) + else: + # The vias will be placed at the right of the cells. + xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space + for row in range(0, self.num_outputs): + for pin_name in ["vdd", "gnd"]: + # The nand and inv are the same height rows... + supply_pin = self.and_inst[row].get_pin(pin_name) + pin_pos = vector(xoffset, supply_pin.cy()) + self.add_power_pin(name=pin_name, + loc=pin_pos, + start_layer=supply_pin.layer) + + # Copy the pins from the predecoders + for pre in self.pre2x4_inst + self.pre3x8_inst: + for pin_name in ["vdd", "gnd"]: + self.copy_layout_pin(pre, pin_name) - def route_predecode_bus_outputs(self, rail_name, pin, output_index, pin_index): + def route_predecode_bus_outputs(self, rail_name, pin, row): """ Connect the routing rail to the given metal1 pin using a routing track at the given y_offset """ - row_index = math.floor(output_index / self.decoders_per_row) - row_remainder = (output_index % self.decoders_per_row) - row_offset = row_index * self.and_inst[0].height - pin_pos = pin.center() - - # y_offset is the same for both the M2 and M4 routes so that the rail - # contacts align and don't cause problems - if pin_index == 0: - # Bottom pitch - y_offset = row_offset - elif pin_index == 1: - # One pitch from top - y_offset = row_offset + self.and_inst[0].height - self.m3_pitch - elif pin_index == 2: - # One pitch from bottom - y_offset = row_offset + self.m3_pitch - else: - debug.error("Invalid decoder pitch.") - - rail_pos = vector(self.predecode_bus[rail_name].x, y_offset) - mid_pos = vector(pin_pos.x, rail_pos.y) - self.add_wire(self.decoder_layers[row_remainder], [rail_pos, mid_pos, pin_pos]) + rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) + self.add_path(self.input_layer, [rail_pos, pin_pos]) - self.add_via_stack_center(from_layer="m2", - to_layer=self.decoder_layers[row_remainder][0], - offset=rail_pos) + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.input_layer, + offset=rail_pos, + directions=self.bus_directions) self.add_via_stack_center(from_layer=pin.layer, - to_layer=self.decoder_layers[row_remainder][2], + to_layer=self.input_layer, offset=pin_pos, directions=("H", "H")) - def route_predecode_bus_inputs(self, rail_name, pin, x_offset): + def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset): """ Connect the routing rail to the given metal1 pin using a jog to the right of the cell at the given x_offset. """ # This routes the pin up to the rail, basically, to avoid conflicts. # It would be fixed with a channel router. - # pin_pos = pin.center() - # mid_point1 = vector(x_offset, pin_pos.y) - # mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2) - # rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y) - # self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos]) + pin_pos = pin.rc() + mid_point1 = vector(x_offset, pin_pos.y) + mid_point2 = vector(x_offset, y_offset) + rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y) + self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos]) - pin_pos = pin.center() - rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) - self.add_path("m1", [pin_pos, rail_pos]) + # pin_pos = pin.center() + # rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) + # self.add_path(self.output_layer, [pin_pos, rail_pos]) self.add_via_stack_center(from_layer=pin.layer, - to_layer="m1", + to_layer=self.output_layer, offset=pin_pos) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", - offset=rail_pos) + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.output_layer, + offset=rail_pos, + directions=self.bus_directions) def input_load(self): if self.determine_predecodes(self.num_inputs)[1]==0: diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 9a658546..8244028e 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -8,29 +8,24 @@ import debug import design import math -import contact from vector import vector from sram_factory import factory -from tech import cell_properties +from globals import OPTS class hierarchical_predecode(design.design): """ - Pre 2x4 and 3x8 decoder shared code. + Pre 2x4 and 3x8 and TBD 4x16 decoder shared code. """ def __init__(self, name, input_number, height=None): self.number_of_inputs = input_number if not height: b = factory.create(module_type="bitcell") - try: - self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple - except AttributeError: - self.cell_multiple = 1 - self.cell_height = self.cell_multiple * b.height + self.cell_height = b.height else: self.cell_height = height - + self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) design.design.__init__(self, name) @@ -44,34 +39,73 @@ class hierarchical_predecode(design.design): def add_modules(self): """ Add the INV and AND gate modules """ - - self.inv = factory.create(module_type="pinv", - height=self.cell_height) - self.add_mod(self.inv) - - self.add_and(self.number_of_inputs) - self.add_mod(self.and_mod) - def add_and(self, inputs): - """ Create the NAND for the predecode input stage """ - if inputs==2: - self.and_mod = factory.create(module_type="pand2", + # FIXME: Default parms are required for hard cells for now. + if self.number_of_inputs == 2: + self.and_mod = factory.create(module_type="pand2_dec", height=self.cell_height) - elif inputs==3: - self.and_mod = factory.create(module_type="pand3", + elif self.number_of_inputs == 3: + self.and_mod = factory.create(module_type="pand3_dec", + height=self.cell_height) + elif self.number_of_inputs == 4: + self.and_mod = factory.create(module_type="pand4_dec", height=self.cell_height) else: - debug.error("Invalid number of predecode inputs: {}".format(inputs), -1) - + debug.error("Invalid number of predecode inputs: {}".format(self.number_of_inputs), -1) + self.add_mod(self.and_mod) + + # This uses the pinv_dec parameterized cell + self.inv = factory.create(module_type="inv_dec", + height=self.cell_height, + size=1) + self.add_mod(self.inv) + + + def create_layout(self): + """ The general organization is from left to right: + 1) a set of M2 rails for input signals + 2) a set of inverters to invert input signals + 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs + 4) a set of AND gates for inversion + """ + self.setup_layout_constraints() + self.route_rails() + self.place_input_inverters() + self.place_and_array() + self.route() + self.add_boundary() + self.DRC_LVS() + def setup_layout_constraints(self): + # Inputs to cells are on input layer + # Outputs from cells are on output layer + if OPTS.tech_name == "s8": + self.bus_layer = "m1" + self.bus_directions = None + self.bus_pitch = self.m1_pitch + self.bus_space = 1.5 * self.m1_space + self.input_layer = "li" + self.output_layer = "m2" + self.output_layer_pitch = self.m2_pitch + else: + self.bus_layer = "m2" + self.bus_directions = None + self.bus_pitch = self.m2_pitch + self.bus_space = self.m2_space + # This requires a special jog to ensure to conflicts with the output layers + self.input_layer = "m1" + self.output_layer = "m1" + self.output_layer_pitch = self.m1_pitch + self.height = self.number_of_outputs * self.and_mod.height # x offset for input inverters - self.x_off_inv_1 = self.number_of_inputs * self.m2_pitch + self.m2_space + # +1 input for spacing for supply rail contacts + self.x_off_inv_1 = (self.number_of_inputs + 1) * self.bus_pitch + self.bus_pitch - # x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches - self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.m2_pitch + # x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra bus pitches + self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.bus_pitch # x offset to output inverters self.width = self.x_off_and + self.and_mod.width @@ -79,28 +113,30 @@ class hierarchical_predecode(design.design): def route_rails(self): """ Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """ input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)] - offset = vector(0.5 * self.m2_width, self.m3_pitch) - self.input_rails = self.create_vertical_pin_bus(layer="m2", - offset=offset, - names=input_names, - length=self.height - 2 * self.m1_pitch) + # Offsets for the perimeter spacing to other modules + # This uses m3 pitch to leave space for power routes + offset = vector(self.bus_pitch, self.bus_pitch) + self.input_rails = self.create_vertical_bus(layer=self.bus_layer, + offset=offset, + names=input_names, + length=self.height - 2 * self.bus_pitch) invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)] non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)] decode_names = invert_names + non_invert_names - offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m3_pitch) - self.decode_rails = self.create_vertical_bus(layer="m2", + offset = vector(self.x_off_inv_1 + self.inv.width + self.bus_pitch, self.bus_pitch) + self.decode_rails = self.create_vertical_bus(layer=self.bus_layer, offset=offset, names=decode_names, - length=self.height - 2 * self.m1_pitch) + length=self.height - 2 * self.bus_pitch) def create_input_inverters(self): """ Create the input inverters to invert input signals for the decode stage. """ - self.in_inst = [] + self.inv_inst = [] for inv_num in range(self.number_of_inputs): name = "pre_inv_{0}".format(inv_num) - self.in_inst.append(self.add_inst(name=name, - mod=self.inv)) + self.inv_inst.append(self.add_inst(name=name, + mod=self.inv)) self.connect_inst(["in_{0}".format(inv_num), "inbar_{0}".format(inv_num), "vdd", "gnd"]) @@ -108,6 +144,7 @@ class hierarchical_predecode(design.design): def place_input_inverters(self): """ Place the input inverters to invert input signals for the decode stage. """ for inv_num in range(self.number_of_inputs): + if (inv_num % 2 == 0): y_off = inv_num * (self.inv.height) mirror = "R0" @@ -115,8 +152,8 @@ class hierarchical_predecode(design.design): y_off = (inv_num + 1) * (self.inv.height) mirror="MX" offset = vector(self.x_off_inv_1, y_off) - self.in_inst[inv_num].place(offset=offset, - mirror=mirror) + self.inv_inst[inv_num].place(offset=offset, + mirror=mirror) def create_and_array(self, connections): """ Create the AND stage for the decodes """ @@ -151,21 +188,30 @@ class hierarchical_predecode(design.design): def route_inputs_to_rails(self): """ Route the uninverted inputs to the second set of rails """ + + top_and_gate = self.and_inst[-1] for num in range(self.number_of_inputs): - # route one signal next to each vdd/gnd rail since this is - # typically where the p/n devices are and there are no - # pins in the and gates. - y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m2_via.width + self.m2_space + if num == 0: + pin = top_and_gate.get_pin("A") + elif num == 1: + pin = top_and_gate.get_pin("B") + elif num == 2: + pin = top_and_gate.get_pin("C") + elif num == 3: + pin = top_and_gate.get_pin("D") + else: + debug.error("Too many inputs for predecoder.", -1) + y_offset = pin.cy() in_pin = "in_{}".format(num) a_pin = "A_{}".format(num) in_pos = vector(self.input_rails[in_pin].x, y_offset) a_pos = vector(self.decode_rails[a_pin].x, y_offset) - self.add_path("m1", [in_pos, a_pos]) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", + self.add_path(self.input_layer, [in_pos, a_pos]) + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, offset=[self.input_rails[in_pin].x, y_offset]) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, offset=[self.decode_rails[a_pin].x, y_offset]) def route_output_and(self): @@ -188,31 +234,47 @@ class hierarchical_predecode(design.design): for inv_num in range(self.number_of_inputs): out_pin = "Abar_{}".format(inv_num) in_pin = "in_{}".format(inv_num) + + inv_out_pin = self.inv_inst[inv_num].get_pin("Z") + inv_out_pos = inv_out_pin.rc() # add output so that it is just below the vdd or gnd rail # since this is where the p/n devices are and there are no # pins in the and gates. - y_offset = (inv_num + 1) * self.inv.height - 3 * self.m1_space - inv_out_pin = self.in_inst[inv_num].get_pin("Z") - inv_out_pos = inv_out_pin.rc() - right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(), 0) - rail_pos = vector(self.decode_rails[out_pin].x, y_offset) - self.add_path(inv_out_pin.layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) + if OPTS.tech_name == "s8": + rail_pos = vector(self.decode_rails[out_pin].x, inv_out_pos.y) + self.add_path(self.output_layer, [inv_out_pos, rail_pos]) + else: + y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch + right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0) + rail_pos = vector(self.decode_rails[out_pin].x, y_offset) + self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) + self.add_via_stack_center(from_layer=inv_out_pin.layer, - to_layer="m2", - offset=rail_pos) + to_layer=self.output_layer, + offset=inv_out_pos) + self.add_via_stack_center(from_layer=self.output_layer, + to_layer=self.bus_layer, + offset=rail_pos, + directions=self.bus_directions) # route input - pin = self.in_inst[inv_num].get_pin("A") - inv_in_pos = pin.lc() + pin = self.inv_inst[inv_num].get_pin("A") + inv_in_pos = pin.center() in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y) - self.add_path("m1", [in_pos, inv_in_pos]) + self.add_path(self.input_layer, [in_pos, inv_in_pos]) self.add_via_stack_center(from_layer=pin.layer, - to_layer="m1", + to_layer=self.input_layer, offset=inv_in_pos) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", - offset=in_pos) + via=self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, + offset=in_pos) + # Create the input pin at this location on the rail + self.add_layout_pin_rect_center(text=in_pin, + layer=self.bus_layer, + offset=in_pos, + height=via.mod.second_layer_height, + width=via.mod.second_layer_width) def route_and_to_rails(self): # This 2D array defines the connection mapping @@ -231,43 +293,67 @@ class hierarchical_predecode(design.design): pin = self.and_inst[k].get_pin(gate_pin) pin_pos = pin.center() rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) - self.add_path("m1", [rail_pos, pin_pos]) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", - offset=rail_pos) + self.add_path(self.input_layer, [rail_pos, pin_pos]) + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, + offset=rail_pos, + directions=self.bus_directions) if gate_pin == "A": direction = None else: direction = ("H", "H") self.add_via_stack_center(from_layer=pin.layer, - to_layer="m1", + to_layer=self.input_layer, offset=pin_pos, directions=direction) def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - # Find the x offsets for where the vias/pins should be placed - in_xoffset = self.in_inst[0].rx() + self.m1_space - # out_xoffset = self.and_inst[0].cx() + self.m1_space - for num in range(0, self.number_of_outputs): - # this will result in duplicate polygons for rails, but who cares - - # Route both supplies + # In s8, we use hand-made decoder cells with vertical power + if OPTS.tech_name == "s8": for n in ["vdd", "gnd"]: - and_pin = self.and_inst[num].get_pin(n) - supply_offset = and_pin.ll().scale(0, 1) - self.add_rect(layer=and_pin.layer, - offset=supply_offset, - width=self.and_inst[num].rx()) + # This makes a wire from top to bottom for both inv and and gates + for i in [self.inv_inst, self.and_inst]: + bot_pins = i[0].get_pins(n) + top_pins = i[-1].get_pins(n) + for (bot_pin, top_pin) in zip(bot_pins, top_pins): + self.add_rect(layer=bot_pin.layer, + offset=vector(bot_pin.lx(), self.bus_pitch), + width=bot_pin.width(), + height=top_pin.uy() - self.bus_pitch) + # This adds power vias at the top of each cell + # (except the last to keep them inside the boundary) + for i in self.inv_inst[:-1:2] + self.and_inst[:-1:2]: + pins = i.get_pins(n) + for pin in pins: + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + + # In other techs, we are using standard cell decoder cells with horizontal power + else: + for num in range(0, self.number_of_outputs): - # Add pins in two locations - for xoffset in [in_xoffset]: - pin_pos = vector(xoffset, and_pin.cy()) - self.add_power_pin(name=n, - loc=pin_pos, - start_layer=and_pin.layer) + # Route both supplies + for n in ["vdd", "gnd"]: + and_pins = self.and_inst[num].get_pins(n) + for and_pin in and_pins: + self.add_segment_center(layer=and_pin.layer, + start=vector(0, and_pin.cy()), + end=vector(self.width, and_pin.cy())) + + # Add pins in two locations + for xoffset in [self.inv_inst[0].lx() - self.bus_space, + self.and_inst[0].lx() - self.bus_space]: + pin_pos = vector(xoffset, and_pin.cy()) + self.add_power_pin(name=n, + loc=pin_pos, + start_layer=and_pin.layer) diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index b1aacc5a..9c7ddfa3 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -5,13 +5,10 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from tech import drc -import debug -import design -from vector import vector from hierarchical_predecode import hierarchical_predecode from globals import OPTS + class hierarchical_predecode2x4(hierarchical_predecode): """ Pre 2x4 decoder used in hierarchical_decoder. @@ -33,21 +30,6 @@ class hierarchical_predecode2x4(hierarchical_predecode): ["in_0", "in_1", "out_3", "vdd", "gnd"]] self.create_and_array(connections) - def create_layout(self): - """ The general organization is from left to right: - 1) a set of M2 rails for input signals - 2) a set of inverters to invert input signals - 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs - 4) a set of AND gates for inversion - """ - self.setup_layout_constraints() - self.route_rails() - self.place_input_inverters() - self.place_and_array() - self.route() - self.add_boundary() - self.DRC_LVS() - def get_and_input_line_combination(self): """ These are the decoder connections of the AND gates to the A,B pins """ combination = [["Abar_0", "Abar_1"], diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index 4f2294f1..e8c44e48 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -5,13 +5,10 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from tech import drc -import debug -import design -from vector import vector from hierarchical_predecode import hierarchical_predecode from globals import OPTS + class hierarchical_predecode3x8(hierarchical_predecode): """ Pre 3x8 decoder used in hierarchical_decoder. @@ -37,22 +34,6 @@ class hierarchical_predecode3x8(hierarchical_predecode): ["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]] self.create_and_array(connections) - def create_layout(self): - """ - The general organization is from left to right: - 1) a set of M2 rails for input signals - 2) a set of inverters to invert input signals - 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs - 4) a set of NAND gates for inversion - """ - self.setup_layout_constraints() - self.route_rails() - self.place_input_inverters() - self.place_and_array() - self.route() - self.add_boundary() - self.DRC_LVS() - def get_and_input_line_combination(self): """ These are the decoder connections of the NAND gates to the A,B,C pins """ combination = [["Abar_0", "Abar_1", "Abar_2"], diff --git a/compiler/modules/hierarchical_predecode4x16.py b/compiler/modules/hierarchical_predecode4x16.py new file mode 100644 index 00000000..4a258bfb --- /dev/null +++ b/compiler/modules/hierarchical_predecode4x16.py @@ -0,0 +1,64 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from hierarchical_predecode import hierarchical_predecode +from globals import OPTS + + +class hierarchical_predecode4x16(hierarchical_predecode): + """ + Pre 4x16 decoder used in hierarchical_decoder. + """ + def __init__(self, name, height=None): + hierarchical_predecode.__init__(self, name, 4, height) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_input_inverters() + connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"], + ["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"], + ["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"], + ["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"], + ["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"], + ["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"], + ["inbar_0", "inbar_1", "inbar_2", "in_3", "out_0", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "in_3", "out_1", "vdd", "gnd"], + ["inbar_0", "in_1", "inbar_2", "in_3", "out_2", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "in_3", "out_3", "vdd", "gnd"], + ["inbar_0", "inbar_1", "in_2", "in_3", "out_4", "vdd", "gnd"], + ["in_0", "inbar_1", "in_2", "in_3", "out_5", "vdd", "gnd"], + ["inbar_0", "in_1", "in_2", "in_3", "out_6", "vdd", "gnd"], + ["in_0", "in_1", "in_2", "in_3", "out_7", "vdd", "gnd"] ] + + self.create_and_array(connections) + + def get_and_input_line_combination(self): + """ These are the decoder connections of the AND gates to the A,B pins """ + combination = [["Abar_0", "Abar_1", "Abar_2", "Abar_3"], + ["A_0", "Abar_1", "Abar_2", "Abar_3"], + ["Abar_0", "A_1", "Abar_2", "Abar_3"], + ["A_0", "A_1", "Abar_2", "Abar_3"], + ["Abar_0", "Abar_1", "A_2" , "Abar_3"], + ["A_0", "Abar_1", "A_2" , "Abar_3"], + ["Abar_0", "A_1", "A_2" , "Abar_3"], + ["A_0", "A_1", "A_2" , "Abar_3"], + ["Abar_0", "Abar_1", "Abar_2", "A_3"], + ["A_0", "Abar_1", "Abar_2", "A_3"], + ["Abar_0", "A_1", "Abar_2", "A_3"], + ["A_0", "A_1", "Abar_2", "A_3"], + ["Abar_0", "Abar_1", "A_2", "A_3"], + ["A_0", "Abar_1", "A_2", "A_3"], + ["Abar_0", "A_1", "A_2", "A_3"], + ["A_0", "A_1", "A_2", "A_3"]] + return combination diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index a27894ae..6293a79d 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -8,7 +8,7 @@ import debug import design from sram_factory import factory from vector import vector - +from tech import layer from globals import OPTS @@ -41,6 +41,10 @@ class port_address(design.design): self.create_wordline_driver() def create_layout(self): + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" self.place_instances() self.route_layout() self.DRC_LVS() @@ -85,11 +89,19 @@ class port_address(design.design): def route_internal(self): for row in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs - decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc() - driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc() - mid1 = decoder_out_pos.scale(0.5, 1) + driver_in_pos.scale(0.5, 0) - mid2 = decoder_out_pos.scale(0.5, 0) + driver_in_pos.scale(0.5, 1) - self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos]) + decoder_out_pin = self.row_decoder_inst.get_pin("decode_{}".format(row)) + decoder_out_pos = decoder_out_pin.rc() + driver_in_pin = self.wordline_driver_inst.get_pin("in_{}".format(row)) + driver_in_pos = driver_in_pin.lc() + self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos) + + self.add_via_stack_center(from_layer=decoder_out_pin.layer, + to_layer=self.route_layer, + offset=decoder_out_pos) + + self.add_via_stack_center(from_layer=driver_in_pin.layer, + to_layer=self.route_layer, + offset=driver_in_pos) def add_modules(self): @@ -97,7 +109,7 @@ class port_address(design.design): num_outputs=self.num_rows) self.add_mod(self.row_decoder) - self.wordline_driver = factory.create(module_type="wordline_driver", + self.wordline_driver = factory.create(module_type="wordline_driver_array", rows=self.num_rows, cols=self.num_cols) self.add_mod(self.wordline_driver) @@ -139,7 +151,6 @@ class port_address(design.design): row_decoder_offset = vector(0, 0) wordline_driver_offset = vector(self.row_decoder.width, 0) - self.wordline_driver_inst.place(wordline_driver_offset) self.row_decoder_inst.place(row_decoder_offset) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 91e20a45..8cc4e272 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -469,45 +469,37 @@ class port_data(design.design): bank_wmask_name = "bank_wmask_{}".format(bit) self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name) - def route_write_mask_and_array_to_write_driver(self,port): - """ Routing of wdriver_sel_{} between write mask AND array and write driver array. Adds layout pin for write - mask AND array output and via for write driver enable """ + def route_write_mask_and_array_to_write_driver(self, port): + """ + Routing of wdriver_sel_{} between write mask AND array and + write driver array. Adds layout pin for write + mask AND array output and via for write driver enable + """ - inst1 = self.write_mask_and_array_inst - inst2 = self.write_driver_array_inst + wmask_inst = self.write_mask_and_array_inst + wdriver_inst = self.write_driver_array_inst - loc = 0 for bit in range(self.num_wmasks): # Bring write mask AND array output pin to port data level - self.copy_layout_pin(inst1, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit)) + self.copy_layout_pin(wmask_inst, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit)) - wmask_out_pin = inst1.get_pin("wmask_out_{0}".format(bit)) - wdriver_en_pin = inst2.get_pin("en_{0}".format(bit)) + wmask_out_pin = wmask_inst.get_pin("wmask_out_{0}".format(bit)) + wdriver_en_pin = wdriver_inst.get_pin("en_{0}".format(bit)) + + wmask_pos = wmask_out_pin.center() + wdriver_pos = wdriver_en_pin.rc() - vector(self.m2_pitch, 0) + mid_pos = vector(wdriver_pos.x, wmask_pos.y) - # The metal2 wdriver_sel_{} wire must hit the en_{} pin after the closest bitline pin that's right of the - # the wdriver_sel_{} pin in the write driver AND array. - if bit == 0: - # When the write mask output pin is right of the bitline, the target is found - while (wmask_out_pin.lx() + self.m2_pitch > inst2.get_pin("data_{0}".format(loc)).rx()): - loc += 1 - length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch - debug.check(loc<=self.num_wmasks, - "Couldn't route the write mask select.") - else: - # Stride by the write size rather than finding the next pin to the right - loc += self.write_size - length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch - - beg_pos = wmask_out_pin.center() - middle_pos = vector(length, wmask_out_pin.cy()) - end_pos = vector(length, wdriver_en_pin.cy()) + # Add driver on mask output + self.add_via_center(layers=self.m1_stack, + offset=wmask_pos) # Add via for the write driver array's enable input self.add_via_center(layers=self.m1_stack, - offset=end_pos) + offset=wdriver_pos) # Route between write mask AND array and write driver array - self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos]) + self.add_wire(self.m1_stack, [wmask_pos, mid_pos, wdriver_pos]) def route_column_mux_to_precharge_array(self, port): """ Routing of BL and BR between col mux and precharge array """ @@ -516,15 +508,12 @@ class port_data(design.design): if self.col_addr_size==0: return - inst1 = self.column_mux_array_inst - inst2 = self.precharge_array_inst + start_bit = 1 if self.port == 0 else 0 - insn2_start_bit = 1 if self.port == 0 else 0 - - self.connect_bitlines(inst1=inst1, - inst2=inst2, + self.connect_bitlines(inst1=self.column_mux_array_inst, + inst2=self.precharge_array_inst, num_bits=self.num_cols, - inst2_start_bit=insn2_start_bit) + inst2_start_bit=start_bit) def route_sense_amp_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver_array.py similarity index 50% rename from compiler/modules/wordline_driver.py rename to compiler/modules/wordline_driver_array.py index 55f5e707..e3d7c4f9 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver_array.py @@ -7,15 +7,12 @@ # import debug import design -import math -from tech import drc +from tech import drc, layer from vector import vector from sram_factory import factory from globals import OPTS -from tech import cell_properties - -class wordline_driver(design.design): +class wordline_driver_array(design.design): """ Creates a Wordline Driver Generates the wordline-driver to drive the bitcell @@ -26,21 +23,9 @@ class wordline_driver(design.design): debug.info(1, "Creating {0}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) - self.bitcell_rows = rows - self.bitcell_cols = cols + self.rows = rows + self.cols = cols - b = factory.create(module_type="bitcell") - try: - self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple - except AttributeError: - self.cell_multiple = 1 - self.cell_height = self.cell_multiple * b.height - - # We may have more than one bitcell per decoder row - self.num_rows = math.ceil(self.bitcell_rows / self.cell_multiple) - # We will place this many final decoders per row - self.decoders_per_row = math.ceil(self.bitcell_rows / self.num_rows) - self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -51,7 +36,10 @@ class wordline_driver(design.design): self.create_drivers() def create_layout(self): - self.setup_layout_constants() + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" self.place_drivers() self.route_layout() self.route_vdd_gnd() @@ -61,104 +49,99 @@ class wordline_driver(design.design): def add_pins(self): # inputs to wordline_driver. - for i in range(self.bitcell_rows): + for i in range(self.rows): self.add_pin("in_{0}".format(i), "INPUT") # Outputs from wordline_driver. - for i in range(self.bitcell_rows): + for i in range(self.rows): self.add_pin("wl_{0}".format(i), "OUTPUT") self.add_pin("en", "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") def add_modules(self): - self.and2 = factory.create(module_type="pand2", - height=self.cell_height, - size=self.bitcell_cols) - self.add_mod(self.and2) + self.wl_driver = factory.create(module_type="wordline_driver", + size=self.cols) + self.add_mod(self.wl_driver) def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - - # Find the x offsets for where the vias/pins should be placed - xoffset_list = [self.and_inst[0].lx()] - for num in range(self.bitcell_rows): - # this will result in duplicate polygons for rails, but who cares - - # use the inverter offset even though it will be the and's too - (gate_offset, y_dir) = self.get_gate_offset(0, - self.and2.height, - num) - # Route both supplies + if OPTS.tech_name == "s8": for name in ["vdd", "gnd"]: - supply_pin = self.and_inst[num].get_pin(name) - - # Add pins in two locations - for xoffset in xoffset_list: - pin_pos = vector(xoffset, supply_pin.cy()) - self.add_power_pin(name, pin_pos) + supply_pins = self.wld_inst[0].get_pins(name) + for pin in supply_pins: + self.add_layout_pin_segment_center(text=name, + layer=pin.layer, + start=pin.bc(), + end=vector(pin.cx(), self.height)) + else: + # Find the x offsets for where the vias/pins should be placed + xoffset_list = [self.wld_inst[0].rx()] + for num in range(self.rows): + # this will result in duplicate polygons for rails, but who cares + + # use the inverter offset even though it will be the and's too + (gate_offset, y_dir) = self.get_gate_offset(0, + self.wl_driver.height, + num) + # Route both supplies + for name in ["vdd", "gnd"]: + supply_pin = self.wld_inst[num].get_pin(name) + + # Add pins in two locations + for xoffset in xoffset_list: + pin_pos = vector(xoffset, supply_pin.cy()) + self.add_power_pin(name, pin_pos) def create_drivers(self): - self.and_inst = [] - for row in range(self.bitcell_rows): + self.wld_inst = [] + for row in range(self.rows): name_and = "wl_driver_and{}".format(row) # add and2 - self.and_inst.append(self.add_inst(name=name_and, - mod=self.and2)) + self.wld_inst.append(self.add_inst(name=name_and, + mod=self.wl_driver)) self.connect_inst(["in_{0}".format(row), "en", "wl_{0}".format(row), "vdd", "gnd"]) - def setup_layout_constants(self): - # We may have more than one bitcell per decoder row - self.driver_rows = math.ceil(self.bitcell_rows / self.cell_multiple) - # We will place this many final decoders per row - self.decoders_per_row = math.ceil(self.bitcell_rows / self.driver_rows) - def place_drivers(self): + for row in range(self.rows): + if (row % 2): + y_offset = self.wl_driver.height * (row + 1) + inst_mirror = "MX" + else: + y_offset = self.wl_driver.height * row + inst_mirror = "R0" + + and2_offset = [self.wl_driver.width, y_offset] + + # add and2 + self.wld_inst[row].place(offset=and2_offset, + mirror=inst_mirror) + # Leave a well gap to separate the bitcell array well from this well well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") - - self.width = self.decoders_per_row * self.and2.width + well_gap - self.height = self.and2.height * self.driver_rows - - for inst_index in range(self.bitcell_rows): - row = math.floor(inst_index / self.decoders_per_row) - dec = inst_index % self.decoders_per_row - - if (row % 2): - y_offset = self.and2.height * (row + 1) - inst_mirror = "MX" - else: - y_offset = self.and2.height * row - inst_mirror = "R0" - - x_offset = dec * self.and2.width - and2_offset = [x_offset, y_offset] - - # add and2 - self.and_inst[inst_index].place(offset=and2_offset, - mirror=inst_mirror) + self.width = self.wl_driver.width + well_gap + self.height = self.wl_driver.height * self.rows def route_layout(self): """ Route all of the signals """ # Wordline enable connection - en_pin = self.and_inst[0].get_pin("B") + en_pin = self.wld_inst[0].get_pin("B") en_bottom_pos = vector(en_pin.lx(), 0) en_pin = self.add_layout_pin(text="en", layer="m2", offset=en_bottom_pos, height=self.height) - for inst_index in range(self.bitcell_rows): - and_inst = self.and_inst[inst_index] - row = math.floor(inst_index / self.decoders_per_row) + for row in range(self.rows): + and_inst = self.wld_inst[row] # Drop a via b_pin = and_inst.get_pin("B") @@ -167,18 +150,12 @@ class wordline_driver(design.design): offset=b_pin.center()) # connect the decoder input pin to and2 A - a_pin = and_inst.get_pin("A") - a_pos = a_pin.center() - # must under the clk line in M1 - self.add_layout_pin_segment_center(text="in_{0}".format(row), - layer="m1", - start=vector(0, a_pos.y), - end=a_pos) + self.copy_layout_pin(and_inst, "A", "in_{0}".format(row)) # output each WL on the right wl_offset = and_inst.get_pin("Z").rc() self.add_layout_pin_segment_center(text="wl_{0}".format(row), - layer="m1", + layer=self.route_layer, start=wl_offset, end=wl_offset - vector(self.m1_width, 0)) @@ -189,7 +166,7 @@ class wordline_driver(design.design): """ stage_effort_list = [] - stage1 = self.and2.get_stage_effort(external_cout, inp_is_rise) + stage1 = self.wl_driver.get_stage_effort(external_cout, inp_is_rise) stage_effort_list.append(stage1) return stage_effort_list @@ -200,5 +177,5 @@ class wordline_driver(design.design): the enable connections in the bank """ # The enable is connected to a and2 for every row. - total_cin = self.and2.get_cin() * self.rows + total_cin = self.wl_driver.get_cin() * self.rows return total_cin diff --git a/compiler/options.py b/compiler/options.py index d891ebd9..1c48d63d 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -134,6 +134,10 @@ class options(optparse.Values): dff_array = "dff_array" dff = "dff" dummy_bitcell = "dummy_bitcell" + inv_dec = "pinv" + nand2_dec = "pnand2" + nand3_dec = "pnand3" + nand4_dec = "pnand4" # Not available right now precharge_array = "precharge_array" ptx = "ptx" replica_bitcell = "replica_bitcell" diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 41189876..435ace1f 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -13,7 +13,7 @@ from sram_factory import factory class pand2(pgate.pgate): """ - This is a simple buffer used for driving loads. + This is an AND (or NAND) with configurable drive strength. """ def __init__(self, name, size=1, height=None, vertical=False, add_wells=True): debug.info(1, "Creating pand2 {}".format(name)) diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 266400c9..578a11c4 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -17,13 +17,13 @@ class pdriver(pgate.pgate): sized for driving a load. """ - def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None, add_wells=True): + def __init__(self, name, inverting=False, fanout=0, size_list=None, height=None, add_wells=True): debug.info(1, "creating pdriver {}".format(name)) self.stage_effort = 3 self.height = height - self.neg_polarity = neg_polarity + self.inverting = inverting self.size_list = size_list self.fanout = fanout @@ -31,8 +31,8 @@ class pdriver(pgate.pgate): debug.error("Either fanout or size list must be specified.", -1) if self.size_list and self.fanout != 0: debug.error("Cannot specify both size_list and fanout.", -1) - if self.size_list and self.neg_polarity: - debug.error("Cannot specify both size_list and neg_polarity.", -1) + if self.size_list and self.inverting: + debug.error("Cannot specify both size_list and inverting.", -1) # Creates the netlist and layout pgate.pgate.__init__(self, name, height, add_wells) @@ -47,9 +47,9 @@ class pdriver(pgate.pgate): int(round(self.fanout ** (1 / self.stage_effort)))) # Increase the number of stages if we need to fix polarity - if self.neg_polarity and (self.num_stages % 2 == 0): + if self.inverting and (self.num_stages % 2 == 0): self.num_stages += 1 - elif not self.neg_polarity and (self.num_stages % 2): + elif not self.inverting and (self.num_stages % 2): self.num_stages += 1 self.size_list = [] diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index cdb89fcb..a8c45641 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -44,7 +44,6 @@ class pgate(design.design): self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer)) # This is the space from a S/D contact to the supply rail - # Assume the contact starts at the active edge contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space # This is a poly-to-poly of a flipped cell poly_to_poly_gate_space = self.poly_extend_active + self.poly_space diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py new file mode 100644 index 00000000..efc21074 --- /dev/null +++ b/compiler/pgates/pinv_dec.py @@ -0,0 +1,217 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import contact +import pinv +import debug +from tech import drc, parameter +from vector import vector +from globals import OPTS +from sram_factory import factory + +if(OPTS.tech_name == "s8"): + from tech import nmos_bins, pmos_bins, accuracy_requirement + + +class pinv_dec(pinv.pinv): + """ + This is another version of pinv but with layout for the decoder. + Other stuff is the same (netlist, sizes, etc.) + """ + + def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True): + + debug.info(2, + "creating pinv_dec structure {0} with size of {1}".format(name, + size)) + if not height: + b = factory.create(module_type="bitcell") + self.cell_height = b.height + else: + self.cell_height = height + + # Inputs to cells are on input layer + # Outputs from cells are on output layer + if OPTS.tech_name == "s8": + self.supply_layer = "m1" + else: + self.supply_layer = "m2" + + pinv.pinv.__init__(self, name, size, beta, self.cell_height, add_wells) + + def determine_tx_mults(self): + """ + Determines the number of fingers needed to achieve the size within + the height constraint. This may fail if the user has a tight height. + """ + + # This is always 1 tx, because we have horizontal transistors. + self.tx_mults = 1 + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") + if OPTS.tech_name == "s8": + (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) + (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) + return + + # Over-ride the route input gate to call the horizontal version. + # Other top-level netlist and layout functions are not changed. + def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None): + """ + Route the input gate to the left side of the cell for access. + Position is actually ignored and is left to be compatible with the pinv. + """ + + nmos_gate_pin = nmos_inst.get_pin("G") + pmos_gate_pin = pmos_inst.get_pin("G") + + # Check if the gates are aligned and give an error if they aren't! + if nmos_gate_pin.ll().y != pmos_gate_pin.ll().y: + self.gds_write("unaliged_gates.gds") + debug.check(nmos_gate_pin.ll().y == pmos_gate_pin.ll().y, + "Connecting unaligned gates not supported. See unaligned_gates.gds.") + + # Pick point on the left of NMOS and up to PMOS + nmos_gate_pos = nmos_gate_pin.rc() + pmos_gate_pos = pmos_gate_pin.lc() + self.add_path("poly", [nmos_gate_pos, pmos_gate_pos]) + + # Center is completely symmetric. + contact_width = contact.poly_contact.width + contact_offset = nmos_gate_pin.lc() \ + - vector(self.poly_extend_active + 0.5 * contact_width, 0) + via = self.add_via_stack_center(from_layer="poly", + to_layer=self.route_layer, + offset=contact_offset, + directions=directions) + self.add_path("poly", [contact_offset, nmos_gate_pin.lc()]) + + self.add_layout_pin_rect_center(text=name, + layer=self.route_layer, + offset=contact_offset, + width=via.mod.second_layer_width, + height=via.mod.second_layer_height) + + def determine_width(self): + self.width = self.pmos_inst.rx() + self.well_extend_active + + def extend_wells(self): + """ Extend bottom to top for each well. """ + + from tech import layer + if "pwell" in layer: + ll = self.nmos_inst.ll() - self.nmos_inst.mod.active_offset + ur = self.nmos_inst.ur() + self.nmos_inst.mod.active_offset + self.add_rect(layer="pwell", + offset=ll, + width=ur.x - ll.x, + height=self.height - ll.y) + + if "nwell" in layer: + ll = self.pmos_inst.ll() - self.pmos_inst.mod.active_offset + ur = self.pmos_inst.ur() + self.pmos_inst.mod.active_offset + self.add_rect(layer="nwell", + offset=ll - vector(self.nwell_enclose_active, 0), + width=ur.x - ll.x + self.nwell_enclose_active, + height=self.height - ll.y + 2 * self.nwell_enclose_active) + + def place_ptx(self): + """ + """ + + # offset so that the input contact is over from the left edge by poly spacing + x_offset = self.nmos.active_offset.y + contact.poly_contact.width + self.poly_space + # center the transistor in the y-dimension + y_offset = self.nmos.width + self.active_space + self.nmos_pos = vector(x_offset, y_offset) + self.nmos_inst.place(self.nmos_pos) + self.nmos_inst.place(self.nmos_pos, + rotate=270) + # place PMOS so it is half a poly spacing down from the top + xoffset = self.nmos_inst.height + 2 * self.poly_extend_active + 2 * self.well_extend_active + drc("pwell_to_nwell") + self.pmos_pos = self.nmos_pos + vector(xoffset, 0) + self.pmos_inst.place(self.pmos_pos, + rotate=270) + + # Output position will be in between the PMOS and NMOS drains + pmos_drain_pos = self.pmos_inst.get_pin("D").center() + nmos_drain_pos = self.nmos_inst.get_pin("D").center() + self.output_pos = vector(0.5 * (pmos_drain_pos.x + nmos_drain_pos.x), nmos_drain_pos.y) + + def route_outputs(self): + """ + Route the output (drains) together. + Optionally, routes output to edge. + """ + + # Get the drain pin + nmos_drain_pin = self.nmos_inst.get_pin("D") + + # Pick point at right most of NMOS and connect over to PMOS + nmos_drain_pos = nmos_drain_pin.lc() + right_side = vector(self.width, nmos_drain_pos.y) + + self.add_layout_pin_segment_center("Z", + self.route_layer, + nmos_drain_pos, + right_side) + + def add_well_contacts(self): + """ Add n/p well taps to the layout and connect to supplies """ + + source_pos = self.pmos_inst.get_pin("S").center() + contact_pos = vector(source_pos.x, self.height) + self.nwell_contact = self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="n", + well_type="n") + self.add_via_stack_center(offset=contact_pos, + from_layer=self.active_stack[2], + to_layer=self.supply_layer) + + source_pos = self.nmos_inst.get_pin("S").center() + contact_pos = vector(source_pos.x, self.height) + self.pwell_contact= self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="p", + well_type="p") + self.add_via_stack_center(offset=contact_pos, + from_layer=self.active_stack[2], + to_layer=self.supply_layer) + + def route_supply_rails(self): + pin = self.nmos_inst.get_pin("S") + source_pos = pin.center() + bottom_pos = source_pos.scale(1, 0) + top_pos = bottom_pos + vector(0, self.height) + self.add_layout_pin_segment_center("gnd", + self.supply_layer, + start=bottom_pos, + end=top_pos) + + pin = self.pmos_inst.get_pin("S") + source_pos = pin.center() + bottom_pos = source_pos.scale(1, 0) + top_pos = bottom_pos + vector(0, self.height) + self.add_layout_pin_segment_center("vdd", + self.supply_layer, + start=bottom_pos, + end=top_pos) + + def connect_rails(self): + """ Connect the nmos and pmos to its respective power rails """ + + source_pos = self.nmos_inst.get_pin("S").center() + self.add_via_stack_center(offset=source_pos, + from_layer=self.route_layer, + to_layer=self.supply_layer) + + source_pos = self.pmos_inst.get_pin("S").center() + self.add_via_stack_center(offset=source_pos, + from_layer=self.route_layer, + to_layer=self.supply_layer) + diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 85b3f0b6..6cbd7cca 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -212,7 +212,10 @@ class pnand3(pgate.pgate): pmos_drain_bottom = self.pmos1_inst.get_pin("D").by() self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space + # This is a more compact offset, but the bottom one works better in the decoders to "center" the pins + # in the height of the gates self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space + # self.inputA_yoffset = self.output_yoffset - self.m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index c584e70b..f6716a1f 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -386,10 +386,12 @@ class ptx(design.design): well_ll = center_pos - vector(0.5 * self.well_width, 0.5 * self.well_height) if well_name in layer: - self.add_rect(layer=well_name, - offset=well_ll, - width=self.well_width, - height=self.well_height) + well = self.add_rect(layer=well_name, + offset=well_ll, + width=self.well_width, + height=self.well_height) + setattr(self, well_name, well) + if "vtg" in layer: self.add_rect(layer="vtg", offset=well_ll, diff --git a/compiler/pgates/wordline_driver.py b/compiler/pgates/wordline_driver.py new file mode 100644 index 00000000..1b035e20 --- /dev/null +++ b/compiler/pgates/wordline_driver.py @@ -0,0 +1,155 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from vector import vector +import design +from sram_factory import factory +from globals import OPTS +from tech import layer + + +class wordline_driver(design.design): + """ + This is an AND (or NAND) with configurable drive strength to drive the wordlines. + It is matched to the bitcell height. + """ + def __init__(self, name, size=1, height=None): + debug.info(1, "Creating wordline_driver {}".format(name)) + self.add_comment("size: {}".format(size)) + design.design.__init__(self, name) + + if height is None: + b = factory.create(module_type="bitcell") + self.height = b.height + else: + self.height = height + self.size = size + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand2_dec") + self.height = self.nand.height + else: + self.nand = factory.create(module_type="nand2_dec", + height=self.height) + + self.driver = factory.create(module_type="inv_dec", + size=self.size, + height=self.nand.height) + + self.add_mod(self.nand) + self.add_mod(self.driver) + + def create_layout(self): + self.width = self.nand.width + self.driver.width + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + + self.place_insts() + self.route_wires() + self.add_layout_pins() + self.route_supply_rails() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="wld_nand", + mod=self.nand) + self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) + + self.driver_inst = self.add_inst(name="wl_driver", + mod=self.driver) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) + + # Add INV to the right + self.driver_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.driver_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + + y_offset = self.height + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, y_offset), + width=self.width) + + def route_wires(self): + + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.driver_inst.get_pin("A") + if OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + + def add_layout_pins(self): + pin = self.driver_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A", "B"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.driver.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + + stage2 = self.driver.get_stage_effort(external_cout, stage1.is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 110dbe15..df6b9d99 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -77,7 +77,9 @@ class sram_factory: """ tech_module_type, tm_overridden = self.get_techmodule_type(module_type) user_module_type, um_overridden = self.get_usermodule_type(module_type) - + #print(module_type, tech_module_type, tm_overridden) + #print(module_type, user_module_type, um_overridden) + # overridden user modules have priority if um_overridden: real_module_type = user_module_type diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index 0acc8926..ed15770d 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -35,7 +35,7 @@ class library_lvs_test(openram_test): debug.error("Missing GDS file {}".format(gds_name)) if not os.path.isfile(sp_name): lvs_errors += 1 - debug.error("Missing SPICE file {}".format(gds_name)) + debug.error("Missing SPICE file {}".format(sp_name)) drc_errors += verify.run_drc(name, gds_name) lvs_errors += verify.run_lvs(f, gds_name, sp_name) diff --git a/compiler/tests/04_pand2_dec_test.py b/compiler/tests/04_pand2_dec_test.py new file mode 100755 index 00000000..92b80203 --- /dev/null +++ b/compiler/tests/04_pand2_dec_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class pand2_dec_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + global verify + import verify + + import pand2_dec + + debug.info(2, "Testing pand2 gate 4x") + a = pand2_dec.pand2_dec(name="pand2x4", size=4) + self.local_check(a) + + globals.end_openram() + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pand3_dec_test.py b/compiler/tests/04_pand3_dec_test.py new file mode 100755 index 00000000..78e576ed --- /dev/null +++ b/compiler/tests/04_pand3_dec_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class pand3_dec_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + global verify + import verify + + import pand3_dec + + debug.info(2, "Testing pand3 gate 4x") + a = pand3_dec.pand3_dec(name="pand3x4", size=4) + self.local_check(a) + + globals.end_openram() + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pand4_dec_test.py b/compiler/tests/04_pand4_dec_test.py new file mode 100755 index 00000000..78e576ed --- /dev/null +++ b/compiler/tests/04_pand4_dec_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class pand3_dec_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + global verify + import verify + + import pand3_dec + + debug.info(2, "Testing pand3 gate 4x") + a = pand3_dec.pand3_dec(name="pand3x4", size=4) + self.local_check(a) + + globals.end_openram() + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pdriver_test.py b/compiler/tests/04_pdriver_test.py index e65b6fad..41af86f6 100755 --- a/compiler/tests/04_pdriver_test.py +++ b/compiler/tests/04_pdriver_test.py @@ -32,13 +32,13 @@ class pdriver_test(openram_test): c = factory.create(module_type="pdriver", fanout = 50) self.local_check(c) - d = factory.create(module_type="pdriver", fanout = 50, neg_polarity = True) + d = factory.create(module_type="pdriver", fanout = 50, inverting = True) self.local_check(d) e = factory.create(module_type="pdriver", fanout = 64) self.local_check(e) - f = factory.create(module_type="pdriver", fanout = 64, neg_polarity = True) + f = factory.create(module_type="pdriver", fanout = 64, inverting = True) self.local_check(f) globals.end_openram() diff --git a/compiler/tests/04_pinv_dec_1x_test.py b/compiler/tests/04_pinv_dec_1x_test.py new file mode 100755 index 00000000..8876ab58 --- /dev/null +++ b/compiler/tests/04_pinv_dec_1x_test.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class pinv_dec_1x_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(2, "Checking 1x size decoder inverter") + tx = factory.create(module_type="pinv_dec", size=1) + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/04_wordline_driver_test.py similarity index 93% rename from compiler/tests/08_wordline_driver_test.py rename to compiler/tests/04_wordline_driver_test.py index 8a18a59d..ada65db7 100755 --- a/compiler/tests/08_wordline_driver_test.py +++ b/compiler/tests/04_wordline_driver_test.py @@ -25,7 +25,7 @@ class wordline_driver_test(openram_test): # check wordline driver for single port debug.info(2, "Checking driver") - tx = factory.create(module_type="wordline_driver", rows=8, cols=32) + tx = factory.create(module_type="wordline_driver") self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 07d995f9..7d155b01 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -21,11 +21,17 @@ class hierarchical_decoder_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + # Checks 2x4 and 2-input NAND decoder debug.info(1, "Testing 16 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) self.local_check(a) - + # Checks 2x4 and 2-input NAND decoder with non-power-of-two debug.info(1, "Testing 17 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=17) diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index ebb06330..94fd4838 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -21,7 +21,12 @@ class hierarchical_predecode2x4_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # checking hierarchical precode 2x4 for single port + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + debug.info(1, "Testing sample for hierarchy_predecode2x4") a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index 63acc416..33f94d9d 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -21,7 +21,12 @@ class hierarchical_predecode3x8_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # checking hierarchical precode 3x8 for single port + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + debug.info(1, "Testing sample for hierarchy_predecode3x8") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode4x16_test.py b/compiler/tests/06_hierarchical_predecode4x16_test.py new file mode 100755 index 00000000..b4ebda38 --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode4x16_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +@unittest.skip("SKIPPING hierarchical_predecode4x16_test") +class hierarchical_predecode4x16_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(1, "Testing sample for hierarchy_predecode4x16") + a = factory.create(module_type="hierarchical_predecode4x16") + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_wordline_driver_pbitcell_test.py b/compiler/tests/08_wordline_driver_array_pbitcell_test.py similarity index 87% rename from compiler/tests/08_wordline_driver_pbitcell_test.py rename to compiler/tests/08_wordline_driver_array_pbitcell_test.py index 3dd5933d..267aaddf 100755 --- a/compiler/tests/08_wordline_driver_pbitcell_test.py +++ b/compiler/tests/08_wordline_driver_array_pbitcell_test.py @@ -15,9 +15,8 @@ from globals import OPTS from sram_factory import factory import debug -#@unittest.skip("SKIPPING 04_driver_test") -class wordline_driver_pbitcell_test(openram_test): +class wordline_driver_array_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -31,7 +30,7 @@ class wordline_driver_pbitcell_test(openram_test): factory.reset() debug.info(2, "Checking driver (multi-port case)") - tx = factory.create(module_type="wordline_driver", rows=8, cols=64) + tx = factory.create(module_type="wordline_driver_array", rows=8, cols=64) self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/08_wordline_driver_array_test.py b/compiler/tests/08_wordline_driver_array_test.py new file mode 100755 index 00000000..3491cc4f --- /dev/null +++ b/compiler/tests/08_wordline_driver_array_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class wordline_driver_array_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check wordline driver for single port + debug.info(2, "Checking driver") + tx = factory.create(module_type="wordline_driver_array", rows=8, cols=32) + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index 6201de6a..e9e282a5 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -20,10 +20,10 @@ class port_data_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1w_1r" - OPTS.num_rw_ports = 0 + 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 c = sram_config(word_size=4, num_words=16) diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index 38a847a9..6cff481b 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -25,28 +25,28 @@ class single_bank_test(openram_test): c = sram_config(word_size=4, num_words=16) - # c.words_per_row=1 - # factory.reset() - # c.recompute_sizes() - # debug.info(1, "No column mux") - # a = factory.create("bank", sram_config=c) - # self.local_check(a) + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) - # c.num_words=32 - # c.words_per_row=2 - # factory.reset() - # c.recompute_sizes() - # debug.info(1, "Two way column mux") - # a = factory.create("bank", sram_config=c) - # self.local_check(a) + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) - # c.num_words=64 - # c.words_per_row=4 - # factory.reset() - # c.recompute_sizes() - # debug.info(1, "Four way column mux") - # a = factory.create("bank", sram_config=c) - # self.local_check(a) + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) c.word_size=2 c.num_words=128 diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 99d534d0..4cae1a9c 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -60,6 +60,7 @@ class openram_test(unittest.TestCase): #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) debug.warning("DRC failed but LVS passed: {}".format(a.name)) + #self.fail("DRC failed but LVS passed: {}".format(a.name)) elif drc_result != 0: #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) From b00163e4e1e22883ed910826ed7ed5e880cc9088 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Fri, 29 May 2020 13:50:34 -0700 Subject: [PATCH 024/206] lvs fix for regression tests --- compiler/modules/replica_column.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 2e3d207e..4ea1b7df 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -114,7 +114,6 @@ class replica_column(design.design): mod=self.dummy_cell) self.connect_inst(self.get_bitcell_pins(0, row)) - def place_instances(self): from tech import cell_properties # Flip the mirrors if we have an odd number of replica+dummy rows at the bottom @@ -149,20 +148,30 @@ class replica_column(design.design): self.cell_inst[row].place(offset=offset, mirror=dir_key) - - def add_layout_pins(self): """ Add the layout pins """ for bl_name in self.cell.get_all_bitline_names(): - bl_pin = self.cell_inst[1].get_pin(bl_name) + bl_pin = self.cell_inst[0].get_pin(bl_name) self.add_layout_pin(text=bl_name, layer=bl_pin.layer, offset=bl_pin.ll(), width=bl_pin.width(), height=self.height) - for row in range(1, self.total_size - 1): + try: + end_caps_enabled = cell_properties.bitcell.end_caps + except AttributeError: + end_caps_enabled = False + + if end_caps_enabled: + row_range_max = self.total_size - 1 + row_range_min = 1 + else: + row_range_max = self.total_size + row_range_min = 0 + + for row in range(row_range_min, row_range_max): for wl_name in self.cell.get_all_wl_names(): wl_pin = self.cell_inst[row].get_pin(wl_name) self.add_layout_pin(text="{0}_{1}".format(wl_name,row), @@ -172,7 +181,7 @@ class replica_column(design.design): height=wl_pin.height()) # For every second row and column, add a via for gnd and vdd - for row in range(1, self.total_size - 1): + for row in range(row_range_min, row_range_max): inst = self.cell_inst[row] for pin_name in ["vdd", "gnd"]: self.copy_layout_pin(inst, pin_name) From 82dc937768e2e88fae741600d8c604348fd7093a Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 29 May 2020 16:53:47 -0700 Subject: [PATCH 025/206] Add missing vias by using via stack function --- compiler/modules/port_data.py | 74 ++++++++++++++++----------------- compiler/pgates/precharge.py | 77 +++++++++++++---------------------- 2 files changed, 65 insertions(+), 86 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 8cc4e272..44bb8ed8 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -534,11 +534,14 @@ class port_data(design.design): else: start_bit=0 - self.channel_route_bitlines(inst1=inst1, - inst1_bls_template=inst1_bls_templ, - inst2=inst2, - num_bits=self.word_size, - inst1_start_bit=start_bit) + # This could be a channel route, but in some techs the bitlines + # are too close together. + print("SA to precharge") + self.connect_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + num_bits=self.word_size, + inst1_start_bit=start_bit) def route_write_driver_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ @@ -558,10 +561,13 @@ class port_data(design.design): else: start_bit=0 - self.channel_route_bitlines(inst1=inst1, inst2=inst2, - num_bits=self.word_size, - inst1_bls_template=inst1_bls_templ, - inst1_start_bit=start_bit) + # This could be a channel route, but in some techs the bitlines + # are too close together. + print("WD to precharge") + self.connect_bitlines(inst1=inst1, inst2=inst2, + num_bits=self.word_size, + inst1_bls_template=inst1_bls_templ, + inst1_start_bit=start_bit) def route_write_driver_to_sense_amp(self, port): """ Routing of BL and BR between write driver and sense amp """ @@ -569,11 +575,12 @@ class port_data(design.design): inst1 = self.write_driver_array_inst inst2 = self.sense_amp_array_inst - # These should be pitch matched in the cell library, - # but just in case, do a channel route. - self.channel_route_bitlines(inst1=inst1, - inst2=inst2, - num_bits=self.word_size) + # This could be a channel route, but in some techs the bitlines + # are too close together. + print("WD to SA") + self.connect_bitlines(inst1=inst1, + inst2=inst2, + num_bits=self.word_size) def route_bitline_pins(self): """ Add the bitline pins for the given port """ @@ -676,10 +683,9 @@ class port_data(design.design): Route the bl and br of two modules using the channel router. """ - bot_inst_group, top_inst_group = self._group_bitline_instances( - inst1, inst2, num_bits, - inst1_bls_template, inst1_start_bit, - inst2_bls_template, inst2_start_bit) + bot_inst_group, top_inst_group = self._group_bitline_instances(inst1, inst2, num_bits, + inst1_bls_template, inst1_start_bit, + inst2_bls_template, inst2_start_bit) # Channel route each mux separately since we don't minimize the number # of tracks in teh channel router yet. If we did, we could route all the bits at once! @@ -688,13 +694,8 @@ class port_data(design.design): bottom_names = self._get_bitline_pins(bot_inst_group, bit) top_names = self._get_bitline_pins(top_inst_group, bit) - if bottom_names[0].layer == "m2": - bitline_dirs = ("H", "V") - elif bottom_names[0].layer == "m1": - bitline_dirs = ("V", "H") - route_map = list(zip(bottom_names, top_names)) - self.create_horizontal_channel_route(route_map, offset, self.m1_stack, bitline_dirs) + self.create_horizontal_channel_route(route_map, offset, self.m1_stack) def connect_bitlines(self, inst1, inst2, num_bits, inst1_bls_template="{inst}_{bit}", @@ -707,26 +708,23 @@ class port_data(design.design): in the middle between the two modules (if needed). """ - bot_inst_group, top_inst_group = self._group_bitline_instances( - inst1, inst2, num_bits, - inst1_bls_template, inst1_start_bit, - inst2_bls_template, inst2_start_bit) + bot_inst_group, top_inst_group = self._group_bitline_instances(inst1, inst2, num_bits, + inst1_bls_template, inst1_start_bit, + inst2_bls_template, inst2_start_bit) for col in range(num_bits): + print(col) bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col) top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col) bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc() top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc() - - yoffset = 0.5 * (top_bl.y + bot_bl.y) - self.add_path("m2", [bot_bl, - vector(bot_bl.x, yoffset), - vector(top_bl.x, yoffset), - top_bl]) - self.add_path("m2", [bot_br, - vector(bot_br.x, yoffset), - vector(top_br.x, yoffset), - top_br]) + + print("BL", bot_bl, top_bl) + print(bot_bl_pin, top_bl_pin) + print("BR", bot_br, top_br) + print(bot_br_pin, top_br_pin) + self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V") + self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V") def graph_exclude_precharge(self): """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index d47e445f..ae314452 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -105,21 +105,19 @@ class precharge(design.design): # center of vdd rail pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) - self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos]) + self.add_path(self.bitline_layer, [pmos_pin.center(), pmos_vdd_pos]) - # if enable is not on M1, the supply can be - if self.en_layer != "m1": - self.add_via_center(layers=self.m1_stack, - offset=pmos_vdd_pos) - + self.add_via_stack_center(from_layer=self.bitline_layer, + to_layer=self.en_layer, + offset=pmos_vdd_pos) self.add_power_pin("vdd", self.well_contact_pos, directions=("V", "V")) - - # Hack for li layers - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=self.well_contact_pos) + + self.add_via_stack_center(from_layer=pmos_pin.layer, + to_layer=self.bitline_layer, + offset=pmos_pin.center(), + directions=("V", "V")) def create_ptx(self): """ @@ -199,14 +197,9 @@ class precharge(design.design): # midway in the 4 M2 tracks offset = self.lower_pmos_inst.get_pin("G").ul() \ + vector(0, 0.5 * self.m2_pitch) - self.add_via_center(layers=self.poly_stack, - offset=offset) - if self.en_layer == "m2": - self.add_via_center(layers=self.m1_stack, - offset=offset) - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=offset) + self.add_via_stack_center(from_layer="poly", + to_layer=self.en_layer, + offset=offset) # adds the en rail on metal1 self.add_layout_pin_segment_center(text="en_bar", @@ -225,13 +218,13 @@ class precharge(design.design): self.nwell_extend_active self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \ vector(0, offset_height) - self.add_via_center(layers=self.active_stack, - offset=self.well_contact_pos, - implant_type="n", - well_type="n") - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=self.well_contact_pos) + self.well_contact = self.add_via_center(layers=self.active_stack, + offset=self.well_contact_pos, + implant_type="n", + well_type="n") + self.add_via_stack_center(from_layer=self.active_stack[2], + to_layer=self.bitline_layer, + offset=self.well_contact_pos) self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space @@ -288,31 +281,19 @@ class precharge(design.design): Adds contacts/via from metal1 to metal2 for bit-lines """ - # No contacts needed if M1 - if self.bitline_layer == "m1": - return - # BL - lower_pin = self.lower_pmos_inst.get_pin("S") - self.lower_via = self.add_via_center(layers=self.m1_stack, - offset=lower_pin.center(), - directions=("V", "V")) + for lower_pin in [self.lower_pmos_inst.get_pin("S"), self.lower_pmos_inst.get_pin("D")]: + self.add_via_stack_center(from_layer=lower_pin.layer, + to_layer=self.bitline_layer, + offset=lower_pin.center(), + directions=("V", "V")) - lower_pin = self.lower_pmos_inst.get_pin("D") - self.lower_via = self.add_via_center(layers=self.m1_stack, - offset=lower_pin.center(), - directions=("V", "V")) - # BR - upper_pin = self.upper_pmos1_inst.get_pin("S") - self.upper_via2 = self.add_via_center(layers=self.m1_stack, - offset=upper_pin.center(), - directions=("V", "V")) - - upper_pin = self.upper_pmos2_inst.get_pin("D") - self.upper_via2 = self.add_via_center(layers=self.m1_stack, - offset=upper_pin.center(), - directions=("V", "V")) + for upper_pin in [self.upper_pmos1_inst.get_pin("S"), self.upper_pmos2_inst.get_pin("D")]: + self.add_via_stack_center(from_layer=upper_pin.layer, + to_layer=self.bitline_layer, + offset=upper_pin.center(), + directions=("V", "V")) def connect_pmos(self, pmos_pin, bit_xoffset): """ From 496a24389c4e95f780a71a1f73959366a9744f4d Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 29 May 2020 16:57:47 -0700 Subject: [PATCH 026/206] Remove prints --- compiler/modules/port_data.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 44bb8ed8..d544b6be 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -536,7 +536,6 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - print("SA to precharge") self.connect_bitlines(inst1=inst1, inst1_bls_template=inst1_bls_templ, inst2=inst2, @@ -563,7 +562,6 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - print("WD to precharge") self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, inst1_bls_template=inst1_bls_templ, @@ -577,7 +575,6 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - print("WD to SA") self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size) @@ -713,16 +710,11 @@ class port_data(design.design): inst2_bls_template, inst2_start_bit) for col in range(num_bits): - print(col) bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col) top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col) bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc() top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc() - print("BL", bot_bl, top_bl) - print(bot_bl_pin, top_bl_pin) - print("BR", bot_br, top_br) - print(bot_br_pin, top_br_pin) self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V") self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V") From b3b03d4d397e82dbeb357ac01f381018226e2b27 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 1 Jun 2020 16:46:00 -0700 Subject: [PATCH 027/206] Hard cells can accept height parameter too. --- compiler/custom/{pand2_dec.py => and2_dec.py} | 33 +++++++++++------- compiler/custom/{pand3_dec.py => and3_dec.py} | 29 ++++++++++------ compiler/custom/{pand4_dec.py => and4_dec.py} | 34 ++++++++++++------- compiler/custom/nand2_dec.py | 10 +++++- compiler/custom/nand3_dec.py | 10 +++++- compiler/custom/nand4_dec.py | 10 +++++- compiler/modules/hierarchical_decoder.py | 19 ++++------- compiler/modules/hierarchical_predecode.py | 6 ++-- compiler/pgates/wordline_driver.py | 8 ++--- ..._pand3_dec_test.py => 04_and2_dec_test.py} | 8 ++--- ..._pand4_dec_test.py => 04_and3_dec_test.py} | 8 ++--- ..._pand2_dec_test.py => 04_and4_dec_test.py} | 8 ++--- 12 files changed, 112 insertions(+), 71 deletions(-) rename compiler/custom/{pand2_dec.py => and2_dec.py} (89%) rename compiler/custom/{pand3_dec.py => and3_dec.py} (90%) rename compiler/custom/{pand4_dec.py => and4_dec.py} (89%) rename compiler/tests/{04_pand3_dec_test.py => 04_and2_dec_test.py} (85%) rename compiler/tests/{04_pand4_dec_test.py => 04_and3_dec_test.py} (85%) rename compiler/tests/{04_pand2_dec_test.py => 04_and4_dec_test.py} (85%) diff --git a/compiler/custom/pand2_dec.py b/compiler/custom/and2_dec.py similarity index 89% rename from compiler/custom/pand2_dec.py rename to compiler/custom/and2_dec.py index 0db490ff..b764bb83 100644 --- a/compiler/custom/pand2_dec.py +++ b/compiler/custom/and2_dec.py @@ -7,34 +7,37 @@ # import debug from vector import vector -import pgate +import design from sram_factory import factory from globals import OPTS +from tech import layer -class pand2_dec(pgate.pgate): +class and2_dec(design.design): """ This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - debug.info(1, "Creating pand2_dec {}".format(name)) + + design.design.__init__(self, name) + + debug.info(1, "Creating and2_dec {}".format(name)) self.add_comment("size: {}".format(size)) - self.size = size - - pgate.pgate.__init__(self, name, height, add_wells) - + self.height = height + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + def create_netlist(self): self.add_pins() self.create_modules() self.create_insts() def create_modules(self): - if OPTS.tech_name == "s8": - self.nand = factory.create(module_type="nand2_dec") - else: - self.nand = factory.create(module_type="nand2_dec", - height=self.height) + self.nand = factory.create(module_type="nand2_dec", + height=self.height) self.inv = factory.create(module_type="inv_dec", height=self.height, @@ -44,7 +47,13 @@ class pand2_dec(pgate.pgate): self.add_mod(self.inv) def create_layout(self): + + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" self.width = self.nand.width + self.inv.width + self.height = self.nand.height self.place_insts() self.add_wires() diff --git a/compiler/custom/pand3_dec.py b/compiler/custom/and3_dec.py similarity index 90% rename from compiler/custom/pand3_dec.py rename to compiler/custom/and3_dec.py index 3ec1eb45..89cc84f8 100644 --- a/compiler/custom/pand3_dec.py +++ b/compiler/custom/and3_dec.py @@ -7,22 +7,26 @@ # import debug from vector import vector -import pgate +import design from sram_factory import factory from globals import OPTS +from tech import layer -class pand3_dec(pgate.pgate): +class and3_dec(design.design): """ This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - debug.info(1, "Creating pand3_dec {}".format(name)) + design.design.__init__(self, name) + debug.info(1, "Creating and3_dec {}".format(name)) self.add_comment("size: {}".format(size)) - self.size = size + self.height = height - pgate.pgate.__init__(self, name, height, add_wells) + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): self.add_pins() @@ -30,11 +34,8 @@ class pand3_dec(pgate.pgate): self.create_insts() def create_modules(self): - if OPTS.tech_name == "s8": - self.nand = factory.create(module_type="nand3_dec") - else: - self.nand = factory.create(module_type="nand3_dec", - height=self.height) + self.nand = factory.create(module_type="nand3_dec", + height=self.height) self.inv = factory.create(module_type="inv_dec", height=self.height, @@ -44,8 +45,14 @@ class pand3_dec(pgate.pgate): self.add_mod(self.inv) def create_layout(self): - self.width = self.nand.width + self.inv.width + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + self.width = self.nand.width + self.inv.width + self.height = self.nand.height + self.place_insts() self.add_wires() self.add_layout_pins() diff --git a/compiler/custom/pand4_dec.py b/compiler/custom/and4_dec.py similarity index 89% rename from compiler/custom/pand4_dec.py rename to compiler/custom/and4_dec.py index ee65a349..f99c048f 100644 --- a/compiler/custom/pand4_dec.py +++ b/compiler/custom/and4_dec.py @@ -7,22 +7,28 @@ # import debug from vector import vector -import pgate +import design from sram_factory import factory from globals import OPTS +from tech import layer -class pand4_dec(pgate.pgate): +class and4_dec(design.design): """ This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - debug.info(1, "Creating pand4_dec {}".format(name)) - self.add_comment("size: {}".format(size)) - - self.size = size - pgate.pgate.__init__(self, name, height, add_wells) + design.design.__init__(self, name) + + debug.info(1, "Creating and4_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + self.size = size + self.height = height + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): self.add_pins() @@ -30,20 +36,24 @@ class pand4_dec(pgate.pgate): self.create_insts() def create_modules(self): - if OPTS.tech_name == "s8": - self.nand = factory.create(module_type="nand4_dec") - else: - self.nand = factory.create(module_type="nand4_dec", - height=self.height) + self.nand = factory.create(module_type="nand4_dec", + height=self.height) self.inv = factory.create(module_type="inv_dec", + height=self.height, size=self.size) self.add_mod(self.nand) self.add_mod(self.inv) def create_layout(self): + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + self.width = self.nand.width + self.inv.width + self.height = self.nand.height self.place_insts() self.add_wires() diff --git a/compiler/custom/nand2_dec.py b/compiler/custom/nand2_dec.py index 30a018a1..c806bf5a 100644 --- a/compiler/custom/nand2_dec.py +++ b/compiler/custom/nand2_dec.py @@ -6,7 +6,7 @@ # All rights reserved. # import design -from tech import GDS, layer, spice, parameter +from tech import GDS, layer, spice, parameter, drc import logical_effort import utils @@ -31,6 +31,14 @@ class nand2_dec(design.design): self.height = nand2_dec.height self.pin_map = nand2_dec.pin_map self.add_pin_types(self.type_list) + + # FIXME: For now... + size = 1 + self.size = size + self.nmos_size = 2 * size + self.pmos_size = parameter["beta"] * size + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" diff --git a/compiler/custom/nand3_dec.py b/compiler/custom/nand3_dec.py index d700062c..5eea68de 100644 --- a/compiler/custom/nand3_dec.py +++ b/compiler/custom/nand3_dec.py @@ -6,7 +6,7 @@ # All rights reserved. # import design -from tech import GDS, layer, spice, parameter +from tech import GDS, layer, spice, parameter, drc import logical_effort import utils @@ -31,6 +31,14 @@ class nand3_dec(design.design): self.height = nand3_dec.height self.pin_map = nand3_dec.pin_map self.add_pin_types(self.type_list) + + # FIXME: For now... + size = 1 + self.size = size + self.nmos_size = 2 * size + self.pmos_size = parameter["beta"] * size + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" diff --git a/compiler/custom/nand4_dec.py b/compiler/custom/nand4_dec.py index 5c2bb882..df3eee14 100644 --- a/compiler/custom/nand4_dec.py +++ b/compiler/custom/nand4_dec.py @@ -6,7 +6,7 @@ # All rights reserved. # import design -from tech import GDS, layer, spice, parameter +from tech import GDS, layer, spice, parameter, drc import logical_effort import utils @@ -31,6 +31,14 @@ class nand4_dec(design.design): self.height = nand4_dec.height self.pin_map = nand4_dec.pin_map self.add_pin_types(self.type_list) + + # FIXME: For now... + size = 1 + self.size = size + self.nmos_size = 2 * size + self.pmos_size = parameter["beta"] * size + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 3cb5d35c..8b711031 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -57,21 +57,15 @@ class hierarchical_decoder(design.design): self.DRC_LVS() def add_modules(self): - if OPTS.tech_name == "s8": - self.and2 = factory.create(module_type="pand2_dec") - else: - self.and2 = factory.create(module_type="pand2_dec", - height=self.cell_height) + self.and2 = factory.create(module_type="and2_dec", + height=self.cell_height) self.add_mod(self.and2) - if OPTS.tech_name == "s8": - self.and3 = factory.create(module_type="pand3_dec") - else: - self.and3 = factory.create(module_type="pand3_dec", - height=self.cell_height) - + + self.and3 = factory.create(module_type="and3_dec", + height=self.cell_height) self.add_mod(self.and3) # TBD - # self.and4 = factory.create(module_type="pand4_dec") + # self.and4 = factory.create(module_type="and4_dec") # self.add_mod(self.and4) self.add_decoders() @@ -180,6 +174,7 @@ class hierarchical_decoder(design.design): # Two extra pitches between modules on left and right self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch self.row_decoder_height = self.and2.height * self.num_outputs + # Extra bus space for supply contacts self.input_routing_width = self.num_inputs * self.bus_pitch + self.bus_space diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 8244028e..fa44b080 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -42,13 +42,13 @@ class hierarchical_predecode(design.design): # FIXME: Default parms are required for hard cells for now. if self.number_of_inputs == 2: - self.and_mod = factory.create(module_type="pand2_dec", + self.and_mod = factory.create(module_type="and2_dec", height=self.cell_height) elif self.number_of_inputs == 3: - self.and_mod = factory.create(module_type="pand3_dec", + self.and_mod = factory.create(module_type="and3_dec", height=self.cell_height) elif self.number_of_inputs == 4: - self.and_mod = factory.create(module_type="pand4_dec", + self.and_mod = factory.create(module_type="and4_dec", height=self.cell_height) else: debug.error("Invalid number of predecode inputs: {}".format(self.number_of_inputs), -1) diff --git a/compiler/pgates/wordline_driver.py b/compiler/pgates/wordline_driver.py index 1b035e20..a817941b 100644 --- a/compiler/pgates/wordline_driver.py +++ b/compiler/pgates/wordline_driver.py @@ -40,12 +40,8 @@ class wordline_driver(design.design): self.create_insts() def create_modules(self): - if OPTS.tech_name == "s8": - self.nand = factory.create(module_type="nand2_dec") - self.height = self.nand.height - else: - self.nand = factory.create(module_type="nand2_dec", - height=self.height) + self.nand = factory.create(module_type="nand2_dec", + height=self.height) self.driver = factory.create(module_type="inv_dec", size=self.size, diff --git a/compiler/tests/04_pand3_dec_test.py b/compiler/tests/04_and2_dec_test.py similarity index 85% rename from compiler/tests/04_pand3_dec_test.py rename to compiler/tests/04_and2_dec_test.py index 78e576ed..355d3b15 100755 --- a/compiler/tests/04_pand3_dec_test.py +++ b/compiler/tests/04_and2_dec_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class pand3_dec_test(openram_test): +class and2_dec_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -23,10 +23,10 @@ class pand3_dec_test(openram_test): global verify import verify - import pand3_dec + import and2_dec - debug.info(2, "Testing pand3 gate 4x") - a = pand3_dec.pand3_dec(name="pand3x4", size=4) + debug.info(2, "Testing and2 gate 4x") + a = and2_dec.and2_dec(name="and2x4", size=4) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_pand4_dec_test.py b/compiler/tests/04_and3_dec_test.py similarity index 85% rename from compiler/tests/04_pand4_dec_test.py rename to compiler/tests/04_and3_dec_test.py index 78e576ed..7794f36b 100755 --- a/compiler/tests/04_pand4_dec_test.py +++ b/compiler/tests/04_and3_dec_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class pand3_dec_test(openram_test): +class and3_dec_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -23,10 +23,10 @@ class pand3_dec_test(openram_test): global verify import verify - import pand3_dec + import and3_dec - debug.info(2, "Testing pand3 gate 4x") - a = pand3_dec.pand3_dec(name="pand3x4", size=4) + debug.info(2, "Testing and3 gate 4x") + a = and3_dec.and3_dec(name="and3x4", size=4) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_pand2_dec_test.py b/compiler/tests/04_and4_dec_test.py similarity index 85% rename from compiler/tests/04_pand2_dec_test.py rename to compiler/tests/04_and4_dec_test.py index 92b80203..7794f36b 100755 --- a/compiler/tests/04_pand2_dec_test.py +++ b/compiler/tests/04_and4_dec_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class pand2_dec_test(openram_test): +class and3_dec_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -23,10 +23,10 @@ class pand2_dec_test(openram_test): global verify import verify - import pand2_dec + import and3_dec - debug.info(2, "Testing pand2 gate 4x") - a = pand2_dec.pand2_dec(name="pand2x4", size=4) + debug.info(2, "Testing and3 gate 4x") + a = and3_dec.and3_dec(name="and3x4", size=4) self.local_check(a) globals.end_openram() From 9ecf98a4c32c52bde08010d2abb0e8b26ac38054 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 1 Jun 2020 16:46:22 -0700 Subject: [PATCH 028/206] SRAM factory uses default name for first instance even if it has arguments. --- compiler/sram_factory.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index df6b9d99..46eed3d4 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -111,11 +111,12 @@ class sram_factory: return obj_item # If no prefered module name is provided, we generate one. - if module_name is None: - # Use the default name if there are default arguments + if not module_name: + # Use the default name for the first cell. # This is especially for library cells so that the # spice and gds files can be found. - if len(kwargs) > 0: + # Subsequent objects will get unique names to help with GDS limitation. + if len(self.objects[real_module_type]) > 0: # Create a unique name and increment the index module_name = "{0}_{1}".format(real_module_type, self.module_indices[real_module_type]) From b39579c109848845c753bcef74b87cf30af7ad50 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Mon, 1 Jun 2020 20:55:15 -0700 Subject: [PATCH 029/206] temp drc fix for regression tests --- compiler/base/hierarchy_layout.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 0da2c4b0..722ac2f1 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1078,15 +1078,18 @@ class layout(): # Route each pin to the trunk for pin in pins: - # If there is sufficient space, Route from the edge of the pins - # Otherwise, route from the center of the pins - if max_y_bc - min_y_uc > pitch: - if pin.center().y == max_y: - mid = vector(trunk_offset.x, pin.bc().y) - else: - mid = vector(trunk_offset.x, pin.uc().y) - else: - mid = vector(trunk_offset.x, pin.center().y) + # This code block currently causes drc violations for the topmost + # port when using multiport, TODO: fix or remove this block + # # If there is sufficient space, Route from the edge of the pins + # # Otherwise, route from the center of the pins + # if max_y_bc - min_y_uc > pitch: + # if pin.center().y == max_y: + # mid = vector(trunk_offset.x, pin.bc().y) + # else: + # mid = vector(trunk_offset.x, pin.uc().y) + # else: + # mid = vector(trunk_offset.x, pin.center().y) + mid = vector(trunk_offset.x, pin.center().y) self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid) From b0aa70ffda436e9b22f5f51c249671e2660c0ed6 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 09:23:27 -0700 Subject: [PATCH 030/206] Fix precharge vdd route layer --- compiler/pgates/precharge.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index ae314452..8b84a1b8 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -105,17 +105,14 @@ class precharge(design.design): # center of vdd rail pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) - self.add_path(self.bitline_layer, [pmos_pin.center(), pmos_vdd_pos]) + self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos]) - self.add_via_stack_center(from_layer=self.bitline_layer, - to_layer=self.en_layer, - offset=pmos_vdd_pos) self.add_power_pin("vdd", self.well_contact_pos, directions=("V", "V")) self.add_via_stack_center(from_layer=pmos_pin.layer, - to_layer=self.bitline_layer, + to_layer=self.en_layer, offset=pmos_pin.center(), directions=("V", "V")) From e69b665689c0b2b29ba0a124b15e6ba505199fca Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 09:31:43 -0700 Subject: [PATCH 031/206] Flatten pbitcell_1 too --- technology/scn4m_subm/mag_lib/setup.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/technology/scn4m_subm/mag_lib/setup.tcl b/technology/scn4m_subm/mag_lib/setup.tcl index 95e7dbea..09bbea27 100644 --- a/technology/scn4m_subm/mag_lib/setup.tcl +++ b/technology/scn4m_subm/mag_lib/setup.tcl @@ -6,7 +6,7 @@ equate class {-circuit1 pfet} {-circuit2 p} flatten class {-circuit1 dummy_cell_6t} flatten class {-circuit1 dummy_cell_1rw_1r} flatten class {-circuit1 dummy_cell_1w_1r} -flatten class {-circuit1 bitcell_array_0} +flatten class {-circuit1 pbitcell} flatten class {-circuit1 pbitcell_0} flatten class {-circuit1 pbitcell_1} property {-circuit1 nfet} remove as ad ps pd From 620604603c9949627df77b5f887f04e334d84e40 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 10:08:37 -0700 Subject: [PATCH 032/206] Fixed offset jogs --- compiler/base/hierarchy_layout.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 4d0345a3..f659aed4 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -450,7 +450,7 @@ class layout(): path=coordinates, layer_widths=layer_widths) - def add_zjog(self, layer, start, end, first_direction="H"): + def add_zjog(self, layer, start, end, first_direction="H", fixed_offset=None): """ Add a simple jog at the halfway point. If layer is a single value, it is a path. @@ -459,11 +459,17 @@ class layout(): # vertical first if first_direction == "V": - mid1 = vector(start.x, 0.5 * start.y + 0.5 * end.y) + if fixed_offset: + mid1 = vector(start.x, fixed_offset) + else: + mid1 = vector(start.x, 0.5 * start.y + 0.5 * end.y) mid2 = vector(end.x, mid1.y) # horizontal first elif first_direction == "H": - mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y) + if fixed_offset: + mid1 = vector(fixed_offset, start.y) + else: + mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y) mid2 = vector(mid1, end.y) else: debug.error("Invalid direction for jog -- must be H or V.") From a1c7474f80aaa35060f503b7bcb886e14f88b2c7 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 10:08:53 -0700 Subject: [PATCH 033/206] Revert to channel route of bitlines --- compiler/modules/port_data.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index d544b6be..0ad0dd86 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -536,11 +536,11 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - self.connect_bitlines(inst1=inst1, - inst1_bls_template=inst1_bls_templ, - inst2=inst2, - num_bits=self.word_size, - inst1_start_bit=start_bit) + self.channel_route_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + num_bits=self.word_size, + inst1_start_bit=start_bit) def route_write_driver_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ @@ -562,10 +562,10 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - self.connect_bitlines(inst1=inst1, inst2=inst2, - num_bits=self.word_size, - inst1_bls_template=inst1_bls_templ, - inst1_start_bit=start_bit) + self.channel_route_bitlines(inst1=inst1, inst2=inst2, + num_bits=self.word_size, + inst1_bls_template=inst1_bls_templ, + inst1_start_bit=start_bit) def route_write_driver_to_sense_amp(self, port): """ Routing of BL and BR between write driver and sense amp """ @@ -714,9 +714,10 @@ class port_data(design.design): top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col) bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc() top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc() - - self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V") - self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V") + + layer_pitch = getattr(self, "{}_pitch".format(top_bl_pin.layer)) + self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V", top_bl_pin.by() - layer_pitch) + self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V", top_bl_pin.by() - 2 * layer_pitch) def graph_exclude_precharge(self): """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" From 45b0601e4b0789f84c1d38230d2ee9a890fafe34 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 11:43:31 -0700 Subject: [PATCH 034/206] Fix via directions in s8 col mux --- .../modules/single_level_column_mux_array.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 5dc9cbf8..1514d64a 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -7,7 +7,7 @@ # import design import debug -from tech import layer +from tech import layer, preferred_directions from vector import vector from sram_factory import factory from globals import OPTS @@ -33,10 +33,15 @@ class single_level_column_mux_array(design.design): if "li" in layer: self.col_mux_stack = self.li_stack - self.col_mux_stack_pitch = self.li_pitch + self.col_mux_stack_pitch = self.m1_pitch else: self.col_mux_stack = self.m1_stack self.col_mux_stack_pitch = self.m1_pitch + + if preferred_directions[self.col_mux_stack[0]] == "V": + self.via_directions = ("H", "H") + else: + self.via_directions = "pref" self.create_netlist() if not OPTS.netlist_only: @@ -173,7 +178,8 @@ class single_level_column_mux_array(design.design): self.get_pin("sel_{}".format(sel_index)).cy()) # Add the poly contact with a shift to account for the rotation self.add_via_center(layers=self.poly_stack, - offset=offset) + offset=offset, + directions=self.via_directions) self.add_path("poly", [offset, gate_offset]) def route_bitlines(self): @@ -224,11 +230,13 @@ class single_level_column_mux_array(design.design): # This via is on the right of the wire self.add_via_center(layers=self.col_mux_stack, - offset=bl_out_offset) + offset=bl_out_offset, + directions=self.via_directions) # This via is on the left of the wire self.add_via_center(layers=self.col_mux_stack, - offset=br_out_offset) + offset=br_out_offset, + directions=self.via_directions) else: self.add_path(self.col_mux_stack[2], [bl_out_offset, bl_offset]) @@ -236,10 +244,12 @@ class single_level_column_mux_array(design.design): # This via is on the right of the wire self.add_via_center(layers=self.col_mux_stack, - offset=bl_out_offset) + offset=bl_out_offset, + directions=self.via_directions) # This via is on the left of the wire self.add_via_center(layers=self.col_mux_stack, - offset=br_out_offset) + offset=br_out_offset, + directions=self.via_directions) def get_drain_cin(self): """Get the relative capacitance of the drain of the NMOS pass TX""" From f1b7b91b1a4958a90b198fce69cd7f21aa9f7ea7 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 11:43:57 -0700 Subject: [PATCH 035/206] Use non-channel route for s8 port_data --- compiler/modules/port_data.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 0ad0dd86..904c82eb 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -180,6 +180,7 @@ class port_data(design.design): # Precharge will be shifted left if needed self.precharge_array = factory.create(module_type="precharge_array", columns=self.num_cols + 1, + port=self.port, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) self.add_mod(self.precharge_array) @@ -250,6 +251,7 @@ class port_data(design.design): # module, which happens before we create the real precharge_array self.precharge_array = factory.create(module_type="precharge_array", columns=self.num_cols + 1, + port=self.port, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) @@ -536,11 +538,18 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - self.channel_route_bitlines(inst1=inst1, - inst1_bls_template=inst1_bls_templ, - inst2=inst2, - num_bits=self.word_size, - inst1_start_bit=start_bit) + if OPTS.tech_name == "s8": + self.connect_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + num_bits=self.word_size, + inst1_start_bit=start_bit) + else: + self.channel_route_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + num_bits=self.word_size, + inst1_start_bit=start_bit) def route_write_driver_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ @@ -562,11 +571,17 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - self.channel_route_bitlines(inst1=inst1, inst2=inst2, - num_bits=self.word_size, - inst1_bls_template=inst1_bls_templ, - inst1_start_bit=start_bit) - + if OPTS.tech_name == "s8": + self.connect_bitlines(inst1=inst1, inst2=inst2, + num_bits=self.word_size, + inst1_bls_template=inst1_bls_templ, + inst1_start_bit=start_bit) + else: + self.channel_route_bitlines(inst1=inst1, inst2=inst2, + num_bits=self.word_size, + inst1_bls_template=inst1_bls_templ, + inst1_start_bit=start_bit) + def route_write_driver_to_sense_amp(self, port): """ Routing of BL and BR between write driver and sense amp """ From fdf51c5a00ca61f8601ae344ee7997be8bd2a5a6 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 11:44:22 -0700 Subject: [PATCH 036/206] Add port option to precharge array --- compiler/modules/precharge_array.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index af612af4..3c64e85f 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -19,13 +19,14 @@ class precharge_array(design.design): of bit line columns, height is the height of the bit-cell array. """ - def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br"): + def __init__(self, name, columns, port, size=1, bitcell_bl="bl", bitcell_br="br"): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br)) self.columns = columns self.size = size + self.port = port self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br @@ -106,7 +107,7 @@ class precharge_array(design.design): xoffset = 0 for i in range(self.columns): tempx = xoffset - if cell_properties.bitcell.mirror.y and (i + 1) % 2: + if cell_properties.bitcell.mirror.y and (i + 1 + self.port) % 2: mirror = "MY" tempx = tempx + self.pc_cell.width else: From fce8e878b9ac55d57865ef2e7a4883f901d3daf4 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 13:57:41 -0700 Subject: [PATCH 037/206] Add port to col mux and simplify route with computation to fix mirror bug. --- .../modules/single_level_column_mux_array.py | 86 +++++++------------ .../07_single_level_column_mux_array_test.py | 6 +- 2 files changed, 34 insertions(+), 58 deletions(-) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 1514d64a..3d80525a 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -20,12 +20,13 @@ class single_level_column_mux_array(design.design): Array of column mux to read the bitlines through the 6T. """ - def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br"): + def __init__(self, name, columns, port, word_size, bitcell_bl="bl", bitcell_br="br"): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br)) self.columns = columns + self.port = port self.word_size = word_size self.words_per_row = int(self.columns / self.word_size) self.bitcell_bl = bitcell_bl @@ -117,7 +118,7 @@ class single_level_column_mux_array(design.design): # For every column, add a pass gate for col_num in range(self.columns): xoffset = col_num * self.mux.width - if cell_properties.bitcell.mirror.y and col_num % 2: + if cell_properties.bitcell.mirror.y and (col_num + self.port) % 2: mirror = "MY" xoffset = xoffset + self.mux.width else: @@ -184,72 +185,47 @@ class single_level_column_mux_array(design.design): def route_bitlines(self): """ Connect the output bit-lines to form the appropriate width mux """ - from tech import cell_properties for j in range(self.columns): - bl_offset = self.mux_inst[j].get_pin("bl_out").bc() - br_offset = self.mux_inst[j].get_pin("br_out").bc() - bl_out_offset = bl_offset - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch) - br_out_offset = br_offset - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch) + bl_offset_begin = self.mux_inst[j].get_pin("bl_out").bc() + br_offset_begin = self.mux_inst[j].get_pin("br_out").bc() - bl_out_offset_end = bl_out_offset + vector(0, self.route_height) - br_out_offset_end = br_out_offset + vector(0, self.route_height) + bl_out_offset_begin = bl_offset_begin - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch) + br_out_offset_begin = br_offset_begin - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch) - if cell_properties.bitcell.mirror.y and j % 2: - tmp_bl_out_end = br_out_offset_end - tmp_br_out_end = bl_out_offset_end - else: - tmp_bl_out_end = bl_out_offset_end - tmp_br_out_end = br_out_offset_end - - if (j % self.words_per_row) == 0: - # Create the metal1 to connect the n-way mux output from the pass gate - # These will be located below the select lines. Yes, these are M2 width - # to ensure vias are enclosed and M1 min width rules. - width = self.m2_width + self.mux.width * (self.words_per_row - 1) - - if cell_properties.bitcell.mirror.y and (j % 2) == 0: - bl = self.mux.get_pin("bl") - br = self.mux.get_pin("br") - dist = abs(bl.ll().x - br.ll().x) - else: - dist = 0 - - self.add_path(self.col_mux_stack[0], [bl_out_offset, bl_out_offset + vector(width + dist, 0)]) - self.add_path(self.col_mux_stack[0], [br_out_offset, br_out_offset + vector(width - dist, 0)]) + # Add the horizontal wires for the first bit + if j % self.words_per_row == 0: + bl_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("bl_out").bc() + br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc() + bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch) + br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch) + + self.add_path(self.col_mux_stack[0], [bl_out_offset_begin, bl_out_offset_end]) + self.add_path(self.col_mux_stack[0], [br_out_offset_begin, br_out_offset_end]) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)), layer=self.col_mux_stack[2], - start=bl_out_offset, - end=tmp_bl_out_end) + start=bl_offset_begin, + end=bl_out_offset_begin) self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)), layer=self.col_mux_stack[2], - start=br_out_offset, - end=tmp_br_out_end) - - # This via is on the right of the wire - self.add_via_center(layers=self.col_mux_stack, - offset=bl_out_offset, - directions=self.via_directions) - - # This via is on the left of the wire - self.add_via_center(layers=self.col_mux_stack, - offset=br_out_offset, - directions=self.via_directions) + start=br_offset_begin, + end=br_out_offset_begin) else: - self.add_path(self.col_mux_stack[2], [bl_out_offset, bl_offset]) - self.add_path(self.col_mux_stack[2], [br_out_offset, br_offset]) + self.add_path(self.col_mux_stack[2], [bl_out_offset_begin, bl_offset_begin]) + self.add_path(self.col_mux_stack[2], [br_out_offset_begin, br_offset_begin]) - # This via is on the right of the wire - self.add_via_center(layers=self.col_mux_stack, - offset=bl_out_offset, - directions=self.via_directions) - # This via is on the left of the wire - self.add_via_center(layers=self.col_mux_stack, - offset=br_out_offset, - directions=self.via_directions) + # This via is on the right of the wire + self.add_via_center(layers=self.col_mux_stack, + offset=bl_out_offset_begin, + directions=self.via_directions) + + # This via is on the left of the wire + self.add_via_center(layers=self.col_mux_stack, + offset=br_out_offset_begin, + directions=self.via_directions) def get_drain_cin(self): """Get the relative capacitance of the drain of the NMOS pass TX""" diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index c5d48689..fb608f68 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -23,15 +23,15 @@ class single_level_column_mux_test(openram_test): # check single level column mux array in single port debug.info(1, "Testing sample for 2-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8) + a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=8) self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4) + a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=4) self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4) + a = factory.create(module_type="single_level_column_mux_array", columns=32, port=0, word_size=4) self.local_check(a) globals.end_openram() From 34209dac3d6aa9680e1311ed34cac7cbb44199a3 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 16:50:07 -0700 Subject: [PATCH 038/206] A port option for correct mirroring in port_data. --- compiler/modules/hierarchical_predecode.py | 2 -- compiler/modules/port_data.py | 1 + compiler/modules/precharge_array.py | 3 +-- .../07_single_level_column_mux_array_pbitcell_test.py | 8 ++++---- compiler/tests/08_precharge_array_1rw1r_test.py | 2 +- compiler/tests/08_precharge_array_test.py | 2 +- compiler/tests/18_port_data_1rw_1r_test.py | 2 +- 7 files changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index fa44b080..d998d882 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -40,7 +40,6 @@ class hierarchical_predecode(design.design): def add_modules(self): """ Add the INV and AND gate modules """ - # FIXME: Default parms are required for hard cells for now. if self.number_of_inputs == 2: self.and_mod = factory.create(module_type="and2_dec", height=self.cell_height) @@ -60,7 +59,6 @@ class hierarchical_predecode(design.design): size=1) self.add_mod(self.inv) - def create_layout(self): """ The general organization is from left to right: 1) a set of M2 rails for input signals diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 904c82eb..ac6022f7 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -196,6 +196,7 @@ class port_data(design.design): if self.col_addr_size > 0: self.column_mux_array = factory.create(module_type="column_mux_array", columns=self.num_cols, + port=self.port, word_size=self.word_size, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 3c64e85f..2acb1063 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -7,7 +7,6 @@ # import design import debug -from tech import drc from vector import vector from sram_factory import factory from globals import OPTS @@ -26,7 +25,7 @@ class precharge_array(design.design): self.columns = columns self.size = size - self.port = port + self.port = port self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br diff --git a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py index 84e62a96..7feb067e 100755 --- a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py +++ b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py @@ -29,19 +29,19 @@ class single_level_column_mux_pbitcell_test(openram_test): factory.reset() debug.info(1, "Testing sample for 2-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=32, port=0, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2") + a = factory.create(module_type="single_level_column_mux_array", columns=32, port=3, word_size=4, bitcell_bl="bl2", bitcell_br="br2") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/08_precharge_array_1rw1r_test.py b/compiler/tests/08_precharge_array_1rw1r_test.py index 8f51c2d4..1381b93a 100755 --- a/compiler/tests/08_precharge_array_1rw1r_test.py +++ b/compiler/tests/08_precharge_array_1rw1r_test.py @@ -29,7 +29,7 @@ class precharge_test(openram_test): factory.reset() debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") - pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0") + pc = factory.create(module_type="precharge_array", columns=3, port=0, bitcell_bl="bl0", bitcell_br="br0") self.local_check(pc) # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index b301a909..c3b823b3 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -23,7 +23,7 @@ class precharge_test(openram_test): # check precharge array in single port debug.info(2, "Checking 3 column precharge") - pc = factory.create(module_type="precharge_array", columns=3) + pc = factory.create(module_type="precharge_array", columns=3, port=0) self.local_check(pc) globals.end_openram() diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index e9e282a5..8081416e 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -36,7 +36,7 @@ class port_data_1rw_1r_test(openram_test): self.local_check(a) a = factory.create("port_data", sram_config=c, port=1) self.local_check(a) - + c.num_words=32 c.words_per_row=2 factory.reset() From eb0c595dbef69cf8fdf67d27b720f5e0ea716a51 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Wed, 3 Jun 2020 12:31:30 +0000 Subject: [PATCH 039/206] SRAM layout and functional tests with spare cols --- compiler/characterizer/functional.py | 35 +++- compiler/characterizer/simulation.py | 54 ++++-- compiler/modules/bank.py | 10 +- compiler/modules/control_logic.py | 4 +- compiler/modules/port_data.py | 14 +- compiler/modules/write_driver_array.py | 17 +- compiler/sram/sram_1bank.py | 157 +++++++++++++++--- compiler/sram/sram_base.py | 28 +++- ...sram_1bank_nomux_1rw_1r_spare_cols_test.py | 2 +- ...=> 20_sram_1bank_nomux_spare_cols_test.py} | 7 +- .../22_sram_1bank_2mux_sparecols_func_test.py | 62 +++++++ ...22_sram_1bank_nomux_sparecols_func_test.py | 60 +++++++ 12 files changed, 373 insertions(+), 77 deletions(-) rename compiler/tests/{20_sram_1bank_nomux_wmask_spare_cols_test.py => 20_sram_1bank_nomux_spare_cols_test.py} (89%) create mode 100755 compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py create mode 100755 compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index cbd9b8d5..8d07a61a 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -18,7 +18,7 @@ from .charutils import * import utils from globals import OPTS from .simulation import simulation -from .delay import delay +# from .delay import delay import graph_util from sram_factory import factory @@ -40,6 +40,9 @@ class functional(simulation): else: self.num_wmasks = 0 + if not self.num_spare_cols: + self.num_spare_cols = 0 + self.set_corner(corner) self.set_spice_constants() self.set_stimulus_variables() @@ -86,6 +89,7 @@ class functional(simulation): if port in self.write_ports: checks.append((self.data_value[port],"data")) checks.append((self.wmask_value[port],"wmask")) + checks.append((self.spare_wen_value[port],"spare_wen")) for (val, name) in checks: debug.check(len(self.cycle_times)==len(val), @@ -226,7 +230,7 @@ class functional(simulation): # Extract dout values from spice timing.lis for (word, dout_port, eo_period, check) in self.read_check: sp_read_value = "" - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check)) if value > self.v_high: sp_read_value = "1" + sp_read_value @@ -281,13 +285,18 @@ class functional(simulation): def gen_data(self): """ Generates a random word to write. """ - random_value = random.randint(0,(2**self.word_size)-1) + if not self.num_spare_cols: + random_value = random.randint(0,(2**(self.word_size))-1) + else: + random_value1 = random.randint(0,(2**(self.word_size))-1) + random_value2 = random.randint(0,(2**(self.num_spare_cols))-1) + random_value = random_value1 + random_value2 data_bits = self.convert_to_bin(random_value,False) return data_bits def gen_addr(self): """ Generates a random address value to write to. """ - if (self.num_spare_rows == 0): + if self.num_spare_rows==0: random_value = random.randint(0,(2**self.addr_size)-1) else: random_value = random.randint(0,((2**(self.addr_size-1)-1))+(self.num_spare_rows * self.words_per_row)) @@ -307,8 +316,7 @@ class functional(simulation): if(is_addr): expected_value = self.addr_size else: - - expected_value = self.word_size + expected_value = self.word_size + self.num_spare_cols for i in range (expected_value - len(new_value)): new_value = "0" + new_value @@ -337,7 +345,7 @@ class functional(simulation): # Add load capacitance to each of the read ports self.sf.write("\n* SRAM output loads\n") for port in self.read_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): 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)) @@ -357,7 +365,7 @@ class functional(simulation): # Generate data input bits self.sf.write("\n* Generation of data and address signals\n") for port in self.write_ports: - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): sig_name="{0}{1}_{2} ".format(self.din_name, port, bit) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05) @@ -386,6 +394,15 @@ class functional(simulation): self.stim.gen_pwl(sig_name, self.cycle_times, self.wmask_values[port][bit], self.period, self.slew, 0.05) + # Generate spare enable bits (for spare cols) + for port in self.write_ports: + if self.num_spare_cols: + self.sf.write("\n* Generation of spare enable signals\n") + for bit in range(self.num_spare_cols): + sig_name = "SPARE_WEN{0}_{1} ".format(port, bit) + self.stim.gen_pwl(sig_name, self.cycle_times, self.spare_wen_values[port][bit], self.period, + self.slew, 0.05) + # Generate CLK signals for port in self.all_ports: self.stim.gen_pulse(sig_name="{0}{1}".format("clk", port), @@ -401,7 +418,7 @@ class functional(simulation): for (word, dout_port, eo_period, check) in self.read_check: t_intital = eo_period - 0.01*self.period t_final = eo_period + 0.01*self.period - for bit in range(self.word_size): + for bit in range(self.word_size + self.num_spare_cols): self.stim.gen_meas_value(meas_name="V{0}_{1}ck{2}".format(dout_port,bit,check), dout="{0}_{1}".format(dout_port,bit), t_intital=t_intital, diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index e0cc6195..9027fca1 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -26,6 +26,10 @@ class simulation(): self.addr_size = self.sram.addr_size self.write_size = self.sram.write_size self.num_spare_rows = self.sram.num_spare_rows + if not self.sram.num_spare_cols: + self.num_spare_cols = 0 + else: + self.num_spare_cols = self.sram.num_spare_cols self.sp_file = spfile self.all_ports = self.sram.all_ports @@ -38,7 +42,6 @@ class simulation(): else: self.num_wmasks = 0 - def set_corner(self,corner): """ Set the corner values """ self.corner = corner @@ -61,10 +64,10 @@ class simulation(): self.pins = self.gen_pin_names(port_signal_names=(self.addr_name,self.din_name,self.dout_name), port_info=(len(self.all_ports),self.write_ports,self.read_ports), abits=self.addr_size, - dbits=self.word_size) + dbits=self.word_size + self.num_spare_cols) debug.check(len(self.sram.pins) == len(self.pins), "Number of pins generated for characterization \ - do match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins, + do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins, self.pins)) #This is TODO once multiport control has been finalized. #self.control_name = "CSB" @@ -82,11 +85,13 @@ class simulation(): self.addr_value = {port:[] for port in self.all_ports} self.data_value = {port:[] for port in self.write_ports} self.wmask_value = {port:[] for port in self.write_ports} + self.spare_wen_value = {port:[] for port in self.write_ports} # Three dimensional list to handle each addr and data bits for each port over the number of checks self.addr_values = {port:[[] for bit in range(self.addr_size)] for port in self.all_ports} - self.data_values = {port:[[] for bit in range(self.word_size)] for port in self.write_ports} + self.data_values = {port:[[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports} self.wmask_values = {port:[[] for bit in range(self.num_wmasks)] for port in self.write_ports} + self.spare_wen_values = {port:[[] for bit in range(self.num_spare_cols)] for port in self.write_ports} # For generating comments in SPICE stimulus self.cycle_comments = [] @@ -113,10 +118,10 @@ class simulation(): def add_data(self, data, port): """ Add the array of data values """ - debug.check(len(data)==self.word_size, "Invalid data word size.") + debug.check(len(data)==(self.word_size + self.num_spare_cols), "Invalid data word size.") self.data_value[port].append(data) - bit = self.word_size - 1 + bit = self.word_size + self.num_spare_cols - 1 for c in data: if c=="0": self.data_values[port][bit].append(0) @@ -137,10 +142,7 @@ class simulation(): if c=="0": self.addr_values[port][bit].append(0) elif c=="1": - if((self.num_spare_rows != 0) and (bit == (self.addr_size - 1))): - self.addr_values[port][bit].append(0) - else: - self.addr_values[port][bit].append(1) + self.addr_values[port][bit].append(1) else: debug.error("Non-binary address string",1) bit -= 1 @@ -161,7 +163,21 @@ class simulation(): debug.error("Non-binary wmask string", 1) bit -= 1 - + def add_spare_wen(self, spare_wen, port): + """ Add the array of spare write enable values (for spare cols) """ + debug.check(len(spare_wen) == self.num_spare_cols, "Invalid spare enable size.") + + self.spare_wen_value[port].append(spare_wen) + bit = self.num_spare_cols - 1 + for c in spare_wen: + if c == "0": + self.spare_wen_values[port][bit].append(0) + elif c == "1": + self.spare_wen_values[port][bit].append(1) + else: + debug.error("Non-binary spare enable signal string", 1) + bit -= 1 + def add_write(self, comment, address, data, wmask, port): """ Add the control values for a write cycle. """ debug.check(port in self.write_ports, @@ -178,6 +194,7 @@ class simulation(): self.add_data(data,port) self.add_address(address,port) self.add_wmask(wmask,port) + self.add_spare_wen("1" * self.num_spare_cols, port) #Add noops to all other ports. for unselected_port in self.all_ports: @@ -197,6 +214,7 @@ class simulation(): self.t_current += self.period self.add_control_one_port(port, "read") self.add_address(address, port) + self.add_spare_wen("0" * self.num_spare_cols, port) # If the port is also a readwrite then add # the same value as previous cycle @@ -204,7 +222,7 @@ class simulation(): try: self.add_data(self.data_value[port][-1], port) except: - self.add_data("0"*self.word_size, port) + self.add_data("0"*(self.word_size + self.num_spare_cols), port) try: self.add_wmask(self.wmask_value[port][-1], port) except: @@ -239,6 +257,7 @@ class simulation(): self.add_data(data, port) self.add_address(address, port) self.add_wmask(wmask, port) + self.add_spare_wen("1" * self.num_spare_cols, port) def add_read_one_port(self, comment, address, port): """ Add the control values for a read cycle. Does not increment the period. """ @@ -250,13 +269,14 @@ class simulation(): self.add_control_one_port(port, "read") self.add_address(address, port) + self.add_spare_wen("0" * self.num_spare_cols, port) # If the port is also a readwrite then add # the same value as previous cycle if port in self.write_ports: try: self.add_data(self.data_value[port][-1], port) except: - self.add_data("0"*self.word_size, port) + self.add_data("0"*(self.word_size + self.num_spare_cols), port) try: self.add_wmask(self.wmask_value[port][-1], port) except: @@ -266,6 +286,7 @@ class simulation(): def add_noop_one_port(self, port): """ Add the control values for a noop to a single port. Does not increment the period. """ self.add_control_one_port(port, "noop") + self.add_spare_wen("0" * self.num_spare_cols, port) try: self.add_address(self.addr_value[port][-1], port) @@ -278,7 +299,7 @@ class simulation(): try: self.add_data(self.data_value[port][-1], port) except: - self.add_data("0"*self.word_size, port) + self.add_data("0"*(self.word_size + self.num_spare_cols), port) try: self.add_wmask(self.wmask_value[port][-1], port) except: @@ -374,6 +395,11 @@ class simulation(): for port in write_index: for bit in range(self.num_wmasks): pin_names.append("WMASK{0}_{1}".format(port,bit)) + + if self.num_spare_cols: + for port in write_index: + for bit in range(self.num_spare_cols): + pin_names.append("SPARE_WEN{0}_{1}".format(port,bit)) for read_output in read_index: for i in range(dbits): diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 40eabde6..ad8b34df 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -105,7 +105,7 @@ class bank(design.design): for bit in range(self.num_wmasks): self.add_pin("bank_wmask{0}_{1}".format(port, bit), "INPUT") for bit in range(self.num_spare_cols): - self.add_pin("spare_wen{0}_{1}".format(port, bit), "INPUT") + self.add_pin("bank_spare_wen{0}_{1}".format(port, bit), "INPUT") for port in self.all_ports: self.add_pin("wl_en{0}".format(port), "INPUT") self.add_pin("vdd", "POWER") @@ -442,7 +442,7 @@ class bank(design.design): for bit in range(self.num_wmasks): temp.append("bank_wmask{0}_{1}".format(port, bit)) for bit in range(self.num_spare_cols): - temp.append("spare_wen{0}_{1}".format(port, bit)) + temp.append("bank_spare_wen{0}_{1}".format(port, bit)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -725,15 +725,15 @@ class bank(design.design): din_name = "din{0}_{1}".format(port, row) self.copy_layout_pin(self.port_data_inst[port], data_name, din_name) - if self.word_size: + if self.write_size: for row in range(self.num_wmasks): wmask_name = "bank_wmask_{}".format(row) bank_wmask_name = "bank_wmask{0}_{1}".format(port, row) self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name) for col in range(self.num_spare_cols): - sparecol_name = "spare_wen{}".format(col) - bank_sparecol_name = "spare_wen{0}_{1}".format(port, col) + sparecol_name = "bank_spare_wen{}".format(col) + bank_sparecol_name = "bank_spare_wen{0}_{1}".format(port, col) self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name) def channel_route_bitlines(self, inst1, inst2, num_bits, diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index e09a6615..41970587 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -107,7 +107,7 @@ class control_logic(design.design): # clk_buf drives a flop for every address addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2) # plus data flops and control flops - num_flops = addr_flops + self.word_size + self.num_control_signals + num_flops = addr_flops + self.word_size + self.num_spare_cols + self.num_control_signals # each flop internally has a FO 5 approximately # plus about 5 fanouts for the control logic clock_fanout = 5 * num_flops + 5 @@ -135,7 +135,7 @@ class control_logic(design.design): # s_en drives every sense amp self.sen_and3 = factory.create(module_type="pand3", - size=self.word_size, + size=self.word_size + self.num_spare_cols, height=dff_height) self.add_mod(self.sen_and3) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 968b4605..483cea93 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -138,7 +138,7 @@ class port_data(design.design): for bit in range(self.num_wmasks): self.add_pin("bank_wmask_{}".format(bit), "INPUT") for bit in range(self.num_spare_cols): - self.add_pin("spare_wen{}".format(bit), "INPUT") + self.add_pin("bank_spare_wen{}".format(bit), "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -387,12 +387,12 @@ class port_data(design.design): for i in range(self.num_wmasks): temp.append("wdriver_sel_{}".format(i)) for i in range(self.num_spare_cols): - temp.append("spare_wen{}".format(i)) + temp.append("bank_spare_wen{}".format(i)) elif self.num_spare_cols and not self.write_size: temp.append("w_en") for i in range(self.num_spare_cols): - temp.append("spare_wen{}".format(i)) + temp.append("bank_spare_wen{}".format(i)) else: temp.append("w_en") temp.extend(["vdd", "gnd"]) @@ -627,9 +627,9 @@ class port_data(design.design): start_bit=0 if self.port==0: - off=1 + off = 1 else: - off=0 + off = 0 if self.num_spare_cols != 0 and self.col_addr_size>0: @@ -722,11 +722,11 @@ class port_data(design.design): self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit), "wdriver_sel_{}".format(bit)) for bit in range(self.num_spare_cols): # Add spare columns' en_{} pins - self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + self.num_wmasks), "spare_wen{}".format(bit)) + self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + self.num_wmasks), "bank_spare_wen{}".format(bit)) elif self.num_spare_cols and not self.write_mask_and_array_inst: self.copy_layout_pin(self.write_driver_array_inst, "en_0", "w_en") for bit in range(self.num_spare_cols): - self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + 1), "spare_wen{}".format(bit)) + self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit + 1), "bank_spare_wen{}".format(bit)) else: self.copy_layout_pin(self.write_driver_array_inst, "en", "w_en") if self.write_mask_and_array_inst: diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 48564e86..b9f6c124 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -142,7 +142,7 @@ class write_driver_array(design.design): offset = self.num_wmasks else: offset = 1 - name = "write_driver{}".format(index) + name = "write_driver{}".format(self.columns + i) self.driver_insts[index]=self.add_inst(name=name, mod=self.driver) @@ -234,27 +234,30 @@ class write_driver_array(design.design): for i in range(self.num_spare_cols): inst = self.driver_insts[self.word_size + i] + en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks), layer="m1", - offset=inst.get_pin(inst.mod.en_name).ll(), - width=self.single_col_width - inst.get_pin(inst.mod.en_name).width()) + offset=en_pin.ll(), + width=self.driver.width - en_pin.width()) elif self.num_spare_cols and not self.write_size: # shorten enable rail to accomodate those for spare write drivers inst = self.driver_insts[0] + en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(0), layer="m1", - offset=inst.get_pin(inst.mod.en_name).ll(), - width=self.width_regular_cols - (self.words_per_row * inst.get_pin(inst.mod.en_name).width())) + offset=en_pin.ll(), + width=self.width_regular_cols - self.words_per_row * en_pin.width()) # individual enables for every spare write driver for i in range(self.num_spare_cols): inst = self.driver_insts[self.word_size + i] + en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1), layer="m1", - offset=inst.get_pin(inst.mod.en_name).ll(), - width=self.single_col_width - inst.get_pin(inst.mod.en_name).width()) + offset=en_pin.ll(), + width=self.driver.width - en_pin.width()) else: inst = self.driver_insts[0] diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 4af19f98..11a17129 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -39,6 +39,11 @@ class sram_1bank(sram_base): else: self.data_dff_insts = self.create_data_dff() + if self.num_spare_cols: + self.spare_wen_dff_insts = self.create_spare_wen_dff() + else: + self.num_spare_cols = 0 + def place_instances(self): """ This places the instances for a single bank SRAM with control @@ -57,6 +62,7 @@ class sram_1bank(sram_base): row_addr_pos = [None] * len(self.all_ports) col_addr_pos = [None] * len(self.all_ports) wmask_pos = [None] * len(self.all_ports) + spare_wen_pos = [None] * len(self.all_ports) data_pos = [None] * len(self.all_ports) # These positions utilize the channel route sizes. @@ -69,9 +75,21 @@ class sram_1bank(sram_base): self.data_bus_size = self.m4_nonpref_pitch * (self.word_size + self.num_spare_cols) + self.data_bus_gap self.wmask_bus_gap = self.m2_nonpref_pitch * 2 self.wmask_bus_size = self.m2_nonpref_pitch * (max(self.num_wmasks + 1, self.col_addr_size + 1)) + self.wmask_bus_gap + if self.num_spare_cols: + self.spare_wen_bus_gap = self.m2_nonpref_pitch * 2 + self.spare_wen_bus_size = self.m2_nonpref_pitch * (max(self.num_spare_cols + 1, self.col_addr_size + 1)) + self.spare_wen_bus_gap + else: + self.spare_wen_bus_size = 0 + + elif self.num_spare_cols and not self.write_size: + self.data_bus_gap = self.m4_nonpref_pitch * 2 + self.data_bus_size = self.m4_nonpref_pitch * (self.word_size + self.num_spare_cols) + self.data_bus_gap + self.spare_wen_bus_gap = self.m2_nonpref_pitch * 2 + self.spare_wen_bus_size = self.m2_nonpref_pitch * (max(self.num_spare_cols + 1, self.col_addr_size + 1)) + self.spare_wen_bus_gap + else: self.data_bus_gap = self.m3_nonpref_pitch * 2 - self.data_bus_size = self.m3_nonpref_pitch * (max(self.word_size + self.num_spare_cols + 1, self.col_addr_size + 1)) + self.data_bus_gap + self.data_bus_size = self.m3_nonpref_pitch * (max(self.word_size + 1, self.col_addr_size + 1)) + self.data_bus_gap self.col_addr_bus_gap = self.m2_nonpref_pitch * 2 self.col_addr_bus_size = self.m2_nonpref_pitch * (self.col_addr_size) + self.col_addr_bus_gap @@ -81,34 +99,57 @@ class sram_1bank(sram_base): if port in self.write_ports: if self.write_size: + bus_size = max(self.wmask_bus_size, self.spare_wen_bus_size) # Add the write mask flops below the write mask AND array. wmask_pos[port] = vector(self.bank.bank_array_ll.x, - - self.wmask_bus_size - self.dff.height) + - bus_size - self.dff.height) self.wmask_dff_insts[port].place(wmask_pos[port]) # Add the data flops below the write mask flops. data_pos[port] = vector(self.bank.bank_array_ll.x, - - self.data_bus_size - self.wmask_bus_size - 2 * self.dff.height) + - self.data_bus_size - bus_size - 2 * self.dff.height) self.data_dff_insts[port].place(data_pos[port]) + + #Add spare write enable flops to the right of write mask flops + if self.num_spare_cols: + spare_wen_pos[port] = vector(self.bank.bank_array_ll.x + self.wmask_dff_insts[port].width + self.bank.m2_gap, + - bus_size - self.dff.height) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) + + elif self.num_spare_cols and not self.write_size: + # Add spare write enable flops below bank (lower right) + spare_wen_pos[port] = vector(self.bank.bank_array_ll.x, + - self.spare_wen_bus_size - self.dff.height) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) + + # Add the data flops below the spare write enable flops. + data_pos[port] = vector(self.bank.bank_array_ll.x, + - self.data_bus_size - self.spare_wen_bus_size - 2 * self.dff.height) + self.data_dff_insts[port].place(data_pos[port]) + else: # Add the data flops below the bank to the right of the lower-left of bank array # This relies on the lower-left of the array of the bank # decoder in upper left, bank in upper right, sensing in lower right. # These flops go below the sensing and leave a gap to channel route to the # sense amps. - if port in self.write_ports: - data_pos[port] = vector(self.bank.bank_array_ll.x, - -self.data_bus_size - self.dff.height) - self.data_dff_insts[port].place(data_pos[port]) + data_pos[port] = vector(self.bank.bank_array_ll.x, + -self.data_bus_size - self.dff.height) + self.data_dff_insts[port].place(data_pos[port]) + else: wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0) data_pos[port] = vector(self.bank.bank_array_ll.x, 0) + spare_wen_pos[port] = vector(self.bank.bank_array_ll.x, 0) # Add the col address flops below the bank to the left of the lower-left of bank array if self.col_addr_dff: if self.write_size: col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, - -self.wmask_bus_size - self.col_addr_dff_insts[port].height) + -bus_size - self.col_addr_dff_insts[port].height) + elif self.num_spare_cols and not self.write_size: + col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, + -self.spare_wen_bus_size - self.col_addr_dff_insts[port].height) else: col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, -self.data_bus_size - self.col_addr_dff_insts[port].height) @@ -134,15 +175,34 @@ class sram_1bank(sram_base): if port in self.write_ports: if self.write_size: - # Add the write mask flops below the write mask AND array. + bus_size = max(self.wmask_bus_size, self.spare_wen_bus_size) + # Add the write mask flops above the write mask AND array. wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width, - self.bank.height + self.wmask_bus_size + self.dff.height) + self.bank.height + bus_size + self.dff.height) self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX") - # Add the data flops below the write mask flops + # Add the data flops above the write mask flops data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, - self.bank.height + self.wmask_bus_size + self.data_bus_size + 2 * self.dff.height) + self.bank.height + bus_size + self.data_bus_size + 2 * self.dff.height) self.data_dff_insts[port].place(data_pos[port], mirror="MX") + + if self.num_spare_cols: + spare_wen_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width + - self.spare_wen_dff_insts[port].width - self.bank.m2_gap, + self.bank.height + bus_size + self.dff.height)) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") + + # Place dffs when spare cols is enabled + elif self.num_spare_cols and not self.write_size: + # Spare wen flops on the upper right, below data flops + spare_wen_pos[port] = vector(self.bank.bank_array_ur.x - self.spare_wen_dff_insts[port].width, + self.bank.height + self.spare_wen_bus_size + self.dff.height) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") + # Add the data flops above the spare write enable flops + data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, + self.bank.height + self.spare_wen_bus_size + self.data_bus_size + 2 * self.dff.height) + self.data_dff_insts[port].place(data_pos[port], mirror="MX") + else: # Add the data flops above the bank to the left of the upper-right of bank array # This relies on the upper-right of the array of the bank @@ -157,7 +217,10 @@ class sram_1bank(sram_base): if self.col_addr_dff: if self.write_size: col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, - self.bank.height + self.wmask_bus_size + self.dff.height) + self.bank.height + bus_size + self.dff.height) + elif self.num_spare_cols and not self.write_size: + col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, + self.bank.height + self.spare_wen_bus_size + self.dff.height) else: col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, self.bank.height + self.data_bus_size + self.dff.height) @@ -218,9 +281,10 @@ class sram_1bank(sram_base): self.copy_layout_pin(self.wmask_dff_insts[port], "din_{}".format(bit), "wmask{0}[{1}]".format(port, bit)) + for bit in range(self.num_spare_cols): - self.copy_layout_pin(self.bank_inst, - "spare_wen{0}_{1}".format(port, bit), + self.copy_layout_pin(self.spare_wen_dff_insts[port], + "din_{}".format(bit), "spare_wen{0}[{1}]".format(port, bit)) def route_layout(self): @@ -242,6 +306,9 @@ class sram_1bank(sram_base): if self.write_size: self.route_wmask_dff() + if self.num_spare_cols: + self.route_spare_wen_dff() + def route_clk(self): """ Route the clock network """ @@ -308,6 +375,15 @@ class sram_1bank(sram_base): self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height)) self.add_wire(self.m2_stack[::-1], [wmask_dff_clk_pos, mid_pos, clk_steiner_pos]) + if self.num_spare_cols: + spare_wen_dff_clk_pin = self.spare_wen_dff_insts[port].get_pin("clk") + spare_wen_dff_clk_pos = spare_wen_dff_clk_pin.center() + mid_pos = vector(clk_steiner_pos.x, spare_wen_dff_clk_pos.y) + # In some designs, the steiner via will be too close to the mid_pos via + # so make the wire as wide as the contacts + self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height)) + self.add_wire(self.m2_stack[::-1], [spare_wen_dff_clk_pos, mid_pos, clk_steiner_pos]) + def route_control_logic(self): """ Route the control logic pins that are not inputs """ @@ -373,20 +449,14 @@ class sram_1bank(sram_base): """ 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 self.write_ports: - if self.write_size: - if port % 2: - offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size) - else: - offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap) + if port % 2: + offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size) else: - if port % 2: - offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size) - else: - offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap) + offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap) dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)] dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] - if self.write_size: + if self.write_size or self.num_spare_cols: for x in dff_names: pin = self.data_dff_insts[port].get_pin(x) pin_offset = pin.center() @@ -399,7 +469,7 @@ class sram_1bank(sram_base): bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] - if self.write_size: + if self.write_size or self.num_spare_cols: for x in bank_names: pin = self.bank_inst.get_pin(x) if port % 2: @@ -411,7 +481,7 @@ class sram_1bank(sram_base): offset=pin_offset) route_map = list(zip(bank_pins, dff_pins)) - if self.write_size: + if self.write_size or self.num_spare_cols: layer_stack = self.m3_stack else: layer_stack = self.m1_stack @@ -448,6 +518,36 @@ class sram_1bank(sram_base): self.create_horizontal_channel_route(netlist=route_map, offset=offset, layer_stack=self.m1_stack) + def route_spare_wen_dff(self): + """ Connect the output of the spare write enable flops to the spare write drivers """ + # This is where the channel will start (y-dimension at least) + for port in self.write_ports: + if port % 2: + # for port 0 + offset = self.spare_wen_dff_insts[port].ll() - vector(0, self.spare_wen_bus_size) + else: + offset = self.spare_wen_dff_insts[port].ul() + vector(0, self.spare_wen_bus_gap) + + dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] + dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] + for x in dff_names: + offset_pin = self.spare_wen_dff_insts[port].get_pin(x).center() + self.add_via_center(layers=self.m1_stack, + offset=offset_pin, + directions=("V", "V")) + + bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + for x in bank_names: + offset_pin = self.bank_inst.get_pin(x).center() + self.add_via_center(layers=self.m1_stack, + offset=offset_pin) + + route_map = list(zip(bank_pins, dff_pins)) + self.create_horizontal_channel_route(netlist=route_map, + offset=offset, + layer_stack=self.m1_stack) + def add_lvs_correspondence_points(self): """ @@ -470,6 +570,9 @@ class sram_1bank(sram_base): if self.write_size: for inst in self.wmask_dff_insts: self.graph_inst_exclude.add(inst) + if self.num_spare_cols: + for inst in self.spare_wen_dff_insts: + self.graph_inst_exclude.add(inst) def graph_exclude_addr_dff(self): """Removes data dff from search graph. """ diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 0ca569af..8002b2f1 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -285,6 +285,10 @@ class sram_base(design, verilog, lef): if self.write_size: self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks) self.add_mod(self.wmask_dff) + + if self.num_spare_cols: + self.spare_wen_dff = factory.create("dff_array", module_name="spare_wen_dff", rows=1, columns=self.num_spare_cols) + self.add_mod(self.spare_wen_dff) # Create the bank module (up to four are instantiated) self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank") @@ -358,7 +362,7 @@ class sram_base(design, verilog, lef): for bit in range(self.num_wmasks): temp.append("bank_wmask{}[{}]".format(port, bit)) for bit in range(self.num_spare_cols): - temp.append("spare_wen{0}[{1}]".format(port, bit)) + temp.append("bank_spare_wen{0}[{1}]".format(port, bit)) for port in self.all_ports: temp.append("wl_en{0}".format(port)) temp.extend(["vdd", "gnd"]) @@ -475,7 +479,29 @@ class sram_base(design, verilog, lef): self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) return insts + + def create_spare_wen_dff(self): + """ Add all spare write enable flops """ + insts = [] + for port in self.all_ports: + if port in self.write_ports: + insts.append(self.add_inst(name="spare_wen_dff{}".format(port), + mod=self.spare_wen_dff)) + else: + insts.append(None) + continue + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.num_spare_cols): + inputs.append("spare_wen{}[{}]".format(port, bit)) + outputs.append("bank_spare_wen{}[{}]".format(port, bit)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) + + return insts + def create_control_logic(self): """ Add control logic instances """ diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py index d2df2aef..575fc51f 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class sram_1bank_nomux_1rw_1r_test(openram_test): +class sram_1bank_nomux_1rw_1r_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/20_sram_1bank_nomux_wmask_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py similarity index 89% rename from compiler/tests/20_sram_1bank_nomux_wmask_spare_cols_test.py rename to compiler/tests/20_sram_1bank_nomux_spare_cols_test.py index dba96ca2..14001c19 100755 --- a/compiler/tests/20_sram_1bank_nomux_wmask_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py @@ -18,14 +18,13 @@ import debug # @unittest.skip("SKIPPING 20_sram_1bank_nomux_wmask_test") -class sram_1bank_nomux_wmask_test(openram_test): +class sram_1bank_nomux_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config c = sram_config(word_size=8, - write_size=4, num_spare_cols=3, num_words=16, num_banks=1) @@ -33,13 +32,13 @@ class sram_1bank_nomux_wmask_test(openram_test): c.words_per_row = 1 c.recompute_sizes() debug.info(1, "Layout test for {}rw,{}r,{}w sram " - "with {} bit words, {} words, {} bit writes, {} words per " + "with {} bit words, {} words, {} spare cols, {} words per " "row, {} banks".format(OPTS.num_rw_ports, OPTS.num_r_ports, OPTS.num_w_ports, c.word_size, c.num_words, - c.write_size, + c.num_spare_cols, c.words_per_row, c.num_banks)) a = factory.create(module_type="sram", sram_config=c) diff --git a/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py b/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py new file mode 100755 index 00000000..902cacdd --- /dev/null +++ b/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING 22_sram_1bank_2mux_sparecols_func_test") +class sram_1bank_2mux_sparecols_func_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=32, + num_spare_cols=3, + num_banks=1) + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Functional test for sram with " + "{} bit words, {} words, {} words per row, {} spare columns, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_spare_cols, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + (fail, error) = f.run() + self.assertTrue(fail,error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py b/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py new file mode 100755 index 00000000..347d15d0 --- /dev/null +++ b/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING 22_sram_func_test") +class sram_1bank_nomux_sparecols_func_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.analytical_delay = False + OPTS.netlist_only = True + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=3, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Functional test for sram with " + "{} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + (fail, error) = f.run() + self.assertTrue(fail,error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 38f5e8b865e41577959f9eb243d63a466f6cc4f1 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 10:01:02 -0700 Subject: [PATCH 040/206] Add col mux tests for multiport --- .../04_single_level_column_mux_1rw_1r_test.py | 45 +++++++++++++++++++ .../tests/04_single_level_column_mux_test.py | 1 - ...ngle_level_column_mux_array_1rw_1r_test.py | 44 ++++++++++++++++++ .../07_single_level_column_mux_array_test.py | 2 - 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100755 compiler/tests/04_single_level_column_mux_1rw_1r_test.py create mode 100755 compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py diff --git a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py new file mode 100755 index 00000000..525191d7 --- /dev/null +++ b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class single_level_column_mux_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(2, "Checking column mux port 0") + tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + debug.info(2, "Checking column mux port 1") + tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_single_level_column_mux_test.py b/compiler/tests/04_single_level_column_mux_test.py index 2b437987..20dfe968 100755 --- a/compiler/tests/04_single_level_column_mux_test.py +++ b/compiler/tests/04_single_level_column_mux_test.py @@ -15,7 +15,6 @@ from globals import OPTS from sram_factory import factory import debug -#@unittest.skip("SKIPPING 04_driver_test") class single_level_column_mux_test(openram_test): diff --git a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py new file mode 100755 index 00000000..a8d804c9 --- /dev/null +++ b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class single_level_column_mux_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(1, "Testing sample for 4-way column_mux_array port 0") + a = factory.create(module_type="single_level_column_mux_array", columns=8, port=0, word_size=2, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) + + debug.info(1, "Testing sample for 4-way column_mux_array port 1") + a = factory.create(module_type="single_level_column_mux_array", columns=8, port=0, word_size=2, bitcell_bl="bl1", bitcell_br="br1") + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index fb608f68..ff7ec1c9 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -19,9 +19,7 @@ class single_level_column_mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - import single_level_column_mux_array - # check single level column mux array in single port debug.info(1, "Testing sample for 2-way column_mux_array") a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=8) self.local_check(a) From 7a602b75a47280399260235fb8ef0edd6e3d4f4b Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 3 Jun 2020 12:54:15 -0700 Subject: [PATCH 041/206] keep dev routing changes to hierarchy_layout --- compiler/base/hierarchy_layout.py | 37 ++++--------------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 36505294..c6f6ff49 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -701,7 +701,7 @@ class layout(): boundary = [self.find_lowest_coords(), self.find_highest_coords()] debug.check(boundary[0] and boundary[1], "No shapes to make a boundary.") - + height = boundary[1][1] - boundary[0][1] width = boundary[1][0] - boundary[0][0] (layer_number, layer_purpose) = techlayer[boundary_layer] @@ -1008,13 +1008,8 @@ class layout(): max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) - # max_x_lc & min_x_rc are for routing to/from the edge of the pins - # to increase spacing between contacts of different nets - max_x_lc = max([pin.lc().x for pin in pins]) - min_x_rc = min([pin.rc().x for pin in pins]) - # if we are less than a pitch, just create a non-preferred layer jog - if max_x_lc - min_x_rc <= pitch: + if max_x - min_x <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] # Add the horizontal trunk on the vertical layer! @@ -1035,15 +1030,7 @@ class layout(): # Route each pin to the trunk for pin in pins: - # If there is sufficient space, Route from the edge of the pins - # Otherwise, route from the center of the pins - if max_x_lc - min_x_rc > pitch: - if pin.center().x == max_x: - mid = vector(pin.lc().x, trunk_offset.y) - else: - mid = vector(pin.rc().x, trunk_offset.y) - else: - mid = vector(pin.center().x, trunk_offset.y) + mid = vector(pin.center().x, trunk_offset.y) self.add_path(self.vertical_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid) @@ -1060,13 +1047,8 @@ class layout(): max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) - # max_y_bc & min_y_uc are for routing to/from the edge of the pins - # to reduce spacing between contacts of different nets - max_y_bc = max([pin.bc().y for pin in pins]) - min_y_uc = min([pin.uc().y for pin in pins]) - # if we are less than a pitch, just create a non-preferred layer jog - if max_y_bc - min_y_uc <= pitch: + if max_y - min_y <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] @@ -1088,17 +1070,6 @@ class layout(): # Route each pin to the trunk for pin in pins: - # This code block currently causes drc violations for the topmost - # port when using multiport, TODO: fix or remove this block - # # If there is sufficient space, Route from the edge of the pins - # # Otherwise, route from the center of the pins - # if max_y_bc - min_y_uc > pitch: - # if pin.center().y == max_y: - # mid = vector(trunk_offset.x, pin.bc().y) - # else: - # mid = vector(trunk_offset.x, pin.uc().y) - # else: - # mid = vector(trunk_offset.x, pin.center().y) mid = vector(trunk_offset.x, pin.center().y) self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, From e93f3f1d2efed0b55ff7222846126e7533573e1c Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 14:30:15 -0700 Subject: [PATCH 042/206] Add 1rw_1r tests --- .../06_hierarchical_decoder_1rw_1r_test.py | 68 ++++++++++++++++++ .../tests/06_hierarchical_decoder_test.py | 6 -- ...6_hierarchical_predecode2x4_1rw_1r_test.py | 42 +++++++++++ .../06_hierarchical_predecode2x4_test.py | 6 -- ...6_hierarchical_predecode3x8_1rw_1r_test.py | 42 +++++++++++ .../06_hierarchical_predecode3x8_test.py | 6 -- .../08_wordline_driver_array_1rw_1r_test.py | 43 ++++++++++++ compiler/tests/18_port_address_1rw_1r_test.py | 40 +++++++++++ compiler/tests/18_port_address_test.py | 1 + .../tests/19_single_bank_wmask_1rw_1r_test.py | 69 +++++++++++++++++++ 10 files changed, 305 insertions(+), 18 deletions(-) create mode 100755 compiler/tests/06_hierarchical_decoder_1rw_1r_test.py create mode 100755 compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py create mode 100755 compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py create mode 100755 compiler/tests/08_wordline_driver_array_1rw_1r_test.py create mode 100755 compiler/tests/18_port_address_1rw_1r_test.py create mode 100755 compiler/tests/19_single_bank_wmask_1rw_1r_test.py diff --git a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py new file mode 100755 index 00000000..5b317e6e --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + # Checks 2x4 and 2-input NAND decoder + debug.info(1, "Testing 16 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=16) + self.local_check(a) + + # Checks 2x4 and 2-input NAND decoder with non-power-of-two + debug.info(1, "Testing 17 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=17) + self.local_check(a) + + # Checks 2x4 with 3x8 and 2-input NAND decoder + debug.info(1, "Testing 32 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=32) + self.local_check(a) + + # Checks 3 x 2x4 and 3-input NAND decoder + debug.info(1, "Testing 64 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=64) + self.local_check(a) + + # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two + debug.info(1, "Testing 132 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=132) + self.local_check(a) + + # Checks 3 x 3x8 and 3-input NAND decoder + debug.info(1, "Testing 512 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=512) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 7d155b01..e2b34cba 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -21,12 +21,6 @@ class hierarchical_decoder_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - # Checks 2x4 and 2-input NAND decoder debug.info(1, "Testing 16 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) diff --git a/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py new file mode 100755 index 00000000..6512238f --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_predecode2x4_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(1, "Testing sample for hierarchy_predecode2x4") + a = factory.create(module_type="hierarchical_predecode2x4") + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index 94fd4838..c2b51f10 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -21,12 +21,6 @@ class hierarchical_predecode2x4_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - debug.info(1, "Testing sample for hierarchy_predecode2x4") a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py new file mode 100755 index 00000000..466a7e40 --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_predecode3x8_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(1, "Testing sample for hierarchy_predecode3x8") + a = factory.create(module_type="hierarchical_predecode3x8") + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index 33f94d9d..c1471a40 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -21,12 +21,6 @@ class hierarchical_predecode3x8_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - debug.info(1, "Testing sample for hierarchy_predecode3x8") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/08_wordline_driver_array_1rw_1r_test.py b/compiler/tests/08_wordline_driver_array_1rw_1r_test.py new file mode 100755 index 00000000..7c97ff75 --- /dev/null +++ b/compiler/tests/08_wordline_driver_array_1rw_1r_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class wordline_driver_array_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + # check wordline driver for single port + debug.info(2, "Checking driver") + tx = factory.create(module_type="wordline_driver_array", rows=8, cols=32) + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_1rw_1r_test.py new file mode 100755 index 00000000..402b30a0 --- /dev/null +++ b/compiler/tests/18_port_address_1rw_1r_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_address_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(1, "Port address 16 rows") + a = factory.create("port_address", cols=16, rows=16) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_address_test.py b/compiler/tests/18_port_address_test.py index 23f35540..a2a8bf0b 100755 --- a/compiler/tests/18_port_address_test.py +++ b/compiler/tests/18_port_address_test.py @@ -13,6 +13,7 @@ from globals import OPTS from sram_factory import factory import debug + class port_address_test(openram_test): def runTest(self): diff --git a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py new file mode 100755 index 00000000..d08ff8cc --- /dev/null +++ b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class single_bank_wmask_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + + + c = sram_config(word_size=8, + write_size=4, + num_words=16, + num_banks=1) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 3b1fe26d252ded614cd61a9efa4ec4e50c8fde62 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 14:33:30 -0700 Subject: [PATCH 043/206] Spacing between decoder and driver for s8 --- compiler/modules/hierarchical_decoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 8b711031..2b624ddc 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -191,7 +191,7 @@ class hierarchical_decoder(design.design): + self.predecoder_width \ + self.internal_routing_width \ + self.nand_width \ - + self.m1_space + + 2 * self.m1_pitch def route_inputs(self): """ Create input bus for the predecoders """ From 4bc1e9a026cb412374b90945ac6a22642635ae1e Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 15:47:03 -0700 Subject: [PATCH 044/206] Fix the bitline spacing in the column mux to a constant. --- compiler/pgates/single_level_column_mux.py | 101 ++++++------------ .../04_single_level_column_mux_1rw_1r_test.py | 2 +- 2 files changed, 32 insertions(+), 71 deletions(-) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index bdd03e55..96935158 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -13,6 +13,7 @@ from sram_factory import factory import logical_effort from utils import round_to_grid + class single_level_column_mux(pgate.pgate): """ This module implements the columnmux bitline cell used in the design. @@ -44,9 +45,18 @@ class single_level_column_mux(pgate.pgate): def create_layout(self): - self.pin_height = 2 * self.m2_width + # If li exists, use li and m1 for the mux, otherwise use m1 and m2 + if "li" in layer: + self.col_mux_stack = self.li_stack + else: + self.col_mux_stack = self.m1_stack + self.pin_layer = self.bitcell.get_pin(self.bitcell_bl).layer + self.pin_pitch = getattr(self, "{}_pitch".format(self.pin_layer)) + self.pin_width = getattr(self, "{}_width".format(self.pin_layer)) + self.pin_height = 2 * self.pin_width self.width = self.bitcell.width self.height = self.nmos_upper.uy() + self.pin_height + self.connect_poly() self.add_bitline_pins() self.connect_bitlines() @@ -58,9 +68,7 @@ class single_level_column_mux(pgate.pgate): # Adds nmos_lower,nmos_upper to the module self.ptx_width = self.tx_size * drc("minwidth_tx") self.nmos = factory.create(module_type="ptx", - width=self.ptx_width, - add_source_contact=False, - add_drain_contact=False) + width=self.ptx_width) self.add_mod(self.nmos) def add_pins(self): @@ -69,40 +77,26 @@ class single_level_column_mux(pgate.pgate): def add_bitline_pins(self): """ Add the top and bottom pins to this cell """ - bl_pin=self.bitcell.get_pin(self.bitcell_bl) - br_pin=self.bitcell.get_pin(self.bitcell_br) - - bl_pos = vector(bl_pin.lx(), 0) - br_pos = vector(br_pin.lx(), 0) - - # The bitline input/output pins must be a least as wide as the metal pitch - # so that there is enough space to route to/from the pins. - # FIXME: bitline_metal_pitch should be greater than the horizontal metal pitch used in port_data - bitline_metal_pitch = self.width / 2 - bitline_width = br_pos.x - bl_pos.x - if bitline_width < bitline_metal_pitch: - bitline_width_increase_bl = round_to_grid((bitline_metal_pitch - bitline_width) / 2) - bitline_width_increase_br = round_to_grid((bitline_metal_pitch - bitline_width) - bitline_width_increase_bl) - bl_pos = bl_pos + vector(-bitline_width_increase_bl, 0) - br_pos = br_pos + vector( bitline_width_increase_br, 0) + bl_pos = vector(self.pin_pitch, 0) + br_pos = vector(self.width - self.pin_pitch, 0) # bl and br self.add_layout_pin(text="bl", - layer=bl_pin.layer, + layer=self.pin_layer, offset=bl_pos + vector(0, self.height - self.pin_height), height=self.pin_height) self.add_layout_pin(text="br", - layer=br_pin.layer, + layer=self.pin_layer, offset=br_pos + vector(0, self.height - self.pin_height), height=self.pin_height) # bl_out and br_out self.add_layout_pin(text="bl_out", - layer=bl_pin.layer, + layer=self.pin_layer, offset=bl_pos, height=self.pin_height) self.add_layout_pin(text="br_out", - layer=br_pin.layer, + layer=self.pin_layer, offset=br_pos, height=self.pin_height) @@ -110,7 +104,7 @@ class single_level_column_mux(pgate.pgate): """ Create the two pass gate NMOS transistors to switch the bitlines""" # Space it in the center - nmos_lower_position = self.nmos.active_offset.scale(0,1) \ + nmos_lower_position = self.nmos.active_offset.scale(0, 1) \ + vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0) self.nmos_lower = self.add_inst(name="mux_tx1", mod=self.nmos, @@ -145,62 +139,29 @@ class single_level_column_mux(pgate.pgate): def connect_bitlines(self): """ Connect the bitlines to the mux transistors """ - # If li exists, use li and m1 for the mux, otherwise use m1 and m2 - if "li" in layer: - self.col_mux_stack = self.li_stack - else: - self.col_mux_stack = self.m1_stack - - # These are on metal2 bl_pin = self.get_pin("bl") br_pin = self.get_pin("br") bl_out_pin = self.get_pin("bl_out") br_out_pin = self.get_pin("br_out") - # These are on metal1 nmos_lower_s_pin = self.nmos_lower.get_pin("S") nmos_lower_d_pin = self.nmos_lower.get_pin("D") nmos_upper_s_pin = self.nmos_upper.get_pin("S") nmos_upper_d_pin = self.nmos_upper.get_pin("D") # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D - self.add_via_center(layers=self.col_mux_stack, - offset=bl_pin.bc(), - directions=("V", "V")) - self.add_via_center(layers=self.col_mux_stack, - offset=br_out_pin.uc(), - directions=("V", "V")) - self.add_via_center(layers=self.col_mux_stack, - offset=nmos_upper_s_pin.center(), - directions=("V", "V")) - self.add_via_center(layers=self.col_mux_stack, - offset=nmos_lower_d_pin.center(), - directions=("V", "V")) - - # Add diffusion contacts - # These were previously omitted with the options: add_source_contact=False, add_drain_contact=False - # They are added now and not previously so that they do not include m1 (which is usually included by default) - # This is only a concern when the local interconnect (li) layer is being used - self.add_via_center(layers=self.active_stack, - offset=nmos_upper_d_pin.center(), - directions=("V", "V"), - implant_type="n", - well_type="nwell") - self.add_via_center(layers=self.active_stack, - offset=nmos_lower_s_pin.center(), - directions=("V", "V"), - implant_type="n", - well_type="nwell") - self.add_via_center(layers=self.active_stack, - offset=nmos_upper_s_pin.center(), - directions=("V", "V"), - implant_type="n", - well_type="nwell") - self.add_via_center(layers=self.active_stack, - offset=nmos_lower_d_pin.center(), - directions=("V", "V"), - implant_type="n", - well_type="nwell") + self.add_via_stack_center(from_layer=bl_pin.layer, + to_layer=self.col_mux_stack[0], + offset=bl_pin.bc()) + self.add_via_stack_center(from_layer=br_out_pin.layer, + to_layer=self.col_mux_stack[0], + offset=br_out_pin.uc()) + self.add_via_stack_center(from_layer=nmos_upper_s_pin.layer, + to_layer=self.col_mux_stack[2], + offset=nmos_upper_s_pin.center()) + self.add_via_stack_center(from_layer=nmos_lower_d_pin.layer, + to_layer=self.col_mux_stack[2], + offset=nmos_lower_d_pin.center()) # bl -> nmos_upper/D on metal1 # bl_out -> nmos_upper/S on metal2 diff --git a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py index 525191d7..69b31cc6 100755 --- a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py +++ b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py @@ -32,7 +32,7 @@ class single_level_column_mux_1rw_1r_test(openram_test): self.local_check(tx) debug.info(2, "Checking column mux port 1") - tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl1", bitcell_br="br1") self.local_check(tx) globals.end_openram() From 4183638f0354cb7ac909c1d6752b67a137d1191d Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 16:05:57 -0700 Subject: [PATCH 045/206] Align precharge bitlines with col mux --- compiler/pgates/precharge.py | 12 ++++++------ ...w1r_test.py => 08_precharge_array_1rw_1r_test.py} | 0 2 files changed, 6 insertions(+), 6 deletions(-) rename compiler/tests/{08_precharge_array_1rw1r_test.py => 08_precharge_array_1rw_1r_test.py} (100%) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 8b84a1b8..278fef3d 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -235,11 +235,11 @@ class precharge(design.design): """ Adds both bit-line and bit-line-bar to the module """ - layer_width = drc("minwidth_" + self.bitline_layer) - layer_space = drc("{0}_to_{0}".format(self.bitline_layer)) + layer_pitch = getattr(self, "{}_pitch".format(self.bitline_layer)) + layer_width = getattr(self, "{}_width".format(self.bitline_layer)) - # adds the BL - self.bl_xoffset = layer_space + 0.5 * layer_width + # adds the BL so it aligns with the col mux + self.bl_xoffset = layer_pitch + 0.5 * layer_width top_pos = vector(self.bl_xoffset, self.height) pin_pos = vector(self.bl_xoffset, 0) self.add_path(self.bitline_layer, [top_pos, pin_pos]) @@ -248,8 +248,8 @@ class precharge(design.design): start=pin_pos, end=top_pos) - # adds the BR - self.br_xoffset = self.width - layer_space - 0.5 * layer_width + # adds the BR so it aligns with the col mux + self.br_xoffset = self.width - layer_pitch - 0.5 * layer_width top_pos = vector(self.br_xoffset, self.height) pin_pos = vector(self.br_xoffset, 0) self.add_path(self.bitline_layer, [top_pos, pin_pos]) diff --git a/compiler/tests/08_precharge_array_1rw1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py similarity index 100% rename from compiler/tests/08_precharge_array_1rw1r_test.py rename to compiler/tests/08_precharge_array_1rw_1r_test.py From b2b7e7800bd01ac27e4051a6f78764d549086205 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 16:39:05 -0700 Subject: [PATCH 046/206] Undo same bitline pitch --- compiler/pgates/precharge.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 278fef3d..893736f4 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -236,10 +236,9 @@ class precharge(design.design): Adds both bit-line and bit-line-bar to the module """ layer_pitch = getattr(self, "{}_pitch".format(self.bitline_layer)) - layer_width = getattr(self, "{}_width".format(self.bitline_layer)) - # adds the BL so it aligns with the col mux - self.bl_xoffset = layer_pitch + 0.5 * layer_width + # adds the BL + self.bl_xoffset = layer_pitch top_pos = vector(self.bl_xoffset, self.height) pin_pos = vector(self.bl_xoffset, 0) self.add_path(self.bitline_layer, [top_pos, pin_pos]) @@ -248,8 +247,8 @@ class precharge(design.design): start=pin_pos, end=top_pos) - # adds the BR so it aligns with the col mux - self.br_xoffset = self.width - layer_pitch - 0.5 * layer_width + # adds the BR + self.br_xoffset = self.width - layer_pitch top_pos = vector(self.br_xoffset, self.height) pin_pos = vector(self.br_xoffset, 0) self.add_path(self.bitline_layer, [top_pos, pin_pos]) From 3927c62e3269f9fa4a0860fdf0a259bc3a14477b Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 16:39:33 -0700 Subject: [PATCH 047/206] Undo extra space due to nwell spacing --- compiler/modules/hierarchical_decoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 2b624ddc..8b711031 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -191,7 +191,7 @@ class hierarchical_decoder(design.design): + self.predecoder_width \ + self.internal_routing_width \ + self.nand_width \ - + 2 * self.m1_pitch + + self.m1_space def route_inputs(self): """ Create input bus for the predecoders """ From 77c95b28da19b37ae638a50b85d53c7bb06bb206 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 16:39:46 -0700 Subject: [PATCH 048/206] Rename precharge test --- compiler/tests/08_precharge_array_1rw_1r_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/tests/08_precharge_array_1rw_1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py index 1381b93a..dde36b23 100755 --- a/compiler/tests/08_precharge_array_1rw_1r_test.py +++ b/compiler/tests/08_precharge_array_1rw_1r_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class precharge_test(openram_test): +class precharge_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) From 249b5355ba8ed083e011c000c3f3fb3ed6f8b01f Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 17:08:04 -0700 Subject: [PATCH 049/206] Adjust rbl route --- compiler/modules/bank.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 393631e5..2aa4e871 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -128,10 +128,10 @@ class bank(design.design): bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name) # This will ensure the pin is only on the top or bottom edge if port % 2: - via_offset = bl_pin.uc() + vector(0, 1.5 * self.m2_pitch) + via_offset = bl_pin.uc() + vector(0, self.m2_pitch) left_right_offset = vector(self.max_x_offset, via_offset.y) else: - via_offset = bl_pin.bc() - vector(0, 1.5 * self.m2_pitch) + via_offset = bl_pin.bc() - vector(0, self.m2_pitch) left_right_offset = vector(self.min_x_offset, via_offset.y) self.add_via_stack_center(from_layer=bl_pin.layer, to_layer="m3", From 7aafa438978cd4f38afa501d40357ddea0593ce6 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 4 Jun 2020 10:22:52 -0700 Subject: [PATCH 050/206] Connect RBL to bottom of precharge cell --- compiler/modules/bank.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 2aa4e871..92e4e8cd 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -123,23 +123,22 @@ class bank(design.design): def route_rbl(self, port): """ Route the rbl_bl and rbl_wl """ - - bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) - bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name) - # This will ensure the pin is only on the top or bottom edge + + # Connect the rbl to the port data pin + bl_pin = self.port_data_inst[port].get_pin("rbl_bl") if port % 2: - via_offset = bl_pin.uc() + vector(0, self.m2_pitch) - left_right_offset = vector(self.max_x_offset, via_offset.y) + pin_offset = bl_pin.uc() + left_right_offset = vector(self.max_x_offset, pin_offset.y) else: - via_offset = bl_pin.bc() - vector(0, self.m2_pitch) - left_right_offset = vector(self.min_x_offset, via_offset.y) + pin_offset = bl_pin.bc() + left_right_offset = vector(self.min_x_offset, pin_offset.y) self.add_via_stack_center(from_layer=bl_pin.layer, to_layer="m3", - offset=via_offset) + offset=pin_offset) self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port), layer="m3", start=left_right_offset, - end=via_offset) + end=pin_offset) def route_bitlines(self, port): """ Route the bitlines depending on the port type rw, w, or r. """ From 717188f85c47dc8dccaa822ef5d7b20e5cdec2b5 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 4 Jun 2020 11:03:39 -0700 Subject: [PATCH 051/206] Change L shape of rbl route --- compiler/sram/sram_1bank.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 8dbcfaf4..f9a40545 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -320,7 +320,9 @@ class sram_1bank(sram_base): # Only input (besides pins) is the replica bitline src_pin = self.control_logic_insts[port].get_pin("rbl_bl") dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port)) - self.connect_hbus(src_pin, dest_pin) + self.add_wire(self.m2_stack[::-1], + [src_pin.center(), vector(src_pin.cx(), dest_pin.cy()), dest_pin.rc()]) + # self.connect_hbus(src_pin, dest_pin) def route_row_addr_dff(self): """ Connect the output of the row flops to the bank pins """ From e06dc3810a333819ba1fdb21258c77cfcbf94f41 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 4 Jun 2020 12:12:19 -0700 Subject: [PATCH 052/206] Move precharge pin to bottom --- compiler/pgates/precharge.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 893736f4..cfc2a688 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -191,14 +191,15 @@ class precharge(design.design): """ # adds the en contact to connect the gates to the en rail - # midway in the 4 M2 tracks - offset = self.lower_pmos_inst.get_pin("G").ul() \ - + vector(0, 0.5 * self.m2_pitch) + pin_offset = self.lower_pmos_inst.get_pin("G").lr() + # This is an extra space down for some techs with contact to active spacing + offset = pin_offset - vector(0, self.poly_space) self.add_via_stack_center(from_layer="poly", to_layer=self.en_layer, offset=offset) - - # adds the en rail on metal1 + self.add_path("poly", + [self.lower_pmos_inst.get_pin("G").bc(), offset]) + # adds the en rail self.add_layout_pin_segment_center(text="en_bar", layer=self.en_layer, start=offset.scale(0, 1), From 2fcecb7227e7de15fe78a3c9f3ab292062b9ae0c Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 4 Jun 2020 16:01:32 -0700 Subject: [PATCH 053/206] Variable zjog. 512 port address test. s8 port address working. --- compiler/base/hierarchy_layout.py | 7 +++--- compiler/modules/hierarchical_decoder.py | 22 ++++++------------- compiler/modules/port_address.py | 2 +- compiler/tests/18_port_address_1rw_1r_test.py | 4 ++++ compiler/tests/18_port_address_test.py | 4 ++++ 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index c6f6ff49..12a9826e 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -450,26 +450,27 @@ class layout(): path=coordinates, layer_widths=layer_widths) - def add_zjog(self, layer, start, end, first_direction="H", fixed_offset=None): + def add_zjog(self, layer, start, end, first_direction="H", var_offset=0.5, fixed_offset=None): """ Add a simple jog at the halfway point. If layer is a single value, it is a path. If layer is a tuple, it is a wire with preferred directions. """ + neg_offset = 1.0 - var_offset # vertical first if first_direction == "V": if fixed_offset: mid1 = vector(start.x, fixed_offset) else: - mid1 = vector(start.x, 0.5 * start.y + 0.5 * end.y) + mid1 = vector(start.x, neg_offset * start.y + var_offset * end.y) mid2 = vector(end.x, mid1.y) # horizontal first elif first_direction == "H": if fixed_offset: mid1 = vector(fixed_offset, start.y) else: - mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y) + mid1 = vector(neg_offset * start.x + var_offset * end.x, start.y) mid2 = vector(mid1, end.y) else: debug.error("Invalid direction for jog -- must be H or V.") diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 8b711031..fbf8ef29 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -48,11 +48,18 @@ class hierarchical_decoder(design.design): self.setup_layout_constants() self.place_pre_decoder() self.place_row_decoder() + + self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space + self.route_inputs() self.route_outputs() self.route_decoder_bus() self.route_vdd_gnd() + self.offset_all_coordinates() + + self.width = self.and_inst[0].rx() + self.m1_space + self.add_boundary() self.DRC_LVS() @@ -178,21 +185,6 @@ class hierarchical_decoder(design.design): # Extra bus space for supply contacts self.input_routing_width = self.num_inputs * self.bus_pitch + self.bus_space - # Calculates height and width of row-decoder - # Calculates height and width of hierarchical decoder - # Add extra pitch for good measure - self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space - if (self.num_inputs == 4 or self.num_inputs == 5): - self.nand_width = self.and2.width - else: - self.nand_width = self.and3.width - - self.width = self.input_routing_width \ - + self.predecoder_width \ - + self.internal_routing_width \ - + self.nand_width \ - + self.m1_space - def route_inputs(self): """ Create input bus for the predecoders """ # Find the left-most predecoder diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 6293a79d..b0c24e1e 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -93,7 +93,7 @@ class port_address(design.design): decoder_out_pos = decoder_out_pin.rc() driver_in_pin = self.wordline_driver_inst.get_pin("in_{}".format(row)) driver_in_pos = driver_in_pin.lc() - self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos) + self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos, var_offset=0.3) self.add_via_stack_center(from_layer=decoder_out_pin.layer, to_layer=self.route_layer, diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_1rw_1r_test.py index 402b30a0..33bff4ed 100755 --- a/compiler/tests/18_port_address_1rw_1r_test.py +++ b/compiler/tests/18_port_address_1rw_1r_test.py @@ -29,6 +29,10 @@ class port_address_1rw_1r_test(openram_test): debug.info(1, "Port address 16 rows") a = factory.create("port_address", cols=16, rows=16) self.local_check(a) + + debug.info(1, "Port address 512 rows") + a = factory.create("port_address", cols=256, rows=512) + self.local_check(a) globals.end_openram() diff --git a/compiler/tests/18_port_address_test.py b/compiler/tests/18_port_address_test.py index a2a8bf0b..11da333e 100755 --- a/compiler/tests/18_port_address_test.py +++ b/compiler/tests/18_port_address_test.py @@ -24,6 +24,10 @@ class port_address_test(openram_test): a = factory.create("port_address", cols=16, rows=16) self.local_check(a) + debug.info(1, "Port address 512 rows") + a = factory.create("port_address", cols=256, rows=512) + self.local_check(a) + globals.end_openram() # run the test from the command line From e14deff3d144f497126cab50675b0074bb7ae247 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 4 Jun 2020 16:03:39 -0700 Subject: [PATCH 054/206] Fixed offset in port_data --- compiler/modules/port_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 34ad4ca9..5e7ed8e5 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -732,8 +732,8 @@ class port_data(design.design): top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc() layer_pitch = getattr(self, "{}_pitch".format(top_bl_pin.layer)) - self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V", top_bl_pin.by() - layer_pitch) - self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V", top_bl_pin.by() - 2 * layer_pitch) + self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V", fixed_offset=top_bl_pin.by() - layer_pitch) + self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V", fixed_offset=top_bl_pin.by() - 2 * layer_pitch) def graph_exclude_precharge(self): """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" From 08f6bd8d241bd6aaa591a134002d8c1cf307ae41 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Fri, 5 Jun 2020 02:53:03 -0700 Subject: [PATCH 055/206] optimize tx binning for area --- compiler/pgates/pgate.py | 146 ++++++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 57 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 918cd17b..8e18d4d2 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -17,22 +17,39 @@ from globals import OPTS if(OPTS.tech_name == "s8"): from tech import nmos_bins, pmos_bins, accuracy_requirement + class pgate(design.design): """ This is a module that implements some shared functions for parameterized gates. """ - def __init__(self, name, height=None): + def __init__(self, name, height=None, add_wells=True): """ Creates a generic cell """ design.design.__init__(self, name) if height: self.height = height elif not height: - # By default, we make it 10 M1 pitch tall - self.height = 10*self.m1_pitch - + # By default, something simple + self.height = 14 * self.m1_pitch + self.add_wells = add_wells + + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + self.route_layer_width = getattr(self, "{}_width".format(self.route_layer)) + self.route_layer_space = getattr(self, "{}_space".format(self.route_layer)) + self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer)) + + # This is the space from a S/D contact to the supply rail + contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space + # This is a poly-to-poly of a flipped cell + poly_to_poly_gate_space = self.poly_extend_active + self.poly_space + self.top_bottom_space = max(contact_to_vdd_rail_space, + poly_to_poly_gate_space) + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -47,27 +64,27 @@ class pgate(design.design): """ Pure virtual function """ debug.error("Must over-ride create_layout.", -1) - def connect_pin_to_rail(self, inst, pin, supply): + def connect_pin_to_rail(self, inst, pin_name, supply_name): """ Connects a ptx pin to a supply rail. """ - source_pin = inst.get_pin(pin) - supply_pin = self.get_pin(supply) - if supply_pin.overlaps(source_pin): - return - - if supply == "gnd": - height = supply_pin.by() - source_pin.by() - elif supply == "vdd": - height = supply_pin.uy() - source_pin.by() - else: - debug.error("Invalid supply name.", -1) + supply_pin = self.get_pin(supply_name) - if abs(height) > 0: - self.add_rect(layer="m1", + source_pins = inst.get_pins(pin_name) + for source_pin in source_pins: + + if supply_name == "gnd": + height = supply_pin.by() - source_pin.by() + elif supply_name == "vdd": + height = supply_pin.uy() - source_pin.by() + else: + debug.error("Invalid supply name.", -1) + + debug.check(supply_pin.layer == source_pin.layer, "Supply pin is not on correct layer.") + self.add_rect(layer=source_pin.layer, offset=source_pin.ll(), height=height, width=source_pin.width()) - def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left"): + def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None): """ Route the input gate to the left side of the cell for access. Position specifies to place the contact the left, center, or @@ -93,8 +110,6 @@ class pgate(design.design): # Center is completely symmetric. contact_width = contact.poly_contact.width - contact_m1_width = contact.poly_contact.second_layer_width - contact_m1_height = contact.poly_contact.second_layer_height if position == "center": contact_offset = left_gate_offset \ @@ -111,18 +126,16 @@ class pgate(design.design): else: debug.error("Invalid contact placement option.", -1) - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=contact_offset) - - self.add_via_center(layers=self.poly_stack, - offset=contact_offset) + via = self.add_via_stack_center(from_layer="poly", + to_layer=self.route_layer, + offset=contact_offset, + directions=directions) self.add_layout_pin_rect_center(text=name, - layer="m1", + layer=self.route_layer, offset=contact_offset, - width=contact_m1_width, - height=contact_m1_height) + width=via.mod.second_layer_width, + height=via.mod.second_layer_height) # This is to ensure that the contact is # connected to the gate mid_point = contact_offset.scale(0.5, 1) \ @@ -137,7 +150,7 @@ class pgate(design.design): # This should match the cells in the cell library self.nwell_y_offset = 0.48 * self.height - full_height = self.height + 0.5* self.m1_width + full_height = self.height + 0.5 * self.m1_width # FIXME: float rounding problem if "nwell" in layer: @@ -148,12 +161,12 @@ class pgate(design.design): nwell_height = nwell_max_offset - self.nwell_y_offset self.add_rect(layer="nwell", offset=nwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=nwell_height) if "vtg" in layer: self.add_rect(layer="vtg", offset=nwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=nwell_height) # Start this half a rail width below the cell @@ -164,12 +177,12 @@ class pgate(design.design): pwell_height = self.nwell_y_offset - pwell_position.y self.add_rect(layer="pwell", offset=pwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=pwell_height) if "vtg" in layer: self.add_rect(layer="vtg", offset=pwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=pwell_height) def add_nwell_contact(self, pmos, pmos_pos): @@ -194,12 +207,10 @@ class pgate(design.design): self.nwell_contact = self.add_via_center(layers=layer_stack, offset=contact_offset, implant_type="n", - well_type="n") - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=contact_offset) + well_type="n", + directions=("V", "V")) - self.add_rect_center(layer="m1", + self.add_rect_center(layer=self.route_layer, offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)), width=self.nwell_contact.mod.second_layer_width, height=self.height - contact_offset.y) @@ -249,13 +260,10 @@ class pgate(design.design): self.pwell_contact= self.add_via_center(layers=layer_stack, offset=contact_offset, implant_type="p", - well_type="p") - - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=contact_offset) - - self.add_rect_center(layer="m1", + well_type="p", + directions=("V", "V")) + + self.add_rect_center(layer=self.route_layer, offset=contact_offset.scale(1, 0.5), width=self.pwell_contact.mod.second_layer_width, height=contact_offset.y) @@ -279,13 +287,33 @@ class pgate(design.design): # offset=implant_offset, # width=implant_width, # height=implant_height) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top and bottom. """ + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) def determine_width(self): """ Determine the width based on the well contacts (assumed to be on the right side) """ + + # It was already set or is left as default (minimum) # Width is determined by well contact and spacing and allowing a supply via between each cell - self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width - self.well_width = self.width + 2 * self.nwell_enclose_active - # Height is an input parameter, so it is not recomputed. + if self.add_wells: + width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width + # Height is an input parameter, so it is not recomputed. + else: + max_active_xoffset = self.find_highest_layer_coords("active").x + max_route_xoffset = self.find_highest_layer_coords(self.route_layer).x + 0.5 * self.m1_space + width = max(max_active_xoffset, max_route_xoffset) + + self.width = width @staticmethod def bin_width(tx_type, target_width): @@ -307,16 +335,20 @@ class pgate(design.design): base_bins = [] scaled_bins = [] scaling_factors = [] - scaled_bins.append(bins[-1]) - base_bins.append(bins[-1]) - scaling_factors.append(1) - for width in bins[0:-1]: + + for width in bins: m = math.ceil(target_width / width) base_bins.append(width) scaling_factors.append(m) scaled_bins.append(m * width) - - select = bisect_left(scaled_bins, target_width) + + select = -1 + for i in reversed(range(0, len(scaled_bins))): + if abs(target_width - scaled_bins[i])/target_width <= 1-accuracy_requirement: + select = i + break + if select == -1: + debug.error("failed to bin tx size {}, try reducing accuracy requirement".format(target_width), 1) scaling_factor = scaling_factors[select] scaled_bin = scaled_bins[select] selected_bin = base_bins[select] @@ -346,4 +378,4 @@ class pgate(design.design): return(scaled_bins) def bin_accuracy(self, ideal_width, width): - return abs(1-(ideal_width - width)/ideal_width) \ No newline at end of file + return abs(1-(ideal_width - width)/ideal_width) From fb3acae9087de2da778d2d032a962649e681cf79 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 09:44:30 -0700 Subject: [PATCH 056/206] PEP8 format testutils. --- compiler/tests/testutils.py | 136 ++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 4cae1a9c..eb75fc44 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -5,23 +5,21 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import unittest,warnings -import pdb,traceback -import sys,os,glob,copy -import shutil +import unittest +import sys, os, glob sys.path.append(os.getenv("OPENRAM_HOME")) 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() - tempgds = "{0}{1}.gds".format(OPTS.openram_temp,w.name) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp, w.name) w.gds_write(tempgds) import verify @@ -36,8 +34,8 @@ class openram_test(unittest.TestCase): self.reset() - tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name) - tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name) + tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name) a.lvs_write(tempspice) # cannot write gds in netlist_only mode @@ -56,34 +54,36 @@ class openram_test(unittest.TestCase): # Only allow DRC to fail and LVS to pass if we are using magic if "magic" in OPTS.drc_exe and lvs_result == 0 and drc_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) + # import shutil + # 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) debug.warning("DRC failed but LVS passed: {}".format(a.name)) - #self.fail("DRC failed but LVS passed: {}".format(a.name)) + # self.fail("DRC failed but LVS passed: {}".format(a.name)) elif drc_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) + # import shutil + # 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)) if lvs_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) + # import shutil + # 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)) - # For debug... - #import pdb; pdb.set_trace() + # import pdb; pdb.set_trace() if OPTS.purge_temp: self.cleanup() def run_pex(self, a, output=None): if output == None: output = OPTS.openram_temp + a.name + ".pex.netlist" - tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name) - tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name) + tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name) import verify result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False) @@ -97,8 +97,8 @@ class openram_test(unittest.TestCase): """ debug.info(1, "Finding feasible period for current test.") delay_obj.set_load_slew(load, slew) - test_port = delay_obj.read_ports[0] #Only test one port, assumes other ports have similar period. - delay_obj.analysis_init(probe_address="1"*sram.addr_size, probe_data=(sram.word_size-1)) + test_port = delay_obj.read_ports[0] # Only test one port, assumes other ports have similar period. + delay_obj.analysis_init(probe_address="1" * sram.addr_size, probe_data=sram.word_size - 1) delay_obj.find_feasible_period_one_port(test_port) return delay_obj.period @@ -130,29 +130,27 @@ class openram_test(unittest.TestCase): for k in data.keys(): if type(data[k])==list: for i in range(len(data[k])): - if not self.isclose(k,data[k][i],golden_data[k][i],error_tolerance): + if not self.isclose(k, data[k][i], golden_data[k][i], error_tolerance): data_matches = False else: - if not 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) - debug.error("Results exceeded {:.1f}% tolerance compared to golden results:\n".format(error_tolerance*100)+data_string) + debug.error("Results exceeded {:.1f}% tolerance compared to golden results:\n".format(error_tolerance * 100) + data_string) return data_matches - - - def isclose(self,key,value,actual_value,error_tolerance=1e-2): + def isclose(self, key, value, actual_value, error_tolerance=1e-2): """ This is used to compare relative values. """ import debug - relative_diff = self.relative_diff(value,actual_value) + relative_diff = self.relative_diff(value, actual_value) check = relative_diff <= error_tolerance if check: - debug.info(2,"CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100)) + debug.info(2, "CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key, value, actual_value, relative_diff * 100)) return True else: - debug.error("NOT CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100)) + debug.error("NOT CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key, value, actual_value, relative_diff * 100)) return False def relative_diff(self, value1, value2): @@ -169,18 +167,14 @@ class openram_test(unittest.TestCase): # Get normalization value norm_value = abs(max(value1, value2)) - # Edge case where greater is a zero - if norm_value == 0: - min_value = abs(min(value1, value2)) return abs(value1 - value2) / norm_value - - def relative_compare(self, value,actual_value,error_tolerance): + def relative_compare(self, value, actual_value, error_tolerance): """ This is used to compare relative values. """ if (value==actual_value): # if we don't need a relative comparison! return True - return (abs(value - actual_value) / max(value,actual_value) <= error_tolerance) + return (abs(value - actual_value) / max(value, actual_value) <= error_tolerance) def isapproxdiff(self, filename1, filename2, error_tolerance=0.001): """Compare two files. @@ -218,23 +212,22 @@ class openram_test(unittest.TestCase): line_num+=1 line1 = fp1.readline().decode('utf-8') line2 = fp2.readline().decode('utf-8') - #print("line1:",line1) - #print("line2:",line2) + # print("line1:", line1) + # print("line2:", line2) # 1. Find all of the floats using a regex line1_floats=rx.findall(line1) line2_floats=rx.findall(line2) - debug.info(3,"line1_floats: "+str(line1_floats)) - debug.info(3,"line2_floats: "+str(line2_floats)) - + debug.info(3, "line1_floats: " + str(line1_floats)) + debug.info(3, "line2_floats: " + str(line2_floats)) # 2. Remove the floats from the string for f in line1_floats: - line1=line1.replace(f,"",1) + line1=line1.replace(f, "", 1) for f in line2_floats: - line2=line2.replace(f,"",1) - #print("line1:",line1) - #print("line2:",line2) + line2=line2.replace(f, "", 1) + # print("line1:", line1) + # print("line2:", line2) # 3. Convert to floats rather than strings line1_floats = [float(x) for x in line1_floats] @@ -242,29 +235,29 @@ class openram_test(unittest.TestCase): # 4. Check if remaining string matches if line1 != line2: - #Uncomment if you want to see all the individual chars of the two lines - #print(str([i for i in line1])) - #print(str([i for i in line2])) + # Uncomment if you want to see all the individual chars of the two lines + # print(str([i for i in line1])) + # print(str([i for i in line2])) if mismatches==0: - debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1, filename2)) mismatches += 1 - debug.error("MISMATCH Line ({0}):\n{1}\n!=\n{2}".format(line_num,line1.rstrip('\n'),line2.rstrip('\n'))) + debug.error("MISMATCH Line ({0}):\n{1}\n!=\n{2}".format(line_num, line1.rstrip('\n'), line2.rstrip('\n'))) # 5. Now compare that the floats match elif len(line1_floats)!=len(line2_floats): if mismatches==0: - debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1, filename2)) mismatches += 1 - debug.error("MISMATCH Line ({0}) Length {1} != {2}".format(line_num,len(line1_floats),len(line2_floats))) + debug.error("MISMATCH Line ({0}) Length {1} != {2}".format(line_num, len(line1_floats), len(line2_floats))) else: - for (float1,float2) in zip(line1_floats,line2_floats): - relative_diff = self.relative_diff(float1,float2) + for (float1, float2) in zip(line1_floats, line2_floats): + relative_diff = self.relative_diff(float1, float2) check = relative_diff <= error_tolerance if not check: if mismatches==0: - debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1, filename2)) mismatches += 1 - debug.error("MISMATCH Line ({0}) Float {1} != {2} diff: {3:.1f}%".format(line_num,float1,float2,relative_diff*100)) + debug.error("MISMATCH Line ({0}) Float {1} != {2} diff: {3:.1f}%".format(line_num, float1, float2, relative_diff * 100)) # Only show the first 10 mismatch lines if not line1 and not line2 or mismatches>10: @@ -275,19 +268,18 @@ class openram_test(unittest.TestCase): # Never reached return False - - def isdiff(self,filename1,filename2): + def isdiff(self, filename1, filename2): """ This is used to compare two files and display the diff if they are different.. """ import debug import filecmp import difflib - check = filecmp.cmp(filename1,filename2) + check = filecmp.cmp(filename1, filename2) if not check: - debug.error("MISMATCH file1={0} file2={1}".format(filename1,filename2)) - f1 = open(filename1,mode="r",encoding='utf-8') + debug.error("MISMATCH file1={0} file2={1}".format(filename1, filename2)) + f1 = open(filename1, mode="r", encoding='utf-8') s1 = f1.readlines() f1.close() - f2 = open(filename2,mode="r",encoding='utf-8') + f2 = open(filename2, mode="r", encoding='utf-8') s2 = f2.readlines() f2.close() mismatches=0 @@ -302,10 +294,13 @@ class openram_test(unittest.TestCase): return False return False else: - debug.info(2,"MATCH {0} {1}".format(filename1,filename2)) + debug.info(2, "MATCH {0} {1}".format(filename1, filename2)) return True + def dbg(): + import pdb; pdb.set_trace() + def header(filename, technology): # Skip the header for gitlab regression import getpass @@ -319,14 +314,18 @@ def header(filename, technology): print("|=========" + tst.center(60) + "=========|") print("|=========" + technology.center(60) + "=========|") print("|=========" + filename.center(60) + "=========|") - from globals import OPTS + from globals import OPTS print("|=========" + OPTS.openram_temp.center(60) + "=========|") print("|==============================================================================|") + def debugTestRunner(post_mortem=None): """unittest runner doing post mortem debugging on failing tests""" + import pdb + import traceback if post_mortem is None and not OPTS.purge_temp: post_mortem = pdb.post_mortem + class DebugTestResult(unittest.TextTestResult): def addError(self, test, err): # called before tearDown() @@ -334,9 +333,10 @@ def debugTestRunner(post_mortem=None): if post_mortem: post_mortem(err[2]) super(DebugTestResult, self).addError(test, err) + def addFailure(self, test, err): traceback.print_exception(*err) - if post_mortem: + if post_mortem: post_mortem(err[2]) super(DebugTestResult, self).addFailure(test, err) return unittest.TextTestRunner(resultclass=DebugTestResult) From 68ffb94d2e36ae22f5ff3198577d0489798b6bdd Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 09:55:57 -0700 Subject: [PATCH 057/206] Rename 05 test to 14 --- ...a_pbitcell_array_test.py => 14_replica_pbitcell_array_test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler/tests/{05_replica_pbitcell_array_test.py => 14_replica_pbitcell_array_test.py} (100%) diff --git a/compiler/tests/05_replica_pbitcell_array_test.py b/compiler/tests/14_replica_pbitcell_array_test.py similarity index 100% rename from compiler/tests/05_replica_pbitcell_array_test.py rename to compiler/tests/14_replica_pbitcell_array_test.py From 2e7f9395f2e3022b5cd086ddf461888b9f8dbf04 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 09:57:16 -0700 Subject: [PATCH 058/206] Rename 05 test to 14 --- .../tests/05_replica_bitcell_array_test.py | 38 ------------------- .../tests/14_replica_bitcell_array_test.py | 9 ++++- 2 files changed, 7 insertions(+), 40 deletions(-) delete mode 100755 compiler/tests/05_replica_bitcell_array_test.py diff --git a/compiler/tests/05_replica_bitcell_array_test.py b/compiler/tests/05_replica_bitcell_array_test.py deleted file mode 100755 index d229b99c..00000000 --- a/compiler/tests/05_replica_bitcell_array_test.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2019 Regents of the University of California -# All rights reserved. -# -import unittest -from testutils import * -import sys,os -sys.path.append(os.getenv("OPENRAM_HOME")) -import globals -from globals import OPTS -from sram_factory import factory -import debug - -class replica_bitcell_array_test(openram_test): - - def runTest(self): - config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) - globals.init_openram(config_file) - - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 0 - OPTS.num_w_ports = 0 - - factory.reset() - debug.info(2, "Testing 4x4 array for bitcell") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0]) - self.local_check(a) - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/14_replica_bitcell_array_test.py b/compiler/tests/14_replica_bitcell_array_test.py index 19413cd5..d229b99c 100755 --- a/compiler/tests/14_replica_bitcell_array_test.py +++ b/compiler/tests/14_replica_bitcell_array_test.py @@ -19,10 +19,15 @@ class replica_bitcell_array_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(2, "Testing 4x4 array for 6t_cell") + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + + factory.reset() + debug.info(2, "Testing 4x4 array for bitcell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0]) self.local_check(a) - + globals.end_openram() # run the test from the command line From a62b85a6b154941158552b430166e4e9462299f5 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 11:29:31 -0700 Subject: [PATCH 059/206] Update mirroring in port_data for bitcell mirrored arrays --- compiler/modules/port_data.py | 21 ++++++++----------- compiler/modules/precharge_array.py | 6 +++--- compiler/modules/sense_amp_array.py | 20 ++++++++---------- .../modules/single_level_column_mux_array.py | 6 +++--- compiler/modules/write_driver_array.py | 5 +++-- compiler/modules/write_mask_and_array.py | 4 ++-- ...ngle_level_column_mux_array_1rw_1r_test.py | 4 ++-- ...le_level_column_mux_array_pbitcell_test.py | 8 +++---- .../tests/08_precharge_array_1rw_1r_test.py | 16 ++++++-------- compiler/tests/08_precharge_array_test.py | 3 +-- compiler/tests/09_sense_amp_array_test.py | 1 - compiler/tests/18_port_data_1rw_1r_test.py | 1 + 12 files changed, 43 insertions(+), 52 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 5e7ed8e5..106f2e85 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -178,14 +178,17 @@ class port_data(design.design): # Extra column +1 is for RBL # Precharge will be shifted left if needed + # Column offset is set to port so extra column can be on left or right + # and mirroring happens correctly self.precharge_array = factory.create(module_type="precharge_array", columns=self.num_cols + 1, - port=self.port, bitcell_bl=self.bl_names[self.port], - bitcell_br=self.br_names[self.port]) + bitcell_br=self.br_names[self.port], + column_offset=self.port - 1) self.add_mod(self.precharge_array) if self.port in self.read_ports: + # RBLs don't get a sense amp self.sense_amp_array = factory.create(module_type="sense_amp_array", word_size=self.word_size, words_per_row=self.words_per_row) @@ -194,9 +197,9 @@ class port_data(design.design): self.sense_amp_array = None if self.col_addr_size > 0: + # RBLs dont get a col mux self.column_mux_array = factory.create(module_type="column_mux_array", columns=self.num_cols, - port=self.port, word_size=self.word_size, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) @@ -205,17 +208,18 @@ class port_data(design.design): self.column_mux_array = None if self.port in self.write_ports: + # RBLs dont get a write driver self.write_driver_array = factory.create(module_type="write_driver_array", columns=self.num_cols, word_size=self.word_size, write_size=self.write_size) self.add_mod(self.write_driver_array) if self.write_size is not None: + # RBLs don't get a write mask self.write_mask_and_array = factory.create(module_type="write_mask_and_array", columns=self.num_cols, word_size=self.word_size, - write_size=self.write_size, - port = self.port) + write_size=self.write_size) self.add_mod(self.write_mask_and_array) else: self.write_mask_and_array = None @@ -248,13 +252,6 @@ class port_data(design.design): self.precharge = factory.create(module_type="precharge", bitcell_bl=self.bl_names[0], bitcell_br=self.br_names[0]) - # We create a dummy here to get bl/br names to add those pins to this - # module, which happens before we create the real precharge_array - self.precharge_array = factory.create(module_type="precharge_array", - columns=self.num_cols + 1, - port=self.port, - bitcell_bl=self.bl_names[self.port], - bitcell_br=self.br_names[self.port]) def create_precharge_array(self): """ Creating Precharge """ diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 2acb1063..84f27fba 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -18,16 +18,16 @@ class precharge_array(design.design): of bit line columns, height is the height of the bit-cell array. """ - def __init__(self, name, columns, port, size=1, bitcell_bl="bl", bitcell_br="br"): + def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br", column_offset=0): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br)) self.columns = columns self.size = size - self.port = port self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br + self.column_offset = column_offset self.create_netlist() if not OPTS.netlist_only: @@ -106,7 +106,7 @@ class precharge_array(design.design): xoffset = 0 for i in range(self.columns): tempx = xoffset - if cell_properties.bitcell.mirror.y and (i + 1 + self.port) % 2: + if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: mirror = "MY" tempx = tempx + self.pc_cell.width else: diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index cb413c45..c532c54a 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -20,7 +20,7 @@ class sense_amp_array(design.design): Dynamically generated sense amp array for all bitlines. """ - def __init__(self, name, word_size, words_per_row): + def __init__(self, name, word_size, words_per_row, column_offset=0): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("word_size {0}".format(word_size)) @@ -28,6 +28,7 @@ class sense_amp_array(design.design): self.word_size = word_size self.words_per_row = words_per_row + self.column_offset = column_offset self.row_size = self.word_size * self.words_per_row self.create_netlist() @@ -102,25 +103,22 @@ class sense_amp_array(design.design): def place_sense_amp_array(self): from tech import cell_properties if self.bitcell.width > self.amp.width: - amp_spacing = self.bitcell.width * self.words_per_row + amp_spacing = self.bitcell.width else: - amp_spacing = self.amp.width * self.words_per_row + amp_spacing = self.amp.width - for i in range(0, self.word_size): - xoffset = amp_spacing * i + for i in range(0, self.row_size, self.words_per_row): + index = int(i / self.words_per_row) + xoffset = i * amp_spacing - # align the xoffset to the grid of bitcells. This way we - # know when to do the mirroring. - grid_x = int(xoffset / self.amp.width) - - if cell_properties.bitcell.mirror.y and grid_x % 2: + if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: mirror = "MY" xoffset = xoffset + self.amp.width else: mirror = "" amp_position = vector(xoffset, 0) - self.local_insts[i].place(offset=amp_position, mirror=mirror) + self.local_insts[index].place(offset=amp_position, mirror=mirror) def add_layout_pins(self): for i in range(len(self.local_insts)): diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 3d80525a..cc38cb45 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -20,17 +20,17 @@ class single_level_column_mux_array(design.design): Array of column mux to read the bitlines through the 6T. """ - def __init__(self, name, columns, port, word_size, bitcell_bl="bl", bitcell_br="br"): + def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br", column_offset=0): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br)) self.columns = columns - self.port = port self.word_size = word_size self.words_per_row = int(self.columns / self.word_size) self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br + self.column_offset = column_offset if "li" in layer: self.col_mux_stack = self.li_stack @@ -118,7 +118,7 @@ class single_level_column_mux_array(design.design): # For every column, add a pass gate for col_num in range(self.columns): xoffset = col_num * self.mux.width - if cell_properties.bitcell.mirror.y and (col_num + self.port) % 2: + if cell_properties.bitcell.mirror.y and (col_num + self.column_offset) % 2: mirror = "MY" xoffset = xoffset + self.mux.width else: diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 49e830e2..105cacc0 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -18,7 +18,7 @@ class write_driver_array(design.design): Dynamically generated write driver array of all bitlines. """ - def __init__(self, name, columns, word_size, write_size=None): + def __init__(self, name, columns, word_size, write_size=None, column_offset=0): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) @@ -27,6 +27,7 @@ class write_driver_array(design.design): self.columns = columns self.word_size = word_size self.write_size = write_size + self.column_offset = column_offset self.words_per_row = int(columns / word_size) if self.write_size: @@ -128,7 +129,7 @@ class write_driver_array(design.design): index = int(i / self.words_per_row) xoffset = i * self.driver_spacing - if cell_properties.bitcell.mirror.y and i % 2: + if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: mirror = "MY" xoffset = xoffset + self.driver.width else: diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index e660d519..c949fe58 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -18,7 +18,7 @@ class write_mask_and_array(design.design): The write mask AND array goes between the write driver array and the sense amp array. """ - def __init__(self, name, columns, word_size, write_size, port=0): + def __init__(self, name, columns, word_size, write_size, column_offset=0): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) @@ -28,7 +28,7 @@ class write_mask_and_array(design.design): self.columns = columns self.word_size = word_size self.write_size = write_size - self.port = port + self.column_offset self.words_per_row = int(columns / word_size) self.num_wmasks = int(word_size / write_size) diff --git a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py index a8d804c9..2a3a8f87 100755 --- a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py +++ b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py @@ -26,11 +26,11 @@ class single_level_column_mux_test(openram_test): OPTS.num_w_ports = 0 debug.info(1, "Testing sample for 4-way column_mux_array port 0") - a = factory.create(module_type="single_level_column_mux_array", columns=8, port=0, word_size=2, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array port 1") - a = factory.create(module_type="single_level_column_mux_array", columns=8, port=0, word_size=2, bitcell_bl="bl1", bitcell_br="br1") + a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl1", bitcell_br="br1") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py index 7feb067e..663ff075 100755 --- a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py +++ b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py @@ -29,19 +29,19 @@ class single_level_column_mux_pbitcell_test(openram_test): factory.reset() debug.info(1, "Testing sample for 2-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=8, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, port=0, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, port=3, word_size=4, bitcell_bl="bl2", bitcell_br="br2") + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2", column_offset=3) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/08_precharge_array_1rw_1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py index dde36b23..7d54e02b 100755 --- a/compiler/tests/08_precharge_array_1rw_1r_test.py +++ b/compiler/tests/08_precharge_array_1rw_1r_test.py @@ -28,18 +28,14 @@ class precharge_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 factory.reset() - debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") - pc = factory.create(module_type="precharge_array", columns=3, port=0, bitcell_bl="bl0", bitcell_br="br0") + debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 0)") + pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(pc) + + debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 1)") + pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0", column_offset=1) self.local_check(pc) - # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") - # pc = precharge_array.precharge_array(name="pre3", 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(name="pre4", columns=3, bitcell_bl="bl2", bitcell_br="br2") - # self.local_check(pc) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index c3b823b3..47843ca3 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -21,9 +21,8 @@ class precharge_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # check precharge array in single port debug.info(2, "Checking 3 column precharge") - pc = factory.create(module_type="precharge_array", columns=3, port=0) + pc = factory.create(module_type="precharge_array", columns=3) self.local_check(pc) globals.end_openram() diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index 459113f2..c71a75e8 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -21,7 +21,6 @@ class sense_amp_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # check sense amp array for single port debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=1") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=1) self.local_check(a) diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index 8081416e..3d415ec0 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -13,6 +13,7 @@ from globals import OPTS from sram_factory import factory import debug + class port_data_1rw_1r_test(openram_test): def runTest(self): From 4fef632dceb4ad9cb5d6f4ab29bbdaacf7f837af Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 12:13:41 -0700 Subject: [PATCH 060/206] Fix syntax error --- compiler/modules/write_mask_and_array.py | 2 +- compiler/tests/07_single_level_column_mux_array_test.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index c949fe58..9c077ed7 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -28,7 +28,7 @@ class write_mask_and_array(design.design): self.columns = columns self.word_size = word_size self.write_size = write_size - self.column_offset + self.column_offset = column_offset self.words_per_row = int(columns / word_size) self.num_wmasks = int(word_size / write_size) diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index ff7ec1c9..b1f74eba 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -21,15 +21,15 @@ class single_level_column_mux_test(openram_test): globals.init_openram(config_file) debug.info(1, "Testing sample for 2-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=8) + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8) self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=4) + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4) self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=32, port=0, word_size=4) + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4) self.local_check(a) globals.end_openram() From 00b51f546478b6a509541b4b5713a460d1d922bd Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 13:49:32 -0700 Subject: [PATCH 061/206] PEP8 format replica_bitcell_array --- compiler/modules/replica_bitcell_array.py | 148 ++++++++++------------ 1 file changed, 69 insertions(+), 79 deletions(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index ff2260d3..05cb1ac6 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -32,8 +32,10 @@ class replica_bitcell_array(design.design): self.right_rbl = right_rbl self.bitcell_ports = bitcell_ports - debug.check(left_rbl+right_rbl==len(self.all_ports),"Invalid number of RBLs for port configuration.") - debug.check(left_rbl+right_rbl==len(self.bitcell_ports),"Bitcell ports must match total RBLs.") + debug.check(left_rbl + right_rbl == len(self.all_ports), + "Invalid number of RBLs for port configuration.") + debug.check(left_rbl + right_rbl == len(self.bitcell_ports), + "Bitcell ports must match total RBLs.") # Two dummy rows/cols plus replica for each port self.extra_rows = 2 + left_rbl + right_rbl @@ -45,8 +47,7 @@ class replica_bitcell_array(design.design): # We don't offset this because we need to align # the replica bitcell in the control logic - #self.offset_all_coordinates() - + # self.offset_all_coordinates() def create_netlist(self): """ Create and connect the netlist """ @@ -90,15 +91,15 @@ class replica_bitcell_array(design.design): # Replica bitlines self.replica_columns = {} - for bit in range(self.left_rbl+self.right_rbl): + for bit in range(self.left_rbl + self.right_rbl): # Creating left_rbl if bit1 port) for port in range(self.left_rbl): # Make names for all RBLs - wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.cell.get_all_wl_names()))] + wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))] # Keep track of the pin that is the RBL self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] self.replica_col_wl_names.extend(wl_names) # Regular WLs self.replica_col_wl_names.extend(self.bitcell_array_wl_names) # Right port WLs (one dummy for each port when we allow >1 port) - for port in range(self.left_rbl,self.left_rbl+self.right_rbl): + for port in range(self.left_rbl, self.left_rbl + self.right_rbl): # Make names for all RBLs - wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.cell.get_all_wl_names()))] + wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))] # Keep track of the pin that is the RBL self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] self.replica_col_wl_names.extend(wl_names) @@ -195,14 +195,13 @@ class replica_bitcell_array(design.design): # Left/right dummy columns are connected identically to the replica column self.dummy_col_wl_names = self.replica_col_wl_names - # Per port bitline names self.replica_bl_names = {} self.replica_wl_names = {} # Array of all port bitline names - for port in range(self.left_rbl+self.right_rbl): - left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x),port) for x in range(len(self.all_ports))] - right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x),port) for x in range(len(self.all_ports))] + for port in range(self.left_rbl + self.right_rbl): + left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x), port) for x in range(len(self.all_ports))] + right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x), port) for x in range(len(self.all_ports))] # Keep track of the left pins that are the RBL self.rbl_bl_names[port]=left_names[self.bitcell_ports[port]] self.rbl_br_names[port]=right_names[self.bitcell_ports[port]] @@ -210,28 +209,25 @@ class replica_bitcell_array(design.design): bl_names = [x for t in zip(left_names, right_names) for x in t] self.replica_bl_names[port] = bl_names - wl_names = ["rbl_{0}_{1}".format(x,port) for x in self.cell.get_all_wl_names()] - #wl_names[port] = "rbl_wl{}".format(port) + wl_names = ["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()] self.replica_wl_names[port] = wl_names - # External pins self.add_pin_list(self.bitcell_array_bl_names, "INOUT") # Need to sort by port order since dictionary values may not be in order bl_names = [self.rbl_bl_names[x] for x in sorted(self.rbl_bl_names.keys())] br_names = [self.rbl_br_names[x] for x in sorted(self.rbl_br_names.keys())] - for (bl_name,br_name) in zip(bl_names,br_names): - self.add_pin(bl_name,"OUTPUT") - self.add_pin(br_name,"OUTPUT") + for (bl_name, br_name) in zip(bl_names, br_names): + self.add_pin(bl_name, "OUTPUT") + self.add_pin(br_name, "OUTPUT") self.add_pin_list(self.bitcell_array_wl_names, "INPUT") # Need to sort by port order since dictionary values may not be in order wl_names = [self.rbl_wl_names[x] for x in sorted(self.rbl_wl_names.keys())] for pin_name in wl_names: - self.add_pin(pin_name,"INPUT") + self.add_pin(pin_name, "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - def create_instances(self): """ Create the module instances used in this design """ @@ -247,77 +243,75 @@ class replica_bitcell_array(design.design): # Replica columns self.replica_col_inst = {} - for port in range(self.left_rbl+self.right_rbl): + for port in range(self.left_rbl + self.right_rbl): self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port), - mod=self.replica_columns[port]) + mod=self.replica_columns[port]) self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies) - # Dummy rows under the bitcell array (connected with with the replica cell wl) self.dummy_row_replica_inst = {} - for port in range(self.left_rbl+self.right_rbl): + for port in range(self.left_rbl + self.right_rbl): self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port), mod=self.dummy_row) self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names[port] + supplies) - # Top/bottom dummy rows or col caps self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot", mod=self.edge_row) - self.connect_inst(self.dummy_row_bl_names + [x+"_bot" for x in self.dummy_cell_wl_names] + supplies) + self.connect_inst(self.dummy_row_bl_names + [x + "_bot" for x in self.dummy_cell_wl_names] + supplies) self.dummy_row_top_inst=self.add_inst(name="dummy_row_top", mod=self.edge_row) - self.connect_inst(self.dummy_row_bl_names + [x+"_top" for x in self.dummy_cell_wl_names] + supplies) - + self.connect_inst(self.dummy_row_bl_names + [x + "_top" for x in self.dummy_cell_wl_names] + supplies) # Left/right Dummy columns self.dummy_col_left_inst=self.add_inst(name="dummy_col_left", mod=self.edge_col_left) - self.connect_inst([x+"_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) + self.connect_inst([x + "_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) self.dummy_col_right_inst=self.add_inst(name="dummy_col_right", - mod=self.edge_col_right) - self.connect_inst([x+"_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) + mod=self.edge_col_right) + self.connect_inst([x + "_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) def create_layout(self): - self.height = (self.row_size+self.extra_rows)*self.dummy_row.height - self.width = (self.column_size+self.extra_cols)*self.cell.width + self.height = (self.row_size + self.extra_rows) * self.dummy_row.height + self.width = (self.column_size + self.extra_cols) * self.cell.width # This is a bitcell x bitcell offset to scale offset = vector(self.cell.width, self.cell.height) - self.bitcell_array_inst.place(offset=[0,0]) + self.bitcell_array_inst.place(offset=[0, 0]) # To the left of the bitcell array for bit in range(self.left_rbl): - self.replica_col_inst[bit].place(offset=offset.scale(-bit-1,-self.left_rbl-1)) + self.replica_col_inst[bit].place(offset=offset.scale(-bit - 1, -self.left_rbl - 1)) # To the right of the bitcell array for bit in range(self.right_rbl): - self.replica_col_inst[self.left_rbl+bit].place(offset=offset.scale(bit,-self.left_rbl-1)+self.bitcell_array_inst.lr()) + self.replica_col_inst[self.left_rbl + bit].place(offset=offset.scale(bit, -self.left_rbl - 1) + self.bitcell_array_inst.lr()) + # FIXME: These depend on the array size itself # Far top dummy row (first row above array is NOT flipped) - flip_dummy = self.right_rbl%2 - self.dummy_row_top_inst.place(offset=offset.scale(0,self.right_rbl+flip_dummy)+self.bitcell_array_inst.ul(), + flip_dummy = self.right_rbl % 2 + self.dummy_row_top_inst.place(offset=offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul(), mirror="MX" if flip_dummy else "R0") + # FIXME: These depend on the array size itself # Far bottom dummy row (first row below array IS flipped) - flip_dummy = (self.left_rbl+1)%2 - self.dummy_row_bot_inst.place(offset=offset.scale(0,-self.left_rbl-1+flip_dummy), + flip_dummy = (self.left_rbl + 1) % 2 + self.dummy_row_bot_inst.place(offset=offset.scale(0, -self.left_rbl - 1 + flip_dummy), mirror="MX" if flip_dummy else "R0") # Far left dummy col - self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl-1,-self.left_rbl-1)) + self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl - 1, -self.left_rbl - 1)) # Far right dummy col - self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl,-self.left_rbl-1)+self.bitcell_array_inst.lr()) + self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr()) # Replica dummy rows for bit in range(self.left_rbl): - self.dummy_row_replica_inst[bit].place(offset=offset.scale(0,-bit-bit%2), - mirror="R0" if bit%2 else "MX") + self.dummy_row_replica_inst[bit].place(offset=offset.scale(0, -bit - bit % 2), + mirror="R0" if bit % 2 else "MX") for bit in range(self.right_rbl): - self.dummy_row_replica_inst[self.left_rbl+bit].place(offset=offset.scale(0,bit+bit%2)+self.bitcell_array_inst.ul(), - mirror="MX" if bit%2 else "R0") + self.dummy_row_replica_inst[self.left_rbl + bit].place(offset=offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(), + mirror="MX" if bit % 2 else "R0") - - self.translate_all(offset.scale(-1-self.left_rbl,-1-self.left_rbl)) + self.translate_all(offset.scale(-1 - self.left_rbl, -1 - self.left_rbl)) self.add_layout_pins() @@ -325,7 +319,6 @@ class replica_bitcell_array(design.design): self.DRC_LVS() - def add_layout_pins(self): """ Add the layout pins """ @@ -338,7 +331,7 @@ class replica_bitcell_array(design.design): for pin in pin_list: self.add_layout_pin(text=pin_name, layer=pin.layer, - offset=pin.ll().scale(0,1), + offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) for bitline in self.bitcell_array_bl_names: @@ -347,17 +340,16 @@ class replica_bitcell_array(design.design): for pin in pin_list: self.add_layout_pin(text=pin_name, layer=pin.layer, - offset=pin.ll().scale(1,0), + offset=pin.ll().scale(1, 0), width=pin.width(), height=self.height) - # Replica wordlines - for port in range(self.left_rbl+self.right_rbl): + for port in range(self.left_rbl + self.right_rbl): inst = self.replica_col_inst[port] - for (pin_name,wl_name) in zip(self.cell.get_all_wl_names(),self.replica_wl_names[port]): + for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.replica_wl_names[port]): # +1 for dummy row - pin_bit = port+1 + pin_bit = port + 1 # +row_size if above the array if port>=self.left_rbl: pin_bit += self.row_size @@ -367,7 +359,7 @@ class replica_bitcell_array(design.design): if wl_name in self.rbl_wl_names.values(): self.add_layout_pin(text=wl_name, layer=pin.layer, - offset=pin.ll().scale(0,1), + offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) @@ -416,8 +408,6 @@ class replica_bitcell_array(design.design): def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" - from tech import drc, parameter - # Dynamic Power from Bitline bl_wire = self.gen_bl_wire() cell_load = 2 * bl_wire.return_input_cap() @@ -425,10 +415,10 @@ class replica_bitcell_array(design.design): freq = spice["default_event_frequency"] bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) - #Calculate the bitcell power which currently only includes leakage + # Calculate the bitcell power which currently only includes leakage cell_power = self.cell.analytical_power(corner, load) - #Leakage power grows with entire array and bitlines. + # Leakage power grows with entire array and bitlines. total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, cell_power.leakage * self.column_size * self.row_size) return total_power @@ -439,13 +429,13 @@ class replica_bitcell_array(design.design): else: height = self.height bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_m1")) + bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1")) bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire def get_wordline_cin(self): """Get the relative input capacitance from the wordline connections in all the bitcell""" - #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns + # A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns bitcell_wl_cin = self.cell.get_wl_cin() total_cin = bitcell_wl_cin * self.column_size return total_cin @@ -457,9 +447,9 @@ class replica_bitcell_array(design.design): def graph_exclude_replica_col_bits(self): """Exclude all replica/dummy cells in the replica columns except the replica bit.""" - for port in range(self.left_rbl+self.right_rbl): + for port in range(self.left_rbl + self.right_rbl): self.replica_columns[port].exclude_all_but_replica() def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" - return self.bitcell_array.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col) + return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col) From 551499670809b0293def390d77e8c28e3a30cfed Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 15:09:22 -0700 Subject: [PATCH 062/206] Auto-generate port dependent cell names. --- compiler/globals.py | 13 ++--- compiler/modules/col_cap_array.py | 4 +- compiler/modules/dummy_array.py | 9 ++-- compiler/modules/replica_column.py | 50 +++++++++---------- compiler/modules/row_cap_array.py | 2 +- compiler/options.py | 5 +- compiler/sram_factory.py | 4 +- .../04_single_level_column_mux_1rw_1r_test.py | 4 +- .../tests/05_bitcell_1rw_1r_array_test.py | 4 +- .../06_hierarchical_decoder_1rw_1r_test.py | 4 +- .../06_hierarchical_decoder_pbitcell_test.py | 4 +- ...6_hierarchical_predecode2x4_1rw_1r_test.py | 3 +- ...hierarchical_predecode2x4_pbitcell_test.py | 4 +- ...6_hierarchical_predecode3x8_1rw_1r_test.py | 4 +- ...hierarchical_predecode3x8_pbitcell_test.py | 4 +- ...ngle_level_column_mux_array_1rw_1r_test.py | 4 +- .../tests/08_precharge_array_1rw_1r_test.py | 2 +- .../08_wordline_driver_array_1rw_1r_test.py | 2 +- .../14_replica_bitcell_1rw_1r_array_test.py | 12 ++--- compiler/tests/18_port_address_1rw_1r_test.py | 4 +- compiler/tests/18_port_data_1rw_1r_test.py | 4 +- compiler/tests/19_single_bank_1rw_1r_test.py | 4 +- compiler/tests/19_single_bank_1w_1r_test.py | 5 +- .../tests/19_single_bank_wmask_1rw_1r_test.py | 4 ++ .../tests/20_psram_1bank_2mux_1rw_1w_test.py | 3 +- .../tests/20_psram_1bank_2mux_1w_1r_test.py | 3 +- compiler/tests/20_psram_1bank_2mux_test.py | 6 +-- .../tests/20_psram_1bank_4mux_1rw_1r_test.py | 3 +- .../tests/20_sram_1bank_2mux_1rw_1r_test.py | 4 +- .../tests/20_sram_1bank_2mux_1w_1r_test.py | 4 +- .../tests/20_sram_1bank_8mux_1rw_1r_test.py | 6 +-- .../tests/20_sram_1bank_nomux_1rw_1r_test.py | 4 +- .../22_sram_1rw_1r_1bank_nomux_func_test.py | 4 +- .../tests/22_sram_wmask_1w_1r_func_test.py | 7 +-- 34 files changed, 82 insertions(+), 121 deletions(-) diff --git a/compiler/globals.py b/compiler/globals.py index 92780c1a..c9469330 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -214,25 +214,18 @@ def setup_bitcell(): if OPTS.num_r_ports > 0: ports += "{}r".format(OPTS.num_r_ports) - OPTS.bitcell = "bitcell_"+ports - OPTS.replica_bitcell = "replica_bitcell_"+ports - OPTS.dummy_bitcell = "dummy_bitcell_"+ports - else: - OPTS.replica_bitcell = "replica_" + OPTS.bitcell - OPTS.replica_bitcell = "dummy_" + OPTS.bitcell + if ports != "": + OPTS.bitcell_suffix = "_" + ports + OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix # See if bitcell exists try: __import__(OPTS.bitcell) - __import__(OPTS.replica_bitcell) - __import__(OPTS.dummy_bitcell) except ImportError: # Use the pbitcell if we couldn't find a custom bitcell # or its custom replica bitcell # Use the pbitcell (and give a warning if not in unit test mode) OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell = "replica_pbitcell" - OPTS.replica_bitcell = "dummy_pbitcell" if not OPTS.is_unit_test: debug.warning("Using the parameterized bitcell which may have suboptimal density.") debug.info(1, "Using bitcell: {}".format(OPTS.bitcell)) diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 3119f4e5..d74ab80a 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -8,6 +8,7 @@ from sram_factory import factory from globals import OPTS from tech import cell_properties + class col_cap_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. @@ -35,8 +36,7 @@ class col_cap_array(bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ - # self.dummy_cell = factory.create(module_type="col_cap_bitcell_1rw_1r") # TODO: make module_type generic - self.dummy_cell = factory.create(module_type="col_cap_bitcell") + self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell)) self.add_mod(self.dummy_cell) self.cell = factory.create(module_type="bitcell") diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index de15d0ce..f4b240da 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -38,20 +38,19 @@ class dummy_array(bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ - self.dummy_cell = factory.create(module_type="dummy_bitcell") + self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell)) self.add_mod(self.dummy_cell) self.cell = factory.create(module_type="bitcell") - def create_instances(self): """ Create the module instances used in this design """ self.cell_inst = {} for col in range(self.column_size): for row in range(self.row_size): name = "bit_r{0}_c{1}".format(row, col) - self.cell_inst[row,col]=self.add_inst(name=name, - mod=self.dummy_cell) + self.cell_inst[row, col]=self.add_inst(name=name, + mod=self.dummy_cell) self.connect_inst(self.get_bitcell_pins(col, row)) def input_load(self): @@ -60,7 +59,7 @@ class dummy_array(bitcell_base_array): def get_wordline_cin(self): """Get the relative input capacitance from the wordline connections in all the bitcell""" - #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns + # A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns bitcell_wl_cin = self.cell.get_wl_cin() total_cin = bitcell_wl_cin * self.column_size return total_cin diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 4ea1b7df..afe87497 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -5,12 +5,12 @@ # import debug import design -from tech import drc, cell_properties -import contact +from tech import cell_properties from sram_factory import factory from vector import vector from globals import OPTS + class replica_column(design.design): """ Generate a replica bitline column for the replica array. @@ -29,11 +29,12 @@ class replica_column(design.design): self.right_rbl = right_rbl self.replica_bit = replica_bit # left, right, regular rows plus top/bottom dummy cells - self.total_size = self.left_rbl+rows+self.right_rbl+2 + self.total_size = self.left_rbl + rows + self.right_rbl + 2 self.column_offset = column_offset - debug.check(replica_bit!=0 and replica_bit!=rows,"Replica bit cannot be the dummy row.") - debug.check(replica_bit<=left_rbl or replica_bit>=self.total_size-right_rbl-1, + debug.check(replica_bit != 0 and replica_bit != rows, + "Replica bit cannot be the dummy row.") + debug.check(replica_bit <= left_rbl or replica_bit >= self.total_size - right_rbl - 1, "Replica bit cannot be in the regular array.") self.create_netlist() @@ -46,7 +47,7 @@ class replica_column(design.design): self.create_instances() def create_layout(self): - self.height = self.total_size*self.cell.height + self.height = self.total_size * self.cell.height self.width = self.cell.width self.place_instances() @@ -58,25 +59,25 @@ class replica_column(design.design): for bl_name in self.cell.get_all_bitline_names(): # In the replica column, these are only outputs! - self.add_pin("{0}_{1}".format(bl_name,0), "OUTPUT") + self.add_pin("{0}_{1}".format(bl_name, 0), "OUTPUT") for row in range(self.total_size): for wl_name in self.cell.get_all_wl_names(): - self.add_pin("{0}_{1}".format(wl_name,row), "INPUT") + self.add_pin("{0}_{1}".format(wl_name, row), "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") def add_modules(self): - self.replica_cell = factory.create(module_type="replica_bitcell") + self.replica_cell = factory.create(module_type="replica_{}".format(OPTS.bitcell)) self.add_mod(self.replica_cell) - self.dummy_cell = factory.create(module_type="dummy_bitcell") + self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell)) self.add_mod(self.dummy_cell) try: - edge_module_type = ("col_cap_bitcell" if cell_properties.bitcell.end_caps else "dummy_bitcell") + edge_module_type = ("col_cap" if cell_properties.bitcell.end_caps else "dummy") except AttributeError: - edge_module_type = "dummy_bitcell" - self.edge_cell = factory.create(module_type=edge_module_type) + edge_module_type = "dummy" + self.edge_cell = factory.create(module_type=edge_module_type + "_" + OPTS.bitcell) self.add_mod(self.edge_cell) # Used for pin names only self.cell = factory.create(module_type="bitcell") @@ -94,7 +95,7 @@ class replica_column(design.design): # Top/bottom cell are always dummy cells. # Regular array cells are replica cells (>left_rbl and self.left_rbl and row self.left_rbl and row < self.total_size - self.right_rbl - 1): self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell) self.connect_inst(self.get_bitcell_pins(0, row)) @@ -118,7 +119,7 @@ class replica_column(design.design): from tech import cell_properties # Flip the mirrors if we have an odd number of replica+dummy rows at the bottom # so that we will start with mirroring rather than not mirroring - rbl_offset = (self.left_rbl+1)%2 + rbl_offset = (self.left_rbl + 1) %2 # if our bitcells are mirrored on the y axis, check if we are in global # column that needs to be flipped. @@ -129,12 +130,10 @@ class replica_column(design.design): xoffset = self.replica_cell.width for row in range(self.total_size): - dir_x = False - name = "bit_r{0}_{1}".format(row,"rbl") - if cell_properties.bitcell.mirror.x and (row+rbl_offset)%2: - dir_x = True + # name = "bit_r{0}_{1}".format(row, "rbl") + dir_x = cell_properties.bitcell.mirror.x and (row + rbl_offset) % 2 - offset = vector(xoffset,self.cell.height*(row+(row+rbl_offset)%2)) + offset = vector(xoffset, self.cell.height * (row + (row + rbl_offset) % 2)) if dir_x and dir_y: dir_key = "XY" @@ -174,9 +173,9 @@ class replica_column(design.design): for row in range(row_range_min, row_range_max): for wl_name in self.cell.get_all_wl_names(): wl_pin = self.cell_inst[row].get_pin(wl_name) - self.add_layout_pin(text="{0}_{1}".format(wl_name,row), + self.add_layout_pin(text="{0}_{1}".format(wl_name, row), layer=wl_pin.layer, - offset=wl_pin.ll().scale(0,1), + offset=wl_pin.ll().scale(0, 1), width=self.width, height=wl_pin.height()) @@ -194,10 +193,10 @@ class replica_column(design.design): pin_names = self.cell.get_all_bitline_names() for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(col)) + bitcell_pins.append(pin + "_{0}".format(col)) pin_names = self.cell.get_all_wl_names() for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(row)) + bitcell_pins.append(pin + "_{0}".format(row)) bitcell_pins.append("vdd") bitcell_pins.append("gnd") @@ -211,12 +210,11 @@ class replica_column(design.design): pin_names = self.cell.get_all_bitline_names() for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(col)) + bitcell_pins.append(pin + "_{0}".format(col)) bitcell_pins.append("vdd") return bitcell_pins - def exclude_all_but_replica(self): """Excludes all bits except the replica cell (self.replica_bit).""" diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index c9a6b4f7..2c7d4677 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -35,7 +35,7 @@ class row_cap_array(bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ - self.dummy_cell = factory.create(module_type="row_cap_bitcell_1rw_1r") # TODO: make module_type generic + self.dummy_cell = factory.create(module_type="row_cap_{}".format(OPTS.bitcell)) self.add_mod(self.dummy_cell) self.cell = factory.create(module_type="bitcell") diff --git a/compiler/options.py b/compiler/options.py index 646b2a68..fb738169 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -124,26 +124,23 @@ class options(optparse.Values): purge_temp = True # These are the default modules that can be over-riden + bitcell_suffix = "" bank_select = "bank_select" bitcell_array = "bitcell_array" bitcell = "bitcell" - col_cap_bitcell = "col_cap_bitcell" column_mux_array = "single_level_column_mux_array" control_logic = "control_logic" decoder = "hierarchical_decoder" delay_chain = "delay_chain" dff_array = "dff_array" dff = "dff" - dummy_bitcell = "dummy_bitcell" inv_dec = "pinv" nand2_dec = "pnand2" nand3_dec = "pnand3" nand4_dec = "pnand4" # Not available right now precharge_array = "precharge_array" ptx = "ptx" - replica_bitcell = "replica_bitcell" replica_bitline = "replica_bitline" - row_cap_bitcell = "row_cap_bitcell" sense_amp_array = "sense_amp_array" sense_amp = "sense_amp" tri_gate_array = "tri_gate_array" diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 46eed3d4..0e9721c7 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -77,8 +77,8 @@ class sram_factory: """ tech_module_type, tm_overridden = self.get_techmodule_type(module_type) user_module_type, um_overridden = self.get_usermodule_type(module_type) - #print(module_type, tech_module_type, tm_overridden) - #print(module_type, user_module_type, um_overridden) + # print(module_type, tech_module_type, tm_overridden) + # print(module_type, user_module_type, um_overridden) # overridden user modules have priority if um_overridden: diff --git a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py index 69b31cc6..a7e79e9b 100755 --- a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py +++ b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py @@ -22,11 +22,11 @@ class single_level_column_mux_1rw_1r_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + debug.info(2, "Checking column mux port 0") tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) diff --git a/compiler/tests/05_bitcell_1rw_1r_array_test.py b/compiler/tests/05_bitcell_1rw_1r_array_test.py index 3426f0c5..0683d127 100755 --- a/compiler/tests/05_bitcell_1rw_1r_array_test.py +++ b/compiler/tests/05_bitcell_1rw_1r_array_test.py @@ -23,12 +23,10 @@ class bitcell_1rw_1r_array_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() debug.info(2, "Testing 4x4 array for cell_1rw_1r") a = factory.create(module_type="bitcell_array", cols=4, rows=4) diff --git a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py index 5b317e6e..844160a6 100755 --- a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py @@ -23,11 +23,11 @@ class hierarchical_decoder_1rw_1r_test(openram_test): globals.init_openram(config_file) # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + # Checks 2x4 and 2-input NAND decoder debug.info(1, "Testing 16 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py index 094e8b2e..e1605067 100755 --- a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py @@ -21,11 +21,11 @@ class hierarchical_decoder_pbitcell_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) # check hierarchical decoder for multi-port - OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - + globals.setup_bitcell() + factory.reset() debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) diff --git a/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py index 6512238f..a3ae2ed2 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py @@ -22,11 +22,10 @@ class hierarchical_predecode2x4_1rw_1r_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() debug.info(1, "Testing sample for hierarchy_predecode2x4") a = factory.create(module_type="hierarchical_predecode2x4") diff --git a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py index b5532891..0f70ddb1 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py @@ -22,11 +22,11 @@ class hierarchical_predecode2x4_pbitcell_test(openram_test): globals.init_openram(config_file) # checking hierarchical precode 2x4 for multi-port - OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - + globals.setup_bitcell() + debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)") a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py index 466a7e40..c0653243 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py @@ -23,11 +23,11 @@ class hierarchical_predecode3x8_1rw_1r_test(openram_test): globals.init_openram(config_file) # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + debug.info(1, "Testing sample for hierarchy_predecode3x8") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py index 1d5ab41b..9170d5c0 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py @@ -22,11 +22,11 @@ class hierarchical_predecode3x8_pbitcell_test(openram_test): globals.init_openram(config_file) # checking hierarchical precode 3x8 for multi-port - OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - + globals.setup_bitcell() + debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py index 2a3a8f87..c758e788 100755 --- a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py +++ b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py @@ -20,11 +20,11 @@ class single_level_column_mux_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + debug.info(1, "Testing sample for 4-way column_mux_array port 0") a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) diff --git a/compiler/tests/08_precharge_array_1rw_1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py index 7d54e02b..c5efb59b 100755 --- a/compiler/tests/08_precharge_array_1rw_1r_test.py +++ b/compiler/tests/08_precharge_array_1rw_1r_test.py @@ -22,10 +22,10 @@ class precharge_1rw_1r_test(openram_test): globals.init_openram(config_file) # check precharge array in multi-port - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() factory.reset() debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 0)") diff --git a/compiler/tests/08_wordline_driver_array_1rw_1r_test.py b/compiler/tests/08_wordline_driver_array_1rw_1r_test.py index 7c97ff75..cf1810d8 100755 --- a/compiler/tests/08_wordline_driver_array_1rw_1r_test.py +++ b/compiler/tests/08_wordline_driver_array_1rw_1r_test.py @@ -23,10 +23,10 @@ class wordline_driver_array_1rw_1r_test(openram_test): globals.init_openram(config_file) # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() # check wordline driver for single port debug.info(2, "Checking driver") diff --git a/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py b/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py index d746eabc..3f869255 100755 --- a/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py +++ b/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py @@ -19,21 +19,17 @@ class replica_bitcell_array_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" - OPTS.col_cap_bitcell="col_cap_bitcell_1rw_1r" - OPTS.row_cap_bitcell="row_cap_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + debug.info(2, "Testing 4x4 array for cell_1rw_1r") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0,1]) + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0, 1]) self.local_check(a) debug.info(2, "Testing 4x4 array for cell_1rw_1r") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0,1]) + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0, 1]) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_1rw_1r_test.py index 33bff4ed..ff09dec9 100755 --- a/compiler/tests/18_port_address_1rw_1r_test.py +++ b/compiler/tests/18_port_address_1rw_1r_test.py @@ -21,11 +21,11 @@ class port_address_1rw_1r_test(openram_test): globals.init_openram(config_file) # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + debug.info(1, "Port address 16 rows") a = factory.create("port_address", cols=16, rows=16) self.local_check(a) diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index 3d415ec0..3a7687d6 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -21,11 +21,11 @@ class port_data_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + c = sram_config(word_size=4, num_words=16) diff --git a/compiler/tests/19_single_bank_1rw_1r_test.py b/compiler/tests/19_single_bank_1rw_1r_test.py index b3e18407..22f83f29 100755 --- a/compiler/tests/19_single_bank_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_1rw_1r_test.py @@ -22,12 +22,10 @@ class single_bank_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=16) diff --git a/compiler/tests/19_single_bank_1w_1r_test.py b/compiler/tests/19_single_bank_1w_1r_test.py index c1228e5a..e3a2d886 100755 --- a/compiler/tests/19_single_bank_1w_1r_test.py +++ b/compiler/tests/19_single_bank_1w_1r_test.py @@ -22,13 +22,10 @@ class single_bank_1w_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1w_1r" - OPTS.replica_bitcell = "replica_bitcell_1w_1r" - OPTS.dummy_bitcell="dummy_bitcell_1w_1r" - OPTS.num_rw_ports = 0 OPTS.num_r_ports = 1 OPTS.num_w_ports = 1 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=16) diff --git a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py index d08ff8cc..ddb97905 100755 --- a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py @@ -22,6 +22,10 @@ class single_bank_wmask_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=8, write_size=4, diff --git a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py index 599cb2ce..f521851b 100755 --- a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py @@ -24,11 +24,10 @@ class psram_1bank_2mux_1rw_1w_test(openram_test): from sram_config import sram_config OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell="replica_pbitcell" - OPTS.dummy_bitcell="dummy_pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 1 OPTS.num_r_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, diff --git a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py index 30b951fb..35912823 100755 --- a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py @@ -24,11 +24,10 @@ class psram_1bank_2mux_1w_1r_test(openram_test): from sram_config import sram_config OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell="replica_pbitcell" - OPTS.dummy_bitcell="dummy_pbitcell" OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, diff --git a/compiler/tests/20_psram_1bank_2mux_test.py b/compiler/tests/20_psram_1bank_2mux_test.py index 44272b2d..92403cd1 100755 --- a/compiler/tests/20_psram_1bank_2mux_test.py +++ b/compiler/tests/20_psram_1bank_2mux_test.py @@ -22,14 +22,12 @@ class psram_1bank_2mux_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell="replica_pbitcell" - OPTS.dummy_bitcell="dummy_pbitcell" - # testing layout of sram using pbitcell with 1 RW port (a 6T-cell equivalent) + OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, diff --git a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py index 49e1a125..145d1723 100755 --- a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py +++ b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py @@ -24,11 +24,10 @@ class psram_1bank_4mux_1rw_1r_test(openram_test): from sram_config import sram_config OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell="replica_pbitcell" - OPTS.dummy_bitcell="dummy_pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=64, diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py index a8d635ba..0a2b7d32 100755 --- a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py @@ -22,12 +22,10 @@ class sram_1bank_2mux_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, diff --git a/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py index bf572700..2c4e29e6 100755 --- a/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py @@ -23,12 +23,10 @@ class psram_1bank_2mux_1w_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1w_1r" - OPTS.replica_bitcell="replica_bitcell_1w_1r" - OPTS.dummy_bitcell="dummy_bitcell_1w_1r" OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, diff --git a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py index 69a623d2..1e4df34d 100755 --- a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py @@ -22,13 +22,11 @@ class sram_1bank_8mux_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + c = sram_config(word_size=2, num_words=128, num_banks=1) diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py index f9b96b84..a516b4f0 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py @@ -22,12 +22,10 @@ class sram_1bank_nomux_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell = "dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=16, diff --git a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py index d271d1e5..f2958f9f 100755 --- a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py +++ b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py @@ -24,12 +24,10 @@ class psram_1bank_nomux_func_test(openram_test): OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 + globals.setup_bitcell() # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload diff --git a/compiler/tests/22_sram_wmask_1w_1r_func_test.py b/compiler/tests/22_sram_wmask_1w_1r_func_test.py index 50acd5bf..b5d83654 100755 --- a/compiler/tests/22_sram_wmask_1w_1r_func_test.py +++ b/compiler/tests/22_sram_wmask_1w_1r_func_test.py @@ -26,14 +26,11 @@ class sram_wmask_1w_1r_func_test(openram_test): OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False - OPTS.bitcell = "bitcell_1w_1r" - OPTS.replica_bitcell = "replica_bitcell_1w_1r" - OPTS.dummy_bitcell = "dummy_bitcell_1w_1r" - OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 - + globals.setup_bitcell() + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer From 0837432d4538b75e74a522092a1d51ebc3667ff6 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 16:47:22 -0700 Subject: [PATCH 063/206] Wordline route layers and (optional) via. --- compiler/modules/bank.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 92e4e8cd..d9659cd5 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -798,22 +798,34 @@ class bank(design.design): for row in range(self.num_rows): # The mid guarantees we exit the input cell to the right. - driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).lc() + driver_wl_pin = self.port_address_inst[port].get_pin("wl_{}".format(row)) + driver_wl_pos = driver_wl_pin.rc() + bitcell_wl_pin = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)) + bitcell_wl_pos = bitcell_wl_pin.lc() mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].rx() + 0.5 * self.bitcell_array_inst.lx(), 0) mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1) - self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + self.add_via_stack_center(from_layer=driver_wl_pin.layer, + to_layer=bitcell_wl_pin.layer, + offset=bitcell_wl_pos, + directions=("H", "H")) def route_port_address_right(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ for row in range(self.num_rows): # The mid guarantees we exit the input cell to the right. - driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).lc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).rc() + driver_wl_pin = self.port_address_inst[port].get_pin("wl_{}".format(row)) + driver_wl_pos = driver_wl_pin.lc() + bitcell_wl_pin = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)) + bitcell_wl_pos = bitcell_wl_pin.rc() mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].lx() + 0.5 * self.bitcell_array_inst.rx(), 0) mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1) - self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + self.add_via_stack_center(from_layer=driver_wl_pin.layer, + to_layer=bitcell_wl_pin.layer, + offset=bitcell_wl_pos, + directions=("H", "H")) def route_column_address_lines(self, port): """ Connecting the select lines of column mux to the address bus """ From ef940e0dc5ab64350f5f8817a988a9c082c9a21e Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Mon, 8 Jun 2020 05:02:04 +0000 Subject: [PATCH 064/206] Fixes for functional test of spare cols --- compiler/characterizer/simulation.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 9027fca1..064eb2c5 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -131,7 +131,6 @@ class simulation(): debug.error("Non-binary data string",1) bit -= 1 - def add_address(self, address, port): """ Add the array of address values """ debug.check(len(address)==self.addr_size, "Invalid address size.") @@ -193,7 +192,7 @@ class simulation(): self.add_control_one_port(port, "write") self.add_data(data,port) self.add_address(address,port) - self.add_wmask(wmask,port) + self.add_wmask(wmask,port) self.add_spare_wen("1" * self.num_spare_cols, port) #Add noops to all other ports. @@ -213,9 +212,8 @@ class simulation(): self.cycle_times.append(self.t_current) self.t_current += self.period self.add_control_one_port(port, "read") - self.add_address(address, port) - self.add_spare_wen("0" * self.num_spare_cols, port) - + self.add_address(address, port) + # If the port is also a readwrite then add # the same value as previous cycle if port in self.write_ports: @@ -227,6 +225,7 @@ class simulation(): self.add_wmask(self.wmask_value[port][-1], port) except: self.add_wmask("0"*self.num_wmasks, port) + self.add_spare_wen("0" * self.num_spare_cols, port) #Add noops to all other ports. for unselected_port in self.all_ports: @@ -269,7 +268,7 @@ class simulation(): self.add_control_one_port(port, "read") self.add_address(address, port) - self.add_spare_wen("0" * self.num_spare_cols, port) + # If the port is also a readwrite then add # the same value as previous cycle if port in self.write_ports: @@ -280,14 +279,13 @@ class simulation(): try: self.add_wmask(self.wmask_value[port][-1], port) except: - self.add_wmask("0"*self.num_wmasks, port) - + self.add_wmask("0"*self.num_wmasks, port) + self.add_spare_wen("0" * self.num_spare_cols, port) def add_noop_one_port(self, port): """ Add the control values for a noop to a single port. Does not increment the period. """ self.add_control_one_port(port, "noop") - self.add_spare_wen("0" * self.num_spare_cols, port) - + try: self.add_address(self.addr_value[port][-1], port) except: @@ -304,7 +302,8 @@ class simulation(): self.add_wmask(self.wmask_value[port][-1], port) except: self.add_wmask("0"*self.num_wmasks, port) - + self.add_spare_wen("0" * self.num_spare_cols, port) + def add_noop_clock_one_port(self, port): """ Add the control values for a noop to a single port. Increments the period. """ debug.info(2, 'Clock only on port {}'.format(port)) From 300522a1a80c6346bf11e0d9185a3f387fe861e6 Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Mon, 8 Jun 2020 14:31:46 +0000 Subject: [PATCH 065/206] Change spare enable pins offset to lower right --- compiler/modules/write_driver_array.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 510325bb..22a75218 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -7,6 +7,7 @@ # import design import debug +from tech import drc from sram_factory import factory from vector import vector from globals import OPTS @@ -239,9 +240,7 @@ class write_driver_array(design.design): en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks), layer="m1", - offset=en_pin.ll(), - width=self.driver.width - en_pin.width()) - + offset=en_pin.lr() + vector(-drc("minwidth_m1"),0)) elif self.num_spare_cols and not self.write_size: # shorten enable rail to accomodate those for spare write drivers @@ -258,9 +257,8 @@ class write_driver_array(design.design): en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1), layer="m1", - offset=en_pin.ll(), - width=self.driver.width - en_pin.width()) - + offset=en_pin.lr() + vector(-drc("minwidth_m1"),0)) + else: inst = self.driver_insts[0] self.add_layout_pin(text=self.en_name, From c39c0efd398e0877088ee80c04c24dbda62bf85f Mon Sep 17 00:00:00 2001 From: Aditi Sinha Date: Mon, 8 Jun 2020 16:38:18 +0000 Subject: [PATCH 066/206] Updated spare col tests --- .../09_sense_amp_array_spare_cols_test.py | 10 ++-- .../10_write_driver_array_spare_cols_test.py | 10 ++-- ...rite_driver_array_wmask_spare_cols_test.py | 4 +- .../tests/18_port_data_spare_cols_test.py | 3 +- .../tests/19_single_bank_spare_cols_test.py | 2 +- ..._sram_1bank_2mux_1rw_1r_spare_cols_test.py | 21 ++++--- ...0_sram_1bank_2mux_1w_1r_spare_cols_test.py | 23 ++++---- ...0_sram_1bank_2mux_wmask_spare_cols_test.py | 22 +++---- ...sram_1bank_nomux_1rw_1r_spare_cols_test.py | 18 +++--- .../20_sram_1bank_nomux_spare_cols_test.py | 2 +- ...0_sram_1bank_nomux_wmask_sparecols_test.py | 57 +++++++++++++++++++ 11 files changed, 112 insertions(+), 60 deletions(-) create mode 100755 compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py diff --git a/compiler/tests/09_sense_amp_array_spare_cols_test.py b/compiler/tests/09_sense_amp_array_spare_cols_test.py index 1eabe196..d8da1dc5 100755 --- a/compiler/tests/09_sense_amp_array_spare_cols_test.py +++ b/compiler/tests/09_sense_amp_array_spare_cols_test.py @@ -15,18 +15,18 @@ from globals import OPTS from sram_factory import factory import debug -class sense_amp_test(openram_test): +class sense_amp_array_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) # check sense amp array for single port - debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 and num_spare_cols=3") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=1, num_spare_cols=3) self.local_check(a) - debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4") + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 and num_spare_cols=2") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4, num_spare_cols=2) self.local_check(a) @@ -37,11 +37,11 @@ class sense_amp_test(openram_test): OPTS.num_r_ports = 0 factory.reset() - debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)") + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2, num_spare_cols=2 (multi-port case)") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2, num_spare_cols=2) self.local_check(a) - debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)") + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4, num_spare_cols=3 (multi-port case)") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4, num_spare_cols=3) self.local_check(a) diff --git a/compiler/tests/10_write_driver_array_spare_cols_test.py b/compiler/tests/10_write_driver_array_spare_cols_test.py index c5e7132f..8b1256d0 100755 --- a/compiler/tests/10_write_driver_array_spare_cols_test.py +++ b/compiler/tests/10_write_driver_array_spare_cols_test.py @@ -15,18 +15,18 @@ from globals import OPTS from sram_factory import factory import debug -class write_driver_test(openram_test): +class write_driver_array_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) # check write driver array for single port - debug.info(2, "Testing write_driver_array for columns=8, word_size=8") + debug.info(2, "Testing write_driver_array for columns=8, word_size=8 and num_spare_cols=3") a = factory.create(module_type="write_driver_array", columns=8, word_size=8, num_spare_cols=3) self.local_check(a) - debug.info(2, "Testing write_driver_array for columns=16, word_size=8") + debug.info(2, "Testing write_driver_array for columns=16, word_size=8 and num_spare_cols=3") a = factory.create(module_type="write_driver_array", columns=16, word_size=8, num_spare_cols=3) self.local_check(a) @@ -37,11 +37,11 @@ class write_driver_test(openram_test): OPTS.num_r_ports = 0 factory.reset() - debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)") + debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case and num_spare_cols=3") a = factory.create(module_type="write_driver_array", columns=8, word_size=8, num_spare_cols=3) self.local_check(a) - debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case)") + debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case and num_spare_cols=3") a = factory.create(module_type="write_driver_array", columns=16, word_size=8, num_spare_cols=3) self.local_check(a) diff --git a/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py b/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py index be85665e..8a1e4788 100755 --- a/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py +++ b/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py @@ -17,7 +17,7 @@ from sram_factory import factory import debug -class write_driver_test(openram_test): +class write_driver_array_wmask_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -33,7 +33,7 @@ class write_driver_test(openram_test): self.local_check(a) debug.info(2, "Testing write_driver_array for columns=16, word_size=8, write_size=4") - a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4) + a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4, num_spare_cols=3) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/18_port_data_spare_cols_test.py b/compiler/tests/18_port_data_spare_cols_test.py index 1695f861..8c08ad0a 100755 --- a/compiler/tests/18_port_data_spare_cols_test.py +++ b/compiler/tests/18_port_data_spare_cols_test.py @@ -22,8 +22,7 @@ class port_data_spare_cols_test(openram_test): c = sram_config(word_size=8, num_words=16, - num_spare_rows=1, - num_spare_cols=1) + num_spare_cols=3) c.words_per_row=1 factory.reset() diff --git a/compiler/tests/19_single_bank_spare_cols_test.py b/compiler/tests/19_single_bank_spare_cols_test.py index fc247bcd..52eeea52 100755 --- a/compiler/tests/19_single_bank_spare_cols_test.py +++ b/compiler/tests/19_single_bank_spare_cols_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class single_bank_test(openram_test): +class single_bank_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py index d087a47b..6adb3e8e 100755 --- a/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py @@ -15,19 +15,17 @@ from globals import OPTS from sram_factory import factory import debug -class sram_1bank_2mux_1rw_1r_test(openram_test): +class sram_1bank_2mux_1rw_1r_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, @@ -38,13 +36,14 @@ class sram_1bank_2mux_1rw_1r_test(openram_test): c.recompute_sizes() debug.info(1, "Layout test for {}rw,{}r,{}w sram " "with {} bit words, {} words, {} words per " - "row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) + "row, {} spare columns, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_spare_cols, + c.num_banks)) a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py index 349f5374..987a297e 100755 --- a/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py @@ -15,20 +15,18 @@ from globals import OPTS from sram_factory import factory import debug -#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error") -class psram_1bank_2mux_1w_1r_test(openram_test): +#@unittest.skip("SKIPPING 20_sram_1bank_2mux_1w_1r_spare_cols_test, odd supply routing error") +class sram_1bank_2mux_1w_1r_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1w_1r" - OPTS.replica_bitcell="replica_bitcell_1w_1r" - OPTS.dummy_bitcell="dummy_bitcell_1w_1r" OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, @@ -39,13 +37,14 @@ class psram_1bank_2mux_1w_1r_test(openram_test): c.recompute_sizes() debug.info(1, "Layout test for {}rw,{}r,{}w sram " "with {} bit words, {} words, {} words per " - "row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_banks)) + "row, {} spare columns, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_spare_cols, + c.num_banks)) a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py index 0e9c74c3..0488b93e 100755 --- a/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py @@ -16,9 +16,8 @@ from globals import OPTS from sram_factory import factory import debug - -# @unittest.skip("SKIPPING 20_sram_1bank_2mux_wmask_test") -class sram_1bank_2mux_wmask_test(openram_test): +# @unittest.skip("SKIPPING 20_sram_1bank_2mux_wmask_spare_cols_test") +class sram_1bank_2mux_wmask_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -34,14 +33,15 @@ class sram_1bank_2mux_wmask_test(openram_test): c.recompute_sizes() debug.info(1, "Layout test for {}rw,{}r,{}w sram " "with {} bit words, {} words, {} bit writes, {} words per " - "row, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.write_size, - c.words_per_row, - c.num_banks)) + "row, {} spare columns, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.write_size, + c.words_per_row, + c.num_spare_cols, + c.num_banks)) a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py index 575fc51f..dbeca8aa 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py @@ -22,12 +22,10 @@ class sram_1bank_nomux_1rw_1r_spare_cols_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell = "dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=16, @@ -39,13 +37,13 @@ class sram_1bank_nomux_1rw_1r_spare_cols_test(openram_test): debug.info(1, "Layout test for {}rw,{}r,{}w sram " "with {} bit words, {} words, {} words per " "row, {} spare columns, {} banks".format(OPTS.num_rw_ports, - OPTS.num_r_ports, - OPTS.num_w_ports, - c.word_size, - c.num_words, - c.words_per_row, - c.num_spare_cols, - c.num_banks)) + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_spare_cols, + c.num_banks)) a = factory.create(module_type="sram", sram_config=c) self.local_check(a, final_verification=True) diff --git a/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py index 14001c19..135518e3 100755 --- a/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py @@ -17,7 +17,7 @@ from sram_factory import factory import debug -# @unittest.skip("SKIPPING 20_sram_1bank_nomux_wmask_test") +# @unittest.skip("SKIPPING 20_sram_1bank_nomux_spare_cols_test") class sram_1bank_nomux_spare_cols_test(openram_test): def runTest(self): diff --git a/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py b/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py new file mode 100755 index 00000000..e22122bd --- /dev/null +++ b/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +@unittest.skip("SKIPPING 20_sram_1bank_nomux_wmask_sparecols_test, not working yet") +class sram_1bank_nomux_wmask_sparecols_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + c = sram_config(word_size=8, + write_size=4, + num_words=16, + num_spare_cols=3, + num_banks=1) + + c.words_per_row = 1 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} bit writes, {} words per " + "row, {} spare columns, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.write_size, + c.words_per_row, + c.num_spare_cols, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 9cc36c6d3a0386492b0ecf4b2ac3a88594257b4c Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 8 Jun 2020 11:01:14 -0700 Subject: [PATCH 067/206] Bus code converted to pins. Fix layers on control signal routes in bank. --- compiler/base/hierarchy_layout.py | 60 +++++++++--------- compiler/modules/bank.py | 71 ++++++++++------------ compiler/modules/control_logic.py | 36 +++++------ compiler/modules/hierarchical_decoder.py | 10 +-- compiler/modules/hierarchical_predecode.py | 16 ++--- compiler/modules/precharge_array.py | 1 + compiler/pgates/precharge.py | 12 +++- 7 files changed, 105 insertions(+), 101 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 12a9826e..d3b84ca5 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -219,8 +219,6 @@ class layout(): if not height: height = drc["minwidth_{}".format(layer)] lpp = techlayer[layer] - if abs(offset[0]-5.16250)<0.01 and abs(offset[1]-8.70750)<0.01: - import pdb; pdb.set_trace() self.objs.append(geometry.rectangle(lpp, offset, width, @@ -838,51 +836,57 @@ class layout(): if not pitch: pitch = getattr(self, "{}_pitch".format(layer)) - line_positions = {} + pins = {} if vertical: for i in range(len(names)): line_offset = offset + vector(i * pitch, 0) if make_pins: - self.add_layout_pin(text=names[i], - layer=layer, - offset=line_offset, - height=length) + new_pin = self.add_layout_pin(text=names[i], + layer=layer, + offset=line_offset, + height=length) else: - self.add_rect(layer=layer, - offset=line_offset, - height=length) - line_positions[names[i]] = line_offset + vector(half_minwidth, 0) + rect = self.add_rect(layer=layer, + offset=line_offset, + height=length) + new_pin = pin_layout(names[i], + [rect.ll(), rect.ur()], + layer) + + pins[names[i]] = new_pin else: for i in range(len(names)): line_offset = offset + vector(0, i * pitch + half_minwidth) if make_pins: - self.add_layout_pin(text=names[i], - layer=layer, - offset=line_offset, - width=length) + new_pin = self.add_layout_pin(text=names[i], + layer=layer, + offset=line_offset, + width=length) else: - self.add_rect(layer=layer, - offset=line_offset, - width=length) - # Make this the center of the rail - line_positions[names[i]] = line_offset + vector(0.5 * length, - half_minwidth) + rect = self.add_rect(layer=layer, + offset=line_offset, + width=length) + new_pin = pin_layout(names[i], + [rect.ll(), rect.ur()], + layer) + + pins[names[i]] = new_pin - return line_positions + return pins - def connect_horizontal_bus(self, mapping, inst, bus_offsets, + def connect_horizontal_bus(self, mapping, inst, bus_pins, layer_stack=("m1", "via1", "m2")): """ Horizontal version of connect_bus. """ - self.connect_bus(mapping, inst, bus_offsets, layer_stack, True) + self.connect_bus(mapping, inst, bus_pins, layer_stack, True) - def connect_vertical_bus(self, mapping, inst, bus_offsets, + def connect_vertical_bus(self, mapping, inst, bus_pins, layer_stack=("m1", "via1", "m2")): """ Vertical version of connect_bus. """ - self.connect_bus(mapping, inst, bus_offsets, layer_stack, False) + self.connect_bus(mapping, inst, bus_pins, layer_stack, False) - def connect_bus(self, mapping, inst, bus_offsets, layer_stack, horizontal): + def connect_bus(self, mapping, inst, bus_pins, layer_stack, horizontal): """ Connect a mapping of pin -> name for a bus. This could be replaced with a channel router in the future. @@ -898,7 +902,7 @@ class layout(): for (pin_name, bus_name) in mapping: pin = inst.get_pin(pin_name) pin_pos = pin.center() - bus_pos = bus_offsets[bus_name] + bus_pos = bus_pins[bus_name].center() if horizontal: # up/down then left/right diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index d9659cd5..36fecfc4 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -593,7 +593,7 @@ class bank(design.design): # Connect the inverter output to the central bus out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc() name = self.control_signals[port][signal] - bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y) + bus_pos = vector(self.bus_pins[port][name].cx(), out_pos.y) self.add_path("m3", [out_pos, bus_pos]) self.add_via_center(layers=self.m2_stack, offset=bus_pos) @@ -628,19 +628,19 @@ class bank(design.design): # Overall central bus width. It includes all the column mux lines, # and control lines. - self.bus_xoffset = [None] * len(self.all_ports) + self.bus_pins = [None] * len(self.all_ports) # Port 0 # 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 control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - self.m3_pitch, self.min_y_offset) # The control bus is routed up to two pitches below the bitcell array control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch - self.bus_xoffset[0] = self.create_bus(layer="m2", - offset=control_bus_offset, - names=self.control_signals[0], - length=control_bus_length, - vertical=True, - make_pins=(self.num_banks==1)) + self.bus_pins[0] = self.create_bus(layer="m2", + offset=control_bus_offset, + names=self.control_signals[0], + length=control_bus_length, + vertical=True, + make_pins=(self.num_banks==1)) # Port 1 if len(self.all_ports)==2: @@ -649,12 +649,12 @@ class bank(design.design): control_bus_offset = vector(self.bitcell_array_right + self.m3_pitch, self.max_y_offset - control_bus_length) # The bus for the right port is reversed so that the rbl_wl is closest to the array - self.bus_xoffset[1] = self.create_bus(layer="m2", - offset=control_bus_offset, - names=list(reversed(self.control_signals[1])), - length=control_bus_length, - vertical=True, - make_pins=(self.num_banks==1)) + self.bus_pins[1] = self.create_bus(layer="m2", + offset=control_bus_offset, + names=list(reversed(self.control_signals[1])), + length=control_bus_length, + vertical=True, + make_pins=(self.num_banks==1)) def route_port_data_to_bitcell_array(self, port): """ Routing of BL and BR between port data and bitcell array """ @@ -919,39 +919,32 @@ class bank(design.design): # pre-decoder and this connection is in metal3 connection = [] connection.append((self.prefix + "p_en_bar{}".format(port), - self.port_data_inst[port].get_pin("p_en_bar").lc(), - self.port_data_inst[port].get_pin("p_en_bar").layer)) + self.port_data_inst[port].get_pin("p_en_bar"))) rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port]) connection.append((self.prefix + "wl_en{}".format(port), - self.bitcell_array_inst.get_pin(rbl_wl_name).lc(), - self.bitcell_array_inst.get_pin(rbl_wl_name).layer)) + self.bitcell_array_inst.get_pin(rbl_wl_name))) if port in self.write_ports: - if port % 2: - connection.append((self.prefix + "w_en{}".format(port), - self.port_data_inst[port].get_pin("w_en").rc(), - self.port_data_inst[port].get_pin("w_en").layer)) - else: - connection.append((self.prefix + "w_en{}".format(port), - self.port_data_inst[port].get_pin("w_en").lc(), - self.port_data_inst[port].get_pin("w_en").layer)) + connection.append((self.prefix + "w_en{}".format(port), + self.port_data_inst[port].get_pin("w_en"))) if port in self.read_ports: connection.append((self.prefix + "s_en{}".format(port), - self.port_data_inst[port].get_pin("s_en").lc(), - self.port_data_inst[port].get_pin("s_en").layer)) + self.port_data_inst[port].get_pin("s_en"))) - for (control_signal, pin_pos, pin_layer) in connection: - if port==0: - y_offset = self.min_y_offset - else: - y_offset = self.max_y_offset - control_pos = vector(self.bus_xoffset[port][control_signal].x, y_offset) - if pin_layer == "m1": - self.add_wire(self.m1_stack, [control_pos, pin_pos]) - elif pin_layer == "m3": - self.add_wire(self.m2_stack[::-1], [control_pos, pin_pos]) + for (control_signal, pin) in connection: + control_pin = self.bus_pins[port][control_signal] + control_pos = vector(control_pin.cx(), pin.cy()) + # If the y doesn't overlap the bus, add a segment + if pin.cy() < control_pin.by(): + self.add_path("m2", [control_pos, control_pin.bc()]) + elif pin.cy() > control_pin.uy(): + self.add_path("m2", [control_pos, control_pin.uc()]) + self.add_path(pin.layer, [control_pos, pin.center()]) + self.add_via_stack_center(from_layer=pin.layer, + to_layer="m2", + offset=control_pos) # clk to wordline_driver control_signal = self.prefix + "wl_en{}".format(port) @@ -961,7 +954,7 @@ class bank(design.design): else: pin_pos = self.port_address_inst[port].get_pin("wl_en").bc() mid_pos = pin_pos - vector(0, 2 * self.m2_gap) # to route down to the top of the bus - control_x_offset = self.bus_xoffset[port][control_signal].x + control_x_offset = self.bus_pins[port][control_signal].cx() control_pos = vector(control_x_offset, mid_pos.y) self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos]) self.add_via_center(layers=self.m1_stack, diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 078dc3ae..c4d9f034 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -384,10 +384,10 @@ class control_logic(design.design): height = self.control_logic_center.y - self.m2_pitch offset = vector(self.ctrl_dff_array.width, 0) - self.rail_offsets = self.create_vertical_bus("m2", - offset, - self.internal_bus_list, - height) + self.input_bus = self.create_vertical_bus("m2", + offset, + self.internal_bus_list, + height) def create_instances(self): """ Create all the instances """ @@ -493,7 +493,7 @@ class control_logic(design.design): # Connect to the rail level with the vdd rail # Use pen since it is in every type of control logic vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by() - in_pos = vector(self.rail_offsets["rbl_bl_delay"].x, vdd_ypos) + in_pos = vector(self.input_bus["rbl_bl_delay"].cx(), vdd_ypos) mid1 = vector(out_pos.x, in_pos.y) self.add_wire(self.m1_stack, [out_pos, mid1, in_pos]) self.add_via_center(layers=self.m1_stack, @@ -548,7 +548,7 @@ class control_logic(design.design): def route_gated_clk_bar(self): clkbuf_map = zip(["A"], ["clk_buf"]) - self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets) + self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.input_bus) out_pos = self.clk_bar_inst.get_pin("Z").center() in_pos = self.gated_clk_bar_inst.get_pin("A").center() @@ -558,7 +558,7 @@ class control_logic(design.design): clkbuf_map = zip(["B"], ["cs"]) self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, - self.rail_offsets, + self.input_bus, self.m2_stack[::-1]) # The pin is on M1, so we need another via as well b_pin = self.gated_clk_bar_inst.get_pin("B") @@ -586,12 +586,12 @@ class control_logic(design.design): clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"]) self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, - self.rail_offsets) + self.input_bus) clkbuf_map = zip(["Z"], ["gated_clk_buf"]) self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, - self.rail_offsets, + self.input_bus, self.m2_stack[::-1]) # The pin is on M1, so we need another via as well z_pin = self.gated_clk_buf_inst.get_pin("Z") @@ -614,7 +614,7 @@ class control_logic(design.design): def route_wlen(self): wlen_map = zip(["A"], ["gated_clk_bar"]) - self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.rail_offsets) + self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.input_bus) self.connect_output(self.wl_en_inst, "Z", "wl_en") @@ -639,7 +639,7 @@ class control_logic(design.design): def route_pen(self): in_map = zip(["A", "B"], ["gated_clk_buf", "rbl_bl_delay"]) - self.connect_vertical_bus(in_map, self.p_en_bar_nand_inst, self.rail_offsets) + self.connect_vertical_bus(in_map, self.p_en_bar_nand_inst, self.input_bus) out_pin = self.p_en_bar_nand_inst.get_pin("Z") out_pos = out_pin.center() @@ -682,7 +682,7 @@ class control_logic(design.design): input_name = "cs" sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name]) - self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.rail_offsets) + self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.input_bus) self.connect_output(self.s_en_gate_inst, "Z", "s_en") @@ -706,7 +706,7 @@ class control_logic(design.design): self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar") rbl_map = zip(["A"], ["rbl_bl_delay"]) - self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets) + self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.input_bus) def create_wen_row(self): @@ -738,7 +738,7 @@ class control_logic(design.design): input_name = "cs" wen_map = zip(["A", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"]) - self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.rail_offsets) + self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus) self.connect_output(self.w_en_gate_inst, "Z", "w_en") @@ -761,13 +761,13 @@ class control_logic(design.design): dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"]) else: dff_out_map = zip(["dout_bar_0"], ["cs"]) - self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, self.m2_stack[::-1]) + self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.input_bus, self.m2_stack[::-1]) # Connect the clock rail to the other clock rail # by routing in the supply rail track to avoid channel conflicts in_pos = self.ctrl_dff_inst.get_pin("clk").uc() mid_pos = in_pos + vector(0, self.and2.height) - rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y) + rail_pos = vector(self.input_bus["clk_buf"].cx(), mid_pos.y) self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos]) self.add_via_center(layers=self.m1_stack, offset=rail_pos) @@ -1001,8 +1001,8 @@ class control_logic(design.design): # Connect this at the bottom of the buffer out_pos = inst.get_pin("Z").center() mid1 = vector(out_pos.x, out_pos.y - 0.25 * inst.mod.height) - mid2 = vector(self.rail_offsets[name].x, mid1.y) - bus_pos = self.rail_offsets[name] + mid2 = vector(self.input_bus[name].cx(), mid1.y) + bus_pos = self.input_bus[name].center() self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos]) # The pin is on M1, so we need another via as well self.add_via_center(layers=self.m1_stack, diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index fbf8ef29..bb2b37ab 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -209,7 +209,7 @@ class hierarchical_decoder(design.design): for i in range(2): index = pre_num * 2 + i - input_pos = self.input_bus["addr_{}".format(index)] + input_pos = self.input_bus["addr_{}".format(index)].center() in_name = "in_{}".format(i) decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) @@ -223,7 +223,7 @@ class hierarchical_decoder(design.design): for i in range(3): index = pre_num * 3 + i + self.no_of_pre2x4 * 2 - input_pos = self.input_bus["addr_{}".format(index)] + input_pos = self.input_bus["addr_{}".format(index)].center() in_name = "in_{}".format(i) decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) @@ -572,7 +572,7 @@ class hierarchical_decoder(design.design): """ pin_pos = pin.center() - rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) + rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y) self.add_path(self.input_layer, [rail_pos, pin_pos]) self.add_via_stack_center(from_layer=self.bus_layer, @@ -595,11 +595,11 @@ class hierarchical_decoder(design.design): pin_pos = pin.rc() mid_point1 = vector(x_offset, pin_pos.y) mid_point2 = vector(x_offset, y_offset) - rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y) + rail_pos = vector(self.predecode_bus[rail_name].cx(), mid_point2.y) self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos]) # pin_pos = pin.center() - # rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) + # rail_pos = vector(self.predecode_bus[rail_name].cx(), pin_pos.y) # self.add_path(self.output_layer, [pin_pos, rail_pos]) self.add_via_stack_center(from_layer=pin.layer, to_layer=self.output_layer, diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index d998d882..03d866fb 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -202,15 +202,15 @@ class hierarchical_predecode(design.design): y_offset = pin.cy() in_pin = "in_{}".format(num) a_pin = "A_{}".format(num) - in_pos = vector(self.input_rails[in_pin].x, y_offset) - a_pos = vector(self.decode_rails[a_pin].x, y_offset) + in_pos = vector(self.input_rails[in_pin].cx(), y_offset) + a_pos = vector(self.decode_rails[a_pin].cx(), y_offset) self.add_path(self.input_layer, [in_pos, a_pos]) self.add_via_stack_center(from_layer=self.input_layer, to_layer=self.bus_layer, - offset=[self.input_rails[in_pin].x, y_offset]) + offset=[self.input_rails[in_pin].cx(), y_offset]) self.add_via_stack_center(from_layer=self.input_layer, to_layer=self.bus_layer, - offset=[self.decode_rails[a_pin].x, y_offset]) + offset=[self.decode_rails[a_pin].cx(), y_offset]) def route_output_and(self): """ @@ -240,12 +240,12 @@ class hierarchical_predecode(design.design): # since this is where the p/n devices are and there are no # pins in the and gates. if OPTS.tech_name == "s8": - rail_pos = vector(self.decode_rails[out_pin].x, inv_out_pos.y) + rail_pos = vector(self.decode_rails[out_pin].cx(), inv_out_pos.y) self.add_path(self.output_layer, [inv_out_pos, rail_pos]) else: y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0) - rail_pos = vector(self.decode_rails[out_pin].x, y_offset) + rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset) self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) self.add_via_stack_center(from_layer=inv_out_pin.layer, @@ -259,7 +259,7 @@ class hierarchical_predecode(design.design): # route input pin = self.inv_inst[inv_num].get_pin("A") inv_in_pos = pin.center() - in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y) + in_pos = vector(self.input_rails[in_pin].cx(), inv_in_pos.y) self.add_path(self.input_layer, [in_pos, inv_in_pos]) self.add_via_stack_center(from_layer=pin.layer, to_layer=self.input_layer, @@ -290,7 +290,7 @@ class hierarchical_predecode(design.design): for rail_pin, gate_pin in zip(index_lst, gate_lst): pin = self.and_inst[k].get_pin(gate_pin) pin_pos = pin.center() - rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) + rail_pos = vector(self.decode_rails[rail_pin].cx(), pin_pos.y) self.add_path(self.input_layer, [rail_pos, pin_pos]) self.add_via_stack_center(from_layer=self.input_layer, to_layer=self.bus_layer, diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 84f27fba..2cf4718a 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -10,6 +10,7 @@ import debug from vector import vector from sram_factory import factory from globals import OPTS +from tech import layer class precharge_array(design.design): diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index cfc2a688..76fc8676 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -13,7 +13,7 @@ from tech import parameter from vector import vector from globals import OPTS from sram_factory import factory -from tech import drc +from tech import drc, layer class precharge(design.design): @@ -38,10 +38,16 @@ class precharge(design.design): if self.bitcell_bl_pin.layer == "m1": self.bitline_layer = "m1" - self.en_layer = "m2" + if "li" in layer: + self.en_layer = "li" + else: + self.en_layer = "m2" else: self.bitline_layer = "m2" - self.en_layer = "m1" + if "li" in layer: + self.en_layer = "li" + else: + self.en_layer = "m1" # Creates the netlist and layout # Since it has variable height, it is not a pgate. From 8c6d5b49bee22effd09b3b1c6ea2a2d0ab67fe42 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 13:09:52 -0700 Subject: [PATCH 068/206] Consider diffusion spacing in active offset --- compiler/pgates/ptx.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index f6716a1f..d52925ec 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -216,7 +216,8 @@ class ptx(design.design): nwell_enclose_active = 0 # Use the max of either so that the poly gates will align properly well_enclose_active = max(pwell_enclose_active, - nwell_enclose_active) + nwell_enclose_active, + self.active_space) self.active_offset = vector([well_enclose_active] * 2) # Well enclosure of active, ensure minwidth as well From 157926960b51631f6ff8d6a74862280fd2d3f93c Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 13:48:16 -0700 Subject: [PATCH 069/206] Flip freepdk45 flop, dff_buf route layer change --- compiler/modules/dff_buf.py | 50 +++++++++++++-------------- technology/freepdk45/gds_lib/dff.gds | Bin 22528 -> 22182 bytes 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 0366b10b..81629771 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -7,7 +7,7 @@ # import debug import design -from tech import parameter +from tech import parameter, layer from tech import cell_properties as props from vector import vector from globals import OPTS @@ -52,7 +52,6 @@ class dff_buf(design.design): def create_layout(self): self.place_instances() self.width = self.inv2_inst.rx() - self.height = self.dff.height self.route_wires() self.add_layout_pins() @@ -120,39 +119,37 @@ class dff_buf(design.design): except AttributeError: pass self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0)) - + # Add INV2 to the right self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) def route_wires(self): + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + # Route dff q to inv1 a q_pin = self.dff_inst.get_pin("Q") a1_pin = self.inv1_inst.get_pin("A") - mid_x_offset = 0.5 * (a1_pin.cx() + q_pin.cx()) - mid1 = vector(mid_x_offset, q_pin.cy()) - mid2 = vector(mid_x_offset, a1_pin.cy()) - self.add_path("m3", [q_pin.center(), mid1, mid2, a1_pin.center()]) - self.add_via_center(layers=self.m2_stack, - offset=q_pin.center()) - self.add_via_center(layers=self.m2_stack, - offset=a1_pin.center()) - self.add_via_center(layers=self.m1_stack, - offset=a1_pin.center()) + mid1 = vector(a1_pin.cx(), q_pin.cy()) + self.add_path(q_pin.layer, [q_pin.center(), mid1, a1_pin.center()]) + self.add_via_stack_center(from_layer=a1_pin.layer, + to_layer=q_pin.layer, + offset=a1_pin.center()) # Route inv1 z to inv2 a z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") - mid_x_offset = 0.5 * (z1_pin.cx() + a2_pin.cx()) - self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy()) - mid2 = vector(mid_x_offset, a2_pin.cy()) - self.add_path("m1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()]) + self.mid_qb_pos = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy()) + self.add_zjog(z1_pin.layer, z1_pin.center(), a2_pin.center()) def add_layout_pins(self): # Continous vdd rail along with label. vdd_pin=self.dff_inst.get_pin("vdd") self.add_layout_pin(text="vdd", - layer="m1", + layer=vdd_pin.layer, offset=vdd_pin.ll(), width=self.width, height=vdd_pin.height()) @@ -160,7 +157,7 @@ class dff_buf(design.design): # Continous gnd rail along with label. gnd_pin=self.dff_inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="m1", + layer=gnd_pin.layer, offset=gnd_pin.ll(), width=self.width, height=vdd_pin.height()) @@ -185,17 +182,20 @@ class dff_buf(design.design): self.add_layout_pin_rect_center(text="Q", layer="m2", offset=q_pos) - self.add_path("m1", [dout_pin.center(), mid_pos, q_pos]) - self.add_via_center(layers=self.m1_stack, - offset=q_pos) + self.add_path(self.route_layer, [dout_pin.center(), mid_pos, q_pos]) + self.add_via_stack_center(from_layer=dout_pin.layer, + to_layer="m2", + offset=q_pos) qb_pos = self.mid_qb_pos + vector(0, self.m2_pitch) self.add_layout_pin_rect_center(text="Qb", layer="m2", offset=qb_pos) - self.add_path("m1", [self.mid_qb_pos, qb_pos]) - self.add_via_center(layers=self.m1_stack, - offset=qb_pos) + self.add_path(self.route_layer, [self.mid_qb_pos, qb_pos]) + a2_pin = self.inv2_inst.get_pin("A") + self.add_via_stack_center(from_layer=a2_pin.layer, + to_layer="m2", + offset=qb_pos) def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" diff --git a/technology/freepdk45/gds_lib/dff.gds b/technology/freepdk45/gds_lib/dff.gds index 526a1861149039682e51578ccf5624d908940ae9..2a6004d145d7ef077006b8a6b4a6abce4d37eef4 100644 GIT binary patch literal 22182 zcmcJX53HrdS;oJ6&;NVRx##}duDCR|v}?0jaMKV{SEvZ4rNF{aRIRwmLukhA%$!$jSYGzx#ssZT**PKJ&fm{7zom?{;sz>87e`_p7QqT9o(h z%3@Vjm#wy{YH+x!mfsRNx<+iL?A&NPky;va0&qA_qIf6Y6uK?P6BH z@ZG3~=oUXTA^KFF)sN3;;6AEx5eNIKYW2PeZ5OlpO^t0|V*m$luc|E^tAcE^7OGvo-}OJ<8wOA=Wu;vH{yRcezB@v_nxXUr^=@Fi%I_u z`d(FM&YaO4;osmdH2<%du>S{|dz#lK<@k@ZE^yj^YuZ2l(A%%$z|GC*&%`hBOzXsH z|J`Q)-Hx;U+5U?5EljtsdpfRpxT5*e+dm;|y5G~9OQgL2@39<`E0TV?eGUK1&r0(H zt!Z)f%bIY?1?I|C>hDwXm$-C&EH|waXZa8LY6Hh*?ChOU(@x(S^nbs`jY04=4JII{k&KC=MPQW0}FbM z;eSW{hWwy6Z5OlpMfHJkh0MRjyC$?<%<32RMm-FA`$>LNc~;;0NYq30trsUGek#xE zX>Vv}YIp4?`Ay|nePM#Kdh#RnV9?o5 z@|()Ddh#85h@O07+FpuJd#UGk*M5@URGy7bzC#a*OTIB}FU6<5)N{LQKgn+@&&DU; zp@+mJZ<@B3;?rL0x!tv&Vb znz7$C^mFWY4gDPZT|+;|e%H{?vEMcHbL@AW>HV&LE^%7-JPX<@_fP2Om?q`q3zN@N zNk2Wm8vg6g{m<%JzBbP@?fbc#upX4`{XXCT}(0d)S{}LbCUW)H})-9!Rgy>UwHvSyX z_Zst;{hD(Mc#hBa8hZ9?o+Cj&*E4ND>-khe&wfokc#hBa8hZ9?^f2h{C;3g~+3~BN z!#%J5KXy*f_Zson-l(`d9~95=`Ce0hd2Iiyc)r)vKb-WUpX-^7OM3p7?LYe^^9=gA zo@sk&{-)=5;qxK7b9lbDG=2WJaYC_|^#gU5zjBrxU`as(Y@p(r8eW3I!?~nRG+Y9>P%~2mH z{ZQ9{{s&5*%CqfXIu-SS(k#n76nvcC6xtjGF6$GVBMy{zxv8SAl5(6MeJZ7=KF-;ecJKj>ID zk+zrht$SiU)(JY+O{DE*{bDQDW1XO5-9*}6*7xqxoIRraBd%`~U!Kr*F}r?-JC(b? zpm`&%yj}e4gtm)WeeX8i`@c(Z;lIT^Skv}*<@F2iRF1w#T6k1^dWyDRRMs!=jrwJM z_D5gS_Pu5OP-~v}!*4`=P1~<4>qnZi=tnO`eNEf%$m`o0^W_cA8~C88Tv*fgjk2C= zhn{PPzNYOv%lgrts2?4V`kJ=y$?KOsuXx9F&BW1ewf)iwZ5OliC+MDv`jvfAZ-#qw z{61CIuY5b|$G1eiX}egAKRy%nyt_pEP20t+e(-zJzDaoxKBAw_6m4&wZ#C|}c;3W2 zk#KxK)HO717qjt$?o`yT-Wv7hIN8Ole*A*^e4k>9!;$)~JZjo57WKo2qkgnK>P_3l zqMkfMeDV+RP1{TPXX9dNFVg)3RQ}lb+!Spu!aSZT`cNX|2pc|E{S^6b}_48-=pjG zRqeUr^0nge32hg%diIU>-^Tr;b6MO!OxwkxzVok9-_?H0{%6`QX7%l>Vth#4_7f8l zKb05t#GxK>(VMoH;*&Q!ACLL7dx6GMbHKD+%*JQ$YiZvJR32gPYnit1F6-OccfuZe zBI?_w?fc65j`kh&ol{X?)An2PdiI!(@>HPm7kf{~w7rnO*6xb>K}Da~84y+ip*eKu_ui+b)k(06qIir%zcEb2R~AL+YVKj=-{#iE{Prs#XOMZIae znANl2ERHnp@WbLuQ?z|l*0;7tJ>!MGrtRDFdUD;u<1zOw{+#&xil1n^m~B7$MeRGV zbwkvfwu@Okd6V`+t~>jVX}eg|bDhz1-O-!2m#%-%>H0&iJ9?ArpUR8zxz5z<>H4EL zZ7*Ga?mx*Nfyx`~rR0x5+Y9*v9r+_rc>^8!BhdCj{y<0m2vq(+NB#)3y^udz+IJSe zr8bMLoAh&hLfggc_;n8Ge(Qe46vtyxV{Y0mX7%j-+;0b}-R%9`cLv&CXg~Mp{Eb84 zz0p_Sw7r*)zx+;J@9$~6#Qv{}PfuvOm~DSc?eFV)L(VE%YQJf_nALOd*MCZRR^;!C zxc4({7qj|>)6#3*2A-dT7l<{!=)$v>v;;&zRDYJg@O}d4ue=`!=YZF#&-B&{hr9!@~pt**=4AgxOBbdj`ERN`sxYd#V45a+SvBhCW& z`(2YgcKxLKlxRO&nqVTHuX*N%JlhiPvepN&FXJ>pN4#o z4l+N;_onS)QP28EPkusg+Ae1G;uG4`$qj< zOL~tb*h~FKM;u68bf)d4`1QVUU+g#g1LI%s6S9}$qbE+i&qz;v^tPAclmF?z_YLMY z@uBUd_~?iOiHpv(y%c|r_i=nb6Xq{^|6K{|i}&?R+t2!YlZKxB&-#VrXTKNYeIS)* z=O5*~=Lw06&b0lkzc*;aC(nocA%^|o=!CY5+4$&*6ZQ+mCm#_X+Fpu3$NM~u_Sbp- za_uwwW48JBH`Ii1e+e`f?KQhmu-{UcDFU@~+ z{9Q)Mv-RJ7_`TRvo^5}fcbPkfQ~ou{$Jl4z&(`_7ePyi2dL#eVwEfEb{HgP<@+a}h zpEVUfm1o;u=UwT0>rro#2U2-fPu?7STGvWsyz9Js`^2vDqMrWOd00C7kDuCJ=s$Tg zH#e45IO`nKv*kM)Y)v|Y^V$$QkR^Wf2_C;!=A z(v$!E9wIq|`(V>{F&m%!7V;g`IC8&RbH9F%KaZ z>N^_ya2}IryO`C7bHHbG&Gu;CT_+x&a5>qr^*m2Nzj|%d zo3@MD_~XxOja;Js!(;Ms=@f0hB(Gn6I_d+ZUt`Ug(x>uleC9au1EpI(81;d+7vc{- zs_{CZxg+u(1v%Wb{jB#X8rOetSl3IR)8MElJ~yH5Vz&M0c!w_Vbkr}Kw%?l9Z|W?R zci;kF)aU05^t);M9S!|*TECy+^D^hjgKtN!|C2te>w9C&jekzx&yv-{TA9}_$0W+vxdoWGgaIaf4$$230s6f+Tf_&%OM zw>xlF|Nevih4Y#}jqHh?hvY9YoVScM{`bo+at_md{e&yan$KF7CVLF$MCR~$U7LgI zV5*wx~Bp_eTF_?|ijO`O0j)A=Wn;H{zL#sUPC3{)YKDIw^mbs~sZGv4>kH z4CgZgmFv0xvKZFQjtNW8&SNw;O}9&& z)&B6jtEJdF|EVCp$tNb)Ddd~P)dyAYUX6Dm_l#|=C)0k!Y`iwt_(|!+)eUL4Ovqdf z&l=4KR8O(TiOdVmTuu8CXT`gW`PjZnZT@5=>ngmzkr>W{enYl5s~w{2iL?Cu7XHHf z9Pf<`=Q`R0Yw9{v&h-xO%bikR)t9A2>V@;9#Qs5D`)%rTV)whTzS&7}#98fm2kqfA z1kbdgzX!3VaunZX$bJJkzXD5MZaG+uZdtx|0TT_3(q5g>@-9J)p(iYCk%!TAv z%=hn3<@d+cCy}#Sv?lj=;k}nc&UC~37>U-M^*)9BcVXz?1G*l&)J~CgTl&7o^!=U< zefU1ZyR~QVPx}JjR~VW02fpv{?wS1y@kb{$o+)3xBEKbjCSS$&YkYe@9>c;RpZj)yFK72LX}Nz-wEe8_>ukhl*ty@} zANM=q`|c8L7eoA^TB-4K?`u-nE{y&pzz6 zojbwAKcu<+&Ae+p>s{aPK6|Y>)u39e2G#Kki`ABXT95yY^mDK3R~ywS)mhcr;^69Q zuHAcJ|F2fno?icFPx`A*KluD5|9z=Z?MazR&2#YHiZ1s^MQ()#yjn=-9!B z_wSQ-Q~yR&{W|(LN50#Y?>OZbmrv8rC)KoF4E>S#-?I|_2geS6zQg%#BZ{e5Udj%j-Qk}&P#G2#h^}E_?uKz&gDW9qQZRN#i^`DgXlT`(mFIK%SuW7qj z)GxeQ{e5Ot!R2>|kJYqY4E>Re_QEf#+)q^+ZB=U4`%BfJo@-1_|F`{%Rdw_)X@oOv z7iaZaSD}9|qg_?6z4qGApfgqbKh@|SQ*-ot`dOoAlFomLp_?tg*DOEfLr;E}L-~|v zKh&FaxMxo)-;{l}{6Vw*sop5aZ@t@-+c&FUXz0HoJ?;61{(YdTj{3N1d$2=aRa^g- z^xG;2y=l8x)NemqRV&9zBd%-L*>he^+r^@u{N%IlsO_YOws+~b_NIIg|67mNM4!`( z{>dNtvd-;ryK;I_KhZ9L9^!xV-8F3&i+b{t&$?+la=UVRQNMA0(nI`jXcsUkH>a2N z${+c%&h2o!D6gyjW9`W3A%4fV)wI1!zn0q%L+^5OxjDV9{;~e?ho9H=LqGR}xOIwt z)+S`O)U&Pp*j-mXs&)P&`4A`H6rZkXyI9sMf8@)$)z2qAw7p9|Qr%asQeN6{p?GIa z+r>8j(LdxT-|}Z`hTpk%kxu(yi=Vdgqkjf!2jxR_!*Na93-t><`N?P9w4J%#IlbtA zNpo)faRmr*a!|acrtM-`ul$iO>-uj?dT4v6{%fiJOXAukio%+UChfxB)&5={ABfK7 zWL=fh+v*=rJNU!T{5bSA-xv?*`j}nyS@=(tr!wK@3stpvWlh_=*7L%tSudh@In#1e zdRu#9zTyu*i_)4g4s+w&(-RBh4O{BjR({Mk_VuMdOYv&(Cn@fjwu?nQaf*Bp9dXLE zy-UyjPCkf^_+r}LsaO8UmvyuUz1u~3U3&I)^bj5K#k9RkPk!=QH*H65S57bXANw?V zh+pE1X?vG{@<+a`b35FwoL=-#oI(%rOZ+fx@A6MPA|DK$%gN>D^fv!7zww8D{T($I zenG$MiQgoBzMtmm*;ancZ{ied0uGN6wf;@p#j;-cBVX1Lr_e*&yY%ee{gYF?LPz{D zZ5P}8$9_hB@)3v3@H^M8xpLdei~e9=q#Q^+h$p7)Vo^_i@>xgwqCL5tIlZVKXun&~ zJ_UPU5$~#LyI9sMf8@)$;gv}bZST_eFHQMi_;)>Wy>fcdfB%fsen`9f_td0ca(Yp} zq~|X3!O**$Ty9P;>hTwTvR{v*$2F%H^~9atAE^&S-e(YBOxwk>p8i5FqH{UZa#MOy zk3aPG%cpwEfws^2Uuu57Q;ayW&u0z20+Xb9uXscMbh6;$1_( zi+I=6A9=iM=ywtCn))1hy~{uGj(jk5E+?0p)7$({;~o0f^+P}RgXs5=rt4>R)o&N?BOCLRIOq3{ zyze94nYPcpk8J3Pcf4B{O{s@WWzu2Fa6$ea!{Xr-j?2Tb-a&k>eKti>3hT-ypL??ckw>b`Zwx* zq|1x`B%ZOJV9R^Huy?JOUA&L9|Cq1DGyK1S?;}@cQ`P&`k?fyHza*f`kY?WuU(h)LFu`5K>LHz z=k%hU6Mx1pDBZf&Kl-5U9pks5y$XF$x{dE7ebDv}{p6XX4@$qO`Hugf^f|p)|E2rEfzu43B;3H}MROhAjlW2ResPEmD^09u< zv2GG=@76E9n)0zu(6Me3ZSU3(Zb|uAC+Ju=iMDs^hcBmmtP^ysn?&2Y^~*PQX{?-4({{0_XS^2PFAe+$@q070{rzSAo;#Ay;FonC zx2)ey)g`AF{gb|WLCUxKSkg~v`vu+p*EHsov-VulPigy=-TJZCEc)>)Nk66S2g-WJ zXmvY{A-BpH!zpdw?$)oJp7d*9O8O~nKfPN&K0oQlk0$+;wx3_tub!ZOdsuZA$7hT8 z*R)+Mj$i1WN_y^ipf|_aE_UjfbCknpHuR?LVyAxNwWOc?PSTsUi$y(W@+&8+58#91 zw`XX3^L(oD{I$foJEXWJZd|JPqd9NdE*AZV?!}~^+>rFZ0L+BbTt!-i@58D;+qv`voe(6xsPigzb-TJ=b4*LF!Nk66S*O&F&?}{^csC|pu-&3kB zIlb8ar7My?DE-p2NgtFxrx*3Tq5A6~{S&#t&ifDWKePwgH1 z2RinTp!N@R>>okfJNA#Qdo^Fat9@7OU90ykia)09VzK{t)?L3_u~XdGsH)9gP20ty zezZ^dRnMU6Jvyx4P1`%_&lrtZ`|!>*hEv);Ec;)%Lz?ev-xWt6QNJzMv|TLLe@pcr zX}lqSJ+r0yo3@KZ{nD>X|CGjBbOlziN0MQ7SB7WKP0?{3r|J$@j5Io~#I@AB_+VV@KG z+}h{ZIlWka((wcFi_WyY%m0Sf8|Ooi^IO)CY5UyyRHOaq@B{IS-n6~TKj+-|f%x6Q zd3VDQFX}egQKiosb zKiuJYbHl&yu|oWFzHH(@rx*RR-}vt%CI>Z^*Jv(h+Afy$+ApcU|L$Z5=f%zXpQ=4e z{7B64$p^Qr;q!+()(VpY5Uy$+=iZg zlXVxoDea%@rtJ&m{ztd;uB2xjvj0wL`@358aXyWnbvwInk1o^iAJv>T52tjVt>)r_ z_xpI_CAuzF=;RPOyzGYFYW`7kM_dhRw~D| zT`cO^=lQIi<3jBB%pdlB+r>70v}baQ#`|MQKjlKwGY=?dL*jXdXno zGu8f_-qwFHei84~uAth9p7sW9?`ZG(X&UzrCA~?zXX7UAqI{F@Jy?H4I`cpDJLQ-k zDvx(tT-`WNaq=noFrQBGb0bsppsoEee<-i_DdpGLm`gvE;_j5{kJ-4ad~M}Le@^4J z%B8);Z=|w&9H~y|w+E|1>(B7C|ZO46vAxj#X?+;i+2rf-No~oX?yd$#qlQkkNa>7FX`XqHLD-0|L>|QbLP1%p6ep7 z`oQFl8s(TOuUOydbD{F{jVJgq<9Shbv8dSALTFNyG^EJlH&!vz%9*bAjoca6cw)#)? z?5F5?9z{Q;?Ps>OKaIC^ex5Z)x1};n;y&-sP2L~j$0VNM-&8rX^GsMe zR5_!gXJ~u#eN;ogi}zU#J>P=E9v|O^v#N1?pW2mayI8D0?~MCe50E=qyh}E17d!QF zc0xJKBk4`s#iBmW0iV%$Xiwx`3Fm?4YPO4I{X^R4PLm#fL3;f>rR}G6>v@hrKT+M$ zo3@KZ|AP~>Kc1y_!-vKDXK4FbWqrS&^g-$ScO-pK`kY?$-#eW2LFw67sYg)yoLlNPRAHk77-Gt7-e(eTv5X;~v|Z_D47#iZ9f(T`c>TjyrVdS6@o{DQ&-@tY7$P z&Hn>hukaP|&u3`+frfr{cKsg7w?XCa4r}NuzK`j*6-)T#?|qyBuKl-hdK$IUd3l+QTE{eVAA{s)=ZxkF>dJ-!pw_uS_b zvC}?tps}I-CU(v(P4=F_x6&R2v2(6#61Rz0CieLLt6*_{{WI<3oN4W5t*!fOj#c*L z9QA+Z@Obr|_FuDiQp#^qZ_eAy#Xr{j4ULmI)bBhSnw*32`P}5341ebOYAWC4{t)Gx zYcH$bhgILqiTu+JQ|)OR&zO&VYh9cpJyMf(#JVymd*wXYGC4vd-r^oD&vkJ5G|lrHYuYZh^>@@a&e0UZ;vCJq zN4}rXcxN)sakn;;zT3Je^&y;WOMiY%-Dhg^cP{fe&X_(Y{ifDLChIcp(`0fkM?Q1z zJkIU^P0FJW2mh-4m(=WkK)-)mW0)D|ZLexPIbVaCC#0L)1xIg^k8?efxr-lD{@U92 zUh2bM!1KiLx|+Oqi~D|=s~0H!1?p~<&Gl#|T-jr@`-m2eKuBo!y{73uf*WSL=K9hSg%qNp)MA~Qa z+#mO#GNp_1kL3Ab@GII=9_RVt*RC!+Kg9SE$B5gIcpcw|nQ6P@_fXYZQr7?f@Bdy8 F{5Nsri2DEl From 148521c458daf7540af5a3bd3ca699ff8d0529e3 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 13:48:47 -0700 Subject: [PATCH 070/206] Remove stdc layer --- compiler/base/hierarchy_layout.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index d3b84ca5..4485f622 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -695,8 +695,8 @@ class layout(): # we should add a boundary just for DRC in some technologies if not self.is_library_cell and not self.bounding_box: # If there is a boundary layer, and we didn't create one, add one. - if "stdc" in techlayer.keys(): - boundary_layer = "stdc" + if "boundary" in techlayer.keys(): + boundary_layer = "boundary" boundary = [self.find_lowest_coords(), self.find_highest_coords()] debug.check(boundary[0] and boundary[1], "No shapes to make a boundary.") @@ -1259,10 +1259,7 @@ class layout(): if OPTS.netlist_only: return - if "stdc" in techlayer.keys(): - boundary_layer = "stdc" - else: - boundary_layer = "boundary" + boundary_layer = "boundary" if not ur: self.bounding_box = self.add_rect(layer=boundary_layer, offset=ll, From a28e747a02bad32b46039391c018aa16fd9477be Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 15:28:50 -0700 Subject: [PATCH 071/206] Fix precharge offset. Move well rules to design class. --- compiler/base/design.py | 14 +++++++ compiler/pgates/precharge.py | 8 +--- compiler/pgates/ptx.py | 20 ++------- compiler/tests/04_precharge_1rw_1r_test.py | 47 ++++++++++++++++++++++ 4 files changed, 66 insertions(+), 23 deletions(-) create mode 100755 compiler/tests/04_precharge_1rw_1r_test.py diff --git a/compiler/base/design.py b/compiler/base/design.py index 7a570b28..1fded37e 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -172,6 +172,20 @@ class design(hierarchy_design): self.well_extend_active = max(self.well_extend_active, self.nwell_extend_active) if "pwell" in layer: self.well_extend_active = max(self.well_extend_active, self.pwell_extend_active) + + # The active offset is due to the well extension + if "pwell" in layer: + self.pwell_enclose_active = drc("pwell_enclose_active") + else: + self.pwell_enclose_active = 0 + if "nwell" in layer: + self.nwell_enclose_active = drc("nwell_enclose_active") + else: + self.nwell_enclose_active = 0 + # Use the max of either so that the poly gates will align properly + self.well_enclose_active = max(self.pwell_enclose_active, + self.nwell_enclose_active, + self.active_space) # These are for debugging previous manual rules if False: diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 76fc8676..07b4f21b 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -149,13 +149,9 @@ class precharge(design.design): # Compute the other pmos2 location, # but determining offset to overlap the source and drain pins overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() - # 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 - bl_xoffset = self.bitcell_bl_pin.lx() - self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, - self.nwell_enclose_active), + self.lower_pmos_position = vector(self.well_enclose_active + 0.5 * self.m1_width, self.initial_yoffset) self.lower_pmos_inst.place(self.lower_pmos_position) @@ -218,7 +214,7 @@ class precharge(design.design): # adds the contact from active to metal1 offset_height = self.upper_pmos1_inst.uy() + \ - 0.5 * contact.active_contact.height + \ + contact.active_contact.height + \ self.nwell_extend_active self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \ vector(0, offset_height) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index d52925ec..6fba247e 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -205,29 +205,15 @@ class ptx(design.design): # Poly height must include poly extension over active self.poly_height = self.tx_width + 2 * self.poly_extend_active - # The active offset is due to the well extension - if "pwell" in layer: - pwell_enclose_active = drc("pwell_enclose_active") - else: - pwell_enclose_active = 0 - if "nwell" in layer: - nwell_enclose_active = drc("nwell_enclose_active") - else: - nwell_enclose_active = 0 - # Use the max of either so that the poly gates will align properly - well_enclose_active = max(pwell_enclose_active, - nwell_enclose_active, - self.active_space) - self.active_offset = vector([well_enclose_active] * 2) + self.active_offset = vector([self.well_enclose_active] * 2) # Well enclosure of active, ensure minwidth as well well_name = "{}well".format(self.well_type) if well_name in layer: well_width_rule = drc("minwidth_" + well_name) - well_enclose_active = drc(well_name + "_enclose_active") - self.well_width = max(self.active_width + 2 * well_enclose_active, + self.well_width = max(self.active_width + 2 * self.well_enclose_active, well_width_rule) - self.well_height = max(self.active_height + 2 * well_enclose_active, + self.well_height = max(self.active_height + 2 * self.well_enclose_active, well_width_rule) # We are going to shift the 0,0, so include that in the width and height self.height = self.well_height - self.active_offset.y diff --git a/compiler/tests/04_precharge_1rw_1r_test.py b/compiler/tests/04_precharge_1rw_1r_test.py new file mode 100755 index 00000000..4765c77c --- /dev/null +++ b/compiler/tests/04_precharge_1rw_1r_test.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class precharge_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check precharge array in multi-port + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Checking precharge for 1rw1r port 0") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + factory.reset() + debug.info(2, "Checking precharge for 1rw1r port 1") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1") + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 580b0601b5d637ec2ff06919350972c3536bf251 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 16:04:39 -0700 Subject: [PATCH 072/206] Unskip 20_psram_1bank_4mux_1rw_1r_test --- compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py index 145d1723..ecbd0863 100755 --- a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py +++ b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py @@ -15,7 +15,6 @@ from globals import OPTS from sram_factory import factory import debug -@unittest.skip("SKIPPING 20_psram_1bank_4mux_1rw_1r_test - Matt sucks, don't do this") class psram_1bank_4mux_1rw_1r_test(openram_test): def runTest(self): From fd49d3ed6ae263d873fabbaeae3e2dd935367a56 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 16:09:15 -0700 Subject: [PATCH 073/206] Add tech specific skip tests for making new techs. --- .gitlab-ci.yml | 16 ++++----- compiler/tests/regress.py | 18 ++++++++-- compiler/tests/skip_tests_s8.txt | 56 ++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 compiler/tests/skip_tests_s8.txt diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 27431cb2..02778d35 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,14 +25,14 @@ scn4m_subm: - .coverage.* expire_in: 1 week -# s8: -# stage: test -# script: -# - coverage run -p $OPENRAM_HOME/tests/regress.py -t s8 -# artifacts: -# paths: -# - .coverage.* -# expire_in: 1 week +s8: + stage: test + script: + - coverage run -p $OPENRAM_HOME/tests/regress.py -t s8 + artifacts: + paths: + - .coverage.* + expire_in: 1 week coverage: stage: coverage diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py index e60b010d..80754d0e 100755 --- a/compiler/tests/regress.py +++ b/compiler/tests/regress.py @@ -12,6 +12,7 @@ import unittest import sys,os sys.path.append(os.getenv("OPENRAM_HOME")) import globals +import debug (OPTS, args) = globals.parse_args() del sys.argv[1:] @@ -22,14 +23,25 @@ header(__file__, OPTS.tech_name) # get a list of all files in the tests directory files = os.listdir(sys.path[0]) +# load a file with all tests to skip in a given technology +# since tech_name is dynamically loaded, we can't use @skip directives +try: + skip_file = open("skip_tests_{}.txt".format(OPTS.tech_name), "r") + skip_tests = skip_file.read().splitlines() + for st in skip_tests: + debug.warning("Skipping: " + st) +except FileNotFoundError: + skip_tests = [] + # assume any file that ends in "test.py" in it is a regression test nametest = re.compile("test\.py$", re.IGNORECASE) -tests = list(filter(nametest.search, files)) -tests.sort() +all_tests = list(filter(nametest.search, files)) +filtered_tests = list(filter(lambda i: i not in skip_tests, all_tests)) +filtered_tests.sort() # import all of the modules filenameToModuleName = lambda f: os.path.splitext(f)[0] -moduleNames = map(filenameToModuleName, tests) +moduleNames = map(filenameToModuleName, filtered_tests) modules = map(__import__, moduleNames) suite = unittest.TestSuite() load = unittest.defaultTestLoader.loadTestsFromModule diff --git a/compiler/tests/skip_tests_s8.txt b/compiler/tests/skip_tests_s8.txt new file mode 100644 index 00000000..8e0f1746 --- /dev/null +++ b/compiler/tests/skip_tests_s8.txt @@ -0,0 +1,56 @@ +04_dummy_pbitcell_test.py +04_pbitcell_test.py +04_precharge_pbitcell_test.py +04_replica_pbitcell_test.py +04_single_level_column_mux_pbitcell_test.py +05_pbitcell_array_test.py +06_hierarchical_decoder_pbitcell_test.py +06_hierarchical_predecode2x4_pbitcell_test.py +06_hierarchical_predecode3x8_pbitcell_test.py +07_single_level_column_mux_array_pbitcell_test.py +08_wordline_driver_array_pbitcell_test.py +09_sense_amp_array_test_pbitcell.py +10_write_driver_array_pbitcell_test.py +10_write_driver_array_wmask_pbitcell_test.py +10_write_mask_and_array_pbitcell_test.py +14_replica_pbitcell_array_test.py +19_bank_select_pbitcell_test.py +05_bitcell_1rw_1r_array_test.py +05_bitcell_array_test.py +05_dummy_array_test.py +05_pbitcell_array_test.py +06_hierarchical_decoder_pbitcell_test.py +06_hierarchical_decoder_test.py +06_hierarchical_predecode2x4_pbitcell_test.py +06_hierarchical_predecode2x4_test.py +06_hierarchical_predecode3x8_pbitcell_test.py +06_hierarchical_predecode3x8_test.py +06_hierarchical_predecode4x16_test.py +04_dummy_pbitcell_test.py +04_pbitcell_test.py +04_precharge_pbitcell_test.py +04_replica_pbitcell_test.py +04_single_level_column_mux_pbitcell_test.py +05_bitcell_1rw_1r_array_test.py +05_bitcell_array_test.py +05_dummy_array_test.py +05_pbitcell_array_test.py +05_pbitcell_array_test.py +06_hierarchical_decoder_pbitcell_test.py +06_hierarchical_decoder_pbitcell_test.py +06_hierarchical_decoder_test.py +06_hierarchical_predecode2x4_pbitcell_test.py +06_hierarchical_predecode2x4_pbitcell_test.py +06_hierarchical_predecode2x4_test.py +06_hierarchical_predecode3x8_pbitcell_test.py +06_hierarchical_predecode3x8_pbitcell_test.py +06_hierarchical_predecode3x8_test.py +06_hierarchical_predecode4x16_test.py +07_single_level_column_mux_array_pbitcell_test.py +08_wordline_driver_array_pbitcell_test.py +09_sense_amp_array_test_pbitcell.py +10_write_driver_array_pbitcell_test.py +10_write_driver_array_wmask_pbitcell_test.py +10_write_mask_and_array_pbitcell_test.py +14_replica_pbitcell_array_test.py +19_bank_select_pbitcell_test.py From c6b875146d40b6779f907bffb78141aafed5a261 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 16:33:59 -0700 Subject: [PATCH 074/206] Use local skip file --- compiler/tests/regress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py index 80754d0e..e13d250a 100755 --- a/compiler/tests/regress.py +++ b/compiler/tests/regress.py @@ -12,7 +12,6 @@ import unittest import sys,os sys.path.append(os.getenv("OPENRAM_HOME")) import globals -import debug (OPTS, args) = globals.parse_args() del sys.argv[1:] @@ -26,7 +25,8 @@ files = os.listdir(sys.path[0]) # load a file with all tests to skip in a given technology # since tech_name is dynamically loaded, we can't use @skip directives try: - skip_file = open("skip_tests_{}.txt".format(OPTS.tech_name), "r") + skip_file_name = "skip_tests_{}.txt".format(OPTS.tech_name) + skip_file = open(skip_file_name, "r") skip_tests = skip_file.read().splitlines() for st in skip_tests: debug.warning("Skipping: " + st) From e6babc301daa9956d745d72f4bc3568d8fff62a9 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 16:34:15 -0700 Subject: [PATCH 075/206] Incrase space for pnand gates --- compiler/pgates/pnand2.py | 6 +++--- compiler/pgates/pnand3.py | 11 +++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 51581e61..5816adcf 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -177,9 +177,9 @@ class pnand2(pgate.pgate): # Top of NMOS drain - nmos_pin = self.nmos2_inst.get_pin("D") - bottom_pin_offset = nmos_pin.uy() - self.inputA_yoffset = bottom_pin_offset + self.m1_pitch + bottom_pin = self.nmos2_inst.get_pin("D") + self.inputA_yoffset = max(bottom_pin.uy() + self.m1_pitch, + self.nmos2_inst.uy() + self.poly_to_active) self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 6cbd7cca..fa028246 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -212,10 +212,9 @@ class pnand3(pgate.pgate): pmos_drain_bottom = self.pmos1_inst.get_pin("D").by() self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space - # This is a more compact offset, but the bottom one works better in the decoders to "center" the pins - # in the height of the gates - self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space - # self.inputA_yoffset = self.output_yoffset - self.m1_pitch + bottom_pin = self.nmos1_inst.get_pin("D") + self.inputA_yoffset = max(bottom_pin.uy() + self.m1_pitch, + self.nmos1_inst.uy() + self.poly_to_active) self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, @@ -223,14 +222,14 @@ class pnand3(pgate.pgate): position="left") # Put B right on the well line - self.inputB_yoffset = self.inputA_yoffset - self.m1_pitch + self.inputB_yoffset = self.inputA_yoffset + self.m1_pitch self.route_input_gate(self.pmos2_inst, self.nmos2_inst, self.inputB_yoffset, "B", position="center") - self.inputC_yoffset = self.inputB_yoffset - self.m1_pitch + self.inputC_yoffset = self.inputB_yoffset + self.m1_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, From 14782914b33330181e89d3cd150774d3786ed83b Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 16:40:59 -0700 Subject: [PATCH 076/206] Remove vertical pand gates --- compiler/tests/04_pand2_test.py | 4 ---- compiler/tests/04_pand3_test.py | 4 ---- 2 files changed, 8 deletions(-) diff --git a/compiler/tests/04_pand2_test.py b/compiler/tests/04_pand2_test.py index 21a0a38e..f7e5f304 100755 --- a/compiler/tests/04_pand2_test.py +++ b/compiler/tests/04_pand2_test.py @@ -29,10 +29,6 @@ class pand2_test(openram_test): a = pand2.pand2(name="pand2x4", size=4) self.local_check(a) - debug.info(2, "Testing vertical pand2 gate 4x") - a = pand2.pand2(name="pand2x4", size=4, vertical=True) - self.local_check(a) - globals.end_openram() # instantiate a copdsay of the class to actually run the test diff --git a/compiler/tests/04_pand3_test.py b/compiler/tests/04_pand3_test.py index f851077b..e58f1ee9 100755 --- a/compiler/tests/04_pand3_test.py +++ b/compiler/tests/04_pand3_test.py @@ -29,10 +29,6 @@ class pand3_test(openram_test): a = pand3.pand3(name="pand3x4", size=4) self.local_check(a) - debug.info(2, "Testing vertical pand3 gate 4x") - a = pand3.pand3(name="pand3x4", size=4, vertical=True) - self.local_check(a) - globals.end_openram() # instantiate a copdsay of the class to actually run the test From 064fe34edf55fbb1c358598652d26820d6b86d67 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 17:16:35 -0700 Subject: [PATCH 077/206] Fix pinvbuf layers --- compiler/pgates/pinvbuf.py | 42 ++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index fe376bc4..5b286e9b 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -9,7 +9,7 @@ import debug import pgate from vector import vector from sram_factory import factory - +from tech import layer class pinvbuf(pgate.pgate): """ @@ -111,33 +111,45 @@ class pinvbuf(pgate.pgate): mirror="MX") def route_wires(self): + if "li" in layer: + route_stack = self.li_stack + else: + route_stack = self.m1_stack + # inv1 Z to inv2 A z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") mid_point = vector(z1_pin.cx(), a2_pin.cy()) - self.add_path("m1", [z1_pin.center(), mid_point, a2_pin.center()]) + self.add_path(z1_pin.layer, [z1_pin.center(), mid_point, a2_pin.center()]) + self.add_via_stack_center(from_layer=z1_pin.layer, + to_layer=a2_pin.layer, + offset=a2_pin.center()) # inv2 Z to inv3 A z2_pin = self.inv2_inst.get_pin("Z") a3_pin = self.inv3_inst.get_pin("A") mid_point = vector(z2_pin.cx(), a3_pin.cy()) - self.add_path("m1", [z2_pin.center(), mid_point, a3_pin.center()]) + self.add_path(z2_pin.layer, [z2_pin.center(), mid_point, a3_pin.center()]) + self.add_via_stack_center(from_layer=z2_pin.layer, + to_layer=a3_pin.layer, + offset=a3_pin.center()) # inv1 Z to inv4 A (up and over) z1_pin = self.inv1_inst.get_pin("Z") a4_pin = self.inv4_inst.get_pin("A") mid_point = vector(z1_pin.cx(), a4_pin.cy()) - self.add_wire(self.m1_stack, + self.add_wire(route_stack, [z1_pin.center(), mid_point, a4_pin.center()]) - self.add_via_center(layers=self.m1_stack, - offset=z1_pin.center()) + self.add_via_stack_center(from_layer=z1_pin.layer, + to_layer=route_stack[2], + offset=z1_pin.center()) def add_layout_pins(self): # Continous vdd rail along with label. vdd_pin = self.inv1_inst.get_pin("vdd") self.add_layout_pin(text="vdd", - layer="m1", + layer=vdd_pin.layer, offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) @@ -145,7 +157,7 @@ class pinvbuf(pgate.pgate): # Continous vdd rail along with label. gnd_pin = self.inv4_inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="m1", + layer=gnd_pin.layer, offset=gnd_pin.ll().scale(0, 1), width=self.width, height=gnd_pin.height()) @@ -153,31 +165,25 @@ class pinvbuf(pgate.pgate): # Continous gnd rail along with label. gnd_pin = self.inv1_inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="m1", + layer=gnd_pin.layer, offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) z_pin = self.inv4_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Z", - layer="m2", + layer=z_pin.layer, offset=z_pin.center()) - self.add_via_center(layers=self.m1_stack, - offset=z_pin.center()) zb_pin = self.inv3_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Zb", - layer="m2", + layer=zb_pin.layer, offset=zb_pin.center()) - self.add_via_center(layers=self.m1_stack, - offset=zb_pin.center()) a_pin = self.inv1_inst.get_pin("A") self.add_layout_pin_rect_center(text="A", - layer="m2", + layer=a_pin.layer, offset=a_pin.center()) - self.add_via_center(layers=self.m1_stack, - offset=a_pin.center()) def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False): """Get the stage efforts of the clk -> clk_buf path""" From d4fc88124ad6f9127e9c2179a06a92e166409846 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 9 Jun 2020 17:18:19 -0700 Subject: [PATCH 078/206] Rename dff_buf test --- compiler/tests/{11_dff_buf_test.py => 04_dff_buf_test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler/tests/{11_dff_buf_test.py => 04_dff_buf_test.py} (100%) diff --git a/compiler/tests/11_dff_buf_test.py b/compiler/tests/04_dff_buf_test.py similarity index 100% rename from compiler/tests/11_dff_buf_test.py rename to compiler/tests/04_dff_buf_test.py From c119e60e797a2fecbcd32d2690a9343626269ec1 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jun 2020 10:14:52 -0700 Subject: [PATCH 079/206] Add more s8 skip tests --- .../tests/09_sense_amp_array_1rw_1r_test.py | 48 ++++++++++++ compiler/tests/skip_tests_s8.txt | 78 +++++++++++-------- 2 files changed, 94 insertions(+), 32 deletions(-) create mode 100755 compiler/tests/09_sense_amp_array_1rw_1r_test.py diff --git a/compiler/tests/09_sense_amp_array_1rw_1r_test.py b/compiler/tests/09_sense_amp_array_1rw_1r_test.py new file mode 100755 index 00000000..a8ed4d2f --- /dev/null +++ b/compiler/tests/09_sense_amp_array_1rw_1r_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class sense_amp_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=1") + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=1) + self.local_check(a) + + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2) + self.local_check(a) + + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4") + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/skip_tests_s8.txt b/compiler/tests/skip_tests_s8.txt index 8e0f1746..9f60d65e 100644 --- a/compiler/tests/skip_tests_s8.txt +++ b/compiler/tests/skip_tests_s8.txt @@ -3,18 +3,6 @@ 04_precharge_pbitcell_test.py 04_replica_pbitcell_test.py 04_single_level_column_mux_pbitcell_test.py -05_pbitcell_array_test.py -06_hierarchical_decoder_pbitcell_test.py -06_hierarchical_predecode2x4_pbitcell_test.py -06_hierarchical_predecode3x8_pbitcell_test.py -07_single_level_column_mux_array_pbitcell_test.py -08_wordline_driver_array_pbitcell_test.py -09_sense_amp_array_test_pbitcell.py -10_write_driver_array_pbitcell_test.py -10_write_driver_array_wmask_pbitcell_test.py -10_write_mask_and_array_pbitcell_test.py -14_replica_pbitcell_array_test.py -19_bank_select_pbitcell_test.py 05_bitcell_1rw_1r_array_test.py 05_bitcell_array_test.py 05_dummy_array_test.py @@ -26,31 +14,57 @@ 06_hierarchical_predecode3x8_pbitcell_test.py 06_hierarchical_predecode3x8_test.py 06_hierarchical_predecode4x16_test.py -04_dummy_pbitcell_test.py -04_pbitcell_test.py -04_precharge_pbitcell_test.py -04_replica_pbitcell_test.py -04_single_level_column_mux_pbitcell_test.py -05_bitcell_1rw_1r_array_test.py -05_bitcell_array_test.py -05_dummy_array_test.py -05_pbitcell_array_test.py -05_pbitcell_array_test.py -06_hierarchical_decoder_pbitcell_test.py -06_hierarchical_decoder_pbitcell_test.py -06_hierarchical_decoder_test.py -06_hierarchical_predecode2x4_pbitcell_test.py -06_hierarchical_predecode2x4_pbitcell_test.py -06_hierarchical_predecode2x4_test.py -06_hierarchical_predecode3x8_pbitcell_test.py -06_hierarchical_predecode3x8_pbitcell_test.py -06_hierarchical_predecode3x8_test.py -06_hierarchical_predecode4x16_test.py 07_single_level_column_mux_array_pbitcell_test.py 08_wordline_driver_array_pbitcell_test.py 09_sense_amp_array_test_pbitcell.py 10_write_driver_array_pbitcell_test.py +10_write_driver_array_test.py 10_write_driver_array_wmask_pbitcell_test.py +10_write_driver_array_wmask_test.py 10_write_mask_and_array_pbitcell_test.py +10_write_mask_and_array_test.py 14_replica_pbitcell_array_test.py +18_port_address_test.py +18_port_data_test.py +18_port_data_wmask_test.py 19_bank_select_pbitcell_test.py +20_psram_1bank_2mux_1rw_1w_test.py +20_psram_1bank_2mux_1rw_1w_wmask_test.py +20_psram_1bank_2mux_1w_1r_test.py +20_psram_1bank_2mux_test.py +20_psram_1bank_4mux_1rw_1r_test.py +20_sram_1bank_2mux_1w_1r_test.py +20_sram_1bank_2mux_test.py +20_sram_1bank_2mux_wmask_test.py +20_sram_1bank_32b_1024_wmask_test.py +20_sram_1bank_4mux_test.py +20_sram_1bank_8mux_test.py +20_sram_1bank_nomux_test.py +20_sram_1bank_nomux_wmask_test.py +20_sram_2bank_test.py +21_hspice_delay_test.py +21_hspice_setuphold_test.py +21_model_delay_test.py +21_ngspice_delay_test.py +21_ngspice_setuphold_test.py +22_psram_1bank_2mux_func_test.py +22_psram_1bank_4mux_func_test.py +22_psram_1bank_8mux_func_test.py +22_psram_1bank_nomux_func_test.py +22_sram_1bank_2mux_func_test.py +22_sram_1bank_4mux_func_test.py +22_sram_1bank_8mux_func_test.py +22_sram_1bank_nomux_func_test.py +22_sram_1rw_1r_1bank_nomux_func_test.py +22_sram_wmask_func_test.py +23_lib_sram_model_corners_test.py +23_lib_sram_model_test.py +23_lib_sram_prune_test.py +23_lib_sram_test.py +24_lef_sram_test.py +25_verilog_sram_test.py +26_hspice_pex_pinv_test.py +26_ngspice_pex_pinv_test.py +26_pex_test.py +30_openram_back_end_test.py +30_openram_front_end_test.py From 5e3332453b2925c689ac2c034cb1a65a10b0cc16 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jun 2020 10:15:23 -0700 Subject: [PATCH 080/206] Allow power pins to start on any layer besides m1 --- compiler/base/hierarchy_layout.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 4485f622..362c0f9e 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1304,13 +1304,10 @@ class layout(): pin.ll(), pin.width(), pin.height()) - elif pin.layer == "m1": - self.add_power_pin(name, pin.center()) - else: - debug.warning("{0} pins of {1} should be on {2} or metal1 for "\ - "supply router." - .format(name, inst.name, self.pwr_grid_layer)) + else: + self.add_power_pin(name, pin.center(), start_layer=pin.layer) + def add_power_pin(self, name, loc, size=[1, 1], directions=None, start_layer="m1"): """ Add a single power pin from the lowest power_grid layer down to M1 (or li) at From 10be2d08b5eac5e532e14366f5d143bb479337ba Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jun 2020 10:23:05 -0700 Subject: [PATCH 081/206] Full path to skip tests file --- compiler/tests/regress.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py index e13d250a..3fd5d6eb 100755 --- a/compiler/tests/regress.py +++ b/compiler/tests/regress.py @@ -25,7 +25,7 @@ files = os.listdir(sys.path[0]) # load a file with all tests to skip in a given technology # since tech_name is dynamically loaded, we can't use @skip directives try: - skip_file_name = "skip_tests_{}.txt".format(OPTS.tech_name) + skip_file_name = "{0}/tests/skip_tests_{1}.txt".format(os.getenv("OPENRAM_HOME"), OPTS.tech_name) skip_file = open(skip_file_name, "r") skip_tests = skip_file.read().splitlines() for st in skip_tests: From f2c45a230ed929e1d08b7e9fee5c020490a44fe3 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jun 2020 11:00:00 -0700 Subject: [PATCH 082/206] Add new replica column test. Add more skip tests. --- .../tests/14_replica_column_1rw_1r_test.py | 46 +++++++++++++++++++ compiler/tests/skip_tests_s8.txt | 17 +++++++ 2 files changed, 63 insertions(+) create mode 100755 compiler/tests/14_replica_column_1rw_1r_test.py diff --git a/compiler/tests/14_replica_column_1rw_1r_test.py b/compiler/tests/14_replica_column_1rw_1r_test.py new file mode 100755 index 00000000..3c1a0e1e --- /dev/null +++ b/compiler/tests/14_replica_column_1rw_1r_test.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class replica_column_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing replica column for 6t_cell") + a = factory.create(module_type="replica_column", rows=4, left_rbl=1, right_rbl=0, replica_bit=1) + self.local_check(a) + + debug.info(2, "Testing replica column for 6t_cell") + a = factory.create(module_type="replica_column", rows=4, left_rbl=1, right_rbl=1, replica_bit=6) + self.local_check(a) + + debug.info(2, "Testing replica column for 6t_cell") + a = factory.create(module_type="replica_column", rows=4, left_rbl=2, right_rbl=0, replica_bit=2) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/skip_tests_s8.txt b/compiler/tests/skip_tests_s8.txt index 9f60d65e..68a6549b 100644 --- a/compiler/tests/skip_tests_s8.txt +++ b/compiler/tests/skip_tests_s8.txt @@ -16,18 +16,35 @@ 06_hierarchical_predecode4x16_test.py 07_single_level_column_mux_array_pbitcell_test.py 08_wordline_driver_array_pbitcell_test.py +08_wordline_driver_array_test.py 09_sense_amp_array_test_pbitcell.py +09_sense_amp_array_test.py 10_write_driver_array_pbitcell_test.py 10_write_driver_array_test.py 10_write_driver_array_wmask_pbitcell_test.py 10_write_driver_array_wmask_test.py 10_write_mask_and_array_pbitcell_test.py 10_write_mask_and_array_test.py +12_tri_gate_array_test.py 14_replica_pbitcell_array_test.py +14_replica_bitcell_array_test.py +14_replica_column_test.py +14_replica_column_1rw_1r_test.py 18_port_address_test.py 18_port_data_test.py 18_port_data_wmask_test.py 19_bank_select_pbitcell_test.py +19_bank_select_test.py +19_psingle_bank_test.py +19_bank_select_pbitcell_test.py +19_pmulti_bank_test.py +19_multi_bank_test.py +19_psingle_bank_test.py +19_single_bank_1w_1r_test.py +19_single_bank_wmask_1rw_1r_test.py +19_single_bank_1rw_1r_test.py +19_single_bank_test.py +19_single_bank_wmask_test.py 20_psram_1bank_2mux_1rw_1w_test.py 20_psram_1bank_2mux_1rw_1w_wmask_test.py 20_psram_1bank_2mux_1w_1r_test.py From 469cd260b954cd191a9291eca69e61170dbf2bea Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jun 2020 14:54:20 -0700 Subject: [PATCH 083/206] Change bitcell array name to match --- ...ell_1rw_1r_array_test.py => 05_bitcell_array_1rw_1r_test.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename compiler/tests/{05_bitcell_1rw_1r_array_test.py => 05_bitcell_array_1rw_1r_test.py} (96%) diff --git a/compiler/tests/05_bitcell_1rw_1r_array_test.py b/compiler/tests/05_bitcell_array_1rw_1r_test.py similarity index 96% rename from compiler/tests/05_bitcell_1rw_1r_array_test.py rename to compiler/tests/05_bitcell_array_1rw_1r_test.py index 0683d127..0e7d5665 100755 --- a/compiler/tests/05_bitcell_1rw_1r_array_test.py +++ b/compiler/tests/05_bitcell_array_1rw_1r_test.py @@ -17,7 +17,7 @@ import debug #@unittest.skip("SKIPPING 05_bitcell_1rw_1r_array_test") -class bitcell_1rw_1r_array_test(openram_test): +class bitcell_array_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) From bfd1abc79f738177c393b0137d5455bcc9d5cf2b Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jun 2020 14:58:55 -0700 Subject: [PATCH 084/206] Replica column pins start at 0 height. --- compiler/modules/replica_column.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index afe87497..603bebfe 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -154,7 +154,7 @@ class replica_column(design.design): bl_pin = self.cell_inst[0].get_pin(bl_name) self.add_layout_pin(text=bl_name, layer=bl_pin.layer, - offset=bl_pin.ll(), + offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) From 78c66d7c3453b7bca2774012998381fd3e892c73 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jun 2020 15:00:29 -0700 Subject: [PATCH 085/206] Disable CI for s8 to commit to dev --- .gitlab-ci.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 02778d35..27431cb2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,14 +25,14 @@ scn4m_subm: - .coverage.* expire_in: 1 week -s8: - stage: test - script: - - coverage run -p $OPENRAM_HOME/tests/regress.py -t s8 - artifacts: - paths: - - .coverage.* - expire_in: 1 week +# s8: +# stage: test +# script: +# - coverage run -p $OPENRAM_HOME/tests/regress.py -t s8 +# artifacts: +# paths: +# - .coverage.* +# expire_in: 1 week coverage: stage: coverage From 0b4b5e713361277beb10cd69e51cab1d4e33afc6 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jun 2020 16:19:24 -0700 Subject: [PATCH 086/206] More exact input spacing in pnand3 --- compiler/pgates/pnand3.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index fa028246..31024d82 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -12,7 +12,7 @@ from vector import vector import logical_effort from sram_factory import factory from globals import OPTS - +import contact class pnand3(pgate.pgate): """ @@ -209,12 +209,21 @@ class pnand3(pgate.pgate): def route_inputs(self): """ Route the A and B and C inputs """ + # We can use this pitch because the contacts and overlap won't be adjacent + non_contact_pitch = 0.5 * self.m1_width + self.m1_space + 0.5 * contact.poly_contact.second_layer_height pmos_drain_bottom = self.pmos1_inst.get_pin("D").by() self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space bottom_pin = self.nmos1_inst.get_pin("D") - self.inputA_yoffset = max(bottom_pin.uy() + self.m1_pitch, - self.nmos1_inst.uy() + self.poly_to_active) + # active contact metal to poly contact metal spacing + active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * contact.poly_contact.second_layer_height + # active diffusion to poly contact spacing + # doesn't use nmos uy because that is calculated using offset + poly height + active_to_poly_contact = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height \ + + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height + + self.inputA_yoffset = max(active_contact_to_poly_contact, + active_to_poly_contact) self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, @@ -222,14 +231,14 @@ class pnand3(pgate.pgate): position="left") # Put B right on the well line - self.inputB_yoffset = self.inputA_yoffset + self.m1_pitch + self.inputB_yoffset = self.inputA_yoffset + non_contact_pitch self.route_input_gate(self.pmos2_inst, self.nmos2_inst, self.inputB_yoffset, "B", position="center") - self.inputC_yoffset = self.inputB_yoffset + self.m1_pitch + self.inputC_yoffset = self.inputB_yoffset + non_contact_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, From fdf92d0da1fdcc88b8c0119600e80aed8ad534b5 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jun 2020 16:41:26 -0700 Subject: [PATCH 087/206] Rename test 14 --- ...1r_array_test.py => 14_replica_bitcell_array_1rw_1r_test.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename compiler/tests/{14_replica_bitcell_1rw_1r_array_test.py => 14_replica_bitcell_array_1rw_1r_test.py} (95%) diff --git a/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py similarity index 95% rename from compiler/tests/14_replica_bitcell_1rw_1r_array_test.py rename to compiler/tests/14_replica_bitcell_array_1rw_1r_test.py index 3f869255..a36fc80f 100755 --- a/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py +++ b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py @@ -13,7 +13,7 @@ from globals import OPTS from sram_factory import factory import debug -class replica_bitcell_array_test(openram_test): +class replica_bitcell_array_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) From 196e5998c8753f410bf31a01dc351231cdda4a99 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 10 Jun 2020 16:52:51 -0700 Subject: [PATCH 088/206] Half poly space per cell at top and bottom. --- compiler/pgates/pgate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index a8c45641..7ff73092 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -46,7 +46,8 @@ class pgate(design.design): # This is the space from a S/D contact to the supply rail contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space # This is a poly-to-poly of a flipped cell - poly_to_poly_gate_space = self.poly_extend_active + self.poly_space + poly_to_poly_gate_space = self.poly_extend_active + 0.5 * self.poly_space + self.top_bottom_space = max(contact_to_vdd_rail_space, poly_to_poly_gate_space) From f973dd6a5cbc0d7d28df9b1640471909ec8ba0a2 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 11 Jun 2020 11:53:34 -0700 Subject: [PATCH 089/206] Save LVS model with no u too for Calibre --- compiler/pgates/ptx.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 6fba247e..d6aa4235 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -127,12 +127,12 @@ class ptx(design.design): area_sd = 2.5 * self.poly_width * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width if OPTS.tech_name == "s8": - # s8 technology is in microns + # s8 technology is in microns, also needs mult parameter (self.tx_width, self.mults) = pgate.bin_width(self.tx_type, self.tx_width) - main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly")) + main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3}".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) # Perimeters are in microns # Area is in u since it is microns square area_str = "pd={0:.2f} ps={0:.2f} as={1:.2f}u ad={1:.2f}u".format(perimeter_sd, @@ -149,11 +149,17 @@ class ptx(design.design): # LVS lib is always in SI units if os.path.exists(OPTS.openram_tech + "lvs_lib"): - self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly")) - + if OPTS.tech_name == "s8": + # s8 requires mult parameter too + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) + else: + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) def setup_layout_constants(self): """ From 098219d56c9bf8127ba45dc69739f7ebc494595c Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 11 Jun 2020 11:53:59 -0700 Subject: [PATCH 090/206] Add npc enclosure to poly contacts --- compiler/base/contact.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 14dbf76e..cc1ca27a 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -7,7 +7,7 @@ # import hierarchy_design import debug -from tech import drc +from tech import drc, layer import tech from vector import vector from sram_factory import factory @@ -44,7 +44,8 @@ class contact(hierarchy_design.hierarchy_design): self.add_comment("well_type: {}\n".format(well_type)) self.is_well_contact = implant_type == well_type - + + # If we have a special tap layer, use it self.layer_stack = layer_stack self.dimensions = dimensions @@ -196,17 +197,19 @@ class contact(hierarchy_design.hierarchy_design): if "npc" not in tech.layer: return + npc_enclose_poly = drc("npc_enclose_poly") + npc_enclose_offset = vector(npc_enclose_poly, npc_enclose_poly) # Only add for poly layers if self.first_layer_name == "poly": self.add_rect(layer="npc", - offset=self.first_layer_position, - width=self.first_layer_width, - height=self.first_layer_height) + offset=self.first_layer_position - npc_enclose_offset, + width=self.first_layer_width + 2 * npc_enclose_poly, + height=self.first_layer_height + 2 * npc_enclose_poly) elif self.second_layer_name == "poly": self.add_rect(layer="npc", - offset=self.second_layer_position, - width=self.second_layer_width, - height=self.second_layer_height) + offset=self.second_layer_position - npc_enclose_offset, + width=self.second_layer_width + 2 * npc_enclose_poly, + height=self.second_layer_height + 2 * npc_enclose_poly) def create_first_layer_enclosure(self): # this is if the first and second layers are different @@ -218,7 +221,11 @@ class contact(hierarchy_design.hierarchy_design): self.first_layer_minwidth) self.first_layer_height = max(self.contact_array_height + 2 * self.first_layer_vertical_enclosure, self.first_layer_minwidth) - self.add_rect(layer=self.first_layer_name, + if self.is_well_contact and self.first_layer_name == "active" and "tap" in layer: + first_layer_name = "tap" + else: + first_layer_name = self.first_layer_name + self.add_rect(layer=first_layer_name, offset=self.first_layer_position, width=self.first_layer_width, height=self.first_layer_height) From 089331ced3ca8fc55e680c99af4dff090115fcef Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 11 Jun 2020 11:54:16 -0700 Subject: [PATCH 091/206] Add stdc bounding box too --- compiler/base/hierarchy_layout.py | 45 +++++++++++++++++++------------ 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 362c0f9e..244d7842 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -695,14 +695,19 @@ class layout(): # we should add a boundary just for DRC in some technologies if not self.is_library_cell and not self.bounding_box: # If there is a boundary layer, and we didn't create one, add one. + boundary_layers = [] if "boundary" in techlayer.keys(): - boundary_layer = "boundary" - boundary = [self.find_lowest_coords(), - self.find_highest_coords()] - debug.check(boundary[0] and boundary[1], "No shapes to make a boundary.") + boundary_layers.append("boundary") + if "stdc" in techlayer.keys(): + boundary_layers.append("stdc") + boundary = [self.find_lowest_coords(), + self.find_highest_coords()] + debug.check(boundary[0] and boundary[1], "No shapes to make a boundary.") - height = boundary[1][1] - boundary[0][1] - width = boundary[1][0] - boundary[0][0] + height = boundary[1][1] - boundary[0][1] + width = boundary[1][0] - boundary[0][0] + + for boundary_layer in boundary_layers: (layer_number, layer_purpose) = techlayer[boundary_layer] gds_layout.addBox(layerNumber=layer_number, purposeNumber=layer_purpose, @@ -1259,17 +1264,23 @@ class layout(): if OPTS.netlist_only: return - boundary_layer = "boundary" - if not ur: - self.bounding_box = self.add_rect(layer=boundary_layer, - offset=ll, - height=self.height, - width=self.width) - else: - self.bounding_box = self.add_rect(layer=boundary_layer, - offset=ll, - height=ur.y - ll.y, - width=ur.x - ll.x) + boundary_layers = [] + if "stdc" in techlayer.keys(): + boundary_layers.append("stdc") + if "boundary" in techlayer.keys(): + boundary_layers.append("boundary") + # Save the last one as self.bounding_box + for boundary_layer in boundary_layers: + if not ur: + self.bounding_box = self.add_rect(layer=boundary_layer, + offset=ll, + height=self.height, + width=self.width) + else: + self.bounding_box = self.add_rect(layer=boundary_layer, + offset=ll, + height=ur.y - ll.y, + width=ur.x - ll.x) def add_enclosure(self, insts, layer="nwell"): """ Add a layer that surrounds the given instances. Useful From 1a2e0046b103007cf7d40a4b757424866033cddf Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 11 Jun 2020 11:54:34 -0700 Subject: [PATCH 092/206] Add contact to gate spacing for precharge --- compiler/pgates/precharge.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 07b4f21b..f366e5b4 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -195,7 +195,10 @@ class precharge(design.design): # adds the en contact to connect the gates to the en rail pin_offset = self.lower_pmos_inst.get_pin("G").lr() # This is an extra space down for some techs with contact to active spacing - offset = pin_offset - vector(0, self.poly_space) + contact_space = max(self.poly_space, + self.contact_to_gate) + 0.5 * contact.poly_contact.first_layer_height + print(self.contact_to_gate) + offset = pin_offset - vector(0, contact_space) self.add_via_stack_center(from_layer="poly", to_layer=self.en_layer, offset=offset) From 7ad2d54a69e008aa7d65acce25618fdd5131cc15 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 11 Jun 2020 11:54:51 -0700 Subject: [PATCH 093/206] Add pin and label purposes --- compiler/base/pin_layout.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index f18956a8..f758a903 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -367,8 +367,17 @@ class pin_layout: + str(self.width()) + "x" + str(self.height()) + " @ " + str(self.ll())) (layer_num, purpose) = layer[self.layer] + try: + from tech import pin_purpose + except ImportError: + pin_purpose = purpose + try: + from tech import label_purpose + except ImportError: + label_purpose = purpose + newLayout.addBox(layerNumber=layer_num, - purposeNumber=purpose, + purposeNumber=pin_purpose, offsetInMicrons=self.ll(), width=self.width(), height=self.height(), @@ -378,7 +387,7 @@ class pin_layout: # imported into Magic. newLayout.addText(text=self.name, layerNumber=layer_num, - purposeNumber=purpose, + purposeNumber=label_purpose, offsetInMicrons=self.center(), magnification=GDS["zoom"], rotate=None) From 8d52794c32f5bbccd24082e09b4a6eddbb5b729e Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 11 Jun 2020 11:57:52 -0700 Subject: [PATCH 094/206] Remove printf in precharge --- compiler/pgates/precharge.py | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index f366e5b4..0a948ff8 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -197,7 +197,6 @@ class precharge(design.design): # This is an extra space down for some techs with contact to active spacing contact_space = max(self.poly_space, self.contact_to_gate) + 0.5 * contact.poly_contact.first_layer_height - print(self.contact_to_gate) offset = pin_offset - vector(0, contact_space) self.add_via_stack_center(from_layer="poly", to_layer=self.en_layer, From e9780ea599567c7c94aa595d51eb5818511044ca Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 11 Jun 2020 15:03:36 -0700 Subject: [PATCH 095/206] Add non-preferred directions for channel routes --- compiler/base/hierarchy_layout.py | 17 ++++++++++++++--- compiler/modules/bank.py | 11 +++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 244d7842..4b6c044d 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1043,7 +1043,8 @@ class layout(): mid = vector(pin.center().x, trunk_offset.y) self.add_path(self.vertical_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, - offset=mid) + offset=mid, + directions=self.directions) def add_vertical_trunk_route(self, pins, @@ -1083,7 +1084,8 @@ class layout(): mid = vector(trunk_offset.x, pin.center().y) self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, - offset=mid) + offset=mid, + directions=self.directions) def create_channel_route(self, netlist, offset, @@ -1146,7 +1148,8 @@ class layout(): overlaps = (not vertical and x_overlap) or (vertical and y_overlap) return overlaps - if not directions: + self.directions = directions + if not directions or directions == "pref": # Use the preferred layer directions if self.get_preferred_direction(layer_stack[0]) == "V": self.vertical_layer = layer_stack[0] @@ -1154,6 +1157,14 @@ class layout(): else: self.vertical_layer = layer_stack[2] self.horizontal_layer = layer_stack[0] + elif directions == "nonpref": + # Use the preferred layer directions + if self.get_preferred_direction(layer_stack[0]) == "V": + self.vertical_layer = layer_stack[2] + self.horizontal_layer = layer_stack[0] + else: + self.vertical_layer = layer_stack[0] + self.horizontal_layer = layer_stack[2] else: # Use the layer directions specified to the router rather than # the preferred directions diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index af14c0ca..12fcb6dd 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -9,7 +9,7 @@ import debug import design from sram_factory import factory from math import log, ceil -from tech import drc +from tech import drc, layer from vector import vector from globals import OPTS @@ -879,9 +879,16 @@ class bank(design.design): column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names] route_map = list(zip(decode_pins, column_mux_pins)) + if "li" in layer: + stack = self.li_stack + directions = "nonpref" + else: + stack = self.m1_stack + directions = "pref" self.create_vertical_channel_route(route_map, offset, - self.m1_stack) + stack, + directions=directions) def add_lvs_correspondence_points(self): """ From 54e4d147f614abdae8d16d27682c46426a0b2365 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 11 Jun 2020 15:03:50 -0700 Subject: [PATCH 096/206] Rail to ptx spacing based on routing layer not m1 --- compiler/pgates/pgate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 67ba4a18..da359174 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -44,7 +44,7 @@ class pgate(design.design): self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer)) # This is the space from a S/D contact to the supply rail - contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space + contact_to_vdd_rail_space = 0.5 * self.route_layer_width + self.route_layer_space # This is a poly-to-poly of a flipped cell poly_to_poly_gate_space = self.poly_extend_active + 0.5 * self.poly_space From 443b8fbe23b7738bc1866a1aa102dd37e030c941 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 12 Jun 2020 14:23:26 -0700 Subject: [PATCH 097/206] Change s8 to sky130 --- compiler/base/hierarchy_layout.py | 2 +- compiler/characterizer/stimuli.py | 2 +- compiler/custom/and2_dec.py | 4 ++-- compiler/custom/and3_dec.py | 4 ++-- compiler/custom/and4_dec.py | 4 ++-- compiler/globals.py | 3 +++ compiler/modules/bitcell_base_array.py | 2 +- compiler/modules/hierarchical_decoder.py | 4 ++-- compiler/modules/hierarchical_predecode.py | 8 ++++---- compiler/modules/port_data.py | 4 ++-- compiler/modules/wordline_driver_array.py | 2 +- compiler/pgates/pgate.py | 2 +- compiler/pgates/pinv.py | 6 +++--- compiler/pgates/pinv_dec.py | 6 +++--- compiler/pgates/pnand2.py | 2 +- compiler/pgates/pnand3.py | 2 +- compiler/pgates/pnor2.py | 2 +- compiler/pgates/precharge.py | 2 +- compiler/pgates/ptx.py | 8 ++++---- compiler/pgates/wordline_driver.py | 4 ++-- compiler/verify/magic.py | 2 +- 21 files changed, 39 insertions(+), 36 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 4b6c044d..3f77c41d 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1348,7 +1348,7 @@ class layout(): offset=loc, directions=directions) # Hack for min area - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": width = round_to_grid(sqrt(drc["minarea_m3"])) height = round_to_grid(drc["minarea_m3"]/width) else: diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index b5a143cf..e5f225dc 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -254,7 +254,7 @@ class stimuli(): includes = self.device_models + [circuit] self.sf.write("* {} process corner\n".format(self.process)) - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": libraries = self.device_libraries for item in list(libraries): if os.path.isfile(item[0]): diff --git a/compiler/custom/and2_dec.py b/compiler/custom/and2_dec.py index b764bb83..e6f314c4 100644 --- a/compiler/custom/and2_dec.py +++ b/compiler/custom/and2_dec.py @@ -87,7 +87,7 @@ class and2_dec(design.design): def route_supply_rails(self): """ Add vdd/gnd rails to the top, (middle), and bottom. """ - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": for name in ["vdd", "gnd"]: for inst in [self.nand_inst, self.inv_inst]: self.copy_layout_pin(inst, name) @@ -105,7 +105,7 @@ class and2_dec(design.design): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") a2_pin = self.inv_inst.get_pin("A") - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": mid1_point = vector(a2_pin.cx(), z1_pin.cy()) else: mid1_point = vector(z1_pin.cx(), a2_pin.cy()) diff --git a/compiler/custom/and3_dec.py b/compiler/custom/and3_dec.py index 89cc84f8..207d545b 100644 --- a/compiler/custom/and3_dec.py +++ b/compiler/custom/and3_dec.py @@ -86,7 +86,7 @@ class and3_dec(design.design): def route_supply_rails(self): """ Add vdd/gnd rails to the top, (middle), and bottom. """ - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": for name in ["vdd", "gnd"]: for inst in [self.nand_inst, self.inv_inst]: self.copy_layout_pin(inst, name) @@ -104,7 +104,7 @@ class and3_dec(design.design): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") a2_pin = self.inv_inst.get_pin("A") - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": mid1_point = vector(a2_pin.cx(), z1_pin.cy()) else: mid1_point = vector(z1_pin.cx(), a2_pin.cy()) diff --git a/compiler/custom/and4_dec.py b/compiler/custom/and4_dec.py index f99c048f..9c68f78b 100644 --- a/compiler/custom/and4_dec.py +++ b/compiler/custom/and4_dec.py @@ -89,7 +89,7 @@ class and4_dec(design.design): def route_supply_rails(self): """ Add vdd/gnd rails to the top, (middle), and bottom. """ - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": for name in ["vdd", "gnd"]: for inst in [self.nand_inst, self.inv_inst]: self.copy_layout_pin(inst, name) @@ -107,7 +107,7 @@ class and4_dec(design.design): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") a2_pin = self.inv_inst.get_pin("A") - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": mid1_point = vector(a2_pin.cx(), z1_pin.cy()) else: mid1_point = vector(z1_pin.cx(), a2_pin.cy()) diff --git a/compiler/globals.py b/compiler/globals.py index eea73c6a..a192ebc9 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -99,6 +99,9 @@ def parse_args(): # Alias SCMOS to 180nm if OPTS.tech_name == "scmos": OPTS.tech_name = "scn4m_subm" + # Alias s8 to sky130 + if OPTS.tech_name == "s8": + OPTS.tech_name = "sky130" return (options, args) diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 9b46a192..e601208b 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -102,7 +102,7 @@ class bitcell_base_array(design.design): height=wl_pin.height()) # For non-square via stacks, vertical/horizontal direction refers to the stack orientation in 2d space - # Default uses prefered directions for each layer; this cell property is only currently used by s8 tech (03/20) + # Default uses prefered directions for each layer; this cell property is only currently used by sky130 tech (03/20) try: bitcell_power_pin_directions = cell_properties.bitcell_power_pin_directions except AttributeError: diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index bb2b37ab..c4a4cd68 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -159,7 +159,7 @@ class hierarchical_decoder(design.design): # Inputs to cells are on input layer # Outputs from cells are on output layer - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": self.bus_layer = "m1" self.bus_directions = "nonpref" self.bus_pitch = self.m1_pitch @@ -525,7 +525,7 @@ class hierarchical_decoder(design.design): must-connects next level up. """ - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": for n in ["vdd", "gnd"]: pins = self.and_inst[0].get_pins(n) for pin in pins: diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 03d866fb..44075656 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -78,7 +78,7 @@ class hierarchical_predecode(design.design): # Inputs to cells are on input layer # Outputs from cells are on output layer - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": self.bus_layer = "m1" self.bus_directions = None self.bus_pitch = self.m1_pitch @@ -239,7 +239,7 @@ class hierarchical_predecode(design.design): # add output so that it is just below the vdd or gnd rail # since this is where the p/n devices are and there are no # pins in the and gates. - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": rail_pos = vector(self.decode_rails[out_pin].cx(), inv_out_pos.y) self.add_path(self.output_layer, [inv_out_pos, rail_pos]) else: @@ -309,8 +309,8 @@ class hierarchical_predecode(design.design): def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - # In s8, we use hand-made decoder cells with vertical power - if OPTS.tech_name == "s8": + # In sky130, we use hand-made decoder cells with vertical power + if OPTS.tech_name == "sky130": for n in ["vdd", "gnd"]: # This makes a wire from top to bottom for both inv and and gates for i in [self.inv_inst, self.and_inst]: diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 3f0505d5..e370b891 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -588,7 +588,7 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - elif OPTS.tech_name == "s8": + elif OPTS.tech_name == "sky130": self.connect_bitlines(inst1=inst1, inst1_bls_template=inst1_bls_templ, inst2=inst2, @@ -646,7 +646,7 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - elif OPTS.tech_name == "s8": + elif OPTS.tech_name == "sky130": self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, inst1_bls_template=inst1_bls_templ, diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index e3d7c4f9..e8a3c110 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -68,7 +68,7 @@ class wordline_driver_array(design.design): Add a pin for each row of vdd/gnd which are must-connects next level up. """ - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": for name in ["vdd", "gnd"]: supply_pins = self.wld_inst[0].get_pins(name) for pin in supply_pins: diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index da359174..09074960 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -14,7 +14,7 @@ from tech import layer, drc from vector import vector from globals import OPTS -if(OPTS.tech_name == "s8"): +if(OPTS.tech_name == "sky130"): from tech import nmos_bins, pmos_bins, accuracy_requirement diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 5e1aab7b..de0ba8e4 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -19,7 +19,7 @@ import logical_effort from sram_factory import factory from errors import drc_error -if(OPTS.tech_name == "s8"): +if(OPTS.tech_name == "sky130"): from tech import nmos_bins, pmos_bins, accuracy_requirement @@ -88,7 +88,7 @@ class pinv(pgate.pgate): self.tx_mults = 1 self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) return @@ -133,7 +133,7 @@ class pinv(pgate.pgate): # Determine the number of mults for each to fit width # into available space - if OPTS.tech_name != "s8": + if OPTS.tech_name != "sky130": self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") nmos_required_mults = max(int(ceil(self.nmos_width / nmos_height_available)), 1) diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py index efc21074..f12a620b 100644 --- a/compiler/pgates/pinv_dec.py +++ b/compiler/pgates/pinv_dec.py @@ -13,7 +13,7 @@ from vector import vector from globals import OPTS from sram_factory import factory -if(OPTS.tech_name == "s8"): +if(OPTS.tech_name == "sky130"): from tech import nmos_bins, pmos_bins, accuracy_requirement @@ -36,7 +36,7 @@ class pinv_dec(pinv.pinv): # Inputs to cells are on input layer # Outputs from cells are on output layer - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": self.supply_layer = "m1" else: self.supply_layer = "m2" @@ -53,7 +53,7 @@ class pinv_dec(pinv.pinv): self.tx_mults = 1 self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) return diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 5816adcf..3a9e2db7 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -37,7 +37,7 @@ class pnand2(pgate.pgate): debug.check(size == 1, "Size 1 pnand2 is only supported now.") self.tx_mults = 1 - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 31024d82..3a9d4221 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -40,7 +40,7 @@ class pnand3(pgate.pgate): "Size 1 pnand3 is only supported now.") self.tx_mults = 1 - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 2126e86c..3cceb7f4 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -37,7 +37,7 @@ class pnor2(pgate.pgate): debug.check(size==1, "Size 1 pnor2 is only supported now.") self.tx_mults = 1 - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 0a948ff8..fdce1a35 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -86,7 +86,7 @@ class precharge(design.design): """ Initializes the upper and lower pmos """ - if(OPTS.tech_name == "s8"): + if(OPTS.tech_name == "sky130"): (self.ptx_width, self.ptx_mults) = pgate.bin_width("pmos", self.ptx_width) self.pmos = factory.create(module_type="ptx", width=self.ptx_width, diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index d6aa4235..fb215d78 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -126,8 +126,8 @@ class ptx(design.design): # be decided in the layout later. area_sd = 2.5 * self.poly_width * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width - if OPTS.tech_name == "s8": - # s8 technology is in microns, also needs mult parameter + if OPTS.tech_name == "sky130": + # sky130 technology is in microns, also needs mult parameter (self.tx_width, self.mults) = pgate.bin_width(self.tx_type, self.tx_width) main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3}".format(spice[self.tx_type], self.mults, @@ -149,8 +149,8 @@ class ptx(design.design): # LVS lib is always in SI units if os.path.exists(OPTS.openram_tech + "lvs_lib"): - if OPTS.tech_name == "s8": - # s8 requires mult parameter too + if OPTS.tech_name == "sky130": + # sky130 requires mult parameter too self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type], self.mults, self.tx_width, diff --git a/compiler/pgates/wordline_driver.py b/compiler/pgates/wordline_driver.py index a817941b..c8cf1326 100644 --- a/compiler/pgates/wordline_driver.py +++ b/compiler/pgates/wordline_driver.py @@ -89,7 +89,7 @@ class wordline_driver(design.design): def route_supply_rails(self): """ Add vdd/gnd rails to the top, (middle), and bottom. """ - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": for name in ["vdd", "gnd"]: for inst in [self.nand_inst, self.driver_inst]: self.copy_layout_pin(inst, name) @@ -110,7 +110,7 @@ class wordline_driver(design.design): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") a2_pin = self.driver_inst.get_pin("A") - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": mid1_point = vector(a2_pin.cx(), z1_pin.cy()) else: mid1_point = vector(z1_pin.cx(), a2_pin.cy()) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 1d3562cc..59346ace 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -68,7 +68,7 @@ def write_magic_script(cell_name, extract=False, final_verification=False): if final_verification: f.write(pre + "extract unique all\n".format(cell_name)) # Hack to work around unit scales in SkyWater - if OPTS.tech_name=="s8": + if OPTS.tech_name=="sky130": f.write(pre + "extract style ngspice(si)\n") f.write(pre + "extract\n".format(cell_name)) # f.write(pre + "ext2spice hierarchy on\n") From 33a32101c9831465ed0e98cd7251acc77ead348a Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 12 Jun 2020 15:23:51 -0700 Subject: [PATCH 098/206] DRC and LVS fixes for pinv_dec --- compiler/base/hierarchy_design.py | 3 +- compiler/modules/dff_buf.py | 8 +++--- compiler/pgates/pinv_dec.py | 40 +++++++++++++++++++++------ compiler/pgates/ptx.py | 31 +++++++++++---------- compiler/tests/04_pinv_dec_1x_test.py | 5 ++++ 5 files changed, 60 insertions(+), 27 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index bb57ae3e..87331315 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -27,7 +27,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # If we have a separate lvs directory, then all the lvs files # should be in there (all or nothing!) lvs_dir = OPTS.openram_tech + "lvs_lib/" - if os.path.exists(lvs_dir): + # Calibre will do the scaling in s8 + if os.path.exists(lvs_dir): # and OPTS.lvs_exe[0]!="calibre": self.lvs_file = lvs_dir + name + ".sp" else: self.lvs_file = self.sp_file diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 81629771..a1e54a4d 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -133,7 +133,7 @@ class dff_buf(design.design): q_pin = self.dff_inst.get_pin("Q") a1_pin = self.inv1_inst.get_pin("A") mid1 = vector(a1_pin.cx(), q_pin.cy()) - self.add_path(q_pin.layer, [q_pin.center(), mid1, a1_pin.center()]) + self.add_path(q_pin.layer, [q_pin.center(), mid1, a1_pin.center()], width=q_pin.height()) self.add_via_stack_center(from_layer=a1_pin.layer, to_layer=q_pin.layer, offset=a1_pin.center()) @@ -177,8 +177,8 @@ class dff_buf(design.design): height=din_pin.height()) dout_pin = self.inv2_inst.get_pin("Z") - mid_pos = dout_pin.center() + vector(self.m1_nonpref_pitch, 0) - q_pos = mid_pos - vector(0, self.m2_pitch) + mid_pos = dout_pin.center() + vector(self.m2_nonpref_pitch, 0) + q_pos = mid_pos - vector(0, 2 * self.m2_nonpref_pitch) self.add_layout_pin_rect_center(text="Q", layer="m2", offset=q_pos) @@ -187,7 +187,7 @@ class dff_buf(design.design): to_layer="m2", offset=q_pos) - qb_pos = self.mid_qb_pos + vector(0, self.m2_pitch) + qb_pos = self.mid_qb_pos + vector(0, 2 * self.m2_nonpref_pitch) self.add_layout_pin_rect_center(text="Qb", layer="m2", offset=qb_pos) diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py index f12a620b..10c8d2fb 100644 --- a/compiler/pgates/pinv_dec.py +++ b/compiler/pgates/pinv_dec.py @@ -109,15 +109,15 @@ class pinv_dec(pinv.pinv): self.add_rect(layer="pwell", offset=ll, width=ur.x - ll.x, - height=self.height - ll.y) + height=self.height - ll.y + 0.5 * self.pwell_contact.height + self.well_enclose_active) if "nwell" in layer: ll = self.pmos_inst.ll() - self.pmos_inst.mod.active_offset ur = self.pmos_inst.ur() + self.pmos_inst.mod.active_offset self.add_rect(layer="nwell", - offset=ll - vector(self.nwell_enclose_active, 0), - width=ur.x - ll.x + self.nwell_enclose_active, - height=self.height - ll.y + 2 * self.nwell_enclose_active) + offset=ll, + width=ur.x - ll.x, + height=self.height - ll.y + 0.5 * self.nwell_contact.height + self.well_enclose_active) def place_ptx(self): """ @@ -125,15 +125,14 @@ class pinv_dec(pinv.pinv): # offset so that the input contact is over from the left edge by poly spacing x_offset = self.nmos.active_offset.y + contact.poly_contact.width + self.poly_space - # center the transistor in the y-dimension + # bottom of the transistor in the y-dimension y_offset = self.nmos.width + self.active_space self.nmos_pos = vector(x_offset, y_offset) - self.nmos_inst.place(self.nmos_pos) self.nmos_inst.place(self.nmos_pos, rotate=270) # place PMOS so it is half a poly spacing down from the top - xoffset = self.nmos_inst.height + 2 * self.poly_extend_active + 2 * self.well_extend_active + drc("pwell_to_nwell") - self.pmos_pos = self.nmos_pos + vector(xoffset, 0) + xoffset = self.nmos_inst.rx() + 2 * self.poly_extend_active + 2 * self.well_extend_active + drc("pwell_to_nwell") + self.pmos_pos = vector(xoffset, y_offset) self.pmos_inst.place(self.pmos_pos, rotate=270) @@ -142,6 +141,31 @@ class pinv_dec(pinv.pinv): nmos_drain_pos = self.nmos_inst.get_pin("D").center() self.output_pos = vector(0.5 * (pmos_drain_pos.x + nmos_drain_pos.x), nmos_drain_pos.y) + if OPTS.tech_name == "s8": + self.add_implants() + + def add_implants(self): + """ + Add top-to-bottom implants for adjacency issues in s8. + """ + # Route to the bottom + ll = (self.nmos_inst.ll() - vector(2 * [self.implant_enclose_active])).scale(1, 0) + # Don't route to the top + ur = self.nmos_inst.ur() + vector(self.implant_enclose_active, 0) + self.add_rect("nimplant", + ll, + ur.x - ll.x, + ur.y - ll.y) + + # Route to the bottom + ll = (self.pmos_inst.ll() - vector(2 * [self.implant_enclose_active])).scale(1, 0) + # Don't route to the top + ur = self.pmos_inst.ur() + vector(self.implant_enclose_active, 0) + self.add_rect("pimplant", + ll, + ur.x - ll.x, + ur.y - ll.y) + def route_outputs(self): """ Route the output (drains) together. diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index fb215d78..c4c53a70 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -16,6 +16,7 @@ import os from globals import OPTS from pgate import pgate + class ptx(design.design): """ This module generates gds and spice of a parametrically NMOS or @@ -24,6 +25,10 @@ class ptx(design.design): given width. Total width is therefore mults*width. Options allow you to connect the fingered gates and active for parallel devices. The add_*_contact option tells which layer to bring source/drain up to. + + ll, ur, width and height refer to the active area. + Wells and poly may extend beyond this. + """ def __init__(self, name="", @@ -221,15 +226,13 @@ class ptx(design.design): well_width_rule) self.well_height = max(self.active_height + 2 * self.well_enclose_active, well_width_rule) - # We are going to shift the 0,0, so include that in the width and height - self.height = self.well_height - self.active_offset.y - self.width = self.well_width - self.active_offset.x else: - # The well is not included in the height and width - self.height = self.poly_height - self.width = self.active_width self.well_height = self.height self.well_width = self.width + + # We are going to shift the 0,0, so include that in the width and height + self.height = self.active_height + self.width = self.active_width # This is the center of the first active contact offset (centered vertically) self.contact_offset = self.active_offset + vector(0.5 * self.active_contact.width, @@ -353,18 +356,18 @@ class ptx(design.design): """ Adding the diffusion (active region = diffusion region) """ - self.add_rect(layer="active", - offset=self.active_offset, - width=self.active_width, - height=self.active_height) + self.active = self.add_rect(layer="active", + offset=self.active_offset, + width=self.active_width, + height=self.active_height) # If the implant must enclose the active, shift offset # and increase width/height enclose_width = self.implant_enclose_active enclose_offset = [enclose_width] * 2 - self.add_rect(layer="{}implant".format(self.implant_type), - offset=self.active_offset - enclose_offset, - width=self.active_width + 2 * enclose_width, - height=self.active_height + 2 * enclose_width) + self.implant = self.add_rect(layer="{}implant".format(self.implant_type), + offset=self.active_offset - enclose_offset, + width=self.active_width + 2 * enclose_width, + height=self.active_height + 2 * enclose_width) def add_well_implant(self): """ diff --git a/compiler/tests/04_pinv_dec_1x_test.py b/compiler/tests/04_pinv_dec_1x_test.py index 8876ab58..92772616 100755 --- a/compiler/tests/04_pinv_dec_1x_test.py +++ b/compiler/tests/04_pinv_dec_1x_test.py @@ -21,6 +21,11 @@ class pinv_dec_1x_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + debug.info(2, "Checking 1x size decoder inverter") tx = factory.create(module_type="pinv_dec", size=1) self.local_check(tx) From 8f1dc7eeea4b664bfc44bb1a3d4960e29b7a694b Mon Sep 17 00:00:00 2001 From: mrg Date: Sat, 13 Jun 2020 06:50:53 -0700 Subject: [PATCH 099/206] Include mirror/rotate on translate_all boundary update --- compiler/base/hierarchy_layout.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 3f77c41d..4716c15c 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -55,14 +55,17 @@ class layout(): # GDS layout ############################################################ def offset_all_coordinates(self): - """ This function is called after everything is placed to - shift the origin in the lowest left corner """ + """ + This function is called after everything is placed to + shift the origin in the lowest left corner + """ offset = self.find_lowest_coords() self.translate_all(offset) return offset def get_gate_offset(self, x_offset, height, inv_num): - """Gets the base offset and y orientation of stacked rows of gates + """ + Gets the base offset and y orientation of stacked rows of gates assuming a minwidth metal1 vdd/gnd rail. Input is which gate in the stack from 0..n """ @@ -120,6 +123,7 @@ class layout(): highesty2 = max(inst.uy() for inst in self.insts) else: highestx2 = highesty2 = None + if highestx1 == None and highestx2 == None: return None elif highestx1 == None: @@ -188,7 +192,7 @@ class layout(): inst.offset = vector(inst.offset - offset) # The instances have a precomputed boundary that we need to update. if inst.__class__.__name__ == "instance": - inst.compute_boundary(inst.offset) + inst.compute_boundary(inst.offset, inst.mirror, inst.rotate) for pin_name in self.pin_map.keys(): # All the pins are absolute coordinates that need to be updated. pin_list = self.pin_map[pin_name] From 91f20f2cf6055d6934df2e017b15a94891ae04e2 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 14 Jun 2020 14:09:45 -0700 Subject: [PATCH 100/206] Better centering of pinv_dec --- compiler/pgates/pinv_dec.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py index 10c8d2fb..10265f24 100644 --- a/compiler/pgates/pinv_dec.py +++ b/compiler/pgates/pinv_dec.py @@ -8,7 +8,7 @@ import contact import pinv import debug -from tech import drc, parameter +from tech import drc, parameter, layer from vector import vector from globals import OPTS from sram_factory import factory @@ -102,31 +102,30 @@ class pinv_dec(pinv.pinv): def extend_wells(self): """ Extend bottom to top for each well. """ - from tech import layer if "pwell" in layer: - ll = self.nmos_inst.ll() - self.nmos_inst.mod.active_offset - ur = self.nmos_inst.ur() + self.nmos_inst.mod.active_offset + ll = (self.nmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0) + ur = self.nmos_inst.ur() + vector(2 * [self.well_enclose_active]) self.add_rect(layer="pwell", offset=ll, width=ur.x - ll.x, height=self.height - ll.y + 0.5 * self.pwell_contact.height + self.well_enclose_active) if "nwell" in layer: - ll = self.pmos_inst.ll() - self.pmos_inst.mod.active_offset - ur = self.pmos_inst.ur() + self.pmos_inst.mod.active_offset + ll = (self.pmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0) + ur = self.pmos_inst.ur() + vector(2 * [self.well_enclose_active]) self.add_rect(layer="nwell", offset=ll, width=ur.x - ll.x, height=self.height - ll.y + 0.5 * self.nwell_contact.height + self.well_enclose_active) - + def place_ptx(self): """ """ + # center the transistors in the y-dimension (it is rotated, so use the width) + y_offset = 0.5 * (self.height - self.nmos.width) + self.nmos.width # offset so that the input contact is over from the left edge by poly spacing x_offset = self.nmos.active_offset.y + contact.poly_contact.width + self.poly_space - # bottom of the transistor in the y-dimension - y_offset = self.nmos.width + self.active_space self.nmos_pos = vector(x_offset, y_offset) self.nmos_inst.place(self.nmos_pos, rotate=270) @@ -206,7 +205,7 @@ class pinv_dec(pinv.pinv): self.add_via_stack_center(offset=contact_pos, from_layer=self.active_stack[2], to_layer=self.supply_layer) - + def route_supply_rails(self): pin = self.nmos_inst.get_pin("S") source_pos = pin.center() From 443c401561f7337f7361a576ba1578c0cfe2579d Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 14 Jun 2020 14:17:04 -0700 Subject: [PATCH 101/206] pnand2 B input spaced from top --- compiler/pgates/pnand2.py | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 3a9e2db7..c3e9a515 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -12,6 +12,7 @@ from globals import OPTS from vector import vector import logical_effort from sram_factory import factory +import contact class pnand2(pgate.pgate): @@ -177,11 +178,26 @@ class pnand2(pgate.pgate): # Top of NMOS drain - bottom_pin = self.nmos2_inst.get_pin("D") - self.inputA_yoffset = max(bottom_pin.uy() + self.m1_pitch, - self.nmos2_inst.uy() + self.poly_to_active) - - self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch + bottom_pin = self.nmos1_inst.get_pin("D") + # active contact metal to poly contact metal spacing + active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * contact.poly_contact.second_layer_height + # active diffusion to poly contact spacing + # doesn't use nmos uy because that is calculated using offset + poly height + active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height + active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height + active_to_poly_contact2 = active_top + drc("contact_to_gate") + 0.5 * self.route_layer_width + self.inputA_yoffset = max(active_contact_to_poly_contact, + active_to_poly_contact, + active_to_poly_contact2) + + self.route_input_gate(self.pmos1_inst, + self.nmos1_inst, + self.inputA_yoffset, + "A", + position="center") + + # self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch + self.inputB_yoffset = self.output_yoffset - self.route_layer_pitch # This will help with the wells and the input/output placement self.route_input_gate(self.pmos2_inst, @@ -189,19 +205,13 @@ class pnand2(pgate.pgate): self.inputB_yoffset, "B", position="center") - - self.route_input_gate(self.pmos1_inst, - self.nmos1_inst, - self.inputA_yoffset, - "A", - position="center") def route_output(self): """ Route the Z output """ # One routing track layer below the PMOS contacts route_layer_offset = 0.5 * self.route_layer_width + self.route_layer_space - output_yoffset = self.pmos1_inst.get_pin("D").by() - route_layer_offset + self.output_yoffset = self.pmos1_inst.get_pin("D").by() - route_layer_offset # PMOS1 drain @@ -213,7 +223,7 @@ class pnand2(pgate.pgate): # Output pin out_offset = vector(nmos_pin.cx() + self.route_layer_pitch, - output_yoffset) + self.output_yoffset) # This routes on M2 # # Midpoints of the L routes go horizontal first then vertical From 8e8a97cc4b05b1cbe0884549d1d17d3d7ecef9f2 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 14 Jun 2020 14:17:35 -0700 Subject: [PATCH 102/206] Add correct boundary to SRAM --- compiler/sram/sram_base.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 8002b2f1..89ecfaaf 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -124,6 +124,8 @@ class sram_base(design, verilog, lef): highest_coord = self.find_highest_coords() self.width = highest_coord[0] self.height = highest_coord[1] + self.add_boundary(ll=vector(0, 0), + ur=vector(self.width, self.height)) start_time = datetime.datetime.now() # We only enable final verification if we have routed the design @@ -142,7 +144,7 @@ class sram_base(design, verilog, lef): for inst in self.insts: self.copy_power_pins(inst, "vdd") self.copy_power_pins(inst, "gnd") - + if not OPTS.route_supplies: # Do not route the power supply (leave as must-connect pins) return From 7dc33285a77bc5b2735049aeac13a472e1b20060 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 14 Jun 2020 14:18:08 -0700 Subject: [PATCH 103/206] Add contact to gate design rule to max for spacing inputs --- compiler/pgates/pnand3.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 3a9d4221..e227d7af 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -14,6 +14,7 @@ from sram_factory import factory from globals import OPTS import contact + class pnand3(pgate.pgate): """ This module generates gds of a parametrically sized 2-input nand. @@ -219,11 +220,13 @@ class pnand3(pgate.pgate): active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * contact.poly_contact.second_layer_height # active diffusion to poly contact spacing # doesn't use nmos uy because that is calculated using offset + poly height - active_to_poly_contact = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height \ - + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height - + active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height + active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height + active_to_poly_contact2 = active_top + drc("contact_to_gate") + 0.5 * self.route_layer_width self.inputA_yoffset = max(active_contact_to_poly_contact, - active_to_poly_contact) + active_to_poly_contact, + active_to_poly_contact2) + self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, From 9930b5f3f60b213c24d7d0f8437ba2dabd2c08e4 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 14 Jun 2020 14:18:25 -0700 Subject: [PATCH 104/206] Do not run tapless unit tests --- compiler/tests/04_pnand2_test.py | 8 ++++---- compiler/tests/04_pnand3_test.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py index 77a6932c..ae0668ae 100755 --- a/compiler/tests/04_pnand2_test.py +++ b/compiler/tests/04_pnand2_test.py @@ -25,10 +25,10 @@ class pnand2_test(openram_test): tx = factory.create(module_type="pnand2", size=1) self.local_check(tx) - debug.info(2, "Checking 2-input nand gate") - tx = factory.create(module_type="pnand2", size=1, add_wells=False) - # Only DRC because well contacts will fail LVS - self.local_drc_check(tx) + # debug.info(2, "Checking 2-input nand gate") + # tx = factory.create(module_type="pnand2", size=1, add_wells=False) + # # Only DRC because well contacts will fail LVS + # self.local_drc_check(tx) globals.end_openram() diff --git a/compiler/tests/04_pnand3_test.py b/compiler/tests/04_pnand3_test.py index 82bf1846..c03e32d6 100755 --- a/compiler/tests/04_pnand3_test.py +++ b/compiler/tests/04_pnand3_test.py @@ -25,10 +25,10 @@ class pnand3_test(openram_test): tx = factory.create(module_type="pnand3", size=1) self.local_check(tx) - debug.info(2, "Checking 3-input nand gate") - tx = factory.create(module_type="pnand3", size=1, add_wells=False) - # Only DRC because well contacts will fail LVS - self.local_drc_check(tx) + # debug.info(2, "Checking 3-input nand gate") + # tx = factory.create(module_type="pnand3", size=1, add_wells=False) + # # Only DRC because well contacts will fail LVS + # self.local_drc_check(tx) globals.end_openram() From 78be9f367a3efa6c7df338766500d5d82a78bbd3 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 14 Jun 2020 15:52:09 -0700 Subject: [PATCH 105/206] Add brain-dead router pins to perimeter --- compiler/base/hierarchy_layout.py | 42 +++++++++++- compiler/modules/control_logic.py | 2 +- compiler/sram/sram_1bank.py | 110 ++++++++++++++++++++++-------- 3 files changed, 123 insertions(+), 31 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 4716c15c..c9b6a7a8 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1354,7 +1354,7 @@ class layout(): # Hack for min area if OPTS.tech_name == "sky130": width = round_to_grid(sqrt(drc["minarea_m3"])) - height = round_to_grid(drc["minarea_m3"]/width) + height = round_to_grid(drc["minarea_m3"] / width) else: width = via.width height = via.height @@ -1364,6 +1364,46 @@ class layout(): width=width, height=height) + def add_perimeter_pin(self, name, pin, side, bbox): + """ + Add a pin along the perimeter side specified by the bbox with + the given name and layer from the pin starting location. + """ + (ll, ur) = bbox + left = ll.x + bottom = ll.y + right = ur.x + top = ur.y + + pin_loc = pin.center() + if side == "left": + peri_pin_loc = vector(left, pin_loc.y) + layer = "m3" + elif side == "right": + layer = "m3" + peri_pin_loc = vector(right, pin_loc.x) + elif side == "top": + layer = "m4" + peri_pin_loc = vector(pin_loc.x, top) + elif side == "bottom": + layer = "m4" + peri_pin_loc = vector(pin_loc.x, bottom) + + self.add_via_stack_center(from_layer=pin.layer, + to_layer=layer, + offset=pin_loc) + + self.add_path(layer, + [pin_loc, peri_pin_loc]) + + self.add_via_stack_center(from_layer=layer, + to_layer="m4", + offset=peri_pin_loc) + + self.add_layout_pin_rect_center(text=name, + layer="m4", + offset=peri_pin_loc) + def add_power_ring(self, bbox): """ Create vdd and gnd power rings around an area of the bounding box diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 64fc4fc8..b85a0d4a 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -1005,7 +1005,7 @@ class control_logic(design.design): def route_output_to_bus_jogged(self, inst, name): # Connect this at the bottom of the buffer out_pos = inst.get_pin("Z").center() - mid1 = vector(out_pos.x, out_pos.y - 0.25 * inst.mod.height) + mid1 = vector(out_pos.x, out_pos.y - 0.4 * inst.mod.height) mid2 = vector(self.input_bus[name].cx(), mid1.y) bus_pos = self.input_bus[name].center() self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos]) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index eb0fe3ad..d1398307 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -246,46 +246,100 @@ class sram_1bank(sram_base): """ Add the top-level pins for a single bank SRAM with control. """ - 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_insts[port], - signal, - signal + "{}".format(port)) + highest_coord = self.find_highest_coords() + lowest_coord = self.find_lowest_coords() + bbox = [lowest_coord, highest_coord] + + + for port in self.all_ports: + # Depending on the port, use the bottom/top or left/right sides + # Port 0 is left/bottom + # Port 1 is right/top + bottom_or_top = "bottom" if port==0 else "top" + left_or_right = "left" if port==0 else "right" + + # Connect the control pins as inputs + for signal in self.control_logic_inputs[port]: + if signal == "clk": + continue + self.add_perimeter_pin(name=signal + "{}".format(port), + pin=self.control_logic_insts[port].get_pin(signal), + side=left_or_right, + bbox=bbox) + # self.copy_layout_pin(self.control_logic_insts[port], + # signal, + # signal + "{}".format(port)) + + self.add_perimeter_pin(name="clk{}".format(port), + pin=self.control_logic_insts[port].get_pin("clk"), + side=bottom_or_top, + bbox=bbox) + + # Data output pins go to BOTTOM/TOP if port in self.read_ports: for bit in range(self.word_size + self.num_spare_cols): - self.copy_layout_pin(self.bank_inst, - "dout{0}_{1}".format(port, bit), - "dout{0}[{1}]".format(port, bit)) + self.add_perimeter_pin(name="dout{0}[{1}]".format(port, bit), + pin=self.bank_inst.get_pin("dout{0}_{1}".format(port, bit)), + side=bottom_or_top, + bbox=bbox) + # self.copy_layout_pin(self.bank_inst, + # "dout{0}_{1}".format(port, bit), + # "dout{0}[{1}]".format(port, bit)) - # Lower address bits + # Lower address bits go to BOTTOM/TOP for bit in range(self.col_addr_size): - self.copy_layout_pin(self.col_addr_dff_insts[port], - "din_{}".format(bit), - "addr{0}[{1}]".format(port, bit)) - # Upper address bits + self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit), + pin=self.col_addr_dff_insts[port].get_pin("din_{}".format(bit)), + side=bottom_or_top, + bbox=bbox) + # self.copy_layout_pin(self.col_addr_dff_insts[port], + # "din_{}".format(bit), + # "addr{0}[{1}]".format(port, bit)) + + # Upper address bits go to LEFT/RIGHT for bit in range(self.row_addr_size): - self.copy_layout_pin(self.row_addr_dff_insts[port], - "din_{}".format(bit), - "addr{0}[{1}]".format(port, bit + self.col_addr_size)) + self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit + self.col_addr_size), + pin=self.row_addr_dff_insts[port].get_pin("din_{}".format(bit)), + side=left_or_right, + bbox=bbox) + # self.copy_layout_pin(self.row_addr_dff_insts[port], + # "din_{}".format(bit), + # "addr{0}[{1}]".format(port, bit + self.col_addr_size)) + # Data input pins go to BOTTOM/TOP if port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): - self.copy_layout_pin(self.data_dff_insts[port], - "din_{}".format(bit), - "din{0}[{1}]".format(port, bit)) + self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit), + pin=self.data_dff_insts[port].get_pin("din_{}".format(bit)), + side=bottom_or_top, + bbox=bbox) + # self.copy_layout_pin(self.data_dff_insts[port], + # "din_{}".format(bit), + # "din{0}[{1}]".format(port, bit)) + # Write mask pins go to BOTTOM/TOP + if port in self.write_ports: if self.write_size: for bit in range(self.num_wmasks): - self.copy_layout_pin(self.wmask_dff_insts[port], - "din_{}".format(bit), - "wmask{0}[{1}]".format(port, bit)) - + self.add_perimeter_pin(name="wmask{0}[{1}]".format(port, bit), + pin=self.wmask_dff_insts[port].get_pin("din_{}".format(bit)), + side=bottom_or_top, + bbox=bbox) + # self.copy_layout_pin(self.wmask_dff_insts[port], + # "din_{}".format(bit), + # "wmask{0}[{1}]".format(port, bit)) + + # Spare wen pins go to BOTTOM/TOP + if port in self.write_ports: for bit in range(self.num_spare_cols): - self.copy_layout_pin(self.spare_wen_dff_insts[port], - "din_{}".format(bit), - "spare_wen{0}[{1}]".format(port, bit)) + self.add_perimeter_pin(name="spare_wen{0}[{1}]".format(port, bit), + pin=self.spare_wen_dff_insts[port].get_pin("din_{}".format(bit)), + side=left_or_right, + bbox=bbox) + # self.copy_layout_pin(self.spare_wen_dff_insts[port], + # "din_{}".format(bit), + # "spare_wen{0}[{1}]".format(port, bit)) def route_layout(self): """ Route a single bank SRAM """ @@ -314,8 +368,6 @@ class sram_1bank(sram_base): # This is the actual input to the SRAM for port in self.all_ports: - self.copy_layout_pin(self.control_logic_insts[port], "clk", "clk{}".format(port)) - # Connect all of these clock pins to the clock in the central bus # This is something like a "spine" clock distribution. The two spines # are clk_buf and clk_buf_bar From 52ee7b0a19e7895eb0b4898193ef1f1586b41240 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 14 Jun 2020 16:44:10 -0700 Subject: [PATCH 106/206] Disable perimeter pins and make an option --- compiler/options.py | 4 ++ compiler/sram/sram_1bank.py | 130 ++++++++++++++++++++---------------- 2 files changed, 78 insertions(+), 56 deletions(-) diff --git a/compiler/options.py b/compiler/options.py index ac61e8f3..6ecfa6ae 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -123,6 +123,10 @@ class options(optparse.Values): analytical_delay = True # Purge the temp directory after a successful # run (doesn't purge on errors, anyhow) + + # Route the input/output pins to the perimeter + perimeter_pins = False + purge_temp = True # These are the default modules that can be over-riden diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index d1398307..bb8c0a7d 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -9,7 +9,7 @@ import debug from vector import vector from sram_base import sram_base from contact import m2_via - +from globals import OPTS class sram_1bank(sram_base): """ @@ -246,7 +246,6 @@ class sram_1bank(sram_base): """ Add the top-level pins for a single bank SRAM with control. """ - highest_coord = self.find_highest_coords() lowest_coord = self.find_lowest_coords() bbox = [lowest_coord, highest_coord] @@ -263,83 +262,102 @@ class sram_1bank(sram_base): for signal in self.control_logic_inputs[port]: if signal == "clk": continue - self.add_perimeter_pin(name=signal + "{}".format(port), - pin=self.control_logic_insts[port].get_pin(signal), - side=left_or_right, - bbox=bbox) - # self.copy_layout_pin(self.control_logic_insts[port], - # signal, - # signal + "{}".format(port)) + if OPTS.perimeter_pins: + self.add_perimeter_pin(name=signal + "{}".format(port), + pin=self.control_logic_insts[port].get_pin(signal), + side=left_or_right, + bbox=bbox) + else: + self.copy_layout_pin(self.control_logic_insts[port], + signal, + signal + "{}".format(port)) - self.add_perimeter_pin(name="clk{}".format(port), - pin=self.control_logic_insts[port].get_pin("clk"), - side=bottom_or_top, - bbox=bbox) + if OPTS.perimeter_pins: + self.add_perimeter_pin(name="clk{}".format(port), + pin=self.control_logic_insts[port].get_pin("clk"), + side=bottom_or_top, + bbox=bbox) + else: + self.copy_layout_pin(self.control_logic_insts[port], + "clk", + "clk{}".format(port)) # Data output pins go to BOTTOM/TOP if port in self.read_ports: for bit in range(self.word_size + self.num_spare_cols): - self.add_perimeter_pin(name="dout{0}[{1}]".format(port, bit), - pin=self.bank_inst.get_pin("dout{0}_{1}".format(port, bit)), - side=bottom_or_top, - bbox=bbox) - # self.copy_layout_pin(self.bank_inst, - # "dout{0}_{1}".format(port, bit), - # "dout{0}[{1}]".format(port, bit)) + if OPTS.perimeter_pins: + self.add_perimeter_pin(name="dout{0}[{1}]".format(port, bit), + pin=self.bank_inst.get_pin("dout{0}_{1}".format(port, bit)), + side=bottom_or_top, + bbox=bbox) + else: + self.copy_layout_pin(self.bank_inst, + "dout{0}_{1}".format(port, bit), + "dout{0}[{1}]".format(port, bit)) # Lower address bits go to BOTTOM/TOP for bit in range(self.col_addr_size): - self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit), - pin=self.col_addr_dff_insts[port].get_pin("din_{}".format(bit)), - side=bottom_or_top, - bbox=bbox) - # self.copy_layout_pin(self.col_addr_dff_insts[port], - # "din_{}".format(bit), - # "addr{0}[{1}]".format(port, bit)) + if OPTS.perimeter_pins: + self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit), + pin=self.col_addr_dff_insts[port].get_pin("din_{}".format(bit)), + side=bottom_or_top, + bbox=bbox) + else: + self.copy_layout_pin(self.col_addr_dff_insts[port], + "din_{}".format(bit), + "addr{0}[{1}]".format(port, bit)) # Upper address bits go to LEFT/RIGHT for bit in range(self.row_addr_size): - self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit + self.col_addr_size), - pin=self.row_addr_dff_insts[port].get_pin("din_{}".format(bit)), - side=left_or_right, - bbox=bbox) - # self.copy_layout_pin(self.row_addr_dff_insts[port], - # "din_{}".format(bit), - # "addr{0}[{1}]".format(port, bit + self.col_addr_size)) - + if OPTS.perimeter_pins: + self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit + self.col_addr_size), + pin=self.row_addr_dff_insts[port].get_pin("din_{}".format(bit)), + side=left_or_right, + bbox=bbox) + else: + self.copy_layout_pin(self.row_addr_dff_insts[port], + "din_{}".format(bit), + "addr{0}[{1}]".format(port, bit + self.col_addr_size)) + # Data input pins go to BOTTOM/TOP if port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): - self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit), - pin=self.data_dff_insts[port].get_pin("din_{}".format(bit)), - side=bottom_or_top, - bbox=bbox) - # self.copy_layout_pin(self.data_dff_insts[port], - # "din_{}".format(bit), - # "din{0}[{1}]".format(port, bit)) + if OPTS.perimeter_pins: + self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit), + pin=self.data_dff_insts[port].get_pin("din_{}".format(bit)), + side=bottom_or_top, + bbox=bbox) + else: + self.copy_layout_pin(self.data_dff_insts[port], + "din_{}".format(bit), + "din{0}[{1}]".format(port, bit)) # Write mask pins go to BOTTOM/TOP if port in self.write_ports: if self.write_size: for bit in range(self.num_wmasks): - self.add_perimeter_pin(name="wmask{0}[{1}]".format(port, bit), - pin=self.wmask_dff_insts[port].get_pin("din_{}".format(bit)), - side=bottom_or_top, - bbox=bbox) - # self.copy_layout_pin(self.wmask_dff_insts[port], - # "din_{}".format(bit), - # "wmask{0}[{1}]".format(port, bit)) + if OPTS.perimeter_pins: + self.add_perimeter_pin(name="wmask{0}[{1}]".format(port, bit), + pin=self.wmask_dff_insts[port].get_pin("din_{}".format(bit)), + side=bottom_or_top, + bbox=bbox) + else: + self.copy_layout_pin(self.wmask_dff_insts[port], + "din_{}".format(bit), + "wmask{0}[{1}]".format(port, bit)) # Spare wen pins go to BOTTOM/TOP if port in self.write_ports: for bit in range(self.num_spare_cols): - self.add_perimeter_pin(name="spare_wen{0}[{1}]".format(port, bit), - pin=self.spare_wen_dff_insts[port].get_pin("din_{}".format(bit)), - side=left_or_right, - bbox=bbox) - # self.copy_layout_pin(self.spare_wen_dff_insts[port], - # "din_{}".format(bit), - # "spare_wen{0}[{1}]".format(port, bit)) + if OPTS.perimeter_pins: + self.add_perimeter_pin(name="spare_wen{0}[{1}]".format(port, bit), + pin=self.spare_wen_dff_insts[port].get_pin("din_{}".format(bit)), + side=left_or_right, + bbox=bbox) + else: + self.copy_layout_pin(self.spare_wen_dff_insts[port], + "din_{}".format(bit), + "spare_wen{0}[{1}]".format(port, bit)) def route_layout(self): """ Route a single bank SRAM """ From 02352c35d766ba69035aa5fff85571aed0513a5d Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 14 Jun 2020 17:10:32 -0700 Subject: [PATCH 107/206] Fix hard coded layer in wmask --- compiler/modules/write_mask_and_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 9c077ed7..581f9484 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -126,7 +126,7 @@ class write_mask_and_array(design.design): for supply in ["gnd", "vdd"]: supply_pin_left = self.and2_insts[0].get_pin(supply) supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply) - self.add_path("m1", [supply_pin_left.lc(), supply_pin_right.rc()]) + self.add_path(supply_pin_left.layer, [supply_pin_left.lc(), supply_pin_right.rc()]) def get_cin(self): """Get the relative capacitance of all the input connections in the bank""" From 6c04166876eb2ced5c3ebb96ba893f98cd0933e7 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 15 Jun 2020 06:05:05 -0700 Subject: [PATCH 108/206] Update port data wmask tests --- .../tests/18_port_data_wmask_1rw_1r_test.py | 114 ++++++++++++++++++ compiler/tests/18_port_data_wmask_test.py | 2 +- 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100755 compiler/tests/18_port_data_wmask_1rw_1r_test.py diff --git a/compiler/tests/18_port_data_wmask_1rw_1r_test.py b/compiler/tests/18_port_data_wmask_1rw_1r_test.py new file mode 100755 index 00000000..52d1c4fb --- /dev/null +++ b/compiler/tests/18_port_data_wmask_1rw_1r_test.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_wmask_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + + c = sram_config(word_size=16, + write_size=4, + num_words=16) + + c.words_per_row = 1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words = 32 + c.words_per_row = 2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words = 64 + c.words_per_row = 4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words = 128 + c.words_per_row = 8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + OPTS.bitcell = "bitcell_1w_1r" + OPTS.num_rw_ports = 0 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + c.num_words = 16 + c.words_per_row = 1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + # + c.num_words = 32 + c.words_per_row = 2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.num_words = 64 + c.words_per_row = 4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.word_size = 8 + c.num_words = 128 + c.words_per_row = 8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_wmask_test.py b/compiler/tests/18_port_data_wmask_test.py index f670990e..1c650c74 100755 --- a/compiler/tests/18_port_data_wmask_test.py +++ b/compiler/tests/18_port_data_wmask_test.py @@ -15,7 +15,7 @@ from sram_factory import factory import debug -class port_data_test(openram_test): +class port_data_wmask_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) From 79b3b9a8b0b231461032333591137dd15f6e8996 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 15 Jun 2020 10:06:04 -0700 Subject: [PATCH 109/206] Switch input/output layers for predecodes --- compiler/modules/hierarchical_predecode.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 44075656..d6cf7388 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -83,9 +83,9 @@ class hierarchical_predecode(design.design): self.bus_directions = None self.bus_pitch = self.m1_pitch self.bus_space = 1.5 * self.m1_space - self.input_layer = "li" - self.output_layer = "m2" - self.output_layer_pitch = self.m2_pitch + self.input_layer = "m2" + self.output_layer = "li" + self.output_layer_pitch = self.li_pitch else: self.bus_layer = "m2" self.bus_directions = None @@ -234,12 +234,12 @@ class hierarchical_predecode(design.design): in_pin = "in_{}".format(inv_num) inv_out_pin = self.inv_inst[inv_num].get_pin("Z") - inv_out_pos = inv_out_pin.rc() + inv_out_pos = inv_out_pin.lr() # add output so that it is just below the vdd or gnd rail # since this is where the p/n devices are and there are no # pins in the and gates. - if OPTS.tech_name == "sky130": + if False and OPTS.tech_name == "sky130": rail_pos = vector(self.decode_rails[out_pin].cx(), inv_out_pos.y) self.add_path(self.output_layer, [inv_out_pos, rail_pos]) else: From 6e0008403e63e879f7615b5769a49b7f56af8f2d Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 15 Jun 2020 10:06:17 -0700 Subject: [PATCH 110/206] Update new tech name --- compiler/pgates/pinv_dec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py index 10265f24..3960f1bf 100644 --- a/compiler/pgates/pinv_dec.py +++ b/compiler/pgates/pinv_dec.py @@ -140,7 +140,7 @@ class pinv_dec(pinv.pinv): nmos_drain_pos = self.nmos_inst.get_pin("D").center() self.output_pos = vector(0.5 * (pmos_drain_pos.x + nmos_drain_pos.x), nmos_drain_pos.y) - if OPTS.tech_name == "s8": + if OPTS.tech_name == "sky130": self.add_implants() def add_implants(self): From 355474ce2c1b2cf23f9e81431e2457d94a836cd3 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 15 Jun 2020 10:07:00 -0700 Subject: [PATCH 111/206] Playing around with pnand2 pin spacing rules --- compiler/pgates/pnand2.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index c3e9a515..12323885 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -176,11 +176,10 @@ class pnand2(pgate.pgate): def route_inputs(self): """ Route the A and B inputs """ - # Top of NMOS drain bottom_pin = self.nmos1_inst.get_pin("D") # active contact metal to poly contact metal spacing - active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * contact.poly_contact.second_layer_height + active_contact_to_poly_contact = bottom_pin.uy() + self.route_layer_space + 0.5 * contact.poly_contact.second_layer_height # active diffusion to poly contact spacing # doesn't use nmos uy because that is calculated using offset + poly height active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height @@ -196,8 +195,15 @@ class pnand2(pgate.pgate): "A", position="center") - # self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch - self.inputB_yoffset = self.output_yoffset - self.route_layer_pitch + self.inputB_yoffset = self.inputA_yoffset + 2 * self.m3_pitch + # # active contact metal to poly contact metal spacing + # active_contact_to_poly_contact = self.output_yoffset - self.route_layer_space - 0.5 * contact.poly_contact.second_layer_height + # active_bottom = self.pmos1_inst.by() + # active_to_poly_contact = active_bottom - self.poly_to_active - 0.5 * contact.poly_contact.first_layer_height + # active_to_poly_contact2 = active_bottom - drc("contact_to_gate") - 0.5 * self.route_layer_width + # self.inputB_yoffset = min(active_contact_to_poly_contact, + # active_to_poly_contact, + # active_to_poly_contact2) # This will help with the wells and the input/output placement self.route_input_gate(self.pmos2_inst, @@ -210,7 +216,7 @@ class pnand2(pgate.pgate): """ Route the Z output """ # One routing track layer below the PMOS contacts - route_layer_offset = 0.5 * self.route_layer_width + self.route_layer_space + route_layer_offset = 0.5 * contact.poly_contact.second_layer_height + self.route_layer_space self.output_yoffset = self.pmos1_inst.get_pin("D").by() - route_layer_offset From 4cb827c3d7a647ee64c82d31d4314beb737891c6 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 15 Jun 2020 10:08:07 -0700 Subject: [PATCH 112/206] Add redundant implant for s8 --- compiler/pgates/single_level_column_mux.py | 36 ++++++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 96935158..b594f2e4 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -11,7 +11,7 @@ from tech import drc, layer from vector import vector from sram_factory import factory import logical_effort -from utils import round_to_grid +from globals import OPTS class single_level_column_mux(pgate.pgate): @@ -113,18 +113,21 @@ class single_level_column_mux(pgate.pgate): # This aligns it directly above the other tx with gates abutting nmos_upper_position = nmos_lower_position \ - + vector(0, self.nmos.active_height + max(self.active_space,self.poly_space)) + + vector(0, self.nmos.active_height + max(self.active_space, self.poly_space)) self.nmos_upper = self.add_inst(name="mux_tx2", mod=self.nmos, offset=nmos_upper_position) self.connect_inst(["br", "sel", "br_out", "gnd"]) + if OPTS.tech_name == "sky130": + self.add_implants() + def connect_poly(self): """ Connect the poly gate of the two pass transistors """ # offset is the top of the lower nmos' diffusion # height is the distance between the nmos' diffusions, which depends on max(self.active_space,self.poly_space) - offset = self.nmos_lower.get_pin("G").ul() - vector(0,self.poly_extend_active) + offset = self.nmos_lower.get_pin("G").ul() - vector(0, self.poly_extend_active) height = self.nmos_upper.get_pin("G").by() + self.poly_extend_active - offset.y self.add_rect(layer="poly", offset=offset, @@ -183,13 +186,26 @@ class single_level_column_mux(pgate.pgate): vector(nmos_lower_s_pin.cx(), br_out_pin.uy()), nmos_lower_s_pin.center()]) # halfway up, move over - mid1 = br_pin.bc().scale(1,0.5) \ - + nmos_lower_d_pin.uc().scale(0,0.5) - mid2 = br_pin.bc().scale(0,0.5) \ - + nmos_lower_d_pin.uc().scale(1,0.5) + mid1 = br_pin.bc().scale(1, 0.5) \ + + nmos_lower_d_pin.uc().scale(0, 0.5) + mid2 = br_pin.bc().scale(0, 0.5) \ + + nmos_lower_d_pin.uc().scale(1, 0.5) self.add_path(self.col_mux_stack[2], [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()]) - + + def add_implants(self): + """ + Add top-to-bottom implants for adjacency issues in s8. + """ + # Route to the bottom + ll = (self.nmos_lower.ll() - vector(2 * [self.implant_enclose_active])).scale(1, 0) + # Don't route to the top + ur = self.nmos_upper.ur() + vector(self.implant_enclose_active, 0) + self.add_rect("nimplant", + ll, + ur.x - ll.x, + ur.y - ll.y) + def add_pn_wells(self): """ Add a well and implant over the whole cell. Also, add the @@ -209,8 +225,8 @@ class single_level_column_mux(pgate.pgate): offset=active_pos) # Add the M1->..->power_grid_layer stack - self.add_power_pin(name = "gnd", - loc = active_pos, + self.add_power_pin(name="gnd", + loc=active_pos, start_layer="m1") # Add well enclosure over all the tx and contact From a862cf3cb21deff9c097f2d7bc51130906ee03e6 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 15 Jun 2020 10:17:54 -0700 Subject: [PATCH 113/206] Test more single level col mux configs --- ...ingle_level_column_mux_array_1rw_1r_test.py | 18 +++++++++++++++++- .../07_single_level_column_mux_array_test.py | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py index c758e788..209133aa 100755 --- a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py +++ b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py @@ -7,7 +7,7 @@ # All rights reserved. # from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS @@ -25,6 +25,14 @@ class single_level_column_mux_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() + debug.info(1, "Testing sample for 2-way column_mux_array port 0") + a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) + + debug.info(1, "Testing sample for 2-way column_mux_array port 1") + a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=4, bitcell_bl="bl1", bitcell_br="br1") + self.local_check(a) + debug.info(1, "Testing sample for 4-way column_mux_array port 0") a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) @@ -33,6 +41,14 @@ class single_level_column_mux_test(openram_test): a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl1", bitcell_br="br1") self.local_check(a) + debug.info(1, "Testing sample for 8-way column_mux_array port 0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=2, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) + + debug.info(1, "Testing sample for 8-way column_mux_array port 1") + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=2, bitcell_bl="bl1", bitcell_br="br1") + self.local_check(a) + globals.end_openram() diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index b1f74eba..c0476a4f 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -7,7 +7,7 @@ # All rights reserved. # from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS From e331d6fae8d776bc142263bb0ee9d22eddbf28d8 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 15 Jun 2020 10:25:53 -0700 Subject: [PATCH 114/206] Permute bus order to avoid conflict in control_logic --- compiler/modules/control_logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index b85a0d4a..d857f24d 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -363,7 +363,7 @@ class control_logic(design.design): # list of output control signals (for making a vertical bus) if self.port_type == "rw": - self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"] + self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "we_bar", "clk_buf", "cs"] elif self.port_type == "r": self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"] else: From abb5ff7bae279d70ab81ddc43d4dd6d294b0d48d Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 15 Jun 2020 10:30:27 -0700 Subject: [PATCH 115/206] Separate route conditions for s8 --- compiler/modules/hierarchical_decoder.py | 1 - compiler/modules/hierarchical_predecode.py | 15 +++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index c4a4cd68..8830d397 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -11,7 +11,6 @@ import math from sram_factory import factory from vector import vector from globals import OPTS -from errors import drc_error class hierarchical_decoder(design.design): diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index d6cf7388..f2274c6e 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -234,19 +234,18 @@ class hierarchical_predecode(design.design): in_pin = "in_{}".format(inv_num) inv_out_pin = self.inv_inst[inv_num].get_pin("Z") - inv_out_pos = inv_out_pin.lr() # add output so that it is just below the vdd or gnd rail # since this is where the p/n devices are and there are no # pins in the and gates. - if False and OPTS.tech_name == "sky130": - rail_pos = vector(self.decode_rails[out_pin].cx(), inv_out_pos.y) - self.add_path(self.output_layer, [inv_out_pos, rail_pos]) + if OPTS.tech_name == "sky130": + inv_out_pos = inv_out_pin.lr() else: - y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch - right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0) - rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset) - self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) + inv_out_pos = inv_out_pin.rc() + y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch + right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0) + rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset) + self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) self.add_via_stack_center(from_layer=inv_out_pin.layer, to_layer=self.output_layer, From 7dfc462ef67e259a07d66773e2da66172457e9be Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 15 Jun 2020 13:58:26 -0700 Subject: [PATCH 116/206] Add magic filter before calibre for sky130 --- compiler/options.py | 2 ++ compiler/verify/__init__.py | 30 ++++++++++++++++++------------ compiler/verify/calibre.py | 26 +++++++++++++++++--------- compiler/verify/magic.py | 25 +++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 21 deletions(-) diff --git a/compiler/options.py b/compiler/options.py index 6ecfa6ae..976595ad 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -110,6 +110,8 @@ class options(optparse.Values): drc_exe = None lvs_exe = None pex_exe = None + # For sky130, we need magic for filtering. + magic_exe = None # Should we print out the banner at startup print_banner = True diff --git a/compiler/verify/__init__.py b/compiler/verify/__init__.py index 59df42db..a28581d8 100644 --- a/compiler/verify/__init__.py +++ b/compiler/verify/__init__.py @@ -18,27 +18,27 @@ If not, OpenRAM will continue as if nothing happened! import os import debug from globals import OPTS -from globals import find_exe from globals import get_tool from tech import drc_name from tech import lvs_name from tech import pex_name -import sys -debug.info(1,"Initializing verify...") +debug.info(1, "Initializing verify...") if not OPTS.check_lvsdrc: - debug.info(1,"LVS/DRC/PEX disabled.") + debug.info(1, "LVS/DRC/PEX disabled.") OPTS.drc_exe = None OPTS.lvs_exe = None OPTS.pex_exe = None else: - debug.info(1, "Finding DRC/LVS/PEX tools.") - OPTS.drc_exe = get_tool("DRC", ["calibre","assura","magic"], drc_name) - OPTS.lvs_exe = get_tool("LVS", ["calibre","assura","netgen"], lvs_name) - OPTS.pex_exe = get_tool("PEX", ["calibre","magic"], pex_name) + debug.info(1, "Finding DRC/LVS/PEX tools.") + OPTS.drc_exe = get_tool("DRC", ["calibre", "assura", "magic"], drc_name) + OPTS.lvs_exe = get_tool("LVS", ["calibre", "assura", "netgen"], lvs_name) + OPTS.pex_exe = get_tool("PEX", ["calibre", "magic"], pex_name) + if OPTS.tech_name == "sky130": + OPTS.magic_exe = get_tool("GDS", ["magic"], None) -if OPTS.drc_exe == None: +if not OPTS.drc_exe: from .none import run_drc, print_drc_stats elif "calibre"==OPTS.drc_exe[0]: from .calibre import run_drc, print_drc_stats @@ -49,7 +49,7 @@ elif "magic"==OPTS.drc_exe[0]: else: debug.warning("Did not find a supported DRC tool.") -if OPTS.lvs_exe == None: +if not OPTS.lvs_exe: from .none import run_lvs, print_lvs_stats elif "calibre"==OPTS.lvs_exe[0]: from .calibre import run_lvs, print_lvs_stats @@ -61,7 +61,7 @@ else: debug.warning("Did not find a supported LVS tool.") -if OPTS.pex_exe == None: +if not OPTS.pex_exe: from .none import run_pex,print_pex_stats elif "calibre"==OPTS.pex_exe[0]: from .calibre import run_pex,print_pex_stats @@ -69,4 +69,10 @@ elif "magic"==OPTS.pex_exe[0]: from .magic import run_pex,print_pex_stats else: debug.warning("Did not find a supported PEX tool.") - + +if OPTS.tech_name == "sky130": + if "magic"==OPTS.magic_exe[0]: + from .magic import filter_gds + else: + debug.warning("Did not find Magic.") + diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 8abca448..1b9d296e 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -20,16 +20,16 @@ Calibre means pointing the code to the proper DRC and LVS rule files. import os import shutil import re -import time import debug from globals import OPTS -from run_script import * +from run_script import run_script # Keep track of statistics num_drc_runs = 0 num_lvs_runs = 0 num_pex_runs = 0 + def write_calibre_drc_script(cell_name, extract, final_verification): """ Write a Calibre runset file and script to run DRC """ # the runset file contains all the options to run calibre @@ -67,6 +67,7 @@ def write_calibre_drc_script(cell_name, extract, final_verification): os.system("chmod u+x {}".format(run_file)) return drc_runset + def write_calibre_lvs_script(cell_name, final_verification): """ Write a Calibre runset file and script to run LVS """ @@ -80,7 +81,7 @@ def write_calibre_lvs_script(cell_name, final_verification): 'lvsSourcePath': cell_name + ".sp", 'lvsSourcePrimary': cell_name, 'lvsSourceSystem': 'SPICE', - 'lvsSpiceFile': "extracted.sp", + 'lvsSpiceFile': "{}.spice".format(cell_name), 'lvsPowerNames': 'vdd', 'lvsGroundNames': 'gnd', 'lvsIncludeSVRFCmds': 1, @@ -130,8 +131,9 @@ def write_calibre_lvs_script(cell_name, final_verification): return lvs_runset -def write_calibre_pex_script(cell_name, extract, output, final_verification): +def write_calibre_pex_script(cell_name, extract, output, final_verification): + """ Write a pex script that can either just extract the netlist or the netlist+parasitics """ if output == None: output = name + ".pex.netlist" @@ -150,10 +152,9 @@ def write_calibre_pex_script(cell_name, extract, output, final_verification): 'pexRunDir': OPTS.openram_temp, 'pexLayoutPaths': cell_name + ".gds", 'pexLayoutPrimary': cell_name, - #'pexSourcePath' : OPTS.openram_temp+"extracted.sp", 'pexSourcePath': cell_name + ".sp", 'pexSourcePrimary': cell_name, - 'pexReportFile': cell_name + ".lvs.report", + 'pexReportFile': cell_name + ".pex.report", 'pexPexNetlistFile': cell_name + ".pex.netlist", 'pexPexReportFile': cell_name + ".pex.report", 'pexMaskDBFile': cell_name + ".maskdb", @@ -179,6 +180,7 @@ def write_calibre_pex_script(cell_name, extract, output, final_verification): return pex_runset + def run_drc(cell_name, gds_name, extract=False, final_verification=False): """Run DRC check on a given top-level name which is implemented in gds_name.""" @@ -186,9 +188,15 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False): global num_drc_runs num_drc_runs += 1 - # Copy file to local dir if it isn't already - if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): - shutil.copy(gds_name, OPTS.openram_temp) + # Filter the layouts through magic as a GDS filter for nsdm/psdm/nwell merging + if OPTS.tech_name == "sky130": + shutil.copy(gds_name, OPTS.openram_temp + "temp.gds") + from magic import filter_gds + filter_gds(cell_name, OPTS.openram_temp + "temp.gds", gds_name) + else: + # Copy file to local dir if it isn't already + if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): + shutil.copy(gds_name, OPTS.openram_temp) drc_runset = write_calibre_drc_script(cell_name, extract, final_verification) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 59346ace..d19537a5 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -33,6 +33,31 @@ num_lvs_runs = 0 num_pex_runs = 0 +def filter_gds(cell_name, input_gds, output_gds): + """ Run the gds through magic for any layer processing """ + global OPTS + + run_file = OPTS.openram_temp + "run_filter.sh" + f = open(run_file, "w") + f.write("#!/bin/sh\n") + f.write("{} -dnull -noconsole << EOF\n".format(OPTS.magic_exe[1])) + f.write("gds polygon subcell true\n") + f.write("gds warning default\n") + f.write("gds read {}\n".format(input_gds)) + f.write("load {}\n".format(cell_name)) + f.write("cellname delete \\(UNNAMED\\)\n") + #f.write("writeall force\n") + f.write("select top cell\n") + f.write("gds write {}\n".format(output_gds)) + f.write("quit -noprompt\n") + f.write("EOF\n") + + f.close() + os.system("chmod u+x {}".format(run_file)) + + (outfile, errfile, resultsfile) = run_script(cell_name, "filter") + + def write_magic_script(cell_name, extract=False, final_verification=False): """ Write a magic script to perform DRC and optionally extraction. """ From 69f56212451979103987bca315b172f719a11383 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 18 Jun 2020 14:54:36 -0700 Subject: [PATCH 117/206] Save raw file from ngspice --- compiler/characterizer/stimuli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index e5f225dc..70152a54 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -303,9 +303,9 @@ class stimuli(): valid_retcode=0 else: # ngspice 27+ supports threading with "set num_threads=4" in the stimulus file or a .spiceinit - cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe, - temp_stim, - OPTS.openram_temp) + cmd = "{0} -b -r {2}timing.raw -o {2}timing.lis {1}".format(OPTS.spice_exe, + temp_stim, + OPTS.openram_temp) # for some reason, ngspice-25 returns 1 when it only has acceptable warnings valid_retcode=1 From 403ea17039a6eac68f4ce751fefc8260e2c7fc3f Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 18 Jun 2020 14:55:01 -0700 Subject: [PATCH 118/206] PEP8 formatting --- compiler/characterizer/functional.py | 136 +++++++++++++-------------- 1 file changed, 64 insertions(+), 72 deletions(-) diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 8d07a61a..2c391e38 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -5,23 +5,18 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import sys,re,shutil -import copy import collections -from design import design import debug -import math -import tech import random from .stimuli import * from .charutils import * -import utils from globals import OPTS from .simulation import simulation # from .delay import delay import graph_util from sram_factory import factory + class functional(simulation): """ Functions to write random data values to a random address then read them back and check @@ -60,7 +55,6 @@ class functional(simulation): self.read_check = [] self.read_results = [] - def run(self, feasible_period=None): if feasible_period: #period defaults to tech.py feasible period otherwise. self.period = feasible_period @@ -85,11 +79,11 @@ class functional(simulation): for port in self.all_ports: checks = [] if port in self.read_ports: - checks.append((self.addr_value[port],"addr")) + checks.append((self.addr_value[port], "addr")) if port in self.write_ports: - checks.append((self.data_value[port],"data")) - checks.append((self.wmask_value[port],"wmask")) - checks.append((self.spare_wen_value[port],"spare_wen")) + checks.append((self.data_value[port], "data")) + checks.append((self.wmask_value[port], "wmask")) + checks.append((self.spare_wen_value[port], "spare_wen")) for (val, name) in checks: debug.check(len(self.cycle_times)==len(val), @@ -108,15 +102,15 @@ class functional(simulation): r_ops = ["noop", "read"] # First cycle idle is always an idle cycle - comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, "0"*self.num_wmasks, 0, self.t_current) + comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current) self.add_noop_all_ports(comment) # 1. Write all the write ports first to seed a bunch of locations. for port in self.write_ports: addr = self.gen_addr() word = self.gen_data() - comment = self.gen_cycle_comment("write", word, addr, "1"*self.num_wmasks, port, self.t_current) - self.add_write_one_port(comment, addr, word, "1"*self.num_wmasks, port) + comment = self.gen_cycle_comment("write", word, addr, "1" * self.num_wmasks, port, self.t_current) + self.add_write_one_port(comment, addr, word, "1" * self.num_wmasks, port) self.stored_words[addr] = word # All other read-only ports are noops. @@ -135,7 +129,7 @@ class functional(simulation): if port in self.write_ports: self.add_noop_one_port(port) else: - comment = self.gen_cycle_comment("read", word, addr, "0"*self.num_wmasks, port, self.t_current) + comment = self.gen_cycle_comment("read", word, addr, "0" * self.num_wmasks, port, self.t_current) self.add_read_one_port(comment, addr, port) self.add_read_check(word, port) self.cycle_times.append(self.t_current) @@ -164,13 +158,13 @@ class functional(simulation): self.add_noop_one_port(port) else: word = self.gen_data() - comment = self.gen_cycle_comment("write", word, addr, "1"*self.num_wmasks, port, self.t_current) - self.add_write_one_port(comment, addr, word, "1"*self.num_wmasks, port) + comment = self.gen_cycle_comment("write", word, addr, "1" * self.num_wmasks, port, self.t_current) + self.add_write_one_port(comment, addr, word, "1" * self.num_wmasks, port) self.stored_words[addr] = word w_addrs.append(addr) elif op == "partial_write": # write only to a word that's been written to - (addr,old_word) = self.get_data() + (addr, old_word) = self.get_data() # two ports cannot write to the same address if addr in w_addrs: self.add_noop_one_port(port) @@ -183,7 +177,7 @@ class functional(simulation): self.stored_words[addr] = new_word w_addrs.append(addr) else: - (addr,word) = random.choice(list(self.stored_words.items())) + (addr, word) = random.choice(list(self.stored_words.items())) # The write driver is not sized sufficiently to drive through the two # bitcell access transistors to the read port. So, for now, we do not allow # a simultaneous write and read to the same address on different ports. This @@ -191,7 +185,7 @@ class functional(simulation): if addr in w_addrs: self.add_noop_one_port(port) else: - comment = self.gen_cycle_comment("read", word, addr, "0"*self.num_wmasks, port, self.t_current) + comment = self.gen_cycle_comment("read", word, addr, "0" * self.num_wmasks, port, self.t_current) self.add_read_one_port(comment, addr, port) self.add_read_check(word, port) @@ -199,7 +193,7 @@ class functional(simulation): self.t_current += self.period # Last cycle idle needed to correctly measure the value on the second to last clock edge - comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, "0"*self.num_wmasks, 0, self.t_current) + comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current) self.add_noop_all_ports(comment) def gen_masked_data(self, old_word, word, wmask): @@ -213,7 +207,7 @@ class functional(simulation): if wmask[bit] == "0": lower = bit * self.write_size upper = lower + self.write_size - 1 - new_word = new_word[:lower] + old_word[lower:upper+1] + new_word[upper + 1:] + new_word = new_word[:lower] + old_word[lower:upper + 1] + new_word[upper + 1:] return new_word @@ -223,7 +217,7 @@ class functional(simulation): self.check except: self.check = 0 - self.read_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, self.check]) + self.read_check.append([word, "{0}{1}".format(self.dout_name, port), self.t_current + self.period, self.check]) self.check += 1 def read_stim_results(self): @@ -231,7 +225,7 @@ class functional(simulation): for (word, dout_port, eo_period, check) in self.read_check: sp_read_value = "" for bit in range(self.word_size + self.num_spare_cols): - value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check)) + value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(), bit, check)) if value > self.v_high: sp_read_value = "1" + sp_read_value elif value < self.v_low: @@ -282,25 +276,24 @@ class functional(simulation): # wmask must be reversed since a python list goes right to left and sram bits go left to right. return wmask[::-1] - def gen_data(self): """ Generates a random word to write. """ if not self.num_spare_cols: - random_value = random.randint(0,(2**(self.word_size))-1) + random_value = random.randint(0, (2 ** self.word_size) - 1) else: - random_value1 = random.randint(0,(2**(self.word_size))-1) - random_value2 = random.randint(0,(2**(self.num_spare_cols))-1) + random_value1 = random.randint(0, (2 ** self.word_size) - 1) + random_value2 = random.randint(0, (2 ** self.num_spare_cols) - 1) random_value = random_value1 + random_value2 - data_bits = self.convert_to_bin(random_value,False) + data_bits = self.convert_to_bin(random_value, False) return data_bits def gen_addr(self): """ Generates a random address value to write to. """ if self.num_spare_rows==0: - random_value = random.randint(0,(2**self.addr_size)-1) + random_value = random.randint(0, (2 ** self.addr_size) - 1) else: - random_value = random.randint(0,((2**(self.addr_size-1)-1))+(self.num_spare_rows * self.words_per_row)) - addr_bits = self.convert_to_bin(random_value,True) + random_value = random.randint(0, ((2 ** (self.addr_size - 1) - 1)) + (self.num_spare_rows * self.words_per_row)) + addr_bits = self.convert_to_bin(random_value, True) return addr_bits def get_data(self): @@ -308,36 +301,36 @@ class functional(simulation): # Used for write masks since they should be writing to previously written addresses addr = random.choice(list(self.stored_words.keys())) word = self.stored_words[addr] - return (addr,word) + return (addr, word) - def convert_to_bin(self,value,is_addr): + def convert_to_bin(self, value, is_addr): """ Converts addr & word to usable binary values. """ - new_value = str.replace(bin(value),"0b","") + new_value = str.replace(bin(value), "0b", "") if(is_addr): expected_value = self.addr_size else: expected_value = self.word_size + self.num_spare_cols - for i in range (expected_value - len(new_value)): + for i in range(expected_value - len(new_value)): new_value = "0" + new_value - #print("Binary Conversion: {} to {}".format(value, new_value)) - return new_value + # print("Binary Conversion: {} to {}".format(value, new_value)) + return new_value def write_functional_stimulus(self): """ Writes SPICE stimulus. """ temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) - self.sf = open(temp_stim,"w") + self.sf = open(temp_stim, "w") self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period)) - self.stim = stimuli(self.sf,self.corner) + self.stim = stimuli(self.sf, self.corner) - #Write include statements + # Write include statements self.stim.write_include(self.sp_file) - #Write Vdd/Gnd statements + # Write Vdd/Gnd statements self.sf.write("\n* Global Power Supplies\n") self.stim.write_supply() - #Instantiate the SRAM + # Instantiate the SRAM self.sf.write("\n* Instantiation of the SRAM\n") self.stim.inst_model(pins=self.pins, model_name=self.sram.name) @@ -362,7 +355,7 @@ class functional(simulation): for comment in self.fn_cycle_comments: self.sf.write("*{}\n".format(comment)) - # Generate data input bits + # Generate data input bits self.sf.write("\n* Generation of data and address signals\n") for port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): @@ -378,10 +371,10 @@ class functional(simulation): # Generate control signals self.sf.write("\n * Generation of control signals\n") for port in self.all_ports: - self.stim.gen_pwl("CSB{}".format(port), self.cycle_times , self.csb_values[port], self.period, self.slew, 0.05) + self.stim.gen_pwl("CSB{}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) for port in self.readwrite_ports: - self.stim.gen_pwl("WEB{}".format(port), self.cycle_times , self.web_values[port], self.period, self.slew, 0.05) + self.stim.gen_pwl("WEB{}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) # Generate wmask bits for port in self.write_ports: @@ -416,11 +409,11 @@ class functional(simulation): # Generate dout value measurements self.sf.write("\n * Generation of dout measurements\n") for (word, dout_port, eo_period, check) in self.read_check: - t_intital = eo_period - 0.01*self.period - t_final = eo_period + 0.01*self.period + t_intital = eo_period - 0.01 * self.period + t_final = eo_period + 0.01 * self.period for bit in range(self.word_size + self.num_spare_cols): - self.stim.gen_meas_value(meas_name="V{0}_{1}ck{2}".format(dout_port,bit,check), - dout="{0}_{1}".format(dout_port,bit), + self.stim.gen_meas_value(meas_name="V{0}_{1}ck{2}".format(dout_port, bit, check), + dout="{0}_{1}".format(dout_port, bit), t_intital=t_intital, t_final=t_final) @@ -450,7 +443,7 @@ class functional(simulation): # Generate new graph every analysis as edges might change depending on test bit self.graph = graph_util.timing_graph() self.sram_spc_name = "X{}".format(self.sram.name) - self.sram.build_graph(self.graph,self.sram_spc_name,self.pins) + self.sram.build_graph(self.graph, self.sram_spc_name, self.pins) # FIXME: refactor to share with delay.py def set_internal_spice_names(self): @@ -458,17 +451,17 @@ class functional(simulation): # For now, only testing these using first read port. port = self.read_ports[0] - self.graph.get_all_paths('{}{}'.format("clk", port), + self.graph.get_all_paths('{}{}'.format("clk", port), '{}{}_{}'.format(self.dout_name, port, 0).lower()) - self.sen_name = self.get_sen_name(self.graph.all_paths) - debug.info(2,"s_en name = {}".format(self.sen_name)) + self.sen_name = self.get_sen_name(self.graph.all_paths) + debug.info(2, "s_en name = {}".format(self.sen_name)) - self.bl_name,self.br_name = self.get_bl_name(self.graph.all_paths, port) - debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name)) + self.bl_name, self.br_name = self.get_bl_name(self.graph.all_paths, port) + debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name)) - self.q_name,self.qbar_name = self.get_bit_name() - debug.info(2,"q name={}\nqbar name={}".format(self.q_name,self.qbar_name)) + self.q_name, self.qbar_name = self.get_bit_name() + debug.info(2, "q name={}\nqbar name={}".format(self.q_name, self.qbar_name)) def get_bit_name(self): """ Get a bit cell name """ @@ -476,10 +469,10 @@ class functional(simulation): storage_names = cell_inst.mod.get_storage_net_names() debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes" "supported for characterization. Storage nets={}").format(storage_names)) - q_name = cell_name+'.'+str(storage_names[0]) - qbar_name = cell_name+'.'+str(storage_names[1]) + q_name = cell_name + '.' + str(storage_names[0]) + qbar_name = cell_name + '.' + str(storage_names[1]) - return (q_name,qbar_name) + return (q_name, qbar_name) # FIXME: refactor to share with delay.py def get_sen_name(self, paths): @@ -489,29 +482,28 @@ class functional(simulation): """ sa_mods = factory.get_mods(OPTS.sense_amp) - # Any sense amp instantiated should be identical, any change to that + # Any sense amp instantiated should be identical, any change to that # will require some identification to determine the mod desired. debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.") enable_name = sa_mods[0].get_enable_name() sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0]) - return sen_name + return sen_name # FIXME: refactor to share with delay.py def get_bl_name(self, paths, port): """Gets the signal name associated with the bitlines in the bank.""" - cell_mod = factory.create(module_type=OPTS.bitcell) + cell_mod = factory.create(module_type=OPTS.bitcell) cell_bl = cell_mod.get_bl_name(port) cell_br = cell_mod.get_br_name(port) - bl_found = False # Only a single path should contain a single s_en name. Anything else is an error. bl_names = [] exclude_set = self.get_bl_name_search_exclusions() for int_net in [cell_bl, cell_br]: bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set)) - return bl_names[0], bl_names[1] + return bl_names[0], bl_names[1] def get_bl_name_search_exclusions(self): """Gets the mods as a set which should be excluded while searching for name.""" @@ -520,9 +512,9 @@ class functional(simulation): # so it makes the search awkward return set(factory.get_mods(OPTS.replica_bitline)) - def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None): + def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None): """ - Finds a single alias for the int_net in given paths. + Finds a single alias for the int_net in given paths. More or less hits cause an error """ @@ -530,14 +522,14 @@ class functional(simulation): for path in paths: aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, int_net, mod, exclusion_set) if net_found and len(aliases) >= 1: - debug.error('Found multiple paths with {} net.'.format(int_net),1) + debug.error('Found multiple paths with {} net.'.format(int_net), 1) elif len(aliases) > 1: - debug.error('Found multiple {} nets in single path.'.format(int_net),1) + debug.error('Found multiple {} nets in single path.'.format(int_net), 1) elif not net_found and len(aliases) == 1: path_net_name = aliases[0] net_found = True if not net_found: - debug.error("Could not find {} net in timing paths.".format(int_net),1) + debug.error("Could not find {} net in timing paths.".format(int_net), 1) - return path_net_name + return path_net_name From 231f90f4923510f7691d537a12a10ddc11af14e2 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 19 Jun 2020 06:47:46 -0700 Subject: [PATCH 119/206] Fix missing space in ptx spice line --- compiler/pgates/ptx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index c4c53a70..fb127158 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -134,7 +134,7 @@ class ptx(design.design): if OPTS.tech_name == "sky130": # sky130 technology is in microns, also needs mult parameter (self.tx_width, self.mults) = pgate.bin_width(self.tx_type, self.tx_width) - main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3}".format(spice[self.tx_type], + main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], self.mults, self.tx_width, drc("minwidth_poly")) From 94c480911b9f3027a647b4f076494f8412c9b8ea Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 19 Jun 2020 07:09:15 -0700 Subject: [PATCH 120/206] ngspice raw save doesn't work with measures --- compiler/characterizer/stimuli.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 70152a54..11dc449a 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -302,10 +302,12 @@ class stimuli(): OPTS.openram_temp) valid_retcode=0 else: - # ngspice 27+ supports threading with "set num_threads=4" in the stimulus file or a .spiceinit - cmd = "{0} -b -r {2}timing.raw -o {2}timing.lis {1}".format(OPTS.spice_exe, - temp_stim, - OPTS.openram_temp) + # ngspice 27+ supports threading with "set num_threads=4" in the stimulus file or a .spiceinit + # Measurements can't be made with a raw file set in ngspice + # -r {2}timing.raw + cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe, + temp_stim, + OPTS.openram_temp) # for some reason, ngspice-25 returns 1 when it only has acceptable warnings valid_retcode=1 From 617a84d4b89d8210a289524985b77da337b1a5a3 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 19 Jun 2020 07:15:27 -0700 Subject: [PATCH 121/206] Fix output name of magic gds filter --- compiler/verify/calibre.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 1b9d296e..d4f31960 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -192,7 +192,7 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False): if OPTS.tech_name == "sky130": shutil.copy(gds_name, OPTS.openram_temp + "temp.gds") from magic import filter_gds - filter_gds(cell_name, OPTS.openram_temp + "temp.gds", gds_name) + filter_gds(cell_name, OPTS.openram_temp + "temp.gds", OPTS.openram_temp + cell_name + ".gds") else: # Copy file to local dir if it isn't already if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): From 239b3ea007c55123bcd6a1107b604c0d129cae0c Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 19 Jun 2020 08:49:48 -0700 Subject: [PATCH 122/206] Make wmask test a 1rw/1r --- ...k_1w_1r_func_test.py => 22_sram_1rw_1r_wmask_func_test.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename compiler/tests/{22_sram_wmask_1w_1r_func_test.py => 22_sram_1rw_1r_wmask_func_test.py} (97%) diff --git a/compiler/tests/22_sram_wmask_1w_1r_func_test.py b/compiler/tests/22_sram_1rw_1r_wmask_func_test.py similarity index 97% rename from compiler/tests/22_sram_wmask_1w_1r_func_test.py rename to compiler/tests/22_sram_1rw_1r_wmask_func_test.py index b5d83654..07cff70e 100755 --- a/compiler/tests/22_sram_wmask_1w_1r_func_test.py +++ b/compiler/tests/22_sram_1rw_1r_wmask_func_test.py @@ -26,8 +26,8 @@ class sram_wmask_1w_1r_func_test(openram_test): OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False - OPTS.num_rw_ports = 0 - OPTS.num_w_ports = 1 + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 globals.setup_bitcell() From 5872f553e1b02cd385cf788c2b99b269ae5e7b15 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 19 Jun 2020 08:53:35 -0700 Subject: [PATCH 123/206] Rename tests for consistency --- ...nomux_func_test.py => 22_sram_1bank_nomux_1rw_1r_func_test.py} | 0 ...wmask_func_test.py => 22_sram_1bank_wmask_1rw_1r_func_test.py} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename compiler/tests/{22_sram_1rw_1r_1bank_nomux_func_test.py => 22_sram_1bank_nomux_1rw_1r_func_test.py} (100%) rename compiler/tests/{22_sram_1rw_1r_wmask_func_test.py => 22_sram_1bank_wmask_1rw_1r_func_test.py} (100%) diff --git a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py b/compiler/tests/22_sram_1bank_nomux_1rw_1r_func_test.py similarity index 100% rename from compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py rename to compiler/tests/22_sram_1bank_nomux_1rw_1r_func_test.py diff --git a/compiler/tests/22_sram_1rw_1r_wmask_func_test.py b/compiler/tests/22_sram_1bank_wmask_1rw_1r_func_test.py similarity index 100% rename from compiler/tests/22_sram_1rw_1r_wmask_func_test.py rename to compiler/tests/22_sram_1bank_wmask_1rw_1r_func_test.py From a2d160dbf5ec8d8bbcc254db071a80b3ee48c075 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 19 Jun 2020 13:40:45 -0700 Subject: [PATCH 124/206] Copy magic config for filter code --- compiler/verify/magic.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index d19537a5..e20c0499 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -37,6 +37,14 @@ def filter_gds(cell_name, input_gds, output_gds): """ Run the gds through magic for any layer processing """ global OPTS + # Copy .magicrc file into temp dir + magic_file = OPTS.openram_tech + "mag_lib/.magicrc" + if os.path.exists(magic_file): + shutil.copy(magic_file, OPTS.openram_temp) + else: + debug.warning("Could not locate .magicrc file: {}".format(magic_file)) + + run_file = OPTS.openram_temp + "run_filter.sh" f = open(run_file, "w") f.write("#!/bin/sh\n") From 208c6526534e4abfd69b0e1ca147f4d90440965b Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Fri, 19 Jun 2020 13:59:33 -0700 Subject: [PATCH 125/206] added error for sky130 with invalid x mirroring (for lvs) --- compiler/modules/replica_column.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 603bebfe..3a58668c 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -36,6 +36,10 @@ class replica_column(design.design): "Replica bit cannot be the dummy row.") debug.check(replica_bit <= left_rbl or replica_bit >= self.total_size - right_rbl - 1, "Replica bit cannot be in the regular array.") + if OPTS.tech_name == "sky130": + debug.check(rows % 2 == 0 and (left_rbl + 1) % 2 == 0, + "sky130 currently requires rows to be even and to start with X mirroring" + + " (left_rbl must be odd) for LVS.") self.create_netlist() if not OPTS.netlist_only: From a13d5359455bff97198966d19ab233a0b6683594 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 22 Jun 2020 11:33:02 -0700 Subject: [PATCH 126/206] PEP8 cleanup --- compiler/base/hierarchy_design.py | 7 +++---- compiler/base/hierarchy_spice.py | 13 ++++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 87331315..20c40f21 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -27,8 +27,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # If we have a separate lvs directory, then all the lvs files # should be in there (all or nothing!) lvs_dir = OPTS.openram_tech + "lvs_lib/" - # Calibre will do the scaling in s8 - if os.path.exists(lvs_dir): # and OPTS.lvs_exe[0]!="calibre": + if os.path.exists(lvs_dir): self.lvs_file = lvs_dir + name + ".sp" else: self.lvs_file = self.sp_file @@ -45,7 +44,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if i.name == inst.name: break else: - debug.error("Couldn't find instance {0}".format(inst_name), -1) + debug.error("Couldn't find instance {0}".format(inst.name), -1) inst_map = inst.mod.pin_map return inst_map @@ -181,7 +180,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): """Given a list of nets, will compare the internal alias of a mod to determine if the nets have a connection to this mod's net (but not inst). """ - if exclusion_set == None: + if not exclusion_set: exclusion_set = set() try: self.name_dict diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 00d7ad44..0e41c4b0 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -10,9 +10,9 @@ import re import os import math import tech -from delay_data import * -from wire_spice_model import * -from power_data import * +from delay_data import delay_data +from wire_spice_model import wire_spice_model +from power_data import power_data import logical_effort @@ -263,7 +263,10 @@ class spice(): Recursive spice subcircuit write; Writes the spice subcircuit from the library or the dynamically generated one """ + if not self.spice: + # If spice isn't defined, we dynamically generate one. + # recursively write the modules for i in self.mods: if self.contains(i, usedMODS): @@ -316,7 +319,7 @@ class spice(): sp.write(".ENDS {0}\n".format(self.name)) else: - # write the subcircuit itself + # If spice is a hard module, output the spice file contents. # Including the file path makes the unit test fail for other users. # if os.path.isfile(self.sp_file): # sp.write("\n* {0}\n".format(self.sp_file)) @@ -356,7 +359,7 @@ class spice(): stage_effort = self.get_stage_effort(relative_cap) # If it fails, then keep running with a valid object. - if stage_effort == None: + if not stage_effort: return delay_data(0.0, 0.0) abs_delay = stage_effort.get_absolute_delay() From 54120f8405d02713fb73ab698cd22e278c266866 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 22 Jun 2020 12:35:37 -0700 Subject: [PATCH 127/206] Add option for removing subckt/instances of cells for row/col caps --- compiler/base/hierarchy_spice.py | 9 ++++++++- compiler/bitcells/col_cap_bitcell_1rw_1r.py | 1 + compiler/bitcells/row_cap_bitcell_1rw_1r.py | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 0e41c4b0..5ba60435 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -40,6 +40,8 @@ class spice(): # THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the # Spice format) self.conns = [] + # If this is set, it will out output subckt or isntances of this (for row/col caps etc.) + self.no_instances = False # Keep track of any comments to add the the spice try: self.commments @@ -264,7 +266,9 @@ class spice(): Writes the spice subcircuit from the library or the dynamically generated one """ - if not self.spice: + if self.no_instances: + return + elif not self.spice: # If spice isn't defined, we dynamically generate one. # recursively write the modules @@ -303,6 +307,9 @@ class spice(): # these are wires and paths if self.conns[i] == []: continue + # Instance with no devices in it needs no subckt/instance + if self.insts[i].mod.no_instances: + continue if lvs_netlist and hasattr(self.insts[i].mod, "lvs_device"): sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name, " ".join(self.conns[i]))) diff --git a/compiler/bitcells/col_cap_bitcell_1rw_1r.py b/compiler/bitcells/col_cap_bitcell_1rw_1r.py index 315ad23f..01818a12 100644 --- a/compiler/bitcells/col_cap_bitcell_1rw_1r.py +++ b/compiler/bitcells/col_cap_bitcell_1rw_1r.py @@ -41,3 +41,4 @@ class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base): self.height = col_cap_bitcell_1rw_1r.height self.pin_map = col_cap_bitcell_1rw_1r.pin_map self.add_pin_types(self.type_list) + self.no_instances = True diff --git a/compiler/bitcells/row_cap_bitcell_1rw_1r.py b/compiler/bitcells/row_cap_bitcell_1rw_1r.py index b50629f0..f7a3a687 100644 --- a/compiler/bitcells/row_cap_bitcell_1rw_1r.py +++ b/compiler/bitcells/row_cap_bitcell_1rw_1r.py @@ -41,3 +41,4 @@ class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base): self.height = row_cap_bitcell_1rw_1r.height self.pin_map = row_cap_bitcell_1rw_1r.pin_map self.add_pin_types(self.type_list) + self.no_instances = True From 0926eab9f54f141785f4898864985ed36b16f8a1 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 22 Jun 2020 12:55:18 -0700 Subject: [PATCH 128/206] PEP8 formatting --- compiler/modules/col_cap_array.py | 24 +++++++-------------- compiler/modules/row_cap_array.py | 35 +++++++++++-------------------- 2 files changed, 19 insertions(+), 40 deletions(-) diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index d74ab80a..ee9302d8 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -17,6 +17,7 @@ class col_cap_array(bitcell_base_array): super().__init__(cols, rows, name, column_offset) self.mirror = mirror + self.no_instances = True self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -47,8 +48,8 @@ class col_cap_array(bitcell_base_array): for col in range(self.column_size): for row in range(self.row_size): name = "bit_r{0}_c{1}".format(row, col) - self.cell_inst[row,col]=self.add_inst(name=name, - mod=self.dummy_cell) + self.cell_inst[row, col]=self.add_inst(name=name, + mod=self.dummy_cell) self.connect_inst(self.get_bitcell_pins(col, row)) def get_bitcell_pins(self, col, row): @@ -73,31 +74,20 @@ class col_cap_array(bitcell_base_array): for col in range(self.column_size): for cell_column in column_list: - bl_pin = self.cell_inst[0,col].get_pin(cell_column) - self.add_layout_pin(text=cell_column+"_{0}".format(col), + bl_pin = self.cell_inst[0, col].get_pin(cell_column) + self.add_layout_pin(text=cell_column + "_{0}".format(col), layer=bl_pin.layer, - offset=bl_pin.ll().scale(1,0), + offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) # Add vdd/gnd via stacks for row in range(self.row_size): for col in range(self.column_size): - inst = self.cell_inst[row,col] + inst = self.cell_inst[row, col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): self.add_power_pin(name=pin.name, loc=pin.center(), start_layer=pin.layer) - - # def input_load(self): - # wl_wire = self.gen_wl_wire() - # return wl_wire.return_input_cap() - # - # def get_wordline_cin(self): - # """Get the relative input capacitance from the wordline connections in all the bitcell""" - # #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns - # bitcell_wl_cin = self.cell.get_wl_cin() - # total_cin = bitcell_wl_cin * self.column_size - # return total_cin diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index 2c7d4677..f108d86e 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -8,6 +8,7 @@ from sram_factory import factory from globals import OPTS from tech import cell_properties + class row_cap_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. @@ -15,7 +16,7 @@ class row_cap_array(bitcell_base_array): def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): super().__init__(cols, rows, name, column_offset) self.mirror = mirror - + self.no_instances = True self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -46,8 +47,8 @@ class row_cap_array(bitcell_base_array): for col in range(self.column_size): for row in range(1, self.row_size - 1): name = "bit_r{0}_c{1}".format(row, col) - self.cell_inst[row,col]=self.add_inst(name=name, - mod=self.dummy_cell) + self.cell_inst[row, col]=self.add_inst(name=name, + mod=self.dummy_cell) self.connect_inst(self.get_bitcell_pins(col, row)) def get_bitcell_pins(self, col, row): @@ -65,8 +66,8 @@ class row_cap_array(bitcell_base_array): def place_array(self, name_template, row_offset=0): # We increase it by a well enclosure so the precharges don't overlap our wells - self.height = self.row_size*self.cell.height - self.width = self.column_size*self.cell.width + self.height = self.row_size * self.cell.height + self.width = self.column_size * self.cell.width xoffset = 0.0 for col in range(self.column_size): @@ -74,7 +75,6 @@ class row_cap_array(bitcell_base_array): tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset) for row in range(1, self.row_size - 1): - name = name_template.format(row, col) tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset) if dir_x and dir_y: @@ -86,8 +86,8 @@ class row_cap_array(bitcell_base_array): else: dir_key = "" - self.cell_inst[row,col].place(offset=[tempx, tempy], - mirror=dir_key) + self.cell_inst[row, col].place(offset=[tempx, tempy], + mirror=dir_key) yoffset += self.cell.height xoffset += self.cell.width @@ -98,31 +98,20 @@ class row_cap_array(bitcell_base_array): for row in range(1, self.row_size - 1): for cell_row in row_list: - wl_pin = self.cell_inst[row,0].get_pin(cell_row) - self.add_layout_pin(text=cell_row+"_{0}".format(row), + wl_pin = self.cell_inst[row, 0].get_pin(cell_row) + self.add_layout_pin(text=cell_row + "_{0}".format(row), layer=wl_pin.layer, - offset=wl_pin.ll().scale(0,1), + offset=wl_pin.ll().scale(0, 1), width=self.width, height=wl_pin.height()) # Add vdd/gnd via stacks for row in range(1, self.row_size - 1): for col in range(self.column_size): - inst = self.cell_inst[row,col] + inst = self.cell_inst[row, col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): self.add_power_pin(name=pin.name, loc=pin.center(), start_layer=pin.layer) - - # def input_load(self): - # wl_wire = self.gen_wl_wire() - # return wl_wire.return_input_cap() - # - # def get_wordline_cin(self): - # """Get the relative input capacitance from the wordline connections in all the bitcell""" - # #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns - # bitcell_wl_cin = self.cell.get_wl_cin() - # total_cin = bitcell_wl_cin * self.column_size - # return total_cin From 40edbfa51f62789ee822085d23839114f69adf48 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 22 Jun 2020 15:41:59 -0700 Subject: [PATCH 129/206] Error out on single port in sky130 --- compiler/bitcells/bitcell.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index 4ed2d053..e91d8c2f 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -10,7 +10,7 @@ import utils from tech import GDS, layer from tech import cell_properties as props import bitcell_base - +from globals import OPTS class bitcell(bitcell_base.bitcell_base): """ @@ -50,6 +50,8 @@ class bitcell(bitcell_base.bitcell_base): self.pin_map = bitcell.pin_map self.add_pin_types(self.type_list) self.nets_match = self.do_nets_exist(self.storage_nets) + + debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells") def get_all_wl_names(self): """ Creates a list of all wordline pin names """ From 92fc30005c028db65376e784c57df1fb5d8043eb Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 22 Jun 2020 16:55:49 -0700 Subject: [PATCH 130/206] Use factory in and_dec tests --- compiler/tests/04_and2_dec_test.py | 11 +++++++---- compiler/tests/04_and3_dec_test.py | 11 +++++++---- compiler/tests/04_and4_dec_test.py | 13 ++++++++----- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/compiler/tests/04_and2_dec_test.py b/compiler/tests/04_and2_dec_test.py index 355d3b15..97ac5749 100755 --- a/compiler/tests/04_and2_dec_test.py +++ b/compiler/tests/04_and2_dec_test.py @@ -23,10 +23,13 @@ class and2_dec_test(openram_test): global verify import verify - import and2_dec - - debug.info(2, "Testing and2 gate 4x") - a = and2_dec.and2_dec(name="and2x4", size=4) + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing and2_dec gate") + a = factory.create(module_type="and2_dec") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_and3_dec_test.py b/compiler/tests/04_and3_dec_test.py index 7794f36b..ec83335b 100755 --- a/compiler/tests/04_and3_dec_test.py +++ b/compiler/tests/04_and3_dec_test.py @@ -23,10 +23,13 @@ class and3_dec_test(openram_test): global verify import verify - import and3_dec - - debug.info(2, "Testing and3 gate 4x") - a = and3_dec.and3_dec(name="and3x4", size=4) + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing and3_dec gate") + a = factory.create(module_type="and3_dec") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_and4_dec_test.py b/compiler/tests/04_and4_dec_test.py index 7794f36b..bdd91c40 100755 --- a/compiler/tests/04_and4_dec_test.py +++ b/compiler/tests/04_and4_dec_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class and3_dec_test(openram_test): +class and4_dec_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -23,10 +23,13 @@ class and3_dec_test(openram_test): global verify import verify - import and3_dec - - debug.info(2, "Testing and3 gate 4x") - a = and3_dec.and3_dec(name="and3x4", size=4) + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing and4_dec gate") + a = factory.create(module_type="and4_dec") self.local_check(a) globals.end_openram() From 7ea3366ef1b554b326202460a05001882b4d1389 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 22 Jun 2020 16:58:01 -0700 Subject: [PATCH 131/206] Disable magic filter in sky130 --- compiler/verify/calibre.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index d4f31960..0a975599 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -189,7 +189,7 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False): num_drc_runs += 1 # Filter the layouts through magic as a GDS filter for nsdm/psdm/nwell merging - if OPTS.tech_name == "sky130": + if OPTS.tech_name == "sky130" and False: shutil.copy(gds_name, OPTS.openram_temp + "temp.gds") from magic import filter_gds filter_gds(cell_name, OPTS.openram_temp + "temp.gds", OPTS.openram_temp + cell_name + ".gds") From 1a528f9739db719eded519d0afc682d2a3988fcc Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 23 Jun 2020 10:08:28 -0700 Subject: [PATCH 132/206] Skip and4_dec test --- compiler/tests/04_and4_dec_test.py | 2 ++ compiler/tests/{skip_tests_s8.txt => skip_tests_sky130.txt} | 0 2 files changed, 2 insertions(+) rename compiler/tests/{skip_tests_s8.txt => skip_tests_sky130.txt} (100%) diff --git a/compiler/tests/04_and4_dec_test.py b/compiler/tests/04_and4_dec_test.py index bdd91c40..ffd7788a 100755 --- a/compiler/tests/04_and4_dec_test.py +++ b/compiler/tests/04_and4_dec_test.py @@ -15,6 +15,8 @@ from globals import OPTS from sram_factory import factory import debug + +@unittest.skip("SKIPPING 04_and4_dec_test") class and4_dec_test(openram_test): def runTest(self): diff --git a/compiler/tests/skip_tests_s8.txt b/compiler/tests/skip_tests_sky130.txt similarity index 100% rename from compiler/tests/skip_tests_s8.txt rename to compiler/tests/skip_tests_sky130.txt From 031862c7495c20518a717fd55005ca98a5eeb226 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 23 Jun 2020 11:56:50 -0700 Subject: [PATCH 133/206] Add metal enclosure to base case of center via stack. --- compiler/base/hierarchy_layout.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index c9b6a7a8..9331f31d 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -599,6 +599,10 @@ class layout(): """ if from_layer == to_layer: + # In the case where we have no vias added, make sure that there is at least + # a metal enclosure. This helps with center-line path routing. + self.add_rect_center(layer=from_layer, + offset=offset) return last_via from_id = layer_indices[from_layer] From e849a9b973e3c316838140aa955db5ce08fb29c1 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 23 Jun 2020 14:53:24 -0700 Subject: [PATCH 134/206] Use different LVS libs based on tech and sky130 --- compiler/base/hierarchy_design.py | 8 ++++++-- compiler/pgates/ptx.py | 28 +++++++++++++--------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 20c40f21..ac3fb30b 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -11,7 +11,7 @@ import verify import debug import os from globals import OPTS - +import tech class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): """ @@ -26,7 +26,11 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # If we have a separate lvs directory, then all the lvs files # should be in there (all or nothing!) - lvs_dir = OPTS.openram_tech + "lvs_lib/" + try: + lvs_subdir = tech.lvs_lib + except AttributeError: + lvs_subdir = "lvs_lib" + lvs_dir = OPTS.openram_tech + lvs_subdir + "/" if os.path.exists(lvs_dir): self.lvs_file = lvs_dir + name + ".sp" else: diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index fb127158..9af08e61 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -131,8 +131,8 @@ class ptx(design.design): # be decided in the layout later. area_sd = 2.5 * self.poly_width * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width - if OPTS.tech_name == "sky130": - # sky130 technology is in microns, also needs mult parameter + if OPTS.tech_name == "sky130" and OPTS.lvs_exe[0] == "calibre": + # sky130 simulation cannot use the mult parameter in simulation (self.tx_width, self.mults) = pgate.bin_width(self.tx_type, self.tx_width) main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], self.mults, @@ -152,19 +152,17 @@ class ptx(design.design): self.spice_device = main_str + area_str self.spice.append("\n* ptx " + self.spice_device) - # LVS lib is always in SI units - if os.path.exists(OPTS.openram_tech + "lvs_lib"): - if OPTS.tech_name == "sky130": - # sky130 requires mult parameter too - self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly")) - else: - self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly")) + if OPTS.tech_name == "sky130" and OPTS.lvs_exe[0] == "calibre": + # sky130 requires mult parameter too + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) + else: + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) def setup_layout_constants(self): """ From 83001e1ab51e97d269194ffccf30e809c34e8094 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 23 Jun 2020 15:39:26 -0700 Subject: [PATCH 135/206] PEP8 formatting --- compiler/base/hierarchy_spice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 5ba60435..5a15fce5 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -215,7 +215,7 @@ class spice(): # We don't define self.lvs and will use self.spice if dynamically created # or they are the same file - if self.lvs_file!=self.sp_file and os.path.isfile(self.lvs_file): + if self.lvs_file != self.sp_file and os.path.isfile(self.lvs_file): debug.info(3, "opening {0}".format(self.lvs_file)) f = open(self.lvs_file) self.lvs = f.readlines() From cfa234a4d0307643f1da33bae846189d3fee3862 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 23 Jun 2020 15:39:42 -0700 Subject: [PATCH 136/206] Extra space between decoders for well spacing --- compiler/modules/hierarchical_decoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 8830d397..3233bdc8 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -152,7 +152,7 @@ class hierarchical_decoder(design.design): self.predecoder_width = self.pre2_4.width # How much space between each predecoder - self.predecoder_spacing = self.and2.height + self.predecoder_spacing = 2 * self.and2.height self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 \ + (self.no_of_pre2x4 + self.no_of_pre3x8 - 1) * self.predecoder_spacing From 22c821f5d868865910d4bffb82f8e23a0bf01373 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 23 Jun 2020 15:40:00 -0700 Subject: [PATCH 137/206] Change port_address test to 256 for riscv --- compiler/tests/18_port_address_1rw_1r_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_1rw_1r_test.py index ff09dec9..caf2cb96 100755 --- a/compiler/tests/18_port_address_1rw_1r_test.py +++ b/compiler/tests/18_port_address_1rw_1r_test.py @@ -30,8 +30,8 @@ class port_address_1rw_1r_test(openram_test): a = factory.create("port_address", cols=16, rows=16) self.local_check(a) - debug.info(1, "Port address 512 rows") - a = factory.create("port_address", cols=256, rows=512) + debug.info(1, "Port address 256 rows") + a = factory.create("port_address", cols=256, rows=256) self.local_check(a) globals.end_openram() From 4e83e8c648025c40898b785a29898228e2fd1966 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Tue, 23 Jun 2020 18:13:17 -0700 Subject: [PATCH 138/206] added contact to locali for wmask --- compiler/modules/write_mask_and_array.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 581f9484..f337ab7a 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -10,7 +10,7 @@ import debug from sram_factory import factory from vector import vector from globals import OPTS - +from tech import layer class write_mask_and_array(design.design): """ @@ -93,7 +93,7 @@ class write_mask_and_array(design.design): self.width = self.bitcell.width * self.columns self.height = self.and2.height - + for i in range(self.num_wmasks): base = vector(i * self.wmask_en_len, 0) self.and2_insts[i].place(base) @@ -121,16 +121,17 @@ class write_mask_and_array(design.design): for supply in ["gnd", "vdd"]: supply_pin=self.and2_insts[i].get_pin(supply) - self.add_power_pin(supply, supply_pin.center()) + if "li" in layer: + self.add_power_pin(supply, supply_pin.center(), start_layer="li", directions = ("H", "H")) + else: + self.add_power_pin(supply, supply_pin.center()) for supply in ["gnd", "vdd"]: supply_pin_left = self.and2_insts[0].get_pin(supply) supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply) self.add_path(supply_pin_left.layer, [supply_pin_left.lc(), supply_pin_right.rc()]) - + def get_cin(self): """Get the relative capacitance of all the input connections in the bank""" # The enable is connected to an and2 for every row. return self.and2.get_cin() * len(self.and2_insts) - - From 22ed725a35446fe3f10d9509d1f11d6d85f56cf9 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Tue, 23 Jun 2020 18:16:14 -0700 Subject: [PATCH 139/206] made 1rw_1r tests for write driver and wmask, fixed typo in portdata_wmask_1rw_1r_test --- .../10_write_driver_array_1rw_1r_test.py | 44 ++++++++++++++++ .../10_write_mask_and_array_1rw_1r_test.py | 51 +++++++++++++++++++ .../tests/18_port_data_wmask_1rw_1r_test.py | 5 ++ 3 files changed, 100 insertions(+) create mode 100644 compiler/tests/10_write_driver_array_1rw_1r_test.py create mode 100644 compiler/tests/10_write_mask_and_array_1rw_1r_test.py diff --git a/compiler/tests/10_write_driver_array_1rw_1r_test.py b/compiler/tests/10_write_driver_array_1rw_1r_test.py new file mode 100644 index 00000000..4acbf053 --- /dev/null +++ b/compiler/tests/10_write_driver_array_1rw_1r_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class write_driver_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing write_driver_array for columns=8, word_size=8") + a = factory.create(module_type="write_driver_array", columns=8, word_size=8) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=8") + a = factory.create(module_type="write_driver_array", columns=16, word_size=8) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/10_write_mask_and_array_1rw_1r_test.py b/compiler/tests/10_write_mask_and_array_1rw_1r_test.py new file mode 100644 index 00000000..73988db9 --- /dev/null +++ b/compiler/tests/10_write_mask_and_array_1rw_1r_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class write_mask_and_array_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4") + a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4) + self.local_check(a) + + debug.info(2, "Testing write_mask_and_array for columns=16, word_size=16, write_size=4") + a = factory.create(module_type="write_mask_and_array", columns=16, word_size=16, write_size=4) + self.local_check(a) + + debug.info(2, "Testing write_mask_and_array for columns=16, word_size=8, write_size=2") + a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_wmask_1rw_1r_test.py b/compiler/tests/18_port_data_wmask_1rw_1r_test.py index 52d1c4fb..74aa7fc0 100755 --- a/compiler/tests/18_port_data_wmask_1rw_1r_test.py +++ b/compiler/tests/18_port_data_wmask_1rw_1r_test.py @@ -22,6 +22,11 @@ class port_data_wmask_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + c = sram_config(word_size=16, write_size=4, num_words=16) From b3d1161957aa0a13f0af5b2764f8c53070dab151 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 08:19:25 -0700 Subject: [PATCH 140/206] Add u+x permissions to new tests --- compiler/tests/10_write_driver_array_1rw_1r_test.py | 0 compiler/tests/10_write_mask_and_array_1rw_1r_test.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 compiler/tests/10_write_driver_array_1rw_1r_test.py mode change 100644 => 100755 compiler/tests/10_write_mask_and_array_1rw_1r_test.py diff --git a/compiler/tests/10_write_driver_array_1rw_1r_test.py b/compiler/tests/10_write_driver_array_1rw_1r_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/10_write_mask_and_array_1rw_1r_test.py b/compiler/tests/10_write_mask_and_array_1rw_1r_test.py old mode 100644 new mode 100755 From a32b5b13e8852a9f68fafbe8fc888aa18fca2160 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 08:26:15 -0700 Subject: [PATCH 141/206] Rename nwell yoffset for consistency --- compiler/pgates/pgate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 09074960..104d7a06 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -150,7 +150,7 @@ class pgate(design.design): """ Extend the n/p wells to cover whole cell """ # This should match the cells in the cell library - self.nwell_y_offset = 0.48 * self.height + self.nwell_yoffset = 0.48 * self.height full_height = self.height + 0.5 * self.m1_width # FIXME: float rounding problem @@ -158,8 +158,8 @@ class pgate(design.design): # Add a rail width to extend the well to the top of the rail nwell_max_offset = max(self.find_highest_layer_coords("nwell").y, full_height) - nwell_position = vector(0, self.nwell_y_offset) - vector(self.well_extend_active, 0) - nwell_height = nwell_max_offset - self.nwell_y_offset + nwell_position = vector(0, self.nwell_yoffset) - vector(self.well_extend_active, 0) + nwell_height = nwell_max_offset - self.nwell_yoffset self.add_rect(layer="nwell", offset=nwell_position, width=self.width + 2 * self.well_extend_active, @@ -175,7 +175,7 @@ class pgate(design.design): pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y, -0.5 * self.m1_width) pwell_position = vector(-self.well_extend_active, pwell_min_offset) - pwell_height = self.nwell_y_offset - pwell_position.y + pwell_height = self.nwell_yoffset - pwell_position.y self.add_rect(layer="pwell", offset=pwell_position, width=self.width + 2 * self.well_extend_active, From cddb16dabce5c1d945c9bb80a21ea871ca564a5f Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 09:17:39 -0700 Subject: [PATCH 142/206] Separate active and poly contact to gate rule --- compiler/base/design.py | 3 ++- compiler/pgates/pnand2.py | 4 ++-- compiler/pgates/pnand3.py | 7 +++---- compiler/pgates/precharge.py | 2 +- compiler/pgates/ptx.py | 6 +++--- technology/freepdk45/tech/tech.py | 4 ++-- technology/scn4m_subm/tech/tech.py | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/base/design.py b/compiler/base/design.py index 1fded37e..2b2d7711 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -205,7 +205,8 @@ class design(hierarchy_design): print("poly_to_active", self.poly_to_active) print("poly_extend_active", self.poly_extend_active) print("poly_to_contact", self.poly_to_contact) - print("contact_to_gate", self.contact_to_gate) + print("active_contact_to_gate", self.active_contact_to_gate) + print("poly_contact_to_gate", self.poly_contact_to_gate) print("well_enclose_active", self.well_enclose_active) print("implant_enclose_active", self.implant_enclose_active) print("implant_space", self.implant_space) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 12323885..9cc530c0 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -184,7 +184,7 @@ class pnand2(pgate.pgate): # doesn't use nmos uy because that is calculated using offset + poly height active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height - active_to_poly_contact2 = active_top + drc("contact_to_gate") + 0.5 * self.route_layer_width + active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width self.inputA_yoffset = max(active_contact_to_poly_contact, active_to_poly_contact, active_to_poly_contact2) @@ -200,7 +200,7 @@ class pnand2(pgate.pgate): # active_contact_to_poly_contact = self.output_yoffset - self.route_layer_space - 0.5 * contact.poly_contact.second_layer_height # active_bottom = self.pmos1_inst.by() # active_to_poly_contact = active_bottom - self.poly_to_active - 0.5 * contact.poly_contact.first_layer_height - # active_to_poly_contact2 = active_bottom - drc("contact_to_gate") - 0.5 * self.route_layer_width + # active_to_poly_contact2 = active_bottom - self.poly_contact_to_gate - 0.5 * self.route_layer_width # self.inputB_yoffset = min(active_contact_to_poly_contact, # active_to_poly_contact, # active_to_poly_contact2) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index e227d7af..03652b9b 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -222,7 +222,7 @@ class pnand3(pgate.pgate): # doesn't use nmos uy because that is calculated using offset + poly height active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height - active_to_poly_contact2 = active_top + drc("contact_to_gate") + 0.5 * self.route_layer_width + active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width self.inputA_yoffset = max(active_contact_to_poly_contact, active_to_poly_contact, active_to_poly_contact2) @@ -233,15 +233,14 @@ class pnand3(pgate.pgate): "A", position="left") - # Put B right on the well line - self.inputB_yoffset = self.inputA_yoffset + non_contact_pitch + self.inputB_yoffset = self.inputA_yoffset + 1.2 * self.m3_pitch self.route_input_gate(self.pmos2_inst, self.nmos2_inst, self.inputB_yoffset, "B", position="center") - self.inputC_yoffset = self.inputB_yoffset + non_contact_pitch + self.inputC_yoffset = self.inputB_yoffset + 1.2 * self.m3_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index fdce1a35..dc016cab 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -196,7 +196,7 @@ class precharge(design.design): pin_offset = self.lower_pmos_inst.get_pin("G").lr() # This is an extra space down for some techs with contact to active spacing contact_space = max(self.poly_space, - self.contact_to_gate) + 0.5 * contact.poly_contact.first_layer_height + self.poly_contact_to_gate) + 0.5 * contact.poly_contact.first_layer_height offset = pin_offset - vector(0, contact_space) self.add_via_stack_center(from_layer="poly", to_layer=self.en_layer, diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 9af08e61..8be003b2 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -196,7 +196,7 @@ class ptx(design.design): # This is the spacing between the poly gates self.min_poly_pitch = self.poly_space + self.poly_width self.contacted_poly_pitch = self.poly_space + contact.poly_contact.width - self.contact_pitch = 2 * self.contact_to_gate + self.poly_width + self.contact_width + self.contact_pitch = 2 * self.active_contact_to_gate + self.poly_width + self.contact_width self.poly_pitch = max(self.min_poly_pitch, self.contacted_poly_pitch, self.contact_pitch) @@ -206,7 +206,7 @@ class ptx(design.design): # Active width is determined by enclosure on both ends and contacted pitch, # at least one poly and n-1 poly pitches self.active_width = 2 * self.end_to_contact + self.active_contact.width \ - + 2 * self.contact_to_gate + self.poly_width + (self.mults - 1) * self.poly_pitch + + 2 * self.active_contact_to_gate + self.poly_width + (self.mults - 1) * self.poly_pitch # Active height is just the transistor width self.active_height = self.tx_width @@ -321,7 +321,7 @@ class ptx(design.design): """ # poly is one contacted spacing from the end and down an extension poly_offset = self.contact_offset \ - + vector(0.5 * self.active_contact.width + 0.5 * self.poly_width + self.contact_to_gate, 0) + + vector(0.5 * self.active_contact.width + 0.5 * self.poly_width + self.active_contact_to_gate, 0) # poly_positions are the bottom center of the poly gates self.poly_positions = [] diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index c0379e44..6de01254 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -234,9 +234,9 @@ drc.add_enclosure("active", enclosure = 0.005) # CONTACT.6 Minimum spacing of contact and gate -drc["contact_to_gate"] = 0.0375 #changed from 0.035 +drc["active_contact_to_gate"] = 0.0375 #changed from 0.035 # CONTACT.7 Minimum spacing of contact and poly -drc["contact_to_poly"] = 0.090 +drc["poly_contact_to_gate"] = 0.090 # CONTACT.1 Minimum width of contact # CONTACT.2 Minimum spacing of contact diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 745b381e..55826ec5 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -217,9 +217,9 @@ drc.add_enclosure("active", layer = "contact", enclosure = _lambda_) # Reserved for other technologies -drc["contact_to_gate"] = 2*_lambda_ +drc["active_contact_to_gate"] = 2*_lambda_ # 5.4 Minimum spacing to gate of transistor -drc["contact_to_poly"] = 2*_lambda_ +drc["poly_contact_to_gate"] = 2*_lambda_ # 6.1 Exact contact size # 5.3 Minimum contact spacing From 13409083301834f77115e4abd3a417dff5009e4f Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 09:24:26 -0700 Subject: [PATCH 143/206] Remove fudge factor for pin spacing --- compiler/pgates/pnand3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 03652b9b..1fd5146f 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -233,14 +233,14 @@ class pnand3(pgate.pgate): "A", position="left") - self.inputB_yoffset = self.inputA_yoffset + 1.2 * self.m3_pitch + self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch self.route_input_gate(self.pmos2_inst, self.nmos2_inst, self.inputB_yoffset, "B", position="center") - self.inputC_yoffset = self.inputB_yoffset + 1.2 * self.m3_pitch + self.inputC_yoffset = self.inputB_yoffset + self.m3_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, From 98ec9442c61966cd602eebfddf68990dcddcdc30 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 10:00:00 -0700 Subject: [PATCH 144/206] Add npc enclosure for pnand2, pnand3, pnor2 --- compiler/pgates/pgate.py | 27 +++++++++++++++++++++++++++ compiler/pgates/pnand2.py | 4 ++++ compiler/pgates/pnand3.py | 3 +++ compiler/pgates/pnor2.py | 3 +++ 4 files changed, 37 insertions(+) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 104d7a06..bb105f36 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -43,6 +43,9 @@ class pgate(design.design): self.route_layer_space = getattr(self, "{}_space".format(self.route_layer)) self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer)) + # hack for enclosing input pin with npc + self.input_pin_vias = [] + # This is the space from a S/D contact to the supply rail contact_to_vdd_rail_space = 0.5 * self.route_layer_width + self.route_layer_space # This is a poly-to-poly of a flipped cell @@ -132,6 +135,8 @@ class pgate(design.design): offset=contact_offset, directions=directions) + self.input_pin_vias.append(via) + self.add_layout_pin_rect_center(text=name, layer=self.route_layer, offset=contact_offset, @@ -146,6 +151,28 @@ class pgate(design.design): height=contact.poly_contact.first_layer_width, width=left_gate_offset.x - contact_offset.x) + def enclose_npc(self): + """ Enclose the poly contacts with npc layer """ + ll = None + ur = None + for via in self.input_pin_vias: + # Find ll/ur + if not ll: + ll = via.ll() + else: + ll = ll.min(via.ll()) + if not ur: + ur = via.ur() + else: + ur = ur.max(via.ur()) + + npc_enclose_poly = drc("npc_enclose_poly") + npc_enclose_offset = vector(npc_enclose_poly, npc_enclose_poly) + self.add_rect(layer="npc", + offset=ll - npc_enclose_offset, + width=(ur.x - ll.x) + 2 * npc_enclose_poly, + height=(ur.y - ll.y) + 2 * npc_enclose_poly) + def extend_wells(self): """ Extend the n/p wells to cover whole cell """ diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 9cc530c0..6a65f721 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -212,6 +212,10 @@ class pnand2(pgate.pgate): "B", position="center") + if OPTS.tech_name == "sky130": + self.enclose_npc() + + def route_output(self): """ Route the Z output """ diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 1fd5146f..0c2bc081 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -246,6 +246,9 @@ class pnand3(pgate.pgate): self.inputC_yoffset, "C", position="right") + + if OPTS.tech_name == "sky130": + self.enclose_npc() def route_output(self): """ Route the Z output """ diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 3cceb7f4..7f75ddd1 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -211,6 +211,9 @@ class pnor2(pgate.pgate): self.output_yoffset = self.inputA_yoffset + self.m1_nonpref_pitch + if OPTS.tech_name == "sky130": + self.enclose_npc() + def route_output(self): """ Route the Z output """ # PMOS2 (right) drain From 93d65e84e1ae95a4c754520a3fe42caccdc1894e Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 10:26:49 -0700 Subject: [PATCH 145/206] Fix power pin layer problems in delay line --- compiler/modules/delay_chain.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index ad9bc1cc..c07395f5 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -178,10 +178,14 @@ class delay_chain(design.design): load_list = self.load_inst_map[inst] for pin_name in ["vdd", "gnd"]: pin = load_list[0].get_pin(pin_name) - self.add_power_pin(pin_name, pin.rc() - vector(self.m1_pitch, 0)) + self.add_power_pin(pin_name, + pin.rc() - vector(self.m1_pitch, 0), + start_layer=pin.layer) - pin = load_list[-1].get_pin(pin_name) - self.add_power_pin(pin_name, pin.rc() - vector(0.5 * self.m1_pitch, 0)) + pin = load_list[-2].get_pin(pin_name) + self.add_power_pin(pin_name, + pin.rc() - vector(self.m1_pitch, 0), + start_layer=pin.layer) def add_layout_pins(self): From 4bc3df8931602269c242457ceed2f95c4da8e4eb Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 11:54:36 -0700 Subject: [PATCH 146/206] Add get_tx_insts and expand add_enclosure --- compiler/base/hierarchy_layout.py | 75 +++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 9331f31d..f90b3129 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -269,6 +269,23 @@ class layout(): width, end.y - start.y) + def get_tx_insts(self, tx_type=None): + """ + Return a list of the instances of given tx type. + """ + tx_list = [] + for i in self.insts: + try: + if tx_type and i.mod.tx_type == tx_type: + tx_list.append(i) + elif not tx_type: + if i.mod.tx_type == "nmos" or i.mod.tx_type == "pmos": + tx_list.append(i) + except AttributeError: + pass + + return tx_list + def get_pin(self, text): """ Return the pin or list of pins @@ -1301,26 +1318,48 @@ class layout(): height=ur.y - ll.y, width=ur.x - ll.x) - def add_enclosure(self, insts, layer="nwell"): - """ Add a layer that surrounds the given instances. Useful + def add_enclosure(self, insts, layer="nwell", extend=0, leftx=None, rightx=None, topy=None, boty=None): + """ + Add a layer that surrounds the given instances. Useful for creating wells, for example. Doesn't check for minimum widths or - spacings.""" + spacings. Extra arg can force a dimension to one side left/right top/bot. + """ - xmin = insts[0].lx() - ymin = insts[0].by() - xmax = insts[0].rx() - ymax = insts[0].uy() - for inst in insts: - xmin = min(xmin, inst.lx()) - ymin = min(ymin, inst.by()) - xmax = max(xmax, inst.rx()) - ymax = max(ymax, inst.uy()) - - self.add_rect(layer=layer, - offset=vector(xmin, ymin), - width=xmax - xmin, - height=ymax - ymin) + if leftx != None: + xmin = leftx + else: + xmin = insts[0].lx() + for inst in insts: + xmin = min(xmin, inst.lx()) + xmin = xmin - extend + if boty != None: + ymin = boty + else: + ymin = insts[0].by() + for inst in insts: + ymin = min(ymin, inst.by()) + ymin = ymin - extend + if rightx != None: + xmax = rightx + else: + xmax = insts[0].rx() + for inst in insts: + xmax = max(xmax, inst.rx()) + xmax = xmax + extend + if topy != None: + ymax = topy + else: + ymax = insts[0].uy() + for inst in insts: + ymax = max(ymax, inst.uy()) + ymax = ymax + extend + rect = self.add_rect(layer=layer, + offset=vector(xmin, ymin), + width=xmax - xmin, + height=ymax - ymin) + return rect + def copy_power_pins(self, inst, name): """ This will copy a power pin if it is on the lowest power_grid layer. @@ -1337,7 +1376,7 @@ class layout(): else: self.add_power_pin(name, pin.center(), start_layer=pin.layer) - + def add_power_pin(self, name, loc, size=[1, 1], directions=None, start_layer="m1"): """ Add a single power pin from the lowest power_grid layer down to M1 (or li) at From e694622f2809bc588d94b0a28040534cd93fd293 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 11:54:59 -0700 Subject: [PATCH 147/206] use add_enclosure to extend implants --- compiler/pgates/pgate.py | 59 ++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index bb105f36..9fa8c916 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -135,8 +135,6 @@ class pgate(design.design): offset=contact_offset, directions=directions) - self.input_pin_vias.append(via) - self.add_layout_pin_rect_center(text=name, layer=self.route_layer, offset=contact_offset, @@ -151,27 +149,7 @@ class pgate(design.design): height=contact.poly_contact.first_layer_width, width=left_gate_offset.x - contact_offset.x) - def enclose_npc(self): - """ Enclose the poly contacts with npc layer """ - ll = None - ur = None - for via in self.input_pin_vias: - # Find ll/ur - if not ll: - ll = via.ll() - else: - ll = ll.min(via.ll()) - if not ur: - ur = via.ur() - else: - ur = ur.max(via.ur()) - - npc_enclose_poly = drc("npc_enclose_poly") - npc_enclose_offset = vector(npc_enclose_poly, npc_enclose_poly) - self.add_rect(layer="npc", - offset=ll - npc_enclose_offset, - width=(ur.x - ll.x) + 2 * npc_enclose_poly, - height=(ur.y - ll.y) + 2 * npc_enclose_poly) + return via def extend_wells(self): """ Extend the n/p wells to cover whole cell """ @@ -179,6 +157,7 @@ class pgate(design.design): # This should match the cells in the cell library self.nwell_yoffset = 0.48 * self.height full_height = self.height + 0.5 * self.m1_width + # FIXME: float rounding problem if "nwell" in layer: @@ -212,6 +191,8 @@ class pgate(design.design): offset=pwell_position, width=self.width + 2 * self.well_extend_active, height=pwell_height) + + self.extend_implants() def add_nwell_contact(self, pmos, pmos_pos): """ Add an nwell contact next to the given pmos device. """ @@ -267,6 +248,36 @@ class pgate(design.design): # Return the top of the well + def extend_implants(self): + """ + Add top-to-bottom implants for adjacency issues in s8. + """ + nmos_insts = self.get_tx_insts("nmos") + pmos_insts = self.get_tx_insts("pmos") + ntap_insts = [self.nwell_contact] + ptap_insts = [self.pwell_contact] + + self.add_enclosure(nmos_insts, + layer="nimplant", + extend=self.implant_enclose_active, + leftx=0, + boty=0) + self.add_enclosure(pmos_insts, + layer="pimplant", + extend=self.implant_enclose_active, + leftx=0, + topy=self.height) + self.add_enclosure(ntap_insts, + layer="nimplant", + extend=self.implant_enclose_active, + rightx=self.width, + topy=self.height) + self.add_enclosure(ptap_insts, + layer="pimplant", + extend=self.implant_enclose_active, + rightx=self.width, + boty=0) + def add_pwell_contact(self, nmos, nmos_pos): """ Add an pwell contact next to the given nmos device. """ @@ -295,7 +306,7 @@ class pgate(design.design): offset=contact_offset.scale(1, 0.5), width=self.pwell_contact.mod.second_layer_width, height=contact_offset.y) - + # Now add the full active and implant for the NMOS # active_offset = nmos_pos + vector(nmos.active_width,0) # This might be needed if the spacing between the actives From 6c523a75561cb3e4ff9e65b1dd0b953c4b028a37 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 11:55:44 -0700 Subject: [PATCH 148/206] use add_enclosure for npc contacts --- compiler/pgates/pnand2.py | 22 +++++++++++----------- compiler/pgates/pnand3.py | 32 ++++++++++++++++---------------- compiler/pgates/pnor2.py | 24 ++++++++++++------------ 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 6a65f721..3974d95b 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -189,11 +189,11 @@ class pnand2(pgate.pgate): active_to_poly_contact, active_to_poly_contact2) - self.route_input_gate(self.pmos1_inst, - self.nmos1_inst, - self.inputA_yoffset, - "A", - position="center") + apin = self.route_input_gate(self.pmos1_inst, + self.nmos1_inst, + self.inputA_yoffset, + "A", + position="center") self.inputB_yoffset = self.inputA_yoffset + 2 * self.m3_pitch # # active contact metal to poly contact metal spacing @@ -206,14 +206,14 @@ class pnand2(pgate.pgate): # active_to_poly_contact2) # This will help with the wells and the input/output placement - self.route_input_gate(self.pmos2_inst, - self.nmos2_inst, - self.inputB_yoffset, - "B", - position="center") + bpin = self.route_input_gate(self.pmos2_inst, + self.nmos2_inst, + self.inputB_yoffset, + "B", + position="center") if OPTS.tech_name == "sky130": - self.enclose_npc() + self.add_enclosure([apin, bpin], "npc", drc("npc_enclose_poly")) def route_output(self): diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 0c2bc081..ff988d88 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -227,28 +227,28 @@ class pnand3(pgate.pgate): active_to_poly_contact, active_to_poly_contact2) - self.route_input_gate(self.pmos1_inst, - self.nmos1_inst, - self.inputA_yoffset, - "A", - position="left") + apin = self.route_input_gate(self.pmos1_inst, + self.nmos1_inst, + self.inputA_yoffset, + "A", + position="left") self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch - self.route_input_gate(self.pmos2_inst, - self.nmos2_inst, - self.inputB_yoffset, - "B", - position="center") + bpin = self.route_input_gate(self.pmos2_inst, + self.nmos2_inst, + self.inputB_yoffset, + "B", + position="center") self.inputC_yoffset = self.inputB_yoffset + self.m3_pitch - self.route_input_gate(self.pmos3_inst, - self.nmos3_inst, - self.inputC_yoffset, - "C", - position="right") + cpin = self.route_input_gate(self.pmos3_inst, + self.nmos3_inst, + self.inputC_yoffset, + "C", + position="right") if OPTS.tech_name == "sky130": - self.enclose_npc() + self.add_enclosure([apin, bpin, cpin], "npc", drc("npc_enclose_poly")) def route_output(self): """ Route the Z output """ diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 7f75ddd1..908bba82 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -195,24 +195,24 @@ class pnor2(pgate.pgate): self.inputB_yoffset = bottom_pin_offset + self.m1_nonpref_pitch self.inputA_yoffset = self.inputB_yoffset + self.m1_nonpref_pitch - self.route_input_gate(self.pmos2_inst, - self.nmos2_inst, - self.inputB_yoffset, - "B", - position="right", - directions=("V", "V")) + bpin = self.route_input_gate(self.pmos2_inst, + self.nmos2_inst, + self.inputB_yoffset, + "B", + position="right", + directions=("V", "V")) # This will help with the wells and the input/output placement - self.route_input_gate(self.pmos1_inst, - self.nmos1_inst, - self.inputA_yoffset, - "A", - directions=("V", "V")) + apin = self.route_input_gate(self.pmos1_inst, + self.nmos1_inst, + self.inputA_yoffset, + "A", + directions=("V", "V")) self.output_yoffset = self.inputA_yoffset + self.m1_nonpref_pitch if OPTS.tech_name == "sky130": - self.enclose_npc() + self.add_enclosure([apin, bpin], "npc", drc("npc_enclose_poly")) def route_output(self): """ Route the Z output """ From ba92467fec2967a7f652c540c10ff80535aa3788 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 12:07:47 -0700 Subject: [PATCH 149/206] Add no well enclosure for techs without wells --- compiler/pgates/pgate.py | 62 +++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 9fa8c916..02a04b97 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -252,31 +252,47 @@ class pgate(design.design): """ Add top-to-bottom implants for adjacency issues in s8. """ + if self.add_wells: + rightx = None + else: + rightx = self.width + nmos_insts = self.get_tx_insts("nmos") + if len(nmos_insts) > 0: + self.add_enclosure(nmos_insts, + layer="nimplant", + extend=self.implant_enclose_active, + leftx=0, + rightx=rightx, + boty=0) + pmos_insts = self.get_tx_insts("pmos") - ntap_insts = [self.nwell_contact] - ptap_insts = [self.pwell_contact] - - self.add_enclosure(nmos_insts, - layer="nimplant", - extend=self.implant_enclose_active, - leftx=0, - boty=0) - self.add_enclosure(pmos_insts, - layer="pimplant", - extend=self.implant_enclose_active, - leftx=0, - topy=self.height) - self.add_enclosure(ntap_insts, - layer="nimplant", - extend=self.implant_enclose_active, - rightx=self.width, - topy=self.height) - self.add_enclosure(ptap_insts, - layer="pimplant", - extend=self.implant_enclose_active, - rightx=self.width, - boty=0) + if len(pmos_insts) > 0: + self.add_enclosure(pmos_insts, + layer="pimplant", + extend=self.implant_enclose_active, + leftx=0, + rightx=rightx, + topy=self.height) + + try: + ntap_insts = [self.nwell_contact] + self.add_enclosure(ntap_insts, + layer="nimplant", + extend=self.implant_enclose_active, + rightx=self.width, + topy=self.height) + except AttributeError: + pass + try: + ptap_insts = [self.pwell_contact] + self.add_enclosure(ptap_insts, + layer="pimplant", + extend=self.implant_enclose_active, + rightx=self.width, + boty=0) + except AttributeError: + pass def add_pwell_contact(self, nmos, nmos_pos): """ Add an pwell contact next to the given nmos device. """ From da900b89ba6759778942114c81068cb23d0b1c41 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 24 Jun 2020 13:48:30 -0700 Subject: [PATCH 150/206] Only expand implants in sky130 --- compiler/pgates/pgate.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 02a04b97..809ffbd9 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -191,8 +191,9 @@ class pgate(design.design): offset=pwell_position, width=self.width + 2 * self.well_extend_active, height=pwell_height) - - self.extend_implants() + + if OPTS.tech_name == "sky130": + self.extend_implants() def add_nwell_contact(self, pmos, pmos_pos): """ Add an nwell contact next to the given pmos device. """ From 57b6d49edb4f39d4c74c0f5587eb078f101c921b Mon Sep 17 00:00:00 2001 From: jcirimel Date: Thu, 25 Jun 2020 06:32:07 -0700 Subject: [PATCH 151/206] fix pinv size bining --- compiler/pgates/pgate.py | 2 +- compiler/pgates/pinv.py | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 09074960..ef1445c0 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -379,4 +379,4 @@ class pgate(design.design): return(scaled_bins) def bin_accuracy(self, ideal_width, width): - return abs(1-(ideal_width - width)/ideal_width) + return 1-abs((ideal_width - width)/ideal_width) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index de0ba8e4..1429e0c8 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -31,6 +31,9 @@ class pinv(pgate.pgate): height is usually the same as the 6t library cell and is measured from center of rail to rail. """ + # binning %error tracker + bin_count = 0 + bin_error = 0 def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True): @@ -44,7 +47,7 @@ class pinv(pgate.pgate): self.nmos_size = size self.pmos_size = beta * size self.beta = beta - + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): @@ -166,30 +169,37 @@ class pinv(pgate.pgate): valid_pmos = [] for bin in pmos_bins: - if self.bin_accuracy(self.pmos_width, bin[0]) > accuracy_requirement: + if abs(self.bin_accuracy(self.pmos_width, bin[0])) > accuracy_requirement and abs(self.bin_accuracy(self.pmos_width, bin[0])) <= 1: valid_pmos.append(bin) valid_pmos.sort(key = operator.itemgetter(1)) valid_nmos = [] for bin in nmos_bins: - if self.bin_accuracy(self.nmos_width, bin[0]) > accuracy_requirement: + if abs(self.bin_accuracy(self.nmos_width, bin[0])) > accuracy_requirement and abs(self.bin_accuracy(self.nmos_width, bin[0])) <= 1: valid_nmos.append(bin) valid_nmos.sort(key = operator.itemgetter(1)) for bin in valid_pmos: if bin[0]/bin[1] < pmos_height_available: self.pmos_width = bin[0]/bin[1] - pmos_mults = valid_pmos[0][1] + pmos_mults = bin[1] break for bin in valid_nmos: if bin[0]/bin[1] < nmos_height_available: self.nmos_width = bin[0]/bin[1] - nmos_mults = valid_pmos[0][1] + nmos_mults = bin[1] break self.tx_mults = max(pmos_mults, nmos_mults) - + debug.info(2, "prebinning {0} tx, target: {4}, found {1} x {2} = {3}".format("pmos", self.pmos_width, pmos_mults, self.pmos_width * pmos_mults, self.pmos_size * drc("minwidth_tx"))) + debug.info(2, "prebinning {0} tx, target: {4}, found {1} x {2} = {3}".format("nmos", self.nmos_width, nmos_mults, self.nmos_width * nmos_mults, self.nmos_size * drc("minwidth_tx"))) + pinv.bin_count += 1 + pinv.bin_error += abs((self.pmos_width * pmos_mults) - (self.pmos_size * drc("minwidth_tx"))/(self.pmos_size * drc("minwidth_tx"))) + pinv.bin_count += 1 + pinv.bin_error += abs((self.nmos_width * nmos_mults) - (self.nmos_size * drc("minwidth_tx"))/(self.nmos_size * drc("minwidth_tx"))) + debug.info(2, "pinv bin count: {0} pinv bin error: {1} percent error {2}".format(pinv.bin_count, pinv.bin_error, pinv.bin_error/pinv.bin_count)) + def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos = factory.create(module_type="ptx", From 59562f2b92563ab718228a629854ede31523e0fe Mon Sep 17 00:00:00 2001 From: jcirimel Date: Thu, 25 Jun 2020 06:44:07 -0700 Subject: [PATCH 152/206] move accuracy_requirement from techfile to config --- compiler/options.py | 2 +- compiler/pgates/pgate.py | 4 ++-- compiler/pgates/pinv.py | 6 +++--- compiler/pgates/pinv_dec.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/options.py b/compiler/options.py index 976595ad..d229b96e 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -58,7 +58,7 @@ class options(optparse.Values): delay_chain_stages = 9 delay_chain_fanout_per_stage = 4 - + accuracy_requirement = 0.75 ################### # Debug options. diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 80a568fc..7f834fc4 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -15,7 +15,7 @@ from vector import vector from globals import OPTS if(OPTS.tech_name == "sky130"): - from tech import nmos_bins, pmos_bins, accuracy_requirement + from tech import nmos_bins, pmos_bins class pgate(design.design): @@ -372,7 +372,7 @@ class pgate(design.design): select = -1 for i in reversed(range(0, len(scaled_bins))): - if abs(target_width - scaled_bins[i])/target_width <= 1-accuracy_requirement: + if abs(target_width - scaled_bins[i])/target_width <= 1-OPTS.accuracy_requirement: select = i break if select == -1: diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 1429e0c8..8b512e72 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -20,7 +20,7 @@ from sram_factory import factory from errors import drc_error if(OPTS.tech_name == "sky130"): - from tech import nmos_bins, pmos_bins, accuracy_requirement + from tech import nmos_bins, pmos_bins class pinv(pgate.pgate): @@ -169,13 +169,13 @@ class pinv(pgate.pgate): valid_pmos = [] for bin in pmos_bins: - if abs(self.bin_accuracy(self.pmos_width, bin[0])) > accuracy_requirement and abs(self.bin_accuracy(self.pmos_width, bin[0])) <= 1: + if abs(self.bin_accuracy(self.pmos_width, bin[0])) > OPTS.accuracy_requirement and abs(self.bin_accuracy(self.pmos_width, bin[0])) <= 1: valid_pmos.append(bin) valid_pmos.sort(key = operator.itemgetter(1)) valid_nmos = [] for bin in nmos_bins: - if abs(self.bin_accuracy(self.nmos_width, bin[0])) > accuracy_requirement and abs(self.bin_accuracy(self.nmos_width, bin[0])) <= 1: + if abs(self.bin_accuracy(self.nmos_width, bin[0])) > OPTS.accuracy_requirement and abs(self.bin_accuracy(self.nmos_width, bin[0])) <= 1: valid_nmos.append(bin) valid_nmos.sort(key = operator.itemgetter(1)) diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py index 3960f1bf..3ce6ad80 100644 --- a/compiler/pgates/pinv_dec.py +++ b/compiler/pgates/pinv_dec.py @@ -14,7 +14,7 @@ from globals import OPTS from sram_factory import factory if(OPTS.tech_name == "sky130"): - from tech import nmos_bins, pmos_bins, accuracy_requirement + from tech import nmos_bins, pmos_bins class pinv_dec(pinv.pinv): From 5941e01b515b11a8b43168ea84989b4f40a7126a Mon Sep 17 00:00:00 2001 From: jcirimel Date: Thu, 25 Jun 2020 08:02:08 -0700 Subject: [PATCH 153/206] add missing parens --- compiler/pgates/pinv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 8b512e72..aa0f7d9a 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -195,9 +195,9 @@ class pinv(pgate.pgate): debug.info(2, "prebinning {0} tx, target: {4}, found {1} x {2} = {3}".format("pmos", self.pmos_width, pmos_mults, self.pmos_width * pmos_mults, self.pmos_size * drc("minwidth_tx"))) debug.info(2, "prebinning {0} tx, target: {4}, found {1} x {2} = {3}".format("nmos", self.nmos_width, nmos_mults, self.nmos_width * nmos_mults, self.nmos_size * drc("minwidth_tx"))) pinv.bin_count += 1 - pinv.bin_error += abs((self.pmos_width * pmos_mults) - (self.pmos_size * drc("minwidth_tx"))/(self.pmos_size * drc("minwidth_tx"))) + pinv.bin_error += abs(((self.pmos_width * pmos_mults) - (self.pmos_size * drc("minwidth_tx")))/(self.pmos_size * drc("minwidth_tx"))) pinv.bin_count += 1 - pinv.bin_error += abs((self.nmos_width * nmos_mults) - (self.nmos_size * drc("minwidth_tx"))/(self.nmos_size * drc("minwidth_tx"))) + pinv.bin_error += abs(((self.nmos_width * nmos_mults) - (self.nmos_size * drc("minwidth_tx")))/(self.nmos_size * drc("minwidth_tx"))) debug.info(2, "pinv bin count: {0} pinv bin error: {1} percent error {2}".format(pinv.bin_count, pinv.bin_error, pinv.bin_error/pinv.bin_count)) def add_ptx(self): From f84ee04fa9e000da364024f557a757a9e947e8d6 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 25 Jun 2020 14:03:59 -0700 Subject: [PATCH 154/206] Single bank passing. Parameterized gate column mux of dff height. End-cap only supply option instead of no vdd in bitcell. --- compiler/modules/bank.py | 14 ++++--- compiler/modules/bitcell_base_array.py | 16 ++++---- compiler/modules/hierarchical_decoder.py | 6 +-- compiler/modules/hierarchical_predecode.py | 44 +++++++++++----------- compiler/modules/replica_bitcell_array.py | 19 ++++++---- compiler/modules/replica_column.py | 13 +++++-- 6 files changed, 61 insertions(+), 51 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 12fcb6dd..6134b143 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -497,18 +497,20 @@ class bank(design.design): Create a 2:4 or 3:8 column address decoder. """ - # Height is a multiple of DFF so that it can be staggered - # and rows do not align with the control logic module - self.dff = factory.create(module_type="dff") + self.dff =factory.create(module_type="dff") if self.col_addr_size == 0: return elif self.col_addr_size == 1: - self.column_decoder = factory.create(module_type="pinvbuf", height=self.dff.height) + self.column_decoder = factory.create(module_type="pinvbuf", + height=self.dff.height) elif self.col_addr_size == 2: - self.column_decoder = factory.create(module_type="hierarchical_predecode2x4", height=self.dff.height) + self.column_decoder = factory.create(module_type="hierarchical_predecode2x4", + height=self.dff.height) + elif self.col_addr_size == 3: - self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=self.dff.height) + self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", + height=self.dff.height) else: # No error checking before? debug.error("Invalid column decoder?", -1) diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index e601208b..6698df33 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -110,17 +110,17 @@ class bitcell_base_array(design.design): # For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps. try: - bitcell_no_vdd_pin = cell_properties.bitcell.no_vdd_via + end_caps_enabled = cell_properties.bitcell.end_caps except AttributeError: - bitcell_no_vdd_pin = False + end_caps_enabled = False # Add vdd/gnd via stacks - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row,col] - for pin_name in ["vdd", "gnd"]: - for pin in inst.get_pins(pin_name): - if not (pin_name == "vdd" and bitcell_no_vdd_pin): + if not end_caps_enabled: + for row in range(self.row_size): + for col in range(self.column_size): + inst = self.cell_inst[row, col] + for pin_name in ["vdd", "gnd"]: + for pin in inst.get_pins(pin_name): self.add_power_pin(name=pin_name, loc=pin.center(), directions=bitcell_power_pin_directions, diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 3233bdc8..141387b4 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -78,12 +78,10 @@ class hierarchical_decoder(design.design): def add_decoders(self): """ Create the decoders based on the number of pre-decodes """ - self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4", - height=self.cell_height) + self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4") self.add_mod(self.pre2_4) - self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8", - height=self.cell_height) + self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8") self.add_mod(self.pre3_8) def determine_predecodes(self, num_inputs): diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index f2274c6e..d4cb51ce 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -20,12 +20,17 @@ class hierarchical_predecode(design.design): def __init__(self, name, input_number, height=None): self.number_of_inputs = input_number + b = factory.create(module_type="bitcell") if not height: - b = factory.create(module_type="bitcell") self.cell_height = b.height - else: + self.column_decoder = False + elif height != b.height: self.cell_height = height - + self.column_decoder = True + else: + self.cell_height = b.height + self.column_decoder = False + self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) design.design.__init__(self, name) @@ -40,21 +45,21 @@ class hierarchical_predecode(design.design): def add_modules(self): """ Add the INV and AND gate modules """ - if self.number_of_inputs == 2: - self.and_mod = factory.create(module_type="and2_dec", - height=self.cell_height) - elif self.number_of_inputs == 3: - self.and_mod = factory.create(module_type="and3_dec", - height=self.cell_height) - elif self.number_of_inputs == 4: - self.and_mod = factory.create(module_type="and4_dec", - height=self.cell_height) + debug.check(self.number_of_inputs < 4, + "Invalid number of predecode inputs: {}".format(self.number_of_inputs)) + + if self.column_decoder: + and_type = "pand{}".format(self.number_of_inputs) + inv_type = "pinv" else: - debug.error("Invalid number of predecode inputs: {}".format(self.number_of_inputs), -1) + and_type = "and{}_dec".format(self.number_of_inputs) + inv_type = "inv_dec" + self.and_mod = factory.create(module_type=and_type, + height=self.cell_height) self.add_mod(self.and_mod) # This uses the pinv_dec parameterized cell - self.inv = factory.create(module_type="inv_dec", + self.inv = factory.create(module_type=inv_type, height=self.cell_height, size=1) self.add_mod(self.inv) @@ -80,7 +85,7 @@ class hierarchical_predecode(design.design): # Outputs from cells are on output layer if OPTS.tech_name == "sky130": self.bus_layer = "m1" - self.bus_directions = None + self.bus_directions = "nonpref" self.bus_pitch = self.m1_pitch self.bus_space = 1.5 * self.m1_space self.input_layer = "m2" @@ -88,7 +93,7 @@ class hierarchical_predecode(design.design): self.output_layer_pitch = self.li_pitch else: self.bus_layer = "m2" - self.bus_directions = None + self.bus_directions = "pref" self.bus_pitch = self.m2_pitch self.bus_space = self.m2_space # This requires a special jog to ensure to conflicts with the output layers @@ -238,10 +243,7 @@ class hierarchical_predecode(design.design): # add output so that it is just below the vdd or gnd rail # since this is where the p/n devices are and there are no # pins in the and gates. - if OPTS.tech_name == "sky130": - inv_out_pos = inv_out_pin.lr() - else: - inv_out_pos = inv_out_pin.rc() + inv_out_pos = inv_out_pin.rc() y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0) rail_pos = vector(self.decode_rails[out_pin].cx(), y_offset) @@ -309,7 +311,7 @@ class hierarchical_predecode(design.design): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ # In sky130, we use hand-made decoder cells with vertical power - if OPTS.tech_name == "sky130": + if not self.column_decoder: for n in ["vdd", "gnd"]: # This makes a wire from top to bottom for both inv and and gates for i in [self.inv_inst, self.and_inst]: diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 05cb1ac6..57bb3cf9 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -380,19 +380,22 @@ class replica_bitcell_array(design.design): # For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps. try: - bitcell_no_vdd_pin = cell_properties.bitcell.no_vdd_via + if cell_properties.bitcell.end_caps_enabled: + supply_insts = [self.dummy_col_left_inst, self.dummy_col_right_inst, + self.dummy_row_top_inst, self.dummy_row_bot_inst] + list(self.replica_col_inst.values()) + else: + supply_insts = self.insts except AttributeError: - bitcell_no_vdd_pin = False + supply_insts = self.insts for pin_name in ["vdd", "gnd"]: - for inst in self.insts: + for inst in supply_insts: pin_list = inst.get_pins(pin_name) for pin in pin_list: - if not (pin_name == "vdd" and bitcell_no_vdd_pin): - self.add_power_pin(name=pin_name, - loc=pin.center(), - directions=("V", "V"), - start_layer=pin.layer) + self.add_power_pin(name=pin_name, + loc=pin.center(), + directions=("V", "V"), + start_layer=pin.layer) def get_rbl_wl_name(self, port): """ Return the WL for the given RBL port """ diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 3a58668c..58d35e7f 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -183,11 +183,16 @@ class replica_column(design.design): width=self.width, height=wl_pin.height()) - # For every second row and column, add a via for gnd and vdd - for row in range(row_range_min, row_range_max): - inst = self.cell_inst[row] + # For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps. + if end_caps_enabled: + supply_insts = [self.cell_inst[0], self.cell_inst[self.total_size - 1]] + else: + supply_insts = self.cell_inst.values() + + for inst in supply_insts: for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) + if pin_name in inst.mod.pins: + self.copy_layout_pin(inst, pin_name) def get_bitcell_pins(self, col, row): """ Creates a list of connections in the bitcell, From 66df659ad464352d1305ce7014aa66ce38c514c9 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 25 Jun 2020 14:25:48 -0700 Subject: [PATCH 155/206] Col decoders are anything not bitcell pitch. --- compiler/modules/hierarchical_decoder.py | 6 ++++-- compiler/modules/hierarchical_predecode.py | 11 +++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 141387b4..3233bdc8 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -78,10 +78,12 @@ class hierarchical_decoder(design.design): def add_decoders(self): """ Create the decoders based on the number of pre-decodes """ - self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4") + self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4", + height=self.cell_height) self.add_mod(self.pre2_4) - self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8") + self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8", + height=self.cell_height) self.add_mod(self.pre3_8) def determine_predecodes(self, num_inputs): diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index d4cb51ce..a89f5fa6 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -24,12 +24,11 @@ class hierarchical_predecode(design.design): if not height: self.cell_height = b.height self.column_decoder = False - elif height != b.height: - self.cell_height = height - self.column_decoder = True else: - self.cell_height = b.height - self.column_decoder = False + self.cell_height = height + # If we are pitch matched to the bitcell, it's a predecoder + # otherwise it's a column decoder (out of pgates) + self.column_decoder = (height != b.height) self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) design.design.__init__(self, name) @@ -311,7 +310,7 @@ class hierarchical_predecode(design.design): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ # In sky130, we use hand-made decoder cells with vertical power - if not self.column_decoder: + if OPTS.tech_name == "sky130" and not self.column_decoder: for n in ["vdd", "gnd"]: # This makes a wire from top to bottom for both inv and and gates for i in [self.inv_inst, self.and_inst]: From 7220b23402c396e6a6cca74757ec056349c359fd Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 25 Jun 2020 15:34:18 -0700 Subject: [PATCH 156/206] Add riscv unit tests --- compiler/sram/sram_1bank.py | 11 +++-- compiler/tests/50_riscv_func_test.py | 65 ++++++++++++++++++++++++++++ compiler/tests/50_riscv_phys_test.py | 59 +++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 4 deletions(-) create mode 100755 compiler/tests/50_riscv_func_test.py create mode 100755 compiler/tests/50_riscv_phys_test.py diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index bb8c0a7d..7d2ccfa8 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -165,7 +165,7 @@ class sram_1bank(sram_base): # The row address bits are placed above the control logic aligned on the right. x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width # It is above the control logic but below the top of the bitcell array - y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height) + y_offset = max(self.control_logic_insts[port].uy(), self.control_logic_insts[port].uy() + self.dff.height) row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_dff_insts[port].place(row_addr_pos[port]) @@ -238,7 +238,7 @@ class sram_1bank(sram_base): # The row address bits are placed above the control logic aligned on the left. x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width # It is below the control logic but below the bottom of the bitcell array - y_offset = min(self.control_logic_insts[port].by(), self.bank_inst.by() + self.row_addr_dff_insts[port].height) + y_offset = min(self.control_logic_insts[port].by(), self.control_logic_insts[port].by() - self.dff.height) row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY") @@ -485,11 +485,14 @@ class sram_1bank(sram_base): flop_pos = flop_pin.center() bank_pos = bank_pin.center() mid_pos = vector(bank_pos.x, flop_pos.y) - self.add_wire(self.m2_stack[::-1], - [flop_pos, mid_pos, bank_pos]) self.add_via_stack_center(from_layer=flop_pin.layer, to_layer="m3", offset=flop_pos) + self.add_path("m3", [flop_pos, mid_pos]) + self.add_via_stack_center(from_layer=bank_pin.layer, + to_layer="m3", + offset=mid_pos) + self.add_path(bank_pin.layer, [mid_pos, bank_pos]) def route_col_addr_dff(self): """ Connect the output of the col flops to the bank pins """ diff --git a/compiler/tests/50_riscv_func_test.py b/compiler/tests/50_riscv_func_test.py new file mode 100755 index 00000000..38756496 --- /dev/null +++ b/compiler/tests/50_riscv_func_test.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test") +class psram_1bank_nomux_func_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.analytical_delay = False + OPTS.netlist_only = True + OPTS.trim_netlist = False + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 1 + globals.setup_bitcell() + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional, delay + from sram_config import sram_config + c = sram_config(word_size=32, + write_size=8, + num_words=256, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Functional test RISC-V memory" + "{} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = OPTS.openram_temp + "sram.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + (fail, error) = f.run() + self.assertTrue(fail,error) + + globals.end_openram() + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/50_riscv_phys_test.py b/compiler/tests/50_riscv_phys_test.py new file mode 100755 index 00000000..2d759c35 --- /dev/null +++ b/compiler/tests/50_riscv_phys_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test") +class psram_1bank_nomux_func_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + OPTS.route_supplies=False + OPTS.perimeter_pins=False + + c = sram_config(word_size=32, + write_size=8, + num_words=256, + num_banks=1) + + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From f11afaa63d057f43a2e24d9cc944c3a954a34ca8 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 25 Jun 2020 17:30:03 -0700 Subject: [PATCH 157/206] Refactor channel route to be a design. --- compiler/base/channel_route.py | 306 ++++++++++++++++++++++++++++++ compiler/base/hierarchy_layout.py | 277 +-------------------------- 2 files changed, 315 insertions(+), 268 deletions(-) create mode 100644 compiler/base/channel_route.py diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py new file mode 100644 index 00000000..7fe1a005 --- /dev/null +++ b/compiler/base/channel_route.py @@ -0,0 +1,306 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import collections +import debug +from tech import drc +from vector import vector +import design + + +class channel_route(design.design): + + unique_id = 0 + + def __init__(self, + netlist, + offset, + layer_stack, + directions=None, + vertical=False, + add_routes=True): + """ + The net list is a list of the nets with each net being a list of pins + to be connected. The offset is the lower-left of where the + routing channel will start. This does NOT try to minimize the + number of tracks -- instead, it picks an order to avoid the + vertical conflicts between pins. The track size must be the number of + nets times the *nonpreferred* routing of the non-track layer pitch. + + """ + name = "cr_{0}".format(channel_route.unique_id) + channel_route.unique_id += 1 + design.design.__init__(self, name) + + self.netlist = netlist + self.offset = offset + self.layer_stack = layer_stack + self.directions = directions + self.vertical = vertical + self.add_routes = add_routes + + if not directions or directions == "pref": + # Use the preferred layer directions + if self.get_preferred_direction(layer_stack[0]) == "V": + self.vertical_layer = layer_stack[0] + self.horizontal_layer = layer_stack[2] + else: + self.vertical_layer = layer_stack[2] + self.horizontal_layer = layer_stack[0] + elif directions == "nonpref": + # Use the preferred layer directions + if self.get_preferred_direction(layer_stack[0]) == "V": + self.vertical_layer = layer_stack[2] + self.horizontal_layer = layer_stack[0] + else: + self.vertical_layer = layer_stack[0] + self.horizontal_layer = layer_stack[2] + else: + # Use the layer directions specified to the router rather than + # the preferred directions + debug.check(directions[0] != directions[1], "Must have unique layer directions.") + if directions[0] == "V": + self.vertical_layer = layer_stack[0] + self.horizontal_layer = layer_stack[2] + else: + self.horizontal_layer = layer_stack[0] + self.vertical_layer = layer_stack[2] + + layer_stuff = self.get_layer_pitch(self.vertical_layer) + (self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff + + layer_stuff = self.get_layer_pitch(self.horizontal_layer) + (self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff + + self.route() + + def remove_net_from_graph(self, pin, g): + """ + Remove the pin from the graph and all conflicts + """ + g.pop(pin, None) + + # Remove the pin from all conflicts + # FIXME: This is O(n^2), so maybe optimize it. + for other_pin, conflicts in g.items(): + if pin in conflicts: + conflicts.remove(pin) + g[other_pin]=conflicts + return g + + def vcg_nets_overlap(self, net1, net2): + """ + Check all the pin pairs on two nets and return a pin + overlap if any pin overlaps. + """ + + if self.vertical: + pitch = self.horizontal_nonpref_pitch + else: + pitch = self.vertical_nonpref_pitch + + for pin1 in net1: + for pin2 in net2: + if self.vcg_pin_overlap(pin1, pin2, pitch): + return True + + return False + + def route(self): + # FIXME: Must extend this to a horizontal conflict graph + # too if we want to minimize the + # number of tracks! + # hcg = {} + + # Initialize the vertical conflict graph (vcg) + # and make a list of all pins + vcg = collections.OrderedDict() + + # Create names for the nets for the graphs + nets = collections.OrderedDict() + index = 0 + # print(netlist) + for pin_list in self.netlist: + net_name = "n{}".format(index) + index += 1 + nets[net_name] = pin_list + + # print("Nets:") + # for net_name in nets: + # print(net_name, [x.name for x in nets[net_name]]) + + # Find the vertical pin conflicts + # FIXME: O(n^2) but who cares for now + for net_name1 in nets: + if net_name1 not in vcg.keys(): + vcg[net_name1] = [] + for net_name2 in nets: + if net_name2 not in vcg.keys(): + vcg[net_name2] = [] + # Skip yourself + if net_name1 == net_name2: + continue + if self.vcg_nets_overlap(nets[net_name1], + nets[net_name2]): + vcg[net_name2].append(net_name1) + + current_offset = self.offset + + # list of routes to do + while vcg: + # from pprint import pformat + # print("VCG:\n", pformat(vcg)) + # get a route from conflict graph with empty fanout set + net_name = None + for net_name, conflicts in vcg.items(): + if len(conflicts) == 0: + vcg = self.remove_net_from_graph(net_name, vcg) + break + else: + # FIXME: We don't support cyclic VCGs right now. + debug.error("Cyclic VCG in channel router.", -1) + + # These are the pins we'll have to connect + pin_list = nets[net_name] + # print("Routing:", net_name, [x.name for x in pin_list]) + + # Remove the net from other constriants in the VCG + vcg = self.remove_net_from_graph(net_name, vcg) + + # Add the trunk routes from the bottom up for + # horizontal or the left to right for vertical + if self.vertical: + if self.add_routes: + self.add_vertical_trunk_route(pin_list, + current_offset, + self.vertical_nonpref_pitch) + # This accounts for the via-to-via spacings + current_offset += vector(self.horizontal_nonpref_pitch, 0) + else: + if self.add_routes: + self.add_horizontal_trunk_route(pin_list, + current_offset, + self.horizontal_nonpref_pitch) + # This accounts for the via-to-via spacings + current_offset += vector(0, self.vertical_nonpref_pitch) + + # Return the size of the channel + if self.vertical: + self.width = 0 + self.height = current_offset.y + return current_offset.y + self.vertical_nonpref_pitch - self.offset.y + else: + self.width = current_offset.x + self.height = 0 + return current_offset.x + self.horizontal_nonpref_pitch - self.offset.x + + def get_layer_pitch(self, layer): + """ Return the track pitch on a given layer """ + try: + # FIXME: Using non-pref pitch here due to overlap bug in VCG constraints. + # It should just result in inefficient channel width but will work. + pitch = getattr(self, "{}_pitch".format(layer)) + nonpref_pitch = getattr(self, "{}_nonpref_pitch".format(layer)) + space = getattr(self, "{}_space".format(layer)) + except AttributeError: + debug.error("Cannot find layer pitch.", -1) + return (nonpref_pitch, pitch, pitch - space, space) + + def add_horizontal_trunk_route(self, + pins, + trunk_offset, + pitch): + """ + Create a trunk route for all pins with + the trunk located at the given y offset. + """ + max_x = max([pin.center().x for pin in pins]) + min_x = min([pin.center().x for pin in pins]) + + # if we are less than a pitch, just create a non-preferred layer jog + if max_x - min_x <= pitch: + half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] + + # Add the horizontal trunk on the vertical layer! + self.add_path(self.vertical_layer, + [vector(min_x - half_layer_width, trunk_offset.y), + vector(max_x + half_layer_width, trunk_offset.y)]) + + # Route each pin to the trunk + for pin in pins: + # No bend needed here + mid = vector(pin.center().x, trunk_offset.y) + self.add_path(self.vertical_layer, [pin.center(), mid]) + else: + # Add the horizontal trunk + self.add_path(self.horizontal_layer, + [vector(min_x, trunk_offset.y), + vector(max_x, trunk_offset.y)]) + + # Route each pin to the trunk + for pin in pins: + mid = vector(pin.center().x, trunk_offset.y) + self.add_path(self.vertical_layer, [pin.center(), mid]) + self.add_via_center(layers=self.layer_stack, + offset=mid, + directions=self.directions) + + def add_vertical_trunk_route(self, + pins, + trunk_offset, + pitch): + """ + Create a trunk route for all pins with the + trunk located at the given x offset. + """ + max_y = max([pin.center().y for pin in pins]) + min_y = min([pin.center().y for pin in pins]) + + # if we are less than a pitch, just create a non-preferred layer jog + if max_y - min_y <= pitch: + + half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] + + # Add the vertical trunk on the horizontal layer! + self.add_path(self.horizontal_layer, + [vector(trunk_offset.x, min_y - half_layer_width), + vector(trunk_offset.x, max_y + half_layer_width)]) + + # Route each pin to the trunk + for pin in pins: + # No bend needed here + mid = vector(trunk_offset.x, pin.center().y) + self.add_path(self.horizontal_layer, [pin.center(), mid]) + else: + # Add the vertical trunk + self.add_path(self.vertical_layer, + [vector(trunk_offset.x, min_y), + vector(trunk_offset.x, max_y)]) + + # Route each pin to the trunk + for pin in pins: + mid = vector(trunk_offset.x, pin.center().y) + self.add_path(self.horizontal_layer, [pin.center(), mid]) + self.add_via_center(layers=self.layer_stack, + offset=mid, + directions=self.directions) + + def vcg_pin_overlap(self, pin1, pin2, pitch): + """ Check for vertical or horizontal overlap of the two pins """ + + # FIXME: If the pins are not in a row, this may break. + # However, a top pin shouldn't overlap another top pin, + # for example, so the extra comparison *shouldn't* matter. + + # Pin 1 must be in the "BOTTOM" set + x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch + + # Pin 1 must be in the "LEFT" set + y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch + overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap) + return overlaps + diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index f90b3129..8720470b 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -5,7 +5,6 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import collections import geometry import gdsMill import debug @@ -1019,282 +1018,24 @@ class layout(): to_layer=dest_pin.layer, offset=out_pos) - def get_layer_pitch(self, layer): - """ Return the track pitch on a given layer """ - try: - # FIXME: Using non-pref pitch here due to overlap bug in VCG constraints. - # It should just result in inefficient channel width but will work. - pitch = getattr(self, "{}_pitch".format(layer)) - nonpref_pitch = getattr(self, "{}_nonpref_pitch".format(layer)) - space = getattr(self, "{}_space".format(layer)) - except AttributeError: - debug.error("Cannot find layer pitch.", -1) - return (nonpref_pitch, pitch, pitch - space, space) - - def add_horizontal_trunk_route(self, - pins, - trunk_offset, - layer_stack, - pitch): - """ - Create a trunk route for all pins with - the trunk located at the given y offset. - """ - max_x = max([pin.center().x for pin in pins]) - min_x = min([pin.center().x for pin in pins]) - - # if we are less than a pitch, just create a non-preferred layer jog - if max_x - min_x <= pitch: - half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] - - # Add the horizontal trunk on the vertical layer! - self.add_path(self.vertical_layer, - [vector(min_x - half_layer_width, trunk_offset.y), - vector(max_x + half_layer_width, trunk_offset.y)]) - - # Route each pin to the trunk - for pin in pins: - # No bend needed here - mid = vector(pin.center().x, trunk_offset.y) - self.add_path(self.vertical_layer, [pin.center(), mid]) - else: - # Add the horizontal trunk - self.add_path(self.horizontal_layer, - [vector(min_x, trunk_offset.y), - vector(max_x, trunk_offset.y)]) - - # Route each pin to the trunk - for pin in pins: - mid = vector(pin.center().x, trunk_offset.y) - self.add_path(self.vertical_layer, [pin.center(), mid]) - self.add_via_center(layers=layer_stack, - offset=mid, - directions=self.directions) - - def add_vertical_trunk_route(self, - pins, - trunk_offset, - layer_stack, - pitch): - """ - Create a trunk route for all pins with the - trunk located at the given x offset. - """ - max_y = max([pin.center().y for pin in pins]) - min_y = min([pin.center().y for pin in pins]) - - # if we are less than a pitch, just create a non-preferred layer jog - if max_y - min_y <= pitch: - - half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] - - # Add the vertical trunk on the horizontal layer! - self.add_path(self.horizontal_layer, - [vector(trunk_offset.x, min_y - half_layer_width), - vector(trunk_offset.x, max_y + half_layer_width)]) - - # Route each pin to the trunk - for pin in pins: - # No bend needed here - mid = vector(trunk_offset.x, pin.center().y) - self.add_path(self.horizontal_layer, [pin.center(), mid]) - else: - # Add the vertical trunk - self.add_path(self.vertical_layer, - [vector(trunk_offset.x, min_y), - vector(trunk_offset.x, max_y)]) - - # Route each pin to the trunk - for pin in pins: - mid = vector(trunk_offset.x, pin.center().y) - self.add_path(self.horizontal_layer, [pin.center(), mid]) - self.add_via_center(layers=layer_stack, - offset=mid, - directions=self.directions) - - def create_channel_route(self, netlist, - offset, - layer_stack, - directions=None, - vertical=False): - """ - The net list is a list of the nets with each net being a list of pins - to be connected. The offset is the lower-left of where the - routing channel will start. This does NOT try to minimize the - number of tracks -- instead, it picks an order to avoid the - vertical conflicts between pins. The track size must be the number of - nets times the *nonpreferred* routing of the non-track layer pitch. - - """ - def remove_net_from_graph(pin, g): - """ - Remove the pin from the graph and all conflicts - """ - g.pop(pin, None) - - # Remove the pin from all conflicts - # FIXME: This is O(n^2), so maybe optimize it. - for other_pin, conflicts in g.items(): - if pin in conflicts: - conflicts.remove(pin) - g[other_pin]=conflicts - return g - - def vcg_nets_overlap(net1, net2, vertical): - """ - Check all the pin pairs on two nets and return a pin - overlap if any pin overlaps. - """ - - if vertical: - pitch = self.horizontal_nonpref_pitch - else: - pitch = self.vertical_nonpref_pitch - - for pin1 in net1: - for pin2 in net2: - if vcg_pin_overlap(pin1, pin2, vertical, pitch): - return True - - return False - - def vcg_pin_overlap(pin1, pin2, vertical, pitch): - """ Check for vertical or horizontal overlap of the two pins """ - - # FIXME: If the pins are not in a row, this may break. - # However, a top pin shouldn't overlap another top pin, - # for example, so the extra comparison *shouldn't* matter. - - # Pin 1 must be in the "BOTTOM" set - x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch - - # Pin 1 must be in the "LEFT" set - y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch - overlaps = (not vertical and x_overlap) or (vertical and y_overlap) - return overlaps - - self.directions = directions - if not directions or directions == "pref": - # Use the preferred layer directions - if self.get_preferred_direction(layer_stack[0]) == "V": - self.vertical_layer = layer_stack[0] - self.horizontal_layer = layer_stack[2] - else: - self.vertical_layer = layer_stack[2] - self.horizontal_layer = layer_stack[0] - elif directions == "nonpref": - # Use the preferred layer directions - if self.get_preferred_direction(layer_stack[0]) == "V": - self.vertical_layer = layer_stack[2] - self.horizontal_layer = layer_stack[0] - else: - self.vertical_layer = layer_stack[0] - self.horizontal_layer = layer_stack[2] - else: - # Use the layer directions specified to the router rather than - # the preferred directions - debug.check(directions[0] != directions[1], "Must have unique layer directions.") - if directions[0] == "V": - self.vertical_layer = layer_stack[0] - self.horizontal_layer = layer_stack[2] - else: - self.horizontal_layer = layer_stack[0] - self.vertical_layer = layer_stack[2] - - layer_stuff = self.get_layer_pitch(self.vertical_layer) - (self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff - - layer_stuff = self.get_layer_pitch(self.horizontal_layer) - (self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff - - # FIXME: Must extend this to a horizontal conflict graph - # too if we want to minimize the - # number of tracks! - # hcg = {} - - # Initialize the vertical conflict graph (vcg) - # and make a list of all pins - vcg = collections.OrderedDict() - - # Create names for the nets for the graphs - nets = collections.OrderedDict() - index = 0 - # print(netlist) - for pin_list in netlist: - net_name = "n{}".format(index) - index += 1 - nets[net_name] = pin_list - - # print("Nets:") - # for net_name in nets: - # print(net_name, [x.name for x in nets[net_name]]) - - # Find the vertical pin conflicts - # FIXME: O(n^2) but who cares for now - for net_name1 in nets: - if net_name1 not in vcg.keys(): - vcg[net_name1] = [] - for net_name2 in nets: - if net_name2 not in vcg.keys(): - vcg[net_name2] = [] - # Skip yourself - if net_name1 == net_name2: - continue - if vcg_nets_overlap(nets[net_name1], - nets[net_name2], - vertical): - vcg[net_name2].append(net_name1) - - # list of routes to do - while vcg: - # from pprint import pformat - # print("VCG:\n", pformat(vcg)) - # get a route from conflict graph with empty fanout set - net_name = None - for net_name, conflicts in vcg.items(): - if len(conflicts) == 0: - vcg = remove_net_from_graph(net_name, vcg) - break - else: - # FIXME: We don't support cyclic VCGs right now. - debug.error("Cyclic VCG in channel router.", -1) - - # These are the pins we'll have to connect - pin_list = nets[net_name] - # print("Routing:", net_name, [x.name for x in pin_list]) - - # 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 for - # horizontal or the left to right for vertical - if vertical: - self.add_vertical_trunk_route(pin_list, - offset, - layer_stack, - self.vertical_nonpref_pitch) - # This accounts for the via-to-via spacings - offset += vector(self.horizontal_nonpref_pitch, 0) - else: - self.add_horizontal_trunk_route(pin_list, - offset, - layer_stack, - self.horizontal_nonpref_pitch) - # This accounts for the via-to-via spacings - offset += vector(0, self.vertical_nonpref_pitch) - def create_vertical_channel_route(self, netlist, offset, layer_stack, directions=None): """ Wrapper to create a vertical channel route """ - self.create_channel_route(netlist, offset, layer_stack, directions, vertical=True) + import channel_route + cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True) + self.add_inst("vc", cr) + self.connect_inst([]) def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None): """ Wrapper to create a horizontal channel route """ - self.create_channel_route(netlist, offset, layer_stack, directions, vertical=False) - + import channel_route + cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False) + self.add_inst("hc", cr) + self.connect_inst([]) + def add_boundary(self, ll=vector(0, 0), ur=None): """ Add boundary for debugging dimensions """ if OPTS.netlist_only: From 9eb1b500eaf79ec905b3ffef4f8e0038ab17347a Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 25 Jun 2020 17:31:23 -0700 Subject: [PATCH 158/206] Skip phys riscv test --- compiler/tests/50_riscv_phys_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/tests/50_riscv_phys_test.py b/compiler/tests/50_riscv_phys_test.py index 2d759c35..0ae11025 100755 --- a/compiler/tests/50_riscv_phys_test.py +++ b/compiler/tests/50_riscv_phys_test.py @@ -15,8 +15,8 @@ from globals import OPTS from sram_factory import factory import debug -#@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test") -class psram_1bank_nomux_func_test(openram_test): +@unittest.skip("SKIPPING 50_riscv_phys_test") +class riscv_phys_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) From 76e5389c3384a8221475b5fd5e04136b991d12a2 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 25 Jun 2020 17:43:17 -0700 Subject: [PATCH 159/206] Change riscv func test name --- compiler/tests/50_riscv_func_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/tests/50_riscv_func_test.py b/compiler/tests/50_riscv_func_test.py index 38756496..bb4cd6b0 100755 --- a/compiler/tests/50_riscv_func_test.py +++ b/compiler/tests/50_riscv_func_test.py @@ -15,8 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -#@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test") -class psram_1bank_nomux_func_test(openram_test): +class riscv_func_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) From d53abba47946890d92a78c5b8f9762ffb7c5080e Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 25 Jun 2020 17:43:44 -0700 Subject: [PATCH 160/206] Always route the channel route since it is it's own design. --- compiler/base/channel_route.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index 7fe1a005..d6048376 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -21,8 +21,7 @@ class channel_route(design.design): offset, layer_stack, directions=None, - vertical=False, - add_routes=True): + vertical=False): """ The net list is a list of the nets with each net being a list of pins to be connected. The offset is the lower-left of where the @@ -41,7 +40,6 @@ class channel_route(design.design): self.layer_stack = layer_stack self.directions = directions self.vertical = vertical - self.add_routes = add_routes if not directions or directions == "pref": # Use the preferred layer directions @@ -174,17 +172,15 @@ class channel_route(design.design): # Add the trunk routes from the bottom up for # horizontal or the left to right for vertical if self.vertical: - if self.add_routes: - self.add_vertical_trunk_route(pin_list, - current_offset, - self.vertical_nonpref_pitch) + self.add_vertical_trunk_route(pin_list, + current_offset, + self.vertical_nonpref_pitch) # This accounts for the via-to-via spacings current_offset += vector(self.horizontal_nonpref_pitch, 0) else: - if self.add_routes: - self.add_horizontal_trunk_route(pin_list, - current_offset, - self.horizontal_nonpref_pitch) + self.add_horizontal_trunk_route(pin_list, + current_offset, + self.horizontal_nonpref_pitch) # This accounts for the via-to-via spacings current_offset += vector(0, self.vertical_nonpref_pitch) From af4ed3dd6edcf25db3a624da5a5d503bf3619f5e Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 26 Jun 2020 06:50:45 -0700 Subject: [PATCH 161/206] Skip riscv func test for time sake --- compiler/tests/50_riscv_func_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/tests/50_riscv_func_test.py b/compiler/tests/50_riscv_func_test.py index bb4cd6b0..1d5720f7 100755 --- a/compiler/tests/50_riscv_func_test.py +++ b/compiler/tests/50_riscv_func_test.py @@ -15,6 +15,7 @@ from globals import OPTS from sram_factory import factory import debug +@unittest.skip("SKIPPING 50_riscv_func_test") class riscv_func_test(openram_test): def runTest(self): From 567675ab31ea4cbd54a0380cd72ba37b84b144a5 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 26 Jun 2020 11:47:12 -0700 Subject: [PATCH 162/206] PEP8 cleanup --- compiler/base/hierarchy_design.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index ac3fb30b..1321f06a 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -31,6 +31,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): except AttributeError: lvs_subdir = "lvs_lib" lvs_dir = OPTS.openram_tech + lvs_subdir + "/" + if os.path.exists(lvs_dir): self.lvs_file = lvs_dir + name + ".sp" else: From e23d41c1d428179c21ff7eafd11c6feea4808f7f Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 26 Jun 2020 11:47:35 -0700 Subject: [PATCH 163/206] PEP8 cleanup --- compiler/modules/sense_amp.py | 39 ++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index ff5638ba..35fbdf42 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -8,11 +8,12 @@ import design import debug import utils -from tech import GDS,layer, parameter,drc +from tech import GDS, layer, parameter, drc from tech import cell_properties as props from globals import OPTS import logical_effort + class sense_amp(design.design): """ This module implements the single sense amp cell used in the design. It @@ -28,10 +29,10 @@ class sense_amp(design.design): props.sense_amp.pin.gnd] type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] if not OPTS.netlist_only: - (width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"]) + (width, height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"]) else: - (width, height) = (0,0) + (width, height) = (0, 0) pin_map = [] def get_bl_names(self): @@ -61,41 +62,41 @@ class sense_amp(design.design): # FIXME: This input load will be applied to both the s_en timing and bitline timing. - #Input load for the bitlines which are connected to the source/drain of a TX. Not the selects. - from tech import spice, parameter + # Input load for the bitlines which are connected to the source/drain of a TX. Not the selects. + from tech import spice # Default is 8x. Per Samira and Hodges-Jackson book: # "Column-mux transistors driven by the decoder must be sized for optimal speed" - bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file. - return spice["min_tx_drain_c"]*(bitline_pmos_size)#ff + bitline_pmos_size = 8 # FIXME: This should be set somewhere and referenced. Probably in tech file. + return spice["min_tx_drain_c"] * bitline_pmos_size # ff def get_stage_effort(self, load): - #Delay of the sense amp will depend on the size of the amp and the output load. + # Delay of the sense amp will depend on the size of the amp and the output load. parasitic_delay = 1 - cin = (parameter["sa_inv_pmos_size"] + parameter["sa_inv_nmos_size"])/drc("minwidth_tx") - sa_size = parameter["sa_inv_nmos_size"]/drc("minwidth_tx") + cin = (parameter["sa_inv_pmos_size"] + parameter["sa_inv_nmos_size"]) / drc("minwidth_tx") + sa_size = parameter["sa_inv_nmos_size"] / drc("minwidth_tx") cc_inv_cin = cin - return logical_effort.logical_effort('column_mux', sa_size, cin, load+cc_inv_cin, parasitic_delay, False) + return logical_effort.logical_effort('column_mux', sa_size, cin, load + cc_inv_cin, parasitic_delay, False) def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" - #Power in this module currently not defined. Returns 0 nW (leakage and dynamic). + # Power in this module currently not defined. Returns 0 nW (leakage and dynamic). total_power = self.return_power() return total_power def get_en_cin(self): """Get the relative capacitance of sense amp enable gate cin""" - pmos_cin = parameter["sa_en_pmos_size"]/drc("minwidth_tx") - nmos_cin = parameter["sa_en_nmos_size"]/drc("minwidth_tx") - #sen is connected to 2 pmos isolation TX and 1 nmos per sense amp. - return 2*pmos_cin + nmos_cin + pmos_cin = parameter["sa_en_pmos_size"] / drc("minwidth_tx") + nmos_cin = parameter["sa_en_nmos_size"] / drc("minwidth_tx") + # sen is connected to 2 pmos isolation TX and 1 nmos per sense amp. + return 2 * pmos_cin + nmos_cin def get_enable_name(self): """Returns name used for enable net""" - #FIXME: A better programmatic solution to designate pins + # FIXME: A better programmatic solution to designate pins enable_name = self.en_name debug.check(enable_name in self.pin_names, "Enable name {} not found in pin list".format(enable_name)) return enable_name - def build_graph(self, graph, inst_name, port_nets): + def build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" - self.add_graph_edges(graph, port_nets) + self.add_graph_edges(graph, port_nets) From f57eeb88eb4af97bfb4a4f8aee46aba8f9fa533b Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 26 Jun 2020 11:47:55 -0700 Subject: [PATCH 164/206] PEP8 cleanup, multiple vdd/gnd support --- compiler/modules/sense_amp_array.py | 38 +++++++++++++---------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 46c30c9d..5d41b2a0 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -28,7 +28,7 @@ class sense_amp_array(design.design): self.add_comment("words_per_row: {0}".format(words_per_row)) self.word_size = word_size - self.words_per_row = words_per_row + self.words_per_row = words_per_row if not num_spare_cols: self.num_spare_cols = 0 else: @@ -77,7 +77,7 @@ class sense_amp_array(design.design): self.DRC_LVS() def add_pins(self): - for i in range(0,self.word_size + self.num_spare_cols): + for i in range(0, self.word_size + self.num_spare_cols): self.add_pin(self.data_name + "_{0}".format(i), "OUTPUT") self.add_pin(self.get_bl_name() + "_{0}".format(i), "INPUT") self.add_pin(self.get_br_name() + "_{0}".format(i), "INPUT") @@ -96,7 +96,7 @@ class sense_amp_array(design.design): def create_sense_amp_array(self): self.local_insts = [] - for i in range(0,self.word_size + self.num_spare_cols): + for i in range(0, self.word_size + self.num_spare_cols): name = "sa_d{0}".format(i) self.local_insts.append(self.add_inst(name=name, mod=self.amp)) @@ -107,14 +107,10 @@ class sense_amp_array(design.design): def place_sense_amp_array(self): from tech import cell_properties - if self.bitcell.width > self.amp.width: - amp_spacing = self.bitcell.width - else: - amp_spacing = self.amp.width for i in range(0, self.row_size, self.words_per_row): index = int(i / self.words_per_row) - xoffset = i * amp_spacing + xoffset = i * self.bitcell.width if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: mirror = "MY" @@ -126,9 +122,9 @@ class sense_amp_array(design.design): self.local_insts[index].place(offset=amp_position, mirror=mirror) # place spare sense amps (will share the same enable as regular sense amps) - for i in range(0,self.num_spare_cols): + for i in range(0, self.num_spare_cols): index = self.word_size + i - xoffset = ((self.word_size * self.words_per_row) + i) * amp_spacing + xoffset = ((self.word_size * self.words_per_row) + i) * self.bitcell.width if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: mirror = "MY" @@ -143,17 +139,17 @@ class sense_amp_array(design.design): for i in range(len(self.local_insts)): inst = self.local_insts[i] - gnd_pin = inst.get_pin("gnd") - self.add_power_pin(name="gnd", - loc=gnd_pin.center(), - start_layer=gnd_pin.layer, - directions=("V", "V")) - - vdd_pin = inst.get_pin("vdd") - self.add_power_pin(name="vdd", - loc=vdd_pin.center(), - start_layer=vdd_pin.layer, - directions=("V", "V")) + for gnd_pin in inst.get_pins("gnd"): + self.add_power_pin(name="gnd", + loc=gnd_pin.center(), + start_layer=gnd_pin.layer, + directions=("V", "V")) + + for vdd_pin in inst.get_pins("vdd"): + self.add_power_pin(name="vdd", + loc=vdd_pin.center(), + start_layer=vdd_pin.layer, + directions=("V", "V")) bl_pin = inst.get_pin(inst.mod.get_bl_names()) br_pin = inst.get_pin(inst.mod.get_br_names()) From 94d700071751973e2c9bb93d3ce266dc658e300f Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 26 Jun 2020 12:16:54 -0700 Subject: [PATCH 165/206] Reduce output clutter from gds write --- 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 8720470b..2a29be6b 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -739,7 +739,7 @@ class layout(): width=width, height=height, center=False) - debug.info(2, "Adding {0} boundary {1}".format(self.name, boundary)) + debug.info(4, "Adding {0} boundary {1}".format(self.name, boundary)) self.visited.append(self.name) From c07e20cbe4d6f2cca60b7fc836fd493e0ce433fc Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 26 Jun 2020 14:27:16 -0700 Subject: [PATCH 166/206] Move mux select from li to m2 in sky130 --- compiler/modules/single_level_column_mux_array.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index cc38cb45..37fd4dc1 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -33,8 +33,8 @@ class single_level_column_mux_array(design.design): self.column_offset = column_offset if "li" in layer: - self.col_mux_stack = self.li_stack - self.col_mux_stack_pitch = self.m1_pitch + self.col_mux_stack = self.m1_stack[::-1] + self.col_mux_stack_pitch = self.m2_pitch else: self.col_mux_stack = self.m1_stack self.col_mux_stack_pitch = self.m1_pitch @@ -155,7 +155,7 @@ class single_level_column_mux_array(design.design): self.route_bitlines() def add_horizontal_input_rail(self): - """ Create address input rails on M1 below the mux transistors """ + """ Create address input rails below the mux transistors """ for j in range(self.words_per_row): offset = vector(0, self.route_height + (j - self.words_per_row) * self.col_mux_stack_pitch) self.add_layout_pin(text="sel_{}".format(j), @@ -177,10 +177,10 @@ class single_level_column_mux_array(design.design): # use the y offset from the sel pin and the x offset from the gate offset = vector(gate_offset.x, self.get_pin("sel_{}".format(sel_index)).cy()) - # Add the poly contact with a shift to account for the rotation - self.add_via_center(layers=self.poly_stack, - offset=offset, - directions=self.via_directions) + self.add_via_stack_center(from_layer="poly", + to_layer=self.col_mux_stack[0], + offset=offset, + directions=self.via_directions) self.add_path("poly", [offset, gate_offset]) def route_bitlines(self): From a7ee17eb2d007ba7dc4e0e84be4d81679bf61d5f Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 26 Jun 2020 15:29:27 -0700 Subject: [PATCH 167/206] Move output of sense amp to side like other techs --- technology/freepdk45/gds_lib/sense_amp.gds | Bin 16384 -> 14702 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/sense_amp.gds b/technology/freepdk45/gds_lib/sense_amp.gds index fecbfcb8cdc6af32968849ba447d240a27c92d8a..53c499f95f892bd63084ab55e93de706bd6a492d 100644 GIT binary patch delta 402 zcmZo@V0>2;$H2zG#1z3G!^p(`1jv?R;AfCwU_fRw@G!BZq^2d7=9Dlnh%mF-DIA%- z^l|V3OQjg`3%+aKG03pu)Vne6iJTGxD+2=)F9QRJW|juh?FbqozBxty6w~A!r7LP+ zwfzXSEN>ucK{OLooT-2E3kA8&Ez0@KlTDSR#Lt{LbGm?mM}QfqgoS}Yq97JYrx=d!UvAX$Y7+Pg8DkjpkWNB>fDoKsh}I4oS9@{L+%i&(zcQ-Ol1rVOuRr0q?x6G z^bZ6L5#O95e~M}HIyFV{vkV}>Bftz)#=^j$@{fT{k}WlFvWC(vC8k24U_TH8X^=TA zZxA#@e6x!3MCQrHno^NS=CIZR&0%5SkYr2GO9AQ<1Y!;b1~y(M23`g>J|>`jI$--i z#)F&*HJ<4&)R{mUCeFmW`I%Z2BiJb*hhkF;5udE2sm%&@#pFwxmQa>}mc-;7jfYU4 zpO(~Q9Zf|^B)2kc1M1cTxiuxfv}ALVRunHJ_=16Ufr9ZW5Cek`PJ_ia-!q@USP!yy h0KusJ0~q)~t4G6sH2eoL1V+=}K!!d8LmX|b2LO#IQC0u| From 2bd498c39cf1a412845bd8f93d9989770e3d62e8 Mon Sep 17 00:00:00 2001 From: mrg Date: Sat, 27 Jun 2020 08:21:30 -0700 Subject: [PATCH 168/206] Change precharge layer to m3 --- compiler/modules/precharge_array.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 2cf4718a..3a3b8c8b 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -75,11 +75,18 @@ class precharge_array(design.design): def add_layout_pins(self): en_bar_pin = self.pc_cell.get_pin("en_bar") - self.add_layout_pin(text="en_bar", - layer=en_bar_pin.layer, - offset=en_bar_pin.ll(), - width=self.width, - height=en_bar_pin.height()) + if en_bar_pin.layer =="li": + en_bar_layer = "m3" + else: + en_bar_layer = en_bar_pin.layer + r = self.add_layout_pin(text="en_bar", + layer=en_bar_layer, + offset=en_bar_pin.ll(), + width=self.width, + height=en_bar_pin.height()) + self.add_via_stack_center(from_layer=en_bar_pin.layer, + to_layer=en_bar_layer, + offset = r.center()) for inst in self.local_insts: self.copy_layout_pin(inst, "vdd") From 609aa98c8be449ba86308eca1536c4c0d5178f91 Mon Sep 17 00:00:00 2001 From: mrg Date: Sat, 27 Jun 2020 08:21:53 -0700 Subject: [PATCH 169/206] Move write mask pin to left of cell to avoid sense amp --- compiler/modules/write_driver_array.py | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 22a75218..a6eb1384 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -154,7 +154,6 @@ class write_driver_array(design.design): self.get_br_name() + "_{0}".format(index), self.en_name + "_{0}".format(i + offset), "vdd", "gnd"]) - def place_write_array(self): from tech import cell_properties if self.bitcell.width > self.driver.width: From c10a6a29c00fcf4ab47c2f3f757982b60eadbf7a Mon Sep 17 00:00:00 2001 From: mrg Date: Sat, 27 Jun 2020 08:22:16 -0700 Subject: [PATCH 170/206] Simplify precharge pin layer --- compiler/pgates/precharge.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index dc016cab..b3d25865 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -38,16 +38,10 @@ class precharge(design.design): if self.bitcell_bl_pin.layer == "m1": self.bitline_layer = "m1" - if "li" in layer: - self.en_layer = "li" - else: - self.en_layer = "m2" + self.en_layer = "m2" else: self.bitline_layer = "m2" - if "li" in layer: - self.en_layer = "li" - else: - self.en_layer = "m1" + self.en_layer = "m1" # Creates the netlist and layout # Since it has variable height, it is not a pgate. From 66ea559209c476804433bc12cf5e0c17cd994f88 Mon Sep 17 00:00:00 2001 From: mrg Date: Sat, 27 Jun 2020 08:23:12 -0700 Subject: [PATCH 171/206] Use channel for dffs all at once --- compiler/modules/write_mask_and_array.py | 17 +- compiler/sram/sram_1bank.py | 314 +++++++++++------------ 2 files changed, 163 insertions(+), 168 deletions(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index f337ab7a..c87d3a90 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -108,8 +108,21 @@ class write_mask_and_array(design.design): end=vector(self.width, en_pin.cy())) for i in range(self.num_wmasks): + # Route the A pin over to the left so that it doesn't conflict with the sense + # amp output which is usually in the center + a_pin = self.and2_insts[i].get_pin("A") + a_pos = a_pin.center() + in_pos = vector(self.and2_insts[i].lx(), + a_pos.y) + self.add_via_stack_center(from_layer=a_pin.layer, + to_layer="m2", + offset=in_pos) + self.add_layout_pin_rect_center(text="wmask_in_{0}".format(i), + layer="m2", + offset=in_pos) + self.add_path(a_pin.layer, [in_pos, a_pos]) + # Copy remaining layout pins - self.copy_layout_pin(self.and2_insts[i], "A", "wmask_in_{0}".format(i)) self.copy_layout_pin(self.and2_insts[i], "Z", "wmask_out_{0}".format(i)) # Add via connections to metal3 for AND array's B pin @@ -122,7 +135,7 @@ class write_mask_and_array(design.design): for supply in ["gnd", "vdd"]: supply_pin=self.and2_insts[i].get_pin(supply) if "li" in layer: - self.add_power_pin(supply, supply_pin.center(), start_layer="li", directions = ("H", "H")) + self.add_power_pin(supply, supply_pin.center(), start_layer="li", directions=("H", "H")) else: self.add_power_pin(supply, supply_pin.center()) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 7d2ccfa8..4e2be40e 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -70,93 +70,12 @@ class sram_1bank(sram_base): # If a horizontal channel, they rely on the vertical channel non-preferred (contacted) pitch. # If a vertical channel, they rely on the horizontal channel non-preferred (contacted) pitch. # So, m3 non-pref pitch means that this is routed on the m2 layer. - if self.write_size: - self.data_bus_gap = self.m4_nonpref_pitch * 2 - self.data_bus_size = self.m4_nonpref_pitch * (self.word_size + self.num_spare_cols) + self.data_bus_gap - self.wmask_bus_gap = self.m2_nonpref_pitch * 2 - self.wmask_bus_size = self.m2_nonpref_pitch * (max(self.num_wmasks + 1, self.col_addr_size + 1)) + self.wmask_bus_gap - if self.num_spare_cols: - self.spare_wen_bus_gap = self.m2_nonpref_pitch * 2 - self.spare_wen_bus_size = self.m2_nonpref_pitch * (max(self.num_spare_cols + 1, self.col_addr_size + 1)) + self.spare_wen_bus_gap - else: - self.spare_wen_bus_size = 0 - - elif self.num_spare_cols and not self.write_size: - self.data_bus_gap = self.m4_nonpref_pitch * 2 - self.data_bus_size = self.m4_nonpref_pitch * (self.word_size + self.num_spare_cols) + self.data_bus_gap - self.spare_wen_bus_gap = self.m2_nonpref_pitch * 2 - self.spare_wen_bus_size = self.m2_nonpref_pitch * (max(self.num_spare_cols + 1, self.col_addr_size + 1)) + self.spare_wen_bus_gap - - else: - self.data_bus_gap = self.m3_nonpref_pitch * 2 - self.data_bus_size = self.m3_nonpref_pitch * (max(self.word_size + 1, self.col_addr_size + 1)) + self.data_bus_gap - - self.col_addr_bus_gap = self.m2_nonpref_pitch * 2 - self.col_addr_bus_size = self.m2_nonpref_pitch * (self.col_addr_size) + self.col_addr_bus_gap + self.data_bus_gap = self.m4_nonpref_pitch * 2 + self.data_bus_size = self.m4_nonpref_pitch * (self.word_size + self.num_spare_cols + self.num_wmasks + self.col_addr_size + self.num_spare_cols) + self.data_bus_gap # Port 0 port = 0 - if port in self.write_ports: - if self.write_size: - bus_size = max(self.wmask_bus_size, self.spare_wen_bus_size) - # Add the write mask flops below the write mask AND array. - wmask_pos[port] = vector(self.bank.bank_array_ll.x, - - bus_size - self.dff.height) - self.wmask_dff_insts[port].place(wmask_pos[port]) - - # Add the data flops below the write mask flops. - data_pos[port] = vector(self.bank.bank_array_ll.x, - - self.data_bus_size - bus_size - 2 * self.dff.height) - self.data_dff_insts[port].place(data_pos[port]) - - #Add spare write enable flops to the right of write mask flops - if self.num_spare_cols: - spare_wen_pos[port] = vector(self.bank.bank_array_ll.x + self.wmask_dff_insts[port].width + self.bank.m2_gap, - - bus_size - self.dff.height) - self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) - - elif self.num_spare_cols and not self.write_size: - # Add spare write enable flops below bank (lower right) - spare_wen_pos[port] = vector(self.bank.bank_array_ll.x, - - self.spare_wen_bus_size - self.dff.height) - self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) - - # Add the data flops below the spare write enable flops. - data_pos[port] = vector(self.bank.bank_array_ll.x, - - self.data_bus_size - self.spare_wen_bus_size - 2 * self.dff.height) - self.data_dff_insts[port].place(data_pos[port]) - - else: - # Add the data flops below the bank to the right of the lower-left of bank array - # This relies on the lower-left of the array of the bank - # decoder in upper left, bank in upper right, sensing in lower right. - # These flops go below the sensing and leave a gap to channel route to the - # sense amps. - data_pos[port] = vector(self.bank.bank_array_ll.x, - -self.data_bus_size - self.dff.height) - self.data_dff_insts[port].place(data_pos[port]) - - else: - wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0) - data_pos[port] = vector(self.bank.bank_array_ll.x, 0) - spare_wen_pos[port] = vector(self.bank.bank_array_ll.x, 0) - - # Add the col address flops below the bank to the left of the lower-left of bank array - if self.col_addr_dff: - if self.write_size: - col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, - -bus_size - self.col_addr_dff_insts[port].height) - elif self.num_spare_cols and not self.write_size: - col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, - -self.spare_wen_bus_size - self.col_addr_dff_insts[port].height) - else: - col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, - -self.data_bus_size - self.col_addr_dff_insts[port].height) - self.col_addr_dff_insts[port].place(col_addr_pos[port]) - else: - col_addr_pos[port] = vector(self.bank.bank_array_ll.x, 0) - # This includes 2 M2 pitches for the row addr clock line. control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch, self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - 2 * self.bank.m2_gap) @@ -169,64 +88,45 @@ class sram_1bank(sram_base): row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_dff_insts[port].place(row_addr_pos[port]) + # Add the col address flops below the bank to the right of the control logic + x_offset = self.control_logic_insts[port].rx() + self.dff.width + y_offset = - self.data_bus_size - self.dff.height + if self.col_addr_dff: + col_addr_pos[port] = vector(x_offset, + y_offset) + self.col_addr_dff_insts[port].place(col_addr_pos[port]) + x_offset = self.col_addr_dff_insts[port].rx() + else: + col_addr_pos[port] = vector(x_offset, 0) + + if port in self.write_ports: + if self.write_size: + # Add the write mask flops below the write mask AND array. + wmask_pos[port] = vector(x_offset, + y_offset) + self.wmask_dff_insts[port].place(wmask_pos[port]) + x_offset = self.wmask_dff_insts[port].rx() + + # Add spare write enable flops to the right of write mask flops + if self.num_spare_cols: + spare_wen_pos[port] = vector(x_offset, + y_offset) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) + x_offset = self.spare_wen_dff_insts[port].rx() + + # Add the data flops below the write mask flops. + data_pos[port] = vector(x_offset, + y_offset) + self.data_dff_insts[port].place(data_pos[port]) + + else: + wmask_pos[port] = vector(x_offset, y_offset) + data_pos[port] = vector(x_offset, y_offset) + spare_wen_pos[port] = vector(x_offset, y_offset) + if len(self.all_ports)>1: # Port 1 port = 1 - - if port in self.write_ports: - if self.write_size: - bus_size = max(self.wmask_bus_size, self.spare_wen_bus_size) - # Add the write mask flops above the write mask AND array. - wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width, - self.bank.height + bus_size + self.dff.height) - self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX") - - # Add the data flops above the write mask flops - data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, - self.bank.height + bus_size + self.data_bus_size + 2 * self.dff.height) - self.data_dff_insts[port].place(data_pos[port], mirror="MX") - - if self.num_spare_cols: - spare_wen_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width - - self.spare_wen_dff_insts[port].width - self.bank.m2_gap, - self.bank.height + bus_size + self.dff.height) - self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") - - # Place dffs when spare cols is enabled - elif self.num_spare_cols and not self.write_size: - # Spare wen flops on the upper right, below data flops - spare_wen_pos[port] = vector(self.bank.bank_array_ur.x - self.spare_wen_dff_insts[port].width, - self.bank.height + self.spare_wen_bus_size + self.dff.height) - self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") - # Add the data flops above the spare write enable flops - data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, - self.bank.height + self.spare_wen_bus_size + self.data_bus_size + 2 * self.dff.height) - self.data_dff_insts[port].place(data_pos[port], mirror="MX") - - else: - # Add the data flops above the bank to the left of the upper-right of bank array - # This relies on the upper-right of the array of the bank - # decoder in upper left, bank in upper right, sensing in lower right. - # These flops go below the sensing and leave a gap to channel route to the - # sense amps. - data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, - self.bank.height + self.data_bus_size + self.dff.height) - self.data_dff_insts[port].place(data_pos[port], mirror="MX") - - # Add the col address flops above the bank to the right of the upper-right of bank array - if self.col_addr_dff: - if self.write_size: - col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, - self.bank.height + bus_size + self.dff.height) - elif self.num_spare_cols and not self.write_size: - col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, - self.bank.height + self.spare_wen_bus_size + self.dff.height) - else: - col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, - self.bank.height + self.data_bus_size + self.dff.height) - self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") - else: - col_addr_pos[port] = self.bank_inst.ur() # This includes 2 M2 pitches for the row addr clock line control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch, @@ -242,6 +142,42 @@ class sram_1bank(sram_base): row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY") + # Add the col address flops below the bank to the right of the control logic + x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width + y_offset = self.bank.height + self.data_bus_size + self.dff.height + if self.col_addr_dff: + col_addr_pos[port] = vector(x_offset - self.col_addr_dff_insts[port].width, + y_offset) + self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") + x_offset = self.col_addr_dff_insts[port].lx() + else: + col_addr_pos[port] = vector(x_offset, y_offset) + + if port in self.write_ports: + if self.write_size: + # Add the write mask flops below the write mask AND array. + wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width, + y_offset) + self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX") + x_offset = self.wmask_dff_insts[port].lx() + + # Add spare write enable flops to the right of write mask flops + if self.num_spare_cols: + spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width, + y_offset) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") + x_offset = self.spare_wen_dff_insts[port].lx() + + # Add the data flops below the write mask flops. + data_pos[port] = vector(x_offset - self.data_dff_insts[port].width, + y_offset) + self.data_dff_insts[port].place(data_pos[port], mirror="MX") + + else: + wmask_pos[port] = vector(x_offset, y_offset) + data_pos[port] = vector(x_offset, y_offset) + spare_wen_pos[port] = vector(x_offset, y_offset) + def add_layout_pins(self): """ Add the top-level pins for a single bank SRAM with control. @@ -250,7 +186,6 @@ class sram_1bank(sram_base): lowest_coord = self.find_lowest_coords() bbox = [lowest_coord, highest_coord] - for port in self.all_ports: # Depending on the port, use the bottom/top or left/right sides # Port 0 is left/bottom @@ -370,17 +305,83 @@ class sram_1bank(sram_base): self.route_row_addr_dff() - if self.col_addr_dff: - self.route_col_addr_dff() + # if self.col_addr_dff: + # self.route_col_addr_dff() - self.route_data_dff() + # self.route_data_dff() - if self.write_size: - self.route_wmask_dff() + # if self.write_size: + # self.route_wmask_dff() - if self.num_spare_cols: - self.route_spare_wen_dff() + # if self.num_spare_cols: + # self.route_spare_wen_dff() + for port in self.all_ports: + self.route_dff(port) + def route_dff(self, port): + + route_map = [] + + # column mux dff + if self.col_addr_size > 0: + dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] + dff_pins = [self.col_addr_dff_insts[port].get_pin(x) for x in dff_names] + bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + route_map.extend(list(zip(bank_pins, dff_pins))) + + # spare wen dff + if self.num_spare_cols > 0 and port in self.write_ports: + dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] + dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] + bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + route_map.extend(list(zip(bank_pins, dff_pins))) + + # wmask dff + if self.num_wmasks > 0 and port in self.write_ports: + dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)] + dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names] + bank_names = ["bank_wmask{0}_{1}".format(port, x) for x in range(self.num_wmasks)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + route_map.extend(list(zip(bank_pins, dff_pins))) + + if port in self.write_ports: + # data dff + dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)] + dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] + bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + route_map.extend(list(zip(bank_pins, dff_pins))) + + if self.num_wmasks > 0 and port in self.write_ports: + vertical_layer = "m4" + layer_stack = self.m3_stack + else: + vertical_layer = "m2" + layer_stack = self.m1_stack + for (pin1, pin2) in route_map: + if pin1.layer != vertical_layer: + self.add_via_stack_center(from_layer=pin1.layer, + to_layer=vertical_layer, + offset=pin1.center()) + if pin2.layer != vertical_layer: + self.add_via_stack_center(from_layer=pin2.layer, + to_layer=vertical_layer, + offset=pin2.center()) + + if port == 0: + offset = vector(self.control_logic_insts[port].rx() + self.dff.width, + - self.data_bus_size + 2 * self.m1_pitch) + else: + offset = vector(0, + self.bank.height + 2 * self.m1_space) + + if len(route_map) > 0: + self.create_horizontal_channel_route(netlist=route_map, + offset=offset, + layer_stack=layer_stack) + def route_clk(self): """ Route the clock network """ @@ -423,8 +424,7 @@ class sram_1bank(sram_base): mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) self.add_wire(self.m2_stack[::-1], [dff_clk_pos, mid_pos, clk_steiner_pos]) - - if port in self.write_ports: + elif port in self.write_ports: data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") data_dff_clk_pos = data_dff_clk_pin.center() mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y) @@ -436,24 +436,6 @@ class sram_1bank(sram_base): self.add_wire(self.m2_stack[::-1], [data_dff_clk_pos, mid_pos, clk_steiner_pos]) - if self.write_size: - wmask_dff_clk_pin = self.wmask_dff_insts[port].get_pin("clk") - wmask_dff_clk_pos = wmask_dff_clk_pin.center() - mid_pos = vector(clk_steiner_pos.x, wmask_dff_clk_pos.y) - # In some designs, the steiner via will be too close to the mid_pos via - # so make the wire as wide as the contacts - self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height)) - self.add_wire(self.m2_stack[::-1], [wmask_dff_clk_pos, mid_pos, clk_steiner_pos]) - - if self.num_spare_cols: - spare_wen_dff_clk_pin = self.spare_wen_dff_insts[port].get_pin("clk") - spare_wen_dff_clk_pos = spare_wen_dff_clk_pin.center() - mid_pos = vector(clk_steiner_pos.x, spare_wen_dff_clk_pos.y) - # In some designs, the steiner via will be too close to the mid_pos via - # so make the wire as wide as the contacts - self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height)) - self.add_wire(self.m2_stack[::-1], [spare_wen_dff_clk_pos, mid_pos, clk_steiner_pos]) - def route_control_logic(self): """ Route the control logic pins that are not inputs """ From 0c9f52e22f3b8a9d1107cf1ebce746ac2f17b28a Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 28 Jun 2020 07:15:06 -0700 Subject: [PATCH 172/206] Realign col decoder and control by 1/4 so metal can pass over --- compiler/modules/bank.py | 11 ++++++++--- compiler/sram/sram_1bank.py | 13 +++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 6134b143..7bbed020 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -217,11 +217,12 @@ class bank(design.design): # Place the col decoder left aligned with wordline driver # This is also placed so that it's supply rails do not align with the SRAM-level # control logic to allow control signals to easily pass over in M3 - # by placing 1/2 a cell pitch down + # by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs + # may be routed in M3 or M4 x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width - y_offset = 0.5 * self.dff.height + self.column_decoder.height + y_offset = 1.25 * self.dff.height + self.column_decoder.height else: y_offset = 0 self.column_decoder_offsets[port] = vector(-x_offset, -y_offset) @@ -258,10 +259,14 @@ class bank(design.design): # UPPER RIGHT QUADRANT # Place the col decoder right aligned with wordline driver # Above the bitcell array with a well spacing + # This is also placed so that it's supply rails do not align with the SRAM-level + # control logic to allow control signals to easily pass over in M3 + # by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs + # may be routed in M3 or M4 x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width - y_offset = self.bitcell_array_top + 0.5 * self.dff.height + self.column_decoder.height + y_offset = self.bitcell_array_top + 1.25 * self.dff.height + self.column_decoder.height else: y_offset = self.bitcell_array_top self.column_decoder_offsets[port] = vector(x_offset, y_offset) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 4e2be40e..b8efd195 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -77,8 +77,10 @@ class sram_1bank(sram_base): port = 0 # This includes 2 M2 pitches for the row addr clock line. + # The delay line is aligned with the bitcell array while the control logic is aligned with the port_data + # using the control_logic_center value. control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch, - self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - 2 * self.bank.m2_gap) + self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y) self.control_logic_insts[port].place(control_pos[port]) # The row address bits are placed above the control logic aligned on the right. @@ -129,10 +131,13 @@ class sram_1bank(sram_base): port = 1 # This includes 2 M2 pitches for the row addr clock line + # The delay line is aligned with the bitcell array while the control logic is aligned with the port_data + # using the control_logic_center value. control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch, - self.bank.bank_array_ur.y + self.control_logic_insts[port].height - \ - (self.control_logic_insts[port].height - self.control_logic_insts[port].mod.control_logic_center.y) - + 2 * self.bank.m2_gap) + self.bank.bank_array_ur.y + + self.control_logic_insts[port].height + - self.control_logic_insts[port].height + + self.control_logic_insts[port].mod.control_logic_center.y) self.control_logic_insts[port].place(control_pos[port], mirror="XY") # The row address bits are placed above the control logic aligned on the left. From e774314add76384d4fdaa4a31346fc3da26027e7 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 28 Jun 2020 14:14:48 -0700 Subject: [PATCH 173/206] Separate write driver pins by M3 pitch --- technology/freepdk45/gds_lib/write_driver.gds | Bin 20480 -> 20332 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/write_driver.gds b/technology/freepdk45/gds_lib/write_driver.gds index 86015e7a7991ff9a3775c8e11fa1fee5ce5113a1..44a67dd0e23566a60892773f14112232386bca06 100644 GIT binary patch delta 281 zcmZoz!1!h!V;ln;0~1pOgA5}R`x78rhJlZPg+U6L&A`LNmXey5SejG9z#ziRYNv2y z_R`0}2P~Ch#4q@+dB-5bic{~#I3bD}GiLuwe{5GOSKp zAO=bdDVceb|C?(-ZGB+w4rS?Bs7z+FP=xZB99$u+%{~qa`~W7% BN$UUr delta 440 zcmaDekFj9^V;ln;0}~L-FfuXl16cwL+zh4+S`5Mr91NTcTnyR_#tb}6Y$>T}iKRIu z3=AU7tab`VW-oml{LNA+M*M>Bns*E`tV|4iKz))x9U2TK$a*)%3F%BOFjr(f%K!wE zr}xLt70I!8hq89qtC(>xFtG75G4L|5@i76_>j*G2Ffdhvv_fb`2?hpc zb_NC*4H4h`!Ca4#3oJerBtKch^3r5Cdqq{Sr3^eWtWI0NMldk&fK5CN Date: Sun, 28 Jun 2020 14:28:18 -0700 Subject: [PATCH 174/206] Pick correct side of pin in channel route. --- compiler/base/channel_route.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index d6048376..8a35891c 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -240,10 +240,18 @@ class channel_route(design.design): # Route each pin to the trunk for pin in pins: mid = vector(pin.center().x, trunk_offset.y) - self.add_path(self.vertical_layer, [pin.center(), mid]) + # Find the correct side of the pin + if pin.cy() < trunk_offset.y: + pin_pos = pin.uc() + else: + pin_pos = pin.bc() + self.add_path(self.vertical_layer, [pin_pos, mid]) self.add_via_center(layers=self.layer_stack, offset=mid, directions=self.directions) + self.add_via_stack_center(from_layer=pin.layer, + to_layer=self.vertical_layer, + offset=pin_pos) def add_vertical_trunk_route(self, pins, @@ -280,10 +288,18 @@ class channel_route(design.design): # Route each pin to the trunk for pin in pins: mid = vector(trunk_offset.x, pin.center().y) - self.add_path(self.horizontal_layer, [pin.center(), mid]) + # Find the correct side of the pin + if pin.cx() < trunk_offset.x: + pin_pos = pin.rc() + else: + pin_pos = pin.lc() + self.add_path(self.horizontal_layer, [pin_pos, mid]) self.add_via_center(layers=self.layer_stack, offset=mid, directions=self.directions) + self.add_via_stack_center(from_layer=pin.layer, + to_layer=self.horizontal_layer, + offset=pin_pos) def vcg_pin_overlap(self, pin1, pin2, pitch): """ Check for vertical or horizontal overlap of the two pins """ From 4df02dad679b92537571af123752effbb78ab4d5 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 28 Jun 2020 14:28:43 -0700 Subject: [PATCH 175/206] Move spare wen_dff to the right by spare columns --- compiler/sram/sram_1bank.py | 42 ++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index b8efd195..61a0e5ef 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -109,18 +109,20 @@ class sram_1bank(sram_base): self.wmask_dff_insts[port].place(wmask_pos[port]) x_offset = self.wmask_dff_insts[port].rx() - # Add spare write enable flops to the right of write mask flops + # Add the data flops below the write mask flops. + data_pos[port] = vector(x_offset, + y_offset) + self.data_dff_insts[port].place(data_pos[port]) + x_offset = self.data_dff_insts[port].rx() + + # Add spare write enable flops to the right of data flops since the spare columns + # will be on the right if self.num_spare_cols: spare_wen_pos[port] = vector(x_offset, y_offset) self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) x_offset = self.spare_wen_dff_insts[port].rx() - # Add the data flops below the write mask flops. - data_pos[port] = vector(x_offset, - y_offset) - self.data_dff_insts[port].place(data_pos[port]) - else: wmask_pos[port] = vector(x_offset, y_offset) data_pos[port] = vector(x_offset, y_offset) @@ -159,6 +161,14 @@ class sram_1bank(sram_base): col_addr_pos[port] = vector(x_offset, y_offset) if port in self.write_ports: + # Add spare write enable flops to the right of the data flops since the spare + # columns will be on the left + if self.num_spare_cols: + spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width, + y_offset) + self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") + x_offset = self.spare_wen_dff_insts[port].lx() + if self.write_size: # Add the write mask flops below the write mask AND array. wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width, @@ -166,18 +176,12 @@ class sram_1bank(sram_base): self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX") x_offset = self.wmask_dff_insts[port].lx() - # Add spare write enable flops to the right of write mask flops - if self.num_spare_cols: - spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width, - y_offset) - self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") - x_offset = self.spare_wen_dff_insts[port].lx() - # Add the data flops below the write mask flops. data_pos[port] = vector(x_offset - self.data_dff_insts[port].width, y_offset) self.data_dff_insts[port].place(data_pos[port], mirror="MX") + else: wmask_pos[port] = vector(x_offset, y_offset) data_pos[port] = vector(x_offset, y_offset) @@ -360,20 +364,9 @@ class sram_1bank(sram_base): route_map.extend(list(zip(bank_pins, dff_pins))) if self.num_wmasks > 0 and port in self.write_ports: - vertical_layer = "m4" layer_stack = self.m3_stack else: - vertical_layer = "m2" layer_stack = self.m1_stack - for (pin1, pin2) in route_map: - if pin1.layer != vertical_layer: - self.add_via_stack_center(from_layer=pin1.layer, - to_layer=vertical_layer, - offset=pin1.center()) - if pin2.layer != vertical_layer: - self.add_via_stack_center(from_layer=pin2.layer, - to_layer=vertical_layer, - offset=pin2.center()) if port == 0: offset = vector(self.control_logic_insts[port].rx() + self.dff.width, @@ -580,6 +573,7 @@ class sram_1bank(sram_base): self.create_horizontal_channel_route(netlist=route_map, offset=offset, layer_stack=self.m1_stack) + def route_spare_wen_dff(self): """ Connect the output of the spare write enable flops to the spare write drivers """ # This is where the channel will start (y-dimension at least) From 225fc69420164c6754c1b5aa1eaea204efe62388 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 28 Jun 2020 14:29:12 -0700 Subject: [PATCH 176/206] Use preferred routing direction --- compiler/modules/bank.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 7bbed020..ed835d43 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -888,7 +888,7 @@ class bank(design.design): route_map = list(zip(decode_pins, column_mux_pins)) if "li" in layer: stack = self.li_stack - directions = "nonpref" + directions = "pref" else: stack = self.m1_stack directions = "pref" From 709535f90fee00f0a7258aa7a7c354c81174b086 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 28 Jun 2020 14:47:17 -0700 Subject: [PATCH 177/206] Fix right perimeter pin coordinate bug --- 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 2a29be6b..50b9f78a 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1165,7 +1165,7 @@ class layout(): layer = "m3" elif side == "right": layer = "m3" - peri_pin_loc = vector(right, pin_loc.x) + peri_pin_loc = vector(right, pin_loc.y) elif side == "top": layer = "m4" peri_pin_loc = vector(pin_loc.x, top) From 051c8d8697c35caddea0e4dba2cb6c0015e63052 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 28 Jun 2020 14:47:54 -0700 Subject: [PATCH 178/206] Only add bitcells to dummy and replica rows and columns (the perimeter) --- compiler/modules/bitcell_base_array.py | 56 +++++++++----------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 6698df33..e162c225 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -9,6 +9,7 @@ import debug import design from tech import cell_properties + class bitcell_base_array(design.design): """ Abstract base class for bitcell-arrays -- bitcell, dummy @@ -68,10 +69,10 @@ class bitcell_base_array(design.design): pin_names = self.cell.get_all_bitline_names() for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(col)) + bitcell_pins.append(pin + "_{0}".format(col)) pin_names = self.cell.get_all_wl_names() for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(row)) + bitcell_pins.append(pin + "_{0}".format(row)) bitcell_pins.append("vdd") bitcell_pins.append("gnd") @@ -85,46 +86,27 @@ class bitcell_base_array(design.design): for col in range(self.column_size): for cell_column in column_list: - bl_pin = self.cell_inst[0,col].get_pin(cell_column) - self.add_layout_pin(text=cell_column+"_{0}".format(col), + bl_pin = self.cell_inst[0, col].get_pin(cell_column) + self.add_layout_pin(text=cell_column + "_{0}".format(col), layer=bl_pin.layer, - offset=bl_pin.ll().scale(1,0), + offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) for row in range(self.row_size): for cell_row in row_list: - wl_pin = self.cell_inst[row,0].get_pin(cell_row) - self.add_layout_pin(text=cell_row+"_{0}".format(row), + wl_pin = self.cell_inst[row, 0].get_pin(cell_row) + self.add_layout_pin(text=cell_row + "_{0}".format(row), layer=wl_pin.layer, - offset=wl_pin.ll().scale(0,1), + offset=wl_pin.ll().scale(0, 1), width=self.width, height=wl_pin.height()) - # For non-square via stacks, vertical/horizontal direction refers to the stack orientation in 2d space - # Default uses prefered directions for each layer; this cell property is only currently used by sky130 tech (03/20) - try: - bitcell_power_pin_directions = cell_properties.bitcell_power_pin_directions - except AttributeError: - bitcell_power_pin_directions = None - - # For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps. - try: - end_caps_enabled = cell_properties.bitcell.end_caps - except AttributeError: - end_caps_enabled = False - - # Add vdd/gnd via stacks - if not end_caps_enabled: - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row, col] - for pin_name in ["vdd", "gnd"]: - for pin in inst.get_pins(pin_name): - self.add_power_pin(name=pin_name, - loc=pin.center(), - directions=bitcell_power_pin_directions, - start_layer=pin.layer) + # Copy a vdd/gnd layout pin from every column in the first row + for col in range(self.column_size): + inst = self.cell_inst[0, col] + for pin_name in ["vdd", "gnd"]: + self.copy_layout_pin(inst, pin_name) def _adjust_x_offset(self, xoffset, col, col_offset): tempx = xoffset @@ -144,11 +126,10 @@ class bitcell_base_array(design.design): dir_x = True return (tempy, dir_x) - def place_array(self, name_template, row_offset=0): # We increase it by a well enclosure so the precharges don't overlap our wells - self.height = self.row_size*self.cell.height - self.width = self.column_size*self.cell.width + self.height = self.row_size * self.cell.height + self.width = self.column_size * self.cell.width xoffset = 0.0 for col in range(self.column_size): @@ -156,7 +137,6 @@ class bitcell_base_array(design.design): tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset) for row in range(self.row_size): - name = name_template.format(row, col) tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset) if dir_x and dir_y: @@ -168,7 +148,7 @@ class bitcell_base_array(design.design): else: dir_key = "" - self.cell_inst[row,col].place(offset=[tempx, tempy], - mirror=dir_key) + self.cell_inst[row, col].place(offset=[tempx, tempy], + mirror=dir_key) yoffset += self.cell.height xoffset += self.cell.width From 20324ab3c4ceca04f379e66d27178e92bc9b9474 Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 28 Jun 2020 14:55:58 -0700 Subject: [PATCH 179/206] Revert write driver pin spacing --- technology/freepdk45/gds_lib/write_driver.gds | Bin 20332 -> 20480 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/write_driver.gds b/technology/freepdk45/gds_lib/write_driver.gds index 44a67dd0e23566a60892773f14112232386bca06..86015e7a7991ff9a3775c8e11fa1fee5ce5113a1 100644 GIT binary patch delta 440 zcmaDekFj9^V;ln;0}~L-FfuXl16cwL+zh4+S`5Mr91NTcTnyR_#tb}6Y$>T}iKRIu z3=AU7tab`VW-oml{LNA+M*M>Bns*E`tV|4iKz))x9U2TK$a*)%3F%BOFjr(f%K!wE zr}xLt70I!8hq89qtC(>xFtG75G4L|5@i76_>j*G2Ffdhvv_fb`2?hpc zb_NC*4H4h`!Ca4#3oJerBtKch^3r5Cdqq{Sr3^eWtWI0NMldk&fK5CND}GiLuwe{5GOSKp zAO=bdDVceb|C?(-ZGB+w4rS?Bs7z+FP=xZB99$u+%{~qa`~W7% BN$UUr From 751eab202bd308b715541424fe0ca9b736ad347d Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 28 Jun 2020 15:06:29 -0700 Subject: [PATCH 180/206] Move row addr flops away from predecode. Route spare wen separately on lower layer. --- compiler/sram/sram_1bank.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 61a0e5ef..73df61bb 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -86,7 +86,7 @@ class sram_1bank(sram_base): # The row address bits are placed above the control logic aligned on the right. x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width # It is above the control logic but below the top of the bitcell array - y_offset = max(self.control_logic_insts[port].uy(), self.control_logic_insts[port].uy() + self.dff.height) + y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height) row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_dff_insts[port].place(row_addr_pos[port]) @@ -145,7 +145,7 @@ class sram_1bank(sram_base): # The row address bits are placed above the control logic aligned on the left. x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width # It is below the control logic but below the bottom of the bitcell array - y_offset = min(self.control_logic_insts[port].by(), self.control_logic_insts[port].by() - self.dff.height) + y_offset = min(self.control_logic_insts[port].by(), self.bank_inst.by() + self.row_addr_dff_insts[port].height) row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY") @@ -180,8 +180,6 @@ class sram_1bank(sram_base): data_pos[port] = vector(x_offset - self.data_dff_insts[port].width, y_offset) self.data_dff_insts[port].place(data_pos[port], mirror="MX") - - else: wmask_pos[port] = vector(x_offset, y_offset) data_pos[port] = vector(x_offset, y_offset) @@ -339,14 +337,6 @@ class sram_1bank(sram_base): bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, dff_pins))) - # spare wen dff - if self.num_spare_cols > 0 and port in self.write_ports: - dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] - dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] - bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] - bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] - route_map.extend(list(zip(bank_pins, dff_pins))) - # wmask dff if self.num_wmasks > 0 and port in self.write_ports: dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)] @@ -379,6 +369,18 @@ class sram_1bank(sram_base): self.create_horizontal_channel_route(netlist=route_map, offset=offset, layer_stack=layer_stack) + + # Route these separately because sometimes the pin pitch on the write driver is too narrow for M3 (FreePDK45) + # spare wen dff + if self.num_spare_cols > 0 and port in self.write_ports: + dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] + dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] + bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + route_map = zip(bank_pins, dff_pins) + self.create_horizontal_channel_route(netlist=route_map, + offset=offset, + layer_stack=self.m1_stack) def route_clk(self): """ Route the clock network """ From 5285468380cfd20e8dc61f58a6503a883a9c131b Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 28 Jun 2020 15:09:47 -0700 Subject: [PATCH 181/206] All bitcells need a vdd/gnd pin --- compiler/modules/bitcell_base_array.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index e162c225..7d241b4d 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -102,11 +102,12 @@ class bitcell_base_array(design.design): width=self.width, height=wl_pin.height()) - # Copy a vdd/gnd layout pin from every column in the first row - for col in range(self.column_size): - inst = self.cell_inst[0, col] - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) + # Copy a vdd/gnd layout pin from every cell + for row in range(self.row_size): + for col in range(self.column_size): + inst = self.cell_inst[row, col] + for pin_name in ["vdd", "gnd"]: + self.copy_layout_pin(inst, pin_name) def _adjust_x_offset(self, xoffset, col, col_offset): tempx = xoffset From 47f541df0e07bbd66a13db7ae203e508c8ac836a Mon Sep 17 00:00:00 2001 From: mrg Date: Sun, 28 Jun 2020 16:58:28 -0700 Subject: [PATCH 182/206] Fix bugs in channel route. --- compiler/base/channel_route.py | 77 +++++++++++++++------------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index 8a35891c..5e9b9531 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -216,42 +216,38 @@ class channel_route(design.design): """ max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) - + # if we are less than a pitch, just create a non-preferred layer jog - if max_x - min_x <= pitch: + non_preferred_route = max_x - min_x <= pitch + + if non_preferred_route: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] - # Add the horizontal trunk on the vertical layer! self.add_path(self.vertical_layer, [vector(min_x - half_layer_width, trunk_offset.y), vector(max_x + half_layer_width, trunk_offset.y)]) - - # Route each pin to the trunk - for pin in pins: - # No bend needed here - mid = vector(pin.center().x, trunk_offset.y) - self.add_path(self.vertical_layer, [pin.center(), mid]) else: # Add the horizontal trunk self.add_path(self.horizontal_layer, [vector(min_x, trunk_offset.y), vector(max_x, trunk_offset.y)]) - # Route each pin to the trunk - for pin in pins: - mid = vector(pin.center().x, trunk_offset.y) - # Find the correct side of the pin - if pin.cy() < trunk_offset.y: - pin_pos = pin.uc() - else: - pin_pos = pin.bc() - self.add_path(self.vertical_layer, [pin_pos, mid]) + # Route each pin to the trunk + for pin in pins: + # Find the correct side of the pin + if pin.cy() < trunk_offset.y: + pin_pos = pin.uc() + else: + pin_pos = pin.bc() + mid = vector(pin_pos.x, trunk_offset.y) + self.add_path(self.vertical_layer, [pin_pos, mid]) + if not non_preferred_route: self.add_via_center(layers=self.layer_stack, offset=mid, directions=self.directions) - self.add_via_stack_center(from_layer=pin.layer, - to_layer=self.vertical_layer, - offset=pin_pos) + self.add_via_stack_center(from_layer=pin.layer, + to_layer=self.vertical_layer, + offset=pin_pos) def add_vertical_trunk_route(self, pins, @@ -263,43 +259,38 @@ class channel_route(design.design): """ max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) - + # if we are less than a pitch, just create a non-preferred layer jog - if max_y - min_y <= pitch: - + non_preferred_route = max_y - min_y <= pitch + + if non_preferred_route: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] - # Add the vertical trunk on the horizontal layer! self.add_path(self.horizontal_layer, [vector(trunk_offset.x, min_y - half_layer_width), vector(trunk_offset.x, max_y + half_layer_width)]) - - # Route each pin to the trunk - for pin in pins: - # No bend needed here - mid = vector(trunk_offset.x, pin.center().y) - self.add_path(self.horizontal_layer, [pin.center(), mid]) else: # Add the vertical trunk self.add_path(self.vertical_layer, [vector(trunk_offset.x, min_y), vector(trunk_offset.x, max_y)]) - # Route each pin to the trunk - for pin in pins: - mid = vector(trunk_offset.x, pin.center().y) - # Find the correct side of the pin - if pin.cx() < trunk_offset.x: - pin_pos = pin.rc() - else: - pin_pos = pin.lc() - self.add_path(self.horizontal_layer, [pin_pos, mid]) + # Route each pin to the trunk + for pin in pins: + # Find the correct side of the pin + if pin.cx() < trunk_offset.x: + pin_pos = pin.rc() + else: + pin_pos = pin.lc() + mid = vector(trunk_offset.x, pin_pos.y) + self.add_path(self.horizontal_layer, [pin_pos, mid]) + if not non_preferred_route: self.add_via_center(layers=self.layer_stack, offset=mid, directions=self.directions) - self.add_via_stack_center(from_layer=pin.layer, - to_layer=self.horizontal_layer, - offset=pin_pos) + self.add_via_stack_center(from_layer=pin.layer, + to_layer=self.horizontal_layer, + offset=pin_pos) def vcg_pin_overlap(self, pin1, pin2, pitch): """ Check for vertical or horizontal overlap of the two pins """ From 5f3a45b91b41d98d99560c9a3b6f2e55fb4c98a5 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 29 Jun 2020 05:54:30 -0700 Subject: [PATCH 183/206] Compute bus size separately for ports --- compiler/sram/sram_1bank.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 73df61bb..ac86eb94 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -71,7 +71,19 @@ class sram_1bank(sram_base): # If a vertical channel, they rely on the horizontal channel non-preferred (contacted) pitch. # So, m3 non-pref pitch means that this is routed on the m2 layer. self.data_bus_gap = self.m4_nonpref_pitch * 2 - self.data_bus_size = self.m4_nonpref_pitch * (self.word_size + self.num_spare_cols + self.num_wmasks + self.col_addr_size + self.num_spare_cols) + self.data_bus_gap + + # Spare wen are on a separate layer so not included + self.data_bus_size = [None] * len(self.all_ports) + for port in self.all_ports: + # All ports need the col addr flops + self.data_bus_size[port] = self.col_addr_size + # Write ports need the data input flops and write mask flops + if port in self.write_ports: + self.data_bus_size[port] += self.num_wmasks + self.word_size + # Convert to length + self.data_bus_size[port] *= self.m4_nonpref_pitch + # Add the gap in unit length + self.data_bus_size[port] += self.data_bus_gap # Port 0 port = 0 @@ -92,7 +104,7 @@ class sram_1bank(sram_base): # Add the col address flops below the bank to the right of the control logic x_offset = self.control_logic_insts[port].rx() + self.dff.width - y_offset = - self.data_bus_size - self.dff.height + y_offset = - self.data_bus_size[port] - self.dff.height if self.col_addr_dff: col_addr_pos[port] = vector(x_offset, y_offset) @@ -151,7 +163,7 @@ class sram_1bank(sram_base): # Add the col address flops below the bank to the right of the control logic x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width - y_offset = self.bank.height + self.data_bus_size + self.dff.height + y_offset = self.bank.height + self.data_bus_size[port] + self.dff.height if self.col_addr_dff: col_addr_pos[port] = vector(x_offset - self.col_addr_dff_insts[port].width, y_offset) @@ -360,7 +372,7 @@ class sram_1bank(sram_base): if port == 0: offset = vector(self.control_logic_insts[port].rx() + self.dff.width, - - self.data_bus_size + 2 * self.m1_pitch) + - self.data_bus_size[port] + 2 * self.m1_pitch) else: offset = vector(0, self.bank.height + 2 * self.m1_space) @@ -507,7 +519,7 @@ class sram_1bank(sram_base): # This is where the channel will start (y-dimension at least) for port in self.write_ports: if port % 2: - offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size) + offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size[port]) else: offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap) From 1bc0775810685ba142e89a917fb7ae7455303ece Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 29 Jun 2020 10:03:24 -0700 Subject: [PATCH 184/206] Only add pins to periphery --- compiler/modules/replica_bitcell_array.py | 14 ++++---------- compiler/modules/replica_column.py | 9 ++------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 57bb3cf9..09a014ce 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -378,16 +378,10 @@ class replica_bitcell_array(design.design): width=pin.width(), height=self.height) - # For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps. - try: - if cell_properties.bitcell.end_caps_enabled: - supply_insts = [self.dummy_col_left_inst, self.dummy_col_right_inst, - self.dummy_row_top_inst, self.dummy_row_bot_inst] + list(self.replica_col_inst.values()) - else: - supply_insts = self.insts - except AttributeError: - supply_insts = self.insts - + # vdd/gnd are only connected in the perimeter cells + # replica column should only have a vdd/gnd in the dummy cell on top/bottom + supply_insts = [self.dummy_col_left_inst, self.dummy_col_right_inst, + self.dummy_row_top_inst, self.dummy_row_bot_inst] + list(self.replica_col_inst.values()) for pin_name in ["vdd", "gnd"]: for inst in supply_insts: pin_list = inst.get_pins(pin_name) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 58d35e7f..9cd65e57 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -183,13 +183,8 @@ class replica_column(design.design): width=self.width, height=wl_pin.height()) - # For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps. - if end_caps_enabled: - supply_insts = [self.cell_inst[0], self.cell_inst[self.total_size - 1]] - else: - supply_insts = self.cell_inst.values() - - for inst in supply_insts: + # Supplies are only connected in the ends + for inst in [self.cell_inst[0], self.cell_inst[self.total_size - 1]]: for pin_name in ["vdd", "gnd"]: if pin_name in inst.mod.pins: self.copy_layout_pin(inst, pin_name) From 07d0f3af8e031f60cf0783deb7348764fbd35667 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 29 Jun 2020 11:46:59 -0700 Subject: [PATCH 185/206] Only copy end-cap pins to the bank level --- compiler/base/hierarchy_layout.py | 4 ++-- compiler/modules/bank.py | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 50b9f78a..378a4657 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1101,7 +1101,7 @@ class layout(): height=ymax - ymin) return rect - def copy_power_pins(self, inst, name): + def copy_power_pins(self, inst, name, add_vias=True): """ This will copy a power pin if it is on the lowest power_grid layer. If it is on M1, it will add a power via too. @@ -1115,7 +1115,7 @@ class layout(): pin.width(), pin.height()) - else: + elif add_vias: self.add_power_pin(name, pin.center(), start_layer=pin.layer) def add_power_pin(self, name, loc, size=[1, 1], directions=None, start_layer="m1"): diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index ed835d43..e18660e3 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -132,14 +132,17 @@ class bank(design.design): # Connect the rbl to the port data pin bl_pin = self.port_data_inst[port].get_pin("rbl_bl") if port % 2: - pin_offset = bl_pin.uc() + pin_pos = bl_pin.uc() + pin_offset = pin_pos + vector(0, self.m3_pitch) left_right_offset = vector(self.max_x_offset, pin_offset.y) else: - pin_offset = bl_pin.bc() + pin_pos = bl_pin.bc() + pin_offset = pin_pos - vector(0, self.m3_pitch) left_right_offset = vector(self.min_x_offset, pin_offset.y) self.add_via_stack_center(from_layer=bl_pin.layer, to_layer="m3", offset=pin_offset) + self.add_path(bl_pin.layer, [pin_offset, pin_pos]) self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port), layer="m3", start=left_right_offset, @@ -583,9 +586,11 @@ class bank(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ + # Copy only the power pins already on the power layer + # (this won't add vias to internal bitcell pins, for example) for inst in self.insts: - self.copy_power_pins(inst, "vdd") - self.copy_power_pins(inst, "gnd") + self.copy_power_pins(inst, "vdd", add_vias=False) + self.copy_power_pins(inst, "gnd", add_vias=False) def route_bank_select(self, port): """ Route the bank select logic. """ From e97644c424d562d3dee9a1a8ca1fdb51937abe54 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 29 Jun 2020 14:42:24 -0700 Subject: [PATCH 186/206] Only do reverse lookup on valid interconnect layers since layer numbers can be shared. --- compiler/base/pin_layout.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index f758a903..dac4e525 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -8,7 +8,7 @@ import debug from tech import GDS, drc from vector import vector -from tech import layer +from tech import layer, layer_indices import math @@ -31,12 +31,15 @@ class pin_layout: debug.check(self.width() > 0, "Zero width pin.") debug.check(self.height() > 0, "Zero height pin.") + # These are the valid pin layers + valid_layers = { x: layer[x] for x in layer_indices.keys()} + # if it's a string, use the name if type(layer_name_pp) == str: self._layer = layer_name_pp # else it is required to be a lpp else: - for (layer_name, lpp) in layer.items(): + for (layer_name, lpp) in valid_layers.items(): if not lpp: continue if self.same_lpp(layer_name_pp, lpp): From 4e7e0c5954316b3d3d0ef1f0a31a6f20a94c4905 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 29 Jun 2020 15:28:16 -0700 Subject: [PATCH 187/206] Skip test in sky130 --- compiler/tests/14_replica_bitcell_array_1rw_1r_test.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py index a36fc80f..626725c4 100755 --- a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py @@ -28,9 +28,11 @@ class replica_bitcell_array_1rw_1r_test(openram_test): a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0, 1]) self.local_check(a) - debug.info(2, "Testing 4x4 array for cell_1rw_1r") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0, 1]) - self.local_check(a) + # Sky 130 has restrictions on the symmetries + if OPTS.tech_name != "sky130": + debug.info(2, "Testing 4x4 array for cell_1rw_1r") + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0, 1]) + self.local_check(a) globals.end_openram() From bec948dcc3d08caab36e9937f73ebb55d511c0e5 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 29 Jun 2020 15:28:55 -0700 Subject: [PATCH 188/206] Fix error in when to add vias for array power --- compiler/modules/replica_bitcell_array.py | 6 +++++- compiler/modules/replica_column.py | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 09a014ce..0c7e412e 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -381,7 +381,7 @@ class replica_bitcell_array(design.design): # vdd/gnd are only connected in the perimeter cells # replica column should only have a vdd/gnd in the dummy cell on top/bottom supply_insts = [self.dummy_col_left_inst, self.dummy_col_right_inst, - self.dummy_row_top_inst, self.dummy_row_bot_inst] + list(self.replica_col_inst.values()) + self.dummy_row_top_inst, self.dummy_row_bot_inst] for pin_name in ["vdd", "gnd"]: for inst in supply_insts: pin_list = inst.get_pins(pin_name) @@ -390,6 +390,10 @@ class replica_bitcell_array(design.design): loc=pin.center(), directions=("V", "V"), start_layer=pin.layer) + + for inst in list(self.replica_col_inst.values()): + self.copy_layout_pin(inst, pin_name) + self.copy_layout_pin(inst, pin_name) def get_rbl_wl_name(self, port): """ Return the WL for the given RBL port """ diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 9cd65e57..9613e6fa 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -184,9 +184,11 @@ class replica_column(design.design): height=wl_pin.height()) # Supplies are only connected in the ends - for inst in [self.cell_inst[0], self.cell_inst[self.total_size - 1]]: + for (index, inst) in self.cell_inst.items(): for pin_name in ["vdd", "gnd"]: - if pin_name in inst.mod.pins: + if inst in [self.cell_inst[0], self.cell_inst[self.total_size - 1]]: + self.copy_power_pins(inst, pin_name) + else: self.copy_layout_pin(inst, pin_name) def get_bitcell_pins(self, col, row): From 459e3789b815ddf7c3332d3a555dc0c912bdf6f4 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 29 Jun 2020 16:23:25 -0700 Subject: [PATCH 189/206] Change control layers in sky130. --- compiler/modules/bank.py | 32 +++++++---- compiler/modules/precharge_array.py | 28 +++++----- compiler/modules/sense_amp_array.py | 25 ++++++--- .../modules/single_level_column_mux_array.py | 56 ++++++++++--------- 4 files changed, 84 insertions(+), 57 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index e18660e3..5716986f 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -592,6 +592,18 @@ class bank(design.design): self.copy_power_pins(inst, "vdd", add_vias=False) self.copy_power_pins(inst, "gnd", add_vias=False) + # If we use the pinvbuf as the decoder, we need to add power pins. + # Other decoders already have them. + if self.col_addr_size == 1: + for port in self.all_ports: + inst = self.column_decoder_inst[port] + for pin_name in ["vdd", "gnd"]: + pin_list = inst.get_pins(pin_name) + for pin in pin_list: + self.add_power_pin(pin_name, + pin.center(), + start_layer=pin.layer) + def route_bank_select(self, port): """ Route the bank select logic. """ @@ -862,6 +874,13 @@ class bank(design.design): if not self.col_addr_size>0: return + if OPTS.tech_name == "sky130": + stack = self.m2_stack + pitch = self.m3_pitch + else: + stack = self.m1_stack + pitch = self.m2_pitch + if self.col_addr_size == 1: # Connect to sel[0] and sel[1] @@ -881,9 +900,9 @@ class bank(design.design): self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) if port % 2: - offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_nonpref_pitch, 0) + offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * pitch, 0) else: - offset = self.column_decoder_inst[port].lr() + vector(self.m2_nonpref_pitch, 0) + offset = self.column_decoder_inst[port].lr() + vector(pitch, 0) decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names] @@ -891,16 +910,9 @@ class bank(design.design): column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names] route_map = list(zip(decode_pins, column_mux_pins)) - if "li" in layer: - stack = self.li_stack - directions = "pref" - else: - stack = self.m1_stack - directions = "pref" self.create_vertical_channel_route(route_map, offset, - stack, - directions=directions) + stack) def add_lvs_correspondence_points(self): """ diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 3a3b8c8b..d37de64f 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -30,6 +30,11 @@ class precharge_array(design.design): self.bitcell_br = bitcell_br self.column_offset = column_offset + if OPTS.tech_name == "sky130": + self.en_bar_layer = "m3" + else: + self.en_bar_layer = "m1" + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -74,21 +79,18 @@ class precharge_array(design.design): def add_layout_pins(self): - en_bar_pin = self.pc_cell.get_pin("en_bar") - if en_bar_pin.layer =="li": - en_bar_layer = "m3" - else: - en_bar_layer = en_bar_pin.layer - r = self.add_layout_pin(text="en_bar", - layer=en_bar_layer, - offset=en_bar_pin.ll(), - width=self.width, - height=en_bar_pin.height()) - self.add_via_stack_center(from_layer=en_bar_pin.layer, - to_layer=en_bar_layer, - offset = r.center()) + en_pin = self.pc_cell.get_pin("en_bar") + start_offset = en_pin.lc().scale(0, 1) + end_offset = start_offset + vector(self.width, 0) + self.add_layout_pin_segment_center(text="en_bar", + layer=self.en_bar_layer, + start=start_offset, + end=end_offset) for inst in self.local_insts: + self.add_via_stack_center(from_layer=en_pin.layer, + to_layer=self.en_bar_layer, + offset=inst.get_pin("en_bar").center()) self.copy_layout_pin(inst, "vdd") for i in range(len(self.local_insts)): diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 5d41b2a0..98cbee66 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -37,6 +37,11 @@ class sense_amp_array(design.design): self.column_offset = column_offset self.row_size = self.word_size * self.words_per_row + if OPTS.tech_name == "sky130": + self.en_layer = "m3" + else: + self.en_layer = "m1" + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -173,14 +178,18 @@ class sense_amp_array(design.design): height=dout_pin.height()) def route_rails(self): - # add sclk rail across entire array - sclk = self.amp.get_pin(self.amp.en_name) - sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0, 1) - self.add_layout_pin(text=self.en_name, - layer=sclk.layer, - offset=sclk_offset, - width=self.width, - height=drc("minwidth_" + sclk.layer)) + # Add enable across the array + en_pin = self.amp.get_pin(self.amp.en_name) + start_offset = en_pin.lc().scale(0, 1) + end_offset = start_offset + vector(self.width, 0) + self.add_layout_pin_segment_center(text=self.en_name, + layer=self.en_layer, + start=start_offset, + end=end_offset) + for inst in self.local_insts: + self.add_via_stack_center(from_layer=en_pin.layer, + to_layer=self.en_layer, + offset=inst.get_pin(self.amp.en_name).center()) def input_load(self): return self.amp.input_load() diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 37fd4dc1..8b01d111 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -32,14 +32,16 @@ class single_level_column_mux_array(design.design): self.bitcell_br = bitcell_br self.column_offset = column_offset - if "li" in layer: - self.col_mux_stack = self.m1_stack[::-1] - self.col_mux_stack_pitch = self.m2_pitch + if OPTS.tech_name == "sky130": + self.sel_layer = "m3" + self.sel_pitch = self.m3_pitch + self.bitline_layer = "m1" else: - self.col_mux_stack = self.m1_stack - self.col_mux_stack_pitch = self.m1_pitch + self.sel_layer = "m1" + self.sel_pitch = self.m2_pitch + self.bitline_layer = "m2" - if preferred_directions[self.col_mux_stack[0]] == "V": + if preferred_directions[self.sel_layer] == "V": self.via_directions = ("H", "H") else: self.via_directions = "pref" @@ -96,7 +98,7 @@ class single_level_column_mux_array(design.design): self.width = self.columns * self.mux.width # one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br # one extra route pitch is to space from the sense amp - self.route_height = (self.words_per_row + 3) * self.col_mux_stack_pitch + self.route_height = (self.words_per_row + 3) * self.sel_pitch def create_array(self): self.mux_inst = [] @@ -157,9 +159,9 @@ class single_level_column_mux_array(design.design): def add_horizontal_input_rail(self): """ Create address input rails below the mux transistors """ for j in range(self.words_per_row): - offset = vector(0, self.route_height + (j - self.words_per_row) * self.col_mux_stack_pitch) + offset = vector(0, self.route_height + (j - self.words_per_row) * self.sel_pitch) self.add_layout_pin(text="sel_{}".format(j), - layer=self.col_mux_stack[0], + layer=self.sel_layer, offset=offset, width=self.mux.width * self.columns) @@ -178,7 +180,7 @@ class single_level_column_mux_array(design.design): offset = vector(gate_offset.x, self.get_pin("sel_{}".format(sel_index)).cy()) self.add_via_stack_center(from_layer="poly", - to_layer=self.col_mux_stack[0], + to_layer=self.sel_layer, offset=offset, directions=self.via_directions) self.add_path("poly", [offset, gate_offset]) @@ -190,42 +192,44 @@ class single_level_column_mux_array(design.design): bl_offset_begin = self.mux_inst[j].get_pin("bl_out").bc() br_offset_begin = self.mux_inst[j].get_pin("br_out").bc() - bl_out_offset_begin = bl_offset_begin - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch) - br_out_offset_begin = br_offset_begin - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch) + bl_out_offset_begin = bl_offset_begin - vector(0, (self.words_per_row + 1) * self.sel_pitch) + br_out_offset_begin = br_offset_begin - vector(0, (self.words_per_row + 2) * self.sel_pitch) # Add the horizontal wires for the first bit if j % self.words_per_row == 0: bl_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("bl_out").bc() br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc() - bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch) - br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch) + bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.sel_pitch) + br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.sel_pitch) - self.add_path(self.col_mux_stack[0], [bl_out_offset_begin, bl_out_offset_end]) - self.add_path(self.col_mux_stack[0], [br_out_offset_begin, br_out_offset_end]) + self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end]) + self.add_path(self.sel_layer, [br_out_offset_begin, br_out_offset_end]) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)), - layer=self.col_mux_stack[2], + layer=self.bitline_layer, start=bl_offset_begin, end=bl_out_offset_begin) self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)), - layer=self.col_mux_stack[2], + layer=self.bitline_layer, start=br_offset_begin, end=br_out_offset_begin) else: - self.add_path(self.col_mux_stack[2], [bl_out_offset_begin, bl_offset_begin]) - self.add_path(self.col_mux_stack[2], [br_out_offset_begin, br_offset_begin]) + self.add_path(self.bitline_layer, [bl_out_offset_begin, bl_offset_begin]) + self.add_path(self.bitline_layer, [br_out_offset_begin, br_offset_begin]) # This via is on the right of the wire - self.add_via_center(layers=self.col_mux_stack, - offset=bl_out_offset_begin, - directions=self.via_directions) + self.add_via_stack_center(from_layer=self.bitline_layer, + to_layer=self.sel_layer, + offset=bl_out_offset_begin, + directions=self.via_directions) # This via is on the left of the wire - self.add_via_center(layers=self.col_mux_stack, - offset=br_out_offset_begin, - directions=self.via_directions) + self.add_via_stack_center(from_layer=self.bitline_layer, + to_layer=self.sel_layer, + offset=br_out_offset_begin, + directions=self.via_directions) def get_drain_cin(self): """Get the relative capacitance of the drain of the NMOS pass TX""" From 372a8a728efb4f0a5cf4af636446626305193a16 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 29 Jun 2020 16:47:34 -0700 Subject: [PATCH 190/206] Off by one error in channel spacing --- compiler/modules/bank.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 5716986f..390d17a3 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -900,7 +900,7 @@ class bank(design.design): self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) if port % 2: - offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * pitch, 0) + offset = self.column_decoder_inst[port].ll() - vector((self.num_col_addr_lines + 1) * pitch, 0) else: offset = self.column_decoder_inst[port].lr() + vector(pitch, 0) From 9b939c9a1a3c9890f299d038da47e7e420a97604 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 30 Jun 2020 07:16:05 -0700 Subject: [PATCH 191/206] DRC/LVS and errors fixes. Only enact pdb if assert fails in debug.error. Only run drc/lvs one time in parse_info by saving result. Cleanup drc/lvs output. --- compiler/base/hierarchy_design.py | 6 +++++- compiler/characterizer/lib.py | 8 ++++++-- compiler/debug.py | 2 +- compiler/verify/calibre.py | 25 +++++++++++-------------- compiler/verify/magic.py | 5 +++-- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 1321f06a..b0370179 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -7,7 +7,6 @@ # import hierarchy_layout import hierarchy_spice -import verify import debug import os from globals import OPTS @@ -55,6 +54,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): def DRC_LVS(self, final_verification=False, force_check=False): """Checks both DRC and LVS for a module""" + import verify # No layout to check if OPTS.netlist_only: @@ -94,6 +94,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): def DRC(self, final_verification=False): """Checks DRC for a module""" + import verify + # Unit tests will check themselves. # Do not run if disabled in options. @@ -117,6 +119,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): def LVS(self, final_verification=False): """Checks LVS for a module""" + import verify + # Unit tests will check themselves. # Do not run if disabled in options. diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index b0df4394..92882db7 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -657,8 +657,12 @@ class lib: )) # information of checks - (drc_errors, lvs_errors) = self.sram.DRC_LVS(final_verification=True) - datasheet.write("{0},{1},".format(drc_errors, lvs_errors)) + # run it only the first time + try: + datasheet.write("{0},{1},".format(self.drc_errors, self.lvs_errors)) + except AttributeError: + (self.drc_errors, self.lvs_errors) = self.sram.DRC_LVS(final_verification=True) + datasheet.write("{0},{1},".format(self.drc_errors, self.lvs_errors)) # write area datasheet.write(str(self.sram.width * self.sram.height) + ',') diff --git a/compiler/debug.py b/compiler/debug.py index a902bca0..f07471cc 100644 --- a/compiler/debug.py +++ b/compiler/debug.py @@ -40,7 +40,7 @@ def error(str, return_value=0): log("ERROR: file {0}: line {1}: {2}\n".format( os.path.basename(filename), line_number, str)) - if globals.OPTS.debug_level > 0: + if globals.OPTS.debug_level > 0 and return_value != 0: import pdb pdb.set_trace() assert return_value == 0 diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 0a975599..443c91ca 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -219,16 +219,14 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False): errors = int(re.split(r'\W+', results[2])[5]) # always display this summary - if errors > 0: - debug.error("{0}\tGeometries: {1}\tChecks: {2}\tErrors: {3}".format(cell_name, + result_str = "{0}\tGeometries: {1}\tChecks: {2}\tErrors: {3}".format(cell_name, geometries, rulechecks, - errors)) + errors) + if errors > 0: + debug.warning(result_str) else: - debug.info(1, "{0}\tGeometries: {1}\tChecks: {2}\tErrors: {3}".format(cell_name, - geometries, - rulechecks, - errors)) + debug.info(1, result_str) return errors @@ -307,16 +305,15 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): out_errors = len(stdouterrors) total_errors = summary_errors + out_errors + ext_errors - if total_errors > 0: - debug.error("{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name, + # always display this summary + result_str = "{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name, summary_errors, out_errors, - ext_errors)) + ext_errors) + if total_errors > 0: + debug.warning(result_str) else: - debug.info(1, "{0}\tSummary: {1}\tOutput: {2}\tExtraction: {3}".format(cell_name, - summary_errors, - out_errors, - ext_errors)) + debug.info(1, result_str) return total_errors diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index e20c0499..5df6e698 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -200,13 +200,14 @@ def run_drc(cell_name, gds_name, extract=True, final_verification=False): # always display this summary + result_str = "DRC Errors {0}\t{1}".format(cell_name, errors) if errors > 0: for line in results: if "error tiles" in line: debug.info(1,line.rstrip("\n")) - debug.error("DRC Errors {0}\t{1}".format(cell_name, errors)) + debug.warning(result_str) else: - debug.info(1, "DRC Errors {0}\t{1}".format(cell_name, errors)) + debug.info(1, result_str) return errors From 8cedeeb3d945c77157c9977668a33751564983e9 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 30 Jun 2020 10:57:41 -0700 Subject: [PATCH 192/206] Widen pitch of control bus in bank. --- compiler/base/hierarchy_layout.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 378a4657..c57de915 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1135,6 +1135,7 @@ class layout(): size=size, offset=loc, directions=directions) + # Hack for min area if OPTS.tech_name == "sky130": width = round_to_grid(sqrt(drc["minarea_m3"])) From eb11ac22f3c3d5a2198817d25e1be500138cf7ec Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 30 Jun 2020 10:58:09 -0700 Subject: [PATCH 193/206] Widen pitch of control bus in bank. --- compiler/modules/bank.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 390d17a3..158ac37b 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -316,7 +316,7 @@ class bank(design.design): self.input_control_signals = [] port_num = 0 for port in range(OPTS.num_rw_ports): - self.input_control_signals.append(["w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num), "wl_en{}".format(port_num)]) + self.input_control_signals.append(["s_en{}".format(port_num), "w_en{}".format(port_num), "p_en_bar{}".format(port_num), "wl_en{}".format(port_num)]) port_num += 1 for port in range(OPTS.num_w_ports): self.input_control_signals.append(["w_en{}".format(port_num), "p_en_bar{}".format(port_num), "wl_en{}".format(port_num)]) @@ -329,7 +329,7 @@ class bank(design.design): self.num_control_lines = [len(x) for x in self.input_control_signals] # The width of this bus is needed to place other modules (e.g. decoder) for each port - self.central_bus_width = [self.m2_pitch * x + self.m2_width for x in self.num_control_lines] + self.central_bus_width = [self.m3_pitch * x + self.m3_width for x in self.num_control_lines] # These will be outputs of the gaters if this is multibank, if not, normal signals. self.control_signals = [] @@ -338,6 +338,7 @@ class bank(design.design): self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]]) else: self.control_signals.append(self.input_control_signals[port]) + # The central bus is the column address (one hot) and row address (binary) if self.col_addr_size>0: @@ -672,7 +673,8 @@ class bank(design.design): names=self.control_signals[0], length=control_bus_length, vertical=True, - make_pins=(self.num_banks==1)) + make_pins=(self.num_banks==1), + pitch=self.m3_pitch) # Port 1 if len(self.all_ports)==2: @@ -686,7 +688,8 @@ class bank(design.design): names=list(reversed(self.control_signals[1])), length=control_bus_length, vertical=True, - make_pins=(self.num_banks==1)) + make_pins=(self.num_banks==1), + pitch=self.m3_pitch) def route_port_data_to_bitcell_array(self, port): """ Routing of BL and BR between port data and bitcell array """ From 5626fd182e2c993ed107154c809802db28c3f68c Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 30 Jun 2020 10:58:24 -0700 Subject: [PATCH 194/206] Extra track in data bus. Remove old code. --- compiler/sram/sram_1bank.py | 146 +----------------------------------- 1 file changed, 3 insertions(+), 143 deletions(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index ac86eb94..cdc5ab92 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -73,10 +73,11 @@ class sram_1bank(sram_base): self.data_bus_gap = self.m4_nonpref_pitch * 2 # Spare wen are on a separate layer so not included - self.data_bus_size = [None] * len(self.all_ports) + # Start with 1 track minimum + self.data_bus_size = [1] * len(self.all_ports) for port in self.all_ports: # All ports need the col addr flops - self.data_bus_size[port] = self.col_addr_size + self.data_bus_size[port] += self.col_addr_size # Write ports need the data input flops and write mask flops if port in self.write_ports: self.data_bus_size[port] += self.num_wmasks + self.word_size @@ -324,16 +325,6 @@ class sram_1bank(sram_base): self.route_row_addr_dff() - # if self.col_addr_dff: - # self.route_col_addr_dff() - - # self.route_data_dff() - - # if self.write_size: - # self.route_wmask_dff() - - # if self.num_spare_cols: - # self.route_spare_wen_dff() for port in self.all_ports: self.route_dff(port) @@ -488,137 +479,6 @@ class sram_1bank(sram_base): offset=mid_pos) self.add_path(bank_pin.layer, [mid_pos, bank_pos]) - def route_col_addr_dff(self): - """ Connect the output of the col flops to the bank pins """ - for port in self.all_ports: - if port % 2: - offset = self.col_addr_dff_insts[port].ll() - vector(0, self.col_addr_bus_size) - else: - offset = self.col_addr_dff_insts[port].ul() + vector(0, self.col_addr_bus_gap) - - bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)] - col_addr_bus_offsets = self.create_horizontal_bus(layer="m1", - offset=offset, - names=bus_names, - length=self.col_addr_dff_insts[port].width) - - dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] - data_dff_map = zip(dff_names, bus_names) - self.connect_horizontal_bus(data_dff_map, - self.col_addr_dff_insts[port], - col_addr_bus_offsets) - - bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)] - data_bank_map = zip(bank_names, bus_names) - self.connect_horizontal_bus(data_bank_map, - self.bank_inst, - col_addr_bus_offsets) - - 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 self.write_ports: - if port % 2: - offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size[port]) - else: - offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap) - - dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)] - dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] - if self.write_size or self.num_spare_cols: - for x in dff_names: - pin = self.data_dff_insts[port].get_pin(x) - pin_offset = pin.center() - self.add_via_center(layers=self.m1_stack, - offset=pin_offset, - directions=("V", "V")) - self.add_via_stack_center(from_layer="m2", - to_layer="m4", - offset=pin_offset) - - bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] - bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] - if self.write_size or self.num_spare_cols: - for x in bank_names: - pin = self.bank_inst.get_pin(x) - if port % 2: - pin_offset = pin.uc() - else: - pin_offset = pin.bc() - self.add_via_stack_center(from_layer=pin.layer, - to_layer="m4", - offset=pin_offset) - - route_map = list(zip(bank_pins, dff_pins)) - if self.write_size or self.num_spare_cols: - layer_stack = self.m3_stack - else: - layer_stack = self.m1_stack - - self.create_horizontal_channel_route(netlist=route_map, - offset=offset, - layer_stack=layer_stack) - - def route_wmask_dff(self): - """ Connect the output of the wmask flops to the write mask AND array """ - # This is where the channel will start (y-dimension at least) - for port in self.write_ports: - if port % 2: - offset = self.wmask_dff_insts[port].ll() - vector(0, self.wmask_bus_size) - else: - offset = self.wmask_dff_insts[port].ul() + vector(0, self.wmask_bus_gap) - - dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)] - dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names] - for x in dff_names: - offset_pin = self.wmask_dff_insts[port].get_pin(x).center() - self.add_via_center(layers=self.m1_stack, - offset=offset_pin, - directions=("V", "V")) - - bank_names = ["bank_wmask{0}_{1}".format(port, x) for x in range(self.num_wmasks)] - bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] - for x in bank_names: - offset_pin = self.bank_inst.get_pin(x).center() - self.add_via_center(layers=self.m1_stack, - offset=offset_pin) - - route_map = list(zip(bank_pins, dff_pins)) - self.create_horizontal_channel_route(netlist=route_map, - offset=offset, - layer_stack=self.m1_stack) - - def route_spare_wen_dff(self): - """ Connect the output of the spare write enable flops to the spare write drivers """ - # This is where the channel will start (y-dimension at least) - for port in self.write_ports: - if port % 2: - # for port 0 - offset = self.spare_wen_dff_insts[port].ll() - vector(0, self.spare_wen_bus_size) - else: - offset = self.spare_wen_dff_insts[port].ul() + vector(0, self.spare_wen_bus_gap) - - dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] - dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] - for x in dff_names: - offset_pin = self.spare_wen_dff_insts[port].get_pin(x).center() - self.add_via_center(layers=self.m1_stack, - offset=offset_pin, - directions=("V", "V")) - - bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] - bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] - for x in bank_names: - offset_pin = self.bank_inst.get_pin(x).center() - self.add_via_center(layers=self.m1_stack, - offset=offset_pin) - - route_map = list(zip(bank_pins, dff_pins)) - self.create_horizontal_channel_route(netlist=route_map, - offset=offset, - layer_stack=self.m1_stack) - - def add_lvs_correspondence_points(self): """ This adds some points for easier debugging if LVS goes wrong. From a48ea522539c8d8a7b1834bd8bfceaabe0a86450 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 30 Jun 2020 13:26:38 -0700 Subject: [PATCH 195/206] Add missing contact to vdd pins. --- compiler/modules/dff_array.py | 87 +++++++++++++++---------------- compiler/modules/dff_buf_array.py | 4 +- 2 files changed, 43 insertions(+), 48 deletions(-) diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 62464834..d3f9b68e 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -7,12 +7,11 @@ # import debug import design -from tech import drc -from math import log from vector import vector from sram_factory import factory from globals import OPTS + class dff_array(design.design): """ This is a simple row (or multiple rows) of flops. @@ -52,42 +51,41 @@ class dff_array(design.design): self.add_mod(self.dff) def add_pins(self): - for row in range(self.rows): + for row in range(self.rows): for col in range(self.columns): - self.add_pin(self.get_din_name(row,col), "INPUT") - for row in range(self.rows): + self.add_pin(self.get_din_name(row, col), "INPUT") + for row in range(self.rows): for col in range(self.columns): - self.add_pin(self.get_dout_name(row,col), "OUTPUT") + self.add_pin(self.get_dout_name(row, col), "OUTPUT") self.add_pin("clk", "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") def create_dff_array(self): self.dff_insts={} - for row in range(self.rows): + for row in range(self.rows): for col in range(self.columns): - name = "dff_r{0}_c{1}".format(row,col) - self.dff_insts[row,col]=self.add_inst(name=name, - mod=self.dff) - instance_ports = [self.get_din_name(row,col), - self.get_dout_name(row,col)] + name = "dff_r{0}_c{1}".format(row, col) + self.dff_insts[row, col]=self.add_inst(name=name, + mod=self.dff) + instance_ports = [self.get_din_name(row, col), + self.get_dout_name(row, col)] for port in self.dff.pin_names: if port != 'D' and port != 'Q': instance_ports.append(port) self.connect_inst(instance_ports) def place_dff_array(self): - for row in range(self.rows): + for row in range(self.rows): for col in range(self.columns): - name = "dff_r{0}_c{1}".format(row,col) if (row % 2 == 0): - base = vector(col*self.dff.width,row*self.dff.height) + base = vector(col * self.dff.width, row * self.dff.height) mirror = "R0" else: - base = vector(col*self.dff.width,(row+1)*self.dff.height) + base = vector(col * self.dff.width, (row + 1) * self.dff.height) mirror = "MX" - self.dff_insts[row,col].place(offset=base, - mirror=mirror) + self.dff_insts[row, col].place(offset=base, + mirror=mirror) def get_din_name(self, row, col): if self.columns == 1: @@ -95,7 +93,7 @@ class dff_array(design.design): elif self.rows == 1: din_name = "din_{0}".format(col) else: - din_name = "din_{0}_{1}".format(row,col) + din_name = "din_{0}_{1}".format(row, col) return din_name @@ -105,61 +103,58 @@ class dff_array(design.design): elif self.rows == 1: dout_name = "dout_{0}".format(col) else: - dout_name = "dout_{0}_{1}".format(row,col) + dout_name = "dout_{0}_{1}".format(row, col) return dout_name - def add_layout_pins(self): for row in range(self.rows): - for col in range(self.columns): + for col in range(self.columns): # Continous vdd rail along with label. - vdd_pin=self.dff_insts[row,col].get_pin("vdd") - self.add_power_pin("vdd", vdd_pin.center()) + vdd_pin=self.dff_insts[row, col].get_pin("vdd") + self.add_power_pin("vdd", vdd_pin.center(), start_layer=vdd_pin.layer) # Continous gnd rail along with label. - gnd_pin=self.dff_insts[row,col].get_pin("gnd") - self.add_power_pin("gnd", gnd_pin.center()) + gnd_pin=self.dff_insts[row, col].get_pin("gnd") + self.add_power_pin("gnd", gnd_pin.center(), start_layer=gnd_pin.layer) - - for row in range(self.rows): - for col in range(self.columns): - din_pin = self.dff_insts[row,col].get_pin("D") - debug.check(din_pin.layer=="m2","DFF D pin not on metal2") - self.add_layout_pin(text=self.get_din_name(row,col), + for row in range(self.rows): + for col in range(self.columns): + din_pin = self.dff_insts[row, col].get_pin("D") + debug.check(din_pin.layer == "m2", "DFF D pin not on metal2") + self.add_layout_pin(text=self.get_din_name(row, col), layer=din_pin.layer, offset=din_pin.ll(), width=din_pin.width(), height=din_pin.height()) - dout_pin = self.dff_insts[row,col].get_pin("Q") - debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2") - self.add_layout_pin(text=self.get_dout_name(row,col), + dout_pin = self.dff_insts[row, col].get_pin("Q") + debug.check(dout_pin.layer == "m2", "DFF Q pin not on metal2") + self.add_layout_pin(text=self.get_dout_name(row, col), layer=dout_pin.layer, offset=dout_pin.ll(), width=dout_pin.width(), height=dout_pin.height()) - - # Create vertical spines to a single horizontal rail - clk_pin = self.dff_insts[0,0].get_pin(self.dff.clk_pin) - clk_ypos = 2*self.m3_pitch+self.m3_width - debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2") + clk_pin = self.dff_insts[0, 0].get_pin(self.dff.clk_pin) + clk_ypos = 2 * self.m3_pitch + self.m3_width + debug.check(clk_pin.layer == "m2", "DFF clk pin not on metal2") self.add_layout_pin_segment_center(text="clk", layer="m3", - start=vector(0,clk_ypos), - end=vector(self.width,clk_ypos)) + start=vector(0, clk_ypos), + end=vector(self.width, clk_ypos)) for col in range(self.columns): - clk_pin = self.dff_insts[0,col].get_pin(self.dff.clk_pin) + clk_pin = self.dff_insts[0, col].get_pin(self.dff.clk_pin) # Make a vertical strip for each column self.add_rect(layer="m2", - offset=clk_pin.ll().scale(1,0), + offset=clk_pin.ll().scale(1, 0), width=self.m2_width, height=self.height) # Drop a via to the M3 pin - self.add_via_center(layers=self.m2_stack, - offset=vector(clk_pin.cx(),clk_ypos)) + self.add_via_stack_center(from_layer=clk_pin.layer, + to_layer="m3", + offset=vector(clk_pin.cx(), clk_ypos)) def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index b608cdeb..1cbd9284 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -167,11 +167,11 @@ class dff_buf_array(design.design): for col in range(self.columns): # Continous vdd rail along with label. vdd_pin=self.dff_insts[row, col].get_pin("vdd") - self.add_power_pin("vdd", vdd_pin.lc()) + self.add_power_pin("vdd", vdd_pin.lc(), start_layer=vdd_pin.layer) # Continous gnd rail along with label. gnd_pin=self.dff_insts[row, col].get_pin("gnd") - self.add_power_pin("gnd", gnd_pin.lc()) + self.add_power_pin("gnd", gnd_pin.lc(), start_layer=gnd_pin.layer) def add_layout_pins(self): From 011ac2fc05c89e678103b1e08bf74fa6a3934da9 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 30 Jun 2020 13:57:45 -0700 Subject: [PATCH 196/206] Don't route to clk to perimeter on m2 --- compiler/modules/control_logic.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index d857f24d..74fad6a5 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -523,12 +523,12 @@ class control_logic(design.design): def route_clk_buf(self): clk_pin = self.clk_buf_inst.get_pin("A") clk_pos = clk_pin.center() - self.add_layout_pin_segment_center(text="clk", - layer="m2", - start=clk_pos, - end=clk_pos.scale(1, 0)) - self.add_via_center(layers=self.m1_stack, - offset=clk_pos) + self.add_layout_pin_rect_center(text="clk", + layer="m2", + offset=clk_pos) + self.add_via_stack_center(from_layer=clk_pin.layer, + to_layer="m2", + offset=clk_pos) self.route_output_to_bus_jogged(self.clk_buf_inst, "clk_buf") From c1fedda575517f5fbc35ec36c0665811e2680a8f Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 30 Jun 2020 15:07:34 -0700 Subject: [PATCH 197/206] Modifications for min area metal. Made add_via_stack_center iterative instead of recursive. Removed add_via_stack (non-center) since it isn't used. Add min area metal during iterative via insertion. --- compiler/base/hierarchy_layout.py | 126 ++++++++++++++---------------- compiler/pgates/pwrite_driver.py | 24 +++--- 2 files changed, 69 insertions(+), 81 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index c57de915..a5272ba5 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -13,6 +13,7 @@ from tech import drc, GDS from tech import layer as techlayer from tech import layer_indices from tech import layer_stacks +from tech import preferred_directions import os from globals import OPTS from vector import vector @@ -521,7 +522,6 @@ class layout(): def get_preferred_direction(self, layer): """ Return the preferred routing directions """ - from tech import preferred_directions return preferred_directions[layer] def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None): @@ -567,24 +567,6 @@ class layout(): self.connect_inst([]) return inst - def add_via_stack(self, offset, from_layer, to_layer, - directions=None, - size=[1, 1], - implant_type=None, - well_type=None): - """ - Punch a stack of vias from a start layer to a target layer. - """ - return self.__add_via_stack_internal(offset=offset, - directions=directions, - from_layer=from_layer, - to_layer=to_layer, - via_func=self.add_via, - last_via=None, - size=size, - implant_type=implant_type, - well_type=well_type) - def add_via_stack_center(self, offset, from_layer, @@ -594,24 +576,7 @@ class layout(): implant_type=None, well_type=None): """ - Punch a stack of vias from a start layer to a target layer by the center - coordinate accounting for mirroring and rotation. - """ - return self.__add_via_stack_internal(offset=offset, - directions=directions, - from_layer=from_layer, - to_layer=to_layer, - via_func=self.add_via_center, - last_via=None, - size=size, - implant_type=implant_type, - well_type=well_type) - - def __add_via_stack_internal(self, offset, directions, from_layer, to_layer, - via_func, last_via, size, implant_type=None, well_type=None): - """ - Punch a stack of vias from a start layer to a target layer. Here we - figure out whether to punch it up or down the stack. + Punch a stack of vias from a start layer to a target layer by the center. """ if from_layer == to_layer: @@ -619,38 +584,65 @@ class layout(): # a metal enclosure. This helps with center-line path routing. self.add_rect_center(layer=from_layer, offset=offset) - return last_via + return None - from_id = layer_indices[from_layer] - to_id = layer_indices[to_layer] + via = None + cur_layer = from_layer + while cur_layer != to_layer: + from_id = layer_indices[cur_layer] + to_id = layer_indices[to_layer] - if from_id < to_id: # grow the stack up - search_id = 0 - next_id = 2 - else: # grow the stack down - search_id = 2 - next_id = 0 + if from_id < to_id: # grow the stack up + search_id = 0 + next_id = 2 + else: # grow the stack down + search_id = 2 + next_id = 0 - curr_stack = next(filter(lambda stack: stack[search_id] == from_layer, layer_stacks), None) - if curr_stack is None: - raise ValueError("Cannot create via from '{0}' to '{1}'." - "Layer '{0}' not defined".format(from_layer, to_layer)) + curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None) + + via = self.add_via_center(layers=curr_stack, + size=size, + offset=offset, + directions=directions, + implant_type=implant_type, + well_type=well_type) + + if cur_layer != from_layer: + self.add_min_area_rect_center(cur_layer, + offset, + via.mod.first_layer_width, + via.mod.first_layer_height) + + cur_layer = curr_stack[next_id] - via = via_func(layers=curr_stack, - size=size, - offset=offset, - directions=directions, - implant_type=implant_type, - well_type=well_type) - - via = self.__add_via_stack_internal(offset=offset, - directions=directions, - from_layer=curr_stack[next_id], - to_layer=to_layer, - via_func=via_func, - last_via=via, - size=size) return via + + def add_min_area_rect_center(self, + layer, + offset, + width=None, + height=None): + """ + Add a minimum area retcangle at the given point. + Either width or height should be fixed. + """ + + min_area = drc("minarea_{}".format(layer)) + if min_area == 0: + return + + min_width = drc("minwidth_{}".format(layer)) + + if preferred_directions[layer] == "V": + height = max(min_area / width, min_width) + else: + width = max(min_area / height, min_width) + + self.add_rect_center(layer=layer, + offset=offset, + width=width, + height=height) def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): """Adds a ptx module to the design.""" @@ -1181,12 +1173,8 @@ class layout(): self.add_path(layer, [pin_loc, peri_pin_loc]) - self.add_via_stack_center(from_layer=layer, - to_layer="m4", - offset=peri_pin_loc) - self.add_layout_pin_rect_center(text=name, - layer="m4", + layer=layer, offset=peri_pin_loc) def add_power_ring(self, bbox): diff --git a/compiler/pgates/pwrite_driver.py b/compiler/pgates/pwrite_driver.py index da5eacdc..87db7b20 100644 --- a/compiler/pgates/pwrite_driver.py +++ b/compiler/pgates/pwrite_driver.py @@ -160,11 +160,11 @@ class pwrite_driver(design.design): track_xoff = self.get_m2_track(1) din_loc = self.din_inst.get_pin("A").center() - self.add_via_stack("m1", "m2", din_loc) + self.add_via_stack_center("m1", "m2", din_loc) din_track = vector(track_xoff,din_loc.y) br_in = self.br_inst.get_pin("in").center() - self.add_via_stack("m1", "m2", br_in) + self.add_via_stack_center("m1", "m2", br_in) br_track = vector(track_xoff,br_in.y) din_in = vector(track_xoff,0) @@ -181,11 +181,11 @@ class pwrite_driver(design.design): track_xoff = self.get_m4_track(self.din_bar_track) din_bar_in = self.din_inst.get_pin("Z").center() - self.add_via_stack("m1", "m3", din_bar_in) + self.add_via_stack_center("m1", "m3", din_bar_in) din_bar_track = vector(track_xoff,din_bar_in.y) bl_in = self.bl_inst.get_pin("in").center() - self.add_via_stack("m1", "m3", bl_in) + self.add_via_stack_center("m1", "m3", bl_in) bl_track = vector(track_xoff,bl_in.y) din_in = vector(track_xoff,0) @@ -204,15 +204,15 @@ class pwrite_driver(design.design): # This M2 pitch is a hack since the A and Z pins align horizontally en_bar_loc = self.en_inst.get_pin("Z").uc() en_bar_track = vector(track_xoff, en_bar_loc.y) - self.add_via_stack("m1", "m3", en_bar_loc) + self.add_via_stack_center("m1", "m3", en_bar_loc) # This is a U route to the right down then left bl_en_loc = self.bl_inst.get_pin("en_bar").center() bl_en_track = vector(track_xoff, bl_en_loc.y) - self.add_via_stack("m1", "m3", bl_en_loc) + self.add_via_stack_center("m1", "m3", bl_en_loc) br_en_loc = self.br_inst.get_pin("en_bar").center() br_en_track = vector(track_xoff, bl_en_loc.y) - self.add_via_stack("m1", "m3", br_en_loc) + self.add_via_stack_center("m1", "m3", br_en_loc) # L shape @@ -237,21 +237,21 @@ class pwrite_driver(design.design): en_loc = self.en_inst.get_pin("A").center() en_rail = vector(en_loc.x, vdd_yloc) - self.add_via_stack("m1", "m2", en_loc) + self.add_via_stack_center("m1", "m2", en_loc) self.add_path("m2", [en_loc, en_rail]) - self.add_via_stack("m2", "m3", en_rail) + self.add_via_stack_center("m2", "m3", en_rail) # Start point in the track on the pin rail en_track = vector(track_xoff, vdd_yloc) - self.add_via_stack("m3", "m4", en_track) + self.add_via_stack_center("m3", "m4", en_track) # This is a U route to the right down then left bl_en_loc = self.bl_inst.get_pin("en").center() bl_en_track = vector(track_xoff, bl_en_loc.y) - self.add_via_stack("m1", "m3", bl_en_loc) + self.add_via_stack_center("m1", "m3", bl_en_loc) br_en_loc = self.br_inst.get_pin("en").center() br_en_track = vector(track_xoff, bl_en_loc.y) - self.add_via_stack("m1", "m3", br_en_loc) + self.add_via_stack_center("m1", "m3", br_en_loc) # U shape self.add_wire(self.m3_stack, From 0a87691176ddd61679535dac5d7a7c324192f86c Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 30 Jun 2020 15:27:10 -0700 Subject: [PATCH 198/206] Run Calibre LVS even if DRC fails. --- compiler/tests/testutils.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index eb75fc44..4a58cfe2 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -48,12 +48,11 @@ class openram_test(unittest.TestCase): # if we ignore things like minimum metal area of pins drc_result=verify.run_drc(a.name, tempgds, extract=True, final_verification=final_verification) - # Always run LVS if we are using magic - if "magic" in OPTS.drc_exe or drc_result == 0: - lvs_result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification) + # We can still run LVS even if DRC fails in Magic OR Calibre + lvs_result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification) # Only allow DRC to fail and LVS to pass if we are using magic - if "magic" in OPTS.drc_exe and lvs_result == 0 and drc_result != 0: + if lvs_result == 0 and drc_result != 0: # import shutil # zip_file = "/tmp/{0}_{1}".format(a.name, os.getpid()) # debug.info(0, "Archiving failed files to {}.zip".format(zip_file)) From 3379f46da1d336a379697aa9cd8c0571eda3c3a1 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 30 Jun 2020 16:22:44 -0700 Subject: [PATCH 199/206] Fail unit test, but mention if LVS passes and DRC fails. --- compiler/tests/testutils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 4a58cfe2..a8da9fb4 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -57,8 +57,7 @@ class openram_test(unittest.TestCase): # 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) - debug.warning("DRC failed but LVS passed: {}".format(a.name)) - # self.fail("DRC failed but LVS passed: {}".format(a.name)) + self.fail("DRC failed but LVS passed: {}".format(a.name)) elif drc_result != 0: # import shutil # zip_file = "/tmp/{0}_{1}".format(a.name, os.getpid()) From b07f30cb9edaed782cd746e906a81ee0d08330db Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 30 Jun 2020 16:23:07 -0700 Subject: [PATCH 200/206] Missing output via in control logic --- compiler/modules/control_logic.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 74fad6a5..78215d49 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -797,10 +797,14 @@ class control_logic(design.design): out_pin = inst.get_pin(pin_name) right_pos = out_pin.center() + vector(self.width - out_pin.cx(), 0) - self.add_layout_pin_segment_center(text=out_name, - layer="m1", - start=out_pin.center(), - end=right_pos) + + self.add_path(out_pin.layer, [out_pin.center(), right_pos]) + self.add_via_stack_center(from_layer=out_pin.layer, + to_layer="m1", + offset=right_pos) + self.add_layout_pin_rect_center(text=out_name, + layer="m1", + offset=right_pos) def route_supply(self): """ Add vdd and gnd to the instance cells """ From 3d0f29ff3a079c33b556a5219e2c9fdb1b815bcd Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Jul 2020 09:22:59 -0700 Subject: [PATCH 201/206] Fix missing via LVS issues. LVS passing for some 20 tests. --- compiler/base/hierarchy_layout.py | 13 ++++++----- compiler/modules/control_logic.py | 37 ++++++++++++++++++++----------- compiler/modules/delay_chain.py | 29 ++++++++++++------------ compiler/sram/sram_1bank.py | 7 +++++- 4 files changed, 53 insertions(+), 33 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index a5272ba5..275b69e9 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -917,8 +917,10 @@ class layout(): (horizontal_layer, via_layer, vertical_layer) = layer_stack if horizontal: route_layer = vertical_layer + bys_layer = horizontal_layer else: route_layer = horizontal_layer + bus_layer = vertical_layer for (pin_name, bus_name) in mapping: pin = inst.get_pin(pin_name) @@ -940,17 +942,18 @@ class layout(): # Connect to the pin on the instances with a via if it is # not on the right layer if pin.layer != route_layer: - self.add_via_center(layers=layer_stack, - offset=pin_pos) + self.add_via_stack_center(from_layer=pin.layer, + to_layer=route_layer, + offset=pin_pos) # FIXME: output pins tend to not be rotate, # but supply pins are. Make consistent? # We only need a via if they happened to align perfectly # so the add_wire didn't add a via if (horizontal and bus_pos.y == pin_pos.y) or (not horizontal and bus_pos.x == pin_pos.x): - self.add_via_center(layers=layer_stack, - offset=bus_pos, - rotate=90) + self.add_via_stack_center(from_layer=route_layer, + to_layer=bus_layer, + offset=bus_pos) def connect_vbus(self, src_pin, dest_pin, hlayer="m3", vlayer="m2"): """ diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 78215d49..020b90ec 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -555,9 +555,15 @@ class control_logic(design.design): clkbuf_map = zip(["A"], ["clk_buf"]) self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.input_bus) - out_pos = self.clk_bar_inst.get_pin("Z").center() - in_pos = self.gated_clk_bar_inst.get_pin("A").center() - self.add_zjog("m1", out_pos, in_pos) + out_pin = self.clk_bar_inst.get_pin("Z") + out_pos = out_pin.center() + in_pin = self.gated_clk_bar_inst.get_pin("A") + in_pos = in_pin.center() + self.add_zjog(out_pin.layer, out_pos, in_pos) + self.add_via_stack_center(from_layer=out_pin.layer, + to_layer=in_pin.layer, + offset=in_pos) + # This is the second gate over, so it needs to be on M3 clkbuf_map = zip(["B"], ["cs"]) @@ -809,23 +815,27 @@ class control_logic(design.design): def route_supply(self): """ Add vdd and gnd to the instance cells """ + if OPTS.tech_name == "sky130": + supply_layer = "li" + else: + supply_layer = "m1" max_row_x_loc = max([inst.rx() for inst in self.row_end_inst]) for inst in self.row_end_inst: pins = inst.get_pins("vdd") for pin in pins: - if pin.layer == "m1": + if pin.layer == supply_layer: row_loc = pin.rc() pin_loc = vector(max_row_x_loc, pin.rc().y) - self.add_power_pin("vdd", pin_loc) - self.add_path("m1", [row_loc, pin_loc]) + self.add_power_pin("vdd", pin_loc, start_layer=pin.layer) + self.add_path(supply_layer, [row_loc, pin_loc]) pins = inst.get_pins("gnd") for pin in pins: - if pin.layer == "m1": + if pin.layer == supply_layer: row_loc = pin.rc() pin_loc = vector(max_row_x_loc, pin.rc().y) - self.add_power_pin("gnd", pin_loc) - self.add_path("m1", [row_loc, pin_loc]) + self.add_power_pin("gnd", pin_loc, start_layer=pin.layer) + self.add_path(supply_layer, [row_loc, pin_loc]) self.copy_layout_pin(self.delay_inst, "gnd") self.copy_layout_pin(self.delay_inst, "vdd") @@ -1008,12 +1018,13 @@ class control_logic(design.design): def route_output_to_bus_jogged(self, inst, name): # Connect this at the bottom of the buffer - out_pos = inst.get_pin("Z").center() + out_pin = inst.get_pin("Z") + out_pos = out_pin.center() mid1 = vector(out_pos.x, out_pos.y - 0.4 * inst.mod.height) mid2 = vector(self.input_bus[name].cx(), mid1.y) bus_pos = self.input_bus[name].center() self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos]) - # The pin is on M1, so we need another via as well - self.add_via_center(layers=self.m1_stack, - offset=out_pos) + self.add_via_stack_center(from_layer=out_pin.layer, + to_layer="m2", + offset=out_pos) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index c07395f5..c261138a 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -140,21 +140,20 @@ class delay_chain(design.design): for load in self.load_inst_map[inv]: # Drop a via on each A pin a_pin = load.get_pin("A") - self.add_via_center(layers=self.m1_stack, - offset=a_pin.center()) - self.add_via_center(layers=self.m2_stack, - offset=a_pin.center()) + self.add_via_stack_center(from_layer=a_pin.layer, + to_layer="m3", + offset=a_pin.center()) # Route an M3 horizontal wire to the furthest z_pin = inv.get_pin("Z") a_pin = inv.get_pin("A") a_max = self.load_inst_map[inv][-1].get_pin("A") - self.add_via_center(layers=self.m1_stack, - offset=a_pin.center()) - self.add_via_center(layers=self.m1_stack, - offset=z_pin.center()) - self.add_via_center(layers=self.m2_stack, - offset=z_pin.center()) + self.add_via_stack_center(from_layer=a_pin.layer, + to_layer="m2", + offset=a_pin.center()) + self.add_via_stack_center(from_layer=z_pin.layer, + to_layer="m3", + offset=z_pin.center()) self.add_path("m3", [z_pin.center(), a_max.center()]) # Route Z to the A of the next stage @@ -191,8 +190,9 @@ class delay_chain(design.design): # input is A pin of first inverter a_pin = self.driver_inst_list[0].get_pin("A") - self.add_via_center(layers=self.m1_stack, - offset=a_pin.center()) + self.add_via_stack_center(from_layer=a_pin.layer, + to_layer="m2", + offset=a_pin.center()) self.add_layout_pin(text="in", layer="m2", offset=a_pin.ll().scale(1, 0), @@ -201,8 +201,9 @@ class delay_chain(design.design): # output is A pin of last load inverter last_driver_inst = self.driver_inst_list[-1] a_pin = self.load_inst_map[last_driver_inst][-1].get_pin("A") - self.add_via_center(layers=self.m1_stack, - offset=a_pin.center()) + self.add_via_stack_center(from_layer=a_pin.layer, + to_layer="m2", + offset=a_pin.center()) mid_point = vector(a_pin.cx() + 3 * self.m2_width, a_pin.cy()) self.add_path("m2", [a_pin.center(), mid_point, mid_point.scale(1, 0)]) self.add_layout_pin_segment_center(text="out", diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index cdc5ab92..52cbb2a1 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -457,7 +457,12 @@ class sram_1bank(sram_base): dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port)) self.add_wire(self.m2_stack[::-1], [src_pin.center(), vector(src_pin.cx(), dest_pin.cy()), dest_pin.rc()]) - # self.connect_hbus(src_pin, dest_pin) + self.add_via_stack_center(from_layer=src_pin.layer, + to_layer="m2", + offset=src_pin.center()) + self.add_via_stack_center(from_layer=dest_pin.layer, + to_layer="m2", + offset=dest_pin.center()) def route_row_addr_dff(self): """ Connect the output of the row flops to the bank pins """ From bb18d05f7535c7239ce860e883d9ee9f47d58eee Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Jul 2020 11:33:25 -0700 Subject: [PATCH 202/206] Move control output via inside module instead of perimeter --- compiler/modules/control_logic.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 020b90ec..a4e26a6f 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -802,15 +802,16 @@ class control_logic(design.design): """ Create an output pin on the right side from the pin of a given instance. """ out_pin = inst.get_pin(pin_name) - right_pos = out_pin.center() + vector(self.width - out_pin.cx(), 0) + out_pos = out_pin.center() + right_pos = out_pos + vector(self.width - out_pin.cx(), 0) - self.add_path(out_pin.layer, [out_pin.center(), right_pos]) self.add_via_stack_center(from_layer=out_pin.layer, - to_layer="m1", - offset=right_pos) - self.add_layout_pin_rect_center(text=out_name, - layer="m1", - offset=right_pos) + to_layer="m2", + offset=out_pos) + self.add_layout_pin_segment_center(text=out_name, + layer="m2", + start=out_pos, + end=right_pos) def route_supply(self): """ Add vdd and gnd to the instance cells """ From c340870ba0ff6c6e86da99ac1e667c27a69ae481 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Jul 2020 14:44:01 -0700 Subject: [PATCH 203/206] Channel route dout wires as well in read write ports --- compiler/base/channel_route.py | 2 +- compiler/base/hierarchy_layout.py | 6 +-- compiler/sram/sram_1bank.py | 64 ++++++++++++++++++++++--------- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index 5e9b9531..cd4bbdc1 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -161,7 +161,7 @@ class channel_route(design.design): else: # FIXME: We don't support cyclic VCGs right now. debug.error("Cyclic VCG in channel router.", -1) - + # These are the pins we'll have to connect pin_list = nets[net_name] # print("Routing:", net_name, [x.name for x in pin_list]) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 275b69e9..8995488a 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1176,9 +1176,9 @@ class layout(): self.add_path(layer, [pin_loc, peri_pin_loc]) - self.add_layout_pin_rect_center(text=name, - layer=layer, - offset=peri_pin_loc) + return self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=peri_pin_loc) def add_power_ring(self, bbox): """ diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 52cbb2a1..d2c58e49 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -81,6 +81,10 @@ class sram_1bank(sram_base): # Write ports need the data input flops and write mask flops if port in self.write_ports: self.data_bus_size[port] += self.num_wmasks + self.word_size + # This is for the din pins that get routed in the same channel + # when we have dout and din together + if port in self.readwrite_ports: + self.data_bus_size[port] += self.word_size # Convert to length self.data_bus_size[port] *= self.m4_nonpref_pitch # Add the gap in unit length @@ -237,18 +241,45 @@ class sram_1bank(sram_base): "clk", "clk{}".format(port)) - # Data output pins go to BOTTOM/TOP - if port in self.read_ports: + # Data input pins go to BOTTOM/TOP + din_ports = [] + if port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): if OPTS.perimeter_pins: + p = self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit), + pin=self.data_dff_insts[port].get_pin("din_{0}".format(bit)), + side=bottom_or_top, + bbox=bbox) + din_ports.append(p) + else: + self.copy_layout_pin(self.bank_inst, + "din{0}_{1}".format(port, bit), + "din{0}[{1}]".format(port, bit)) + + # Data output pins go to BOTTOM/TOP + if port in self.readwrite_ports and OPTS.perimeter_pins: + for bit in range(self.word_size + self.num_spare_cols): + # This should be routed next to the din pin + p = din_ports[bit] + self.add_layout_pin_rect_center(text="dout{0}[{1}]".format(port, bit), + layer=p.layer, + offset=p.center() + vector(self.m3_pitch, 0), + width=p.width(), + height=p.height()) + elif port in self.read_ports: + for bit in range(self.word_size + self.num_spare_cols): + if OPTS.perimeter_pins: + # This should have a clear route to the perimeter if there are no din routes self.add_perimeter_pin(name="dout{0}[{1}]".format(port, bit), pin=self.bank_inst.get_pin("dout{0}_{1}".format(port, bit)), side=bottom_or_top, bbox=bbox) else: - self.copy_layout_pin(self.bank_inst, - "dout{0}_{1}".format(port, bit), + self.copy_layout_pin(self.data_dff_insts[port], + "dout_{}".format(bit), "dout{0}[{1}]".format(port, bit)) + + # Lower address bits go to BOTTOM/TOP for bit in range(self.col_addr_size): @@ -274,19 +305,6 @@ class sram_1bank(sram_base): "din_{}".format(bit), "addr{0}[{1}]".format(port, bit + self.col_addr_size)) - # Data input pins go to BOTTOM/TOP - if port in self.write_ports: - for bit in range(self.word_size + self.num_spare_cols): - if OPTS.perimeter_pins: - self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit), - pin=self.data_dff_insts[port].get_pin("din_{}".format(bit)), - side=bottom_or_top, - bbox=bbox) - else: - self.copy_layout_pin(self.data_dff_insts[port], - "din_{}".format(bit), - "din{0}[{1}]".format(port, bit)) - # Write mask pins go to BOTTOM/TOP if port in self.write_ports: if self.write_size: @@ -349,12 +367,22 @@ class sram_1bank(sram_base): route_map.extend(list(zip(bank_pins, dff_pins))) if port in self.write_ports: - # data dff + # synchronized inputs from data dff dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)] dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, dff_pins))) + + if port in self.readwrite_ports and OPTS.perimeter_pins: + # outputs from sense amp + # These are the output pins which had their pin placed on the perimeter, so route from the + # sense amp which should not align with write driver input + sram_names = ["dout{0}[{1}]".format(port, x) for x in range(self.word_size + self.num_spare_cols)] + sram_pins = [self.get_pin(x) for x in sram_names] + bank_names = ["dout{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] + bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] + route_map.extend(list(zip(bank_pins, sram_pins))) if self.num_wmasks > 0 and port in self.write_ports: layer_stack = self.m3_stack From 8cd1cba8184106944d7cc6d97131afa2dac1ce96 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Jul 2020 14:44:18 -0700 Subject: [PATCH 204/206] Fix missing via in wmask driver --- compiler/modules/port_data.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index e370b891..bd6b39e2 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -377,11 +377,11 @@ class port_data(design.design): temp.append("{0}_{1}".format(br_name, bit)) else: temp.append("{0}_out_{1}".format(bl_name, bit)) - temp.append("{0}_out_{1}".format(br_name, bit)) + temp.append("{0}_out_{1}".format(br_name, bit)) - for bit in range(self.num_spare_cols): + for bit in range(self.num_spare_cols): temp.append("spare{0}_{1}".format(bl_name, bit)) - temp.append("spare{0}_{1}".format(br_name, bit)) + temp.append("spare{0}_{1}".format(br_name, bit)) if self.write_size is not None: for i in range(self.num_wmasks): @@ -522,13 +522,14 @@ class port_data(design.design): wdriver_pos = wdriver_en_pin.rc() - vector(self.m2_pitch, 0) mid_pos = vector(wdriver_pos.x, wmask_pos.y) - # Add driver on mask output - self.add_via_center(layers=self.m1_stack, - offset=wmask_pos) + self.add_via_stack_center(from_layer=wmask_out_pin.layer, + to_layer="m1", + offset=wmask_pos) # Add via for the write driver array's enable input - self.add_via_center(layers=self.m1_stack, - offset=wdriver_pos) + self.add_via_stack_center(from_layer=wdriver_en_pin.layer, + to_layer="m2", + offset=wdriver_pos) # Route between write mask AND array and write driver array self.add_wire(self.m1_stack, [wmask_pos, mid_pos, wdriver_pos]) From bed2e36550d15f34a6ec9a8815a50e79c21d0f14 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Jul 2020 14:44:48 -0700 Subject: [PATCH 205/206] Simplify write mask supply via logic --- compiler/modules/write_mask_and_array.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index c87d3a90..d48aefef 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -134,10 +134,7 @@ class write_mask_and_array(design.design): for supply in ["gnd", "vdd"]: supply_pin=self.and2_insts[i].get_pin(supply) - if "li" in layer: - self.add_power_pin(supply, supply_pin.center(), start_layer="li", directions=("H", "H")) - else: - self.add_power_pin(supply, supply_pin.center()) + self.add_power_pin(supply, supply_pin.center(), start_layer=supply_pin.layer) for supply in ["gnd", "vdd"]: supply_pin_left = self.and2_insts[0].get_pin(supply) From d48f48324855c441f83583fd760c98d9e09f3287 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Jul 2020 15:10:20 -0700 Subject: [PATCH 206/206] Fix swapped instance bug in perimeter pins. --- compiler/sram/sram_1bank.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index d2c58e49..a9731ed5 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -252,8 +252,8 @@ class sram_1bank(sram_base): bbox=bbox) din_ports.append(p) else: - self.copy_layout_pin(self.bank_inst, - "din{0}_{1}".format(port, bit), + self.copy_layout_pin(self.data_dff_insts[port], + "din_{}".format(bit), "din{0}[{1}]".format(port, bit)) # Data output pins go to BOTTOM/TOP @@ -275,8 +275,8 @@ class sram_1bank(sram_base): side=bottom_or_top, bbox=bbox) else: - self.copy_layout_pin(self.data_dff_insts[port], - "dout_{}".format(bit), + self.copy_layout_pin(self.bank_inst, + "dout{0}_{1}".format(port, bit), "dout{0}[{1}]".format(port, bit))