mirror of https://github.com/VLSIDA/OpenRAM.git
Development version of new pin data structure. Tests pass LVS/DRC except for bank level.
This commit is contained in:
parent
7aa42f9a00
commit
cf940fb15d
|
|
@ -1,3 +1,4 @@
|
|||
.DS_Store
|
||||
*~
|
||||
*.pyc
|
||||
*.log
|
||||
|
|
|
|||
1793
compiler/bank.py
1793
compiler/bank.py
File diff suppressed because it is too large
Load Diff
|
|
@ -11,15 +11,17 @@ class bitcell(design.design):
|
|||
library.
|
||||
"""
|
||||
|
||||
pins = ["BL", "BR", "WL", "vdd", "gnd"]
|
||||
chars = utils.auto_measure_libcell(pins, "cell_6t", GDS["unit"], layer["boundary"])
|
||||
pin_names = ["BL", "BR", "WL", "vdd", "gnd"]
|
||||
(width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"], layer["boundary"])
|
||||
|
||||
def __init__(self, name="cell_6t"):
|
||||
design.design.__init__(self, name)
|
||||
def __init__(self):
|
||||
design.design.__init__(self, "cell_6t")
|
||||
debug.info(2, "Create bitcell object")
|
||||
|
||||
self.width = bitcell.chars["width"]
|
||||
self.height = bitcell.chars["height"]
|
||||
self.width = bitcell.width
|
||||
self.height = bitcell.height
|
||||
self.pin_map = bitcell.pin_map
|
||||
|
||||
def delay(self, slew, load=0, swing = 0.5):
|
||||
# delay of bit cell is not like a driver(from WL)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class bitcell_array(design.design):
|
|||
Connects the word lines and bit lines.
|
||||
"""
|
||||
|
||||
def __init__(self, name, cols, rows):
|
||||
def __init__(self, cols, rows, name="bitcell_array"):
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
|
||||
|
||||
|
|
@ -23,11 +23,15 @@ class bitcell_array(design.design):
|
|||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
self.bitcell_chars = self.mod_bitcell.chars
|
||||
self.cell = self.mod_bitcell()
|
||||
self.add_mod(self.cell)
|
||||
|
||||
self.height = self.row_size*self.cell.height
|
||||
self.width = self.column_size*self.cell.width
|
||||
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
self.add_labels()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -40,26 +44,8 @@ class bitcell_array(design.design):
|
|||
self.add_pin("gnd")
|
||||
|
||||
def create_layout(self):
|
||||
self.create_cell()
|
||||
self.setup_layout_constants()
|
||||
self.add_cells()
|
||||
self.offset_all_coordinates()
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.vdd_positions = []
|
||||
self.gnd_positions = []
|
||||
self.BL_positions = []
|
||||
self.BR_positions = []
|
||||
self.WL_positions = []
|
||||
self.height = self.row_size * self.cell.height
|
||||
self.width = self.column_size * self.cell.width
|
||||
|
||||
def create_cell(self):
|
||||
self.cell = self.mod_bitcell()
|
||||
self.add_mod(self.cell)
|
||||
|
||||
def add_cells(self):
|
||||
xoffset = 0.0
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
yoffset = 0.0
|
||||
for row in range(self.row_size):
|
||||
|
|
@ -70,79 +56,110 @@ class bitcell_array(design.design):
|
|||
dir_key = "MX"
|
||||
else:
|
||||
tempy = yoffset
|
||||
dir_key = "R0"
|
||||
dir_key = ""
|
||||
|
||||
if OPTS.trim_noncritical == True:
|
||||
if row == self.row_size - 1:
|
||||
self.add_inst(name=name,
|
||||
mod=self.cell,
|
||||
offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
self.connect_inst(["bl[{0}]".format(col),
|
||||
"br[{0}]".format(col),
|
||||
"wl[{0}]".format(row),
|
||||
"vdd",
|
||||
"gnd"])
|
||||
else:
|
||||
self.add_inst(name=name,
|
||||
mod=self.cell,
|
||||
offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
self.connect_inst(["bl[{0}]".format(col),
|
||||
"br[{0}]".format(col),
|
||||
"wl[{0}]".format(row),
|
||||
"vdd",
|
||||
"gnd"])
|
||||
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||
mod=self.cell,
|
||||
offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
self.connect_inst(["bl[{0}]".format(col),
|
||||
"br[{0}]".format(col),
|
||||
"wl[{0}]".format(row),
|
||||
"vdd",
|
||||
"gnd"])
|
||||
yoffset += self.cell.height
|
||||
xoffset += self.cell.width
|
||||
|
||||
def add_labels(self):
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
# Our cells have multiple gnd pins for now.
|
||||
# FIXME: fix for multiple vdd too
|
||||
vdd_pin = self.cell.get_pin("vdd")
|
||||
|
||||
# shift it up by the overlap amount (gnd_pin) too
|
||||
# must find the lower gnd pin to determine this overlap
|
||||
lower_y = self.cell.height
|
||||
gnd_pins = self.cell.get_pins("gnd")
|
||||
for gnd_pin in gnd_pins:
|
||||
if gnd_pin.layer=="metal2" and gnd_pin.by()<lower_y:
|
||||
lower_y=gnd_pin.by()
|
||||
|
||||
# lower_y is negative, so subtract off double this amount for each pair of
|
||||
# overlapping cells
|
||||
full_height = self.height - 2*lower_y
|
||||
|
||||
vdd_pin = self.cell.get_pin("vdd")
|
||||
lower_x = vdd_pin.lx()
|
||||
# lower_x is negative, so subtract off double this amount for each pair of
|
||||
# overlapping cells
|
||||
full_width = self.width - 2*lower_x
|
||||
|
||||
offset = vector(0.0, 0.0)
|
||||
for col in range(self.column_size):
|
||||
offset.y = 0.0
|
||||
self.add_label(text="bl[{0}]".format(col),
|
||||
layer="metal2",
|
||||
offset=offset + vector(self.bitcell_chars["BL"][0],0))
|
||||
self.add_label(text="br[{0}]".format(col),
|
||||
layer="metal2",
|
||||
offset=offset + vector(self.bitcell_chars["BR"][0],0))
|
||||
self.BL_positions.append(offset + vector(self.bitcell_chars["BL"][0],0))
|
||||
self.BR_positions.append(offset + vector(self.bitcell_chars["BR"][0],0))
|
||||
# get the pin of the lower row cell and make it the full width
|
||||
bl_pin = self.cell_inst[0,col].get_pin("BL")
|
||||
br_pin = self.cell_inst[0,col].get_pin("BR")
|
||||
self.add_layout_pin(text="bl[{0}]".format(col),
|
||||
layer="metal2",
|
||||
offset=bl_pin.ll(),
|
||||
width=bl_pin.width(),
|
||||
height=full_height)
|
||||
self.add_layout_pin(text="br[{0}]".format(col),
|
||||
layer="metal2",
|
||||
offset=br_pin.ll(),
|
||||
width=br_pin.width(),
|
||||
height=full_height)
|
||||
|
||||
# gnd offset is 0 in our cell, but it be non-zero
|
||||
self.add_label(text="gnd",
|
||||
layer="metal2",
|
||||
offset=offset + vector(self.bitcell_chars["gnd"][0],0))
|
||||
self.gnd_positions.append(offset + vector(self.bitcell_chars["gnd"][0],0))
|
||||
|
||||
for row in range(self.row_size):
|
||||
# only add row labels on the left most column
|
||||
if col == 0:
|
||||
# flipped row
|
||||
if row % 2:
|
||||
base_offset = offset + vector(0, self.cell.height)
|
||||
vdd_offset = base_offset - vector(0,self.bitcell_chars["vdd"][1])
|
||||
wl_offset = base_offset - vector(0,self.bitcell_chars["WL"][1])
|
||||
# unflipped row
|
||||
else:
|
||||
vdd_offset = offset + vector(0,self.bitcell_chars["vdd"][1])
|
||||
wl_offset = offset + vector(0,self.bitcell_chars["WL"][1])
|
||||
# add vdd label and offset
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_offset)
|
||||
self.vdd_positions.append(vdd_offset)
|
||||
# add gnd label and offset
|
||||
self.add_label(text="wl[{0}]".format(row),
|
||||
layer="metal1",
|
||||
offset=wl_offset)
|
||||
self.WL_positions.append(wl_offset)
|
||||
|
||||
# increments to the next row height
|
||||
offset.y += self.cell.height
|
||||
gnd_pins = self.cell_inst[0,col].get_pins("gnd")
|
||||
for gnd_pin in gnd_pins:
|
||||
# avoid duplicates by only doing even rows
|
||||
# also skip if it is not the full height (a through rail)
|
||||
if gnd_pin.layer=="metal2" and col%2 == 0 and gnd_pin.height()>=self.cell.height:
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal2",
|
||||
offset=gnd_pin.ll(),
|
||||
width=gnd_pin.width(),
|
||||
height=full_height)
|
||||
|
||||
# increments to the next column width
|
||||
offset.x += self.cell.width
|
||||
|
||||
offset.x = 0.0
|
||||
for row in range(self.row_size):
|
||||
wl_pin = self.cell_inst[row,0].get_pin("WL")
|
||||
vdd_pins = self.cell_inst[row,0].get_pins("vdd")
|
||||
gnd_pins = self.cell_inst[row,0].get_pins("gnd")
|
||||
|
||||
for gnd_pin in gnd_pins:
|
||||
if gnd_pin.layer=="metal1":
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_pin.ll(),
|
||||
width=full_width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
# add vdd label and offset
|
||||
# only add to even rows to avoid duplicates
|
||||
for vdd_pin in vdd_pins:
|
||||
if row % 2 == 0 and vdd_pin.layer=="metal1":
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_pin.ll(),
|
||||
width=full_width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
# add wl label and offset
|
||||
self.add_layout_pin(text="wl[{0}]".format(row),
|
||||
layer="metal1",
|
||||
offset=wl_pin.ll(),
|
||||
width=full_width,
|
||||
height=wl_pin.height())
|
||||
|
||||
# increments to the next row height
|
||||
offset.y += self.cell.height
|
||||
|
||||
def delay(self, slew, load=0):
|
||||
from tech import drc
|
||||
wl_wire = self.gen_wl_wire()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ A calibre DRC runset file contains, at the minimum, the following information:
|
|||
*drcLayoutPrimary: cell_6t
|
||||
*drcLayoutSystem: GDSII
|
||||
*drcResultsformat: ASCII
|
||||
*drcResultsFile: cell_6t.drc.db
|
||||
*drcResultsFile: cell_6t.drc.results
|
||||
*drcSummaryFile: cell_6t.drc.summary
|
||||
*cmnFDILayerMapFile: ./layer.map
|
||||
*cmnFDIUseLayerMap: 1
|
||||
|
|
@ -28,7 +28,7 @@ To open the results, you can do this:
|
|||
|
||||
calibredrv cell_6t.gds
|
||||
Select Verification->Start RVE.
|
||||
Select the cell_6t.drc.db file.
|
||||
Select the cell_6t.drc.results file.
|
||||
Click on the errors and they will highlight in the design layout viewer.
|
||||
|
||||
For LVS:
|
||||
|
|
@ -44,7 +44,7 @@ For LVS:
|
|||
*lvsPowerNames: vdd
|
||||
*lvsGroundNames: vss
|
||||
*lvsIgnorePorts: 1
|
||||
*lvsERCDatabase: cell_6t.erc.db
|
||||
*lvsERCDatabase: cell_6t.erc.results
|
||||
*lvsERCSummaryFile: cell_6t.erc.summary
|
||||
*lvsReportFile: cell_6t.lvs.report
|
||||
*lvsMaskDBFile: cell_6t.maskdb
|
||||
|
|
@ -82,7 +82,7 @@ def run_drc(name, gds_name):
|
|||
'drcLayoutPrimary': name,
|
||||
'drcLayoutSystem': 'GDSII',
|
||||
'drcResultsformat': 'ASCII',
|
||||
'drcResultsFile': OPTS.openram_temp + name + ".drc.db",
|
||||
'drcResultsFile': OPTS.openram_temp + name + ".drc.results",
|
||||
'drcSummaryFile': OPTS.openram_temp + name + ".drc.summary",
|
||||
'cmnFDILayerMapFile': drc["layer_map"],
|
||||
'cmnFDIUseLayerMap': 1
|
||||
|
|
@ -153,7 +153,7 @@ def run_lvs(name, gds_name, sp_name):
|
|||
'lvsIncludeSVRFCmds': 1,
|
||||
'lvsSVRFCmds': '{VIRTUAL CONNECT NAME VDD? GND? ?}',
|
||||
'lvsIgnorePorts': 1,
|
||||
'lvsERCDatabase': OPTS.openram_temp + name + ".erc.db",
|
||||
'lvsERCDatabase': OPTS.openram_temp + name + ".erc.results",
|
||||
'lvsERCSummaryFile': OPTS.openram_temp + name + ".erc.summary",
|
||||
'lvsReportFile': OPTS.openram_temp + name + ".lvs.report",
|
||||
'lvsMaskDBFile': OPTS.openram_temp + name + ".maskdb",
|
||||
|
|
|
|||
|
|
@ -31,16 +31,11 @@ class contact(design.design):
|
|||
self.create_contact_array()
|
||||
self.create_first_layer_enclosure()
|
||||
self.create_second_layer_enclosure()
|
||||
self.offset_all_coordinates()
|
||||
|
||||
def offset_all_coordinates(self):
|
||||
coordinate = self.find_lowest_coords()
|
||||
self.offset_attributes(coordinate)
|
||||
self.translate(coordinate)
|
||||
|
||||
|
||||
self.height = max(obj.offset.y + obj.height for obj in self.objs)
|
||||
self.width = max(obj.offset.x + obj.width for obj in self.objs)
|
||||
|
||||
|
||||
def setup_layers(self):
|
||||
(first_layer, via_layer, second_layer) = self.layer_stack
|
||||
self.first_layer_name = first_layer
|
||||
|
|
@ -51,49 +46,50 @@ class contact(design.design):
|
|||
self.contact_width = drc["minwidth_{0}". format(self.via_layer_name)]
|
||||
self.contact_to_contact = drc["{0}_to_{0}".format(self.via_layer_name)]
|
||||
self.contact_pitch = self.contact_width + self.contact_to_contact
|
||||
self.contact_array_width = self.contact_width \
|
||||
+ (self.dimensions[0] - 1) * self.contact_pitch
|
||||
self.contact_array_height = self.contact_width \
|
||||
+ (self.dimensions[1] - 1) * self.contact_pitch
|
||||
self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch
|
||||
self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch
|
||||
|
||||
# FIME break this up
|
||||
self.first_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.first_layer_name)] - self.contact_array_width) / 2,
|
||||
drc["{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)])
|
||||
self.first_layer_vertical_enclosure = max((drc["minarea_{0}".format(self.first_layer_name)]
|
||||
/ (self.contact_array_width + 2 * self.first_layer_horizontal_enclosure) - self.contact_array_height) / 2,
|
||||
(drc["minheight_{0}".format(
|
||||
self.first_layer_name)] - self.contact_array_height) / 2,
|
||||
(drc["minheight_{0}".format(self.first_layer_name)] - self.contact_array_height) / 2,
|
||||
drc["{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)])
|
||||
|
||||
self.second_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.second_layer_name)] - self.contact_array_width) / 2,
|
||||
drc["{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)])
|
||||
self.second_layer_vertical_enclosure = max((drc["minarea_{0}".format(self.second_layer_name)]
|
||||
/ (self.contact_array_width + 2 * self.second_layer_horizontal_enclosure) - self.contact_array_height) / 2,
|
||||
(drc["minheight_{0}".format(
|
||||
self.second_layer_name)] - self.contact_array_height) / 2,
|
||||
(drc["minheight_{0}".format(self.second_layer_name)] - self.contact_array_height) / 2,
|
||||
drc["{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)])
|
||||
# offset for the via array
|
||||
self.via_layer_position =vector(max(self.first_layer_horizontal_enclosure,self.second_layer_horizontal_enclosure),
|
||||
max(self.first_layer_vertical_enclosure,self.second_layer_vertical_enclosure))
|
||||
# this is if the first and second layers are different
|
||||
self.first_layer_position = vector(max(self.second_layer_horizontal_enclosure - self.first_layer_horizontal_enclosure,0),
|
||||
max(self.second_layer_vertical_enclosure - self.first_layer_vertical_enclosure,0))
|
||||
self.second_layer_position = vector(max(self.first_layer_horizontal_enclosure - self.second_layer_horizontal_enclosure,0),
|
||||
max(self.first_layer_vertical_enclosure - self.second_layer_vertical_enclosure,0))
|
||||
|
||||
def create_contact_array(self):
|
||||
""" Create the contact array at the origin"""
|
||||
self.via_layer_position = vector(0, 0)
|
||||
for i in range(self.dimensions[1]):
|
||||
offset = [0, 0 + self.contact_pitch * i]
|
||||
offset = self.via_layer_position + vector(0, self.contact_pitch * i)
|
||||
for j in range(self.dimensions[0]):
|
||||
self.add_rect(layer=self.via_layer_name,
|
||||
offset=offset,
|
||||
width=self.contact_width,
|
||||
height=self.contact_width)
|
||||
offset = [offset[0] + self.contact_pitch, offset[1]]
|
||||
offset = offset + vector(self.contact_pitch,0)
|
||||
|
||||
def create_first_layer_enclosure(self):
|
||||
width = self.first_layer_width = self.contact_array_width \
|
||||
+ 2 * self.first_layer_horizontal_enclosure
|
||||
height = self.first_layer_height = self.contact_array_height \
|
||||
+ 2 * self.first_layer_vertical_enclosure
|
||||
offset = self.first_layer_position = vector(-self.first_layer_horizontal_enclosure,
|
||||
-self.first_layer_vertical_enclosure)
|
||||
self.add_rect(layer=self.first_layer_name,
|
||||
offset=offset,
|
||||
offset=self.first_layer_position,
|
||||
width=width,
|
||||
height=height)
|
||||
|
||||
|
|
@ -102,9 +98,7 @@ class contact(design.design):
|
|||
+ 2 * self.second_layer_horizontal_enclosure
|
||||
height = self.second_layer_height = self.contact_array_height \
|
||||
+ 2 * self.second_layer_vertical_enclosure
|
||||
offset = self.second_layer_position = vector(-self.second_layer_horizontal_enclosure,
|
||||
-self.second_layer_vertical_enclosure)
|
||||
self.add_rect(layer=self.second_layer_name,
|
||||
offset=offset,
|
||||
offset=self.second_layer_position,
|
||||
width=width,
|
||||
height=height)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,220 @@
|
|||
import debug
|
||||
import design
|
||||
from tech import drc
|
||||
from pinv import pinv
|
||||
from contact import contact
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
|
||||
class delay_chain(design.design):
|
||||
"""
|
||||
Generate a logic effort based delay chain.
|
||||
Input is a list contains the electrical effort of each stage.
|
||||
"""
|
||||
|
||||
def __init__(self, fanout_list, name="delay_chain"):
|
||||
"""init function"""
|
||||
design.design.__init__(self, name)
|
||||
# FIXME: input should be logic effort value
|
||||
# and there should be functions to get
|
||||
# area efficient inverter stage list
|
||||
|
||||
# number of inverters including any fanout loads.
|
||||
self.fanout_list = fanout_list
|
||||
self.num_inverters = 1 + sum(fanout_list)
|
||||
self.num_top_half = round(self.num_inverters / 2.0)
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.add_pins()
|
||||
self.create_module()
|
||||
self.route_inv()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
""" Add the pins of the delay chain"""
|
||||
self.add_pin("in")
|
||||
self.add_pin("out")
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_module(self):
|
||||
""" Add the inverter logical module """
|
||||
|
||||
self.create_inv_list()
|
||||
|
||||
self.inv = pinv(route_output=False)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
# half chain length is the width of the layout
|
||||
# invs are stacked into 2 levels so input/output are close
|
||||
# extra metal is for the gnd connection U
|
||||
self.width = self.num_top_half * self.inv.width + 2*drc["metal1_to_metal1"] + 0.5*drc["minwidth_metal1"]
|
||||
self.height = 2 * self.inv.height
|
||||
|
||||
self.add_inv_list()
|
||||
|
||||
def create_inv_list(self):
|
||||
"""
|
||||
Generate a list of inverters. Each inverter has a stage
|
||||
number and a flag indicating if it is a dummy load. This is
|
||||
the order that they will get placed too.
|
||||
"""
|
||||
# First stage is always 0 and is not a dummy load
|
||||
self.inv_list=[[0,False]]
|
||||
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
|
||||
for i in range(fanout_size-1):
|
||||
# Add the dummy loads
|
||||
self.inv_list.append([stage_num+1, True])
|
||||
|
||||
# Add the gate to drive the next stage
|
||||
self.inv_list.append([stage_num+1, False])
|
||||
|
||||
def add_inv_list(self):
|
||||
"""add the inverter and connect them based on the stage list """
|
||||
a_pin = self.inv.get_pin("A")
|
||||
dummy_load_counter = 1
|
||||
self.inv_inst_list = []
|
||||
for i in range(self.num_inverters):
|
||||
# First place the gates
|
||||
if i < self.num_top_half:
|
||||
# add top level that is upside down
|
||||
inv_offset = vector(i * self.inv.width, 2 * self.inv.height)
|
||||
inv_mirror="MX"
|
||||
via_offset = inv_offset + a_pin.ll().scale(1,-1)
|
||||
m1m2_via_rotate=270
|
||||
else:
|
||||
# add bottom level from right to left
|
||||
inv_offset = vector((self.num_inverters - i) * self.inv.width, 0)
|
||||
inv_mirror="MY"
|
||||
via_offset = inv_offset + a_pin.ll().scale(-1,1)
|
||||
m1m2_via_rotate=90
|
||||
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=via_offset,
|
||||
rotate=m1m2_via_rotate)
|
||||
cur_inv=self.add_inst(name="dinv{}".format(i),
|
||||
mod=self.inv,
|
||||
offset=inv_offset,
|
||||
mirror=inv_mirror)
|
||||
# keep track of the inverter instances so we can use them to get the pins
|
||||
self.inv_inst_list.append(cur_inv)
|
||||
|
||||
# Second connect them logically
|
||||
cur_stage = self.inv_list[i][0]
|
||||
next_stage = self.inv_list[i][0]+1
|
||||
if i == 0:
|
||||
input = "in"
|
||||
else:
|
||||
input = "s{}".format(cur_stage)
|
||||
if i == self.num_inverters-1:
|
||||
output = "out"
|
||||
else:
|
||||
output = "s{}".format(next_stage)
|
||||
|
||||
# if the gate is a dummy load don't connect the output
|
||||
# else reset the counter
|
||||
if self.inv_list[i][1]:
|
||||
output = output+"n{0}".format(dummy_load_counter)
|
||||
dummy_load_counter += 1
|
||||
else:
|
||||
dummy_load_counter = 1
|
||||
|
||||
self.connect_inst(args=[input, output, "vdd", "gnd"])
|
||||
|
||||
|
||||
|
||||
def route_inv(self):
|
||||
""" Add metal routing for each of the fanout stages """
|
||||
start_inv = end_inv = 0
|
||||
z_pin = self.inv.get_pin("Z")
|
||||
a_pin = self.inv.get_pin("A")
|
||||
for fanout in self.fanout_list:
|
||||
# end inv number depends on the fan out number
|
||||
end_inv = start_inv + fanout
|
||||
start_inv_offset = self.inv_inst_list[start_inv].offset
|
||||
end_inv_offset = self.inv_inst_list[end_inv].offset
|
||||
if start_inv < self.num_top_half:
|
||||
start_o_offset = start_inv_offset + z_pin.ll().scale(1,-1)
|
||||
m1m2_via_rotate = 270
|
||||
y_dir = -1
|
||||
else:
|
||||
start_o_offset = start_inv_offset + z_pin.ll().scale(-1,1)
|
||||
m1m2_via_rotate = 90
|
||||
y_dir = 1
|
||||
|
||||
M2_start = start_o_offset + vector(0,drc["minwidth_metal2"]).scale(1,y_dir*0.5)
|
||||
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=start_o_offset,
|
||||
rotate=m1m2_via_rotate)
|
||||
|
||||
if end_inv < self.num_top_half:
|
||||
end_i_offset = end_inv_offset + a_pin.ll().scale(1,-1)
|
||||
M2_end = end_i_offset - vector(0, 0.5 * drc["minwidth_metal2"])
|
||||
else:
|
||||
end_i_offset = end_inv_offset + a_pin.ll().scale(-1,1)
|
||||
M2_end = end_i_offset + vector(0, 0.5 * drc["minwidth_metal2"])
|
||||
|
||||
# We need a wire if the routing spans multiple rows
|
||||
if start_inv < self.num_top_half and end_inv >= self.num_top_half:
|
||||
mid = vector(self.num_top_half * self.inv.width - 0.5 * drc["minwidth_metal2"],
|
||||
M2_start[1])
|
||||
self.add_wire(("metal2", "via2", "metal3"),
|
||||
[M2_start, mid, M2_end])
|
||||
else:
|
||||
self.add_path(("metal2"), [M2_start, M2_end])
|
||||
# set the start of next one after current end
|
||||
start_inv = end_inv
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add vdd and gnd rails and the input/output. Connect the gnd rails internally on
|
||||
the top end with no input/output to obstruct. """
|
||||
vdd_pin = self.inv.get_pin("vdd")
|
||||
gnd_pin = self.inv.get_pin("gnd")
|
||||
for i in range(3):
|
||||
(offset,y_dir)=self.get_gate_offset(0, self.inv.height, i)
|
||||
rail_width = self.num_top_half * self.inv.width
|
||||
if i % 2:
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=offset + vdd_pin.ll().scale(1,y_dir),
|
||||
width=rail_width,
|
||||
height=drc["minwidth_metal1"])
|
||||
else:
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=offset + gnd_pin.ll().scale(1,y_dir),
|
||||
width=rail_width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
# Use the right most parts of the gnd rails and add a U connector
|
||||
# We still have the two gnd pins, but it is an either-or connect
|
||||
gnd_pins = self.get_pin("gnd")
|
||||
gnd_start = gnd_pins[0].rc()
|
||||
gnd_mid1 = gnd_start + vector(2*drc["metal1_to_metal1"],0)
|
||||
gnd_end = gnd_pins[1].rc()
|
||||
gnd_mid2 = gnd_end + vector(2*drc["metal1_to_metal1"],0)
|
||||
#self.add_wire(("metal1","via1","metal2"), [gnd_start, gnd_mid1, gnd_mid2, gnd_end])
|
||||
self.add_path("metal1", [gnd_start, gnd_mid1, gnd_mid2, gnd_end])
|
||||
|
||||
# input is A pin of first inverter
|
||||
a_pin = self.inv.get_pin("A")
|
||||
first_offset = self.inv_inst_list[0].offset
|
||||
self.add_layout_pin(text="in",
|
||||
layer="metal1",
|
||||
offset=first_offset+a_pin.ll().scale(1,-1) - vector(0,drc["minwidth_metal1"]))
|
||||
|
||||
|
||||
|
||||
# output is Z pin of last inverter
|
||||
z_pin = self.inv.get_pin("Z")
|
||||
self.add_layout_pin(text="out",
|
||||
layer="metal1",
|
||||
offset=z_pin.ll().scale(0,1),
|
||||
width=self.inv.width-z_pin.lx())
|
||||
|
||||
|
||||
|
|
@ -21,6 +21,7 @@ write_driver_array = "write_driver_array"
|
|||
tri_gate = "tri_gate"
|
||||
tri_gate_array = "tri_gate_array"
|
||||
wordline_driver = "wordline_driver"
|
||||
replica_bitline = "replica_bitline"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
bitcell = "bitcell"
|
||||
delay_chain = "logic_effort_dc"
|
||||
delay_chain = "delay_chain"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ write_driver_array = "write_driver_array"
|
|||
tri_gate = "tri_gate"
|
||||
tri_gate_array = "tri_gate_array"
|
||||
wordline_driver = "wordline_driver"
|
||||
replica_bitline = "replica_bitline"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
bitcell = "bitcell"
|
||||
delay_chain = "logic_effort_dc"
|
||||
delay_chain = "delay_chain"
|
||||
|
|
|
|||
|
|
@ -651,8 +651,9 @@ class VlsiLayout:
|
|||
|
||||
def getLabelDBInfo(self,label_name):
|
||||
"""
|
||||
Return the coordinates in DB units and layer of a label
|
||||
Return the coordinates in DB units and layer of all matching labels
|
||||
"""
|
||||
label_list = []
|
||||
label_layer = None
|
||||
label_coordinate = [None, None]
|
||||
|
||||
|
|
@ -661,18 +662,24 @@ class VlsiLayout:
|
|||
if Text.textString == label_name or Text.textString == label_name+"\x00":
|
||||
label_layer = Text.drawingLayer
|
||||
label_coordinate = Text.coordinates[0]
|
||||
if label_layer!=None:
|
||||
label_list.append((label_coordinate,label_layer))
|
||||
|
||||
debug.check(label_layer!=None,"Did not find label {0}.".format(label_name))
|
||||
return (label_coordinate, label_layer)
|
||||
debug.check(len(label_list)>0,"Did not find labels {0}.".format(label_name))
|
||||
return label_list
|
||||
|
||||
|
||||
def getLabelInfo(self,label_name):
|
||||
"""
|
||||
Return the coordinates in USER units and layer of a label
|
||||
"""
|
||||
(label_coordinate,label_layer)=self.getLabelDBInfo(label_name)
|
||||
user_coordinates = [x*self.units[0] for x in label_coordinate]
|
||||
return (user_coordinates,label_layer)
|
||||
label_list=self.getLabelDBInfo(label_name)
|
||||
new_list=[]
|
||||
for label in label_list:
|
||||
(label_coordinate,label_layer)=label
|
||||
user_coordinates = [x*self.units[0] for x in label_coordinate]
|
||||
new_list.append(user_coordinates,label_layer)
|
||||
return new_list
|
||||
|
||||
def getPinShapeByLocLayer(self, coordinate, layer):
|
||||
"""
|
||||
|
|
@ -729,16 +736,25 @@ class VlsiLayout:
|
|||
Search for a pin label and return the largest enclosing rectangle
|
||||
on the same layer as the pin label.
|
||||
"""
|
||||
(label_coordinate,label_layer)=self.getLabelDBInfo(label_name)
|
||||
return self.getPinShapeByDBLocLayer(label_coordinate, label_layer)
|
||||
label_list=self.getLabelDBInfo(label_name)
|
||||
shape_list=[]
|
||||
for label in label_list:
|
||||
(label_coordinate,label_layer)=label
|
||||
shape_list.append(self.getPinShapeByDBLocLayer(label_coordinate, label_layer))
|
||||
return shape_list
|
||||
|
||||
def getAllPinShapesByLabel(self,label_name):
|
||||
"""
|
||||
Search for a pin label and return ALL the enclosing rectangles on the same layer
|
||||
as the pin label.
|
||||
"""
|
||||
(label_coordinate,label_layer)=self.getLabelDBInfo(label_name)
|
||||
return self.getAllPinShapesByDBLocLayer(label_coordinate, label_layer)
|
||||
|
||||
label_list=self.getLabelDBInfo(label_name)
|
||||
shape_list=[]
|
||||
for label in label_list:
|
||||
(label_coordinate,label_layer)=label
|
||||
shape_list.append(self.getAllPinShapesByDBLocLayer(label_coordinate, label_layer))
|
||||
return shape_list
|
||||
|
||||
def getAllPinShapesInStructureList(self,coordinates,layer):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -32,13 +32,18 @@ class instance(geometry):
|
|||
def __init__(self, name, mod, offset, mirror, rotate):
|
||||
"""Initializes an instance to represent a module"""
|
||||
geometry.__init__(self)
|
||||
debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.")
|
||||
|
||||
self.name = name
|
||||
self.mod = mod
|
||||
self.gds = mod.gds
|
||||
self.rotate = rotate
|
||||
self.offset = vector(offset).snap_to_grid()
|
||||
self.mirror = mirror
|
||||
|
||||
|
||||
self.boundary = [vector(0,0),vector(mod.width,mod.height)]
|
||||
self.transform(offset,mirror,rotate)
|
||||
|
||||
debug.info(3, "creating instance: " + self.name)
|
||||
|
||||
def gds_write_file(self, newLayout):
|
||||
|
|
@ -53,6 +58,95 @@ class instance(geometry):
|
|||
mirror=self.mirror,
|
||||
rotate=self.rotate)
|
||||
|
||||
def normalize(self):
|
||||
""" Re-find the LL and UR points after a transform """
|
||||
(first,second)=self.boundary
|
||||
ll = vector(min(first[0],second[0]),min(first[1],second[1]))
|
||||
ur = vector(max(first[0],second[0]),max(first[1],second[1]))
|
||||
self.boundary=[ll,ur]
|
||||
|
||||
def transform(self,offset,mirror,rotate):
|
||||
""" 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. """
|
||||
(ll,ur) = self.boundary
|
||||
if mirror=="MX":
|
||||
ll=ll.scale(1,-1)
|
||||
ur=ur.scale(1,-1)
|
||||
elif mirror=="MY":
|
||||
ll=ll.scale(-1,1)
|
||||
ur=ur.scale(-1,1)
|
||||
elif mirror=="XY":
|
||||
ll=ll.scale(-1,-1)
|
||||
ur=ur.scale(-1,-1)
|
||||
|
||||
if rotate==90:
|
||||
ll=ll.rotate_scale(-1,1)
|
||||
ur=ur.rotate_scale(-1,1)
|
||||
elif rotate==180:
|
||||
ll=ll.scale(-1,-1)
|
||||
ur=ur.scale(-1,-1)
|
||||
elif rotate==270:
|
||||
ll=ll.rotate_scale(1,-1)
|
||||
ur=ur.rotate_scale(1,-1)
|
||||
|
||||
self.boundary=[offset+ll,offset+ur]
|
||||
self.normalize()
|
||||
|
||||
def ll(self):
|
||||
""" Return the lower left corner """
|
||||
return self.boundary[0]
|
||||
|
||||
def ur(self):
|
||||
""" Return the upper right corner """
|
||||
return self.boundary[1]
|
||||
|
||||
def lr(self):
|
||||
""" Return the lower right corner """
|
||||
return vector(self.boundary[1].x, self.boundary[0].y)
|
||||
|
||||
def ul(self):
|
||||
""" Return the upper left corner """
|
||||
return vector(self.boundary[0].x, self.boundary[1].y)
|
||||
|
||||
|
||||
def uy(self):
|
||||
""" Return the upper edge """
|
||||
return self.boundary[1].y
|
||||
|
||||
def by(self):
|
||||
""" Return the bottom edge """
|
||||
return self.boundary[0].y
|
||||
|
||||
def lx(self):
|
||||
""" Return the left edge """
|
||||
return self.boundary[0].x
|
||||
|
||||
def rx(self):
|
||||
""" Return the right edge """
|
||||
return self.boundary[1].x
|
||||
|
||||
def get_pin(self,name):
|
||||
""" Return an absolute pin that is offset and transformed based on
|
||||
this instance location. """
|
||||
|
||||
import copy
|
||||
pin = copy.deepcopy(self.mod.get_pin(name))
|
||||
pin.transform(self.offset,self.mirror,self.rotate)
|
||||
return pin
|
||||
|
||||
def get_pins(self,name):
|
||||
""" Return an absolute pin that is offset and transformed based on
|
||||
this instance location. """
|
||||
|
||||
import copy
|
||||
pin = copy.deepcopy(self.mod.get_pins(name))
|
||||
|
||||
new_pins = []
|
||||
for p in pin:
|
||||
p.transform(self.offset,self.mirror,self.rotate)
|
||||
new_pins.append(p)
|
||||
return new_pins
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
return "inst: " + self.name + " mod=" + self.mod.name
|
||||
|
|
|
|||
|
|
@ -18,76 +18,60 @@ class hierarchical_decoder(design.design):
|
|||
Dynamically generated hierarchical decoder.
|
||||
"""
|
||||
|
||||
def __init__(self, nand2_nmos_width, nand3_nmos_width, rows):
|
||||
def __init__(self, rows):
|
||||
design.design.__init__(self, "hierarchical_decoder_{0}rows".format(rows))
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
self.bitcell_height = self.mod_bitcell.chars["height"]
|
||||
self.bitcell_height = self.mod_bitcell.height
|
||||
|
||||
self.pre2x4_inst = []
|
||||
self.pre3x8_inst = []
|
||||
|
||||
self.rows = rows
|
||||
self.nand2_nmos_width = nand2_nmos_width
|
||||
self.nand3_nmos_width = nand3_nmos_width
|
||||
self.num_inputs = int(math.log(self.rows, 2))
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_layout(self):
|
||||
self.add_modules()
|
||||
self.setup_layout_offsets()
|
||||
self.setup_layout_constants()
|
||||
self.create_decoder()
|
||||
# We only need to call the offset_all_coordinate function when there
|
||||
# are vertical metal rails.
|
||||
if (self.num_inputs >= 4):
|
||||
self.offset_all_coordinates()
|
||||
|
||||
def create_decoder(self):
|
||||
self.add_pins()
|
||||
self.dimensions_hierarchy_decoder()
|
||||
self.create_pre_decoder()
|
||||
self.create_row_decoder()
|
||||
self.create_vertical_rail()
|
||||
self.route_vdd_gnd()
|
||||
# We only need to call the offset_all_coordinate function when there
|
||||
# are vertical metal rails.
|
||||
#if (self.num_inputs >= 4):
|
||||
# self.offset_all_coordinates()
|
||||
|
||||
def add_modules(self):
|
||||
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
|
||||
# Vertical metal rail gap definition
|
||||
self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2
|
||||
self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"]
|
||||
self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"]
|
||||
self.metal2_spacing = self.metal2_extend_contact + drc["metal2_to_metal2"]
|
||||
self.metal2_pitch = self.metal2_spacing + drc["minwidth_metal2"]
|
||||
self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2
|
||||
# used to shift contact when connecting to NAND3 C pin down
|
||||
self.contact_shift = (self.m1m2_via.first_layer_width - self.m1m2_via.contact_width) / 2
|
||||
|
||||
self.inv = pinv(nmos_width=drc["minwidth_tx"],
|
||||
beta=2,
|
||||
height=self.bitcell_height)
|
||||
self.inv = pinv()
|
||||
self.add_mod(self.inv)
|
||||
self.nand2 = nand_2(nmos_width=self.nand2_nmos_width,
|
||||
height=self.bitcell_height)
|
||||
self.nand2 = nand_2()
|
||||
self.add_mod(self.nand2)
|
||||
self.nand3 = nand_3(nmos_width=self.nand3_nmos_width,
|
||||
height=self.bitcell_height)
|
||||
self.nand3 = nand_3()
|
||||
self.add_mod(self.nand3)
|
||||
|
||||
# CREATION OF PRE-DECODER
|
||||
self.pre2_4 = pre2x4(self.nand2_nmos_width, "pre2x4")
|
||||
self.pre2_4 = pre2x4()
|
||||
self.add_mod(self.pre2_4)
|
||||
self.pre3_8 = pre3x8(self.nand3_nmos_width, "pre3x8")
|
||||
self.pre3_8 = pre3x8()
|
||||
self.add_mod(self.pre3_8)
|
||||
|
||||
def setup_layout_offsets(self):
|
||||
self.vdd_positions = []
|
||||
self.gnd_positions = []
|
||||
self.decode_out_positions = []
|
||||
self.A_positions = []
|
||||
|
||||
self.pre_decoder_vdd_positions = []
|
||||
self.pre_decoder_gnd_positions = []
|
||||
|
||||
def determine_predecodes(self,num_inputs):
|
||||
# Determines the number of 2:4 pre-decoder and 3:8 pre-decoder needed
|
||||
# based on the number of inputs
|
||||
"""Determines the number of 2:4 pre-decoder and 3:8 pre-decoder
|
||||
needed based on the number of inputs"""
|
||||
if (num_inputs == 2):
|
||||
return (1,0)
|
||||
elif (num_inputs == 3):
|
||||
|
|
@ -112,489 +96,444 @@ class hierarchical_decoder(design.design):
|
|||
self.no_of_pre2x4=p2x4
|
||||
self.no_of_pre3x8=p3x8
|
||||
|
||||
# Stromg the index of the vertical rails in different groups. These
|
||||
# vertical lines is used to connect pre-decoder to row-decoder
|
||||
self.predecoder_output_groups = [] # This array is a 2D array.
|
||||
self.group_sizes = []
|
||||
self.predec_groups = [] # This array is a 2D array.
|
||||
|
||||
# Distributing vertical rails to different groups. One group belongs to one pre-decoder.
|
||||
# For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will
|
||||
# have total 16 output lines out of these 3 pre-decoders and they will
|
||||
# be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
|
||||
# in self.predecoder_output_groups
|
||||
# in self.predec_groups
|
||||
index = 0
|
||||
for i in range(self.no_of_pre2x4):
|
||||
lines = []
|
||||
for j in range(4):
|
||||
lines.append(index)
|
||||
index = index + 1
|
||||
self.predecoder_output_groups.append(lines)
|
||||
self.group_sizes.append(4)
|
||||
self.predec_groups.append(lines)
|
||||
|
||||
for i in range(self.no_of_pre3x8):
|
||||
lines = []
|
||||
for j in range(8):
|
||||
lines.append(index)
|
||||
index = index + 1
|
||||
self.predecoder_output_groups.append(lines)
|
||||
self.group_sizes.append(8)
|
||||
self.predec_groups.append(lines)
|
||||
|
||||
self.calculate_dimensions()
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
""" Add the module pins """
|
||||
|
||||
for i in range(self.num_inputs):
|
||||
self.add_pin("A[{0}]".format(i))
|
||||
|
||||
if(self.num_inputs >= 4):
|
||||
for j in range(self.rows):
|
||||
self.add_pin("decode_out[{0}]".format(j))
|
||||
else:
|
||||
for j in range(self.rows):
|
||||
self.add_pin("out[{0}]".format(j))
|
||||
for j in range(self.rows):
|
||||
self.add_pin("decode[{0}]".format(j))
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def dimensions_hierarchy_decoder(self):
|
||||
self.total_number_of_predecoder_outputs = (4 * self.no_of_pre2x4
|
||||
+ 8 * self.no_of_pre3x8)
|
||||
def calculate_dimensions(self):
|
||||
""" Calculate the overal dimensions of the hierarchical decoder """
|
||||
|
||||
# If we have 4 or fewer rows, the predecoder is the decoder itself
|
||||
if self.num_inputs>=4:
|
||||
self.total_number_of_predecoder_outputs = 4*self.no_of_pre2x4 + 8*self.no_of_pre3x8
|
||||
else:
|
||||
self.total_number_of_predecoder_outputs = 0
|
||||
|
||||
# Calculates height and width of pre-decoder,
|
||||
if(self.no_of_pre3x8 > 0):
|
||||
self.predecoder_width = self.pre3_8.width
|
||||
else:
|
||||
self.predecoder_width = self.pre2_4.width
|
||||
self.predecoder_height = (self.pre2_4.height * self.no_of_pre2x4
|
||||
+ self.pre3_8.height * self.no_of_pre3x8)
|
||||
self.predecoder_height = self.pre2_4.height*self.no_of_pre2x4 + self.pre3_8.height*self.no_of_pre3x8
|
||||
|
||||
# Calculates height and width of row-decoder
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
nand_width = self.nand2.width
|
||||
else:
|
||||
nand_width = self.nand3.width
|
||||
total_gap = (self.gap_between_rail_offset
|
||||
* self.total_number_of_predecoder_outputs)
|
||||
self.row_decoder_width = (nand_width + total_gap
|
||||
+ self.inv.width)
|
||||
self.routing_width = self.metal2_pitch*self.total_number_of_predecoder_outputs
|
||||
self.row_decoder_width = nand_width + self.routing_width + self.inv.width
|
||||
self.row_decoder_height = self.inv.height * self.rows
|
||||
|
||||
# Calculates height and width of hierarchical decoder
|
||||
self.height = (self.predecoder_height
|
||||
+ self.row_decoder_height)
|
||||
self.width = self.predecoder_width + total_gap
|
||||
self.height = self.predecoder_height + self.row_decoder_height
|
||||
self.width = self.predecoder_width + self.routing_width
|
||||
|
||||
def create_pre_decoder(self):
|
||||
""" Creates pre-decoder and places labels input address [A] """
|
||||
|
||||
for i in range(self.no_of_pre2x4):
|
||||
self.add_pre2x4(i)
|
||||
self.add_lables_pre2x4(i)
|
||||
for j in range(self.no_of_pre3x8):
|
||||
pre3x8_yoffset=self.add_pre3x8(j)
|
||||
self.add_lables_pre3x8(j,pre3x8_yoffset)
|
||||
|
||||
for i in range(self.no_of_pre3x8):
|
||||
self.add_pre3x8(i)
|
||||
|
||||
def add_pre2x4(self,i):
|
||||
def add_pre2x4(self,num):
|
||||
""" Add a 2x4 predecoder """
|
||||
|
||||
if (self.num_inputs == 2):
|
||||
base = vector(0,0)
|
||||
mod_dir = vector(1,1)
|
||||
base = vector(self.routing_width,0)
|
||||
mirror = "RO"
|
||||
index_off1 = index_off2 = 0
|
||||
else:
|
||||
base= vector(self.pre2_4.width, i * self.pre2_4.height)
|
||||
mod_dir = vector(-1,1)
|
||||
base= vector(self.routing_width+self.pre2_4.width, num * self.pre2_4.height)
|
||||
mirror = "MY"
|
||||
index_off1 = i * 2
|
||||
index_off2 = i * 4
|
||||
index_off1 = num * 2
|
||||
index_off2 = num * 4
|
||||
|
||||
pins = []
|
||||
for input_index in range(2):
|
||||
pins.append("A[{0}]".format(input_index + index_off1))
|
||||
for output_index in range(4):
|
||||
pins.append("out[{0}]".format(output_index + index_off2))
|
||||
pins = pins + ["vdd", "gnd"]
|
||||
pins.extend(["vdd", "gnd"])
|
||||
|
||||
self.add_inst(name="pre[{0}]".format(i),
|
||||
mod=self.pre2_4,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num),
|
||||
mod=self.pre2_4,
|
||||
offset=base,
|
||||
mirror=mirror))
|
||||
self.connect_inst(pins)
|
||||
|
||||
vdd_offset = base + self.pre2_4.vdd_position.scale(mod_dir)
|
||||
self.pre_decoder_vdd_positions.append(vdd_offset)
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_offset)
|
||||
self.add_pre2x4_pins(num)
|
||||
|
||||
gnd_offset = base + self.pre2_4.gnd_position.scale(mod_dir)
|
||||
self.pre_decoder_gnd_positions.append(gnd_offset)
|
||||
self.add_label(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_offset)
|
||||
|
||||
|
||||
def add_lables_pre2x4(self,i):
|
||||
pre2_4_base = i * self.pre2_4.height
|
||||
# ADDING LABELS FOR INPUT SIDE OF THE 2:4 PRE-DECODER
|
||||
if (self.num_inputs == 2):
|
||||
xoffset = self.pre2_4.x_off_inv_1
|
||||
else:
|
||||
xoffset = self.pre2_4.width - self.pre2_4.x_off_inv_1
|
||||
for inv_2x4 in range(2):
|
||||
if (inv_2x4 % 2 == 0):
|
||||
pin_y = self.inv.A_position.y
|
||||
else:
|
||||
pin_y = (self.inv.height - self.inv.A_position.y
|
||||
- drc["metal1_to_metal1"])
|
||||
yoffset = pre2_4_base + inv_2x4 * self.inv.height + pin_y
|
||||
self.add_label(text="A[{0}]".format(inv_2x4 + i * 2),
|
||||
layer="metal1",
|
||||
offset=[xoffset, yoffset])
|
||||
self.A_positions.append(vector(xoffset, yoffset))
|
||||
def add_pre2x4_pins(self,num):
|
||||
""" Add the input pins to the 2x4 predecoder """
|
||||
|
||||
# ADDING LABELS FOR OUTPUT SIDE OF THE 2:4 PRE-DECODER
|
||||
for inv_2x4 in range(4):
|
||||
if (self.num_inputs == 2):
|
||||
xoffset = self.pre2_4.x_off_inv_2 + self.inv.Z_position.x
|
||||
else:
|
||||
xoffset = 0
|
||||
if (inv_2x4 % 2 == 0):
|
||||
pin_y = self.inv.Z_position.y
|
||||
else:
|
||||
pin_y = self.inv.height - self.inv.Z_position.y
|
||||
yoffset = pre2_4_base + inv_2x4 * self.inv.height + pin_y
|
||||
self.add_label(text="out[{0}]".format(inv_2x4 + i * 4),
|
||||
layer="metal1",
|
||||
offset=[xoffset, yoffset])
|
||||
for i in range(2):
|
||||
pin = self.pre2x4_inst[num].get_pin("in[{}]".format(i))
|
||||
pin_offset = pin.ll()
|
||||
|
||||
pin = self.pre2_4.get_pin("in[{}]".format(i))
|
||||
self.add_layout_pin(text="A[{0}]".format(i + 2*num ),
|
||||
layer="metal2",
|
||||
offset=pin_offset,
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
def add_pre3x8(self,j):
|
||||
|
||||
def add_pre3x8(self,num):
|
||||
""" Add 3x8 numbered predecoder """
|
||||
if (self.num_inputs == 3):
|
||||
offset = vector(0,0)
|
||||
offset = vector(self.routing_width,0)
|
||||
mirror ="R0"
|
||||
mod_dir = vector(1,1)
|
||||
index_off1 = index_off2 = 0
|
||||
else:
|
||||
offset = vector(self.pre3_8.width,
|
||||
self.no_of_pre2x4 * self.pre2_4.height
|
||||
+ j * self.pre3_8.height)
|
||||
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
|
||||
offset = vector(self.routing_width+self.pre3_8.width, height)
|
||||
mirror="MY"
|
||||
mod_dir = vector(-1,1)
|
||||
index_off1 = j * 3 + self.no_of_pre2x4 * 2
|
||||
index_off2 = j * 8 + self.no_of_pre2x4 * 4
|
||||
|
||||
# If we had 2x4 predecodes, those are used as the lower
|
||||
# decode output bits
|
||||
in_index_offset = num * 3 + self.no_of_pre2x4 * 2
|
||||
out_index_offset = num * 8 + self.no_of_pre2x4 * 4
|
||||
|
||||
pins = []
|
||||
for input_index in range(3):
|
||||
pins.append("A[{0}]".format(input_index + index_off1))
|
||||
pins.append("A[{0}]".format(input_index + in_index_offset))
|
||||
for output_index in range(8):
|
||||
pins.append("out[{0}]".format(output_index + index_off2))
|
||||
pins = pins + ["vdd", "gnd"]
|
||||
pins.append("out[{0}]".format(output_index + out_index_offset))
|
||||
pins.extend(["vdd", "gnd"])
|
||||
|
||||
self.add_inst(name="pre3x8[{0}]".format(j),
|
||||
mod=self.pre3_8,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num),
|
||||
mod=self.pre3_8,
|
||||
offset=offset,
|
||||
mirror=mirror))
|
||||
self.connect_inst(pins)
|
||||
|
||||
vdd_offset = offset + self.pre3_8.vdd_position.scale(mod_dir)
|
||||
self.pre_decoder_vdd_positions.append(vdd_offset)
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_offset)
|
||||
# The 3x8 predecoders will be stacked, so use yoffset
|
||||
self.add_pre3x8_pins(num,offset)
|
||||
|
||||
gnd_offset = offset + self.pre3_8.gnd_position.scale(mod_dir)
|
||||
self.pre_decoder_gnd_positions.append(gnd_offset)
|
||||
self.add_label(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_offset)
|
||||
return offset.y
|
||||
def add_pre3x8_pins(self,num,offset):
|
||||
""" Add the input pins to the 3x8 predecoder at the given offset """
|
||||
|
||||
def add_lables_pre3x8(self,j,pre3x8_yoffset):
|
||||
# ADDING LABELS FOR INPUT SIDE OF THE 3:8 PRE-DECODER
|
||||
if (self.num_inputs == 3):
|
||||
xoffset = self.pre3_8.x_off_inv_1
|
||||
else:
|
||||
xoffset = self.pre3_8.width - self.pre3_8.x_off_inv_1
|
||||
for inv_3x8 in range(3):
|
||||
if (inv_3x8 % 2 == 0):
|
||||
pin_y = self.inv.A_position.y
|
||||
else:
|
||||
pin_y = (self.inv.height - self.inv.Z_position.y
|
||||
-drc["minwidth_metal1"])
|
||||
yoffset = pre3x8_yoffset + inv_3x8 * (self.inv.height) + pin_y
|
||||
A_index = self.no_of_pre2x4 * 2 + inv_3x8 + j * 3
|
||||
self.add_label(text="A[{0}]".format(A_index),
|
||||
layer="metal1",
|
||||
offset=[xoffset, yoffset])
|
||||
self.A_positions.append(vector(xoffset, yoffset))
|
||||
for i in range(3):
|
||||
pin = self.pre3x8_inst[num].get_pin("in[{}]".format(i))
|
||||
pin_offset = pin.ll()
|
||||
self.add_layout_pin(text="A[{0}]".format(i + 3*num + 2*self.no_of_pre2x4),
|
||||
layer="metal2",
|
||||
offset=pin_offset,
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
# ADDING LABELS FOR OUTPUT SIDE OF THE 3:8 PRE-DECODER
|
||||
for inv_3x8 in range(8):
|
||||
if (self.num_inputs == 3):
|
||||
xoffset = self.pre3_8.x_off_inv_2 + self.inv.Z_position.x
|
||||
else:
|
||||
xoffset = 0
|
||||
|
||||
if (inv_3x8 % 2 == 0):
|
||||
pin_y = self.inv.Z_position.y
|
||||
else:
|
||||
pin_y = self.inv.height - self.inv.Z_position.y
|
||||
yoffset = pre3x8_yoffset + inv_3x8 * self.inv.height + pin_y
|
||||
out_index = self.no_of_pre2x4 * 4 + inv_3x8 + j * 8
|
||||
self.add_label(text="out[{0}]".format(out_index),
|
||||
layer="metal1",
|
||||
offset=[xoffset, yoffset])
|
||||
|
||||
def create_row_decoder(self):
|
||||
# Create the row-decoder using NAND2/NAND3 and Inverter and places the
|
||||
# output labels [out/decode_out]
|
||||
""" Create the row-decoder by placing NAND2/NAND3 and Inverters
|
||||
and add the primary decoder output pins. """
|
||||
if (self.num_inputs >= 4):
|
||||
self.add_decoder_nand_array_and_labels()
|
||||
self.add_decoder_inv_array_and_labels()
|
||||
self.add_decoder_nand_array()
|
||||
self.add_decoder_inv_array_and_pins()
|
||||
|
||||
def add_decoder_nand_array_and_labels(self):
|
||||
def add_decoder_nand_array(self):
|
||||
""" Add a column of NAND gates for final decode """
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs <5.
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
nand = self.nand2
|
||||
correct = 0
|
||||
nand_name ="NAND2"
|
||||
self.add_nand_array(nand,correct,nand_name)
|
||||
self.add_nand_array(nand_mod=self.nand2)
|
||||
# FIXME: Can we convert this to the connect_inst with checks?
|
||||
for i in range(self.group_sizes[0]):
|
||||
for j in range(self.group_sizes[1]):
|
||||
for i in range(len(self.predec_groups[0])):
|
||||
for j in range(len(self.predec_groups[1])):
|
||||
pins =["out[{0}]".format(i),
|
||||
"out[{0}]".format(j + self.group_sizes[0]),
|
||||
"Z[{0}]".format(self.group_sizes[1] * i + j),
|
||||
"out[{0}]".format(j + len(self.predec_groups[0])),
|
||||
"Z[{0}]".format(len(self.predec_groups[1])*i + j),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(args=pins, check=False)
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs >5.
|
||||
elif (self.num_inputs > 5):
|
||||
nand = self.nand3
|
||||
correct = drc["minwidth_metal1"]
|
||||
nand_name ="NAND3"
|
||||
self.add_nand_array(nand,correct,nand_name)
|
||||
self.add_nand_array(nand_mod=self.nand3,
|
||||
correct=drc["minwidth_metal1"])
|
||||
# This will not check that the inst connections match.
|
||||
for i in range(self.group_sizes[0]):
|
||||
for j in range(self.group_sizes[1]):
|
||||
for k in range(self.group_sizes[2]):
|
||||
Z_index = (self.group_sizes[1] * self.group_sizes[2]* i
|
||||
+ self.group_sizes[2] * j + k)
|
||||
for i in range(len(self.predec_groups[0])):
|
||||
for j in range(len(self.predec_groups[1])):
|
||||
for k in range(len(self.predec_groups[2])):
|
||||
Z_index = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \
|
||||
+ len(self.predec_groups[2])*j + k
|
||||
pins = ["out[{0}]".format(i),
|
||||
"out[{0}]".format(j + self.group_sizes[0]),
|
||||
"out[{0}]".format(k + self.group_sizes[0] + self.group_sizes[1]),
|
||||
"out[{0}]".format(j + len(self.predec_groups[0])),
|
||||
"out[{0}]".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
||||
"Z[{0}]".format(Z_index),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(args=pins, check=False)
|
||||
|
||||
def add_nand_array(self,nand,correct,nand_name):
|
||||
def add_nand_array(self, nand_mod, correct=0):
|
||||
""" Add a column of NAND gates for the decoder above the predecoders."""
|
||||
|
||||
z_pin = nand_mod.get_pin("Z")
|
||||
a_pin = self.inv.get_pin("A")
|
||||
rect_height = z_pin.uy()-a_pin.by()
|
||||
|
||||
for row in range(self.rows):
|
||||
name = nand_name+"_[{0}]".format(row)
|
||||
name = "DEC_NAND[{0}]".format(row)
|
||||
if ((row % 2) == 0):
|
||||
y_off = self.predecoder_height + (nand.height) * (row)
|
||||
y_off = self.predecoder_height + nand_mod.height*row
|
||||
y_dir = 1
|
||||
mirror = "R0"
|
||||
rect_offset = vector(self.routing_width + nand_mod.width, y_off + z_pin.uy() - rect_height)
|
||||
|
||||
else:
|
||||
y_off = self.predecoder_height + (nand.height) * (row + 1)
|
||||
y_dir = - 1
|
||||
y_off = self.predecoder_height + nand_mod.height*(row + 1)
|
||||
y_dir = -1
|
||||
mirror = "MX"
|
||||
rect_offset =vector(self.routing_width + nand_mod.width, y_off - z_pin.uy())
|
||||
|
||||
self.add_inst(name=name,
|
||||
mod=nand,
|
||||
offset=[0, y_off],
|
||||
mod=nand_mod,
|
||||
offset=[self.routing_width, y_off],
|
||||
mirror=mirror)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[nand.width - correct,
|
||||
y_off + y_dir * (nand.Z_position.y-correct)],
|
||||
width=drc["minwidth_metal1"],
|
||||
height=y_dir * drc["minwidth_metal1"])
|
||||
|
||||
def add_decoder_inv_array_and_labels(self):
|
||||
# Row Decoder INVERTER array insts.
|
||||
self.add_rect(layer="metal1",
|
||||
offset=rect_offset,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=rect_height)
|
||||
|
||||
|
||||
def add_decoder_inv_array_and_pins(self):
|
||||
"""Add a column of INV gates for the decoder above the predecoders
|
||||
and to the right of the NAND decoders."""
|
||||
|
||||
z_pin = self.inv.get_pin("Z")
|
||||
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
x_off = self.nand2.width
|
||||
x_off = self.routing_width + self.nand2.width
|
||||
else:
|
||||
x_off = self.nand3.width
|
||||
x_off = self.routing_width + self.nand3.width
|
||||
|
||||
for row in range(self.rows):
|
||||
name = "INVERTER_[{0}]".format(row)
|
||||
if ((row % 2) == 0):
|
||||
name = "DEC_INV_[{0}]".format(row)
|
||||
if (row % 2 == 0):
|
||||
inv_row_height = self.inv.height * row
|
||||
mirror = "R0"
|
||||
y_dir = 1
|
||||
else:
|
||||
inv_row_height = self.inv.height * (row + 1)
|
||||
mirror = "MX"
|
||||
y_dir = -1
|
||||
y_off = self.predecoder_height + inv_row_height
|
||||
|
||||
offset = vector(x_off,y_off)
|
||||
|
||||
self.add_inst(name=name,
|
||||
mod=self.inv,
|
||||
offset=[x_off, y_off],
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
# This will not check that the inst connections match.
|
||||
self.connect_inst(args=["Z[{0}]".format(row),
|
||||
"decode_out[{0}]".format(row),
|
||||
"decode[{0}]".format(row),
|
||||
"vdd", "gnd"],
|
||||
check=False)
|
||||
|
||||
# add vdd and gnd label
|
||||
for row in range(self.rows):
|
||||
if ((row % 2) == 0):
|
||||
offset = vector(0, self.predecoder_height + row*(self.inv.height))
|
||||
vdd_offset = offset + self.inv.vdd_position.scale(0,1)
|
||||
gnd_offset = offset + self.inv.gnd_position.scale(0,1)
|
||||
else:
|
||||
offset = vector(0, self.predecoder_height + (row+1)*(self.inv.height))
|
||||
vdd_offset = offset + self.inv.vdd_position.scale(0, -1)
|
||||
gnd_offset = offset + self.inv.gnd_position.scale(0, -1)
|
||||
self.vdd_positions.append(vdd_offset)
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_offset)
|
||||
self.gnd_positions.append(gnd_offset)
|
||||
self.add_label(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_offset)
|
||||
self.add_layout_pin(text="decode[{0}]".format(row),
|
||||
layer="metal1",
|
||||
offset=offset+z_pin.ll().scale(1,y_dir),
|
||||
width=z_pin.width(),
|
||||
height=y_dir*z_pin.height())
|
||||
|
||||
# add output label for Row Decoder INVERTER array.
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
x_off = self.nand2.width + self.inv.Z_position.x
|
||||
else:
|
||||
x_off = self.nand3.width + self.inv.Z_position.x
|
||||
|
||||
for row in range(self.rows):
|
||||
if ((row % 2) == 0):
|
||||
pin_y = row * self.inv.height + self.inv.Z_position.y
|
||||
else:
|
||||
pin_y = (row+1)*self.inv.height - self.inv.Z_position.y
|
||||
y_off = self.predecoder_height + pin_y
|
||||
|
||||
self.add_label(text="decode_out[{0}]".format(row),
|
||||
layer="metal1",
|
||||
offset=[x_off, y_off])
|
||||
self.decode_out_positions.append(vector(x_off, y_off))
|
||||
|
||||
def create_vertical_rail(self):
|
||||
# VERTICAL METAL RAILS TO CONNECT PREDECODER TO DECODE STAGE
|
||||
""" Creates vertical metal 2 rails to connect predecoder and decoder stages."""
|
||||
|
||||
# This is not needed for inputs <4 since they have no pre/decode stages.
|
||||
if (self.num_inputs >= 4):
|
||||
# Array for saving the X offsets of the vertical rails. These rail
|
||||
# offsets are accessed with indices.
|
||||
vertical_rail_x_offsets = []
|
||||
self.rail_x_offsets = []
|
||||
for i in range(self.total_number_of_predecoder_outputs):
|
||||
vertical_rail_x_offsets.append(-self.gap_between_rail_offset \
|
||||
* (self.total_number_of_predecoder_outputs - i))
|
||||
# The offsets go into the negative x direction
|
||||
# assuming the predecodes are placed at (self.routing_width,0)
|
||||
x_offset = self.metal2_pitch * i
|
||||
self.rail_x_offsets.append(x_offset)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[-self.gap_between_rail_offset * (i + 1),
|
||||
0],
|
||||
offset=vector(x_offset,0),
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
# Horizontal metal extensions from pre-decoder 2x4ouput.
|
||||
for i in range(self.no_of_pre2x4):
|
||||
self.extend_horizontal_to_pre2x4(i,vertical_rail_x_offsets)
|
||||
self.connect_rails_to_predecodes()
|
||||
self.connect_rails_to_decoder()
|
||||
|
||||
# Horizontal metal extensions from pre-decoder 3x8 ouput.
|
||||
for i in range(self.no_of_pre3x8):
|
||||
self.extend_horizontal_to_pre3x8(i,vertical_rail_x_offsets)
|
||||
def connect_rails_to_predecodes(self):
|
||||
""" Iterates through all of the predecodes and connects to the rails including the offsets """
|
||||
|
||||
self.connect_vertial_rails_to_decoder(vertical_rail_x_offsets)
|
||||
for i in range(self.no_of_pre2x4):
|
||||
self.connect_rails_to_pre2x4(i)
|
||||
|
||||
for i in range(self.no_of_pre3x8):
|
||||
self.connect_rails_to_pre3x8(i)
|
||||
|
||||
def extend_horizontal_to_pre2x4(self, output_index, vertical_rail_x_offsets):
|
||||
for inv_2x4 in range(4):
|
||||
line_index = output_index * 4 + inv_2x4
|
||||
current_inv_height = (output_index * self.pre2_4.height
|
||||
+ inv_2x4 * (self.inv.height))
|
||||
|
||||
if (inv_2x4 % 2 == 0):
|
||||
pin_y = self.inv.Z_position.y
|
||||
def connect_rails_to_pre2x4(self, predecode_num):
|
||||
""" Connects the 2x4 predecoder outputs to the vertical rails """
|
||||
|
||||
z_pin = self.inv.get_pin("Z")
|
||||
pin = z_pin.ll()
|
||||
for i in range(4):
|
||||
index = predecode_num * 4 + i
|
||||
current_inv_height = predecode_num*self.pre2_4.height + i*self.inv.height
|
||||
|
||||
if (i % 2 == 0):
|
||||
pin_y = pin.y
|
||||
else:
|
||||
pin_y = (self.inv.height - drc["minwidth_metal1"]
|
||||
- self.inv.Z_position.y)
|
||||
yoffset = current_inv_height + pin_y
|
||||
pin_y = self.inv.height - drc["minwidth_metal1"] - pin.y
|
||||
|
||||
self.add_extend_rails(yoffset = yoffset,
|
||||
xoffset = vertical_rail_x_offsets[line_index])
|
||||
self.connect_rail(vector(self.rail_x_offsets[index], current_inv_height + pin_y))
|
||||
|
||||
def connect_rails_to_pre3x8(self, predecode_num):
|
||||
""" Connects the 3x8 predecoder outputs to the vertical rails """
|
||||
|
||||
def extend_horizontal_to_pre3x8(self, output_index, vertical_rail_x_offsets):
|
||||
for inv_3x8 in range(8):
|
||||
line_index = output_index * 8 + inv_3x8 + self.no_of_pre2x4 * 4
|
||||
current_inv_height = output_index * (self.pre3_8.height) \
|
||||
+ inv_3x8 * (self.inv.height) \
|
||||
+ self.no_of_pre2x4 * self.pre2_4.height
|
||||
z_pin = self.inv.get_pin("Z")
|
||||
pin = z_pin.ll()
|
||||
for i in range(8):
|
||||
index = predecode_num * 8 + i + self.no_of_pre2x4 * 4
|
||||
current_inv_height = predecode_num*self.pre3_8.height \
|
||||
+ i*self.inv.height \
|
||||
+ self.no_of_pre2x4*self.pre2_4.height
|
||||
|
||||
if (inv_3x8 % 2 == 0):
|
||||
pin_y = self.inv.Z_position.y
|
||||
if (i % 2 == 0):
|
||||
pin_y = pin.y
|
||||
else:
|
||||
pin_y = (self.inv.height - drc["minwidth_metal1"]
|
||||
- self.inv.Z_position.y)
|
||||
yoffset = current_inv_height + pin_y
|
||||
pin_y = self.inv.height - drc["minwidth_metal1"] - pin.y
|
||||
|
||||
self.connect_rail(vector(self.rail_x_offsets[index], current_inv_height + pin_y))
|
||||
|
||||
self.add_extend_rails(yoffset = yoffset,
|
||||
xoffset = vertical_rail_x_offsets[line_index])
|
||||
|
||||
def connect_vertial_rails_to_decoder(self, vertical_rail_x_offsets):
|
||||
# METAL CONNECTION FROM THE VERTICAL RAIL TOWARDS THE DECODER.
|
||||
# PRE-DECODER OUTPUT ARE CONNECTED TO THIS SAME RAIL ALSO
|
||||
# To makes these connections groups of line index that was stored in
|
||||
# self.predecoder_output_groups are used
|
||||
# Inputs of NAND2/NAND3 gates come from diffrent groups.
|
||||
# For example for these groups [ [0,1,2,3] ,[4,5,6,7],
|
||||
# [8,9,10,11,12,13,14,15] ] the first NAND3 inputs are connected to
|
||||
# [0,4,8] and second NAND3 is connected to [0,4,9] ........... and the
|
||||
# 128th NAND3 is connected to [3,7,15]
|
||||
def connect_rails_to_decoder(self):
|
||||
""" Use the self.predec_groups to determine the connections to the decoder NAND gates.
|
||||
Inputs of NAND2/NAND3 gates come from different groups.
|
||||
For example for these groups [ [0,1,2,3] ,[4,5,6,7],
|
||||
[8,9,10,11,12,13,14,15] ] the first NAND3 inputs are connected to
|
||||
[0,4,8] and second NAND3 is connected to [0,4,9] ........... and the
|
||||
128th NAND3 is connected to [3,7,15]
|
||||
"""
|
||||
row_index = 0
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
for line_index_A in self.predecoder_output_groups[0]:
|
||||
for line_index_B in self.predecoder_output_groups[1]:
|
||||
a_pin = self.nand2.get_pin("A")
|
||||
b_pin = self.nand2.get_pin("B")
|
||||
|
||||
for index_A in self.predec_groups[0]:
|
||||
for index_B in self.predec_groups[1]:
|
||||
|
||||
current_inv_height = self.predecoder_height + row_index * (self.inv.height)
|
||||
current_inv_height = self.predecoder_height + row_index*self.inv.height
|
||||
if (row_index % 2 == 0):
|
||||
yoffset_A = current_inv_height + self.nand2.A_position.y
|
||||
yoffset_B = current_inv_height + self.nand2.B_position.y
|
||||
yoffset_A = current_inv_height + a_pin.by()
|
||||
yoffset_B = current_inv_height + b_pin.by()
|
||||
|
||||
else:
|
||||
base = current_inv_height + self.inv.height - drc["minwidth_metal1"]
|
||||
yoffset_A = base - self.nand2.A_position.y
|
||||
yoffset_B = base - self.nand2.B_position.y
|
||||
yoffset_A = base - a_pin.by()
|
||||
yoffset_B = base - b_pin.by()
|
||||
|
||||
row_index = row_index + 1
|
||||
self.add_extend_rails(yoffset =yoffset_A,
|
||||
xoffset =vertical_rail_x_offsets[line_index_A])
|
||||
self.add_extend_rails(yoffset =yoffset_B,
|
||||
xoffset =vertical_rail_x_offsets[line_index_B])
|
||||
self.connect_rail(vector(self.rail_x_offsets[index_A], yoffset_A))
|
||||
self.connect_rail(vector(self.rail_x_offsets[index_B], yoffset_B))
|
||||
|
||||
elif (self.num_inputs > 5):
|
||||
for line_index_A in self.predecoder_output_groups[0]:
|
||||
for line_index_B in self.predecoder_output_groups[1]:
|
||||
for line_index_C in self.predecoder_output_groups[2]:
|
||||
a_pin = self.nand3.get_pin("A")
|
||||
b_pin = self.nand3.get_pin("B")
|
||||
c_pin = self.nand3.get_pin("C")
|
||||
|
||||
for index_A in self.predec_groups[0]:
|
||||
for index_B in self.predec_groups[1]:
|
||||
for index_C in self.predec_groups[2]:
|
||||
|
||||
current_inv_height = self.predecoder_height + row_index * (self.inv.height)
|
||||
current_inv_height = self.predecoder_height + row_index*self.inv.height
|
||||
|
||||
if (row_index % 2 == 0):
|
||||
yoffset_A = current_inv_height + self.nand3.A_position.y
|
||||
yoffset_B = current_inv_height + self.nand3.B_position.y
|
||||
yoffset_C = current_inv_height + self.nand3.C_position.y
|
||||
yoffset_A = current_inv_height + a_pin.by()
|
||||
yoffset_B = current_inv_height + b_pin.by()
|
||||
yoffset_C = current_inv_height + c_pin.by()
|
||||
contact_C_yoffset = yoffset_C - self.contact_shift
|
||||
else:
|
||||
base = current_inv_height + self.inv.height - drc["minwidth_metal1"]
|
||||
yoffset_A = base - self.nand3.A_position.y
|
||||
yoffset_B = base - self.nand3.B_position.y
|
||||
yoffset_C = base - self.nand3.C_position.y
|
||||
yoffset_A = base - a_pin.by()
|
||||
yoffset_B = base - b_pin.by()
|
||||
yoffset_C = base - c_pin.by()
|
||||
contact_C_yoffset = yoffset_C
|
||||
|
||||
row_index = row_index + 1
|
||||
|
||||
self.add_extend_rails(yoffset =yoffset_A,
|
||||
xoffset =vertical_rail_x_offsets[line_index_A])
|
||||
self.add_extend_rails(yoffset =yoffset_B,
|
||||
xoffset =vertical_rail_x_offsets[line_index_B])
|
||||
self.add_extend_rails(yoffset =yoffset_C,
|
||||
xoffset =vertical_rail_x_offsets[line_index_C],
|
||||
contact_yoffset = contact_C_yoffset)
|
||||
self.connect_rail(vector(self.rail_x_offsets[index_A], yoffset_A))
|
||||
self.connect_rail(vector(self.rail_x_offsets[index_B], yoffset_B))
|
||||
self.connect_rail(vector(self.rail_x_offsets[index_C], yoffset_C)) # contact_C_y_offset
|
||||
|
||||
def add_extend_rails(self, yoffset, xoffset, contact_yoffset=0):
|
||||
def route_vdd_gnd(self):
|
||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||
|
||||
for num in range(0,self.total_number_of_predecoder_outputs + self.rows):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
# use the inverter offset even though it will be the nand's too
|
||||
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
|
||||
# route vdd
|
||||
vdd_offset = gate_offset + self.inv.get_pin("vdd").ll().scale(1,y_dir)
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_offset,
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
# route gnd
|
||||
gnd_offset = gate_offset+self.inv.get_pin("gnd").ll().scale(1,y_dir)
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_offset,
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
|
||||
def connect_rail(self, offset,contact_yoffset=0):
|
||||
""" Adds a via at location and extends to self.routing_width """
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[xoffset, yoffset],
|
||||
width=-xoffset,
|
||||
offset=offset,
|
||||
width=self.routing_width-offset.x,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
if contact_yoffset!=0:
|
||||
yoffset = contact_yoffset
|
||||
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[xoffset + self.gap_between_rails,
|
||||
yoffset - self.via_shift],
|
||||
offset=offset + vector(self.metal2_spacing,-self.via_shift),
|
||||
rotate=90)
|
||||
|
||||
def delay(self, slew, load = 0.0):
|
||||
|
|
|
|||
|
|
@ -6,212 +6,254 @@ from contact import contact
|
|||
from pinv import pinv
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from nand_2 import nand_2
|
||||
from nand_3 import nand_3
|
||||
|
||||
|
||||
class hierarchical_predecode(design.design):
|
||||
"""
|
||||
Pre 2x4 and 3x8 decoder shared code.
|
||||
"""
|
||||
def __init__(self, nmos_width, cellname, input_number):
|
||||
design.design.__init__(self, cellname)
|
||||
def __init__(self, input_number):
|
||||
self.number_of_inputs = input_number
|
||||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||
design.design.__init__(self, name="pre{0}x{1}".format(self.number_of_inputs,self.number_of_outputs))
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
self.bitcell_height = self.mod_bitcell.chars["height"]
|
||||
self.bitcell_height = self.mod_bitcell.height
|
||||
|
||||
self.nmos_width = nmos_width
|
||||
self.number_of_inputs = input_number
|
||||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||
|
||||
def add_pins(self):
|
||||
for k in range(self.number_of_inputs):
|
||||
self.add_pin("A[{0}]".format(k))
|
||||
self.add_pin("in[{0}]".format(k))
|
||||
for i in range(self.number_of_outputs):
|
||||
self.add_pin("out[{0}]".format(i))
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_modules(self):
|
||||
layer_stack = ("metal1", "via1", "metal2")
|
||||
self.m1m2_via = contact(layer_stack=layer_stack)
|
||||
self.inv = pinv(nmos_width=drc["minwidth_tx"],
|
||||
beta=2,
|
||||
height=self.bitcell_height)
|
||||
""" Create the INV and NAND gate """
|
||||
|
||||
self.inv = pinv()
|
||||
self.add_mod(self.inv)
|
||||
# create_nand redefine in sub class based on number of inputs
|
||||
self.create_nand()
|
||||
|
||||
self.create_nand(self.number_of_inputs)
|
||||
self.add_mod(self.nand)
|
||||
|
||||
def set_up_constrain(self):
|
||||
self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2
|
||||
self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2
|
||||
self.via_shift = (self.m1m2_via.second_layer_width
|
||||
- self.m1m2_via.first_layer_width) / 2
|
||||
self.metal2_extend_contact = (self.m1m2_via.second_layer_height
|
||||
- self.m1m2_via.contact_width) / 2
|
||||
def create_nand(self,inputs):
|
||||
""" Create the NAND for the predecode input stage """
|
||||
if inputs==2:
|
||||
self.nand = nand_2()
|
||||
elif inputs==3:
|
||||
self.nand = nand_3()
|
||||
else:
|
||||
debug.error("Invalid number of predecode inputs.",-1)
|
||||
|
||||
def setup_constraints(self):
|
||||
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
|
||||
self.metal2_space = drc["metal2_to_metal2"]
|
||||
self.metal1_space = drc["metal1_to_metal1"]
|
||||
self.metal2_width = drc["minwidth_metal2"]
|
||||
self.metal1_width = drc["minwidth_metal1"]
|
||||
# we are going to use horizontal vias, so use the via height
|
||||
# use a conservative douple spacing just to get rid of annoying via DRCs
|
||||
self.metal2_pitch = self.m1m2_via.height + 2*self.metal2_space
|
||||
# This is to shift the rotated vias to be on m2 pitch
|
||||
self.via_x_shift = self.m1m2_via.height + self.m1m2_via.via_layer_position.scale(0,-1).y
|
||||
# This is to shift the via if the metal1 and metal2 overlaps are different
|
||||
self.via_y_shift = self.m1m2_via.second_layer_position.x - self.m1m2_via.first_layer_position.x + self.m1m2_via.via_layer_position.scale(-0.5,0).x
|
||||
|
||||
# The rail offsets are indexed by the label
|
||||
self.rails = {}
|
||||
|
||||
self.gap_between_rails = (self.metal2_extend_contact
|
||||
+ drc["metal2_to_metal2"])
|
||||
self.gap_between_rail_offset = (self.gap_between_rails
|
||||
+ drc["minwidth_metal2"])
|
||||
|
||||
def setup_constrains(self):
|
||||
self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2
|
||||
self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2
|
||||
|
||||
# Contact shift used connecting NAND3 inputs to the rail
|
||||
self.contact_shift = (self.m1m2_via.first_layer_width - self.m1m2_via.contact_width) / 2
|
||||
|
||||
self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"]
|
||||
self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"]
|
||||
|
||||
self.rails_x_offset = []
|
||||
# set_rail_height redefine in sub class
|
||||
self.set_rail_height()
|
||||
# Creating the left hand side metal2 rails for input connections
|
||||
for hrail_1 in range(self.number_of_inputs):
|
||||
xoffset_1 = (self.metal2_extend_contact
|
||||
+ (hrail_1 * self.gap_between_rail_offset))
|
||||
self.rails_x_offset.append(xoffset_1)
|
||||
# x offset for Xpre2x4_inv
|
||||
self.x_off_inv_1 = self.rails_x_offset[-1] + self.gap_between_rail_offset
|
||||
# Non inverted input rails
|
||||
for rail_index in range(self.number_of_inputs):
|
||||
xoffset = rail_index * self.metal2_pitch
|
||||
self.rails["in[{}]".format(rail_index)]=xoffset
|
||||
# x offset for input inverters
|
||||
self.x_off_inv_1 = self.number_of_inputs*self.metal2_pitch
|
||||
|
||||
# Creating the right hand side metal2 rails for output connections
|
||||
for hrail_2 in range(2 * self.number_of_inputs + 2):
|
||||
xoffset_2 = self.x_off_inv_1 + self.inv.width + self.gap_between_rails + (hrail_2 * self.gap_between_rail_offset)
|
||||
self.rails_x_offset.append(xoffset_2)
|
||||
self.xoffset_2=self.rails_x_offset[-1]
|
||||
for rail_index in range(2 * self.number_of_inputs):
|
||||
xoffset = self.x_off_inv_1 + self.inv.width + ((rail_index+1) * self.metal2_pitch)
|
||||
if rail_index < self.number_of_inputs:
|
||||
self.rails["Abar[{}]".format(rail_index)]=xoffset
|
||||
else:
|
||||
self.rails["A[{}]".format(rail_index-self.number_of_inputs)]=xoffset
|
||||
|
||||
self.x_off_nand = self.xoffset_2 + self.gap_between_rail_offset
|
||||
# x offset to NAND decoder includes the left rails, mid rails and inverters, plus an extra m2 pitch
|
||||
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (1 + 2*self.number_of_inputs) * self.metal2_pitch
|
||||
|
||||
|
||||
# x offset to output inverters
|
||||
self.x_off_inv_2 = self.x_off_nand + self.nand.width
|
||||
self.update_size()
|
||||
|
||||
def update_size(self):
|
||||
# Height width are computed
|
||||
self.width = self.x_off_inv_2 + self.inv.width
|
||||
self.set_height()
|
||||
self.size = vector(self.width, self.height)
|
||||
correct =vector(0, 0.5 * drc["minwidth_metal1"])
|
||||
self.vdd_position = self.size - correct - vector(0, self.inv.height)
|
||||
self.gnd_position = self.size - correct
|
||||
self.height = self.number_of_outputs * self.nand.height
|
||||
|
||||
def create_rails(self):
|
||||
for x_off in self.rails_x_offset:
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[x_off, 0],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.rail_height)
|
||||
|
||||
def add_output_inverters(self):
|
||||
self.decode_out_positions = []
|
||||
for inv_2x4 in range(self.number_of_outputs):
|
||||
name = "Xpre2x4_nand_inv[{0}]".format(inv_2x4)
|
||||
if (inv_2x4 % 2 == 0):
|
||||
y_factor = inv_2x4
|
||||
mirror = "R0"
|
||||
correct = self.inv.Z_position
|
||||
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
||||
for label in self.rails.keys():
|
||||
# these are not primary inputs, so they shouldn't have a
|
||||
# label or LVS complains about different names on one net
|
||||
if label.startswith("in"):
|
||||
self.add_layout_pin(text=label,
|
||||
layer="metal2",
|
||||
offset=[self.rails[label], 0],
|
||||
width=self.metal2_width,
|
||||
height=self.height - drc["metal2_to_metal2"])
|
||||
else:
|
||||
y_factor =inv_2x4 + 1
|
||||
mirror = "MX"
|
||||
correct = self.inv.Z_position.scale(1,-1) - vector(0,
|
||||
drc["minwidth_metal1"])
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[self.rails[label], 0],
|
||||
width=self.metal2_width,
|
||||
height=self.height - drc["metal2_to_metal2"])
|
||||
|
||||
def add_input_inverters(self):
|
||||
""" Create 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)
|
||||
offset = vector(self.x_off_inv_1, y_off)
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off = (inv_num + 1) * (self.inv.height)
|
||||
offset = vector(self.x_off_inv_1, y_off)
|
||||
mirror="MX"
|
||||
self.add_inst(name=name,
|
||||
mod=self.inv,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["in[{0}]".format(inv_num),
|
||||
"inbar[{0}]".format(inv_num),
|
||||
"vdd", "gnd"])
|
||||
|
||||
def add_output_inverters(self):
|
||||
""" Create inverters for the inverted output decode signals. """
|
||||
|
||||
self.decode_out_positions = []
|
||||
z_pin = self.inv.get_pin("Z")
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
name = "Xpre2x4_nand_inv[{}]".format(inv_num)
|
||||
if (inv_num % 2 == 0):
|
||||
y_factor = inv_num
|
||||
mirror = "R0"
|
||||
y_dir = 1
|
||||
else:
|
||||
y_factor =inv_num + 1
|
||||
mirror = "MX"
|
||||
y_dir = -1
|
||||
base = vector(self.x_off_inv_2, self.inv.height * y_factor)
|
||||
self.add_inst(name=name,
|
||||
mod=self.inv,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["Z[{0}]".format(inv_2x4),
|
||||
"out[{0}]".format(inv_2x4),
|
||||
self.connect_inst(["Z[{}]".format(inv_num),
|
||||
"out[{}]".format(inv_num),
|
||||
"vdd", "gnd"])
|
||||
output_inv_out_offset = base + correct
|
||||
self.decode_out_positions.append(output_inv_out_offset)
|
||||
|
||||
z_pin = self.inv.get_pin("Z")
|
||||
self.add_layout_pin(text="out[{}]".format(inv_num),
|
||||
layer="metal1",
|
||||
offset=base+z_pin.ll().scale(1,y_dir),
|
||||
width=z_pin.width(),
|
||||
height=z_pin.height()*y_dir)
|
||||
|
||||
|
||||
def add_nand(self,connections):
|
||||
""" Create the NAND stage for the decodes """
|
||||
z_pin = self.nand.get_pin("Z")
|
||||
a_pin = self.inv.get_pin("A")
|
||||
for nand_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
|
||||
name = "Xpre"+inout+"_nand[{0}]".format(nand_input)
|
||||
name = "Xpre{0}_nand[{1}]".format(inout,nand_input)
|
||||
rect_height = z_pin.uy()-a_pin.by()
|
||||
if (nand_input % 2 == 0):
|
||||
y_off = nand_input * (self.nand.height)
|
||||
mirror = "R0"
|
||||
offset = [self.x_off_nand + self.nand.width,
|
||||
y_off + self.nand.Z_position.y]
|
||||
rect_offset = vector(self.x_off_nand + self.nand.width,
|
||||
y_off + z_pin.uy() - rect_height)
|
||||
else:
|
||||
y_off = (nand_input + 1) * (self.nand.height)
|
||||
mirror = "MX"
|
||||
offset =[self.x_off_nand + self.nand.width,
|
||||
y_off - self.nand.Z_position.y - drc["minwidth_metal1"]]
|
||||
rect_offset =vector(self.x_off_nand + self.nand.width,
|
||||
y_off - z_pin.uy())
|
||||
self.add_inst(name=name,
|
||||
mod=self.nand,
|
||||
offset=[self.x_off_nand, y_off],
|
||||
mirror=mirror)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=drc["minwidth_metal1"])
|
||||
offset=rect_offset,
|
||||
width=self.metal1_width,
|
||||
height=rect_height)
|
||||
self.connect_inst(connections[nand_input])
|
||||
|
||||
def route(self):
|
||||
self.route_input_inverters()
|
||||
self.route_inputs_to_rails()
|
||||
self.route_nand_to_rails()
|
||||
self.route_vdd_gnd_from_rails_to_gates()
|
||||
self.route_vdd_gnd()
|
||||
|
||||
def route_input_inverters(self):
|
||||
# All conections of the inputs inverters [Inputs, outputs, vdd, gnd]
|
||||
output_shift = self.set_output_shift()
|
||||
for inv_rout in range(self.number_of_inputs):
|
||||
setup = self.setup_route_input_inverter(inv_rout,output_shift)
|
||||
y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset = setup
|
||||
#add output
|
||||
correct = y_dir * (output_shift + drc["minwidth_metal1"])
|
||||
output_metal = self.cal_input_inverters_output(setup,output_shift,inv_rout)
|
||||
offset1,offset2=output_metal[0]
|
||||
offset3,offset4=output_metal[1]
|
||||
def route_inputs_to_rails(self):
|
||||
""" Route the uninverted inputs to the second set of rails """
|
||||
for num in range(self.number_of_inputs):
|
||||
# route one signal next to each vdd/gnd rail since this is
|
||||
# typically where the p/n devices are and there are no
|
||||
# pins in the nand gates.
|
||||
y_offset = (num+self.number_of_inputs) * self.inv.height + 2*self.metal1_space
|
||||
in_pin = "in[{}]".format(num)
|
||||
a_pin = "A[{}]".format(num)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset1,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=offset2.y - offset1.y)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset3,
|
||||
width=offset4.x - offset3.x,
|
||||
height=drc["minwidth_metal1"])
|
||||
off_via = [self.rails_x_offset[inv_rout + self.number_of_inputs+2] + self.gap_between_rails,
|
||||
inv_vdd_offset.y- self.via_shift - correct]
|
||||
offset=[self.rails[in_pin],y_offset],
|
||||
width=self.rails[a_pin] + self.metal2_width - self.rails[in_pin],
|
||||
height=self.metal1_width)
|
||||
self.add_via(layers = ("metal1", "via1", "metal2"),
|
||||
offset=off_via,
|
||||
offset=[self.rails[in_pin] + self.via_x_shift, y_offset + self.via_y_shift],
|
||||
rotate=90)
|
||||
#route input
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[self.rails_x_offset[inv_rout],
|
||||
inv_in_offset.y],
|
||||
width=inv_in_offset.x - self.rails_x_offset[inv_rout] + drc["minwidth_metal2"],
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[self.rails_x_offset[inv_rout] + self.gap_between_rails,
|
||||
inv_in_offset.y - self.via_shift],
|
||||
self.add_via(layers = ("metal1", "via1", "metal2"),
|
||||
offset=[self.rails[a_pin] + self.via_x_shift, y_offset + self.via_y_shift],
|
||||
rotate=90)
|
||||
# route vdd
|
||||
|
||||
def route_input_inverters(self):
|
||||
"""
|
||||
Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd]
|
||||
"""
|
||||
for inv_num in range(self.number_of_inputs):
|
||||
(inv_offset, y_dir) = self.get_gate_offset(self.x_off_inv_1, self.inv.height, inv_num)
|
||||
|
||||
out_pin = "Abar[{}]".format(inv_num)
|
||||
in_pin = "in[{}]".format(inv_num)
|
||||
|
||||
#add output so that it is just below the vdd or gnd rail
|
||||
# since this is where the p/n devices are and there are no
|
||||
# pins in the nand gates.
|
||||
y_offset = (inv_num+1) * self.inv.height - 3*self.metal1_space
|
||||
inv_out_offset = inv_offset+self.inv.get_pin("Z").ur().scale(1,y_dir)-vector(0,self.metal1_width).scale(1,y_dir)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=inv_vdd_offset,
|
||||
width=self.rails_x_offset[self.number_of_inputs] - inv_vdd_offset.x + drc["minwidth_metal2"],
|
||||
height=drc["minwidth_metal1"])
|
||||
# route gnd
|
||||
offset=[inv_out_offset.x,y_offset],
|
||||
width=self.rails[out_pin]-inv_out_offset.x + self.metal2_width,
|
||||
height=self.metal1_width)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=inv_gnd_offset,
|
||||
width=self.rails_x_offset[self.number_of_inputs+1] - inv_gnd_offset.x + drc["minwidth_metal2"],
|
||||
height=drc["minwidth_metal1"])
|
||||
offset=inv_out_offset,
|
||||
width=self.metal1_width,
|
||||
height=y_offset-inv_out_offset.y)
|
||||
self.add_via(layers = ("metal1", "via1", "metal2"),
|
||||
offset=[self.rails[out_pin] + self.via_x_shift, y_offset + self.via_y_shift],
|
||||
rotate=90)
|
||||
|
||||
def setup_route_input_inverter(self, inv_rout, output_shift):
|
||||
# add Inputs, vdd, gnd of the inputs inverters
|
||||
if (inv_rout % 2 == 0):
|
||||
base_offset=[self.x_off_inv_1, inv_rout * self.inv.height ]
|
||||
y_dir = 1
|
||||
else:
|
||||
base_offset=[self.x_off_inv_1, 2 * self.inv.height - drc["minwidth_metal1"]]
|
||||
y_dir = -1
|
||||
inv_out_offset = base_offset+self.inv.Z_position.scale(1,y_dir)
|
||||
inv_in_offset = base_offset+self.inv.A_position.scale(1,y_dir)
|
||||
inv_vdd_offset = base_offset+self.inv.vdd_position.scale(1,y_dir)
|
||||
inv_gnd_offset = base_offset+self.inv.gnd_position.scale(1,y_dir)
|
||||
#return info to create output of the input inverter
|
||||
return [y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset]
|
||||
|
||||
#route input
|
||||
inv_in_offset = inv_offset+self.inv.get_pin("A").ll().scale(1,y_dir)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[self.rails[in_pin], inv_in_offset.y],
|
||||
width=inv_in_offset.x - self.rails[in_pin],
|
||||
height=self.metal1_width)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[self.rails[in_pin] + self.via_x_shift, inv_in_offset.y + self.via_y_shift],
|
||||
rotate=90)
|
||||
|
||||
|
||||
def route_nand_to_rails(self):
|
||||
# This 2D array defines the connection mapping
|
||||
|
|
@ -219,65 +261,50 @@ class hierarchical_predecode(design.design):
|
|||
for k in range(self.number_of_outputs):
|
||||
# create x offset list
|
||||
index_lst= nand_input_line_combination[k]
|
||||
line_x_offset = []
|
||||
for index in index_lst:
|
||||
line_x_offset.append(self.rails_x_offset[index])
|
||||
# create y offset list
|
||||
yoffset_nand_in, correct= self.create_y_offsets(k)
|
||||
# connect based on the two list
|
||||
for i in range(self.number_of_inputs):
|
||||
x_offset = line_x_offset[i]
|
||||
y_offset = yoffset_nand_in[i]
|
||||
# Connecting the i-th input of Nand3 gate
|
||||
(nand_offset,y_dir) = self.get_gate_offset(self.x_off_nand,self.nand.height,k)
|
||||
|
||||
if self.number_of_inputs == 2:
|
||||
gate_lst = ["A","B"]
|
||||
else:
|
||||
gate_lst = ["A","B","C"]
|
||||
|
||||
# this will connect pins A,B or A,B,C
|
||||
for rail_pin,gate_pin in zip(index_lst,gate_lst):
|
||||
pin_offset = nand_offset+self.nand.get_pin(gate_pin).ll().scale(1,y_dir)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[x_offset, y_offset],
|
||||
width=self.x_off_nand - x_offset,
|
||||
height=drc["minwidth_metal1"])
|
||||
offset=[self.rails[rail_pin], pin_offset.y],
|
||||
width=pin_offset.x - self.rails[rail_pin],
|
||||
height=self.metal1_width)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[x_offset+ self.gap_between_rails,
|
||||
y_offset - self.via_shift - correct[i]],
|
||||
rotate=90)
|
||||
# Extended of the top NAND2 to the left hand side input rails
|
||||
if(k == self.number_of_outputs - 1):
|
||||
x_offset = self.rails_x_offset[i]
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[x_offset, y_offset],
|
||||
width=self.x_off_nand - x_offset,
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_via(layers = ("metal1", "via1", "metal2"),
|
||||
offset=[x_offset + self.gap_between_rails,
|
||||
y_offset - self.via_shift],
|
||||
rotate=90)
|
||||
offset=[self.rails[rail_pin] + self.via_x_shift, pin_offset.y + self.via_y_shift],
|
||||
rotate=90)
|
||||
|
||||
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||
|
||||
for num in range(0,self.number_of_outputs):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
# use the inverter offset even though it will be the nand's too
|
||||
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
|
||||
|
||||
# route vdd
|
||||
vdd_offset = gate_offset + self.inv.get_pin("vdd").ll().scale(1,y_dir)
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_offset,
|
||||
width=self.x_off_inv_2 + self.inv.width + self.metal2_width,
|
||||
height=self.metal1_width)
|
||||
|
||||
# route gnd
|
||||
gnd_offset = gate_offset+self.inv.get_pin("gnd").ll().scale(1,y_dir)
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_offset,
|
||||
width=self.x_off_inv_2 + self.inv.width + self.metal2_width,
|
||||
height=self.metal1_width)
|
||||
|
||||
|
||||
def route_vdd_gnd_from_rails_to_gates(self):
|
||||
via_correct = self.get_via_correct()
|
||||
for k in range(self.number_of_outputs):
|
||||
power_line_index = self.number_of_inputs + 1 - (k%2)
|
||||
yoffset = k * self.inv.height - 0.5 * drc["minwidth_metal1"]
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[self.rails_x_offset[power_line_index],
|
||||
yoffset],
|
||||
width=self.x_off_nand - self.rails_x_offset[power_line_index],
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_via(layers = ("metal1", "via1", "metal2"),
|
||||
offset=[self.rails_x_offset[power_line_index] + self.gap_between_rails,
|
||||
yoffset - via_correct.y],
|
||||
rotate=90)
|
||||
|
||||
yoffset = (self.number_of_outputs * self.inv.height
|
||||
- 0.5 * drc["minwidth_metal1"])
|
||||
v_metal = self.get_vertical_metal()
|
||||
via_y = self.get_via_y()
|
||||
index = self.number_of_inputs + 1
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[self.rails_x_offset[index], yoffset],
|
||||
width=self.x_off_nand - self.rails_x_offset[index],
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_rect(layer=v_metal,
|
||||
offset=[self.rails_x_offset[index], self.rail_height],
|
||||
width=drc["minwidth_"+v_metal],
|
||||
height=yoffset - self.rail_height)
|
||||
self.add_via(layers = ("metal1", "via1", "metal2"),
|
||||
offset=[self.rails_x_offset[index] + self.gap_between_rails,
|
||||
via_y] - via_correct,
|
||||
rotate=90)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
from tech import drc
|
||||
import debug
|
||||
import design
|
||||
from nand_2 import nand_2
|
||||
from vector import vector
|
||||
from hierarchical_predecode import hierarchical_predecode
|
||||
|
||||
|
|
@ -9,110 +8,46 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
"""
|
||||
Pre 2x4 decoder used in hierarchical_decoder.
|
||||
"""
|
||||
def __init__(self, nmos_width, cellname):
|
||||
hierarchical_predecode.__init__(self, nmos_width, cellname, 2)
|
||||
def __init__(self):
|
||||
hierarchical_predecode.__init__(self, 2)
|
||||
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.setup_constrains()
|
||||
self.setup_constraints()
|
||||
self.create_layout()
|
||||
self.route()
|
||||
|
||||
def create_nand(self):
|
||||
self.nand = nand_2(nmos_width=self.nmos_width,
|
||||
height=self.bitcell_height)
|
||||
|
||||
def set_rail_height(self):
|
||||
self.rail_height = (self.number_of_outputs * self.nand.height
|
||||
- (self.number_of_outputs - 1) * drc["minwidth_metal2"])
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_layout(self):
|
||||
""" The general organization is from left to right:
|
||||
1) a set of M2 rails for input signals
|
||||
2) a set of inverters to invert input signals
|
||||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of NAND gates for inversion
|
||||
"""
|
||||
self.create_rails()
|
||||
self.add_inv2x4()
|
||||
self.add_input_inverters()
|
||||
self.add_output_inverters()
|
||||
connections =[["A[0]", "A[1]", "Z[3]", "vdd", "gnd"],
|
||||
["B[0]", "A[1]", "Z[2]", "vdd", "gnd"],
|
||||
["A[0]", "B[1]", "Z[1]", "vdd", "gnd"],
|
||||
["B[0]", "B[1]", "Z[0]", "vdd", "gnd"]]
|
||||
connections =[["in[0]", "in[1]", "Z[3]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"],
|
||||
["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"]]
|
||||
self.add_nand(connections)
|
||||
|
||||
def set_height(self):
|
||||
self.height = 4 * self.nand.height
|
||||
|
||||
def add_inv2x4(self):
|
||||
self.A_positions = []
|
||||
for inv_2x4 in range(self.number_of_inputs):
|
||||
name = "Xpre2x4_inv[{0}]".format(inv_2x4)
|
||||
if (inv_2x4 % 2 == 0):
|
||||
y_off = inv_2x4 * (self.inv.height)
|
||||
offset = vector(self.x_off_inv_1, y_off)
|
||||
mirror = "R0"
|
||||
A_off = self.inv.A_position.scale(0, 1)
|
||||
else:
|
||||
y_off = (inv_2x4 + 1) * (self.inv.height)
|
||||
offset = vector(self.x_off_inv_1, y_off)
|
||||
mirror="MX"
|
||||
A_off = vector(0, - self.inv.A_position.y - drc["minwidth_metal1"])
|
||||
self.A_positions.append(offset + A_off)
|
||||
self.add_inst(name=name,
|
||||
mod=self.inv,
|
||||
offset=offset,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["A[{0}]".format(inv_2x4),
|
||||
"B[{0}]".format(inv_2x4),
|
||||
"vdd", "gnd"])
|
||||
|
||||
def cal_input_inverters_output(self,setup,output_shift,inv_rout):
|
||||
y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset = setup
|
||||
correct = y_dir * (output_shift + drc["minwidth_metal1"])
|
||||
out_offset = vector(inv_out_offset)
|
||||
if y_dir == -1:
|
||||
out_offset.y = inv_vdd_offset.y + output_shift + drc["minwidth_metal1"]
|
||||
|
||||
vertical1 = out_offset
|
||||
vertical2 = vertical1 + vector(0,
|
||||
(inv_vdd_offset.y - inv_out_offset.y) * y_dir
|
||||
- output_shift)
|
||||
horizontal1 = vector(inv_out_offset.x,
|
||||
inv_vdd_offset.y - correct)
|
||||
horizontal2 = horizontal1 + vector(self.rails_x_offset[inv_rout + 4] - inv_out_offset.x+ drc["minwidth_metal2"],
|
||||
0)
|
||||
return [[vertical1,vertical2],[horizontal1,horizontal2]]
|
||||
|
||||
def set_output_shift(self):
|
||||
return 2 * drc["minwidth_metal1"]
|
||||
self.route()
|
||||
|
||||
def get_nand_input_line_combination(self):
|
||||
combination = [[4, 5], [6, 5], [4, 7], [6, 7]]
|
||||
""" These are the decoder connections of the NAND gates to the A,B pins """
|
||||
combination = [["Abar[0]", "Abar[1]"],
|
||||
["A[0]", "Abar[1]"],
|
||||
["Abar[0]", "A[1]"],
|
||||
["A[0]", "A[1]"]]
|
||||
return combination
|
||||
|
||||
def create_y_offsets(self,k):
|
||||
# create y offset list
|
||||
if (k % 2 == 0):
|
||||
y_off = k * (self.nand.height)
|
||||
direct = 1
|
||||
else:
|
||||
y_off = (k + 1) * (self.nand.height) - drc["minwidth_metal1"]
|
||||
direct = - 1
|
||||
correct =[0,0]
|
||||
yoffset_nand_in = [y_off + direct * self.nand.A_position.y,
|
||||
y_off + direct * self.nand.B_position.y]
|
||||
return yoffset_nand_in, correct
|
||||
|
||||
def get_via_correct(self):
|
||||
return vector(0, self.via_shift)
|
||||
|
||||
def get_vertical_metal(self):
|
||||
return "metal1"
|
||||
|
||||
def get_via_y(self):
|
||||
return self.rail_height
|
||||
|
||||
def delay(self, slew, load = 0.0 ):
|
||||
# A -> B
|
||||
# in -> inbar
|
||||
a_t_b_delay = self.inv.delay(slew=slew,load = self.nand.input_load())
|
||||
|
||||
# out -> z
|
||||
# inbar -> z
|
||||
b_t_z_delay = self.nand.delay(slew=a_t_b_delay.slew,load = self.inv.input_load())
|
||||
result = a_t_b_delay + b_t_z_delay
|
||||
|
||||
|
|
@ -122,4 +57,4 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
return result
|
||||
|
||||
def input_load(self):
|
||||
return self.inv.input_load()
|
||||
return self.nand.input_load()
|
||||
|
|
|
|||
|
|
@ -1,105 +1,69 @@
|
|||
from tech import drc
|
||||
import debug
|
||||
import design
|
||||
from nand_3 import nand_3
|
||||
from vector import vector
|
||||
from hierarchical_predecode import hierarchical_predecode
|
||||
|
||||
|
||||
class hierarchical_predecode3x8(hierarchical_predecode):
|
||||
"""
|
||||
Pre 3x8 decoder used in hierarchical_decoder.
|
||||
"""
|
||||
def __init__(self, nmos_width, cellname):
|
||||
hierarchical_predecode.__init__(self, nmos_width, cellname, 3)
|
||||
def __init__(self):
|
||||
hierarchical_predecode.__init__(self, 3)
|
||||
|
||||
self.add_pins()
|
||||
self.create_modules()
|
||||
self.setup_constrains()
|
||||
self.setup_constraints()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_layout(self):
|
||||
""" The general organization is from left to right:
|
||||
1) a set of M2 rails for input signals
|
||||
2) a set of inverters to invert input signals
|
||||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||
4) a set of NAND gates for inversion
|
||||
"""
|
||||
self.create_rails()
|
||||
self.add_input_inverters()
|
||||
self.add_output_inverters()
|
||||
connections=[["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"],
|
||||
["in[0]", "in[1]", "inbar[2]", "Z[6]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "in[2]", "Z[5]", "vdd", "gnd"],
|
||||
["in[0]", "inbar[1]", "inbar[2]", "Z[4]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "in[2]", "Z[3]", "vdd", "gnd"],
|
||||
["inbar[0]", "in[1]", "inbar[2]", "Z[2]", "vdd", "gnd"],
|
||||
["inbar[0]", "inbar[1]", "in[2]", "Z[1]", "vdd", "gnd"],
|
||||
["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"]]
|
||||
self.add_nand(connections)
|
||||
self.route()
|
||||
|
||||
def create_nand(self):
|
||||
self.nand = nand_3(nmos_width=self.nmos_width,
|
||||
height=self.bitcell_height)
|
||||
|
||||
def set_rail_height(self):
|
||||
self.rail_height = (self.number_of_outputs * self.nand.height
|
||||
- 1.5 * drc["minwidth_metal2"])
|
||||
def create_layout(self):
|
||||
self.create_rails()
|
||||
self.add_output_inverters()
|
||||
connections=[["A[0]", "A[1]", "A[2]", "Z[7]", "vdd", "gnd"],
|
||||
["A[0]", "A[1]", "B[2]", "Z[6]", "vdd", "gnd"],
|
||||
["A[0]", "B[1]", "A[2]", "Z[5]", "vdd", "gnd"],
|
||||
["A[0]", "B[1]", "B[2]", "Z[4]", "vdd", "gnd"],
|
||||
["B[0]", "A[1]", "A[2]", "Z[3]", "vdd", "gnd"],
|
||||
["B[0]", "A[1]", "B[2]", "Z[2]", "vdd", "gnd"],
|
||||
["B[0]", "B[1]", "A[2]", "Z[1]", "vdd", "gnd"],
|
||||
["B[0]", "B[1]", "B[2]", "Z[0]", "vdd", "gnd"]]
|
||||
self.add_nand(connections)
|
||||
|
||||
def set_height(self):
|
||||
self.height = 8 * self.nand.height
|
||||
|
||||
def cal_input_inverters_output(self,setup,output_shift,inv_rout):
|
||||
y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset = setup
|
||||
correct = y_dir * (output_shift + drc["minwidth_metal1"])
|
||||
|
||||
out_offset = inv_out_offset + vector(0, output_shift + correct)
|
||||
vertical1 = out_offset
|
||||
vertical2 = (vertical1.scale(1, 0) + inv_vdd_offset.scale(0, 1)
|
||||
+ vector(0, - correct))
|
||||
horizontal1 = vertical1
|
||||
horizontal2 = vector(self.rails_x_offset[inv_rout + 5] + drc["minwidth_metal2"],
|
||||
vertical2.y)
|
||||
return [[vertical1,vertical2],[horizontal1,horizontal2]]
|
||||
|
||||
def set_output_shift(self):
|
||||
return 1.5 * drc["minwidth_metal1"]
|
||||
|
||||
def get_nand_input_line_combination(self):
|
||||
combination = [[5, 6, 7], [5, 6, 10],
|
||||
[5, 9, 7], [5, 9, 10],
|
||||
[8, 6, 7], [8, 6, 10],
|
||||
[8, 9, 7], [8, 9, 10]]
|
||||
""" These are the decoder connections of the NAND gates to the A,B,C pins """
|
||||
combination = [["Abar[0]", "Abar[1]", "Abar[2]"],
|
||||
["Abar[0]", "Abar[1]", "A[2]"],
|
||||
["Abar[0]", "A[1]", "Abar[2]"],
|
||||
["Abar[0]", "A[1]", "A[2]"],
|
||||
["A[0]", "Abar[1]", "Abar[2]"],
|
||||
["A[0]", "Abar[1]", "A[2]"],
|
||||
["A[0]", "A[1]", "Abar[2]"],
|
||||
["A[0]", "A[1]", "A[2]"]]
|
||||
return combination
|
||||
|
||||
def create_y_offsets(self,k):
|
||||
if (k % 2 == 0):
|
||||
y_off = k * (self.nand.height)
|
||||
y_dir =1
|
||||
correct = [0,0,self.contact_shift]
|
||||
else:
|
||||
y_off = 2 * self.inv.height - drc["minwidth_metal1"] + (k - 1) * (self.nand.height)
|
||||
y_dir = -1
|
||||
correct = [0,self.contact_shift,0]
|
||||
yoffset_nand_in = [y_off + y_dir*self.nand.A_position[1],
|
||||
y_off + y_dir*self.nand.B_position[1],
|
||||
y_off + y_dir*self.nand.C_position[1]]
|
||||
return yoffset_nand_in, correct
|
||||
|
||||
def get_via_correct(self):
|
||||
return vector(0, self.via_shift+self.contact_shift)
|
||||
|
||||
def get_vertical_metal(self):
|
||||
return "metal2"
|
||||
|
||||
def get_via_y(self):
|
||||
yoffset = (self.number_of_outputs * self.inv.height
|
||||
- 0.5 * drc["minwidth_metal1"])
|
||||
return yoffset
|
||||
|
||||
def delay(self, slew, load = 0.0 ):
|
||||
# A -> z
|
||||
b_t_z_delay = self.nand.delay(slew=slew,
|
||||
load = self.input_load())
|
||||
# A -> Abar
|
||||
a_t_b_delay = self.inv.delay(slew=slew,load = self.nand.input_load())
|
||||
|
||||
# Abar -> z
|
||||
b_t_z_delay = self.nand.delay(slew=a_t_b_delay.slew,load = self.inv.input_load())
|
||||
result = a_t_b_delay + b_t_z_delay
|
||||
|
||||
# Z -> out
|
||||
a_t_out_delay = self.inv.delay(slew=b_t_z_delay.slew,
|
||||
load = load)
|
||||
result = b_t_z_delay + a_t_out_delay
|
||||
a_t_out_delay = self.inv.delay(slew=b_t_z_delay.slew,load = load)
|
||||
result = result + a_t_out_delay
|
||||
return result
|
||||
|
||||
|
||||
def input_load(self):
|
||||
return self.nand.input_load()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from tech import drc, GDS
|
|||
from tech import layer as techlayer
|
||||
import os
|
||||
from vector import vector
|
||||
from pin_layout import pin_layout
|
||||
|
||||
class layout:
|
||||
"""
|
||||
|
|
@ -21,9 +22,9 @@ class layout:
|
|||
self.name = name
|
||||
self.width = None
|
||||
self.height = None
|
||||
self.insts = [] # Holds module/cell layout instances
|
||||
self.objs = [] # Holds all other objects (labels, geometries, etc)
|
||||
|
||||
self.insts = [] # Holds module/cell layout instances
|
||||
self.objs = [] # Holds all other objects (labels, geometries, etc)
|
||||
self.pin_map = {} # Holds name->(vector,layer) map for all pins
|
||||
self.visited = False # Flag for traversing the hierarchy
|
||||
|
||||
self.gds_read()
|
||||
|
|
@ -38,6 +39,21 @@ class layout:
|
|||
self.offset_attributes(coordinate)
|
||||
self.translate(coordinate)
|
||||
|
||||
def get_gate_offset(self, x_offset, height, inv_num):
|
||||
"""Gets the base offset and y orientation of stacked rows of gates
|
||||
assuming a minwidth metal1 vdd/gnd rail. Input is which gate
|
||||
in the stack from 0..n
|
||||
"""
|
||||
|
||||
if (inv_num % 2 == 0):
|
||||
base_offset=vector(x_offset, inv_num * height)
|
||||
y_dir = 1
|
||||
else:
|
||||
# we lose a rail after every 2 gates
|
||||
base_offset=vector(x_offset, (inv_num+1) * height - (inv_num%2)*drc["minwidth_metal1"])
|
||||
y_dir = -1
|
||||
|
||||
return (base_offset,y_dir)
|
||||
|
||||
|
||||
def find_lowest_coords(self):
|
||||
|
|
@ -99,24 +115,46 @@ class layout:
|
|||
for inst in self.insts:
|
||||
inst.offset = vector(inst.offset - coordinate)
|
||||
|
||||
# FIXME: Make name optional and pick a random one if not specified
|
||||
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))
|
||||
message = []
|
||||
for x in self.insts:
|
||||
message.append(x.name)
|
||||
debug.info(4, "adding instance" + ",".join(message))
|
||||
debug.info(4, "adding instance" + ",".join(x.name for x in self.insts))
|
||||
return self.insts[-1]
|
||||
|
||||
def get_inst(self, name):
|
||||
"""Retrieve an instance by name"""
|
||||
for inst in self.insts:
|
||||
if inst.name == name:
|
||||
return inst
|
||||
return None
|
||||
|
||||
def add_rect(self, layer, offset, width, height):
|
||||
"""Adds a rectangle on a given layer,offset with width and height"""
|
||||
# negative layers indicate "unused" layers in a given technology
|
||||
layerNumber = techlayer[layer]
|
||||
if layerNumber >= 0:
|
||||
self.objs.append(geometry.rectangle(layerNumber, offset, width, height))
|
||||
return self.objs[-1]
|
||||
return None
|
||||
|
||||
|
||||
def get_pin(self, text):
|
||||
""" Return the pin or list of pins """
|
||||
debug.check(len(self.pin_map[text])==1,"Should use a pin iterator since more than one pin.")
|
||||
# If we have one pin, return it and not the list.
|
||||
# Otherwise, should use get_pins()
|
||||
return self.pin_map[text][0]
|
||||
|
||||
def add_layout_pin(self, text, layer, offset, width, height):
|
||||
def get_pins(self, text):
|
||||
""" Return a pin list (instead of a single pin) """
|
||||
return self.pin_map[text]
|
||||
|
||||
def add_layout_pin(self, text, layer, offset, width=None, height=None):
|
||||
"""Create a labeled pin"""
|
||||
if width==None:
|
||||
width=drc["minwidth_{0}".format(layer)]
|
||||
if height==None:
|
||||
height=drc["minwidth_{0}".format(layer)]
|
||||
self.add_rect(layer=layer,
|
||||
offset=offset,
|
||||
width=width,
|
||||
|
|
@ -124,7 +162,12 @@ class layout:
|
|||
self.add_label(text=text,
|
||||
layer=layer,
|
||||
offset=offset)
|
||||
|
||||
|
||||
try:
|
||||
self.pin_map[text].append(pin_layout(text,vector(offset,offset+vector(width,height)),layer))
|
||||
except KeyError:
|
||||
self.pin_map[text] = [pin_layout(text,vector(offset,offset+vector(width,height)),layer)]
|
||||
|
||||
|
||||
def add_label(self, text, layer, offset=[0,0],zoom=-1):
|
||||
"""Adds a text label on the given layer,offset, and zoom level"""
|
||||
|
|
@ -132,6 +175,8 @@ class layout:
|
|||
layerNumber = techlayer[layer]
|
||||
if layerNumber >= 0:
|
||||
self.objs.append(geometry.label(text, layerNumber, offset, zoom))
|
||||
return self.objs[-1]
|
||||
return None
|
||||
|
||||
|
||||
def add_path(self, layer, coordinates, width=None):
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ class spice:
|
|||
|
||||
def connect_inst(self, args, check=True):
|
||||
"""Connects the pins of the last instance added
|
||||
It is preferred to use the other function with the check to find if
|
||||
there is a problem. The check otion can be set to false
|
||||
It is preferred to use the function with the check to find if
|
||||
there is a problem. The check option can be set to false
|
||||
where we dynamically generate groups of connections after a
|
||||
group of modules are generated."""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,188 +0,0 @@
|
|||
import debug
|
||||
import design
|
||||
from tech import drc
|
||||
from pinv import pinv
|
||||
from contact import contact
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
|
||||
class logic_effort_dc(design.design):
|
||||
"""
|
||||
Generate a logic effort based delay chain.
|
||||
Input is a list contains the electrical effort of each stage.
|
||||
"""
|
||||
|
||||
def __init__(self, name, stage_list):
|
||||
"""init function"""
|
||||
design.design.__init__(self, "delay_chain")
|
||||
#fix me: input should be logic effort value
|
||||
# and there should be functions to get
|
||||
# area effecient inverter stage list
|
||||
|
||||
# chain_length is number of inverters in the load
|
||||
# plus 1 for the input
|
||||
chain_length = 1 + sum(stage_list)
|
||||
# half chain length is the width of the layeout
|
||||
# invs are stacked into 2 levels so input/output are close
|
||||
half_length = round(chain_length / 2.0)
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
self.bitcell_height = self.mod_bitcell.chars["height"]
|
||||
|
||||
self.add_pins()
|
||||
self.create_module()
|
||||
self.cal_cell_size(half_length)
|
||||
self.create_inv_stage_lst(stage_list)
|
||||
self.add_inv_lst(chain_length)
|
||||
self.route_inv(stage_list)
|
||||
self.add_vddgnd_label()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
"""add the pins of the delay chain"""
|
||||
self.add_pin("clk_in")
|
||||
self.add_pin("clk_out")
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def cal_cell_size(self, half_length):
|
||||
""" calculate the width and height of the cell"""
|
||||
self.width = half_length * self.inv.width
|
||||
self.height = 2 * self.bitcell_height
|
||||
|
||||
def create_module(self):
|
||||
"""add the inverters"""
|
||||
self.inv = pinv(nmos_width=drc["minwidth_tx"],
|
||||
route_output=False)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
|
||||
def create_inv_stage_lst(self, stage_list):
|
||||
""" Generate a list to indicate what stage each inv is in """
|
||||
self.inv_stage_lst = [[0, True]]
|
||||
stage_num = 0
|
||||
for stage in stage_list:
|
||||
stage_num = stage_num + 1
|
||||
repeat_times = stage
|
||||
for i in range(repeat_times):
|
||||
if i == repeat_times - 1:
|
||||
# the first one need to connected to the next stage
|
||||
self.inv_stage_lst.append([stage_num, True])
|
||||
else:
|
||||
# the rest should not drive any thing
|
||||
self.inv_stage_lst.append([stage_num, False])
|
||||
|
||||
def add_inv_lst(self, chain_length):
|
||||
"""add the inverter and connect them based on the stage list """
|
||||
half_length = round(chain_length / 2.0)
|
||||
self.inv_positions = []
|
||||
for i in range(chain_length):
|
||||
if i < half_length:
|
||||
# add top level
|
||||
inv_offset = [i * self.inv.width,
|
||||
2 * self.inv.height]
|
||||
inv_mirror="MX"
|
||||
self.inv_positions.append(inv_offset)
|
||||
offset = inv_offset + \
|
||||
self.inv.input_position.scale(1,-1)
|
||||
m1m2_via_rotate=270
|
||||
if i == 0:
|
||||
self.clk_in_offset = offset
|
||||
else:
|
||||
# add bottom level
|
||||
inv_offset = [(chain_length - i) * self.inv.width,
|
||||
0]
|
||||
inv_mirror="MY"
|
||||
self.inv_positions.append(inv_offset)
|
||||
offset = inv_offset + \
|
||||
self.inv.input_position.scale(-1,1)
|
||||
m1m2_via_rotate=90
|
||||
if i == chain_length - 1:
|
||||
self.clk_out_offset = inv_offset + \
|
||||
self.inv.output_position.scale(-1,1)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset,
|
||||
rotate=m1m2_via_rotate)
|
||||
self.add_inst(name="inv_chain%d" % i,
|
||||
mod=self.inv,
|
||||
offset=inv_offset,
|
||||
mirror=inv_mirror)
|
||||
|
||||
# connecting spice
|
||||
if i == 0:
|
||||
self.connect_inst(args=["clk_in", "s" + str(self.inv_stage_lst[i][0] + 1),
|
||||
"vdd", "gnd"],
|
||||
check=False)
|
||||
spare_node_counter = 1
|
||||
elif i == chain_length - 1:
|
||||
self.connect_inst(args=["s" + str(self.inv_stage_lst[i][0]), "clk_out", "vdd", "gnd"],
|
||||
check=False)
|
||||
else:
|
||||
if self.inv_stage_lst[i][1] == True:
|
||||
self.connect_inst(args=["s" + str(self.inv_stage_lst[i][0]),
|
||||
"s" + str(self.inv_stage_lst[i][0] + 1), "vdd", "gnd"],
|
||||
check=False)
|
||||
spare_node_counter = 1
|
||||
else:
|
||||
self.connect_inst(args=["s" + str(self.inv_stage_lst[i][0]), "s" \
|
||||
+ str(self.inv_stage_lst[i][0] + 1) + "n" \
|
||||
+ str(spare_node_counter), "vdd", "gnd"],
|
||||
check=False)
|
||||
spare_node_counter += 1
|
||||
|
||||
def route_inv(self, stage_list):
|
||||
"""add metal routing based on the stage list """
|
||||
half_length = round((sum(stage_list) + 1) / 2.0)
|
||||
start_inv = end_inv = 0
|
||||
for stage in stage_list:
|
||||
# end inv number depends on the fan out number
|
||||
end_inv = start_inv + stage
|
||||
start_inv_offset = self.inv_positions[start_inv]
|
||||
end_inv_offset = self.inv_positions[end_inv]
|
||||
|
||||
if start_inv < half_length:
|
||||
start_o_offset = start_inv_offset + \
|
||||
self.inv.output_position.scale(1,-1)
|
||||
m1m2_via_rotate =270
|
||||
m1m2_via_vc = vector(1,-.5)
|
||||
else:
|
||||
start_o_offset = start_inv_offset + \
|
||||
self.inv.output_position.scale(-1,1)
|
||||
m1m2_via_rotate =90
|
||||
m1m2_via_vc = vector(1,.5)
|
||||
M2_start = start_o_offset + vector(0,drc["minwidth_metal2"]).scale(m1m2_via_vc)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=start_o_offset,
|
||||
rotate=m1m2_via_rotate)
|
||||
|
||||
if end_inv < half_length:
|
||||
end_i_offset = end_inv_offset + \
|
||||
self.inv.input_position.scale(1,-1)
|
||||
M2_end = end_i_offset - vector(0, 0.5 * drc["minwidth_metal2"])
|
||||
else:
|
||||
end_i_offset = end_inv_offset + \
|
||||
self.inv.input_position.scale(-1,1)
|
||||
M2_end = end_i_offset + vector(0, 0.5 * drc["minwidth_metal2"])
|
||||
|
||||
if start_inv < half_length and end_inv >= half_length:
|
||||
mid = [half_length * self.inv.width \
|
||||
- 0.5 * drc["minwidth_metal2"], M2_start[1]]
|
||||
self.add_wire(("metal2", "via2", "metal3"),
|
||||
[M2_start, mid, M2_end])
|
||||
else:
|
||||
self.add_path(("metal2"), [M2_start, M2_end])
|
||||
# set the start of next one after current end
|
||||
start_inv = end_inv
|
||||
|
||||
def add_vddgnd_label(self):
|
||||
"""add vdd and gnd labels"""
|
||||
for i in range(3):
|
||||
if i % 2:
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=[0, i * self.bitcell_height])
|
||||
else:
|
||||
self.add_label(text="gnd",
|
||||
layer="metal1",
|
||||
offset=[0, i * self.bitcell_height])
|
||||
|
|
@ -10,33 +10,19 @@ class ms_flop(design.design):
|
|||
Memory address flip-flop
|
||||
"""
|
||||
|
||||
pins = ["din", "dout", "dout_bar", "clk", "vdd", "gnd"]
|
||||
chars = utils.auto_measure_libcell(pins, "ms_flop", GDS["unit"], layer["boundary"])
|
||||
|
||||
def __init__(self, name):
|
||||
pin_names = ["din", "dout", "dout_bar", "clk", "vdd", "gnd"]
|
||||
(width,height) = utils.get_libcell_size("ms_flop", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "ms_flop", GDS["unit"], layer["boundary"])
|
||||
|
||||
def __init__(self, name="ms_flop"):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
self.width = ms_flop.chars["width"]
|
||||
self.height = ms_flop.chars["height"]
|
||||
|
||||
self.clk_offset = ms_flop.chars["clk"]
|
||||
self.din_offset = ms_flop.chars["din"]
|
||||
self.dout_offset = ms_flop.chars["dout"]
|
||||
self.dout_bar_offset = ms_flop.chars["dout_bar"]
|
||||
|
||||
self.width = ms_flop.width
|
||||
self.height = ms_flop.height
|
||||
self.pin_map = ms_flop.pin_map
|
||||
|
||||
def delay(self, slew, load = 0.0):
|
||||
#import pinv
|
||||
# use inv to mimic the delay
|
||||
# din -> mout
|
||||
#ref = pinv.pinv("reference_inv")
|
||||
#mid_load = ref.input_load()
|
||||
#din_t_mout_delay = ref.delay(slew = slew, load = mid_load)
|
||||
|
||||
# mout -> out
|
||||
#mout_t_out_delay = ref.delay(slew = slew, load = load)
|
||||
#result = din_t_mout_delay + mout_t_out_delay
|
||||
|
||||
# dont k how to calculate this now, use constant in tech file
|
||||
# dont know how to calculate this now, use constant in tech file
|
||||
from tech import spice
|
||||
result = self.return_delay(spice["msflop_delay"], spice["msflop_slew"])
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -12,47 +12,34 @@ class ms_flop_array(design.design):
|
|||
hierdecoder
|
||||
"""
|
||||
|
||||
def __init__(self, name, columns, word_size):
|
||||
def __init__(self, columns, word_size, name=""):
|
||||
self.columns = columns
|
||||
self.word_size = word_size
|
||||
if name=="":
|
||||
name = "flop_array_c{0}_w{1}".format(columns,word_size)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating %s" % self.name)
|
||||
debug.info(1, "Creating {}".format(self.name))
|
||||
|
||||
c = reload(__import__(OPTS.config.ms_flop))
|
||||
self.mod_ms_flop = getattr(c, OPTS.config.ms_flop)
|
||||
self.ms_flop_chars = self.mod_ms_flop.chars
|
||||
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 = self.columns / self.word_size
|
||||
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
self.add_modules()
|
||||
self.setup_layout_constants()
|
||||
self.add_pins()
|
||||
self.create_ms_flop_array()
|
||||
self.add_labels()
|
||||
self.add_layout_pins()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
self.ms_flop = self.mod_ms_flop("ms_flop")
|
||||
self.add_mod(self.ms_flop)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.width = self.columns * self.ms_flop.width
|
||||
self.height = self.ms_flop.height
|
||||
self.words_per_row = self.columns / self.word_size
|
||||
|
||||
self.flop_positions = []
|
||||
self.vdd_positions = []
|
||||
self.gnd_positions = []
|
||||
self.clk_positions = []
|
||||
self.dout_positions = []
|
||||
self.dout_bar_positions = []
|
||||
self.din_positions = []
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("din[{0}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("dout[{0}]".format(i))
|
||||
self.add_pin("dout_bar[{0}]".format(i))
|
||||
self.add_pin("clk")
|
||||
|
|
@ -60,97 +47,83 @@ class ms_flop_array(design.design):
|
|||
self.add_pin("gnd")
|
||||
|
||||
def create_ms_flop_array(self):
|
||||
for i in range(self.word_size):
|
||||
name = "Xdff%d" % i
|
||||
if (i % 2 == 0):
|
||||
x_off = i * self.ms_flop.width * self.words_per_row
|
||||
mirror = "None"
|
||||
self.ms_inst={}
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xdff{0}".format(i)
|
||||
if (i % 2 == 0 or self.words_per_row>1):
|
||||
base = vector(i*self.ms.width,0)
|
||||
mirror = "R0"
|
||||
else:
|
||||
if (self.words_per_row == 1):
|
||||
x_off = (i + 1) * self.ms_flop.width
|
||||
mirror="MY"
|
||||
else:
|
||||
x_off = i * self.ms_flop.width * self.words_per_row
|
||||
self.add_inst(name=name,
|
||||
mod=self.ms_flop,
|
||||
offset=[x_off, 0],
|
||||
mirror=mirror)
|
||||
self.connect_inst(["din[{0}]".format(i),
|
||||
"dout[{0}]".format(i),
|
||||
"dout_bar[{0}]".format(i),
|
||||
base = vector((i+1)*self.ms.width,0)
|
||||
mirror = "MY"
|
||||
self.ms_inst[i]=self.add_inst(name=name,
|
||||
mod=self.ms,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["din[{0}]".format(i/self.words_per_row),
|
||||
"dout[{0}]".format(i/self.words_per_row),
|
||||
"dout_bar[{0}]".format(i/self.words_per_row),
|
||||
"clk",
|
||||
"vdd", "gnd"])
|
||||
self.flop_positions.append(vector(x_off, 0))
|
||||
|
||||
def add_labels(self):
|
||||
for i in range(self.word_size):
|
||||
def add_layout_pins(self):
|
||||
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
i_str = "[{0}]".format(i)
|
||||
if (i % 2 == 0 or self.words_per_row > 1):
|
||||
base = vector(i * self.ms_flop.width * self.words_per_row, 0)
|
||||
self.add_label(text="gnd",
|
||||
layer="metal2",
|
||||
offset=base + self.ms_flop_chars["gnd"])
|
||||
self.add_label(text="din" + i_str,
|
||||
layer="metal2",
|
||||
offset=base + self.ms_flop_chars["din"])
|
||||
self.add_label(text="dout" + i_str,
|
||||
layer="metal2",
|
||||
offset=base + self.ms_flop_chars["dout"])
|
||||
self.add_label(text="dout_bar" + i_str,
|
||||
layer="metal2",
|
||||
offset=base + self.ms_flop_chars["dout_bar"])
|
||||
|
||||
self.din_positions.append(base + self.ms_flop_chars["din"])
|
||||
self.dout_positions.append(base + self.ms_flop_chars["dout"])
|
||||
self.dout_bar_positions.append(base + self.ms_flop_chars["dout_bar"])
|
||||
self.gnd_positions.append(base + self.ms_flop_chars["gnd"])
|
||||
else:
|
||||
base = vector((i + 1) * self.ms_flop.width, 0)
|
||||
gnd_offset = base + vector(self.ms_flop_chars["gnd"]).scale(-1,1)
|
||||
din_offset = base + vector(self.ms_flop_chars["din"]).scale(-1,1)
|
||||
dout_offset = base + vector(self.ms_flop_chars["dout"]).scale(-1,1)
|
||||
dout_bar_offset = base + vector(self.ms_flop_chars["dout_bar"]).scale(-1,1)
|
||||
# Avoid duplicate rails by only doing even columns or last one
|
||||
for gnd_pin in self.ms_inst[i].get_pins("gnd"):
|
||||
if gnd_pin.layer!="metal2":
|
||||
continue
|
||||
if i%2==0 or i+self.words_per_row>=self.columns:
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal2",
|
||||
offset=gnd_pin.ll(),
|
||||
width=gnd_pin.width(),
|
||||
height=gnd_pin.height())
|
||||
|
||||
self.add_label(text="gnd",
|
||||
layer="metal2",
|
||||
offset=gnd_offset)
|
||||
self.add_label(text="din" + i_str,
|
||||
layer="metal2",
|
||||
offset=din_offset)
|
||||
self.add_label(text="dout" + i_str,
|
||||
layer="metal2",
|
||||
offset=dout_offset)
|
||||
self.add_label(text="dout_bar" + i_str,
|
||||
layer="metal2",
|
||||
offset=dout_bar_offset)
|
||||
din_pin = self.ms_inst[i].get_pin("din")
|
||||
self.add_layout_pin(text="din"+i_str,
|
||||
layer="metal2",
|
||||
offset=din_pin.ll(),
|
||||
width=din_pin.width(),
|
||||
height=din_pin.height())
|
||||
|
||||
self.gnd_positions.append(gnd_offset)
|
||||
self.din_positions.append(din_offset)
|
||||
self.dout_positions.append(dout_offset)
|
||||
self.dout_bar_positions.append(dout_bar_offset)
|
||||
dout_pin = self.ms_inst[i].get_pin("dout")
|
||||
self.add_layout_pin(text="dout"+i_str,
|
||||
layer="metal2",
|
||||
offset=dout_pin.ll(),
|
||||
width=dout_pin.width(),
|
||||
height=dout_pin.height())
|
||||
|
||||
doutbar_pin = self.ms_inst[i].get_pin("dout_bar")
|
||||
self.add_layout_pin(text="dout_bar"+i_str,
|
||||
layer="metal2",
|
||||
offset=doutbar_pin.ll(),
|
||||
width=doutbar_pin.width(),
|
||||
height=doutbar_pin.height())
|
||||
|
||||
|
||||
# Continous "clk" rail along with label.
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[0, self.ms_flop_chars["clk"][1]],
|
||||
width=self.width,
|
||||
height=-drc["minwidth_metal1"])
|
||||
self.add_label(text="clk",
|
||||
layer="metal1",
|
||||
offset=self.ms_flop_chars["clk"])
|
||||
self.clk_positions.append(vector(self.ms_flop_chars["clk"]))
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="metal1",
|
||||
offset=self.ms_inst[0].get_pin("clk").ll().scale(0,1),
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
|
||||
# Continous "Vdd" rail along with label.
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[0, self.ms_flop_chars["vdd"][1] - 0.5 * drc["minwidth_metal1"]],
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vector(self.ms_flop_chars["vdd"]).scale(0, 1))
|
||||
self.vdd_positions.append(vector(self.ms_flop_chars["vdd"]).scale(0, 1))
|
||||
for vdd_pin in self.ms_inst[i].get_pins("vdd"):
|
||||
if vdd_pin.layer!="metal1":
|
||||
continue
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_pin.ll().scale(0,1),
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
|
||||
def delay(self, slew, load=0.0):
|
||||
result = self.ms_flop.delay(slew = slew,
|
||||
load = load)
|
||||
result = self.ms.delay(slew = slew,
|
||||
load = load)
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -21,15 +21,17 @@ class nand_2(design.design):
|
|||
|
||||
unique_id = 1
|
||||
|
||||
def __init__(self, nmos_width=1, height=bitcell.chars["height"]):
|
||||
def __init__(self, nmos_width=2*drc["minwidth_tx"], height=bitcell.height):
|
||||
"""Constructor : Creates a cell for a simple 2 input nand"""
|
||||
name = "nand2_{0}".format(nand_2.unique_id)
|
||||
nand_2.unique_id += 1
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "create nand_2 structure {0} with size of {1}".format(
|
||||
name, nmos_width))
|
||||
debug.info(2, "create nand_2 structure {0} with size of {1}".format(name, nmos_width))
|
||||
|
||||
self.nmos_width = nmos_width
|
||||
self.nmos_size = nmos_width
|
||||
# FIXME why is this?
|
||||
self.pmos_size = nmos_width
|
||||
self.tx_mults = 1
|
||||
self.height = height
|
||||
|
||||
self.add_pins()
|
||||
|
|
@ -42,7 +44,6 @@ class nand_2(design.design):
|
|||
|
||||
def create_layout(self):
|
||||
""" Layout """
|
||||
self.determine_sizes()
|
||||
self.create_ptx()
|
||||
self.setup_layout_constants()
|
||||
self.add_rails()
|
||||
|
|
@ -58,14 +59,6 @@ class nand_2(design.design):
|
|||
self.route_pins()
|
||||
self.extend_wells()
|
||||
self.extend_active()
|
||||
self.setup_layout_offsets()
|
||||
|
||||
# Determine transistor size
|
||||
def determine_sizes(self):
|
||||
""" Determine the size of the transistors """
|
||||
self.nmos_size = self.nmos_width
|
||||
self.pmos_size = self.nmos_width
|
||||
self.tx_mults = 1
|
||||
|
||||
# transistors are created here but not yet placed or added as a module
|
||||
def create_ptx(self):
|
||||
|
|
@ -103,23 +96,19 @@ class nand_2(design.design):
|
|||
rail_height = drc["minwidth_metal1"]
|
||||
self.rail_height = rail_height
|
||||
# Relocate the origin
|
||||
self.gnd_position = vector(0, - 0.5 * drc["minwidth_metal1"])
|
||||
self.add_rect(layer="metal1",
|
||||
offset=self.gnd_position,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
self.add_label(text="gnd",
|
||||
layer="metal1",
|
||||
offset=self.gnd_position)
|
||||
self.gnd_loc = vector(0, - 0.5 * drc["minwidth_metal1"])
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=self.gnd_loc,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
|
||||
self.vdd_position = vector(0, self.height - 0.5 * drc["minwidth_metal1"])
|
||||
self.add_rect(layer="metal1",
|
||||
offset=self.vdd_position,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=self.vdd_position)
|
||||
self.vdd_loc = vector(0, self.height - 0.5 * drc["minwidth_metal1"])
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=self.vdd_loc,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
|
||||
def add_ptx(self):
|
||||
""" transistors are added and placed inside the layout """
|
||||
|
|
@ -297,7 +286,7 @@ class nand_2(design.design):
|
|||
+ self.pmos2.active_height
|
||||
+ drc["metal1_to_metal1"]
|
||||
+ self.pmos2.active_contact.second_layer_width))
|
||||
if (self.nmos_width == drc["minwidth_tx"]):
|
||||
if (self.nmos_size == drc["minwidth_tx"]):
|
||||
yoffset = (self.pmos_position1.y
|
||||
+ self.pmos1.poly_positions[0].y
|
||||
+ drc["poly_extend_active"]
|
||||
|
|
@ -322,13 +311,11 @@ class nand_2(design.design):
|
|||
- self.poly_contact.height)
|
||||
yoffset += self.poly_contact.via_layer_position.x
|
||||
offset = self.input_position1 = vector(0, yoffset)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=input_length,
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_label(text="A",
|
||||
layer="metal1",
|
||||
offset=offset)
|
||||
self.add_layout_pin(text="A",
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
width=input_length,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
def route_input_gate_B(self):
|
||||
""" routing for input B """
|
||||
|
|
@ -338,7 +325,7 @@ class nand_2(design.design):
|
|||
+ drc["metal1_to_metal1"]
|
||||
+ self.nmos2.active_height
|
||||
+ drc["minwidth_metal1"])
|
||||
if (self.nmos_width == drc["minwidth_tx"]):
|
||||
if (self.nmos_size == drc["minwidth_tx"]):
|
||||
yoffset = (self.nmos_position1.y
|
||||
+ self.nmos1.poly_positions[0].y
|
||||
+ self.nmos1.poly_height
|
||||
|
|
@ -350,11 +337,11 @@ class nand_2(design.design):
|
|||
rotate=90)
|
||||
|
||||
input_length = self.pmos2.poly_positions[0].x - self.poly_contact.height
|
||||
self.input_position2 = vector(xoffset - self.poly_contact.width,
|
||||
yoffset + self.poly_contact.via_layer_position.x)
|
||||
input_position2 = vector(xoffset - self.poly_contact.width,
|
||||
yoffset + self.poly_contact.via_layer_position.x)
|
||||
self.add_layout_pin(text="B",
|
||||
layer="metal1",
|
||||
offset=self.input_position2.scale(0,1),
|
||||
offset=input_position2.scale(0,1),
|
||||
width=(input_length + self.pmos_position2.x + drc["minwidth_poly"]),
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
|
|
@ -363,13 +350,13 @@ class nand_2(design.design):
|
|||
yoffset = (self.nmos1.height - 2 * drc["minwidth_metal1"] / 3 +
|
||||
(self.height - self.pmos1.height - self.nmos1.height - drc["minwidth_metal1"]) / 2 )
|
||||
xoffset = self.drain_position.x
|
||||
offset = self.output_position = vector(xoffset, yoffset)
|
||||
offset = vector(xoffset, yoffset)
|
||||
output_length = self.width - xoffset
|
||||
self.add_layout_pin(text="Z",
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
width=output_length,
|
||||
height=drc["minwidth_metal1"])
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
width=output_length,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
def extend_wells(self):
|
||||
""" Extension of well """
|
||||
|
|
@ -435,13 +422,6 @@ class nand_2(design.design):
|
|||
width=width,
|
||||
height=self.nmos1.active_height)
|
||||
|
||||
def setup_layout_offsets(self):
|
||||
""" Defining the position of i/o pins for the two input nand gate """
|
||||
self.A_position = self.A_position = self.input_position1
|
||||
self.B_position = self.B_position = self.input_position2
|
||||
self.Z_position = self.Z_position = self.output_position
|
||||
self.vdd_position = self.vdd_position
|
||||
self.gnd_position = self.gnd_position
|
||||
|
||||
def input_load(self):
|
||||
from tech import spice
|
||||
|
|
|
|||
|
|
@ -21,14 +21,17 @@ class nand_3(design.design):
|
|||
|
||||
unique_id = 1
|
||||
|
||||
def __init__(self, nmos_width=1, height=bitcell.chars["height"]):
|
||||
def __init__(self, nmos_width=3*drc["minwidth_tx"], height=bitcell.height):
|
||||
"""Constructor : Creates a cell for a simple 3 input nand"""
|
||||
name = "nand3_{0}".format(nand_3.unique_id)
|
||||
nand_3.unique_id += 1
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "create nand_3 structure {0} with size of {1}".format(name, nmos_width))
|
||||
|
||||
self.nmos_width = nmos_width
|
||||
self.nmos_size = nmos_width
|
||||
# FIXME: Why is this??
|
||||
self.pmos_size = 2 * nmos_width / 3
|
||||
self.tx_mults = 1
|
||||
self.height = height
|
||||
|
||||
self.add_pins()
|
||||
|
|
@ -41,7 +44,6 @@ class nand_3(design.design):
|
|||
|
||||
def create_layout(self):
|
||||
""" create layout """
|
||||
self.determine_sizes()
|
||||
self.create_ptx()
|
||||
self.setup_layout_constants()
|
||||
self.add_rails()
|
||||
|
|
@ -58,48 +60,29 @@ class nand_3(design.design):
|
|||
self.extend_active()
|
||||
self.connect_rails()
|
||||
self.route_pins()
|
||||
self.setup_layout_offsets()
|
||||
|
||||
def determine_sizes(self):
|
||||
""" Determine the size of the transistors used in this module """
|
||||
self.nmos_size = self.nmos_width
|
||||
self.pmos_size = 2 * self.nmos_width / 3
|
||||
self.tx_mults = 1
|
||||
|
||||
def create_ptx(self):
|
||||
""" Create ptx but not yet placed"""
|
||||
self.nmos1 = ptx(width=self.nmos_size,
|
||||
self.nmos = ptx(width=self.nmos_size,
|
||||
mults=self.tx_mults,
|
||||
tx_type="nmos")
|
||||
self.add_mod(self.nmos1)
|
||||
self.nmos2 = ptx(width=self.nmos_size,
|
||||
mults=self.tx_mults,
|
||||
tx_type="nmos")
|
||||
self.add_mod(self.nmos2)
|
||||
self.nmos3 = ptx(width=self.nmos_size,
|
||||
mults=self.tx_mults,
|
||||
tx_type="nmos")
|
||||
self.add_mod(self.nmos3)
|
||||
self.add_mod(self.nmos)
|
||||
self.add_mod(self.nmos)
|
||||
self.add_mod(self.nmos)
|
||||
|
||||
self.pmos1 = ptx(width=self.pmos_size,
|
||||
self.pmos = ptx(width=self.pmos_size,
|
||||
mults=self.tx_mults,
|
||||
tx_type="pmos")
|
||||
self.add_mod(self.pmos1)
|
||||
self.pmos2 = ptx(width=self.pmos_size,
|
||||
mults=self.tx_mults,
|
||||
tx_type="pmos")
|
||||
self.add_mod(self.pmos2)
|
||||
self.pmos3 = ptx(width=self.pmos_size,
|
||||
mults=self.tx_mults,
|
||||
tx_type="pmos")
|
||||
self.add_mod(self.pmos3)
|
||||
self.add_mod(self.pmos)
|
||||
self.add_mod(self.pmos)
|
||||
self.add_mod(self.pmos)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
""" setup layout constraints """
|
||||
self.well_width = self.nmos1.active_position.x \
|
||||
+ 3 * self.pmos1.active_width + drc["active_to_body_active"] \
|
||||
+ drc["well_enclosure_active"] - self.nmos3.active_contact.width \
|
||||
+ self.pmos1.active_contact.height + drc["minwidth_metal1"] \
|
||||
self.well_width = self.nmos.active_position.x \
|
||||
+ 3 * self.pmos.active_width + drc["active_to_body_active"] \
|
||||
+ drc["well_enclosure_active"] - self.nmos.active_contact.width \
|
||||
+ self.pmos.active_contact.height + drc["minwidth_metal1"] \
|
||||
+ (drc["metal1_to_metal1"] - drc["well_enclosure_active"])
|
||||
self.width = self.width = self.well_width
|
||||
|
||||
|
|
@ -109,86 +92,86 @@ class nand_3(design.design):
|
|||
self.rail_height = rail_height = drc["minwidth_metal1"]
|
||||
|
||||
# Relocate the origin
|
||||
self.gnd_position = vector(0 , - 0.5 * drc["minwidth_metal1"])
|
||||
self.gnd_loc = vector(0 , - 0.5 * drc["minwidth_metal1"])
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=self.gnd_position,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
layer="metal1",
|
||||
offset=self.gnd_loc,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
|
||||
self.vdd_position = vector(0, self.height - 0.5 * drc["minwidth_metal1"])
|
||||
self.vdd_loc = vector(0, self.height - 0.5 * drc["minwidth_metal1"])
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=self.vdd_position,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
layer="metal1",
|
||||
offset=self.vdd_loc,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
|
||||
def add_ptx(self):
|
||||
""" transistors are added and placed inside the layout """
|
||||
# determines the spacing between the edge and nmos (rail to active
|
||||
# metal or poly_to_poly spacing)
|
||||
self.edge_to_nmos = max(drc["metal1_to_metal1"]
|
||||
- self.nmos1.active_contact_positions[0].y,
|
||||
- self.nmos.active_contact_positions[0].y,
|
||||
0.5 * drc["poly_to_poly"] - 0.5 * drc["minwidth_metal1"]
|
||||
- self.nmos1.poly_positions[0].y)
|
||||
- self.nmos.poly_positions[0].y)
|
||||
|
||||
# Extra offset is calculated to make room for B and C contancts
|
||||
xoffset = (self.nmos1.active_contact.width
|
||||
xoffset = (self.nmos.active_contact.width
|
||||
+ drc["minwidth_metal1"]
|
||||
+ (drc["metal1_to_metal1"] - drc["well_enclosure_active"]))
|
||||
|
||||
# determine the position of the first transistor from the left
|
||||
self.nmos_position1 = vector(xoffset,
|
||||
0.5 * drc["minwidth_metal1"] + self.edge_to_nmos)
|
||||
offset = self.nmos_position1 + vector(0,self.nmos1.height)
|
||||
self.add_inst(name="nmos1",
|
||||
mod=self.nmos1,
|
||||
offset = self.nmos_position1 + vector(0,self.nmos.height)
|
||||
self.add_inst(name="nmos",
|
||||
mod=self.nmos,
|
||||
offset=offset,
|
||||
mirror="MX")
|
||||
self.connect_inst(["net2", "A", "gnd", "gnd"])
|
||||
|
||||
self.nmos_position2 = (self.nmos_position1
|
||||
+ vector(self.nmos2.active_width,0)
|
||||
- vector(self.nmos2.active_contact.width,0))
|
||||
offset = self.nmos_position2 + vector(0, self.nmos2.height)
|
||||
+ vector(self.nmos.active_width,0)
|
||||
- vector(self.nmos.active_contact.width,0))
|
||||
offset = self.nmos_position2 + vector(0, self.nmos.height)
|
||||
self.add_inst(name="nmos2",
|
||||
mod=self.nmos2,
|
||||
mod=self.nmos,
|
||||
offset=offset,
|
||||
mirror="MX")
|
||||
self.connect_inst(["net1", "B", "net2", "gnd"])
|
||||
|
||||
p2tp3 = vector(self.nmos3.active_width - self.nmos3.active_contact.width,
|
||||
self.nmos3.height)
|
||||
p2tp3 = vector(self.nmos.active_width - self.nmos.active_contact.width,
|
||||
self.nmos.height)
|
||||
self.nmos_position3 = self.nmos_position2 + p2tp3
|
||||
self.add_inst(name="nmos3",
|
||||
mod=self.nmos3,
|
||||
mod=self.nmos,
|
||||
offset=self.nmos_position3,
|
||||
mirror="MX")
|
||||
self.connect_inst(["Z", "C", "net1", "gnd"])
|
||||
|
||||
# determines the spacing between the edge and pmos
|
||||
self.edge_to_pmos = max(drc["metal1_to_metal1"]
|
||||
- self.pmos1.active_contact_positions[0].y,
|
||||
- self.pmos.active_contact_positions[0].y,
|
||||
0.5 * drc["poly_to_poly"] - 0.5 * drc["minwidth_metal1"]
|
||||
- self.pmos1.poly_positions[0].y)
|
||||
- self.pmos.poly_positions[0].y)
|
||||
|
||||
self.pmos_position1 = vector(self.nmos_position1.x,
|
||||
self.height - 0.5 * drc["minwidth_metal1"]
|
||||
- self.pmos1.height - self.edge_to_pmos)
|
||||
- self.pmos.height - self.edge_to_pmos)
|
||||
self.add_inst(name="pmos1",
|
||||
mod=self.pmos1,
|
||||
mod=self.pmos,
|
||||
offset=self.pmos_position1)
|
||||
self.connect_inst(["Z", "A", "vdd", "vdd"])
|
||||
|
||||
self.pmos_position2 = vector(self.nmos_position2.x, self.pmos_position1.y)
|
||||
self.add_inst(name="pmos2",
|
||||
mod=self.pmos2,
|
||||
mod=self.pmos,
|
||||
offset=self.pmos_position2)
|
||||
self.connect_inst(["vdd", "B", "Z", "vdd"])
|
||||
|
||||
self.pmos_position3 = vector(self.nmos_position3.x, self.pmos_position1.y)
|
||||
self.add_inst(name="pmos3",
|
||||
mod=self.pmos3,
|
||||
mod=self.pmos,
|
||||
offset=self.pmos_position3)
|
||||
self.connect_inst(["Z", "C", "vdd", "vdd"])
|
||||
|
||||
|
|
@ -196,20 +179,20 @@ class nand_3(design.design):
|
|||
""" create well contacts """
|
||||
layer_stack = ("active", "contact", "metal1")
|
||||
|
||||
xoffset = (self.nmos_position3.x + self.pmos1.active_position.x
|
||||
+ self.pmos1.active_width + drc["active_to_body_active"])
|
||||
yoffset = self.pmos_position1.y + self.pmos1.active_contact_positions[0].y
|
||||
xoffset = (self.nmos_position3.x + self.pmos.active_position.x
|
||||
+ self.pmos.active_width + drc["active_to_body_active"])
|
||||
yoffset = self.pmos_position1.y + self.pmos.active_contact_positions[0].y
|
||||
self.nwell_contact_position = vector(xoffset, yoffset)
|
||||
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos1.num_of_tacts))
|
||||
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos.num_of_tacts))
|
||||
|
||||
xoffset = self.nmos_position3.x + (self.nmos1.active_position.x
|
||||
+ self.nmos1.active_width
|
||||
xoffset = self.nmos_position3.x + (self.nmos.active_position.x
|
||||
+ self.nmos.active_width
|
||||
+ drc["active_to_body_active"])
|
||||
yoffset = self.nmos_position1.y + (self.nmos1.height
|
||||
- self.nmos1.active_contact_positions[0].y
|
||||
- self.nmos1.active_contact.height)
|
||||
yoffset = self.nmos_position1.y + (self.nmos.height
|
||||
- self.nmos.active_contact_positions[0].y
|
||||
- self.nmos.active_contact.height)
|
||||
self.pwell_contact_position = vector(xoffset, yoffset)
|
||||
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos1.num_of_tacts))
|
||||
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos.num_of_tacts))
|
||||
|
||||
def connect_well_contacts(self):
|
||||
""" Connect well contacts to vdd and gnd rail """
|
||||
|
|
@ -223,7 +206,7 @@ class nand_3(design.design):
|
|||
width=drc["minwidth_metal1"],
|
||||
height=well_tap_length)
|
||||
|
||||
well_tap_length = self.nmos1.active_height
|
||||
well_tap_length = self.nmos.active_height
|
||||
offset = vector(self.pwell_contact_position
|
||||
+ self.pwell_contact.second_layer_position
|
||||
- self.pwell_contact.first_layer_position).scale(1,0)
|
||||
|
|
@ -234,23 +217,23 @@ class nand_3(design.design):
|
|||
|
||||
def connect_rails(self):
|
||||
""" Connect transistor pmos drains to vdd and nmos drains to gnd rail """
|
||||
correct = vector(self.pmos1.active_contact.width - drc["minwidth_metal1"],
|
||||
correct = vector(self.pmos.active_contact.width - drc["minwidth_metal1"],
|
||||
0).scale(0.5,0)
|
||||
poffset = self.pmos_position1 + self.pmos1.active_contact_positions[0] + correct
|
||||
poffset = self.pmos_position1 + self.pmos.active_contact_positions[0] + correct
|
||||
temp_height = self.height - poffset.y
|
||||
self.add_rect(layer="metal1",
|
||||
offset=poffset,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=temp_height)
|
||||
|
||||
poffset = [self.pmos_position3.x + self.pmos3.active_contact_positions[0].x + correct.x,
|
||||
poffset = [self.pmos_position3.x + self.pmos.active_contact_positions[0].x + correct.x,
|
||||
poffset.y]
|
||||
self.add_rect(layer="metal1",
|
||||
offset=poffset,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=temp_height)
|
||||
|
||||
poffset = self.nmos_position1 + self.nmos1.active_contact_positions[0] + correct
|
||||
poffset = self.nmos_position1 + self.nmos.active_contact_positions[0] + correct
|
||||
self.add_rect(layer="metal1",
|
||||
offset=poffset.scale(1,0),
|
||||
width=drc["minwidth_metal1"],
|
||||
|
|
@ -263,57 +246,57 @@ class nand_3(design.design):
|
|||
|
||||
def connect_poly(self):
|
||||
""" Connect poly """
|
||||
yoffset_nmos1 = (self.nmos_position1.y + self.nmos1.poly_positions[0].y
|
||||
+ self.nmos1.poly_height)
|
||||
poly_length = (self.pmos_position1.y + self.pmos1.poly_positions[0].y
|
||||
- yoffset_nmos1 + drc["minwidth_poly"])
|
||||
yoffset_nmos = (self.nmos_position1.y + self.nmos.poly_positions[0].y
|
||||
+ self.nmos.poly_height)
|
||||
poly_length = (self.pmos_position1.y + self.pmos.poly_positions[0].y
|
||||
- yoffset_nmos + drc["minwidth_poly"])
|
||||
|
||||
offset = vector(self.nmos_position1.x + self.nmos1.poly_positions[0].x,
|
||||
yoffset_nmos1 - drc["minwidth_poly"])
|
||||
offset = vector(self.nmos_position1.x + self.nmos.poly_positions[0].x,
|
||||
yoffset_nmos - drc["minwidth_poly"])
|
||||
self.add_rect(layer="poly",
|
||||
offset=offset,
|
||||
width=drc["minwidth_poly"],
|
||||
height=poly_length)
|
||||
self.add_rect(layer="poly",
|
||||
offset=[offset.x + self.pmos1.active_contact.width + 2 * drc["minwidth_poly"],
|
||||
offset=[offset.x + self.pmos.active_contact.width + 2 * drc["minwidth_poly"],
|
||||
offset.y],
|
||||
width=drc["minwidth_poly"],
|
||||
height=poly_length)
|
||||
self.add_rect(layer="poly",
|
||||
offset=[offset.x + 2 * self.pmos1.active_contact.width + 4 * drc["minwidth_poly"],
|
||||
offset=[offset.x + 2 * self.pmos.active_contact.width + 4 * drc["minwidth_poly"],
|
||||
offset.y],
|
||||
width=drc["minwidth_poly"],
|
||||
height=poly_length)
|
||||
|
||||
def connect_drains(self):
|
||||
""" Connect pmos and nmos drains. The output will be routed to this connection point. """
|
||||
yoffset = drc["minwidth_metal1"] + (self.nmos1.active_contact_positions[0].y
|
||||
yoffset = drc["minwidth_metal1"] + (self.nmos.active_contact_positions[0].y
|
||||
- drc["well_enclosure_active"]
|
||||
+ drc["metal1_to_metal1"])
|
||||
drain_length = (self.height - yoffset + 0.5 * drc["minwidth_metal1"]
|
||||
- (self.pmos1.height - self.pmos1.active_contact_positions[0].y))
|
||||
- (self.pmos.height - self.pmos.active_contact_positions[0].y))
|
||||
layer_stack = ("metal1", "via1", "metal2")
|
||||
for position in self.pmos1.active_contact_positions[1:][::2]:
|
||||
diff_active_via = self.pmos2.active_contact.width - self.m1m2_via.second_layer_width
|
||||
offset = (self.pmos_position2 + self.pmos2.active_contact_positions[0]
|
||||
for position in self.pmos.active_contact_positions[1:][::2]:
|
||||
diff_active_via = self.pmos.active_contact.width - self.m1m2_via.second_layer_width
|
||||
offset = (self.pmos_position2 + self.pmos.active_contact_positions[0]
|
||||
+ vector(diff_active_via / 2,0))
|
||||
self.add_via(layer_stack,offset)
|
||||
|
||||
width = (2 * self.pmos3.active_width
|
||||
- self.pmos3.active_contact.width
|
||||
- (self.pmos2.active_contact.width
|
||||
width = (2 * self.pmos.active_width
|
||||
- self.pmos.active_contact.width
|
||||
- (self.pmos.active_contact.width
|
||||
- self.m1m2_via.second_layer_width))
|
||||
self.add_rect(layer="metal2",
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=self.m1m2_via.second_layer_width)
|
||||
|
||||
xoffset = (self.pmos_position3.x + self.pmos3.active_contact_positions[1].x
|
||||
xoffset = (self.pmos_position3.x + self.pmos.active_contact_positions[1].x
|
||||
+ diff_active_via / 2)
|
||||
self.add_via(layer_stack,[xoffset,offset.y])
|
||||
|
||||
xoffset = (self.nmos_position3.x + self.nmos3.active_position.x
|
||||
+ self.nmos3.active_width - self.nmos3.active_contact.width / 2)
|
||||
xoffset = (self.nmos_position3.x + self.nmos.active_position.x
|
||||
+ self.nmos.active_width - self.nmos.active_contact.width / 2)
|
||||
self.drain_position = vector(xoffset,
|
||||
drc["minwidth_metal1"] + drc["metal1_to_metal1"])
|
||||
length = self.height - 2 * (drc["minwidth_metal1"] + drc["metal1_to_metal1"])
|
||||
|
|
@ -332,140 +315,72 @@ class nand_3(design.design):
|
|||
def route_input_gate_A(self):
|
||||
""" routing for input A """
|
||||
|
||||
offset = (self.pmos_position1
|
||||
+ self.pmos1.poly_positions[0]
|
||||
- vector(drc["minwidth_poly"] / 2, self.poly_contact.width))
|
||||
self.add_contact(layers=("poly", "contact", "metal1"),
|
||||
offset=offset,
|
||||
rotate=90)
|
||||
self.add_rect(layer="poly",
|
||||
offset=offset + vector(drc["minwidth_poly"] / 2,0),
|
||||
width=-(self.poly_contact.first_layer_position.y + drc["minwidth_poly"]),
|
||||
height=self.poly_contact.first_layer_width)
|
||||
offset = self.pmos_position1 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height)
|
||||
via_offset = offset + self.poly_contact.first_layer_position.scale(-1,0) - vector(self.poly_contact.first_layer_width,0) + vector(drc["minwidth_poly"],0)
|
||||
|
||||
self.add_contact(layers=("poly", "contact", "metal1"),
|
||||
offset=via_offset)
|
||||
|
||||
offset = vector(offset.x,
|
||||
self.pmos_position1.y + self.pmos1.poly_positions[0].y)
|
||||
self.add_layout_pin(text="A",
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
width=-offset.x,
|
||||
height=-drc["minwidth_metal1"])
|
||||
self.A_position = vector(0, offset.y - drc["minwidth_metal1"])
|
||||
layer="metal1",
|
||||
offset=offset.scale(0,1),
|
||||
width=offset.x,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
|
||||
def route_input_gate_B(self):
|
||||
""" routing for input B """
|
||||
xoffset = self.pmos2.poly_positions[0].x \
|
||||
+ self.pmos_position2.x - drc["minwidth_poly"]
|
||||
yoffset = self.nmos_position1.y + self.nmos1.height \
|
||||
- drc["well_enclosure_active"] + (self.nmos1.active_contact.height \
|
||||
- self.nmos1.active_height) / 2 \
|
||||
+ drc["metal1_to_metal1"]
|
||||
|
||||
offset = self.pmos_position2 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height+1*(self.m1m2_via.width+drc["metal1_to_metal1"]))
|
||||
via_offset = offset + self.poly_contact.first_layer_position.scale(-1,0) - vector(self.poly_contact.first_layer_width,0) + vector(drc["minwidth_poly"],0)
|
||||
|
||||
self.add_contact(layers=("poly", "contact", "metal1"),
|
||||
offset=[xoffset,yoffset])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[xoffset,yoffset])
|
||||
offset=via_offset)
|
||||
|
||||
xoffset = self.pmos2.poly_positions[0].x + self.pmos_position2.x \
|
||||
- drc["minwidth_poly"] + self.m1m2_via.width
|
||||
length = -xoffset + self.m1m2_via.width
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[xoffset, yoffset],
|
||||
width=length,
|
||||
height=-drc["minwidth_metal2"])
|
||||
|
||||
self.B_position = vector(0, yoffset - drc["minwidth_metal1"])
|
||||
self.add_label(text="B",
|
||||
layer="metal1",
|
||||
offset=self.B_position)
|
||||
|
||||
xoffset = self.pmos_position1.x + self.pmos1.active_position.x \
|
||||
- drc["metal1_to_metal1"] + (self.pmos1.active_contact.width \
|
||||
- self.m1m2_via.second_layer_width) / 2
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[xoffset,yoffset - drc["minwidth_metal2"]],
|
||||
rotate=90)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[xoffset, yoffset],
|
||||
width=-xoffset,
|
||||
height=-drc["minwidth_metal1"])
|
||||
self.add_layout_pin(text="B",
|
||||
layer="metal1",
|
||||
offset=offset.scale(0,1),
|
||||
width=offset.x,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
|
||||
def route_input_gate_C(self):
|
||||
""" routing for input A """
|
||||
xoffset = self.pmos3.poly_positions[0].x \
|
||||
+ self.pmos_position3.x - drc["minwidth_poly"]
|
||||
yoffset = self.nmos_position1.y + self.nmos1.height \
|
||||
- drc["well_enclosure_active"] + (self.nmos1.active_contact.height \
|
||||
- self.nmos1.active_height) / 2 + drc["metal1_to_metal1"]
|
||||
""" routing for input C """
|
||||
offset = self.pmos_position3 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height+2*(self.m1m2_via.width+drc["metal1_to_metal1"]))
|
||||
via_offset = offset + self.poly_contact.first_layer_position.scale(-1,0) - vector(self.poly_contact.first_layer_width,0) + vector(drc["minwidth_poly"],0)
|
||||
|
||||
self.add_contact(layers=("poly", "contact", "metal1"),
|
||||
offset=[xoffset,yoffset])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[xoffset,yoffset])
|
||||
offset=via_offset)
|
||||
|
||||
xoffset = self.pmos3.poly_positions[0].x \
|
||||
+ self.pmos_position3.x - drc["minwidth_poly"] \
|
||||
+ self.m1m2_via.width
|
||||
length = -xoffset + self.m1m2_via.width
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[xoffset,
|
||||
yoffset - drc["minwidth_metal2"] - drc["metal2_to_metal2"]],
|
||||
width=length,
|
||||
height=-drc["minwidth_metal2"])
|
||||
|
||||
# FIXME: Convert to add_layout_pin?
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[xoffset - self.m1m2_via.width,
|
||||
yoffset],
|
||||
width=self.m1m2_via.width,
|
||||
height=-drc["minwidth_metal2"] - drc["metal2_to_metal2"])
|
||||
self.C_position = vector(0,
|
||||
self.B_position.y - drc["metal2_to_metal2"] - drc["minwidth_metal1"] \
|
||||
- (self.m1m2_via.second_layer_width
|
||||
- self.m1m2_via.first_layer_width))
|
||||
self.add_label(text="C",
|
||||
layer="metal1",
|
||||
offset=self.C_position)
|
||||
|
||||
xoffset = self.pmos_position1.x + self.pmos1.active_position.x \
|
||||
- drc["metal1_to_metal1"] + (self.pmos1.active_contact.width \
|
||||
- self.m1m2_via.second_layer_width) / 2
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[xoffset,
|
||||
yoffset - 2 * drc["minwidth_metal2"] - self.m1m2_via.width],
|
||||
rotate=90)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[xoffset,
|
||||
yoffset - 2 * drc["minwidth_metal2"]],
|
||||
width=-xoffset,
|
||||
height=-drc["minwidth_metal1"])
|
||||
self.add_layout_pin(text="C",
|
||||
layer="metal1",
|
||||
offset=offset.scale(0,1),
|
||||
width=offset.x,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
def route_output(self):
|
||||
""" routing for output Z """
|
||||
xoffset = self.nmos_position3.x + self.nmos3.active_position.x \
|
||||
+ self.nmos3.active_width - self.nmos3.active_contact.width / 2
|
||||
yoffset = (self.nmos1.height + (self.height - drc["minwidth_metal1"]
|
||||
- self.pmos1.height - self.nmos1.height) / 2
|
||||
xoffset = self.nmos_position3.x + self.nmos.active_position.x \
|
||||
+ self.nmos.active_width - self.nmos.active_contact.width / 2
|
||||
yoffset = (self.nmos.height + (self.height - drc["minwidth_metal1"]
|
||||
- self.pmos.height - self.nmos.height) / 2
|
||||
- (drc["minwidth_metal1"] / 2))
|
||||
# FIXME Convert to add_layout_pin?
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[xoffset, yoffset],
|
||||
width=self.well_width - xoffset,
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_layout_pin(text="Z",
|
||||
layer="metal1",
|
||||
offset=vector(xoffset, yoffset),
|
||||
width=self.well_width - xoffset,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
self.Z_position = vector(self.well_width, yoffset)
|
||||
self.add_label(text="Z",
|
||||
layer="metal1",
|
||||
offset=self.Z_position)
|
||||
|
||||
def extend_wells(self):
|
||||
""" extension of well """
|
||||
middle_point = self.nmos_position1.y + self.nmos1.pwell_position.y \
|
||||
+ self.nmos1.well_height + (self.pmos_position1.y
|
||||
+ self.pmos1.nwell_position.y
|
||||
middle_point = self.nmos_position1.y + self.nmos.pwell_position.y \
|
||||
+ self.nmos.well_height + (self.pmos_position1.y
|
||||
+ self.pmos.nwell_position.y
|
||||
- self.nmos_position1.y
|
||||
- self.nmos1.pwell_position.y
|
||||
- self.nmos1.well_height) / 2
|
||||
- self.nmos.pwell_position.y
|
||||
- self.nmos.well_height) / 2
|
||||
offset = self.nwell_position = vector(0, middle_point)
|
||||
self.nwell_height = self.height - middle_point
|
||||
self.add_rect(layer="nwell",
|
||||
|
|
@ -489,45 +404,37 @@ class nand_3(design.design):
|
|||
|
||||
def extend_active(self):
|
||||
""" extension of active region """
|
||||
self.active_width = self.pmos1.active_width \
|
||||
+ drc["active_to_body_active"] + self.pmos1.active_contact.width
|
||||
offset = (self.pmos1.active_position+self.pmos_position3.scale(1,0)
|
||||
self.active_width = self.pmos.active_width \
|
||||
+ drc["active_to_body_active"] + self.pmos.active_contact.width
|
||||
offset = (self.pmos.active_position+self.pmos_position3.scale(1,0)
|
||||
+ self.pmos_position1.scale(0,1))
|
||||
self.add_rect(layer="active",
|
||||
offset=offset,
|
||||
width=self.active_width,
|
||||
height=self.pmos1.active_height)
|
||||
height=self.pmos.active_height)
|
||||
|
||||
offset = offset + vector(self.pmos1.active_width,0)
|
||||
width = self.active_width - self.pmos1.active_width
|
||||
offset = offset + vector(self.pmos.active_width,0)
|
||||
width = self.active_width - self.pmos.active_width
|
||||
self.add_rect(layer="nimplant",
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=self.pmos1.active_height)
|
||||
height=self.pmos.active_height)
|
||||
|
||||
offset = [self.nmos_position3.x + self.nmos1.active_position.x,
|
||||
self.nmos_position1.y + self.nmos1.height
|
||||
- self.nmos1.active_position.y - self.nmos1.active_height]
|
||||
offset = [self.nmos_position3.x + self.nmos.active_position.x,
|
||||
self.nmos_position1.y + self.nmos.height
|
||||
- self.nmos.active_position.y - self.nmos.active_height]
|
||||
self.add_rect(layer="active",
|
||||
offset=offset,
|
||||
width=self.active_width,
|
||||
height=self.nmos1.active_height)
|
||||
height=self.nmos.active_height)
|
||||
|
||||
offset = offset + vector(self.nmos1.active_width,0)
|
||||
width = self.active_width - self.nmos1.active_width
|
||||
offset = offset + vector(self.nmos.active_width,0)
|
||||
width = self.active_width - self.nmos.active_width
|
||||
self.add_rect(layer="pimplant",
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=self.nmos1.active_height)
|
||||
height=self.nmos.active_height)
|
||||
|
||||
def setup_layout_offsets(self):
|
||||
""" Defining the position of i/o pins for the three input nand gate """
|
||||
self.A_position = self.A_position
|
||||
self.B_position = self.B_position
|
||||
self.C_position = self.C_position
|
||||
self.Z_position = self.Z_position
|
||||
self.vdd_position = self.vdd_position
|
||||
self.gnd_position = self.gnd_position
|
||||
|
||||
def input_load(self):
|
||||
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class nor_2(design.design):
|
|||
|
||||
unique_id = 1
|
||||
|
||||
def __init__(self, nmos_width=1, height=bitcell.chars["height"]):
|
||||
def __init__(self, nmos_width=drc["minwidth_tx"], height=bitcell.height):
|
||||
"""Constructor : Creates a cell for a simple 2 input nor"""
|
||||
name = "nor2_{0}".format(nor_2.unique_id)
|
||||
nor_2.unique_id += 1
|
||||
|
|
@ -47,9 +47,9 @@ class nor_2(design.design):
|
|||
|
||||
# These aren't for instantiating, but we use them to get the dimensions
|
||||
self.nwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"),
|
||||
dimensions=(1, self.pmos1.num_of_tacts))
|
||||
dimensions=(1, self.pmos1.num_of_tacts))
|
||||
self.pwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"),
|
||||
dimensions=(1, self.nmos1.num_of_tacts))
|
||||
dimensions=(1, self.nmos1.num_of_tacts))
|
||||
|
||||
self.setup_layout_constants()
|
||||
self.add_rails()
|
||||
|
|
@ -81,12 +81,12 @@ class nor_2(design.design):
|
|||
0.5 * gate_to_gate - test_nmos.poly_positions[0].y)
|
||||
edge_to_pmos = max(drc["metal1_to_metal1"] - test_pmos.active_contact_positions[0].y,
|
||||
0.5 * gate_to_gate - test_pmos.poly_positions[0].y)
|
||||
route_margin = .5 * (self.poly_contact.second_layer_width
|
||||
+ drc["minwidth_metal1"]) + drc["metal1_to_metal1"]
|
||||
pmos_position = vector(0, self.height - 0.5 * drc["minwidth_metal1"]
|
||||
- edge_to_pmos - test_pmos.height)
|
||||
nmos_position = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos)
|
||||
leftspace = (0.7 * (pmos_position.y - nmos_position.y)
|
||||
route_margin = 0.5 * (self.poly_contact.second_layer_width
|
||||
+ drc["minwidth_metal1"]) + drc["metal1_to_metal1"]
|
||||
pmos_loc = vector(0, self.height - 0.5 * drc["minwidth_metal1"]
|
||||
- edge_to_pmos - test_pmos.height)
|
||||
nmos_loc = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos)
|
||||
leftspace = (0.7 * (pmos_loc.y - nmos_loc.y)
|
||||
- 3 * route_margin - 2 * drc["metal1_to_metal1"])
|
||||
if leftspace >= 0:
|
||||
break
|
||||
|
|
@ -126,28 +126,26 @@ class nor_2(design.design):
|
|||
half_gate_to_gate - self.nmos1.poly_positions[0].y)
|
||||
|
||||
# determine the position of the first transistor from the left
|
||||
self.nmos_position1 = vector(0,
|
||||
0.5 * drc["minwidth_metal1"] + edge_to_nmos)
|
||||
offset = self.nmos_position1 + vector(0,self.nmos1.height)
|
||||
self.nmos_loc1 = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos)
|
||||
offset = self.nmos_loc1 + vector(0,self.nmos1.height)
|
||||
|
||||
x = vector(self.nmos2.active_width - self.nmos2.active_contact.width, 0)
|
||||
self.nmos_position2 = x + self.nmos_position1.scale(0,1)
|
||||
self.nmos_loc2 = x + self.nmos_loc1.scale(0,1)
|
||||
|
||||
# determines the spacing between the edge and pmos
|
||||
edge_to_pmos = max(drc["metal1_to_metal1"] - self.pmos1.active_contact_positions[0].y,
|
||||
half_gate_to_gate - self.pmos1.poly_positions[0].y)
|
||||
self.pmos_position1 = vector(0,
|
||||
self.height - 0.5 * drc["minwidth_metal1"]
|
||||
- edge_to_pmos - self.pmos1.height)
|
||||
self.pmos_position2 = self.pmos_position1 + vector(self.pmos1.width,0)
|
||||
self.pmos_loc1 = vector(0, self.height - 0.5 * drc["minwidth_metal1"]
|
||||
- edge_to_pmos - self.pmos1.height)
|
||||
self.pmos_loc2 = self.pmos_loc1 + vector(self.pmos1.width,0)
|
||||
|
||||
self.well_width = max(self.pmos_position2.x + self.pmos2.active_position.x
|
||||
+ self.pmos2.active_width
|
||||
+ drc["active_to_body_active"] + self.nwell_contact.width
|
||||
+ drc["well_enclosure_active"],
|
||||
self.nmos_position2.x + self.nmos2.active_position.x
|
||||
+ self.nmos2.active_width
|
||||
+ drc["active_to_body_active"] + drc["well_enclosure_active"])
|
||||
self.well_width = max(self.pmos_loc2.x + self.pmos2.active_position.x
|
||||
+ self.pmos2.active_width
|
||||
+ drc["active_to_body_active"] + self.nwell_contact.width
|
||||
+ drc["well_enclosure_active"],
|
||||
self.nmos_loc2.x + self.nmos2.active_position.x
|
||||
+ self.nmos2.active_width
|
||||
+ drc["active_to_body_active"] + drc["well_enclosure_active"])
|
||||
self.width = self.well_width
|
||||
|
||||
def add_rails(self):
|
||||
|
|
@ -155,43 +153,43 @@ class nor_2(design.design):
|
|||
rail_height = drc["minwidth_metal1"]
|
||||
self.rail_height = rail_height
|
||||
# Relocate the origin
|
||||
self.gnd_position = vector(0, - 0.5 * drc["minwidth_metal1"])
|
||||
self.gnd_loc = vector(0, - 0.5 * drc["minwidth_metal1"])
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=self.gnd_position,
|
||||
offset=self.gnd_loc,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
|
||||
self.vdd_position = self.gnd_position + vector(0, self.height)
|
||||
self.vdd_loc = self.gnd_loc + vector(0, self.height)
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=self.vdd_position,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
layer="metal1",
|
||||
offset=self.vdd_loc,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
|
||||
def add_ptx(self):
|
||||
""" transistors are placed in the layout"""
|
||||
offset = self.nmos_position1 + vector(0, self.nmos1.height)
|
||||
offset = self.nmos_loc1 + vector(0, self.nmos1.height)
|
||||
self.add_inst(name="nmos1",
|
||||
mod=self.nmos1,
|
||||
offset=offset,
|
||||
mirror="MX")
|
||||
self.connect_inst(["Z", "A", "gnd", "gnd"])
|
||||
|
||||
offset = self.nmos_position2 + vector(0, self.nmos2.height)
|
||||
offset = self.nmos_loc2 + vector(0, self.nmos2.height)
|
||||
self.add_inst(name="nmos2",
|
||||
mod=self.nmos2,
|
||||
offset=offset,
|
||||
mirror="MX")
|
||||
self.connect_inst(["Z", "B", "gnd", "gnd"])
|
||||
|
||||
offset = self.pmos_position1
|
||||
offset = self.pmos_loc1
|
||||
self.add_inst(name="pmos1",
|
||||
mod=self.pmos1,
|
||||
offset=offset)
|
||||
self.connect_inst(["vdd", "A", "net1", "vdd"])
|
||||
|
||||
offset = self.pmos_position2
|
||||
offset = self.pmos_loc2
|
||||
self.add_inst(name="pmos2",
|
||||
mod=self.pmos2,
|
||||
offset=offset)
|
||||
|
|
@ -200,48 +198,48 @@ class nor_2(design.design):
|
|||
def add_well_contacts(self):
|
||||
layer_stack = ("active", "contact", "metal1")
|
||||
|
||||
xoffset = (self.pmos_position2.x + self.pmos1.active_position.x
|
||||
xoffset = (self.pmos_loc2.x + self.pmos1.active_position.x
|
||||
+ self.pmos1.active_width + drc["active_to_body_active"])
|
||||
yoffset = (self.pmos_position1.y
|
||||
yoffset = (self.pmos_loc1.y
|
||||
+ self.pmos1.active_contact_positions[0].y)
|
||||
self.nwell_contact_position = vector(xoffset, yoffset)
|
||||
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos1.num_of_tacts))
|
||||
self.nwell_contact_loc = vector(xoffset, yoffset)
|
||||
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_loc,(1,self.pmos1.num_of_tacts))
|
||||
|
||||
xoffset = self.nmos_position2.x + (self.nmos1.active_position.x
|
||||
+ self.nmos1.active_width
|
||||
+ drc["active_to_body_active"])
|
||||
yoffset = (self.nmos_position1.y + self.nmos1.height
|
||||
- self.nmos1.active_contact_positions[0].y
|
||||
- self.nmos1.active_contact.height)
|
||||
self.pwell_contact_position = vector(xoffset, yoffset)
|
||||
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos1.num_of_tacts))
|
||||
xoffset = self.nmos_loc2.x + (self.nmos1.active_position.x
|
||||
+ self.nmos1.active_width
|
||||
+ drc["active_to_body_active"])
|
||||
yoffset = (self.nmos_loc1.y + self.nmos1.height
|
||||
- self.nmos1.active_contact_positions[0].y
|
||||
- self.nmos1.active_contact.height)
|
||||
self.pwell_contact_loc = vector(xoffset, yoffset)
|
||||
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_loc,(1,self.nmos1.num_of_tacts))
|
||||
|
||||
|
||||
def route(self):
|
||||
self.route_pins()
|
||||
self.connect_well_contacts()
|
||||
M1_track = (self.B_position.y + drc["metal1_to_metal1"]
|
||||
+ .5 * (self.poly_contact.second_layer_width
|
||||
+ drc["minwidth_metal1"]))
|
||||
M1_track = (self.B_loc.y + drc["metal1_to_metal1"]
|
||||
+ .5 * (self.poly_contact.second_layer_width
|
||||
+ drc["minwidth_metal1"]))
|
||||
self.connect_tx(M1_track)
|
||||
self.connect_poly()
|
||||
|
||||
def connect_well_contacts(self):
|
||||
""" Connect well contacts to vdd and gnd rail """
|
||||
well_tap_length = self.height - self.nwell_contact_position.y
|
||||
xoffset = (self.nwell_contact_position.x
|
||||
+ self.nwell_contact.second_layer_position.x
|
||||
- self.nwell_contact.first_layer_position.x)
|
||||
offset = [xoffset, self.nwell_contact_position.y]
|
||||
well_tap_length = self.height - self.nwell_contact_loc.y
|
||||
xoffset = (self.nwell_contact_loc.x
|
||||
+ self.nwell_contact.second_layer_position.x
|
||||
- self.nwell_contact.first_layer_position.x)
|
||||
offset = [xoffset, self.nwell_contact_loc.y]
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=well_tap_length)
|
||||
|
||||
offset = (self.pwell_contact_position.scale(1,0)
|
||||
+ self.pwell_contact.second_layer_position.scale(1,0)
|
||||
- self.pwell_contact.first_layer_position.scale(1,0))
|
||||
well_tap_length = self.pwell_contact_position.y
|
||||
|
||||
offset = (self.pwell_contact_loc.scale(1,0)
|
||||
+ self.pwell_contact.second_layer_position.scale(1,0)
|
||||
- self.pwell_contact.first_layer_position.scale(1,0))
|
||||
well_tap_length = self.pwell_contact_loc.y
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal1"],
|
||||
|
|
@ -251,11 +249,11 @@ class nor_2(design.design):
|
|||
"""Connect transistor pmos drains to vdd and nmos drains to gnd rail"""
|
||||
# the first pmos drain to Vdd
|
||||
for i in range(len(self.pmos1.active_contact_positions)):
|
||||
contact_pos = self.pmos_position1 + self.pmos1.active_contact_positions[i]
|
||||
contact_pos = self.pmos_loc1 + self.pmos1.active_contact_positions[i]
|
||||
if i % 2 == 0:
|
||||
correct = self.pmos1.active_contact.second_layer_position.scale(1,0)
|
||||
drain_posistion = contact_pos + correct
|
||||
height = self.vdd_position.y - drain_posistion.y
|
||||
height = self.vdd_loc.y - drain_posistion.y
|
||||
self.add_rect(layer="metal1",
|
||||
offset=drain_posistion,
|
||||
width=drc["minwidth_metal1"],
|
||||
|
|
@ -263,36 +261,36 @@ class nor_2(design.design):
|
|||
else:
|
||||
# source to pmos2
|
||||
correct = (self.pmos1.active_contact.second_layer_position.scale(1,0)
|
||||
+ vector(self.pmos1.active_contact.second_layer_width,
|
||||
0).scale(.5,0))
|
||||
source_position = contact_pos + correct
|
||||
mid = [self.pmos_position2.x, M1_track]
|
||||
self.add_path("metal1", [source_position, mid])
|
||||
+ vector(self.pmos1.active_contact.second_layer_width,
|
||||
0).scale(.5,0))
|
||||
source_loc = contact_pos + correct
|
||||
mid = [self.pmos_loc2.x, M1_track]
|
||||
self.add_path("metal1", [source_loc, mid])
|
||||
|
||||
# the second pmos
|
||||
for i in range(len(self.pmos2.active_contact_positions)):
|
||||
if i % 2 == 0:
|
||||
# source to pmos2
|
||||
pmos_active =self.pmos_position2+self.pmos2.active_contact_positions[i]
|
||||
pmos_active =self.pmos_loc2+self.pmos2.active_contact_positions[i]
|
||||
correct= (self.pmos2.active_contact.second_layer_position.scale(1,0)
|
||||
+ vector(0.5 * self.pmos2.active_contact.second_layer_width,0))
|
||||
source_position = pmos_active + correct
|
||||
mid = [self.pmos_position2.x, M1_track]
|
||||
self.add_path("metal1", [source_position, mid])
|
||||
+ vector(0.5 * self.pmos2.active_contact.second_layer_width,0))
|
||||
source_loc = pmos_active + correct
|
||||
mid = [self.pmos_loc2.x, M1_track]
|
||||
self.add_path("metal1", [source_loc, mid])
|
||||
# two nmos source to gnd
|
||||
source_posistion1 = (self.nmos_position1
|
||||
+ self.nmos1.active_contact_positions[0]
|
||||
+ self.nmos1.active_contact.second_layer_position.scale(1,0))
|
||||
height = self.gnd_position.y - source_posistion1.y
|
||||
source_posistion1 = (self.nmos_loc1
|
||||
+ self.nmos1.active_contact_positions[0]
|
||||
+ self.nmos1.active_contact.second_layer_position.scale(1,0))
|
||||
height = self.gnd_loc.y - source_posistion1.y
|
||||
self.add_rect(layer="metal1",
|
||||
offset=source_posistion1,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=height)
|
||||
|
||||
source_posistion2 = (self.nmos_position2
|
||||
+ self.nmos2.active_contact_positions[1]
|
||||
+ self.nmos2.active_contact.second_layer_position.scale(1,0))
|
||||
height = self.gnd_position.y - source_posistion2.y
|
||||
source_posistion2 = (self.nmos_loc2
|
||||
+ self.nmos2.active_contact_positions[1]
|
||||
+ self.nmos2.active_contact.second_layer_position.scale(1,0))
|
||||
height = self.gnd_loc.y - source_posistion2.y
|
||||
self.add_rect(layer="metal1",
|
||||
offset=source_posistion2,
|
||||
width=drc["minwidth_metal1"],
|
||||
|
|
@ -301,29 +299,29 @@ class nor_2(design.design):
|
|||
def connect_poly(self):
|
||||
"""connect connect poly between nmos and pmos"""
|
||||
# connect pmos1 poly
|
||||
nmos_gate = (self.nmos_position1
|
||||
nmos_gate = (self.nmos_loc1
|
||||
+ self.nmos1.poly_positions[0]
|
||||
+ vector(0.5 * drc["minwidth_poly"], 0))
|
||||
for i in range(len(self.pmos1.poly_positions)):
|
||||
pmos_gate = (self.pmos_position1
|
||||
+ self.pmos1.poly_positions[i]
|
||||
+ vector(0.5 * drc["minwidth_poly"], 0))
|
||||
pmos_gate = (self.pmos_loc1
|
||||
+ self.pmos1.poly_positions[i]
|
||||
+ vector(0.5 * drc["minwidth_poly"], 0))
|
||||
mid1 = [pmos_gate.x, pmos_gate.y - drc["poly_to_active"]]
|
||||
self.add_path("poly", [nmos_gate, mid1, pmos_gate])
|
||||
|
||||
# connect pmos2 poly
|
||||
nmos_gate = vector(self.nmos_position2[0]
|
||||
+ self.nmos2.poly_positions[0].x
|
||||
+ 0.5 * drc["minwidth_poly"],
|
||||
self.nmos_position1.y
|
||||
+ self.nmos1.poly_positions[0].y)
|
||||
nmos_gate = vector(self.nmos_loc2[0]
|
||||
+ self.nmos2.poly_positions[0].x
|
||||
+ 0.5 * drc["minwidth_poly"],
|
||||
self.nmos_loc1.y
|
||||
+ self.nmos1.poly_positions[0].y)
|
||||
for i in range(len(self.pmos2.poly_positions)):
|
||||
pmos_gate = (self.pmos_position2
|
||||
+ self.pmos2.poly_positions[i]
|
||||
+ vector(0.5 * drc["minwidth_poly"], 0))
|
||||
pmos_gate = (self.pmos_loc2
|
||||
+ self.pmos2.poly_positions[i]
|
||||
+ vector(0.5 * drc["minwidth_poly"], 0))
|
||||
mid1 = vector(pmos_gate.x,
|
||||
nmos_gate.y + self.nmos2.height
|
||||
+ drc["poly_to_active"])
|
||||
+ drc["poly_to_active"])
|
||||
self.add_path("poly", [nmos_gate, mid1, pmos_gate])
|
||||
|
||||
def route_pins(self):
|
||||
|
|
@ -337,41 +335,39 @@ class nor_2(design.design):
|
|||
def route_input_A(self):
|
||||
"""create input A layout"""
|
||||
xoffset = self.nmos1.poly_positions[0].x
|
||||
yoffset = self.nmos_position1.y + self.nmos1.height \
|
||||
+ 0.3 * (self.pmos_position1.y - self.nmos_position1.y \
|
||||
yoffset = self.nmos_loc1.y + self.nmos1.height \
|
||||
+ 0.3 * (self.pmos_loc1.y - self.nmos_loc1.y \
|
||||
- self.nmos1.height)
|
||||
self.A_position = vector(xoffset, yoffset)
|
||||
self.A_loc = vector(xoffset, yoffset)
|
||||
# gate input
|
||||
offset = self.A_position - vector(0, 0.5 * self.poly_contact.width)
|
||||
offset = self.A_loc - vector(0, 0.5 * self.poly_contact.width)
|
||||
self.add_contact(layers=("poly", "contact", "metal1"),
|
||||
offset=offset,
|
||||
rotate=90)
|
||||
|
||||
# connect gate input to tx gate
|
||||
offset = self.A_position - vector(self.poly_contact.first_layer_position.y,
|
||||
0.5 * self.poly_contact.width)
|
||||
offset = self.A_loc - vector(self.poly_contact.first_layer_position.y,
|
||||
0.5 * self.poly_contact.width)
|
||||
self.add_rect(layer="poly",
|
||||
offset=offset,
|
||||
width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"],
|
||||
height=self.poly_contact.first_layer_width)
|
||||
# extend the metal to the boundary of the cell
|
||||
input_length = self.A_position.x
|
||||
offset = [0, self.A_position.y - 0.5 * drc["minwidth_metal1"]]
|
||||
input_length = self.A_loc.x
|
||||
offset = [0, self.A_loc.y - 0.5 * drc["minwidth_metal1"]]
|
||||
self.add_layout_pin(text="A",
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
width=input_length,
|
||||
height=drc["minwidth_metal1"])
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
width=input_length,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
def route_input_B(self):
|
||||
"""create input B layout """
|
||||
xoffset = self.pmos2.poly_positions[0].x \
|
||||
+ self.pmos_position2.x
|
||||
yoffset = self.A_position.y \
|
||||
+ 0.5 * (self.poly_contact.second_layer_width \
|
||||
+ drc["minwidth_metal1"]) + drc["metal1_to_metal1"]
|
||||
self.B_position = vector(xoffset, yoffset)
|
||||
offset = self.B_position - vector(0, 0.5 * self.poly_contact.width)
|
||||
xoffset = self.pmos2.poly_positions[0].x + self.pmos_loc2.x
|
||||
yoffset = self.A_loc.y + 0.5 * (self.poly_contact.second_layer_width \
|
||||
+ drc["minwidth_metal1"]) + drc["metal1_to_metal1"]
|
||||
self.B_loc = vector(xoffset, yoffset)
|
||||
offset = self.B_loc - vector(0, 0.5 * self.poly_contact.width)
|
||||
self.add_contact(layers=("poly", "contact", "metal1"),
|
||||
offset=offset,
|
||||
rotate=90)
|
||||
|
|
@ -381,49 +377,53 @@ class nor_2(design.design):
|
|||
width=-(self.poly_contact.first_layer_position.y + drc["minwidth_poly"]),
|
||||
height=self.poly_contact.first_layer_width)
|
||||
self.add_layout_pin(text="B",
|
||||
layer="metal1",
|
||||
offset=[0,
|
||||
self.B_position.y - 0.5 * drc["minwidth_metal1"]],
|
||||
width=self.B_position.x,
|
||||
height=drc["minwidth_metal1"])
|
||||
layer="metal1",
|
||||
offset=[0, self.B_loc.y - 0.5 * drc["minwidth_metal1"]],
|
||||
width=self.B_loc.x,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
def route_output(self):
|
||||
"""route the output to nmos pmos """
|
||||
self.Z_position = vector(self.width, self.A_position.y)
|
||||
self.Z_loc = vector(self.width, self.A_loc.y)
|
||||
# route nmos drain to Z
|
||||
nmos_contact = (self.nmos_position1
|
||||
+ self.nmos1.active_contact_positions[1]
|
||||
+ self.nmos1.active_contact.second_layer_position
|
||||
+ vector(self.nmos1.active_contact.second_layer_width,
|
||||
0).scale(0.5, 0))
|
||||
mid = [nmos_contact.x, self.A_position.y]
|
||||
self.add_path("metal1", [self.Z_position, mid, nmos_contact])
|
||||
|
||||
nmos_contact = (self.nmos_loc1
|
||||
+ self.nmos1.active_contact_positions[1]
|
||||
+ self.nmos1.active_contact.second_layer_position
|
||||
+ vector(self.nmos1.active_contact.second_layer_width,0).scale(0.5, 0))
|
||||
mid = [nmos_contact.x, self.A_loc.y]
|
||||
self.add_path("metal1", [self.Z_loc, mid, nmos_contact])
|
||||
|
||||
for i in range(len(self.pmos2.poly_positions) + 1):
|
||||
if i % 2 == 1:
|
||||
# pmos2 drain to Z
|
||||
pmos_contact = (self.pmos_position2
|
||||
+ self.pmos1.active_contact_positions[i]
|
||||
+ self.pmos2.active_contact.second_layer_position.scale(1, 0)
|
||||
+ vector(self.pmos2.active_contact.second_layer_width,
|
||||
0).scale(0.5, 0))
|
||||
pmos_contact = (self.pmos_loc2
|
||||
+ self.pmos1.active_contact_positions[i]
|
||||
+ self.pmos2.active_contact.second_layer_position.scale(1, 0)
|
||||
+ vector(self.pmos2.active_contact.second_layer_width,0).scale(0.5, 0))
|
||||
offset = pmos_contact - vector(0.5 * self.m1m2_via.width, 0)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
mid = [pmos_contact.x, self.Z_position.y]
|
||||
mid = [pmos_contact.x, self.Z_loc.y]
|
||||
self.add_wire(("metal1", "via1", "metal2"),
|
||||
[self.Z_position, mid, pmos_contact])
|
||||
[self.Z_loc, mid, pmos_contact])
|
||||
|
||||
self.add_layout_pin(text="Z",
|
||||
layer="metal1",
|
||||
offset=mid - vector(0,0.5*drc["minwidth_metal1"]),
|
||||
width=self.Z_loc.x-mid[0],
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
|
||||
def extend_wells(self):
|
||||
""" extend well for well contact"""
|
||||
middle_point = (self.nmos_position1.y
|
||||
+ self.nmos1.pwell_position.y
|
||||
+ self.nmos1.well_height
|
||||
+ (self.pmos_position1.y
|
||||
+ self.pmos1.nwell_position.y
|
||||
- self.nmos_position1.y
|
||||
- self.nmos1.pwell_position.y
|
||||
- self.nmos1.well_height) / 2 )
|
||||
middle_point = (self.nmos_loc1.y
|
||||
+ self.nmos1.pwell_position.y
|
||||
+ self.nmos1.well_height
|
||||
+ (self.pmos_loc1.y
|
||||
+ self.pmos1.nwell_position.y
|
||||
- self.nmos_loc1.y
|
||||
- self.nmos1.pwell_position.y
|
||||
- self.nmos1.well_height) / 2 )
|
||||
self.nwell_position = vector(0, middle_point)
|
||||
self.nwell_height = self.height - middle_point
|
||||
self.add_rect(layer="nwell",
|
||||
|
|
@ -449,11 +449,11 @@ class nor_2(design.design):
|
|||
def extend_active(self):
|
||||
""" extend active for well contact"""
|
||||
self.active_width = self.pmos1.active_width \
|
||||
+ drc["active_to_body_active"] \
|
||||
+ self.pmos1.active_contact.width
|
||||
offset = (self.pmos_position2.scale(1,0)
|
||||
+ self.pmos_position1.scale(0,1)
|
||||
+ self.pmos1.active_position)
|
||||
+ drc["active_to_body_active"] \
|
||||
+ self.pmos1.active_contact.width
|
||||
offset = (self.pmos_loc2.scale(1,0)
|
||||
+ self.pmos_loc1.scale(0,1)
|
||||
+ self.pmos1.active_position)
|
||||
self.add_rect(layer="active",
|
||||
offset=offset,
|
||||
width=self.active_width,
|
||||
|
|
@ -466,9 +466,9 @@ class nor_2(design.design):
|
|||
height=self.pmos1.active_height)
|
||||
|
||||
offset = (self.nmos1.active_position.scale(1,-1)
|
||||
+ self.nmos_position2.scale(1,0)
|
||||
+ self.nmos_position1.scale(0,1)
|
||||
+ vector(0, self.nmos1.height - self.nmos1.active_height))
|
||||
+ self.nmos_loc2.scale(1,0)
|
||||
+ self.nmos_loc1.scale(0,1)
|
||||
+ vector(0, self.nmos1.height - self.nmos1.active_height))
|
||||
self.add_rect(layer="active",
|
||||
offset=offset,
|
||||
width=self.active_width,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from tech import drc
|
|||
from tech import layer as techlayer
|
||||
import debug
|
||||
from vector import vector
|
||||
from utils import snap_to_grid
|
||||
|
||||
class path():
|
||||
"""
|
||||
|
|
@ -34,7 +35,7 @@ class path():
|
|||
def create_rectilinear_route(self):
|
||||
""" Add intermediate nodes if it isn't rectilinear. Also skip
|
||||
repeated nodes. Also, convert to vector if the aren't."""
|
||||
pl = self.position_list
|
||||
pl = [snap_to_grid(x) for x in self.position_list]
|
||||
|
||||
self.position_list = []
|
||||
for index in range(len(pl) - 1):
|
||||
|
|
@ -74,7 +75,6 @@ class path():
|
|||
using the position list of the corners. """
|
||||
pl = self.position_list # position list
|
||||
for index in range(len(pl) - 1):
|
||||
|
||||
# if we have x motion
|
||||
if pl[index][0] != pl[index + 1][0]:
|
||||
line_length = pl[index + 1][0] - pl[index][0]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
from vector import vector
|
||||
from tech import layer
|
||||
|
||||
class pin_layout:
|
||||
"""
|
||||
A class to represent a rectangular design pin. It is limited to a
|
||||
single shape.
|
||||
"""
|
||||
|
||||
def __init__(self, name, rect, layer_name_num):
|
||||
self.name = name
|
||||
# repack the rect as a vector, just in case
|
||||
if type(rect[0])==vector:
|
||||
self.rect = rect
|
||||
else:
|
||||
self.rect = [vector(rect[0]),vector(rect[1])]
|
||||
# if it's a layer number look up the layer name. this assumes a unique layer number.
|
||||
if type(layer_name_num)==int:
|
||||
self.layer = layer.keys()[layer.values().index(layer_name_num)]
|
||||
else:
|
||||
self.layer=layer_name_num
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
return "({} layer={} ll={} ur={})".format(self.name,self.layer,self.rect[0],self.rect[1])
|
||||
|
||||
def __repr__(self):
|
||||
""" override print function output """
|
||||
return "({} layer={} ll={} ur={})".format(self.name,self.layer,self.rect[0],self.rect[1])
|
||||
|
||||
def height(self):
|
||||
""" Return height. Abs is for pre-normalized value."""
|
||||
return abs(self.rect[1].y-self.rect[0].y)
|
||||
|
||||
def width(self):
|
||||
""" Return width. Abs is for pre-normalized value."""
|
||||
return abs(self.rect[1].x-self.rect[0].x)
|
||||
|
||||
def normalize(self):
|
||||
""" Re-find the LL and UR points after a transform """
|
||||
(first,second)=self.rect
|
||||
ll = vector(min(first[0],second[0]),min(first[1],second[1]))
|
||||
ur = vector(max(first[0],second[0]),max(first[1],second[1]))
|
||||
self.rect=[ll,ur]
|
||||
|
||||
def transform(self,offset,mirror,rotate):
|
||||
""" 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. """
|
||||
(ll,ur) = self.rect
|
||||
if mirror=="MX":
|
||||
ll=ll.scale(1,-1)
|
||||
ur=ur.scale(1,-1)
|
||||
elif mirror=="MY":
|
||||
ll=ll.scale(-1,1)
|
||||
ur=ur.scale(-1,1)
|
||||
elif mirror=="XY":
|
||||
ll=ll.scale(-1,-1)
|
||||
ur=ur.scale(-1,-1)
|
||||
|
||||
if rotate==90:
|
||||
ll=ll.rotate_scale(-1,1)
|
||||
ur=ur.rotate_scale(-1,1)
|
||||
elif rotate==180:
|
||||
ll=ll.scale(-1,-1)
|
||||
ur=ur.scale(-1,-1)
|
||||
elif rotate==270:
|
||||
ll=ll.rotate_scale(1,-1)
|
||||
ur=ur.rotate_scale(1,-1)
|
||||
|
||||
self.rect=[offset+ll,offset+ur]
|
||||
self.normalize()
|
||||
|
||||
def center(self):
|
||||
return vector(0.5*(self.rect[0].x+self.rect[1].x),0.5*(self.rect[0].y+self.rect[1].y))
|
||||
|
||||
def cx(self):
|
||||
""" Center x """
|
||||
return 0.5*(self.rect[0].x+self.rect[1].x)
|
||||
|
||||
def cy(self):
|
||||
""" Center y """
|
||||
return 0.5*(self.rect[0].y+self.rect[1].y)
|
||||
|
||||
# The four possible corners
|
||||
def ll(self):
|
||||
""" Lower left point """
|
||||
return self.rect[0]
|
||||
|
||||
def ul(self):
|
||||
""" Upper left point """
|
||||
return vector(self.rect[0].x,self.rect[1].y)
|
||||
|
||||
def br(self):
|
||||
""" Bottom right point """
|
||||
return vector(self.rect[1].x,self.rect[0].y)
|
||||
|
||||
def ur(self):
|
||||
""" Upper right point """
|
||||
return self.rect[1]
|
||||
|
||||
# The possible y edge values
|
||||
def uy(self):
|
||||
""" Upper y value """
|
||||
return self.rect[1].y
|
||||
|
||||
def by(self):
|
||||
""" Bottom y value """
|
||||
return self.rect[0].y
|
||||
|
||||
# The possible x edge values
|
||||
|
||||
def lx(self):
|
||||
""" Left x value """
|
||||
return self.rect[0].x
|
||||
|
||||
def rx(self):
|
||||
""" Right x value """
|
||||
return self.rect[1].x
|
||||
|
||||
|
||||
# The edge centers
|
||||
def rc(self):
|
||||
""" Right center point """
|
||||
return vector(self.rect[1].x,0.5*(self.rect[0].y+self.rect[1].y))
|
||||
|
||||
def lc(self):
|
||||
""" Left center point """
|
||||
return vector(self.rect[0].x,0.5*(self.rect[0].y+self.rect[1].y))
|
||||
|
||||
def uc(self):
|
||||
""" Upper center point """
|
||||
return vector(0.5*(self.rect[0].x+self.rect[1].x),self.rect[1].y)
|
||||
|
||||
def bc(self):
|
||||
""" Bottom center point """
|
||||
return vector(0.5*(self.rect[0].x+self.rect[1].x),self.rect[0].y)
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ class pinv(design.design):
|
|||
|
||||
unique_id = 1
|
||||
|
||||
def __init__(self, nmos_width=1, beta=3, height=bitcell.chars["height"], route_output=True):
|
||||
def __init__(self, nmos_width=drc["minwidth_tx"], beta=parameter["pinv_beta"], height=bitcell.height, route_output=True):
|
||||
"""Constructor : Creates a cell for a simple inverter"""
|
||||
name = "pinv{0}".format(pinv.unique_id)
|
||||
pinv.unique_id += 1
|
||||
|
|
@ -64,7 +64,6 @@ class pinv(design.design):
|
|||
self.connect_rails()
|
||||
self.connect_tx()
|
||||
self.route_pins()
|
||||
self.setup_layout_offsets()
|
||||
|
||||
def determine_tx_mults(self):
|
||||
"""Determines the number of fingers needed to achieve same size with a height constraint"""
|
||||
|
|
@ -75,7 +74,7 @@ class pinv(design.design):
|
|||
# this should be 2*poly extension beyond active?
|
||||
minwidth_box_poly = 2 * drc["minwidth_poly"] \
|
||||
+ drc["poly_to_poly"]
|
||||
well_to_well = max(drc["pwell_enclose_nwell"],
|
||||
well_to_well = max(drc["pwell_to_nwell"],
|
||||
minwidth_poly_contact,
|
||||
minwidth_box_poly)
|
||||
|
||||
|
|
@ -126,17 +125,17 @@ class pinv(design.design):
|
|||
|
||||
self.gnd_position = vector(0, - 0.5 * drc["minwidth_metal1"]) # for tiling purposes
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=self.gnd_position,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
layer="metal1",
|
||||
offset=self.gnd_position,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
|
||||
self.vdd_position = vector(0, self.height - 0.5 * drc["minwidth_metal1"])
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=self.vdd_position,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
layer="metal1",
|
||||
offset=self.vdd_position,
|
||||
width=rail_width,
|
||||
height=rail_height)
|
||||
|
||||
def add_ptx(self):
|
||||
"""Adds pmos and nmos to the layout"""
|
||||
|
|
@ -294,10 +293,9 @@ class pinv(design.design):
|
|||
width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"],
|
||||
height=self.poly_contact.first_layer_width)
|
||||
|
||||
input_length = self.pmos.poly_positions[0].x \
|
||||
- self.poly_contact.height
|
||||
input_length = self.pmos.poly_positions[0].x - self.poly_contact.height
|
||||
# Determine the y-coordinate for the placement of the metal1 via
|
||||
self.input_position = vector(0, .5*(self.height - drc["minwidth_metal1"]
|
||||
self.input_position = vector(0, 0.5*(self.height - drc["minwidth_metal1"]
|
||||
+ self.nmos.height - self.pmos.height))
|
||||
self.add_layout_pin(text="A",
|
||||
layer="metal1",
|
||||
|
|
@ -313,16 +311,18 @@ class pinv(design.design):
|
|||
self.input_position.y)
|
||||
output_length = self.width - offset.x
|
||||
if self.route_output == True:
|
||||
self.output_position = offset + vector(output_length,0)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=output_length,
|
||||
height=drc["minwidth_metal1"])
|
||||
# This extends the output to the edge of the cell
|
||||
self.add_layout_pin(text="Z",
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
width=output_length,
|
||||
height=drc["minwidth_metal1"])
|
||||
else:
|
||||
self.output_position = offset
|
||||
self.add_label(text="Z",
|
||||
layer="metal1",
|
||||
offset=offset)
|
||||
# This leaves the output as an internal pin (min sized)
|
||||
self.add_layout_pin(text="Z",
|
||||
layer="metal1",
|
||||
offset=offset)
|
||||
|
||||
|
||||
def add_well_contacts(self):
|
||||
"""Adds n/p well taps to the layout"""
|
||||
|
|
@ -403,10 +403,6 @@ class pinv(design.design):
|
|||
self.route_input_gate()
|
||||
self.route_output_drain()
|
||||
|
||||
def setup_layout_offsets(self):
|
||||
self.A_position = self.input_position
|
||||
self.Z_position = self.output_position
|
||||
|
||||
def input_load(self):
|
||||
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
|
||||
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@ class precharge(design.design):
|
|||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
self.bitcell_chars = self.mod_bitcell.chars
|
||||
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.ptx_width = ptx_width
|
||||
self.beta = beta
|
||||
self.width = self.bitcell.width
|
||||
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
|
|
@ -33,7 +34,6 @@ class precharge(design.design):
|
|||
def create_layout(self):
|
||||
self.create_ptx()
|
||||
self.create_contacts()
|
||||
self.setup_layout_constants()
|
||||
self.add_ptx()
|
||||
self.connect_poly()
|
||||
self.add_pclk()
|
||||
|
|
@ -46,11 +46,9 @@ class precharge(design.design):
|
|||
def create_ptx(self):
|
||||
"""Initializes the upper and lower pmos"""
|
||||
self.lower_pmos = ptx(width=self.ptx_width,
|
||||
mults=1,
|
||||
tx_type="pmos")
|
||||
self.add_mod(self.lower_pmos)
|
||||
self.upper_pmos = ptx(width=self.beta * self.ptx_width,
|
||||
mults=1,
|
||||
tx_type="pmos")
|
||||
self.upper_pmos = self.upper_pmos
|
||||
self.add_mod(self.upper_pmos)
|
||||
|
|
@ -72,10 +70,6 @@ class precharge(design.design):
|
|||
self.lower_contact = contact(layer_stack=("metal1", "via1", "metal2"),
|
||||
dimensions=self.lower_dimensions)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.width = self.bitcell_chars["width"]
|
||||
self.BL_position = vector(self.bitcell_chars["BL"][0], 0)
|
||||
self.BR_position = vector(self.bitcell_chars["BR"][0], 0)
|
||||
|
||||
def add_ptx(self):
|
||||
"""Adds both the upper_pmos and lower_pmos to the module"""
|
||||
|
|
@ -144,10 +138,10 @@ class precharge(design.design):
|
|||
offset.y= offset.y + self.poly_contact.second_layer_position.x
|
||||
self.pclk_position = vector(0, offset.y)
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="metal1",
|
||||
offset=self.pclk_position,
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
layer="metal1",
|
||||
offset=self.pclk_position,
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
def add_nwell_contact(self):
|
||||
"""Adds a nwell tap to connect to the vdd rail"""
|
||||
|
|
@ -182,11 +176,11 @@ class precharge(design.design):
|
|||
def add_vdd_rail(self):
|
||||
"""Adds a vdd rail at the top of the cell"""
|
||||
# adds the rail across the width of the cell
|
||||
self.vdd_position = vector(self.pclk_position.x,
|
||||
self.height - drc["minwidth_metal1"])
|
||||
vdd_position = vector(self.pclk_position.x,
|
||||
self.height - drc["minwidth_metal1"])
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=self.vdd_position,
|
||||
offset=vdd_position,
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
|
|
@ -206,16 +200,16 @@ class precharge(design.design):
|
|||
def add_bitlines(self):
|
||||
"""Adds both bit-line and bit-line-bar to the module"""
|
||||
# adds the BL on metal 2
|
||||
offset = self.BL_position - vector(0.5 * drc["minwidth_metal2"],0)
|
||||
self.add_layout_pin(text="bl",
|
||||
offset = vector(self.bitcell.get_pin("BL").cx(),0) - vector(0.5 * drc["minwidth_metal2"],0)
|
||||
self.add_layout_pin(text="BL",
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
width=drc['minwidth_metal2'],
|
||||
height=self.height)
|
||||
|
||||
# adds the BR on metal 2
|
||||
offset = self.BR_position - vector(0.5 * drc["minwidth_metal2"],0)
|
||||
self.add_layout_pin(text="br",
|
||||
offset = vector(self.bitcell.get_pin("BR").cx(),0) - vector(0.5 * drc["minwidth_metal2"],0)
|
||||
self.add_layout_pin(text="BR",
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
width=drc['minwidth_metal2'],
|
||||
|
|
@ -277,8 +271,8 @@ class precharge(design.design):
|
|||
"""Connects bit-lines to lower_pmos"""
|
||||
mos,mos_pos,contact = dest
|
||||
mos_active = (mos_pos + mos.active_contact_positions[0])
|
||||
offset = vector(self.BL_position.x, mos_active.y)
|
||||
xlength = (mos_active.x + correct_x - self.BL_position.x
|
||||
offset = vector(self.bitcell.get_pin("BL").cx() , mos_active.y)
|
||||
xlength = (mos_active.x + correct_x - offset.x
|
||||
+ 0.5 * drc["minwidth_metal2"])
|
||||
self.add_rect(layer="metal2",
|
||||
offset=offset,
|
||||
|
|
@ -290,7 +284,7 @@ class precharge(design.design):
|
|||
mos,mos_pos,contact = dest
|
||||
offset = mos_pos + vector(correct_x,
|
||||
mos.active_contact_positions[0].y)
|
||||
xlength = self.BR_position.x - offset.x - 0.5 * drc["minwidth_metal2"]
|
||||
xlength = self.bitcell.get_pin("BR").cx() - offset.x - 0.5 * drc["minwidth_metal2"]
|
||||
self.add_rect(layer="metal2",
|
||||
offset=offset,
|
||||
width=xlength,
|
||||
|
|
|
|||
|
|
@ -11,14 +11,22 @@ class precharge_array(design.design):
|
|||
of bit line columns, height is the height of the bit-cell array.
|
||||
"""
|
||||
|
||||
def __init__(self, name, columns, ptx_width, beta=2):
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {0}".format(name))
|
||||
def __init__(self, columns, ptx_width, beta=2):
|
||||
design.design.__init__(self, "precharge_array")
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
|
||||
self.columns = columns
|
||||
self.ptx_width = ptx_width
|
||||
self.beta = beta
|
||||
|
||||
self.pc_cell = precharge(name="precharge_cell",
|
||||
ptx_width=self.ptx_width,
|
||||
beta=self.beta)
|
||||
self.add_mod(self.pc_cell)
|
||||
|
||||
self.width = self.columns * self.pc_cell.width
|
||||
self.height = self.pc_cell.height
|
||||
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
|
@ -32,49 +40,23 @@ class precharge_array(design.design):
|
|||
self.add_pin("vdd")
|
||||
|
||||
def create_layout(self):
|
||||
self.create_pc_cell()
|
||||
self.setup_layout_constants()
|
||||
self.add_pc()
|
||||
self.add_rails()
|
||||
self.offset_all_coordinates()
|
||||
self.add_insts()
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.vdd_positions = []
|
||||
self.BL_positions = []
|
||||
self.BR_positions = []
|
||||
|
||||
self.width = self.columns * self.pc_cell.width
|
||||
self.height = self.pc_cell.height
|
||||
|
||||
def add_rails(self):
|
||||
self.add_vdd_rail()
|
||||
self.add_pclk_rail()
|
||||
|
||||
def add_vdd_rail(self):
|
||||
offset = self.pc_cell.vdd_position
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
offset=self.pc_cell.get_pin("vdd").ll(),
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
self.vdd_positions.append(offset)
|
||||
|
||||
def add_pclk_rail(self):
|
||||
self.pclk_position = self.pc_cell.pclk_position
|
||||
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="metal1",
|
||||
offset=self.pclk_position,
|
||||
offset=self.pc_cell.get_pin("clk").ll(),
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
#self.offset_all_coordinates()
|
||||
|
||||
def create_pc_cell(self):
|
||||
"""Initializes a single precharge cell"""
|
||||
self.pc_cell = precharge(name="precharge_cell",
|
||||
ptx_width=self.ptx_width,
|
||||
beta=self.beta)
|
||||
self.add_mod(self.pc_cell)
|
||||
|
||||
def add_pc(self):
|
||||
def add_insts(self):
|
||||
"""Creates a precharge array by horizontally tiling the precharge cell"""
|
||||
self.pc_cell_positions = []
|
||||
for i in range(self.columns):
|
||||
|
|
@ -84,14 +66,12 @@ class precharge_array(design.design):
|
|||
self.add_inst(name=name,
|
||||
mod=self.pc_cell,
|
||||
offset=offset)
|
||||
self.add_label(text="bl[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=offset+ self.pc_cell.BL_position.scale(1,0))
|
||||
self.add_label(text="br[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=offset+ self.pc_cell.BR_position.scale(1,0))
|
||||
self.add_layout_pin(text="bl[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=offset+ self.pc_cell.get_pin("BL").ll().scale(1,0))
|
||||
self.add_layout_pin(text="br[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=offset+ self.pc_cell.get_pin("BR").ll().scale(1,0))
|
||||
self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i),
|
||||
"clk", "vdd"])
|
||||
|
||||
self.BL_positions.append(offset + self.pc_cell.BL_position.scale(1,0))
|
||||
self.BR_positions.append(offset + self.pc_cell.BR_position.scale(1,0))
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ import re
|
|||
class ptx(design.design):
|
||||
"""
|
||||
This module generates gds and spice of a parametrically NMOS or PMOS sized transistor.
|
||||
Creates a simple MOS transistor
|
||||
Creates a simple MOS transistor. poly_positions are the ll of the poly gate. active_contact_positions
|
||||
is an array of the positions of the ll of active contacts (left to right)
|
||||
"""
|
||||
def __init__(self, width=1, mults=1, tx_type="nmos"):
|
||||
def __init__(self, width=drc["minwidth_tx"], mults=1, tx_type="nmos"):
|
||||
name = "{0}_m{1}_w{2}".format(tx_type, mults, width)
|
||||
# remove periods for newer spice compatibility
|
||||
name=re.sub('\.','_',name)
|
||||
|
|
@ -243,8 +244,7 @@ class ptx(design.design):
|
|||
|
||||
# left_most contact column
|
||||
contact_xoffset = 0
|
||||
contact_yoffset = (self.active_height \
|
||||
- self.active_contact.height) / 2
|
||||
contact_yoffset = (self.active_height - self.active_contact.height) / 2
|
||||
offset = vector(contact_xoffset, contact_yoffset)
|
||||
self.add_contact(layers=("active", "contact", "metal1"),
|
||||
offset=offset,
|
||||
|
|
|
|||
|
|
@ -10,13 +10,14 @@ class replica_bitcell(design.design):
|
|||
is a hand-made cell, so the layout and netlist should be available in
|
||||
the technology library. """
|
||||
|
||||
pins = ["BL", "BR", "WL", "vdd", "gnd"]
|
||||
chars = utils.auto_measure_libcell(pins, "replica_cell_6t", GDS["unit"], layer["boundary"])
|
||||
pin_names = ["BL", "BR", "WL", "vdd", "gnd"]
|
||||
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"], layer["boundary"])
|
||||
|
||||
def __init__(self, name="replica_cell_6t"):
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "Create bitcell object")
|
||||
def __init__(self):
|
||||
design.design.__init__(self, "replica_cell_6t")
|
||||
debug.info(2, "Create replica bitcell object")
|
||||
|
||||
|
||||
self.width = replica_bitcell.chars["width"]
|
||||
self.height = replica_bitcell.chars["height"]
|
||||
self.width = replica_bitcell.width
|
||||
self.height = replica_bitcell.height
|
||||
self.pin_map = replica_bitcell.pin_map
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ class replica_bitline(design.design):
|
|||
Used for memory timing control
|
||||
"""
|
||||
|
||||
def __init__(self, name, rows):
|
||||
design.design.__init__(self, "replica_bitline")
|
||||
def __init__(self, rows, name="replica_bitline"):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
g = reload(__import__(OPTS.config.delay_chain))
|
||||
self.mod_delay_chain = getattr(g, OPTS.config.delay_chain)
|
||||
|
|
@ -27,386 +27,303 @@ class replica_bitline(design.design):
|
|||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
self.bitcell_chars = self.mod_bitcell.chars
|
||||
|
||||
for pin in ["en", "out", "vdd", "gnd"]:
|
||||
self.add_pin(pin)
|
||||
self.rows = rows
|
||||
|
||||
self.create_modules()
|
||||
self.cal_modules_offset()
|
||||
self.calculate_module_offsets()
|
||||
self.add_modules()
|
||||
self.route()
|
||||
self.offset_all_coordinates()
|
||||
self.add_layout_pins()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def cal_modules_offset(self):
|
||||
pinv_error_offset = 0.025
|
||||
# leave some room for metal1 routing
|
||||
margin = 3 * drc["minwidth_metal1"]
|
||||
# witdth + min_spacing of M1 & M2
|
||||
m1rail_space = drc["minwidth_metal1"] + drc["metal1_to_metal1"]
|
||||
m2rail_space = drc["minwidth_metal2"] + drc["metal2_to_metal2"]
|
||||
# leave some margin as bit cell layout exceeds its own orgin
|
||||
route_margin = 8 * m2rail_space
|
||||
well_margin = 2 * drc["pwell_enclose_nwell"]
|
||||
bitcell_array_spacing = max(route_margin, well_margin)
|
||||
# now extra space for BL and WL of RBC
|
||||
gnd_route_margin = 5 * m2rail_space
|
||||
|
||||
y_off = (self.inv.height * 2 + pinv_error_offset
|
||||
+ max(drc["pwell_enclose_nwell"],
|
||||
m1rail_space * 4))
|
||||
self.delay_chain_offset = vector(self.delay_chain.height,y_off)
|
||||
self.en_input_offset = vector(0, y_off - m2rail_space)
|
||||
self.en_nor_offset = vector(self.nor.width + margin,
|
||||
self.inv.height * 2)
|
||||
self.BL_inv_offset = vector(self.en_nor_offset.x - self.inv.width, 0)
|
||||
self.access_tx_offset = vector(self.en_nor_offset.x - self.nor.width
|
||||
+ self.access_tx.height + margin,
|
||||
self.inv.height * 0.5)
|
||||
self.replica_bitline_offset = vector(self.delay_chain_offset.x
|
||||
+ bitcell_array_spacing,
|
||||
self.bitcell_chars["height"]
|
||||
+ gnd_route_margin)
|
||||
self.delay_inv_offset = vector(self.delay_chain_offset.x - self.inv.width,
|
||||
self.inv.height * 2)
|
||||
|
||||
self.height = m1rail_space + max(self.delay_chain_offset.y + self.inv.height,
|
||||
self.replica_bitline_offset.y
|
||||
+ self.bitline_load.height
|
||||
+ 0.5 * self.bitcell_chars["height"])
|
||||
self.width = (self.replica_bitline_offset.x + self.replica_bitcell.width)
|
||||
|
||||
|
||||
def create_modules(self):
|
||||
""" create module """
|
||||
self.replica_bitcell = self.mod_replica_bitcell()
|
||||
self.add_mod(self.replica_bitcell)
|
||||
|
||||
# This is the replica bitline load column that is the same height as our array
|
||||
self.bitline_load = bitcell_array(name="bitline_load",
|
||||
cols=1,
|
||||
rows=self.rows)
|
||||
self.add_mod(self.bitline_load)
|
||||
|
||||
# FIXME: This just creates 3 1x inverters
|
||||
self.delay_chain = self.mod_delay_chain("delay_chain",
|
||||
[1, 1, 1])
|
||||
self.add_mod(self.delay_chain)
|
||||
|
||||
self.inv = pinv(nmos_width=drc["minwidth_tx"])
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def calculate_module_offsets(self):
|
||||
""" Calculate all the module offsets """
|
||||
|
||||
# These aren't for instantiating, but we use them to get the dimensions
|
||||
self.active_contact = contact(layer_stack=("active", "contact", "poly"))
|
||||
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
|
||||
self.poly_contact_offset = vector(0.5*self.poly_contact.width,0.5*self.poly_contact.height)
|
||||
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
|
||||
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
|
||||
|
||||
self.nor = nor_2(nmos_width=drc["minwidth_tx"])
|
||||
self.add_mod(self.nor)
|
||||
# M1/M2 routing pitch is based on contacted pitch
|
||||
self.m1_pitch = max(self.m1m2_via.width,self.m1m2_via.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
|
||||
self.m2_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
|
||||
|
||||
# This corrects the offset pitch difference between M2 and M1
|
||||
self.offset_fix = vector(0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]),0)
|
||||
|
||||
self.access_tx = ptx(width=drc["minwidth_tx"],
|
||||
mults=1,
|
||||
tx_type="pmos")
|
||||
# delay chain will be rotated 90, so move it over a width
|
||||
# we move it up a inv height just for some routing room
|
||||
self.rbl_inv_offset = vector(self.delay_chain.height, self.inv.width)
|
||||
# access TX goes right on top of inverter, leave space for an inverter which is
|
||||
# about the same as a TX. We'll need to add rails though.
|
||||
self.access_tx_offset = vector(1.5*self.inv.height,self.rbl_inv_offset.y) + vector(0,2.25*self.inv.width)
|
||||
self.delay_chain_offset = self.rbl_inv_offset + vector(0,4*self.inv.width)
|
||||
|
||||
# Replica bitline and such are not rotated, but they must be placed far enough
|
||||
# away from the delay chain/inverter with space for three M2 tracks
|
||||
self.bitcell_offset = self.rbl_inv_offset + vector(2*self.m2_pitch, 0) + vector(0, self.bitcell.height + self.inv.width)
|
||||
|
||||
self.rbl_offset = self.bitcell_offset
|
||||
|
||||
|
||||
self.height = self.rbl_offset.y + self.rbl.height
|
||||
self.width = self.rbl_offset.x + self.bitcell.width
|
||||
|
||||
|
||||
def create_modules(self):
|
||||
""" Create modules for later instantiation """
|
||||
self.bitcell = self.replica_bitcell = self.mod_replica_bitcell()
|
||||
self.add_mod(self.bitcell)
|
||||
|
||||
# This is the replica bitline load column that is the height of our array
|
||||
self.rbl = bitcell_array(name="bitline_load", cols=1, rows=self.rows)
|
||||
self.add_mod(self.rbl)
|
||||
|
||||
self.delay_chain = self.mod_delay_chain([1, 1, 1])
|
||||
self.add_mod(self.delay_chain)
|
||||
|
||||
self.inv = pinv()
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.access_tx = ptx(tx_type="pmos")
|
||||
self.add_mod(self.access_tx)
|
||||
|
||||
def add_modules(self):
|
||||
"""add mod instance in layout """
|
||||
self.add_inst(name="BL_inv",
|
||||
mod=self.inv,
|
||||
offset=self.BL_inv_offset)
|
||||
""" 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=self.add_inst(name="rbl_inv",
|
||||
mod=self.inv,
|
||||
offset=self.rbl_inv_offset+vector(0,self.inv.width),
|
||||
rotate=270,
|
||||
mirror="MX")
|
||||
self.connect_inst(["bl[0]", "out", "vdd", "gnd"])
|
||||
|
||||
self.add_inst(name="BL_access_tx",
|
||||
mod=self.access_tx,
|
||||
offset=self.access_tx_offset,
|
||||
rotate=90)
|
||||
self.tx_inst=self.add_inst(name="rbl_access_tx",
|
||||
mod=self.access_tx,
|
||||
offset=self.access_tx_offset,
|
||||
rotate=90)
|
||||
# D, G, S, B
|
||||
self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"])
|
||||
# add the well and poly contact
|
||||
|
||||
self.add_inst(name="delay_chain",
|
||||
mod=self.delay_chain,
|
||||
offset=self.delay_chain_offset,
|
||||
rotate=90)
|
||||
self.dc_inst=self.add_inst(name="delay_chain",
|
||||
mod=self.delay_chain,
|
||||
offset=self.delay_chain_offset,
|
||||
rotate=90)
|
||||
self.connect_inst(["en", "delayed_en", "vdd", "gnd"])
|
||||
|
||||
self.add_inst(name="bitcell",
|
||||
mod=self.replica_bitcell,
|
||||
offset=self.replica_bitline_offset,
|
||||
mirror="MX")
|
||||
self.rbc_inst=self.add_inst(name="bitcell",
|
||||
mod=self.replica_bitcell,
|
||||
offset=self.bitcell_offset,
|
||||
mirror="MX")
|
||||
self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"])
|
||||
|
||||
self.add_loads()
|
||||
self.expan_the_well_to_BL_inv()
|
||||
self.rbl_inst=self.add_inst(name="load",
|
||||
mod=self.rbl,
|
||||
offset=self.rbl_offset)
|
||||
self.connect_inst(["bl[0]", "br[0]"] + ["gnd"]*self.rows + ["vdd", "gnd"])
|
||||
|
||||
|
||||
def expan_the_well_to_BL_inv(self):
|
||||
width = self.BL_inv_offset.x - self.access_tx_offset.x + self.inv.width
|
||||
well_offset = self.access_tx_offset - vector(self.access_tx.width, 0)
|
||||
for layer in ["nwell", "vtg"]:
|
||||
self.add_rect(layer=layer,
|
||||
offset=well_offset,
|
||||
width=width,
|
||||
height= 2*self.access_tx.width)
|
||||
|
||||
def add_loads(self):
|
||||
self.add_inst(name="load",
|
||||
mod=self.bitline_load,
|
||||
offset=self.replica_bitline_offset)
|
||||
temp = []
|
||||
for i in range(1):
|
||||
temp.append("bl[{0}]".format(i))
|
||||
temp.append("br[{0}]".format(i))
|
||||
for j in range(self.rows):
|
||||
temp.append("gnd".format(j))
|
||||
temp = temp + ["vdd", "gnd"]
|
||||
self.connect_inst(temp)
|
||||
|
||||
def route(self):
|
||||
"""connect modules together"""
|
||||
# calculate pin offset
|
||||
correct = vector(0, 0.5 * drc["minwidth_metal1"])
|
||||
self.out_offset = self.BL_inv_offset + self.inv.Z_position + correct
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=self.out_offset)
|
||||
m1_pin_offset = self.out_offset - correct
|
||||
self.add_rect(layer="metal1",
|
||||
offset=m1_pin_offset,
|
||||
width=self.m1m2_via.width,
|
||||
height=self.m1m2_via.height)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=m1_pin_offset,
|
||||
width=self.m2m3_via.width,
|
||||
height=self.m2m3_via.height)
|
||||
|
||||
BL_inv_in = self.BL_inv_offset + self.inv.A_position + correct
|
||||
BL_offset = self.replica_bitline_offset + vector(1,0).scale(self.bitcell_chars["BL"])
|
||||
pin_offset = self.delay_chain.clk_out_offset.rotate_scale(-1,1)
|
||||
delay_chain_output = self.delay_chain_offset + pin_offset
|
||||
vdd_offset = vector(self.delay_chain_offset.x + 9 * drc["minwidth_metal2"],
|
||||
self.height)
|
||||
self.create_input()
|
||||
|
||||
self.route_BL_t_BL_inv(BL_offset, BL_inv_in)
|
||||
self.route_access_tx(delay_chain_output, BL_inv_in, vdd_offset)
|
||||
self.route_vdd()
|
||||
""" Connect all the signals together """
|
||||
self.route_gnd()
|
||||
# route loads after gnd and vdd created
|
||||
self.route_loads(vdd_offset)
|
||||
self.route_RC(vdd_offset)
|
||||
self.route_vdd()
|
||||
self.route_access_tx()
|
||||
|
||||
def create_input(self):
|
||||
# create routing module based on module offset
|
||||
correct = vector(0.5 * drc["minwidth_metal1"], 0)
|
||||
pin_offset = self.delay_chain.clk_in_offset.rotate_scale(-1,1)
|
||||
input_offset = self.delay_chain_offset + pin_offset + correct
|
||||
mid1 = [input_offset.x, self.en_input_offset.y]
|
||||
self.add_path("metal1", [self.en_input_offset, mid1, input_offset])
|
||||
|
||||
self.add_label(text="en",
|
||||
layer="metal1",
|
||||
offset=self.en_input_offset)
|
||||
def route_access_tx(self):
|
||||
# GATE ROUTE
|
||||
# 1. Add the poly contact and nwell enclosure
|
||||
# Determines the y-coordinate of where to place the gate input poly pin
|
||||
# (middle in between the pmos and nmos)
|
||||
|
||||
def route_BL_t_BL_inv(self, BL_offset, BL_inv_in):
|
||||
# BL_inv input to M3
|
||||
mid1 = BL_inv_in - vector(0,
|
||||
drc["metal2_to_metal2"] + self.m1m2_via.width)
|
||||
mid2 = vector(self.en_nor_offset.x + 3 * drc["metal1_to_metal1"],
|
||||
mid1.y)
|
||||
mid3 = vector(mid2.x,
|
||||
self.replica_bitline_offset.y - self.replica_bitcell.height
|
||||
- 0.5 * (self.m1m2_via.height + drc["metal1_to_metal1"])
|
||||
- 2 * drc["metal1_to_metal1"])
|
||||
self.add_wire(layers=("metal2", "via1", "metal1"),
|
||||
coordinates=[BL_inv_in, mid1, mid2, mid3])
|
||||
|
||||
# need to fix the mid point as this is done with two wire
|
||||
# this seems to cover the metal1 error of the wire
|
||||
offset = mid3 - vector( [0.5 * drc["minwidth_metal1"]] * 2)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
mid4 = [BL_offset.x, mid3.y]
|
||||
self.add_wire(layers=("metal1", "via1", "metal2"),
|
||||
coordinates=[BL_offset, mid4, mid3])
|
||||
|
||||
def route_access_tx(self, delay_chain_output, BL_inv_in, vdd_offset):
|
||||
self.route_tx_gate(delay_chain_output)
|
||||
self.route_tx_drain(vdd_offset)
|
||||
self.route_tx_source(BL_inv_in)
|
||||
|
||||
def route_tx_gate(self, delay_chain_output):
|
||||
# gate input for access tx
|
||||
offset = (self.access_tx.poly_positions[0].rotate_scale(0,1)
|
||||
+ self.access_tx_offset)
|
||||
width = -6 * drc["minwidth_metal1"]
|
||||
self.add_rect(layer="poly",
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=drc["minwidth_poly"])
|
||||
y_off = 0.5 * (drc["minwidth_poly"] - self.poly_contact.height)
|
||||
offset = offset + vector(width, y_off)
|
||||
# finds the lower right of the poly gate
|
||||
poly_offset = self.access_tx_offset + self.access_tx.poly_positions[0].rotate_scale(-1,1)
|
||||
# This centers the contact on the poly
|
||||
contact_offset = poly_offset.scale(0,1) + self.dc_inst.get_pin("out").ll().scale(1,0) \
|
||||
+ vector(-drc["poly_extend_contact"], -0.5*self.poly_contact.height + 0.5*drc["minwidth_poly"])
|
||||
self.add_contact(layers=("poly", "contact", "metal1"),
|
||||
offset=offset)
|
||||
# route gate to delay_chain output
|
||||
gate_offset = offset + vector(0.5 * drc["minwidth_metal1"],
|
||||
0.5 * self.poly_contact.width)
|
||||
self.route_access_tx_t_delay_chain(gate_offset, delay_chain_output)
|
||||
self.route_access_tx_t_WL(gate_offset)
|
||||
offset=contact_offset)
|
||||
self.add_rect(layer="poly",
|
||||
offset=poly_offset,
|
||||
width=contact_offset.x-poly_offset.x,
|
||||
height=drc["minwidth_poly"])
|
||||
nwell_offset = self.rbl_inv_offset + vector(-self.inv.height,self.inv.width)
|
||||
self.add_rect(layer="nwell",
|
||||
offset=nwell_offset,
|
||||
width=0.5*self.inv.height,
|
||||
height=self.delay_chain_offset.y-nwell_offset.y)
|
||||
|
||||
def route_access_tx_t_delay_chain(self, offset, delay_chain_output):
|
||||
m2rail_space = (drc["minwidth_metal2"] + drc["metal2_to_metal2"])
|
||||
mid1 = vector(offset.x, self.delay_chain_offset.y - 3 * m2rail_space)
|
||||
mid2 = [delay_chain_output.x, mid1.y]
|
||||
# Note the inverted wire stack
|
||||
self.add_wire(layers=("metal1", "via1", "metal2"),
|
||||
coordinates=[offset, mid1, mid2, delay_chain_output])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=delay_chain_output,
|
||||
mirror="MX")
|
||||
# 2. Route delay chain output to access tx gate
|
||||
delay_en_offset = self.dc_inst.get_pin("out").bc()
|
||||
delay_en_end_offset = contact_offset + vector(self.poly_contact.width,self.poly_contact.height).scale(0.5,0.5)
|
||||
self.add_path("metal1", [delay_en_offset,delay_en_end_offset])
|
||||
|
||||
def route_access_tx_t_WL(self, offset):
|
||||
m1m2_via_offset = offset - vector(0.5 * self.m1m2_via.width,
|
||||
0.5 * self.m1m2_via.height)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=m1m2_via_offset)
|
||||
# route gate to RC WL
|
||||
RC_WL = self.replica_bitline_offset - vector(0,1).scale(self.bitcell_chars["WL"])
|
||||
mid1 = vector(offset.x, 0)
|
||||
mid2 = vector(self.en_nor_offset.x + 3 * drc["metal1_to_metal1"],
|
||||
mid1.y)
|
||||
mid3 = vector(RC_WL.x - drc["minwidth_metal1"] - self.m1m2_via.height,
|
||||
mid1.y)
|
||||
mid4 = vector(mid3.x, RC_WL.y)
|
||||
self.add_path("metal2", [offset, mid1, mid2, mid3, mid4])
|
||||
|
||||
offset = mid4 - vector([0.5 * drc["minwidth_metal1"]] * 2)
|
||||
width = RC_WL.x - offset.x
|
||||
# enter the bit line array with metal1
|
||||
via_offset = [mid4.x - 0.5 * self.m1m2_via.width,
|
||||
offset.y
|
||||
- 0.5 * (self.m1m2_via.height
|
||||
- drc["minwidth_metal1"])]
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=via_offset)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=drc["minwidth_metal1"])
|
||||
# 3. Route the mid-point of previous route to the bitcell WL
|
||||
# route bend of previous net to bitcell WL
|
||||
wl_offset = self.rbc_inst.get_pin("WL").lc()
|
||||
wl_mid = vector(delay_en_end_offset.x,wl_offset.y)
|
||||
self.add_path("metal1", [delay_en_end_offset, wl_mid, wl_offset])
|
||||
|
||||
def route_tx_drain(self,vdd_offset):
|
||||
# route drain to Vdd
|
||||
active_offset = self.access_tx.active_contact_positions[1].rotate_scale(-1,1)
|
||||
correct = vector(-0.5 * drc["minwidth_metal1"],
|
||||
0.5 * self.access_tx.active_contact.width)
|
||||
drain_offset = self.access_tx_offset + active_offset + correct
|
||||
close_Vdd_offset = self.BL_inv_offset + vector(0, self.inv.height)
|
||||
self.add_path("metal1", [drain_offset, close_Vdd_offset])
|
||||
|
||||
mid = [vdd_offset.x, close_Vdd_offset.y]
|
||||
self.add_wire(layers=("metal1", "via1", "metal2"),
|
||||
coordinates=[close_Vdd_offset, mid, vdd_offset])
|
||||
|
||||
def route_tx_source(self, BL_inv_in):
|
||||
# route source to BL inv input which is connected to BL
|
||||
active_offset = self.access_tx.active_contact_positions[0].rotate_scale(-1,1)
|
||||
correct = vector(-0.5 * drc["minwidth_metal1"],
|
||||
0.5 * self.access_tx.active_contact.width)
|
||||
source_offset = self.access_tx_offset + active_offset + correct
|
||||
self.add_path("metal1", [source_offset, BL_inv_in])
|
||||
# SOURCE ROUTE
|
||||
# Route the source to the vdd rail
|
||||
source_offset = self.access_tx_offset + self.access_tx.active_contact_positions[1].rotate_scale(-1,1)\
|
||||
+ vector(self.active_contact.width,self.active_contact.height).rotate_scale(-0.5,0.5)
|
||||
|
||||
inv_vdd_offset = self.rbl_inv_inst.get_pin("vdd").uc()
|
||||
vdd_offset = inv_vdd_offset.scale(1,0) + source_offset.scale(0,1)
|
||||
self.add_path("metal1", [source_offset, vdd_offset])
|
||||
|
||||
# DRAIN ROUTE
|
||||
# Route the drain to the RBL inverter input
|
||||
drain_offset = self.access_tx_offset + self.access_tx.active_contact_positions[0].rotate_scale(-1,1) \
|
||||
+ self.poly_contact_offset.rotate_scale(-1,1)
|
||||
mid1 = drain_offset.scale(1,0) + vector(0,self.rbl_inv_offset.y+self.inv.width+self.m2_pitch)
|
||||
inv_A_offset = self.rbl_inv_inst.get_pin("A").uc()
|
||||
mid2 = vector(inv_A_offset.x, mid1.y)
|
||||
self.add_path("metal1",[drain_offset, mid1, mid2, inv_A_offset])
|
||||
|
||||
# Route the connection of the drain route (mid2) to the RBL bitline (left)
|
||||
drain_offset = mid2
|
||||
# Route the M2 to the right of the vdd rail between rbl_inv and bitcell
|
||||
gnd_pin = self.rbl_inv_inst.get_pin("gnd").ll()
|
||||
mid1 = vector(gnd_pin.x+self.m2_pitch,drain_offset.y)
|
||||
# Via will go halfway down from the bitcell
|
||||
bl_offset = self.rbc_inst.get_pin("BL").bc()
|
||||
via_offset = bl_offset - vector(0,0.5*self.inv.width)
|
||||
mid2 = vector(mid1.x,via_offset.y)
|
||||
# self.add_contact(layers=("metal1", "via1", "metal2"),
|
||||
# offset=via_offset - vector(0.5*drc["minwidth_metal2"],0.5*drc["minwidth_metal1"]))
|
||||
self.add_wire(("metal1","via1","metal2"),[drain_offset,mid1,mid2,via_offset,bl_offset])
|
||||
#self.add_path("metal2",[via_offset,bl_offset])
|
||||
|
||||
def route_vdd(self):
|
||||
vdd_offset = vector(0, self.height)
|
||||
# Add a rail in M2 that is to the right of the inverter gnd pin
|
||||
# The replica column may not fit in a single standard cell pitch, so add the vdd rail to the
|
||||
# right of it.
|
||||
vdd_start = vector(self.bitcell_offset.x + self.bitcell.width + self.m1_pitch,0)
|
||||
# It is the height of the entire RBL and bitcell
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_offset,
|
||||
width=self.width,
|
||||
layer="metal1",
|
||||
offset=vdd_start,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=self.rbl.height+self.bitcell.height+2*self.inv.width+0.5*drc["minwidth_metal1"])
|
||||
|
||||
# Connect the vdd pins of the bitcell load directly to vdd
|
||||
vdd_pins = self.rbl_inst.get_pin("vdd")
|
||||
for pin in vdd_pins:
|
||||
offset = vector(vdd_start.x,pin.by())
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=self.rbl_offset.x-vdd_start.x,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
# Also connect the replica bitcell vdd pin to vdd
|
||||
pin = self.rbc_inst.get_pin("vdd")
|
||||
offset = vector(vdd_start.x,pin.by())
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=self.bitcell_offset.x-vdd_start.x,
|
||||
height=drc["minwidth_metal1"])
|
||||
# delay chain vdd to vertical vdd rail and
|
||||
start = self.delay_chain_offset - vector(0.5 * self.delay_chain.height, 0)
|
||||
m1rail_space = (drc["minwidth_metal1"] + drc["metal1_to_metal1"])
|
||||
mid1 = start - vector(0, m1rail_space)
|
||||
mid2 = vector(self.delay_chain_offset.x + 9 * drc["minwidth_metal2"],
|
||||
mid1.y)
|
||||
end = [mid2.x, vdd_offset.y]
|
||||
self.add_path(layer=("metal1"),
|
||||
coordinates=[start, mid1, mid2])
|
||||
self.add_wire(layers=("metal1", "via1", "metal2"),
|
||||
coordinates=[mid1, mid2, end])
|
||||
|
||||
# Add a second vdd pin. No need for full length. It is must connect at the next level.
|
||||
inv_vdd_offset = self.rbl_inv_inst.get_pin("vdd").ll()
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=inv_vdd_offset.scale(1,0),
|
||||
width=drc["minwidth_metal1"],
|
||||
height=self.delay_chain_offset.y)
|
||||
|
||||
|
||||
|
||||
|
||||
def route_gnd(self):
|
||||
"""route gnd of delay chain, en_nor, en_inv and BL_inv"""
|
||||
# route delay chain gnd to BL_inv gnd
|
||||
# gnd Node between BL_inv access tx and delay chain, and is below
|
||||
# en_input
|
||||
self.gnd_position = self.delay_chain_offset
|
||||
BL_gnd_offset = self.BL_inv_offset
|
||||
mid1 = vector(0, self.BL_inv_offset.y)
|
||||
rail2_space = drc["minwidth_metal2"] + drc["metal2_to_metal2"]
|
||||
y_off = self.gnd_position.y + self.delay_chain.width + rail2_space
|
||||
mid2 = vector(mid1.x, y_off)
|
||||
share_gnd = vector(self.gnd_position.x, mid2.y)
|
||||
# Note the inverted stacks
|
||||
lst = [BL_gnd_offset, mid1, mid2, share_gnd, self.gnd_position]
|
||||
self.add_wire(layers=("metal1", "via1", "metal2"),
|
||||
coordinates=lst)
|
||||
self.add_label(text="gnd",
|
||||
layer="metal1",
|
||||
offset=self.gnd_position)
|
||||
# connect to the metal1 gnd of delay chain
|
||||
offset = mid2 - vector(0.5 * drc["minwidth_metal1"], 0)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=-self.delay_chain.width)
|
||||
offset = [offset.x + self.delay_chain.height,
|
||||
mid2.y]
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=-self.delay_chain.width)
|
||||
""" Route all signals connected to gnd """
|
||||
|
||||
# Add a rail in M1 from bottom to two along delay chain
|
||||
gnd_start = self.rbl_inv_inst.get_pin("gnd").ll() - self.offset_fix
|
||||
|
||||
# It is the height of the entire RBL and bitcell
|
||||
self.add_rect(layer="metal2",
|
||||
offset=gnd_start,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.rbl.height+self.bitcell.height+self.inv.width)
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_start.scale(1,0),
|
||||
width=drc["minwidth_metal2"],
|
||||
height=2*self.inv.width)
|
||||
|
||||
# Connect the WL pins directly to gnd
|
||||
for row in range(self.rows):
|
||||
wl = "wl[{}]".format(row)
|
||||
pin = self.rbl_inst.get_pin(wl)
|
||||
offset = vector(gnd_start.x,pin.by())
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=self.rbl_offset.x-gnd_start.x,
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
|
||||
def route_loads(self,vdd_offset):
|
||||
"""connect all the loads word line to gnd"""
|
||||
# Add via for the delay chain
|
||||
offset = self.delay_chain_offset - vector(0.5*drc["minwidth_metal1"],0) - self.offset_fix
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=vdd_offset,
|
||||
mirror="MX")
|
||||
gnd_offset = (self.delay_chain_offset
|
||||
+ vector([drc["minwidth_metal1"]] * 2).scale(-.5,.5))
|
||||
for i in range(self.rows):
|
||||
WL_offset = (self.replica_bitline_offset
|
||||
+ self.bitline_load.WL_positions[i].scale(0,1))
|
||||
mid = [self.delay_chain_offset.x + 6 * drc["minwidth_metal2"],
|
||||
gnd_offset.y]
|
||||
self.add_wire(layers=("metal1", "via1", "metal2"),
|
||||
coordinates=[gnd_offset, mid, WL_offset])
|
||||
if i % 2 == 0:
|
||||
load_vdd_offset = (self.replica_bitline_offset
|
||||
+ self.bitline_load.vdd_positions[i])
|
||||
mid = [vdd_offset.x, load_vdd_offset.y]
|
||||
self.add_wire(layers=("metal1", "via1", "metal2"),
|
||||
coordinates=[vdd_offset, mid, load_vdd_offset])
|
||||
offset=offset)
|
||||
|
||||
def route_RC(self,vdd_offset):
|
||||
"""route vdd gnd to the replica cell """
|
||||
# connect vdd
|
||||
RC_vdd = self.replica_bitline_offset + vector(1,-1).scale(self.bitcell_chars["vdd"])
|
||||
mid = [vdd_offset.x, RC_vdd.y]
|
||||
# Note the inverted stacks
|
||||
self.add_wire(layers=("metal1", "via1", "metal2"),
|
||||
coordinates=[vdd_offset, mid, RC_vdd])
|
||||
# Add via for the inverter
|
||||
offset = self.rbl_inv_offset - vector(0.5*drc["minwidth_metal1"],self.m1m2_via.height) - self.offset_fix
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
|
||||
gnd_offset = self.BL_inv_offset - vector(self.inv.width, 0)
|
||||
load_gnd = self.replica_bitline_offset + vector(self.bitcell_chars["gnd"][0],
|
||||
self.bitline_load.height)
|
||||
mid = [load_gnd.x, gnd_offset.y]
|
||||
self.add_wire(layers=("metal1", "via1", "metal2"),
|
||||
coordinates=[gnd_offset, mid, load_gnd])
|
||||
# Connect the bitcell gnd pin to the rail
|
||||
gnd_pins = self.get_pin("gnd")
|
||||
gnd_start = self.get_pin("gnd").uc()
|
||||
rbl_gnd_pins = self.rbl_inst.get_pin("gnd")
|
||||
# Find the left most rail on M2
|
||||
gnd_pin = None
|
||||
for pin in rbl_gnd_pins:
|
||||
if gnd_pin == None or (pin.layer=="metal2" and pin.lx()<gnd_pin.lx()):
|
||||
gnd_pin = pin
|
||||
gnd_end = gnd_pin.uc()
|
||||
# Add a couple midpoints so that the wire will drop a via and then route horizontal on M1
|
||||
gnd_mid1 = gnd_start + vector(0,2*drc["metal2_to_metal2"])
|
||||
gnd_mid2 = gnd_end + vector(0,2*drc["metal2_to_metal2"])
|
||||
self.add_wire(("metal1","via1","metal2"), [gnd_start, gnd_mid1, gnd_mid2, gnd_end])
|
||||
|
||||
|
||||
load_gnd = self.replica_bitline_offset + vector(0,
|
||||
self.bitline_load.height)
|
||||
mid = [load_gnd.x, gnd_offset.y]
|
||||
self.add_wire(("metal1", "via1", "metal2"), [gnd_offset, mid, load_gnd])
|
||||
# Add a second gnd pin to the second delay chain rail. No need for full length.
|
||||
dc_gnd_offset = self.dc_inst.get_pin("gnd")[1].ll()
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=dc_gnd_offset.scale(1,0),
|
||||
width=drc["minwidth_metal1"],
|
||||
height=self.delay_chain_offset.y)
|
||||
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Route the input and output signal """
|
||||
en_offset = self.delay_chain_offset+self.delay_chain.get_pin("in").ur().rotate_scale(-1,1)
|
||||
self.add_layout_pin(text="en",
|
||||
layer="metal1",
|
||||
offset=en_offset.scale(1,0),
|
||||
width=drc["minwidth_metal1"],
|
||||
height=en_offset.y)
|
||||
|
||||
out_offset = self.rbl_inv_offset+self.inv.get_pin("Z").ur().rotate_scale(-1,1) - vector(0,self.inv.width)
|
||||
self.add_layout_pin(text="out",
|
||||
layer="metal1",
|
||||
offset=out_offset.scale(1,0),
|
||||
width=drc["minwidth_metal1"],
|
||||
height=out_offset.y)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,15 +11,17 @@ class sense_amp(design.design):
|
|||
Sense amplifier to read a pair of bit-lines.
|
||||
"""
|
||||
|
||||
pins = ["BL", "BR", "Dout", "SCLK", "vdd", "gnd"]
|
||||
chars = utils.auto_measure_libcell(pins, "sense_amp", GDS["unit"], layer["boundary"])
|
||||
pin_names = ["BL", "BR", "Dout", "SCLK", "vdd", "gnd"]
|
||||
(width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"], layer["boundary"])
|
||||
|
||||
def __init__(self, name):
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "Create Sense Amp object")
|
||||
|
||||
self.width = sense_amp.chars["width"]
|
||||
self.height = sense_amp.chars["height"]
|
||||
self.width = sense_amp.width
|
||||
self.height = sense_amp.height
|
||||
self.pin_map = sense_amp.pin_map
|
||||
|
||||
def delay(self, slew, load=0.0):
|
||||
from tech import spice
|
||||
|
|
|
|||
|
|
@ -16,10 +16,15 @@ class sense_amp_array(design.design):
|
|||
|
||||
c = reload(__import__(OPTS.config.sense_amp))
|
||||
self.mod_sense_amp = getattr(c, OPTS.config.sense_amp)
|
||||
self.sense_amp_chars = self.mod_sense_amp.chars
|
||||
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.add_pins()
|
||||
self.create_layout()
|
||||
|
|
@ -27,110 +32,90 @@ class sense_amp_array(design.design):
|
|||
|
||||
def add_pins(self):
|
||||
|
||||
if (self.words_per_row == 1):
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("bl[{0}]".format(i))
|
||||
self.add_pin("br[{0}]".format(i))
|
||||
else:
|
||||
for i in range(self.word_size):
|
||||
index = i * self.words_per_row
|
||||
self.add_pin("bl_out[{0}]".format(index))
|
||||
self.add_pin("br_out[{0}]".format(index))
|
||||
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("data_out[{0}]".format(i))
|
||||
for i in range(0,self.row_size,self.words_per_row):
|
||||
self.add_pin("data[{0}]".format(i/self.words_per_row))
|
||||
self.add_pin("bl[{0}]".format(i))
|
||||
self.add_pin("br[{0}]".format(i))
|
||||
|
||||
self.add_pin("sclk")
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_layout(self):
|
||||
self.create_sense_amp()
|
||||
self.setup_layout_constants()
|
||||
|
||||
self.add_sense_amp()
|
||||
self.connect_rails()
|
||||
self.offset_all_coordinates()
|
||||
#self.offset_all_coordinates()
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.vdd_positions = []
|
||||
self.gnd_positions = []
|
||||
self.SCLK_positions = []
|
||||
self.amp_positions = []
|
||||
self.Data_out_positions = []
|
||||
self.height = self.amp.height
|
||||
self.width = self.amp.width * self.word_size * self.words_per_row
|
||||
|
||||
def create_sense_amp(self):
|
||||
self.amp = self.mod_sense_amp("sense_amp")
|
||||
self.add_mod(self.amp)
|
||||
|
||||
|
||||
def add_sense_amp(self):
|
||||
for i in range(self.word_size):
|
||||
name = "sa_d{0}".format(i)
|
||||
index = i * self.words_per_row
|
||||
amp_position = vector(self.amp.width * index, 0)
|
||||
BL_offset = amp_position + vector(self.sense_amp_chars["BL"][0], 0)
|
||||
BR_offset = amp_position + vector(self.sense_amp_chars["BR"][0], 0)
|
||||
|
||||
bl_pin = self.amp.get_pin("BL")
|
||||
br_pin = self.amp.get_pin("BR")
|
||||
dout_pin = self.amp.get_pin("Dout")
|
||||
|
||||
for i in range(0,self.row_size,self.words_per_row):
|
||||
|
||||
name = "sa_d{0}".format(i)
|
||||
amp_position = vector(self.amp.width * i, 0)
|
||||
|
||||
bl_offset = amp_position + bl_pin.ll().scale(1,0)
|
||||
br_offset = amp_position + br_pin.ll().scale(1,0)
|
||||
dout_offset = amp_position + dout_pin.ll()
|
||||
|
||||
self.add_inst(name=name,
|
||||
mod=self.amp,
|
||||
offset=amp_position)
|
||||
self.amp_positions.append(amp_position)
|
||||
if (self.words_per_row == 1):
|
||||
self.add_label(text="bl[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=BL_offset)
|
||||
self.add_label(text="br[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=BR_offset)
|
||||
self.connect_inst(["bl[{0}]".format(i),"br[{0}]".format(i),
|
||||
"data_out[{0}]".format(i),
|
||||
"sclk", "vdd", "gnd"])
|
||||
else:
|
||||
self.add_label(text="bl_out[{0}]".format(index),
|
||||
layer="metal2",
|
||||
offset=BL_offset)
|
||||
self.add_label(text="br_out[{0}]".format(index),
|
||||
layer="metal2",
|
||||
offset=BR_offset)
|
||||
self.connect_inst(["bl_out[{0}]".format(index), "br_out[{0}]".format(index),
|
||||
"data_out[{0}]".format(i),
|
||||
"sclk", "vdd", "gnd"])
|
||||
self.connect_inst(["bl[{0}]".format(i),"br[{0}]".format(i),
|
||||
"data[{0}]".format(i/self.words_per_row),
|
||||
"sclk", "vdd", "gnd"])
|
||||
|
||||
self.add_layout_pin(text="bl[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=bl_offset,
|
||||
width=bl_pin.width(),
|
||||
height=bl_pin.height())
|
||||
self.add_layout_pin(text="br[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=br_offset,
|
||||
width=br_pin.width(),
|
||||
height=br_pin.height())
|
||||
|
||||
self.add_layout_pin(text="data[{0}]".format(i/self.words_per_row),
|
||||
layer="metal2",
|
||||
offset=dout_offset,
|
||||
width=dout_pin.width(),
|
||||
height=dout_pin.height())
|
||||
|
||||
|
||||
|
||||
self.add_label(text="data_out[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=amp_position + self.sense_amp_chars["Dout"])
|
||||
self.Data_out_positions.append(amp_position + self.sense_amp_chars["Dout"])
|
||||
|
||||
def connect_rails(self):
|
||||
base_offset = vector(0, - 0.5 * drc["minwidth_metal1"])
|
||||
# add vdd rail across entire array
|
||||
vdd_offset = base_offset + vector(self.sense_amp_chars["vdd"]).scale(0,1)
|
||||
vdd_offset = self.amp.get_pin("vdd").ll().scale(0,1)
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_offset,
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
self.vdd_positions.append(vdd_offset)
|
||||
|
||||
# NOTE:the gnd rails are vertical so it is not connected horizontally
|
||||
# add gnd rail across entire array
|
||||
gnd_offset = base_offset + vector(self.sense_amp_chars["gnd"]).scale(0,1)
|
||||
gnd_offset = self.amp.get_pin("gnd").ll().scale(0,1)
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_offset,
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
self.gnd_positions.append(gnd_offset)
|
||||
|
||||
# add sclk rail across entire array
|
||||
sclk_offset = base_offset + vector(self.sense_amp_chars["SCLK"]).scale(0,1)
|
||||
sclk_offset = self.amp.get_pin("SCLK").ll().scale(0,1)
|
||||
self.add_layout_pin(text="sclk",
|
||||
layer="metal1",
|
||||
offset=sclk_offset,
|
||||
width=self.width,
|
||||
height=drc["minwidth_metal1"])
|
||||
self.SCLK_positions.append(sclk_offset)
|
||||
|
||||
def delay(self, slew, load=0.0):
|
||||
result = self.amp.delay(slew=slew, load=load)
|
||||
|
|
|
|||
|
|
@ -18,205 +18,186 @@ class single_level_column_mux(design.design):
|
|||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
self.bitcell_chars = self.mod_bitcell.chars
|
||||
|
||||
self.tx_size = tx_size
|
||||
self.ptx_width = self.tx_size * drc["minwidth_tx"]
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
|
||||
def add_pins(self):
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
self.ptx_width = tx_size * drc["minwidth_tx"]
|
||||
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
# This is not instantiated and used for calculations only.
|
||||
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
|
||||
self.pwell_contact = contact(layer_stack=("active", "contact", "metal1"))
|
||||
self.well_contact = contact(layer_stack=("active", "contact", "metal1"))
|
||||
|
||||
self.create_ptx()
|
||||
self.add_ptx()
|
||||
|
||||
self.width = self.bitcell.width
|
||||
# The height is bigger than necessary.
|
||||
self.height = 2*self.nmos.height
|
||||
self.connect_poly()
|
||||
self.connect_to_bitlines()
|
||||
self.add_gnd_rail()
|
||||
self.add_well_contacts()
|
||||
self.setup_layout_constants()
|
||||
|
||||
|
||||
|
||||
|
||||
def create_ptx(self):
|
||||
"""Initializes the nmos1 and nmos2 transistors"""
|
||||
self.nmos1 = ptx(width=self.ptx_width,
|
||||
mults=1,
|
||||
tx_type="nmos")
|
||||
self.add_mod(self.nmos1)
|
||||
self.nmos2 = ptx(width=self.ptx_width,
|
||||
mults=1,
|
||||
tx_type="nmos")
|
||||
self.nmos2 = self.nmos2
|
||||
self.add_mod(self.nmos2)
|
||||
|
||||
def add_ptx(self):
|
||||
""" Create the two pass gate NMOS transistors to switch the bitlines"""
|
||||
|
||||
# Adds nmos1,nmos2 to the module
|
||||
self.nmos1_position = (vector(drc["minwidth_metal1"],
|
||||
drc["poly_extend_active"])
|
||||
- vector([drc["well_enclosure_active"]]*2))
|
||||
self.add_inst(name="M_1",
|
||||
mod=self.nmos1,
|
||||
self.nmos = ptx(width=self.ptx_width)
|
||||
self.add_mod(self.nmos)
|
||||
|
||||
self.nmos1_position = vector(drc["minwidth_metal1"], drc["poly_extend_active"]) \
|
||||
- vector([drc["well_enclosure_active"]]*2)
|
||||
self.add_inst(name="mux_tx1",
|
||||
mod=self.nmos,
|
||||
offset=self.nmos1_position)
|
||||
self.connect_inst(["bl", "sel", "bl_out", "gnd"])
|
||||
|
||||
nmos2_to_nmos1 = vector(self.nmos1.active_width,
|
||||
self.nmos1.active_height + drc["minwidth_poly"]
|
||||
+ 2* drc["poly_extend_active"])
|
||||
nmos2_to_nmos1 = vector(self.nmos.active_width,
|
||||
self.nmos.active_height + drc["minwidth_poly"] + 2*drc["poly_extend_active"])
|
||||
self.nmos2_position = self.nmos1_position + nmos2_to_nmos1
|
||||
self.add_inst(name="M_2",
|
||||
mod=self.nmos2,
|
||||
self.add_inst(name="mux_tx2",
|
||||
mod=self.nmos,
|
||||
offset=self.nmos2_position)
|
||||
self.connect_inst(["br", "sel", "br_out", "gnd"])
|
||||
|
||||
|
||||
def connect_poly(self):
|
||||
self.poly_offset = (self.nmos1_position
|
||||
+ self.nmos1.poly_positions[0]
|
||||
+ vector(0,self.nmos1.poly_height))
|
||||
width=self.nmos2_position.x- self.nmos1_position.x+ drc["minwidth_poly"]
|
||||
self.poly = self.add_rect(layer="poly",
|
||||
offset=self.poly_offset,
|
||||
width=width,
|
||||
height=drc["minwidth_poly"])
|
||||
self.add_label(text="col_addr",
|
||||
layer="poly",
|
||||
offset=self.poly_offset)
|
||||
""" Connect the poly gate of the two pass transistors """
|
||||
|
||||
self.poly_offset = self.nmos1_position + self.nmos.poly_positions[0] \
|
||||
+ vector(0,self.nmos.poly_height)
|
||||
width=self.nmos2_position.x - self.nmos1_position.x + drc["minwidth_poly"]
|
||||
self.add_layout_pin(text="col_addr",
|
||||
layer="poly",
|
||||
offset=self.poly_offset,
|
||||
width=width,
|
||||
height=drc["minwidth_poly"])
|
||||
|
||||
def connect_to_bitlines(self):
|
||||
offset = [self.nmos1.active_contact_positions[0].x + self.m1m2_via.contact_width / 2
|
||||
+ 3 * (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2,
|
||||
self.nmos1.active_position.y+ self.nmos1.active_height]
|
||||
offset = self.nmos1_position + offset
|
||||
connection = vector(0,
|
||||
self.nmos2.active_height+ 2 * drc["poly_extend_active"] \
|
||||
+ drc["minwidth_poly"] + drc["minwidth_metal2"])
|
||||
self.add_rect(layer="metal2",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=connection.y - drc["minwidth_metal2"])
|
||||
""" """
|
||||
|
||||
self.BL_position = (vector(self.bitcell_chars["BL"][0]- 0.5 * self.m1m2_via.width,
|
||||
offset.y)
|
||||
+ connection)
|
||||
# place the contact at the top of the src/drain
|
||||
offset = self.nmos1_position + vector(self.nmos.active_contact_positions[0].x + 0.5*self.m1m2_via.contact_width
|
||||
+ 3 * (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width),
|
||||
self.nmos.active_position.y + self.nmos.active_height - self.m1m2_via.height)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset,
|
||||
mirror="MX")
|
||||
self.add_label(text="bl",
|
||||
layer="metal2",
|
||||
offset=self.BL_position)
|
||||
offset=offset)
|
||||
|
||||
self.add_rect(layer="metal2",
|
||||
offset=self.BL_position - vector(0, 2 * drc["minwidth_metal2"]),
|
||||
width=drc["minwidth_metal2"],
|
||||
height=2 * drc["minwidth_metal2"])
|
||||
bl_offset = vector(self.bitcell.get_pin("BL").lx(),self.height)
|
||||
self.add_layout_pin(text="bl",
|
||||
layer="metal2",
|
||||
offset=bl_offset - vector(0, 2*drc["minwidth_metal2"]),
|
||||
width=drc["minwidth_metal2"],
|
||||
height=2*drc["minwidth_metal2"])
|
||||
# draw an enclosing rectangle for the small jog
|
||||
start = offset + vector(0.5*self.m1m2_via.width,0.5*self.m1m2_via.height)
|
||||
end = self.get_pin("bl").bc()
|
||||
mid1 = vector(start.x,0.5*(start.y+end.y))
|
||||
mid2 = vector(end.x,mid1.y)
|
||||
self.add_path("metal2",[start,mid1,mid2,end])
|
||||
|
||||
width = self.bitcell_chars["BL"][0]- 0.5 * self.m1m2_via.width - offset.x+ drc["minwidth_metal2"]
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[offset[0],
|
||||
self.BL_position.y- 2*drc["minwidth_metal2"]],
|
||||
width=width,
|
||||
height=drc["minwidth_metal2"])
|
||||
|
||||
offset = self.nmos1_position + self.nmos1.active_contact_positions[1]
|
||||
# place the contact at the bottom of the src/drain
|
||||
offset = self.nmos1_position + self.nmos.active_contact_positions[1]
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[self.bitcell_chars["BL"][0]- 0.5 * self.m1m2_via.width,
|
||||
0],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=(drc["minwidth_metal2"] + offset[1]))
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[self.bitcell_chars["BL"][0]- 0.5 * self.m1m2_via.width,
|
||||
offset[1]],
|
||||
width=(offset.x- self.bitcell_chars["BL"][0]- 0.5 * self.m1m2_via.width
|
||||
+ 2 * drc["minwidth_metal2"]),
|
||||
offset=[self.bitcell.get_pin("BL").lx(), offset.y],
|
||||
width=(offset.x - self.bitcell.get_pin("BL").lx() + 2*drc["minwidth_metal2"]),
|
||||
height=drc["minwidth_metal2"])
|
||||
self.BL_out_position = vector(self.bitcell_chars["BL"][0]- 0.5* self.m1m2_via.width,
|
||||
0)
|
||||
self.add_label(text="bl_out",
|
||||
layer="metal2",
|
||||
offset=self.BL_out_position)
|
||||
|
||||
offset = [self.nmos2.active_contact_positions[1].x - self.m1m2_via.contact_width / 2,
|
||||
self.nmos2.active_position.y+ self.nmos2.active_height]
|
||||
offset = self.nmos2_position + offset
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset,
|
||||
mirror="MX")
|
||||
mid = offset + vector(drc["minwidth_metal2"],0)
|
||||
self.add_rect(layer="metal2",
|
||||
offset= mid,
|
||||
width= (self.bitcell_chars["BR"][0]- mid.x+ 0.5*self.m1m2_via.width),
|
||||
height=-drc["minwidth_metal2"])
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[self.bitcell_chars["BR"][0]- 0.5*self.m1m2_via.width,
|
||||
offset.y- drc["metal1_to_metal1"]],
|
||||
self.add_layout_pin(text="bl_out",
|
||||
layer="metal2",
|
||||
offset=[self.bitcell.get_pin("BL").lx(), 0],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=2*drc["minwidth_metal2"])
|
||||
self.BR_position = vector(self.bitcell_chars["BR"][0]- 0.5 * self.m1m2_via.width,
|
||||
self.BL_position.y)
|
||||
self.add_label(text="br",
|
||||
layer="metal2",
|
||||
offset=self.BR_position)
|
||||
height=drc["minwidth_metal2"] + offset.y)
|
||||
|
||||
offset = self.nmos2_position + self.nmos2.active_contact_positions[0]
|
||||
self.BR_out_position = vector(self.bitcell_chars["BR"][0]- 0.5 * self.m1m2_via.width,
|
||||
0)
|
||||
self.add_label(text="br_out",
|
||||
layer="metal2",
|
||||
offset=self.BR_out_position)
|
||||
BL_out_position = vector(self.bitcell.get_pin("BL").lx()- 0.5*self.m1m2_via.width, 0)
|
||||
|
||||
|
||||
# place the contact at the top of the src/drain
|
||||
offset = self.nmos2_position + vector(self.nmos.active_contact_positions[1].x - 0.5*self.m1m2_via.contact_width
|
||||
- 2 * (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width),
|
||||
self.nmos.active_position.y + self.nmos.active_height - self.m1m2_via.height)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
|
||||
br_offset = vector(self.bitcell.get_pin("BR").lx(),self.height) - vector(0,2*drc["minwidth_metal2"])
|
||||
self.add_layout_pin(text="br",
|
||||
layer="metal2",
|
||||
offset=br_offset,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=2*drc["minwidth_metal2"])
|
||||
|
||||
# draw an enclosing rectangle for the small jog
|
||||
ll = vector(min(offset.x,br_offset.x),min(offset.y,br_offset.y))
|
||||
ur = vector(max(offset.x+self.m1m2_via.width,br_offset.x+drc["minwidth_metal2"]),
|
||||
max(offset.y+self.m1m2_via.height,br_offset.y+2*drc["minwidth_metal2"]))
|
||||
self.add_rect(layer="metal2",
|
||||
offset=ll,
|
||||
width=ur.x-ll.x,
|
||||
height=ur.y-ll.y)
|
||||
|
||||
|
||||
# place the contact in the bottom
|
||||
offset = self.nmos2_position + self.nmos.active_contact_positions[0]
|
||||
BR_out_position = vector(self.bitcell.get_pin("BR").lx(), 0)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=offset,
|
||||
width=self.BR_out_position.x - offset[0],
|
||||
width=BR_out_position.x - offset.x,
|
||||
height=drc["minwidth_metal2"])
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[self.BR_out_position.x,
|
||||
offset.y+ drc["minwidth_metal2"]],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=-(offset.y+ drc["minwidth_metal2"]))
|
||||
self.add_layout_pin(text="br_out",
|
||||
layer="metal2",
|
||||
offset=[BR_out_position.x, 0],
|
||||
height=offset.y+ drc["minwidth_metal2"])
|
||||
|
||||
def add_gnd_rail(self):
|
||||
self.gnd_position = vector(self.bitcell_chars["gnd"][0]- 0.5 * self.m1m2_via.width,
|
||||
0)
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal2",
|
||||
offset=self.gnd_position,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.BL_position[1])
|
||||
|
||||
gnd_pins = self.bitcell.get_pins("gnd")
|
||||
for gnd_pin in gnd_pins:
|
||||
# only use vertical gnd pins that span the whole cell
|
||||
if gnd_pin.layer == "metal2" and gnd_pin.height >= self.bitcell.height:
|
||||
gnd_position = vector(gnd_pin.lx(), 0)
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal2",
|
||||
offset=gnd_position,
|
||||
height=self.get_pin("bl").uy())
|
||||
|
||||
def add_well_contacts(self):
|
||||
offset = vector(self.gnd_position.x+ drc["minwidth_metal2"],
|
||||
self.nmos1.poly_height / 2)
|
||||
# find right most gnd rail
|
||||
gnd_pins = self.bitcell.get_pins("gnd")
|
||||
right_gnd = None
|
||||
for gnd_pin in gnd_pins:
|
||||
if right_gnd == None or gnd_pin.lx()>right_gnd.lx():
|
||||
right_gnd = gnd_pin
|
||||
|
||||
# Add to the right (first) gnd rail
|
||||
m1m2_offset = right_gnd.ll() + vector(-0.5*self.m1m2_via.width,self.nmos.poly_height/2)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset - vector(self.m1m2_via.width / 2, 0),
|
||||
mirror="MY")
|
||||
offset=m1m2_offset)
|
||||
active_offset = right_gnd.ll() + vector(-self.m1m2_via.width,self.nmos.poly_height/2)
|
||||
self.add_contact(layers=("active", "contact", "metal1"),
|
||||
offset=offset - vector(self.m1m2_via.width, 0),
|
||||
mirror="MY")
|
||||
temp = vector(self.m1m2_via.width,
|
||||
(self.pwell_contact.first_layer_height - self.pwell_contact.second_layer_height) / 2)
|
||||
offset_implant = offset - temp + vector([drc["implant_to_contact"]]*2).scale(1,-1)
|
||||
offset=active_offset)
|
||||
|
||||
offset_implant = active_offset + vector([drc["implant_to_contact"]]*2).scale(-1,-1)
|
||||
implant_width = 2*drc["implant_to_contact"] + self.well_contact.width
|
||||
implant_height = 2*drc["implant_to_contact"] + self.well_contact.height
|
||||
self.add_rect(layer="pimplant",
|
||||
offset=offset_implant,
|
||||
width=-(2*drc["implant_to_contact"] + self.pwell_contact.first_layer_width),
|
||||
height=2*drc["implant_to_contact"] + self.pwell_contact.width)
|
||||
width=implant_width,
|
||||
height=implant_height)
|
||||
|
||||
offset_well = self.nmos1_position + vector(self.nmos1.width, 0)
|
||||
offset_well = self.nmos1_position + vector(self.nmos.width, 0)
|
||||
self.add_rect(layer="pwell",
|
||||
offset=offset_well,
|
||||
width=self.gnd_position.x+ drc["minwidth_metal2"] - offset_well[0],
|
||||
height=self.nmos1.height + drc["minwidth_poly"])
|
||||
width=offset_implant.x + implant_width + drc["well_enclosure_active"] - offset_well.x,
|
||||
height=self.nmos2_position.y)
|
||||
self.add_rect(layer="vtg",
|
||||
offset=offset_well,
|
||||
width=self.gnd_position.x+ drc["minwidth_metal2"] - offset_well[0],
|
||||
height=self.nmos1.height + drc["minwidth_poly"])
|
||||
width=offset_implant.x + implant_width + drc["well_enclosure_active"] - offset_well.x,
|
||||
height=self.nmos2_position.y)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.width = self.width = self.bitcell_chars["width"]
|
||||
self.height = self.height = self.BL_position[1]
|
||||
|
|
|
|||
|
|
@ -14,28 +14,25 @@ class single_level_column_mux_array(design.design):
|
|||
Array of column mux to read the bitlines through the 6T.
|
||||
"""
|
||||
|
||||
def __init__(self, rows, columns, word_size):
|
||||
def __init__(self, columns, word_size):
|
||||
design.design.__init__(self, "columnmux_array")
|
||||
debug.info(1, "Creating {0}".format(self.name))
|
||||
self.rows = rows
|
||||
self.columns = columns
|
||||
self.word_size = word_size
|
||||
self.words_per_row = self.columns / self.word_size
|
||||
self.row_addr_size = self.decoder_inputs = int(math.log(self.rows, 2))
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
self.offset_all_coordinates()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(self.columns):
|
||||
self.add_pin("bl[{0}]".format(i))
|
||||
self.add_pin("br[{0}]".format(i))
|
||||
for i in range(self.columns / self.words_per_row):
|
||||
self.add_pin("bl_out[{0}]".format(i * self.words_per_row))
|
||||
self.add_pin("br_out[{0}]".format(i * self.words_per_row))
|
||||
self.add_pin("bl[{}]".format(i))
|
||||
self.add_pin("br[{}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("bl_out[{}]".format(i))
|
||||
self.add_pin("br_out[{}]".format(i))
|
||||
for i in range(self.words_per_row):
|
||||
self.add_pin("sel[{0}]".format(i))
|
||||
self.add_pin("sel[{}]".format(i))
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_layout(self):
|
||||
|
|
@ -47,226 +44,169 @@ class single_level_column_mux_array(design.design):
|
|||
def add_modules(self):
|
||||
self.mux = single_level_column_mux(name="single_level_column_mux",
|
||||
tx_size=8)
|
||||
self.single_mux = self.mux
|
||||
self.add_mod(self.mux)
|
||||
|
||||
# This is not instantiated and used for calculations only.
|
||||
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
|
||||
|
||||
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.column_addr_size = num_of_inputs = int(self.words_per_row / 2)
|
||||
self.width = (self.columns * self.mux.width)
|
||||
self.gnd_positions = []
|
||||
self.BL_out_positions = []
|
||||
self.BR_out_positions = []
|
||||
self.BL_positions = []
|
||||
self.BR_positions = []
|
||||
self.addr_line_positions = []
|
||||
|
||||
spacing = self.m1m2_via.width + drc['metal1_to_metal1']
|
||||
self.height = self.mux.height + spacing + 4 * drc['metal2_to_metal2']
|
||||
if (self.words_per_row > 1):
|
||||
# 1 for BL and another for BR
|
||||
self.height = self.height + (self.words_per_row + 1) * spacing
|
||||
self.width = self.columns * self.mux.width
|
||||
|
||||
self.m1_pitch = self.m1m2_via.width + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
|
||||
# To correct the offset between M1 and M2 via enclosures
|
||||
self.offset_fix = vector(0,0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]))
|
||||
# one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br
|
||||
# one extra route pitch is to space from the sense amp
|
||||
self.route_height = (self.words_per_row + 3)*self.m1_pitch
|
||||
# mux height plus routing signal height plus well spacing at the top
|
||||
self.height = self.mux.height + self.route_height + drc["pwell_to_nwell"]
|
||||
|
||||
def create_array(self):
|
||||
for i in range(self.columns):
|
||||
name = "XMUX{0}".format(i)
|
||||
x_off = vector(i * self.mux.width, 0)
|
||||
self.add_inst(name=name,
|
||||
mod=self.mux,
|
||||
offset=x_off)
|
||||
self.mux_inst = []
|
||||
|
||||
""" draw a vertical m2 rail to extend BL BR & gnd on top of the cell """
|
||||
# FIXME: These are just min metal squares, are they needed?
|
||||
self.add_rect(layer="metal2",
|
||||
offset=x_off + self.mux.BL_position,
|
||||
width=drc['minwidth_metal2'],
|
||||
height=drc['minwidth_metal2'])
|
||||
self.add_rect(layer="metal2",
|
||||
offset=x_off + self.mux.BR_position,
|
||||
width=drc['minwidth_metal2'],
|
||||
height=drc['minwidth_metal2'])
|
||||
self.add_rect(layer="metal2",
|
||||
offset=x_off + self.mux.gnd_position,
|
||||
width=drc['minwidth_metal2'],
|
||||
height=drc['minwidth_metal2'])
|
||||
# For every column, add a pass gate
|
||||
for col_num in range(self.columns):
|
||||
name = "XMUX{0}".format(col_num)
|
||||
x_off = vector(col_num * self.mux.width, self.route_height)
|
||||
self.mux_inst.append(self.add_inst(name=name,
|
||||
mod=self.mux,
|
||||
offset=x_off))
|
||||
|
||||
""" add labels for the column_mux array """
|
||||
BL = self.mux.BL_position + vector(i * self.mux.width, 0)
|
||||
self.BL_positions.append(BL)
|
||||
self.add_label(text="bl[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=BL)
|
||||
offset = self.mux_inst[-1].get_pin("bl").ll()
|
||||
self.add_layout_pin(text="bl[{}]".format(col_num),
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
height=self.height-offset.y)
|
||||
|
||||
BR = self.mux.BR_position + vector(i * self.mux.width, 0)
|
||||
self.BR_positions.append(BR)
|
||||
self.add_label(text="br[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=BR)
|
||||
offset = self.mux_inst[-1].get_pin("br").ll()
|
||||
self.add_layout_pin(text="br[{}]".format(col_num),
|
||||
layer="metal2",
|
||||
offset=offset,
|
||||
height=self.height-offset.y)
|
||||
|
||||
gnd = self.mux.gnd_position + vector(i * self.mux.width, 0)
|
||||
self.gnd_positions.append(gnd)
|
||||
self.add_label(text="gnd",
|
||||
layer="metal2",
|
||||
offset=gnd)
|
||||
gnd_pins = self.mux_inst[-1].get_pins("gnd")
|
||||
for gnd_pin in gnd_pins:
|
||||
# only do even colums to avoid duplicates
|
||||
offset = gnd_pin.ll()
|
||||
if col_num % 2 == 0:
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal2",
|
||||
offset=offset.scale(1,0),
|
||||
height=self.height)
|
||||
|
||||
self.connect_inst(["bl[{}]".format(col_num),
|
||||
"br[{}]".format(col_num),
|
||||
"bl_out[{}]".format(int(col_num/self.words_per_row)),
|
||||
"br_out[{}]".format(int(col_num/self.words_per_row)),
|
||||
"sel[{}]".format(col_num % self.words_per_row),
|
||||
"gnd"])
|
||||
|
||||
for i in range(self.word_size):
|
||||
base =vector(i * self.words_per_row * self.mux.width, 0)
|
||||
BL_out = base + self.mux.BL_out_position
|
||||
BR_out = base + self.mux.BR_out_position
|
||||
self.add_label(text="bl_out[{0}]".format(i * self.words_per_row),
|
||||
layer="metal2",
|
||||
offset=BL_out)
|
||||
self.add_label(text="br_out[{0}]".format(i * self.words_per_row),
|
||||
layer="metal2",
|
||||
offset=BR_out)
|
||||
self.BL_out_positions.append(BL_out)
|
||||
self.BR_out_positions.append(BR_out)
|
||||
|
||||
if(self.words_per_row == 2):
|
||||
for i in range(self.columns / 2):
|
||||
# This will not check that the inst connections match.
|
||||
self.connect_inst(args=["bl[{0}]".format(2 * i),
|
||||
"br[{0}]".format(2 * i),
|
||||
"bl_out[{0}]".format(2 * i),
|
||||
"br_out[{0}]".format(2 * i),
|
||||
"sel[{0}]".format(0), "gnd"],
|
||||
check=False)
|
||||
# This will not check that the inst connections match.
|
||||
self.connect_inst(args=["bl[{0}]".format(2 * i + 1),
|
||||
"br[{0}]".format(2 * i + 1),
|
||||
"bl_out[{0}]".format(2 * i),
|
||||
"br_out[{0}]".format(2 * i),
|
||||
"sel[{0}]".format(1), "gnd"],
|
||||
check=False)
|
||||
if(self.words_per_row == 4):
|
||||
for i in range(self.columns / 4):
|
||||
# This will not check that the inst connections match.
|
||||
self.connect_inst(args=["bl[{0}]".format(4 * i),
|
||||
"br[{0}]".format(4 * i),
|
||||
"bl_out[{0}]".format(4 * i),
|
||||
"br_out[{0}]".format(4 * i),
|
||||
"sel[{0}]".format(0), "gnd"],
|
||||
check=False)
|
||||
# This will not check that the inst connections match.
|
||||
self.connect_inst(args=["bl[{0}]".format(4 * i + 1),
|
||||
"br[{0}]".format(4 * i + 1),
|
||||
"bl_out[{0}]".format(4 * i),
|
||||
"br_out[{0}]".format(4 * i),
|
||||
"sel[{0}]".format(1), "gnd"],
|
||||
check=False)
|
||||
# This will not check that the inst connections match.
|
||||
self.connect_inst(args=["bl[{0}]".format(4 * i + 2),
|
||||
"br[{0}]".format(4 * i + 2),
|
||||
"bl_out[{0}]".format(4 * i),
|
||||
"br_out[{0}]".format(4 * i),
|
||||
"sel[{0}]".format(2), "gnd"],
|
||||
check=False)
|
||||
# This will not check that the inst connections match.
|
||||
self.connect_inst(args=["bl[{0}]".format(4 * i + 3),
|
||||
"br[{0}]".format(4 * i + 3),
|
||||
"bl_out[{0}]".format(4 * i),
|
||||
"br_out[{0}]".format(4 * i),
|
||||
"sel[{0}]".format(3), "gnd"],
|
||||
check=False)
|
||||
|
||||
|
||||
def add_routing(self):
|
||||
self.add_horizontal_input_rail()
|
||||
self.add_vertical_poly_rail()
|
||||
self.routing_BL_BR()
|
||||
self.route_bitlines()
|
||||
|
||||
def add_horizontal_input_rail(self):
|
||||
""" HORIZONTAL ADDRESS INPUTS TO THE COLUMN MUX ARRAY """
|
||||
if (self.words_per_row > 1):
|
||||
for j in range(self.words_per_row):
|
||||
offset = vector(0, -(j + 1) * self.m1m2_via.width
|
||||
- j * drc['metal1_to_metal1'])
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=self.mux.width * self.columns,
|
||||
height=self.m1m2_via.width)
|
||||
self.addr_line_positions.append(offset)
|
||||
""" Create address input rails on M1 below the mux transistors """
|
||||
for j in range(self.words_per_row):
|
||||
offset = vector(0, self.route_height - (j+1)*self.m1_pitch)
|
||||
self.add_layout_pin(text="sel[{}]".format(j),
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
width=self.mux.width * self.columns,
|
||||
height=self.m1m2_via.width)
|
||||
|
||||
def add_vertical_poly_rail(self):
|
||||
""" VERTICAL POLY METAL EXTENSION AND POLY CONTACT """
|
||||
for j1 in range(self.columns):
|
||||
pattern = math.floor(j1 / self.words_per_row) * self.words_per_row
|
||||
height = ((self.m1m2_via.width + drc['metal1_to_metal1'])
|
||||
*(pattern - j1))
|
||||
nmos1_poly = self.mux.nmos1_position + self.mux.nmos1.poly_positions[0]
|
||||
offset = nmos1_poly.scale(1, 0) + vector(j1 * self.mux.width, 0)
|
||||
""" Connect the poly to the address rails """
|
||||
|
||||
# Offset to the first transistor gate in the pass gate
|
||||
nmos_offset = (self.mux.nmos1_position + self.mux.nmos.poly_positions[0]).scale(1,0)
|
||||
for col in range(self.columns):
|
||||
# which select bit should this column connect to depends on the position in the word
|
||||
sel_index = col % self.words_per_row
|
||||
# Add the column x offset to find the right select bit
|
||||
gate_offset = nmos_offset + vector(col * self.mux.width , 0)
|
||||
# height to connect the gate to the correct horizontal row
|
||||
sel_height = self.get_pin("sel[{}]".format(sel_index)).by()
|
||||
# use the y offset from the sel pin and the x offset from the gate
|
||||
offset = vector(gate_offset.x,self.get_pin("sel[{}]".format(sel_index)).by())
|
||||
self.add_rect(layer="poly",
|
||||
offset=offset,
|
||||
width=drc["minwidth_poly"],
|
||||
height= height -self.m1m2_via.width)
|
||||
height=self.route_height - sel_height)
|
||||
|
||||
# This is not instantiated and used for calculations only.
|
||||
poly_contact = contact(layer_stack=("metal1", "contact", "poly"))
|
||||
offset = offset.scale(1, 0) + vector(0, height - poly_contact.width)
|
||||
# Add the poly contact with a shift to account for the rotation
|
||||
self.add_contact(layers=("metal1", "contact", "poly"),
|
||||
offset=offset,
|
||||
mirror="MX",
|
||||
offset=offset + vector(self.m1m2_via.height,0),
|
||||
rotate=90)
|
||||
|
||||
def routing_BL_BR(self):
|
||||
""" OUTPUT BIT-LINE CONNECTIONS (BL_OUT, BR_OUT) """
|
||||
if (self.words_per_row > 1):
|
||||
for j in range(self.columns / self.words_per_row):
|
||||
base = vector(self.mux.width * self.words_per_row * j,
|
||||
self.m1m2_via.width + drc['metal1_to_metal1'])
|
||||
def route_bitlines(self):
|
||||
""" Connect the output bit-lines to form the appropriate width mux """
|
||||
for j in range(self.columns):
|
||||
bl_offset = self.mux_inst[j].get_pin("bl_out").ll()
|
||||
br_offset = self.mux_inst[j].get_pin("br_out").ll()
|
||||
|
||||
bl_out_offset = bl_offset - vector(0,(self.words_per_row+1)*self.m1_pitch)
|
||||
br_out_offset = br_offset - vector(0,(self.words_per_row+2)*self.m1_pitch)
|
||||
|
||||
if (j % self.words_per_row) == 0:
|
||||
# Create the metal1 to connect the n-way mux output from the pass gate
|
||||
# These will be located below the select lines. Yes, these are M2 width
|
||||
# to ensure vias are enclosed and M1 min width rules.
|
||||
width = self.m1m2_via.width + self.mux.width * (self.words_per_row - 1)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=base.scale(1,-self.words_per_row) + self.mux.BL_position.scale(1,0),
|
||||
offset=bl_out_offset,
|
||||
width=width,
|
||||
height=-self.m1m2_via.width)
|
||||
height=drc["minwidth_metal2"])
|
||||
self.add_rect(layer="metal1",
|
||||
offset=base.scale(1,-self.words_per_row-1) + self.mux.BR_position.scale(1,0),
|
||||
offset=br_out_offset,
|
||||
width=width,
|
||||
height=-self.m1m2_via.width)
|
||||
height=drc["minwidth_metal2"])
|
||||
|
||||
|
||||
height = base.y * (self.words_per_row + 2) + 3 * drc['metal2_to_metal2']
|
||||
base = vector(base.x, - height)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=base + self.mux.BL_position.scale(1,0),
|
||||
width=drc['minwidth_metal2'],
|
||||
height=height)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=base + self.mux.BR_position.scale(1,0),
|
||||
width=drc['minwidth_metal2'],
|
||||
height=height)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=base + self.mux.gnd_position.scale(1,0),
|
||||
width=drc['minwidth_metal2'],
|
||||
height=height)
|
||||
# Extend the bitline output rails and gnd downward on the first bit of each n-way mux
|
||||
self.add_layout_pin(text="bl_out[{}]".format(int(j/self.words_per_row)),
|
||||
layer="metal2",
|
||||
offset=bl_out_offset.scale(1,0),
|
||||
width=drc['minwidth_metal2'],
|
||||
height=self.route_height)
|
||||
self.add_layout_pin(text="br_out[{}]".format(int(j/self.words_per_row)),
|
||||
layer="metal2",
|
||||
offset=br_out_offset.scale(1,0),
|
||||
width=drc['minwidth_metal2'],
|
||||
height=self.route_height)
|
||||
|
||||
for j in range(self.columns):
|
||||
""" adding vertical metal rails to route BL_out and BR_out vertical rails """
|
||||
contact_spacing = self.m1m2_via.width + drc['metal1_to_metal1']
|
||||
height = self.words_per_row * contact_spacing + self.m1m2_via.width
|
||||
offset = vector(self.mux.BL_position.x + self.mux.width * j, 0)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=offset,
|
||||
width=drc['minwidth_metal2'],
|
||||
height=-height)
|
||||
offset = offset + vector(self.m1m2_via.height, - height)
|
||||
# This via is on the right of the wire
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset,
|
||||
offset=bl_out_offset + vector(self.m1m2_via.height,0),
|
||||
rotate=90)
|
||||
# This via is on the left of the wire
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset= br_out_offset,
|
||||
rotate=90)
|
||||
|
||||
offset = vector(self.mux.BR_position.x + self.mux.width * j, 0)
|
||||
height = height + contact_spacing
|
||||
else:
|
||||
|
||||
self.add_rect(layer="metal2",
|
||||
offset=offset,
|
||||
offset=bl_out_offset,
|
||||
width=drc['minwidth_metal2'],
|
||||
height= - height)
|
||||
offset = offset + vector(self.m1m2_via.height/2, - height)
|
||||
layer_diff = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width)
|
||||
height=self.route_height-bl_out_offset.y)
|
||||
# This via is on the right of the wire
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset= offset + vector(layer_diff, 0),
|
||||
offset=bl_out_offset + vector(self.m1m2_via.height,0),
|
||||
rotate=90)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=br_out_offset,
|
||||
width=drc['minwidth_metal2'],
|
||||
height=self.route_height-br_out_offset.y)
|
||||
# This via is on the left of the wire
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset= br_out_offset,
|
||||
rotate=90)
|
||||
|
||||
self.add_label(text="COLUMN_MUX",
|
||||
layer="text",
|
||||
offset=[self.width / 2.0, self.height / 2.0])
|
||||
|
||||
|
||||
|
|
|
|||
167
compiler/sram.py
167
compiler/sram.py
|
|
@ -18,21 +18,23 @@ class sram(design.design):
|
|||
"""
|
||||
|
||||
def __init__(self, word_size, num_words, num_banks, name):
|
||||
mod_list = ["control_logic", "ms_flop_array", "ms_flop", "bitcell"]
|
||||
for mod_name in mod_list:
|
||||
config_mod_name = getattr(OPTS.config, mod_name)
|
||||
class_file = reload(__import__(config_mod_name))
|
||||
mod_class = getattr(class_file , config_mod_name)
|
||||
setattr (self, "mod_"+mod_name, mod_class)
|
||||
|
||||
c = reload(__import__(OPTS.config.control_logic))
|
||||
self.mod_control_logic = getattr(c, OPTS.config.control_logic)
|
||||
|
||||
c = reload(__import__(OPTS.config.ms_flop_array))
|
||||
self.mod_ms_flop_array = getattr(c, OPTS.config.ms_flop_array)
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
self.bitcell = self.mod_bitcell()
|
||||
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
# in case we create more than one SRAM
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
self.ms_flop_chars = self.mod_ms_flop.chars
|
||||
self.bitcell_chars = self.mod_bitcell.chars
|
||||
|
||||
self.word_size = word_size
|
||||
self.num_words = num_words
|
||||
self.num_banks = num_banks
|
||||
|
|
@ -41,75 +43,69 @@ class sram(design.design):
|
|||
self.num_words))
|
||||
|
||||
design.design.__init__(self, name)
|
||||
self.ctrl_positions = {}
|
||||
|
||||
|
||||
self.control_size = 6
|
||||
self.bank_to_bus_distance = 5*drc["minwidth_metal3"]
|
||||
|
||||
self.compute_sizes()
|
||||
self.add_pins()
|
||||
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def compute_sizes(self):
|
||||
""" Computes the required sizes to create the memory """
|
||||
self.check_num_banks(self.num_banks)
|
||||
""" Computes the organization of the memory using bitcell size by trying to make it square."""
|
||||
debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.")
|
||||
|
||||
self.num_words_per_bank = self.num_words/self.num_banks
|
||||
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
|
||||
|
||||
self.bank_area = self.bitcell_chars["width"]*\
|
||||
self.bitcell_chars["height"]*self.num_bits_per_bank
|
||||
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
|
||||
self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank
|
||||
self.bank_side_length = math.sqrt(self.bank_area)
|
||||
|
||||
self.tentative_num_cols = int(self.bank_side_length/self.bitcell_chars["width"])
|
||||
self.words_per_row = self.cal_words_per_row(self.tentative_num_cols,
|
||||
self.word_size)
|
||||
self.tentative_num_rows = self.num_bits_per_bank \
|
||||
/(self.words_per_row \
|
||||
*self.word_size)
|
||||
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows,
|
||||
self.words_per_row)
|
||||
# Estimate the words per row given the height of the bitcell and the square side length
|
||||
self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width)
|
||||
self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size)
|
||||
|
||||
# Estimate the number of rows given the tentative words per row
|
||||
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size)
|
||||
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
|
||||
|
||||
# Fix the number of columns and rows
|
||||
self.num_cols = self.words_per_row*self.word_size
|
||||
self.num_rows = self.num_words_per_bank/self.words_per_row
|
||||
|
||||
|
||||
# Compute the address and bank sizes
|
||||
self.row_addr_size = int(log(self.num_rows, 2))
|
||||
self.col_addr_size = int(log(self.words_per_row, 2))
|
||||
self.bank_addr_size = self.col_addr_size \
|
||||
+ self.row_addr_size
|
||||
self.addr_size = self.bank_addr_size + \
|
||||
int(math.log(self.num_banks, 2))
|
||||
self.bank_addr_size = self.col_addr_size + self.row_addr_size
|
||||
self.addr_size = self.bank_addr_size + int(math.log(self.num_banks, 2))
|
||||
|
||||
self.control_size = 6
|
||||
|
||||
self.bank_to_bus_distance = 5*drc["minwidth_metal3"]
|
||||
def estimate_words_per_row(self,tentative_num_cols, word_size):
|
||||
"""This provides a heuristic rounded estimate for the number of words
|
||||
per row."""
|
||||
|
||||
def check_num_banks(self,num_banks):
|
||||
if(num_banks != 1 and num_banks != 2 and num_banks != 4):
|
||||
debug.error("Valid number of banks are 1 , 2 and 4.")
|
||||
sys.exit(-1)
|
||||
|
||||
def cal_words_per_row(self,tentative_num_cols, word_size):
|
||||
if(tentative_num_cols < 1.5*word_size):
|
||||
words_per_row = 1
|
||||
elif(tentative_num_cols > 3*word_size):
|
||||
words_per_row = 4
|
||||
if tentative_num_cols < 1.5*word_size:
|
||||
return 1
|
||||
elif tentative_num_cols > 3*word_size:
|
||||
return 4
|
||||
else:
|
||||
words_per_row = 2
|
||||
return words_per_row
|
||||
return 2
|
||||
|
||||
def amend_words_per_row(self,tentative_num_rows, words_per_row):
|
||||
"""This picks the number of words per row more accurately by limiting
|
||||
it to a minimum and maximum.
|
||||
"""
|
||||
# Recompute the words per row given a hard max
|
||||
if(tentative_num_rows > 512):
|
||||
if(tentative_num_rows*words_per_row > 2048):
|
||||
debug.error("Number of rows exceeds 512")
|
||||
sys.exit(-1)
|
||||
words_per_row = words_per_row*tentative_num_rows/512
|
||||
|
||||
debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048")
|
||||
return words_per_row*tentative_num_rows/512
|
||||
# Recompute the words per row given a hard min
|
||||
if(tentative_num_rows < 16):
|
||||
if(tentative_num_rows*words_per_row < 16):
|
||||
debug.error("minimum number of rows is 16, but given {0}".format(
|
||||
tentative_num_rows))
|
||||
sys.exit(-1)
|
||||
words_per_row = words_per_row*tentative_num_rows/16
|
||||
debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
|
||||
return words_per_row*tentative_num_rows/16
|
||||
|
||||
return words_per_row
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -124,12 +120,14 @@ class sram(design.design):
|
|||
|
||||
def create_layout(self):
|
||||
""" Layout creation """
|
||||
|
||||
self.create_modules()
|
||||
self.add_modules()
|
||||
self.add_routing()
|
||||
|
||||
def add_routing(self):
|
||||
""" Route all of the modules """
|
||||
|
||||
if (self.num_banks == 2 or self.num_banks == 4):
|
||||
self.route_2or4_banks()
|
||||
if (self.num_banks == 4):
|
||||
|
|
@ -139,6 +137,7 @@ class sram(design.design):
|
|||
|
||||
def create_multibank_modules(self):
|
||||
""" Add the multibank address flops and bank decoder """
|
||||
|
||||
self.msf_msb_address = self.mod_ms_flop_array(name="msf_msb_address",
|
||||
columns=self.num_banks/2,
|
||||
word_size=self.num_banks/2)
|
||||
|
|
@ -259,8 +258,8 @@ class sram(design.design):
|
|||
temp.append("ADDR[{0}]".format(i))
|
||||
if(self.num_banks > 1):
|
||||
temp.append("bank_select[{0}]".format(self.bank_count))
|
||||
temp = temp + ["s_en" , "w_en", "tri_en_bar", "tri_en",
|
||||
"clk_bar", "clk" , "vdd" , "gnd" ]
|
||||
temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en",
|
||||
"clk_bar","clk" , "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
# Saving control line properties
|
||||
|
|
@ -359,13 +358,13 @@ class sram(design.design):
|
|||
line_gap = 2*drc[m2m]
|
||||
return bits*(line_width + line_gap) - line_gap
|
||||
|
||||
def add_control_logic(self, position, mirror):
|
||||
def add_control_logic(self, position, rotate):
|
||||
""" Add and place control logic """
|
||||
self.control_position = position
|
||||
self.add_inst(name="control",
|
||||
mod=self.control,
|
||||
offset=self.control_position,
|
||||
mirror=mirror)
|
||||
rotate=rotate)
|
||||
temp = ["CSb", "WEb", "OEb", "s_en", "w_en", "tri_en",
|
||||
"tri_en_bar", "clk_bar", "clk", "vdd", "gnd"]
|
||||
self.connect_inst(temp)
|
||||
|
|
@ -377,20 +376,20 @@ class sram(design.design):
|
|||
# FIXME: document
|
||||
loc = vector(- 2 * drc["minwidth_metal3"],
|
||||
self.bank_positions[0].y + self.bank.decoder_position.y
|
||||
+ 2 * drc["minwidth_metal3"])
|
||||
self.add_control_logic(loc, "R90")
|
||||
+ 2 * drc["minwidth_metal3"])
|
||||
self.add_control_logic(loc, 90)
|
||||
|
||||
self.width = self.bank.width + self.control.height + 2*drc["minwidth_metal3"]
|
||||
self.height = self.bank.height
|
||||
self.control.CSb_position.rotate_scale(-1,1)
|
||||
self.CSb_position = (self.control.CSb_position.rotate_scale(-1,1)
|
||||
+self.control_position)
|
||||
+self.control_position)
|
||||
self.OEb_position = (self.control.OEb_position.rotate_scale(-1,1)
|
||||
+self.control_position)
|
||||
+self.control_position)
|
||||
self.WEb_position = (self.control.WEb_position.rotate_scale(-1,1)
|
||||
+self.control_position)
|
||||
+self.control_position)
|
||||
self.clk_position = (self.control.clk_position.rotate_scale(-1,1)
|
||||
+self.control_position)
|
||||
+self.control_position)
|
||||
for i in range(0, self.word_size):
|
||||
self.add_label(text="DATA[{0}]".format(i),
|
||||
layer="metal3",
|
||||
|
|
@ -404,25 +403,23 @@ class sram(design.design):
|
|||
self.bank_w = self.bank.width
|
||||
|
||||
self.num_vertical_line = self.bank_addr_size + self.control_size \
|
||||
+ self.num_banks + self.num_banks/2
|
||||
+ self.num_banks + self.num_banks/2
|
||||
self.num_horizontal_line = self.word_size
|
||||
|
||||
self.vertical_bus_width = self.calculate_bus_width("metal2",
|
||||
self.num_vertical_line)
|
||||
self.horizontal_bus_width = self.calculate_bus_width("metal3",
|
||||
self.num_horizontal_line)
|
||||
self.vertical_bus_width = self.calculate_bus_width("metal2", self.num_vertical_line)
|
||||
self.horizontal_bus_width = self.calculate_bus_width("metal3", self.num_horizontal_line)
|
||||
|
||||
self.vertical_bus_height = (self.num_banks/2)*(self.bank_h + self.bank_to_bus_distance) \
|
||||
+ self.horizontal_bus_width
|
||||
+ self.horizontal_bus_width
|
||||
self.horizontal_bus_height = (2 * (self.bank_w + self.bank_to_bus_distance)
|
||||
+ self.vertical_bus_width)
|
||||
+ self.vertical_bus_width)
|
||||
|
||||
self.vertical_bus_offset = vector(self.bank_w + self.bank_to_bus_distance,
|
||||
self.sram_power_rail_gap)
|
||||
self.horizontal_bus_offset = vector(0,
|
||||
self.bank_h + self.bank_to_bus_distance
|
||||
+ self.sram_power_rail_gap
|
||||
+ self.horizontal_bus_width)
|
||||
+ self.sram_power_rail_gap
|
||||
+ self.horizontal_bus_width)
|
||||
|
||||
# Vertical bus
|
||||
self.vertical_line_positions = self.create_bus(layer="metal2",
|
||||
|
|
@ -444,7 +441,7 @@ class sram(design.design):
|
|||
|
||||
self.width = 2*(self.bank_w + self.bank_to_bus_distance) + self.vertical_bus_width
|
||||
self.height = (self.num_banks/2)*(self.bank_h + self.bank_to_bus_distance) \
|
||||
+ self.horizontal_bus_width + self.sram_power_rail_gap
|
||||
+ self.horizontal_bus_width + self.sram_power_rail_gap
|
||||
|
||||
# Add Control logic for Bank = 2 and Bank =4
|
||||
|
||||
|
|
@ -464,7 +461,7 @@ class sram(design.design):
|
|||
if (self.num_banks == 2):
|
||||
self.control_position = vector(0, control_bus_offset.y
|
||||
+ self.ms_flop_chars["width"])
|
||||
self.add_control_logic(self.control_position, "R0")
|
||||
self.add_control_logic(self.control_position, 0)
|
||||
|
||||
self.CSb_position = self.control_position + self.control.CSb_position
|
||||
self.OEb_position = self.control_position + self.control.OEb_position
|
||||
|
|
@ -494,8 +491,8 @@ class sram(design.design):
|
|||
temp.append("msb{0}".format(i))
|
||||
temp.append("msb{0}_bar".format(i))
|
||||
else:
|
||||
temp = temp + ["bank_select[1]", "bank_select[0]"]
|
||||
temp = temp + ["clk", "vdd", "gnd"]
|
||||
temp.extend(["bank_select[1]", "bank_select[0]"])
|
||||
temp.extend(["clk", "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
self.add_banks_0and1()
|
||||
|
|
@ -575,7 +572,7 @@ class sram(design.design):
|
|||
|
||||
self.control_position = vector(0, self.msb_decoder_position.y
|
||||
+ self.msb_decoder.height)
|
||||
self.add_control_logic(self.control_position, "R0")
|
||||
self.add_control_logic(self.control_position, 0)
|
||||
|
||||
self.CSb_position = self.control_position + self.control.CSb_position
|
||||
self.OEb_position = self.control_position + self.control.OEb_position
|
||||
|
|
@ -738,13 +735,13 @@ class sram(design.design):
|
|||
self.add_via(layers=("metal2", "via2", "metal3"),
|
||||
offset=[bank_side[i].x + drc["minwidth_metal2"],
|
||||
control_side[i].y],
|
||||
mirror="R90")
|
||||
rotate=90)
|
||||
elif (self.num_banks == 2 or self.num_banks == 4):
|
||||
for i in range(self.control_size):
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[self.vertical_line_positions[i].x + drc["minwidth_metal2"],
|
||||
self.control_bus_line_positions[i].y],
|
||||
mirror="R90")
|
||||
rotate=90)
|
||||
control_attr = self.bank_property[i]
|
||||
control_side_line_position = (getattr(self.control,control_attr)
|
||||
+self.control_position)
|
||||
|
|
@ -759,7 +756,7 @@ class sram(design.design):
|
|||
offset=[control_side_line_position.x
|
||||
+ drc["minwidth_metal2"],
|
||||
self.control_bus_line_positions[i].y],
|
||||
mirror="R90")
|
||||
rotate=90)
|
||||
for i in range(self.num_banks/2):
|
||||
# MSB line connections
|
||||
msb_line = self.control_size + self.num_banks/2 - 1 - i
|
||||
|
|
@ -843,7 +840,7 @@ class sram(design.design):
|
|||
height=drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=end - vector(0, 0.5 * self.m1m2_via.width),
|
||||
mirror="R90")
|
||||
rotate=90)
|
||||
for i in range(4):
|
||||
bank_select_line = self.control_size + 2 + self.bank_addr_size + i
|
||||
msb_decoder_out = (self.msb_decoder_position
|
||||
|
|
@ -943,7 +940,7 @@ class sram(design.design):
|
|||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[control_vdd1_position.x + drc["minwidth_metal2"],
|
||||
control_vdd_supply.y],
|
||||
mirror="R90")
|
||||
rotate=90)
|
||||
|
||||
if (self.control.width > self.bank.width):
|
||||
last_bank = self.num_banks - 1
|
||||
|
|
@ -964,7 +961,7 @@ class sram(design.design):
|
|||
offset=[control_vdd2_position.x
|
||||
+ drc["minwidth_metal2"],
|
||||
control_vdd_supply.y],
|
||||
mirror="R90")
|
||||
rotate=90)
|
||||
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal2",
|
||||
|
|
@ -1078,12 +1075,12 @@ class sram(design.design):
|
|||
offset=[self.sram_bank_left_gnd_positions[0].x
|
||||
+ drc["minwidth_metal2"],
|
||||
control_gnd_supply.y],
|
||||
mirror="R90")
|
||||
rotate=90)
|
||||
# Control gnd
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[control_gnd_position.x + drc["minwidth_metal2"],
|
||||
control_gnd_supply.y],
|
||||
mirror="R90")
|
||||
rotate=90)
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal2",
|
||||
offset=[control_gnd_position.x,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ class contact_test(unittest.TestCase):
|
|||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import contact
|
||||
|
||||
|
|
@ -27,30 +28,22 @@ class contact_test(unittest.TestCase):
|
|||
|
||||
# Check single 1 x 1 contact"
|
||||
debug.info(2, "1 x 1 {} test".format(stack_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
c = contact.contact(layer_stack, (1, 1))
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(c)
|
||||
|
||||
# check vertical array with one in the middle and two ends
|
||||
debug.info(2, "1 x 3 {} test".format(stack_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
c = contact.contact(layer_stack, (1, 3))
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(c)
|
||||
|
||||
# check horizontal array with one in the middle and two ends
|
||||
debug.info(2, "3 x 1 {} test".format(stack_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
c = contact.contact(layer_stack, (3, 1))
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(c)
|
||||
|
||||
# check 3x3 array for all possible neighbors
|
||||
debug.info(2, "3 x 3 {} test".format(stack_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
c = contact.contact(layer_stack, (3, 3))
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(c)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ class path_test(unittest.TestCase):
|
|||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import path
|
||||
import tech
|
||||
|
|
@ -32,11 +33,9 @@ class path_test(unittest.TestCase):
|
|||
[4 * min_space, 3 * min_space ],
|
||||
[0, 3 * min_space ],
|
||||
[0, 6 * min_space ]]
|
||||
OPTS.check_lvsdrc = False
|
||||
w = design.design("path_test0")
|
||||
path.path(w,layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
|
||||
min_space = 2 * tech.drc["minwidth_metal1"]
|
||||
|
|
@ -51,11 +50,9 @@ class path_test(unittest.TestCase):
|
|||
[-1 * min_space, 4 * min_space],
|
||||
[-1 * min_space, 0]]
|
||||
position_list = [[x+min_space, y+min_space] for x,y in old_position_list]
|
||||
OPTS.check_lvsdrc = False
|
||||
w = design.design("path_test1")
|
||||
path.path(w,layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
min_space = 2 * tech.drc["minwidth_metal2"]
|
||||
layer_stack = ("metal2")
|
||||
|
|
@ -69,11 +66,9 @@ class path_test(unittest.TestCase):
|
|||
[-1 * min_space, 4 * min_space],
|
||||
[-1 * min_space, 0]]
|
||||
position_list = [[x-min_space, y-min_space] for x,y in old_position_list]
|
||||
OPTS.check_lvsdrc = False
|
||||
w = design.design("path_test2")
|
||||
path.path(w, layer_stack, position_list)
|
||||
self.local_check(w)
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
min_space = 2 * tech.drc["minwidth_metal3"]
|
||||
layer_stack = ("metal3")
|
||||
|
|
@ -88,11 +83,8 @@ class path_test(unittest.TestCase):
|
|||
[-1 * min_space, 0]]
|
||||
# run on the reverse list
|
||||
position_list.reverse()
|
||||
OPTS.check_lvsdrc = False
|
||||
w = design.design("path_test3")
|
||||
path.path(w, layer_stack, position_list)
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
self.local_check(w)
|
||||
|
||||
# return it back to it's normal state
|
||||
|
|
|
|||
|
|
@ -28,10 +28,9 @@ class ptx_test(unittest.TestCase):
|
|||
fet = ptx.ptx(width=tech.drc["minwidth_tx"],
|
||||
mults=1,
|
||||
tx_type="nmos")
|
||||
# return it back to it's normal state
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
self.local_check(fet)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,9 @@ class ptx_test(unittest.TestCase):
|
|||
fet = ptx.ptx(width=tech.drc["minwidth_tx"],
|
||||
mults=1,
|
||||
tx_type="pmos")
|
||||
# return it back to it's normal state
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
self.local_check(fet)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,9 @@ class ptx_test(unittest.TestCase):
|
|||
fet = ptx.ptx(width=tech.drc["minwidth_tx"],
|
||||
mults=3,
|
||||
tx_type="nmos")
|
||||
# return it back to it's normal state
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
self.local_check(fet)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def add_mods(self, fet):
|
||||
|
|
|
|||
|
|
@ -28,10 +28,9 @@ class ptx_test(unittest.TestCase):
|
|||
fet = ptx.ptx(width=tech.drc["minwidth_tx"],
|
||||
mults=3,
|
||||
tx_type="pmos")
|
||||
# return it back to it's normal state
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
self.local_check(fet)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def add_mods(self, fet):
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ class wire_test(unittest.TestCase):
|
|||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import wire
|
||||
import tech
|
||||
|
|
@ -36,10 +37,8 @@ class wire_test(unittest.TestCase):
|
|||
[-1 * min_space, 4 * min_space],
|
||||
[-1 * min_space, 0]]
|
||||
position_list = [[x-min_space, y-min_space] for x,y in old_position_list]
|
||||
OPTS.check_lvsdrc = False
|
||||
w = design.design("wire_test1")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(w)
|
||||
|
||||
min_space = 2 * (tech.drc["minwidth_poly"] +
|
||||
|
|
@ -55,10 +54,8 @@ class wire_test(unittest.TestCase):
|
|||
[-1 * min_space, 4 * min_space],
|
||||
[-1 * min_space, 0]]
|
||||
position_list = [[x+min_space, y+min_space] for x,y in old_position_list]
|
||||
OPTS.check_lvsdrc = False
|
||||
w = design.design("wire_test2")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(w)
|
||||
|
||||
min_space = 2 * (tech.drc["minwidth_metal2"] +
|
||||
|
|
@ -73,10 +70,8 @@ class wire_test(unittest.TestCase):
|
|||
[7 * min_space, 4 * min_space],
|
||||
[-1 * min_space, 4 * min_space],
|
||||
[-1 * min_space, 0]]
|
||||
OPTS.check_lvsdrc = False
|
||||
w = design.design("wire_test3")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(w)
|
||||
|
||||
|
||||
|
|
@ -92,10 +87,8 @@ class wire_test(unittest.TestCase):
|
|||
[7 * min_space, 4 * min_space],
|
||||
[-1 * min_space, 4 * min_space],
|
||||
[-1 * min_space, 0]]
|
||||
OPTS.check_lvsdrc = False
|
||||
w = design.design("wire_test4")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(w)
|
||||
|
||||
min_space = 2 * (tech.drc["minwidth_metal2"] +
|
||||
|
|
@ -111,10 +104,8 @@ class wire_test(unittest.TestCase):
|
|||
[-1 * min_space, 4 * min_space],
|
||||
[-1 * min_space, 0]]
|
||||
position_list.reverse()
|
||||
OPTS.check_lvsdrc = False
|
||||
w = design.design("wire_test5")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(w)
|
||||
|
||||
min_space = 2 * (tech.drc["minwidth_metal2"] +
|
||||
|
|
@ -130,10 +121,8 @@ class wire_test(unittest.TestCase):
|
|||
[-1 * min_space, 4 * min_space],
|
||||
[-1 * min_space, 0]]
|
||||
position_list.reverse()
|
||||
OPTS.check_lvsdrc = False
|
||||
w = design.design("wire_test6")
|
||||
wire.wire(w, layer_stack, position_list)
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(w)
|
||||
|
||||
# return it back to it's normal state
|
||||
|
|
|
|||
|
|
@ -31,8 +31,9 @@ class nand_2_test(unittest.TestCase):
|
|||
|
||||
debug.info(2, "Checking 2-input nand gate")
|
||||
tx = nand_2.nand_2(nmos_width=2 * tech.drc["minwidth_tx"])
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(tx)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ class nand_3_test(unittest.TestCase):
|
|||
|
||||
debug.info(2, "Checking 3-input nand gate")
|
||||
tx = nand_3.nand_3(nmos_width=3 * tech.drc["minwidth_tx"])
|
||||
self.local_check(tx)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(tx)
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
|
|
|
|||
|
|
@ -30,8 +30,9 @@ class nor_2_test(unittest.TestCase):
|
|||
|
||||
debug.info(2, "Checking 2-input nor gate")
|
||||
tx = nor_2.nor_2(nmos_width=2 * tech.drc["minwidth_tx"])
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(tx)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
|
|
|
|||
|
|
@ -20,28 +20,24 @@ class pinv_test(unittest.TestCase):
|
|||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import pinv
|
||||
import tech
|
||||
|
||||
# debug.info(2, "Checking min size inverter")
|
||||
# OPTS.check_lvsdrc = False
|
||||
# tx = pinv.pinv(nmos_width=tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
|
||||
# OPTS.check_lvsdrc = True
|
||||
# self.local_check(tx)
|
||||
|
||||
# debug.info(2, "Checking 2x min size inverter")
|
||||
# OPTS.check_lvsdrc = False
|
||||
# tx = pinv.pinv(nmos_width=2 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
|
||||
# OPTS.check_lvsdrc = True
|
||||
# self.local_check(tx)
|
||||
|
||||
debug.info(2, "Checking 5x min size inverter")
|
||||
OPTS.check_lvsdrc = False
|
||||
tx = pinv.pinv(nmos_width=5 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
|
||||
OPTS.check_lvsdrc = True
|
||||
debug.info(2, "Checking min size inverter")
|
||||
tx = pinv.pinv(nmos_width=tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Checking 2x min size inverter")
|
||||
tx = pinv.pinv(nmos_width=2 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
|
||||
self.local_check(tx)
|
||||
|
||||
debug.info(2, "Checking 5x min size inverter")
|
||||
tx = pinv.pinv(nmos_width=5 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
|
||||
self.local_check(tx)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
|
|
@ -57,6 +53,10 @@ class pinv_test(unittest.TestCase):
|
|||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env python2.7
|
||||
"""
|
||||
Run a regresion test on a precharge cell
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from testutils import header
|
||||
import sys,os
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
import debug
|
||||
import calibre
|
||||
import sys
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
class precharge_test(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import precharge
|
||||
import tech
|
||||
|
||||
debug.info(2, "Checking precharge")
|
||||
tx = precharge.precharge(name="precharge_driver", ptx_width=tech.drc["minwidth_tx"])
|
||||
self.local_check(tx)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
||||
tx.sp_write(tempspice)
|
||||
tx.gds_write(tempgds)
|
||||
|
||||
self.assertFalse(calibre.run_drc(tx.name, tempgds))
|
||||
self.assertFalse(calibre.run_lvs(tx.name, tempgds, tempspice))
|
||||
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main()
|
||||
|
|
@ -28,12 +28,12 @@ class wordline_driver_test(unittest.TestCase):
|
|||
import tech
|
||||
|
||||
debug.info(2, "Checking driver")
|
||||
tx = wordline_driver.wordline_driver(name="Wordline_driver", rows=8)
|
||||
tx = wordline_driver.wordline_driver(rows=8)
|
||||
self.local_check(tx)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
self.local_check(tx)
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, tx):
|
||||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
tempgds = OPTS.openram_temp + "temp.gds"
|
||||
|
|
|
|||
|
|
@ -25,12 +25,11 @@ class array_test(unittest.TestCase):
|
|||
|
||||
import bitcell_array
|
||||
|
||||
debug.info(2, "Testing 3x3 array for 6t_cell")
|
||||
a = bitcell_array.bitcell_array(name="bitcell_array", cols=3, rows=3)
|
||||
debug.info(2, "Testing 4x4 array for 6t_cell")
|
||||
a = bitcell_array.bitcell_array(name="bitcell_array", cols=4, rows=4)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
self.local_check(a)
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
|
|||
|
|
@ -18,50 +18,32 @@ class hierarchical_decoder_test(unittest.TestCase):
|
|||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import hierarchical_decoder
|
||||
import tech
|
||||
|
||||
debug.info(1, "Testing 4 row sample for hierarchical_decoder")
|
||||
OPTS.check_lvsdrc = False
|
||||
a = hierarchical_decoder.hierarchical_decoder(nand2_nmos_width=2 * tech.drc["minwidth_tx"],
|
||||
nand3_nmos_width=3 * tech.drc["minwidth_tx"],
|
||||
rows=4)
|
||||
OPTS.check_lvsdrc = True
|
||||
a = hierarchical_decoder.hierarchical_decoder(rows=4)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 8 row sample for hierarchical_decoder")
|
||||
OPTS.check_lvsdrc = False
|
||||
a = hierarchical_decoder.hierarchical_decoder(nand2_nmos_width=2 * tech.drc["minwidth_tx"],
|
||||
nand3_nmos_width=3 * tech.drc["minwidth_tx"],
|
||||
rows=8)
|
||||
OPTS.check_lvsdrc = True
|
||||
a = hierarchical_decoder.hierarchical_decoder(rows=8)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 32 row sample for hierarchical_decoder")
|
||||
OPTS.check_lvsdrc = False
|
||||
a = hierarchical_decoder.hierarchical_decoder(nand2_nmos_width=2 * tech.drc["minwidth_tx"],
|
||||
nand3_nmos_width=3 * tech.drc["minwidth_tx"],
|
||||
rows=32)
|
||||
OPTS.check_lvsdrc = True
|
||||
a = hierarchical_decoder.hierarchical_decoder(rows=32)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 128 row sample for hierarchical_decoder")
|
||||
OPTS.check_lvsdrc = False
|
||||
a = hierarchical_decoder.hierarchical_decoder(nand2_nmos_width=2 * tech.drc["minwidth_tx"],
|
||||
nand3_nmos_width=3 * tech.drc["minwidth_tx"],
|
||||
rows=128)
|
||||
OPTS.check_lvsdrc = True
|
||||
a = hierarchical_decoder.hierarchical_decoder(rows=128)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 512 row sample for hierarchical_decoder")
|
||||
OPTS.check_lvsdrc = False
|
||||
a = hierarchical_decoder.hierarchical_decoder(nand2_nmos_width=2 * tech.drc["minwidth_tx"],
|
||||
nand3_nmos_width=3 * tech.drc["minwidth_tx"],
|
||||
rows=512)
|
||||
OPTS.check_lvsdrc = True
|
||||
a = hierarchical_decoder.hierarchical_decoder(rows=512)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
@ -77,6 +59,11 @@ class hierarchical_decoder_test(unittest.TestCase):
|
|||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -24,12 +24,11 @@ class hierarchical_predecode2x4_test(unittest.TestCase):
|
|||
import hierarchical_predecode2x4 as pre
|
||||
import tech
|
||||
|
||||
debug.info(1, "Testing sample for hierarchy_decoder")
|
||||
a = pre.hierarchical_predecode2x4(nmos_width=2 * tech.drc["minwidth_tx"],
|
||||
cellname="test_pre2x4")
|
||||
OPTS.check_lvsdrc = True
|
||||
debug.info(1, "Testing sample for hierarchy_predecode2x4")
|
||||
a = pre.hierarchical_predecode2x4()
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
|
|||
|
|
@ -24,12 +24,11 @@ class hierarchical_predecode3x8_test(unittest.TestCase):
|
|||
import hierarchical_predecode3x8 as pre
|
||||
import tech
|
||||
|
||||
debug.info(1, "Testing sample for hierarchy_decoder")
|
||||
a = pre.hierarchical_predecode3x8(nmos_width=3 * tech.drc["minwidth_tx"],
|
||||
cellname="test_pre3x8")
|
||||
OPTS.check_lvsdrc = True
|
||||
debug.info(1, "Testing sample for hierarchy_predecode3x8")
|
||||
a = pre.hierarchical_predecode3x8()
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
|
|||
|
|
@ -22,12 +22,20 @@ class single_level_column_mux_test(unittest.TestCase):
|
|||
OPTS.check_lvsdrc = False
|
||||
|
||||
import single_level_column_mux_array
|
||||
|
||||
debug.info(1, "Testing sample for columnmux_array")
|
||||
a = single_level_column_mux_array.single_level_column_mux_array(
|
||||
rows=32, columns=32, word_size=16)
|
||||
OPTS.check_lvsdrc = True
|
||||
|
||||
debug.info(1, "Testing sample for 2-way column_mux_array")
|
||||
a = single_level_column_mux_array.single_level_column_mux_array(columns=16, word_size=8)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing sample for 4-way column_mux_array")
|
||||
a = single_level_column_mux_array.single_level_column_mux_array(columns=16, word_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing sample for 8-way column_mux_array")
|
||||
a = single_level_column_mux_array.single_level_column_mux_array(columns=32, word_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
@ -43,7 +51,9 @@ class single_level_column_mux_test(unittest.TestCase):
|
|||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
@ -20,17 +20,16 @@ class precharge_test(unittest.TestCase):
|
|||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import precharge_array
|
||||
import tech
|
||||
|
||||
debug.info(2, "Checking 3 column precharge")
|
||||
OPTS.check_lvsdrc = False
|
||||
pc = precharge_array.precharge_array(
|
||||
name="precharge_array", columns=3, ptx_width=tech.drc["minwidth_tx"], beta=2)
|
||||
OPTS.check_lvsdrc = True
|
||||
pc = precharge_array.precharge_array(columns=3, ptx_width=tech.drc["minwidth_tx"], beta=2)
|
||||
self.local_check(pc)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, pc):
|
||||
|
|
|
|||
|
|
@ -20,16 +20,20 @@ class sense_amp_test(unittest.TestCase):
|
|||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import sense_amp_array
|
||||
|
||||
|
||||
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2")
|
||||
OPTS.check_lvsdrc = False
|
||||
a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2)
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4")
|
||||
a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
@ -45,6 +49,11 @@ class sense_amp_test(unittest.TestCase):
|
|||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -20,15 +20,19 @@ class write_driver_test(unittest.TestCase):
|
|||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import write_driver_array
|
||||
|
||||
debug.info(2, "Testing write_driver_array for columns=128, word_size=128")
|
||||
OPTS.check_lvsdrc = False
|
||||
a = write_driver_array.write_driver_array(columns=16, word_size=16)
|
||||
OPTS.check_lvsdrc = True
|
||||
debug.info(2, "Testing write_driver_array for columns=8, word_size=8")
|
||||
a = write_driver_array.write_driver_array(columns=8, word_size=8)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing write_driver_array for columns=16, word_size=8")
|
||||
a = write_driver_array.write_driver_array(columns=16, word_size=8)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
@ -44,6 +48,11 @@ class write_driver_test(unittest.TestCase):
|
|||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
# instantiate a copy of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
|
|||
|
|
@ -21,16 +21,19 @@ class dff_array_test(unittest.TestCase):
|
|||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import ms_flop_array
|
||||
|
||||
debug.info(1, "Testing sample for dff_array")
|
||||
OPTS.check_lvsdrc = False
|
||||
a = ms_flop_array.ms_flop_array(
|
||||
name="test1", columns=64, word_size=32)
|
||||
OPTS.check_lvsdrc = True
|
||||
debug.info(2, "Testing ms_flop_array for columns=8, word_size=8")
|
||||
a = ms_flop_array.ms_flop_array(columns=8, word_size=8)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing ms_flop_array for columns=16, word_size=8")
|
||||
a = ms_flop_array.ms_flop_array(columns=16, word_size=8)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
@ -46,6 +49,11 @@ class dff_array_test(unittest.TestCase):
|
|||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -23,11 +23,15 @@ class tri_gate_array_test(unittest.TestCase):
|
|||
|
||||
import tri_gate_array
|
||||
|
||||
debug.info(1, "Testing sample for tri_gate_array")
|
||||
a = tri_gate_array.tri_gate_array(columns=16, word_size=16)
|
||||
OPTS.check_lvsdrc = True
|
||||
debug.info(1, "Testing tri_gate_array for columns=8, word_size=8")
|
||||
a = tri_gate_array.tri_gate_array(columns=8, word_size=8)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing tri_gate_array for columns=16, word_size=8")
|
||||
a = tri_gate_array.tri_gate_array(columns=16, word_size=8)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
@ -43,6 +47,11 @@ class tri_gate_array_test(unittest.TestCase):
|
|||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
# reset the static duplicate name checker for unit tests
|
||||
import design
|
||||
design.design.name_map=[]
|
||||
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
|
|
@ -27,10 +27,10 @@ class replica_bitline_test(unittest.TestCase):
|
|||
import replica_bitline
|
||||
|
||||
debug.info(2, "Testing RBL")
|
||||
a = replica_bitline.replica_bitline("chain", 13)
|
||||
OPTS.check_lvsdrc = True
|
||||
a = replica_bitline.replica_bitline(13)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
@ -16,21 +16,20 @@ OPTS = globals.get_opts()
|
|||
#@unittest.skip("SKIPPING 14_delay_chain_test")
|
||||
|
||||
|
||||
class logic_effort_dc_test(unittest.TestCase):
|
||||
class delay_chain_test(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||
# we will manually run lvs/drc
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
import logic_effort_dc
|
||||
import delay_chain
|
||||
|
||||
debug.info(2, "Testing delay_chain")
|
||||
a = logic_effort_dc.logic_effort_dc(
|
||||
"chain", stage_list=[4, 4, 4, 4, 4])
|
||||
OPTS.check_lvsdrc = True
|
||||
a = delay_chain.delay_chain(fanout_list=[4, 4, 4, 4])
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
@ -26,9 +26,9 @@ class control_logic_test(unittest.TestCase):
|
|||
|
||||
debug.info(1, "Testing sample for control_logic")
|
||||
a = control_logic.control_logic(num_rows=128)
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
@ -25,16 +25,23 @@ class bank_test(unittest.TestCase):
|
|||
|
||||
import bank
|
||||
|
||||
# override these from the config file
|
||||
OPTS.word_size=8
|
||||
OPTS.num_words=128
|
||||
OPTS.num_banks=1
|
||||
|
||||
debug.info(1, "Testing sample 8bit, 64word BANK")
|
||||
a = bank.bank(word_size=OPTS.num_words, num_words=OPTS.num_words, words_per_row=2, num_banks=OPTS.num_banks, name="test_sram1")
|
||||
OPTS.check_lvsdrc = True
|
||||
debug.info(1, "No column mux")
|
||||
a = bank.bank(word_size=4, num_words=64, words_per_row=2, num_banks=1, name="test_sram1")
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Two way column mux")
|
||||
a = bank.bank(word_size=4, num_words=64, words_per_row=2, num_banks=1, name="test_sram2")
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Four way column mux")
|
||||
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="test_sram3")
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Eight way column mux")
|
||||
a = bank.bank(word_size=2, num_words=64, words_per_row=8, num_banks=1, name="test_sram4")
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ class sram_1bank_test(unittest.TestCase):
|
|||
|
||||
debug.info(1, "Testing sample 8bit, 64word SRAM, 1 bank")
|
||||
a = sram.sram(word_size=8, num_words=128, num_banks=1, name="test_sram1")
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ class sram_2bank_test(unittest.TestCase):
|
|||
|
||||
debug.info(1, "Testing sample 8bit, 128word SRAM, 2 banks")
|
||||
a = sram.sram(word_size=8, num_words=128, num_banks=2, name="test_sram1")
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ class sram_4bank_test(unittest.TestCase):
|
|||
|
||||
debug.info(1, "Testing sample 8bit, 128word SRAM, 4 banks")
|
||||
a = sram.sram(word_size=8, num_words=128, num_banks=4, name="test_sram1")
|
||||
OPTS.check_lvsdrc = True
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
def local_check(self, a):
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ write_driver_array = "write_driver_array"
|
|||
tri_gate = "tri_gate"
|
||||
tri_gate_array = "tri_gate_array"
|
||||
wordline_driver = "wordline_driver"
|
||||
replica_bitline = "replica_bitline"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
bitcell = "bitcell"
|
||||
delay_chain = "logic_effort_dc"
|
||||
delay_chain = "delay_chain"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ write_driver_array = "write_driver_array"
|
|||
tri_gate = "tri_gate"
|
||||
tri_gate_array = "tri_gate_array"
|
||||
wordline_driver = "wordline_driver"
|
||||
replica_bitline = "replica_bitline"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
bitcell = "bitcell"
|
||||
delay_chain = "logic_effort_dc"
|
||||
delay_chain = "delay_chain"
|
||||
|
|
|
|||
|
|
@ -10,16 +10,22 @@ class tri_gate(design.design):
|
|||
netlist should be available in the technology library.
|
||||
"""
|
||||
|
||||
pins = ["in", "en", "en_bar", "out", "gnd", "vdd"]
|
||||
chars = utils.auto_measure_libcell(pins, "tri_gate", GDS["unit"], layer["boundary"])
|
||||
pin_names = ["in", "en", "en_bar", "out", "gnd", "vdd"]
|
||||
(width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"], layer["boundary"])
|
||||
|
||||
def __init__(self, name):
|
||||
unique_id = 1
|
||||
|
||||
def __init__(self, name=""):
|
||||
if name=="":
|
||||
name = "tri{0}".format(tri_gate.unique_id)
|
||||
tri_gate.unique_id += 1
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "Create tri_gate object")
|
||||
|
||||
|
||||
self.width = tri_gate.chars["width"]
|
||||
self.height = tri_gate.chars["height"]
|
||||
self.width = tri_gate.width
|
||||
self.height = tri_gate.height
|
||||
self.pin_map = tri_gate.pin_map
|
||||
|
||||
def delay(self, slew, load=0.0):
|
||||
from tech import spice
|
||||
|
|
|
|||
|
|
@ -16,119 +16,113 @@ class tri_gate_array(design.design):
|
|||
|
||||
c = reload(__import__(OPTS.config.tri_gate))
|
||||
self.mod_tri_gate = getattr(c, OPTS.config.tri_gate)
|
||||
self.tri_gate_chars = self.mod_tri_gate.chars
|
||||
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 = self.columns / self.word_size
|
||||
self.width = (self.columns / self.words_per_row) * self.tri.width
|
||||
self.height = self.tri.height
|
||||
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_layout(self):
|
||||
"""generate layout """
|
||||
self.add_modules()
|
||||
self.setup_layout_constants()
|
||||
self.add_pins()
|
||||
self.create_write_array()
|
||||
self.add_metal_rails()
|
||||
self.add_labels()
|
||||
self.create_array()
|
||||
self.add_layout_pins()
|
||||
|
||||
def add_pins(self):
|
||||
"""create the name of pins depend on the word size"""
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("IN[{0}]".format(i))
|
||||
self.add_pin("in[{0}]".format(i))
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("OUT[{0}]".format(i))
|
||||
self.add_pin("out[{0}]".format(i))
|
||||
for pin in ["en", "en_bar", "vdd", "gnd"]:
|
||||
self.add_pin(pin)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
"""caculate the size of tri gate array"""
|
||||
self.words_per_row = self.columns / self.word_size
|
||||
self.width = (self.columns / self.words_per_row) * self.tri.width
|
||||
self.height = self.tri.height
|
||||
self.tri_gate_positions = []
|
||||
self.vdd_positions = []
|
||||
self.gnd_positions = []
|
||||
self.in_positions = []
|
||||
self.out_positions = []
|
||||
|
||||
def add_modules(self):
|
||||
"""instantiation of a tri gate"""
|
||||
self.tri = self.mod_tri_gate("tri_gate")
|
||||
self.add_mod(self.tri)
|
||||
|
||||
def create_write_array(self):
|
||||
def create_array(self):
|
||||
"""add tri gate to the array """
|
||||
for i in range(self.word_size):
|
||||
mirror = "R0"
|
||||
if (i % 2 == 0):
|
||||
name = "Xtri_gate{0}".format(i)
|
||||
x_off = i * self.tri.width * self.words_per_row
|
||||
self.tri_inst = {}
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xtri_gate{0}".format(i)
|
||||
if (i % 2 == 0 or self.words_per_row > 1):
|
||||
base = vector(i*self.tri.width, 0)
|
||||
mirror = "R0"
|
||||
else:
|
||||
name = "Xtri_gate{0}".format(i)
|
||||
if (self.words_per_row == 1):
|
||||
x_off = (i + 1) * self.tri.width * self.words_per_row
|
||||
mirror = "MY"
|
||||
else:
|
||||
x_off = i * self.tri.width * self.words_per_row
|
||||
self.add_inst(name=name,
|
||||
mod=self.tri,
|
||||
offset=[x_off, 0],
|
||||
mirror = mirror)
|
||||
self.connect_inst(["in[{0}]".format(i),
|
||||
"out[{0}]".format(i),
|
||||
base = vector((i+1)*self.tri.width, 0)
|
||||
mirror = "MY"
|
||||
self.tri_inst[i]=self.add_inst(name=name,
|
||||
mod=self.tri,
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["in[{0}]".format(i/self.words_per_row),
|
||||
"out[{0}]".format(i/self.words_per_row),
|
||||
"en", "en_bar", "vdd", "gnd"])
|
||||
|
||||
def add_metal_rails(self):
|
||||
"""Connect en en_bar and vdd together """
|
||||
correct = vector(0, 0.5 * drc["minwidth_metal1"])
|
||||
width = (self.tri.width * self.columns
|
||||
- (self.words_per_row - 1) * self.tri.width)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=(self.tri_gate_chars["en"] - correct).scale(0, 1),
|
||||
width=width,
|
||||
height=drc['minwidth_metal1'])
|
||||
self.add_rect(layer="metal1",
|
||||
offset=(self.tri_gate_chars["en_bar"] - correct).scale(0, 1),
|
||||
width=width,
|
||||
height=drc['minwidth_metal1'])
|
||||
self.add_rect(layer="metal1",
|
||||
offset=(self.tri_gate_chars["vdd"] - correct).scale(0, 1),
|
||||
width=width,
|
||||
height=drc['minwidth_metal1'])
|
||||
|
||||
def add_labels(self):
|
||||
"""add label for pins"""
|
||||
for i in range(self.word_size):
|
||||
if (i % 2 == 0 or self.words_per_row > 1):
|
||||
x_off = i * self.tri.width * self.words_per_row
|
||||
dir_vector = vector(1,1)
|
||||
else:
|
||||
x_off = (i + 1) * self.tri.width * self.words_per_row
|
||||
dir_vector = vector(-1,1)
|
||||
def add_layout_pins(self):
|
||||
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
|
||||
pin_offset={}
|
||||
for pin in ["en", "en_bar", "vdd", "gnd", "in", "out"]:
|
||||
pin_offset[pin] = vector(x_off, 0) + dir_vector.scale(self.tri_gate_chars[pin])
|
||||
# Avoid duplicate pins by only doing even columns
|
||||
for gnd_pin in self.tri_inst[i].get_pins("gnd"):
|
||||
if i%2 == 0 and gnd_pin.layer=="metal2":
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal2",
|
||||
offset=gnd_pin.ll(),
|
||||
width=gnd_pin.width(),
|
||||
height=gnd_pin.height())
|
||||
|
||||
for pin in ["en", "en_bar", "vdd"]:
|
||||
self.add_label(text=pin,
|
||||
layer="metal1",
|
||||
offset=pin_offset[pin])
|
||||
self.add_label(text="gnd",
|
||||
layer="metal2",
|
||||
offset=pin_offset["gnd"])
|
||||
self.add_label(text="in[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=pin_offset["in"])
|
||||
self.add_label(text="out[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=pin_offset["out"])
|
||||
in_pin = self.tri_inst[i].get_pin("in")
|
||||
self.add_layout_pin(text="in[{0}]".format(i/self.words_per_row),
|
||||
layer="metal2",
|
||||
offset=in_pin.ll(),
|
||||
width=in_pin.width(),
|
||||
height=in_pin.height())
|
||||
|
||||
out_pin = self.tri_inst[i].get_pin("out")
|
||||
self.add_layout_pin(text="out[{0}]".format(i/self.words_per_row),
|
||||
layer="metal2",
|
||||
offset=out_pin.ll(),
|
||||
width=out_pin.width(),
|
||||
height=out_pin.height())
|
||||
|
||||
|
||||
|
||||
width = self.tri.width * self.columns - (self.words_per_row - 1) * self.tri.width
|
||||
en_pin = self.tri_inst[0].get_pin("en")
|
||||
self.add_layout_pin(text="en",
|
||||
layer="metal1",
|
||||
offset=en_pin.ll().scale(0, 1),
|
||||
width=width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
enbar_pin = self.tri_inst[0].get_pin("en_bar")
|
||||
self.add_layout_pin(text="en_bar",
|
||||
layer="metal1",
|
||||
offset=enbar_pin.ll().scale(0, 1),
|
||||
width=width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
vdd_pin = self.tri_inst[0].get_pin("vdd")
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_pin.ll().scale(0, 1),
|
||||
width=width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
for gnd_pin in self.tri_inst[0].get_pins("gnd"):
|
||||
if gnd_pin.layer=="metal1":
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_pin.ll().scale(0, 1),
|
||||
width=width,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
self.vdd_positions.append(pin_offset["vdd"])
|
||||
self.gnd_positions.append(pin_offset["gnd"])
|
||||
self.in_positions.append(pin_offset["in"])
|
||||
self.out_positions.append(pin_offset["out"])
|
||||
|
||||
def delay(self, slew, load=0.0):
|
||||
result = self.tri.delay(slew = slew, load = load)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import os
|
|||
import gdsMill
|
||||
import tech
|
||||
import globals
|
||||
from vector import vector
|
||||
from pin_layout import pin_layout
|
||||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
|
|
@ -20,14 +22,18 @@ def snap_to_grid(offset):
|
|||
out_offset = [xoff, yoff]
|
||||
return out_offset
|
||||
|
||||
|
||||
def gds_pin_center(gdsPin):
|
||||
def pin_center(boundary):
|
||||
"""
|
||||
This returns the center of a pin shape
|
||||
This returns the center of a pin shape in the vlsiLayout border format.
|
||||
"""
|
||||
boundary = gdsPin[2]
|
||||
return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])]
|
||||
|
||||
def pin_rect(boundary):
|
||||
"""
|
||||
This returns a LL,UR point pair.
|
||||
"""
|
||||
return [vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])]
|
||||
|
||||
def auto_measure_libcell(pin_list, name, units, layer):
|
||||
"""
|
||||
Open a GDS file and find the pins in pin_list as text on a given layer.
|
||||
|
|
@ -45,7 +51,49 @@ def auto_measure_libcell(pin_list, name, units, layer):
|
|||
[cell["width"], cell["height"]] = measure_result
|
||||
|
||||
for pin in pin_list:
|
||||
cell[str(pin)] = gds_pin_center(cell_vlsi.getPinShapeByLabel(str(pin)))
|
||||
(name,layer,boundary)=cell_vlsi.getPinShapeByLabel(str(pin))
|
||||
cell[str(pin)] = pin_center(boundary)
|
||||
return cell
|
||||
|
||||
|
||||
|
||||
def get_libcell_size(name, units, layer):
|
||||
"""
|
||||
Open a GDS file and return the library cell size from either the
|
||||
bounding box or a border layer.
|
||||
"""
|
||||
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
|
||||
cell_vlsi = gdsMill.VlsiLayout(units=units)
|
||||
reader = gdsMill.Gds2reader(cell_vlsi)
|
||||
reader.loadFromFile(cell_gds)
|
||||
|
||||
cell = {}
|
||||
measure_result = cell_vlsi.getLayoutBorder(layer)
|
||||
if measure_result == None:
|
||||
measure_result = cell_vlsi.measureSize(name)
|
||||
# returns width,height
|
||||
return measure_result
|
||||
|
||||
|
||||
def get_libcell_pins(pin_list, name, units, layer):
|
||||
"""
|
||||
Open a GDS file and find the pins in pin_list as text on a given layer.
|
||||
Return these as a rectangle layer pair for each pin.
|
||||
"""
|
||||
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
|
||||
cell_vlsi = gdsMill.VlsiLayout(units=units)
|
||||
reader = gdsMill.Gds2reader(cell_vlsi)
|
||||
reader.loadFromFile(cell_gds)
|
||||
|
||||
cell = {}
|
||||
for pin in pin_list:
|
||||
cell[str(pin)]=[]
|
||||
label_list=cell_vlsi.getPinShapeByLabel(str(pin))
|
||||
for label in label_list:
|
||||
(name,layer,boundary)=label
|
||||
rect = pin_rect(boundary)
|
||||
# this is a list because other cells/designs may have must-connect pins
|
||||
cell[str(pin)].append(pin_layout(pin, rect, layer))
|
||||
return cell
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -41,10 +41,8 @@ class wire(path):
|
|||
self.horiz_layer_width = drc["minwidth_{0}".format(horiz_layer)]
|
||||
via_connect = contact(self.layer_stack,
|
||||
(1, 1))
|
||||
self.node_to_node = [drc["minwidth_" + str(self.horiz_layer_name)] \
|
||||
+ via_connect.width,
|
||||
drc["minwidth_" + str(self.horiz_layer_name)] \
|
||||
+ via_connect.height]
|
||||
self.node_to_node = [drc["minwidth_" + str(self.horiz_layer_name)] + via_connect.width,
|
||||
drc["minwidth_" + str(self.horiz_layer_name)] + via_connect.height]
|
||||
|
||||
# create a 1x1 contact
|
||||
def create_vias(self):
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ class wordline_driver(design.design):
|
|||
Generates the wordline-driver to drive the bitcell
|
||||
"""
|
||||
|
||||
def __init__(self, name, rows):
|
||||
design.design.__init__(self, name)
|
||||
def __init__(self, rows):
|
||||
design.design.__init__(self, "wordline_driver")
|
||||
|
||||
self.rows = rows
|
||||
self.add_pins()
|
||||
|
|
@ -26,11 +26,11 @@ class wordline_driver(design.design):
|
|||
def add_pins(self):
|
||||
# inputs to wordline_driver.
|
||||
for i in range(self.rows):
|
||||
self.add_pin("decode_out[{0}]".format(i))
|
||||
self.add_pin("in[{0}]".format(i))
|
||||
# Outputs from wordline_driver.
|
||||
for i in range(self.rows):
|
||||
self.add_pin("wl[{0}]".format(i))
|
||||
self.add_pin("clk")
|
||||
self.add_pin("en")
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
|
|
@ -40,12 +40,11 @@ class wordline_driver(design.design):
|
|||
self.create_layout()
|
||||
|
||||
def add_layout(self):
|
||||
self.inv = pinv(nmos_width=drc["minwidth_tx"],
|
||||
beta=parameter["pinv_beta"])
|
||||
self.inv = pinv()
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.NAND2 = nand_2(nmos_width=2*drc["minwidth_tx"])
|
||||
self.add_mod(self.NAND2)
|
||||
self.nand2 = nand_2()
|
||||
self.add_mod(self.nand2)
|
||||
|
||||
|
||||
|
||||
|
|
@ -53,87 +52,80 @@ class wordline_driver(design.design):
|
|||
def offsets_of_gates(self):
|
||||
self.x_offset0 = 2 * drc["minwidth_metal1"] + 5 * drc["metal1_to_metal1"]
|
||||
self.x_offset1 = self.x_offset0 + self.inv.width
|
||||
self.x_offset2 = self.x_offset1 + self.NAND2.width
|
||||
self.x_offset2 = self.x_offset1 + self.nand2.width
|
||||
|
||||
self.width = self.x_offset2 + self.inv.width
|
||||
self.height = self.inv.height * self.rows
|
||||
|
||||
# Defining offset postions
|
||||
self.decode_out_positions = []
|
||||
self.clk_positions = []
|
||||
self.WL_positions = []
|
||||
self.vdd_positions = []
|
||||
self.gnd_positions = []
|
||||
|
||||
def create_layout(self):
|
||||
# Clk connection
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"],
|
||||
2 * drc["minwidth_metal1"]],
|
||||
width=drc["minwidth_metal1"],
|
||||
height=self.height + 4*drc["minwidth_metal1"])
|
||||
self.clk_positions.append([drc["minwidth_metal1"] + 2*drc["metal1_to_metal1"],
|
||||
self.height])
|
||||
self.add_label(text="clk",
|
||||
layer="metal1",
|
||||
offset=self.clk_positions[0])
|
||||
|
||||
# Wordline enable connection
|
||||
self.add_layout_pin(text="en",
|
||||
layer="metal2",
|
||||
offset=[drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"],0],
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.height)
|
||||
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=[0, -0.5*drc["minwidth_metal1"]],
|
||||
width=self.x_offset0,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
for row in range(self.rows):
|
||||
name_inv1 = "Wordline_driver_inv_clk%d" % (row)
|
||||
name_nand = "Wordline_driver_nand%d" % (row)
|
||||
name_inv2 = "Wordline_driver_inv%d" % (row)
|
||||
name_inv1 = "wl_driver_inv_en{}".format(row)
|
||||
name_nand = "wl_driver_nand{}".format(row)
|
||||
name_inv2 = "wl_driver_inv{}".format(row)
|
||||
|
||||
# Extend vdd and gnd of Wordline_driver
|
||||
yoffset = (row + 1) * self.inv.height - 0.5 * drc["minwidth_metal2"]
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[0, yoffset],
|
||||
width=self.x_offset0,
|
||||
height=drc["minwidth_metal2"])
|
||||
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[drc["minwidth_metal1"], yoffset],
|
||||
mirror="R90")
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[self.x_offset0 + drc["minwidth_metal1"],
|
||||
yoffset],
|
||||
mirror="R90")
|
||||
inv_nand2B_connection_height = (abs(self.inv.Z_position.y
|
||||
- self.NAND2.B_position.y)
|
||||
+ drc["minwidth_metal1"])
|
||||
inv_nand2B_connection_height = (abs(self.inv.get_pin("Z").ll().y
|
||||
- self.nand2.get_pin("B").ll().y)
|
||||
+ drc["minwidth_metal1"])
|
||||
|
||||
if (row % 2):
|
||||
y_offset = self.inv.height*(row + 1)
|
||||
name_inv1_offset = [self.x_offset0, y_offset]
|
||||
nand2_offset=[self.x_offset1, y_offset]
|
||||
inv2_offset=[self.x_offset2, y_offset]
|
||||
inst_mirror = "MX"
|
||||
cell_dir = vector(0,-1)
|
||||
m1tm2_rotate=270
|
||||
m1tm2_mirror="R0"
|
||||
else:
|
||||
y_offset = self.inv.height*row
|
||||
name_inv1_offset = [self.x_offset0, y_offset]
|
||||
nand2_offset=[self.x_offset1, y_offset]
|
||||
inv2_offset=[self.x_offset2, y_offset]
|
||||
inst_mirror = "R0"
|
||||
cell_dir = vector(0,1)
|
||||
m1tm2_rotate=90
|
||||
m1tm2_mirror="MX"
|
||||
|
||||
name_inv1_offset = [self.x_offset0, y_offset]
|
||||
nand2_offset=[self.x_offset1, y_offset]
|
||||
inv2_offset=[self.x_offset2, y_offset]
|
||||
base_offset = vector(self.width, y_offset)
|
||||
|
||||
# Extend vdd and gnd of wordline_driver
|
||||
yoffset = (row + 1) * self.inv.height - 0.5 * drc["minwidth_metal1"]
|
||||
if (row % 2):
|
||||
pin_name = "gnd"
|
||||
else:
|
||||
pin_name = "vdd"
|
||||
|
||||
self.add_layout_pin(text=pin_name,
|
||||
layer="metal1",
|
||||
offset=[0, yoffset],
|
||||
width=self.x_offset0,
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
|
||||
# add inv1 based on the info above
|
||||
self.add_inst(name=name_inv1,
|
||||
mod=self.inv,
|
||||
offset=name_inv1_offset,
|
||||
mirror=inst_mirror )
|
||||
self.connect_inst(["clk", "clk_bar[{0}]".format(row),
|
||||
self.connect_inst(["en", "en_bar[{0}]".format(row),
|
||||
"vdd", "gnd"])
|
||||
# add nand 2
|
||||
self.add_inst(name=name_nand,
|
||||
mod=self.NAND2,
|
||||
mod=self.nand2,
|
||||
offset=nand2_offset,
|
||||
mirror=inst_mirror)
|
||||
self.connect_inst(["decode_out[{0}]".format(row),
|
||||
"clk_bar[{0}]".format(row),
|
||||
self.connect_inst(["in[{0}]".format(row),
|
||||
"en_bar[{0}]".format(row),
|
||||
"net[{0}]".format(row),
|
||||
"vdd", "gnd"])
|
||||
# add inv2
|
||||
|
|
@ -146,73 +138,65 @@ class wordline_driver(design.design):
|
|||
"vdd", "gnd"])
|
||||
|
||||
# clk connection
|
||||
clk_offset= [drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"],
|
||||
y_offset + cell_dir.y * self.inv.A_position.y]
|
||||
clk_offset= vector(drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"],
|
||||
y_offset + cell_dir.y * self.inv.get_pin("A").by())
|
||||
self.add_rect(layer="metal1",
|
||||
offset=clk_offset,
|
||||
width=self.x_offset0 - 2*drc["metal1_to_metal1"],
|
||||
height=cell_dir.y *drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=clk_offset)
|
||||
|
||||
# first inv to nand2 B
|
||||
inv_to_nand2B_offset = [self.x_offset1 - drc["minwidth_metal1"],
|
||||
y_offset + cell_dir.y * self.NAND2.B_position.y]
|
||||
y_offset + cell_dir.y * self.nand2.get_pin("B").by()]
|
||||
self.add_rect(layer="metal1",
|
||||
offset=inv_to_nand2B_offset,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=cell_dir.y*inv_nand2B_connection_height)
|
||||
# Nand2 out to 2nd inv
|
||||
nand2_to_2ndinv_offset =[self.x_offset2,
|
||||
y_offset + cell_dir.y * self.NAND2.Z_position.y]
|
||||
y_offset + cell_dir.y * self.nand2.get_pin("Z").by()]
|
||||
self.add_rect(layer="metal1",
|
||||
offset=nand2_to_2ndinv_offset,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=cell_dir.y * drc["minwidth_metal1"])
|
||||
# nand2 A connection
|
||||
|
||||
# connect the decoder input pin to nand2 A
|
||||
input_offset=vector(0, y_offset + cell_dir.y*self.nand2.get_pin("A").by())
|
||||
mid_via_offset = vector(clk_offset.x,input_offset.y) + vector(drc["minwidth_metal2"]+drc["metal2_to_metal2"],0)
|
||||
# must under the clk line in M1
|
||||
self.add_layout_pin(text="in[{0}]".format(row),
|
||||
layer="metal1",
|
||||
offset=input_offset,
|
||||
width=mid_via_offset.x+drc["minwidth_metal1"],
|
||||
height=cell_dir.y*drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=mid_via_offset,
|
||||
mirror=inst_mirror)
|
||||
# now connect to the nand2 A
|
||||
self.add_rect(layer="metal2",
|
||||
offset=[0, y_offset + cell_dir.y * self.NAND2.A_position.y],
|
||||
width=self.x_offset1,
|
||||
offset=mid_via_offset,
|
||||
width=self.x_offset1-mid_via_offset.x,
|
||||
height=cell_dir.y*drc["minwidth_metal2"])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[self.x_offset1,
|
||||
y_offset + cell_dir.y * self.NAND2.A_position.y],
|
||||
y_offset + cell_dir.y * self.nand2.get_pin("A").by()],
|
||||
rotate=m1tm2_rotate,
|
||||
mirror=m1tm2_mirror)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[0,
|
||||
y_offset +cell_dir.y*self.NAND2.A_position.y],
|
||||
mirror=inst_mirror)
|
||||
|
||||
# output each WL on the right
|
||||
wl_offset = base_offset + self.inv.get_pin("Z").ll().scale(cell_dir)
|
||||
self.add_layout_pin(text="wl[{0}]".format(row),
|
||||
layer="metal1",
|
||||
offset=wl_offset,
|
||||
width=drc["minwidth_metal1"]*cell_dir.y,
|
||||
height=drc["minwidth_metal1"]*cell_dir.y)
|
||||
|
||||
base_offset = vector(self.width, y_offset)
|
||||
decode_out_offset = base_offset.scale(0,1)+self.NAND2.A_position.scale(cell_dir)
|
||||
wl_offset = base_offset + self.inv.Z_position.scale(cell_dir)
|
||||
vdd_offset = base_offset + self.inv.vdd_position.scale(cell_dir)
|
||||
gnd_offset = base_offset + self.inv.gnd_position.scale(cell_dir)
|
||||
|
||||
self.add_label(text="decode_out[{0}]".format(row),
|
||||
layer="metal2",
|
||||
offset=decode_out_offset)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=wl_offset,
|
||||
width=drc["minwidth_metal1"]*cell_dir.y,
|
||||
height=drc["minwidth_metal1"]*cell_dir.y)
|
||||
self.add_label(text="wl[{0}]".format(row),
|
||||
layer="metal1",
|
||||
offset=wl_offset)
|
||||
self.add_label(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_offset)
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_offset)
|
||||
|
||||
self.decode_out_positions.append(decode_out_offset)
|
||||
self.WL_positions.append(wl_offset)
|
||||
self.vdd_positions.append(vdd_offset)
|
||||
self.gnd_positions.append(gnd_offset)
|
||||
|
||||
def delay(self, slew, load=0):
|
||||
# decode_out -> net
|
||||
decode_t_net = self.NAND2.delay(slew, self.inv.input_load())
|
||||
# decode -> net
|
||||
decode_t_net = self.nand2.delay(slew, self.inv.input_load())
|
||||
|
||||
# net -> wl
|
||||
net_t_wl = self.inv.delay(decode_t_net.slew, load)
|
||||
|
|
@ -221,4 +205,4 @@ class wordline_driver(design.design):
|
|||
return result
|
||||
|
||||
def input_load(self):
|
||||
return self.NAND2.input_load()
|
||||
return self.nand2.input_load()
|
||||
|
|
|
|||
|
|
@ -11,13 +11,15 @@ class write_driver(design.design):
|
|||
the technology library.
|
||||
"""
|
||||
|
||||
pins = ["din", "BL", "BR", "en", "gnd", "vdd"]
|
||||
chars = utils.auto_measure_libcell(pins, "write_driver", GDS["unit"], layer["boundary"])
|
||||
pin_names = ["din", "BL", "BR", "en", "gnd", "vdd"]
|
||||
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"], layer["boundary"])
|
||||
|
||||
def __init__(self, name):
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "Create write_driver object")
|
||||
|
||||
self.width = write_driver.chars["width"]
|
||||
self.height = write_driver.chars["height"]
|
||||
self.width = write_driver.width
|
||||
self.height = write_driver.height
|
||||
self.pin_map = write_driver.pin_map
|
||||
|
||||
|
|
|
|||
|
|
@ -17,126 +17,106 @@ class write_driver_array(design.design):
|
|||
|
||||
c = reload(__import__(OPTS.config.write_driver))
|
||||
self.mod_write_driver = getattr(c, OPTS.config.write_driver)
|
||||
self.write_driver_chars = self.mod_write_driver.chars
|
||||
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 = columns / word_size
|
||||
|
||||
self.width = self.columns * self.driver.width
|
||||
self.height = self.height = self.driver.height
|
||||
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("data_in[{0}]".format(i))
|
||||
if (self.words_per_row == 1):
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("bl[{0}]".format(i))
|
||||
self.add_pin("br[{0}]".format(i))
|
||||
else:
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("bl_out[{0}]".format(i * self.words_per_row))
|
||||
self.add_pin("br_out[{0}]".format(i * self.words_per_row))
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
self.add_pin("data[{0}]".format(i/self.words_per_row))
|
||||
self.add_pin("bl[{0}]".format(i))
|
||||
self.add_pin("br[{0}]".format(i))
|
||||
self.add_pin("wen")
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_layout(self):
|
||||
self.add_write_driver_module()
|
||||
self.setup_layout_constants()
|
||||
self.create_write_array()
|
||||
self.add_metal_rails()
|
||||
self.add_labels()
|
||||
self.offset_all_coordinates()
|
||||
|
||||
def add_write_driver_module(self):
|
||||
self.driver = self.mod_write_driver("write_driver")
|
||||
self.add_mod(self.driver)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.width = self.columns * self.driver.width
|
||||
self.height = self.height = self.driver.height
|
||||
self.gnd_positions = []
|
||||
self.vdd_positions = []
|
||||
self.wen_positions = []
|
||||
self.BL_out_positions = []
|
||||
self.BR_out_positions = []
|
||||
self.driver_positions = []
|
||||
self.Data_in_positions = []
|
||||
self.add_layout_pins()
|
||||
#self.offset_all_coordinates()
|
||||
|
||||
def create_write_array(self):
|
||||
for i in range(self.word_size):
|
||||
name = "Xwrite_driver%d" % i
|
||||
x_off = (i* self.driver.width * self.words_per_row)
|
||||
self.driver_positions.append(vector(x_off, 0))
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
name = "Xwrite_driver{}".format(i)
|
||||
if (i % 2 == 0 or self.words_per_row>1):
|
||||
base = vector(i * self.driver.width,0)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector((i+1) * self.driver.width,0)
|
||||
mirror = "MY"
|
||||
|
||||
self.add_inst(name=name,
|
||||
mod=self.driver,
|
||||
offset=[x_off, 0])
|
||||
if (self.words_per_row == 1):
|
||||
self.connect_inst(["data_in[{0}]".format(i),
|
||||
"bl[{0}]".format(i),
|
||||
"br[{0}]".format(i),
|
||||
"wen", "vdd", "gnd"])
|
||||
offset=base,
|
||||
mirror=mirror)
|
||||
self.connect_inst(["data[{0}]".format(i/self.words_per_row),
|
||||
"bl[{0}]".format(i/self.words_per_row),
|
||||
"br[{0}]".format(i/self.words_per_row),
|
||||
"wen", "vdd", "gnd"])
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
bl_pin = self.driver.get_pin("BL")
|
||||
br_pin = self.driver.get_pin("BR")
|
||||
din_pin = self.driver.get_pin("din")
|
||||
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
if (i % 2 == 0 or self.words_per_row > 1):
|
||||
base = vector(i*self.driver.width, 0)
|
||||
x_dir = 1
|
||||
else:
|
||||
self.connect_inst(["data_in[{0}]".format(i),
|
||||
"bl_out[{0}]".format(i * self.words_per_row),
|
||||
"br_out[{0}]".format(i * self.words_per_row),
|
||||
"wen", "vdd", "gnd"])
|
||||
base = vector((i+1)*self.driver.width, 0)
|
||||
x_dir = -1
|
||||
|
||||
bl_offset = base + bl_pin.ll().scale(x_dir,1)
|
||||
br_offset = base + br_pin.ll().scale(x_dir,1)
|
||||
din_offset = base + din_pin.ll().scale(x_dir,1)
|
||||
|
||||
|
||||
def add_metal_rails(self):
|
||||
base = vector(0, - 0.5*drc["minwidth_metal1"])
|
||||
self.add_rect(layer="metal1",
|
||||
offset=base + vector(self.write_driver_chars["en"]).scale(0, 1),
|
||||
width=self.width - (self.words_per_row - 1) * self.driver.width,
|
||||
height=drc['minwidth_metal1'])
|
||||
self.add_rect(layer="metal1",
|
||||
offset=base + vector(self.write_driver_chars["vdd"]).scale(0, 1),
|
||||
width=self.width,
|
||||
height=drc['minwidth_metal1'])
|
||||
self.add_rect(layer="metal1",
|
||||
offset=base + vector(self.write_driver_chars["gnd"]).scale(0, 1),
|
||||
width=self.width,
|
||||
height=drc['minwidth_metal1'])
|
||||
self.add_layout_pin(text="data[{0}]".format(i/self.words_per_row),
|
||||
layer="metal2",
|
||||
offset=din_offset,
|
||||
width=x_dir*din_pin.width(),
|
||||
height=din_pin.height())
|
||||
self.add_layout_pin(text="bl[{0}]".format(i/self.words_per_row),
|
||||
layer="metal2",
|
||||
offset=bl_offset,
|
||||
width=x_dir*bl_pin.width(),
|
||||
height=bl_pin.height())
|
||||
|
||||
self.add_layout_pin(text="br[{0}]".format(i/self.words_per_row),
|
||||
layer="metal2",
|
||||
offset=br_offset,
|
||||
width=x_dir*br_pin.width(),
|
||||
height=br_pin.height())
|
||||
|
||||
|
||||
def add_labels(self):
|
||||
for i in range(self.word_size):
|
||||
base = vector(i * self.driver.width * self.words_per_row, 0)
|
||||
BL_offset = base + self.write_driver_chars["BL"]
|
||||
BR_offset = base + self.write_driver_chars["BR"]
|
||||
|
||||
self.add_label(text="data_in[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=base + self.write_driver_chars["din"])
|
||||
if (self.words_per_row == 1):
|
||||
self.add_label(text="bl[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=BL_offset)
|
||||
self.add_label(text="br[{0}]".format(i),
|
||||
layer="metal2",
|
||||
offset=BR_offset)
|
||||
else:
|
||||
self.add_label(text="bl_out[{0}]".format(i*self.words_per_row),
|
||||
layer="metal2",
|
||||
offset=BL_offset)
|
||||
self.add_label(text="br_out[{0}]".format(i*self.words_per_row),
|
||||
layer="metal2",
|
||||
offset=BR_offset)
|
||||
self.BL_out_positions.append(BL_offset)
|
||||
self.BR_out_positions.append(BR_offset)
|
||||
self.Data_in_positions.append(base + self.write_driver_chars["din"])
|
||||
|
||||
base = vector(0, - 0.5 * drc["minwidth_metal1"])
|
||||
self.add_label(text="wen",
|
||||
layer="metal1",
|
||||
offset=base + vector(self.write_driver_chars["en"]).scale(0,1))
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=base + vector(self.write_driver_chars["vdd"]).scale(0,1))
|
||||
self.add_label(text="gnd",
|
||||
layer="metal1",
|
||||
offset=base + vector(self.write_driver_chars["gnd"]).scale(0,1))
|
||||
self.wen_positions.append(base + vector(self.write_driver_chars["en"]).scale(0,1))
|
||||
self.vdd_positions.append(base + vector(self.write_driver_chars["vdd"]).scale(0,1))
|
||||
self.gnd_positions.append(base + vector(self.write_driver_chars["gnd"]).scale(0,1))
|
||||
self.add_layout_pin(text="wen",
|
||||
layer="metal1",
|
||||
offset=self.driver.get_pin("en").ll().scale(0,1),
|
||||
width=self.width - (self.words_per_row - 1) * self.driver.width,
|
||||
height=drc['minwidth_metal1'])
|
||||
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=self.driver.get_pin("vdd").ll().scale(0,1),
|
||||
width=self.width,
|
||||
height=drc['minwidth_metal1'])
|
||||
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=self.driver.get_pin("gnd").ll().scale(0,1),
|
||||
width=self.width,
|
||||
height=drc['minwidth_metal1'])
|
||||
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -85,7 +85,7 @@ drc["minwidth_tx"]=0.09
|
|||
drc["minlength_channel"] = 0.05
|
||||
|
||||
#well rules
|
||||
drc["pwell_enclose_nwell"] = 0.225
|
||||
drc["pwell_to_nwell"] = 0.225
|
||||
drc["minwidth_well"] = 0.2
|
||||
|
||||
#poly rules
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -70,7 +70,7 @@ drc["minwidth_tx"] = 1.2
|
|||
drc["minlength_channel"] = 0.6
|
||||
|
||||
#well rules
|
||||
drc["pwell_enclose_nwell"] = 0
|
||||
drc["pwell_to_nwell"] = 0
|
||||
drc["minwidth_well"] = 3.6
|
||||
|
||||
#poly rules
|
||||
|
|
|
|||
Loading…
Reference in New Issue