Merge branch 'dev' into multiport_characterization

This commit is contained in:
Hunter Nichols 2018-08-28 00:37:26 -07:00
commit 0bb4b48439
40 changed files with 1422 additions and 835 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

8
compiler/tests/04_precharge_test.py Executable file → Normal file
View File

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

16
compiler/tests/08_precharge_array_test.py Executable file → Normal file
View File

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

View File

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