mirror of https://github.com/VLSIDA/OpenRAM.git
SRAM layout and functional tests with spare cols
This commit is contained in:
parent
c7d86b21ae
commit
eb0c595dbe
|
|
@ -18,7 +18,7 @@ from .charutils import *
|
||||||
import utils
|
import utils
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from .simulation import simulation
|
from .simulation import simulation
|
||||||
from .delay import delay
|
# from .delay import delay
|
||||||
import graph_util
|
import graph_util
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
@ -40,6 +40,9 @@ class functional(simulation):
|
||||||
else:
|
else:
|
||||||
self.num_wmasks = 0
|
self.num_wmasks = 0
|
||||||
|
|
||||||
|
if not self.num_spare_cols:
|
||||||
|
self.num_spare_cols = 0
|
||||||
|
|
||||||
self.set_corner(corner)
|
self.set_corner(corner)
|
||||||
self.set_spice_constants()
|
self.set_spice_constants()
|
||||||
self.set_stimulus_variables()
|
self.set_stimulus_variables()
|
||||||
|
|
@ -86,6 +89,7 @@ class functional(simulation):
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
checks.append((self.data_value[port],"data"))
|
checks.append((self.data_value[port],"data"))
|
||||||
checks.append((self.wmask_value[port],"wmask"))
|
checks.append((self.wmask_value[port],"wmask"))
|
||||||
|
checks.append((self.spare_wen_value[port],"spare_wen"))
|
||||||
|
|
||||||
for (val, name) in checks:
|
for (val, name) in checks:
|
||||||
debug.check(len(self.cycle_times)==len(val),
|
debug.check(len(self.cycle_times)==len(val),
|
||||||
|
|
@ -226,7 +230,7 @@ class functional(simulation):
|
||||||
# Extract dout values from spice timing.lis
|
# Extract dout values from spice timing.lis
|
||||||
for (word, dout_port, eo_period, check) in self.read_check:
|
for (word, dout_port, eo_period, check) in self.read_check:
|
||||||
sp_read_value = ""
|
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))
|
value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check))
|
||||||
if value > self.v_high:
|
if value > self.v_high:
|
||||||
sp_read_value = "1" + sp_read_value
|
sp_read_value = "1" + sp_read_value
|
||||||
|
|
@ -281,13 +285,18 @@ class functional(simulation):
|
||||||
|
|
||||||
def gen_data(self):
|
def gen_data(self):
|
||||||
""" Generates a random word to write. """
|
""" 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)
|
data_bits = self.convert_to_bin(random_value,False)
|
||||||
return data_bits
|
return data_bits
|
||||||
|
|
||||||
def gen_addr(self):
|
def gen_addr(self):
|
||||||
""" Generates a random address value to write to. """
|
""" 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)
|
random_value = random.randint(0,(2**self.addr_size)-1)
|
||||||
else:
|
else:
|
||||||
random_value = random.randint(0,((2**(self.addr_size-1)-1))+(self.num_spare_rows * self.words_per_row))
|
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):
|
if(is_addr):
|
||||||
expected_value = self.addr_size
|
expected_value = self.addr_size
|
||||||
else:
|
else:
|
||||||
|
expected_value = self.word_size + self.num_spare_cols
|
||||||
expected_value = self.word_size
|
|
||||||
for i in range (expected_value - len(new_value)):
|
for i in range (expected_value - len(new_value)):
|
||||||
new_value = "0" + new_value
|
new_value = "0" + new_value
|
||||||
|
|
||||||
|
|
@ -337,7 +345,7 @@ class functional(simulation):
|
||||||
# Add load capacitance to each of the read ports
|
# Add load capacitance to each of the read ports
|
||||||
self.sf.write("\n* SRAM output loads\n")
|
self.sf.write("\n* SRAM output loads\n")
|
||||||
for port in self.read_ports:
|
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)
|
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))
|
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
|
# Generate data input bits
|
||||||
self.sf.write("\n* Generation of data and address signals\n")
|
self.sf.write("\n* Generation of data and address signals\n")
|
||||||
for port in self.write_ports:
|
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)
|
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)
|
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.stim.gen_pwl(sig_name, self.cycle_times, self.wmask_values[port][bit], self.period,
|
||||||
self.slew, 0.05)
|
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
|
# Generate CLK signals
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.stim.gen_pulse(sig_name="{0}{1}".format("clk", port),
|
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:
|
for (word, dout_port, eo_period, check) in self.read_check:
|
||||||
t_intital = eo_period - 0.01*self.period
|
t_intital = eo_period - 0.01*self.period
|
||||||
t_final = 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),
|
self.stim.gen_meas_value(meas_name="V{0}_{1}ck{2}".format(dout_port,bit,check),
|
||||||
dout="{0}_{1}".format(dout_port,bit),
|
dout="{0}_{1}".format(dout_port,bit),
|
||||||
t_intital=t_intital,
|
t_intital=t_intital,
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ class simulation():
|
||||||
self.addr_size = self.sram.addr_size
|
self.addr_size = self.sram.addr_size
|
||||||
self.write_size = self.sram.write_size
|
self.write_size = self.sram.write_size
|
||||||
self.num_spare_rows = self.sram.num_spare_rows
|
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.sp_file = spfile
|
||||||
|
|
||||||
self.all_ports = self.sram.all_ports
|
self.all_ports = self.sram.all_ports
|
||||||
|
|
@ -38,7 +42,6 @@ class simulation():
|
||||||
else:
|
else:
|
||||||
self.num_wmasks = 0
|
self.num_wmasks = 0
|
||||||
|
|
||||||
|
|
||||||
def set_corner(self,corner):
|
def set_corner(self,corner):
|
||||||
""" Set the corner values """
|
""" Set the corner values """
|
||||||
self.corner = corner
|
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),
|
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),
|
port_info=(len(self.all_ports),self.write_ports,self.read_ports),
|
||||||
abits=self.addr_size,
|
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),
|
debug.check(len(self.sram.pins) == len(self.pins),
|
||||||
"Number of pins generated for characterization \
|
"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))
|
self.pins))
|
||||||
#This is TODO once multiport control has been finalized.
|
#This is TODO once multiport control has been finalized.
|
||||||
#self.control_name = "CSB"
|
#self.control_name = "CSB"
|
||||||
|
|
@ -82,11 +85,13 @@ class simulation():
|
||||||
self.addr_value = {port:[] for port in self.all_ports}
|
self.addr_value = {port:[] for port in self.all_ports}
|
||||||
self.data_value = {port:[] for port in self.write_ports}
|
self.data_value = {port:[] for port in self.write_ports}
|
||||||
self.wmask_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
|
# 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.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.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
|
# For generating comments in SPICE stimulus
|
||||||
self.cycle_comments = []
|
self.cycle_comments = []
|
||||||
|
|
@ -113,10 +118,10 @@ class simulation():
|
||||||
|
|
||||||
def add_data(self, data, port):
|
def add_data(self, data, port):
|
||||||
""" Add the array of data values """
|
""" 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)
|
self.data_value[port].append(data)
|
||||||
bit = self.word_size - 1
|
bit = self.word_size + self.num_spare_cols - 1
|
||||||
for c in data:
|
for c in data:
|
||||||
if c=="0":
|
if c=="0":
|
||||||
self.data_values[port][bit].append(0)
|
self.data_values[port][bit].append(0)
|
||||||
|
|
@ -137,10 +142,7 @@ class simulation():
|
||||||
if c=="0":
|
if c=="0":
|
||||||
self.addr_values[port][bit].append(0)
|
self.addr_values[port][bit].append(0)
|
||||||
elif c=="1":
|
elif c=="1":
|
||||||
if((self.num_spare_rows != 0) and (bit == (self.addr_size - 1))):
|
self.addr_values[port][bit].append(1)
|
||||||
self.addr_values[port][bit].append(0)
|
|
||||||
else:
|
|
||||||
self.addr_values[port][bit].append(1)
|
|
||||||
else:
|
else:
|
||||||
debug.error("Non-binary address string",1)
|
debug.error("Non-binary address string",1)
|
||||||
bit -= 1
|
bit -= 1
|
||||||
|
|
@ -161,7 +163,21 @@ class simulation():
|
||||||
debug.error("Non-binary wmask string", 1)
|
debug.error("Non-binary wmask string", 1)
|
||||||
bit -= 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):
|
def add_write(self, comment, address, data, wmask, port):
|
||||||
""" Add the control values for a write cycle. """
|
""" Add the control values for a write cycle. """
|
||||||
debug.check(port in self.write_ports,
|
debug.check(port in self.write_ports,
|
||||||
|
|
@ -178,6 +194,7 @@ class simulation():
|
||||||
self.add_data(data,port)
|
self.add_data(data,port)
|
||||||
self.add_address(address,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.
|
#Add noops to all other ports.
|
||||||
for unselected_port in self.all_ports:
|
for unselected_port in self.all_ports:
|
||||||
|
|
@ -197,6 +214,7 @@ class simulation():
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
self.add_control_one_port(port, "read")
|
self.add_control_one_port(port, "read")
|
||||||
self.add_address(address, port)
|
self.add_address(address, port)
|
||||||
|
self.add_spare_wen("0" * self.num_spare_cols, port)
|
||||||
|
|
||||||
# If the port is also a readwrite then add
|
# If the port is also a readwrite then add
|
||||||
# the same value as previous cycle
|
# the same value as previous cycle
|
||||||
|
|
@ -204,7 +222,7 @@ class simulation():
|
||||||
try:
|
try:
|
||||||
self.add_data(self.data_value[port][-1], port)
|
self.add_data(self.data_value[port][-1], port)
|
||||||
except:
|
except:
|
||||||
self.add_data("0"*self.word_size, port)
|
self.add_data("0"*(self.word_size + self.num_spare_cols), port)
|
||||||
try:
|
try:
|
||||||
self.add_wmask(self.wmask_value[port][-1], port)
|
self.add_wmask(self.wmask_value[port][-1], port)
|
||||||
except:
|
except:
|
||||||
|
|
@ -239,6 +257,7 @@ class simulation():
|
||||||
self.add_data(data, port)
|
self.add_data(data, port)
|
||||||
self.add_address(address, 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)
|
||||||
|
|
||||||
def add_read_one_port(self, comment, address, port):
|
def add_read_one_port(self, comment, address, port):
|
||||||
""" Add the control values for a read cycle. Does not increment the period. """
|
""" 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_control_one_port(port, "read")
|
||||||
self.add_address(address, port)
|
self.add_address(address, port)
|
||||||
|
self.add_spare_wen("0" * self.num_spare_cols, port)
|
||||||
# If the port is also a readwrite then add
|
# If the port is also a readwrite then add
|
||||||
# the same value as previous cycle
|
# the same value as previous cycle
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
try:
|
try:
|
||||||
self.add_data(self.data_value[port][-1], port)
|
self.add_data(self.data_value[port][-1], port)
|
||||||
except:
|
except:
|
||||||
self.add_data("0"*self.word_size, port)
|
self.add_data("0"*(self.word_size + self.num_spare_cols), port)
|
||||||
try:
|
try:
|
||||||
self.add_wmask(self.wmask_value[port][-1], port)
|
self.add_wmask(self.wmask_value[port][-1], port)
|
||||||
except:
|
except:
|
||||||
|
|
@ -266,6 +286,7 @@ class simulation():
|
||||||
def add_noop_one_port(self, port):
|
def add_noop_one_port(self, port):
|
||||||
""" Add the control values for a noop to a single port. Does not increment the period. """
|
""" 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_control_one_port(port, "noop")
|
||||||
|
self.add_spare_wen("0" * self.num_spare_cols, port)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.add_address(self.addr_value[port][-1], port)
|
self.add_address(self.addr_value[port][-1], port)
|
||||||
|
|
@ -278,7 +299,7 @@ class simulation():
|
||||||
try:
|
try:
|
||||||
self.add_data(self.data_value[port][-1], port)
|
self.add_data(self.data_value[port][-1], port)
|
||||||
except:
|
except:
|
||||||
self.add_data("0"*self.word_size, port)
|
self.add_data("0"*(self.word_size + self.num_spare_cols), port)
|
||||||
try:
|
try:
|
||||||
self.add_wmask(self.wmask_value[port][-1], port)
|
self.add_wmask(self.wmask_value[port][-1], port)
|
||||||
except:
|
except:
|
||||||
|
|
@ -374,6 +395,11 @@ class simulation():
|
||||||
for port in write_index:
|
for port in write_index:
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
pin_names.append("WMASK{0}_{1}".format(port,bit))
|
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 read_output in read_index:
|
||||||
for i in range(dbits):
|
for i in range(dbits):
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class bank(design.design):
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
self.add_pin("bank_wmask{0}_{1}".format(port, bit), "INPUT")
|
self.add_pin("bank_wmask{0}_{1}".format(port, bit), "INPUT")
|
||||||
for bit in range(self.num_spare_cols):
|
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:
|
for port in self.all_ports:
|
||||||
self.add_pin("wl_en{0}".format(port), "INPUT")
|
self.add_pin("wl_en{0}".format(port), "INPUT")
|
||||||
self.add_pin("vdd", "POWER")
|
self.add_pin("vdd", "POWER")
|
||||||
|
|
@ -442,7 +442,7 @@ class bank(design.design):
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
temp.append("bank_wmask{0}_{1}".format(port, bit))
|
temp.append("bank_wmask{0}_{1}".format(port, bit))
|
||||||
for bit in range(self.num_spare_cols):
|
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"])
|
temp.extend(["vdd", "gnd"])
|
||||||
|
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
@ -725,15 +725,15 @@ class bank(design.design):
|
||||||
din_name = "din{0}_{1}".format(port, row)
|
din_name = "din{0}_{1}".format(port, row)
|
||||||
self.copy_layout_pin(self.port_data_inst[port], data_name, din_name)
|
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):
|
for row in range(self.num_wmasks):
|
||||||
wmask_name = "bank_wmask_{}".format(row)
|
wmask_name = "bank_wmask_{}".format(row)
|
||||||
bank_wmask_name = "bank_wmask{0}_{1}".format(port, 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)
|
self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
|
||||||
|
|
||||||
for col in range(self.num_spare_cols):
|
for col in range(self.num_spare_cols):
|
||||||
sparecol_name = "spare_wen{}".format(col)
|
sparecol_name = "bank_spare_wen{}".format(col)
|
||||||
bank_sparecol_name = "spare_wen{0}_{1}".format(port, 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)
|
self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name)
|
||||||
|
|
||||||
def channel_route_bitlines(self, inst1, inst2, num_bits,
|
def channel_route_bitlines(self, inst1, inst2, num_bits,
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ class control_logic(design.design):
|
||||||
# clk_buf drives a flop for every address
|
# clk_buf drives a flop for every address
|
||||||
addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
|
addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
|
||||||
# plus data flops and control flops
|
# 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
|
# each flop internally has a FO 5 approximately
|
||||||
# plus about 5 fanouts for the control logic
|
# plus about 5 fanouts for the control logic
|
||||||
clock_fanout = 5 * num_flops + 5
|
clock_fanout = 5 * num_flops + 5
|
||||||
|
|
@ -135,7 +135,7 @@ class control_logic(design.design):
|
||||||
|
|
||||||
# s_en drives every sense amp
|
# s_en drives every sense amp
|
||||||
self.sen_and3 = factory.create(module_type="pand3",
|
self.sen_and3 = factory.create(module_type="pand3",
|
||||||
size=self.word_size,
|
size=self.word_size + self.num_spare_cols,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.sen_and3)
|
self.add_mod(self.sen_and3)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ class port_data(design.design):
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
self.add_pin("bank_wmask_{}".format(bit), "INPUT")
|
self.add_pin("bank_wmask_{}".format(bit), "INPUT")
|
||||||
for bit in range(self.num_spare_cols):
|
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("vdd", "POWER")
|
||||||
self.add_pin("gnd", "GROUND")
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
|
|
@ -387,12 +387,12 @@ class port_data(design.design):
|
||||||
for i in range(self.num_wmasks):
|
for i in range(self.num_wmasks):
|
||||||
temp.append("wdriver_sel_{}".format(i))
|
temp.append("wdriver_sel_{}".format(i))
|
||||||
for i in range(self.num_spare_cols):
|
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:
|
elif self.num_spare_cols and not self.write_size:
|
||||||
temp.append("w_en")
|
temp.append("w_en")
|
||||||
for i in range(self.num_spare_cols):
|
for i in range(self.num_spare_cols):
|
||||||
temp.append("spare_wen{}".format(i))
|
temp.append("bank_spare_wen{}".format(i))
|
||||||
else:
|
else:
|
||||||
temp.append("w_en")
|
temp.append("w_en")
|
||||||
temp.extend(["vdd", "gnd"])
|
temp.extend(["vdd", "gnd"])
|
||||||
|
|
@ -627,9 +627,9 @@ class port_data(design.design):
|
||||||
start_bit=0
|
start_bit=0
|
||||||
|
|
||||||
if self.port==0:
|
if self.port==0:
|
||||||
off=1
|
off = 1
|
||||||
else:
|
else:
|
||||||
off=0
|
off = 0
|
||||||
|
|
||||||
|
|
||||||
if self.num_spare_cols != 0 and self.col_addr_size>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))
|
self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit), "wdriver_sel_{}".format(bit))
|
||||||
for bit in range(self.num_spare_cols):
|
for bit in range(self.num_spare_cols):
|
||||||
# Add spare columns' en_{} pins
|
# 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:
|
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")
|
self.copy_layout_pin(self.write_driver_array_inst, "en_0", "w_en")
|
||||||
for bit in range(self.num_spare_cols):
|
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:
|
else:
|
||||||
self.copy_layout_pin(self.write_driver_array_inst, "en", "w_en")
|
self.copy_layout_pin(self.write_driver_array_inst, "en", "w_en")
|
||||||
if self.write_mask_and_array_inst:
|
if self.write_mask_and_array_inst:
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ class write_driver_array(design.design):
|
||||||
offset = self.num_wmasks
|
offset = self.num_wmasks
|
||||||
else:
|
else:
|
||||||
offset = 1
|
offset = 1
|
||||||
name = "write_driver{}".format(index)
|
name = "write_driver{}".format(self.columns + i)
|
||||||
self.driver_insts[index]=self.add_inst(name=name,
|
self.driver_insts[index]=self.add_inst(name=name,
|
||||||
mod=self.driver)
|
mod=self.driver)
|
||||||
|
|
||||||
|
|
@ -234,27 +234,30 @@ class write_driver_array(design.design):
|
||||||
|
|
||||||
for i in range(self.num_spare_cols):
|
for i in range(self.num_spare_cols):
|
||||||
inst = self.driver_insts[self.word_size + i]
|
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),
|
self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks),
|
||||||
layer="m1",
|
layer="m1",
|
||||||
offset=inst.get_pin(inst.mod.en_name).ll(),
|
offset=en_pin.ll(),
|
||||||
width=self.single_col_width - inst.get_pin(inst.mod.en_name).width())
|
width=self.driver.width - en_pin.width())
|
||||||
|
|
||||||
|
|
||||||
elif self.num_spare_cols and not self.write_size:
|
elif self.num_spare_cols and not self.write_size:
|
||||||
# shorten enable rail to accomodate those for spare write drivers
|
# shorten enable rail to accomodate those for spare write drivers
|
||||||
inst = self.driver_insts[0]
|
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),
|
self.add_layout_pin(text=self.en_name + "_{0}".format(0),
|
||||||
layer="m1",
|
layer="m1",
|
||||||
offset=inst.get_pin(inst.mod.en_name).ll(),
|
offset=en_pin.ll(),
|
||||||
width=self.width_regular_cols - (self.words_per_row * inst.get_pin(inst.mod.en_name).width()))
|
width=self.width_regular_cols - self.words_per_row * en_pin.width())
|
||||||
|
|
||||||
# individual enables for every spare write driver
|
# individual enables for every spare write driver
|
||||||
for i in range(self.num_spare_cols):
|
for i in range(self.num_spare_cols):
|
||||||
inst = self.driver_insts[self.word_size + i]
|
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),
|
self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1),
|
||||||
layer="m1",
|
layer="m1",
|
||||||
offset=inst.get_pin(inst.mod.en_name).ll(),
|
offset=en_pin.ll(),
|
||||||
width=self.single_col_width - inst.get_pin(inst.mod.en_name).width())
|
width=self.driver.width - en_pin.width())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
inst = self.driver_insts[0]
|
inst = self.driver_insts[0]
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,11 @@ class sram_1bank(sram_base):
|
||||||
else:
|
else:
|
||||||
self.data_dff_insts = self.create_data_dff()
|
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):
|
def place_instances(self):
|
||||||
"""
|
"""
|
||||||
This places the instances for a single bank SRAM with control
|
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)
|
row_addr_pos = [None] * len(self.all_ports)
|
||||||
col_addr_pos = [None] * len(self.all_ports)
|
col_addr_pos = [None] * len(self.all_ports)
|
||||||
wmask_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)
|
data_pos = [None] * len(self.all_ports)
|
||||||
|
|
||||||
# These positions utilize the channel route sizes.
|
# 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.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_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
|
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:
|
else:
|
||||||
self.data_bus_gap = self.m3_nonpref_pitch * 2
|
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_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.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 port in self.write_ports:
|
||||||
if self.write_size:
|
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.
|
# Add the write mask flops below the write mask AND array.
|
||||||
wmask_pos[port] = vector(self.bank.bank_array_ll.x,
|
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])
|
self.wmask_dff_insts[port].place(wmask_pos[port])
|
||||||
|
|
||||||
# Add the data flops below the write mask flops.
|
# Add the data flops below the write mask flops.
|
||||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
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])
|
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:
|
else:
|
||||||
# Add the data flops below the bank to the right of the lower-left of bank array
|
# 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
|
# This relies on the lower-left of the array of the bank
|
||||||
# decoder in upper left, bank in upper right, sensing in lower right.
|
# 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
|
# These flops go below the sensing and leave a gap to channel route to the
|
||||||
# sense amps.
|
# sense amps.
|
||||||
if port in self.write_ports:
|
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
-self.data_bus_size - self.dff.height)
|
||||||
-self.data_bus_size - self.dff.height)
|
self.data_dff_insts[port].place(data_pos[port])
|
||||||
self.data_dff_insts[port].place(data_pos[port])
|
|
||||||
else:
|
else:
|
||||||
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
||||||
data_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
|
# 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.col_addr_dff:
|
||||||
if self.write_size:
|
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,
|
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:
|
else:
|
||||||
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
|
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.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 port in self.write_ports:
|
||||||
if self.write_size:
|
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,
|
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")
|
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,
|
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")
|
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:
|
else:
|
||||||
# Add the data flops above the bank to the left of the upper-right of bank array
|
# 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
|
# 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.col_addr_dff:
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
|
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:
|
else:
|
||||||
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
|
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.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],
|
self.copy_layout_pin(self.wmask_dff_insts[port],
|
||||||
"din_{}".format(bit),
|
"din_{}".format(bit),
|
||||||
"wmask{0}[{1}]".format(port, bit))
|
"wmask{0}[{1}]".format(port, bit))
|
||||||
|
|
||||||
for bit in range(self.num_spare_cols):
|
for bit in range(self.num_spare_cols):
|
||||||
self.copy_layout_pin(self.bank_inst,
|
self.copy_layout_pin(self.spare_wen_dff_insts[port],
|
||||||
"spare_wen{0}_{1}".format(port, bit),
|
"din_{}".format(bit),
|
||||||
"spare_wen{0}[{1}]".format(port, bit))
|
"spare_wen{0}[{1}]".format(port, bit))
|
||||||
|
|
||||||
def route_layout(self):
|
def route_layout(self):
|
||||||
|
|
@ -242,6 +306,9 @@ class sram_1bank(sram_base):
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
self.route_wmask_dff()
|
self.route_wmask_dff()
|
||||||
|
|
||||||
|
if self.num_spare_cols:
|
||||||
|
self.route_spare_wen_dff()
|
||||||
|
|
||||||
def route_clk(self):
|
def route_clk(self):
|
||||||
""" Route the clock network """
|
""" 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_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])
|
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):
|
def route_control_logic(self):
|
||||||
""" Route the control logic pins that are not inputs """
|
""" 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 """
|
""" Connect the output of the data flops to the write driver """
|
||||||
# This is where the channel will start (y-dimension at least)
|
# This is where the channel will start (y-dimension at least)
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
if self.write_size:
|
if port % 2:
|
||||||
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)
|
|
||||||
else:
|
|
||||||
offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap)
|
|
||||||
else:
|
else:
|
||||||
if port % 2:
|
offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap)
|
||||||
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)
|
|
||||||
|
|
||||||
dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)]
|
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]
|
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:
|
for x in dff_names:
|
||||||
pin = self.data_dff_insts[port].get_pin(x)
|
pin = self.data_dff_insts[port].get_pin(x)
|
||||||
pin_offset = pin.center()
|
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_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]
|
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:
|
for x in bank_names:
|
||||||
pin = self.bank_inst.get_pin(x)
|
pin = self.bank_inst.get_pin(x)
|
||||||
if port % 2:
|
if port % 2:
|
||||||
|
|
@ -411,7 +481,7 @@ class sram_1bank(sram_base):
|
||||||
offset=pin_offset)
|
offset=pin_offset)
|
||||||
|
|
||||||
route_map = list(zip(bank_pins, dff_pins))
|
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
|
layer_stack = self.m3_stack
|
||||||
else:
|
else:
|
||||||
layer_stack = self.m1_stack
|
layer_stack = self.m1_stack
|
||||||
|
|
@ -448,6 +518,36 @@ class sram_1bank(sram_base):
|
||||||
self.create_horizontal_channel_route(netlist=route_map,
|
self.create_horizontal_channel_route(netlist=route_map,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layer_stack=self.m1_stack)
|
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):
|
def add_lvs_correspondence_points(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -470,6 +570,9 @@ class sram_1bank(sram_base):
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
for inst in self.wmask_dff_insts:
|
for inst in self.wmask_dff_insts:
|
||||||
self.graph_inst_exclude.add(inst)
|
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):
|
def graph_exclude_addr_dff(self):
|
||||||
"""Removes data dff from search graph. """
|
"""Removes data dff from search graph. """
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,10 @@ class sram_base(design, verilog, lef):
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks)
|
self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks)
|
||||||
self.add_mod(self.wmask_dff)
|
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)
|
# Create the bank module (up to four are instantiated)
|
||||||
self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank")
|
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):
|
for bit in range(self.num_wmasks):
|
||||||
temp.append("bank_wmask{}[{}]".format(port, bit))
|
temp.append("bank_wmask{}[{}]".format(port, bit))
|
||||||
for bit in range(self.num_spare_cols):
|
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:
|
for port in self.all_ports:
|
||||||
temp.append("wl_en{0}".format(port))
|
temp.append("wl_en{0}".format(port))
|
||||||
temp.extend(["vdd", "gnd"])
|
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"])
|
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
||||||
|
|
||||||
return insts
|
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):
|
def create_control_logic(self):
|
||||||
""" Add control logic instances """
|
""" Add control logic instances """
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import debug
|
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):
|
def runTest(self):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,13 @@ import debug
|
||||||
|
|
||||||
|
|
||||||
# @unittest.skip("SKIPPING 20_sram_1bank_nomux_wmask_test")
|
# @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):
|
def runTest(self):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
from sram_config import sram_config
|
from sram_config import sram_config
|
||||||
c = sram_config(word_size=8,
|
c = sram_config(word_size=8,
|
||||||
write_size=4,
|
|
||||||
num_spare_cols=3,
|
num_spare_cols=3,
|
||||||
num_words=16,
|
num_words=16,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
|
|
@ -33,13 +32,13 @@ class sram_1bank_nomux_wmask_test(openram_test):
|
||||||
c.words_per_row = 1
|
c.words_per_row = 1
|
||||||
c.recompute_sizes()
|
c.recompute_sizes()
|
||||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram "
|
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,
|
"row, {} banks".format(OPTS.num_rw_ports,
|
||||||
OPTS.num_r_ports,
|
OPTS.num_r_ports,
|
||||||
OPTS.num_w_ports,
|
OPTS.num_w_ports,
|
||||||
c.word_size,
|
c.word_size,
|
||||||
c.num_words,
|
c.num_words,
|
||||||
c.write_size,
|
c.num_spare_cols,
|
||||||
c.words_per_row,
|
c.words_per_row,
|
||||||
c.num_banks))
|
c.num_banks))
|
||||||
a = factory.create(module_type="sram", sram_config=c)
|
a = factory.create(module_type="sram", sram_config=c)
|
||||||
|
|
@ -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())
|
||||||
|
|
@ -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())
|
||||||
Loading…
Reference in New Issue