mirror of https://github.com/VLSIDA/OpenRAM.git
Add initial support and unit tests for 2 port SRAM
This commit is contained in:
parent
6ac5adaeca
commit
3221d3e744
|
|
@ -61,7 +61,8 @@ class bank(design.design):
|
||||||
#self.add_lvs_correspondence_points()
|
#self.add_lvs_correspondence_points()
|
||||||
|
|
||||||
# Remember the bank center for further placement
|
# 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()
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,10 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
self.data_dff_insts = self.create_data_dff()
|
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
|
This places the instances for a single bank SRAM with control
|
||||||
logic.
|
logic and up to 2 ports.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# No orientation or offset
|
# No orientation or offset
|
||||||
|
|
@ -57,39 +57,70 @@ class sram_1bank(sram_base):
|
||||||
# the sense amps/column mux and cell array)
|
# the sense amps/column mux and cell array)
|
||||||
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
|
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
|
||||||
# up to the row address DFFs.
|
# up to the row address DFFs.
|
||||||
for port in self.all_ports:
|
control_pos = [None]*len(self.all_ports)
|
||||||
control_pos = vector(-self.control_logic_rw.width - 2*self.m2_pitch,
|
row_addr_pos = [None]*len(self.all_ports)
|
||||||
self.bank.bank_center.y - self.control_logic_rw.control_logic_center.y)
|
col_addr_pos = [None]*len(self.all_ports)
|
||||||
self.control_logic_insts[port].place(control_pos)
|
data_pos = [None]*len(self.all_ports)
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
# 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):
|
def add_layout_pins(self):
|
||||||
"""
|
"""
|
||||||
Add the top-level pins for a single bank SRAM with control.
|
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):
|
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))
|
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 """
|
""" Route a single bank SRAM """
|
||||||
|
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
@ -151,11 +182,12 @@ class sram_1bank(sram_base):
|
||||||
dff_clk_pos = dff_clk_pin.center()
|
dff_clk_pos = dff_clk_pin.center()
|
||||||
mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y)
|
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])
|
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")
|
if port in self.write_ports:
|
||||||
data_dff_clk_pos = data_dff_clk_pin.center()
|
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
|
||||||
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
|
data_dff_clk_pos = data_dff_clk_pin.center()
|
||||||
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
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
|
# This uses a metal2 track to the right of the control/row addr DFF
|
||||||
# to route vertically.
|
# to route vertically.
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,9 @@ class sram_base(design):
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
""" Layout creation """
|
""" Layout creation """
|
||||||
self.place_modules()
|
self.place_instances()
|
||||||
self.route()
|
|
||||||
|
self.route_layout()
|
||||||
|
|
||||||
self.add_lvs_correspondence_points()
|
self.add_lvs_correspondence_points()
|
||||||
|
|
||||||
|
|
@ -369,9 +370,13 @@ class sram_base(design):
|
||||||
def create_data_dff(self):
|
def create_data_dff(self):
|
||||||
""" Add and place all data flops """
|
""" Add and place all data flops """
|
||||||
insts = []
|
insts = []
|
||||||
for port in self.write_ports:
|
for port in self.all_ports:
|
||||||
insts.append(self.add_inst(name="data_dff{}".format(port),
|
if port in self.write_ports:
|
||||||
mod=self.data_dff))
|
insts.append(self.add_inst(name="data_dff{}".format(port),
|
||||||
|
mod=self.data_dff))
|
||||||
|
else:
|
||||||
|
insts.append(None)
|
||||||
|
continue
|
||||||
|
|
||||||
# inputs, outputs/output/bar
|
# inputs, outputs/output/bar
|
||||||
inputs = []
|
inputs = []
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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()
|
||||||
Loading…
Reference in New Issue