Merge branch 'dev' into supply_routing

This commit is contained in:
Matt Guthaus
2018-11-06 07:56:29 -08:00
37 changed files with 487 additions and 120 deletions
@@ -0,0 +1,23 @@
import design
import debug
import utils
from tech import GDS,layer
class replica_bitcell_1rw_1r(design.design):
"""
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 = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"], layer["boundary"])
def __init__(self):
design.design.__init__(self, "replica_cell_1rw_1r")
debug.info(2, "Create replica bitcell 1rw+1r object")
self.width = replica_bitcell_1rw_1r.width
self.height = replica_bitcell_1rw_1r.height
self.pin_map = replica_bitcell_1rw_1r.pin_map
+13 -1
View File
@@ -20,6 +20,10 @@ class functional(simulation):
def __init__(self, sram, spfile, corner):
simulation.__init__(self, sram, spfile, corner)
# Seed the characterizer with a constant seed for unit tests
if OPTS.is_unit_test:
random.seed(12345)
self.set_corner(corner)
self.set_spice_constants()
@@ -31,7 +35,15 @@ class functional(simulation):
self.stored_words = {}
self.write_check = []
self.read_check = []
def set_spice_constants(self):
"""Spice constants for functional test"""
simulation.set_spice_constants(self)
#Heuristic increase for functional period. Base feasible period typically does not pass the functional test
#for column mux of this size. Increase the feasible period by 20% for this case.
if self.sram.words_per_row >= 4:
self.period = self.period*1.2
def run(self):
# Generate a random sequence of reads and writes
self.write_random_memory_sequence()
+2 -3
View File
@@ -3,7 +3,6 @@ from design import design
import debug
import math
import tech
import random
from .stimuli import *
from .trim_spice import *
from .charutils import *
@@ -209,14 +208,14 @@ class simulation():
t_current,
t_current+self.period)
elif op == "write":
comment = "\tWriting {0} to address {1} (from port {2}) during cylce {3} ({4}ns - {5}ns)".format(word,
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr,
port,
int(t_current/self.period),
t_current,
t_current+self.period)
else:
comment = "\tReading {0} from address {1} (from port {2}) during cylce {3} ({4}ns - {5}ns)".format(word,
comment = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr,
port,
int(t_current/self.period),
-7
View File
@@ -9,10 +9,3 @@ temperatures = [25]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
#Setting for multiport
# netlist_only = True
# bitcell = "pbitcell"
# replica_bitcell="replica_pbitcell"
# num_rw_ports = 1
# num_r_ports = 0
# num_w_ports = 1
+1 -8
View File
@@ -3,16 +3,9 @@ num_words = 16
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [ 5.0 ]
supply_voltages = [ 3.3 ]
temperatures = [ 25 ]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
#Setting for multiport
netlist_only = True
bitcell = "pbitcell"
replica_bitcell="replica_pbitcell"
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 1
+17 -5
View File
@@ -18,13 +18,14 @@ class control_logic(design.design):
Dynamically generated Control logic for the total SRAM circuit.
"""
def __init__(self, num_rows, port_type="rw"):
def __init__(self, num_rows, words_per_row, port_type="rw"):
""" Constructor """
name = "control_logic_" + port_type
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(name))
self.num_rows = num_rows
self.words_per_row = words_per_row
self.port_type = port_type
if self.port_type == "rw":
@@ -92,14 +93,25 @@ class control_logic(design.design):
from importlib import reload
c = reload(__import__(OPTS.replica_bitline))
replica_bitline = getattr(c, OPTS.replica_bitline)
# FIXME: These should be tuned according to the size!
delay_stages = 4 # Must be non-inverting
delay_fanout = 3 # This can be anything >=2
delay_stages, delay_fanout = self.get_delay_chain_size()
bitcell_loads = int(math.ceil(self.num_rows / 2.0))
self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads, name="replica_bitline_"+self.port_type)
self.add_mod(self.replica_bitline)
def get_delay_chain_size(self):
"""Determine the size of the delay chain used for the Sense Amp Enable """
# FIXME: These should be tuned according to the additional size parameters
delay_fanout = 3 # This can be anything >=2
# Delay stages Must be non-inverting
if self.words_per_row >= 8:
delay_stages = 8
elif self.words_per_row == 4:
delay_stages = 6
else:
delay_stages = 4
return (delay_stages, delay_fanout)
def setup_signal_busses(self):
""" Setup bus names, determine the size of the busses etc """
+65 -9
View File
@@ -189,16 +189,66 @@ class replica_bitline(design.design):
if pin.layer != "metal1":
continue
self.add_path("metal1", [pin_right, pin_extension])
pin_width_ydir = pin.uy()-pin.by()
#Width is set to pin y width to avoid DRC issues with m1 gaps
self.add_path("metal1", [pin_right, pin_extension], pin_width_ydir)
self.add_power_pin("gnd", pin_extension)
# for multiport, need to short wordlines to each other so they all connect to gnd
# for multiport, need to short wordlines to each other so they all connect to gnd.
wl_last = self.wl_list[self.total_ports-1]+"_{}".format(row)
pin_last = self.rbl_inst.get_pin(wl_last)
self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0))
def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None):
"""Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins."""
#Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord.
#This is my (Hunter) first time editing layout in openram so this function is likely not optimal.
if self.total_ports > 1:
#1. Create vertical metal for all the bitlines to connect to
#m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped
correct_y = vector(0, 0.5*drc("minwidth_metal1"))
#x spacing depends on the side being drawn. Unknown to me (Hunter) why the size of the space differs by the side.
#I assume this is related to how a wire is draw, but I have not investigated the issue.
if pin_side == "right":
correct_x = vector(0.5*drc("minwidth_metal1"), 0)
if offset_x_vec != None:
correct_x = offset_x_vec
else:
correct_x = vector(1.5*drc("minwidth_metal1"), 0)
if wl_pin_a.uy() > wl_pin_b.uy():
self.add_path("metal1", [wl_pin_a.rc()+correct_x+correct_y, wl_pin_b.rc()+correct_x-correct_y])
else:
self.add_path("metal1", [wl_pin_a.rc()+correct_x-correct_y, wl_pin_b.rc()+correct_x+correct_y])
elif pin_side == "left":
if offset_x_vec != None:
correct_x = offset_x_vec
else:
correct_x = vector(1.5*drc("minwidth_metal1"), 0)
if wl_pin_a.uy() > wl_pin_b.uy():
self.add_path("metal1", [wl_pin_a.lc()-correct_x+correct_y, wl_pin_b.lc()-correct_x-correct_y])
else:
self.add_path("metal1", [wl_pin_a.lc()-correct_x-correct_y, wl_pin_b.lc()-correct_x+correct_y])
else:
debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1)
correct = vector(0.5*drc("minwidth_metal1"), 0)
self.add_path("metal1", [pin.rc()-correct, pin_last.rc()-correct])
#2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this.
for port in range(self.total_ports):
if is_replica_cell:
wl = self.wl_list[port]
pin = self.rbc_inst.get_pin(wl)
else:
wl = self.wl_list[port]+"_{}".format(cell_row)
pin = self.rbl_inst.get_pin(wl)
if pin_side == "left":
self.add_path("metal1", [pin.lc()-correct_x, pin.lc()])
elif pin_side == "right":
self.add_path("metal1", [pin.rc()+correct_x, pin.rc()])
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
@@ -217,8 +267,13 @@ class replica_bitline(design.design):
# Replica bitcell needs to be routed up to M3
pin=self.rbc_inst.get_pin("vdd")
# Don't rotate this via to vit in FreePDK45
self.add_power_pin("vdd", pin.center(), rotate=0)
# Don't rotate this via to vit in FreePDK45. In the custom cell, the pin cannot be placed
# directly on vdd or there will be a drc error with a wordline. Place the pin slightly farther
# away then route to it. A better solution would be to rotate the m1 in the via or move the pin
# a m1_pitch below the entire cell.
pin_extension = pin.center() - vector(0,self.m1_pitch)
self.add_power_pin("vdd", pin_extension, rotate=0)
self.add_path("metal1", [pin.center(), pin_extension])
for pin in self.rbc_inst.get_pins("gnd"):
self.add_power_pin("gnd", pin.center())
@@ -267,9 +322,10 @@ class replica_bitline(design.design):
wl_last = self.wl_list[self.total_ports-1]
pin = self.rbc_inst.get_pin(wl)
pin_last = self.rbc_inst.get_pin(wl_last)
x_offset = self.short_wordlines(pin, pin_last, "left", True)
correct = vector(0.5*drc("minwidth_metal1"), 0)
self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct])
#correct = vector(0.5*drc("minwidth_metal1"), 0)
#self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct])
# DRAIN ROUTE
# Route the drain to the vdd rail
+1
View File
@@ -25,6 +25,7 @@ class sense_amp(design.design):
def input_load(self):
#Input load for the bitlines which are connected to the source/drain of a TX. Not the selects.
from tech import spice, parameter
bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file.
return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff
@@ -218,7 +218,7 @@ class single_level_column_mux_array(design.design):
rotate=90)
def analytical_delay(self, vdd, slew, load=0.0):
from tech import spice
from tech import spice, parameter
r = spice["min_tx_r"]/(self.mux.ptx_width/parameter["min_tx_size"])
#Drains of mux transistors make up capacitance.
c_para = spice["min_tx_drain_c"]*(self.mux.ptx_width/parameter["min_tx_size"])*self.words_per_row#ff
+3 -3
View File
@@ -223,13 +223,13 @@ class sram_base(design):
from control_logic import control_logic
# Create the control logic module for each port type
if OPTS.num_rw_ports>0:
self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, port_type="rw")
self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="rw")
self.add_mod(self.control_logic_rw)
if OPTS.num_w_ports>0:
self.control_logic_w = control_logic(num_rows=self.num_rows, port_type="w")
self.control_logic_w = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="w")
self.add_mod(self.control_logic_w)
if OPTS.num_r_ports>0:
self.control_logic_r = control_logic(num_rows=self.num_rows, port_type="r")
self.control_logic_r = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="r")
self.add_mod(self.control_logic_r)
# Create the address and control flops (but not the clk)
+22 -2
View File
@@ -24,6 +24,26 @@ class replica_bitline_test(openram_test):
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
#debug.error("Exiting...", 1)
stages=8
rows=100
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
#check replica bitline in handmade multi-port 1rw+1r cell
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
stages=4
fanout=4
rows=13
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
stages=8
rows=100
@@ -31,7 +51,7 @@ class replica_bitline_test(openram_test):
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
# check replica bitline in multi-port
# check replica bitline in pbitcell multi-port
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.num_rw_ports = 1
@@ -61,7 +81,7 @@ class replica_bitline_test(openram_test):
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
stages=8
rows=100
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
+5 -5
View File
@@ -20,7 +20,7 @@ class control_logic_test(openram_test):
# check control logic for single port
debug.info(1, "Testing sample for control_logic")
a = control_logic.control_logic(num_rows=128)
a = control_logic.control_logic(num_rows=128, words_per_row=1)
self.local_check(a)
# check control logic for multi-port
@@ -31,7 +31,7 @@ class control_logic_test(openram_test):
OPTS.num_r_ports = 0
debug.info(1, "Testing sample for control_logic for multiport")
a = control_logic.control_logic(num_rows=128)
a = control_logic.control_logic(num_rows=128, words_per_row=1)
self.local_check(a)
# Check port specific control logic
@@ -40,15 +40,15 @@ class control_logic_test(openram_test):
OPTS.num_r_ports = 1
debug.info(1, "Testing sample for control_logic for multiport, only write control logic")
a = control_logic.control_logic(num_rows=128, port_type="rw")
a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="rw")
self.local_check(a)
debug.info(1, "Testing sample for control_logic for multiport, only write control logic")
a = control_logic.control_logic(num_rows=128, port_type="w")
a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="w")
self.local_check(a)
debug.info(1, "Testing sample for control_logic for multiport, only read control logic")
a = control_logic.control_logic(num_rows=128, port_type="r")
a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="r")
self.local_check(a)
globals.end_openram()
@@ -20,6 +20,9 @@ class psram_1bank_2mux_func_test(openram_test):
OPTS.netlist_only = True
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
@@ -20,6 +20,9 @@ class psram_1bank_4mux_func_test(openram_test):
OPTS.netlist_only = True
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
@@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 22_psram_1bank_8mux_func_test")
#@unittest.skip("SKIPPING 22_psram_1bank_8mux_func_test")
class psram_1bank_8mux_func_test(openram_test):
def runTest(self):
@@ -20,6 +20,9 @@ class psram_1bank_8mux_func_test(openram_test):
OPTS.netlist_only = True
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
@@ -29,7 +32,7 @@ class psram_1bank_8mux_func_test(openram_test):
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=512,
num_words=256,
num_banks=1)
c.words_per_row=8
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
@@ -20,6 +20,9 @@ class psram_1bank_nomux_func_test(openram_test):
OPTS.netlist_only = True
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
@@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 22_sram_1bank_4mux_func_test")
#@unittest.skip("SKIPPING 22_sram_1bank_4mux_func_test")
class sram_1bank_4mux_func_test(openram_test):
def runTest(self):
@@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 22_sram_1bank_8mux_func_test")
#@unittest.skip("SKIPPING 22_sram_1bank_8mux_func_test")
class sram_1bank_8mux_func_test(openram_test):
def runTest(self):
@@ -30,7 +30,7 @@ class sram_1bank_8mux_func_test(openram_test):
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=512,
num_words=256,
num_banks=1)
c.words_per_row=8
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
@@ -38,6 +38,7 @@ class sram_1bank_8mux_func_test(openram_test):
c.words_per_row,
c.num_banks))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
@@ -39,7 +39,7 @@ class sram_1bank_nomux_func_test(openram_test):
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f.num_cycles = 10
(fail, error) = f.run()
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""
Run a functioal test on 1 bank 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
#@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test")
class psram_1bank_nomux_func_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
# 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 import sram
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.words_per_row=1
debug.info(1, "Functional test for sram 1rw,1r with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f.num_cycles = 10
(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()