SRAM layout and functional tests with spare cols

This commit is contained in:
Aditi Sinha 2020-06-03 12:31:30 +00:00
parent c7d86b21ae
commit eb0c595dbe
12 changed files with 373 additions and 77 deletions

View File

@ -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,

View File

@ -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):

View File

@ -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,

View File

@ -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)

View File

@ -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:

View File

@ -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]

View File

@ -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. """

View File

@ -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 """

View File

@ -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"))

View File

@ -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)

View File

@ -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())

View File

@ -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())