mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into multiport_characterization
This commit is contained in:
commit
0bb4b48439
|
|
@ -49,6 +49,10 @@ class geometry:
|
|||
ll = vector(min(first[0],second[0]),min(first[1],second[1]))
|
||||
ur = vector(max(first[0],second[0]),max(first[1],second[1]))
|
||||
self.boundary=[ll,ur]
|
||||
|
||||
def update_boundary(self):
|
||||
""" Update the boundary with a new placement. """
|
||||
self.compute_boundary(self.offset,self.mirror,self.rotate)
|
||||
|
||||
def compute_boundary(self,offset=vector(0,0),mirror="",rotate=0):
|
||||
""" Transform with offset, mirror and rotation to get the absolute pin location.
|
||||
|
|
@ -124,7 +128,7 @@ class instance(geometry):
|
|||
An instance of an instance/module with a specified location and
|
||||
rotation
|
||||
"""
|
||||
def __init__(self, name, mod, offset, mirror, rotate):
|
||||
def __init__(self, name, mod, offset=[0,0], mirror="R0", rotate=0):
|
||||
"""Initializes an instance to represent a module"""
|
||||
geometry.__init__(self)
|
||||
debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.")
|
||||
|
|
|
|||
|
|
@ -118,6 +118,16 @@ class layout(lef.lef):
|
|||
for pin in pin_list:
|
||||
pin.rect = [pin.ll() - offset, pin.ur() - offset]
|
||||
|
||||
def place_inst(self, name, offset, mirror="R0", rotate=0):
|
||||
""" This updates the placement of an instance. """
|
||||
inst = self.get_inst(name)
|
||||
debug.info(3, "placing instance {}".format(inst))
|
||||
# Update the placement of an already added instance
|
||||
inst.offset = offset
|
||||
inst.mirror = mirror
|
||||
inst.rotate = rotate
|
||||
inst.update_boundary()
|
||||
return inst
|
||||
|
||||
def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0):
|
||||
"""Adds an instance of a mod to this module"""
|
||||
|
|
|
|||
|
|
@ -91,19 +91,26 @@ class spice(verilog.verilog):
|
|||
group of modules are generated."""
|
||||
|
||||
if (check and (len(self.insts[-1].mod.pins) != len(args))):
|
||||
debug.error("Connections: {}".format(self.insts[-1].mod.pins))
|
||||
debug.error("Connections: {}".format(args))
|
||||
import pprint
|
||||
modpins_string=pprint.pformat(self.insts[-1].mod.pins)
|
||||
argpins_string=pprint.pformat(args)
|
||||
debug.error("Connections: {}".format(modpins_string))
|
||||
debug.error("Connections: {}".format(argpins_string))
|
||||
debug.error("Number of net connections ({0}) does not match last instance ({1})".format(len(self.insts[-1].mod.pins),
|
||||
len(args)), 1)
|
||||
self.conns.append(args)
|
||||
|
||||
if check and (len(self.insts)!=len(self.conns)):
|
||||
import pprint
|
||||
insts_string=pprint.pformat(self.insts)
|
||||
conns_string=pprint.pformat(self.conns)
|
||||
|
||||
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
|
||||
len(self.insts),
|
||||
len(self.conns)))
|
||||
debug.error("Instances: \n"+str(self.insts))
|
||||
debug.error("Instances: \n"+str(insts_string))
|
||||
debug.error("-----")
|
||||
debug.error("Connections: \n"+str(self.conns),1)
|
||||
debug.error("Connections: \n"+str(conns_string),1)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -204,6 +204,11 @@ def read_config(config_file, is_unit_test=True):
|
|||
|
||||
OPTS.is_unit_test=is_unit_test
|
||||
|
||||
# If we are only generating a netlist, we can't do DRC/LVS
|
||||
if OPTS.netlist_only:
|
||||
OPTS.check_lvsdrc=False
|
||||
|
||||
|
||||
# If config didn't set output name, make a reasonable default.
|
||||
if (OPTS.output_name == ""):
|
||||
OPTS.output_name = "sram_{0}rw_{1}b_{2}w_{3}bank_{4}".format(OPTS.rw_ports,
|
||||
|
|
@ -372,6 +377,9 @@ def report_status():
|
|||
print("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
|
||||
OPTS.num_words,
|
||||
OPTS.num_banks))
|
||||
if OPTS.netlist_only:
|
||||
print("Netlist only mode (no physical design is being done).")
|
||||
|
||||
if not OPTS.check_lvsdrc:
|
||||
print("DRC/LVS/PEX checking is disabled.")
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ class bank(design.design):
|
|||
self.num_words = num_words
|
||||
self.words_per_row = words_per_row
|
||||
self.num_banks = num_banks
|
||||
|
||||
self.total_write = OPTS.rw_ports + OPTS.w_ports
|
||||
self.total_read = OPTS.rw_ports + OPTS.r_ports
|
||||
self.total_ports = OPTS.rw_ports + OPTS.w_ports + OPTS.r_ports
|
||||
|
||||
# The local control signals are gated when we have bank select logic,
|
||||
# so this prefix will be added to all of the input signals to create
|
||||
|
|
@ -50,41 +54,51 @@ class bank(design.design):
|
|||
self.prefix="gated_"
|
||||
else:
|
||||
self.prefix=""
|
||||
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.compute_sizes()
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.add_modules()
|
||||
self.setup_layout_constraints()
|
||||
self.create_modules()
|
||||
|
||||
# FIXME: Move this to the add modules function
|
||||
self.add_bank_select()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_modules()
|
||||
self.setup_routing_constraints()
|
||||
self.route_layout()
|
||||
|
||||
|
||||
# Can remove the following, but it helps for debug!
|
||||
self.add_lvs_correspondence_points()
|
||||
#self.add_lvs_correspondence_points()
|
||||
|
||||
# Remember the bank center for further placement
|
||||
self.bank_center=self.offset_all_coordinates().scale(-1,-1)
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
""" Adding pins for Bank module"""
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("dout[{0}]".format(i),"OUT")
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("din[{0}]".format(i),"IN")
|
||||
for i in range(self.addr_size):
|
||||
self.add_pin("addr[{0}]".format(i),"INPUT")
|
||||
for k in range(self.total_read):
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("dout{0}[{1}]".format(k,i),"OUT")
|
||||
for k in range(self.total_write):
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("din{0}[{1}]".format(k,i),"IN")
|
||||
for k in range(self.total_ports):
|
||||
for i in range(self.addr_size):
|
||||
self.add_pin("addr{0}[{1}]".format(k,i),"INPUT")
|
||||
|
||||
# For more than one bank, we have a bank select and name
|
||||
# the signals gated_*.
|
||||
if self.num_banks > 1:
|
||||
self.add_pin("bank_sel","INPUT")
|
||||
for pin in ["s_en","w_en","clk_buf_bar","clk_buf"]:
|
||||
for k in range(self.total_read):
|
||||
self.add_pin("s_en{0}".format(k), "INPUT")
|
||||
for k in range(self.total_write):
|
||||
self.add_pin("w_en{0}".format(k), "INPUT")
|
||||
for pin in ["clk_buf_bar","clk_buf"]:
|
||||
self.add_pin(pin,"INPUT")
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
|
@ -106,25 +120,46 @@ class bank(design.design):
|
|||
|
||||
self.route_vdd_gnd()
|
||||
|
||||
def add_modules(self):
|
||||
def create_modules(self):
|
||||
""" Add modules. The order should not matter! """
|
||||
|
||||
# Above the bitcell array
|
||||
self.add_bitcell_array()
|
||||
self.add_precharge_array()
|
||||
self.create_bitcell_array()
|
||||
self.create_precharge_array()
|
||||
|
||||
# Below the bitcell array
|
||||
self.add_column_mux_array()
|
||||
self.add_sense_amp_array()
|
||||
self.add_write_driver_array()
|
||||
self.create_column_mux_array()
|
||||
self.create_sense_amp_array()
|
||||
self.create_write_driver_array()
|
||||
|
||||
# To the left of the bitcell array
|
||||
self.add_row_decoder()
|
||||
self.add_wordline_driver()
|
||||
self.add_column_decoder()
|
||||
self.create_row_decoder()
|
||||
self.create_wordline_driver()
|
||||
self.create_column_decoder()
|
||||
|
||||
self.create_bank_select()
|
||||
|
||||
|
||||
def place_modules(self):
|
||||
""" Add modules. The order should not matter! """
|
||||
|
||||
# Above the bitcell array
|
||||
self.place_bitcell_array()
|
||||
self.place_precharge_array()
|
||||
|
||||
# Below the bitcell array
|
||||
self.place_column_mux_array()
|
||||
self.place_sense_amp_array()
|
||||
self.place_write_driver_array()
|
||||
|
||||
# To the left of the bitcell array
|
||||
self.place_row_decoder()
|
||||
self.place_wordline_driver()
|
||||
self.place_column_decoder()
|
||||
|
||||
|
||||
self.place_bank_select()
|
||||
|
||||
def compute_sizes(self):
|
||||
""" Computes the required sizes to create the bank """
|
||||
|
||||
|
|
@ -146,7 +181,8 @@ class bank(design.design):
|
|||
# Number of control lines in the bus
|
||||
self.num_control_lines = 4
|
||||
# The order of the control signals on the control bus:
|
||||
self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en"]
|
||||
self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"]
|
||||
|
||||
# These will be outputs of the gaters if this is multibank, if not, normal signals.
|
||||
if self.num_banks > 1:
|
||||
self.control_signals = ["gated_"+str for str in self.input_control_signals]
|
||||
|
|
@ -167,17 +203,31 @@ class bank(design.design):
|
|||
2*self.m2_pitch)
|
||||
|
||||
|
||||
|
||||
def create_modules(self):
|
||||
def add_modules(self):
|
||||
""" Create all the modules using the class loader """
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
|
||||
rows=self.num_rows)
|
||||
self.add_mod(self.bitcell_array)
|
||||
|
||||
# create arrays of bitline and bitline_bar names for read, write, or all ports
|
||||
self.read_bl_list = self.bitcell.list_read_bl_names()
|
||||
self.read_br_list = self.bitcell.list_read_br_names()
|
||||
|
||||
self.write_bl_list = self.bitcell.list_write_bl_names()
|
||||
self.write_br_list = self.bitcell.list_write_br_names()
|
||||
|
||||
self.total_bl_list = self.bitcell.list_all_bl_names()
|
||||
self.total_br_list = self.bitcell.list_all_br_names()
|
||||
|
||||
self.total_wl_list = self.bitcell.list_all_wl_names()
|
||||
self.total_bitline_list = self.bitcell.list_all_bitline_names()
|
||||
|
||||
self.precharge_array = self.mod_precharge_array(columns=self.num_cols)
|
||||
self.add_mod(self.precharge_array)
|
||||
self.precharge_array = []
|
||||
for k in range(self.total_read):
|
||||
self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.read_bl_list[k], bitcell_br=self.read_br_list[k]))
|
||||
self.add_mod(self.precharge_array[k])
|
||||
|
||||
if self.col_addr_size > 0:
|
||||
self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols,
|
||||
|
|
@ -207,171 +257,219 @@ class bank(design.design):
|
|||
self.add_mod(self.bank_select)
|
||||
|
||||
|
||||
def add_bitcell_array(self):
|
||||
""" Adding Bitcell Array """
|
||||
def create_bitcell_array(self):
|
||||
""" Creating Bitcell Array """
|
||||
|
||||
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
||||
mod=self.bitcell_array,
|
||||
offset=vector(0,0))
|
||||
mod=self.bitcell_array)
|
||||
|
||||
|
||||
temp = []
|
||||
for i in range(self.num_cols):
|
||||
temp.append("bl[{0}]".format(i))
|
||||
temp.append("br[{0}]".format(i))
|
||||
for j in range(self.num_rows):
|
||||
temp.append("wl[{0}]".format(j))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
for col in range(self.num_cols):
|
||||
for bitline in self.total_bitline_list:
|
||||
temp.append(bitline+"[{0}]".format(col))
|
||||
for row in range(self.num_rows):
|
||||
for wordline in self.total_wl_list:
|
||||
temp.append(wordline+"[{0}]".format(row))
|
||||
temp.append("vdd")
|
||||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
||||
def place_bitcell_array(self):
|
||||
""" Placing Bitcell Array """
|
||||
self.place_inst(name="bitcell_array",
|
||||
offset=vector(0,0))
|
||||
|
||||
def add_precharge_array(self):
|
||||
""" Adding Precharge """
|
||||
|
||||
def create_precharge_array(self):
|
||||
""" Creating Precharge """
|
||||
|
||||
# The wells must be far enough apart
|
||||
# The enclosure is for the well and the spacing is to the bitcell wells
|
||||
y_offset = self.bitcell_array.height + self.m2_gap
|
||||
self.precharge_array_inst=self.add_inst(name="precharge_array",
|
||||
mod=self.precharge_array,
|
||||
offset=vector(0,y_offset))
|
||||
temp = []
|
||||
for i in range(self.num_cols):
|
||||
temp.append("bl[{0}]".format(i))
|
||||
temp.append("br[{0}]".format(i))
|
||||
temp.extend([self.prefix+"clk_buf_bar", "vdd"])
|
||||
self.connect_inst(temp)
|
||||
self.precharge_array_inst = []
|
||||
for k in range(self.total_read):
|
||||
self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(k),
|
||||
mod=self.precharge_array[k]))
|
||||
temp = []
|
||||
for i in range(self.num_cols):
|
||||
temp.append(self.read_bl_list[k]+"[{0}]".format(i))
|
||||
temp.append(self.read_br_list[k]+"[{0}]".format(i))
|
||||
temp.extend([self.prefix+"clk_buf_bar", "vdd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
def add_column_mux_array(self):
|
||||
""" Adding Column Mux when words_per_row > 1 . """
|
||||
def place_precharge_array(self):
|
||||
""" Placing Precharge """
|
||||
|
||||
# FIXME: place for multiport
|
||||
for k in range(self.total_read):
|
||||
# The wells must be far enough apart
|
||||
# The enclosure is for the well and the spacing is to the bitcell wells
|
||||
y_offset = self.bitcell_array.height + self.m2_gap
|
||||
self.place_inst(name=self.precharge_array_inst[k].name,
|
||||
offset=vector(0,y_offset))
|
||||
|
||||
def create_column_mux_array(self):
|
||||
""" Creating Column Mux when words_per_row > 1 . """
|
||||
if self.col_addr_size == 0:
|
||||
return
|
||||
|
||||
self.col_mux_array_inst = []
|
||||
for k in range(self.total_ports):
|
||||
self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(k),
|
||||
mod=self.column_mux_array))
|
||||
|
||||
temp = []
|
||||
for i in range(self.num_cols):
|
||||
temp.append(self.total_bl_list[k]+"[{0}]".format(i))
|
||||
temp.append(self.total_br_list[k]+"[{0}]".format(i))
|
||||
for h in range(self.words_per_row):
|
||||
temp.append("sel{0}[{1}]".format(k,h))
|
||||
for j in range(self.word_size):
|
||||
temp.append(self.total_bl_list[k]+"_out[{0}]".format(j))
|
||||
temp.append(self.total_br_list[k]+"_out[{0}]".format(j))
|
||||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
||||
def place_column_mux_array(self):
|
||||
""" Placing Column Mux when words_per_row > 1 . """
|
||||
if self.col_addr_size > 0:
|
||||
self.column_mux_height = self.column_mux_array.height + self.m2_gap
|
||||
else:
|
||||
self.column_mux_height = 0
|
||||
return
|
||||
|
||||
y_offset = self.column_mux_height
|
||||
self.col_mux_array_inst=self.add_inst(name="column_mux_array",
|
||||
mod=self.column_mux_array,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
temp = []
|
||||
for i in range(self.num_cols):
|
||||
temp.append("bl[{0}]".format(i))
|
||||
temp.append("br[{0}]".format(i))
|
||||
for k in range(self.words_per_row):
|
||||
temp.append("sel[{0}]".format(k))
|
||||
for j in range(self.word_size):
|
||||
temp.append("bl_out[{0}]".format(j))
|
||||
temp.append("br_out[{0}]".format(j))
|
||||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
for k in range(self.total_ports):
|
||||
y_offset = self.column_mux_height
|
||||
self.place_inst(name=self.col_mux_array_inst[k].name,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
|
||||
def create_sense_amp_array(self):
|
||||
""" Creating Sense amp """
|
||||
|
||||
def add_sense_amp_array(self):
|
||||
""" Adding Sense amp """
|
||||
self.sense_amp_array_inst = []
|
||||
for k in range(self.total_read):
|
||||
self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(k),
|
||||
mod=self.sense_amp_array))
|
||||
|
||||
y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap
|
||||
self.sense_amp_array_inst=self.add_inst(name="sense_amp_array",
|
||||
mod=self.sense_amp_array,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
temp = []
|
||||
for i in range(self.word_size):
|
||||
temp.append("dout[{0}]".format(i))
|
||||
if self.words_per_row == 1:
|
||||
temp.append("bl[{0}]".format(i))
|
||||
temp.append("br[{0}]".format(i))
|
||||
else:
|
||||
temp.append("bl_out[{0}]".format(i))
|
||||
temp.append("br_out[{0}]".format(i))
|
||||
|
||||
temp.extend([self.prefix+"s_en", "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
temp = []
|
||||
for i in range(self.word_size):
|
||||
temp.append("dout{0}[{1}]".format(k,i))
|
||||
if self.words_per_row == 1:
|
||||
temp.append(self.read_bl_list[k]+"[{0}]".format(i))
|
||||
temp.append(self.read_br_list[k]+"[{0}]".format(i))
|
||||
else:
|
||||
temp.append(self.read_bl_list[k]+"_out[{0}]".format(i))
|
||||
temp.append(self.read_br_list[k]+"_out[{0}]".format(i))
|
||||
|
||||
temp.extend([self.prefix+"s_en{0}".format(k), "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
def add_write_driver_array(self):
|
||||
""" Adding Write Driver """
|
||||
def place_sense_amp_array(self):
|
||||
""" Placing Sense amp """
|
||||
|
||||
y_offset = self.sense_amp_array.height + self.column_mux_height \
|
||||
+ self.m2_gap + self.write_driver_array.height
|
||||
self.write_driver_array_inst=self.add_inst(name="write_driver_array",
|
||||
mod=self.write_driver_array,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
# FIXME: place for multiport
|
||||
for k in range(self.total_read):
|
||||
y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap
|
||||
self.place_inst(name=self.sense_amp_array_inst[k].name,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
|
||||
def create_write_driver_array(self):
|
||||
""" Creating Write Driver """
|
||||
|
||||
temp = []
|
||||
for i in range(self.word_size):
|
||||
temp.append("din[{0}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
if (self.words_per_row == 1):
|
||||
temp.append("bl[{0}]".format(i))
|
||||
temp.append("br[{0}]".format(i))
|
||||
else:
|
||||
temp.append("bl_out[{0}]".format(i))
|
||||
temp.append("br_out[{0}]".format(i))
|
||||
temp.extend([self.prefix+"w_en", "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
self.write_driver_array_inst = []
|
||||
for k in range(self.total_write):
|
||||
self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(k),
|
||||
mod=self.write_driver_array))
|
||||
|
||||
def add_row_decoder(self):
|
||||
""" Add the hierarchical row decoder """
|
||||
temp = []
|
||||
for i in range(self.word_size):
|
||||
temp.append("din{0}[{1}]".format(k,i))
|
||||
for i in range(self.word_size):
|
||||
if (self.words_per_row == 1):
|
||||
temp.append(self.write_bl_list[k]+"[{0}]".format(i))
|
||||
temp.append(self.write_br_list[k]+"[{0}]".format(i))
|
||||
else:
|
||||
temp.append(self.write_bl_list[k]+"_out[{0}]".format(i))
|
||||
temp.append(self.write_br_list[k]+"_out[{0}]".format(i))
|
||||
temp.extend([self.prefix+"w_en{0}".format(k), "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
def place_write_driver_array(self):
|
||||
""" Placing Write Driver """
|
||||
|
||||
# FIXME: place for multiport
|
||||
for k in range(self.total_write):
|
||||
y_offset = self.sense_amp_array.height + self.column_mux_height \
|
||||
+ self.m2_gap + self.write_driver_array.height
|
||||
self.place_inst(name=self.write_driver_array_inst[k].name,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
|
||||
|
||||
|
||||
def create_row_decoder(self):
|
||||
""" Create the hierarchical row decoder """
|
||||
|
||||
self.row_decoder_inst = []
|
||||
for k in range(self.total_ports):
|
||||
self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(k),
|
||||
mod=self.row_decoder))
|
||||
|
||||
temp = []
|
||||
for i in range(self.row_addr_size):
|
||||
temp.append("addr{0}[{1}]".format(k,i+self.col_addr_size))
|
||||
for j in range(self.num_rows):
|
||||
temp.append("dec_out{0}[{1}]".format(k,j))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
def place_row_decoder(self):
|
||||
""" Place the hierarchical row decoder """
|
||||
|
||||
# The address and control bus will be in between decoder and the main memory array
|
||||
# This bus will route address bits to the decoder input and column mux inputs.
|
||||
# The wires are actually routed after we placed the stuff on both sides.
|
||||
# The predecoder is below the x-axis and the main decoder is above the x-axis
|
||||
# The address flop and decoder are aligned in the x coord.
|
||||
|
||||
x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||
self.row_decoder_inst=self.add_inst(name="row_decoder",
|
||||
mod=self.row_decoder,
|
||||
offset=vector(x_offset,0))
|
||||
# FIXME: place for multiport
|
||||
for k in range(self.total_ports):
|
||||
x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||
self.place_inst(name=self.row_decoder_inst[k].name,
|
||||
offset=vector(x_offset,0))
|
||||
|
||||
temp = []
|
||||
for i in range(self.row_addr_size):
|
||||
temp.append("addr[{0}]".format(i+self.col_addr_size))
|
||||
for j in range(self.num_rows):
|
||||
temp.append("dec_out[{0}]".format(j))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
def create_wordline_driver(self):
|
||||
""" Create the Wordline Driver """
|
||||
|
||||
def add_wordline_driver(self):
|
||||
""" Wordline Driver """
|
||||
self.wordline_driver_inst = []
|
||||
for k in range(self.total_ports):
|
||||
self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(k),
|
||||
mod=self.wordline_driver))
|
||||
|
||||
# The wordline driver is placed to the right of the main decoder width.
|
||||
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch
|
||||
self.wordline_driver_inst=self.add_inst(name="wordline_driver",
|
||||
mod=self.wordline_driver,
|
||||
offset=vector(x_offset,0))
|
||||
temp = []
|
||||
for i in range(self.num_rows):
|
||||
temp.append("dec_out{0}[{1}]".format(k,i))
|
||||
for i in range(self.num_rows):
|
||||
temp.append(self.total_wl_list[k]+"[{0}]".format(i))
|
||||
temp.append(self.prefix+"clk_buf")
|
||||
temp.append("vdd")
|
||||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
||||
temp = []
|
||||
for i in range(self.num_rows):
|
||||
temp.append("dec_out[{0}]".format(i))
|
||||
for i in range(self.num_rows):
|
||||
temp.append("wl[{0}]".format(i))
|
||||
temp.append(self.prefix+"clk_buf")
|
||||
temp.append("vdd")
|
||||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
def place_wordline_driver(self):
|
||||
""" Place the Wordline Driver """
|
||||
|
||||
# FIXME: place for multiport
|
||||
for k in range(self.total_ports):
|
||||
# The wordline driver is placed to the right of the main decoder width.
|
||||
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch
|
||||
self.place_inst(name=self.wordline_driver_inst[k].name,
|
||||
offset=vector(x_offset,0))
|
||||
|
||||
|
||||
def add_column_decoder_module(self):
|
||||
def create_column_decoder(self):
|
||||
"""
|
||||
Create a 2:4 or 3:8 column address decoder.
|
||||
"""
|
||||
# Place the col decoder right aligned with row decoder
|
||||
x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width)
|
||||
y_off = -(self.col_decoder.height + 2*drc["well_to_well"])
|
||||
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
|
||||
mod=self.col_decoder,
|
||||
offset=vector(x_off,y_off))
|
||||
|
||||
temp = []
|
||||
for i in range(self.col_addr_size):
|
||||
temp.append("addr[{0}]".format(i))
|
||||
for j in range(self.num_col_addr_lines):
|
||||
temp.append("sel[{0}]".format(j))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
def add_column_decoder(self):
|
||||
"""
|
||||
Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2,
|
||||
2:4 decoder, or 3:8 decoder.
|
||||
"""
|
||||
if self.col_addr_size == 0:
|
||||
return
|
||||
elif self.col_addr_size == 1:
|
||||
|
|
@ -384,59 +482,100 @@ class bank(design.design):
|
|||
else:
|
||||
# No error checking before?
|
||||
debug.error("Invalid column decoder?",-1)
|
||||
|
||||
self.col_decoder_inst = []
|
||||
for k in range(self.total_ports):
|
||||
self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(k),
|
||||
mod=self.col_decoder))
|
||||
|
||||
temp = []
|
||||
for i in range(self.col_addr_size):
|
||||
temp.append("addr{0}[{1}]".format(k,i))
|
||||
for j in range(self.num_col_addr_lines):
|
||||
temp.append("sel{0}[{1}]".format(k,j))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
def place_column_decoder(self):
|
||||
"""
|
||||
Place a 2:4 or 3:8 column address decoder.
|
||||
"""
|
||||
if self.col_addr_size == 0:
|
||||
return
|
||||
|
||||
self.add_column_decoder_module()
|
||||
# FIXME: place for multiport
|
||||
for k in range(self.total_ports):
|
||||
# Place the col decoder right aligned with row decoder
|
||||
x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width)
|
||||
y_off = -(self.col_decoder.height + 2*drc["well_to_well"])
|
||||
self.place_inst(name=self.col_decoder_inst[k].name,
|
||||
offset=vector(x_off,y_off))
|
||||
|
||||
|
||||
def create_bank_select(self):
|
||||
""" Create the bank select logic. """
|
||||
|
||||
def add_bank_select(self):
|
||||
""" Instantiate the bank select logic. """
|
||||
if not self.num_banks > 1:
|
||||
return
|
||||
|
||||
self.bank_select_inst = []
|
||||
for k in range(self.total_ports):
|
||||
self.bank_select_inst.append(self.add_inst(name="bank_select",
|
||||
mod=self.bank_select))
|
||||
|
||||
temp = []
|
||||
temp.extend(self.input_control_signals)
|
||||
temp.append("bank_sel")
|
||||
temp.extend(self.control_signals)
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
def place_bank_select(self):
|
||||
""" Place the bank select logic. """
|
||||
|
||||
if not self.num_banks > 1:
|
||||
return
|
||||
|
||||
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||
if self.col_addr_size > 0:
|
||||
y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by())
|
||||
else:
|
||||
y_off = self.row_decoder_inst.by()
|
||||
y_off -= (self.bank_select.height + drc["well_to_well"])
|
||||
self.bank_select_pos = vector(x_off,y_off)
|
||||
self.bank_select_inst = self.add_inst(name="bank_select",
|
||||
mod=self.bank_select,
|
||||
offset=self.bank_select_pos)
|
||||
|
||||
temp = []
|
||||
temp.extend(self.input_control_signals)
|
||||
temp.append("bank_sel")
|
||||
temp.extend(self.control_signals)
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
# FIXME: place for multiport
|
||||
for k in range(self.total_ports):
|
||||
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||
if self.col_addr_size > 0:
|
||||
y_off = min(self.col_decoder_inst[0].by(), self.col_mux_array_inst[0].by())
|
||||
else:
|
||||
y_off = self.row_decoder_inst[0].by()
|
||||
y_off -= (self.bank_select.height + drc["well_to_well"])
|
||||
self.bank_select_pos = vector(x_off,y_off)
|
||||
self.place_inst(name=self.bank_select_inst[k].name,
|
||||
offset=self.bank_select_pos)
|
||||
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
|
||||
# These are the instances that every bank has
|
||||
top_instances = [self.bitcell_array_inst,
|
||||
self.precharge_array_inst,
|
||||
self.sense_amp_array_inst,
|
||||
self.write_driver_array_inst,
|
||||
self.row_decoder_inst,
|
||||
self.wordline_driver_inst]
|
||||
# Add these if we use the part...
|
||||
if self.col_addr_size > 0:
|
||||
top_instances.append(self.col_decoder_inst)
|
||||
top_instances.append(self.col_mux_array_inst)
|
||||
top_instances = [self.bitcell_array_inst]
|
||||
|
||||
for k in range(self.total_ports):
|
||||
top_instances.extend([self.precharge_array_inst[k],
|
||||
self.sense_amp_array_inst[k],
|
||||
self.write_driver_array_inst[k],
|
||||
self.row_decoder_inst[k],
|
||||
self.wordline_driver_inst[k]])
|
||||
# Add these if we use the part...
|
||||
if self.col_addr_size > 0:
|
||||
top_instances.append(self.col_decoder_inst[k])
|
||||
top_instances.append(self.col_mux_array_inst[k])
|
||||
|
||||
if self.num_banks > 1:
|
||||
top_instances.append(self.bank_select_inst)
|
||||
if self.num_banks > 1:
|
||||
top_instances.append(self.bank_select_inst[k])
|
||||
|
||||
|
||||
for inst in top_instances:
|
||||
# Column mux has no vdd
|
||||
if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst):
|
||||
if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst[0]):
|
||||
self.copy_layout_pin(inst, "vdd")
|
||||
# Precharge has no gnd
|
||||
if inst != self.precharge_array_inst:
|
||||
if inst != self.precharge_array_inst[0]:
|
||||
self.copy_layout_pin(inst, "gnd")
|
||||
|
||||
def route_bank_select(self):
|
||||
|
|
@ -446,7 +585,7 @@ class bank(design.design):
|
|||
|
||||
for gated_name in self.control_signals:
|
||||
# Connect the inverter output to the central bus
|
||||
out_pos = self.bank_select_inst.get_pin(gated_name).rc()
|
||||
out_pos = self.bank_select_inst[0].get_pin(gated_name).rc()
|
||||
bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y)
|
||||
self.add_path("metal3",[out_pos, bus_pos])
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
|
|
@ -460,16 +599,19 @@ class bank(design.design):
|
|||
rotate=90)
|
||||
|
||||
|
||||
def setup_layout_constraints(self):
|
||||
""" After the modules are instantiated, find the dimensions for the
|
||||
control bus, power ring, etc. """
|
||||
def setup_routing_constraints(self):
|
||||
"""
|
||||
After the modules are instantiated, find the dimensions for the
|
||||
control bus, power ring, etc.
|
||||
"""
|
||||
|
||||
#The minimum point is either the bottom of the address flops,
|
||||
#the column decoder (if there is one).
|
||||
write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch
|
||||
row_decoder_min_y_offset = self.row_decoder_inst.by()
|
||||
write_driver_min_y_offset = self.write_driver_array_inst[0].by() - 3*self.m2_pitch
|
||||
row_decoder_min_y_offset = self.row_decoder_inst[0].by()
|
||||
|
||||
if self.col_addr_size > 0:
|
||||
col_decoder_min_y_offset = self.col_decoder_inst.by()
|
||||
col_decoder_min_y_offset = self.col_decoder_inst[0].by()
|
||||
else:
|
||||
col_decoder_min_y_offset = row_decoder_min_y_offset
|
||||
|
||||
|
|
@ -483,10 +625,11 @@ class bank(design.design):
|
|||
|
||||
# The max point is always the top of the precharge bitlines
|
||||
# Add a vdd and gnd power rail above the array
|
||||
self.max_y_offset = self.precharge_array_inst.uy() + 3*self.m1_width
|
||||
# FIXME: Update multiport
|
||||
self.max_y_offset = self.precharge_array_inst[0].uy() + 3*self.m1_width
|
||||
self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width
|
||||
self.min_x_offset = self.row_decoder_inst.lx()
|
||||
|
||||
self.min_x_offset = self.row_decoder_inst[0].lx()
|
||||
|
||||
# # Create the core bbox for the power rings
|
||||
ur = vector(self.max_x_offset, self.max_y_offset)
|
||||
ll = vector(self.min_x_offset, self.min_y_offset)
|
||||
|
|
@ -519,17 +662,19 @@ class bank(design.design):
|
|||
def route_precharge_to_bitcell_array(self):
|
||||
""" Routing of BL and BR between pre-charge and bitcell array """
|
||||
|
||||
for i in range(self.num_cols):
|
||||
precharge_bl = self.precharge_array_inst.get_pin("bl[{}]".format(i)).bc()
|
||||
precharge_br = self.precharge_array_inst.get_pin("br[{}]".format(i)).bc()
|
||||
bitcell_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).uc()
|
||||
bitcell_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).uc()
|
||||
# FIXME: Update for multiport
|
||||
for k in range(self.total_read):
|
||||
for i in range(self.num_cols):
|
||||
precharge_bl = self.precharge_array_inst[k].get_pin("bl[{}]".format(i)).bc()
|
||||
precharge_br = self.precharge_array_inst[k].get_pin("br[{}]".format(i)).bc()
|
||||
bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[k]+"[{}]".format(i)).uc()
|
||||
bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_list[k]+"[{}]".format(i)).uc()
|
||||
|
||||
yoffset = 0.5*(precharge_bl.y+bitcell_bl.y)
|
||||
self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset),
|
||||
vector(bitcell_bl.x,yoffset), bitcell_bl])
|
||||
self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset),
|
||||
vector(bitcell_br.x,yoffset), bitcell_br])
|
||||
yoffset = 0.5*(precharge_bl.y+bitcell_bl.y)
|
||||
self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset),
|
||||
vector(bitcell_bl.x,yoffset), bitcell_bl])
|
||||
self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset),
|
||||
vector(bitcell_br.x,yoffset), bitcell_br])
|
||||
|
||||
|
||||
def route_col_mux_to_bitcell_array(self):
|
||||
|
|
@ -539,51 +684,56 @@ class bank(design.design):
|
|||
if self.col_addr_size==0:
|
||||
return
|
||||
|
||||
for i in range(self.num_cols):
|
||||
col_mux_bl = self.col_mux_array_inst.get_pin("bl[{}]".format(i)).uc()
|
||||
col_mux_br = self.col_mux_array_inst.get_pin("br[{}]".format(i)).uc()
|
||||
bitcell_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).bc()
|
||||
bitcell_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).bc()
|
||||
# FIXME: Update for multiport
|
||||
for k in range(self.total_ports):
|
||||
for i in range(self.num_cols):
|
||||
col_mux_bl = self.col_mux_array_inst[k].get_pin("bl[{}]".format(i)).uc()
|
||||
col_mux_br = self.col_mux_array_inst[k].get_pin("br[{}]".format(i)).uc()
|
||||
bitcell_bl = self.bitcell_array_inst.get_pin(self.total_bl_list[k]+"[{}]".format(i)).bc()
|
||||
bitcell_br = self.bitcell_array_inst.get_pin(self.total_br_list[k]+"[{}]".format(i)).bc()
|
||||
|
||||
yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y)
|
||||
self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset),
|
||||
vector(bitcell_bl.x,yoffset), bitcell_bl])
|
||||
self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset),
|
||||
vector(bitcell_br.x,yoffset), bitcell_br])
|
||||
yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y)
|
||||
self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset),
|
||||
vector(bitcell_bl.x,yoffset), bitcell_bl])
|
||||
self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset),
|
||||
vector(bitcell_br.x,yoffset), bitcell_br])
|
||||
|
||||
def route_sense_amp_to_col_mux_or_bitcell_array(self):
|
||||
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
|
||||
|
||||
for i in range(self.word_size):
|
||||
sense_amp_bl = self.sense_amp_array_inst.get_pin("bl[{}]".format(i)).uc()
|
||||
sense_amp_br = self.sense_amp_array_inst.get_pin("br[{}]".format(i)).uc()
|
||||
for k in range(self.total_read):
|
||||
for i in range(self.word_size):
|
||||
sense_amp_bl = self.sense_amp_array_inst[k].get_pin("bl[{}]".format(i)).uc()
|
||||
sense_amp_br = self.sense_amp_array_inst[k].get_pin("br[{}]".format(i)).uc()
|
||||
|
||||
if self.col_addr_size>0:
|
||||
# Sense amp is connected to the col mux
|
||||
connect_bl = self.col_mux_array_inst.get_pin("bl_out[{}]".format(i)).bc()
|
||||
connect_br = self.col_mux_array_inst.get_pin("br_out[{}]".format(i)).bc()
|
||||
else:
|
||||
# Sense amp is directly connected to the bitcell array
|
||||
connect_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).bc()
|
||||
connect_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).bc()
|
||||
|
||||
if self.col_addr_size>0:
|
||||
# Sense amp is connected to the col mux
|
||||
connect_bl = self.col_mux_array_inst[k].get_pin("bl_out[{}]".format(i)).bc()
|
||||
connect_br = self.col_mux_array_inst[k].get_pin("br_out[{}]".format(i)).bc()
|
||||
else:
|
||||
# Sense amp is directly connected to the bitcell array
|
||||
connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[k]+"[{}]".format(i)).bc()
|
||||
connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[k]+"[{}]".format(i)).bc()
|
||||
|
||||
yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
|
||||
self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset),
|
||||
vector(connect_bl.x,yoffset), connect_bl])
|
||||
self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset),
|
||||
vector(connect_br.x,yoffset), connect_br])
|
||||
|
||||
yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
|
||||
self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset),
|
||||
vector(connect_bl.x,yoffset), connect_bl])
|
||||
self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset),
|
||||
vector(connect_br.x,yoffset), connect_br])
|
||||
|
||||
|
||||
def route_sense_amp_out(self):
|
||||
""" Add pins for the sense amp output """
|
||||
for i in range(self.word_size):
|
||||
data_pin = self.sense_amp_array_inst.get_pin("data[{}]".format(i))
|
||||
self.add_layout_pin_rect_center(text="dout[{}]".format(i),
|
||||
data_pin = self.sense_amp_array_inst[0].get_pin("data[{}]".format(i))
|
||||
self.add_layout_pin_rect_center(text="dout0[{}]".format(i),
|
||||
layer=data_pin.layer,
|
||||
offset=data_pin.center(),
|
||||
height=data_pin.height(),
|
||||
width=data_pin.width()),
|
||||
width=data_pin.width())
|
||||
|
||||
|
||||
def route_row_decoder(self):
|
||||
""" Routes the row decoder inputs and supplies """
|
||||
|
||||
|
|
@ -591,8 +741,8 @@ class bank(design.design):
|
|||
for i in range(self.row_addr_size):
|
||||
addr_idx = i + self.col_addr_size
|
||||
decoder_name = "addr[{}]".format(i)
|
||||
addr_name = "addr[{}]".format(addr_idx)
|
||||
self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name)
|
||||
addr_name = "addr0[{}]".format(addr_idx)
|
||||
self.copy_layout_pin(self.row_decoder_inst[0], decoder_name, addr_name)
|
||||
|
||||
|
||||
def route_write_driver(self):
|
||||
|
|
@ -600,26 +750,25 @@ class bank(design.design):
|
|||
|
||||
for i in range(self.word_size):
|
||||
data_name = "data[{}]".format(i)
|
||||
din_name = "din[{}]".format(i)
|
||||
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
|
||||
din_name = "din0[{}]".format(i)
|
||||
self.copy_layout_pin(self.write_driver_array_inst[0], data_name, din_name)
|
||||
|
||||
|
||||
|
||||
def route_wordline_driver(self):
|
||||
""" Connecting Wordline driver output to Bitcell WL connection """
|
||||
|
||||
# we don't care about bends after connecting to the input pin, so let the path code decide.
|
||||
|
||||
for i in range(self.num_rows):
|
||||
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
|
||||
decoder_out_pos = self.row_decoder_inst.get_pin("decode[{}]".format(i)).rc()
|
||||
driver_in_pos = self.wordline_driver_inst.get_pin("in[{}]".format(i)).lc()
|
||||
decoder_out_pos = self.row_decoder_inst[0].get_pin("decode[{}]".format(i)).rc()
|
||||
driver_in_pos = self.wordline_driver_inst[0].get_pin("in[{}]".format(i)).lc()
|
||||
mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0)
|
||||
mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1)
|
||||
self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos])
|
||||
|
||||
# The mid guarantees we exit the input cell to the right.
|
||||
driver_wl_pos = self.wordline_driver_inst.get_pin("wl[{}]".format(i)).rc()
|
||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin("wl[{}]".format(i)).lc()
|
||||
driver_wl_pos = self.wordline_driver_inst[0].get_pin("wl[{}]".format(i)).rc()
|
||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[0]+"[{}]".format(i)).lc()
|
||||
mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0)
|
||||
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
|
||||
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||
|
|
@ -639,7 +788,7 @@ class bank(design.design):
|
|||
decode_names = ["Zb", "Z"]
|
||||
|
||||
# The Address LSB
|
||||
self.copy_layout_pin(self.col_decoder_inst, "A", "addr[0]")
|
||||
self.copy_layout_pin(self.col_decoder_inst[0], "A", "addr0[0]")
|
||||
|
||||
elif self.col_addr_size > 1:
|
||||
decode_names = []
|
||||
|
|
@ -648,22 +797,22 @@ class bank(design.design):
|
|||
|
||||
for i in range(self.col_addr_size):
|
||||
decoder_name = "in[{}]".format(i)
|
||||
addr_name = "addr[{}]".format(i)
|
||||
self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name)
|
||||
addr_name = "addr0[{}]".format(i)
|
||||
self.copy_layout_pin(self.col_decoder_inst[0], decoder_name, addr_name)
|
||||
|
||||
|
||||
# This will do a quick "river route" on two layers.
|
||||
# When above the top select line it will offset "inward" again to prevent conflicts.
|
||||
# This could be done on a single layer, but we follow preferred direction rules for later routing.
|
||||
top_y_offset = self.col_mux_array_inst.get_pin("sel[{}]".format(self.num_col_addr_lines-1)).cy()
|
||||
top_y_offset = self.col_mux_array_inst[0].get_pin("sel[{}]".format(self.num_col_addr_lines-1)).cy()
|
||||
for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)):
|
||||
mux_name = "sel[{}]".format(i)
|
||||
mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc()
|
||||
mux_addr_pos = self.col_mux_array_inst[0].get_pin(mux_name).lc()
|
||||
|
||||
decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center()
|
||||
decode_out_pos = self.col_decoder_inst[0].get_pin(decode_name).center()
|
||||
|
||||
# To get to the edge of the decoder and one track out
|
||||
delta_offset = self.col_decoder_inst.rx() - decode_out_pos.x + self.m2_pitch
|
||||
delta_offset = self.col_decoder_inst[0].rx() - decode_out_pos.x + self.m2_pitch
|
||||
if decode_out_pos.y > top_y_offset:
|
||||
mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y)
|
||||
else:
|
||||
|
|
@ -714,7 +863,7 @@ class bank(design.design):
|
|||
for i in range(self.word_size):
|
||||
data_name = "dec_out[{}]".format(i)
|
||||
pin_name = "in[{}]".format(i)
|
||||
data_pin = self.wordline_driver_inst.get_pin(pin_name)
|
||||
data_pin = self.wordline_driver_inst[0].get_pin(pin_name)
|
||||
self.add_label(text=data_name,
|
||||
layer="metal1",
|
||||
offset=data_pin.center())
|
||||
|
|
@ -728,9 +877,9 @@ class bank(design.design):
|
|||
# Connection from the central bus to the main control block crosses
|
||||
# pre-decoder and this connection is in metal3
|
||||
connection = []
|
||||
connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc()))
|
||||
connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc()))
|
||||
connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
|
||||
connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst[0].get_pin("en").lc()))
|
||||
connection.append((self.prefix+"w_en0", self.write_driver_array_inst[0].get_pin("en").lc()))
|
||||
connection.append((self.prefix+"s_en0", self.sense_amp_array_inst[0].get_pin("en").lc()))
|
||||
|
||||
for (control_signal, pin_pos) in connection:
|
||||
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
|
||||
|
|
@ -741,7 +890,7 @@ class bank(design.design):
|
|||
|
||||
# clk to wordline_driver
|
||||
control_signal = self.prefix+"clk_buf"
|
||||
pin_pos = self.wordline_driver_inst.get_pin("en").uc()
|
||||
pin_pos = self.wordline_driver_inst[0].get_pin("en").uc()
|
||||
mid_pos = pin_pos + vector(0,self.m1_pitch)
|
||||
control_x_offset = self.bus_xoffset[control_signal].x
|
||||
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class bank_select(design.design):
|
|||
# Number of control lines in the bus
|
||||
self.num_control_lines = 4
|
||||
# The order of the control signals on the control bus:
|
||||
self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en"]
|
||||
self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"]
|
||||
# These will be outputs of the gaters if this is multibank
|
||||
self.control_signals = ["gated_"+str for str in self.input_control_signals]
|
||||
|
||||
|
|
|
|||
|
|
@ -44,56 +44,54 @@ class bitcell(design.design):
|
|||
"vdd",
|
||||
"gnd"]
|
||||
return bitcell_pins
|
||||
|
||||
|
||||
def list_row_pins(self):
|
||||
""" Creates a list of all row pins (except for gnd and vdd) """
|
||||
def list_all_wl_names(self):
|
||||
""" Creates a list of all wordline pin names """
|
||||
row_pins = ["wl"]
|
||||
return row_pins
|
||||
|
||||
def list_read_row_pins(self):
|
||||
""" Creates a list of row pins associated with read ports """
|
||||
def list_read_wl_names(self):
|
||||
""" Creates a list of wordline pin names associated with read ports """
|
||||
row_pins = ["wl"]
|
||||
return row_pins
|
||||
|
||||
def list_write_row_pins(self):
|
||||
""" Creates a list of row pins associated with write ports """
|
||||
def list_write_wl_names(self):
|
||||
""" Creates a list of wordline pin names associated with write ports """
|
||||
row_pins = ["wl"]
|
||||
return row_pins
|
||||
|
||||
|
||||
def list_all_column_pins(self):
|
||||
""" Creates a list of all column pins (except for gnd and vdd) """
|
||||
def list_all_bitline_names(self):
|
||||
""" Creates a list of all bitline pin names (both bl and br) """
|
||||
column_pins = ["bl", "br"]
|
||||
return column_pins
|
||||
|
||||
def list_column_pins(self):
|
||||
""" Creates a list of all column pins (except for gnd and vdd) """
|
||||
def list_all_bl_names(self):
|
||||
""" Creates a list of all bl pins names """
|
||||
column_pins = ["bl"]
|
||||
return column_pins
|
||||
|
||||
def list_column_bar_pins(self):
|
||||
""" Creates a list of all column pins (except for gnd and vdd) """
|
||||
def list_all_br_names(self):
|
||||
""" Creates a list of all br pins names """
|
||||
column_pins = ["br"]
|
||||
return column_pins
|
||||
|
||||
def list_read_column_pins(self):
|
||||
""" Creates a list of column pins associated with read ports """
|
||||
def list_read_bl_names(self):
|
||||
""" Creates a list of bl pin names associated with read ports """
|
||||
column_pins = ["bl"]
|
||||
return column_pins
|
||||
|
||||
def list_read_bar_column_pins(self):
|
||||
""" Creates a list of column pins associated with read_bar ports """
|
||||
def list_read_br_names(self):
|
||||
""" Creates a list of br pin names associated with read ports """
|
||||
column_pins = ["br"]
|
||||
return column_pins
|
||||
|
||||
def list_write_column_pins(self):
|
||||
""" Creates a list of column pins associated with write ports """
|
||||
def list_write_bl_names(self):
|
||||
""" Creates a list of bl pin names associated with write ports """
|
||||
column_pins = ["bl"]
|
||||
return column_pins
|
||||
|
||||
def list_write_bar_column_pins(self):
|
||||
""" Creates a list of column pins asscociated with write_bar ports"""
|
||||
def list_write_br_names(self):
|
||||
""" Creates a list of br pin names asscociated with write ports"""
|
||||
column_pins = ["br"]
|
||||
return column_pins
|
||||
|
||||
|
|
|
|||
|
|
@ -31,19 +31,18 @@ class bitcell_array(design.design):
|
|||
self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width
|
||||
self.width = self.column_size*self.cell.width + self.m1_width
|
||||
|
||||
self.add_pins()
|
||||
self.create_netlist()
|
||||
|
||||
self.create_layout()
|
||||
self.add_layout_pins()
|
||||
|
||||
# We don't offset this because we need to align
|
||||
# the replica bitcell in the control logic
|
||||
#self.offset_all_coordinates()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
row_list = self.cell.list_row_pins()
|
||||
column_list = self.cell.list_all_column_pins()
|
||||
row_list = self.cell.list_all_wl_names()
|
||||
column_list = self.cell.list_all_bitline_names()
|
||||
for col in range(self.column_size):
|
||||
for cell_column in column_list:
|
||||
self.add_pin(cell_column+"[{0}]".format(col))
|
||||
|
|
@ -55,7 +54,6 @@ class bitcell_array(design.design):
|
|||
|
||||
def create_layout(self):
|
||||
xoffset = 0.0
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
yoffset = 0.0
|
||||
for row in range(self.row_size):
|
||||
|
|
@ -68,21 +66,34 @@ class bitcell_array(design.design):
|
|||
tempy = yoffset
|
||||
dir_key = ""
|
||||
|
||||
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||
mod=self.cell,
|
||||
offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
self.connect_inst(self.cell.list_bitcell_pins(col, row))
|
||||
|
||||
self.place_inst(name=name,
|
||||
offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
yoffset += self.cell.height
|
||||
xoffset += self.cell.width
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_netlist(self):
|
||||
""" Create and connect the netlist """
|
||||
self.add_pins()
|
||||
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
for row in range(self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||
mod=self.cell)
|
||||
self.connect_inst(self.cell.list_bitcell_pins(col, row))
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
|
||||
row_list = self.cell.list_row_pins()
|
||||
column_list = self.cell.list_all_column_pins()
|
||||
row_list = self.cell.list_all_wl_names()
|
||||
column_list = self.cell.list_all_bitline_names()
|
||||
|
||||
offset = vector(0.0, 0.0)
|
||||
for col in range(self.column_size):
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class control_logic(design.design):
|
|||
# leave space for the bus plus one extra space
|
||||
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
|
||||
# Outputs to the bank
|
||||
self.output_list = ["s_en", "w_en", "clk_buf_bar", "clk_buf"]
|
||||
self.output_list = ["s_en0", "w_en0", "clk_buf_bar", "clk_buf"]
|
||||
self.supply_list = ["vdd", "gnd"]
|
||||
|
||||
|
||||
|
|
@ -231,7 +231,7 @@ class control_logic(design.design):
|
|||
mod=self.inv8,
|
||||
offset=self.s_en_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
|
||||
self.connect_inst(["pre_s_en_bar", "s_en0", "vdd", "gnd"])
|
||||
|
||||
|
||||
self.row_end_inst.append(self.s_en_inst)
|
||||
|
|
@ -313,7 +313,7 @@ class control_logic(design.design):
|
|||
mod=self.inv8,
|
||||
offset=w_en_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"])
|
||||
self.connect_inst(["pre_w_en_bar", "w_en0", "vdd", "gnd"])
|
||||
x_off += self.inv8.width
|
||||
|
||||
self.row_end_inst.append(self.w_en_inst)
|
||||
|
|
@ -406,7 +406,7 @@ class control_logic(design.design):
|
|||
self.add_path("metal1",[self.pre_w_en_inst.get_pin("Z").center(), self.pre_w_en_bar_inst.get_pin("A").center()])
|
||||
self.add_path("metal1",[self.pre_w_en_bar_inst.get_pin("Z").center(), self.w_en_inst.get_pin("A").center()])
|
||||
|
||||
self.connect_output(self.w_en_inst, "Z", "w_en")
|
||||
self.connect_output(self.w_en_inst, "Z", "w_en0")
|
||||
|
||||
def route_sen(self):
|
||||
rbl_out_pos = self.rbl_inst.get_pin("out").bc()
|
||||
|
|
@ -417,7 +417,7 @@ class control_logic(design.design):
|
|||
|
||||
self.add_path("metal1",[self.pre_s_en_bar_inst.get_pin("Z").center(), self.s_en_inst.get_pin("A").center()])
|
||||
|
||||
self.connect_output(self.s_en_inst, "Z", "s_en")
|
||||
self.connect_output(self.s_en_inst, "Z", "s_en0")
|
||||
|
||||
def route_clk(self):
|
||||
""" Route the clk and clk_buf_bar signal internally """
|
||||
|
|
|
|||
|
|
@ -29,11 +29,15 @@ class dff_array(design.design):
|
|||
self.width = self.columns * self.dff.width
|
||||
self.height = self.rows * self.dff.height
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_dff_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_dff_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
|
@ -53,22 +57,28 @@ class dff_array(design.design):
|
|||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*self.dff.width,row*self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
||||
mod=self.dff,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
mod=self.dff)
|
||||
self.connect_inst([self.get_din_name(row,col),
|
||||
self.get_dout_name(row,col),
|
||||
"clk",
|
||||
"vdd",
|
||||
"gnd"])
|
||||
|
||||
def place_dff_array(self):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*self.dff.width,row*self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.place_inst(name=name,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
|
||||
def get_din_name(self, row, col):
|
||||
if self.columns == 1:
|
||||
din_name = "din[{0}]".format(row)
|
||||
|
|
|
|||
|
|
@ -27,11 +27,15 @@ class dff_buf_array(design.design):
|
|||
self.width = self.columns * self.dff.width
|
||||
self.height = self.rows * self.dff.height
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_dff_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_dff_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
|
@ -52,16 +56,8 @@ class dff_buf_array(design.design):
|
|||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*self.dff.width,row*self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
||||
mod=self.dff,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
mod=self.dff)
|
||||
self.connect_inst([self.get_din_name(row,col),
|
||||
self.get_dout_name(row,col),
|
||||
self.get_dout_bar_name(row,col),
|
||||
|
|
@ -69,6 +65,20 @@ class dff_buf_array(design.design):
|
|||
"vdd",
|
||||
"gnd"])
|
||||
|
||||
def place_dff_array(self):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*self.dff.width,row*self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.place_inst(name=name,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
|
||||
def get_din_name(self, row, col):
|
||||
if self.columns == 1:
|
||||
din_name = "din[{0}]".format(row)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ class hierarchical_decoder(design.design):
|
|||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell_height = self.mod_bitcell.height
|
||||
|
||||
self.NAND_FORMAT = "DEC_NAND[{0}]"
|
||||
self.INV_FORMAT = "DEC_INV_[{0}]"
|
||||
|
||||
self.pre2x4_inst = []
|
||||
self.pre3x8_inst = []
|
||||
|
||||
|
|
@ -33,22 +36,27 @@ class hierarchical_decoder(design.design):
|
|||
self.num_inputs = int(math.log(self.rows, 2))
|
||||
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
self.offset_all_coordinates()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_layout(self):
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.setup_layout_constants()
|
||||
self.setup_netlist_constants()
|
||||
self.add_pins()
|
||||
self.create_pre_decoder()
|
||||
self.create_row_decoder()
|
||||
self.create_input_rail()
|
||||
self.create_predecode_rail()
|
||||
self.route_vdd_gnd()
|
||||
|
||||
def create_layout(self):
|
||||
self.setup_layout_constants()
|
||||
self.place_pre_decoder()
|
||||
self.place_row_decoder()
|
||||
self.route_input_rails()
|
||||
self.route_predecode_rails()
|
||||
self.route_vdd_gnd()
|
||||
self.offset_all_coordinates()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
self.inv = pinv()
|
||||
self.add_mod(self.inv)
|
||||
|
|
@ -89,7 +97,7 @@ class hierarchical_decoder(design.design):
|
|||
else:
|
||||
debug.error("Invalid number of inputs for hierarchical decoder",-1)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
def setup_netlist_constants(self):
|
||||
self.predec_groups = [] # This array is a 2D array.
|
||||
|
||||
# Distributing vertical rails to different groups. One group belongs to one pre-decoder.
|
||||
|
|
@ -112,92 +120,9 @@ class hierarchical_decoder(design.design):
|
|||
index = index + 1
|
||||
self.predec_groups.append(lines)
|
||||
|
||||
self.calculate_dimensions()
|
||||
|
||||
def create_input_rail(self):
|
||||
""" Create input rails for the predecoders """
|
||||
# inputs should be as high as the decoders
|
||||
input_height = self.no_of_pre2x4*self.pre2_4.height + self.no_of_pre3x8*self.pre3_8.height
|
||||
|
||||
# Find the left-most predecoder
|
||||
min_x = 0
|
||||
if self.no_of_pre2x4 > 0:
|
||||
min_x = min(min_x, -self.pre2_4.width)
|
||||
if self.no_of_pre3x8 > 0:
|
||||
min_x = min(min_x, -self.pre3_8.width)
|
||||
input_offset=vector(min_x - self.input_routing_width,0)
|
||||
|
||||
input_bus_names = ["addr[{0}]".format(i) for i in range(self.num_inputs)]
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=input_offset,
|
||||
names=input_bus_names,
|
||||
length=input_height)
|
||||
|
||||
self.connect_input_to_predecodes()
|
||||
|
||||
|
||||
def connect_input_to_predecodes(self):
|
||||
""" Connect the vertical input rail to the predecoders """
|
||||
for pre_num in range(self.no_of_pre2x4):
|
||||
for i in range(2):
|
||||
index = pre_num * 2 + i
|
||||
|
||||
input_pos = self.input_rails["addr[{}]".format(index)]
|
||||
|
||||
in_name = "in[{}]".format(i)
|
||||
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
|
||||
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
|
||||
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
|
||||
|
||||
self.connect_input_rail(decoder_offset, input_offset)
|
||||
|
||||
|
||||
for pre_num in range(self.no_of_pre3x8):
|
||||
for i in range(3):
|
||||
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
|
||||
|
||||
input_pos = self.input_rails["addr[{}]".format(index)]
|
||||
|
||||
in_name = "in[{}]".format(i)
|
||||
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
|
||||
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
|
||||
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
|
||||
|
||||
self.connect_input_rail(decoder_offset, input_offset)
|
||||
|
||||
|
||||
def connect_input_rail(self, input_offset, output_offset):
|
||||
""" Connect a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
|
||||
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=input_offset,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=output_offset,
|
||||
rotate=90)
|
||||
self.add_path(("metal3"), [input_offset, output_offset])
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
""" Add the module pins """
|
||||
|
||||
for i in range(self.num_inputs):
|
||||
self.add_pin("addr[{0}]".format(i))
|
||||
|
||||
for j in range(self.rows):
|
||||
self.add_pin("decode[{0}]".format(j))
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def calculate_dimensions(self):
|
||||
""" Calculate the overal dimensions of the hierarchical decoder """
|
||||
def setup_layout_constants(self):
|
||||
""" Calculate the overall dimensions of the hierarchical decoder """
|
||||
|
||||
# If we have 4 or fewer rows, the predecoder is the decoder itself
|
||||
if self.num_inputs>=4:
|
||||
|
|
@ -227,24 +152,105 @@ class hierarchical_decoder(design.design):
|
|||
self.height = self.row_decoder_height
|
||||
self.width = self.input_routing_width + self.predecoder_width \
|
||||
+ self.internal_routing_width + nand_width + self.inv.width
|
||||
|
||||
def route_input_rails(self):
|
||||
""" Create input rails for the predecoders """
|
||||
# inputs should be as high as the decoders
|
||||
input_height = self.no_of_pre2x4*self.pre2_4.height + self.no_of_pre3x8*self.pre3_8.height
|
||||
|
||||
# Find the left-most predecoder
|
||||
min_x = 0
|
||||
if self.no_of_pre2x4 > 0:
|
||||
min_x = min(min_x, -self.pre2_4.width)
|
||||
if self.no_of_pre3x8 > 0:
|
||||
min_x = min(min_x, -self.pre3_8.width)
|
||||
input_offset=vector(min_x - self.input_routing_width,0)
|
||||
|
||||
input_bus_names = ["addr[{0}]".format(i) for i in range(self.num_inputs)]
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=input_offset,
|
||||
names=input_bus_names,
|
||||
length=input_height)
|
||||
|
||||
self.route_input_to_predecodes()
|
||||
|
||||
|
||||
def route_input_to_predecodes(self):
|
||||
""" Route the vertical input rail to the predecoders """
|
||||
for pre_num in range(self.no_of_pre2x4):
|
||||
for i in range(2):
|
||||
index = pre_num * 2 + i
|
||||
|
||||
input_pos = self.input_rails["addr[{}]".format(index)]
|
||||
|
||||
in_name = "in[{}]".format(i)
|
||||
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
|
||||
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
|
||||
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
|
||||
|
||||
self.route_input_rail(decoder_offset, input_offset)
|
||||
|
||||
|
||||
for pre_num in range(self.no_of_pre3x8):
|
||||
for i in range(3):
|
||||
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
|
||||
|
||||
input_pos = self.input_rails["addr[{}]".format(index)]
|
||||
|
||||
in_name = "in[{}]".format(i)
|
||||
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
|
||||
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
|
||||
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
|
||||
|
||||
self.route_input_rail(decoder_offset, input_offset)
|
||||
|
||||
|
||||
def route_input_rail(self, input_offset, output_offset):
|
||||
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
|
||||
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=input_offset,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=output_offset,
|
||||
rotate=90)
|
||||
self.add_path(("metal3"), [input_offset, output_offset])
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
""" Add the module pins """
|
||||
|
||||
for i in range(self.num_inputs):
|
||||
self.add_pin("addr[{0}]".format(i))
|
||||
|
||||
for j in range(self.rows):
|
||||
self.add_pin("decode[{0}]".format(j))
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
|
||||
def create_pre_decoder(self):
|
||||
""" Creates pre-decoder and places labels input address [A] """
|
||||
|
||||
for i in range(self.no_of_pre2x4):
|
||||
self.add_pre2x4(i)
|
||||
self.create_pre2x4(i)
|
||||
|
||||
for i in range(self.no_of_pre3x8):
|
||||
self.add_pre3x8(i)
|
||||
self.create_pre3x8(i)
|
||||
|
||||
def add_pre2x4(self,num):
|
||||
def create_pre2x4(self,num):
|
||||
""" Add a 2x4 predecoder to the left of the origin """
|
||||
|
||||
if (self.num_inputs == 2):
|
||||
base = vector(-self.pre2_4.width,0)
|
||||
index_off1 = index_off2 = 0
|
||||
else:
|
||||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||
index_off1 = num * 2
|
||||
index_off2 = num * 4
|
||||
|
||||
|
|
@ -256,20 +262,12 @@ class hierarchical_decoder(design.design):
|
|||
pins.extend(["vdd", "gnd"])
|
||||
|
||||
self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num),
|
||||
mod=self.pre2_4,
|
||||
offset=base))
|
||||
mod=self.pre2_4))
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
def add_pre3x8(self,num):
|
||||
def create_pre3x8(self,num):
|
||||
""" Add 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
||||
if (self.num_inputs == 3):
|
||||
offset = vector(-self.pre_3_8.width,0)
|
||||
mirror ="R0"
|
||||
else:
|
||||
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
|
||||
offset = vector(-self.pre3_8.width, height)
|
||||
|
||||
# If we had 2x4 predecodes, those are used as the lower
|
||||
# decode output bits
|
||||
in_index_offset = num * 3 + self.no_of_pre2x4 * 2
|
||||
|
|
@ -283,79 +281,112 @@ class hierarchical_decoder(design.design):
|
|||
pins.extend(["vdd", "gnd"])
|
||||
|
||||
self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num),
|
||||
mod=self.pre3_8,
|
||||
offset=offset))
|
||||
mod=self.pre3_8))
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
def place_pre_decoder(self):
|
||||
""" Creates pre-decoder and places labels input address [A] """
|
||||
|
||||
for i in range(self.no_of_pre2x4):
|
||||
self.place_pre2x4(i)
|
||||
|
||||
for i in range(self.no_of_pre3x8):
|
||||
self.place_pre3x8(i)
|
||||
|
||||
def place_pre2x4(self,num):
|
||||
""" Place 2x4 predecoder to the left of the origin """
|
||||
|
||||
if (self.num_inputs == 2):
|
||||
base = vector(-self.pre2_4.width,0)
|
||||
else:
|
||||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||
|
||||
self.place_inst(name="pre[{0}]".format(num),
|
||||
offset=base)
|
||||
|
||||
|
||||
def place_pre3x8(self,num):
|
||||
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
||||
if (self.num_inputs == 3):
|
||||
offset = vector(-self.pre_3_8.width,0)
|
||||
mirror ="R0"
|
||||
else:
|
||||
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
|
||||
offset = vector(-self.pre3_8.width, height)
|
||||
|
||||
self.place_inst(name="pre3x8[{0}]".format(num),
|
||||
offset=offset)
|
||||
|
||||
|
||||
def create_row_decoder(self):
|
||||
""" Create the row-decoder by placing NAND2/NAND3 and Inverters
|
||||
and add the primary decoder output pins. """
|
||||
if (self.num_inputs >= 4):
|
||||
self.add_decoder_nand_array()
|
||||
self.add_decoder_inv_array()
|
||||
self.route_decoder()
|
||||
self.create_decoder_nand_array()
|
||||
self.create_decoder_inv_array()
|
||||
|
||||
|
||||
def add_decoder_nand_array(self):
|
||||
def create_decoder_nand_array(self):
|
||||
""" Add a column of NAND gates for final decode """
|
||||
|
||||
self.nand_inst = []
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs <5.
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
self.add_nand_array(nand_mod=self.nand2)
|
||||
# FIXME: Can we convert this to the connect_inst with checks?
|
||||
for i in range(len(self.predec_groups[0])):
|
||||
for j in range(len(self.predec_groups[1])):
|
||||
row = len(self.predec_groups[1])*i + j
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand2))
|
||||
pins =["out[{0}]".format(i),
|
||||
"out[{0}]".format(j + len(self.predec_groups[0])),
|
||||
"Z[{0}]".format(len(self.predec_groups[1])*i + j),
|
||||
"Z[{0}]".format(row),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(args=pins, check=False)
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs >5.
|
||||
elif (self.num_inputs > 5):
|
||||
self.add_nand_array(nand_mod=self.nand3,
|
||||
correct=drc["minwidth_metal1"])
|
||||
# This will not check that the inst connections match.
|
||||
for i in range(len(self.predec_groups[0])):
|
||||
for j in range(len(self.predec_groups[1])):
|
||||
for k in range(len(self.predec_groups[2])):
|
||||
Z_index = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \
|
||||
+ len(self.predec_groups[2])*j + k
|
||||
row = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \
|
||||
+ len(self.predec_groups[2])*j + k
|
||||
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand3))
|
||||
|
||||
pins = ["out[{0}]".format(i),
|
||||
"out[{0}]".format(j + len(self.predec_groups[0])),
|
||||
"out[{0}]".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
||||
"Z[{0}]".format(Z_index),
|
||||
"Z[{0}]".format(row),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(args=pins, check=False)
|
||||
self.connect_inst(pins)
|
||||
|
||||
def add_nand_array(self, nand_mod, correct=0):
|
||||
""" Add a column of NAND gates for the decoder above the predecoders."""
|
||||
|
||||
def create_decoder_inv_array(self):
|
||||
"""
|
||||
Add a column of INV gates for the decoder.
|
||||
"""
|
||||
|
||||
self.nand_inst = []
|
||||
self.inv_inst = []
|
||||
for row in range(self.rows):
|
||||
name = "DEC_NAND[{0}]".format(row)
|
||||
if ((row % 2) == 0):
|
||||
y_off = nand_mod.height*row
|
||||
y_dir = 1
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off = nand_mod.height*(row + 1)
|
||||
y_dir = -1
|
||||
mirror = "MX"
|
||||
name = self.INV_FORMAT.format(row)
|
||||
self.inv_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv))
|
||||
self.connect_inst(args=["Z[{0}]".format(row),
|
||||
"decode[{0}]".format(row),
|
||||
"vdd", "gnd"])
|
||||
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=nand_mod,
|
||||
offset=[self.internal_routing_width, y_off],
|
||||
mirror=mirror))
|
||||
|
||||
|
||||
|
||||
def add_decoder_inv_array(self):
|
||||
"""Add a column of INV gates for the decoder above the predecoders
|
||||
and to the right of the NAND decoders."""
|
||||
def place_decoder_inv_array(self):
|
||||
"""
|
||||
Place the column of INV gates for the decoder above the predecoders
|
||||
and to the right of the NAND decoders.
|
||||
"""
|
||||
|
||||
z_pin = self.inv.get_pin("Z")
|
||||
|
||||
|
|
@ -364,9 +395,8 @@ class hierarchical_decoder(design.design):
|
|||
else:
|
||||
x_off = self.internal_routing_width + self.nand3.width
|
||||
|
||||
self.inv_inst = []
|
||||
for row in range(self.rows):
|
||||
name = "DEC_INV_[{0}]".format(row)
|
||||
name = self.INV_FORMAT.format(row)
|
||||
if (row % 2 == 0):
|
||||
inv_row_height = self.inv.height * row
|
||||
mirror = "R0"
|
||||
|
|
@ -377,17 +407,52 @@ class hierarchical_decoder(design.design):
|
|||
y_dir = -1
|
||||
y_off = inv_row_height
|
||||
offset = vector(x_off,y_off)
|
||||
|
||||
self.inv_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv,
|
||||
offset=offset,
|
||||
mirror=mirror))
|
||||
self.place_inst(name=name,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
# This will not check that the inst connections match.
|
||||
self.connect_inst(args=["Z[{0}]".format(row),
|
||||
"decode[{0}]".format(row),
|
||||
"vdd", "gnd"],
|
||||
check=False)
|
||||
def place_row_decoder(self):
|
||||
"""
|
||||
Place the row-decoder by placing NAND2/NAND3 and Inverters
|
||||
and add the primary decoder output pins.
|
||||
"""
|
||||
if (self.num_inputs >= 4):
|
||||
self.place_decoder_nand_array()
|
||||
self.place_decoder_inv_array()
|
||||
self.route_decoder()
|
||||
|
||||
|
||||
def place_decoder_nand_array(self):
|
||||
""" Add a column of NAND gates for final decode """
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs <5.
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
self.place_nand_array(nand_mod=self.nand2)
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs >5.
|
||||
# FIXME: why this correct offset?)
|
||||
elif (self.num_inputs > 5):
|
||||
self.place_nand_array(nand_mod=self.nand3)
|
||||
|
||||
def place_nand_array(self, nand_mod):
|
||||
""" Add a column of NAND gates for the decoder above the predecoders."""
|
||||
|
||||
for row in range(self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
if ((row % 2) == 0):
|
||||
y_off = nand_mod.height*row
|
||||
y_dir = 1
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off = nand_mod.height*(row + 1)
|
||||
y_dir = -1
|
||||
mirror = "MX"
|
||||
|
||||
self.place_inst(name=name,
|
||||
offset=[self.internal_routing_width, y_off],
|
||||
mirror=mirror)
|
||||
|
||||
|
||||
|
||||
|
||||
def route_decoder(self):
|
||||
|
|
@ -412,7 +477,7 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
|
||||
|
||||
def create_predecode_rail(self):
|
||||
def route_predecode_rails(self):
|
||||
""" Creates vertical metal 2 rails to connect predecoder and decoder stages."""
|
||||
|
||||
# This is not needed for inputs <4 since they have no pre/decode stages.
|
||||
|
|
@ -426,10 +491,10 @@ class hierarchical_decoder(design.design):
|
|||
length=self.height)
|
||||
|
||||
|
||||
self.connect_rails_to_predecodes()
|
||||
self.connect_rails_to_decoder()
|
||||
self.route_rails_to_predecodes()
|
||||
self.route_rails_to_decoder()
|
||||
|
||||
def connect_rails_to_predecodes(self):
|
||||
def route_rails_to_predecodes(self):
|
||||
""" Iterates through all of the predecodes and connects to the rails including the offsets """
|
||||
|
||||
# FIXME: convert to connect_bus
|
||||
|
|
@ -438,7 +503,7 @@ class hierarchical_decoder(design.design):
|
|||
predecode_name = "predecode[{}]".format(pre_num * 4 + i)
|
||||
out_name = "out[{}]".format(i)
|
||||
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
||||
self.connect_predecode_rail_m3(predecode_name, pin)
|
||||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
|
||||
# FIXME: convert to connect_bus
|
||||
|
|
@ -447,11 +512,11 @@ class hierarchical_decoder(design.design):
|
|||
predecode_name = "predecode[{}]".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
||||
out_name = "out[{}]".format(i)
|
||||
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
||||
self.connect_predecode_rail_m3(predecode_name, pin)
|
||||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
|
||||
|
||||
def connect_rails_to_decoder(self):
|
||||
def route_rails_to_decoder(self):
|
||||
""" Use the self.predec_groups to determine the connections to the decoder NAND gates.
|
||||
Inputs of NAND2/NAND3 gates come from different groups.
|
||||
For example for these groups [ [0,1,2,3] ,[4,5,6,7],
|
||||
|
|
@ -465,9 +530,9 @@ class hierarchical_decoder(design.design):
|
|||
for index_B in self.predec_groups[1]:
|
||||
# FIXME: convert to connect_bus?
|
||||
predecode_name = "predecode[{}]".format(index_A)
|
||||
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode[{}]".format(index_B)
|
||||
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
row_index = row_index + 1
|
||||
|
||||
elif (self.num_inputs > 5):
|
||||
|
|
@ -476,11 +541,11 @@ class hierarchical_decoder(design.design):
|
|||
for index_C in self.predec_groups[2]:
|
||||
# FIXME: convert to connect_bus?
|
||||
predecode_name = "predecode[{}]".format(index_A)
|
||||
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode[{}]".format(index_B)
|
||||
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
predecode_name = "predecode[{}]".format(index_C)
|
||||
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C"))
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C"))
|
||||
row_index = row_index + 1
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
|
|
@ -516,7 +581,7 @@ class hierarchical_decoder(design.design):
|
|||
self.copy_layout_pin(pre, "gnd")
|
||||
|
||||
|
||||
def connect_predecode_rail(self, rail_name, pin):
|
||||
def route_predecode_rail(self, rail_name, pin):
|
||||
""" Connect the routing rail to the given metal1 pin """
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y)
|
||||
self.add_path("metal1", [rail_pos, pin.lc()])
|
||||
|
|
@ -525,7 +590,7 @@ class hierarchical_decoder(design.design):
|
|||
rotate=90)
|
||||
|
||||
|
||||
def connect_predecode_rail_m3(self, rail_name, pin):
|
||||
def route_predecode_rail_m3(self, rail_name, pin):
|
||||
""" Connect the routing rail to the given metal1 pin """
|
||||
# This routes the pin up to the rail, basically, to avoid conflicts.
|
||||
# It would be fixed with a channel router.
|
||||
|
|
|
|||
|
|
@ -37,17 +37,17 @@ class hierarchical_predecode(design.design):
|
|||
self.inv = pinv()
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.create_nand(self.number_of_inputs)
|
||||
self.add_nand(self.number_of_inputs)
|
||||
self.add_mod(self.nand)
|
||||
|
||||
def create_nand(self,inputs):
|
||||
def add_nand(self,inputs):
|
||||
""" Create the NAND for the predecode input stage """
|
||||
if inputs==2:
|
||||
self.nand = pnand2()
|
||||
elif inputs==3:
|
||||
self.nand = pnand3()
|
||||
else:
|
||||
debug.error("Invalid number of predecode inputs.",-1)
|
||||
debug.error("Invalid number of predecode inputs: {}".format(inputs),-1)
|
||||
|
||||
def setup_constraints(self):
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ class hierarchical_predecode(design.design):
|
|||
# Height width are computed
|
||||
self.width = self.x_off_inv_2 + self.inv.width
|
||||
|
||||
def create_rails(self):
|
||||
def route_rails(self):
|
||||
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
||||
input_names = ["in[{}]".format(x) for x in range(self.number_of_inputs)]
|
||||
offset = vector(0.5*self.m2_width,2*self.m1_width)
|
||||
|
|
@ -86,10 +86,21 @@ class hierarchical_predecode(design.design):
|
|||
length=self.height - 2*self.m1_width)
|
||||
|
||||
|
||||
def add_input_inverters(self):
|
||||
def create_input_inverters(self):
|
||||
""" Create the input inverters to invert input signals for the decode stage. """
|
||||
|
||||
self.in_inst = []
|
||||
for inv_num in range(self.number_of_inputs):
|
||||
name = "Xpre_inv[{0}]".format(inv_num)
|
||||
self.in_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv))
|
||||
self.connect_inst(["in[{0}]".format(inv_num),
|
||||
"inbar[{0}]".format(inv_num),
|
||||
"vdd", "gnd"])
|
||||
|
||||
def place_input_inverters(self):
|
||||
""" Place the input inverters to invert input signals for the decode stage. """
|
||||
|
||||
for inv_num in range(self.number_of_inputs):
|
||||
name = "Xpre_inv[{0}]".format(inv_num)
|
||||
if (inv_num % 2 == 0):
|
||||
|
|
@ -99,18 +110,26 @@ class hierarchical_predecode(design.design):
|
|||
y_off = (inv_num + 1) * (self.inv.height)
|
||||
mirror="MX"
|
||||
offset = vector(self.x_off_inv_1, y_off)
|
||||
self.in_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv,
|
||||
offset=offset,
|
||||
mirror=mirror))
|
||||
self.connect_inst(["in[{0}]".format(inv_num),
|
||||
"inbar[{0}]".format(inv_num),
|
||||
"vdd", "gnd"])
|
||||
self.place_inst(name=name,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def add_output_inverters(self):
|
||||
def create_output_inverters(self):
|
||||
""" Create inverters for the inverted output decode signals. """
|
||||
|
||||
self.inv_inst = []
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
name = "Xpre_nand_inv[{}]".format(inv_num)
|
||||
self.inv_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv))
|
||||
self.connect_inst(["Z[{}]".format(inv_num),
|
||||
"out[{}]".format(inv_num),
|
||||
"vdd", "gnd"])
|
||||
|
||||
|
||||
def place_output_inverters(self):
|
||||
""" Place inverters for the inverted output decode signals. """
|
||||
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
name = "Xpre_nand_inv[{}]".format(inv_num)
|
||||
if (inv_num % 2 == 0):
|
||||
|
|
@ -120,19 +139,23 @@ class hierarchical_predecode(design.design):
|
|||
y_off =(inv_num + 1)*self.inv.height
|
||||
mirror = "MX"
|
||||
offset = vector(self.x_off_inv_2, y_off)
|
||||
self.inv_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv,
|
||||
offset=offset,
|
||||
mirror=mirror))
|
||||
self.connect_inst(["Z[{}]".format(inv_num),
|
||||
"out[{}]".format(inv_num),
|
||||
"vdd", "gnd"])
|
||||
|
||||
|
||||
self.place_inst(name=name,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def add_nand(self,connections):
|
||||
def create_nand_array(self,connections):
|
||||
""" Create the NAND stage for the decodes """
|
||||
self.nand_inst = []
|
||||
for nand_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
|
||||
name = "Xpre{0}_nand[{1}]".format(inout,nand_input)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand))
|
||||
self.connect_inst(connections[nand_input])
|
||||
|
||||
|
||||
def place_nand_array(self):
|
||||
""" Place the NAND stage for the decodes """
|
||||
for nand_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
|
||||
name = "Xpre{0}_nand[{1}]".format(inout,nand_input)
|
||||
|
|
@ -143,11 +166,9 @@ class hierarchical_predecode(design.design):
|
|||
y_off = (nand_input + 1) * self.inv.height
|
||||
mirror = "MX"
|
||||
offset = vector(self.x_off_nand, y_off)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand,
|
||||
offset=offset,
|
||||
mirror=mirror))
|
||||
self.connect_inst(connections[nand_input])
|
||||
self.place_inst(name=name,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
|
||||
def route(self):
|
||||
|
|
|
|||
|
|
@ -14,8 +14,17 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.setup_constraints()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_netlist(self):
|
||||
self.create_input_inverters()
|
||||
self.create_output_inverters()
|
||||
connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"],
|
||||
["in[0]", "in[1]", "Z[3]", "vdd", "gnd"]]
|
||||
self.create_nand_array(connections)
|
||||
|
||||
def create_layout(self):
|
||||
""" The general organization is from left to right:
|
||||
|
|
@ -24,15 +33,12 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of NAND gates for inversion
|
||||
"""
|
||||
self.create_rails()
|
||||
self.add_input_inverters()
|
||||
self.add_output_inverters()
|
||||
connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"],
|
||||
["in[0]", "in[1]", "Z[3]", "vdd", "gnd"]]
|
||||
self.add_nand(connections)
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_output_inverters()
|
||||
self.place_nand_array()
|
||||
self.route()
|
||||
self.DRC_LVS()
|
||||
|
||||
def get_nand_input_line_combination(self):
|
||||
""" These are the decoder connections of the NAND gates to the A,B pins """
|
||||
|
|
|
|||
|
|
@ -14,19 +14,12 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.setup_constraints()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_layout(self):
|
||||
""" The general organization is from left to right:
|
||||
1) a set of M2 rails for input signals
|
||||
2) a set of inverters to invert input signals
|
||||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of NAND gates for inversion
|
||||
"""
|
||||
self.create_rails()
|
||||
self.add_input_inverters()
|
||||
self.add_output_inverters()
|
||||
def create_netlist(self):
|
||||
self.create_input_inverters()
|
||||
self.create_output_inverters()
|
||||
connections=[["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "inbar[2]", "Z[1]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "inbar[2]", "Z[2]", "vdd", "gnd"],
|
||||
|
|
@ -35,9 +28,23 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
["in[0]", "inbar[1]", "in[2]", "Z[5]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "in[2]", "Z[6]", "vdd", "gnd"],
|
||||
["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"]]
|
||||
self.add_nand(connections)
|
||||
self.route()
|
||||
self.create_nand_array(connections)
|
||||
|
||||
def create_layout(self):
|
||||
"""
|
||||
The general organization is from left to right:
|
||||
1) a set of M2 rails for input signals
|
||||
2) a set of inverters to invert input signals
|
||||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of NAND gates for inversion
|
||||
"""
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_output_inverters()
|
||||
self.place_nand_array()
|
||||
self.route()
|
||||
self.DRC_LVS()
|
||||
|
||||
def get_nand_input_line_combination(self):
|
||||
""" These are the decoder connections of the NAND gates to the A,B,C pins """
|
||||
combination = [["Abar[0]", "Abar[1]", "Abar[2]"],
|
||||
|
|
|
|||
|
|
@ -30,14 +30,18 @@ class ms_flop_array(design.design):
|
|||
self.height = self.ms.height
|
||||
self.words_per_row = int(self.columns / self.word_size)
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_ms_flop_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_ms_flop_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("din[{0}]".format(i))
|
||||
|
|
@ -52,25 +56,28 @@ class ms_flop_array(design.design):
|
|||
self.ms_inst={}
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xdff{0}".format(i)
|
||||
if (i % 2 == 0 or self.words_per_row>1):
|
||||
base = vector(i*self.ms.width,0)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector((i+1)*self.ms.width,0)
|
||||
mirror = "MY"
|
||||
|
||||
index = int(i/self.words_per_row)
|
||||
|
||||
self.ms_inst[index]=self.add_inst(name=name,
|
||||
mod=self.ms,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
mod=self.ms)
|
||||
self.connect_inst(["din[{0}]".format(index),
|
||||
"dout[{0}]".format(index),
|
||||
"dout_bar[{0}]".format(index),
|
||||
"clk",
|
||||
"vdd", "gnd"])
|
||||
|
||||
def place_ms_flop_array(self):
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xdff{0}".format(i)
|
||||
if (i % 2 == 0 or self.words_per_row>1):
|
||||
base = vector(i*self.ms.width,0)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector((i+1)*self.ms.width,0)
|
||||
mirror = "MY"
|
||||
self.place_inst(name=name,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
for i in range(self.word_size):
|
||||
|
|
|
|||
|
|
@ -11,21 +11,24 @@ class precharge_array(design.design):
|
|||
of bit line columns, height is the height of the bit-cell array.
|
||||
"""
|
||||
|
||||
def __init__(self, columns, size=1):
|
||||
design.design.__init__(self, "precharge_array")
|
||||
unique_id = 1
|
||||
|
||||
def __init__(self, columns, size=1, bitcell_bl="bl", bitcell_br="br"):
|
||||
name = "precharge_array_{}".format(precharge_array.unique_id)
|
||||
precharge_array.unique_id += 1
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
|
||||
self.columns = columns
|
||||
|
||||
self.pc_cell = precharge(name="precharge", size=size)
|
||||
self.pc_cell = precharge(name="precharge", size=size, bitcell_bl=bitcell_bl, bitcell_br=bitcell_br)
|
||||
self.add_mod(self.pc_cell)
|
||||
|
||||
self.width = self.columns * self.pc_cell.width
|
||||
self.height = self.pc_cell.height
|
||||
|
||||
self.add_pins()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
"""Adds pins for spice file"""
|
||||
|
|
@ -35,9 +38,14 @@ class precharge_array(design.design):
|
|||
self.add_pin("en")
|
||||
self.add_pin("vdd")
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_insts()
|
||||
|
||||
def create_layout(self):
|
||||
self.add_insts()
|
||||
self.place_insts()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
|
@ -51,20 +59,8 @@ class precharge_array(design.design):
|
|||
for inst in self.local_insts:
|
||||
self.copy_layout_pin(inst, "vdd")
|
||||
|
||||
|
||||
|
||||
def add_insts(self):
|
||||
"""Creates a precharge array by horizontally tiling the precharge cell"""
|
||||
self.local_insts = []
|
||||
for i in range(self.columns):
|
||||
name = "pre_column_{0}".format(i)
|
||||
offset = vector(self.pc_cell.width * i, 0)
|
||||
inst = self.add_inst(name=name,
|
||||
mod=self.pc_cell,
|
||||
offset=offset)
|
||||
self.local_insts.append(inst)
|
||||
|
||||
self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), "en", "vdd"])
|
||||
for i in range(len(self.local_insts)):
|
||||
inst = self.local_insts[i]
|
||||
bl_pin = inst.get_pin("bl")
|
||||
self.add_layout_pin(text="bl[{0}]".format(i),
|
||||
layer="metal2",
|
||||
|
|
@ -77,4 +73,25 @@ class precharge_array(design.design):
|
|||
offset=br_pin.ll(),
|
||||
width=drc["minwidth_metal2"],
|
||||
height=bl_pin.height())
|
||||
|
||||
|
||||
def create_insts(self):
|
||||
"""Creates a precharge array by horizontally tiling the precharge cell"""
|
||||
self.local_insts = []
|
||||
for i in range(self.columns):
|
||||
name = "pre_column_{0}".format(i)
|
||||
offset = vector(self.pc_cell.width * i, 0)
|
||||
inst = self.add_inst(name=name,
|
||||
mod=self.pc_cell,
|
||||
offset=offset)
|
||||
self.local_insts.append(inst)
|
||||
self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), "en", "vdd"])
|
||||
|
||||
|
||||
def place_insts(self):
|
||||
""" Places precharge array by horizontally tiling the precharge cell"""
|
||||
for i in range(self.columns):
|
||||
name = "pre_column_{0}".format(i)
|
||||
offset = vector(self.pc_cell.width * i, 0)
|
||||
inst = self.place_inst(name=name,
|
||||
offset=offset)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class sense_amp_array(design.design):
|
|||
self.height = self.amp.height
|
||||
self.width = self.amp.width * self.word_size * self.words_per_row
|
||||
|
||||
self.add_pins()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
|
@ -42,36 +42,42 @@ class sense_amp_array(design.design):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_sense_amp_array()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.add_sense_amp()
|
||||
self.connect_rails()
|
||||
self.place_sense_amp_array()
|
||||
self.add_layout_pins()
|
||||
self.route_rails()
|
||||
|
||||
|
||||
def add_sense_amp(self):
|
||||
|
||||
bl_pin = self.amp.get_pin("bl")
|
||||
br_pin = self.amp.get_pin("br")
|
||||
dout_pin = self.amp.get_pin("dout")
|
||||
|
||||
amp_spacing = self.amp.width * self.words_per_row
|
||||
def create_sense_amp_array(self):
|
||||
self.local_insts = []
|
||||
for i in range(0,self.word_size):
|
||||
|
||||
name = "sa_d{0}".format(i)
|
||||
amp_position = vector(amp_spacing * i, 0)
|
||||
|
||||
bl_offset = amp_position + bl_pin.ll().scale(1,0)
|
||||
br_offset = amp_position + br_pin.ll().scale(1,0)
|
||||
dout_offset = amp_position + dout_pin.ll()
|
||||
|
||||
inst = self.add_inst(name=name,
|
||||
mod=self.amp,
|
||||
offset=amp_position)
|
||||
self.local_insts.append(self.add_inst(name=name,
|
||||
mod=self.amp))
|
||||
self.connect_inst(["bl[{0}]".format(i),
|
||||
"br[{0}]".format(i),
|
||||
"data[{0}]".format(i),
|
||||
"en", "vdd", "gnd"])
|
||||
|
||||
def place_sense_amp_array(self):
|
||||
|
||||
amp_spacing = self.amp.width * self.words_per_row
|
||||
for i in range(0,self.word_size):
|
||||
|
||||
name = "sa_d{0}".format(i)
|
||||
amp_position = vector(amp_spacing * i, 0)
|
||||
self.place_inst(name=name,
|
||||
offset=amp_position)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
for i in range(len(self.local_insts)):
|
||||
inst = self.local_insts[i]
|
||||
|
||||
gnd_pos = inst.get_pin("gnd").center()
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
|
|
@ -79,33 +85,36 @@ class sense_amp_array(design.design):
|
|||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer="metal3",
|
||||
offset=gnd_pos)
|
||||
|
||||
vdd_pos = inst.get_pin("vdd").center()
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=vdd_pos)
|
||||
self.add_layout_pin_rect_center(text="vdd",
|
||||
layer="metal3",
|
||||
offset=vdd_pos)
|
||||
|
||||
bl_pin = inst.get_pin("bl")
|
||||
br_pin = inst.get_pin("br")
|
||||
dout_pin = inst.get_pin("dout")
|
||||
|
||||
self.add_layout_pin(text="bl[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=bl_offset,
|
||||
offset=bl_pin.ll(),
|
||||
width=bl_pin.width(),
|
||||
height=bl_pin.height())
|
||||
self.add_layout_pin(text="br[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=br_offset,
|
||||
offset=br_pin.ll(),
|
||||
width=br_pin.width(),
|
||||
height=br_pin.height())
|
||||
|
||||
self.add_layout_pin(text="data[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=dout_offset,
|
||||
offset=dout_pin.ll(),
|
||||
width=dout_pin.width(),
|
||||
height=dout_pin.height())
|
||||
|
||||
|
||||
def connect_rails(self):
|
||||
|
||||
def route_rails(self):
|
||||
# add sclk rail across entire array
|
||||
sclk_offset = self.amp.get_pin("en").ll().scale(0,1)
|
||||
self.add_layout_pin(text="en",
|
||||
|
|
|
|||
|
|
@ -20,9 +20,8 @@ class single_level_column_mux_array(design.design):
|
|||
self.columns = columns
|
||||
self.word_size = word_size
|
||||
self.words_per_row = int(self.columns / self.word_size)
|
||||
self.add_pins()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(self.columns):
|
||||
|
|
@ -35,10 +34,14 @@ class single_level_column_mux_array(design.design):
|
|||
self.add_pin("br_out[{}]".format(i))
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_layout(self):
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.setup_layout_constants()
|
||||
self.create_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.setup_layout_constants()
|
||||
self.place_array()
|
||||
self.add_routing()
|
||||
# Find the highest shapes to determine height before adding well
|
||||
highest = self.find_highest_coords()
|
||||
|
|
@ -46,6 +49,7 @@ class single_level_column_mux_array(design.design):
|
|||
self.add_layout_pins()
|
||||
self.add_enclosure(self.mux_inst, "pwell")
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
|
|
@ -65,14 +69,11 @@ class single_level_column_mux_array(design.design):
|
|||
|
||||
def create_array(self):
|
||||
self.mux_inst = []
|
||||
|
||||
# For every column, add a pass gate
|
||||
for col_num in range(self.columns):
|
||||
name = "XMUX{0}".format(col_num)
|
||||
x_off = vector(col_num * self.mux.width, self.route_height)
|
||||
self.mux_inst.append(self.add_inst(name=name,
|
||||
mod=self.mux,
|
||||
offset=x_off))
|
||||
mod=self.mux))
|
||||
|
||||
self.connect_inst(["bl[{}]".format(col_num),
|
||||
"br[{}]".format(col_num),
|
||||
|
|
@ -81,6 +82,14 @@ class single_level_column_mux_array(design.design):
|
|||
"sel[{}]".format(col_num % self.words_per_row),
|
||||
"gnd"])
|
||||
|
||||
def place_array(self):
|
||||
# For every column, add a pass gate
|
||||
for col_num in range(self.columns):
|
||||
name = "XMUX{0}".format(col_num)
|
||||
x_off = vector(col_num * self.mux.width, self.route_height)
|
||||
self.place_inst(name=name,
|
||||
offset=x_off)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the pins after we determine the height. """
|
||||
|
|
|
|||
|
|
@ -27,14 +27,17 @@ class tri_gate_array(design.design):
|
|||
self.width = (self.columns / self.words_per_row) * self.tri.width
|
||||
self.height = self.tri.height
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_layout(self):
|
||||
"""generate layout """
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
"""create the name of pins depend on the word size"""
|
||||
|
|
@ -50,15 +53,21 @@ class tri_gate_array(design.design):
|
|||
self.tri_inst = {}
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xtri_gate{0}".format(i)
|
||||
base = vector(i*self.tri.width, 0)
|
||||
self.tri_inst[i]=self.add_inst(name=name,
|
||||
mod=self.tri,
|
||||
offset=base)
|
||||
mod=self.tri)
|
||||
index = int(i/self.words_per_row)
|
||||
self.connect_inst(["in[{0}]".format(index),
|
||||
"out[{0}]".format(index),
|
||||
"en", "en_bar", "vdd", "gnd"])
|
||||
|
||||
def place_array(self):
|
||||
""" Place the tri gate to the array """
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xtri_gate{0}".format(i)
|
||||
base = vector(i*self.tri.width, 0)
|
||||
self.place_inst(name=name,
|
||||
offset=base)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ class wordline_driver(design.design):
|
|||
design.design.__init__(self, "wordline_driver")
|
||||
|
||||
self.rows = rows
|
||||
self.add_pins()
|
||||
self.design_layout()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.offset_all_coordinates()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
|
@ -36,14 +36,18 @@ class wordline_driver(design.design):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def design_layout(self):
|
||||
self.create_modules()
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_drivers()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_drivers()
|
||||
self.route_layout()
|
||||
self.route_vdd_gnd()
|
||||
|
||||
|
||||
def create_modules(self):
|
||||
def add_modules(self):
|
||||
self.inv = pinv()
|
||||
self.add_mod(self.inv)
|
||||
|
||||
|
|
@ -84,7 +88,37 @@ class wordline_driver(design.design):
|
|||
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
def create_drivers(self):
|
||||
self.inv1_inst = []
|
||||
self.nand_inst = []
|
||||
self.inv2_inst = []
|
||||
for row in range(self.rows):
|
||||
name_inv1 = "wl_driver_inv_en{}".format(row)
|
||||
name_nand = "wl_driver_nand{}".format(row)
|
||||
name_inv2 = "wl_driver_inv{}".format(row)
|
||||
|
||||
# add inv1 based on the info above
|
||||
self.inv1_inst.append(self.add_inst(name=name_inv1,
|
||||
mod=self.inv_no_output))
|
||||
self.connect_inst(["en",
|
||||
"en_bar[{0}]".format(row),
|
||||
"vdd", "gnd"])
|
||||
# add nand 2
|
||||
self.nand_inst.append(self.add_inst(name=name_nand,
|
||||
mod=self.nand2))
|
||||
self.connect_inst(["en_bar[{0}]".format(row),
|
||||
"in[{0}]".format(row),
|
||||
"wl_bar[{0}]".format(row),
|
||||
"vdd", "gnd"])
|
||||
# add inv2
|
||||
self.inv2_inst.append(self.add_inst(name=name_inv2,
|
||||
mod=self.inv))
|
||||
self.connect_inst(["wl_bar[{0}]".format(row),
|
||||
"wl[{0}]".format(row),
|
||||
"vdd", "gnd"])
|
||||
|
||||
|
||||
def place_drivers(self):
|
||||
inv1_xoffset = 2*self.m1_width + 5*self.m1_space
|
||||
nand2_xoffset = inv1_xoffset + self.inv.width
|
||||
inv2_xoffset = nand2_xoffset + self.nand2.width
|
||||
|
|
@ -93,9 +127,6 @@ class wordline_driver(design.design):
|
|||
self.height = self.inv.height * self.rows
|
||||
|
||||
|
||||
self.inv1_inst = []
|
||||
self.nand_inst = []
|
||||
self.inv2_inst = []
|
||||
for row in range(self.rows):
|
||||
name_inv1 = "wl_driver_inv_en{}".format(row)
|
||||
name_nand = "wl_driver_nand{}".format(row)
|
||||
|
|
@ -113,30 +144,17 @@ class wordline_driver(design.design):
|
|||
inv2_offset=[inv2_xoffset, y_offset]
|
||||
|
||||
# add inv1 based on the info above
|
||||
self.inv1_inst.append(self.add_inst(name=name_inv1,
|
||||
mod=self.inv_no_output,
|
||||
offset=inv1_offset,
|
||||
mirror=inst_mirror))
|
||||
self.connect_inst(["en",
|
||||
"en_bar[{0}]".format(row),
|
||||
"vdd", "gnd"])
|
||||
self.place_inst(name=name_inv1,
|
||||
offset=inv1_offset,
|
||||
mirror=inst_mirror)
|
||||
# add nand 2
|
||||
self.nand_inst.append(self.add_inst(name=name_nand,
|
||||
mod=self.nand2,
|
||||
offset=nand2_offset,
|
||||
mirror=inst_mirror))
|
||||
self.connect_inst(["en_bar[{0}]".format(row),
|
||||
"in[{0}]".format(row),
|
||||
"wl_bar[{0}]".format(row),
|
||||
"vdd", "gnd"])
|
||||
self.place_inst(name=name_nand,
|
||||
offset=nand2_offset,
|
||||
mirror=inst_mirror)
|
||||
# add inv2
|
||||
self.inv2_inst.append(self.add_inst(name=name_inv2,
|
||||
mod=self.inv,
|
||||
offset=inv2_offset,
|
||||
mirror=inst_mirror))
|
||||
self.connect_inst(["wl_bar[{0}]".format(row),
|
||||
"wl[{0}]".format(row),
|
||||
"vdd", "gnd"])
|
||||
self.place_inst(name=name_inv2,
|
||||
offset=inv2_offset,
|
||||
mirror=inst_mirror)
|
||||
|
||||
|
||||
def route_layout(self):
|
||||
|
|
|
|||
|
|
@ -28,9 +28,8 @@ class write_driver_array(design.design):
|
|||
self.width = self.columns * self.driver.width
|
||||
self.height = self.height = self.driver.height
|
||||
|
||||
self.add_pins()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(self.word_size):
|
||||
|
|
@ -42,20 +41,22 @@ class write_driver_array(design.design):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_layout(self):
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_write_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_write_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_write_array(self):
|
||||
self.driver_insts = {}
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xwrite_driver{}".format(i)
|
||||
base = vector(i * self.driver.width,0)
|
||||
|
||||
index = int(i/self.words_per_row)
|
||||
self.driver_insts[index]=self.add_inst(name=name,
|
||||
mod=self.driver,
|
||||
offset=base)
|
||||
mod=self.driver)
|
||||
|
||||
self.connect_inst(["data[{0}]".format(index),
|
||||
"bl[{0}]".format(index),
|
||||
|
|
@ -63,6 +64,15 @@ class write_driver_array(design.design):
|
|||
"en", "vdd", "gnd"])
|
||||
|
||||
|
||||
def place_write_array(self):
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xwrite_driver{}".format(i)
|
||||
base = vector(i * self.driver.width,0)
|
||||
|
||||
self.place_inst(name=name,
|
||||
offset=base)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
for i in range(self.word_size):
|
||||
din_pin = self.driver_insts[i].get_pin("din")
|
||||
|
|
|
|||
|
|
@ -40,7 +40,10 @@ report_status()
|
|||
import verify
|
||||
import sram
|
||||
|
||||
output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in ["sp","gds","v","lib","lef"]]
|
||||
output_extensions = ["sp","v","lib"]
|
||||
if not OPTS.netlist_only:
|
||||
output_extensions.extend(["gds","lef"])
|
||||
output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in output_extensions]
|
||||
print("Output files are: ")
|
||||
print(*output_files,sep="\n")
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ class options(optparse.Values):
|
|||
# This is the verbosity level to control debug information. 0 is none, 1
|
||||
# is minimal, etc.
|
||||
debug_level = 0
|
||||
# When enabled, layout is not generated (and no DRC or LVS are performed)
|
||||
netlist_only = False
|
||||
# This determines whether LVS and DRC is checked for each submodule.
|
||||
check_lvsdrc = True
|
||||
# Variable to select the variant of spice
|
||||
|
|
|
|||
|
|
@ -28,9 +28,8 @@ class pbitcell(pgate.pgate):
|
|||
self.num_write = num_write
|
||||
self.num_read = num_read
|
||||
|
||||
self.add_pins()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
pbitcell.width = self.width
|
||||
pbitcell.height = self.height
|
||||
|
|
@ -61,6 +60,9 @@ class pbitcell(pgate.pgate):
|
|||
self.add_pin("gnd")
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
||||
def create_layout(self):
|
||||
self.create_ptx()
|
||||
self.calculate_spacing()
|
||||
|
|
@ -75,6 +77,7 @@ class pbitcell(pgate.pgate):
|
|||
self.add_read_ports()
|
||||
self.extend_well()
|
||||
self.offset_all_coordinates()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def create_ptx(self):
|
||||
|
|
@ -1055,10 +1058,9 @@ class pbitcell(pgate.pgate):
|
|||
bitcell_pins.append("gnd")
|
||||
|
||||
return bitcell_pins
|
||||
|
||||
|
||||
def list_row_pins(self):
|
||||
""" Creates a list of all row pins (except for gnd and vdd) """
|
||||
def list_all_wl_names(self):
|
||||
""" Creates a list of all wordline pin names """
|
||||
row_pins = []
|
||||
for k in range(self.num_readwrite):
|
||||
row_pins.append("rwwl{0}".format(k))
|
||||
|
|
@ -1069,8 +1071,8 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
return row_pins
|
||||
|
||||
def list_read_row_pins(self):
|
||||
""" Creates a list of row pins associated with read ports """
|
||||
def list_read_wl_names(self):
|
||||
""" Creates a list of wordline pin names associated with read ports """
|
||||
row_pins = []
|
||||
for k in range(self.num_readwrite):
|
||||
row_pins.append("rwwl{0}".format(k))
|
||||
|
|
@ -1079,8 +1081,8 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
return row_pins
|
||||
|
||||
def list_write_row_pins(self):
|
||||
""" Creates a list of row pins associated with write ports """
|
||||
def list_write_wl_names(self):
|
||||
""" Creates a list of wordline pin names associated with write ports """
|
||||
row_pins = []
|
||||
for k in range(self.num_readwrite):
|
||||
row_pins.append("rwwl{0}".format(k))
|
||||
|
|
@ -1090,8 +1092,8 @@ class pbitcell(pgate.pgate):
|
|||
return row_pins
|
||||
|
||||
|
||||
def list_all_column_pins(self):
|
||||
""" Creates a list of all bitline pins """
|
||||
def list_all_bitline_names(self):
|
||||
""" Creates a list of all bitline pin names (both bl and br) """
|
||||
column_pins = []
|
||||
for k in range(self.num_readwrite):
|
||||
column_pins.append("rwbl{0}".format(k))
|
||||
|
|
@ -1105,8 +1107,8 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
return column_pins
|
||||
|
||||
def list_column_pins(self):
|
||||
""" Creates a list of all bitline bar pins """
|
||||
def list_all_bl_names(self):
|
||||
""" Creates a list of all bl pins names """
|
||||
column_pins = []
|
||||
for k in range(self.num_readwrite):
|
||||
column_pins.append("rwbl{0}".format(k))
|
||||
|
|
@ -1117,8 +1119,8 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
return column_pins
|
||||
|
||||
def list_column_bar_pins(self):
|
||||
""" Creates a list of all bitline bar pins """
|
||||
def list_all_br_names(self):
|
||||
""" Creates a list of all br pins names """
|
||||
column_pins = []
|
||||
for k in range(self.num_readwrite):
|
||||
column_pins.append("rwbl_bar{0}".format(k))
|
||||
|
|
@ -1129,8 +1131,8 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
return column_pins
|
||||
|
||||
def list_read_column_pins(self):
|
||||
""" Creates a list of column pins associated with read ports """
|
||||
def list_read_bl_names(self):
|
||||
""" Creates a list of bl pin names associated with read ports """
|
||||
column_pins = []
|
||||
for k in range(self.num_readwrite):
|
||||
column_pins.append("rwbl{0}".format(k))
|
||||
|
|
@ -1139,8 +1141,8 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
return column_pins
|
||||
|
||||
def list_read_bar_column_pins(self):
|
||||
""" Creates a list of column pins associated with read_bar ports """
|
||||
def list_read_br_names(self):
|
||||
""" Creates a list of br pin names associated with read ports """
|
||||
column_pins = []
|
||||
for k in range(self.num_readwrite):
|
||||
column_pins.append("rwbl_bar{0}".format(k))
|
||||
|
|
@ -1149,8 +1151,8 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
return column_pins
|
||||
|
||||
def list_write_column_pins(self):
|
||||
""" Creates a list of column pins associated with write ports """
|
||||
def list_write_bl_names(self):
|
||||
""" Creates a list of bl pin names associated with write ports """
|
||||
column_pins = []
|
||||
for k in range(self.num_readwrite):
|
||||
column_pins.append("rwbl{0}".format(k))
|
||||
|
|
@ -1159,8 +1161,8 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
return column_pins
|
||||
|
||||
def list_write_bar_column_pins(self):
|
||||
""" Creates a list of column pins asscociated with write_bar ports"""
|
||||
def list_write_br_names(self):
|
||||
""" Creates a list of br pin names asscociated with write ports"""
|
||||
column_pins = []
|
||||
for k in range(self.num_readwrite):
|
||||
column_pins.append("rwbl_bar{0}".format(k))
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ class pinv(pgate.pgate):
|
|||
self.height = height # Maybe minimize height if not defined in future?
|
||||
self.route_output = False
|
||||
|
||||
self.add_pins()
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
# for run-time, we won't check every transitor DRC/LVS independently
|
||||
|
|
@ -50,6 +51,10 @@ class pinv(pgate.pgate):
|
|||
""" Adds pins for spice netlist """
|
||||
self.add_pin_list(["A", "Z", "vdd", "gnd"])
|
||||
|
||||
def create_netlist(self):
|
||||
""" Calls all functions related to the generation of the netlist """
|
||||
self.add_pins()
|
||||
|
||||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
|
|
|
|||
|
|
@ -19,47 +19,43 @@ class pinvbuf(design.design):
|
|||
|
||||
def __init__(self, driver_size=4, height=bitcell.height, name=""):
|
||||
|
||||
stage_effort = 4
|
||||
self.stage_effort = 4
|
||||
self.row_height = height
|
||||
# FIXME: Change the number of stages to support high drives.
|
||||
|
||||
# stage effort of 4 or less
|
||||
# The pinvbuf has a FO of 2 for the first stage, so the second stage
|
||||
# should be sized "half" to prevent loading of the first stage
|
||||
predriver_size = max(int(driver_size/(stage_effort/2)),1)
|
||||
|
||||
self.driver_size = driver_size
|
||||
self.predriver_size = max(int(self.driver_size/(self.stage_effort/2)),1)
|
||||
if name=="":
|
||||
name = "pinvbuf_{0}_{1}_{2}".format(predriver_size, driver_size, pinvbuf.unique_id)
|
||||
name = "pinvbuf_{0}_{1}_{2}".format(self.predriver_size, self.driver_size, pinvbuf.unique_id)
|
||||
pinvbuf.unique_id += 1
|
||||
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
|
||||
|
||||
# Shield the cap, but have at least a stage effort of 4
|
||||
input_size = max(1,int(predriver_size/stage_effort))
|
||||
self.inv = pinv(size=input_size, height=height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.inv1 = pinv(size=predriver_size, height=height)
|
||||
self.add_mod(self.inv1)
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
self.inv2 = pinv(size=driver_size, height=height)
|
||||
self.add_mod(self.inv2)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_insts()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.width = 2*self.inv1.width + self.inv2.width
|
||||
self.height = 2*self.inv1.height
|
||||
|
||||
self.create_layout()
|
||||
|
||||
|
||||
self.place_insts()
|
||||
self.route_wires()
|
||||
self.add_layout_pins()
|
||||
|
||||
self.offset_all_coordinates()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_layout(self):
|
||||
self.add_pins()
|
||||
self.add_insts()
|
||||
self.add_wires()
|
||||
self.add_layout_pins()
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("A")
|
||||
|
|
@ -68,35 +64,58 @@ class pinvbuf(design.design):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def add_insts(self):
|
||||
# Add INV1 to the right (capacitance shield)
|
||||
def add_modules(self):
|
||||
|
||||
# Shield the cap, but have at least a stage effort of 4
|
||||
input_size = max(1,int(self.predriver_size/self.stage_effort))
|
||||
self.inv = pinv(size=input_size, height=self.row_height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.inv1 = pinv(size=self.predriver_size, height=self.row_height)
|
||||
self.add_mod(self.inv1)
|
||||
|
||||
self.inv2 = pinv(size=self.driver_size, height=self.row_height)
|
||||
self.add_mod(self.inv2)
|
||||
|
||||
def create_insts(self):
|
||||
# Create INV1 (capacitance shield)
|
||||
self.inv1_inst=self.add_inst(name="buf_inv1",
|
||||
mod=self.inv,
|
||||
offset=vector(0,0))
|
||||
mod=self.inv)
|
||||
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
|
||||
|
||||
|
||||
# Add INV2 to the right
|
||||
self.inv2_inst=self.add_inst(name="buf_inv2",
|
||||
mod=self.inv1,
|
||||
offset=vector(self.inv1_inst.rx(),0))
|
||||
mod=self.inv1)
|
||||
self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
|
||||
|
||||
# Add INV3 to the right
|
||||
self.inv3_inst=self.add_inst(name="buf_inv3",
|
||||
mod=self.inv2,
|
||||
offset=vector(self.inv2_inst.rx(),0))
|
||||
mod=self.inv2)
|
||||
self.connect_inst(["z_int", "Zb", "vdd", "gnd"])
|
||||
|
||||
# Add INV4 to the bottom
|
||||
self.inv4_inst=self.add_inst(name="buf_inv4",
|
||||
mod=self.inv2,
|
||||
offset=vector(self.inv2_inst.rx(),2*self.inv2.height),
|
||||
mirror = "MX")
|
||||
mod=self.inv2)
|
||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||
|
||||
def place_insts(self):
|
||||
# Add INV1 to the right (capacitance shield)
|
||||
self.place_inst(name="buf_inv1",
|
||||
offset=vector(0,0))
|
||||
|
||||
# Add INV2 to the right
|
||||
self.place_inst(name="buf_inv2",
|
||||
offset=vector(self.inv1_inst.rx(),0))
|
||||
|
||||
# Add INV3 to the right
|
||||
self.place_inst(name="buf_inv3",
|
||||
offset=vector(self.inv2_inst.rx(),0))
|
||||
|
||||
# Add INV4 to the bottom
|
||||
self.place_inst(name="buf_inv4",
|
||||
offset=vector(self.inv2_inst.rx(),2*self.inv2.height),
|
||||
mirror = "MX")
|
||||
|
||||
|
||||
def add_wires(self):
|
||||
def route_wires(self):
|
||||
# inv1 Z to inv2 A
|
||||
z1_pin = self.inv1_inst.get_pin("Z")
|
||||
a2_pin = self.inv2_inst.get_pin("A")
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class pnand2(pgate.pgate):
|
|||
debug.check(size==1,"Size 1 pnand2 is only supported now.")
|
||||
self.tx_mults = 1
|
||||
|
||||
self.add_pins()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
#self.DRC_LVS()
|
||||
|
||||
|
|
@ -44,6 +44,10 @@ class pnand2(pgate.pgate):
|
|||
""" Adds pins for spice netlist """
|
||||
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
||||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ class pnand3(pgate.pgate):
|
|||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
bitcell = getattr(c, OPTS.bitcell)
|
||||
mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
bitcell = mod_bitcell()
|
||||
|
||||
unique_id = 1
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ class pnand3(pgate.pgate):
|
|||
debug.check(size==1,"Size 1 pnand3 is only supported now.")
|
||||
self.tx_mults = 1
|
||||
|
||||
self.add_pins()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
#self.DRC_LVS()
|
||||
|
||||
|
|
@ -46,6 +47,9 @@ class pnand3(pgate.pgate):
|
|||
""" Adds pins for spice netlist """
|
||||
self.add_pin_list(["A", "B", "C", "Z", "vdd", "gnd"])
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
||||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class pnor2(pgate.pgate):
|
|||
debug.check(size==1,"Size 1 pnor2 is only supported now.")
|
||||
self.tx_mults = 1
|
||||
|
||||
self.add_pins()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
#self.DRC_LVS()
|
||||
|
||||
|
|
@ -45,6 +45,9 @@ class pnor2(pgate.pgate):
|
|||
""" Adds pins for spice netlist """
|
||||
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
||||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,11 @@ class precharge(pgate.pgate):
|
|||
This module implements the precharge bitline cell used in the design.
|
||||
"""
|
||||
|
||||
def __init__(self, name, size=1, BL="bl", BR="br"):
|
||||
unique_id = 1
|
||||
|
||||
def __init__(self, name, size=1, bitcell_bl="bl", bitcell_br="br"):
|
||||
name = name+"_{}".format(precharge.unique_id)
|
||||
precharge.unique_id += 1
|
||||
pgate.pgate.__init__(self, name)
|
||||
debug.info(2, "create single precharge cell: {0}".format(name))
|
||||
|
||||
|
|
@ -24,16 +28,18 @@ class precharge(pgate.pgate):
|
|||
self.beta = parameter["beta"]
|
||||
self.ptx_width = self.beta*parameter["min_tx_size"]
|
||||
self.width = self.bitcell.width
|
||||
self.BL = BL
|
||||
self.BR = BR
|
||||
self.bitcell_bl = bitcell_bl
|
||||
self.bitcell_br = bitcell_br
|
||||
|
||||
self.add_pins()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin_list([self.BL, self.BR, "en", "vdd"])
|
||||
self.add_pin_list(["bl", "br", "en", "vdd"])
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
||||
def create_layout(self):
|
||||
self.create_ptx()
|
||||
self.add_ptx()
|
||||
|
|
@ -43,6 +49,7 @@ class precharge(pgate.pgate):
|
|||
self.add_vdd_rail()
|
||||
self.add_bitlines()
|
||||
self.connect_to_bitlines()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_ptx(self):
|
||||
"""Initializes the upper and lower pmos"""
|
||||
|
|
@ -84,12 +91,12 @@ class precharge(pgate.pgate):
|
|||
"""Adds both the upper_pmos and lower_pmos to the module"""
|
||||
# adds the lower pmos to layout
|
||||
#base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0)
|
||||
self.lower_pmos_position = vector(self.bitcell.get_pin(self.BL).lx(),
|
||||
self.lower_pmos_position = vector(self.bitcell.get_pin(self.bitcell_bl).lx(),
|
||||
self.pmos.active_offset.y)
|
||||
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
|
||||
mod=self.pmos,
|
||||
offset=self.lower_pmos_position)
|
||||
self.connect_inst([self.BL, "en", self.BR, "vdd"])
|
||||
self.connect_inst(["bl", "en", "br", "vdd"])
|
||||
|
||||
# adds the upper pmos(s) to layout
|
||||
ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width
|
||||
|
|
@ -97,13 +104,13 @@ class precharge(pgate.pgate):
|
|||
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
|
||||
mod=self.pmos,
|
||||
offset=self.upper_pmos1_pos)
|
||||
self.connect_inst([self.BL, "en", "vdd", "vdd"])
|
||||
self.connect_inst(["bl", "en", "vdd", "vdd"])
|
||||
|
||||
upper_pmos2_pos = self.upper_pmos1_pos + self.overlap_offset
|
||||
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
|
||||
mod=self.pmos,
|
||||
offset=upper_pmos2_pos)
|
||||
self.connect_inst([self.BR, "en", "vdd", "vdd"])
|
||||
self.connect_inst(["br", "en", "vdd", "vdd"])
|
||||
|
||||
def connect_poly(self):
|
||||
"""Connects the upper and lower pmos together"""
|
||||
|
|
@ -161,16 +168,16 @@ class precharge(pgate.pgate):
|
|||
def add_bitlines(self):
|
||||
"""Adds both bit-line and bit-line-bar to the module"""
|
||||
# adds the BL on metal 2
|
||||
offset = vector(self.bitcell.get_pin(self.BL).cx(),0) - vector(0.5 * self.m2_width,0)
|
||||
self.add_layout_pin(text=self.BL,
|
||||
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0)
|
||||
self.add_layout_pin(text="bl",
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
width=drc['minwidth_metal2'],
|
||||
height=self.height)
|
||||
|
||||
# adds the BR on metal 2
|
||||
offset = vector(self.bitcell.get_pin(self.BR).cx(),0) - vector(0.5 * self.m2_width,0)
|
||||
self.add_layout_pin(text=self.BR,
|
||||
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0)
|
||||
self.add_layout_pin(text="br",
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
width=drc['minwidth_metal2'],
|
||||
|
|
@ -178,10 +185,10 @@ class precharge(pgate.pgate):
|
|||
|
||||
def connect_to_bitlines(self):
|
||||
self.add_bitline_contacts()
|
||||
self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin(self.BL))
|
||||
self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin(self.BR))
|
||||
self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),self.get_pin(self.BL))
|
||||
self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin(self.BR))
|
||||
self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl"))
|
||||
self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin("br"))
|
||||
self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl"))
|
||||
self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br"))
|
||||
|
||||
|
||||
def add_bitline_contacts(self):
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class ptx(design.design):
|
|||
self.connect_poly = connect_poly
|
||||
self.num_contacts = num_contacts
|
||||
|
||||
self.create_spice()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
self.translate_all(self.active_offset)
|
||||
|
|
@ -57,7 +57,7 @@ class ptx(design.design):
|
|||
self.add_poly()
|
||||
self.add_active_contacts()
|
||||
|
||||
def create_spice(self):
|
||||
def create_netlist(self):
|
||||
self.add_pin_list(["D", "G", "S", "B"])
|
||||
|
||||
# self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
|
||||
|
|
|
|||
|
|
@ -23,12 +23,15 @@ class single_level_column_mux(design.design):
|
|||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.ptx_width = tx_size * drc["minwidth_tx"]
|
||||
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
|
||||
self.add_ptx()
|
||||
|
||||
def create_layout(self):
|
||||
self.pin_height = 2*self.m2_width
|
||||
self.width = self.bitcell.width
|
||||
self.height = self.nmos_upper.uy() + self.pin_height
|
||||
|
|
|
|||
|
|
@ -37,9 +37,7 @@ class sram():
|
|||
else:
|
||||
debug.error("Invalid number of banks.",-1)
|
||||
|
||||
self.s.compute_sizes()
|
||||
self.s.create_modules()
|
||||
self.s.add_pins()
|
||||
self.s.create_netlist()
|
||||
self.s.create_layout()
|
||||
|
||||
# Can remove the following, but it helps for debug!
|
||||
|
|
@ -101,19 +99,20 @@ class sram():
|
|||
lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file)
|
||||
print_time("Characterization", datetime.datetime.now(), start_time)
|
||||
|
||||
# Write the layout
|
||||
start_time = datetime.datetime.now()
|
||||
gdsname = OPTS.output_path + self.s.name + ".gds"
|
||||
print("GDS: Writing to {0}".format(gdsname))
|
||||
self.s.gds_write(gdsname)
|
||||
print_time("GDS", datetime.datetime.now(), start_time)
|
||||
if not OPTS.netlist_only:
|
||||
# Write the layout
|
||||
start_time = datetime.datetime.now()
|
||||
gdsname = OPTS.output_path + self.s.name + ".gds"
|
||||
print("GDS: Writing to {0}".format(gdsname))
|
||||
self.s.gds_write(gdsname)
|
||||
print_time("GDS", datetime.datetime.now(), start_time)
|
||||
|
||||
# Create a LEF physical model
|
||||
start_time = datetime.datetime.now()
|
||||
lefname = OPTS.output_path + self.s.name + ".lef"
|
||||
print("LEF: Writing to {0}".format(lefname))
|
||||
self.s.lef_write(lefname)
|
||||
print_time("LEF", datetime.datetime.now(), start_time)
|
||||
# Create a LEF physical model
|
||||
start_time = datetime.datetime.now()
|
||||
lefname = OPTS.output_path + self.s.name + ".lef"
|
||||
print("LEF: Writing to {0}".format(lefname))
|
||||
self.s.lef_write(lefname)
|
||||
print_time("LEF", datetime.datetime.now(), start_time)
|
||||
|
||||
# Write a verilog model
|
||||
start_time = datetime.datetime.now()
|
||||
|
|
|
|||
|
|
@ -20,14 +20,38 @@ class sram_1bank(sram_base):
|
|||
def __init__(self, word_size, num_words, name):
|
||||
sram_base.__init__(self, word_size, num_words, 1, name)
|
||||
|
||||
def add_modules(self):
|
||||
def create_netlist(self):
|
||||
self.compute_sizes()
|
||||
self.add_modules()
|
||||
# Must run this after add modules to get control pin names
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
|
||||
def create_modules(self):
|
||||
"""
|
||||
This adds the moduels for a single bank SRAM with control
|
||||
This adds the modules for a single bank SRAM with control
|
||||
logic.
|
||||
"""
|
||||
|
||||
self.bank_inst = self.create_bank()
|
||||
|
||||
self.control_logic_inst = self.create_control_logic()
|
||||
|
||||
self.row_addr_dff_inst = self.create_row_addr_dff()
|
||||
|
||||
if self.col_addr_dff:
|
||||
self.col_addr_dff_inst = self.create_col_addr_dff()
|
||||
|
||||
self.data_dff_inst = self.create_data_dff()
|
||||
|
||||
def place_modules(self):
|
||||
"""
|
||||
This places the modules for a single bank SRAM with control
|
||||
logic.
|
||||
"""
|
||||
|
||||
# No orientation or offset
|
||||
self.bank_inst = self.add_bank(0, [0, 0], 1, 1)
|
||||
self.place_bank(self.bank_inst, [0, 0], 1, 1)
|
||||
|
||||
# The control logic is placed such that the vertical center (between the delay/RBL and
|
||||
# the actual control logic is aligned with the vertical center of the bank (between
|
||||
|
|
@ -36,12 +60,14 @@ class sram_1bank(sram_base):
|
|||
# up to the row address DFFs.
|
||||
control_pos = vector(-self.control_logic.width - 2*self.m2_pitch,
|
||||
self.bank.bank_center.y - self.control_logic.control_logic_center.y)
|
||||
self.add_control_logic(position=control_pos)
|
||||
self.place_inst(name=self.control_logic_inst.name,
|
||||
offset=control_pos)
|
||||
|
||||
# The row address bits are placed above the control logic aligned on the right.
|
||||
row_addr_pos = vector(self.control_logic_inst.rx() - self.row_addr_dff.width,
|
||||
self.control_logic_inst.uy())
|
||||
self.add_row_addr_dff(row_addr_pos)
|
||||
self.place_inst(name=self.row_addr_dff_inst.name,
|
||||
offset=row_addr_pos)
|
||||
|
||||
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
||||
data_gap = -self.m2_pitch*(self.word_size+1)
|
||||
|
|
@ -51,7 +77,8 @@ class sram_1bank(sram_base):
|
|||
if self.col_addr_dff:
|
||||
col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width,
|
||||
data_gap - self.col_addr_dff.height)
|
||||
self.add_col_addr_dff(col_addr_pos)
|
||||
self.place_inst(name=self.col_addr_dff_inst.name,
|
||||
offset=col_addr_pos)
|
||||
|
||||
# Add the data flops below the bank to the right of the center of bank:
|
||||
# This relies on the center point of the bank:
|
||||
|
|
@ -60,12 +87,13 @@ class sram_1bank(sram_base):
|
|||
# sense amps.
|
||||
data_pos = vector(self.bank.bank_center.x,
|
||||
data_gap - self.data_dff.height)
|
||||
self.add_data_dff(data_pos)
|
||||
self.place_inst(self.data_dff.name,
|
||||
offset=data_pos)
|
||||
|
||||
# two supply rails are already included in the bank, so just 2 here.
|
||||
self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
|
||||
self.height = self.bank.height
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
"""
|
||||
Add the top-level pins for a single bank SRAM with control.
|
||||
|
|
@ -75,8 +103,8 @@ class sram_1bank(sram_base):
|
|||
self.copy_layout_pin(self.control_logic_inst, n)
|
||||
|
||||
for i in range(self.word_size):
|
||||
dout_name = "dout[{}]".format(i)
|
||||
self.copy_layout_pin(self.bank_inst, dout_name, dout_name.upper())
|
||||
dout_name = "dout0[{}]".format(i)
|
||||
self.copy_layout_pin(self.bank_inst, dout_name, "DOUT[{}]".format(i))
|
||||
|
||||
# Lower address bits
|
||||
for i in range(self.col_addr_size):
|
||||
|
|
@ -179,7 +207,7 @@ class sram_1bank(sram_base):
|
|||
""" Connect the output of the row flops to the bank pins """
|
||||
for i in range(self.row_addr_size):
|
||||
flop_name = "dout[{}]".format(i)
|
||||
bank_name = "addr[{}]".format(i+self.col_addr_size)
|
||||
bank_name = "addr0[{}]".format(i+self.col_addr_size)
|
||||
flop_pin = self.row_addr_dff_inst.get_pin(flop_name)
|
||||
bank_pin = self.bank_inst.get_pin(bank_name)
|
||||
flop_pos = flop_pin.center()
|
||||
|
|
@ -204,7 +232,7 @@ class sram_1bank(sram_base):
|
|||
data_dff_map = zip(dff_names, bus_names)
|
||||
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst, col_addr_bus_offsets)
|
||||
|
||||
bank_names = ["addr[{}]".format(x) for x in range(self.col_addr_size)]
|
||||
bank_names = ["addr0[{}]".format(x) for x in range(self.col_addr_size)]
|
||||
data_bank_map = zip(bank_names, bus_names)
|
||||
self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets)
|
||||
|
||||
|
|
@ -215,7 +243,7 @@ class sram_1bank(sram_base):
|
|||
offset = self.data_dff_inst.ul() + vector(0, self.m1_pitch)
|
||||
|
||||
dff_names = ["dout[{}]".format(x) for x in range(self.word_size)]
|
||||
bank_names = ["din[{}]".format(x) for x in range(self.word_size)]
|
||||
bank_names = ["din0[{}]".format(x) for x in range(self.word_size)]
|
||||
|
||||
route_map = list(zip(bank_names, dff_names))
|
||||
dff_pins = {key: self.data_dff_inst.get_pin(key) for key in dff_names }
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ class sram_base(design):
|
|||
self.num_words = num_words
|
||||
self.num_banks = num_banks
|
||||
|
||||
self.bank_insts = []
|
||||
|
||||
def compute_sizes(self):
|
||||
""" Computes the organization of the memory using bitcell size by trying to make it square."""
|
||||
|
||||
|
|
@ -114,12 +116,17 @@ class sram_base(design):
|
|||
self.add_pin("gnd","GROUND")
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
""" Netlist creation """
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
|
||||
def create_layout(self):
|
||||
""" Layout creation """
|
||||
self.add_modules()
|
||||
self.place_modules()
|
||||
self.route()
|
||||
self.add_lvs_correspondence_points()
|
||||
|
||||
|
||||
def compute_bus_sizes(self):
|
||||
""" Compute the independent bus widths shared between two and four bank SRAMs """
|
||||
|
||||
|
|
@ -150,7 +157,7 @@ class sram_base(design):
|
|||
""" Add the horizontal and vertical busses """
|
||||
# Vertical bus
|
||||
# The order of the control signals on the control bus:
|
||||
self.control_bus_names = ["clk_buf", "clk_buf_bar", "w_en", "s_en"]
|
||||
self.control_bus_names = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"]
|
||||
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=self.vertical_bus_offset,
|
||||
|
|
@ -235,7 +242,7 @@ class sram_base(design):
|
|||
self.copy_layout_pin(inst, "gnd")
|
||||
|
||||
|
||||
def create_multi_bank_modules(self):
|
||||
def add_multi_bank_modules(self):
|
||||
""" Create the multibank address flops and bank decoder """
|
||||
from dff_buf_array import dff_buf_array
|
||||
self.msb_address = dff_buf_array(name="msb_address",
|
||||
|
|
@ -247,7 +254,7 @@ class sram_base(design):
|
|||
self.msb_decoder = self.bank.decoder.pre2_4
|
||||
self.add_mod(self.msb_decoder)
|
||||
|
||||
def create_modules(self):
|
||||
def add_modules(self):
|
||||
""" Create all the modules that will be used """
|
||||
|
||||
from control_logic import control_logic
|
||||
|
|
@ -280,7 +287,7 @@ class sram_base(design):
|
|||
|
||||
# Create bank decoder
|
||||
if(self.num_banks > 1):
|
||||
self.create_multi_bank_modules()
|
||||
self.add_multi_bank_modules()
|
||||
|
||||
self.bank_count = 0
|
||||
|
||||
|
|
@ -289,7 +296,28 @@ class sram_base(design):
|
|||
|
||||
|
||||
|
||||
def add_bank(self, bank_num, position, x_flip, y_flip):
|
||||
def create_bank(self):
|
||||
""" Create a bank """
|
||||
bank_num = len(self.bank_insts)
|
||||
self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num),
|
||||
mod=self.bank))
|
||||
|
||||
temp = []
|
||||
for i in range(self.word_size):
|
||||
temp.append("DOUT[{0}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
temp.append("BANK_DIN[{0}]".format(i))
|
||||
for i in range(self.bank_addr_size):
|
||||
temp.append("A[{0}]".format(i))
|
||||
if(self.num_banks > 1):
|
||||
temp.append("bank_sel[{0}]".format(bank_num))
|
||||
temp.extend(["s_en0", "w_en0", "clk_buf_bar","clk_buf" , "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
return self.bank_insts[-1]
|
||||
|
||||
|
||||
def place_bank(self, bank_inst, position, x_flip, y_flip):
|
||||
""" Place a bank at the given position with orientations """
|
||||
|
||||
# x_flip == 1 --> no flip in x_axis
|
||||
|
|
@ -313,32 +341,19 @@ class sram_base(design):
|
|||
else:
|
||||
bank_mirror = "R0"
|
||||
|
||||
bank_inst=self.add_inst(name="bank{0}".format(bank_num),
|
||||
mod=self.bank,
|
||||
offset=position,
|
||||
mirror=bank_mirror,
|
||||
rotate=bank_rotation)
|
||||
|
||||
temp = []
|
||||
for i in range(self.word_size):
|
||||
temp.append("DOUT[{0}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
temp.append("BANK_DIN[{0}]".format(i))
|
||||
for i in range(self.bank_addr_size):
|
||||
temp.append("A[{0}]".format(i))
|
||||
if(self.num_banks > 1):
|
||||
temp.append("bank_sel[{0}]".format(bank_num))
|
||||
temp.extend(["s_en", "w_en", "clk_buf_bar","clk_buf" , "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
self.place_inst(name=bank_inst.name,
|
||||
offset=position,
|
||||
mirror=bank_mirror,
|
||||
rotate=bank_rotation)
|
||||
|
||||
return bank_inst
|
||||
|
||||
|
||||
def add_row_addr_dff(self, position):
|
||||
""" Add and place all address flops for the main decoder """
|
||||
self.row_addr_dff_inst = self.add_inst(name="row_address",
|
||||
mod=self.row_addr_dff,
|
||||
offset=position)
|
||||
|
||||
def create_row_addr_dff(self):
|
||||
""" Add all address flops for the main decoder """
|
||||
inst = self.add_inst(name="row_address",
|
||||
mod=self.row_addr_dff)
|
||||
|
||||
# inputs, outputs/output/bar
|
||||
inputs = []
|
||||
outputs = []
|
||||
|
|
@ -347,13 +362,13 @@ class sram_base(design):
|
|||
outputs.append("A[{}]".format(i+self.col_addr_size))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"])
|
||||
|
||||
return inst
|
||||
|
||||
def add_col_addr_dff(self, position):
|
||||
def create_col_addr_dff(self):
|
||||
""" Add and place all address flops for the column decoder """
|
||||
self.col_addr_dff_inst = self.add_inst(name="row_address",
|
||||
mod=self.col_addr_dff,
|
||||
offset=position)
|
||||
inst = self.add_inst(name="col_address",
|
||||
mod=self.col_addr_dff)
|
||||
|
||||
# inputs, outputs/output/bar
|
||||
inputs = []
|
||||
outputs = []
|
||||
|
|
@ -362,12 +377,13 @@ class sram_base(design):
|
|||
outputs.append("A[{}]".format(i))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"])
|
||||
|
||||
def add_data_dff(self, position):
|
||||
return inst
|
||||
|
||||
def create_data_dff(self):
|
||||
""" Add and place all data flops """
|
||||
self.data_dff_inst = self.add_inst(name="data_dff",
|
||||
mod=self.data_dff,
|
||||
offset=position)
|
||||
inst = self.add_inst(name="data_dff",
|
||||
mod=self.data_dff)
|
||||
|
||||
# inputs, outputs/output/bar
|
||||
inputs = []
|
||||
outputs = []
|
||||
|
|
@ -376,15 +392,16 @@ class sram_base(design):
|
|||
outputs.append("BANK_DIN[{}]".format(i))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"])
|
||||
return inst
|
||||
|
||||
def add_control_logic(self, position):
|
||||
def create_control_logic(self):
|
||||
""" Add and place control logic """
|
||||
self.control_logic_inst=self.add_inst(name="control",
|
||||
mod=self.control_logic,
|
||||
offset=position)
|
||||
inst = self.add_inst(name="control",
|
||||
mod=self.control_logic)
|
||||
|
||||
self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"])
|
||||
|
||||
|
||||
return inst
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -69,9 +69,9 @@ def check_file_format_tab(file_name):
|
|||
if len(key_positions)>10:
|
||||
line_numbers = key_positions[:10] + [" ..."]
|
||||
else:
|
||||
line_numbers = key_positoins
|
||||
line_numbers = key_positions
|
||||
debug.info(0, '\nFound ' + str(len(key_positions)) + ' tabs in ' +
|
||||
str(file_name) + ' (lines ' + ",".join(str(x) for x in line_positions) + ')')
|
||||
str(file_name) + ' (lines ' + ",".join(str(x) for x in line_numbers) + ')')
|
||||
f.close()
|
||||
return len(key_positions)
|
||||
|
||||
|
|
|
|||
|
|
@ -30,16 +30,16 @@ class precharge_test(openram_test):
|
|||
OPTS.rw_ports = 2
|
||||
OPTS.r_ports = 2
|
||||
OPTS.w_ports = 2
|
||||
tx = precharge.precharge(name="precharge_driver", size=1, BL="rwbl0", BR="rwbl_bar0")
|
||||
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="rwbl0", bitcell_br="rwbl_bar0")
|
||||
self.local_check(tx)
|
||||
|
||||
tx = precharge.precharge(name="precharge_driver", size=1, BL="wbl0", BR="wbl_bar0")
|
||||
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="wbl0", bitcell_br="wbl_bar0")
|
||||
self.local_check(tx)
|
||||
|
||||
tx = precharge.precharge(name="precharge_driver", size=1, BL="rbl0", BR="rbl_bar0")
|
||||
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="rbl0", bitcell_br="rbl_bar0")
|
||||
self.local_check(tx)
|
||||
|
||||
#globals.end_openram()
|
||||
globals.end_openram()
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -24,9 +24,23 @@ class precharge_test(openram_test):
|
|||
debug.info(2, "Checking 3 column precharge")
|
||||
pc = precharge_array.precharge_array(columns=3)
|
||||
self.local_check(pc)
|
||||
|
||||
debug.info(2, "Checking precharge for pbitcell")
|
||||
OPTS.bitcell = "pbitcell"
|
||||
OPTS.rw_ports = 2
|
||||
OPTS.r_ports = 2
|
||||
OPTS.w_ports = 2
|
||||
|
||||
pc = precharge_array.precharge_array(columns=3, bitcell_bl="rwbl0", bitcell_br="rwbl_bar0")
|
||||
self.local_check(pc)
|
||||
|
||||
pc = precharge_array.precharge_array(columns=3, bitcell_bl="wbl0", bitcell_br="wbl_bar0")
|
||||
self.local_check(pc)
|
||||
|
||||
pc = precharge_array.precharge_array(columns=3, bitcell_bl="rbl0", bitcell_br="rbl_bar0")
|
||||
self.local_check(pc)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Run a regression test on various srams
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header,openram_test
|
||||
import sys,os
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
@unittest.skip("Multiported Bank not working yet")
|
||||
class single_bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
global verify
|
||||
import verify
|
||||
|
||||
from bank import bank
|
||||
OPTS.bitcell = "pbitcell"
|
||||
|
||||
# testing all port configurations (with no column mux) to verify layout between bitcell array and peripheral circuitry
|
||||
OPTS.rw_ports = 2
|
||||
OPTS.w_ports = 2
|
||||
OPTS.r_ports = 2
|
||||
|
||||
debug.info(1, "No column mux")
|
||||
a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_2w_2r_single")
|
||||
self.local_check(a)
|
||||
"""
|
||||
OPTS.rw_ports = 0
|
||||
OPTS.w_ports = 2
|
||||
OPTS.r_ports = 2
|
||||
|
||||
debug.info(1, "No column mux")
|
||||
a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_0rw_2w_2r_single")
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.rw_ports = 2
|
||||
OPTS.w_ports = 0
|
||||
OPTS.r_ports = 2
|
||||
|
||||
debug.info(1, "No column mux")
|
||||
a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_0w_2r_single")
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.rw_ports = 2
|
||||
OPTS.w_ports = 2
|
||||
OPTS.r_ports = 0
|
||||
|
||||
debug.info(1, "No column mux")
|
||||
a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_2w_0r_single")
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.rw_ports = 2
|
||||
OPTS.w_ports = 0
|
||||
OPTS.r_ports = 0
|
||||
|
||||
debug.info(1, "No column mux")
|
||||
a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_0w_0r_single")
|
||||
self.local_check(a)
|
||||
|
||||
# testing with various column muxes
|
||||
OPTS.rw_ports = 2
|
||||
OPTS.w_ports = 2
|
||||
OPTS.r_ports = 2
|
||||
|
||||
debug.info(1, "Two way column mux")
|
||||
a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single")
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Four way column mux")
|
||||
a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single")
|
||||
self.local_check(a)
|
||||
|
||||
# Eight way has a short circuit of one column mux select to gnd rail
|
||||
debug.info(1, "Eight way column mux")
|
||||
a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single")
|
||||
self.local_check(a)
|
||||
"""
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main()
|
||||
Loading…
Reference in New Issue