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])) ll = vector(min(first[0],second[0]),min(first[1],second[1]))
ur = vector(max(first[0],second[0]),max(first[1],second[1])) ur = vector(max(first[0],second[0]),max(first[1],second[1]))
self.boundary=[ll,ur] 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): def compute_boundary(self,offset=vector(0,0),mirror="",rotate=0):
""" Transform with offset, mirror and rotation to get the absolute pin location. """ 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 An instance of an instance/module with a specified location and
rotation 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""" """Initializes an instance to represent a module"""
geometry.__init__(self) geometry.__init__(self)
debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.") 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: for pin in pin_list:
pin.rect = [pin.ll() - offset, pin.ur() - offset] 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): def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0):
"""Adds an instance of a mod to this module""" """Adds an instance of a mod to this module"""

View File

@ -91,19 +91,26 @@ class spice(verilog.verilog):
group of modules are generated.""" group of modules are generated."""
if (check and (len(self.insts[-1].mod.pins) != len(args))): if (check and (len(self.insts[-1].mod.pins) != len(args))):
debug.error("Connections: {}".format(self.insts[-1].mod.pins)) import pprint
debug.error("Connections: {}".format(args)) 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), debug.error("Number of net connections ({0}) does not match last instance ({1})".format(len(self.insts[-1].mod.pins),
len(args)), 1) len(args)), 1)
self.conns.append(args) self.conns.append(args)
if check and (len(self.insts)!=len(self.conns)): 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, debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
len(self.insts), len(self.insts),
len(self.conns))) len(self.conns)))
debug.error("Instances: \n"+str(self.insts)) debug.error("Instances: \n"+str(insts_string))
debug.error("-----") 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 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 config didn't set output name, make a reasonable default.
if (OPTS.output_name == ""): if (OPTS.output_name == ""):
OPTS.output_name = "sram_{0}rw_{1}b_{2}w_{3}bank_{4}".format(OPTS.rw_ports, 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, print("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
OPTS.num_words, OPTS.num_words,
OPTS.num_banks)) OPTS.num_banks))
if OPTS.netlist_only:
print("Netlist only mode (no physical design is being done).")
if not OPTS.check_lvsdrc: if not OPTS.check_lvsdrc:
print("DRC/LVS/PEX checking is disabled.") print("DRC/LVS/PEX checking is disabled.")

View File

@ -42,6 +42,10 @@ class bank(design.design):
self.num_words = num_words self.num_words = num_words
self.words_per_row = words_per_row self.words_per_row = words_per_row
self.num_banks = num_banks 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, # 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 # 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_" self.prefix="gated_"
else: else:
self.prefix="" self.prefix=""
self.create_netlist()
self.create_layout()
def create_netlist(self):
self.compute_sizes() self.compute_sizes()
self.add_pins() self.add_pins()
self.create_modules()
self.add_modules() self.add_modules()
self.setup_layout_constraints() self.create_modules()
# FIXME: Move this to the add modules function def create_layout(self):
self.add_bank_select() self.place_modules()
self.setup_routing_constraints()
self.route_layout() self.route_layout()
# Can remove the following, but it helps for debug! # 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 # Remember the bank center for further placement
self.bank_center=self.offset_all_coordinates().scale(-1,-1) self.bank_center=self.offset_all_coordinates().scale(-1,-1)
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
""" Adding pins for Bank module""" """ Adding pins for Bank module"""
for i in range(self.word_size): for k in range(self.total_read):
self.add_pin("dout[{0}]".format(i),"OUT") for i in range(self.word_size):
for i in range(self.word_size): self.add_pin("dout{0}[{1}]".format(k,i),"OUT")
self.add_pin("din[{0}]".format(i),"IN") for k in range(self.total_write):
for i in range(self.addr_size): for i in range(self.word_size):
self.add_pin("addr[{0}]".format(i),"INPUT") 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 # For more than one bank, we have a bank select and name
# the signals gated_*. # the signals gated_*.
if self.num_banks > 1: if self.num_banks > 1:
self.add_pin("bank_sel","INPUT") 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(pin,"INPUT")
self.add_pin("vdd","POWER") self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND") self.add_pin("gnd","GROUND")
@ -106,25 +120,46 @@ class bank(design.design):
self.route_vdd_gnd() self.route_vdd_gnd()
def add_modules(self): def create_modules(self):
""" Add modules. The order should not matter! """ """ Add modules. The order should not matter! """
# Above the bitcell array # Above the bitcell array
self.add_bitcell_array() self.create_bitcell_array()
self.add_precharge_array() self.create_precharge_array()
# Below the bitcell array # Below the bitcell array
self.add_column_mux_array() self.create_column_mux_array()
self.add_sense_amp_array() self.create_sense_amp_array()
self.add_write_driver_array() self.create_write_driver_array()
# To the left of the bitcell array # To the left of the bitcell array
self.add_row_decoder() self.create_row_decoder()
self.add_wordline_driver() self.create_wordline_driver()
self.add_column_decoder() 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): def compute_sizes(self):
""" Computes the required sizes to create the bank """ """ Computes the required sizes to create the bank """
@ -146,7 +181,8 @@ class bank(design.design):
# Number of control lines in the bus # Number of control lines in the bus
self.num_control_lines = 4 self.num_control_lines = 4
# The order of the control signals on the control bus: # 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. # These will be outputs of the gaters if this is multibank, if not, normal signals.
if self.num_banks > 1: if self.num_banks > 1:
self.control_signals = ["gated_"+str for str in self.input_control_signals] self.control_signals = ["gated_"+str for str in self.input_control_signals]
@ -167,17 +203,31 @@ class bank(design.design):
2*self.m2_pitch) 2*self.m2_pitch)
def add_modules(self):
def create_modules(self):
""" Create all the modules using the class loader """ """ Create all the modules using the class loader """
self.bitcell = self.mod_bitcell() self.bitcell = self.mod_bitcell()
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols, self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
rows=self.num_rows) rows=self.num_rows)
self.add_mod(self.bitcell_array) 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.precharge_array = []
self.add_mod(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: if self.col_addr_size > 0:
self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols, 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) self.add_mod(self.bank_select)
def add_bitcell_array(self): def create_bitcell_array(self):
""" Adding Bitcell Array """ """ Creating Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="bitcell_array", self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array, mod=self.bitcell_array)
offset=vector(0,0))
temp = [] temp = []
for i in range(self.num_cols): for col in range(self.num_cols):
temp.append("bl[{0}]".format(i)) for bitline in self.total_bitline_list:
temp.append("br[{0}]".format(i)) temp.append(bitline+"[{0}]".format(col))
for j in range(self.num_rows): for row in range(self.num_rows):
temp.append("wl[{0}]".format(j)) for wordline in self.total_wl_list:
temp.extend(["vdd", "gnd"]) temp.append(wordline+"[{0}]".format(row))
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp) 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 self.precharge_array_inst = []
# The enclosure is for the well and the spacing is to the bitcell wells for k in range(self.total_read):
y_offset = self.bitcell_array.height + self.m2_gap self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(k),
self.precharge_array_inst=self.add_inst(name="precharge_array", mod=self.precharge_array[k]))
mod=self.precharge_array, temp = []
offset=vector(0,y_offset)) for i in range(self.num_cols):
temp = [] temp.append(self.read_bl_list[k]+"[{0}]".format(i))
for i in range(self.num_cols): temp.append(self.read_br_list[k]+"[{0}]".format(i))
temp.append("bl[{0}]".format(i)) temp.extend([self.prefix+"clk_buf_bar", "vdd"])
temp.append("br[{0}]".format(i)) self.connect_inst(temp)
temp.extend([self.prefix+"clk_buf_bar", "vdd"])
self.connect_inst(temp)
def add_column_mux_array(self): def place_precharge_array(self):
""" Adding Column Mux when words_per_row > 1 . """ """ 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: if self.col_addr_size > 0:
self.column_mux_height = self.column_mux_array.height + self.m2_gap self.column_mux_height = self.column_mux_array.height + self.m2_gap
else: else:
self.column_mux_height = 0 self.column_mux_height = 0
return return
y_offset = self.column_mux_height for k in range(self.total_ports):
self.col_mux_array_inst=self.add_inst(name="column_mux_array", y_offset = self.column_mux_height
mod=self.column_mux_array, self.place_inst(name=self.col_mux_array_inst[k].name,
offset=vector(0,y_offset).scale(-1,-1)) offset=vector(0,y_offset).scale(-1,-1))
temp = []
for i in range(self.num_cols): def create_sense_amp_array(self):
temp.append("bl[{0}]".format(i)) """ Creating Sense amp """
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)
def add_sense_amp_array(self): self.sense_amp_array_inst = []
""" Adding Sense amp """ 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 temp = []
self.sense_amp_array_inst=self.add_inst(name="sense_amp_array", for i in range(self.word_size):
mod=self.sense_amp_array, temp.append("dout{0}[{1}]".format(k,i))
offset=vector(0,y_offset).scale(-1,-1)) if self.words_per_row == 1:
temp = [] temp.append(self.read_bl_list[k]+"[{0}]".format(i))
for i in range(self.word_size): temp.append(self.read_br_list[k]+"[{0}]".format(i))
temp.append("dout[{0}]".format(i)) else:
if self.words_per_row == 1: temp.append(self.read_bl_list[k]+"_out[{0}]".format(i))
temp.append("bl[{0}]".format(i)) temp.append(self.read_br_list[k]+"_out[{0}]".format(i))
temp.append("br[{0}]".format(i))
else: temp.extend([self.prefix+"s_en{0}".format(k), "vdd", "gnd"])
temp.append("bl_out[{0}]".format(i)) self.connect_inst(temp)
temp.append("br_out[{0}]".format(i))
temp.extend([self.prefix+"s_en", "vdd", "gnd"])
self.connect_inst(temp)
def add_write_driver_array(self): def place_sense_amp_array(self):
""" Adding Write Driver """ """ Placing Sense amp """
y_offset = self.sense_amp_array.height + self.column_mux_height \ # FIXME: place for multiport
+ self.m2_gap + self.write_driver_array.height for k in range(self.total_read):
self.write_driver_array_inst=self.add_inst(name="write_driver_array", y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap
mod=self.write_driver_array, self.place_inst(name=self.sense_amp_array_inst[k].name,
offset=vector(0,y_offset).scale(-1,-1)) offset=vector(0,y_offset).scale(-1,-1))
def create_write_driver_array(self):
""" Creating Write Driver """
temp = [] self.write_driver_array_inst = []
for i in range(self.word_size): for k in range(self.total_write):
temp.append("din[{0}]".format(i)) self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(k),
for i in range(self.word_size): mod=self.write_driver_array))
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)
def add_row_decoder(self): temp = []
""" Add the hierarchical row decoder """ 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 # 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. # 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 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 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. # 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) # FIXME: place for multiport
self.row_decoder_inst=self.add_inst(name="row_decoder", for k in range(self.total_ports):
mod=self.row_decoder, x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
offset=vector(x_offset,0)) self.place_inst(name=self.row_decoder_inst[k].name,
offset=vector(x_offset,0))
temp = []
for i in range(self.row_addr_size): def create_wordline_driver(self):
temp.append("addr[{0}]".format(i+self.col_addr_size)) """ Create the Wordline Driver """
for j in range(self.num_rows):
temp.append("dec_out[{0}]".format(j))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def add_wordline_driver(self): self.wordline_driver_inst = []
""" Wordline Driver """ 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. temp = []
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch for i in range(self.num_rows):
self.wordline_driver_inst=self.add_inst(name="wordline_driver", temp.append("dec_out{0}[{1}]".format(k,i))
mod=self.wordline_driver, for i in range(self.num_rows):
offset=vector(x_offset,0)) 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 = [] def place_wordline_driver(self):
for i in range(self.num_rows): """ Place the Wordline Driver """
temp.append("dec_out[{0}]".format(i))
for i in range(self.num_rows): # FIXME: place for multiport
temp.append("wl[{0}]".format(i)) for k in range(self.total_ports):
temp.append(self.prefix+"clk_buf") # The wordline driver is placed to the right of the main decoder width.
temp.append("vdd") x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch
temp.append("gnd") self.place_inst(name=self.wordline_driver_inst[k].name,
self.connect_inst(temp) 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. 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: if self.col_addr_size == 0:
return return
elif self.col_addr_size == 1: elif self.col_addr_size == 1:
@ -384,59 +482,100 @@ class bank(design.design):
else: else:
# No error checking before? # No error checking before?
debug.error("Invalid column decoder?",-1) 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): if not self.num_banks > 1:
""" Instantiate the bank select logic. """ 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: if not self.num_banks > 1:
return return
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) # FIXME: place for multiport
if self.col_addr_size > 0: for k in range(self.total_ports):
y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by()) x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
else: if self.col_addr_size > 0:
y_off = self.row_decoder_inst.by() y_off = min(self.col_decoder_inst[0].by(), self.col_mux_array_inst[0].by())
y_off -= (self.bank_select.height + drc["well_to_well"]) else:
self.bank_select_pos = vector(x_off,y_off) y_off = self.row_decoder_inst[0].by()
self.bank_select_inst = self.add_inst(name="bank_select", y_off -= (self.bank_select.height + drc["well_to_well"])
mod=self.bank_select, self.bank_select_pos = vector(x_off,y_off)
offset=self.bank_select_pos) self.place_inst(name=self.bank_select_inst[k].name,
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)
def route_vdd_gnd(self): def route_vdd_gnd(self):
""" Propagate all vdd/gnd pins up to this level for all modules """ """ Propagate all vdd/gnd pins up to this level for all modules """
# These are the instances that every bank has # These are the instances that every bank has
top_instances = [self.bitcell_array_inst, top_instances = [self.bitcell_array_inst]
self.precharge_array_inst,
self.sense_amp_array_inst, for k in range(self.total_ports):
self.write_driver_array_inst, top_instances.extend([self.precharge_array_inst[k],
self.row_decoder_inst, self.sense_amp_array_inst[k],
self.wordline_driver_inst] self.write_driver_array_inst[k],
# Add these if we use the part... self.row_decoder_inst[k],
if self.col_addr_size > 0: self.wordline_driver_inst[k]])
top_instances.append(self.col_decoder_inst) # Add these if we use the part...
top_instances.append(self.col_mux_array_inst) 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: if self.num_banks > 1:
top_instances.append(self.bank_select_inst) top_instances.append(self.bank_select_inst[k])
for inst in top_instances: for inst in top_instances:
# Column mux has no vdd # 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") self.copy_layout_pin(inst, "vdd")
# Precharge has no gnd # Precharge has no gnd
if inst != self.precharge_array_inst: if inst != self.precharge_array_inst[0]:
self.copy_layout_pin(inst, "gnd") self.copy_layout_pin(inst, "gnd")
def route_bank_select(self): def route_bank_select(self):
@ -446,7 +585,7 @@ class bank(design.design):
for gated_name in self.control_signals: for gated_name in self.control_signals:
# Connect the inverter output to the central bus # 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) bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y)
self.add_path("metal3",[out_pos, bus_pos]) self.add_path("metal3",[out_pos, bus_pos])
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
@ -460,16 +599,19 @@ class bank(design.design):
rotate=90) rotate=90)
def setup_layout_constraints(self): def setup_routing_constraints(self):
""" After the modules are instantiated, find the dimensions for the """
control bus, power ring, etc. """ 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 minimum point is either the bottom of the address flops,
#the column decoder (if there is one). #the column decoder (if there is one).
write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch 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.by() row_decoder_min_y_offset = self.row_decoder_inst[0].by()
if self.col_addr_size > 0: 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: else:
col_decoder_min_y_offset = row_decoder_min_y_offset 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 # The max point is always the top of the precharge bitlines
# Add a vdd and gnd power rail above the array # 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.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 # # Create the core bbox for the power rings
ur = vector(self.max_x_offset, self.max_y_offset) ur = vector(self.max_x_offset, self.max_y_offset)
ll = vector(self.min_x_offset, self.min_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): def route_precharge_to_bitcell_array(self):
""" Routing of BL and BR between pre-charge and bitcell array """ """ Routing of BL and BR between pre-charge and bitcell array """
for i in range(self.num_cols): # FIXME: Update for multiport
precharge_bl = self.precharge_array_inst.get_pin("bl[{}]".format(i)).bc() for k in range(self.total_read):
precharge_br = self.precharge_array_inst.get_pin("br[{}]".format(i)).bc() for i in range(self.num_cols):
bitcell_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).uc() precharge_bl = self.precharge_array_inst[k].get_pin("bl[{}]".format(i)).bc()
bitcell_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).uc() 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) yoffset = 0.5*(precharge_bl.y+bitcell_bl.y)
self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset),
vector(bitcell_bl.x,yoffset), bitcell_bl]) vector(bitcell_bl.x,yoffset), bitcell_bl])
self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset), self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset),
vector(bitcell_br.x,yoffset), bitcell_br]) vector(bitcell_br.x,yoffset), bitcell_br])
def route_col_mux_to_bitcell_array(self): def route_col_mux_to_bitcell_array(self):
@ -539,51 +684,56 @@ class bank(design.design):
if self.col_addr_size==0: if self.col_addr_size==0:
return return
for i in range(self.num_cols): # FIXME: Update for multiport
col_mux_bl = self.col_mux_array_inst.get_pin("bl[{}]".format(i)).uc() for k in range(self.total_ports):
col_mux_br = self.col_mux_array_inst.get_pin("br[{}]".format(i)).uc() for i in range(self.num_cols):
bitcell_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).bc() col_mux_bl = self.col_mux_array_inst[k].get_pin("bl[{}]".format(i)).uc()
bitcell_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).bc() 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) yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y)
self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset),
vector(bitcell_bl.x,yoffset), bitcell_bl]) vector(bitcell_bl.x,yoffset), bitcell_bl])
self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset), self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset),
vector(bitcell_br.x,yoffset), bitcell_br]) vector(bitcell_br.x,yoffset), bitcell_br])
def route_sense_amp_to_col_mux_or_bitcell_array(self): 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 """ """ Routing of BL and BR between sense_amp and column mux or bitcell array """
for i in range(self.word_size): for k in range(self.total_read):
sense_amp_bl = self.sense_amp_array_inst.get_pin("bl[{}]".format(i)).uc() for i in range(self.word_size):
sense_amp_br = self.sense_amp_array_inst.get_pin("br[{}]".format(i)).uc() 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: if self.col_addr_size>0:
# Sense amp is connected to the col mux # Sense amp is connected to the col mux
connect_bl = self.col_mux_array_inst.get_pin("bl_out[{}]".format(i)).bc() connect_bl = self.col_mux_array_inst[k].get_pin("bl_out[{}]".format(i)).bc()
connect_br = self.col_mux_array_inst.get_pin("br_out[{}]".format(i)).bc() connect_br = self.col_mux_array_inst[k].get_pin("br_out[{}]".format(i)).bc()
else: else:
# Sense amp is directly connected to the bitcell array # Sense amp is directly connected to the bitcell array
connect_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).bc() connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[k]+"[{}]".format(i)).bc()
connect_br = self.bitcell_array_inst.get_pin("br[{}]".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), yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
vector(connect_bl.x,yoffset), connect_bl]) self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset),
self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset), vector(connect_bl.x,yoffset), connect_bl])
vector(connect_br.x,yoffset), connect_br]) 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): def route_sense_amp_out(self):
""" Add pins for the sense amp output """ """ Add pins for the sense amp output """
for i in range(self.word_size): for i in range(self.word_size):
data_pin = self.sense_amp_array_inst.get_pin("data[{}]".format(i)) data_pin = self.sense_amp_array_inst[0].get_pin("data[{}]".format(i))
self.add_layout_pin_rect_center(text="dout[{}]".format(i), self.add_layout_pin_rect_center(text="dout0[{}]".format(i),
layer=data_pin.layer, layer=data_pin.layer,
offset=data_pin.center(), offset=data_pin.center(),
height=data_pin.height(), height=data_pin.height(),
width=data_pin.width()), width=data_pin.width())
def route_row_decoder(self): def route_row_decoder(self):
""" Routes the row decoder inputs and supplies """ """ Routes the row decoder inputs and supplies """
@ -591,8 +741,8 @@ class bank(design.design):
for i in range(self.row_addr_size): for i in range(self.row_addr_size):
addr_idx = i + self.col_addr_size addr_idx = i + self.col_addr_size
decoder_name = "addr[{}]".format(i) decoder_name = "addr[{}]".format(i)
addr_name = "addr[{}]".format(addr_idx) addr_name = "addr0[{}]".format(addr_idx)
self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name) self.copy_layout_pin(self.row_decoder_inst[0], decoder_name, addr_name)
def route_write_driver(self): def route_write_driver(self):
@ -600,26 +750,25 @@ class bank(design.design):
for i in range(self.word_size): for i in range(self.word_size):
data_name = "data[{}]".format(i) data_name = "data[{}]".format(i)
din_name = "din[{}]".format(i) din_name = "din0[{}]".format(i)
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) self.copy_layout_pin(self.write_driver_array_inst[0], data_name, din_name)
def route_wordline_driver(self): def route_wordline_driver(self):
""" Connecting Wordline driver output to Bitcell WL connection """ """ 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): for i in range(self.num_rows):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs # 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() decoder_out_pos = self.row_decoder_inst[0].get_pin("decode[{}]".format(i)).rc()
driver_in_pos = self.wordline_driver_inst.get_pin("in[{}]".format(i)).lc() 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) 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) 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]) self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos])
# The mid guarantees we exit the input cell to the right. # The mid guarantees we exit the input cell to the right.
driver_wl_pos = self.wordline_driver_inst.get_pin("wl[{}]".format(i)).rc() driver_wl_pos = self.wordline_driver_inst[0].get_pin("wl[{}]".format(i)).rc()
bitcell_wl_pos = self.bitcell_array_inst.get_pin("wl[{}]".format(i)).lc() 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) 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) 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]) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
@ -639,7 +788,7 @@ class bank(design.design):
decode_names = ["Zb", "Z"] decode_names = ["Zb", "Z"]
# The Address LSB # 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: elif self.col_addr_size > 1:
decode_names = [] decode_names = []
@ -648,22 +797,22 @@ class bank(design.design):
for i in range(self.col_addr_size): for i in range(self.col_addr_size):
decoder_name = "in[{}]".format(i) decoder_name = "in[{}]".format(i)
addr_name = "addr[{}]".format(i) addr_name = "addr0[{}]".format(i)
self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name) self.copy_layout_pin(self.col_decoder_inst[0], decoder_name, addr_name)
# This will do a quick "river route" on two layers. # This will do a quick "river route" on two layers.
# When above the top select line it will offset "inward" again to prevent conflicts. # 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. # 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)): for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)):
mux_name = "sel[{}]".format(i) 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 # 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: 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) mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y)
else: else:
@ -714,7 +863,7 @@ class bank(design.design):
for i in range(self.word_size): for i in range(self.word_size):
data_name = "dec_out[{}]".format(i) data_name = "dec_out[{}]".format(i)
pin_name = "in[{}]".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, self.add_label(text=data_name,
layer="metal1", layer="metal1",
offset=data_pin.center()) offset=data_pin.center())
@ -728,9 +877,9 @@ class bank(design.design):
# Connection from the central bus to the main control block crosses # Connection from the central bus to the main control block crosses
# pre-decoder and this connection is in metal3 # pre-decoder and this connection is in metal3
connection = [] connection = []
connection.append((self.prefix+"clk_buf_bar", self.precharge_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_en", self.write_driver_array_inst.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_en", self.sense_amp_array_inst.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: for (control_signal, pin_pos) in connection:
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
@ -741,7 +890,7 @@ class bank(design.design):
# clk to wordline_driver # clk to wordline_driver
control_signal = self.prefix+"clk_buf" 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) mid_pos = pin_pos + vector(0,self.m1_pitch)
control_x_offset = self.bus_xoffset[control_signal].x control_x_offset = self.bus_xoffset[control_signal].x
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y) 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 # Number of control lines in the bus
self.num_control_lines = 4 self.num_control_lines = 4
# The order of the control signals on the control bus: # 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 # These will be outputs of the gaters if this is multibank
self.control_signals = ["gated_"+str for str in self.input_control_signals] self.control_signals = ["gated_"+str for str in self.input_control_signals]

View File

@ -44,56 +44,54 @@ class bitcell(design.design):
"vdd", "vdd",
"gnd"] "gnd"]
return bitcell_pins return bitcell_pins
def list_row_pins(self): def list_all_wl_names(self):
""" Creates a list of all row pins (except for gnd and vdd) """ """ Creates a list of all wordline pin names """
row_pins = ["wl"] row_pins = ["wl"]
return row_pins return row_pins
def list_read_row_pins(self): def list_read_wl_names(self):
""" Creates a list of row pins associated with read ports """ """ Creates a list of wordline pin names associated with read ports """
row_pins = ["wl"] row_pins = ["wl"]
return row_pins return row_pins
def list_write_row_pins(self): def list_write_wl_names(self):
""" Creates a list of row pins associated with write ports """ """ Creates a list of wordline pin names associated with write ports """
row_pins = ["wl"] row_pins = ["wl"]
return row_pins return row_pins
def list_all_bitline_names(self):
def list_all_column_pins(self): """ Creates a list of all bitline pin names (both bl and br) """
""" Creates a list of all column pins (except for gnd and vdd) """
column_pins = ["bl", "br"] column_pins = ["bl", "br"]
return column_pins return column_pins
def list_column_pins(self): def list_all_bl_names(self):
""" Creates a list of all column pins (except for gnd and vdd) """ """ Creates a list of all bl pins names """
column_pins = ["bl"] column_pins = ["bl"]
return column_pins return column_pins
def list_column_bar_pins(self): def list_all_br_names(self):
""" Creates a list of all column pins (except for gnd and vdd) """ """ Creates a list of all br pins names """
column_pins = ["br"] column_pins = ["br"]
return column_pins return column_pins
def list_read_column_pins(self): def list_read_bl_names(self):
""" Creates a list of column pins associated with read ports """ """ Creates a list of bl pin names associated with read ports """
column_pins = ["bl"] column_pins = ["bl"]
return column_pins return column_pins
def list_read_bar_column_pins(self): def list_read_br_names(self):
""" Creates a list of column pins associated with read_bar ports """ """ Creates a list of br pin names associated with read ports """
column_pins = ["br"] column_pins = ["br"]
return column_pins return column_pins
def list_write_column_pins(self): def list_write_bl_names(self):
""" Creates a list of column pins associated with write ports """ """ Creates a list of bl pin names associated with write ports """
column_pins = ["bl"] column_pins = ["bl"]
return column_pins return column_pins
def list_write_bar_column_pins(self): def list_write_br_names(self):
""" Creates a list of column pins asscociated with write_bar ports""" """ Creates a list of br pin names asscociated with write ports"""
column_pins = ["br"] column_pins = ["br"]
return column_pins 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.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.width = self.column_size*self.cell.width + self.m1_width
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
self.add_layout_pins()
# We don't offset this because we need to align # We don't offset this because we need to align
# the replica bitcell in the control logic # the replica bitcell in the control logic
#self.offset_all_coordinates() #self.offset_all_coordinates()
self.DRC_LVS()
def add_pins(self): def add_pins(self):
row_list = self.cell.list_row_pins() row_list = self.cell.list_all_wl_names()
column_list = self.cell.list_all_column_pins() column_list = self.cell.list_all_bitline_names()
for col in range(self.column_size): for col in range(self.column_size):
for cell_column in column_list: for cell_column in column_list:
self.add_pin(cell_column+"[{0}]".format(col)) self.add_pin(cell_column+"[{0}]".format(col))
@ -55,7 +54,6 @@ class bitcell_array(design.design):
def create_layout(self): def create_layout(self):
xoffset = 0.0 xoffset = 0.0
self.cell_inst = {}
for col in range(self.column_size): for col in range(self.column_size):
yoffset = 0.0 yoffset = 0.0
for row in range(self.row_size): for row in range(self.row_size):
@ -68,21 +66,34 @@ class bitcell_array(design.design):
tempy = yoffset tempy = yoffset
dir_key = "" dir_key = ""
self.cell_inst[row,col]=self.add_inst(name=name, self.place_inst(name=name,
mod=self.cell, offset=[xoffset, tempy],
offset=[xoffset, tempy], mirror=dir_key)
mirror=dir_key)
self.connect_inst(self.cell.list_bitcell_pins(col, row))
yoffset += self.cell.height yoffset += self.cell.height
xoffset += self.cell.width 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): def add_layout_pins(self):
""" Add the layout pins """ """ Add the layout pins """
row_list = self.cell.list_row_pins() row_list = self.cell.list_all_wl_names()
column_list = self.cell.list_all_column_pins() column_list = self.cell.list_all_bitline_names()
offset = vector(0.0, 0.0) offset = vector(0.0, 0.0)
for col in range(self.column_size): 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 # leave space for the bus plus one extra space
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
# Outputs to the bank # 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"] self.supply_list = ["vdd", "gnd"]
@ -231,7 +231,7 @@ class control_logic(design.design):
mod=self.inv8, mod=self.inv8,
offset=self.s_en_offset, offset=self.s_en_offset,
mirror=mirror) 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) self.row_end_inst.append(self.s_en_inst)
@ -313,7 +313,7 @@ class control_logic(design.design):
mod=self.inv8, mod=self.inv8,
offset=w_en_offset, offset=w_en_offset,
mirror=mirror) 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 x_off += self.inv8.width
self.row_end_inst.append(self.w_en_inst) 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_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.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): def route_sen(self):
rbl_out_pos = self.rbl_inst.get_pin("out").bc() 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.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): def route_clk(self):
""" Route the clk and clk_buf_bar signal internally """ """ 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.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height self.height = self.rows * self.dff.height
self.create_netlist()
self.create_layout() self.create_layout()
def create_layout(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.create_dff_array() self.create_dff_array()
def create_layout(self):
self.place_dff_array()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS() self.DRC_LVS()
@ -53,22 +57,28 @@ class dff_array(design.design):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col) 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, self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff, mod=self.dff)
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(row,col), self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col), self.get_dout_name(row,col),
"clk", "clk",
"vdd", "vdd",
"gnd"]) "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): def get_din_name(self, row, col):
if self.columns == 1: if self.columns == 1:
din_name = "din[{0}]".format(row) 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.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height self.height = self.rows * self.dff.height
self.create_netlist()
self.create_layout() self.create_layout()
def create_layout(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.create_dff_array() self.create_dff_array()
def create_layout(self):
self.place_dff_array()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS() self.DRC_LVS()
@ -52,16 +56,8 @@ class dff_buf_array(design.design):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col) 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, self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff, mod=self.dff)
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(row,col), self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col), self.get_dout_name(row,col),
self.get_dout_bar_name(row,col), self.get_dout_bar_name(row,col),
@ -69,6 +65,20 @@ class dff_buf_array(design.design):
"vdd", "vdd",
"gnd"]) "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): def get_din_name(self, row, col):
if self.columns == 1: if self.columns == 1:
din_name = "din[{0}]".format(row) 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.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell_height = self.mod_bitcell.height self.bitcell_height = self.mod_bitcell.height
self.NAND_FORMAT = "DEC_NAND[{0}]"
self.INV_FORMAT = "DEC_INV_[{0}]"
self.pre2x4_inst = [] self.pre2x4_inst = []
self.pre3x8_inst = [] self.pre3x8_inst = []
@ -33,22 +36,27 @@ class hierarchical_decoder(design.design):
self.num_inputs = int(math.log(self.rows, 2)) self.num_inputs = int(math.log(self.rows, 2))
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) (self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
self.create_netlist()
self.create_layout() self.create_layout()
self.offset_all_coordinates()
self.DRC_LVS()
def create_layout(self): def create_netlist(self):
self.add_modules() self.add_modules()
self.setup_layout_constants() self.setup_netlist_constants()
self.add_pins() self.add_pins()
self.create_pre_decoder() self.create_pre_decoder()
self.create_row_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): def add_modules(self):
self.inv = pinv() self.inv = pinv()
self.add_mod(self.inv) self.add_mod(self.inv)
@ -89,7 +97,7 @@ class hierarchical_decoder(design.design):
else: else:
debug.error("Invalid number of inputs for hierarchical decoder",-1) 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. self.predec_groups = [] # This array is a 2D array.
# Distributing vertical rails to different groups. One group belongs to one pre-decoder. # 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 index = index + 1
self.predec_groups.append(lines) self.predec_groups.append(lines)
self.calculate_dimensions()
def create_input_rail(self): def setup_layout_constants(self):
""" Create input rails for the predecoders """ """ Calculate the overall dimensions of the hierarchical decoder """
# 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 """
# If we have 4 or fewer rows, the predecoder is the decoder itself # If we have 4 or fewer rows, the predecoder is the decoder itself
if self.num_inputs>=4: if self.num_inputs>=4:
@ -227,24 +152,105 @@ class hierarchical_decoder(design.design):
self.height = self.row_decoder_height self.height = self.row_decoder_height
self.width = self.input_routing_width + self.predecoder_width \ self.width = self.input_routing_width + self.predecoder_width \
+ self.internal_routing_width + nand_width + self.inv.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): def create_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """ """ Creates pre-decoder and places labels input address [A] """
for i in range(self.no_of_pre2x4): for i in range(self.no_of_pre2x4):
self.add_pre2x4(i) self.create_pre2x4(i)
for i in range(self.no_of_pre3x8): 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 """ """ Add a 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2): if (self.num_inputs == 2):
base = vector(-self.pre2_4.width,0)
index_off1 = index_off2 = 0 index_off1 = index_off2 = 0
else: else:
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
index_off1 = num * 2 index_off1 = num * 2
index_off2 = num * 4 index_off2 = num * 4
@ -256,20 +262,12 @@ class hierarchical_decoder(design.design):
pins.extend(["vdd", "gnd"]) pins.extend(["vdd", "gnd"])
self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num), self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num),
mod=self.pre2_4, mod=self.pre2_4))
offset=base))
self.connect_inst(pins) 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 """ """ 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 # If we had 2x4 predecodes, those are used as the lower
# decode output bits # decode output bits
in_index_offset = num * 3 + self.no_of_pre2x4 * 2 in_index_offset = num * 3 + self.no_of_pre2x4 * 2
@ -283,79 +281,112 @@ class hierarchical_decoder(design.design):
pins.extend(["vdd", "gnd"]) pins.extend(["vdd", "gnd"])
self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num), self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num),
mod=self.pre3_8, mod=self.pre3_8))
offset=offset))
self.connect_inst(pins) 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): def create_row_decoder(self):
""" Create the row-decoder by placing NAND2/NAND3 and Inverters """ Create the row-decoder by placing NAND2/NAND3 and Inverters
and add the primary decoder output pins. """ and add the primary decoder output pins. """
if (self.num_inputs >= 4): if (self.num_inputs >= 4):
self.add_decoder_nand_array() self.create_decoder_nand_array()
self.add_decoder_inv_array() self.create_decoder_inv_array()
self.route_decoder()
def add_decoder_nand_array(self): def create_decoder_nand_array(self):
""" Add a column of NAND gates for final decode """ """ Add a column of NAND gates for final decode """
self.nand_inst = []
# Row Decoder NAND GATE array for address inputs <5. # Row Decoder NAND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_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 i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])): 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), pins =["out[{0}]".format(i),
"out[{0}]".format(j + len(self.predec_groups[0])), "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"] "vdd", "gnd"]
self.connect_inst(args=pins, check=False) self.connect_inst(pins)
# Row Decoder NAND GATE array for address inputs >5. # Row Decoder NAND GATE array for address inputs >5.
elif (self.num_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 i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])): for j in range(len(self.predec_groups[1])):
for k in range(len(self.predec_groups[2])): for k in range(len(self.predec_groups[2])):
Z_index = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \ row = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \
+ len(self.predec_groups[2])*j + k + 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), pins = ["out[{0}]".format(i),
"out[{0}]".format(j + len(self.predec_groups[0])), "out[{0}]".format(j + len(self.predec_groups[0])),
"out[{0}]".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), "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"] "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): for row in range(self.rows):
name = "DEC_NAND[{0}]".format(row) name = self.INV_FORMAT.format(row)
if ((row % 2) == 0): self.inv_inst.append(self.add_inst(name=name,
y_off = nand_mod.height*row mod=self.inv))
y_dir = 1 self.connect_inst(args=["Z[{0}]".format(row),
mirror = "R0" "decode[{0}]".format(row),
else: "vdd", "gnd"])
y_off = nand_mod.height*(row + 1)
y_dir = -1
mirror = "MX"
self.nand_inst.append(self.add_inst(name=name,
mod=nand_mod,
offset=[self.internal_routing_width, y_off],
mirror=mirror))
def place_decoder_inv_array(self):
"""
def add_decoder_inv_array(self): Place the column of INV gates for the decoder above the predecoders
"""Add a column of INV gates for the decoder above the predecoders and to the right of the NAND decoders.
and to the right of the NAND decoders.""" """
z_pin = self.inv.get_pin("Z") z_pin = self.inv.get_pin("Z")
@ -364,9 +395,8 @@ class hierarchical_decoder(design.design):
else: else:
x_off = self.internal_routing_width + self.nand3.width x_off = self.internal_routing_width + self.nand3.width
self.inv_inst = []
for row in range(self.rows): for row in range(self.rows):
name = "DEC_INV_[{0}]".format(row) name = self.INV_FORMAT.format(row)
if (row % 2 == 0): if (row % 2 == 0):
inv_row_height = self.inv.height * row inv_row_height = self.inv.height * row
mirror = "R0" mirror = "R0"
@ -377,17 +407,52 @@ class hierarchical_decoder(design.design):
y_dir = -1 y_dir = -1
y_off = inv_row_height y_off = inv_row_height
offset = vector(x_off,y_off) offset = vector(x_off,y_off)
self.place_inst(name=name,
self.inv_inst.append(self.add_inst(name=name, offset=offset,
mod=self.inv, mirror=mirror)
offset=offset,
mirror=mirror))
# This will not check that the inst connections match. def place_row_decoder(self):
self.connect_inst(args=["Z[{0}]".format(row), """
"decode[{0}]".format(row), Place the row-decoder by placing NAND2/NAND3 and Inverters
"vdd", "gnd"], and add the primary decoder output pins.
check=False) """
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): 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.""" """ 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. # 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) length=self.height)
self.connect_rails_to_predecodes() self.route_rails_to_predecodes()
self.connect_rails_to_decoder() 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 """ """ Iterates through all of the predecodes and connects to the rails including the offsets """
# FIXME: convert to connect_bus # FIXME: convert to connect_bus
@ -438,7 +503,7 @@ class hierarchical_decoder(design.design):
predecode_name = "predecode[{}]".format(pre_num * 4 + i) predecode_name = "predecode[{}]".format(pre_num * 4 + i)
out_name = "out[{}]".format(i) out_name = "out[{}]".format(i)
pin = self.pre2x4_inst[pre_num].get_pin(out_name) 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 # 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) predecode_name = "predecode[{}]".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
out_name = "out[{}]".format(i) out_name = "out[{}]".format(i)
pin = self.pre3x8_inst[pre_num].get_pin(out_name) 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. """ Use the self.predec_groups to determine the connections to the decoder NAND gates.
Inputs of NAND2/NAND3 gates come from different groups. Inputs of NAND2/NAND3 gates come from different groups.
For example for these groups [ [0,1,2,3] ,[4,5,6,7], 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]: for index_B in self.predec_groups[1]:
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
predecode_name = "predecode[{}]".format(index_A) 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) 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 row_index = row_index + 1
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
@ -476,11 +541,11 @@ class hierarchical_decoder(design.design):
for index_C in self.predec_groups[2]: for index_C in self.predec_groups[2]:
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
predecode_name = "predecode[{}]".format(index_A) 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) 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) 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 row_index = row_index + 1
def route_vdd_gnd(self): def route_vdd_gnd(self):
@ -516,7 +581,7 @@ class hierarchical_decoder(design.design):
self.copy_layout_pin(pre, "gnd") 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 """ """ Connect the routing rail to the given metal1 pin """
rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y) rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y)
self.add_path("metal1", [rail_pos, pin.lc()]) self.add_path("metal1", [rail_pos, pin.lc()])
@ -525,7 +590,7 @@ class hierarchical_decoder(design.design):
rotate=90) 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 """ """ Connect the routing rail to the given metal1 pin """
# This routes the pin up to the rail, basically, to avoid conflicts. # This routes the pin up to the rail, basically, to avoid conflicts.
# It would be fixed with a channel router. # It would be fixed with a channel router.

View File

@ -37,17 +37,17 @@ class hierarchical_predecode(design.design):
self.inv = pinv() self.inv = pinv()
self.add_mod(self.inv) self.add_mod(self.inv)
self.create_nand(self.number_of_inputs) self.add_nand(self.number_of_inputs)
self.add_mod(self.nand) self.add_mod(self.nand)
def create_nand(self,inputs): def add_nand(self,inputs):
""" Create the NAND for the predecode input stage """ """ Create the NAND for the predecode input stage """
if inputs==2: if inputs==2:
self.nand = pnand2() self.nand = pnand2()
elif inputs==3: elif inputs==3:
self.nand = pnand3() self.nand = pnand3()
else: else:
debug.error("Invalid number of predecode inputs.",-1) debug.error("Invalid number of predecode inputs: {}".format(inputs),-1)
def setup_constraints(self): def setup_constraints(self):
@ -65,7 +65,7 @@ class hierarchical_predecode(design.design):
# Height width are computed # Height width are computed
self.width = self.x_off_inv_2 + self.inv.width 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 """ """ 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)] input_names = ["in[{}]".format(x) for x in range(self.number_of_inputs)]
offset = vector(0.5*self.m2_width,2*self.m1_width) 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) 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. """ """ Create the input inverters to invert input signals for the decode stage. """
self.in_inst = [] 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): for inv_num in range(self.number_of_inputs):
name = "Xpre_inv[{0}]".format(inv_num) name = "Xpre_inv[{0}]".format(inv_num)
if (inv_num % 2 == 0): if (inv_num % 2 == 0):
@ -99,18 +110,26 @@ class hierarchical_predecode(design.design):
y_off = (inv_num + 1) * (self.inv.height) y_off = (inv_num + 1) * (self.inv.height)
mirror="MX" mirror="MX"
offset = vector(self.x_off_inv_1, y_off) offset = vector(self.x_off_inv_1, y_off)
self.in_inst.append(self.add_inst(name=name, self.place_inst(name=name,
mod=self.inv, offset=offset,
offset=offset, mirror=mirror)
mirror=mirror))
self.connect_inst(["in[{0}]".format(inv_num),
"inbar[{0}]".format(inv_num),
"vdd", "gnd"])
def add_output_inverters(self): def create_output_inverters(self):
""" Create inverters for the inverted output decode signals. """ """ Create inverters for the inverted output decode signals. """
self.inv_inst = [] 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): for inv_num in range(self.number_of_outputs):
name = "Xpre_nand_inv[{}]".format(inv_num) name = "Xpre_nand_inv[{}]".format(inv_num)
if (inv_num % 2 == 0): if (inv_num % 2 == 0):
@ -120,19 +139,23 @@ class hierarchical_predecode(design.design):
y_off =(inv_num + 1)*self.inv.height y_off =(inv_num + 1)*self.inv.height
mirror = "MX" mirror = "MX"
offset = vector(self.x_off_inv_2, y_off) offset = vector(self.x_off_inv_2, y_off)
self.inv_inst.append(self.add_inst(name=name, self.place_inst(name=name,
mod=self.inv, offset=offset,
offset=offset, mirror=mirror)
mirror=mirror))
self.connect_inst(["Z[{}]".format(inv_num),
"out[{}]".format(inv_num),
"vdd", "gnd"])
def add_nand(self,connections): def create_nand_array(self,connections):
""" Create the NAND stage for the decodes """ """ Create the NAND stage for the decodes """
self.nand_inst = [] 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): for nand_input in range(self.number_of_outputs):
inout = str(self.number_of_inputs)+"x"+str(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) 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 y_off = (nand_input + 1) * self.inv.height
mirror = "MX" mirror = "MX"
offset = vector(self.x_off_nand, y_off) offset = vector(self.x_off_nand, y_off)
self.nand_inst.append(self.add_inst(name=name, self.place_inst(name=name,
mod=self.nand, offset=offset,
offset=offset, mirror=mirror)
mirror=mirror))
self.connect_inst(connections[nand_input])
def route(self): def route(self):

View File

@ -14,8 +14,17 @@ class hierarchical_predecode2x4(hierarchical_predecode):
self.add_pins() self.add_pins()
self.create_modules() self.create_modules()
self.setup_constraints() self.setup_constraints()
self.create_netlist()
self.create_layout() 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): def create_layout(self):
""" The general organization is from left to right: """ 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 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
4) a set of NAND gates for inversion 4) a set of NAND gates for inversion
""" """
self.create_rails() self.route_rails()
self.add_input_inverters() self.place_input_inverters()
self.add_output_inverters() self.place_output_inverters()
connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"], self.place_nand_array()
["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() self.route()
self.DRC_LVS()
def get_nand_input_line_combination(self): def get_nand_input_line_combination(self):
""" These are the decoder connections of the NAND gates to the A,B pins """ """ 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.add_pins()
self.create_modules() self.create_modules()
self.setup_constraints() self.setup_constraints()
self.create_netlist()
self.create_layout() self.create_layout()
self.DRC_LVS()
def create_layout(self): def create_netlist(self):
""" The general organization is from left to right: self.create_input_inverters()
1) a set of M2 rails for input signals self.create_output_inverters()
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()
connections=[["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"], connections=[["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"],
["in[0]", "inbar[1]", "inbar[2]", "Z[1]", "vdd", "gnd"], ["in[0]", "inbar[1]", "inbar[2]", "Z[1]", "vdd", "gnd"],
["inbar[0]", "in[1]", "inbar[2]", "Z[2]", "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"], ["in[0]", "inbar[1]", "in[2]", "Z[5]", "vdd", "gnd"],
["inbar[0]", "in[1]", "in[2]", "Z[6]", "vdd", "gnd"], ["inbar[0]", "in[1]", "in[2]", "Z[6]", "vdd", "gnd"],
["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"]] ["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"]]
self.add_nand(connections) self.create_nand_array(connections)
self.route()
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): def get_nand_input_line_combination(self):
""" These are the decoder connections of the NAND gates to the A,B,C pins """ """ These are the decoder connections of the NAND gates to the A,B,C pins """
combination = [["Abar[0]", "Abar[1]", "Abar[2]"], 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.height = self.ms.height
self.words_per_row = int(self.columns / self.word_size) self.words_per_row = int(self.columns / self.word_size)
self.create_netlist()
self.create_layout() self.create_layout()
def create_layout(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.create_ms_flop_array() self.create_ms_flop_array()
def create_layout(self):
self.place_ms_flop_array()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
for i in range(self.word_size): for i in range(self.word_size):
self.add_pin("din[{0}]".format(i)) self.add_pin("din[{0}]".format(i))
@ -52,25 +56,28 @@ class ms_flop_array(design.design):
self.ms_inst={} self.ms_inst={}
for i in range(0,self.columns,self.words_per_row): for i in range(0,self.columns,self.words_per_row):
name = "Xdff{0}".format(i) 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) index = int(i/self.words_per_row)
self.ms_inst[index]=self.add_inst(name=name, self.ms_inst[index]=self.add_inst(name=name,
mod=self.ms, mod=self.ms)
offset=base,
mirror=mirror)
self.connect_inst(["din[{0}]".format(index), self.connect_inst(["din[{0}]".format(index),
"dout[{0}]".format(index), "dout[{0}]".format(index),
"dout_bar[{0}]".format(index), "dout_bar[{0}]".format(index),
"clk", "clk",
"vdd", "gnd"]) "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): def add_layout_pins(self):
for i in range(self.word_size): 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. of bit line columns, height is the height of the bit-cell array.
""" """
def __init__(self, columns, size=1): unique_id = 1
design.design.__init__(self, "precharge_array")
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)) debug.info(1, "Creating {0}".format(self.name))
self.columns = columns 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.add_mod(self.pc_cell)
self.width = self.columns * self.pc_cell.width self.width = self.columns * self.pc_cell.width
self.height = self.pc_cell.height self.height = self.pc_cell.height
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
self.DRC_LVS()
def add_pins(self): def add_pins(self):
"""Adds pins for spice file""" """Adds pins for spice file"""
@ -35,9 +38,14 @@ class precharge_array(design.design):
self.add_pin("en") self.add_pin("en")
self.add_pin("vdd") self.add_pin("vdd")
def create_netlist(self):
self.add_pins()
self.create_insts()
def create_layout(self): def create_layout(self):
self.add_insts() self.place_insts()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS()
def add_layout_pins(self): def add_layout_pins(self):
@ -51,20 +59,8 @@ class precharge_array(design.design):
for inst in self.local_insts: for inst in self.local_insts:
self.copy_layout_pin(inst, "vdd") self.copy_layout_pin(inst, "vdd")
for i in range(len(self.local_insts)):
inst = self.local_insts[i]
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"])
bl_pin = inst.get_pin("bl") bl_pin = inst.get_pin("bl")
self.add_layout_pin(text="bl[{0}]".format(i), self.add_layout_pin(text="bl[{0}]".format(i),
layer="metal2", layer="metal2",
@ -77,4 +73,25 @@ class precharge_array(design.design):
offset=br_pin.ll(), offset=br_pin.ll(),
width=drc["minwidth_metal2"], width=drc["minwidth_metal2"],
height=bl_pin.height()) 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.height = self.amp.height
self.width = self.amp.width * self.word_size * self.words_per_row self.width = self.amp.width * self.word_size * self.words_per_row
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
self.DRC_LVS() self.DRC_LVS()
@ -42,36 +42,42 @@ class sense_amp_array(design.design):
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def create_netlist(self):
self.add_pins()
self.create_sense_amp_array()
def create_layout(self): def create_layout(self):
self.place_sense_amp_array()
self.add_sense_amp() self.add_layout_pins()
self.connect_rails() self.route_rails()
def add_sense_amp(self): def create_sense_amp_array(self):
self.local_insts = []
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
for i in range(0,self.word_size): for i in range(0,self.word_size):
name = "sa_d{0}".format(i) name = "sa_d{0}".format(i)
amp_position = vector(amp_spacing * i, 0) self.local_insts.append(self.add_inst(name=name,
mod=self.amp))
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.connect_inst(["bl[{0}]".format(i), self.connect_inst(["bl[{0}]".format(i),
"br[{0}]".format(i), "br[{0}]".format(i),
"data[{0}]".format(i), "data[{0}]".format(i),
"en", "vdd", "gnd"]) "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() gnd_pos = inst.get_pin("gnd").center()
self.add_via_center(layers=("metal2", "via2", "metal3"), 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", self.add_layout_pin_rect_center(text="gnd",
layer="metal3", layer="metal3",
offset=gnd_pos) offset=gnd_pos)
vdd_pos = inst.get_pin("vdd").center() vdd_pos = inst.get_pin("vdd").center()
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=vdd_pos) offset=vdd_pos)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal3", layer="metal3",
offset=vdd_pos) 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), self.add_layout_pin(text="bl[{0}]".format(i),
layer="metal2", layer="metal2",
offset=bl_offset, offset=bl_pin.ll(),
width=bl_pin.width(), width=bl_pin.width(),
height=bl_pin.height()) height=bl_pin.height())
self.add_layout_pin(text="br[{0}]".format(i), self.add_layout_pin(text="br[{0}]".format(i),
layer="metal2", layer="metal2",
offset=br_offset, offset=br_pin.ll(),
width=br_pin.width(), width=br_pin.width(),
height=br_pin.height()) height=br_pin.height())
self.add_layout_pin(text="data[{0}]".format(i), self.add_layout_pin(text="data[{0}]".format(i),
layer="metal2", layer="metal2",
offset=dout_offset, offset=dout_pin.ll(),
width=dout_pin.width(), width=dout_pin.width(),
height=dout_pin.height()) height=dout_pin.height())
def connect_rails(self): def route_rails(self):
# add sclk rail across entire array # add sclk rail across entire array
sclk_offset = self.amp.get_pin("en").ll().scale(0,1) sclk_offset = self.amp.get_pin("en").ll().scale(0,1)
self.add_layout_pin(text="en", self.add_layout_pin(text="en",

View File

@ -20,9 +20,8 @@ class single_level_column_mux_array(design.design):
self.columns = columns self.columns = columns
self.word_size = word_size self.word_size = word_size
self.words_per_row = int(self.columns / self.word_size) self.words_per_row = int(self.columns / self.word_size)
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
self.DRC_LVS()
def add_pins(self): def add_pins(self):
for i in range(self.columns): 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("br_out[{}]".format(i))
self.add_pin("gnd") self.add_pin("gnd")
def create_layout(self): def create_netlist(self):
self.add_pins()
self.add_modules() self.add_modules()
self.setup_layout_constants()
self.create_array() self.create_array()
def create_layout(self):
self.setup_layout_constants()
self.place_array()
self.add_routing() self.add_routing()
# Find the highest shapes to determine height before adding well # Find the highest shapes to determine height before adding well
highest = self.find_highest_coords() highest = self.find_highest_coords()
@ -46,6 +49,7 @@ class single_level_column_mux_array(design.design):
self.add_layout_pins() self.add_layout_pins()
self.add_enclosure(self.mux_inst, "pwell") self.add_enclosure(self.mux_inst, "pwell")
self.DRC_LVS()
def add_modules(self): def add_modules(self):
@ -65,14 +69,11 @@ class single_level_column_mux_array(design.design):
def create_array(self): def create_array(self):
self.mux_inst = [] self.mux_inst = []
# For every column, add a pass gate # For every column, add a pass gate
for col_num in range(self.columns): for col_num in range(self.columns):
name = "XMUX{0}".format(col_num) 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, self.mux_inst.append(self.add_inst(name=name,
mod=self.mux, mod=self.mux))
offset=x_off))
self.connect_inst(["bl[{}]".format(col_num), self.connect_inst(["bl[{}]".format(col_num),
"br[{}]".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), "sel[{}]".format(col_num % self.words_per_row),
"gnd"]) "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): def add_layout_pins(self):
""" Add the pins after we determine the height. """ """ 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.width = (self.columns / self.words_per_row) * self.tri.width
self.height = self.tri.height self.height = self.tri.height
self.create_netlist()
self.create_layout() self.create_layout()
self.DRC_LVS()
def create_layout(self): def create_netlist(self):
"""generate layout """
self.add_pins() self.add_pins()
self.create_array() self.create_array()
def create_layout(self):
self.place_array()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS()
def add_pins(self): def add_pins(self):
"""create the name of pins depend on the word size""" """create the name of pins depend on the word size"""
@ -50,15 +53,21 @@ class tri_gate_array(design.design):
self.tri_inst = {} self.tri_inst = {}
for i in range(0,self.columns,self.words_per_row): for i in range(0,self.columns,self.words_per_row):
name = "Xtri_gate{0}".format(i) name = "Xtri_gate{0}".format(i)
base = vector(i*self.tri.width, 0)
self.tri_inst[i]=self.add_inst(name=name, self.tri_inst[i]=self.add_inst(name=name,
mod=self.tri, mod=self.tri)
offset=base)
index = int(i/self.words_per_row) index = int(i/self.words_per_row)
self.connect_inst(["in[{0}]".format(index), self.connect_inst(["in[{0}]".format(index),
"out[{0}]".format(index), "out[{0}]".format(index),
"en", "en_bar", "vdd", "gnd"]) "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): def add_layout_pins(self):

View File

@ -20,8 +20,8 @@ class wordline_driver(design.design):
design.design.__init__(self, "wordline_driver") design.design.__init__(self, "wordline_driver")
self.rows = rows self.rows = rows
self.add_pins() self.create_netlist()
self.design_layout() self.create_layout()
self.offset_all_coordinates() self.offset_all_coordinates()
self.DRC_LVS() self.DRC_LVS()
@ -36,14 +36,18 @@ class wordline_driver(design.design):
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def design_layout(self): def create_netlist(self):
self.create_modules() self.add_pins()
self.add_modules() self.add_modules()
self.create_drivers()
def create_layout(self):
self.place_drivers()
self.route_layout() self.route_layout()
self.route_vdd_gnd() self.route_vdd_gnd()
def create_modules(self): def add_modules(self):
self.inv = pinv() self.inv = pinv()
self.add_mod(self.inv) 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 inv1_xoffset = 2*self.m1_width + 5*self.m1_space
nand2_xoffset = inv1_xoffset + self.inv.width nand2_xoffset = inv1_xoffset + self.inv.width
inv2_xoffset = nand2_xoffset + self.nand2.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.height = self.inv.height * self.rows
self.inv1_inst = []
self.nand_inst = []
self.inv2_inst = []
for row in range(self.rows): for row in range(self.rows):
name_inv1 = "wl_driver_inv_en{}".format(row) name_inv1 = "wl_driver_inv_en{}".format(row)
name_nand = "wl_driver_nand{}".format(row) name_nand = "wl_driver_nand{}".format(row)
@ -113,30 +144,17 @@ class wordline_driver(design.design):
inv2_offset=[inv2_xoffset, y_offset] inv2_offset=[inv2_xoffset, y_offset]
# add inv1 based on the info above # add inv1 based on the info above
self.inv1_inst.append(self.add_inst(name=name_inv1, self.place_inst(name=name_inv1,
mod=self.inv_no_output, offset=inv1_offset,
offset=inv1_offset, mirror=inst_mirror)
mirror=inst_mirror))
self.connect_inst(["en",
"en_bar[{0}]".format(row),
"vdd", "gnd"])
# add nand 2 # add nand 2
self.nand_inst.append(self.add_inst(name=name_nand, self.place_inst(name=name_nand,
mod=self.nand2, offset=nand2_offset,
offset=nand2_offset, mirror=inst_mirror)
mirror=inst_mirror))
self.connect_inst(["en_bar[{0}]".format(row),
"in[{0}]".format(row),
"wl_bar[{0}]".format(row),
"vdd", "gnd"])
# add inv2 # add inv2
self.inv2_inst.append(self.add_inst(name=name_inv2, self.place_inst(name=name_inv2,
mod=self.inv, offset=inv2_offset,
offset=inv2_offset, mirror=inst_mirror)
mirror=inst_mirror))
self.connect_inst(["wl_bar[{0}]".format(row),
"wl[{0}]".format(row),
"vdd", "gnd"])
def route_layout(self): def route_layout(self):

View File

@ -28,9 +28,8 @@ class write_driver_array(design.design):
self.width = self.columns * self.driver.width self.width = self.columns * self.driver.width
self.height = self.height = self.driver.height self.height = self.height = self.driver.height
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
self.DRC_LVS()
def add_pins(self): def add_pins(self):
for i in range(self.word_size): for i in range(self.word_size):
@ -42,20 +41,22 @@ class write_driver_array(design.design):
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def create_layout(self): def create_netlist(self):
self.add_pins()
self.create_write_array() self.create_write_array()
def create_layout(self):
self.place_write_array()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS()
def create_write_array(self): def create_write_array(self):
self.driver_insts = {} self.driver_insts = {}
for i in range(0,self.columns,self.words_per_row): for i in range(0,self.columns,self.words_per_row):
name = "Xwrite_driver{}".format(i) name = "Xwrite_driver{}".format(i)
base = vector(i * self.driver.width,0)
index = int(i/self.words_per_row) index = int(i/self.words_per_row)
self.driver_insts[index]=self.add_inst(name=name, self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver, mod=self.driver)
offset=base)
self.connect_inst(["data[{0}]".format(index), self.connect_inst(["data[{0}]".format(index),
"bl[{0}]".format(index), "bl[{0}]".format(index),
@ -63,6 +64,15 @@ class write_driver_array(design.design):
"en", "vdd", "gnd"]) "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): def add_layout_pins(self):
for i in range(self.word_size): for i in range(self.word_size):
din_pin = self.driver_insts[i].get_pin("din") din_pin = self.driver_insts[i].get_pin("din")

View File

@ -40,7 +40,10 @@ report_status()
import verify import verify
import sram 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 are: ")
print(*output_files,sep="\n") 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 # This is the verbosity level to control debug information. 0 is none, 1
# is minimal, etc. # is minimal, etc.
debug_level = 0 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. # This determines whether LVS and DRC is checked for each submodule.
check_lvsdrc = True check_lvsdrc = True
# Variable to select the variant of spice # Variable to select the variant of spice

View File

@ -28,9 +28,8 @@ class pbitcell(pgate.pgate):
self.num_write = num_write self.num_write = num_write
self.num_read = num_read self.num_read = num_read
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
self.DRC_LVS()
pbitcell.width = self.width pbitcell.width = self.width
pbitcell.height = self.height pbitcell.height = self.height
@ -61,6 +60,9 @@ class pbitcell(pgate.pgate):
self.add_pin("gnd") self.add_pin("gnd")
def create_netlist(self):
self.add_pins()
def create_layout(self): def create_layout(self):
self.create_ptx() self.create_ptx()
self.calculate_spacing() self.calculate_spacing()
@ -75,6 +77,7 @@ class pbitcell(pgate.pgate):
self.add_read_ports() self.add_read_ports()
self.extend_well() self.extend_well()
self.offset_all_coordinates() self.offset_all_coordinates()
self.DRC_LVS()
def create_ptx(self): def create_ptx(self):
@ -1055,10 +1058,9 @@ class pbitcell(pgate.pgate):
bitcell_pins.append("gnd") bitcell_pins.append("gnd")
return bitcell_pins return bitcell_pins
def list_row_pins(self): def list_all_wl_names(self):
""" Creates a list of all row pins (except for gnd and vdd) """ """ Creates a list of all wordline pin names """
row_pins = [] row_pins = []
for k in range(self.num_readwrite): for k in range(self.num_readwrite):
row_pins.append("rwwl{0}".format(k)) row_pins.append("rwwl{0}".format(k))
@ -1069,8 +1071,8 @@ class pbitcell(pgate.pgate):
return row_pins return row_pins
def list_read_row_pins(self): def list_read_wl_names(self):
""" Creates a list of row pins associated with read ports """ """ Creates a list of wordline pin names associated with read ports """
row_pins = [] row_pins = []
for k in range(self.num_readwrite): for k in range(self.num_readwrite):
row_pins.append("rwwl{0}".format(k)) row_pins.append("rwwl{0}".format(k))
@ -1079,8 +1081,8 @@ class pbitcell(pgate.pgate):
return row_pins return row_pins
def list_write_row_pins(self): def list_write_wl_names(self):
""" Creates a list of row pins associated with write ports """ """ Creates a list of wordline pin names associated with write ports """
row_pins = [] row_pins = []
for k in range(self.num_readwrite): for k in range(self.num_readwrite):
row_pins.append("rwwl{0}".format(k)) row_pins.append("rwwl{0}".format(k))
@ -1090,8 +1092,8 @@ class pbitcell(pgate.pgate):
return row_pins return row_pins
def list_all_column_pins(self): def list_all_bitline_names(self):
""" Creates a list of all bitline pins """ """ Creates a list of all bitline pin names (both bl and br) """
column_pins = [] column_pins = []
for k in range(self.num_readwrite): for k in range(self.num_readwrite):
column_pins.append("rwbl{0}".format(k)) column_pins.append("rwbl{0}".format(k))
@ -1105,8 +1107,8 @@ class pbitcell(pgate.pgate):
return column_pins return column_pins
def list_column_pins(self): def list_all_bl_names(self):
""" Creates a list of all bitline bar pins """ """ Creates a list of all bl pins names """
column_pins = [] column_pins = []
for k in range(self.num_readwrite): for k in range(self.num_readwrite):
column_pins.append("rwbl{0}".format(k)) column_pins.append("rwbl{0}".format(k))
@ -1117,8 +1119,8 @@ class pbitcell(pgate.pgate):
return column_pins return column_pins
def list_column_bar_pins(self): def list_all_br_names(self):
""" Creates a list of all bitline bar pins """ """ Creates a list of all br pins names """
column_pins = [] column_pins = []
for k in range(self.num_readwrite): for k in range(self.num_readwrite):
column_pins.append("rwbl_bar{0}".format(k)) column_pins.append("rwbl_bar{0}".format(k))
@ -1129,8 +1131,8 @@ class pbitcell(pgate.pgate):
return column_pins return column_pins
def list_read_column_pins(self): def list_read_bl_names(self):
""" Creates a list of column pins associated with read ports """ """ Creates a list of bl pin names associated with read ports """
column_pins = [] column_pins = []
for k in range(self.num_readwrite): for k in range(self.num_readwrite):
column_pins.append("rwbl{0}".format(k)) column_pins.append("rwbl{0}".format(k))
@ -1139,8 +1141,8 @@ class pbitcell(pgate.pgate):
return column_pins return column_pins
def list_read_bar_column_pins(self): def list_read_br_names(self):
""" Creates a list of column pins associated with read_bar ports """ """ Creates a list of br pin names associated with read ports """
column_pins = [] column_pins = []
for k in range(self.num_readwrite): for k in range(self.num_readwrite):
column_pins.append("rwbl_bar{0}".format(k)) column_pins.append("rwbl_bar{0}".format(k))
@ -1149,8 +1151,8 @@ class pbitcell(pgate.pgate):
return column_pins return column_pins
def list_write_column_pins(self): def list_write_bl_names(self):
""" Creates a list of column pins associated with write ports """ """ Creates a list of bl pin names associated with write ports """
column_pins = [] column_pins = []
for k in range(self.num_readwrite): for k in range(self.num_readwrite):
column_pins.append("rwbl{0}".format(k)) column_pins.append("rwbl{0}".format(k))
@ -1159,8 +1161,8 @@ class pbitcell(pgate.pgate):
return column_pins return column_pins
def list_write_bar_column_pins(self): def list_write_br_names(self):
""" Creates a list of column pins asscociated with write_bar ports""" """ Creates a list of br pin names asscociated with write ports"""
column_pins = [] column_pins = []
for k in range(self.num_readwrite): for k in range(self.num_readwrite):
column_pins.append("rwbl_bar{0}".format(k)) 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.height = height # Maybe minimize height if not defined in future?
self.route_output = False self.route_output = False
self.add_pins()
self.create_netlist()
self.create_layout() self.create_layout()
# for run-time, we won't check every transitor DRC/LVS independently # 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 """ """ Adds pins for spice netlist """
self.add_pin_list(["A", "Z", "vdd", "gnd"]) 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): def create_layout(self):
""" Calls all functions related to the generation of the layout """ """ 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=""): 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. # FIXME: Change the number of stages to support high drives.
# stage effort of 4 or less # stage effort of 4 or less
# The pinvbuf has a FO of 2 for the first stage, so the second stage # 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 # 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=="": 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 pinvbuf.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.create_netlist()
# Shield the cap, but have at least a stage effort of 4 self.create_layout()
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.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.width = 2*self.inv1.width + self.inv2.width
self.height = 2*self.inv1.height self.height = 2*self.inv1.height
self.create_layout() self.place_insts()
self.route_wires()
self.add_layout_pins()
self.offset_all_coordinates() self.offset_all_coordinates()
self.DRC_LVS() self.DRC_LVS()
def create_layout(self):
self.add_pins()
self.add_insts()
self.add_wires()
self.add_layout_pins()
def add_pins(self): def add_pins(self):
self.add_pin("A") self.add_pin("A")
@ -68,35 +64,58 @@ class pinvbuf(design.design):
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def add_insts(self): def add_modules(self):
# Add INV1 to the right (capacitance shield)
# 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", self.inv1_inst=self.add_inst(name="buf_inv1",
mod=self.inv, mod=self.inv)
offset=vector(0,0))
self.connect_inst(["A", "zb_int", "vdd", "gnd"]) self.connect_inst(["A", "zb_int", "vdd", "gnd"])
# Add INV2 to the right
self.inv2_inst=self.add_inst(name="buf_inv2", self.inv2_inst=self.add_inst(name="buf_inv2",
mod=self.inv1, mod=self.inv1)
offset=vector(self.inv1_inst.rx(),0))
self.connect_inst(["zb_int", "z_int", "vdd", "gnd"]) self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
# Add INV3 to the right
self.inv3_inst=self.add_inst(name="buf_inv3", self.inv3_inst=self.add_inst(name="buf_inv3",
mod=self.inv2, mod=self.inv2)
offset=vector(self.inv2_inst.rx(),0))
self.connect_inst(["z_int", "Zb", "vdd", "gnd"]) self.connect_inst(["z_int", "Zb", "vdd", "gnd"])
# Add INV4 to the bottom
self.inv4_inst=self.add_inst(name="buf_inv4", self.inv4_inst=self.add_inst(name="buf_inv4",
mod=self.inv2, mod=self.inv2)
offset=vector(self.inv2_inst.rx(),2*self.inv2.height),
mirror = "MX")
self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) 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 # inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z") z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A") 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.") debug.check(size==1,"Size 1 pnand2 is only supported now.")
self.tx_mults = 1 self.tx_mults = 1
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
#self.DRC_LVS() #self.DRC_LVS()
@ -44,6 +44,10 @@ class pnand2(pgate.pgate):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
def create_netlist(self):
self.add_pins()
def create_layout(self): def create_layout(self):
""" Calls all functions related to the generation of the layout """ """ Calls all functions related to the generation of the layout """

View File

@ -14,7 +14,8 @@ class pnand3(pgate.pgate):
from importlib import reload from importlib import reload
c = reload(__import__(OPTS.bitcell)) c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell) mod_bitcell = getattr(c, OPTS.bitcell)
bitcell = mod_bitcell()
unique_id = 1 unique_id = 1
@ -37,7 +38,7 @@ class pnand3(pgate.pgate):
debug.check(size==1,"Size 1 pnand3 is only supported now.") debug.check(size==1,"Size 1 pnand3 is only supported now.")
self.tx_mults = 1 self.tx_mults = 1
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
#self.DRC_LVS() #self.DRC_LVS()
@ -46,6 +47,9 @@ class pnand3(pgate.pgate):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """
self.add_pin_list(["A", "B", "C", "Z", "vdd", "gnd"]) self.add_pin_list(["A", "B", "C", "Z", "vdd", "gnd"])
def create_netlist(self):
self.add_pins()
def create_layout(self): def create_layout(self):
""" Calls all functions related to the generation of the layout """ """ 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.") debug.check(size==1,"Size 1 pnor2 is only supported now.")
self.tx_mults = 1 self.tx_mults = 1
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
#self.DRC_LVS() #self.DRC_LVS()
@ -45,6 +45,9 @@ class pnor2(pgate.pgate):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
def create_netlist(self):
self.add_pins()
def create_layout(self): def create_layout(self):
""" Calls all functions related to the generation of the layout """ """ 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. 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) pgate.pgate.__init__(self, name)
debug.info(2, "create single precharge cell: {0}".format(name)) debug.info(2, "create single precharge cell: {0}".format(name))
@ -24,16 +28,18 @@ class precharge(pgate.pgate):
self.beta = parameter["beta"] self.beta = parameter["beta"]
self.ptx_width = self.beta*parameter["min_tx_size"] self.ptx_width = self.beta*parameter["min_tx_size"]
self.width = self.bitcell.width self.width = self.bitcell.width
self.BL = BL self.bitcell_bl = bitcell_bl
self.BR = BR self.bitcell_br = bitcell_br
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
self.DRC_LVS()
def add_pins(self): 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): def create_layout(self):
self.create_ptx() self.create_ptx()
self.add_ptx() self.add_ptx()
@ -43,6 +49,7 @@ class precharge(pgate.pgate):
self.add_vdd_rail() self.add_vdd_rail()
self.add_bitlines() self.add_bitlines()
self.connect_to_bitlines() self.connect_to_bitlines()
self.DRC_LVS()
def create_ptx(self): def create_ptx(self):
"""Initializes the upper and lower pmos""" """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 both the upper_pmos and lower_pmos to the module"""
# adds the lower pmos to layout # adds the lower pmos to layout
#base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0) #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.pmos.active_offset.y)
self.lower_pmos_inst=self.add_inst(name="lower_pmos", self.lower_pmos_inst=self.add_inst(name="lower_pmos",
mod=self.pmos, mod=self.pmos,
offset=self.lower_pmos_position) 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 # adds the upper pmos(s) to layout
ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width 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", self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
mod=self.pmos, mod=self.pmos,
offset=self.upper_pmos1_pos) 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 upper_pmos2_pos = self.upper_pmos1_pos + self.overlap_offset
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2", self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
mod=self.pmos, mod=self.pmos,
offset=upper_pmos2_pos) offset=upper_pmos2_pos)
self.connect_inst([self.BR, "en", "vdd", "vdd"]) self.connect_inst(["br", "en", "vdd", "vdd"])
def connect_poly(self): def connect_poly(self):
"""Connects the upper and lower pmos together""" """Connects the upper and lower pmos together"""
@ -161,16 +168,16 @@ class precharge(pgate.pgate):
def add_bitlines(self): def add_bitlines(self):
"""Adds both bit-line and bit-line-bar to the module""" """Adds both bit-line and bit-line-bar to the module"""
# adds the BL on metal 2 # adds the BL on metal 2
offset = vector(self.bitcell.get_pin(self.BL).cx(),0) - vector(0.5 * self.m2_width,0) offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0)
self.add_layout_pin(text=self.BL, self.add_layout_pin(text="bl",
layer="metal2", layer="metal2",
offset=offset, offset=offset,
width=drc['minwidth_metal2'], width=drc['minwidth_metal2'],
height=self.height) height=self.height)
# adds the BR on metal 2 # adds the BR on metal 2
offset = vector(self.bitcell.get_pin(self.BR).cx(),0) - vector(0.5 * self.m2_width,0) offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0)
self.add_layout_pin(text=self.BR, self.add_layout_pin(text="br",
layer="metal2", layer="metal2",
offset=offset, offset=offset,
width=drc['minwidth_metal2'], width=drc['minwidth_metal2'],
@ -178,10 +185,10 @@ class precharge(pgate.pgate):
def connect_to_bitlines(self): def connect_to_bitlines(self):
self.add_bitline_contacts() 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("S"),self.get_pin("bl"))
self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin(self.BR)) 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(self.BL)) 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(self.BR)) self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br"))
def add_bitline_contacts(self): def add_bitline_contacts(self):

View File

@ -39,7 +39,7 @@ class ptx(design.design):
self.connect_poly = connect_poly self.connect_poly = connect_poly
self.num_contacts = num_contacts self.num_contacts = num_contacts
self.create_spice() self.create_netlist()
self.create_layout() self.create_layout()
self.translate_all(self.active_offset) self.translate_all(self.active_offset)
@ -57,7 +57,7 @@ class ptx(design.design):
self.add_poly() self.add_poly()
self.add_active_contacts() self.add_active_contacts()
def create_spice(self): def create_netlist(self):
self.add_pin_list(["D", "G", "S", "B"]) self.add_pin_list(["D", "G", "S", "B"])
# self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # 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.bitcell = self.mod_bitcell()
self.ptx_width = tx_size * drc["minwidth_tx"] 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() 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() self.add_ptx()
def create_layout(self):
self.pin_height = 2*self.m2_width self.pin_height = 2*self.m2_width
self.width = self.bitcell.width self.width = self.bitcell.width
self.height = self.nmos_upper.uy() + self.pin_height self.height = self.nmos_upper.uy() + self.pin_height

View File

@ -37,9 +37,7 @@ class sram():
else: else:
debug.error("Invalid number of banks.",-1) debug.error("Invalid number of banks.",-1)
self.s.compute_sizes() self.s.create_netlist()
self.s.create_modules()
self.s.add_pins()
self.s.create_layout() self.s.create_layout()
# Can remove the following, but it helps for debug! # 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) lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file)
print_time("Characterization", datetime.datetime.now(), start_time) print_time("Characterization", datetime.datetime.now(), start_time)
# Write the layout if not OPTS.netlist_only:
start_time = datetime.datetime.now() # Write the layout
gdsname = OPTS.output_path + self.s.name + ".gds" start_time = datetime.datetime.now()
print("GDS: Writing to {0}".format(gdsname)) gdsname = OPTS.output_path + self.s.name + ".gds"
self.s.gds_write(gdsname) print("GDS: Writing to {0}".format(gdsname))
print_time("GDS", datetime.datetime.now(), start_time) self.s.gds_write(gdsname)
print_time("GDS", datetime.datetime.now(), start_time)
# Create a LEF physical model # Create a LEF physical model
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
lefname = OPTS.output_path + self.s.name + ".lef" lefname = OPTS.output_path + self.s.name + ".lef"
print("LEF: Writing to {0}".format(lefname)) print("LEF: Writing to {0}".format(lefname))
self.s.lef_write(lefname) self.s.lef_write(lefname)
print_time("LEF", datetime.datetime.now(), start_time) print_time("LEF", datetime.datetime.now(), start_time)
# Write a verilog model # Write a verilog model
start_time = datetime.datetime.now() start_time = datetime.datetime.now()

View File

@ -20,14 +20,38 @@ class sram_1bank(sram_base):
def __init__(self, word_size, num_words, name): def __init__(self, word_size, num_words, name):
sram_base.__init__(self, word_size, num_words, 1, 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. logic.
""" """
# No orientation or offset # 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 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 # 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. # up to the row address DFFs.
control_pos = vector(-self.control_logic.width - 2*self.m2_pitch, control_pos = vector(-self.control_logic.width - 2*self.m2_pitch,
self.bank.bank_center.y - self.control_logic.control_logic_center.y) 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. # 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, row_addr_pos = vector(self.control_logic_inst.rx() - self.row_addr_dff.width,
self.control_logic_inst.uy()) 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 # 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) data_gap = -self.m2_pitch*(self.word_size+1)
@ -51,7 +77,8 @@ class sram_1bank(sram_base):
if self.col_addr_dff: if self.col_addr_dff:
col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width, 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) 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: # Add the data flops below the bank to the right of the center of bank:
# This relies on the center point of the bank: # This relies on the center point of the bank:
@ -60,12 +87,13 @@ class sram_1bank(sram_base):
# sense amps. # sense amps.
data_pos = vector(self.bank.bank_center.x, data_pos = vector(self.bank.bank_center.x,
data_gap - self.data_dff.height) 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. # 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.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
self.height = self.bank.height self.height = self.bank.height
def add_layout_pins(self): def add_layout_pins(self):
""" """
Add the top-level pins for a single bank SRAM with control. 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) self.copy_layout_pin(self.control_logic_inst, n)
for i in range(self.word_size): for i in range(self.word_size):
dout_name = "dout[{}]".format(i) dout_name = "dout0[{}]".format(i)
self.copy_layout_pin(self.bank_inst, dout_name, dout_name.upper()) self.copy_layout_pin(self.bank_inst, dout_name, "DOUT[{}]".format(i))
# Lower address bits # Lower address bits
for i in range(self.col_addr_size): 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 """ """ Connect the output of the row flops to the bank pins """
for i in range(self.row_addr_size): for i in range(self.row_addr_size):
flop_name = "dout[{}]".format(i) 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) flop_pin = self.row_addr_dff_inst.get_pin(flop_name)
bank_pin = self.bank_inst.get_pin(bank_name) bank_pin = self.bank_inst.get_pin(bank_name)
flop_pos = flop_pin.center() flop_pos = flop_pin.center()
@ -204,7 +232,7 @@ class sram_1bank(sram_base):
data_dff_map = zip(dff_names, bus_names) data_dff_map = zip(dff_names, bus_names)
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst, col_addr_bus_offsets) 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) data_bank_map = zip(bank_names, bus_names)
self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets) 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) offset = self.data_dff_inst.ul() + vector(0, self.m1_pitch)
dff_names = ["dout[{}]".format(x) for x in range(self.word_size)] 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)) route_map = list(zip(bank_names, dff_names))
dff_pins = {key: self.data_dff_inst.get_pin(key) for key in 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_words = num_words
self.num_banks = num_banks self.num_banks = num_banks
self.bank_insts = []
def compute_sizes(self): def compute_sizes(self):
""" Computes the organization of the memory using bitcell size by trying to make it square.""" """ 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") self.add_pin("gnd","GROUND")
def create_netlist(self):
""" Netlist creation """
self.add_modules()
self.add_pins()
def create_layout(self): def create_layout(self):
""" Layout creation """ """ Layout creation """
self.add_modules() self.place_modules()
self.route() self.route()
self.add_lvs_correspondence_points() self.add_lvs_correspondence_points()
def compute_bus_sizes(self): def compute_bus_sizes(self):
""" Compute the independent bus widths shared between two and four bank SRAMs """ """ 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 """ """ Add the horizontal and vertical busses """
# Vertical bus # Vertical bus
# The order of the control signals on the control 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", self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=self.vertical_bus_offset, offset=self.vertical_bus_offset,
@ -235,7 +242,7 @@ class sram_base(design):
self.copy_layout_pin(inst, "gnd") 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 """ """ Create the multibank address flops and bank decoder """
from dff_buf_array import dff_buf_array from dff_buf_array import dff_buf_array
self.msb_address = dff_buf_array(name="msb_address", 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.msb_decoder = self.bank.decoder.pre2_4
self.add_mod(self.msb_decoder) self.add_mod(self.msb_decoder)
def create_modules(self): def add_modules(self):
""" Create all the modules that will be used """ """ Create all the modules that will be used """
from control_logic import control_logic from control_logic import control_logic
@ -280,7 +287,7 @@ class sram_base(design):
# Create bank decoder # Create bank decoder
if(self.num_banks > 1): if(self.num_banks > 1):
self.create_multi_bank_modules() self.add_multi_bank_modules()
self.bank_count = 0 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 """ """ Place a bank at the given position with orientations """
# x_flip == 1 --> no flip in x_axis # x_flip == 1 --> no flip in x_axis
@ -313,32 +341,19 @@ class sram_base(design):
else: else:
bank_mirror = "R0" bank_mirror = "R0"
bank_inst=self.add_inst(name="bank{0}".format(bank_num), self.place_inst(name=bank_inst.name,
mod=self.bank, offset=position,
offset=position, mirror=bank_mirror,
mirror=bank_mirror, rotate=bank_rotation)
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)
return bank_inst return bank_inst
def add_row_addr_dff(self, position): def create_row_addr_dff(self):
""" Add and place all address flops for the main decoder """ """ Add all address flops for the main decoder """
self.row_addr_dff_inst = self.add_inst(name="row_address", inst = self.add_inst(name="row_address",
mod=self.row_addr_dff, mod=self.row_addr_dff)
offset=position)
# inputs, outputs/output/bar # inputs, outputs/output/bar
inputs = [] inputs = []
outputs = [] outputs = []
@ -347,13 +362,13 @@ class sram_base(design):
outputs.append("A[{}]".format(i+self.col_addr_size)) outputs.append("A[{}]".format(i+self.col_addr_size))
self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) 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 """ """ Add and place all address flops for the column decoder """
self.col_addr_dff_inst = self.add_inst(name="row_address", inst = self.add_inst(name="col_address",
mod=self.col_addr_dff, mod=self.col_addr_dff)
offset=position)
# inputs, outputs/output/bar # inputs, outputs/output/bar
inputs = [] inputs = []
outputs = [] outputs = []
@ -362,12 +377,13 @@ class sram_base(design):
outputs.append("A[{}]".format(i)) outputs.append("A[{}]".format(i))
self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"])
return inst
def add_data_dff(self, position):
def create_data_dff(self):
""" Add and place all data flops """ """ Add and place all data flops """
self.data_dff_inst = self.add_inst(name="data_dff", inst = self.add_inst(name="data_dff",
mod=self.data_dff, mod=self.data_dff)
offset=position)
# inputs, outputs/output/bar # inputs, outputs/output/bar
inputs = [] inputs = []
outputs = [] outputs = []
@ -376,15 +392,16 @@ class sram_base(design):
outputs.append("BANK_DIN[{}]".format(i)) outputs.append("BANK_DIN[{}]".format(i))
self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) 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 """ """ Add and place control logic """
self.control_logic_inst=self.add_inst(name="control", inst = self.add_inst(name="control",
mod=self.control_logic, mod=self.control_logic)
offset=position)
self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"]) 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: if len(key_positions)>10:
line_numbers = key_positions[:10] + [" ..."] line_numbers = key_positions[:10] + [" ..."]
else: else:
line_numbers = key_positoins line_numbers = key_positions
debug.info(0, '\nFound ' + str(len(key_positions)) + ' tabs in ' + 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() f.close()
return len(key_positions) 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.rw_ports = 2
OPTS.r_ports = 2 OPTS.r_ports = 2
OPTS.w_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) 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) 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) self.local_check(tx)
#globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test
if __name__ == "__main__": 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") debug.info(2, "Checking 3 column precharge")
pc = precharge_array.precharge_array(columns=3) pc = precharge_array.precharge_array(columns=3)
self.local_check(pc) 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() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test
if __name__ == "__main__": 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()