mirror of https://github.com/VLSIDA/OpenRAM.git
Begin adding wmask netlist and spice tests.
This commit is contained in:
parent
5258016c9f
commit
dfa2b29b8f
|
|
@ -37,7 +37,7 @@ class functional(simulation):
|
||||||
#self.set_feasible_period(sram, spfile, corner)
|
#self.set_feasible_period(sram, spfile, corner)
|
||||||
self.set_stimulus_variables()
|
self.set_stimulus_variables()
|
||||||
self.create_signal_names()
|
self.create_signal_names()
|
||||||
|
self.initilize_wmask()
|
||||||
|
|
||||||
# Number of checks can be changed
|
# Number of checks can be changed
|
||||||
self.num_cycles = 2
|
self.num_cycles = 2
|
||||||
|
|
@ -45,6 +45,20 @@ class functional(simulation):
|
||||||
self.write_check = []
|
self.write_check = []
|
||||||
self.read_check = []
|
self.read_check = []
|
||||||
|
|
||||||
|
def initilize_wmask(self):
|
||||||
|
self.wmask = [None]*self.num_wmask
|
||||||
|
self.num_wmask = int(self.word_size/self.write_size)
|
||||||
|
self.wmask_enabled = False
|
||||||
|
if self.word_size !=self.write_size:
|
||||||
|
self.wmask_enabled = True
|
||||||
|
# initialize first wmask bit to 1, otherwise 0
|
||||||
|
for bit in range(self.num_wmask):
|
||||||
|
if bit == 0:
|
||||||
|
self.wmask[self.num_wmask-1 - bit] = 1
|
||||||
|
else:
|
||||||
|
self.wmask[self.num_wmask-1 - bit] = 0
|
||||||
|
print(self.wmask)
|
||||||
|
|
||||||
def run(self, feasible_period=None):
|
def run(self, feasible_period=None):
|
||||||
if feasible_period: #period defaults to tech.py feasible period otherwise.
|
if feasible_period: #period defaults to tech.py feasible period otherwise.
|
||||||
self.period = feasible_period
|
self.period = feasible_period
|
||||||
|
|
@ -71,14 +85,35 @@ class functional(simulation):
|
||||||
check = 0
|
check = 0
|
||||||
|
|
||||||
# First cycle idle
|
# First cycle idle
|
||||||
comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, 0, self.t_current)
|
comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, 0, self.wmask, self.t_current)
|
||||||
self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size)
|
self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size)
|
||||||
|
|
||||||
# Write at least once
|
# Write at least once
|
||||||
addr = self.gen_addr()
|
addr = self.gen_addr()
|
||||||
word = self.gen_data()
|
word = self.gen_data()
|
||||||
comment = self.gen_cycle_comment("write", word, addr, 0, self.t_current)
|
comment = self.gen_cycle_comment("write", word, addr, 0, self.wmask, self.t_current)
|
||||||
self.add_write(comment, addr, word, 0)
|
self.add_write(comment, addr, word, self.wmask, 0)
|
||||||
|
if self.wmask_enabled:
|
||||||
|
old_word = ""
|
||||||
|
if self.stored_words.get(addr) == None:
|
||||||
|
for i in range(self.word_size):
|
||||||
|
old_word += "X"
|
||||||
|
else:
|
||||||
|
old_word = self.stored_words[addr]
|
||||||
|
for bit in self.wmask:
|
||||||
|
# Don't write the bits of the new word to the address
|
||||||
|
if self.wmask[bit] == 0:
|
||||||
|
lower = bit * self.write_size
|
||||||
|
upper = lower + self.write_size - 1
|
||||||
|
if bit == self.num_wmask-1:
|
||||||
|
word = word[0:lower] + old_word[lower:upper+1]
|
||||||
|
elif bit == 0:
|
||||||
|
word = old_word[lower:upper+1] + word [upper+1:self.word_size]
|
||||||
|
else:
|
||||||
|
word = word[0:lower] + old_word[lower:upper+1] + word [upper+1:self.word_size]cfusms
|
||||||
|
#word = word.replace(word[lower:upper+1],old_word[lower:upper+1],1)
|
||||||
|
self.stored_words[addr] = word
|
||||||
|
else:
|
||||||
self.stored_words[addr] = word
|
self.stored_words[addr] = word
|
||||||
|
|
||||||
# Read at least once. For multiport, it is important that one read cycle uses all RW and R port to read from the same address simultaniously.
|
# Read at least once. For multiport, it is important that one read cycle uses all RW and R port to read from the same address simultaniously.
|
||||||
|
|
@ -87,7 +122,7 @@ class functional(simulation):
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
|
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
|
||||||
else:
|
else:
|
||||||
comment = self.gen_cycle_comment("read", word, addr, port, self.t_current)
|
comment = self.gen_cycle_comment("read", word, addr, port, self.wmask, self.t_current)
|
||||||
self.add_read_one_port(comment, addr, rw_read_din_data, port)
|
self.add_read_one_port(comment, addr, rw_read_din_data, port)
|
||||||
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
|
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
|
||||||
check += 1
|
check += 1
|
||||||
|
|
@ -95,6 +130,7 @@ class functional(simulation):
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
|
|
||||||
# Perform a random sequence of writes and reads on random ports, using random addresses and random words
|
# Perform a random sequence of writes and reads on random ports, using random addresses and random words
|
||||||
|
# and random write masks (if applicable)
|
||||||
for i in range(self.num_cycles):
|
for i in range(self.num_cycles):
|
||||||
w_addrs = []
|
w_addrs = []
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
|
|
@ -112,11 +148,12 @@ class functional(simulation):
|
||||||
elif op == "write":
|
elif op == "write":
|
||||||
addr = self.gen_addr()
|
addr = self.gen_addr()
|
||||||
word = self.gen_data()
|
word = self.gen_data()
|
||||||
|
wmask = self.gen_wmask()
|
||||||
# two ports cannot write to the same address
|
# two ports cannot write to the same address
|
||||||
if addr in w_addrs:
|
if addr in w_addrs:
|
||||||
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
|
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
|
||||||
else:
|
else:
|
||||||
comment = self.gen_cycle_comment("write", word, addr, port, self.t_current)
|
comment = self.gen_cycle_comment("write", word, addr, port, wmask, self.t_current)
|
||||||
self.add_write_one_port(comment, addr, word, port)
|
self.add_write_one_port(comment, addr, word, port)
|
||||||
self.stored_words[addr] = word
|
self.stored_words[addr] = word
|
||||||
w_addrs.append(addr)
|
w_addrs.append(addr)
|
||||||
|
|
@ -126,7 +163,7 @@ class functional(simulation):
|
||||||
if addr in w_addrs:
|
if addr in w_addrs:
|
||||||
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
|
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
|
||||||
else:
|
else:
|
||||||
comment = self.gen_cycle_comment("read", word, addr, port, self.t_current)
|
comment = self.gen_cycle_comment("read", word, addr, port, self.wmask, self.t_current)
|
||||||
self.add_read_one_port(comment, addr, rw_read_din_data, port)
|
self.add_read_one_port(comment, addr, rw_read_din_data, port)
|
||||||
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
|
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
|
||||||
check += 1
|
check += 1
|
||||||
|
|
@ -135,7 +172,7 @@ class functional(simulation):
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
|
|
||||||
# Last cycle idle needed to correctly measure the value on the second to last clock edge
|
# Last cycle idle needed to correctly measure the value on the second to last clock edge
|
||||||
comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, 0, self.t_current)
|
comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, 0, self.wmask, self.t_current)
|
||||||
self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size)
|
self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size)
|
||||||
|
|
||||||
def read_stim_results(self):
|
def read_stim_results(self):
|
||||||
|
|
@ -171,6 +208,13 @@ class functional(simulation):
|
||||||
return(0, error)
|
return(0, error)
|
||||||
return(1, "SUCCESS")
|
return(1, "SUCCESS")
|
||||||
|
|
||||||
|
def gen_wmask(self):
|
||||||
|
wmask_bits = [None]*self.num_wmask
|
||||||
|
for bit in range(self.num_wmask):
|
||||||
|
rand = random.randint(0, 1)
|
||||||
|
wmask_bits[bit] = rand
|
||||||
|
return wmask_bits
|
||||||
|
|
||||||
def gen_data(self):
|
def gen_data(self):
|
||||||
""" Generates a random word to write. """
|
""" Generates a random word to write. """
|
||||||
rand = random.randint(0,(2**self.word_size)-1)
|
rand = random.randint(0,(2**self.word_size)-1)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@ class simulation():
|
||||||
self.readwrite_ports = self.sram.readwrite_ports
|
self.readwrite_ports = self.sram.readwrite_ports
|
||||||
self.read_ports = self.sram.read_ports
|
self.read_ports = self.sram.read_ports
|
||||||
self.write_ports = self.sram.write_ports
|
self.write_ports = self.sram.write_ports
|
||||||
|
self.num_wmask = int(self.word_size/self.write_size)
|
||||||
|
self.wmask_enabled = False
|
||||||
|
if self.word_size !=self.write_size:
|
||||||
|
self.wmask_enabled = True
|
||||||
|
|
||||||
def set_corner(self,corner):
|
def set_corner(self,corner):
|
||||||
""" Set the corner values """
|
""" Set the corner values """
|
||||||
|
|
@ -127,7 +131,7 @@ class simulation():
|
||||||
debug.error("Non-binary address string",1)
|
debug.error("Non-binary address string",1)
|
||||||
bit -= 1
|
bit -= 1
|
||||||
|
|
||||||
def add_write(self, comment, address, data, 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, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports))
|
debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports))
|
||||||
debug.info(2, comment)
|
debug.info(2, comment)
|
||||||
|
|
@ -223,12 +227,22 @@ class simulation():
|
||||||
time_spacing,
|
time_spacing,
|
||||||
comment))
|
comment))
|
||||||
|
|
||||||
def gen_cycle_comment(self, op, word, addr, port, t_current):
|
def gen_cycle_comment(self, op, word, addr, port, wmask, t_current):
|
||||||
if op == "noop":
|
if op == "noop":
|
||||||
comment = "\tIdle during cycle {0} ({1}ns - {2}ns)".format(int(t_current/self.period),
|
comment = "\tIdle during cycle {0} ({1}ns - {2}ns)".format(int(t_current/self.period),
|
||||||
t_current,
|
t_current,
|
||||||
t_current+self.period)
|
t_current+self.period)
|
||||||
elif op == "write":
|
elif op == "write":
|
||||||
|
if (self.wmask_enabled):
|
||||||
|
comment = "\tWriting {0} to address {1} with mask bit {0} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
|
||||||
|
addr,
|
||||||
|
wmask,
|
||||||
|
port,
|
||||||
|
int(
|
||||||
|
t_current / self.period),
|
||||||
|
t_current + self.period)
|
||||||
|
|
||||||
|
else:
|
||||||
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
|
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
|
||||||
addr,
|
addr,
|
||||||
port,
|
port,
|
||||||
|
|
@ -268,7 +282,9 @@ class simulation():
|
||||||
if (port in read_index) and (port in write_index):
|
if (port in read_index) and (port in write_index):
|
||||||
pin_names.append("WEB{0}".format(port))
|
pin_names.append("WEB{0}".format(port))
|
||||||
if (self.write_size != self.word_size):
|
if (self.write_size != self.word_size):
|
||||||
pin_names.append("WMASK{0}".format(port))
|
num_wmask = int(self.word_size/self.write_size)
|
||||||
|
for bit in range(num_wmask):
|
||||||
|
pin_names.append("WMASK{0}_{1}".format(port,bit))
|
||||||
|
|
||||||
for port in range(total_ports):
|
for port in range(total_ports):
|
||||||
pin_names.append("{0}{1}".format(tech.spice["clk"], port))
|
pin_names.append("{0}{1}".format(tech.spice["clk"], port))
|
||||||
|
|
|
||||||
|
|
@ -759,8 +759,6 @@ class control_logic(design.design):
|
||||||
|
|
||||||
def route_dffs(self):
|
def route_dffs(self):
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
#print("hi")
|
|
||||||
#if (self.word_size == self.write_size):
|
|
||||||
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
|
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
|
||||||
elif self.port_type == "r":
|
elif self.port_type == "r":
|
||||||
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
|
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,12 @@ class multibank(design.design):
|
||||||
words_per_row=self.words_per_row)
|
words_per_row=self.words_per_row)
|
||||||
self.add_mod(self.sense_amp_array)
|
self.add_mod(self.sense_amp_array)
|
||||||
|
|
||||||
|
if (self.write_size != self.word_size):
|
||||||
|
self.write_mask_driver_array = self.mod_write_mask_driver_array(columns=self.num_cols,
|
||||||
|
word_size=self.word_size,
|
||||||
|
write_size=self.write_size)
|
||||||
|
self.add_mod(self.write_mask_driver_array)
|
||||||
|
else:
|
||||||
self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols,
|
self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols,
|
||||||
word_size=self.word_size)
|
word_size=self.word_size)
|
||||||
self.add_mod(self.write_driver_array)
|
self.add_mod(self.write_driver_array)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ class port_data(design.design):
|
||||||
|
|
||||||
sram_config.set_local_config(self)
|
sram_config.set_local_config(self)
|
||||||
self.port = port
|
self.port = port
|
||||||
|
self.num_wmask = int(self.word_size/self.write_size)
|
||||||
|
|
||||||
if name == "":
|
if name == "":
|
||||||
name = "port_data_{0}".format(self.port)
|
name = "port_data_{0}".format(self.port)
|
||||||
|
|
@ -53,6 +54,8 @@ class port_data(design.design):
|
||||||
|
|
||||||
if self.write_driver_array:
|
if self.write_driver_array:
|
||||||
self.create_write_driver_array()
|
self.create_write_driver_array()
|
||||||
|
if (self.word_size != self.write_size):
|
||||||
|
self.create_write_mask_array()
|
||||||
else:
|
else:
|
||||||
self.write_driver_array_inst = None
|
self.write_driver_array_inst = None
|
||||||
|
|
||||||
|
|
@ -164,6 +167,12 @@ class port_data(design.design):
|
||||||
columns=self.num_cols,
|
columns=self.num_cols,
|
||||||
word_size=self.word_size)
|
word_size=self.word_size)
|
||||||
self.add_mod(self.write_driver_array)
|
self.add_mod(self.write_driver_array)
|
||||||
|
if (self.word_size != self.write_size):
|
||||||
|
self.write_mask_array = factory.create(module_type="write_mask_array",
|
||||||
|
columns=self.num_cols,
|
||||||
|
word_size=self.word_size,
|
||||||
|
write_size=self.write_size)
|
||||||
|
self.add_mod(self.write_driver_array)
|
||||||
else:
|
else:
|
||||||
self.write_driver_array = None
|
self.write_driver_array = None
|
||||||
|
|
||||||
|
|
@ -280,6 +289,18 @@ class port_data(design.design):
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
|
|
||||||
|
def create_write_mask_array(self):
|
||||||
|
""" Creating Write Masks """
|
||||||
|
self.write_mask_array_inst = self.add_inst(name="write_mask_array{}".format(self.port),
|
||||||
|
mod=self.write_mask_array)
|
||||||
|
|
||||||
|
temp = []
|
||||||
|
for bit in range(self.num_wmask):
|
||||||
|
temp.append("write_mask_".format(bit))
|
||||||
|
temp.extend(["w_en", "vdd", "gnd"])
|
||||||
|
self.connect_inst(temp)
|
||||||
|
|
||||||
|
|
||||||
def place_write_driver_array(self, offset):
|
def place_write_driver_array(self, offset):
|
||||||
""" Placing Write Driver """
|
""" Placing Write Driver """
|
||||||
self.write_driver_array_inst.place(offset=offset, mirror="MX")
|
self.write_driver_array_inst.place(offset=offset, mirror="MX")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
from math import log
|
||||||
|
import design
|
||||||
|
from tech import drc
|
||||||
|
import debug
|
||||||
|
from sram_factory import factory
|
||||||
|
from vector import vector
|
||||||
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
|
class write_mask_array(design.design):
|
||||||
|
"""
|
||||||
|
Array of tristate drivers to write to the bitlines through the column mux.
|
||||||
|
Dynamically generated write driver array of all bitlines.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, columns, word_size, write_size):
|
||||||
|
design.design.__init__(self, name)
|
||||||
|
debug.info(1, "Creating {0}".format(self.name))
|
||||||
|
self.add_comment("columns: {0}".format(columns))
|
||||||
|
self.add_comment("word_size {0}".format(word_size))
|
||||||
|
self.add_comment("write_size {0}".format(write_size))
|
||||||
|
|
||||||
|
self.columns = columns
|
||||||
|
self.word_size = word_size
|
||||||
|
self.write_size = write_size
|
||||||
|
self.words_per_row = int(columns / word_size)
|
||||||
|
self.num_wmask = int(word_size / write_size)
|
||||||
|
|
||||||
|
self.create_netlist()
|
||||||
|
# if not OPTS.netlist_only:
|
||||||
|
# self.create_layout()
|
||||||
|
|
||||||
|
def create_netlist(self):
|
||||||
|
self.add_modules()
|
||||||
|
self.add_pins()
|
||||||
|
self.create_write_mask_array()
|
||||||
|
self.create_and2_array()
|
||||||
|
|
||||||
|
|
||||||
|
# def create_layout(self):
|
||||||
|
#
|
||||||
|
# if self.bitcell.width > self.driver.width:
|
||||||
|
# self.width = self.columns * self.bitcell.width
|
||||||
|
# else:
|
||||||
|
# self.width = self.columns * self.driver.width
|
||||||
|
#
|
||||||
|
# self.height = self.driver.height
|
||||||
|
#
|
||||||
|
# self.place_write_array()
|
||||||
|
# self.add_layout_pins()
|
||||||
|
# self.add_boundary()
|
||||||
|
# self.DRC_LVS()
|
||||||
|
|
||||||
|
def add_pins(self):
|
||||||
|
for bit in range(self.num_wmask):
|
||||||
|
self.add_pin("wdriver_sel_{}".format(bit))
|
||||||
|
self.add_pin("en")
|
||||||
|
self.add_pin("vdd")
|
||||||
|
self.add_pin("gnd")
|
||||||
|
|
||||||
|
def add_modules(self):
|
||||||
|
self.wmask = factory.create(module_type="dff_buf")
|
||||||
|
self.add_mod(self.wmask)
|
||||||
|
dff_height = self.wmask.height
|
||||||
|
|
||||||
|
self.and2 = factory.create(module_type="pand2",
|
||||||
|
size=4,
|
||||||
|
height=dff_height)
|
||||||
|
self.add_mod(self.and2)
|
||||||
|
|
||||||
|
|
||||||
|
def create_write_mask_array(self):
|
||||||
|
self.wmask_insts = {}
|
||||||
|
for bit in range(self.num_wmask):
|
||||||
|
name = "write_mask_{}".format(bit)
|
||||||
|
self.wmask_insts[bit] = self.add_inst(name=name,
|
||||||
|
mod=self.wmask)
|
||||||
|
|
||||||
|
self.connect_inst(["wmask_{}".format(bit),
|
||||||
|
"bank_wmask_{}".format(bit),
|
||||||
|
"bank_wmask_bar_{}".format(bit),
|
||||||
|
"clk", "vdd", "gnd"])
|
||||||
|
|
||||||
|
def create_and2_array(self):
|
||||||
|
self.and2_insts = {}
|
||||||
|
for bit in range(self.num_wmask):
|
||||||
|
name = "and2_{}".format(bit)
|
||||||
|
self.and2_insts[bit] = self.add_inst(name=name,
|
||||||
|
mod=self.and2)
|
||||||
|
self.connect_inst(["bank_wmask_{}".format(bit),
|
||||||
|
"en",
|
||||||
|
"wdriver_sel_{}".format(bit),
|
||||||
|
"vdd", "gnd"])
|
||||||
|
|
||||||
|
|
||||||
|
# def place_write_array(self):
|
||||||
|
# if self.bitcell.width > self.driver.width:
|
||||||
|
# driver_spacing = self.bitcell.width
|
||||||
|
# else:
|
||||||
|
# driver_spacing = self.driver.width
|
||||||
|
#
|
||||||
|
# for i in range(0, self.columns, self.words_per_row):
|
||||||
|
# index = int(i / self.words_per_row)
|
||||||
|
# base = vector(i * driver_spacing, 0)
|
||||||
|
# self.driver_insts[index].place(base)
|
||||||
|
|
||||||
|
# def add_layout_pins(self):
|
||||||
|
# for i in range(self.word_size):
|
||||||
|
# din_pin = self.driver_insts[i].get_pin("din")
|
||||||
|
# self.add_layout_pin(text="data_{0}".format(i),
|
||||||
|
# layer="metal2",
|
||||||
|
# offset=din_pin.ll(),
|
||||||
|
# width=din_pin.width(),
|
||||||
|
# height=din_pin.height())
|
||||||
|
# bl_pin = self.driver_insts[i].get_pin("bl")
|
||||||
|
# self.add_layout_pin(text="bl_{0}".format(i),
|
||||||
|
# layer="metal2",
|
||||||
|
# offset=bl_pin.ll(),
|
||||||
|
# width=bl_pin.width(),
|
||||||
|
# height=bl_pin.height())
|
||||||
|
#
|
||||||
|
# br_pin = self.driver_insts[i].get_pin("br")
|
||||||
|
# self.add_layout_pin(text="br_{0}".format(i),
|
||||||
|
# layer="metal2",
|
||||||
|
# offset=br_pin.ll(),
|
||||||
|
# width=br_pin.width(),
|
||||||
|
# height=br_pin.height())
|
||||||
|
#
|
||||||
|
# for n in ["vdd", "gnd"]:
|
||||||
|
# pin_list = self.driver_insts[i].get_pins(n)
|
||||||
|
# for pin in pin_list:
|
||||||
|
# pin_pos = pin.center()
|
||||||
|
# # Add the M2->M3 stack
|
||||||
|
# self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||||
|
# offset=pin_pos)
|
||||||
|
# self.add_layout_pin_rect_center(text=n,
|
||||||
|
# layer="metal3",
|
||||||
|
# offset=pin_pos)
|
||||||
|
#
|
||||||
|
# self.add_layout_pin(text="en",
|
||||||
|
# layer="metal1",
|
||||||
|
# offset=self.driver_insts[0].get_pin("en").ll().scale(0, 1),
|
||||||
|
# width=self.width,
|
||||||
|
# height=drc('minwidth_metal1'))
|
||||||
|
|
||||||
|
# def get_w_en_cin(self):
|
||||||
|
# """Get the relative capacitance of all the enable connections in the bank"""
|
||||||
|
# # The enable is connected to a nand2 for every row.
|
||||||
|
# return self.driver.get_w_en_cin() * len(self.driver_insts)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -128,9 +128,9 @@ class sram_1bank(sram_base):
|
||||||
# Add the write mask flops to the left of the din flops.
|
# Add the write mask flops to the left of the din flops.
|
||||||
if (self.write_size != self.word_size):
|
if (self.write_size != self.word_size):
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
wmask_pos[port] = vector(self.bank.bank_array_ll.x - self.control_logic_insts[port].width,
|
||||||
self.bank.height + max_gap_size + self.data_dff_insts[port].height)
|
-max_gap_size - self.wmask_dff_insts[port].height)
|
||||||
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
|
self.wmask_dff_insts[port].place(wmask_pos[port])
|
||||||
|
|
||||||
|
|
||||||
if len(self.all_ports)>1:
|
if len(self.all_ports)>1:
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ class sram_base(design, verilog, lef):
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
""" Add pins for entire SRAM. """
|
""" Add pins for entire SRAM. """
|
||||||
|
self.num_masks = int(self.word_size/self.write_size)
|
||||||
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.add_pin("DIN{0}[{1}]".format(port,bit),"INPUT")
|
self.add_pin("DIN{0}[{1}]".format(port,bit),"INPUT")
|
||||||
|
|
@ -71,7 +72,8 @@ class sram_base(design, verilog, lef):
|
||||||
# add the optional write mask pins
|
# add the optional write mask pins
|
||||||
if self.word_size != self.write_size:
|
if self.word_size != self.write_size:
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
self.add_pin("wmask{}".format(port),"INPUT")
|
for bit in range(self.num_masks):
|
||||||
|
self.add_pin("wmask{0}[{1}]".format(port,bit),"INPUT")
|
||||||
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.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT")
|
self.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT")
|
||||||
|
|
@ -278,7 +280,9 @@ class sram_base(design, verilog, lef):
|
||||||
self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size)
|
self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size)
|
||||||
self.add_mod(self.data_dff)
|
self.add_mod(self.data_dff)
|
||||||
|
|
||||||
self.wmask_dff = dff_array(name="wmask_dff", rows=1, columns=int(self.word_size/self.write_size))
|
if (self.write_size != self.word_size):
|
||||||
|
num_wmask = int(self.word_size/self.write_size)
|
||||||
|
self.wmask_dff = dff_array(name="wmask_dff", rows=1, columns=num_wmask)
|
||||||
self.add_mod(self.wmask_dff)
|
self.add_mod(self.wmask_dff)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -469,7 +473,7 @@ class sram_base(design, verilog, lef):
|
||||||
outputs = []
|
outputs = []
|
||||||
for bit in range(num_wmask):
|
for bit in range(num_wmask):
|
||||||
inputs.append("wmask{}[{}]".format(port, bit))
|
inputs.append("wmask{}[{}]".format(port, bit))
|
||||||
outputs.append("BANK_WMASK{}[{}]".format(port, bit))
|
outputs.append("bank_wmask{}[{}]".format(port, bit))
|
||||||
|
|
||||||
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 sram_wmask_func_test")
|
||||||
|
class sram_wmask_func_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||||
|
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=8,
|
||||||
|
num_words=16,
|
||||||
|
write_size=4,
|
||||||
|
num_banks=1)
|
||||||
|
c.words_per_row=4
|
||||||
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} bit writes, {} banks".format(c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.write_size,
|
||||||
|
c.num_banks))
|
||||||
|
s = factory.create(module_type="sram", sram_config=c)
|
||||||
|
tempspice = OPTS.openram_temp + "temp.sp"
|
||||||
|
s.sp_write(tempspice)
|
||||||
|
|
||||||
|
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||||
|
|
||||||
|
f = functional(s.s, tempspice, corner)
|
||||||
|
f.num_cycles = 10
|
||||||
|
(fail, error) = f.run()
|
||||||
|
self.assertTrue(fail, error)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# 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