mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into multiport_characterization
This commit is contained in:
commit
8a0411279e
|
|
@ -57,6 +57,9 @@ class geometry:
|
|||
def compute_boundary(self,offset=vector(0,0),mirror="",rotate=0):
|
||||
""" Transform with offset, mirror and rotation to get the absolute pin location.
|
||||
We must then re-find the ll and ur. The master is the cell instance. """
|
||||
if OPTS.netlist_only:
|
||||
return
|
||||
|
||||
(ll,ur) = [vector(0,0),vector(self.width,self.height)]
|
||||
if mirror=="MX":
|
||||
ll=ll.scale(1,-1)
|
||||
|
|
@ -192,6 +195,14 @@ class instance(geometry):
|
|||
mirror=self.mirror,
|
||||
rotate=self.rotate)
|
||||
|
||||
def place(self, offset, mirror="R0", rotate=0):
|
||||
""" This updates the placement of an instance. """
|
||||
debug.info(3, "placing instance {}".format(self.name))
|
||||
# Update the placement of an already added instance
|
||||
self.offset = vector(offset)
|
||||
self.mirror = mirror
|
||||
self.rotate = rotate
|
||||
self.update_boundary()
|
||||
|
||||
|
||||
def get_pin(self,name,index=-1):
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import debug
|
|||
from tech import drc, GDS
|
||||
from tech import layer as techlayer
|
||||
import os
|
||||
from globals import OPTS
|
||||
from vector import vector
|
||||
from pin_layout import pin_layout
|
||||
import lef
|
||||
|
|
@ -118,17 +119,6 @@ class layout(lef.lef):
|
|||
for pin in pin_list:
|
||||
pin.rect = [pin.ll() - offset, pin.ur() - offset]
|
||||
|
||||
def place_inst(self, name, offset, mirror="R0", rotate=0):
|
||||
""" This updates the placement of an instance. """
|
||||
inst = self.get_inst(name)
|
||||
debug.info(3, "placing instance {}".format(inst))
|
||||
# Update the placement of an already added instance
|
||||
inst.offset = offset
|
||||
inst.mirror = mirror
|
||||
inst.rotate = rotate
|
||||
inst.update_boundary()
|
||||
return inst
|
||||
|
||||
def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0):
|
||||
"""Adds an instance of a mod to this module"""
|
||||
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
|
||||
|
|
@ -436,6 +426,10 @@ class layout(lef.lef):
|
|||
def gds_read(self):
|
||||
"""Reads a GDSII file in the library and checks if it exists
|
||||
Otherwise, start a new layout for dynamic generation."""
|
||||
if OPTS.netlist_only:
|
||||
self.gds = None
|
||||
return
|
||||
|
||||
# open the gds file if it exists or else create a blank layout
|
||||
if os.path.isfile(self.gds_file):
|
||||
debug.info(3, "opening %s" % self.gds_file)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ supply_voltages = [1.0]
|
|||
temperatures = [25]
|
||||
|
||||
output_path = "temp"
|
||||
output_name = "sram_2_16_1_freepdk45"
|
||||
output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,num_words,num_banks,tech_name)
|
||||
|
||||
#Below are some additions to test additional ports on sram
|
||||
#bitcell = "pbitcell"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ process_corners = ["TT"]
|
|||
supply_voltages = [ 5.0 ]
|
||||
temperatures = [ 25 ]
|
||||
|
||||
|
||||
output_path = "temp"
|
||||
output_name = "sram_2_16_1_scn3me_subm"
|
||||
output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,num_words,num_banks,tech_name)
|
||||
|
||||
|
|
|
|||
|
|
@ -142,22 +142,31 @@ def init_openram(config_file, is_unit_test=True):
|
|||
|
||||
|
||||
|
||||
def get_tool(tool_type, preferences):
|
||||
def get_tool(tool_type, preferences, default_name=None):
|
||||
"""
|
||||
Find which tool we have from a list of preferences and return the
|
||||
one selected and its full path.
|
||||
one selected and its full path. If default is specified,
|
||||
find that one only and error otherwise.
|
||||
"""
|
||||
debug.info(2,"Finding {} tool...".format(tool_type))
|
||||
|
||||
for name in preferences:
|
||||
exe_name = find_exe(name)
|
||||
if exe_name != None:
|
||||
debug.info(1, "Using {0}: {1}".format(tool_type,exe_name))
|
||||
return(name,exe_name)
|
||||
if default_name:
|
||||
exe_name=find_exe(default_name)
|
||||
if exe_name == None:
|
||||
debug.error("{0} not found. Cannot find {1} tool.".format(default_name,tool_type),2)
|
||||
else:
|
||||
debug.info(1, "Could not find {0}, trying next {1} tool.".format(name,tool_type))
|
||||
debug.info(1, "Using {0}: {1}".format(tool_type,exe_name))
|
||||
return(default_name,exe_name)
|
||||
else:
|
||||
return(None,"")
|
||||
for name in preferences:
|
||||
exe_name = find_exe(name)
|
||||
if exe_name != None:
|
||||
debug.info(1, "Using {0}: {1}".format(tool_type,exe_name))
|
||||
return(name,exe_name)
|
||||
else:
|
||||
debug.info(1, "Could not find {0}, trying next {1} tool.".format(name,tool_type))
|
||||
else:
|
||||
return(None,"")
|
||||
|
||||
|
||||
def read_config(config_file, is_unit_test=True):
|
||||
|
|
@ -207,7 +216,6 @@ def read_config(config_file, is_unit_test=True):
|
|||
# If we are only generating a netlist, we can't do DRC/LVS
|
||||
if OPTS.netlist_only:
|
||||
OPTS.check_lvsdrc=False
|
||||
|
||||
|
||||
# If config didn't set output name, make a reasonable default.
|
||||
if (OPTS.output_name == ""):
|
||||
|
|
|
|||
|
|
@ -22,17 +22,6 @@ class bank(design.design):
|
|||
|
||||
def __init__(self, word_size, num_words, words_per_row, num_banks=1, name=""):
|
||||
|
||||
mod_list = ["bitcell", "decoder", "ms_flop_array", "wordline_driver",
|
||||
"bitcell_array", "sense_amp_array", "precharge_array",
|
||||
"column_mux_array", "write_driver_array",
|
||||
"dff", "bank_select"]
|
||||
from importlib import reload
|
||||
for mod_name in mod_list:
|
||||
config_mod_name = getattr(OPTS, mod_name)
|
||||
class_file = reload(__import__(config_mod_name))
|
||||
mod_class = getattr(class_file , config_mod_name)
|
||||
setattr (self, "mod_"+mod_name, mod_class)
|
||||
|
||||
if name == "":
|
||||
name = "bank_{0}_{1}".format(word_size, num_words)
|
||||
design.design.__init__(self, name)
|
||||
|
|
@ -56,7 +45,8 @@ class bank(design.design):
|
|||
self.prefix=""
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
|
|
@ -205,6 +195,19 @@ class bank(design.design):
|
|||
|
||||
def add_modules(self):
|
||||
""" Create all the modules using the class loader """
|
||||
|
||||
mod_list = ["bitcell", "decoder", "ms_flop_array", "wordline_driver",
|
||||
"bitcell_array", "sense_amp_array", "precharge_array",
|
||||
"column_mux_array", "write_driver_array",
|
||||
"dff", "bank_select"]
|
||||
from importlib import reload
|
||||
for mod_name in mod_list:
|
||||
config_mod_name = getattr(OPTS, mod_name)
|
||||
class_file = reload(__import__(config_mod_name))
|
||||
mod_class = getattr(class_file , config_mod_name)
|
||||
setattr (self, "mod_"+mod_name, mod_class)
|
||||
|
||||
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
|
||||
|
|
@ -277,8 +280,7 @@ class bank(design.design):
|
|||
|
||||
def place_bitcell_array(self):
|
||||
""" Placing Bitcell Array """
|
||||
self.place_inst(name="bitcell_array",
|
||||
offset=vector(0,0))
|
||||
self.bitcell_array_inst.place(vector(0,0))
|
||||
|
||||
|
||||
def create_precharge_array(self):
|
||||
|
|
@ -303,8 +305,7 @@ class bank(design.design):
|
|||
# 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))
|
||||
self.precharge_array_inst[k].place(vector(0,y_offset))
|
||||
|
||||
def create_column_mux_array(self):
|
||||
""" Creating Column Mux when words_per_row > 1 . """
|
||||
|
|
@ -338,8 +339,7 @@ class bank(design.design):
|
|||
|
||||
for k in range(self.total_ports):
|
||||
y_offset = self.column_mux_height
|
||||
self.place_inst(name=self.col_mux_array_inst[k].name,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
self.col_mux_array_inst[k].place(vector(0,y_offset).scale(-1,-1))
|
||||
|
||||
def create_sense_amp_array(self):
|
||||
""" Creating Sense amp """
|
||||
|
|
@ -368,8 +368,7 @@ class bank(design.design):
|
|||
# FIXME: place for multiport
|
||||
for k in range(self.total_read):
|
||||
y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap
|
||||
self.place_inst(name=self.sense_amp_array_inst[k].name,
|
||||
offset=vector(0,y_offset).scale(-1,-1))
|
||||
self.sense_amp_array_inst[k].place(vector(0,y_offset).scale(-1,-1))
|
||||
|
||||
def create_write_driver_array(self):
|
||||
""" Creating Write Driver """
|
||||
|
|
@ -399,8 +398,7 @@ class bank(design.design):
|
|||
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))
|
||||
self.write_driver_array_inst[k].place(vector(0,y_offset).scale(-1,-1))
|
||||
|
||||
|
||||
|
||||
|
|
@ -432,8 +430,7 @@ class bank(design.design):
|
|||
# FIXME: place for multiport
|
||||
for k in range(self.total_ports):
|
||||
x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||
self.place_inst(name=self.row_decoder_inst[k].name,
|
||||
offset=vector(x_offset,0))
|
||||
self.row_decoder_inst[k].place(vector(x_offset,0))
|
||||
|
||||
|
||||
def create_wordline_driver(self):
|
||||
|
|
@ -461,8 +458,7 @@ class bank(design.design):
|
|||
for k in range(self.total_ports):
|
||||
# The wordline driver is placed to the right of the main decoder width.
|
||||
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch
|
||||
self.place_inst(name=self.wordline_driver_inst[k].name,
|
||||
offset=vector(x_offset,0))
|
||||
self.wordline_driver_inst[k].place(vector(x_offset,0))
|
||||
|
||||
|
||||
def create_column_decoder(self):
|
||||
|
|
@ -508,8 +504,7 @@ class bank(design.design):
|
|||
# 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))
|
||||
self.col_decoder_inst[k].place(vector(x_off,y_off))
|
||||
|
||||
|
||||
def create_bank_select(self):
|
||||
|
|
@ -545,8 +540,7 @@ class bank(design.design):
|
|||
y_off = self.row_decoder_inst[0].by()
|
||||
y_off -= (self.bank_select.height + drc["well_to_well"])
|
||||
self.bank_select_pos = vector(x_off,y_off)
|
||||
self.place_inst(name=self.bank_select_inst[k].name,
|
||||
offset=self.bank_select_pos)
|
||||
self.bank_select_inst[k].place(self.bank_select_pos)
|
||||
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
|
|
@ -580,23 +574,25 @@ class bank(design.design):
|
|||
|
||||
def route_bank_select(self):
|
||||
""" Route the bank select logic. """
|
||||
for input_name in self.input_control_signals+["bank_sel"]:
|
||||
self.copy_layout_pin(self.bank_select_inst, input_name)
|
||||
|
||||
for k in range(self.total_ports):
|
||||
for input_name in self.input_control_signals+["bank_sel"]:
|
||||
self.copy_layout_pin(self.bank_select_inst[k], input_name)
|
||||
|
||||
for gated_name in self.control_signals:
|
||||
# Connect the inverter output to the central bus
|
||||
out_pos = self.bank_select_inst[0].get_pin(gated_name).rc()
|
||||
bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y)
|
||||
self.add_path("metal3",[out_pos, bus_pos])
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=bus_pos,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=out_pos,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=out_pos,
|
||||
rotate=90)
|
||||
for gated_name in self.control_signals:
|
||||
# Connect the inverter output to the central bus
|
||||
out_pos = self.bank_select_inst[k].get_pin(gated_name).rc()
|
||||
bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y)
|
||||
self.add_path("metal3",[out_pos, bus_pos])
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=bus_pos,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=out_pos,
|
||||
rotate=90)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=out_pos,
|
||||
rotate=90)
|
||||
|
||||
|
||||
def setup_routing_constraints(self):
|
||||
|
|
|
|||
|
|
@ -18,9 +18,29 @@ class bank_select(design.design):
|
|||
def __init__(self, name="bank_select"):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_modules()
|
||||
|
||||
def create_layout(self):
|
||||
self.calculate_module_offsets()
|
||||
self.place_modules()
|
||||
self.route_modules()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
|
||||
# Number of control lines in the bus
|
||||
self.num_control_lines = 4
|
||||
# The order of the control signals on the control bus:
|
||||
# FIXME: Update for multiport (these names are not right)
|
||||
self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"]
|
||||
# These will be outputs of the gaters if this is multibank
|
||||
self.control_signals = ["gated_"+str for str in self.input_control_signals]
|
||||
|
|
@ -31,14 +51,7 @@ class bank_select(design.design):
|
|||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
self.create_modules()
|
||||
self.calculate_module_offsets()
|
||||
self.add_modules()
|
||||
self.route_modules()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_modules(self):
|
||||
def add_modules(self):
|
||||
""" Create modules for later instantiation """
|
||||
# 1x Inverter
|
||||
self.inv = pinv()
|
||||
|
|
@ -67,15 +80,10 @@ class bank_select(design.design):
|
|||
self.height = self.yoffset_maxpoint + 2*self.m1_pitch
|
||||
self.width = self.xoffset_inv + self.inv4x.width
|
||||
|
||||
def add_modules(self):
|
||||
def create_modules(self):
|
||||
|
||||
# bank select inverter
|
||||
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
|
||||
|
||||
# bank select inverter (must be made unique if more than one OR)
|
||||
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
|
||||
mod=self.inv,
|
||||
offset=[self.xoffset_bank_sel_inv, 0])
|
||||
mod=self.inv)
|
||||
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
|
||||
|
||||
self.logic_inst = []
|
||||
|
|
@ -87,6 +95,51 @@ class bank_select(design.design):
|
|||
name_nor = "nor_{}".format(input_name)
|
||||
name_inv = "inv_{}".format(input_name)
|
||||
|
||||
# These require OR (nor2+inv) gates since they are active low.
|
||||
# (writes occur on clk low)
|
||||
if input_name in ("clk_buf"):
|
||||
|
||||
self.logic_inst.append(self.add_inst(name=name_nor,
|
||||
mod=self.nor2))
|
||||
self.connect_inst([input_name,
|
||||
"bank_sel_bar",
|
||||
gated_name+"_temp_bar",
|
||||
"vdd",
|
||||
"gnd"])
|
||||
|
||||
# the rest are AND (nand2+inv) gates
|
||||
else:
|
||||
self.logic_inst.append(self.add_inst(name=name_nand,
|
||||
mod=self.nand2))
|
||||
self.connect_inst([input_name,
|
||||
"bank_sel",
|
||||
gated_name+"_temp_bar",
|
||||
"vdd",
|
||||
"gnd"])
|
||||
|
||||
# They all get inverters on the output
|
||||
self.inv_inst.append(self.add_inst(name=name_inv,
|
||||
mod=self.inv4x))
|
||||
self.connect_inst([gated_name+"_temp_bar",
|
||||
gated_name,
|
||||
"vdd",
|
||||
"gnd"])
|
||||
|
||||
def place_modules(self):
|
||||
|
||||
# bank select inverter
|
||||
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
|
||||
|
||||
# bank select inverter (must be made unique if more than one OR)
|
||||
self.bank_sel_inv.place(vector(self.xoffset_bank_sel_inv, 0))
|
||||
|
||||
for i in range(self.num_control_lines):
|
||||
|
||||
logic_inst = self.logic_inst[i]
|
||||
inv_inst = self.inv_inst[i]
|
||||
|
||||
input_name = self.input_control_signals[i]
|
||||
|
||||
y_offset = self.inv.height * i
|
||||
if i%2:
|
||||
y_offset += self.inv.height
|
||||
|
|
@ -98,40 +151,18 @@ class bank_select(design.design):
|
|||
# (writes occur on clk low)
|
||||
if input_name in ("clk_buf"):
|
||||
|
||||
self.logic_inst.append(self.add_inst(name=name_nor,
|
||||
mod=self.nor2,
|
||||
offset=[self.xoffset_nor, y_offset],
|
||||
mirror=mirror))
|
||||
self.connect_inst([input_name,
|
||||
"bank_sel_bar",
|
||||
gated_name+"_temp_bar",
|
||||
"vdd",
|
||||
"gnd"])
|
||||
|
||||
logic_inst.place(offset=[self.xoffset_nor, y_offset],
|
||||
mirror=mirror)
|
||||
|
||||
# the rest are AND (nand2+inv) gates
|
||||
else:
|
||||
self.logic_inst.append(self.add_inst(name=name_nand,
|
||||
mod=self.nand2,
|
||||
offset=[self.xoffset_nand, y_offset],
|
||||
mirror=mirror))
|
||||
bank_sel_signal = "bank_sel"
|
||||
self.connect_inst([input_name,
|
||||
"bank_sel",
|
||||
gated_name+"_temp_bar",
|
||||
"vdd",
|
||||
"gnd"])
|
||||
logic_inst.place(offset=[self.xoffset_nand, y_offset],
|
||||
mirror=mirror)
|
||||
|
||||
# They all get inverters on the output
|
||||
self.inv_inst.append(self.add_inst(name=name_inv,
|
||||
mod=self.inv4x,
|
||||
offset=[self.xoffset_inv, y_offset],
|
||||
mirror=mirror))
|
||||
self.connect_inst([gated_name+"_temp_bar",
|
||||
gated_name,
|
||||
"vdd",
|
||||
"gnd"])
|
||||
|
||||
inv_inst.place(offset=[self.xoffset_inv, y_offset],
|
||||
mirror=mirror)
|
||||
|
||||
|
||||
def route_modules(self):
|
||||
|
||||
|
|
|
|||
|
|
@ -21,38 +21,27 @@ class bitcell_array(design.design):
|
|||
self.column_size = cols
|
||||
self.row_size = rows
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.cell = self.mod_bitcell()
|
||||
self.add_mod(self.cell)
|
||||
|
||||
# We increase it by a well enclosure so the precharges don't overlap our wells
|
||||
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.create_netlist()
|
||||
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
# We don't offset this because we need to align
|
||||
# the replica bitcell in the control logic
|
||||
#self.offset_all_coordinates()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
row_list = self.cell.list_all_wl_names()
|
||||
column_list = self.cell.list_all_bitline_names()
|
||||
for col in range(self.column_size):
|
||||
for cell_column in column_list:
|
||||
self.add_pin(cell_column+"[{0}]".format(col))
|
||||
for row in range(self.row_size):
|
||||
for cell_row in row_list:
|
||||
self.add_pin(cell_row+"[{0}]".format(row))
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_netlist(self):
|
||||
""" Create and connect the netlist """
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
# We increase it by a well enclosure so the precharges don't overlap our wells
|
||||
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
|
||||
|
||||
xoffset = 0.0
|
||||
for col in range(self.column_size):
|
||||
yoffset = 0.0
|
||||
|
|
@ -66,20 +55,38 @@ class bitcell_array(design.design):
|
|||
tempy = yoffset
|
||||
dir_key = ""
|
||||
|
||||
self.place_inst(name=name,
|
||||
offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
self.cell_inst[row,col].place(offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
yoffset += self.cell.height
|
||||
xoffset += self.cell.width
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_netlist(self):
|
||||
""" Create and connect the netlist """
|
||||
self.add_pins()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
row_list = self.cell.list_all_wl_names()
|
||||
column_list = self.cell.list_all_bitline_names()
|
||||
for col in range(self.column_size):
|
||||
for cell_column in column_list:
|
||||
self.add_pin(cell_column+"[{0}]".format(col))
|
||||
for row in range(self.row_size):
|
||||
for cell_row in row_list:
|
||||
self.add_pin(cell_row+"[{0}]".format(row))
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.cell = self.mod_bitcell()
|
||||
self.add_mod(self.cell)
|
||||
|
||||
def create_modules(self):
|
||||
""" Create the module instances used in this design """
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
for row in range(self.row_size):
|
||||
|
|
@ -87,8 +94,7 @@ class bitcell_array(design.design):
|
|||
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||
mod=self.cell)
|
||||
self.connect_inst(self.cell.list_bitcell_pins(col, row))
|
||||
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
|
||||
|
|
|
|||
|
|
@ -24,17 +24,26 @@ class control_logic(design.design):
|
|||
debug.info(1, "Creating {}".format(self.name))
|
||||
|
||||
self.num_rows = num_rows
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.setup_signal_busses()
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_modules()
|
||||
|
||||
def create_layout(self):
|
||||
""" Create layout and route between modules """
|
||||
self.setup_layout_offsets()
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.add_rails()
|
||||
self.add_modules()
|
||||
self.add_routing()
|
||||
self.route_rails()
|
||||
self.place_modules()
|
||||
self.route_all()
|
||||
|
||||
self.add_lvs_correspondence_points()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -46,8 +55,8 @@ class control_logic(design.design):
|
|||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
def create_modules(self):
|
||||
""" add all the required modules """
|
||||
def add_modules(self):
|
||||
""" Add all the required modules """
|
||||
|
||||
dff = dff_inv()
|
||||
dff_height = dff.height
|
||||
|
|
@ -83,14 +92,8 @@ class control_logic(design.design):
|
|||
self.add_mod(self.replica_bitline)
|
||||
|
||||
|
||||
def setup_layout_offsets(self):
|
||||
""" Setup layout offsets, determine the size of the busses etc """
|
||||
# These aren't for instantiating, but we use them to get the dimensions
|
||||
#self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height)
|
||||
|
||||
# Have the cell gap leave enough room to route an M2 wire.
|
||||
# Some cells may have pwell/nwell spacing problems too when the wells are different heights.
|
||||
#self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"])
|
||||
def setup_signal_busses(self):
|
||||
""" Setup bus names, determine the size of the busses etc """
|
||||
|
||||
# List of input control signals
|
||||
self.input_list =["csb","web"]
|
||||
|
|
@ -104,7 +107,7 @@ class control_logic(design.design):
|
|||
self.supply_list = ["vdd", "gnd"]
|
||||
|
||||
|
||||
def add_rails(self):
|
||||
def route_rails(self):
|
||||
""" Add the input signal inverted tracks """
|
||||
height = 4*self.inv1.height - self.m2_pitch
|
||||
offset = vector(self.ctrl_dff_array.width,0)
|
||||
|
|
@ -112,7 +115,20 @@ class control_logic(design.design):
|
|||
self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height)
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
def create_modules(self):
|
||||
""" Create all the modules """
|
||||
self.create_dffs()
|
||||
self.create_clk_row()
|
||||
self.create_we_row()
|
||||
# self.create_trien_row()
|
||||
# self.create_trien_bar_row()
|
||||
self.create_rbl_in_row()
|
||||
self.create_sen_row()
|
||||
self.create_rbl()
|
||||
|
||||
|
||||
|
||||
def place_modules(self):
|
||||
""" Place all the modules """
|
||||
# Keep track of all right-most instances to determine row boundary
|
||||
# and add the vdd/gnd pins
|
||||
|
|
@ -120,20 +136,18 @@ class control_logic(design.design):
|
|||
|
||||
|
||||
# Add the control flops on the left of the bus
|
||||
self.add_dffs()
|
||||
self.place_dffs()
|
||||
|
||||
# Add the logic on the right of the bus
|
||||
self.add_clk_row(row=0) # clk is a double-high cell
|
||||
self.add_we_row(row=2)
|
||||
# self.add_trien_row(row=3)
|
||||
# self.add_trien_bar_row(row=4)
|
||||
self.add_rbl_in_row(row=3)
|
||||
self.add_sen_row(row=4)
|
||||
self.add_rbl(row=5)
|
||||
self.place_clk_row(row=0) # clk is a double-high cell
|
||||
self.place_we_row(row=2)
|
||||
# self.place_trien_row(row=3)
|
||||
# self.place_trien_bar_row(row=4)
|
||||
self.place_rbl_in_row(row=3)
|
||||
self.place_sen_row(row=4)
|
||||
self.place_rbl(row=5)
|
||||
|
||||
|
||||
self.add_lvs_correspondence_points()
|
||||
|
||||
# This offset is used for placement of the control logic in
|
||||
# the SRAM level.
|
||||
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), self.rbl_inst.by())
|
||||
|
|
@ -142,9 +156,9 @@ class control_logic(design.design):
|
|||
self.height = self.rbl_inst.uy() + self.m3_pitch
|
||||
# Max of modules or logic rows
|
||||
self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch
|
||||
|
||||
|
||||
|
||||
def add_routing(self):
|
||||
def route_all(self):
|
||||
""" Routing between modules """
|
||||
self.route_dffs()
|
||||
#self.route_trien()
|
||||
|
|
@ -156,84 +170,92 @@ class control_logic(design.design):
|
|||
self.route_supply()
|
||||
|
||||
|
||||
def add_rbl(self,row):
|
||||
""" Add the replica bitline """
|
||||
def create_rbl(self):
|
||||
""" Create the replica bitline """
|
||||
self.rbl_inst=self.add_inst(name="replica_bitline",
|
||||
mod=self.replica_bitline)
|
||||
self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"])
|
||||
|
||||
def place_rbl(self,row):
|
||||
""" Place the replica bitline """
|
||||
y_off = row * self.inv1.height + 2*self.m1_pitch
|
||||
|
||||
# Add the RBL above the rows
|
||||
# Add to the right of the control rows and routing channel
|
||||
self.replica_bitline_offset = vector(0, y_off)
|
||||
self.rbl_inst=self.add_inst(name="replica_bitline",
|
||||
mod=self.replica_bitline,
|
||||
offset=self.replica_bitline_offset)
|
||||
self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"])
|
||||
self.rbl_inst.place(self.replica_bitline_offset)
|
||||
|
||||
|
||||
def add_clk_row(self,row):
|
||||
""" Add the multistage clock buffer below the control flops """
|
||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
clkbuf_offset = vector(x_off,y_off)
|
||||
def create_clk_row(self):
|
||||
""" Create the multistage clock buffer """
|
||||
self.clkbuf_inst = self.add_inst(name="clkbuf",
|
||||
mod=self.clkbuf,
|
||||
offset=clkbuf_offset)
|
||||
|
||||
mod=self.clkbuf)
|
||||
self.connect_inst(["clk","clk_buf_bar","clk_buf","vdd","gnd"])
|
||||
|
||||
def place_clk_row(self,row):
|
||||
""" Place the multistage clock buffer below the control flops """
|
||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
clkbuf_offset = vector(x_off,y_off)
|
||||
self.clkbuf_inst.place(clkbuf_offset)
|
||||
self.row_end_inst.append(self.clkbuf_inst)
|
||||
|
||||
|
||||
def add_rbl_in_row(self,row):
|
||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
|
||||
# input: clk_buf_bar,CS output: rbl_in_bar
|
||||
self.rbl_in_bar_offset = vector(x_off, y_off)
|
||||
def create_rbl_in_row(self):
|
||||
self.rbl_in_bar_inst=self.add_inst(name="nand3_rbl_in_bar",
|
||||
mod=self.nand2,
|
||||
offset=self.rbl_in_bar_offset,
|
||||
mirror=mirror)
|
||||
mod=self.nand2)
|
||||
self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"])
|
||||
x_off += self.nand2.width
|
||||
|
||||
# input: rbl_in_bar, output: rbl_in
|
||||
self.rbl_in_offset = vector(x_off, y_off)
|
||||
self.rbl_in_inst=self.add_inst(name="inv_rbl_in",
|
||||
mod=self.inv1,
|
||||
offset=self.rbl_in_offset,
|
||||
mirror=mirror)
|
||||
mod=self.inv1)
|
||||
self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"])
|
||||
|
||||
self.row_end_inst.append(self.rbl_in_inst)
|
||||
|
||||
def add_sen_row(self,row):
|
||||
""" The sense enable buffer gets placed to the far right of the
|
||||
row. """
|
||||
|
||||
def place_rbl_in_row(self,row):
|
||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
|
||||
self.rbl_in_bar_offset = vector(x_off, y_off)
|
||||
self.rbl_in_bar_inst.place(offset=self.rbl_in_bar_offset,
|
||||
mirror=mirror)
|
||||
x_off += self.nand2.width
|
||||
|
||||
self.rbl_in_offset = vector(x_off, y_off)
|
||||
self.rbl_in_inst.place(offset=self.rbl_in_offset,
|
||||
mirror=mirror)
|
||||
self.row_end_inst.append(self.rbl_in_inst)
|
||||
|
||||
def create_sen_row(self):
|
||||
""" Create the sense enable buffer. """
|
||||
# input: pre_s_en, output: pre_s_en_bar
|
||||
self.pre_s_en_bar_offset = vector(x_off, y_off)
|
||||
self.pre_s_en_bar_inst=self.add_inst(name="inv_pre_s_en_bar",
|
||||
mod=self.inv2,
|
||||
offset=self.pre_s_en_bar_offset,
|
||||
mirror=mirror)
|
||||
mod=self.inv2)
|
||||
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
|
||||
|
||||
x_off += self.inv2.width
|
||||
|
||||
# BUFFER INVERTERS FOR S_EN
|
||||
# input: input: pre_s_en_bar, output: s_en
|
||||
self.s_en_offset = vector(x_off, y_off)
|
||||
self.s_en_inst=self.add_inst(name="inv_s_en",
|
||||
mod=self.inv8,
|
||||
offset=self.s_en_offset,
|
||||
mirror=mirror)
|
||||
mod=self.inv8)
|
||||
self.connect_inst(["pre_s_en_bar", "s_en0", "vdd", "gnd"])
|
||||
|
||||
def place_sen_row(self,row):
|
||||
"""
|
||||
The sense enable buffer gets placed to the far right of the
|
||||
row.
|
||||
"""
|
||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
self.pre_s_en_bar_offset = vector(x_off, y_off)
|
||||
self.pre_s_en_bar_inst.place(offset=self.pre_s_en_bar_offset,
|
||||
mirror=mirror)
|
||||
x_off += self.inv2.width
|
||||
|
||||
self.s_en_offset = vector(x_off, y_off)
|
||||
self.s_en_inst.place(offset=self.s_en_offset,
|
||||
mirror=mirror)
|
||||
self.row_end_inst.append(self.s_en_inst)
|
||||
|
||||
|
||||
|
|
@ -256,14 +278,16 @@ class control_logic(design.design):
|
|||
self.copy_layout_pin(self.ctrl_dff_inst, "din[1]", "web")
|
||||
|
||||
|
||||
def add_dffs(self):
|
||||
def create_dffs(self):
|
||||
""" Add the three input DFFs (with inverters) """
|
||||
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
|
||||
mod=self.ctrl_dff_array,
|
||||
offset=vector(0,0))
|
||||
|
||||
mod=self.ctrl_dff_array)
|
||||
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
|
||||
|
||||
def place_dffs(self):
|
||||
""" Place the input DFFs (with inverters) """
|
||||
self.ctrl_dff_inst.place(vector(0,0))
|
||||
|
||||
|
||||
def get_offset(self,row):
|
||||
""" Compute the y-offset and mirroring """
|
||||
|
|
@ -276,48 +300,53 @@ class control_logic(design.design):
|
|||
|
||||
return (y_off,mirror)
|
||||
|
||||
def add_we_row(self,row):
|
||||
def create_we_row(self):
|
||||
# input: WE, CS output: w_en_bar
|
||||
self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar",
|
||||
mod=self.nand3)
|
||||
self.connect_inst(["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"])
|
||||
|
||||
# input: w_en_bar, output: pre_w_en
|
||||
self.pre_w_en_inst=self.add_inst(name="inv_pre_w_en",
|
||||
mod=self.inv1)
|
||||
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
|
||||
|
||||
# BUFFER INVERTERS FOR W_EN
|
||||
self.pre_w_en_bar_inst=self.add_inst(name="inv_pre_w_en_bar",
|
||||
mod=self.inv2)
|
||||
self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"])
|
||||
|
||||
self.w_en_inst=self.add_inst(name="inv_w_en2",
|
||||
mod=self.inv8)
|
||||
self.connect_inst(["pre_w_en_bar", "w_en0", "vdd", "gnd"])
|
||||
|
||||
|
||||
def place_we_row(self,row):
|
||||
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
|
||||
(y_off,mirror)=self.get_offset(row)
|
||||
|
||||
# input: WE, CS output: w_en_bar
|
||||
w_en_bar_offset = vector(x_off, y_off)
|
||||
self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar",
|
||||
mod=self.nand3,
|
||||
offset=w_en_bar_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"])
|
||||
self.w_en_bar_inst.place(offset=w_en_bar_offset,
|
||||
mirror=mirror)
|
||||
x_off += self.nand3.width
|
||||
|
||||
# input: w_en_bar, output: pre_w_en
|
||||
pre_w_en_offset = vector(x_off, y_off)
|
||||
self.pre_w_en_inst=self.add_inst(name="inv_pre_w_en",
|
||||
mod=self.inv1,
|
||||
offset=pre_w_en_offset,
|
||||
mirror=mirror)
|
||||
|
||||
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
|
||||
self.pre_w_en_inst.place(offset=pre_w_en_offset,
|
||||
mirror=mirror)
|
||||
x_off += self.inv1.width
|
||||
|
||||
# BUFFER INVERTERS FOR W_EN
|
||||
pre_w_en_bar_offset = vector(x_off, y_off)
|
||||
self.pre_w_en_bar_inst=self.add_inst(name="inv_pre_w_en_bar",
|
||||
mod=self.inv2,
|
||||
offset=pre_w_en_bar_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"])
|
||||
self.pre_w_en_bar_inst.place(offset=pre_w_en_bar_offset,
|
||||
mirror=mirror)
|
||||
x_off += self.inv2.width
|
||||
|
||||
w_en_offset = vector(x_off, y_off)
|
||||
self.w_en_inst=self.add_inst(name="inv_w_en2",
|
||||
mod=self.inv8,
|
||||
offset=w_en_offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["pre_w_en_bar", "w_en0", "vdd", "gnd"])
|
||||
self.w_en_inst.place(offset=w_en_offset,
|
||||
mirror=mirror)
|
||||
x_off += self.inv8.width
|
||||
|
||||
self.row_end_inst.append(self.w_en_inst)
|
||||
|
||||
|
||||
|
||||
def route_rbl_in(self):
|
||||
""" Connect the logic for the rbl_in generation """
|
||||
|
|
|
|||
|
|
@ -24,14 +24,23 @@ class delay_chain(design.design):
|
|||
# number of inverters including any fanout loads.
|
||||
self.fanout_list = fanout_list
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_module()
|
||||
self.add_inverters()
|
||||
self.create_inverters()
|
||||
|
||||
def create_layout(self):
|
||||
# Each stage is a a row
|
||||
self.height = len(self.fanout_list)*self.inv.height
|
||||
# The width is determined by the largest fanout plus the driver
|
||||
self.width = (max(self.fanout_list)+1) * self.inv.width
|
||||
|
||||
self.place_inverters()
|
||||
self.route_inverters()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
|
@ -43,40 +52,27 @@ class delay_chain(design.design):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_module(self):
|
||||
""" Add the inverter logical module """
|
||||
def add_modules(self):
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.inv = pinv(route_output=False)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
# Each stage is a a row
|
||||
self.height = len(self.fanout_list)*self.inv.height
|
||||
# The width is determined by the largest fanout plus the driver
|
||||
self.width = (max(self.fanout_list)+1) * self.inv.width
|
||||
|
||||
|
||||
def add_inverters(self):
|
||||
""" Add the inverters and connect them based on the stage list """
|
||||
def create_inverters(self):
|
||||
""" Create the inverters and connect them based on the stage list """
|
||||
self.driver_inst_list = []
|
||||
self.rightest_load_inst = {}
|
||||
self.load_inst_map = {}
|
||||
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
|
||||
if stage_num % 2:
|
||||
inv_mirror = "MX"
|
||||
inv_offset = vector(0, (stage_num+1)* self.inv.height)
|
||||
else:
|
||||
inv_mirror = "R0"
|
||||
inv_offset = vector(0, stage_num * self.inv.height)
|
||||
|
||||
# Add the inverter
|
||||
cur_driver=self.add_inst(name="dinv{}".format(stage_num),
|
||||
mod=self.inv,
|
||||
offset=inv_offset,
|
||||
mirror=inv_mirror)
|
||||
mod=self.inv)
|
||||
# keep track of the inverter instances so we can use them to get the pins
|
||||
self.driver_inst_list.append(cur_driver)
|
||||
|
||||
|
||||
# Hook up the driver
|
||||
if stage_num+1==len(self.fanout_list):
|
||||
stageout_name = "out"
|
||||
|
|
@ -91,11 +87,8 @@ class delay_chain(design.design):
|
|||
# Now add the dummy loads to the right
|
||||
self.load_inst_map[cur_driver]=[]
|
||||
for i in range(fanout_size):
|
||||
inv_offset += vector(self.inv.width,0)
|
||||
cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num,i),
|
||||
mod=self.inv,
|
||||
offset=inv_offset,
|
||||
mirror=inv_mirror)
|
||||
mod=self.inv)
|
||||
# Fanout stage is always driven by driver and output is disconnected
|
||||
disconnect_name = "n_{0}_{1}".format(stage_num,i)
|
||||
self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"])
|
||||
|
|
@ -105,6 +98,29 @@ class delay_chain(design.design):
|
|||
else:
|
||||
# Keep track of the last one so we can add the the wire later
|
||||
self.rightest_load_inst[cur_driver]=cur_load
|
||||
|
||||
def place_inverters(self):
|
||||
""" Place the inverters and connect them based on the stage list """
|
||||
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
|
||||
if stage_num % 2:
|
||||
inv_mirror = "MX"
|
||||
inv_offset = vector(0, (stage_num+1)* self.inv.height)
|
||||
else:
|
||||
inv_mirror = "R0"
|
||||
inv_offset = vector(0, stage_num * self.inv.height)
|
||||
|
||||
# Add the inverter
|
||||
cur_driver=self.driver_inst_list[stage_num]
|
||||
cur_driver.place(offset=inv_offset,
|
||||
mirror=inv_mirror)
|
||||
|
||||
# Now add the dummy loads to the right
|
||||
load_list = self.load_inst_map[cur_driver]
|
||||
for i in range(fanout_size):
|
||||
inv_offset += vector(self.inv.width,0)
|
||||
load_list[i].place(offset=inv_offset,
|
||||
mirror=inv_mirror)
|
||||
|
||||
|
||||
def add_route(self, pin1, pin2):
|
||||
""" This guarantees that we route from the top to bottom row correctly. """
|
||||
|
|
|
|||
|
|
@ -20,27 +20,30 @@ class dff_array(design.design):
|
|||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns))
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_dff_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.columns * self.dff.width
|
||||
self.height = self.rows * self.dff.height
|
||||
|
||||
self.place_dff_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.dff))
|
||||
self.mod_dff = getattr(c, OPTS.dff)
|
||||
self.dff = self.mod_dff("dff")
|
||||
self.add_mod(self.dff)
|
||||
|
||||
self.width = self.columns * self.dff.width
|
||||
self.height = self.rows * self.dff.height
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_dff_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_dff_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
|
|
@ -75,9 +78,8 @@ class dff_array(design.design):
|
|||
else:
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.place_inst(name=name,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.dff_insts[row,col].place(offset=base,
|
||||
mirror=mirror)
|
||||
|
||||
def get_din_name(self, row, col):
|
||||
if self.columns == 1:
|
||||
|
|
|
|||
|
|
@ -26,29 +26,41 @@ class dff_buf(design.design):
|
|||
debug.check(inv1_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
|
||||
debug.check(inv2_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
|
||||
|
||||
self.inv1_size=inv1_size
|
||||
self.inv2_size=inv2_size
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.dff.width + self.inv1.width + self.inv2.width
|
||||
self.height = self.dff.height
|
||||
|
||||
self.place_modules()
|
||||
self.route_wires()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.dff))
|
||||
self.mod_dff = getattr(c, OPTS.dff)
|
||||
self.dff = self.mod_dff("dff")
|
||||
self.add_mod(self.dff)
|
||||
|
||||
self.inv1 = pinv(size=inv1_size,height=self.dff.height)
|
||||
self.inv1 = pinv(size=self.inv1_size,height=self.dff.height)
|
||||
self.add_mod(self.inv1)
|
||||
|
||||
self.inv2 = pinv(size=inv2_size,height=self.dff.height)
|
||||
self.inv2 = pinv(size=self.inv2_size,height=self.dff.height)
|
||||
self.add_mod(self.inv2)
|
||||
|
||||
self.width = self.dff.width + self.inv1.width + self.inv2.width
|
||||
self.height = self.dff.height
|
||||
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
self.add_pins()
|
||||
self.add_insts()
|
||||
self.add_wires()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("D")
|
||||
|
|
@ -58,26 +70,30 @@ class dff_buf(design.design):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def add_insts(self):
|
||||
# Add the DFF
|
||||
def create_modules(self):
|
||||
self.dff_inst=self.add_inst(name="dff_buf_dff",
|
||||
mod=self.dff,
|
||||
offset=vector(0,0))
|
||||
mod=self.dff)
|
||||
self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
|
||||
|
||||
# Add INV1 to the right
|
||||
self.inv1_inst=self.add_inst(name="dff_buf_inv1",
|
||||
mod=self.inv1,
|
||||
offset=vector(self.dff_inst.rx(),0))
|
||||
mod=self.inv1)
|
||||
self.connect_inst(["qint", "Qb", "vdd", "gnd"])
|
||||
|
||||
# Add INV2 to the right
|
||||
self.inv2_inst=self.add_inst(name="dff_buf_inv2",
|
||||
mod=self.inv2,
|
||||
offset=vector(self.inv1_inst.rx(),0))
|
||||
mod=self.inv2)
|
||||
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
|
||||
|
||||
def place_modules(self):
|
||||
# Add the DFF
|
||||
self.dff_inst.place(vector(0,0))
|
||||
|
||||
# Add INV1 to the right
|
||||
self.inv1_inst.place(vector(self.dff_inst.rx(),0))
|
||||
|
||||
def add_wires(self):
|
||||
# Add INV2 to the right
|
||||
self.inv2_inst.place(vector(self.inv1_inst.rx(),0))
|
||||
|
||||
def route_wires(self):
|
||||
# Route dff q to inv1 a
|
||||
q_pin = self.dff_inst.get_pin("Q")
|
||||
a1_pin = self.inv1_inst.get_pin("A")
|
||||
|
|
|
|||
|
|
@ -20,21 +20,21 @@ class dff_buf_array(design.design):
|
|||
name = "dff_buf_array_{0}x{1}".format(rows, columns)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
|
||||
self.dff = dff_buf.dff_buf(inv1_size, inv2_size)
|
||||
self.add_mod(self.dff)
|
||||
|
||||
self.width = self.columns * self.dff.width
|
||||
self.height = self.rows * self.dff.height
|
||||
|
||||
self.inv1_size = inv1_size
|
||||
self.inv2_size = inv2_size
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_dff_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.columns * self.dff.width
|
||||
self.height = self.rows * self.dff.height
|
||||
self.place_dff_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
|
@ -51,6 +51,10 @@ class dff_buf_array(design.design):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def add_modules(self):
|
||||
self.dff = dff_buf.dff_buf(self.inv1_size, self.inv2_size)
|
||||
self.add_mod(self.dff)
|
||||
|
||||
def create_dff_array(self):
|
||||
self.dff_insts={}
|
||||
for row in range(self.rows):
|
||||
|
|
@ -75,9 +79,8 @@ class dff_buf_array(design.design):
|
|||
else:
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.place_inst(name=name,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.dff_insts[row,col].place(offset=base,
|
||||
mirror=mirror)
|
||||
|
||||
def get_din_name(self, row, col):
|
||||
if self.columns == 1:
|
||||
|
|
|
|||
|
|
@ -18,31 +18,30 @@ class dff_inv(design.design):
|
|||
name = "dff_inv_{0}".format(inv_size)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
|
||||
self.inv_size = inv_size
|
||||
|
||||
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
|
||||
# This causes a DRC in the pinv which assumes min width rails. This ensures the output
|
||||
# contact does not violate spacing to the rail in the NMOS.
|
||||
debug.check(inv_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.dff))
|
||||
self.mod_dff = getattr(c, OPTS.dff)
|
||||
self.dff = self.mod_dff("dff")
|
||||
self.add_mod(self.dff)
|
||||
|
||||
self.inv1 = pinv(size=inv_size,height=self.dff.height)
|
||||
self.add_mod(self.inv1)
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_modules()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.dff.width + self.inv1.width
|
||||
self.height = self.dff.height
|
||||
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
self.add_pins()
|
||||
self.add_insts()
|
||||
self.place_modules()
|
||||
self.add_wires()
|
||||
self.add_layout_pins()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -53,18 +52,31 @@ class dff_inv(design.design):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def add_insts(self):
|
||||
# Add the DFF
|
||||
def add_modules(self):
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.dff))
|
||||
self.mod_dff = getattr(c, OPTS.dff)
|
||||
self.dff = self.mod_dff("dff")
|
||||
self.add_mod(self.dff)
|
||||
|
||||
self.inv1 = pinv(size=self.inv_size,height=self.dff.height)
|
||||
self.add_mod(self.inv1)
|
||||
|
||||
def create_modules(self):
|
||||
self.dff_inst=self.add_inst(name="dff_inv_dff",
|
||||
mod=self.dff,
|
||||
offset=vector(0,0))
|
||||
mod=self.dff)
|
||||
self.connect_inst(["D", "Q", "clk", "vdd", "gnd"])
|
||||
|
||||
# Add INV1 to the right
|
||||
self.inv1_inst=self.add_inst(name="dff_inv_inv1",
|
||||
mod=self.inv1,
|
||||
offset=vector(self.dff_inst.rx(),0))
|
||||
mod=self.inv1)
|
||||
self.connect_inst(["Q", "Qb", "vdd", "gnd"])
|
||||
|
||||
def place_modules(self):
|
||||
# Place the DFF
|
||||
self.dff_inst.place(vector(0,0))
|
||||
|
||||
# Place the INV1 to the right
|
||||
self.inv1_inst.place(vector(self.dff_inst.rx(),0))
|
||||
|
||||
|
||||
def add_wires(self):
|
||||
|
|
|
|||
|
|
@ -20,21 +20,29 @@ class dff_inv_array(design.design):
|
|||
name = "dff_inv_array_{0}x{1}".format(rows, columns)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
self.inv_size = inv_size
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
self.dff = dff_inv.dff_inv(inv_size)
|
||||
self.add_mod(self.dff)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_dff_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.columns * self.dff.width
|
||||
self.height = self.rows * self.dff.height
|
||||
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
self.add_pins()
|
||||
self.create_dff_array()
|
||||
self.place_dff_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
self.dff = dff_inv.dff_inv(self.inv_size)
|
||||
self.add_mod(self.dff)
|
||||
|
||||
def add_pins(self):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
|
|
@ -52,16 +60,8 @@ class dff_inv_array(design.design):
|
|||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*self.dff.width,row*self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
||||
mod=self.dff,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
mod=self.dff)
|
||||
self.connect_inst([self.get_din_name(row,col),
|
||||
self.get_dout_name(row,col),
|
||||
self.get_dout_bar_name(row,col),
|
||||
|
|
@ -69,6 +69,19 @@ class dff_inv_array(design.design):
|
|||
"vdd",
|
||||
"gnd"])
|
||||
|
||||
def place_dff_array(self):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*self.dff.width,row*self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
||||
mirror = "MX"
|
||||
self.dff_insts[row,col].place(offset=base,
|
||||
mirror=mirror)
|
||||
|
||||
def get_din_name(self, row, col):
|
||||
if self.columns == 1:
|
||||
din_name = "din[{0}]".format(row)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ class hierarchical_decoder(design.design):
|
|||
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
|
|
@ -302,8 +303,7 @@ class hierarchical_decoder(design.design):
|
|||
else:
|
||||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||
|
||||
self.place_inst(name="pre[{0}]".format(num),
|
||||
offset=base)
|
||||
self.pre2x4_inst[num].place(base)
|
||||
|
||||
|
||||
def place_pre3x8(self,num):
|
||||
|
|
@ -315,8 +315,7 @@ class hierarchical_decoder(design.design):
|
|||
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)
|
||||
self.pre3x8_inst[num].place(offset)
|
||||
|
||||
|
||||
def create_row_decoder(self):
|
||||
|
|
@ -396,7 +395,6 @@ class hierarchical_decoder(design.design):
|
|||
x_off = self.internal_routing_width + self.nand3.width
|
||||
|
||||
for row in range(self.rows):
|
||||
name = self.INV_FORMAT.format(row)
|
||||
if (row % 2 == 0):
|
||||
inv_row_height = self.inv.height * row
|
||||
mirror = "R0"
|
||||
|
|
@ -407,9 +405,8 @@ class hierarchical_decoder(design.design):
|
|||
y_dir = -1
|
||||
y_off = inv_row_height
|
||||
offset = vector(x_off,y_off)
|
||||
self.place_inst(name=name,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
self.inv_inst[row].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def place_row_decoder(self):
|
||||
"""
|
||||
|
|
@ -448,9 +445,8 @@ class hierarchical_decoder(design.design):
|
|||
y_dir = -1
|
||||
mirror = "MX"
|
||||
|
||||
self.place_inst(name=name,
|
||||
offset=[self.internal_routing_width, y_off],
|
||||
mirror=mirror)
|
||||
self.nand_inst[row].place(offset=[self.internal_routing_width, y_off],
|
||||
mirror=mirror)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class hierarchical_predecode(design.design):
|
|||
else:
|
||||
debug.error("Invalid number of predecode inputs: {}".format(inputs),-1)
|
||||
|
||||
def setup_constraints(self):
|
||||
def setup_layout_constraints(self):
|
||||
|
||||
self.height = self.number_of_outputs * self.nand.height
|
||||
|
||||
|
|
@ -88,7 +88,6 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
def create_input_inverters(self):
|
||||
""" Create the input inverters to invert input signals for the decode stage. """
|
||||
|
||||
self.in_inst = []
|
||||
for inv_num in range(self.number_of_inputs):
|
||||
name = "Xpre_inv[{0}]".format(inv_num)
|
||||
|
|
@ -100,9 +99,7 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
def place_input_inverters(self):
|
||||
""" Place the input inverters to invert input signals for the decode stage. """
|
||||
|
||||
for inv_num in range(self.number_of_inputs):
|
||||
name = "Xpre_inv[{0}]".format(inv_num)
|
||||
if (inv_num % 2 == 0):
|
||||
y_off = inv_num * (self.inv.height)
|
||||
mirror = "R0"
|
||||
|
|
@ -110,13 +107,11 @@ class hierarchical_predecode(design.design):
|
|||
y_off = (inv_num + 1) * (self.inv.height)
|
||||
mirror="MX"
|
||||
offset = vector(self.x_off_inv_1, y_off)
|
||||
self.place_inst(name=name,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
self.in_inst[inv_num].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def create_output_inverters(self):
|
||||
""" Create inverters for the inverted output decode signals. """
|
||||
|
||||
self.inv_inst = []
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
name = "Xpre_nand_inv[{}]".format(inv_num)
|
||||
|
|
@ -129,9 +124,7 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
def place_output_inverters(self):
|
||||
""" Place inverters for the inverted output decode signals. """
|
||||
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
name = "Xpre_nand_inv[{}]".format(inv_num)
|
||||
if (inv_num % 2 == 0):
|
||||
y_off = inv_num * self.inv.height
|
||||
mirror = "R0"
|
||||
|
|
@ -139,9 +132,8 @@ class hierarchical_predecode(design.design):
|
|||
y_off =(inv_num + 1)*self.inv.height
|
||||
mirror = "MX"
|
||||
offset = vector(self.x_off_inv_2, y_off)
|
||||
self.place_inst(name=name,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
self.inv_inst[inv_num].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def create_nand_array(self,connections):
|
||||
""" Create the NAND stage for the decodes """
|
||||
|
|
@ -158,7 +150,6 @@ class hierarchical_predecode(design.design):
|
|||
""" Place the NAND stage for the decodes """
|
||||
for nand_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
|
||||
name = "Xpre{0}_nand[{1}]".format(inout,nand_input)
|
||||
if (nand_input % 2 == 0):
|
||||
y_off = nand_input * self.inv.height
|
||||
mirror = "R0"
|
||||
|
|
@ -166,9 +157,8 @@ class hierarchical_predecode(design.design):
|
|||
y_off = (nand_input + 1) * self.inv.height
|
||||
mirror = "MX"
|
||||
offset = vector(self.x_off_nand, y_off)
|
||||
self.place_inst(name=name,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
self.nand_inst[nand_input].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
|
||||
def route(self):
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import debug
|
|||
import design
|
||||
from vector import vector
|
||||
from hierarchical_predecode import hierarchical_predecode
|
||||
from globals import OPTS
|
||||
|
||||
class hierarchical_predecode2x4(hierarchical_predecode):
|
||||
"""
|
||||
|
|
@ -11,13 +12,13 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
def __init__(self):
|
||||
hierarchical_predecode.__init__(self, 2)
|
||||
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.setup_constraints()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.create_input_inverters()
|
||||
self.create_output_inverters()
|
||||
connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"],
|
||||
|
|
@ -33,6 +34,7 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of NAND gates for inversion
|
||||
"""
|
||||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_output_inverters()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import debug
|
|||
import design
|
||||
from vector import vector
|
||||
from hierarchical_predecode import hierarchical_predecode
|
||||
from globals import OPTS
|
||||
|
||||
class hierarchical_predecode3x8(hierarchical_predecode):
|
||||
"""
|
||||
|
|
@ -11,13 +12,13 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
def __init__(self):
|
||||
hierarchical_predecode.__init__(self, 3)
|
||||
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.setup_constraints()
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.create_input_inverters()
|
||||
self.create_output_inverters()
|
||||
connections=[["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"],
|
||||
|
|
@ -38,6 +39,7 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of NAND gates for inversion
|
||||
"""
|
||||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_output_inverters()
|
||||
|
|
|
|||
|
|
@ -20,27 +20,31 @@ class ms_flop_array(design.design):
|
|||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
|
||||
self.words_per_row = int(self.columns / self.word_size)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_ms_flop_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.columns * self.ms.width
|
||||
self.height = self.ms.height
|
||||
|
||||
self.place_ms_flop_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.ms_flop))
|
||||
self.mod_ms_flop = getattr(c, OPTS.ms_flop)
|
||||
self.ms = self.mod_ms_flop("ms_flop")
|
||||
self.add_mod(self.ms)
|
||||
|
||||
self.width = self.columns * self.ms.width
|
||||
self.height = self.ms.height
|
||||
self.words_per_row = int(self.columns / self.word_size)
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_ms_flop_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_ms_flop_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(self.word_size):
|
||||
|
|
@ -67,16 +71,15 @@ class ms_flop_array(design.design):
|
|||
|
||||
def place_ms_flop_array(self):
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xdff{0}".format(i)
|
||||
index = int(i/self.words_per_row)
|
||||
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)
|
||||
self.ms_inst[index].place(offset=base,
|
||||
mirror=mirror)
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import debug
|
|||
from tech import drc
|
||||
from vector import vector
|
||||
from precharge import precharge
|
||||
|
||||
from globals import OPTS
|
||||
|
||||
class precharge_array(design.design):
|
||||
"""
|
||||
|
|
@ -20,15 +20,13 @@ class precharge_array(design.design):
|
|||
debug.info(1, "Creating {0}".format(self.name))
|
||||
|
||||
self.columns = columns
|
||||
|
||||
self.pc_cell = precharge(name="precharge", size=size, bitcell_bl=bitcell_bl, bitcell_br=bitcell_br)
|
||||
self.add_mod(self.pc_cell)
|
||||
|
||||
self.width = self.columns * self.pc_cell.width
|
||||
self.height = self.pc_cell.height
|
||||
self.size = size
|
||||
self.bitcell_bl = bitcell_bl
|
||||
self.bitcell_br = bitcell_br
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def add_pins(self):
|
||||
"""Adds pins for spice file"""
|
||||
|
|
@ -39,15 +37,26 @@ class precharge_array(design.design):
|
|||
self.add_pin("vdd")
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_insts()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.columns * self.pc_cell.width
|
||||
self.height = self.pc_cell.height
|
||||
|
||||
self.place_insts()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
self.pc_cell = precharge(name="precharge",
|
||||
size=self.size,
|
||||
bitcell_bl=self.bitcell_bl,
|
||||
bitcell_br=self.bitcell_br)
|
||||
self.add_mod(self.pc_cell)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
self.add_layout_pin(text="en",
|
||||
|
|
@ -91,7 +100,5 @@ class precharge_array(design.design):
|
|||
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)
|
||||
self.local_insts[i].place(offset)
|
||||
|
|
|
|||
|
|
@ -18,27 +18,26 @@ class replica_bitline(design.design):
|
|||
def __init__(self, delay_stages, delay_fanout, bitcell_loads, name="replica_bitline"):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
from importlib import reload
|
||||
g = reload(__import__(OPTS.delay_chain))
|
||||
self.mod_delay_chain = getattr(g, OPTS.delay_chain)
|
||||
|
||||
g = reload(__import__(OPTS.replica_bitcell))
|
||||
self.mod_replica_bitcell = getattr(g, OPTS.replica_bitcell)
|
||||
|
||||
for pin in ["en", "out", "vdd", "gnd"]:
|
||||
self.add_pin(pin)
|
||||
self.bitcell_loads = bitcell_loads
|
||||
self.delay_stages = delay_stages
|
||||
self.delay_fanout = delay_fanout
|
||||
|
||||
self.create_modules()
|
||||
self.calculate_module_offsets()
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
|
||||
def create_layout(self):
|
||||
self.calculate_module_offsets()
|
||||
self.place_modules()
|
||||
self.route()
|
||||
self.add_layout_pins()
|
||||
|
||||
self.offset_all_coordinates()
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
#self.add_lvs_correspondence_points()
|
||||
|
||||
|
|
@ -48,6 +47,10 @@ class replica_bitline(design.design):
|
|||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for pin in ["en", "out", "vdd", "gnd"]:
|
||||
self.add_pin(pin)
|
||||
|
||||
def calculate_module_offsets(self):
|
||||
""" Calculate all the module offsets """
|
||||
|
||||
|
|
@ -74,8 +77,16 @@ class replica_bitline(design.design):
|
|||
|
||||
|
||||
|
||||
def create_modules(self):
|
||||
""" Create modules for later instantiation """
|
||||
def add_modules(self):
|
||||
""" Add the modules for later usage """
|
||||
|
||||
from importlib import reload
|
||||
g = reload(__import__(OPTS.delay_chain))
|
||||
self.mod_delay_chain = getattr(g, OPTS.delay_chain)
|
||||
|
||||
g = reload(__import__(OPTS.replica_bitcell))
|
||||
self.mod_replica_bitcell = getattr(g, OPTS.replica_bitcell)
|
||||
|
||||
self.bitcell = self.replica_bitcell = self.mod_replica_bitcell()
|
||||
self.add_mod(self.bitcell)
|
||||
|
||||
|
|
@ -93,38 +104,48 @@ class replica_bitline(design.design):
|
|||
self.access_tx = ptx(tx_type="pmos")
|
||||
self.add_mod(self.access_tx)
|
||||
|
||||
def add_modules(self):
|
||||
""" Add all of the module instances in the logical netlist """
|
||||
def create_modules(self):
|
||||
""" Create all of the module instances in the logical netlist """
|
||||
|
||||
# This is the threshold detect inverter on the output of the RBL
|
||||
self.rbl_inv_inst=self.add_inst(name="rbl_inv",
|
||||
mod=self.inv,
|
||||
offset=self.rbl_inv_offset,
|
||||
rotate=180)
|
||||
mod=self.inv)
|
||||
self.connect_inst(["bl[0]", "out", "vdd", "gnd"])
|
||||
|
||||
self.tx_inst=self.add_inst(name="rbl_access_tx",
|
||||
mod=self.access_tx,
|
||||
offset=self.access_tx_offset)
|
||||
mod=self.access_tx)
|
||||
# D, G, S, B
|
||||
self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"])
|
||||
# add the well and poly contact
|
||||
|
||||
self.dc_inst=self.add_inst(name="delay_chain",
|
||||
mod=self.delay_chain,
|
||||
offset=self.delay_chain_offset)
|
||||
mod=self.delay_chain)
|
||||
self.connect_inst(["en", "delayed_en", "vdd", "gnd"])
|
||||
|
||||
self.rbc_inst=self.add_inst(name="bitcell",
|
||||
mod=self.replica_bitcell,
|
||||
offset=self.bitcell_offset,
|
||||
mirror="MX")
|
||||
mod=self.replica_bitcell)
|
||||
self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"])
|
||||
|
||||
self.rbl_inst=self.add_inst(name="load",
|
||||
mod=self.rbl,
|
||||
offset=self.rbl_offset)
|
||||
mod=self.rbl)
|
||||
self.connect_inst(["bl[0]", "br[0]"] + ["gnd"]*self.bitcell_loads + ["vdd", "gnd"])
|
||||
|
||||
def place_modules(self):
|
||||
""" Add all of the module instances in the logical netlist """
|
||||
|
||||
# This is the threshold detect inverter on the output of the RBL
|
||||
self.rbl_inv_inst.place(offset=self.rbl_inv_offset,
|
||||
rotate=180)
|
||||
|
||||
self.tx_inst.place(self.access_tx_offset)
|
||||
|
||||
self.dc_inst.place(self.delay_chain_offset)
|
||||
|
||||
self.rbc_inst.place(offset=self.bitcell_offset,
|
||||
mirror="MX")
|
||||
|
||||
self.rbl_inst.place(self.rbl_offset)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -248,8 +269,6 @@ class replica_bitline(design.design):
|
|||
self.copy_layout_pin(self.dc_inst,"vdd")
|
||||
self.copy_layout_pin(self.rbc_inst,"vdd")
|
||||
|
||||
|
||||
|
||||
# Connect the WL and vdd pins directly to the center and right vdd rails
|
||||
# Connect RBL vdd pins to center and right rails
|
||||
rbl_vdd_pins = self.rbl_inst.get_pins("vdd")
|
||||
|
|
@ -269,9 +288,6 @@ class replica_bitline(design.design):
|
|||
offset=end,
|
||||
rotate=90)
|
||||
|
||||
|
||||
|
||||
|
||||
# Add via for the inverter
|
||||
pin = self.rbl_inv_inst.get_pin("vdd")
|
||||
start = vector(self.left_vdd_pin.cx(),pin.cy())
|
||||
|
|
|
|||
|
|
@ -14,44 +14,46 @@ class sense_amp_array(design.design):
|
|||
design.design.__init__(self, "sense_amp_array")
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
|
||||
self.word_size = word_size
|
||||
self.words_per_row = words_per_row
|
||||
self.row_size = self.word_size * self.words_per_row
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_sense_amp_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.height = self.amp.height
|
||||
self.width = self.amp.width * self.word_size * self.words_per_row
|
||||
|
||||
self.place_sense_amp_array()
|
||||
self.add_layout_pins()
|
||||
self.route_rails()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(0,self.word_size):
|
||||
self.add_pin("data[{0}]".format(i))
|
||||
self.add_pin("bl[{0}]".format(i))
|
||||
self.add_pin("br[{0}]".format(i))
|
||||
self.add_pin("en")
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def add_modules(self):
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.sense_amp))
|
||||
self.mod_sense_amp = getattr(c, OPTS.sense_amp)
|
||||
self.amp = self.mod_sense_amp("sense_amp")
|
||||
self.add_mod(self.amp)
|
||||
|
||||
self.word_size = word_size
|
||||
self.words_per_row = words_per_row
|
||||
self.row_size = self.word_size * self.words_per_row
|
||||
|
||||
self.height = self.amp.height
|
||||
self.width = self.amp.width * self.word_size * self.words_per_row
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
|
||||
for i in range(0,self.word_size):
|
||||
self.add_pin("data[{0}]".format(i))
|
||||
self.add_pin("bl[{0}]".format(i))
|
||||
self.add_pin("br[{0}]".format(i))
|
||||
|
||||
self.add_pin("en")
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_sense_amp_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_sense_amp_array()
|
||||
self.add_layout_pins()
|
||||
self.route_rails()
|
||||
|
||||
|
||||
def create_sense_amp_array(self):
|
||||
self.local_insts = []
|
||||
for i in range(0,self.word_size):
|
||||
|
|
@ -68,11 +70,8 @@ class sense_amp_array(design.design):
|
|||
|
||||
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)
|
||||
self.local_insts[i].place(amp_position)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from tech import drc
|
|||
import debug
|
||||
import math
|
||||
from vector import vector
|
||||
|
||||
from globals import OPTS
|
||||
|
||||
class single_level_column_mux_array(design.design):
|
||||
"""
|
||||
|
|
@ -20,23 +20,14 @@ class single_level_column_mux_array(design.design):
|
|||
self.columns = columns
|
||||
self.word_size = word_size
|
||||
self.words_per_row = int(self.columns / self.word_size)
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(self.columns):
|
||||
self.add_pin("bl[{}]".format(i))
|
||||
self.add_pin("br[{}]".format(i))
|
||||
for i in range(self.words_per_row):
|
||||
self.add_pin("sel[{}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("bl_out[{}]".format(i))
|
||||
self.add_pin("br_out[{}]".format(i))
|
||||
self.add_pin("gnd")
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_array()
|
||||
|
||||
def create_layout(self):
|
||||
|
|
@ -51,6 +42,17 @@ class single_level_column_mux_array(design.design):
|
|||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(self.columns):
|
||||
self.add_pin("bl[{}]".format(i))
|
||||
self.add_pin("br[{}]".format(i))
|
||||
for i in range(self.words_per_row):
|
||||
self.add_pin("sel[{}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("bl_out[{}]".format(i))
|
||||
self.add_pin("br_out[{}]".format(i))
|
||||
self.add_pin("gnd")
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
# FIXME: Why is this 8x?
|
||||
|
|
@ -87,8 +89,7 @@ class single_level_column_mux_array(design.design):
|
|||
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)
|
||||
self.mux_inst[col_num].place(x_off)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
|
|
|||
|
|
@ -14,31 +14,34 @@ class tri_gate_array(design.design):
|
|||
design.design.__init__(self, "tri_gate_array")
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
|
||||
self.columns = columns
|
||||
self.word_size = word_size
|
||||
self.words_per_row = int(self.columns / self.word_size)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = (self.columns / self.words_per_row) * self.tri.width
|
||||
self.height = self.tri.height
|
||||
|
||||
self.place_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.tri_gate))
|
||||
self.mod_tri_gate = getattr(c, OPTS.tri_gate)
|
||||
self.tri = self.mod_tri_gate("tri_gate")
|
||||
self.add_mod(self.tri)
|
||||
|
||||
self.columns = columns
|
||||
self.word_size = word_size
|
||||
|
||||
self.words_per_row = int(self.columns / self.word_size)
|
||||
self.width = (self.columns / self.words_per_row) * self.tri.width
|
||||
self.height = self.tri.height
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
"""create the name of pins depend on the word size"""
|
||||
for i in range(self.word_size):
|
||||
|
|
@ -63,10 +66,8 @@ class tri_gate_array(design.design):
|
|||
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)
|
||||
self.tri_inst[i].place(base)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
|
|
|||
|
|
@ -20,11 +20,23 @@ class wordline_driver(design.design):
|
|||
design.design.__init__(self, "wordline_driver")
|
||||
|
||||
self.rows = rows
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_drivers()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_drivers()
|
||||
self.route_layout()
|
||||
self.route_vdd_gnd()
|
||||
self.offset_all_coordinates()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
# inputs to wordline_driver.
|
||||
for i in range(self.rows):
|
||||
|
|
@ -36,16 +48,6 @@ class wordline_driver(design.design):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_drivers()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_drivers()
|
||||
self.route_layout()
|
||||
self.route_vdd_gnd()
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
self.inv = pinv()
|
||||
|
|
@ -128,10 +130,6 @@ class wordline_driver(design.design):
|
|||
|
||||
|
||||
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)
|
||||
|
||||
if (row % 2):
|
||||
y_offset = self.inv.height*(row + 1)
|
||||
inst_mirror = "MX"
|
||||
|
|
@ -144,17 +142,14 @@ class wordline_driver(design.design):
|
|||
inv2_offset=[inv2_xoffset, y_offset]
|
||||
|
||||
# add inv1 based on the info above
|
||||
self.place_inst(name=name_inv1,
|
||||
offset=inv1_offset,
|
||||
mirror=inst_mirror)
|
||||
self.inv1_inst[row].place(offset=inv1_offset,
|
||||
mirror=inst_mirror)
|
||||
# add nand 2
|
||||
self.place_inst(name=name_nand,
|
||||
offset=nand2_offset,
|
||||
mirror=inst_mirror)
|
||||
self.nand_inst[row].place(offset=nand2_offset,
|
||||
mirror=inst_mirror)
|
||||
# add inv2
|
||||
self.place_inst(name=name_inv2,
|
||||
offset=inv2_offset,
|
||||
mirror=inst_mirror)
|
||||
self.inv2_inst[row].place(offset=inv2_offset,
|
||||
mirror=inst_mirror)
|
||||
|
||||
|
||||
def route_layout(self):
|
||||
|
|
|
|||
|
|
@ -15,21 +15,27 @@ class write_driver_array(design.design):
|
|||
design.design.__init__(self, "write_driver_array")
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.write_driver))
|
||||
self.mod_write_driver = getattr(c, OPTS.write_driver)
|
||||
self.driver = self.mod_write_driver("write_driver")
|
||||
self.add_mod(self.driver)
|
||||
|
||||
self.columns = columns
|
||||
self.word_size = word_size
|
||||
self.words_per_row = int(columns / word_size)
|
||||
|
||||
self.width = self.columns * self.driver.width
|
||||
self.height = self.height = self.driver.height
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_write_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.width = self.columns * self.driver.width
|
||||
self.height = self.driver.height
|
||||
|
||||
self.place_write_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(self.word_size):
|
||||
|
|
@ -41,14 +47,12 @@ class write_driver_array(design.design):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.create_write_array()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_write_array()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
def add_modules(self):
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.write_driver))
|
||||
self.mod_write_driver = getattr(c, OPTS.write_driver)
|
||||
self.driver = self.mod_write_driver("write_driver")
|
||||
self.add_mod(self.driver)
|
||||
|
||||
def create_write_array(self):
|
||||
self.driver_insts = {}
|
||||
|
|
@ -66,11 +70,9 @@ class write_driver_array(design.design):
|
|||
|
||||
def place_write_array(self):
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xwrite_driver{}".format(i)
|
||||
index = int(i/self.words_per_row)
|
||||
base = vector(i * self.driver.width,0)
|
||||
|
||||
self.place_inst(name=name,
|
||||
offset=base)
|
||||
self.driver_insts[index].place(base)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
|
|
|||
|
|
@ -24,14 +24,18 @@ class options(optparse.Values):
|
|||
check_lvsdrc = True
|
||||
# Variable to select the variant of spice
|
||||
spice_name = ""
|
||||
# Should we print out the banner at startup
|
||||
print_banner = True
|
||||
# The spice executable being used which is derived from the user PATH.
|
||||
spice_exe = ""
|
||||
# Variable to select the variant of drc, lvs, pex
|
||||
drc_name = ""
|
||||
lvs_name = ""
|
||||
pex_name = ""
|
||||
# The DRC/LVS/PEX executable being used which is derived from the user PATH.
|
||||
drc_exe = None
|
||||
lvs_exe = None
|
||||
pex_exe = None
|
||||
# The spice executable being used which is derived from the user PATH.
|
||||
spice_exe = ""
|
||||
# Should we print out the banner at startup
|
||||
print_banner = True
|
||||
# Run with extracted parasitics
|
||||
use_pex = False
|
||||
# Remove noncritical memory cells for characterization speed-up
|
||||
|
|
|
|||
|
|
@ -29,16 +29,47 @@ class pbitcell(pgate.pgate):
|
|||
self.num_read = num_read
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
# FIXME: Why is this static set here?
|
||||
pbitcell.width = self.width
|
||||
pbitcell.height = self.height
|
||||
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_storage()
|
||||
|
||||
if(self.num_readwrite > 0):
|
||||
self.create_readwrite_ports()
|
||||
if(self.num_write > 0):
|
||||
self.create_write_ports()
|
||||
if(self.num_read > 0):
|
||||
self.create_read_ports()
|
||||
|
||||
def create_layout(self):
|
||||
self.calculate_spacing()
|
||||
self.calculate_postions()
|
||||
|
||||
self.place_storage()
|
||||
self.route_storage()
|
||||
self.route_rails()
|
||||
|
||||
if(self.num_readwrite > 0):
|
||||
self.place_readwrite_ports()
|
||||
if(self.num_write > 0):
|
||||
self.place_write_ports()
|
||||
if(self.num_read > 0):
|
||||
self.place_read_ports()
|
||||
self.extend_well()
|
||||
|
||||
self.offset_all_coordinates()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
"""
|
||||
Adding pins for pbitcell module
|
||||
"""
|
||||
for k in range(self.num_readwrite):
|
||||
self.add_pin("rwbl{}".format(k))
|
||||
self.add_pin("rwbl_bar{}".format(k))
|
||||
|
|
@ -59,33 +90,8 @@ class pbitcell(pgate.pgate):
|
|||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
||||
def create_layout(self):
|
||||
self.create_ptx()
|
||||
self.calculate_spacing()
|
||||
self.calculate_postions()
|
||||
self.add_storage()
|
||||
self.add_rails()
|
||||
if(self.num_readwrite > 0):
|
||||
self.add_readwrite_ports()
|
||||
if(self.num_write > 0):
|
||||
self.add_write_ports()
|
||||
if(self.num_read > 0):
|
||||
self.add_read_ports()
|
||||
self.extend_well()
|
||||
self.offset_all_coordinates()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def create_ptx(self):
|
||||
"""
|
||||
Calculate transistor sizes and create ptx for read/write, write, and read ports
|
||||
"""
|
||||
|
||||
""" calculate transistor sizes """
|
||||
def add_modules(self):
|
||||
# if there are any read/write ports, then the inverter nmos is sized based the number of them
|
||||
if(self.num_readwrite > 0):
|
||||
inverter_nmos_width = self.num_readwrite*3*parameter["min_tx_size"]
|
||||
|
|
@ -129,11 +135,9 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
|
||||
def calculate_spacing(self):
|
||||
"""
|
||||
Calculate transistor spacings
|
||||
"""
|
||||
""" Calculate transistor spacings """
|
||||
|
||||
""" calculate metal contact extensions over transistor active """
|
||||
# calculate metal contact extensions over transistor active
|
||||
self.inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height)
|
||||
self.readwrite_nmos_contact_extension = 0.5*(self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height)
|
||||
self.write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height)
|
||||
|
|
@ -142,7 +146,7 @@ class pbitcell(pgate.pgate):
|
|||
# calculate the distance threshold for different gate contact spacings
|
||||
self.gate_contact_thres = drc["poly_to_active"] - drc["minwidth_metal2"]
|
||||
|
||||
""" calculations for horizontal transistor to tansistor spacing """
|
||||
#calculations for horizontal transistor to tansistor spacing
|
||||
# inverter spacings
|
||||
self.inverter_to_inverter_spacing = contact.poly.height + drc["minwidth_metal1"]
|
||||
self.inverter_to_write_spacing = drc["pwell_to_nwell"] + 2*drc["well_enclosure_active"]
|
||||
|
|
@ -264,9 +268,9 @@ class pbitcell(pgate.pgate):
|
|||
self.height = self.topmost_ypos - self.botmost_ypos - array_vdd_overlap
|
||||
|
||||
|
||||
def add_storage(self):
|
||||
def place_storage(self):
|
||||
"""
|
||||
Creates the crossed coupled inverters that act as storage for the bitcell.
|
||||
Places the crossed coupled inverters that act as storage for the bitcell.
|
||||
The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar".
|
||||
"""
|
||||
|
||||
|
|
@ -276,26 +280,14 @@ class pbitcell(pgate.pgate):
|
|||
inverter_pmos_ypos = self.inverter_nmos.active_height + self.inverter_gap
|
||||
|
||||
# create active for nmos
|
||||
self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left",
|
||||
mod=self.inverter_nmos,
|
||||
offset=[left_inverter_xpos,0])
|
||||
self.connect_inst(["Q_bar", "Q", "gnd", "gnd"])
|
||||
|
||||
self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right",
|
||||
mod=self.inverter_nmos,
|
||||
offset=[right_inverter_xpos,0])
|
||||
self.connect_inst(["gnd", "Q_bar", "Q", "gnd"])
|
||||
self.inverter_nmos_left.place([left_inverter_xpos,0])
|
||||
self.inverter_nmos_right.place([right_inverter_xpos,0])
|
||||
|
||||
# create active for pmos
|
||||
self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left",
|
||||
mod=self.inverter_pmos,
|
||||
offset=[left_inverter_xpos, inverter_pmos_ypos])
|
||||
self.connect_inst(["Q_bar", "Q", "vdd", "vdd"])
|
||||
|
||||
self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right",
|
||||
mod=self.inverter_pmos,
|
||||
offset=[right_inverter_xpos, inverter_pmos_ypos])
|
||||
self.connect_inst(["vdd", "Q_bar", "Q", "vdd"])
|
||||
self.inverter_pmos_left.place([left_inverter_xpos, inverter_pmos_ypos])
|
||||
self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos])
|
||||
|
||||
def route_storage(self):
|
||||
|
||||
# connect input (gate) of inverters
|
||||
self.add_path("poly", [self.inverter_nmos_left.get_pin("G").uc(), self.inverter_pmos_left.get_pin("G").bc()])
|
||||
|
|
@ -327,13 +319,38 @@ class pbitcell(pgate.pgate):
|
|||
self.left_building_edge = -self.inverter_tile_width
|
||||
self.right_building_edge = self.inverter_tile_width
|
||||
|
||||
|
||||
def add_rails(self):
|
||||
|
||||
def create_storage(self):
|
||||
"""
|
||||
Creates the crossed coupled inverters that act as storage for the bitcell.
|
||||
The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar".
|
||||
"""
|
||||
|
||||
# create active for nmos
|
||||
self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left",
|
||||
mod=self.inverter_nmos)
|
||||
self.connect_inst(["Q_bar", "Q", "gnd", "gnd"])
|
||||
|
||||
self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right",
|
||||
mod=self.inverter_nmos)
|
||||
self.connect_inst(["gnd", "Q_bar", "Q", "gnd"])
|
||||
|
||||
# create active for pmos
|
||||
self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left",
|
||||
mod=self.inverter_pmos)
|
||||
self.connect_inst(["Q_bar", "Q", "vdd", "vdd"])
|
||||
|
||||
self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right",
|
||||
mod=self.inverter_pmos)
|
||||
self.connect_inst(["vdd", "Q_bar", "Q", "vdd"])
|
||||
|
||||
|
||||
def route_rails(self):
|
||||
"""
|
||||
Add gnd and vdd rails and connects them to the inverters
|
||||
"""
|
||||
|
||||
""" Add rails for vdd and gnd """
|
||||
# Add rails for vdd and gnd
|
||||
self.gnd_position = vector(self.leftmost_xpos, -self.rail_tile_height)
|
||||
self.gnd = self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
|
|
@ -350,7 +367,7 @@ class pbitcell(pgate.pgate):
|
|||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
""" Connect inverters to rails """
|
||||
# Connect inverters to rails
|
||||
# connect inverter nmos to gnd
|
||||
gnd_pos_left = vector(self.inverter_nmos_left.get_pin("S").bc().x, self.gnd_position.y)
|
||||
self.add_path("metal1", [self.inverter_nmos_left.get_pin("S").bc(), gnd_pos_left])
|
||||
|
|
@ -366,9 +383,9 @@ class pbitcell(pgate.pgate):
|
|||
self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right])
|
||||
|
||||
|
||||
def add_readwrite_ports(self):
|
||||
def create_readwrite_ports(self):
|
||||
"""
|
||||
Adds read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell.
|
||||
Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell.
|
||||
A read or write is enabled by setting a Read-Write-Wordline (RWWL) high, subsequently turning on the transistor.
|
||||
The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q).
|
||||
In a write operation, driving RWBL high or low sets the value of the cell.
|
||||
|
|
@ -376,20 +393,38 @@ class pbitcell(pgate.pgate):
|
|||
This is a differential design, so each write port has a mirrored port that connects RWBL_bar to Q_bar.
|
||||
"""
|
||||
|
||||
""" Define variables relevant to write transistors """
|
||||
# define offset correction due to rotation of the ptx module
|
||||
readwrite_rotation_correct = self.readwrite_nmos.active_height
|
||||
|
||||
# define write transistor variables as empty arrays based on the number of write ports
|
||||
self.readwrite_nmos_left = [None] * self.num_readwrite
|
||||
self.readwrite_nmos_right = [None] * self.num_readwrite
|
||||
|
||||
# iterate over the number of read/write ports
|
||||
for k in range(0,self.num_readwrite):
|
||||
# add read/write transistors
|
||||
self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k),
|
||||
mod=self.readwrite_nmos)
|
||||
self.connect_inst(["Q", "rwwl{}".format(k), "rwbl{}".format(k), "gnd"])
|
||||
|
||||
self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k),
|
||||
mod=self.readwrite_nmos)
|
||||
self.connect_inst(["Q_bar", "rwwl{}".format(k), "rwbl_bar{}".format(k), "gnd"])
|
||||
|
||||
|
||||
def place_readwrite_ports(self):
|
||||
"""
|
||||
Places read/write ports in the bit cell.
|
||||
"""
|
||||
|
||||
# Define variables relevant to write transistors
|
||||
self.rwwl_positions = [None] * self.num_readwrite
|
||||
self.rwbl_positions = [None] * self.num_readwrite
|
||||
self.rwbl_bar_positions = [None] * self.num_readwrite
|
||||
|
||||
# define offset correction due to rotation of the ptx module
|
||||
readwrite_rotation_correct = self.readwrite_nmos.active_height
|
||||
|
||||
# iterate over the number of read/write ports
|
||||
for k in range(0,self.num_readwrite):
|
||||
""" Add transistors """
|
||||
# Add transistors
|
||||
# calculate read/write transistor offsets
|
||||
left_readwrite_transistor_xpos = self.left_building_edge \
|
||||
- self.inverter_to_write_spacing \
|
||||
|
|
@ -402,19 +437,13 @@ class pbitcell(pgate.pgate):
|
|||
+ readwrite_rotation_correct
|
||||
|
||||
# add read/write transistors
|
||||
self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k),
|
||||
mod=self.readwrite_nmos,
|
||||
offset=[left_readwrite_transistor_xpos,0],
|
||||
rotate=90)
|
||||
self.connect_inst(["Q", "rwwl{}".format(k), "rwbl{}".format(k), "gnd"])
|
||||
self.readwrite_nmos_left[k].place(offset=[left_readwrite_transistor_xpos,0],
|
||||
rotate=90)
|
||||
|
||||
self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k),
|
||||
mod=self.readwrite_nmos,
|
||||
offset=[right_readwrite_transistor_xpos,0],
|
||||
rotate=90)
|
||||
self.connect_inst(["Q_bar", "rwwl{}".format(k), "rwbl_bar{}".format(k), "gnd"])
|
||||
self.readwrite_nmos_right[k].place(offset=[right_readwrite_transistor_xpos,0],
|
||||
rotate=90)
|
||||
|
||||
""" Add RWWL lines """
|
||||
# Add RWWL lines
|
||||
# calculate RWWL position
|
||||
rwwl_ypos = self.gnd_position.y - (k+1)*self.rowline_tile_height
|
||||
self.rwwl_positions[k] = vector(self.leftmost_xpos, rwwl_ypos)
|
||||
|
|
@ -426,7 +455,7 @@ class pbitcell(pgate.pgate):
|
|||
width=self.width,
|
||||
height=contact.m1m2.width)
|
||||
|
||||
""" Source/RWBL/RWBL_bar connections """
|
||||
# Source/RWBL/RWBL_bar connections
|
||||
# add metal1-to-metal2 contacts on top of read/write transistor source pins for connection to WBL and WBL_bar
|
||||
offset_left = self.readwrite_nmos_left[k].get_pin("S").center()
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
|
|
@ -453,7 +482,7 @@ class pbitcell(pgate.pgate):
|
|||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
""" Gate/RWWL connections """
|
||||
# Gate/RWWL connections
|
||||
# add poly-to-meltal2 contacts to connect gate of read/write transistors to RWWL (contact next to gate)
|
||||
# contact must be placed a metal1 width below the source pin to avoid drc from source pin routings
|
||||
if(self.readwrite_nmos_contact_extension > self.gate_contact_thres):
|
||||
|
|
@ -502,7 +531,7 @@ class pbitcell(pgate.pgate):
|
|||
self.add_path("metal2", [left_gate_contact, left_rwwl_contact])
|
||||
self.add_path("metal2", [right_gate_contact, right_rwwl_contact])
|
||||
|
||||
""" Drain/Storage connections """
|
||||
# Drain/Storage connections
|
||||
# this path only needs to be drawn once on the last iteration of the loop
|
||||
if(k == self.num_readwrite-1):
|
||||
# add contacts to connect gate of inverters to drain of read/write transistors
|
||||
|
|
@ -536,34 +565,56 @@ class pbitcell(pgate.pgate):
|
|||
# end if
|
||||
# end for
|
||||
|
||||
""" update furthest left and right transistor edges """
|
||||
# update furthest left and right transistor edges
|
||||
self.left_building_edge = left_readwrite_transistor_xpos - self.readwrite_nmos.active_height
|
||||
self.right_building_edge = right_readwrite_transistor_xpos
|
||||
|
||||
|
||||
|
||||
def add_write_ports(self):
|
||||
def create_write_ports(self):
|
||||
"""
|
||||
Adds write ports to the bit cell. A differential pair of transistors can write only.
|
||||
Creates write ports in the bit cell. A differential pair of transistors can write only.
|
||||
A write is enabled by setting a Write-Rowline (WWL) high, subsequently turning on the transistor.
|
||||
The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q).
|
||||
In a write operation, driving WBL high or low sets the value of the cell.
|
||||
This is a differential design, so each write port has a mirrored port that connects WBL_bar to Q_bar.
|
||||
"""
|
||||
|
||||
""" Define variables relevant to write transistors """
|
||||
# Define variables relevant to write transistors
|
||||
# define offset correction due to rotation of the ptx module
|
||||
write_rotation_correct = self.write_nmos.active_height
|
||||
|
||||
# define write transistor variables as empty arrays based on the number of write ports
|
||||
self.write_nmos_left = [None] * self.num_write
|
||||
self.write_nmos_right = [None] * self.num_write
|
||||
self.wwl_positions = [None] * self.num_write
|
||||
self.wbl_positions = [None] * self.num_write
|
||||
self.wbl_bar_positions = [None] * self.num_write
|
||||
|
||||
# iterate over the number of write ports
|
||||
for k in range(0,self.num_write):
|
||||
""" Add transistors """
|
||||
# add write transistors
|
||||
self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k),
|
||||
mod=self.write_nmos)
|
||||
self.connect_inst(["Q", "wwl{}".format(k), "wbl{}".format(k), "gnd"])
|
||||
|
||||
self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k),
|
||||
mod=self.write_nmos)
|
||||
self.connect_inst(["Q_bar", "wwl{}".format(k), "wbl_bar{}".format(k), "gnd"])
|
||||
|
||||
|
||||
def place_write_ports(self):
|
||||
"""
|
||||
Places write ports in the bit cell.
|
||||
"""
|
||||
|
||||
# Define variables relevant to write transistors
|
||||
self.wwl_positions = [None] * self.num_write
|
||||
self.wbl_positions = [None] * self.num_write
|
||||
self.wbl_bar_positions = [None] * self.num_write
|
||||
|
||||
# define offset correction due to rotation of the ptx module
|
||||
write_rotation_correct = self.write_nmos.active_height
|
||||
|
||||
# iterate over the number of write ports
|
||||
for k in range(0,self.num_write):
|
||||
# Add transistors
|
||||
# calculate write transistor offsets
|
||||
left_write_transistor_xpos = self.left_building_edge \
|
||||
- (not self.readwrite_port_flag)*self.inverter_to_write_spacing \
|
||||
|
|
@ -578,19 +629,13 @@ class pbitcell(pgate.pgate):
|
|||
+ write_rotation_correct
|
||||
|
||||
# add write transistors
|
||||
self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k),
|
||||
mod=self.write_nmos,
|
||||
offset=[left_write_transistor_xpos,0],
|
||||
rotate=90)
|
||||
self.connect_inst(["Q", "wwl{}".format(k), "wbl{}".format(k), "gnd"])
|
||||
self.write_nmos_left[k].place(offset=[left_write_transistor_xpos,0],
|
||||
rotate=90)
|
||||
|
||||
self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k),
|
||||
mod=self.write_nmos,
|
||||
offset=[right_write_transistor_xpos,0],
|
||||
rotate=90)
|
||||
self.connect_inst(["Q_bar", "wwl{}".format(k), "wbl_bar{}".format(k), "gnd"])
|
||||
self.write_nmos_right[k].place(offset=[right_write_transistor_xpos,0],
|
||||
rotate=90)
|
||||
|
||||
""" Add WWL lines """
|
||||
# Add WWL lines
|
||||
# calculate WWL position
|
||||
wwl_ypos = self.gnd_position.y \
|
||||
- self.num_readwrite*self.rowline_tile_height \
|
||||
|
|
@ -604,7 +649,7 @@ class pbitcell(pgate.pgate):
|
|||
width=self.width,
|
||||
height=contact.m1m2.width)
|
||||
|
||||
""" Source/WBL/WBL_bar connections """
|
||||
# Source/WBL/WBL_bar connections
|
||||
# add metal1-to-metal2 contacts on top of write transistor source pins for connection to WBL and WBL_bar
|
||||
offset_left = self.write_nmos_left[k].get_pin("S").center()
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
|
|
@ -631,7 +676,7 @@ class pbitcell(pgate.pgate):
|
|||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
""" Gate/WWL connections """
|
||||
# Gate/WWL connections
|
||||
# add poly-to-meltal2 contacts to connect gate of write transistors to WWL (contact next to gate)
|
||||
# contact must be placed a metal width below the source pin to avoid drc from source pin routings
|
||||
if(self.write_nmos_contact_extension > self.gate_contact_thres):
|
||||
|
|
@ -680,7 +725,7 @@ class pbitcell(pgate.pgate):
|
|||
self.add_path("metal2", [left_gate_contact, left_wwl_contact])
|
||||
self.add_path("metal2", [right_gate_contact, right_wwl_contact])
|
||||
|
||||
""" Drain/Storage connections """
|
||||
# Drain/Storage connections
|
||||
# this path only needs to be drawn once on the last iteration of the loop
|
||||
if(k == self.num_write-1):
|
||||
# add contacts to connect gate of inverters to drain of write transistors
|
||||
|
|
@ -711,17 +756,15 @@ class pbitcell(pgate.pgate):
|
|||
midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.write_nmos_right[k].get_pin("D").rc().y)
|
||||
self.add_path("metal1", [right_storage_contact, midR0], width=contact.poly.second_layer_width)
|
||||
self.add_path("metal1", [midR0+vector(0,0.5*contact.poly.second_layer_width), midR1, self.write_nmos_right[k].get_pin("D").rc()])
|
||||
# end if
|
||||
# end for
|
||||
|
||||
""" update furthest left and right transistor edges """
|
||||
# update furthest left and right transistor edges
|
||||
self.left_building_edge = left_write_transistor_xpos - self.write_nmos.active_height
|
||||
self.right_building_edge = right_write_transistor_xpos
|
||||
|
||||
|
||||
def add_read_ports(self):
|
||||
def create_read_ports(self):
|
||||
"""
|
||||
Adds read ports to the bit cell. A differential pair of ports can read only.
|
||||
Creates read ports in the bit cell. A differential pair of ports can read only.
|
||||
Two transistors function as a read port, denoted as the "read transistor" and the "read-access transistor".
|
||||
The read transistor is connected to RWL (gate), RBL (drain), and the read-access transistor (source).
|
||||
The read-access transistor is connected to Q_bar (gate), gnd (source), and the read transistor (drain).
|
||||
|
|
@ -731,7 +774,42 @@ class pbitcell(pgate.pgate):
|
|||
using sense amps. This is a differential design, so each read port has a mirrored port that connects RBL_bar to Q.
|
||||
"""
|
||||
|
||||
""" Define variables relevant to read transistors """
|
||||
# define read transistor variables as empty arrays based on the number of read ports
|
||||
self.read_nmos_left = [None] * self.num_read
|
||||
self.read_nmos_right = [None] * self.num_read
|
||||
self.read_access_nmos_left = [None] * self.num_read
|
||||
self.read_access_nmos_right = [None] * self.num_read
|
||||
|
||||
# iterate over the number of read ports
|
||||
for k in range(0,self.num_read):
|
||||
# add read-access transistors
|
||||
self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k),
|
||||
mod=self.read_nmos)
|
||||
self.connect_inst(["RA_to_R_left{}".format(k), " Q_bar", "gnd", "gnd"])
|
||||
|
||||
self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right{}".format(k),
|
||||
mod=self.read_nmos)
|
||||
self.connect_inst(["RA_to_R_right{}".format(k), "Q", "gnd", "gnd"])
|
||||
|
||||
# add read transistors
|
||||
self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k),
|
||||
mod=self.read_nmos)
|
||||
self.connect_inst(["rbl{}".format(k), "rwl{}".format(k), "RA_to_R_left{}".format(k), "gnd"])
|
||||
|
||||
self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k),
|
||||
mod=self.read_nmos)
|
||||
self.connect_inst(["rbl_bar{}".format(k), "rwl{}".format(k), "RA_to_R_right{}".format(k), "gnd"])
|
||||
|
||||
def place_read_ports(self):
|
||||
"""
|
||||
Places the read ports in the bit cell.
|
||||
"""
|
||||
|
||||
# Define variables relevant to read transistors
|
||||
self.rwl_positions = [None] * self.num_read
|
||||
self.rbl_positions = [None] * self.num_read
|
||||
self.rbl_bar_positions = [None] * self.num_read
|
||||
|
||||
# define offset correction due to rotation of the ptx module
|
||||
read_rotation_correct = self.read_nmos.active_height
|
||||
|
||||
|
|
@ -749,7 +827,7 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
# iterate over the number of read ports
|
||||
for k in range(0,self.num_read):
|
||||
""" Add transistors """
|
||||
# Add transistors
|
||||
# calculate transistor offsets
|
||||
left_read_transistor_xpos = self.left_building_edge \
|
||||
- self.write_to_read_spacing \
|
||||
|
|
@ -787,7 +865,7 @@ class pbitcell(pgate.pgate):
|
|||
rotate=90)
|
||||
self.connect_inst(["rbl_bar{}".format(k), "rwl{}".format(k), "RA_to_R_right{}".format(k), "gnd"])
|
||||
|
||||
""" Add RWL lines """
|
||||
# Add RWL lines
|
||||
# calculate RWL position
|
||||
rwl_ypos = self.gnd_position.y \
|
||||
- self.num_readwrite*self.rowline_tile_height \
|
||||
|
|
@ -802,7 +880,7 @@ class pbitcell(pgate.pgate):
|
|||
width=self.width,
|
||||
height=contact.m1m2.width)
|
||||
|
||||
""" Drain of read transistor / RBL & RBL_bar connection """
|
||||
# Drain of read transistor / RBL & RBL_bar connection
|
||||
# add metal1-to-metal2 contacts on top of read transistor drain pins for connection to RBL and RBL_bar
|
||||
offset_left = self.read_nmos_left[k].get_pin("D").center()
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
|
|
@ -829,7 +907,7 @@ class pbitcell(pgate.pgate):
|
|||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
""" Gate of read transistor / RWL connection """
|
||||
# Gate of read transistor / RWL connection
|
||||
# add poly-to-meltal2 contacts to connect gate of read transistors to RWL (contact next to gate)
|
||||
if(self.read_nmos_contact_extension > self.gate_contact_thres):
|
||||
contact_xpos = self.read_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width
|
||||
|
|
@ -874,7 +952,7 @@ class pbitcell(pgate.pgate):
|
|||
self.add_path("metal2", [left_gate_contact, left_rwl_contact])
|
||||
self.add_path("metal2", [right_gate_contact, right_rwl_contact])
|
||||
|
||||
""" Source of read-access transistor / GND connection """
|
||||
# Source of read-access transistor / GND connection
|
||||
# connect source of read-access transistor to GND (metal1 path)
|
||||
gnd_offset_left = vector(self.read_access_nmos_left[k].get_pin("S").bc().x, self.gnd_position.y)
|
||||
self.add_path("metal1", [self.read_access_nmos_left[k].get_pin("S").bc(), gnd_offset_left])
|
||||
|
|
@ -882,7 +960,7 @@ class pbitcell(pgate.pgate):
|
|||
gnd_offset_right = vector(self.read_access_nmos_right[k].get_pin("S").bc().x, self.gnd_position.y)
|
||||
self.add_path("metal1", [self.read_access_nmos_right[k].get_pin("S").bc(), gnd_offset_right])
|
||||
|
||||
""" Gate of read-access transistor / storage connection """
|
||||
# Gate of read-access transistor / storage connection
|
||||
# add poly-to-metal1 contacts to connect gate of read-access transistors to output of inverters (contact next to gate)
|
||||
if(self.read_nmos_contact_extension > self.gate_contact_thres):
|
||||
contact_xpos = self.read_nmos_left[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width
|
||||
|
|
@ -931,7 +1009,7 @@ class pbitcell(pgate.pgate):
|
|||
self.add_path("metal1", [right_gate_contact, midR0, midR1, midR2, right_inverter_offset])
|
||||
# end for
|
||||
|
||||
|
||||
|
||||
def extend_well(self):
|
||||
"""
|
||||
Connects wells between ptx modules to avoid drc spacing issues.
|
||||
|
|
@ -939,7 +1017,7 @@ class pbitcell(pgate.pgate):
|
|||
the well connections must be done piecewise to avoid pwell and nwell overlap.
|
||||
"""
|
||||
|
||||
""" extend pwell to encompass entire nmos region of the cell up to the height of the inverter nmos well """
|
||||
# extend pwell to encompass entire nmos region of the cell up to the height of the inverter nmos well
|
||||
offset = vector(self.leftmost_xpos, self.botmost_ypos)
|
||||
well_height = -self.botmost_ypos + self.inverter_nmos.cell_well_height - drc["well_enclosure_active"]
|
||||
self.add_rect(layer="pwell",
|
||||
|
|
@ -947,7 +1025,9 @@ class pbitcell(pgate.pgate):
|
|||
width=self.width,
|
||||
height=well_height)
|
||||
|
||||
""" extend pwell over read/write and write transistors to the height of the write transistor well (read/write and write transistors are the same height) """
|
||||
# extend pwell over read/write and write transistors to the
|
||||
# height of the write transistor well (read/write and write
|
||||
# transistors are the same height)
|
||||
if(self.num_write > 0):
|
||||
# calculate the edge of the write transistor well closest to the center
|
||||
left_write_well_xpos = self.write_nmos_left[0].offset.x + drc["well_enclosure_active"]
|
||||
|
|
@ -973,7 +1053,7 @@ class pbitcell(pgate.pgate):
|
|||
width=write_well_width,
|
||||
height=write_well_height)
|
||||
|
||||
""" extend pwell over the read transistors to the height of the bitcell """
|
||||
# extend pwell over the read transistors to the height of the bitcell
|
||||
if(self.num_read > 0):
|
||||
# calculate the edge of the read transistor well clostest to the center
|
||||
left_read_well_xpos = self.read_nmos_left[0].offset.x + drc["well_enclosure_active"]
|
||||
|
|
@ -995,7 +1075,7 @@ class pbitcell(pgate.pgate):
|
|||
width=read_well_width,
|
||||
height=read_well_height)
|
||||
|
||||
""" extend nwell to encompass inverter_pmos """
|
||||
# extend nwell to encompass inverter_pmos
|
||||
# calculate offset of the left pmos well
|
||||
inverter_well_xpos = -self.inverter_tile_width - drc["well_enclosure_active"]
|
||||
inverter_well_ypos = self.inverter_nmos.active_height + self.inverter_gap - drc["well_enclosure_active"]
|
||||
|
|
@ -1012,7 +1092,7 @@ class pbitcell(pgate.pgate):
|
|||
height=well_height)
|
||||
|
||||
|
||||
""" add well contacts """
|
||||
# add well contacts
|
||||
# connect pimplants to gnd
|
||||
offset = vector(0, self.gnd_position.y + 0.5*contact.well.second_layer_width)
|
||||
self.add_contact_center(layers=("active", "contact", "metal1"),
|
||||
|
|
|
|||
|
|
@ -41,40 +41,49 @@ class pinv(pgate.pgate):
|
|||
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
# for run-time, we won't check every transitor DRC/LVS independently
|
||||
# but this may be uncommented for debug purposes
|
||||
#self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
""" Adds pins for spice netlist """
|
||||
self.add_pin_list(["A", "Z", "vdd", "gnd"])
|
||||
|
||||
def create_netlist(self):
|
||||
""" Calls all functions related to the generation of the netlist """
|
||||
self.add_pins()
|
||||
self.determine_tx_mults()
|
||||
self.add_ptx()
|
||||
self.create_ptx()
|
||||
|
||||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
self.determine_tx_mults()
|
||||
self.create_ptx()
|
||||
self.setup_layout_constants()
|
||||
self.add_supply_rails()
|
||||
self.add_ptx()
|
||||
self.route_supply_rails()
|
||||
self.place_ptx()
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.connect_rails()
|
||||
self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", rotate=0)
|
||||
self.route_outputs()
|
||||
|
||||
def add_pins(self):
|
||||
""" Adds pins for spice netlist """
|
||||
self.add_pin_list(["A", "Z", "vdd", "gnd"])
|
||||
|
||||
|
||||
def determine_tx_mults(self):
|
||||
"""
|
||||
Determines the number of fingers needed to achieve the size within
|
||||
the height constraint. This may fail if the user has a tight height.
|
||||
"""
|
||||
|
||||
# This may make the result differ when the layout is created...
|
||||
if OPTS.netlist_only:
|
||||
self.tx_mults = 1
|
||||
self.nmos_width = self.nmos_size*drc["minwidth_tx"]
|
||||
self.pmos_width = self.pmos_size*drc["minwidth_tx"]
|
||||
return
|
||||
|
||||
# Do a quick sanity check and bail if unlikely feasible height
|
||||
# Sanity check. can we make an inverter in the height with minimum tx sizes?
|
||||
# Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain)
|
||||
|
|
@ -138,7 +147,7 @@ class pinv(pgate.pgate):
|
|||
|
||||
|
||||
|
||||
def create_ptx(self):
|
||||
def add_ptx(self):
|
||||
""" Create the PMOS and NMOS transistors. """
|
||||
self.nmos = ptx(width=self.nmos_width,
|
||||
mults=self.tx_mults,
|
||||
|
|
@ -154,7 +163,7 @@ class pinv(pgate.pgate):
|
|||
connect_active=True)
|
||||
self.add_mod(self.pmos)
|
||||
|
||||
def add_supply_rails(self):
|
||||
def route_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top and bottom. """
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer="metal1",
|
||||
|
|
@ -166,26 +175,34 @@ class pinv(pgate.pgate):
|
|||
offset=vector(0.5*self.width,self.height),
|
||||
width=self.width)
|
||||
|
||||
def add_ptx(self):
|
||||
def create_ptx(self):
|
||||
"""
|
||||
Add PMOS and NMOS to the layout at the upper-most and lowest position
|
||||
Create the PMOS and NMOS netlist.
|
||||
"""
|
||||
|
||||
self.pmos_inst=self.add_inst(name="pinv_pmos",
|
||||
mod=self.pmos)
|
||||
self.connect_inst(["Z", "A", "vdd", "vdd"])
|
||||
|
||||
self.nmos_inst=self.add_inst(name="pinv_nmos",
|
||||
mod=self.nmos)
|
||||
self.connect_inst(["Z", "A", "gnd", "gnd"])
|
||||
|
||||
|
||||
def place_ptx(self):
|
||||
"""
|
||||
Place PMOS and NMOS to the layout at the upper-most and lowest position
|
||||
to provide maximum routing in channel
|
||||
"""
|
||||
|
||||
# place PMOS so it is half a poly spacing down from the top
|
||||
self.pmos_pos = self.pmos.active_offset.scale(1,0) \
|
||||
+ vector(0, self.height-self.pmos.active_height-self.top_bottom_space)
|
||||
self.pmos_inst=self.add_inst(name="pinv_pmos",
|
||||
mod=self.pmos,
|
||||
offset=self.pmos_pos)
|
||||
self.connect_inst(["Z", "A", "vdd", "vdd"])
|
||||
self.pmos_inst.place(self.pmos_pos)
|
||||
|
||||
# place NMOS so that it is half a poly spacing up from the bottom
|
||||
self.nmos_pos = self.nmos.active_offset.scale(1,0) + vector(0,self.top_bottom_space)
|
||||
self.nmos_inst=self.add_inst(name="pinv_nmos",
|
||||
mod=self.nmos,
|
||||
offset=self.nmos_pos)
|
||||
self.connect_inst(["Z", "A", "gnd", "gnd"])
|
||||
self.nmos_inst.place(self.nmos_pos)
|
||||
|
||||
|
||||
# Output position will be in between the PMOS and NMOS drains
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ class pinvbuf(design.design):
|
|||
debug.info(1, "Creating {}".format(self.name))
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
|
|
@ -49,7 +50,7 @@ class pinvbuf(design.design):
|
|||
self.width = 2*self.inv1.width + self.inv2.width
|
||||
self.height = 2*self.inv1.height
|
||||
|
||||
self.place_insts()
|
||||
self.place_modules()
|
||||
self.route_wires()
|
||||
self.add_layout_pins()
|
||||
|
||||
|
|
@ -96,23 +97,19 @@ class pinvbuf(design.design):
|
|||
mod=self.inv2)
|
||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||
|
||||
def place_insts(self):
|
||||
def place_modules(self):
|
||||
# Add INV1 to the right (capacitance shield)
|
||||
self.place_inst(name="buf_inv1",
|
||||
offset=vector(0,0))
|
||||
self.inv1_inst.place(vector(0,0))
|
||||
|
||||
# Add INV2 to the right
|
||||
self.place_inst(name="buf_inv2",
|
||||
offset=vector(self.inv1_inst.rx(),0))
|
||||
self.inv2_inst.place(vector(self.inv1_inst.rx(),0))
|
||||
|
||||
# Add INV3 to the right
|
||||
self.place_inst(name="buf_inv3",
|
||||
offset=vector(self.inv2_inst.rx(),0))
|
||||
self.inv3_inst.place(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")
|
||||
self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),2*self.inv2.height),
|
||||
mirror = "MX")
|
||||
|
||||
|
||||
def route_wires(self):
|
||||
|
|
|
|||
|
|
@ -36,32 +36,34 @@ class pnand2(pgate.pgate):
|
|||
self.tx_mults = 1
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
#self.DRC_LVS()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
""" Adds pins for spice netlist """
|
||||
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_ptx()
|
||||
self.create_ptx()
|
||||
|
||||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
self.create_ptx()
|
||||
self.setup_layout_constants()
|
||||
self.add_supply_rails()
|
||||
self.add_ptx()
|
||||
self.route_supply_rails()
|
||||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
||||
def create_ptx(self):
|
||||
def add_pins(self):
|
||||
""" Adds pins for spice netlist """
|
||||
self.add_pin_list(["A", "B", "Z", "vdd", "gnd"])
|
||||
|
||||
|
||||
def add_ptx(self):
|
||||
""" Create the PMOS and NMOS transistors. """
|
||||
self.nmos = ptx(width=self.nmos_width,
|
||||
mults=self.tx_mults,
|
||||
|
|
@ -105,7 +107,7 @@ class pnand2(pgate.pgate):
|
|||
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
|
||||
drc["poly_extend_active"], self.poly_space)
|
||||
|
||||
def add_supply_rails(self):
|
||||
def route_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top and bottom. """
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer="metal1",
|
||||
|
|
@ -117,37 +119,47 @@ class pnand2(pgate.pgate):
|
|||
offset=vector(0.5*self.width,self.height),
|
||||
width=self.width)
|
||||
|
||||
def add_ptx(self):
|
||||
def create_ptx(self):
|
||||
"""
|
||||
Add PMOS and NMOS to the layout at the upper-most and lowest position
|
||||
Add PMOS and NMOS to the netlist.
|
||||
"""
|
||||
|
||||
self.pmos1_inst=self.add_inst(name="pnand2_pmos1",
|
||||
mod=self.pmos)
|
||||
self.connect_inst(["vdd", "A", "Z", "vdd"])
|
||||
|
||||
self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
|
||||
mod=self.pmos)
|
||||
self.connect_inst(["Z", "B", "vdd", "vdd"])
|
||||
|
||||
self.nmos1_inst=self.add_inst(name="pnand2_nmos1",
|
||||
mod=self.nmos)
|
||||
self.connect_inst(["Z", "B", "net1", "gnd"])
|
||||
|
||||
self.nmos2_inst=self.add_inst(name="pnand2_nmos2",
|
||||
mod=self.nmos)
|
||||
self.connect_inst(["net1", "A", "gnd", "gnd"])
|
||||
|
||||
|
||||
def place_ptx(self):
|
||||
"""
|
||||
Place PMOS and NMOS to the layout at the upper-most and lowest position
|
||||
to provide maximum routing in channel
|
||||
"""
|
||||
|
||||
pmos1_pos = vector(self.pmos.active_offset.x,
|
||||
self.height - self.pmos.active_height - self.top_bottom_space)
|
||||
self.pmos1_inst=self.add_inst(name="pnand2_pmos1",
|
||||
mod=self.pmos,
|
||||
offset=pmos1_pos)
|
||||
self.connect_inst(["vdd", "A", "Z", "vdd"])
|
||||
self.pmos1_inst.place(pmos1_pos)
|
||||
|
||||
self.pmos2_pos = pmos1_pos + self.overlap_offset
|
||||
self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
|
||||
mod=self.pmos,
|
||||
offset=self.pmos2_pos)
|
||||
self.connect_inst(["Z", "B", "vdd", "vdd"])
|
||||
self.pmos2_inst.place(self.pmos2_pos)
|
||||
|
||||
|
||||
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
|
||||
self.nmos1_inst=self.add_inst(name="pnand2_nmos1",
|
||||
mod=self.nmos,
|
||||
offset=nmos1_pos)
|
||||
self.connect_inst(["Z", "B", "net1", "gnd"])
|
||||
self.nmos1_inst.place(nmos1_pos)
|
||||
|
||||
self.nmos2_pos = nmos1_pos + self.overlap_offset
|
||||
self.nmos2_inst=self.add_inst(name="pnand2_nmos2",
|
||||
mod=self.nmos,
|
||||
offset=self.nmos2_pos)
|
||||
self.connect_inst(["net1", "A", "gnd", "gnd"])
|
||||
self.nmos2_inst.place(self.nmos2_pos)
|
||||
|
||||
# Output position will be in between the PMOS and NMOS
|
||||
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ class pnand3(pgate.pgate):
|
|||
self.tx_mults = 1
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
#self.DRC_LVS()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -49,21 +49,22 @@ class pnand3(pgate.pgate):
|
|||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_ptx()
|
||||
self.create_ptx()
|
||||
|
||||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
self.create_ptx()
|
||||
self.setup_layout_constants()
|
||||
self.add_supply_rails()
|
||||
self.add_ptx()
|
||||
self.route_supply_rails()
|
||||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.extend_wells(self.well_pos)
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
||||
def create_ptx(self):
|
||||
def add_ptx(self):
|
||||
""" Create the PMOS and NMOS transistors. """
|
||||
self.nmos = ptx(width=self.nmos_width,
|
||||
mults=self.tx_mults,
|
||||
|
|
@ -103,7 +104,7 @@ class pnand3(pgate.pgate):
|
|||
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
|
||||
drc["poly_extend_active"], self.poly_space)
|
||||
|
||||
def add_supply_rails(self):
|
||||
def route_supply_rails(self):
|
||||
""" Add vdd/gnd rails to the top and bottom. """
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer="metal1",
|
||||
|
|
@ -115,50 +116,61 @@ class pnand3(pgate.pgate):
|
|||
offset=vector(0.5*self.width,self.height),
|
||||
width=self.width)
|
||||
|
||||
def add_ptx(self):
|
||||
def create_ptx(self):
|
||||
"""
|
||||
Add PMOS and NMOS to the layout at the upper-most and lowest position
|
||||
Create the PMOS and NMOS in the netlist.
|
||||
"""
|
||||
|
||||
self.pmos1_inst=self.add_inst(name="pnand3_pmos1",
|
||||
mod=self.pmos)
|
||||
self.connect_inst(["vdd", "A", "Z", "vdd"])
|
||||
|
||||
self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
|
||||
mod=self.pmos)
|
||||
self.connect_inst(["Z", "B", "vdd", "vdd"])
|
||||
|
||||
self.pmos3_inst = self.add_inst(name="pnand3_pmos3",
|
||||
mod=self.pmos)
|
||||
self.connect_inst(["Z", "C", "vdd", "vdd"])
|
||||
|
||||
self.nmos1_inst=self.add_inst(name="pnand3_nmos1",
|
||||
mod=self.nmos)
|
||||
self.connect_inst(["Z", "C", "net1", "gnd"])
|
||||
|
||||
self.nmos2_inst=self.add_inst(name="pnand3_nmos2",
|
||||
mod=self.nmos)
|
||||
self.connect_inst(["net1", "B", "net2", "gnd"])
|
||||
|
||||
self.nmos3_inst=self.add_inst(name="pnand3_nmos3",
|
||||
mod=self.nmos)
|
||||
self.connect_inst(["net2", "A", "gnd", "gnd"])
|
||||
|
||||
|
||||
def place_ptx(self):
|
||||
"""
|
||||
Place the PMOS and NMOS in the layout at the upper-most and lowest position
|
||||
to provide maximum routing in channel
|
||||
"""
|
||||
|
||||
pmos1_pos = vector(self.pmos.active_offset.x,
|
||||
self.height - self.pmos.active_height - self.top_bottom_space)
|
||||
self.pmos1_inst=self.add_inst(name="pnand3_pmos1",
|
||||
mod=self.pmos,
|
||||
offset=pmos1_pos)
|
||||
self.connect_inst(["vdd", "A", "Z", "vdd"])
|
||||
self.pmos1_inst.place(pmos1_pos)
|
||||
|
||||
pmos2_pos = pmos1_pos + self.overlap_offset
|
||||
self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
|
||||
mod=self.pmos,
|
||||
offset=pmos2_pos)
|
||||
self.connect_inst(["Z", "B", "vdd", "vdd"])
|
||||
self.pmos2_inst.place(pmos2_pos)
|
||||
|
||||
self.pmos3_pos = pmos2_pos + self.overlap_offset
|
||||
self.pmos3_inst = self.add_inst(name="pnand3_pmos3",
|
||||
mod=self.pmos,
|
||||
offset=self.pmos3_pos)
|
||||
self.connect_inst(["Z", "C", "vdd", "vdd"])
|
||||
self.pmos3_inst.place(self.pmos3_pos)
|
||||
|
||||
|
||||
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
|
||||
self.nmos1_inst=self.add_inst(name="pnand3_nmos1",
|
||||
mod=self.nmos,
|
||||
offset=nmos1_pos)
|
||||
self.connect_inst(["Z", "C", "net1", "gnd"])
|
||||
self.nmos1_inst.place(nmos1_pos)
|
||||
|
||||
nmos2_pos = nmos1_pos + self.overlap_offset
|
||||
self.nmos2_inst=self.add_inst(name="pnand3_nmos2",
|
||||
mod=self.nmos,
|
||||
offset=nmos2_pos)
|
||||
self.connect_inst(["net1", "B", "net2", "gnd"])
|
||||
self.nmos2_inst.place(nmos2_pos)
|
||||
|
||||
|
||||
self.nmos3_pos = nmos2_pos + self.overlap_offset
|
||||
self.nmos3_inst=self.add_inst(name="pnand3_nmos3",
|
||||
mod=self.nmos,
|
||||
offset=self.nmos3_pos)
|
||||
self.connect_inst(["net2", "A", "gnd", "gnd"])
|
||||
self.nmos3_inst.place(self.nmos3_pos)
|
||||
|
||||
# This should be placed at the top of the NMOS well
|
||||
self.well_pos = vector(0,self.nmos1_inst.uy())
|
||||
|
|
|
|||
|
|
@ -32,38 +32,38 @@ class precharge(pgate.pgate):
|
|||
self.bitcell_br = bitcell_br
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_ptx()
|
||||
self.create_ptx()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_ptx()
|
||||
self.connect_poly()
|
||||
self.route_en()
|
||||
self.place_nwell_and_contact()
|
||||
self.route_vdd_rail()
|
||||
self.route_bitlines()
|
||||
self.connect_to_bitlines()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin_list(["bl", "br", "en", "vdd"])
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
||||
def create_layout(self):
|
||||
self.create_ptx()
|
||||
self.add_ptx()
|
||||
self.connect_poly()
|
||||
self.add_en()
|
||||
self.add_nwell_and_contact()
|
||||
self.add_vdd_rail()
|
||||
self.add_bitlines()
|
||||
self.connect_to_bitlines()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_ptx(self):
|
||||
def add_ptx(self):
|
||||
"""Initializes the upper and lower pmos"""
|
||||
self.pmos = ptx(width=self.ptx_width,
|
||||
tx_type="pmos")
|
||||
self.add_mod(self.pmos)
|
||||
|
||||
# Compute the other pmos2 location, but determining offset to overlap the
|
||||
# source and drain pins
|
||||
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
|
||||
|
||||
|
||||
|
||||
def add_vdd_rail(self):
|
||||
def route_vdd_rail(self):
|
||||
|
||||
"""Adds a vdd rail at the top of the cell"""
|
||||
# adds the rail across the width of the cell
|
||||
vdd_position = vector(0, self.height - self.m1_width)
|
||||
|
|
@ -87,31 +87,43 @@ class precharge(pgate.pgate):
|
|||
offset=vdd_pos.scale(0,1))
|
||||
|
||||
|
||||
def add_ptx(self):
|
||||
"""Adds both the upper_pmos and lower_pmos to the module"""
|
||||
def create_ptx(self):
|
||||
"""Create both the upper_pmos and lower_pmos to the module"""
|
||||
|
||||
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
|
||||
mod=self.pmos)
|
||||
self.connect_inst(["bl", "en", "br", "vdd"])
|
||||
|
||||
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
|
||||
mod=self.pmos)
|
||||
self.connect_inst(["bl", "en", "vdd", "vdd"])
|
||||
|
||||
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
|
||||
mod=self.pmos)
|
||||
self.connect_inst(["br", "en", "vdd", "vdd"])
|
||||
|
||||
|
||||
def place_ptx(self):
|
||||
"""Place both the upper_pmos and lower_pmos to the module"""
|
||||
|
||||
# Compute the other pmos2 location, but determining offset to overlap the
|
||||
# source and drain pins
|
||||
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
|
||||
|
||||
# adds the lower pmos to layout
|
||||
#base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0)
|
||||
self.lower_pmos_position = vector(self.bitcell.get_pin(self.bitcell_bl).lx(),
|
||||
self.pmos.active_offset.y)
|
||||
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
|
||||
mod=self.pmos,
|
||||
offset=self.lower_pmos_position)
|
||||
self.connect_inst(["bl", "en", "br", "vdd"])
|
||||
self.lower_pmos_inst.place(self.lower_pmos_position)
|
||||
|
||||
# adds the upper pmos(s) to layout
|
||||
ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width
|
||||
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
|
||||
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
|
||||
mod=self.pmos,
|
||||
offset=self.upper_pmos1_pos)
|
||||
self.connect_inst(["bl", "en", "vdd", "vdd"])
|
||||
self.upper_pmos1_inst.place(self.upper_pmos1_pos)
|
||||
|
||||
upper_pmos2_pos = self.upper_pmos1_pos + self.overlap_offset
|
||||
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
|
||||
mod=self.pmos,
|
||||
offset=upper_pmos2_pos)
|
||||
self.connect_inst(["br", "en", "vdd", "vdd"])
|
||||
|
||||
self.upper_pmos2_inst.place(upper_pmos2_pos)
|
||||
|
||||
def connect_poly(self):
|
||||
"""Connects the upper and lower pmos together"""
|
||||
|
||||
|
|
@ -131,7 +143,7 @@ class precharge(pgate.pgate):
|
|||
width=xlength,
|
||||
height=self.poly_width)
|
||||
|
||||
def add_en(self):
|
||||
def route_en(self):
|
||||
"""Adds the en input rail, en contact/vias, and connects to the pmos"""
|
||||
# adds the en contact to connect the gates to the en rail on metal1
|
||||
offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space)
|
||||
|
|
@ -146,7 +158,7 @@ class precharge(pgate.pgate):
|
|||
end=offset.scale(0,1)+vector(self.width,0))
|
||||
|
||||
|
||||
def add_nwell_and_contact(self):
|
||||
def place_nwell_and_contact(self):
|
||||
"""Adds a nwell tap to connect to the vdd rail"""
|
||||
# adds the contact from active to metal1
|
||||
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \
|
||||
|
|
@ -165,7 +177,7 @@ class precharge(pgate.pgate):
|
|||
height=self.height)
|
||||
|
||||
|
||||
def add_bitlines(self):
|
||||
def route_bitlines(self):
|
||||
"""Adds both bit-line and bit-line-bar to the module"""
|
||||
# adds the BL on metal 2
|
||||
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import debug
|
|||
from tech import drc, info, spice
|
||||
from vector import vector
|
||||
from contact import contact
|
||||
from globals import OPTS
|
||||
import path
|
||||
|
||||
class ptx(design.design):
|
||||
|
|
@ -40,13 +41,9 @@ class ptx(design.design):
|
|||
self.num_contacts = num_contacts
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
self.translate_all(self.active_offset)
|
||||
|
||||
# for run-time, we won't check every transitor DRC independently
|
||||
# but this may be uncommented for debug purposes
|
||||
#self.DRC()
|
||||
|
||||
|
||||
def create_layout(self):
|
||||
|
|
@ -56,6 +53,11 @@ class ptx(design.design):
|
|||
self.add_well_implant()
|
||||
self.add_poly()
|
||||
self.add_active_contacts()
|
||||
self.translate_all(self.active_offset)
|
||||
|
||||
# for run-time, we won't check every transitor DRC independently
|
||||
# but this may be uncommented for debug purposes
|
||||
#self.DRC()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pin_list(["D", "G", "S", "B"])
|
||||
|
|
|
|||
|
|
@ -17,21 +17,19 @@ class single_level_column_mux(design.design):
|
|||
design.design.__init__(self, name)
|
||||
debug.info(2, "create single column mux cell: {0}".format(name))
|
||||
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.ptx_width = tx_size * drc["minwidth_tx"]
|
||||
self.tx_size = tx_size
|
||||
|
||||
self.create_netlist()
|
||||
self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.add_ptx()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.pin_height = 2*self.m2_width
|
||||
self.width = self.bitcell.width
|
||||
self.height = self.nmos_upper.uy() + self.pin_height
|
||||
|
|
@ -39,7 +37,23 @@ class single_level_column_mux(design.design):
|
|||
self.add_bitline_pins()
|
||||
self.connect_bitlines()
|
||||
self.add_wells()
|
||||
|
||||
def add_modules(self):
|
||||
from importlib import reload
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
# Adds nmos_lower,nmos_upper to the module
|
||||
self.ptx_width = self.tx_size * drc["minwidth_tx"]
|
||||
self.nmos = ptx(width=self.ptx_width)
|
||||
self.add_mod(self.nmos)
|
||||
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
|
||||
|
||||
def add_bitline_pins(self):
|
||||
""" Add the top and bottom pins to this cell """
|
||||
|
||||
|
|
@ -70,10 +84,6 @@ class single_level_column_mux(design.design):
|
|||
def add_ptx(self):
|
||||
""" Create the two pass gate NMOS transistors to switch the bitlines"""
|
||||
|
||||
# Adds nmos_lower,nmos_upper to the module
|
||||
self.nmos = ptx(width=self.ptx_width)
|
||||
self.add_mod(self.nmos)
|
||||
|
||||
# Space it in the center
|
||||
nmos_lower_position = self.nmos.active_offset.scale(0,1) + vector(0.5*self.bitcell.width-0.5*self.nmos.active_width,0)
|
||||
self.nmos_lower=self.add_inst(name="mux_tx1",
|
||||
|
|
|
|||
|
|
@ -38,18 +38,9 @@ class sram():
|
|||
debug.error("Invalid number of banks.",-1)
|
||||
|
||||
self.s.create_netlist()
|
||||
self.s.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.s.create_layout()
|
||||
|
||||
# Can remove the following, but it helps for debug!
|
||||
self.s.add_lvs_correspondence_points()
|
||||
|
||||
self.s.offset_all_coordinates()
|
||||
highest_coord = self.s.find_highest_coords()
|
||||
self.s.width = highest_coord[0]
|
||||
self.s.height = highest_coord[1]
|
||||
|
||||
self.s.DRC_LVS(final_verification=True)
|
||||
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("SRAM creation", datetime.datetime.now(), start_time)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,18 +22,16 @@ class sram_1bank(sram_base):
|
|||
|
||||
def create_netlist(self):
|
||||
self.compute_sizes()
|
||||
self.add_modules()
|
||||
# Must run this after add modules to get control pin names
|
||||
self.add_pins()
|
||||
sram_base.create_netlist(self)
|
||||
self.create_modules()
|
||||
|
||||
|
||||
def create_modules(self):
|
||||
"""
|
||||
This adds the modules for a single bank SRAM with control
|
||||
logic.
|
||||
"""
|
||||
|
||||
self.bank_inst = self.create_bank()
|
||||
self.bank_inst=self.create_bank(0)
|
||||
|
||||
self.control_logic_inst = self.create_control_logic()
|
||||
|
||||
|
|
@ -43,7 +41,7 @@ class sram_1bank(sram_base):
|
|||
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
|
||||
|
|
@ -60,14 +58,12 @@ class sram_1bank(sram_base):
|
|||
# up to the row address DFFs.
|
||||
control_pos = vector(-self.control_logic.width - 2*self.m2_pitch,
|
||||
self.bank.bank_center.y - self.control_logic.control_logic_center.y)
|
||||
self.place_inst(name=self.control_logic_inst.name,
|
||||
offset=control_pos)
|
||||
self.control_logic_inst.place(control_pos)
|
||||
|
||||
# The row address bits are placed above the control logic aligned on the right.
|
||||
row_addr_pos = vector(self.control_logic_inst.rx() - self.row_addr_dff.width,
|
||||
self.control_logic_inst.uy())
|
||||
self.place_inst(name=self.row_addr_dff_inst.name,
|
||||
offset=row_addr_pos)
|
||||
self.row_addr_dff_inst.place(row_addr_pos)
|
||||
|
||||
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
||||
data_gap = -self.m2_pitch*(self.word_size+1)
|
||||
|
|
@ -77,8 +73,7 @@ class sram_1bank(sram_base):
|
|||
if self.col_addr_dff:
|
||||
col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width,
|
||||
data_gap - self.col_addr_dff.height)
|
||||
self.place_inst(name=self.col_addr_dff_inst.name,
|
||||
offset=col_addr_pos)
|
||||
self.col_addr_dff_inst.place(col_addr_pos)
|
||||
|
||||
# Add the data flops below the bank to the right of the center of bank:
|
||||
# This relies on the center point of the bank:
|
||||
|
|
@ -87,12 +82,11 @@ class sram_1bank(sram_base):
|
|||
# sense amps.
|
||||
data_pos = vector(self.bank.bank_center.x,
|
||||
data_gap - self.data_dff.height)
|
||||
self.place_inst(self.data_dff.name,
|
||||
offset=data_pos)
|
||||
self.data_dff_inst.place(data_pos)
|
||||
|
||||
# two supply rails are already included in the bank, so just 2 here.
|
||||
self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
|
||||
self.height = self.bank.height
|
||||
# self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
|
||||
# self.height = self.bank.height
|
||||
|
||||
def add_layout_pins(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -117,16 +117,31 @@ class sram_base(design):
|
|||
|
||||
|
||||
def create_netlist(self):
|
||||
""" Netlist creation """
|
||||
""" Netlist creation """
|
||||
|
||||
# Must create the control logic before pins to get the pins
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
|
||||
|
||||
# This is for the lib file if we don't create layout
|
||||
self.width=0
|
||||
self.height=0
|
||||
|
||||
def create_layout(self):
|
||||
""" Layout creation """
|
||||
self.place_modules()
|
||||
self.route()
|
||||
self.add_lvs_correspondence_points()
|
||||
|
||||
self.offset_all_coordinates()
|
||||
|
||||
highest_coord = self.find_highest_coords()
|
||||
self.width = highest_coord[0]
|
||||
self.height = highest_coord[1]
|
||||
|
||||
self.DRC_LVS(final_verification=True)
|
||||
|
||||
|
||||
def compute_bus_sizes(self):
|
||||
""" Compute the independent bus widths shared between two and four bank SRAMs """
|
||||
|
||||
|
|
@ -296,9 +311,8 @@ class sram_base(design):
|
|||
|
||||
|
||||
|
||||
def create_bank(self):
|
||||
def create_bank(self,bank_num):
|
||||
""" 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))
|
||||
|
||||
|
|
@ -341,8 +355,7 @@ class sram_base(design):
|
|||
else:
|
||||
bank_mirror = "R0"
|
||||
|
||||
self.place_inst(name=bank_inst.name,
|
||||
offset=position,
|
||||
bank_inst.place(offset=position,
|
||||
mirror=bank_mirror,
|
||||
rotate=bank_rotation)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python2.7
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Run regresion tests on a parameterized bitcell
|
||||
"""
|
||||
|
|
@ -13,9 +13,7 @@ import debug
|
|||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
#@unittest.skip("SKIPPING 04_pbitcell_test")
|
||||
|
||||
|
||||
@unittest.skip("SKIPPING 04_pbitcell_test")
|
||||
class pbitcell_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import globals
|
|||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
#@unittest.skip("SKIPPING 05_pbitcell_array_test")
|
||||
@unittest.skip("SKIPPING 05_pbitcell_array_test")
|
||||
class pbitcell_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import globals
|
|||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
@unittest.skip("Multiported Bank not working yet")
|
||||
class single_bank_test(openram_test):
|
||||
@unittest.skip("SKIPPING 19_psingle_bank_test")
|
||||
class psingle_bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ if not OPTS.check_lvsdrc:
|
|||
OPTS.pex_exe = None
|
||||
else:
|
||||
debug.info(1, "Finding DRC/LVS/PEX tools.")
|
||||
OPTS.drc_exe = get_tool("DRC",["calibre","assura","magic"])
|
||||
OPTS.lvs_exe = get_tool("LVS",["calibre","assura","netgen"])
|
||||
OPTS.pex_exe = get_tool("PEX",["calibre","magic"])
|
||||
OPTS.drc_exe = get_tool("DRC", ["calibre","assura","magic"], OPTS.drc_name)
|
||||
OPTS.lvs_exe = get_tool("LVS", ["calibre","assura","netgen"], OPTS.lvs_name)
|
||||
OPTS.pex_exe = get_tool("PEX", ["calibre","magic"], OPTS.pex_name)
|
||||
|
||||
if OPTS.check_lvsdrc and OPTS.tech_name == "freepdk45":
|
||||
debug.check(OPTS.drc_exe[0]!="magic","Magic does not support FreePDK45 for DRC.")
|
||||
|
|
|
|||
|
|
@ -1,59 +1,15 @@
|
|||
"""
|
||||
This is a DRC/LVS/PEX interface file for magic + netgen.
|
||||
|
||||
This assumes you have the SCMOS magic rules installed. Get these from:
|
||||
ftp://ftp.mosis.edu/pub/sondeen/magic/new/beta/current.tar.gz
|
||||
and install them in:
|
||||
cd /opt/local/lib/magic/sys
|
||||
tar zxvf current.tar.gz
|
||||
ln -s 2001a current
|
||||
|
||||
1. magic can perform drc with the following:
|
||||
#!/bin/sh
|
||||
magic -dnull -noconsole << EOF
|
||||
tech load SCN3ME_SUBM.30
|
||||
#scalegrid 1 2
|
||||
gds rescale no
|
||||
gds polygon subcell true
|
||||
gds warning default
|
||||
gds read $1
|
||||
load $1
|
||||
writeall force
|
||||
drc count
|
||||
drc why
|
||||
quit -noprompt
|
||||
EOF
|
||||
|
||||
2. magic can perform extraction with the following:
|
||||
#!/bin/sh
|
||||
rm -f $1.ext
|
||||
rm -f $1.spice
|
||||
magic -dnull -noconsole << EOF
|
||||
tech load SCN3ME_SUBM.30
|
||||
#scalegrid 1 2
|
||||
gds rescale no
|
||||
gds polygon subcell true
|
||||
gds warning default
|
||||
gds read $1
|
||||
extract
|
||||
ext2spice scale off
|
||||
ext2spice
|
||||
quit -noprompt
|
||||
EOF
|
||||
|
||||
3. netgen can perform LVS with:
|
||||
#!/bin/sh
|
||||
netgen -noconsole <<EOF
|
||||
readnet spice $1.spice
|
||||
readnet spice $1.sp
|
||||
ignore class c
|
||||
equate class {$1.spice nfet} {$2.sp n}
|
||||
equate class {$1.spice pfet} {$2.sp p}
|
||||
permute default
|
||||
compare hierarchical $1.spice {$1.sp $1}
|
||||
run converge
|
||||
EOF
|
||||
We include the tech file for SCN3ME_SUBM in the tech directory,
|
||||
that is included in OpenRAM during DRC.
|
||||
You can use this interactively by appending the magic system path in
|
||||
your .magicrc file
|
||||
path sys /Users/mrg/openram/technology/scn3me_subm/tech
|
||||
|
||||
We require the version 30 Magic rules which allow via stacking.
|
||||
We obtained this file from Qflow ( http://opencircuitdesign.com/qflow/index.html )
|
||||
and include its appropriate license.
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -300,6 +256,25 @@ def run_pex(name, gds_name, sp_name, output=None):
|
|||
run_drc(name, gds_name)
|
||||
run_lvs(name, gds_name, sp_name)
|
||||
|
||||
"""
|
||||
2. magic can perform extraction with the following:
|
||||
#!/bin/sh
|
||||
rm -f $1.ext
|
||||
rm -f $1.spice
|
||||
magic -dnull -noconsole << EOF
|
||||
tech load SCN3ME_SUBM.30
|
||||
#scalegrid 1 2
|
||||
gds rescale no
|
||||
gds polygon subcell true
|
||||
gds warning default
|
||||
gds read $1
|
||||
extract
|
||||
ext2spice scale off
|
||||
ext2spice
|
||||
quit -noprompt
|
||||
EOF
|
||||
"""
|
||||
|
||||
pex_rules = drc["xrc_rules"]
|
||||
pex_runset = {
|
||||
'pexRulesFile': pex_rules,
|
||||
|
|
|
|||
Loading…
Reference in New Issue