Development version of new pin data structure. Tests pass LVS/DRC except for bank level.

This commit is contained in:
Matt Guthaus 2017-08-23 15:02:15 -07:00
parent 7aa42f9a00
commit cf940fb15d
84 changed files with 4153 additions and 4590 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.DS_Store .DS_Store
*~ *~
*.pyc *.pyc
*.log

File diff suppressed because it is too large Load Diff

View File

@ -11,15 +11,17 @@ class bitcell(design.design):
library. library.
""" """
pins = ["BL", "BR", "WL", "vdd", "gnd"] pin_names = ["BL", "BR", "WL", "vdd", "gnd"]
chars = utils.auto_measure_libcell(pins, "cell_6t", GDS["unit"], layer["boundary"]) (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"): def __init__(self):
design.design.__init__(self, name) design.design.__init__(self, "cell_6t")
debug.info(2, "Create bitcell object") debug.info(2, "Create bitcell object")
self.width = bitcell.chars["width"] self.width = bitcell.width
self.height = bitcell.chars["height"] self.height = bitcell.height
self.pin_map = bitcell.pin_map
def delay(self, slew, load=0, swing = 0.5): def delay(self, slew, load=0, swing = 0.5):
# delay of bit cell is not like a driver(from WL) # delay of bit cell is not like a driver(from WL)

View File

@ -13,7 +13,7 @@ class bitcell_array(design.design):
Connects the word lines and bit lines. 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) design.design.__init__(self, name)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) 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)) c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, 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.add_pins()
self.create_layout() self.create_layout()
self.add_labels() self.add_layout_pins()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
@ -40,26 +44,8 @@ class bitcell_array(design.design):
self.add_pin("gnd") self.add_pin("gnd")
def create_layout(self): 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 xoffset = 0.0
self.cell_inst = {}
for col in range(self.column_size): for col in range(self.column_size):
yoffset = 0.0 yoffset = 0.0
for row in range(self.row_size): for row in range(self.row_size):
@ -70,79 +56,110 @@ class bitcell_array(design.design):
dir_key = "MX" dir_key = "MX"
else: else:
tempy = yoffset tempy = yoffset
dir_key = "R0" dir_key = ""
if OPTS.trim_noncritical == True: self.cell_inst[row,col]=self.add_inst(name=name,
if row == self.row_size - 1: mod=self.cell,
self.add_inst(name=name, offset=[xoffset, tempy],
mod=self.cell, mirror=dir_key)
offset=[xoffset, tempy], self.connect_inst(["bl[{0}]".format(col),
mirror=dir_key) "br[{0}]".format(col),
self.connect_inst(["bl[{0}]".format(col), "wl[{0}]".format(row),
"br[{0}]".format(col), "vdd",
"wl[{0}]".format(row), "gnd"])
"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"])
yoffset += self.cell.height yoffset += self.cell.height
xoffset += self.cell.width 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) offset = vector(0.0, 0.0)
for col in range(self.column_size): for col in range(self.column_size):
offset.y = 0.0 # get the pin of the lower row cell and make it the full width
self.add_label(text="bl[{0}]".format(col), bl_pin = self.cell_inst[0,col].get_pin("BL")
layer="metal2", br_pin = self.cell_inst[0,col].get_pin("BR")
offset=offset + vector(self.bitcell_chars["BL"][0],0)) self.add_layout_pin(text="bl[{0}]".format(col),
self.add_label(text="br[{0}]".format(col), layer="metal2",
layer="metal2", offset=bl_pin.ll(),
offset=offset + vector(self.bitcell_chars["BR"][0],0)) width=bl_pin.width(),
self.BL_positions.append(offset + vector(self.bitcell_chars["BL"][0],0)) height=full_height)
self.BR_positions.append(offset + vector(self.bitcell_chars["BR"][0],0)) 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 # gnd offset is 0 in our cell, but it be non-zero
self.add_label(text="gnd", gnd_pins = self.cell_inst[0,col].get_pins("gnd")
layer="metal2", for gnd_pin in gnd_pins:
offset=offset + vector(self.bitcell_chars["gnd"][0],0)) # avoid duplicates by only doing even rows
self.gnd_positions.append(offset + vector(self.bitcell_chars["gnd"][0],0)) # 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:
for row in range(self.row_size): self.add_layout_pin(text="gnd",
# only add row labels on the left most column layer="metal2",
if col == 0: offset=gnd_pin.ll(),
# flipped row width=gnd_pin.width(),
if row % 2: height=full_height)
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
# increments to the next column width # increments to the next column width
offset.x += self.cell.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): def delay(self, slew, load=0):
from tech import drc from tech import drc
wl_wire = self.gen_wl_wire() wl_wire = self.gen_wl_wire()

View File

@ -15,7 +15,7 @@ A calibre DRC runset file contains, at the minimum, the following information:
*drcLayoutPrimary: cell_6t *drcLayoutPrimary: cell_6t
*drcLayoutSystem: GDSII *drcLayoutSystem: GDSII
*drcResultsformat: ASCII *drcResultsformat: ASCII
*drcResultsFile: cell_6t.drc.db *drcResultsFile: cell_6t.drc.results
*drcSummaryFile: cell_6t.drc.summary *drcSummaryFile: cell_6t.drc.summary
*cmnFDILayerMapFile: ./layer.map *cmnFDILayerMapFile: ./layer.map
*cmnFDIUseLayerMap: 1 *cmnFDIUseLayerMap: 1
@ -28,7 +28,7 @@ To open the results, you can do this:
calibredrv cell_6t.gds calibredrv cell_6t.gds
Select Verification->Start RVE. 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. Click on the errors and they will highlight in the design layout viewer.
For LVS: For LVS:
@ -44,7 +44,7 @@ For LVS:
*lvsPowerNames: vdd *lvsPowerNames: vdd
*lvsGroundNames: vss *lvsGroundNames: vss
*lvsIgnorePorts: 1 *lvsIgnorePorts: 1
*lvsERCDatabase: cell_6t.erc.db *lvsERCDatabase: cell_6t.erc.results
*lvsERCSummaryFile: cell_6t.erc.summary *lvsERCSummaryFile: cell_6t.erc.summary
*lvsReportFile: cell_6t.lvs.report *lvsReportFile: cell_6t.lvs.report
*lvsMaskDBFile: cell_6t.maskdb *lvsMaskDBFile: cell_6t.maskdb
@ -82,7 +82,7 @@ def run_drc(name, gds_name):
'drcLayoutPrimary': name, 'drcLayoutPrimary': name,
'drcLayoutSystem': 'GDSII', 'drcLayoutSystem': 'GDSII',
'drcResultsformat': 'ASCII', 'drcResultsformat': 'ASCII',
'drcResultsFile': OPTS.openram_temp + name + ".drc.db", 'drcResultsFile': OPTS.openram_temp + name + ".drc.results",
'drcSummaryFile': OPTS.openram_temp + name + ".drc.summary", 'drcSummaryFile': OPTS.openram_temp + name + ".drc.summary",
'cmnFDILayerMapFile': drc["layer_map"], 'cmnFDILayerMapFile': drc["layer_map"],
'cmnFDIUseLayerMap': 1 'cmnFDIUseLayerMap': 1
@ -153,7 +153,7 @@ def run_lvs(name, gds_name, sp_name):
'lvsIncludeSVRFCmds': 1, 'lvsIncludeSVRFCmds': 1,
'lvsSVRFCmds': '{VIRTUAL CONNECT NAME VDD? GND? ?}', 'lvsSVRFCmds': '{VIRTUAL CONNECT NAME VDD? GND? ?}',
'lvsIgnorePorts': 1, 'lvsIgnorePorts': 1,
'lvsERCDatabase': OPTS.openram_temp + name + ".erc.db", 'lvsERCDatabase': OPTS.openram_temp + name + ".erc.results",
'lvsERCSummaryFile': OPTS.openram_temp + name + ".erc.summary", 'lvsERCSummaryFile': OPTS.openram_temp + name + ".erc.summary",
'lvsReportFile': OPTS.openram_temp + name + ".lvs.report", 'lvsReportFile': OPTS.openram_temp + name + ".lvs.report",
'lvsMaskDBFile': OPTS.openram_temp + name + ".maskdb", 'lvsMaskDBFile': OPTS.openram_temp + name + ".maskdb",

View File

@ -31,16 +31,11 @@ class contact(design.design):
self.create_contact_array() self.create_contact_array()
self.create_first_layer_enclosure() self.create_first_layer_enclosure()
self.create_second_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.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) self.width = max(obj.offset.x + obj.width for obj in self.objs)
def setup_layers(self): def setup_layers(self):
(first_layer, via_layer, second_layer) = self.layer_stack (first_layer, via_layer, second_layer) = self.layer_stack
self.first_layer_name = first_layer 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_width = drc["minwidth_{0}". format(self.via_layer_name)]
self.contact_to_contact = drc["{0}_to_{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_pitch = self.contact_width + self.contact_to_contact
self.contact_array_width = self.contact_width \ self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch
+ (self.dimensions[0] - 1) * self.contact_pitch self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch
self.contact_array_height = self.contact_width \
+ (self.dimensions[1] - 1) * self.contact_pitch
# FIME break this up # FIME break this up
self.first_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.first_layer_name)] - self.contact_array_width) / 2, 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)]) 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.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, / (self.contact_array_width + 2 * self.first_layer_horizontal_enclosure) - self.contact_array_height) / 2,
(drc["minheight_{0}".format( (drc["minheight_{0}".format(self.first_layer_name)] - self.contact_array_height) / 2,
self.first_layer_name)] - self.contact_array_height) / 2,
drc["{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)]) 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, 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)]) 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.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, / (self.contact_array_width + 2 * self.second_layer_horizontal_enclosure) - self.contact_array_height) / 2,
(drc["minheight_{0}".format( (drc["minheight_{0}".format(self.second_layer_name)] - self.contact_array_height) / 2,
self.second_layer_name)] - self.contact_array_height) / 2,
drc["{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)]) 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): def create_contact_array(self):
""" Create the contact array at the origin""" """ Create the contact array at the origin"""
self.via_layer_position = vector(0, 0)
for i in range(self.dimensions[1]): 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]): for j in range(self.dimensions[0]):
self.add_rect(layer=self.via_layer_name, self.add_rect(layer=self.via_layer_name,
offset=offset, offset=offset,
width=self.contact_width, width=self.contact_width,
height=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): def create_first_layer_enclosure(self):
width = self.first_layer_width = self.contact_array_width \ width = self.first_layer_width = self.contact_array_width \
+ 2 * self.first_layer_horizontal_enclosure + 2 * self.first_layer_horizontal_enclosure
height = self.first_layer_height = self.contact_array_height \ height = self.first_layer_height = self.contact_array_height \
+ 2 * self.first_layer_vertical_enclosure + 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, self.add_rect(layer=self.first_layer_name,
offset=offset, offset=self.first_layer_position,
width=width, width=width,
height=height) height=height)
@ -102,9 +98,7 @@ class contact(design.design):
+ 2 * self.second_layer_horizontal_enclosure + 2 * self.second_layer_horizontal_enclosure
height = self.second_layer_height = self.contact_array_height \ height = self.second_layer_height = self.contact_array_height \
+ 2 * self.second_layer_vertical_enclosure + 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, self.add_rect(layer=self.second_layer_name,
offset=offset, offset=self.second_layer_position,
width=width, width=width,
height=height) height=height)

File diff suppressed because it is too large Load Diff

220
compiler/delay_chain.py Normal file
View File

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

View File

@ -21,6 +21,7 @@ write_driver_array = "write_driver_array"
tri_gate = "tri_gate" tri_gate = "tri_gate"
tri_gate_array = "tri_gate_array" tri_gate_array = "tri_gate_array"
wordline_driver = "wordline_driver" wordline_driver = "wordline_driver"
replica_bitline = "replica_bitline"
replica_bitcell = "replica_bitcell" replica_bitcell = "replica_bitcell"
bitcell = "bitcell" bitcell = "bitcell"
delay_chain = "logic_effort_dc" delay_chain = "delay_chain"

View File

@ -21,6 +21,7 @@ write_driver_array = "write_driver_array"
tri_gate = "tri_gate" tri_gate = "tri_gate"
tri_gate_array = "tri_gate_array" tri_gate_array = "tri_gate_array"
wordline_driver = "wordline_driver" wordline_driver = "wordline_driver"
replica_bitline = "replica_bitline"
replica_bitcell = "replica_bitcell" replica_bitcell = "replica_bitcell"
bitcell = "bitcell" bitcell = "bitcell"
delay_chain = "logic_effort_dc" delay_chain = "delay_chain"

View File

@ -651,8 +651,9 @@ class VlsiLayout:
def getLabelDBInfo(self,label_name): 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_layer = None
label_coordinate = [None, None] label_coordinate = [None, None]
@ -661,18 +662,24 @@ class VlsiLayout:
if Text.textString == label_name or Text.textString == label_name+"\x00": if Text.textString == label_name or Text.textString == label_name+"\x00":
label_layer = Text.drawingLayer label_layer = Text.drawingLayer
label_coordinate = Text.coordinates[0] 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)) debug.check(len(label_list)>0,"Did not find labels {0}.".format(label_name))
return (label_coordinate, label_layer) return label_list
def getLabelInfo(self,label_name): def getLabelInfo(self,label_name):
""" """
Return the coordinates in USER units and layer of a label Return the coordinates in USER units and layer of a label
""" """
(label_coordinate,label_layer)=self.getLabelDBInfo(label_name) label_list=self.getLabelDBInfo(label_name)
user_coordinates = [x*self.units[0] for x in label_coordinate] new_list=[]
return (user_coordinates,label_layer) 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): def getPinShapeByLocLayer(self, coordinate, layer):
""" """
@ -729,16 +736,25 @@ class VlsiLayout:
Search for a pin label and return the largest enclosing rectangle Search for a pin label and return the largest enclosing rectangle
on the same layer as the pin label. on the same layer as the pin label.
""" """
(label_coordinate,label_layer)=self.getLabelDBInfo(label_name) label_list=self.getLabelDBInfo(label_name)
return self.getPinShapeByDBLocLayer(label_coordinate, label_layer) 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): def getAllPinShapesByLabel(self,label_name):
""" """
Search for a pin label and return ALL the enclosing rectangles on the same layer Search for a pin label and return ALL the enclosing rectangles on the same layer
as the pin label. 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): def getAllPinShapesInStructureList(self,coordinates,layer):
""" """

View File

@ -32,13 +32,18 @@ class instance(geometry):
def __init__(self, name, mod, offset, mirror, rotate): def __init__(self, name, mod, offset, mirror, rotate):
"""Initializes an instance to represent a module""" """Initializes an instance to represent a module"""
geometry.__init__(self) geometry.__init__(self)
debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.")
self.name = name self.name = name
self.mod = mod self.mod = mod
self.gds = mod.gds self.gds = mod.gds
self.rotate = rotate self.rotate = rotate
self.offset = vector(offset).snap_to_grid() self.offset = vector(offset).snap_to_grid()
self.mirror = mirror 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) debug.info(3, "creating instance: " + self.name)
def gds_write_file(self, newLayout): def gds_write_file(self, newLayout):
@ -53,6 +58,95 @@ class instance(geometry):
mirror=self.mirror, mirror=self.mirror,
rotate=self.rotate) 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): def __str__(self):
""" override print function output """ """ override print function output """
return "inst: " + self.name + " mod=" + self.mod.name return "inst: " + self.name + " mod=" + self.mod.name

View File

@ -18,76 +18,60 @@ class hierarchical_decoder(design.design):
Dynamically generated hierarchical decoder. 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)) design.design.__init__(self, "hierarchical_decoder_{0}rows".format(rows))
c = reload(__import__(OPTS.config.bitcell)) c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, 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.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.num_inputs = int(math.log(self.rows, 2))
self.create_layout() self.create_layout()
self.DRC_LVS() self.DRC_LVS()
def create_layout(self): def create_layout(self):
self.add_modules() self.add_modules()
self.setup_layout_offsets()
self.setup_layout_constants() 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.add_pins()
self.dimensions_hierarchy_decoder()
self.create_pre_decoder() self.create_pre_decoder()
self.create_row_decoder() self.create_row_decoder()
self.create_vertical_rail() 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): def add_modules(self):
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
# Vertical metal rail gap definition # Vertical metal rail gap definition
self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2 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.metal2_spacing = self.metal2_extend_contact + drc["metal2_to_metal2"]
self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_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 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 # 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.contact_shift = (self.m1m2_via.first_layer_width - self.m1m2_via.contact_width) / 2
self.inv = pinv(nmos_width=drc["minwidth_tx"], self.inv = pinv()
beta=2,
height=self.bitcell_height)
self.add_mod(self.inv) self.add_mod(self.inv)
self.nand2 = nand_2(nmos_width=self.nand2_nmos_width, self.nand2 = nand_2()
height=self.bitcell_height)
self.add_mod(self.nand2) self.add_mod(self.nand2)
self.nand3 = nand_3(nmos_width=self.nand3_nmos_width, self.nand3 = nand_3()
height=self.bitcell_height)
self.add_mod(self.nand3) self.add_mod(self.nand3)
# CREATION OF PRE-DECODER # CREATION OF PRE-DECODER
self.pre2_4 = pre2x4(self.nand2_nmos_width, "pre2x4") self.pre2_4 = pre2x4()
self.add_mod(self.pre2_4) 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) 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): def determine_predecodes(self,num_inputs):
# Determines the number of 2:4 pre-decoder and 3:8 pre-decoder needed """Determines the number of 2:4 pre-decoder and 3:8 pre-decoder
# based on the number of inputs needed based on the number of inputs"""
if (num_inputs == 2): if (num_inputs == 2):
return (1,0) return (1,0)
elif (num_inputs == 3): elif (num_inputs == 3):
@ -112,489 +96,444 @@ class hierarchical_decoder(design.design):
self.no_of_pre2x4=p2x4 self.no_of_pre2x4=p2x4
self.no_of_pre3x8=p3x8 self.no_of_pre3x8=p3x8
# Stromg the index of the vertical rails in different groups. These self.predec_groups = [] # This array is a 2D array.
# vertical lines is used to connect pre-decoder to row-decoder
self.predecoder_output_groups = [] # This array is a 2D array.
self.group_sizes = []
# Distributing vertical rails to different groups. One group belongs to one pre-decoder. # Distributing vertical rails to different groups. One group belongs to one pre-decoder.
# For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will # 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 # 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] ] # 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 index = 0
for i in range(self.no_of_pre2x4): for i in range(self.no_of_pre2x4):
lines = [] lines = []
for j in range(4): for j in range(4):
lines.append(index) lines.append(index)
index = index + 1 index = index + 1
self.predecoder_output_groups.append(lines) self.predec_groups.append(lines)
self.group_sizes.append(4)
for i in range(self.no_of_pre3x8): for i in range(self.no_of_pre3x8):
lines = [] lines = []
for j in range(8): for j in range(8):
lines.append(index) lines.append(index)
index = index + 1 index = index + 1
self.predecoder_output_groups.append(lines) self.predec_groups.append(lines)
self.group_sizes.append(8)
self.calculate_dimensions()
def add_pins(self): def add_pins(self):
""" Add the module pins """
for i in range(self.num_inputs): for i in range(self.num_inputs):
self.add_pin("A[{0}]".format(i)) self.add_pin("A[{0}]".format(i))
if(self.num_inputs >= 4): for j in range(self.rows):
for j in range(self.rows): self.add_pin("decode[{0}]".format(j))
self.add_pin("decode_out[{0}]".format(j))
else:
for j in range(self.rows):
self.add_pin("out[{0}]".format(j))
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def dimensions_hierarchy_decoder(self): def calculate_dimensions(self):
self.total_number_of_predecoder_outputs = (4 * self.no_of_pre2x4 """ Calculate the overal dimensions of the hierarchical decoder """
+ 8 * self.no_of_pre3x8)
# 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, # Calculates height and width of pre-decoder,
if(self.no_of_pre3x8 > 0): if(self.no_of_pre3x8 > 0):
self.predecoder_width = self.pre3_8.width self.predecoder_width = self.pre3_8.width
else: else:
self.predecoder_width = self.pre2_4.width self.predecoder_width = self.pre2_4.width
self.predecoder_height = (self.pre2_4.height * self.no_of_pre2x4 self.predecoder_height = self.pre2_4.height*self.no_of_pre2x4 + self.pre3_8.height*self.no_of_pre3x8
+ self.pre3_8.height * self.no_of_pre3x8)
# Calculates height and width of row-decoder # Calculates height and width of row-decoder
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
nand_width = self.nand2.width nand_width = self.nand2.width
else: else:
nand_width = self.nand3.width nand_width = self.nand3.width
total_gap = (self.gap_between_rail_offset self.routing_width = self.metal2_pitch*self.total_number_of_predecoder_outputs
* self.total_number_of_predecoder_outputs) self.row_decoder_width = nand_width + self.routing_width + self.inv.width
self.row_decoder_width = (nand_width + total_gap
+ self.inv.width)
self.row_decoder_height = self.inv.height * self.rows self.row_decoder_height = self.inv.height * self.rows
# Calculates height and width of hierarchical decoder # Calculates height and width of hierarchical decoder
self.height = (self.predecoder_height self.height = self.predecoder_height + self.row_decoder_height
+ self.row_decoder_height) self.width = self.predecoder_width + self.routing_width
self.width = self.predecoder_width + total_gap
def create_pre_decoder(self): def create_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """ """ Creates pre-decoder and places labels input address [A] """
for i in range(self.no_of_pre2x4): for i in range(self.no_of_pre2x4):
self.add_pre2x4(i) self.add_pre2x4(i)
self.add_lables_pre2x4(i)
for j in range(self.no_of_pre3x8): for i in range(self.no_of_pre3x8):
pre3x8_yoffset=self.add_pre3x8(j) self.add_pre3x8(i)
self.add_lables_pre3x8(j,pre3x8_yoffset)
def add_pre2x4(self,i): def add_pre2x4(self,num):
""" Add a 2x4 predecoder """
if (self.num_inputs == 2): if (self.num_inputs == 2):
base = vector(0,0) base = vector(self.routing_width,0)
mod_dir = vector(1,1)
mirror = "RO" mirror = "RO"
index_off1 = index_off2 = 0 index_off1 = index_off2 = 0
else: else:
base= vector(self.pre2_4.width, i * self.pre2_4.height) base= vector(self.routing_width+self.pre2_4.width, num * self.pre2_4.height)
mod_dir = vector(-1,1)
mirror = "MY" mirror = "MY"
index_off1 = i * 2 index_off1 = num * 2
index_off2 = i * 4 index_off2 = num * 4
pins = [] pins = []
for input_index in range(2): for input_index in range(2):
pins.append("A[{0}]".format(input_index + index_off1)) pins.append("A[{0}]".format(input_index + index_off1))
for output_index in range(4): for output_index in range(4):
pins.append("out[{0}]".format(output_index + index_off2)) 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), self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num),
mod=self.pre2_4, mod=self.pre2_4,
offset=base, offset=base,
mirror=mirror) mirror=mirror))
self.connect_inst(pins) self.connect_inst(pins)
vdd_offset = base + self.pre2_4.vdd_position.scale(mod_dir) self.add_pre2x4_pins(num)
self.pre_decoder_vdd_positions.append(vdd_offset)
self.add_label(text="vdd",
layer="metal1",
offset=vdd_offset)
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): def add_pre2x4_pins(self,num):
pre2_4_base = i * self.pre2_4.height """ Add the input pins to the 2x4 predecoder """
# 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))
# ADDING LABELS FOR OUTPUT SIDE OF THE 2:4 PRE-DECODER for i in range(2):
for inv_2x4 in range(4): pin = self.pre2x4_inst[num].get_pin("in[{}]".format(i))
if (self.num_inputs == 2): pin_offset = pin.ll()
xoffset = self.pre2_4.x_off_inv_2 + self.inv.Z_position.x
else: pin = self.pre2_4.get_pin("in[{}]".format(i))
xoffset = 0 self.add_layout_pin(text="A[{0}]".format(i + 2*num ),
if (inv_2x4 % 2 == 0): layer="metal2",
pin_y = self.inv.Z_position.y offset=pin_offset,
else: width=pin.width(),
pin_y = self.inv.height - self.inv.Z_position.y height=pin.height())
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])
def add_pre3x8(self,j):
def add_pre3x8(self,num):
""" Add 3x8 numbered predecoder """
if (self.num_inputs == 3): if (self.num_inputs == 3):
offset = vector(0,0) offset = vector(self.routing_width,0)
mirror ="R0" mirror ="R0"
mod_dir = vector(1,1)
index_off1 = index_off2 = 0
else: else:
offset = vector(self.pre3_8.width, height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
self.no_of_pre2x4 * self.pre2_4.height offset = vector(self.routing_width+self.pre3_8.width, height)
+ j * self.pre3_8.height)
mirror="MY" mirror="MY"
mod_dir = vector(-1,1)
index_off1 = j * 3 + self.no_of_pre2x4 * 2 # If we had 2x4 predecodes, those are used as the lower
index_off2 = j * 8 + self.no_of_pre2x4 * 4 # decode output bits
in_index_offset = num * 3 + self.no_of_pre2x4 * 2
out_index_offset = num * 8 + self.no_of_pre2x4 * 4
pins = [] pins = []
for input_index in range(3): 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): for output_index in range(8):
pins.append("out[{0}]".format(output_index + index_off2)) pins.append("out[{0}]".format(output_index + out_index_offset))
pins = pins + ["vdd", "gnd"] pins.extend(["vdd", "gnd"])
self.add_inst(name="pre3x8[{0}]".format(j), self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num),
mod=self.pre3_8, mod=self.pre3_8,
offset=offset, offset=offset,
mirror=mirror) mirror=mirror))
self.connect_inst(pins) self.connect_inst(pins)
vdd_offset = offset + self.pre3_8.vdd_position.scale(mod_dir) # The 3x8 predecoders will be stacked, so use yoffset
self.pre_decoder_vdd_positions.append(vdd_offset) self.add_pre3x8_pins(num,offset)
self.add_label(text="vdd",
layer="metal1",
offset=vdd_offset)
gnd_offset = offset + self.pre3_8.gnd_position.scale(mod_dir) def add_pre3x8_pins(self,num,offset):
self.pre_decoder_gnd_positions.append(gnd_offset) """ Add the input pins to the 3x8 predecoder at the given offset """
self.add_label(text="gnd",
layer="metal1",
offset=gnd_offset)
return offset.y
def add_lables_pre3x8(self,j,pre3x8_yoffset): for i in range(3):
# ADDING LABELS FOR INPUT SIDE OF THE 3:8 PRE-DECODER pin = self.pre3x8_inst[num].get_pin("in[{}]".format(i))
if (self.num_inputs == 3): pin_offset = pin.ll()
xoffset = self.pre3_8.x_off_inv_1 self.add_layout_pin(text="A[{0}]".format(i + 3*num + 2*self.no_of_pre2x4),
else: layer="metal2",
xoffset = self.pre3_8.width - self.pre3_8.x_off_inv_1 offset=pin_offset,
for inv_3x8 in range(3): width=pin.width(),
if (inv_3x8 % 2 == 0): height=pin.height())
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))
# 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): def create_row_decoder(self):
# Create the row-decoder using NAND2/NAND3 and Inverter and places the """ Create the row-decoder by placing NAND2/NAND3 and Inverters
# output labels [out/decode_out] and add the primary decoder output pins. """
if (self.num_inputs >= 4): if (self.num_inputs >= 4):
self.add_decoder_nand_array_and_labels() self.add_decoder_nand_array()
self.add_decoder_inv_array_and_labels() 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. # Row Decoder NAND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
nand = self.nand2 self.add_nand_array(nand_mod=self.nand2)
correct = 0
nand_name ="NAND2"
self.add_nand_array(nand,correct,nand_name)
# FIXME: Can we convert this to the connect_inst with checks? # FIXME: Can we convert this to the connect_inst with checks?
for i in range(self.group_sizes[0]): for i in range(len(self.predec_groups[0])):
for j in range(self.group_sizes[1]): for j in range(len(self.predec_groups[1])):
pins =["out[{0}]".format(i), pins =["out[{0}]".format(i),
"out[{0}]".format(j + self.group_sizes[0]), "out[{0}]".format(j + len(self.predec_groups[0])),
"Z[{0}]".format(self.group_sizes[1] * i + j), "Z[{0}]".format(len(self.predec_groups[1])*i + j),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(args=pins, check=False) self.connect_inst(args=pins, check=False)
# Row Decoder NAND GATE array for address inputs >5. # Row Decoder NAND GATE array for address inputs >5.
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
nand = self.nand3 self.add_nand_array(nand_mod=self.nand3,
correct = drc["minwidth_metal1"] correct=drc["minwidth_metal1"])
nand_name ="NAND3"
self.add_nand_array(nand,correct,nand_name)
# This will not check that the inst connections match. # This will not check that the inst connections match.
for i in range(self.group_sizes[0]): for i in range(len(self.predec_groups[0])):
for j in range(self.group_sizes[1]): for j in range(len(self.predec_groups[1])):
for k in range(self.group_sizes[2]): for k in range(len(self.predec_groups[2])):
Z_index = (self.group_sizes[1] * self.group_sizes[2]* i Z_index = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \
+ self.group_sizes[2] * j + k) + len(self.predec_groups[2])*j + k
pins = ["out[{0}]".format(i), pins = ["out[{0}]".format(i),
"out[{0}]".format(j + self.group_sizes[0]), "out[{0}]".format(j + len(self.predec_groups[0])),
"out[{0}]".format(k + self.group_sizes[0] + self.group_sizes[1]), "out[{0}]".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
"Z[{0}]".format(Z_index), "Z[{0}]".format(Z_index),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(args=pins, check=False) 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): for row in range(self.rows):
name = nand_name+"_[{0}]".format(row) name = "DEC_NAND[{0}]".format(row)
if ((row % 2) == 0): if ((row % 2) == 0):
y_off = self.predecoder_height + (nand.height) * (row) y_off = self.predecoder_height + nand_mod.height*row
y_dir = 1 y_dir = 1
mirror = "R0" mirror = "R0"
rect_offset = vector(self.routing_width + nand_mod.width, y_off + z_pin.uy() - rect_height)
else: else:
y_off = self.predecoder_height + (nand.height) * (row + 1) y_off = self.predecoder_height + nand_mod.height*(row + 1)
y_dir = - 1 y_dir = -1
mirror = "MX" mirror = "MX"
rect_offset =vector(self.routing_width + nand_mod.width, y_off - z_pin.uy())
self.add_inst(name=name, self.add_inst(name=name,
mod=nand, mod=nand_mod,
offset=[0, y_off], offset=[self.routing_width, y_off],
mirror=mirror) 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): self.add_rect(layer="metal1",
# Row Decoder INVERTER array insts. 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): if (self.num_inputs == 4 or self.num_inputs == 5):
x_off = self.nand2.width x_off = self.routing_width + self.nand2.width
else: else:
x_off = self.nand3.width x_off = self.routing_width + self.nand3.width
for row in range(self.rows): for row in range(self.rows):
name = "INVERTER_[{0}]".format(row) name = "DEC_INV_[{0}]".format(row)
if ((row % 2) == 0): if (row % 2 == 0):
inv_row_height = self.inv.height * row inv_row_height = self.inv.height * row
mirror = "R0" mirror = "R0"
y_dir = 1
else: else:
inv_row_height = self.inv.height * (row + 1) inv_row_height = self.inv.height * (row + 1)
mirror = "MX" mirror = "MX"
y_dir = -1
y_off = self.predecoder_height + inv_row_height y_off = self.predecoder_height + inv_row_height
offset = vector(x_off,y_off)
self.add_inst(name=name, self.add_inst(name=name,
mod=self.inv, mod=self.inv,
offset=[x_off, y_off], offset=offset,
mirror=mirror) mirror=mirror)
# This will not check that the inst connections match. # This will not check that the inst connections match.
self.connect_inst(args=["Z[{0}]".format(row), self.connect_inst(args=["Z[{0}]".format(row),
"decode_out[{0}]".format(row), "decode[{0}]".format(row),
"vdd", "gnd"], "vdd", "gnd"],
check=False) check=False)
# add vdd and gnd label self.add_layout_pin(text="decode[{0}]".format(row),
for row in range(self.rows): layer="metal1",
if ((row % 2) == 0): offset=offset+z_pin.ll().scale(1,y_dir),
offset = vector(0, self.predecoder_height + row*(self.inv.height)) width=z_pin.width(),
vdd_offset = offset + self.inv.vdd_position.scale(0,1) height=y_dir*z_pin.height())
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)
# 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): 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): if (self.num_inputs >= 4):
# Array for saving the X offsets of the vertical rails. These rail # Array for saving the X offsets of the vertical rails. These rail
# offsets are accessed with indices. # offsets are accessed with indices.
vertical_rail_x_offsets = [] self.rail_x_offsets = []
for i in range(self.total_number_of_predecoder_outputs): for i in range(self.total_number_of_predecoder_outputs):
vertical_rail_x_offsets.append(-self.gap_between_rail_offset \ # The offsets go into the negative x direction
* (self.total_number_of_predecoder_outputs - i)) # 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", self.add_rect(layer="metal2",
offset=[-self.gap_between_rail_offset * (i + 1), offset=vector(x_offset,0),
0],
width=drc["minwidth_metal2"], width=drc["minwidth_metal2"],
height=self.height) height=self.height)
# Horizontal metal extensions from pre-decoder 2x4ouput. self.connect_rails_to_predecodes()
for i in range(self.no_of_pre2x4): self.connect_rails_to_decoder()
self.extend_horizontal_to_pre2x4(i,vertical_rail_x_offsets)
# Horizontal metal extensions from pre-decoder 3x8 ouput. def connect_rails_to_predecodes(self):
for i in range(self.no_of_pre3x8): """ Iterates through all of the predecodes and connects to the rails including the offsets """
self.extend_horizontal_to_pre3x8(i,vertical_rail_x_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): def connect_rails_to_pre2x4(self, predecode_num):
pin_y = self.inv.Z_position.y """ 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: else:
pin_y = (self.inv.height - drc["minwidth_metal1"] pin_y = self.inv.height - drc["minwidth_metal1"] - pin.y
- self.inv.Z_position.y)
yoffset = current_inv_height + pin_y
self.add_extend_rails(yoffset = yoffset, self.connect_rail(vector(self.rail_x_offsets[index], current_inv_height + pin_y))
xoffset = vertical_rail_x_offsets[line_index])
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): z_pin = self.inv.get_pin("Z")
for inv_3x8 in range(8): pin = z_pin.ll()
line_index = output_index * 8 + inv_3x8 + self.no_of_pre2x4 * 4 for i in range(8):
current_inv_height = output_index * (self.pre3_8.height) \ index = predecode_num * 8 + i + self.no_of_pre2x4 * 4
+ inv_3x8 * (self.inv.height) \ current_inv_height = predecode_num*self.pre3_8.height \
+ self.no_of_pre2x4 * self.pre2_4.height + i*self.inv.height \
+ self.no_of_pre2x4*self.pre2_4.height
if (inv_3x8 % 2 == 0): if (i % 2 == 0):
pin_y = self.inv.Z_position.y pin_y = pin.y
else: else:
pin_y = (self.inv.height - drc["minwidth_metal1"] pin_y = self.inv.height - drc["minwidth_metal1"] - pin.y
- self.inv.Z_position.y)
yoffset = current_inv_height + pin_y self.connect_rail(vector(self.rail_x_offsets[index], current_inv_height + pin_y))
self.add_extend_rails(yoffset = yoffset, def connect_rails_to_decoder(self):
xoffset = vertical_rail_x_offsets[line_index]) """ Use the self.predec_groups to determine the connections to the decoder NAND gates.
Inputs of NAND2/NAND3 gates come from different groups.
def connect_vertial_rails_to_decoder(self, vertical_rail_x_offsets): For example for these groups [ [0,1,2,3] ,[4,5,6,7],
# METAL CONNECTION FROM THE VERTICAL RAIL TOWARDS THE DECODER. [8,9,10,11,12,13,14,15] ] the first NAND3 inputs are connected to
# PRE-DECODER OUTPUT ARE CONNECTED TO THIS SAME RAIL ALSO [0,4,8] and second NAND3 is connected to [0,4,9] ........... and the
# To makes these connections groups of line index that was stored in 128th NAND3 is connected to [3,7,15]
# 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]
row_index = 0 row_index = 0
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
for line_index_A in self.predecoder_output_groups[0]: a_pin = self.nand2.get_pin("A")
for line_index_B in self.predecoder_output_groups[1]: 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): if (row_index % 2 == 0):
yoffset_A = current_inv_height + self.nand2.A_position.y yoffset_A = current_inv_height + a_pin.by()
yoffset_B = current_inv_height + self.nand2.B_position.y yoffset_B = current_inv_height + b_pin.by()
else: else:
base = current_inv_height + self.inv.height - drc["minwidth_metal1"] base = current_inv_height + self.inv.height - drc["minwidth_metal1"]
yoffset_A = base - self.nand2.A_position.y yoffset_A = base - a_pin.by()
yoffset_B = base - self.nand2.B_position.y yoffset_B = base - b_pin.by()
row_index = row_index + 1 row_index = row_index + 1
self.add_extend_rails(yoffset =yoffset_A, self.connect_rail(vector(self.rail_x_offsets[index_A], yoffset_A))
xoffset =vertical_rail_x_offsets[line_index_A]) self.connect_rail(vector(self.rail_x_offsets[index_B], yoffset_B))
self.add_extend_rails(yoffset =yoffset_B,
xoffset =vertical_rail_x_offsets[line_index_B])
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
for line_index_A in self.predecoder_output_groups[0]: a_pin = self.nand3.get_pin("A")
for line_index_B in self.predecoder_output_groups[1]: b_pin = self.nand3.get_pin("B")
for line_index_C in self.predecoder_output_groups[2]: 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): if (row_index % 2 == 0):
yoffset_A = current_inv_height + self.nand3.A_position.y yoffset_A = current_inv_height + a_pin.by()
yoffset_B = current_inv_height + self.nand3.B_position.y yoffset_B = current_inv_height + b_pin.by()
yoffset_C = current_inv_height + self.nand3.C_position.y yoffset_C = current_inv_height + c_pin.by()
contact_C_yoffset = yoffset_C - self.contact_shift contact_C_yoffset = yoffset_C - self.contact_shift
else: else:
base = current_inv_height + self.inv.height - drc["minwidth_metal1"] base = current_inv_height + self.inv.height - drc["minwidth_metal1"]
yoffset_A = base - self.nand3.A_position.y yoffset_A = base - a_pin.by()
yoffset_B = base - self.nand3.B_position.y yoffset_B = base - b_pin.by()
yoffset_C = base - self.nand3.C_position.y yoffset_C = base - c_pin.by()
contact_C_yoffset = yoffset_C contact_C_yoffset = yoffset_C
row_index = row_index + 1 row_index = row_index + 1
self.add_extend_rails(yoffset =yoffset_A, self.connect_rail(vector(self.rail_x_offsets[index_A], yoffset_A))
xoffset =vertical_rail_x_offsets[line_index_A]) self.connect_rail(vector(self.rail_x_offsets[index_B], yoffset_B))
self.add_extend_rails(yoffset =yoffset_B, self.connect_rail(vector(self.rail_x_offsets[index_C], yoffset_C)) # contact_C_y_offset
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)
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", self.add_rect(layer="metal1",
offset=[xoffset, yoffset], offset=offset,
width=-xoffset, width=self.routing_width-offset.x,
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
if contact_yoffset!=0: if contact_yoffset!=0:
yoffset = contact_yoffset yoffset = contact_yoffset
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=[xoffset + self.gap_between_rails, offset=offset + vector(self.metal2_spacing,-self.via_shift),
yoffset - self.via_shift],
rotate=90) rotate=90)
def delay(self, slew, load = 0.0): def delay(self, slew, load = 0.0):

View File

@ -6,212 +6,254 @@ from contact import contact
from pinv import pinv from pinv import pinv
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from nand_2 import nand_2
from nand_3 import nand_3
class hierarchical_predecode(design.design): class hierarchical_predecode(design.design):
""" """
Pre 2x4 and 3x8 decoder shared code. Pre 2x4 and 3x8 decoder shared code.
""" """
def __init__(self, nmos_width, cellname, input_number): def __init__(self, input_number):
design.design.__init__(self, cellname) 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)) c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, 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): def add_pins(self):
for k in range(self.number_of_inputs): 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): for i in range(self.number_of_outputs):
self.add_pin("out[{0}]".format(i)) self.add_pin("out[{0}]".format(i))
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def create_modules(self): def create_modules(self):
layer_stack = ("metal1", "via1", "metal2") """ Create the INV and NAND gate """
self.m1m2_via = contact(layer_stack=layer_stack)
self.inv = pinv(nmos_width=drc["minwidth_tx"], self.inv = pinv()
beta=2,
height=self.bitcell_height)
self.add_mod(self.inv) 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) self.add_mod(self.nand)
def set_up_constrain(self): def create_nand(self,inputs):
self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2 """ Create the NAND for the predecode input stage """
self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2 if inputs==2:
self.via_shift = (self.m1m2_via.second_layer_width self.nand = nand_2()
- self.m1m2_via.first_layer_width) / 2 elif inputs==3:
self.metal2_extend_contact = (self.m1m2_via.second_layer_height self.nand = nand_3()
- self.m1m2_via.contact_width) / 2 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 # Non inverted input rails
+ drc["metal2_to_metal2"]) for rail_index in range(self.number_of_inputs):
self.gap_between_rail_offset = (self.gap_between_rails xoffset = rail_index * self.metal2_pitch
+ drc["minwidth_metal2"]) self.rails["in[{}]".format(rail_index)]=xoffset
# x offset for input inverters
def setup_constrains(self): self.x_off_inv_1 = self.number_of_inputs*self.metal2_pitch
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
# Creating the right hand side metal2 rails for output connections # Creating the right hand side metal2 rails for output connections
for hrail_2 in range(2 * self.number_of_inputs + 2): for rail_index in range(2 * self.number_of_inputs):
xoffset_2 = self.x_off_inv_1 + self.inv.width + self.gap_between_rails + (hrail_2 * self.gap_between_rail_offset) xoffset = self.x_off_inv_1 + self.inv.width + ((rail_index+1) * self.metal2_pitch)
self.rails_x_offset.append(xoffset_2) if rail_index < self.number_of_inputs:
self.xoffset_2=self.rails_x_offset[-1] 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.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.width = self.x_off_inv_2 + self.inv.width
self.set_height() self.height = self.number_of_outputs * self.nand.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
def create_rails(self): def create_rails(self):
for x_off in self.rails_x_offset: """ Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
self.add_rect(layer="metal2", for label in self.rails.keys():
offset=[x_off, 0], # these are not primary inputs, so they shouldn't have a
width=drc["minwidth_metal2"], # label or LVS complains about different names on one net
height=self.rail_height) if label.startswith("in"):
self.add_layout_pin(text=label,
def add_output_inverters(self): layer="metal2",
self.decode_out_positions = [] offset=[self.rails[label], 0],
for inv_2x4 in range(self.number_of_outputs): width=self.metal2_width,
name = "Xpre2x4_nand_inv[{0}]".format(inv_2x4) height=self.height - drc["metal2_to_metal2"])
if (inv_2x4 % 2 == 0):
y_factor = inv_2x4
mirror = "R0"
correct = self.inv.Z_position
else: else:
y_factor =inv_2x4 + 1 self.add_rect(layer="metal2",
mirror = "MX" offset=[self.rails[label], 0],
correct = self.inv.Z_position.scale(1,-1) - vector(0, width=self.metal2_width,
drc["minwidth_metal1"]) 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) base = vector(self.x_off_inv_2, self.inv.height * y_factor)
self.add_inst(name=name, self.add_inst(name=name,
mod=self.inv, mod=self.inv,
offset=base, offset=base,
mirror=mirror) mirror=mirror)
self.connect_inst(["Z[{0}]".format(inv_2x4), self.connect_inst(["Z[{}]".format(inv_num),
"out[{0}]".format(inv_2x4), "out[{}]".format(inv_num),
"vdd", "gnd"]) "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): 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): for nand_input in range(self.number_of_outputs):
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs) inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
name = "Xpre"+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): if (nand_input % 2 == 0):
y_off = nand_input * (self.nand.height) y_off = nand_input * (self.nand.height)
mirror = "R0" mirror = "R0"
offset = [self.x_off_nand + self.nand.width, rect_offset = vector(self.x_off_nand + self.nand.width,
y_off + self.nand.Z_position.y] y_off + z_pin.uy() - rect_height)
else: else:
y_off = (nand_input + 1) * (self.nand.height) y_off = (nand_input + 1) * (self.nand.height)
mirror = "MX" mirror = "MX"
offset =[self.x_off_nand + self.nand.width, rect_offset =vector(self.x_off_nand + self.nand.width,
y_off - self.nand.Z_position.y - drc["minwidth_metal1"]] y_off - z_pin.uy())
self.add_inst(name=name, self.add_inst(name=name,
mod=self.nand, mod=self.nand,
offset=[self.x_off_nand, y_off], offset=[self.x_off_nand, y_off],
mirror=mirror) mirror=mirror)
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=offset, offset=rect_offset,
width=drc["minwidth_metal1"], width=self.metal1_width,
height=drc["minwidth_metal1"]) height=rect_height)
self.connect_inst(connections[nand_input]) self.connect_inst(connections[nand_input])
def route(self): def route(self):
self.route_input_inverters() self.route_input_inverters()
self.route_inputs_to_rails()
self.route_nand_to_rails() self.route_nand_to_rails()
self.route_vdd_gnd_from_rails_to_gates() self.route_vdd_gnd()
def route_input_inverters(self): def route_inputs_to_rails(self):
# All conections of the inputs inverters [Inputs, outputs, vdd, gnd] """ Route the uninverted inputs to the second set of rails """
output_shift = self.set_output_shift() for num in range(self.number_of_inputs):
for inv_rout in range(self.number_of_inputs): # route one signal next to each vdd/gnd rail since this is
setup = self.setup_route_input_inverter(inv_rout,output_shift) # typically where the p/n devices are and there are no
y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset = setup # pins in the nand gates.
#add output y_offset = (num+self.number_of_inputs) * self.inv.height + 2*self.metal1_space
correct = y_dir * (output_shift + drc["minwidth_metal1"]) in_pin = "in[{}]".format(num)
output_metal = self.cal_input_inverters_output(setup,output_shift,inv_rout) a_pin = "A[{}]".format(num)
offset1,offset2=output_metal[0]
offset3,offset4=output_metal[1]
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=offset1, offset=[self.rails[in_pin],y_offset],
width=drc["minwidth_metal1"], width=self.rails[a_pin] + self.metal2_width - self.rails[in_pin],
height=offset2.y - offset1.y) height=self.metal1_width)
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]
self.add_via(layers = ("metal1", "via1", "metal2"), 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) rotate=90)
#route input self.add_via(layers = ("metal1", "via1", "metal2"),
self.add_rect(layer="metal1", offset=[self.rails[a_pin] + self.via_x_shift, y_offset + self.via_y_shift],
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],
rotate=90) 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", self.add_rect(layer="metal1",
offset=inv_vdd_offset, offset=[inv_out_offset.x,y_offset],
width=self.rails_x_offset[self.number_of_inputs] - inv_vdd_offset.x + drc["minwidth_metal2"], width=self.rails[out_pin]-inv_out_offset.x + self.metal2_width,
height=drc["minwidth_metal1"]) height=self.metal1_width)
# route gnd
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=inv_gnd_offset, offset=inv_out_offset,
width=self.rails_x_offset[self.number_of_inputs+1] - inv_gnd_offset.x + drc["minwidth_metal2"], width=self.metal1_width,
height=drc["minwidth_metal1"]) 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 #route input
if (inv_rout % 2 == 0): inv_in_offset = inv_offset+self.inv.get_pin("A").ll().scale(1,y_dir)
base_offset=[self.x_off_inv_1, inv_rout * self.inv.height ] self.add_rect(layer="metal1",
y_dir = 1 offset=[self.rails[in_pin], inv_in_offset.y],
else: width=inv_in_offset.x - self.rails[in_pin],
base_offset=[self.x_off_inv_1, 2 * self.inv.height - drc["minwidth_metal1"]] height=self.metal1_width)
y_dir = -1 self.add_via(layers=("metal1", "via1", "metal2"),
inv_out_offset = base_offset+self.inv.Z_position.scale(1,y_dir) offset=[self.rails[in_pin] + self.via_x_shift, inv_in_offset.y + self.via_y_shift],
inv_in_offset = base_offset+self.inv.A_position.scale(1,y_dir) rotate=90)
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]
def route_nand_to_rails(self): def route_nand_to_rails(self):
# This 2D array defines the connection mapping # This 2D array defines the connection mapping
@ -219,65 +261,50 @@ class hierarchical_predecode(design.design):
for k in range(self.number_of_outputs): for k in range(self.number_of_outputs):
# create x offset list # create x offset list
index_lst= nand_input_line_combination[k] index_lst= nand_input_line_combination[k]
line_x_offset = [] (nand_offset,y_dir) = self.get_gate_offset(self.x_off_nand,self.nand.height,k)
for index in index_lst:
line_x_offset.append(self.rails_x_offset[index]) if self.number_of_inputs == 2:
# create y offset list gate_lst = ["A","B"]
yoffset_nand_in, correct= self.create_y_offsets(k) else:
# connect based on the two list gate_lst = ["A","B","C"]
for i in range(self.number_of_inputs):
x_offset = line_x_offset[i] # this will connect pins A,B or A,B,C
y_offset = yoffset_nand_in[i] for rail_pin,gate_pin in zip(index_lst,gate_lst):
# Connecting the i-th input of Nand3 gate pin_offset = nand_offset+self.nand.get_pin(gate_pin).ll().scale(1,y_dir)
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=[x_offset, y_offset], offset=[self.rails[rail_pin], pin_offset.y],
width=self.x_off_nand - x_offset, width=pin_offset.x - self.rails[rail_pin],
height=drc["minwidth_metal1"]) height=self.metal1_width)
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=[x_offset+ self.gap_between_rails, offset=[self.rails[rail_pin] + self.via_x_shift, pin_offset.y + self.via_y_shift],
y_offset - self.via_shift - correct[i]], rotate=90)
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] def route_vdd_gnd(self):
self.add_rect(layer="metal1", """ Add a pin for each row of vdd/gnd which are must-connects next level up. """
offset=[x_offset, y_offset],
width=self.x_off_nand - x_offset, for num in range(0,self.number_of_outputs):
height=drc["minwidth_metal1"]) # this will result in duplicate polygons for rails, but who cares
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[x_offset + self.gap_between_rails, # use the inverter offset even though it will be the nand's too
y_offset - self.via_shift], (gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
rotate=90)
# 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)

View File

@ -1,7 +1,6 @@
from tech import drc from tech import drc
import debug import debug
import design import design
from nand_2 import nand_2
from vector import vector from vector import vector
from hierarchical_predecode import hierarchical_predecode from hierarchical_predecode import hierarchical_predecode
@ -9,110 +8,46 @@ class hierarchical_predecode2x4(hierarchical_predecode):
""" """
Pre 2x4 decoder used in hierarchical_decoder. Pre 2x4 decoder used in hierarchical_decoder.
""" """
def __init__(self, nmos_width, cellname): def __init__(self):
hierarchical_predecode.__init__(self, nmos_width, cellname, 2) hierarchical_predecode.__init__(self, 2)
self.add_pins() self.add_pins()
self.create_modules() self.create_modules()
self.setup_constrains() self.setup_constraints()
self.create_layout() self.create_layout()
self.route() self.DRC_LVS()
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"])
def create_layout(self): 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.create_rails()
self.add_inv2x4() self.add_input_inverters()
self.add_output_inverters() self.add_output_inverters()
connections =[["A[0]", "A[1]", "Z[3]", "vdd", "gnd"], connections =[["in[0]", "in[1]", "Z[3]", "vdd", "gnd"],
["B[0]", "A[1]", "Z[2]", "vdd", "gnd"], ["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"],
["A[0]", "B[1]", "Z[1]", "vdd", "gnd"], ["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"],
["B[0]", "B[1]", "Z[0]", "vdd", "gnd"]] ["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"]]
self.add_nand(connections) self.add_nand(connections)
self.route()
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"]
def get_nand_input_line_combination(self): 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 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 ): 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()) 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()) 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 result = a_t_b_delay + b_t_z_delay
@ -122,4 +57,4 @@ class hierarchical_predecode2x4(hierarchical_predecode):
return result return result
def input_load(self): def input_load(self):
return self.inv.input_load() return self.nand.input_load()

View File

@ -1,105 +1,69 @@
from tech import drc from tech import drc
import debug import debug
import design import design
from nand_3 import nand_3
from vector import vector from vector import vector
from hierarchical_predecode import hierarchical_predecode from hierarchical_predecode import hierarchical_predecode
class hierarchical_predecode3x8(hierarchical_predecode): class hierarchical_predecode3x8(hierarchical_predecode):
""" """
Pre 3x8 decoder used in hierarchical_decoder. Pre 3x8 decoder used in hierarchical_decoder.
""" """
def __init__(self, nmos_width, cellname): def __init__(self):
hierarchical_predecode.__init__(self, nmos_width, cellname, 3) hierarchical_predecode.__init__(self, 3)
self.add_pins() self.add_pins()
self.create_modules() self.create_modules()
self.setup_constrains() self.setup_constraints()
self.create_layout() 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() 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): def get_nand_input_line_combination(self):
combination = [[5, 6, 7], [5, 6, 10], """ These are the decoder connections of the NAND gates to the A,B,C pins """
[5, 9, 7], [5, 9, 10], combination = [["Abar[0]", "Abar[1]", "Abar[2]"],
[8, 6, 7], [8, 6, 10], ["Abar[0]", "Abar[1]", "A[2]"],
[8, 9, 7], [8, 9, 10]] ["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 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 ): def delay(self, slew, load = 0.0 ):
# A -> z # A -> Abar
b_t_z_delay = self.nand.delay(slew=slew, a_t_b_delay = self.inv.delay(slew=slew,load = self.nand.input_load())
load = self.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 # Z -> out
a_t_out_delay = self.inv.delay(slew=b_t_z_delay.slew, a_t_out_delay = self.inv.delay(slew=b_t_z_delay.slew,load = load)
load = load) result = result + a_t_out_delay
result = b_t_z_delay + a_t_out_delay
return result return result
def input_load(self): def input_load(self):
return self.nand.input_load() return self.nand.input_load()

View File

@ -6,6 +6,7 @@ from tech import drc, GDS
from tech import layer as techlayer from tech import layer as techlayer
import os import os
from vector import vector from vector import vector
from pin_layout import pin_layout
class layout: class layout:
""" """
@ -21,9 +22,9 @@ class layout:
self.name = name self.name = name
self.width = None self.width = None
self.height = None self.height = None
self.insts = [] # Holds module/cell layout instances self.insts = [] # Holds module/cell layout instances
self.objs = [] # Holds all other objects (labels, geometries, etc) 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.visited = False # Flag for traversing the hierarchy
self.gds_read() self.gds_read()
@ -38,6 +39,21 @@ class layout:
self.offset_attributes(coordinate) self.offset_attributes(coordinate)
self.translate(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): def find_lowest_coords(self):
@ -99,24 +115,46 @@ class layout:
for inst in self.insts: for inst in self.insts:
inst.offset = vector(inst.offset - coordinate) 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): def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0):
"""Adds an instance of a mod to this module""" """Adds an instance of a mod to this module"""
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
message = [] debug.info(4, "adding instance" + ",".join(x.name for x in self.insts))
for x in self.insts: return self.insts[-1]
message.append(x.name)
debug.info(4, "adding instance" + ",".join(message))
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): def add_rect(self, layer, offset, width, height):
"""Adds a rectangle on a given layer,offset with width and height""" """Adds a rectangle on a given layer,offset with width and height"""
# negative layers indicate "unused" layers in a given technology # negative layers indicate "unused" layers in a given technology
layerNumber = techlayer[layer] layerNumber = techlayer[layer]
if layerNumber >= 0: if layerNumber >= 0:
self.objs.append(geometry.rectangle(layerNumber, offset, width, height)) 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""" """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, self.add_rect(layer=layer,
offset=offset, offset=offset,
width=width, width=width,
@ -124,7 +162,12 @@ class layout:
self.add_label(text=text, self.add_label(text=text,
layer=layer, layer=layer,
offset=offset) 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): def add_label(self, text, layer, offset=[0,0],zoom=-1):
"""Adds a text label on the given layer,offset, and zoom level""" """Adds a text label on the given layer,offset, and zoom level"""
@ -132,6 +175,8 @@ class layout:
layerNumber = techlayer[layer] layerNumber = techlayer[layer]
if layerNumber >= 0: if layerNumber >= 0:
self.objs.append(geometry.label(text, layerNumber, offset, zoom)) self.objs.append(geometry.label(text, layerNumber, offset, zoom))
return self.objs[-1]
return None
def add_path(self, layer, coordinates, width=None): def add_path(self, layer, coordinates, width=None):

View File

@ -45,8 +45,8 @@ class spice:
def connect_inst(self, args, check=True): def connect_inst(self, args, check=True):
"""Connects the pins of the last instance added """Connects the pins of the last instance added
It is preferred to use the other function with the check to find if It is preferred to use the function with the check to find if
there is a problem. The check otion can be set to false there is a problem. The check option can be set to false
where we dynamically generate groups of connections after a where we dynamically generate groups of connections after a
group of modules are generated.""" group of modules are generated."""

View File

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

View File

@ -10,33 +10,19 @@ class ms_flop(design.design):
Memory address flip-flop Memory address flip-flop
""" """
pins = ["din", "dout", "dout_bar", "clk", "vdd", "gnd"] pin_names = ["din", "dout", "dout_bar", "clk", "vdd", "gnd"]
chars = utils.auto_measure_libcell(pins, "ms_flop", GDS["unit"], layer["boundary"]) (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):
def __init__(self, name="ms_flop"):
design.design.__init__(self, name) design.design.__init__(self, name)
self.width = ms_flop.chars["width"] self.width = ms_flop.width
self.height = ms_flop.chars["height"] self.height = ms_flop.height
self.pin_map = ms_flop.pin_map
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"]
def delay(self, slew, load = 0.0): def delay(self, slew, load = 0.0):
#import pinv # dont know how to calculate this now, use constant in tech file
# 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
from tech import spice from tech import spice
result = self.return_delay(spice["msflop_delay"], spice["msflop_slew"]) result = self.return_delay(spice["msflop_delay"], spice["msflop_slew"])
return result return result

View File

@ -12,47 +12,34 @@ class ms_flop_array(design.design):
hierdecoder hierdecoder
""" """
def __init__(self, name, columns, word_size): def __init__(self, columns, word_size, name=""):
self.columns = columns self.columns = columns
self.word_size = word_size self.word_size = word_size
if name=="":
name = "flop_array_c{0}_w{1}".format(columns,word_size)
design.design.__init__(self, name) 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)) c = reload(__import__(OPTS.config.ms_flop))
self.mod_ms_flop = getattr(c, 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() self.create_layout()
def create_layout(self): def create_layout(self):
self.add_modules()
self.setup_layout_constants()
self.add_pins() self.add_pins()
self.create_ms_flop_array() self.create_ms_flop_array()
self.add_labels() self.add_layout_pins()
self.DRC_LVS() 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): def add_pins(self):
for i in range(self.word_size): for i in range(self.word_size):
self.add_pin("din[{0}]".format(i)) self.add_pin("din[{0}]".format(i))
for i in range(self.word_size):
self.add_pin("dout[{0}]".format(i)) self.add_pin("dout[{0}]".format(i))
self.add_pin("dout_bar[{0}]".format(i)) self.add_pin("dout_bar[{0}]".format(i))
self.add_pin("clk") self.add_pin("clk")
@ -60,97 +47,83 @@ class ms_flop_array(design.design):
self.add_pin("gnd") self.add_pin("gnd")
def create_ms_flop_array(self): def create_ms_flop_array(self):
for i in range(self.word_size): self.ms_inst={}
name = "Xdff%d" % i for i in range(0,self.columns,self.words_per_row):
if (i % 2 == 0): name = "Xdff{0}".format(i)
x_off = i * self.ms_flop.width * self.words_per_row if (i % 2 == 0 or self.words_per_row>1):
mirror = "None" base = vector(i*self.ms.width,0)
mirror = "R0"
else: else:
if (self.words_per_row == 1): base = vector((i+1)*self.ms.width,0)
x_off = (i + 1) * self.ms_flop.width mirror = "MY"
mirror="MY" self.ms_inst[i]=self.add_inst(name=name,
else: mod=self.ms,
x_off = i * self.ms_flop.width * self.words_per_row offset=base,
self.add_inst(name=name, mirror=mirror)
mod=self.ms_flop, self.connect_inst(["din[{0}]".format(i/self.words_per_row),
offset=[x_off, 0], "dout[{0}]".format(i/self.words_per_row),
mirror=mirror) "dout_bar[{0}]".format(i/self.words_per_row),
self.connect_inst(["din[{0}]".format(i),
"dout[{0}]".format(i),
"dout_bar[{0}]".format(i),
"clk", "clk",
"vdd", "gnd"]) "vdd", "gnd"])
self.flop_positions.append(vector(x_off, 0))
def add_labels(self): def add_layout_pins(self):
for i in range(self.word_size):
for i in range(0,self.columns,self.words_per_row):
i_str = "[{0}]".format(i) 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"]) # Avoid duplicate rails by only doing even columns or last one
self.dout_positions.append(base + self.ms_flop_chars["dout"]) for gnd_pin in self.ms_inst[i].get_pins("gnd"):
self.dout_bar_positions.append(base + self.ms_flop_chars["dout_bar"]) if gnd_pin.layer!="metal2":
self.gnd_positions.append(base + self.ms_flop_chars["gnd"]) continue
else: if i%2==0 or i+self.words_per_row>=self.columns:
base = vector((i + 1) * self.ms_flop.width, 0) self.add_layout_pin(text="gnd",
gnd_offset = base + vector(self.ms_flop_chars["gnd"]).scale(-1,1) layer="metal2",
din_offset = base + vector(self.ms_flop_chars["din"]).scale(-1,1) offset=gnd_pin.ll(),
dout_offset = base + vector(self.ms_flop_chars["dout"]).scale(-1,1) width=gnd_pin.width(),
dout_bar_offset = base + vector(self.ms_flop_chars["dout_bar"]).scale(-1,1) height=gnd_pin.height())
self.add_label(text="gnd", din_pin = self.ms_inst[i].get_pin("din")
layer="metal2", self.add_layout_pin(text="din"+i_str,
offset=gnd_offset) layer="metal2",
self.add_label(text="din" + i_str, offset=din_pin.ll(),
layer="metal2", width=din_pin.width(),
offset=din_offset) height=din_pin.height())
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)
self.gnd_positions.append(gnd_offset) dout_pin = self.ms_inst[i].get_pin("dout")
self.din_positions.append(din_offset) self.add_layout_pin(text="dout"+i_str,
self.dout_positions.append(dout_offset) layer="metal2",
self.dout_bar_positions.append(dout_bar_offset) 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. # Continous "clk" rail along with label.
self.add_rect(layer="metal1", self.add_layout_pin(text="clk",
offset=[0, self.ms_flop_chars["clk"][1]], layer="metal1",
width=self.width, offset=self.ms_inst[0].get_pin("clk").ll().scale(0,1),
height=-drc["minwidth_metal1"]) width=self.width,
self.add_label(text="clk", height=drc["minwidth_metal1"])
layer="metal1",
offset=self.ms_flop_chars["clk"])
self.clk_positions.append(vector(self.ms_flop_chars["clk"]))
# Continous "Vdd" rail along with label. # Continous "Vdd" rail along with label.
self.add_rect(layer="metal1", for vdd_pin in self.ms_inst[i].get_pins("vdd"):
offset=[0, self.ms_flop_chars["vdd"][1] - 0.5 * drc["minwidth_metal1"]], if vdd_pin.layer!="metal1":
width=self.width, continue
height=drc["minwidth_metal1"]) self.add_layout_pin(text="vdd",
self.add_label(text="vdd", layer="metal1",
layer="metal1", offset=vdd_pin.ll().scale(0,1),
offset=vector(self.ms_flop_chars["vdd"]).scale(0, 1)) width=self.width,
self.vdd_positions.append(vector(self.ms_flop_chars["vdd"]).scale(0, 1)) height=drc["minwidth_metal1"])
def delay(self, slew, load=0.0): def delay(self, slew, load=0.0):
result = self.ms_flop.delay(slew = slew, result = self.ms.delay(slew = slew,
load = load) load = load)
return result return result

View File

@ -21,15 +21,17 @@ class nand_2(design.design):
unique_id = 1 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""" """Constructor : Creates a cell for a simple 2 input nand"""
name = "nand2_{0}".format(nand_2.unique_id) name = "nand2_{0}".format(nand_2.unique_id)
nand_2.unique_id += 1 nand_2.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(2, "create nand_2 structure {0} with size of {1}".format( debug.info(2, "create nand_2 structure {0} with size of {1}".format(name, nmos_width))
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.height = height
self.add_pins() self.add_pins()
@ -42,7 +44,6 @@ class nand_2(design.design):
def create_layout(self): def create_layout(self):
""" Layout """ """ Layout """
self.determine_sizes()
self.create_ptx() self.create_ptx()
self.setup_layout_constants() self.setup_layout_constants()
self.add_rails() self.add_rails()
@ -58,14 +59,6 @@ class nand_2(design.design):
self.route_pins() self.route_pins()
self.extend_wells() self.extend_wells()
self.extend_active() 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 # transistors are created here but not yet placed or added as a module
def create_ptx(self): def create_ptx(self):
@ -103,23 +96,19 @@ class nand_2(design.design):
rail_height = drc["minwidth_metal1"] rail_height = drc["minwidth_metal1"]
self.rail_height = rail_height self.rail_height = rail_height
# Relocate the origin # 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_rect(layer="metal1", self.add_layout_pin(text="gnd",
offset=self.gnd_position, layer="metal1",
width=rail_width, offset=self.gnd_loc,
height=rail_height) width=rail_width,
self.add_label(text="gnd", height=rail_height)
layer="metal1",
offset=self.gnd_position)
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_rect(layer="metal1", self.add_layout_pin(text="vdd",
offset=self.vdd_position, layer="metal1",
width=rail_width, offset=self.vdd_loc,
height=rail_height) width=rail_width,
self.add_label(text="vdd", height=rail_height)
layer="metal1",
offset=self.vdd_position)
def add_ptx(self): def add_ptx(self):
""" transistors are added and placed inside the layout """ """ transistors are added and placed inside the layout """
@ -297,7 +286,7 @@ class nand_2(design.design):
+ self.pmos2.active_height + self.pmos2.active_height
+ drc["metal1_to_metal1"] + drc["metal1_to_metal1"]
+ self.pmos2.active_contact.second_layer_width)) + 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 yoffset = (self.pmos_position1.y
+ self.pmos1.poly_positions[0].y + self.pmos1.poly_positions[0].y
+ drc["poly_extend_active"] + drc["poly_extend_active"]
@ -322,13 +311,11 @@ class nand_2(design.design):
- self.poly_contact.height) - self.poly_contact.height)
yoffset += self.poly_contact.via_layer_position.x yoffset += self.poly_contact.via_layer_position.x
offset = self.input_position1 = vector(0, yoffset) offset = self.input_position1 = vector(0, yoffset)
self.add_rect(layer="metal1", self.add_layout_pin(text="A",
offset=offset, layer="metal1",
width=input_length, offset=offset,
height=drc["minwidth_metal1"]) width=input_length,
self.add_label(text="A", height=drc["minwidth_metal1"])
layer="metal1",
offset=offset)
def route_input_gate_B(self): def route_input_gate_B(self):
""" routing for input B """ """ routing for input B """
@ -338,7 +325,7 @@ class nand_2(design.design):
+ drc["metal1_to_metal1"] + drc["metal1_to_metal1"]
+ self.nmos2.active_height + self.nmos2.active_height
+ drc["minwidth_metal1"]) + drc["minwidth_metal1"])
if (self.nmos_width == drc["minwidth_tx"]): if (self.nmos_size == drc["minwidth_tx"]):
yoffset = (self.nmos_position1.y yoffset = (self.nmos_position1.y
+ self.nmos1.poly_positions[0].y + self.nmos1.poly_positions[0].y
+ self.nmos1.poly_height + self.nmos1.poly_height
@ -350,11 +337,11 @@ class nand_2(design.design):
rotate=90) rotate=90)
input_length = self.pmos2.poly_positions[0].x - self.poly_contact.height input_length = self.pmos2.poly_positions[0].x - self.poly_contact.height
self.input_position2 = vector(xoffset - self.poly_contact.width, input_position2 = vector(xoffset - self.poly_contact.width,
yoffset + self.poly_contact.via_layer_position.x) yoffset + self.poly_contact.via_layer_position.x)
self.add_layout_pin(text="B", self.add_layout_pin(text="B",
layer="metal1", 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"]), width=(input_length + self.pmos_position2.x + drc["minwidth_poly"]),
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
@ -363,13 +350,13 @@ class nand_2(design.design):
yoffset = (self.nmos1.height - 2 * drc["minwidth_metal1"] / 3 + yoffset = (self.nmos1.height - 2 * drc["minwidth_metal1"] / 3 +
(self.height - self.pmos1.height - self.nmos1.height - drc["minwidth_metal1"]) / 2 ) (self.height - self.pmos1.height - self.nmos1.height - drc["minwidth_metal1"]) / 2 )
xoffset = self.drain_position.x xoffset = self.drain_position.x
offset = self.output_position = vector(xoffset, yoffset) offset = vector(xoffset, yoffset)
output_length = self.width - xoffset output_length = self.width - xoffset
self.add_layout_pin(text="Z", self.add_layout_pin(text="Z",
layer="metal1", layer="metal1",
offset=offset, offset=offset,
width=output_length, width=output_length,
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
def extend_wells(self): def extend_wells(self):
""" Extension of well """ """ Extension of well """
@ -435,13 +422,6 @@ class nand_2(design.design):
width=width, width=width,
height=self.nmos1.active_height) 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): def input_load(self):
from tech import spice from tech import spice

View File

@ -21,14 +21,17 @@ class nand_3(design.design):
unique_id = 1 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""" """Constructor : Creates a cell for a simple 3 input nand"""
name = "nand3_{0}".format(nand_3.unique_id) name = "nand3_{0}".format(nand_3.unique_id)
nand_3.unique_id += 1 nand_3.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(2, "create nand_3 structure {0} with size of {1}".format(name, nmos_width)) 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.height = height
self.add_pins() self.add_pins()
@ -41,7 +44,6 @@ class nand_3(design.design):
def create_layout(self): def create_layout(self):
""" create layout """ """ create layout """
self.determine_sizes()
self.create_ptx() self.create_ptx()
self.setup_layout_constants() self.setup_layout_constants()
self.add_rails() self.add_rails()
@ -58,48 +60,29 @@ class nand_3(design.design):
self.extend_active() self.extend_active()
self.connect_rails() self.connect_rails()
self.route_pins() 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): def create_ptx(self):
""" Create ptx but not yet placed""" """ Create ptx but not yet placed"""
self.nmos1 = ptx(width=self.nmos_size, self.nmos = ptx(width=self.nmos_size,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos") tx_type="nmos")
self.add_mod(self.nmos1) self.add_mod(self.nmos)
self.nmos2 = ptx(width=self.nmos_size, self.add_mod(self.nmos)
mults=self.tx_mults, self.add_mod(self.nmos)
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.pmos1 = ptx(width=self.pmos_size, self.pmos = ptx(width=self.pmos_size,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="pmos") tx_type="pmos")
self.add_mod(self.pmos1) self.add_mod(self.pmos)
self.pmos2 = ptx(width=self.pmos_size, self.add_mod(self.pmos)
mults=self.tx_mults, self.add_mod(self.pmos)
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)
def setup_layout_constants(self): def setup_layout_constants(self):
""" setup layout constraints """ """ setup layout constraints """
self.well_width = self.nmos1.active_position.x \ self.well_width = self.nmos.active_position.x \
+ 3 * self.pmos1.active_width + drc["active_to_body_active"] \ + 3 * self.pmos.active_width + drc["active_to_body_active"] \
+ drc["well_enclosure_active"] - self.nmos3.active_contact.width \ + drc["well_enclosure_active"] - self.nmos.active_contact.width \
+ self.pmos1.active_contact.height + drc["minwidth_metal1"] \ + self.pmos.active_contact.height + drc["minwidth_metal1"] \
+ (drc["metal1_to_metal1"] - drc["well_enclosure_active"]) + (drc["metal1_to_metal1"] - drc["well_enclosure_active"])
self.width = self.width = self.well_width self.width = self.width = self.well_width
@ -109,86 +92,86 @@ class nand_3(design.design):
self.rail_height = rail_height = drc["minwidth_metal1"] self.rail_height = rail_height = drc["minwidth_metal1"]
# Relocate the origin # 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", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=self.gnd_position, offset=self.gnd_loc,
width=rail_width, width=rail_width,
height=rail_height) 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", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=self.vdd_position, offset=self.vdd_loc,
width=rail_width, width=rail_width,
height=rail_height) height=rail_height)
def add_ptx(self): def add_ptx(self):
""" transistors are added and placed inside the layout """ """ transistors are added and placed inside the layout """
# determines the spacing between the edge and nmos (rail to active # determines the spacing between the edge and nmos (rail to active
# metal or poly_to_poly spacing) # metal or poly_to_poly spacing)
self.edge_to_nmos = max(drc["metal1_to_metal1"] 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"] 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 # 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["minwidth_metal1"]
+ (drc["metal1_to_metal1"] - drc["well_enclosure_active"])) + (drc["metal1_to_metal1"] - drc["well_enclosure_active"]))
# determine the position of the first transistor from the left # determine the position of the first transistor from the left
self.nmos_position1 = vector(xoffset, self.nmos_position1 = vector(xoffset,
0.5 * drc["minwidth_metal1"] + self.edge_to_nmos) 0.5 * drc["minwidth_metal1"] + self.edge_to_nmos)
offset = self.nmos_position1 + vector(0,self.nmos1.height) offset = self.nmos_position1 + vector(0,self.nmos.height)
self.add_inst(name="nmos1", self.add_inst(name="nmos",
mod=self.nmos1, mod=self.nmos,
offset=offset, offset=offset,
mirror="MX") mirror="MX")
self.connect_inst(["net2", "A", "gnd", "gnd"]) self.connect_inst(["net2", "A", "gnd", "gnd"])
self.nmos_position2 = (self.nmos_position1 self.nmos_position2 = (self.nmos_position1
+ vector(self.nmos2.active_width,0) + vector(self.nmos.active_width,0)
- vector(self.nmos2.active_contact.width,0)) - vector(self.nmos.active_contact.width,0))
offset = self.nmos_position2 + vector(0, self.nmos2.height) offset = self.nmos_position2 + vector(0, self.nmos.height)
self.add_inst(name="nmos2", self.add_inst(name="nmos2",
mod=self.nmos2, mod=self.nmos,
offset=offset, offset=offset,
mirror="MX") mirror="MX")
self.connect_inst(["net1", "B", "net2", "gnd"]) self.connect_inst(["net1", "B", "net2", "gnd"])
p2tp3 = vector(self.nmos3.active_width - self.nmos3.active_contact.width, p2tp3 = vector(self.nmos.active_width - self.nmos.active_contact.width,
self.nmos3.height) self.nmos.height)
self.nmos_position3 = self.nmos_position2 + p2tp3 self.nmos_position3 = self.nmos_position2 + p2tp3
self.add_inst(name="nmos3", self.add_inst(name="nmos3",
mod=self.nmos3, mod=self.nmos,
offset=self.nmos_position3, offset=self.nmos_position3,
mirror="MX") mirror="MX")
self.connect_inst(["Z", "C", "net1", "gnd"]) self.connect_inst(["Z", "C", "net1", "gnd"])
# determines the spacing between the edge and pmos # determines the spacing between the edge and pmos
self.edge_to_pmos = max(drc["metal1_to_metal1"] 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"] 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.pmos_position1 = vector(self.nmos_position1.x,
self.height - 0.5 * drc["minwidth_metal1"] 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", self.add_inst(name="pmos1",
mod=self.pmos1, mod=self.pmos,
offset=self.pmos_position1) offset=self.pmos_position1)
self.connect_inst(["Z", "A", "vdd", "vdd"]) self.connect_inst(["Z", "A", "vdd", "vdd"])
self.pmos_position2 = vector(self.nmos_position2.x, self.pmos_position1.y) self.pmos_position2 = vector(self.nmos_position2.x, self.pmos_position1.y)
self.add_inst(name="pmos2", self.add_inst(name="pmos2",
mod=self.pmos2, mod=self.pmos,
offset=self.pmos_position2) offset=self.pmos_position2)
self.connect_inst(["vdd", "B", "Z", "vdd"]) self.connect_inst(["vdd", "B", "Z", "vdd"])
self.pmos_position3 = vector(self.nmos_position3.x, self.pmos_position1.y) self.pmos_position3 = vector(self.nmos_position3.x, self.pmos_position1.y)
self.add_inst(name="pmos3", self.add_inst(name="pmos3",
mod=self.pmos3, mod=self.pmos,
offset=self.pmos_position3) offset=self.pmos_position3)
self.connect_inst(["Z", "C", "vdd", "vdd"]) self.connect_inst(["Z", "C", "vdd", "vdd"])
@ -196,20 +179,20 @@ class nand_3(design.design):
""" create well contacts """ """ create well contacts """
layer_stack = ("active", "contact", "metal1") layer_stack = ("active", "contact", "metal1")
xoffset = (self.nmos_position3.x + self.pmos1.active_position.x xoffset = (self.nmos_position3.x + self.pmos.active_position.x
+ self.pmos1.active_width + drc["active_to_body_active"]) + self.pmos.active_width + drc["active_to_body_active"])
yoffset = self.pmos_position1.y + self.pmos1.active_contact_positions[0].y yoffset = self.pmos_position1.y + self.pmos.active_contact_positions[0].y
self.nwell_contact_position = vector(xoffset, yoffset) 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 xoffset = self.nmos_position3.x + (self.nmos.active_position.x
+ self.nmos1.active_width + self.nmos.active_width
+ drc["active_to_body_active"]) + drc["active_to_body_active"])
yoffset = self.nmos_position1.y + (self.nmos1.height yoffset = self.nmos_position1.y + (self.nmos.height
- self.nmos1.active_contact_positions[0].y - self.nmos.active_contact_positions[0].y
- self.nmos1.active_contact.height) - self.nmos.active_contact.height)
self.pwell_contact_position = vector(xoffset, yoffset) 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): def connect_well_contacts(self):
""" Connect well contacts to vdd and gnd rail """ """ Connect well contacts to vdd and gnd rail """
@ -223,7 +206,7 @@ class nand_3(design.design):
width=drc["minwidth_metal1"], width=drc["minwidth_metal1"],
height=well_tap_length) height=well_tap_length)
well_tap_length = self.nmos1.active_height well_tap_length = self.nmos.active_height
offset = vector(self.pwell_contact_position offset = vector(self.pwell_contact_position
+ self.pwell_contact.second_layer_position + self.pwell_contact.second_layer_position
- self.pwell_contact.first_layer_position).scale(1,0) - self.pwell_contact.first_layer_position).scale(1,0)
@ -234,23 +217,23 @@ class nand_3(design.design):
def connect_rails(self): def connect_rails(self):
""" Connect transistor pmos drains to vdd and nmos drains to gnd rail """ """ 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) 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 temp_height = self.height - poffset.y
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=poffset, offset=poffset,
width=drc["minwidth_metal1"], width=drc["minwidth_metal1"],
height=temp_height) 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] poffset.y]
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=poffset, offset=poffset,
width=drc["minwidth_metal1"], width=drc["minwidth_metal1"],
height=temp_height) 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", self.add_rect(layer="metal1",
offset=poffset.scale(1,0), offset=poffset.scale(1,0),
width=drc["minwidth_metal1"], width=drc["minwidth_metal1"],
@ -263,57 +246,57 @@ class nand_3(design.design):
def connect_poly(self): def connect_poly(self):
""" Connect poly """ """ Connect poly """
yoffset_nmos1 = (self.nmos_position1.y + self.nmos1.poly_positions[0].y yoffset_nmos = (self.nmos_position1.y + self.nmos.poly_positions[0].y
+ self.nmos1.poly_height) + self.nmos.poly_height)
poly_length = (self.pmos_position1.y + self.pmos1.poly_positions[0].y poly_length = (self.pmos_position1.y + self.pmos.poly_positions[0].y
- yoffset_nmos1 + drc["minwidth_poly"]) - yoffset_nmos + drc["minwidth_poly"])
offset = vector(self.nmos_position1.x + self.nmos1.poly_positions[0].x, offset = vector(self.nmos_position1.x + self.nmos.poly_positions[0].x,
yoffset_nmos1 - drc["minwidth_poly"]) yoffset_nmos - drc["minwidth_poly"])
self.add_rect(layer="poly", self.add_rect(layer="poly",
offset=offset, offset=offset,
width=drc["minwidth_poly"], width=drc["minwidth_poly"],
height=poly_length) height=poly_length)
self.add_rect(layer="poly", 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], offset.y],
width=drc["minwidth_poly"], width=drc["minwidth_poly"],
height=poly_length) height=poly_length)
self.add_rect(layer="poly", 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], offset.y],
width=drc["minwidth_poly"], width=drc["minwidth_poly"],
height=poly_length) height=poly_length)
def connect_drains(self): def connect_drains(self):
""" Connect pmos and nmos drains. The output will be routed to this connection point. """ """ 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["well_enclosure_active"]
+ drc["metal1_to_metal1"]) + drc["metal1_to_metal1"])
drain_length = (self.height - yoffset + 0.5 * drc["minwidth_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") layer_stack = ("metal1", "via1", "metal2")
for position in self.pmos1.active_contact_positions[1:][::2]: for position in self.pmos.active_contact_positions[1:][::2]:
diff_active_via = self.pmos2.active_contact.width - self.m1m2_via.second_layer_width diff_active_via = self.pmos.active_contact.width - self.m1m2_via.second_layer_width
offset = (self.pmos_position2 + self.pmos2.active_contact_positions[0] offset = (self.pmos_position2 + self.pmos.active_contact_positions[0]
+ vector(diff_active_via / 2,0)) + vector(diff_active_via / 2,0))
self.add_via(layer_stack,offset) self.add_via(layer_stack,offset)
width = (2 * self.pmos3.active_width width = (2 * self.pmos.active_width
- self.pmos3.active_contact.width - self.pmos.active_contact.width
- (self.pmos2.active_contact.width - (self.pmos.active_contact.width
- self.m1m2_via.second_layer_width)) - self.m1m2_via.second_layer_width))
self.add_rect(layer="metal2", self.add_rect(layer="metal2",
offset=offset, offset=offset,
width=width, width=width,
height=self.m1m2_via.second_layer_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) + diff_active_via / 2)
self.add_via(layer_stack,[xoffset,offset.y]) self.add_via(layer_stack,[xoffset,offset.y])
xoffset = (self.nmos_position3.x + self.nmos3.active_position.x xoffset = (self.nmos_position3.x + self.nmos.active_position.x
+ self.nmos3.active_width - self.nmos3.active_contact.width / 2) + self.nmos.active_width - self.nmos.active_contact.width / 2)
self.drain_position = vector(xoffset, self.drain_position = vector(xoffset,
drc["minwidth_metal1"] + drc["metal1_to_metal1"]) drc["minwidth_metal1"] + drc["metal1_to_metal1"])
length = self.height - 2 * (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): def route_input_gate_A(self):
""" routing for input A """ """ routing for input A """
offset = (self.pmos_position1 offset = self.pmos_position1 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height)
+ self.pmos1.poly_positions[0] 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)
- vector(drc["minwidth_poly"] / 2, self.poly_contact.width))
self.add_contact(layers=("poly", "contact", "metal1"), self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset, offset=via_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 = vector(offset.x,
self.pmos_position1.y + self.pmos1.poly_positions[0].y)
self.add_layout_pin(text="A", self.add_layout_pin(text="A",
layer="metal1", layer="metal1",
offset=offset, offset=offset.scale(0,1),
width=-offset.x, width=offset.x,
height=-drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
self.A_position = vector(0, offset.y - drc["minwidth_metal1"])
def route_input_gate_B(self): def route_input_gate_B(self):
""" routing for input B """ """ routing for input B """
xoffset = self.pmos2.poly_positions[0].x \
+ self.pmos_position2.x - drc["minwidth_poly"] offset = self.pmos_position2 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height+1*(self.m1m2_via.width+drc["metal1_to_metal1"]))
yoffset = self.nmos_position1.y + self.nmos1.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)
- drc["well_enclosure_active"] + (self.nmos1.active_contact.height \
- self.nmos1.active_height) / 2 \
+ drc["metal1_to_metal1"]
self.add_contact(layers=("poly", "contact", "metal1"), self.add_contact(layers=("poly", "contact", "metal1"),
offset=[xoffset,yoffset]) offset=via_offset)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[xoffset,yoffset])
xoffset = self.pmos2.poly_positions[0].x + self.pmos_position2.x \ self.add_layout_pin(text="B",
- drc["minwidth_poly"] + self.m1m2_via.width layer="metal1",
length = -xoffset + self.m1m2_via.width offset=offset.scale(0,1),
self.add_rect(layer="metal2", width=offset.x,
offset=[xoffset, yoffset], height=drc["minwidth_metal1"])
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"])
def route_input_gate_C(self): def route_input_gate_C(self):
""" routing for input A """ """ routing for input C """
xoffset = self.pmos3.poly_positions[0].x \ offset = self.pmos_position3 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height+2*(self.m1m2_via.width+drc["metal1_to_metal1"]))
+ self.pmos_position3.x - drc["minwidth_poly"] 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)
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"]
self.add_contact(layers=("poly", "contact", "metal1"), self.add_contact(layers=("poly", "contact", "metal1"),
offset=[xoffset,yoffset]) offset=via_offset)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[xoffset,yoffset])
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_layout_pin(text="C",
self.add_rect(layer="metal2", layer="metal1",
offset=[xoffset - self.m1m2_via.width, offset=offset.scale(0,1),
yoffset], width=offset.x,
width=self.m1m2_via.width, height=drc["minwidth_metal1"])
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"])
def route_output(self): def route_output(self):
""" routing for output Z """ """ routing for output Z """
xoffset = self.nmos_position3.x + self.nmos3.active_position.x \ xoffset = self.nmos_position3.x + self.nmos.active_position.x \
+ self.nmos3.active_width - self.nmos3.active_contact.width / 2 + self.nmos.active_width - self.nmos.active_contact.width / 2
yoffset = (self.nmos1.height + (self.height - drc["minwidth_metal1"] yoffset = (self.nmos.height + (self.height - drc["minwidth_metal1"]
- self.pmos1.height - self.nmos1.height) / 2 - self.pmos.height - self.nmos.height) / 2
- (drc["minwidth_metal1"] / 2)) - (drc["minwidth_metal1"] / 2))
# FIXME Convert to add_layout_pin? self.add_layout_pin(text="Z",
self.add_rect(layer="metal1", layer="metal1",
offset=[xoffset, yoffset], offset=vector(xoffset, yoffset),
width=self.well_width - xoffset, width=self.well_width - xoffset,
height=drc["minwidth_metal1"]) 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): def extend_wells(self):
""" extension of well """ """ extension of well """
middle_point = self.nmos_position1.y + self.nmos1.pwell_position.y \ middle_point = self.nmos_position1.y + self.nmos.pwell_position.y \
+ self.nmos1.well_height + (self.pmos_position1.y + self.nmos.well_height + (self.pmos_position1.y
+ self.pmos1.nwell_position.y + self.pmos.nwell_position.y
- self.nmos_position1.y - self.nmos_position1.y
- self.nmos1.pwell_position.y - self.nmos.pwell_position.y
- self.nmos1.well_height) / 2 - self.nmos.well_height) / 2
offset = self.nwell_position = vector(0, middle_point) offset = self.nwell_position = vector(0, middle_point)
self.nwell_height = self.height - middle_point self.nwell_height = self.height - middle_point
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
@ -489,45 +404,37 @@ class nand_3(design.design):
def extend_active(self): def extend_active(self):
""" extension of active region """ """ extension of active region """
self.active_width = self.pmos1.active_width \ self.active_width = self.pmos.active_width \
+ drc["active_to_body_active"] + self.pmos1.active_contact.width + drc["active_to_body_active"] + self.pmos.active_contact.width
offset = (self.pmos1.active_position+self.pmos_position3.scale(1,0) offset = (self.pmos.active_position+self.pmos_position3.scale(1,0)
+ self.pmos_position1.scale(0,1)) + self.pmos_position1.scale(0,1))
self.add_rect(layer="active", self.add_rect(layer="active",
offset=offset, offset=offset,
width=self.active_width, width=self.active_width,
height=self.pmos1.active_height) height=self.pmos.active_height)
offset = offset + vector(self.pmos1.active_width,0) offset = offset + vector(self.pmos.active_width,0)
width = self.active_width - self.pmos1.active_width width = self.active_width - self.pmos.active_width
self.add_rect(layer="nimplant", self.add_rect(layer="nimplant",
offset=offset, offset=offset,
width=width, width=width,
height=self.pmos1.active_height) height=self.pmos.active_height)
offset = [self.nmos_position3.x + self.nmos1.active_position.x, offset = [self.nmos_position3.x + self.nmos.active_position.x,
self.nmos_position1.y + self.nmos1.height self.nmos_position1.y + self.nmos.height
- self.nmos1.active_position.y - self.nmos1.active_height] - self.nmos.active_position.y - self.nmos.active_height]
self.add_rect(layer="active", self.add_rect(layer="active",
offset=offset, offset=offset,
width=self.active_width, width=self.active_width,
height=self.nmos1.active_height) height=self.nmos.active_height)
offset = offset + vector(self.nmos1.active_width,0) offset = offset + vector(self.nmos.active_width,0)
width = self.active_width - self.nmos1.active_width width = self.active_width - self.nmos.active_width
self.add_rect(layer="pimplant", self.add_rect(layer="pimplant",
offset=offset, offset=offset,
width=width, 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): def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]

View File

@ -20,7 +20,7 @@ class nor_2(design.design):
unique_id = 1 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""" """Constructor : Creates a cell for a simple 2 input nor"""
name = "nor2_{0}".format(nor_2.unique_id) name = "nor2_{0}".format(nor_2.unique_id)
nor_2.unique_id += 1 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 # These aren't for instantiating, but we use them to get the dimensions
self.nwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"), 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"), 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.setup_layout_constants()
self.add_rails() self.add_rails()
@ -81,12 +81,12 @@ class nor_2(design.design):
0.5 * gate_to_gate - test_nmos.poly_positions[0].y) 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, 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) 0.5 * gate_to_gate - test_pmos.poly_positions[0].y)
route_margin = .5 * (self.poly_contact.second_layer_width route_margin = 0.5 * (self.poly_contact.second_layer_width
+ drc["minwidth_metal1"]) + drc["metal1_to_metal1"] + drc["minwidth_metal1"]) + drc["metal1_to_metal1"]
pmos_position = vector(0, self.height - 0.5 * drc["minwidth_metal1"] pmos_loc = vector(0, self.height - 0.5 * drc["minwidth_metal1"]
- edge_to_pmos - test_pmos.height) - edge_to_pmos - test_pmos.height)
nmos_position = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos) nmos_loc = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos)
leftspace = (0.7 * (pmos_position.y - nmos_position.y) leftspace = (0.7 * (pmos_loc.y - nmos_loc.y)
- 3 * route_margin - 2 * drc["metal1_to_metal1"]) - 3 * route_margin - 2 * drc["metal1_to_metal1"])
if leftspace >= 0: if leftspace >= 0:
break break
@ -126,28 +126,26 @@ class nor_2(design.design):
half_gate_to_gate - self.nmos1.poly_positions[0].y) half_gate_to_gate - self.nmos1.poly_positions[0].y)
# determine the position of the first transistor from the left # determine the position of the first transistor from the left
self.nmos_position1 = vector(0, self.nmos_loc1 = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos)
0.5 * drc["minwidth_metal1"] + edge_to_nmos) offset = self.nmos_loc1 + vector(0,self.nmos1.height)
offset = self.nmos_position1 + vector(0,self.nmos1.height)
x = vector(self.nmos2.active_width - self.nmos2.active_contact.width, 0) 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 # determines the spacing between the edge and pmos
edge_to_pmos = max(drc["metal1_to_metal1"] - self.pmos1.active_contact_positions[0].y, 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) half_gate_to_gate - self.pmos1.poly_positions[0].y)
self.pmos_position1 = vector(0, self.pmos_loc1 = vector(0, self.height - 0.5 * drc["minwidth_metal1"]
self.height - 0.5 * drc["minwidth_metal1"] - edge_to_pmos - self.pmos1.height)
- edge_to_pmos - self.pmos1.height) self.pmos_loc2 = self.pmos_loc1 + vector(self.pmos1.width,0)
self.pmos_position2 = self.pmos_position1 + vector(self.pmos1.width,0)
self.well_width = max(self.pmos_position2.x + self.pmos2.active_position.x self.well_width = max(self.pmos_loc2.x + self.pmos2.active_position.x
+ self.pmos2.active_width + self.pmos2.active_width
+ drc["active_to_body_active"] + self.nwell_contact.width + drc["active_to_body_active"] + self.nwell_contact.width
+ drc["well_enclosure_active"], + drc["well_enclosure_active"],
self.nmos_position2.x + self.nmos2.active_position.x self.nmos_loc2.x + self.nmos2.active_position.x
+ self.nmos2.active_width + self.nmos2.active_width
+ drc["active_to_body_active"] + drc["well_enclosure_active"]) + drc["active_to_body_active"] + drc["well_enclosure_active"])
self.width = self.well_width self.width = self.well_width
def add_rails(self): def add_rails(self):
@ -155,43 +153,43 @@ class nor_2(design.design):
rail_height = drc["minwidth_metal1"] rail_height = drc["minwidth_metal1"]
self.rail_height = rail_height self.rail_height = rail_height
# Relocate the origin # 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", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=self.gnd_position, offset=self.gnd_loc,
width=rail_width, width=rail_width,
height=rail_height) 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", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=self.vdd_position, offset=self.vdd_loc,
width=rail_width, width=rail_width,
height=rail_height) height=rail_height)
def add_ptx(self): def add_ptx(self):
""" transistors are placed in the layout""" """ 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", self.add_inst(name="nmos1",
mod=self.nmos1, mod=self.nmos1,
offset=offset, offset=offset,
mirror="MX") mirror="MX")
self.connect_inst(["Z", "A", "gnd", "gnd"]) 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", self.add_inst(name="nmos2",
mod=self.nmos2, mod=self.nmos2,
offset=offset, offset=offset,
mirror="MX") mirror="MX")
self.connect_inst(["Z", "B", "gnd", "gnd"]) self.connect_inst(["Z", "B", "gnd", "gnd"])
offset = self.pmos_position1 offset = self.pmos_loc1
self.add_inst(name="pmos1", self.add_inst(name="pmos1",
mod=self.pmos1, mod=self.pmos1,
offset=offset) offset=offset)
self.connect_inst(["vdd", "A", "net1", "vdd"]) self.connect_inst(["vdd", "A", "net1", "vdd"])
offset = self.pmos_position2 offset = self.pmos_loc2
self.add_inst(name="pmos2", self.add_inst(name="pmos2",
mod=self.pmos2, mod=self.pmos2,
offset=offset) offset=offset)
@ -200,48 +198,48 @@ class nor_2(design.design):
def add_well_contacts(self): def add_well_contacts(self):
layer_stack = ("active", "contact", "metal1") 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"]) + 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.pmos1.active_contact_positions[0].y)
self.nwell_contact_position = vector(xoffset, yoffset) self.nwell_contact_loc = 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_loc,(1,self.pmos1.num_of_tacts))
xoffset = self.nmos_position2.x + (self.nmos1.active_position.x xoffset = self.nmos_loc2.x + (self.nmos1.active_position.x
+ self.nmos1.active_width + self.nmos1.active_width
+ drc["active_to_body_active"]) + drc["active_to_body_active"])
yoffset = (self.nmos_position1.y + self.nmos1.height yoffset = (self.nmos_loc1.y + self.nmos1.height
- self.nmos1.active_contact_positions[0].y - self.nmos1.active_contact_positions[0].y
- self.nmos1.active_contact.height) - self.nmos1.active_contact.height)
self.pwell_contact_position = vector(xoffset, yoffset) self.pwell_contact_loc = 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_loc,(1,self.nmos1.num_of_tacts))
def route(self): def route(self):
self.route_pins() self.route_pins()
self.connect_well_contacts() self.connect_well_contacts()
M1_track = (self.B_position.y + drc["metal1_to_metal1"] M1_track = (self.B_loc.y + drc["metal1_to_metal1"]
+ .5 * (self.poly_contact.second_layer_width + .5 * (self.poly_contact.second_layer_width
+ drc["minwidth_metal1"])) + drc["minwidth_metal1"]))
self.connect_tx(M1_track) self.connect_tx(M1_track)
self.connect_poly() self.connect_poly()
def connect_well_contacts(self): def connect_well_contacts(self):
""" Connect well contacts to vdd and gnd rail """ """ Connect well contacts to vdd and gnd rail """
well_tap_length = self.height - self.nwell_contact_position.y well_tap_length = self.height - self.nwell_contact_loc.y
xoffset = (self.nwell_contact_position.x xoffset = (self.nwell_contact_loc.x
+ self.nwell_contact.second_layer_position.x + self.nwell_contact.second_layer_position.x
- self.nwell_contact.first_layer_position.x) - self.nwell_contact.first_layer_position.x)
offset = [xoffset, self.nwell_contact_position.y] offset = [xoffset, self.nwell_contact_loc.y]
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=offset, offset=offset,
width=drc["minwidth_metal1"], width=drc["minwidth_metal1"],
height=well_tap_length) height=well_tap_length)
offset = (self.pwell_contact_position.scale(1,0) offset = (self.pwell_contact_loc.scale(1,0)
+ self.pwell_contact.second_layer_position.scale(1,0) + self.pwell_contact.second_layer_position.scale(1,0)
- self.pwell_contact.first_layer_position.scale(1,0)) - self.pwell_contact.first_layer_position.scale(1,0))
well_tap_length = self.pwell_contact_position.y well_tap_length = self.pwell_contact_loc.y
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=offset, offset=offset,
width=drc["minwidth_metal1"], 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""" """Connect transistor pmos drains to vdd and nmos drains to gnd rail"""
# the first pmos drain to Vdd # the first pmos drain to Vdd
for i in range(len(self.pmos1.active_contact_positions)): 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: if i % 2 == 0:
correct = self.pmos1.active_contact.second_layer_position.scale(1,0) correct = self.pmos1.active_contact.second_layer_position.scale(1,0)
drain_posistion = contact_pos + correct 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", self.add_rect(layer="metal1",
offset=drain_posistion, offset=drain_posistion,
width=drc["minwidth_metal1"], width=drc["minwidth_metal1"],
@ -263,36 +261,36 @@ class nor_2(design.design):
else: else:
# source to pmos2 # source to pmos2
correct = (self.pmos1.active_contact.second_layer_position.scale(1,0) correct = (self.pmos1.active_contact.second_layer_position.scale(1,0)
+ vector(self.pmos1.active_contact.second_layer_width, + vector(self.pmos1.active_contact.second_layer_width,
0).scale(.5,0)) 0).scale(.5,0))
source_position = contact_pos + correct source_loc = contact_pos + correct
mid = [self.pmos_position2.x, M1_track] mid = [self.pmos_loc2.x, M1_track]
self.add_path("metal1", [source_position, mid]) self.add_path("metal1", [source_loc, mid])
# the second pmos # the second pmos
for i in range(len(self.pmos2.active_contact_positions)): for i in range(len(self.pmos2.active_contact_positions)):
if i % 2 == 0: if i % 2 == 0:
# source to pmos2 # 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) correct= (self.pmos2.active_contact.second_layer_position.scale(1,0)
+ vector(0.5 * self.pmos2.active_contact.second_layer_width,0)) + vector(0.5 * self.pmos2.active_contact.second_layer_width,0))
source_position = pmos_active + correct source_loc = pmos_active + correct
mid = [self.pmos_position2.x, M1_track] mid = [self.pmos_loc2.x, M1_track]
self.add_path("metal1", [source_position, mid]) self.add_path("metal1", [source_loc, mid])
# two nmos source to gnd # two nmos source to gnd
source_posistion1 = (self.nmos_position1 source_posistion1 = (self.nmos_loc1
+ self.nmos1.active_contact_positions[0] + self.nmos1.active_contact_positions[0]
+ self.nmos1.active_contact.second_layer_position.scale(1,0)) + self.nmos1.active_contact.second_layer_position.scale(1,0))
height = self.gnd_position.y - source_posistion1.y height = self.gnd_loc.y - source_posistion1.y
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=source_posistion1, offset=source_posistion1,
width=drc["minwidth_metal1"], width=drc["minwidth_metal1"],
height=height) height=height)
source_posistion2 = (self.nmos_position2 source_posistion2 = (self.nmos_loc2
+ self.nmos2.active_contact_positions[1] + self.nmos2.active_contact_positions[1]
+ self.nmos2.active_contact.second_layer_position.scale(1,0)) + self.nmos2.active_contact.second_layer_position.scale(1,0))
height = self.gnd_position.y - source_posistion2.y height = self.gnd_loc.y - source_posistion2.y
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=source_posistion2, offset=source_posistion2,
width=drc["minwidth_metal1"], width=drc["minwidth_metal1"],
@ -301,29 +299,29 @@ class nor_2(design.design):
def connect_poly(self): def connect_poly(self):
"""connect connect poly between nmos and pmos""" """connect connect poly between nmos and pmos"""
# connect pmos1 poly # connect pmos1 poly
nmos_gate = (self.nmos_position1 nmos_gate = (self.nmos_loc1
+ self.nmos1.poly_positions[0] + self.nmos1.poly_positions[0]
+ vector(0.5 * drc["minwidth_poly"], 0)) + vector(0.5 * drc["minwidth_poly"], 0))
for i in range(len(self.pmos1.poly_positions)): for i in range(len(self.pmos1.poly_positions)):
pmos_gate = (self.pmos_position1 pmos_gate = (self.pmos_loc1
+ self.pmos1.poly_positions[i] + self.pmos1.poly_positions[i]
+ vector(0.5 * drc["minwidth_poly"], 0)) + vector(0.5 * drc["minwidth_poly"], 0))
mid1 = [pmos_gate.x, pmos_gate.y - drc["poly_to_active"]] mid1 = [pmos_gate.x, pmos_gate.y - drc["poly_to_active"]]
self.add_path("poly", [nmos_gate, mid1, pmos_gate]) self.add_path("poly", [nmos_gate, mid1, pmos_gate])
# connect pmos2 poly # connect pmos2 poly
nmos_gate = vector(self.nmos_position2[0] nmos_gate = vector(self.nmos_loc2[0]
+ self.nmos2.poly_positions[0].x + self.nmos2.poly_positions[0].x
+ 0.5 * drc["minwidth_poly"], + 0.5 * drc["minwidth_poly"],
self.nmos_position1.y self.nmos_loc1.y
+ self.nmos1.poly_positions[0].y) + self.nmos1.poly_positions[0].y)
for i in range(len(self.pmos2.poly_positions)): for i in range(len(self.pmos2.poly_positions)):
pmos_gate = (self.pmos_position2 pmos_gate = (self.pmos_loc2
+ self.pmos2.poly_positions[i] + self.pmos2.poly_positions[i]
+ vector(0.5 * drc["minwidth_poly"], 0)) + vector(0.5 * drc["minwidth_poly"], 0))
mid1 = vector(pmos_gate.x, mid1 = vector(pmos_gate.x,
nmos_gate.y + self.nmos2.height nmos_gate.y + self.nmos2.height
+ drc["poly_to_active"]) + drc["poly_to_active"])
self.add_path("poly", [nmos_gate, mid1, pmos_gate]) self.add_path("poly", [nmos_gate, mid1, pmos_gate])
def route_pins(self): def route_pins(self):
@ -337,41 +335,39 @@ class nor_2(design.design):
def route_input_A(self): def route_input_A(self):
"""create input A layout""" """create input A layout"""
xoffset = self.nmos1.poly_positions[0].x xoffset = self.nmos1.poly_positions[0].x
yoffset = self.nmos_position1.y + self.nmos1.height \ yoffset = self.nmos_loc1.y + self.nmos1.height \
+ 0.3 * (self.pmos_position1.y - self.nmos_position1.y \ + 0.3 * (self.pmos_loc1.y - self.nmos_loc1.y \
- self.nmos1.height) - self.nmos1.height)
self.A_position = vector(xoffset, yoffset) self.A_loc = vector(xoffset, yoffset)
# gate input # 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"), self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset, offset=offset,
rotate=90) rotate=90)
# connect gate input to tx gate # connect gate input to tx gate
offset = self.A_position - vector(self.poly_contact.first_layer_position.y, offset = self.A_loc - vector(self.poly_contact.first_layer_position.y,
0.5 * self.poly_contact.width) 0.5 * self.poly_contact.width)
self.add_rect(layer="poly", self.add_rect(layer="poly",
offset=offset, offset=offset,
width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"], width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"],
height=self.poly_contact.first_layer_width) height=self.poly_contact.first_layer_width)
# extend the metal to the boundary of the cell # extend the metal to the boundary of the cell
input_length = self.A_position.x input_length = self.A_loc.x
offset = [0, self.A_position.y - 0.5 * drc["minwidth_metal1"]] offset = [0, self.A_loc.y - 0.5 * drc["minwidth_metal1"]]
self.add_layout_pin(text="A", self.add_layout_pin(text="A",
layer="metal1", layer="metal1",
offset=offset, offset=offset,
width=input_length, width=input_length,
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
def route_input_B(self): def route_input_B(self):
"""create input B layout """ """create input B layout """
xoffset = self.pmos2.poly_positions[0].x \ xoffset = self.pmos2.poly_positions[0].x + self.pmos_loc2.x
+ self.pmos_position2.x yoffset = self.A_loc.y + 0.5 * (self.poly_contact.second_layer_width \
yoffset = self.A_position.y \ + drc["minwidth_metal1"]) + drc["metal1_to_metal1"]
+ 0.5 * (self.poly_contact.second_layer_width \ self.B_loc = vector(xoffset, yoffset)
+ drc["minwidth_metal1"]) + drc["metal1_to_metal1"] offset = self.B_loc - vector(0, 0.5 * self.poly_contact.width)
self.B_position = vector(xoffset, yoffset)
offset = self.B_position - vector(0, 0.5 * self.poly_contact.width)
self.add_contact(layers=("poly", "contact", "metal1"), self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset, offset=offset,
rotate=90) rotate=90)
@ -381,49 +377,53 @@ class nor_2(design.design):
width=-(self.poly_contact.first_layer_position.y + drc["minwidth_poly"]), width=-(self.poly_contact.first_layer_position.y + drc["minwidth_poly"]),
height=self.poly_contact.first_layer_width) height=self.poly_contact.first_layer_width)
self.add_layout_pin(text="B", self.add_layout_pin(text="B",
layer="metal1", layer="metal1",
offset=[0, offset=[0, self.B_loc.y - 0.5 * drc["minwidth_metal1"]],
self.B_position.y - 0.5 * drc["minwidth_metal1"]], width=self.B_loc.x,
width=self.B_position.x, height=drc["minwidth_metal1"])
height=drc["minwidth_metal1"])
def route_output(self): def route_output(self):
"""route the output to nmos pmos """ """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 # route nmos drain to Z
nmos_contact = (self.nmos_position1 nmos_contact = (self.nmos_loc1
+ self.nmos1.active_contact_positions[1] + self.nmos1.active_contact_positions[1]
+ self.nmos1.active_contact.second_layer_position + self.nmos1.active_contact.second_layer_position
+ vector(self.nmos1.active_contact.second_layer_width, + vector(self.nmos1.active_contact.second_layer_width,0).scale(0.5, 0))
0).scale(0.5, 0)) mid = [nmos_contact.x, self.A_loc.y]
mid = [nmos_contact.x, self.A_position.y] self.add_path("metal1", [self.Z_loc, mid, nmos_contact])
self.add_path("metal1", [self.Z_position, mid, nmos_contact])
for i in range(len(self.pmos2.poly_positions) + 1): for i in range(len(self.pmos2.poly_positions) + 1):
if i % 2 == 1: if i % 2 == 1:
# pmos2 drain to Z # pmos2 drain to Z
pmos_contact = (self.pmos_position2 pmos_contact = (self.pmos_loc2
+ self.pmos1.active_contact_positions[i] + self.pmos1.active_contact_positions[i]
+ self.pmos2.active_contact.second_layer_position.scale(1, 0) + self.pmos2.active_contact.second_layer_position.scale(1, 0)
+ vector(self.pmos2.active_contact.second_layer_width, + vector(self.pmos2.active_contact.second_layer_width,0).scale(0.5, 0))
0).scale(0.5, 0))
offset = pmos_contact - vector(0.5 * self.m1m2_via.width, 0) offset = pmos_contact - vector(0.5 * self.m1m2_via.width, 0)
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset) 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.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): def extend_wells(self):
""" extend well for well contact""" """ extend well for well contact"""
middle_point = (self.nmos_position1.y middle_point = (self.nmos_loc1.y
+ self.nmos1.pwell_position.y + self.nmos1.pwell_position.y
+ self.nmos1.well_height + self.nmos1.well_height
+ (self.pmos_position1.y + (self.pmos_loc1.y
+ self.pmos1.nwell_position.y + self.pmos1.nwell_position.y
- self.nmos_position1.y - self.nmos_loc1.y
- self.nmos1.pwell_position.y - self.nmos1.pwell_position.y
- self.nmos1.well_height) / 2 ) - self.nmos1.well_height) / 2 )
self.nwell_position = vector(0, middle_point) self.nwell_position = vector(0, middle_point)
self.nwell_height = self.height - middle_point self.nwell_height = self.height - middle_point
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
@ -449,11 +449,11 @@ class nor_2(design.design):
def extend_active(self): def extend_active(self):
""" extend active for well contact""" """ extend active for well contact"""
self.active_width = self.pmos1.active_width \ self.active_width = self.pmos1.active_width \
+ drc["active_to_body_active"] \ + drc["active_to_body_active"] \
+ self.pmos1.active_contact.width + self.pmos1.active_contact.width
offset = (self.pmos_position2.scale(1,0) offset = (self.pmos_loc2.scale(1,0)
+ self.pmos_position1.scale(0,1) + self.pmos_loc1.scale(0,1)
+ self.pmos1.active_position) + self.pmos1.active_position)
self.add_rect(layer="active", self.add_rect(layer="active",
offset=offset, offset=offset,
width=self.active_width, width=self.active_width,
@ -466,9 +466,9 @@ class nor_2(design.design):
height=self.pmos1.active_height) height=self.pmos1.active_height)
offset = (self.nmos1.active_position.scale(1,-1) offset = (self.nmos1.active_position.scale(1,-1)
+ self.nmos_position2.scale(1,0) + self.nmos_loc2.scale(1,0)
+ self.nmos_position1.scale(0,1) + self.nmos_loc1.scale(0,1)
+ vector(0, self.nmos1.height - self.nmos1.active_height)) + vector(0, self.nmos1.height - self.nmos1.active_height))
self.add_rect(layer="active", self.add_rect(layer="active",
offset=offset, offset=offset,
width=self.active_width, width=self.active_width,

View File

@ -2,6 +2,7 @@ from tech import drc
from tech import layer as techlayer from tech import layer as techlayer
import debug import debug
from vector import vector from vector import vector
from utils import snap_to_grid
class path(): class path():
""" """
@ -34,7 +35,7 @@ class path():
def create_rectilinear_route(self): def create_rectilinear_route(self):
""" Add intermediate nodes if it isn't rectilinear. Also skip """ Add intermediate nodes if it isn't rectilinear. Also skip
repeated nodes. Also, convert to vector if the aren't.""" 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 = [] self.position_list = []
for index in range(len(pl) - 1): for index in range(len(pl) - 1):
@ -74,7 +75,6 @@ class path():
using the position list of the corners. """ using the position list of the corners. """
pl = self.position_list # position list pl = self.position_list # position list
for index in range(len(pl) - 1): for index in range(len(pl) - 1):
# if we have x motion # if we have x motion
if pl[index][0] != pl[index + 1][0]: if pl[index][0] != pl[index + 1][0]:
line_length = pl[index + 1][0] - pl[index][0] line_length = pl[index + 1][0] - pl[index][0]

137
compiler/pin_layout.py Normal file
View File

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

View File

@ -18,7 +18,7 @@ class pinv(design.design):
unique_id = 1 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""" """Constructor : Creates a cell for a simple inverter"""
name = "pinv{0}".format(pinv.unique_id) name = "pinv{0}".format(pinv.unique_id)
pinv.unique_id += 1 pinv.unique_id += 1
@ -64,7 +64,6 @@ class pinv(design.design):
self.connect_rails() self.connect_rails()
self.connect_tx() self.connect_tx()
self.route_pins() self.route_pins()
self.setup_layout_offsets()
def determine_tx_mults(self): def determine_tx_mults(self):
"""Determines the number of fingers needed to achieve same size with a height constraint""" """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? # this should be 2*poly extension beyond active?
minwidth_box_poly = 2 * drc["minwidth_poly"] \ minwidth_box_poly = 2 * drc["minwidth_poly"] \
+ drc["poly_to_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_poly_contact,
minwidth_box_poly) 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.gnd_position = vector(0, - 0.5 * drc["minwidth_metal1"]) # for tiling purposes
self.add_layout_pin(text="gnd", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=self.gnd_position, offset=self.gnd_position,
width=rail_width, width=rail_width,
height=rail_height) height=rail_height)
self.vdd_position = vector(0, self.height - 0.5 * drc["minwidth_metal1"]) self.vdd_position = vector(0, self.height - 0.5 * drc["minwidth_metal1"])
self.add_layout_pin(text="vdd", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=self.vdd_position, offset=self.vdd_position,
width=rail_width, width=rail_width,
height=rail_height) height=rail_height)
def add_ptx(self): def add_ptx(self):
"""Adds pmos and nmos to the layout""" """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"], width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"],
height=self.poly_contact.first_layer_width) height=self.poly_contact.first_layer_width)
input_length = self.pmos.poly_positions[0].x \ input_length = self.pmos.poly_positions[0].x - self.poly_contact.height
- self.poly_contact.height
# Determine the y-coordinate for the placement of the metal1 via # 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.nmos.height - self.pmos.height))
self.add_layout_pin(text="A", self.add_layout_pin(text="A",
layer="metal1", layer="metal1",
@ -313,16 +311,18 @@ class pinv(design.design):
self.input_position.y) self.input_position.y)
output_length = self.width - offset.x output_length = self.width - offset.x
if self.route_output == True: if self.route_output == True:
self.output_position = offset + vector(output_length,0) # This extends the output to the edge of the cell
self.add_rect(layer="metal1", self.add_layout_pin(text="Z",
offset=offset, layer="metal1",
width=output_length, offset=offset,
height=drc["minwidth_metal1"]) width=output_length,
height=drc["minwidth_metal1"])
else: else:
self.output_position = offset # This leaves the output as an internal pin (min sized)
self.add_label(text="Z", self.add_layout_pin(text="Z",
layer="metal1", layer="metal1",
offset=offset) offset=offset)
def add_well_contacts(self): def add_well_contacts(self):
"""Adds n/p well taps to the layout""" """Adds n/p well taps to the layout"""
@ -403,10 +403,6 @@ class pinv(design.design):
self.route_input_gate() self.route_input_gate()
self.route_output_drain() 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): def input_load(self):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"] return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]

View File

@ -18,10 +18,11 @@ class precharge(design.design):
c = reload(__import__(OPTS.config.bitcell)) c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, 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.ptx_width = ptx_width
self.beta = beta self.beta = beta
self.width = self.bitcell.width
self.add_pins() self.add_pins()
self.create_layout() self.create_layout()
@ -33,7 +34,6 @@ class precharge(design.design):
def create_layout(self): def create_layout(self):
self.create_ptx() self.create_ptx()
self.create_contacts() self.create_contacts()
self.setup_layout_constants()
self.add_ptx() self.add_ptx()
self.connect_poly() self.connect_poly()
self.add_pclk() self.add_pclk()
@ -46,11 +46,9 @@ class precharge(design.design):
def create_ptx(self): def create_ptx(self):
"""Initializes the upper and lower pmos""" """Initializes the upper and lower pmos"""
self.lower_pmos = ptx(width=self.ptx_width, self.lower_pmos = ptx(width=self.ptx_width,
mults=1,
tx_type="pmos") tx_type="pmos")
self.add_mod(self.lower_pmos) self.add_mod(self.lower_pmos)
self.upper_pmos = ptx(width=self.beta * self.ptx_width, self.upper_pmos = ptx(width=self.beta * self.ptx_width,
mults=1,
tx_type="pmos") tx_type="pmos")
self.upper_pmos = self.upper_pmos self.upper_pmos = self.upper_pmos
self.add_mod(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"), self.lower_contact = contact(layer_stack=("metal1", "via1", "metal2"),
dimensions=self.lower_dimensions) 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): def add_ptx(self):
"""Adds both the upper_pmos and lower_pmos to the module""" """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 offset.y= offset.y + self.poly_contact.second_layer_position.x
self.pclk_position = vector(0, offset.y) self.pclk_position = vector(0, offset.y)
self.add_layout_pin(text="clk", self.add_layout_pin(text="clk",
layer="metal1", layer="metal1",
offset=self.pclk_position, offset=self.pclk_position,
width=self.width, width=self.width,
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
def add_nwell_contact(self): def add_nwell_contact(self):
"""Adds a nwell tap to connect to the vdd rail""" """Adds a nwell tap to connect to the vdd rail"""
@ -182,11 +176,11 @@ class precharge(design.design):
def add_vdd_rail(self): def add_vdd_rail(self):
"""Adds a vdd rail at the top of the cell""" """Adds a vdd rail at the top of the cell"""
# adds the rail across the width of the cell # adds the rail across the width of the cell
self.vdd_position = vector(self.pclk_position.x, vdd_position = vector(self.pclk_position.x,
self.height - drc["minwidth_metal1"]) self.height - drc["minwidth_metal1"])
self.add_layout_pin(text="vdd", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=self.vdd_position, offset=vdd_position,
width=self.width, width=self.width,
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
@ -206,16 +200,16 @@ class precharge(design.design):
def add_bitlines(self): def add_bitlines(self):
"""Adds both bit-line and bit-line-bar to the module""" """Adds both bit-line and bit-line-bar to the module"""
# adds the BL on metal 2 # adds the BL on metal 2
offset = self.BL_position - vector(0.5 * drc["minwidth_metal2"],0) offset = vector(self.bitcell.get_pin("BL").cx(),0) - vector(0.5 * drc["minwidth_metal2"],0)
self.add_layout_pin(text="bl", self.add_layout_pin(text="BL",
layer="metal2", layer="metal2",
offset=offset, offset=offset,
width=drc['minwidth_metal2'], width=drc['minwidth_metal2'],
height=self.height) height=self.height)
# adds the BR on metal 2 # adds the BR on metal 2
offset = self.BR_position - vector(0.5 * drc["minwidth_metal2"],0) offset = vector(self.bitcell.get_pin("BR").cx(),0) - vector(0.5 * drc["minwidth_metal2"],0)
self.add_layout_pin(text="br", self.add_layout_pin(text="BR",
layer="metal2", layer="metal2",
offset=offset, offset=offset,
width=drc['minwidth_metal2'], width=drc['minwidth_metal2'],
@ -277,8 +271,8 @@ class precharge(design.design):
"""Connects bit-lines to lower_pmos""" """Connects bit-lines to lower_pmos"""
mos,mos_pos,contact = dest mos,mos_pos,contact = dest
mos_active = (mos_pos + mos.active_contact_positions[0]) mos_active = (mos_pos + mos.active_contact_positions[0])
offset = vector(self.BL_position.x, mos_active.y) offset = vector(self.bitcell.get_pin("BL").cx() , mos_active.y)
xlength = (mos_active.x + correct_x - self.BL_position.x xlength = (mos_active.x + correct_x - offset.x
+ 0.5 * drc["minwidth_metal2"]) + 0.5 * drc["minwidth_metal2"])
self.add_rect(layer="metal2", self.add_rect(layer="metal2",
offset=offset, offset=offset,
@ -290,7 +284,7 @@ class precharge(design.design):
mos,mos_pos,contact = dest mos,mos_pos,contact = dest
offset = mos_pos + vector(correct_x, offset = mos_pos + vector(correct_x,
mos.active_contact_positions[0].y) 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", self.add_rect(layer="metal2",
offset=offset, offset=offset,
width=xlength, width=xlength,

View File

@ -11,14 +11,22 @@ class precharge_array(design.design):
of bit line columns, height is the height of the bit-cell array. of bit line columns, height is the height of the bit-cell array.
""" """
def __init__(self, name, columns, ptx_width, beta=2): def __init__(self, columns, ptx_width, beta=2):
design.design.__init__(self, name) design.design.__init__(self, "precharge_array")
debug.info(1, "Creating {0}".format(name)) debug.info(1, "Creating {0}".format(self.name))
self.columns = columns self.columns = columns
self.ptx_width = ptx_width self.ptx_width = ptx_width
self.beta = beta 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.add_pins()
self.create_layout() self.create_layout()
self.DRC_LVS() self.DRC_LVS()
@ -32,49 +40,23 @@ class precharge_array(design.design):
self.add_pin("vdd") self.add_pin("vdd")
def create_layout(self): def create_layout(self):
self.create_pc_cell() self.add_insts()
self.setup_layout_constants()
self.add_pc()
self.add_rails()
self.offset_all_coordinates()
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", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=offset, offset=self.pc_cell.get_pin("vdd").ll(),
width=self.width, width=self.width,
height=drc["minwidth_metal1"]) 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", self.add_layout_pin(text="clk",
layer="metal1", layer="metal1",
offset=self.pclk_position, offset=self.pc_cell.get_pin("clk").ll(),
width=self.width, width=self.width,
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
#self.offset_all_coordinates()
def create_pc_cell(self): def add_insts(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):
"""Creates a precharge array by horizontally tiling the precharge cell""" """Creates a precharge array by horizontally tiling the precharge cell"""
self.pc_cell_positions = [] self.pc_cell_positions = []
for i in range(self.columns): for i in range(self.columns):
@ -84,14 +66,12 @@ class precharge_array(design.design):
self.add_inst(name=name, self.add_inst(name=name,
mod=self.pc_cell, mod=self.pc_cell,
offset=offset) offset=offset)
self.add_label(text="bl[{0}]".format(i), self.add_layout_pin(text="bl[{0}]".format(i),
layer="metal2", layer="metal2",
offset=offset+ self.pc_cell.BL_position.scale(1,0)) offset=offset+ self.pc_cell.get_pin("BL").ll().scale(1,0))
self.add_label(text="br[{0}]".format(i), self.add_layout_pin(text="br[{0}]".format(i),
layer="metal2", layer="metal2",
offset=offset+ self.pc_cell.BR_position.scale(1,0)) offset=offset+ self.pc_cell.get_pin("BR").ll().scale(1,0))
self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i),
"clk", "vdd"]) "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))

View File

@ -8,9 +8,10 @@ import re
class ptx(design.design): class ptx(design.design):
""" """
This module generates gds and spice of a parametrically NMOS or PMOS sized transistor. 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) name = "{0}_m{1}_w{2}".format(tx_type, mults, width)
# remove periods for newer spice compatibility # remove periods for newer spice compatibility
name=re.sub('\.','_',name) name=re.sub('\.','_',name)
@ -243,8 +244,7 @@ class ptx(design.design):
# left_most contact column # left_most contact column
contact_xoffset = 0 contact_xoffset = 0
contact_yoffset = (self.active_height \ contact_yoffset = (self.active_height - self.active_contact.height) / 2
- self.active_contact.height) / 2
offset = vector(contact_xoffset, contact_yoffset) offset = vector(contact_xoffset, contact_yoffset)
self.add_contact(layers=("active", "contact", "metal1"), self.add_contact(layers=("active", "contact", "metal1"),
offset=offset, offset=offset,

View File

@ -10,13 +10,14 @@ class replica_bitcell(design.design):
is a hand-made cell, so the layout and netlist should be available in is a hand-made cell, so the layout and netlist should be available in
the technology library. """ the technology library. """
pins = ["BL", "BR", "WL", "vdd", "gnd"] pin_names = ["BL", "BR", "WL", "vdd", "gnd"]
chars = utils.auto_measure_libcell(pins, "replica_cell_6t", GDS["unit"], layer["boundary"]) (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"): def __init__(self):
design.design.__init__(self, name) design.design.__init__(self, "replica_cell_6t")
debug.info(2, "Create bitcell object") debug.info(2, "Create replica bitcell object")
self.width = replica_bitcell.width
self.width = replica_bitcell.chars["width"] self.height = replica_bitcell.height
self.height = replica_bitcell.chars["height"] self.pin_map = replica_bitcell.pin_map

View File

@ -16,8 +16,8 @@ class replica_bitline(design.design):
Used for memory timing control Used for memory timing control
""" """
def __init__(self, name, rows): def __init__(self, rows, name="replica_bitline"):
design.design.__init__(self, "replica_bitline") design.design.__init__(self, name)
g = reload(__import__(OPTS.config.delay_chain)) g = reload(__import__(OPTS.config.delay_chain))
self.mod_delay_chain = getattr(g, 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)) c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, 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"]: for pin in ["en", "out", "vdd", "gnd"]:
self.add_pin(pin) self.add_pin(pin)
self.rows = rows self.rows = rows
self.create_modules() self.create_modules()
self.cal_modules_offset() self.calculate_module_offsets()
self.add_modules() self.add_modules()
self.route() self.route()
self.offset_all_coordinates() self.add_layout_pins()
self.DRC_LVS() self.DRC_LVS()
def cal_modules_offset(self): def calculate_module_offsets(self):
pinv_error_offset = 0.025 """ Calculate all the module offsets """
# 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)
# These aren't for instantiating, but we use them to get the dimensions # 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 = 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.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3")) self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
self.nor = nor_2(nmos_width=drc["minwidth_tx"]) # M1/M2 routing pitch is based on contacted pitch
self.add_mod(self.nor) 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"], # delay chain will be rotated 90, so move it over a width
mults=1, # we move it up a inv height just for some routing room
tx_type="pmos") 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) self.add_mod(self.access_tx)
def add_modules(self): def add_modules(self):
"""add mod instance in layout """ """ Add all of the module instances in the logical netlist """
self.add_inst(name="BL_inv", # This is the threshold detect inverter on the output of the RBL
mod=self.inv, self.rbl_inv_inst=self.add_inst(name="rbl_inv",
offset=self.BL_inv_offset) 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.connect_inst(["bl[0]", "out", "vdd", "gnd"])
self.add_inst(name="BL_access_tx", self.tx_inst=self.add_inst(name="rbl_access_tx",
mod=self.access_tx, mod=self.access_tx,
offset=self.access_tx_offset, offset=self.access_tx_offset,
rotate=90) rotate=90)
# D, G, S, B # D, G, S, B
self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"]) self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"])
# add the well and poly contact
self.add_inst(name="delay_chain", self.dc_inst=self.add_inst(name="delay_chain",
mod=self.delay_chain, mod=self.delay_chain,
offset=self.delay_chain_offset, offset=self.delay_chain_offset,
rotate=90) rotate=90)
self.connect_inst(["en", "delayed_en", "vdd", "gnd"]) self.connect_inst(["en", "delayed_en", "vdd", "gnd"])
self.add_inst(name="bitcell", self.rbc_inst=self.add_inst(name="bitcell",
mod=self.replica_bitcell, mod=self.replica_bitcell,
offset=self.replica_bitline_offset, offset=self.bitcell_offset,
mirror="MX") mirror="MX")
self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"]) self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"])
self.add_loads() self.rbl_inst=self.add_inst(name="load",
self.expan_the_well_to_BL_inv() 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): def route(self):
"""connect modules together""" """ Connect all the signals 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()
self.route_gnd() self.route_gnd()
# route loads after gnd and vdd created self.route_vdd()
self.route_loads(vdd_offset) self.route_access_tx()
self.route_RC(vdd_offset)
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", def route_access_tx(self):
layer="metal1", # GATE ROUTE
offset=self.en_input_offset) # 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): # finds the lower right of the poly gate
# BL_inv input to M3 poly_offset = self.access_tx_offset + self.access_tx.poly_positions[0].rotate_scale(-1,1)
mid1 = BL_inv_in - vector(0, # This centers the contact on the poly
drc["metal2_to_metal2"] + self.m1m2_via.width) contact_offset = poly_offset.scale(0,1) + self.dc_inst.get_pin("out").ll().scale(1,0) \
mid2 = vector(self.en_nor_offset.x + 3 * drc["metal1_to_metal1"], + vector(-drc["poly_extend_contact"], -0.5*self.poly_contact.height + 0.5*drc["minwidth_poly"])
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)
self.add_contact(layers=("poly", "contact", "metal1"), self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset) offset=contact_offset)
# route gate to delay_chain output self.add_rect(layer="poly",
gate_offset = offset + vector(0.5 * drc["minwidth_metal1"], offset=poly_offset,
0.5 * self.poly_contact.width) width=contact_offset.x-poly_offset.x,
self.route_access_tx_t_delay_chain(gate_offset, delay_chain_output) height=drc["minwidth_poly"])
self.route_access_tx_t_WL(gate_offset) 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): # 2. Route delay chain output to access tx gate
m2rail_space = (drc["minwidth_metal2"] + drc["metal2_to_metal2"]) delay_en_offset = self.dc_inst.get_pin("out").bc()
mid1 = vector(offset.x, self.delay_chain_offset.y - 3 * m2rail_space) delay_en_end_offset = contact_offset + vector(self.poly_contact.width,self.poly_contact.height).scale(0.5,0.5)
mid2 = [delay_chain_output.x, mid1.y] self.add_path("metal1", [delay_en_offset,delay_en_end_offset])
# 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")
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) # 3. Route the mid-point of previous route to the bitcell WL
width = RC_WL.x - offset.x # route bend of previous net to bitcell WL
# enter the bit line array with metal1 wl_offset = self.rbc_inst.get_pin("WL").lc()
via_offset = [mid4.x - 0.5 * self.m1m2_via.width, wl_mid = vector(delay_en_end_offset.x,wl_offset.y)
offset.y self.add_path("metal1", [delay_en_end_offset, wl_mid, wl_offset])
- 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"])
def route_tx_drain(self,vdd_offset): # SOURCE ROUTE
# route drain to Vdd # Route the source to the vdd rail
active_offset = self.access_tx.active_contact_positions[1].rotate_scale(-1,1) source_offset = self.access_tx_offset + self.access_tx.active_contact_positions[1].rotate_scale(-1,1)\
correct = vector(-0.5 * drc["minwidth_metal1"], + vector(self.active_contact.width,self.active_contact.height).rotate_scale(-0.5,0.5)
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])
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): 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", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=vdd_offset, offset=vdd_start,
width=self.width, 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"]) 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) # Add a second vdd pin. No need for full length. It is must connect at the next level.
m1rail_space = (drc["minwidth_metal1"] + drc["metal1_to_metal1"]) inv_vdd_offset = self.rbl_inv_inst.get_pin("vdd").ll()
mid1 = start - vector(0, m1rail_space) self.add_layout_pin(text="vdd",
mid2 = vector(self.delay_chain_offset.x + 9 * drc["minwidth_metal2"], layer="metal1",
mid1.y) offset=inv_vdd_offset.scale(1,0),
end = [mid2.x, vdd_offset.y] width=drc["minwidth_metal1"],
self.add_path(layer=("metal1"), height=self.delay_chain_offset.y)
coordinates=[start, mid1, mid2])
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=[mid1, mid2, end])
def route_gnd(self): def route_gnd(self):
"""route gnd of delay chain, en_nor, en_inv and BL_inv""" """ Route all signals connected to gnd """
# route delay chain gnd to BL_inv gnd
# gnd Node between BL_inv access tx and delay chain, and is below # Add a rail in M1 from bottom to two along delay chain
# en_input gnd_start = self.rbl_inv_inst.get_pin("gnd").ll() - self.offset_fix
self.gnd_position = self.delay_chain_offset
BL_gnd_offset = self.BL_inv_offset # It is the height of the entire RBL and bitcell
mid1 = vector(0, self.BL_inv_offset.y) self.add_rect(layer="metal2",
rail2_space = drc["minwidth_metal2"] + drc["metal2_to_metal2"] offset=gnd_start,
y_off = self.gnd_position.y + self.delay_chain.width + rail2_space width=drc["minwidth_metal2"],
mid2 = vector(mid1.x, y_off) height=self.rbl.height+self.bitcell.height+self.inv.width)
share_gnd = vector(self.gnd_position.x, mid2.y) self.add_layout_pin(text="gnd",
# Note the inverted stacks layer="metal1",
lst = [BL_gnd_offset, mid1, mid2, share_gnd, self.gnd_position] offset=gnd_start.scale(1,0),
self.add_wire(layers=("metal1", "via1", "metal2"), width=drc["minwidth_metal2"],
coordinates=lst) height=2*self.inv.width)
self.add_label(text="gnd",
layer="metal1", # Connect the WL pins directly to gnd
offset=self.gnd_position) for row in range(self.rows):
# connect to the metal1 gnd of delay chain wl = "wl[{}]".format(row)
offset = mid2 - vector(0.5 * drc["minwidth_metal1"], 0) pin = self.rbl_inst.get_pin(wl)
self.add_rect(layer="metal1", offset = vector(gnd_start.x,pin.by())
offset=offset, self.add_rect(layer="metal1",
width=drc["minwidth_metal1"], offset=offset,
height=-self.delay_chain.width) width=self.rbl_offset.x-gnd_start.x,
offset = [offset.x + self.delay_chain.height, height=drc["minwidth_metal1"])
mid2.y] self.add_via(layers=("metal1", "via1", "metal2"),
self.add_rect(layer="metal1", offset=offset)
offset=offset,
width=drc["minwidth_metal1"],
height=-self.delay_chain.width)
def route_loads(self,vdd_offset): # Add via for the delay chain
"""connect all the loads word line to gnd""" offset = self.delay_chain_offset - vector(0.5*drc["minwidth_metal1"],0) - self.offset_fix
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=vdd_offset, offset=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])
def route_RC(self,vdd_offset): # Add via for the inverter
"""route vdd gnd to the replica cell """ offset = self.rbl_inv_offset - vector(0.5*drc["minwidth_metal1"],self.m1m2_via.height) - self.offset_fix
# connect vdd self.add_via(layers=("metal1", "via1", "metal2"),
RC_vdd = self.replica_bitline_offset + vector(1,-1).scale(self.bitcell_chars["vdd"]) offset=offset)
mid = [vdd_offset.x, RC_vdd.y]
# Note the inverted stacks
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=[vdd_offset, mid, RC_vdd])
gnd_offset = self.BL_inv_offset - vector(self.inv.width, 0) # Connect the bitcell gnd pin to the rail
load_gnd = self.replica_bitline_offset + vector(self.bitcell_chars["gnd"][0], gnd_pins = self.get_pin("gnd")
self.bitline_load.height) gnd_start = self.get_pin("gnd").uc()
mid = [load_gnd.x, gnd_offset.y] rbl_gnd_pins = self.rbl_inst.get_pin("gnd")
self.add_wire(layers=("metal1", "via1", "metal2"), # Find the left most rail on M2
coordinates=[gnd_offset, mid, load_gnd]) 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, # Add a second gnd pin to the second delay chain rail. No need for full length.
self.bitline_load.height) dc_gnd_offset = self.dc_inst.get_pin("gnd")[1].ll()
mid = [load_gnd.x, gnd_offset.y] self.add_layout_pin(text="gnd",
self.add_wire(("metal1", "via1", "metal2"), [gnd_offset, mid, load_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)

View File

@ -11,15 +11,17 @@ class sense_amp(design.design):
Sense amplifier to read a pair of bit-lines. Sense amplifier to read a pair of bit-lines.
""" """
pins = ["BL", "BR", "Dout", "SCLK", "vdd", "gnd"] pin_names = ["BL", "BR", "Dout", "SCLK", "vdd", "gnd"]
chars = utils.auto_measure_libcell(pins, "sense_amp", GDS["unit"], layer["boundary"]) (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): def __init__(self, name):
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(2, "Create Sense Amp object") debug.info(2, "Create Sense Amp object")
self.width = sense_amp.chars["width"] self.width = sense_amp.width
self.height = sense_amp.chars["height"] self.height = sense_amp.height
self.pin_map = sense_amp.pin_map
def delay(self, slew, load=0.0): def delay(self, slew, load=0.0):
from tech import spice from tech import spice

View File

@ -16,10 +16,15 @@ class sense_amp_array(design.design):
c = reload(__import__(OPTS.config.sense_amp)) c = reload(__import__(OPTS.config.sense_amp))
self.mod_sense_amp = getattr(c, 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.word_size = word_size
self.words_per_row = words_per_row 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.add_pins()
self.create_layout() self.create_layout()
@ -27,110 +32,90 @@ class sense_amp_array(design.design):
def add_pins(self): def add_pins(self):
if (self.words_per_row == 1): for i in range(0,self.row_size,self.words_per_row):
for i in range(self.word_size): self.add_pin("data[{0}]".format(i/self.words_per_row))
self.add_pin("bl[{0}]".format(i)) self.add_pin("bl[{0}]".format(i))
self.add_pin("br[{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))
self.add_pin("sclk") self.add_pin("sclk")
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def create_layout(self): def create_layout(self):
self.create_sense_amp()
self.setup_layout_constants()
self.add_sense_amp() self.add_sense_amp()
self.connect_rails() 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): def add_sense_amp(self):
for i in range(self.word_size):
name = "sa_d{0}".format(i) bl_pin = self.amp.get_pin("BL")
index = i * self.words_per_row br_pin = self.amp.get_pin("BR")
amp_position = vector(self.amp.width * index, 0) dout_pin = self.amp.get_pin("Dout")
BL_offset = amp_position + vector(self.sense_amp_chars["BL"][0], 0)
BR_offset = amp_position + vector(self.sense_amp_chars["BR"][0], 0) 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, self.add_inst(name=name,
mod=self.amp, mod=self.amp,
offset=amp_position) offset=amp_position)
self.amp_positions.append(amp_position) self.connect_inst(["bl[{0}]".format(i),"br[{0}]".format(i),
if (self.words_per_row == 1): "data[{0}]".format(i/self.words_per_row),
self.add_label(text="bl[{0}]".format(i), "sclk", "vdd", "gnd"])
layer="metal2",
offset=BL_offset) self.add_layout_pin(text="bl[{0}]".format(i),
self.add_label(text="br[{0}]".format(i), layer="metal2",
layer="metal2", offset=bl_offset,
offset=BR_offset) width=bl_pin.width(),
self.connect_inst(["bl[{0}]".format(i),"br[{0}]".format(i), height=bl_pin.height())
"data_out[{0}]".format(i), self.add_layout_pin(text="br[{0}]".format(i),
"sclk", "vdd", "gnd"]) layer="metal2",
else: offset=br_offset,
self.add_label(text="bl_out[{0}]".format(index), width=br_pin.width(),
layer="metal2", height=br_pin.height())
offset=BL_offset)
self.add_label(text="br_out[{0}]".format(index), self.add_layout_pin(text="data[{0}]".format(i/self.words_per_row),
layer="metal2", layer="metal2",
offset=BR_offset) offset=dout_offset,
self.connect_inst(["bl_out[{0}]".format(index), "br_out[{0}]".format(index), width=dout_pin.width(),
"data_out[{0}]".format(i), height=dout_pin.height())
"sclk", "vdd", "gnd"])
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): def connect_rails(self):
base_offset = vector(0, - 0.5 * drc["minwidth_metal1"])
# add vdd rail across entire array # 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", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=vdd_offset, offset=vdd_offset,
width=self.width, width=self.width,
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
self.vdd_positions.append(vdd_offset)
# NOTE:the gnd rails are vertical so it is not connected horizontally # NOTE:the gnd rails are vertical so it is not connected horizontally
# add gnd rail across entire array # 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", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=gnd_offset, offset=gnd_offset,
width=self.width, width=self.width,
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
self.gnd_positions.append(gnd_offset)
# add sclk rail across entire array # 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", self.add_layout_pin(text="sclk",
layer="metal1", layer="metal1",
offset=sclk_offset, offset=sclk_offset,
width=self.width, width=self.width,
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
self.SCLK_positions.append(sclk_offset)
def delay(self, slew, load=0.0): def delay(self, slew, load=0.0):
result = self.amp.delay(slew=slew, load=load) result = self.amp.delay(slew=slew, load=load)

View File

@ -18,205 +18,186 @@ class single_level_column_mux(design.design):
c = reload(__import__(OPTS.config.bitcell)) c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, OPTS.config.bitcell) self.mod_bitcell = getattr(c, OPTS.config.bitcell)
self.bitcell_chars = self.mod_bitcell.chars self.bitcell = self.mod_bitcell()
self.tx_size = tx_size self.ptx_width = tx_size * drc["minwidth_tx"]
self.ptx_width = self.tx_size * drc["minwidth_tx"]
self.add_pins()
self.create_layout()
def add_pins(self):
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"]) self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
self.create_layout()
def create_layout(self): def create_layout(self):
# This is not instantiated and used for calculations only. # This is not instantiated and used for calculations only.
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) 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.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_poly()
self.connect_to_bitlines() self.connect_to_bitlines()
self.add_gnd_rail() self.add_gnd_rail()
self.add_well_contacts() self.add_well_contacts()
self.setup_layout_constants()
def create_ptx(self): def create_ptx(self):
"""Initializes the nmos1 and nmos2 transistors""" """ Create the two pass gate NMOS transistors to switch the bitlines"""
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):
# Adds nmos1,nmos2 to the module # Adds nmos1,nmos2 to the module
self.nmos1_position = (vector(drc["minwidth_metal1"], self.nmos = ptx(width=self.ptx_width)
drc["poly_extend_active"]) self.add_mod(self.nmos)
- vector([drc["well_enclosure_active"]]*2))
self.add_inst(name="M_1", self.nmos1_position = vector(drc["minwidth_metal1"], drc["poly_extend_active"]) \
mod=self.nmos1, - vector([drc["well_enclosure_active"]]*2)
self.add_inst(name="mux_tx1",
mod=self.nmos,
offset=self.nmos1_position) offset=self.nmos1_position)
self.connect_inst(["bl", "sel", "bl_out", "gnd"]) self.connect_inst(["bl", "sel", "bl_out", "gnd"])
nmos2_to_nmos1 = vector(self.nmos1.active_width, nmos2_to_nmos1 = vector(self.nmos.active_width,
self.nmos1.active_height + drc["minwidth_poly"] self.nmos.active_height + drc["minwidth_poly"] + 2*drc["poly_extend_active"])
+ 2* drc["poly_extend_active"])
self.nmos2_position = self.nmos1_position + nmos2_to_nmos1 self.nmos2_position = self.nmos1_position + nmos2_to_nmos1
self.add_inst(name="M_2", self.add_inst(name="mux_tx2",
mod=self.nmos2, mod=self.nmos,
offset=self.nmos2_position) offset=self.nmos2_position)
self.connect_inst(["br", "sel", "br_out", "gnd"]) self.connect_inst(["br", "sel", "br_out", "gnd"])
def connect_poly(self): def connect_poly(self):
self.poly_offset = (self.nmos1_position """ Connect the poly gate of the two pass transistors """
+ self.nmos1.poly_positions[0]
+ vector(0,self.nmos1.poly_height)) self.poly_offset = self.nmos1_position + self.nmos.poly_positions[0] \
width=self.nmos2_position.x- self.nmos1_position.x+ drc["minwidth_poly"] + vector(0,self.nmos.poly_height)
self.poly = self.add_rect(layer="poly", width=self.nmos2_position.x - self.nmos1_position.x + drc["minwidth_poly"]
offset=self.poly_offset, self.add_layout_pin(text="col_addr",
width=width, layer="poly",
height=drc["minwidth_poly"]) offset=self.poly_offset,
self.add_label(text="col_addr", width=width,
layer="poly", height=drc["minwidth_poly"])
offset=self.poly_offset)
def connect_to_bitlines(self): 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, # place the contact at the top of the src/drain
offset.y) offset = self.nmos1_position + vector(self.nmos.active_contact_positions[0].x + 0.5*self.m1m2_via.contact_width
+ connection) + 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"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset, offset=offset)
mirror="MX")
self.add_label(text="bl",
layer="metal2",
offset=self.BL_position)
self.add_rect(layer="metal2", bl_offset = vector(self.bitcell.get_pin("BL").lx(),self.height)
offset=self.BL_position - vector(0, 2 * drc["minwidth_metal2"]), self.add_layout_pin(text="bl",
width=drc["minwidth_metal2"], layer="metal2",
height=2 * drc["minwidth_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"] # place the contact at the bottom of the src/drain
self.add_rect(layer="metal2", offset = self.nmos1_position + self.nmos.active_contact_positions[1]
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]
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset) offset=offset)
self.add_rect(layer="metal2", self.add_rect(layer="metal2",
offset=[self.bitcell_chars["BL"][0]- 0.5 * self.m1m2_via.width, offset=[self.bitcell.get_pin("BL").lx(), offset.y],
0], width=(offset.x - self.bitcell.get_pin("BL").lx() + 2*drc["minwidth_metal2"]),
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"]),
height=drc["minwidth_metal2"]) height=drc["minwidth_metal2"])
self.BL_out_position = vector(self.bitcell_chars["BL"][0]- 0.5* self.m1m2_via.width, self.add_layout_pin(text="bl_out",
0) layer="metal2",
self.add_label(text="bl_out", offset=[self.bitcell.get_pin("BL").lx(), 0],
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"]],
width=drc["minwidth_metal2"], width=drc["minwidth_metal2"],
height=2*drc["minwidth_metal2"]) height=drc["minwidth_metal2"] + offset.y)
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)
offset = self.nmos2_position + self.nmos2.active_contact_positions[0] BL_out_position = vector(self.bitcell.get_pin("BL").lx()- 0.5*self.m1m2_via.width, 0)
self.BR_out_position = vector(self.bitcell_chars["BR"][0]- 0.5 * self.m1m2_via.width,
0)
self.add_label(text="br_out", # place the contact at the top of the src/drain
layer="metal2", offset = self.nmos2_position + vector(self.nmos.active_contact_positions[1].x - 0.5*self.m1m2_via.contact_width
offset=self.BR_out_position) - 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"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset) offset=offset)
self.add_rect(layer="metal2", self.add_rect(layer="metal2",
offset=offset, offset=offset,
width=self.BR_out_position.x - offset[0], width=BR_out_position.x - offset.x,
height=drc["minwidth_metal2"]) height=drc["minwidth_metal2"])
self.add_rect(layer="metal2", self.add_layout_pin(text="br_out",
offset=[self.BR_out_position.x, layer="metal2",
offset.y+ drc["minwidth_metal2"]], offset=[BR_out_position.x, 0],
width=drc["minwidth_metal2"], height=offset.y+ drc["minwidth_metal2"])
height=-(offset.y+ drc["minwidth_metal2"]))
def add_gnd_rail(self): def add_gnd_rail(self):
self.gnd_position = vector(self.bitcell_chars["gnd"][0]- 0.5 * self.m1m2_via.width, gnd_pins = self.bitcell.get_pins("gnd")
0) for gnd_pin in gnd_pins:
self.add_layout_pin(text="gnd", # only use vertical gnd pins that span the whole cell
layer="metal2", if gnd_pin.layer == "metal2" and gnd_pin.height >= self.bitcell.height:
offset=self.gnd_position, gnd_position = vector(gnd_pin.lx(), 0)
width=drc["minwidth_metal2"], self.add_layout_pin(text="gnd",
height=self.BL_position[1]) layer="metal2",
offset=gnd_position,
height=self.get_pin("bl").uy())
def add_well_contacts(self): def add_well_contacts(self):
offset = vector(self.gnd_position.x+ drc["minwidth_metal2"], # find right most gnd rail
self.nmos1.poly_height / 2) 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"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset - vector(self.m1m2_via.width / 2, 0), offset=m1m2_offset)
mirror="MY") active_offset = right_gnd.ll() + vector(-self.m1m2_via.width,self.nmos.poly_height/2)
self.add_contact(layers=("active", "contact", "metal1"), self.add_contact(layers=("active", "contact", "metal1"),
offset=offset - vector(self.m1m2_via.width, 0), offset=active_offset)
mirror="MY")
temp = vector(self.m1m2_via.width, offset_implant = active_offset + vector([drc["implant_to_contact"]]*2).scale(-1,-1)
(self.pwell_contact.first_layer_height - self.pwell_contact.second_layer_height) / 2) implant_width = 2*drc["implant_to_contact"] + self.well_contact.width
offset_implant = offset - temp + vector([drc["implant_to_contact"]]*2).scale(1,-1) implant_height = 2*drc["implant_to_contact"] + self.well_contact.height
self.add_rect(layer="pimplant", self.add_rect(layer="pimplant",
offset=offset_implant, offset=offset_implant,
width=-(2*drc["implant_to_contact"] + self.pwell_contact.first_layer_width), width=implant_width,
height=2*drc["implant_to_contact"] + self.pwell_contact.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", self.add_rect(layer="pwell",
offset=offset_well, offset=offset_well,
width=self.gnd_position.x+ drc["minwidth_metal2"] - offset_well[0], width=offset_implant.x + implant_width + drc["well_enclosure_active"] - offset_well.x,
height=self.nmos1.height + drc["minwidth_poly"]) height=self.nmos2_position.y)
self.add_rect(layer="vtg", self.add_rect(layer="vtg",
offset=offset_well, offset=offset_well,
width=self.gnd_position.x+ drc["minwidth_metal2"] - offset_well[0], width=offset_implant.x + implant_width + drc["well_enclosure_active"] - offset_well.x,
height=self.nmos1.height + drc["minwidth_poly"]) 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]

View File

@ -14,28 +14,25 @@ class single_level_column_mux_array(design.design):
Array of column mux to read the bitlines through the 6T. 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") design.design.__init__(self, "columnmux_array")
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.rows = rows
self.columns = columns self.columns = columns
self.word_size = word_size self.word_size = word_size
self.words_per_row = self.columns / self.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.add_pins()
self.create_layout() self.create_layout()
self.offset_all_coordinates()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
for i in range(self.columns): for i in range(self.columns):
self.add_pin("bl[{0}]".format(i)) self.add_pin("bl[{}]".format(i))
self.add_pin("br[{0}]".format(i)) self.add_pin("br[{}]".format(i))
for i in range(self.columns / self.words_per_row): for i in range(self.word_size):
self.add_pin("bl_out[{0}]".format(i * self.words_per_row)) self.add_pin("bl_out[{}]".format(i))
self.add_pin("br_out[{0}]".format(i * self.words_per_row)) self.add_pin("br_out[{}]".format(i))
for i in range(self.words_per_row): 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") self.add_pin("gnd")
def create_layout(self): def create_layout(self):
@ -47,226 +44,169 @@ class single_level_column_mux_array(design.design):
def add_modules(self): def add_modules(self):
self.mux = single_level_column_mux(name="single_level_column_mux", self.mux = single_level_column_mux(name="single_level_column_mux",
tx_size=8) tx_size=8)
self.single_mux = self.mux
self.add_mod(self.mux) self.add_mod(self.mux)
# This is not instantiated and used for calculations only. # This is not instantiated and used for calculations only.
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
def setup_layout_constants(self): def setup_layout_constants(self):
self.column_addr_size = num_of_inputs = int(self.words_per_row / 2) self.column_addr_size = num_of_inputs = int(self.words_per_row / 2)
self.width = (self.columns * self.mux.width) self.width = self.columns * self.mux.width
self.gnd_positions = []
self.BL_out_positions = [] self.m1_pitch = self.m1m2_via.width + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.BR_out_positions = [] # To correct the offset between M1 and M2 via enclosures
self.BL_positions = [] self.offset_fix = vector(0,0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]))
self.BR_positions = [] # one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br
self.addr_line_positions = [] # one extra route pitch is to space from the sense amp
self.route_height = (self.words_per_row + 3)*self.m1_pitch
spacing = self.m1m2_via.width + drc['metal1_to_metal1'] # mux height plus routing signal height plus well spacing at the top
self.height = self.mux.height + spacing + 4 * drc['metal2_to_metal2'] self.height = self.mux.height + self.route_height + drc["pwell_to_nwell"]
if (self.words_per_row > 1):
# 1 for BL and another for BR
self.height = self.height + (self.words_per_row + 1) * spacing
def create_array(self): def create_array(self):
for i in range(self.columns): self.mux_inst = []
name = "XMUX{0}".format(i)
x_off = vector(i * self.mux.width, 0)
self.add_inst(name=name,
mod=self.mux,
offset=x_off)
""" draw a vertical m2 rail to extend BL BR & gnd on top of the cell """ # For every column, add a pass gate
# FIXME: These are just min metal squares, are they needed? for col_num in range(self.columns):
self.add_rect(layer="metal2", name = "XMUX{0}".format(col_num)
offset=x_off + self.mux.BL_position, x_off = vector(col_num * self.mux.width, self.route_height)
width=drc['minwidth_metal2'], self.mux_inst.append(self.add_inst(name=name,
height=drc['minwidth_metal2']) mod=self.mux,
self.add_rect(layer="metal2", offset=x_off))
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'])
""" add labels for the column_mux array """ offset = self.mux_inst[-1].get_pin("bl").ll()
BL = self.mux.BL_position + vector(i * self.mux.width, 0) self.add_layout_pin(text="bl[{}]".format(col_num),
self.BL_positions.append(BL) layer="metal2",
self.add_label(text="bl[{0}]".format(i), offset=offset,
layer="metal2", height=self.height-offset.y)
offset=BL)
BR = self.mux.BR_position + vector(i * self.mux.width, 0) offset = self.mux_inst[-1].get_pin("br").ll()
self.BR_positions.append(BR) self.add_layout_pin(text="br[{}]".format(col_num),
self.add_label(text="br[{0}]".format(i), layer="metal2",
layer="metal2", offset=offset,
offset=BR) height=self.height-offset.y)
gnd = self.mux.gnd_position + vector(i * self.mux.width, 0) gnd_pins = self.mux_inst[-1].get_pins("gnd")
self.gnd_positions.append(gnd) for gnd_pin in gnd_pins:
self.add_label(text="gnd", # only do even colums to avoid duplicates
layer="metal2", offset = gnd_pin.ll()
offset=gnd) 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): def add_routing(self):
self.add_horizontal_input_rail() self.add_horizontal_input_rail()
self.add_vertical_poly_rail() self.add_vertical_poly_rail()
self.routing_BL_BR() self.route_bitlines()
def add_horizontal_input_rail(self): def add_horizontal_input_rail(self):
""" HORIZONTAL ADDRESS INPUTS TO THE COLUMN MUX ARRAY """ """ Create address input rails on M1 below the mux transistors """
if (self.words_per_row > 1): for j in range(self.words_per_row):
for j in range(self.words_per_row): offset = vector(0, self.route_height - (j+1)*self.m1_pitch)
offset = vector(0, -(j + 1) * self.m1m2_via.width self.add_layout_pin(text="sel[{}]".format(j),
- j * drc['metal1_to_metal1']) layer="metal1",
self.add_rect(layer="metal1", offset=offset,
offset=offset, width=self.mux.width * self.columns,
width=self.mux.width * self.columns, height=self.m1m2_via.width)
height=self.m1m2_via.width)
self.addr_line_positions.append(offset)
def add_vertical_poly_rail(self): def add_vertical_poly_rail(self):
""" VERTICAL POLY METAL EXTENSION AND POLY CONTACT """ """ Connect the poly to the address rails """
for j1 in range(self.columns):
pattern = math.floor(j1 / self.words_per_row) * self.words_per_row # Offset to the first transistor gate in the pass gate
height = ((self.m1m2_via.width + drc['metal1_to_metal1']) nmos_offset = (self.mux.nmos1_position + self.mux.nmos.poly_positions[0]).scale(1,0)
*(pattern - j1)) for col in range(self.columns):
nmos1_poly = self.mux.nmos1_position + self.mux.nmos1.poly_positions[0] # which select bit should this column connect to depends on the position in the word
offset = nmos1_poly.scale(1, 0) + vector(j1 * self.mux.width, 0) 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", self.add_rect(layer="poly",
offset=offset, offset=offset,
width=drc["minwidth_poly"], 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. # Add the poly contact with a shift to account for the rotation
poly_contact = contact(layer_stack=("metal1", "contact", "poly"))
offset = offset.scale(1, 0) + vector(0, height - poly_contact.width)
self.add_contact(layers=("metal1", "contact", "poly"), self.add_contact(layers=("metal1", "contact", "poly"),
offset=offset, offset=offset + vector(self.m1m2_via.height,0),
mirror="MX",
rotate=90) rotate=90)
def routing_BL_BR(self): def route_bitlines(self):
""" OUTPUT BIT-LINE CONNECTIONS (BL_OUT, BR_OUT) """ """ Connect the output bit-lines to form the appropriate width mux """
if (self.words_per_row > 1): for j in range(self.columns):
for j in range(self.columns / self.words_per_row): bl_offset = self.mux_inst[j].get_pin("bl_out").ll()
base = vector(self.mux.width * self.words_per_row * j, br_offset = self.mux_inst[j].get_pin("br_out").ll()
self.m1m2_via.width + drc['metal1_to_metal1'])
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) width = self.m1m2_via.width + self.mux.width * (self.words_per_row - 1)
self.add_rect(layer="metal1", 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, width=width,
height=-self.m1m2_via.width) height=drc["minwidth_metal2"])
self.add_rect(layer="metal1", 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, width=width,
height=-self.m1m2_via.width) height=drc["minwidth_metal2"])
height = base.y * (self.words_per_row + 2) + 3 * drc['metal2_to_metal2'] # Extend the bitline output rails and gnd downward on the first bit of each n-way mux
base = vector(base.x, - height) self.add_layout_pin(text="bl_out[{}]".format(int(j/self.words_per_row)),
self.add_rect(layer="metal2", layer="metal2",
offset=base + self.mux.BL_position.scale(1,0), offset=bl_out_offset.scale(1,0),
width=drc['minwidth_metal2'], width=drc['minwidth_metal2'],
height=height) height=self.route_height)
self.add_rect(layer="metal2", self.add_layout_pin(text="br_out[{}]".format(int(j/self.words_per_row)),
offset=base + self.mux.BR_position.scale(1,0), layer="metal2",
width=drc['minwidth_metal2'], offset=br_out_offset.scale(1,0),
height=height) width=drc['minwidth_metal2'],
self.add_rect(layer="metal2", height=self.route_height)
offset=base + self.mux.gnd_position.scale(1,0),
width=drc['minwidth_metal2'],
height=height)
for j in range(self.columns): # This via is on the right of the wire
""" 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)
self.add_via(layers=("metal1", "via1", "metal2"), 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) rotate=90)
offset = vector(self.mux.BR_position.x + self.mux.width * j, 0) else:
height = height + contact_spacing
self.add_rect(layer="metal2", self.add_rect(layer="metal2",
offset=offset, offset=bl_out_offset,
width=drc['minwidth_metal2'], width=drc['minwidth_metal2'],
height= - height) height=self.route_height-bl_out_offset.y)
offset = offset + vector(self.m1m2_via.height/2, - height) # This via is on the right of the wire
layer_diff = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width)
self.add_via(layers=("metal1", "via1", "metal2"), 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) rotate=90)
self.add_label(text="COLUMN_MUX",
layer="text",
offset=[self.width / 2.0, self.height / 2.0])

View File

@ -18,21 +18,23 @@ class sram(design.design):
""" """
def __init__(self, word_size, num_words, num_banks, name): 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: c = reload(__import__(OPTS.config.control_logic))
config_mod_name = getattr(OPTS.config, mod_name) self.mod_control_logic = getattr(c, OPTS.config.control_logic)
class_file = reload(__import__(config_mod_name))
mod_class = getattr(class_file , config_mod_name) c = reload(__import__(OPTS.config.ms_flop_array))
setattr (self, "mod_"+mod_name, mod_class) 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 # reset the static duplicate name checker for unit tests
# in case we create more than one SRAM # in case we create more than one SRAM
import design import design
design.design.name_map=[] 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.word_size = word_size
self.num_words = num_words self.num_words = num_words
self.num_banks = num_banks self.num_banks = num_banks
@ -41,75 +43,69 @@ class sram(design.design):
self.num_words)) self.num_words))
design.design.__init__(self, name) 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.compute_sizes()
self.add_pins() self.add_pins()
self.create_layout() self.create_layout()
self.DRC_LVS() self.DRC_LVS()
def compute_sizes(self): def compute_sizes(self):
""" Computes the required sizes to create the memory """ """ Computes the organization of the memory using bitcell size by trying to make it square."""
self.check_num_banks(self.num_banks) 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_words_per_bank = self.num_words/self.num_banks
self.num_bits_per_bank = self.word_size*self.num_words_per_bank self.num_bits_per_bank = self.word_size*self.num_words_per_bank
self.bank_area = self.bitcell_chars["width"]*\ # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
self.bitcell_chars["height"]*self.num_bits_per_bank self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank
self.bank_side_length = math.sqrt(self.bank_area) self.bank_side_length = math.sqrt(self.bank_area)
self.tentative_num_cols = int(self.bank_side_length/self.bitcell_chars["width"]) # Estimate the words per row given the height of the bitcell and the square side length
self.words_per_row = self.cal_words_per_row(self.tentative_num_cols, self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width)
self.word_size) self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size)
self.tentative_num_rows = self.num_bits_per_bank \
/(self.words_per_row \ # Estimate the number of rows given the tentative words per row
*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 = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
self.words_per_row)
# Fix the number of columns and rows
self.num_cols = self.words_per_row*self.word_size self.num_cols = self.words_per_row*self.word_size
self.num_rows = self.num_words_per_bank/self.words_per_row 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.row_addr_size = int(log(self.num_rows, 2))
self.col_addr_size = int(log(self.words_per_row, 2)) self.col_addr_size = int(log(self.words_per_row, 2))
self.bank_addr_size = self.col_addr_size \ self.bank_addr_size = self.col_addr_size + self.row_addr_size
+ self.row_addr_size self.addr_size = self.bank_addr_size + int(math.log(self.num_banks, 2))
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 tentative_num_cols < 1.5*word_size:
if(num_banks != 1 and num_banks != 2 and num_banks != 4): return 1
debug.error("Valid number of banks are 1 , 2 and 4.") elif tentative_num_cols > 3*word_size:
sys.exit(-1) return 4
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
else: else:
words_per_row = 2 return 2
return words_per_row
def amend_words_per_row(self,tentative_num_rows, words_per_row): 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 > 512):
if(tentative_num_rows*words_per_row > 2048): debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048")
debug.error("Number of rows exceeds 512") return words_per_row*tentative_num_rows/512
sys.exit(-1) # Recompute the words per row given a hard min
words_per_row = words_per_row*tentative_num_rows/512
if(tentative_num_rows < 16): if(tentative_num_rows < 16):
if(tentative_num_rows*words_per_row < 16): debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
debug.error("minimum number of rows is 16, but given {0}".format( return words_per_row*tentative_num_rows/16
tentative_num_rows))
sys.exit(-1)
words_per_row = words_per_row*tentative_num_rows/16
return words_per_row return words_per_row
def add_pins(self): def add_pins(self):
@ -124,12 +120,14 @@ class sram(design.design):
def create_layout(self): def create_layout(self):
""" Layout creation """ """ Layout creation """
self.create_modules() self.create_modules()
self.add_modules() self.add_modules()
self.add_routing() self.add_routing()
def add_routing(self): def add_routing(self):
""" Route all of the modules """ """ Route all of the modules """
if (self.num_banks == 2 or self.num_banks == 4): if (self.num_banks == 2 or self.num_banks == 4):
self.route_2or4_banks() self.route_2or4_banks()
if (self.num_banks == 4): if (self.num_banks == 4):
@ -139,6 +137,7 @@ class sram(design.design):
def create_multibank_modules(self): def create_multibank_modules(self):
""" Add the multibank address flops and bank decoder """ """ Add the multibank address flops and bank decoder """
self.msf_msb_address = self.mod_ms_flop_array(name="msf_msb_address", self.msf_msb_address = self.mod_ms_flop_array(name="msf_msb_address",
columns=self.num_banks/2, columns=self.num_banks/2,
word_size=self.num_banks/2) word_size=self.num_banks/2)
@ -259,8 +258,8 @@ class sram(design.design):
temp.append("ADDR[{0}]".format(i)) temp.append("ADDR[{0}]".format(i))
if(self.num_banks > 1): if(self.num_banks > 1):
temp.append("bank_select[{0}]".format(self.bank_count)) temp.append("bank_select[{0}]".format(self.bank_count))
temp = temp + ["s_en" , "w_en", "tri_en_bar", "tri_en", temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en",
"clk_bar", "clk" , "vdd" , "gnd" ] "clk_bar","clk" , "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
# Saving control line properties # Saving control line properties
@ -359,13 +358,13 @@ class sram(design.design):
line_gap = 2*drc[m2m] line_gap = 2*drc[m2m]
return bits*(line_width + line_gap) - line_gap 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 """ """ Add and place control logic """
self.control_position = position self.control_position = position
self.add_inst(name="control", self.add_inst(name="control",
mod=self.control, mod=self.control,
offset=self.control_position, offset=self.control_position,
mirror=mirror) rotate=rotate)
temp = ["CSb", "WEb", "OEb", "s_en", "w_en", "tri_en", temp = ["CSb", "WEb", "OEb", "s_en", "w_en", "tri_en",
"tri_en_bar", "clk_bar", "clk", "vdd", "gnd"] "tri_en_bar", "clk_bar", "clk", "vdd", "gnd"]
self.connect_inst(temp) self.connect_inst(temp)
@ -377,20 +376,20 @@ class sram(design.design):
# FIXME: document # FIXME: document
loc = vector(- 2 * drc["minwidth_metal3"], loc = vector(- 2 * drc["minwidth_metal3"],
self.bank_positions[0].y + self.bank.decoder_position.y self.bank_positions[0].y + self.bank.decoder_position.y
+ 2 * drc["minwidth_metal3"]) + 2 * drc["minwidth_metal3"])
self.add_control_logic(loc, "R90") self.add_control_logic(loc, 90)
self.width = self.bank.width + self.control.height + 2*drc["minwidth_metal3"] self.width = self.bank.width + self.control.height + 2*drc["minwidth_metal3"]
self.height = self.bank.height self.height = self.bank.height
self.control.CSb_position.rotate_scale(-1,1) self.control.CSb_position.rotate_scale(-1,1)
self.CSb_position = (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.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.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.clk_position = (self.control.clk_position.rotate_scale(-1,1)
+self.control_position) +self.control_position)
for i in range(0, self.word_size): for i in range(0, self.word_size):
self.add_label(text="DATA[{0}]".format(i), self.add_label(text="DATA[{0}]".format(i),
layer="metal3", layer="metal3",
@ -404,25 +403,23 @@ class sram(design.design):
self.bank_w = self.bank.width self.bank_w = self.bank.width
self.num_vertical_line = self.bank_addr_size + self.control_size \ 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.num_horizontal_line = self.word_size
self.vertical_bus_width = self.calculate_bus_width("metal2", self.vertical_bus_width = self.calculate_bus_width("metal2", self.num_vertical_line)
self.num_vertical_line) self.horizontal_bus_width = self.calculate_bus_width("metal3", self.num_horizontal_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.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.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.vertical_bus_offset = vector(self.bank_w + self.bank_to_bus_distance,
self.sram_power_rail_gap) self.sram_power_rail_gap)
self.horizontal_bus_offset = vector(0, self.horizontal_bus_offset = vector(0,
self.bank_h + self.bank_to_bus_distance self.bank_h + self.bank_to_bus_distance
+ self.sram_power_rail_gap + self.sram_power_rail_gap
+ self.horizontal_bus_width) + self.horizontal_bus_width)
# Vertical bus # Vertical bus
self.vertical_line_positions = self.create_bus(layer="metal2", 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.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.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 # Add Control logic for Bank = 2 and Bank =4
@ -464,7 +461,7 @@ class sram(design.design):
if (self.num_banks == 2): if (self.num_banks == 2):
self.control_position = vector(0, control_bus_offset.y self.control_position = vector(0, control_bus_offset.y
+ self.ms_flop_chars["width"]) + 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.CSb_position = self.control_position + self.control.CSb_position
self.OEb_position = self.control_position + self.control.OEb_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}".format(i))
temp.append("msb{0}_bar".format(i)) temp.append("msb{0}_bar".format(i))
else: else:
temp = temp + ["bank_select[1]", "bank_select[0]"] temp.extend(["bank_select[1]", "bank_select[0]"])
temp = temp + ["clk", "vdd", "gnd"] temp.extend(["clk", "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
self.add_banks_0and1() self.add_banks_0and1()
@ -575,7 +572,7 @@ class sram(design.design):
self.control_position = vector(0, self.msb_decoder_position.y self.control_position = vector(0, self.msb_decoder_position.y
+ self.msb_decoder.height) + 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.CSb_position = self.control_position + self.control.CSb_position
self.OEb_position = self.control_position + self.control.OEb_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"), self.add_via(layers=("metal2", "via2", "metal3"),
offset=[bank_side[i].x + drc["minwidth_metal2"], offset=[bank_side[i].x + drc["minwidth_metal2"],
control_side[i].y], control_side[i].y],
mirror="R90") rotate=90)
elif (self.num_banks == 2 or self.num_banks == 4): elif (self.num_banks == 2 or self.num_banks == 4):
for i in range(self.control_size): for i in range(self.control_size):
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.vertical_line_positions[i].x + drc["minwidth_metal2"], offset=[self.vertical_line_positions[i].x + drc["minwidth_metal2"],
self.control_bus_line_positions[i].y], self.control_bus_line_positions[i].y],
mirror="R90") rotate=90)
control_attr = self.bank_property[i] control_attr = self.bank_property[i]
control_side_line_position = (getattr(self.control,control_attr) control_side_line_position = (getattr(self.control,control_attr)
+self.control_position) +self.control_position)
@ -759,7 +756,7 @@ class sram(design.design):
offset=[control_side_line_position.x offset=[control_side_line_position.x
+ drc["minwidth_metal2"], + drc["minwidth_metal2"],
self.control_bus_line_positions[i].y], self.control_bus_line_positions[i].y],
mirror="R90") rotate=90)
for i in range(self.num_banks/2): for i in range(self.num_banks/2):
# MSB line connections # MSB line connections
msb_line = self.control_size + self.num_banks/2 - 1 - i msb_line = self.control_size + self.num_banks/2 - 1 - i
@ -843,7 +840,7 @@ class sram(design.design):
height=drc["minwidth_metal1"]) height=drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=end - vector(0, 0.5 * self.m1m2_via.width), offset=end - vector(0, 0.5 * self.m1m2_via.width),
mirror="R90") rotate=90)
for i in range(4): for i in range(4):
bank_select_line = self.control_size + 2 + self.bank_addr_size + i bank_select_line = self.control_size + 2 + self.bank_addr_size + i
msb_decoder_out = (self.msb_decoder_position msb_decoder_out = (self.msb_decoder_position
@ -943,7 +940,7 @@ class sram(design.design):
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=[control_vdd1_position.x + drc["minwidth_metal2"], offset=[control_vdd1_position.x + drc["minwidth_metal2"],
control_vdd_supply.y], control_vdd_supply.y],
mirror="R90") rotate=90)
if (self.control.width > self.bank.width): if (self.control.width > self.bank.width):
last_bank = self.num_banks - 1 last_bank = self.num_banks - 1
@ -964,7 +961,7 @@ class sram(design.design):
offset=[control_vdd2_position.x offset=[control_vdd2_position.x
+ drc["minwidth_metal2"], + drc["minwidth_metal2"],
control_vdd_supply.y], control_vdd_supply.y],
mirror="R90") rotate=90)
self.add_layout_pin(text="vdd", self.add_layout_pin(text="vdd",
layer="metal2", layer="metal2",
@ -1078,12 +1075,12 @@ class sram(design.design):
offset=[self.sram_bank_left_gnd_positions[0].x offset=[self.sram_bank_left_gnd_positions[0].x
+ drc["minwidth_metal2"], + drc["minwidth_metal2"],
control_gnd_supply.y], control_gnd_supply.y],
mirror="R90") rotate=90)
# Control gnd # Control gnd
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=[control_gnd_position.x + drc["minwidth_metal2"], offset=[control_gnd_position.x + drc["minwidth_metal2"],
control_gnd_supply.y], control_gnd_supply.y],
mirror="R90") rotate=90)
self.add_layout_pin(text="gnd", self.add_layout_pin(text="gnd",
layer="metal2", layer="metal2",
offset=[control_gnd_position.x, offset=[control_gnd_position.x,

View File

@ -18,6 +18,7 @@ class contact_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import contact import contact
@ -27,30 +28,22 @@ class contact_test(unittest.TestCase):
# Check single 1 x 1 contact" # Check single 1 x 1 contact"
debug.info(2, "1 x 1 {} test".format(stack_name)) debug.info(2, "1 x 1 {} test".format(stack_name))
OPTS.check_lvsdrc = False
c = contact.contact(layer_stack, (1, 1)) c = contact.contact(layer_stack, (1, 1))
OPTS.check_lvsdrc = True
self.local_check(c) self.local_check(c)
# check vertical array with one in the middle and two ends # check vertical array with one in the middle and two ends
debug.info(2, "1 x 3 {} test".format(stack_name)) debug.info(2, "1 x 3 {} test".format(stack_name))
OPTS.check_lvsdrc = False
c = contact.contact(layer_stack, (1, 3)) c = contact.contact(layer_stack, (1, 3))
OPTS.check_lvsdrc = True
self.local_check(c) self.local_check(c)
# check horizontal array with one in the middle and two ends # check horizontal array with one in the middle and two ends
debug.info(2, "3 x 1 {} test".format(stack_name)) debug.info(2, "3 x 1 {} test".format(stack_name))
OPTS.check_lvsdrc = False
c = contact.contact(layer_stack, (3, 1)) c = contact.contact(layer_stack, (3, 1))
OPTS.check_lvsdrc = True
self.local_check(c) self.local_check(c)
# check 3x3 array for all possible neighbors # check 3x3 array for all possible neighbors
debug.info(2, "3 x 3 {} test".format(stack_name)) debug.info(2, "3 x 3 {} test".format(stack_name))
OPTS.check_lvsdrc = False
c = contact.contact(layer_stack, (3, 3)) c = contact.contact(layer_stack, (3, 3))
OPTS.check_lvsdrc = True
self.local_check(c) self.local_check(c)
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True

View File

@ -18,6 +18,7 @@ class path_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import path import path
import tech import tech
@ -32,11 +33,9 @@ class path_test(unittest.TestCase):
[4 * min_space, 3 * min_space ], [4 * min_space, 3 * min_space ],
[0, 3 * min_space ], [0, 3 * min_space ],
[0, 6 * min_space ]] [0, 6 * min_space ]]
OPTS.check_lvsdrc = False
w = design.design("path_test0") w = design.design("path_test0")
path.path(w,layer_stack, position_list) path.path(w,layer_stack, position_list)
self.local_check(w) self.local_check(w)
OPTS.check_lvsdrc = True
min_space = 2 * tech.drc["minwidth_metal1"] 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, 4 * min_space],
[-1 * min_space, 0]] [-1 * min_space, 0]]
position_list = [[x+min_space, y+min_space] for x,y in old_position_list] position_list = [[x+min_space, y+min_space] for x,y in old_position_list]
OPTS.check_lvsdrc = False
w = design.design("path_test1") w = design.design("path_test1")
path.path(w,layer_stack, position_list) path.path(w,layer_stack, position_list)
self.local_check(w) self.local_check(w)
OPTS.check_lvsdrc = True
min_space = 2 * tech.drc["minwidth_metal2"] min_space = 2 * tech.drc["minwidth_metal2"]
layer_stack = ("metal2") layer_stack = ("metal2")
@ -69,11 +66,9 @@ class path_test(unittest.TestCase):
[-1 * min_space, 4 * min_space], [-1 * min_space, 4 * min_space],
[-1 * min_space, 0]] [-1 * min_space, 0]]
position_list = [[x-min_space, y-min_space] for x,y in old_position_list] position_list = [[x-min_space, y-min_space] for x,y in old_position_list]
OPTS.check_lvsdrc = False
w = design.design("path_test2") w = design.design("path_test2")
path.path(w, layer_stack, position_list) path.path(w, layer_stack, position_list)
self.local_check(w) self.local_check(w)
OPTS.check_lvsdrc = True
min_space = 2 * tech.drc["minwidth_metal3"] min_space = 2 * tech.drc["minwidth_metal3"]
layer_stack = ("metal3") layer_stack = ("metal3")
@ -88,11 +83,8 @@ class path_test(unittest.TestCase):
[-1 * min_space, 0]] [-1 * min_space, 0]]
# run on the reverse list # run on the reverse list
position_list.reverse() position_list.reverse()
OPTS.check_lvsdrc = False
w = design.design("path_test3") w = design.design("path_test3")
path.path(w, layer_stack, position_list) path.path(w, layer_stack, position_list)
OPTS.check_lvsdrc = True
self.local_check(w) self.local_check(w)
# return it back to it's normal state # return it back to it's normal state

View File

@ -28,10 +28,9 @@ class ptx_test(unittest.TestCase):
fet = ptx.ptx(width=tech.drc["minwidth_tx"], fet = ptx.ptx(width=tech.drc["minwidth_tx"],
mults=1, mults=1,
tx_type="nmos") tx_type="nmos")
# return it back to it's normal state
OPTS.check_lvsdrc = True
self.local_check(fet) self.local_check(fet)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -28,10 +28,9 @@ class ptx_test(unittest.TestCase):
fet = ptx.ptx(width=tech.drc["minwidth_tx"], fet = ptx.ptx(width=tech.drc["minwidth_tx"],
mults=1, mults=1,
tx_type="pmos") tx_type="pmos")
# return it back to it's normal state
OPTS.check_lvsdrc = True
self.local_check(fet) self.local_check(fet)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -28,10 +28,9 @@ class ptx_test(unittest.TestCase):
fet = ptx.ptx(width=tech.drc["minwidth_tx"], fet = ptx.ptx(width=tech.drc["minwidth_tx"],
mults=3, mults=3,
tx_type="nmos") tx_type="nmos")
# return it back to it's normal state
OPTS.check_lvsdrc = True
self.local_check(fet) self.local_check(fet)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def add_mods(self, fet): def add_mods(self, fet):

View File

@ -28,10 +28,9 @@ class ptx_test(unittest.TestCase):
fet = ptx.ptx(width=tech.drc["minwidth_tx"], fet = ptx.ptx(width=tech.drc["minwidth_tx"],
mults=3, mults=3,
tx_type="pmos") tx_type="pmos")
# return it back to it's normal state
OPTS.check_lvsdrc = True
self.local_check(fet) self.local_check(fet)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def add_mods(self, fet): def add_mods(self, fet):

View File

@ -18,6 +18,7 @@ class wire_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import wire import wire
import tech import tech
@ -36,10 +37,8 @@ class wire_test(unittest.TestCase):
[-1 * min_space, 4 * min_space], [-1 * min_space, 4 * min_space],
[-1 * min_space, 0]] [-1 * min_space, 0]]
position_list = [[x-min_space, y-min_space] for x,y in old_position_list] position_list = [[x-min_space, y-min_space] for x,y in old_position_list]
OPTS.check_lvsdrc = False
w = design.design("wire_test1") w = design.design("wire_test1")
wire.wire(w, layer_stack, position_list) wire.wire(w, layer_stack, position_list)
OPTS.check_lvsdrc = True
self.local_check(w) self.local_check(w)
min_space = 2 * (tech.drc["minwidth_poly"] + 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, 4 * min_space],
[-1 * min_space, 0]] [-1 * min_space, 0]]
position_list = [[x+min_space, y+min_space] for x,y in old_position_list] position_list = [[x+min_space, y+min_space] for x,y in old_position_list]
OPTS.check_lvsdrc = False
w = design.design("wire_test2") w = design.design("wire_test2")
wire.wire(w, layer_stack, position_list) wire.wire(w, layer_stack, position_list)
OPTS.check_lvsdrc = True
self.local_check(w) self.local_check(w)
min_space = 2 * (tech.drc["minwidth_metal2"] + min_space = 2 * (tech.drc["minwidth_metal2"] +
@ -73,10 +70,8 @@ class wire_test(unittest.TestCase):
[7 * min_space, 4 * min_space], [7 * min_space, 4 * min_space],
[-1 * min_space, 4 * min_space], [-1 * min_space, 4 * min_space],
[-1 * min_space, 0]] [-1 * min_space, 0]]
OPTS.check_lvsdrc = False
w = design.design("wire_test3") w = design.design("wire_test3")
wire.wire(w, layer_stack, position_list) wire.wire(w, layer_stack, position_list)
OPTS.check_lvsdrc = True
self.local_check(w) self.local_check(w)
@ -92,10 +87,8 @@ class wire_test(unittest.TestCase):
[7 * min_space, 4 * min_space], [7 * min_space, 4 * min_space],
[-1 * min_space, 4 * min_space], [-1 * min_space, 4 * min_space],
[-1 * min_space, 0]] [-1 * min_space, 0]]
OPTS.check_lvsdrc = False
w = design.design("wire_test4") w = design.design("wire_test4")
wire.wire(w, layer_stack, position_list) wire.wire(w, layer_stack, position_list)
OPTS.check_lvsdrc = True
self.local_check(w) self.local_check(w)
min_space = 2 * (tech.drc["minwidth_metal2"] + 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, 4 * min_space],
[-1 * min_space, 0]] [-1 * min_space, 0]]
position_list.reverse() position_list.reverse()
OPTS.check_lvsdrc = False
w = design.design("wire_test5") w = design.design("wire_test5")
wire.wire(w, layer_stack, position_list) wire.wire(w, layer_stack, position_list)
OPTS.check_lvsdrc = True
self.local_check(w) self.local_check(w)
min_space = 2 * (tech.drc["minwidth_metal2"] + 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, 4 * min_space],
[-1 * min_space, 0]] [-1 * min_space, 0]]
position_list.reverse() position_list.reverse()
OPTS.check_lvsdrc = False
w = design.design("wire_test6") w = design.design("wire_test6")
wire.wire(w, layer_stack, position_list) wire.wire(w, layer_stack, position_list)
OPTS.check_lvsdrc = True
self.local_check(w) self.local_check(w)
# return it back to it's normal state # return it back to it's normal state

View File

@ -31,8 +31,9 @@ class nand_2_test(unittest.TestCase):
debug.info(2, "Checking 2-input nand gate") debug.info(2, "Checking 2-input nand gate")
tx = nand_2.nand_2(nmos_width=2 * tech.drc["minwidth_tx"]) tx = nand_2.nand_2(nmos_width=2 * tech.drc["minwidth_tx"])
OPTS.check_lvsdrc = True
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -28,9 +28,9 @@ class nand_3_test(unittest.TestCase):
debug.info(2, "Checking 3-input nand gate") debug.info(2, "Checking 3-input nand gate")
tx = nand_3.nand_3(nmos_width=3 * tech.drc["minwidth_tx"]) tx = nand_3.nand_3(nmos_width=3 * tech.drc["minwidth_tx"])
self.local_check(tx)
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True
self.local_check(tx)
globals.end_openram() globals.end_openram()
def local_check(self, tx): def local_check(self, tx):

View File

@ -30,8 +30,9 @@ class nor_2_test(unittest.TestCase):
debug.info(2, "Checking 2-input nor gate") debug.info(2, "Checking 2-input nor gate")
tx = nor_2.nor_2(nmos_width=2 * tech.drc["minwidth_tx"]) tx = nor_2.nor_2(nmos_width=2 * tech.drc["minwidth_tx"])
OPTS.check_lvsdrc = True
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, tx): def local_check(self, tx):

View File

@ -20,28 +20,24 @@ class pinv_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import pinv import pinv
import tech import tech
# debug.info(2, "Checking min size inverter") 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"])
# 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
self.local_check(tx) 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() globals.end_openram()
def local_check(self, tx): def local_check(self, tx):
@ -57,6 +53,10 @@ class pinv_test(unittest.TestCase):
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) os.remove(tempgds)
# reset the static duplicate name checker for unit tests
import design
design.design.name_map=[]

View File

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

View File

@ -28,12 +28,12 @@ class wordline_driver_test(unittest.TestCase):
import tech import tech
debug.info(2, "Checking driver") 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 OPTS.check_lvsdrc = True
self.local_check(tx)
globals.end_openram() globals.end_openram()
def local_check(self, tx): def local_check(self, tx):
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds" tempgds = OPTS.openram_temp + "temp.gds"

View File

@ -25,12 +25,11 @@ class array_test(unittest.TestCase):
import bitcell_array import bitcell_array
debug.info(2, "Testing 3x3 array for 6t_cell") debug.info(2, "Testing 4x4 array for 6t_cell")
a = bitcell_array.bitcell_array(name="bitcell_array", cols=3, rows=3) a = bitcell_array.bitcell_array(name="bitcell_array", cols=4, rows=4)
self.local_check(a)
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True
self.local_check(a)
globals.end_openram() globals.end_openram()
def local_check(self, a): def local_check(self, a):

View File

@ -18,50 +18,32 @@ class hierarchical_decoder_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import hierarchical_decoder import hierarchical_decoder
import tech import tech
debug.info(1, "Testing 4 row sample for hierarchical_decoder") debug.info(1, "Testing 4 row sample for hierarchical_decoder")
OPTS.check_lvsdrc = False a = hierarchical_decoder.hierarchical_decoder(rows=4)
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
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 8 row sample for hierarchical_decoder") debug.info(1, "Testing 8 row sample for hierarchical_decoder")
OPTS.check_lvsdrc = False a = hierarchical_decoder.hierarchical_decoder(rows=8)
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
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 32 row sample for hierarchical_decoder") debug.info(1, "Testing 32 row sample for hierarchical_decoder")
OPTS.check_lvsdrc = False a = hierarchical_decoder.hierarchical_decoder(rows=32)
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
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder") debug.info(1, "Testing 128 row sample for hierarchical_decoder")
OPTS.check_lvsdrc = False a = hierarchical_decoder.hierarchical_decoder(rows=128)
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
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 512 row sample for hierarchical_decoder") debug.info(1, "Testing 512 row sample for hierarchical_decoder")
OPTS.check_lvsdrc = False a = hierarchical_decoder.hierarchical_decoder(rows=512)
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
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, a): def local_check(self, a):
@ -77,6 +59,11 @@ class hierarchical_decoder_test(unittest.TestCase):
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) 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 # instantiate a copdsay of the class to actually run the test
if __name__ == "__main__": if __name__ == "__main__":
(OPTS, args) = globals.parse_args() (OPTS, args) = globals.parse_args()

View File

@ -24,12 +24,11 @@ class hierarchical_predecode2x4_test(unittest.TestCase):
import hierarchical_predecode2x4 as pre import hierarchical_predecode2x4 as pre
import tech import tech
debug.info(1, "Testing sample for hierarchy_decoder") debug.info(1, "Testing sample for hierarchy_predecode2x4")
a = pre.hierarchical_predecode2x4(nmos_width=2 * tech.drc["minwidth_tx"], a = pre.hierarchical_predecode2x4()
cellname="test_pre2x4")
OPTS.check_lvsdrc = True
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, a): def local_check(self, a):

View File

@ -24,12 +24,11 @@ class hierarchical_predecode3x8_test(unittest.TestCase):
import hierarchical_predecode3x8 as pre import hierarchical_predecode3x8 as pre
import tech import tech
debug.info(1, "Testing sample for hierarchy_decoder") debug.info(1, "Testing sample for hierarchy_predecode3x8")
a = pre.hierarchical_predecode3x8(nmos_width=3 * tech.drc["minwidth_tx"], a = pre.hierarchical_predecode3x8()
cellname="test_pre3x8")
OPTS.check_lvsdrc = True
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, a): def local_check(self, a):

View File

@ -22,12 +22,20 @@ class single_level_column_mux_test(unittest.TestCase):
OPTS.check_lvsdrc = False OPTS.check_lvsdrc = False
import single_level_column_mux_array import single_level_column_mux_array
debug.info(1, "Testing sample for columnmux_array") debug.info(1, "Testing sample for 2-way column_mux_array")
a = single_level_column_mux_array.single_level_column_mux_array( a = single_level_column_mux_array.single_level_column_mux_array(columns=16, word_size=8)
rows=32, columns=32, word_size=16)
OPTS.check_lvsdrc = True
self.local_check(a) 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() globals.end_openram()
def local_check(self, a): def local_check(self, a):
@ -43,7 +51,9 @@ class single_level_column_mux_test(unittest.TestCase):
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) 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 # instantiate a copdsay of the class to actually run the test
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -20,17 +20,16 @@ class precharge_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import precharge_array import precharge_array
import tech import tech
debug.info(2, "Checking 3 column precharge") debug.info(2, "Checking 3 column precharge")
OPTS.check_lvsdrc = False pc = precharge_array.precharge_array(columns=3, ptx_width=tech.drc["minwidth_tx"], beta=2)
pc = precharge_array.precharge_array(
name="precharge_array", columns=3, ptx_width=tech.drc["minwidth_tx"], beta=2)
OPTS.check_lvsdrc = True
self.local_check(pc) self.local_check(pc)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, pc): def local_check(self, pc):

View File

@ -20,16 +20,20 @@ class sense_amp_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import sense_amp_array import sense_amp_array
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") 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) a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2)
OPTS.check_lvsdrc = True
self.local_check(a) 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() globals.end_openram()
def local_check(self, a): def local_check(self, a):
@ -45,6 +49,11 @@ class sense_amp_test(unittest.TestCase):
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) 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 # instantiate a copy of the class to actually run the test
if __name__ == "__main__": if __name__ == "__main__":
(OPTS, args) = globals.parse_args() (OPTS, args) = globals.parse_args()

View File

@ -20,15 +20,19 @@ class write_driver_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import write_driver_array import write_driver_array
debug.info(2, "Testing write_driver_array for columns=128, word_size=128") debug.info(2, "Testing write_driver_array for columns=8, word_size=8")
OPTS.check_lvsdrc = False a = write_driver_array.write_driver_array(columns=8, word_size=8)
a = write_driver_array.write_driver_array(columns=16, word_size=16)
OPTS.check_lvsdrc = True
self.local_check(a) 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() globals.end_openram()
def local_check(self, a): def local_check(self, a):
@ -44,6 +48,11 @@ class write_driver_test(unittest.TestCase):
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) 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 # instantiate a copy of the class to actually run the test
if __name__ == "__main__": if __name__ == "__main__":
(OPTS, args) = globals.parse_args() (OPTS, args) = globals.parse_args()

View File

@ -21,16 +21,19 @@ class dff_array_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import ms_flop_array import ms_flop_array
debug.info(1, "Testing sample for dff_array") debug.info(2, "Testing ms_flop_array for columns=8, word_size=8")
OPTS.check_lvsdrc = False a = ms_flop_array.ms_flop_array(columns=8, word_size=8)
a = ms_flop_array.ms_flop_array(
name="test1", columns=64, word_size=32)
OPTS.check_lvsdrc = True
self.local_check(a) 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() globals.end_openram()
def local_check(self, a): def local_check(self, a):
@ -46,6 +49,11 @@ class dff_array_test(unittest.TestCase):
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) 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 # instantiate a copdsay of the class to actually run the test
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -23,11 +23,15 @@ class tri_gate_array_test(unittest.TestCase):
import tri_gate_array import tri_gate_array
debug.info(1, "Testing sample for tri_gate_array") debug.info(1, "Testing tri_gate_array for columns=8, word_size=8")
a = tri_gate_array.tri_gate_array(columns=16, word_size=16) a = tri_gate_array.tri_gate_array(columns=8, word_size=8)
OPTS.check_lvsdrc = True
self.local_check(a) 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() globals.end_openram()
def local_check(self, a): def local_check(self, a):
@ -43,6 +47,11 @@ class tri_gate_array_test(unittest.TestCase):
os.remove(tempspice) os.remove(tempspice)
os.remove(tempgds) 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 # instantiate a copdsay of the class to actually run the test
if __name__ == "__main__": if __name__ == "__main__":
(OPTS, args) = globals.parse_args() (OPTS, args) = globals.parse_args()

View File

@ -27,10 +27,10 @@ class replica_bitline_test(unittest.TestCase):
import replica_bitline import replica_bitline
debug.info(2, "Testing RBL") debug.info(2, "Testing RBL")
a = replica_bitline.replica_bitline("chain", 13) a = replica_bitline.replica_bitline(13)
OPTS.check_lvsdrc = True
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, a): def local_check(self, a):

View File

@ -16,21 +16,20 @@ OPTS = globals.get_opts()
#@unittest.skip("SKIPPING 14_delay_chain_test") #@unittest.skip("SKIPPING 14_delay_chain_test")
class logic_effort_dc_test(unittest.TestCase): class delay_chain_test(unittest.TestCase):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
# we will manually run lvs/drc # we will manually run lvs/drc
OPTS.check_lvsdrc = False OPTS.check_lvsdrc = False
import logic_effort_dc import delay_chain
debug.info(2, "Testing delay_chain") debug.info(2, "Testing delay_chain")
a = logic_effort_dc.logic_effort_dc( a = delay_chain.delay_chain(fanout_list=[4, 4, 4, 4])
"chain", stage_list=[4, 4, 4, 4, 4])
OPTS.check_lvsdrc = True
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, a): def local_check(self, a):

View File

@ -26,9 +26,9 @@ class control_logic_test(unittest.TestCase):
debug.info(1, "Testing sample for control_logic") debug.info(1, "Testing sample for control_logic")
a = control_logic.control_logic(num_rows=128) a = control_logic.control_logic(num_rows=128)
OPTS.check_lvsdrc = True
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, a): def local_check(self, a):

View File

@ -25,16 +25,23 @@ class bank_test(unittest.TestCase):
import bank import bank
# override these from the config file debug.info(1, "No column mux")
OPTS.word_size=8 a = bank.bank(word_size=4, num_words=64, words_per_row=2, num_banks=1, name="test_sram1")
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
self.local_check(a) 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() globals.end_openram()
def local_check(self, a): def local_check(self, a):

View File

@ -27,9 +27,9 @@ class sram_1bank_test(unittest.TestCase):
debug.info(1, "Testing sample 8bit, 64word SRAM, 1 bank") 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") a = sram.sram(word_size=8, num_words=128, num_banks=1, name="test_sram1")
OPTS.check_lvsdrc = True
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, a): def local_check(self, a):

View File

@ -27,9 +27,9 @@ class sram_2bank_test(unittest.TestCase):
debug.info(1, "Testing sample 8bit, 128word SRAM, 2 banks") 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") a = sram.sram(word_size=8, num_words=128, num_banks=2, name="test_sram1")
OPTS.check_lvsdrc = True
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, a): def local_check(self, a):

View File

@ -27,9 +27,9 @@ class sram_4bank_test(unittest.TestCase):
debug.info(1, "Testing sample 8bit, 128word SRAM, 4 banks") 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") a = sram.sram(word_size=8, num_words=128, num_banks=4, name="test_sram1")
OPTS.check_lvsdrc = True
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
def local_check(self, a): def local_check(self, a):

View File

@ -22,6 +22,7 @@ write_driver_array = "write_driver_array"
tri_gate = "tri_gate" tri_gate = "tri_gate"
tri_gate_array = "tri_gate_array" tri_gate_array = "tri_gate_array"
wordline_driver = "wordline_driver" wordline_driver = "wordline_driver"
replica_bitline = "replica_bitline"
replica_bitcell = "replica_bitcell" replica_bitcell = "replica_bitcell"
bitcell = "bitcell" bitcell = "bitcell"
delay_chain = "logic_effort_dc" delay_chain = "delay_chain"

View File

@ -22,6 +22,7 @@ write_driver_array = "write_driver_array"
tri_gate = "tri_gate" tri_gate = "tri_gate"
tri_gate_array = "tri_gate_array" tri_gate_array = "tri_gate_array"
wordline_driver = "wordline_driver" wordline_driver = "wordline_driver"
replica_bitline = "replica_bitline"
replica_bitcell = "replica_bitcell" replica_bitcell = "replica_bitcell"
bitcell = "bitcell" bitcell = "bitcell"
delay_chain = "logic_effort_dc" delay_chain = "delay_chain"

View File

@ -10,16 +10,22 @@ class tri_gate(design.design):
netlist should be available in the technology library. netlist should be available in the technology library.
""" """
pins = ["in", "en", "en_bar", "out", "gnd", "vdd"] pin_names = ["in", "en", "en_bar", "out", "gnd", "vdd"]
chars = utils.auto_measure_libcell(pins, "tri_gate", GDS["unit"], layer["boundary"]) (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) design.design.__init__(self, name)
debug.info(2, "Create tri_gate object") debug.info(2, "Create tri_gate object")
self.width = tri_gate.width
self.width = tri_gate.chars["width"] self.height = tri_gate.height
self.height = tri_gate.chars["height"] self.pin_map = tri_gate.pin_map
def delay(self, slew, load=0.0): def delay(self, slew, load=0.0):
from tech import spice from tech import spice

View File

@ -16,119 +16,113 @@ class tri_gate_array(design.design):
c = reload(__import__(OPTS.config.tri_gate)) c = reload(__import__(OPTS.config.tri_gate))
self.mod_tri_gate = getattr(c, 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.columns = columns
self.word_size = word_size 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.create_layout()
self.DRC_LVS() self.DRC_LVS()
def create_layout(self): def create_layout(self):
"""generate layout """ """generate layout """
self.add_modules()
self.setup_layout_constants()
self.add_pins() self.add_pins()
self.create_write_array() self.create_array()
self.add_metal_rails() self.add_layout_pins()
self.add_labels()
def add_pins(self): def add_pins(self):
"""create the name of pins depend on the word size""" """create the name of pins depend on the word size"""
for i in range(self.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): 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"]: for pin in ["en", "en_bar", "vdd", "gnd"]:
self.add_pin(pin) self.add_pin(pin)
def setup_layout_constants(self): def create_array(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):
"""add tri gate to the array """ """add tri gate to the array """
for i in range(self.word_size): self.tri_inst = {}
mirror = "R0" for i in range(0,self.columns,self.words_per_row):
if (i % 2 == 0): name = "Xtri_gate{0}".format(i)
name = "Xtri_gate{0}".format(i) if (i % 2 == 0 or self.words_per_row > 1):
x_off = i * self.tri.width * self.words_per_row base = vector(i*self.tri.width, 0)
mirror = "R0"
else: else:
name = "Xtri_gate{0}".format(i) base = vector((i+1)*self.tri.width, 0)
if (self.words_per_row == 1): mirror = "MY"
x_off = (i + 1) * self.tri.width * self.words_per_row self.tri_inst[i]=self.add_inst(name=name,
mirror = "MY" mod=self.tri,
else: offset=base,
x_off = i * self.tri.width * self.words_per_row mirror=mirror)
self.add_inst(name=name, self.connect_inst(["in[{0}]".format(i/self.words_per_row),
mod=self.tri, "out[{0}]".format(i/self.words_per_row),
offset=[x_off, 0],
mirror = mirror)
self.connect_inst(["in[{0}]".format(i),
"out[{0}]".format(i),
"en", "en_bar", "vdd", "gnd"]) "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): def add_layout_pins(self):
"""add label for pins"""
for i in range(self.word_size): for i in range(0,self.columns,self.words_per_row):
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)
pin_offset={} # Avoid duplicate pins by only doing even columns
for pin in ["en", "en_bar", "vdd", "gnd", "in", "out"]: for gnd_pin in self.tri_inst[i].get_pins("gnd"):
pin_offset[pin] = vector(x_off, 0) + dir_vector.scale(self.tri_gate_chars[pin]) 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"]: in_pin = self.tri_inst[i].get_pin("in")
self.add_label(text=pin, self.add_layout_pin(text="in[{0}]".format(i/self.words_per_row),
layer="metal1", layer="metal2",
offset=pin_offset[pin]) offset=in_pin.ll(),
self.add_label(text="gnd", width=in_pin.width(),
layer="metal2", height=in_pin.height())
offset=pin_offset["gnd"])
self.add_label(text="in[{0}]".format(i), out_pin = self.tri_inst[i].get_pin("out")
layer="metal2", self.add_layout_pin(text="out[{0}]".format(i/self.words_per_row),
offset=pin_offset["in"]) layer="metal2",
self.add_label(text="out[{0}]".format(i), offset=out_pin.ll(),
layer="metal2", width=out_pin.width(),
offset=pin_offset["out"]) 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): def delay(self, slew, load=0.0):
result = self.tri.delay(slew = slew, load = load) result = self.tri.delay(slew = slew, load = load)

View File

@ -2,6 +2,8 @@ import os
import gdsMill import gdsMill
import tech import tech
import globals import globals
from vector import vector
from pin_layout import pin_layout
OPTS = globals.OPTS OPTS = globals.OPTS
@ -20,14 +22,18 @@ def snap_to_grid(offset):
out_offset = [xoff, yoff] out_offset = [xoff, yoff]
return out_offset return out_offset
def pin_center(boundary):
def gds_pin_center(gdsPin):
""" """
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])] 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): 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. 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 [cell["width"], cell["height"]] = measure_result
for pin in pin_list: 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 return cell

View File

@ -41,10 +41,8 @@ class wire(path):
self.horiz_layer_width = drc["minwidth_{0}".format(horiz_layer)] self.horiz_layer_width = drc["minwidth_{0}".format(horiz_layer)]
via_connect = contact(self.layer_stack, via_connect = contact(self.layer_stack,
(1, 1)) (1, 1))
self.node_to_node = [drc["minwidth_" + str(self.horiz_layer_name)] \ self.node_to_node = [drc["minwidth_" + str(self.horiz_layer_name)] + via_connect.width,
+ via_connect.width, drc["minwidth_" + str(self.horiz_layer_name)] + via_connect.height]
drc["minwidth_" + str(self.horiz_layer_name)] \
+ via_connect.height]
# create a 1x1 contact # create a 1x1 contact
def create_vias(self): def create_vias(self):

View File

@ -15,8 +15,8 @@ class wordline_driver(design.design):
Generates the wordline-driver to drive the bitcell Generates the wordline-driver to drive the bitcell
""" """
def __init__(self, name, rows): def __init__(self, rows):
design.design.__init__(self, name) design.design.__init__(self, "wordline_driver")
self.rows = rows self.rows = rows
self.add_pins() self.add_pins()
@ -26,11 +26,11 @@ class wordline_driver(design.design):
def add_pins(self): def add_pins(self):
# inputs to wordline_driver. # inputs to wordline_driver.
for i in range(self.rows): 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. # Outputs from wordline_driver.
for i in range(self.rows): for i in range(self.rows):
self.add_pin("wl[{0}]".format(i)) self.add_pin("wl[{0}]".format(i))
self.add_pin("clk") self.add_pin("en")
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
@ -40,12 +40,11 @@ class wordline_driver(design.design):
self.create_layout() self.create_layout()
def add_layout(self): def add_layout(self):
self.inv = pinv(nmos_width=drc["minwidth_tx"], self.inv = pinv()
beta=parameter["pinv_beta"])
self.add_mod(self.inv) self.add_mod(self.inv)
self.NAND2 = nand_2(nmos_width=2*drc["minwidth_tx"]) self.nand2 = nand_2()
self.add_mod(self.NAND2) self.add_mod(self.nand2)
@ -53,87 +52,80 @@ class wordline_driver(design.design):
def offsets_of_gates(self): def offsets_of_gates(self):
self.x_offset0 = 2 * drc["minwidth_metal1"] + 5 * drc["metal1_to_metal1"] self.x_offset0 = 2 * drc["minwidth_metal1"] + 5 * drc["metal1_to_metal1"]
self.x_offset1 = self.x_offset0 + self.inv.width 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.width = self.x_offset2 + self.inv.width
self.height = self.inv.height * self.rows 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): def create_layout(self):
# Clk connection # Wordline enable connection
self.add_rect(layer="metal1", self.add_layout_pin(text="en",
offset=[drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"], layer="metal2",
2 * drc["minwidth_metal1"]], offset=[drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"],0],
width=drc["minwidth_metal1"], width=drc["minwidth_metal2"],
height=self.height + 4*drc["minwidth_metal1"]) height=self.height)
self.clk_positions.append([drc["minwidth_metal1"] + 2*drc["metal1_to_metal1"],
self.height]) self.add_layout_pin(text="gnd",
self.add_label(text="clk", layer="metal1",
layer="metal1", offset=[0, -0.5*drc["minwidth_metal1"]],
offset=self.clk_positions[0]) width=self.x_offset0,
height=drc["minwidth_metal1"])
for row in range(self.rows): for row in range(self.rows):
name_inv1 = "Wordline_driver_inv_clk%d" % (row) name_inv1 = "wl_driver_inv_en{}".format(row)
name_nand = "Wordline_driver_nand%d" % (row) name_nand = "wl_driver_nand{}".format(row)
name_inv2 = "Wordline_driver_inv%d" % (row) name_inv2 = "wl_driver_inv{}".format(row)
# Extend vdd and gnd of Wordline_driver inv_nand2B_connection_height = (abs(self.inv.get_pin("Z").ll().y
yoffset = (row + 1) * self.inv.height - 0.5 * drc["minwidth_metal2"] - self.nand2.get_pin("B").ll().y)
self.add_rect(layer="metal2", + drc["minwidth_metal1"])
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"])
if (row % 2): if (row % 2):
y_offset = self.inv.height*(row + 1) 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" inst_mirror = "MX"
cell_dir = vector(0,-1) cell_dir = vector(0,-1)
m1tm2_rotate=270 m1tm2_rotate=270
m1tm2_mirror="R0" m1tm2_mirror="R0"
else: else:
y_offset = self.inv.height*row 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" inst_mirror = "R0"
cell_dir = vector(0,1) cell_dir = vector(0,1)
m1tm2_rotate=90 m1tm2_rotate=90
m1tm2_mirror="MX" 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 # add inv1 based on the info above
self.add_inst(name=name_inv1, self.add_inst(name=name_inv1,
mod=self.inv, mod=self.inv,
offset=name_inv1_offset, offset=name_inv1_offset,
mirror=inst_mirror ) mirror=inst_mirror )
self.connect_inst(["clk", "clk_bar[{0}]".format(row), self.connect_inst(["en", "en_bar[{0}]".format(row),
"vdd", "gnd"]) "vdd", "gnd"])
# add nand 2 # add nand 2
self.add_inst(name=name_nand, self.add_inst(name=name_nand,
mod=self.NAND2, mod=self.nand2,
offset=nand2_offset, offset=nand2_offset,
mirror=inst_mirror) mirror=inst_mirror)
self.connect_inst(["decode_out[{0}]".format(row), self.connect_inst(["in[{0}]".format(row),
"clk_bar[{0}]".format(row), "en_bar[{0}]".format(row),
"net[{0}]".format(row), "net[{0}]".format(row),
"vdd", "gnd"]) "vdd", "gnd"])
# add inv2 # add inv2
@ -146,73 +138,65 @@ class wordline_driver(design.design):
"vdd", "gnd"]) "vdd", "gnd"])
# clk connection # clk connection
clk_offset= [drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"], clk_offset= vector(drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"],
y_offset + cell_dir.y * self.inv.A_position.y] y_offset + cell_dir.y * self.inv.get_pin("A").by())
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=clk_offset, offset=clk_offset,
width=self.x_offset0 - 2*drc["metal1_to_metal1"], width=self.x_offset0 - 2*drc["metal1_to_metal1"],
height=cell_dir.y *drc["minwidth_metal1"]) height=cell_dir.y *drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=clk_offset)
# first inv to nand2 B # first inv to nand2 B
inv_to_nand2B_offset = [self.x_offset1 - drc["minwidth_metal1"], 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", self.add_rect(layer="metal1",
offset=inv_to_nand2B_offset, offset=inv_to_nand2B_offset,
width=drc["minwidth_metal1"], width=drc["minwidth_metal1"],
height=cell_dir.y*inv_nand2B_connection_height) height=cell_dir.y*inv_nand2B_connection_height)
# Nand2 out to 2nd inv # Nand2 out to 2nd inv
nand2_to_2ndinv_offset =[self.x_offset2, 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", self.add_rect(layer="metal1",
offset=nand2_to_2ndinv_offset, offset=nand2_to_2ndinv_offset,
width=drc["minwidth_metal1"], width=drc["minwidth_metal1"],
height=cell_dir.y * 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", self.add_rect(layer="metal2",
offset=[0, y_offset + cell_dir.y * self.NAND2.A_position.y], offset=mid_via_offset,
width=self.x_offset1, width=self.x_offset1-mid_via_offset.x,
height=cell_dir.y*drc["minwidth_metal2"]) height=cell_dir.y*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"), self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.x_offset1, 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, rotate=m1tm2_rotate,
mirror=m1tm2_mirror) 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): def delay(self, slew, load=0):
# decode_out -> net # decode -> net
decode_t_net = self.NAND2.delay(slew, self.inv.input_load()) decode_t_net = self.nand2.delay(slew, self.inv.input_load())
# net -> wl # net -> wl
net_t_wl = self.inv.delay(decode_t_net.slew, load) net_t_wl = self.inv.delay(decode_t_net.slew, load)
@ -221,4 +205,4 @@ class wordline_driver(design.design):
return result return result
def input_load(self): def input_load(self):
return self.NAND2.input_load() return self.nand2.input_load()

View File

@ -11,13 +11,15 @@ class write_driver(design.design):
the technology library. the technology library.
""" """
pins = ["din", "BL", "BR", "en", "gnd", "vdd"] pin_names = ["din", "BL", "BR", "en", "gnd", "vdd"]
chars = utils.auto_measure_libcell(pins, "write_driver", GDS["unit"], layer["boundary"]) (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): def __init__(self, name):
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(2, "Create write_driver object") debug.info(2, "Create write_driver object")
self.width = write_driver.chars["width"] self.width = write_driver.width
self.height = write_driver.chars["height"] self.height = write_driver.height
self.pin_map = write_driver.pin_map

View File

@ -17,126 +17,106 @@ class write_driver_array(design.design):
c = reload(__import__(OPTS.config.write_driver)) c = reload(__import__(OPTS.config.write_driver))
self.mod_write_driver = getattr(c, 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.columns = columns
self.word_size = word_size self.word_size = word_size
self.words_per_row = columns / 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.add_pins()
self.create_layout() self.create_layout()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
for i in range(self.word_size): for i in range(0,self.columns,self.words_per_row):
self.add_pin("data_in[{0}]".format(i)) self.add_pin("data[{0}]".format(i/self.words_per_row))
if (self.words_per_row == 1): self.add_pin("bl[{0}]".format(i))
for i in range(self.word_size): self.add_pin("br[{0}]".format(i))
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))
self.add_pin("wen") self.add_pin("wen")
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def create_layout(self): def create_layout(self):
self.add_write_driver_module()
self.setup_layout_constants()
self.create_write_array() self.create_write_array()
self.add_metal_rails() self.add_layout_pins()
self.add_labels() #self.offset_all_coordinates()
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 = []
def create_write_array(self): def create_write_array(self):
for i in range(self.word_size): for i in range(0,self.columns,self.words_per_row):
name = "Xwrite_driver%d" % i name = "Xwrite_driver{}".format(i)
x_off = (i* self.driver.width * self.words_per_row) if (i % 2 == 0 or self.words_per_row>1):
self.driver_positions.append(vector(x_off, 0)) 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, self.add_inst(name=name,
mod=self.driver, mod=self.driver,
offset=[x_off, 0]) offset=base,
if (self.words_per_row == 1): mirror=mirror)
self.connect_inst(["data_in[{0}]".format(i), self.connect_inst(["data[{0}]".format(i/self.words_per_row),
"bl[{0}]".format(i), "bl[{0}]".format(i/self.words_per_row),
"br[{0}]".format(i), "br[{0}]".format(i/self.words_per_row),
"wen", "vdd", "gnd"]) "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: else:
self.connect_inst(["data_in[{0}]".format(i), base = vector((i+1)*self.driver.width, 0)
"bl_out[{0}]".format(i * self.words_per_row), x_dir = -1
"br_out[{0}]".format(i * self.words_per_row),
"wen", "vdd", "gnd"]) 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): self.add_layout_pin(text="data[{0}]".format(i/self.words_per_row),
base = vector(0, - 0.5*drc["minwidth_metal1"]) layer="metal2",
self.add_rect(layer="metal1", offset=din_offset,
offset=base + vector(self.write_driver_chars["en"]).scale(0, 1), width=x_dir*din_pin.width(),
width=self.width - (self.words_per_row - 1) * self.driver.width, height=din_pin.height())
height=drc['minwidth_metal1']) self.add_layout_pin(text="bl[{0}]".format(i/self.words_per_row),
self.add_rect(layer="metal1", layer="metal2",
offset=base + vector(self.write_driver_chars["vdd"]).scale(0, 1), offset=bl_offset,
width=self.width, width=x_dir*bl_pin.width(),
height=drc['minwidth_metal1']) height=bl_pin.height())
self.add_rect(layer="metal1",
offset=base + vector(self.write_driver_chars["gnd"]).scale(0, 1), self.add_layout_pin(text="br[{0}]".format(i/self.words_per_row),
width=self.width, layer="metal2",
height=drc['minwidth_metal1']) offset=br_offset,
width=x_dir*br_pin.width(),
height=br_pin.height())
def add_labels(self): self.add_layout_pin(text="wen",
for i in range(self.word_size): layer="metal1",
base = vector(i * self.driver.width * self.words_per_row, 0) offset=self.driver.get_pin("en").ll().scale(0,1),
BL_offset = base + self.write_driver_chars["BL"] width=self.width - (self.words_per_row - 1) * self.driver.width,
BR_offset = base + self.write_driver_chars["BR"] height=drc['minwidth_metal1'])
self.add_label(text="data_in[{0}]".format(i), self.add_layout_pin(text="vdd",
layer="metal2", layer="metal1",
offset=base + self.write_driver_chars["din"]) offset=self.driver.get_pin("vdd").ll().scale(0,1),
if (self.words_per_row == 1): width=self.width,
self.add_label(text="bl[{0}]".format(i), height=drc['minwidth_metal1'])
layer="metal2",
offset=BL_offset) self.add_layout_pin(text="gnd",
self.add_label(text="br[{0}]".format(i), layer="metal1",
layer="metal2", offset=self.driver.get_pin("gnd").ll().scale(0,1),
offset=BR_offset) width=self.width,
else: height=drc['minwidth_metal1'])
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))

View File

@ -85,7 +85,7 @@ drc["minwidth_tx"]=0.09
drc["minlength_channel"] = 0.05 drc["minlength_channel"] = 0.05
#well rules #well rules
drc["pwell_enclose_nwell"] = 0.225 drc["pwell_to_nwell"] = 0.225
drc["minwidth_well"] = 0.2 drc["minwidth_well"] = 0.2
#poly rules #poly rules

View File

@ -70,7 +70,7 @@ drc["minwidth_tx"] = 1.2
drc["minlength_channel"] = 0.6 drc["minlength_channel"] = 0.6
#well rules #well rules
drc["pwell_enclose_nwell"] = 0 drc["pwell_to_nwell"] = 0
drc["minwidth_well"] = 3.6 drc["minwidth_well"] = 3.6
#poly rules #poly rules