From 3221d3e74411c4eb16c2260d5cf6b0dc2e2ad90c Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 14 Nov 2018 17:05:23 -0800 Subject: [PATCH] Add initial support and unit tests for 2 port SRAM --- compiler/modules/bank.py | 3 +- compiler/sram_1bank.py | 114 +++++++++++------- compiler/sram_base.py | 15 ++- .../tests/20_sram_1bank_2mux_1rw_1r_test.py | 43 +++++++ .../tests/20_sram_1bank_nomux_1rw_1r_test.py | 43 +++++++ 5 files changed, 171 insertions(+), 47 deletions(-) create mode 100755 compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py create mode 100755 compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index f2226797..54bb57c4 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -61,7 +61,8 @@ class bank(design.design): #self.add_lvs_correspondence_points() # Remember the bank center for further placement - self.bank_center=self.offset_all_coordinates().scale(-1,-1) + self.bank_array_ll = self.offset_all_coordinates().scale(-1,-1) + self.bank_array_ur = self.bitcell_array_inst.ur() self.DRC_LVS() diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index cfbf1a9e..92c999dc 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -43,10 +43,10 @@ class sram_1bank(sram_base): self.data_dff_insts = self.create_data_dff() - def place_modules(self): + def place_instances(self): """ - This places the modules for a single bank SRAM with control - logic. + This places the instances for a single bank SRAM with control + logic and up to 2 ports. """ # No orientation or offset @@ -57,39 +57,70 @@ class sram_1bank(sram_base): # the sense amps/column mux and cell array) # The x-coordinate is placed to allow a single clock wire (plus an extra pitch) # up to the row address DFFs. - for port in self.all_ports: - control_pos = vector(-self.control_logic_rw.width - 2*self.m2_pitch, - self.bank.bank_center.y - self.control_logic_rw.control_logic_center.y) - self.control_logic_insts[port].place(control_pos) - - # The row address bits are placed above the control logic aligned on the right. - row_addr_pos = vector(self.control_logic_insts[0].rx() - self.row_addr_dff.width, - self.control_logic_insts[0].uy()) - self.row_addr_dff_insts[port].place(row_addr_pos) - - # This is M2 pitch even though it is on M1 to help stem via spacings on the trunk - data_gap = -self.m2_pitch*(self.word_size+1) - - # Add the column address below the bank under the control - # The column address flops are aligned with the data flops - if self.col_addr_dff: - col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width, - data_gap - self.col_addr_dff.height) - self.col_addr_dff_insts[port].place(col_addr_pos) - - # Add the data flops below the bank to the right of the center of bank: - # This relies on the center point 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 = vector(self.bank.bank_center.x, - data_gap - self.data_dff.height) - self.data_dff_insts[port].place(data_pos) - - # two supply rails are already included in the bank, so just 2 here. - # self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch - # self.height = self.bank.height + control_pos = [None]*len(self.all_ports) + row_addr_pos = [None]*len(self.all_ports) + col_addr_pos = [None]*len(self.all_ports) + data_pos = [None]*len(self.all_ports) + # This is M2 pitch even though it is on M1 to help stem via spacings on the trunk + data_gap = self.m2_pitch*(self.word_size+1) + + # Port 0 + port = 0 + control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch, + self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y) + self.control_logic_insts[port].place(control_pos[port]) + + # The row address bits are placed above the control logic aligned on the right. + row_addr_pos[port] = vector(self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width, + self.control_logic_insts[port].uy()) + self.row_addr_dff_insts[port].place(row_addr_pos[port]) + + # Add the col address flops below the bank to the left of the lower-left of bank array + if self.col_addr_dff: + col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.central_bus_width, + -data_gap - self.col_addr_dff_insts[port].height) + self.col_addr_dff_insts[port].place(col_addr_pos[port]) + + # 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, + -data_gap - self.data_dff_insts[port].height) + self.data_dff_insts[port].place(data_pos[port]) + + + # Port 1 + port = 1 + control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch, + self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y) + self.control_logic_insts[port].place(control_pos[port], mirror="MY") + + # The row address bits are placed above the control logic aligned on the left. + row_addr_pos[port] = vector(self.bank_inst.rx() + self.row_addr_dff_insts[port].width, + self.control_logic_insts[port].uy()) + self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="MY") + + # Add the col address flops above the bank to the right of the upper-right of bank array + if self.col_addr_dff: + col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.central_bus_width, + self.bank_inst.uy() + data_gap + self.col_addr_dff_insts[port].height) + self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") + + # 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. + if port in self.write_ports: + data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, + self.bank.uy() + data_gap + self.data_dff_insts[port].height) + self.data_dff_insts[port].place(data_pos[port], mirror="MX") + + def add_layout_pins(self): """ Add the top-level pins for a single bank SRAM with control. @@ -114,7 +145,7 @@ class sram_1bank(sram_base): for bit in range(self.word_size): self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit)) - def route(self): + def route_layout(self): """ Route a single bank SRAM """ self.add_layout_pins() @@ -151,11 +182,12 @@ class sram_1bank(sram_base): dff_clk_pos = dff_clk_pin.center() mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y) self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos]) - - data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") - data_dff_clk_pos = data_dff_clk_pin.center() - mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y) - self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos]) + + if 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(bank_clk_buf_pos.x, data_dff_clk_pos.y) + self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos]) # This uses a metal2 track to the right of the control/row addr DFF # to route vertically. diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 74220e63..879c4a04 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -75,8 +75,9 @@ class sram_base(design): def create_layout(self): """ Layout creation """ - self.place_modules() - self.route() + self.place_instances() + + self.route_layout() self.add_lvs_correspondence_points() @@ -369,9 +370,13 @@ class sram_base(design): def create_data_dff(self): """ Add and place all data flops """ insts = [] - for port in self.write_ports: - insts.append(self.add_inst(name="data_dff{}".format(port), - mod=self.data_dff)) + for port in self.all_ports: + if port in self.write_ports: + insts.append(self.add_inst(name="data_dff{}".format(port), + mod=self.data_dff)) + else: + insts.append(None) + continue # inputs, outputs/output/bar inputs = [] diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py new file mode 100755 index 00000000..49fd47be --- /dev/null +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank, 2 port SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class sram_1bank_2mux_1rw_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_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_banks=1) + + c.words_per_row=2 + debug.info(1, "Single bank, two way column mux 1rw, 1r with control logic") + a = sram(c, "sram") + 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() diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py new file mode 100755 index 00000000..673dcbca --- /dev/null +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +""" +Run a regression test on a 1 bank, 2 port SRAM +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class sram_1bank_nomux_1rw_1r_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + from sram import sram + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.replica_bitcell = "replica_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_banks=1) + + c.words_per_row=1 + debug.info(1, "Single bank, no column mux 1rw, 1r with control logic") + a = sram(c, "sram") + 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()