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
*~
*.pyc
*.log

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -13,7 +13,7 @@ class bitcell_array(design.design):
Connects the word lines and bit lines.
"""
def __init__(self, name, cols, rows):
def __init__(self, cols, rows, name="bitcell_array"):
design.design.__init__(self, name)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
@ -23,11 +23,15 @@ class bitcell_array(design.design):
c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
self.bitcell_chars = self.mod_bitcell.chars
self.cell = self.mod_bitcell()
self.add_mod(self.cell)
self.height = self.row_size*self.cell.height
self.width = self.column_size*self.cell.width
self.add_pins()
self.create_layout()
self.add_labels()
self.add_layout_pins()
self.DRC_LVS()
def add_pins(self):
@ -40,26 +44,8 @@ class bitcell_array(design.design):
self.add_pin("gnd")
def create_layout(self):
self.create_cell()
self.setup_layout_constants()
self.add_cells()
self.offset_all_coordinates()
def setup_layout_constants(self):
self.vdd_positions = []
self.gnd_positions = []
self.BL_positions = []
self.BR_positions = []
self.WL_positions = []
self.height = self.row_size * self.cell.height
self.width = self.column_size * self.cell.width
def create_cell(self):
self.cell = self.mod_bitcell()
self.add_mod(self.cell)
def add_cells(self):
xoffset = 0.0
self.cell_inst = {}
for col in range(self.column_size):
yoffset = 0.0
for row in range(self.row_size):
@ -70,79 +56,110 @@ class bitcell_array(design.design):
dir_key = "MX"
else:
tempy = yoffset
dir_key = "R0"
dir_key = ""
if OPTS.trim_noncritical == True:
if row == self.row_size - 1:
self.add_inst(name=name,
mod=self.cell,
offset=[xoffset, tempy],
mirror=dir_key)
self.connect_inst(["bl[{0}]".format(col),
"br[{0}]".format(col),
"wl[{0}]".format(row),
"vdd",
"gnd"])
else:
self.add_inst(name=name,
mod=self.cell,
offset=[xoffset, tempy],
mirror=dir_key)
self.connect_inst(["bl[{0}]".format(col),
"br[{0}]".format(col),
"wl[{0}]".format(row),
"vdd",
"gnd"])
self.cell_inst[row,col]=self.add_inst(name=name,
mod=self.cell,
offset=[xoffset, tempy],
mirror=dir_key)
self.connect_inst(["bl[{0}]".format(col),
"br[{0}]".format(col),
"wl[{0}]".format(row),
"vdd",
"gnd"])
yoffset += self.cell.height
xoffset += self.cell.width
def add_labels(self):
def add_layout_pins(self):
# Our cells have multiple gnd pins for now.
# FIXME: fix for multiple vdd too
vdd_pin = self.cell.get_pin("vdd")
# shift it up by the overlap amount (gnd_pin) too
# must find the lower gnd pin to determine this overlap
lower_y = self.cell.height
gnd_pins = self.cell.get_pins("gnd")
for gnd_pin in gnd_pins:
if gnd_pin.layer=="metal2" and gnd_pin.by()<lower_y:
lower_y=gnd_pin.by()
# lower_y is negative, so subtract off double this amount for each pair of
# overlapping cells
full_height = self.height - 2*lower_y
vdd_pin = self.cell.get_pin("vdd")
lower_x = vdd_pin.lx()
# lower_x is negative, so subtract off double this amount for each pair of
# overlapping cells
full_width = self.width - 2*lower_x
offset = vector(0.0, 0.0)
for col in range(self.column_size):
offset.y = 0.0
self.add_label(text="bl[{0}]".format(col),
layer="metal2",
offset=offset + vector(self.bitcell_chars["BL"][0],0))
self.add_label(text="br[{0}]".format(col),
layer="metal2",
offset=offset + vector(self.bitcell_chars["BR"][0],0))
self.BL_positions.append(offset + vector(self.bitcell_chars["BL"][0],0))
self.BR_positions.append(offset + vector(self.bitcell_chars["BR"][0],0))
# get the pin of the lower row cell and make it the full width
bl_pin = self.cell_inst[0,col].get_pin("BL")
br_pin = self.cell_inst[0,col].get_pin("BR")
self.add_layout_pin(text="bl[{0}]".format(col),
layer="metal2",
offset=bl_pin.ll(),
width=bl_pin.width(),
height=full_height)
self.add_layout_pin(text="br[{0}]".format(col),
layer="metal2",
offset=br_pin.ll(),
width=br_pin.width(),
height=full_height)
# gnd offset is 0 in our cell, but it be non-zero
self.add_label(text="gnd",
layer="metal2",
offset=offset + vector(self.bitcell_chars["gnd"][0],0))
self.gnd_positions.append(offset + vector(self.bitcell_chars["gnd"][0],0))
for row in range(self.row_size):
# only add row labels on the left most column
if col == 0:
# flipped row
if row % 2:
base_offset = offset + vector(0, self.cell.height)
vdd_offset = base_offset - vector(0,self.bitcell_chars["vdd"][1])
wl_offset = base_offset - vector(0,self.bitcell_chars["WL"][1])
# unflipped row
else:
vdd_offset = offset + vector(0,self.bitcell_chars["vdd"][1])
wl_offset = offset + vector(0,self.bitcell_chars["WL"][1])
# add vdd label and offset
self.add_label(text="vdd",
layer="metal1",
offset=vdd_offset)
self.vdd_positions.append(vdd_offset)
# add gnd label and offset
self.add_label(text="wl[{0}]".format(row),
layer="metal1",
offset=wl_offset)
self.WL_positions.append(wl_offset)
# increments to the next row height
offset.y += self.cell.height
gnd_pins = self.cell_inst[0,col].get_pins("gnd")
for gnd_pin in gnd_pins:
# avoid duplicates by only doing even rows
# also skip if it is not the full height (a through rail)
if gnd_pin.layer=="metal2" and col%2 == 0 and gnd_pin.height()>=self.cell.height:
self.add_layout_pin(text="gnd",
layer="metal2",
offset=gnd_pin.ll(),
width=gnd_pin.width(),
height=full_height)
# increments to the next column width
offset.x += self.cell.width
offset.x = 0.0
for row in range(self.row_size):
wl_pin = self.cell_inst[row,0].get_pin("WL")
vdd_pins = self.cell_inst[row,0].get_pins("vdd")
gnd_pins = self.cell_inst[row,0].get_pins("gnd")
for gnd_pin in gnd_pins:
if gnd_pin.layer=="metal1":
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll(),
width=full_width,
height=drc["minwidth_metal1"])
# add vdd label and offset
# only add to even rows to avoid duplicates
for vdd_pin in vdd_pins:
if row % 2 == 0 and vdd_pin.layer=="metal1":
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll(),
width=full_width,
height=drc["minwidth_metal1"])
# add wl label and offset
self.add_layout_pin(text="wl[{0}]".format(row),
layer="metal1",
offset=wl_pin.ll(),
width=full_width,
height=wl_pin.height())
# increments to the next row height
offset.y += self.cell.height
def delay(self, slew, load=0):
from tech import drc
wl_wire = self.gen_wl_wire()

View File

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

View File

@ -31,16 +31,11 @@ class contact(design.design):
self.create_contact_array()
self.create_first_layer_enclosure()
self.create_second_layer_enclosure()
self.offset_all_coordinates()
def offset_all_coordinates(self):
coordinate = self.find_lowest_coords()
self.offset_attributes(coordinate)
self.translate(coordinate)
self.height = max(obj.offset.y + obj.height for obj in self.objs)
self.width = max(obj.offset.x + obj.width for obj in self.objs)
def setup_layers(self):
(first_layer, via_layer, second_layer) = self.layer_stack
self.first_layer_name = first_layer
@ -51,49 +46,50 @@ class contact(design.design):
self.contact_width = drc["minwidth_{0}". format(self.via_layer_name)]
self.contact_to_contact = drc["{0}_to_{0}".format(self.via_layer_name)]
self.contact_pitch = self.contact_width + self.contact_to_contact
self.contact_array_width = self.contact_width \
+ (self.dimensions[0] - 1) * self.contact_pitch
self.contact_array_height = self.contact_width \
+ (self.dimensions[1] - 1) * self.contact_pitch
self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch
self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch
# FIME break this up
self.first_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.first_layer_name)] - self.contact_array_width) / 2,
drc["{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)])
self.first_layer_vertical_enclosure = max((drc["minarea_{0}".format(self.first_layer_name)]
/ (self.contact_array_width + 2 * self.first_layer_horizontal_enclosure) - self.contact_array_height) / 2,
(drc["minheight_{0}".format(
self.first_layer_name)] - self.contact_array_height) / 2,
(drc["minheight_{0}".format(self.first_layer_name)] - self.contact_array_height) / 2,
drc["{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)])
self.second_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.second_layer_name)] - self.contact_array_width) / 2,
drc["{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)])
self.second_layer_vertical_enclosure = max((drc["minarea_{0}".format(self.second_layer_name)]
/ (self.contact_array_width + 2 * self.second_layer_horizontal_enclosure) - self.contact_array_height) / 2,
(drc["minheight_{0}".format(
self.second_layer_name)] - self.contact_array_height) / 2,
(drc["minheight_{0}".format(self.second_layer_name)] - self.contact_array_height) / 2,
drc["{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)])
# offset for the via array
self.via_layer_position =vector(max(self.first_layer_horizontal_enclosure,self.second_layer_horizontal_enclosure),
max(self.first_layer_vertical_enclosure,self.second_layer_vertical_enclosure))
# this is if the first and second layers are different
self.first_layer_position = vector(max(self.second_layer_horizontal_enclosure - self.first_layer_horizontal_enclosure,0),
max(self.second_layer_vertical_enclosure - self.first_layer_vertical_enclosure,0))
self.second_layer_position = vector(max(self.first_layer_horizontal_enclosure - self.second_layer_horizontal_enclosure,0),
max(self.first_layer_vertical_enclosure - self.second_layer_vertical_enclosure,0))
def create_contact_array(self):
""" Create the contact array at the origin"""
self.via_layer_position = vector(0, 0)
for i in range(self.dimensions[1]):
offset = [0, 0 + self.contact_pitch * i]
offset = self.via_layer_position + vector(0, self.contact_pitch * i)
for j in range(self.dimensions[0]):
self.add_rect(layer=self.via_layer_name,
offset=offset,
width=self.contact_width,
height=self.contact_width)
offset = [offset[0] + self.contact_pitch, offset[1]]
offset = offset + vector(self.contact_pitch,0)
def create_first_layer_enclosure(self):
width = self.first_layer_width = self.contact_array_width \
+ 2 * self.first_layer_horizontal_enclosure
height = self.first_layer_height = self.contact_array_height \
+ 2 * self.first_layer_vertical_enclosure
offset = self.first_layer_position = vector(-self.first_layer_horizontal_enclosure,
-self.first_layer_vertical_enclosure)
self.add_rect(layer=self.first_layer_name,
offset=offset,
offset=self.first_layer_position,
width=width,
height=height)
@ -102,9 +98,7 @@ class contact(design.design):
+ 2 * self.second_layer_horizontal_enclosure
height = self.second_layer_height = self.contact_array_height \
+ 2 * self.second_layer_vertical_enclosure
offset = self.second_layer_position = vector(-self.second_layer_horizontal_enclosure,
-self.second_layer_vertical_enclosure)
self.add_rect(layer=self.second_layer_name,
offset=offset,
offset=self.second_layer_position,
width=width,
height=height)

File diff suppressed because it is too large Load Diff

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_array = "tri_gate_array"
wordline_driver = "wordline_driver"
replica_bitline = "replica_bitline"
replica_bitcell = "replica_bitcell"
bitcell = "bitcell"
delay_chain = "logic_effort_dc"
delay_chain = "delay_chain"

View File

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

View File

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

View File

@ -32,13 +32,18 @@ class instance(geometry):
def __init__(self, name, mod, offset, mirror, rotate):
"""Initializes an instance to represent a module"""
geometry.__init__(self)
debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.")
self.name = name
self.mod = mod
self.gds = mod.gds
self.rotate = rotate
self.offset = vector(offset).snap_to_grid()
self.mirror = mirror
self.boundary = [vector(0,0),vector(mod.width,mod.height)]
self.transform(offset,mirror,rotate)
debug.info(3, "creating instance: " + self.name)
def gds_write_file(self, newLayout):
@ -53,6 +58,95 @@ class instance(geometry):
mirror=self.mirror,
rotate=self.rotate)
def normalize(self):
""" Re-find the LL and UR points after a transform """
(first,second)=self.boundary
ll = vector(min(first[0],second[0]),min(first[1],second[1]))
ur = vector(max(first[0],second[0]),max(first[1],second[1]))
self.boundary=[ll,ur]
def transform(self,offset,mirror,rotate):
""" Transform with offset, mirror and rotation to get the absolute pin location.
We must then re-find the ll and ur. The master is the cell instance. """
(ll,ur) = self.boundary
if mirror=="MX":
ll=ll.scale(1,-1)
ur=ur.scale(1,-1)
elif mirror=="MY":
ll=ll.scale(-1,1)
ur=ur.scale(-1,1)
elif mirror=="XY":
ll=ll.scale(-1,-1)
ur=ur.scale(-1,-1)
if rotate==90:
ll=ll.rotate_scale(-1,1)
ur=ur.rotate_scale(-1,1)
elif rotate==180:
ll=ll.scale(-1,-1)
ur=ur.scale(-1,-1)
elif rotate==270:
ll=ll.rotate_scale(1,-1)
ur=ur.rotate_scale(1,-1)
self.boundary=[offset+ll,offset+ur]
self.normalize()
def ll(self):
""" Return the lower left corner """
return self.boundary[0]
def ur(self):
""" Return the upper right corner """
return self.boundary[1]
def lr(self):
""" Return the lower right corner """
return vector(self.boundary[1].x, self.boundary[0].y)
def ul(self):
""" Return the upper left corner """
return vector(self.boundary[0].x, self.boundary[1].y)
def uy(self):
""" Return the upper edge """
return self.boundary[1].y
def by(self):
""" Return the bottom edge """
return self.boundary[0].y
def lx(self):
""" Return the left edge """
return self.boundary[0].x
def rx(self):
""" Return the right edge """
return self.boundary[1].x
def get_pin(self,name):
""" Return an absolute pin that is offset and transformed based on
this instance location. """
import copy
pin = copy.deepcopy(self.mod.get_pin(name))
pin.transform(self.offset,self.mirror,self.rotate)
return pin
def get_pins(self,name):
""" Return an absolute pin that is offset and transformed based on
this instance location. """
import copy
pin = copy.deepcopy(self.mod.get_pins(name))
new_pins = []
for p in pin:
p.transform(self.offset,self.mirror,self.rotate)
new_pins.append(p)
return new_pins
def __str__(self):
""" override print function output """
return "inst: " + self.name + " mod=" + self.mod.name

View File

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

View File

@ -6,212 +6,254 @@ from contact import contact
from pinv import pinv
from vector import vector
from globals import OPTS
from nand_2 import nand_2
from nand_3 import nand_3
class hierarchical_predecode(design.design):
"""
Pre 2x4 and 3x8 decoder shared code.
"""
def __init__(self, nmos_width, cellname, input_number):
design.design.__init__(self, cellname)
def __init__(self, input_number):
self.number_of_inputs = input_number
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
design.design.__init__(self, name="pre{0}x{1}".format(self.number_of_inputs,self.number_of_outputs))
c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
self.bitcell_height = self.mod_bitcell.chars["height"]
self.bitcell_height = self.mod_bitcell.height
self.nmos_width = nmos_width
self.number_of_inputs = input_number
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
def add_pins(self):
for k in range(self.number_of_inputs):
self.add_pin("A[{0}]".format(k))
self.add_pin("in[{0}]".format(k))
for i in range(self.number_of_outputs):
self.add_pin("out[{0}]".format(i))
self.add_pin("vdd")
self.add_pin("gnd")
def create_modules(self):
layer_stack = ("metal1", "via1", "metal2")
self.m1m2_via = contact(layer_stack=layer_stack)
self.inv = pinv(nmos_width=drc["minwidth_tx"],
beta=2,
height=self.bitcell_height)
""" Create the INV and NAND gate """
self.inv = pinv()
self.add_mod(self.inv)
# create_nand redefine in sub class based on number of inputs
self.create_nand()
self.create_nand(self.number_of_inputs)
self.add_mod(self.nand)
def set_up_constrain(self):
self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2
self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2
self.via_shift = (self.m1m2_via.second_layer_width
- self.m1m2_via.first_layer_width) / 2
self.metal2_extend_contact = (self.m1m2_via.second_layer_height
- self.m1m2_via.contact_width) / 2
def create_nand(self,inputs):
""" Create the NAND for the predecode input stage """
if inputs==2:
self.nand = nand_2()
elif inputs==3:
self.nand = nand_3()
else:
debug.error("Invalid number of predecode inputs.",-1)
def setup_constraints(self):
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.metal2_space = drc["metal2_to_metal2"]
self.metal1_space = drc["metal1_to_metal1"]
self.metal2_width = drc["minwidth_metal2"]
self.metal1_width = drc["minwidth_metal1"]
# we are going to use horizontal vias, so use the via height
# use a conservative douple spacing just to get rid of annoying via DRCs
self.metal2_pitch = self.m1m2_via.height + 2*self.metal2_space
# This is to shift the rotated vias to be on m2 pitch
self.via_x_shift = self.m1m2_via.height + self.m1m2_via.via_layer_position.scale(0,-1).y
# This is to shift the via if the metal1 and metal2 overlaps are different
self.via_y_shift = self.m1m2_via.second_layer_position.x - self.m1m2_via.first_layer_position.x + self.m1m2_via.via_layer_position.scale(-0.5,0).x
# The rail offsets are indexed by the label
self.rails = {}
self.gap_between_rails = (self.metal2_extend_contact
+ drc["metal2_to_metal2"])
self.gap_between_rail_offset = (self.gap_between_rails
+ drc["minwidth_metal2"])
def setup_constrains(self):
self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2
self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2
# Contact shift used connecting NAND3 inputs to the rail
self.contact_shift = (self.m1m2_via.first_layer_width - self.m1m2_via.contact_width) / 2
self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"]
self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"]
self.rails_x_offset = []
# set_rail_height redefine in sub class
self.set_rail_height()
# Creating the left hand side metal2 rails for input connections
for hrail_1 in range(self.number_of_inputs):
xoffset_1 = (self.metal2_extend_contact
+ (hrail_1 * self.gap_between_rail_offset))
self.rails_x_offset.append(xoffset_1)
# x offset for Xpre2x4_inv
self.x_off_inv_1 = self.rails_x_offset[-1] + self.gap_between_rail_offset
# Non inverted input rails
for rail_index in range(self.number_of_inputs):
xoffset = rail_index * self.metal2_pitch
self.rails["in[{}]".format(rail_index)]=xoffset
# x offset for input inverters
self.x_off_inv_1 = self.number_of_inputs*self.metal2_pitch
# Creating the right hand side metal2 rails for output connections
for hrail_2 in range(2 * self.number_of_inputs + 2):
xoffset_2 = self.x_off_inv_1 + self.inv.width + self.gap_between_rails + (hrail_2 * self.gap_between_rail_offset)
self.rails_x_offset.append(xoffset_2)
self.xoffset_2=self.rails_x_offset[-1]
for rail_index in range(2 * self.number_of_inputs):
xoffset = self.x_off_inv_1 + self.inv.width + ((rail_index+1) * self.metal2_pitch)
if rail_index < self.number_of_inputs:
self.rails["Abar[{}]".format(rail_index)]=xoffset
else:
self.rails["A[{}]".format(rail_index-self.number_of_inputs)]=xoffset
self.x_off_nand = self.xoffset_2 + self.gap_between_rail_offset
# x offset to NAND decoder includes the left rails, mid rails and inverters, plus an extra m2 pitch
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (1 + 2*self.number_of_inputs) * self.metal2_pitch
# x offset to output inverters
self.x_off_inv_2 = self.x_off_nand + self.nand.width
self.update_size()
def update_size(self):
# Height width are computed
self.width = self.x_off_inv_2 + self.inv.width
self.set_height()
self.size = vector(self.width, self.height)
correct =vector(0, 0.5 * drc["minwidth_metal1"])
self.vdd_position = self.size - correct - vector(0, self.inv.height)
self.gnd_position = self.size - correct
self.height = self.number_of_outputs * self.nand.height
def create_rails(self):
for x_off in self.rails_x_offset:
self.add_rect(layer="metal2",
offset=[x_off, 0],
width=drc["minwidth_metal2"],
height=self.rail_height)
def add_output_inverters(self):
self.decode_out_positions = []
for inv_2x4 in range(self.number_of_outputs):
name = "Xpre2x4_nand_inv[{0}]".format(inv_2x4)
if (inv_2x4 % 2 == 0):
y_factor = inv_2x4
mirror = "R0"
correct = self.inv.Z_position
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
for label in self.rails.keys():
# these are not primary inputs, so they shouldn't have a
# label or LVS complains about different names on one net
if label.startswith("in"):
self.add_layout_pin(text=label,
layer="metal2",
offset=[self.rails[label], 0],
width=self.metal2_width,
height=self.height - drc["metal2_to_metal2"])
else:
y_factor =inv_2x4 + 1
mirror = "MX"
correct = self.inv.Z_position.scale(1,-1) - vector(0,
drc["minwidth_metal1"])
self.add_rect(layer="metal2",
offset=[self.rails[label], 0],
width=self.metal2_width,
height=self.height - drc["metal2_to_metal2"])
def add_input_inverters(self):
""" Create the input inverters to invert input signals for the decode stage. """
for inv_num in range(self.number_of_inputs):
name = "Xpre_inv[{0}]".format(inv_num)
if (inv_num % 2 == 0):
y_off = inv_num * (self.inv.height)
offset = vector(self.x_off_inv_1, y_off)
mirror = "R0"
else:
y_off = (inv_num + 1) * (self.inv.height)
offset = vector(self.x_off_inv_1, y_off)
mirror="MX"
self.add_inst(name=name,
mod=self.inv,
offset=offset,
mirror=mirror)
self.connect_inst(["in[{0}]".format(inv_num),
"inbar[{0}]".format(inv_num),
"vdd", "gnd"])
def add_output_inverters(self):
""" Create inverters for the inverted output decode signals. """
self.decode_out_positions = []
z_pin = self.inv.get_pin("Z")
for inv_num in range(self.number_of_outputs):
name = "Xpre2x4_nand_inv[{}]".format(inv_num)
if (inv_num % 2 == 0):
y_factor = inv_num
mirror = "R0"
y_dir = 1
else:
y_factor =inv_num + 1
mirror = "MX"
y_dir = -1
base = vector(self.x_off_inv_2, self.inv.height * y_factor)
self.add_inst(name=name,
mod=self.inv,
offset=base,
mirror=mirror)
self.connect_inst(["Z[{0}]".format(inv_2x4),
"out[{0}]".format(inv_2x4),
self.connect_inst(["Z[{}]".format(inv_num),
"out[{}]".format(inv_num),
"vdd", "gnd"])
output_inv_out_offset = base + correct
self.decode_out_positions.append(output_inv_out_offset)
z_pin = self.inv.get_pin("Z")
self.add_layout_pin(text="out[{}]".format(inv_num),
layer="metal1",
offset=base+z_pin.ll().scale(1,y_dir),
width=z_pin.width(),
height=z_pin.height()*y_dir)
def add_nand(self,connections):
""" Create the NAND stage for the decodes """
z_pin = self.nand.get_pin("Z")
a_pin = self.inv.get_pin("A")
for nand_input in range(self.number_of_outputs):
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
name = "Xpre"+inout+"_nand[{0}]".format(nand_input)
name = "Xpre{0}_nand[{1}]".format(inout,nand_input)
rect_height = z_pin.uy()-a_pin.by()
if (nand_input % 2 == 0):
y_off = nand_input * (self.nand.height)
mirror = "R0"
offset = [self.x_off_nand + self.nand.width,
y_off + self.nand.Z_position.y]
rect_offset = vector(self.x_off_nand + self.nand.width,
y_off + z_pin.uy() - rect_height)
else:
y_off = (nand_input + 1) * (self.nand.height)
mirror = "MX"
offset =[self.x_off_nand + self.nand.width,
y_off - self.nand.Z_position.y - drc["minwidth_metal1"]]
rect_offset =vector(self.x_off_nand + self.nand.width,
y_off - z_pin.uy())
self.add_inst(name=name,
mod=self.nand,
offset=[self.x_off_nand, y_off],
mirror=mirror)
self.add_rect(layer="metal1",
offset=offset,
width=drc["minwidth_metal1"],
height=drc["minwidth_metal1"])
offset=rect_offset,
width=self.metal1_width,
height=rect_height)
self.connect_inst(connections[nand_input])
def route(self):
self.route_input_inverters()
self.route_inputs_to_rails()
self.route_nand_to_rails()
self.route_vdd_gnd_from_rails_to_gates()
self.route_vdd_gnd()
def route_input_inverters(self):
# All conections of the inputs inverters [Inputs, outputs, vdd, gnd]
output_shift = self.set_output_shift()
for inv_rout in range(self.number_of_inputs):
setup = self.setup_route_input_inverter(inv_rout,output_shift)
y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset = setup
#add output
correct = y_dir * (output_shift + drc["minwidth_metal1"])
output_metal = self.cal_input_inverters_output(setup,output_shift,inv_rout)
offset1,offset2=output_metal[0]
offset3,offset4=output_metal[1]
def route_inputs_to_rails(self):
""" Route the uninverted inputs to the second set of rails """
for num in range(self.number_of_inputs):
# route one signal next to each vdd/gnd rail since this is
# typically where the p/n devices are and there are no
# pins in the nand gates.
y_offset = (num+self.number_of_inputs) * self.inv.height + 2*self.metal1_space
in_pin = "in[{}]".format(num)
a_pin = "A[{}]".format(num)
self.add_rect(layer="metal1",
offset=offset1,
width=drc["minwidth_metal1"],
height=offset2.y - offset1.y)
self.add_rect(layer="metal1",
offset=offset3,
width=offset4.x - offset3.x,
height=drc["minwidth_metal1"])
off_via = [self.rails_x_offset[inv_rout + self.number_of_inputs+2] + self.gap_between_rails,
inv_vdd_offset.y- self.via_shift - correct]
offset=[self.rails[in_pin],y_offset],
width=self.rails[a_pin] + self.metal2_width - self.rails[in_pin],
height=self.metal1_width)
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=off_via,
offset=[self.rails[in_pin] + self.via_x_shift, y_offset + self.via_y_shift],
rotate=90)
#route input
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[inv_rout],
inv_in_offset.y],
width=inv_in_offset.x - self.rails_x_offset[inv_rout] + drc["minwidth_metal2"],
height=drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[inv_rout] + self.gap_between_rails,
inv_in_offset.y - self.via_shift],
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails[a_pin] + self.via_x_shift, y_offset + self.via_y_shift],
rotate=90)
# route vdd
def route_input_inverters(self):
"""
Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd]
"""
for inv_num in range(self.number_of_inputs):
(inv_offset, y_dir) = self.get_gate_offset(self.x_off_inv_1, self.inv.height, inv_num)
out_pin = "Abar[{}]".format(inv_num)
in_pin = "in[{}]".format(inv_num)
#add output so that it is just below the vdd or gnd rail
# since this is where the p/n devices are and there are no
# pins in the nand gates.
y_offset = (inv_num+1) * self.inv.height - 3*self.metal1_space
inv_out_offset = inv_offset+self.inv.get_pin("Z").ur().scale(1,y_dir)-vector(0,self.metal1_width).scale(1,y_dir)
self.add_rect(layer="metal1",
offset=inv_vdd_offset,
width=self.rails_x_offset[self.number_of_inputs] - inv_vdd_offset.x + drc["minwidth_metal2"],
height=drc["minwidth_metal1"])
# route gnd
offset=[inv_out_offset.x,y_offset],
width=self.rails[out_pin]-inv_out_offset.x + self.metal2_width,
height=self.metal1_width)
self.add_rect(layer="metal1",
offset=inv_gnd_offset,
width=self.rails_x_offset[self.number_of_inputs+1] - inv_gnd_offset.x + drc["minwidth_metal2"],
height=drc["minwidth_metal1"])
offset=inv_out_offset,
width=self.metal1_width,
height=y_offset-inv_out_offset.y)
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails[out_pin] + self.via_x_shift, y_offset + self.via_y_shift],
rotate=90)
def setup_route_input_inverter(self, inv_rout, output_shift):
# add Inputs, vdd, gnd of the inputs inverters
if (inv_rout % 2 == 0):
base_offset=[self.x_off_inv_1, inv_rout * self.inv.height ]
y_dir = 1
else:
base_offset=[self.x_off_inv_1, 2 * self.inv.height - drc["minwidth_metal1"]]
y_dir = -1
inv_out_offset = base_offset+self.inv.Z_position.scale(1,y_dir)
inv_in_offset = base_offset+self.inv.A_position.scale(1,y_dir)
inv_vdd_offset = base_offset+self.inv.vdd_position.scale(1,y_dir)
inv_gnd_offset = base_offset+self.inv.gnd_position.scale(1,y_dir)
#return info to create output of the input inverter
return [y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset]
#route input
inv_in_offset = inv_offset+self.inv.get_pin("A").ll().scale(1,y_dir)
self.add_rect(layer="metal1",
offset=[self.rails[in_pin], inv_in_offset.y],
width=inv_in_offset.x - self.rails[in_pin],
height=self.metal1_width)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.rails[in_pin] + self.via_x_shift, inv_in_offset.y + self.via_y_shift],
rotate=90)
def route_nand_to_rails(self):
# This 2D array defines the connection mapping
@ -219,65 +261,50 @@ class hierarchical_predecode(design.design):
for k in range(self.number_of_outputs):
# create x offset list
index_lst= nand_input_line_combination[k]
line_x_offset = []
for index in index_lst:
line_x_offset.append(self.rails_x_offset[index])
# create y offset list
yoffset_nand_in, correct= self.create_y_offsets(k)
# connect based on the two list
for i in range(self.number_of_inputs):
x_offset = line_x_offset[i]
y_offset = yoffset_nand_in[i]
# Connecting the i-th input of Nand3 gate
(nand_offset,y_dir) = self.get_gate_offset(self.x_off_nand,self.nand.height,k)
if self.number_of_inputs == 2:
gate_lst = ["A","B"]
else:
gate_lst = ["A","B","C"]
# this will connect pins A,B or A,B,C
for rail_pin,gate_pin in zip(index_lst,gate_lst):
pin_offset = nand_offset+self.nand.get_pin(gate_pin).ll().scale(1,y_dir)
self.add_rect(layer="metal1",
offset=[x_offset, y_offset],
width=self.x_off_nand - x_offset,
height=drc["minwidth_metal1"])
offset=[self.rails[rail_pin], pin_offset.y],
width=pin_offset.x - self.rails[rail_pin],
height=self.metal1_width)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[x_offset+ self.gap_between_rails,
y_offset - self.via_shift - correct[i]],
rotate=90)
# Extended of the top NAND2 to the left hand side input rails
if(k == self.number_of_outputs - 1):
x_offset = self.rails_x_offset[i]
self.add_rect(layer="metal1",
offset=[x_offset, y_offset],
width=self.x_off_nand - x_offset,
height=drc["minwidth_metal1"])
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[x_offset + self.gap_between_rails,
y_offset - self.via_shift],
rotate=90)
offset=[self.rails[rail_pin] + self.via_x_shift, pin_offset.y + self.via_y_shift],
rotate=90)
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
for num in range(0,self.number_of_outputs):
# this will result in duplicate polygons for rails, but who cares
# use the inverter offset even though it will be the nand's too
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
# route vdd
vdd_offset = gate_offset + self.inv.get_pin("vdd").ll().scale(1,y_dir)
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_offset,
width=self.x_off_inv_2 + self.inv.width + self.metal2_width,
height=self.metal1_width)
# route gnd
gnd_offset = gate_offset+self.inv.get_pin("gnd").ll().scale(1,y_dir)
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_offset,
width=self.x_off_inv_2 + self.inv.width + self.metal2_width,
height=self.metal1_width)
def route_vdd_gnd_from_rails_to_gates(self):
via_correct = self.get_via_correct()
for k in range(self.number_of_outputs):
power_line_index = self.number_of_inputs + 1 - (k%2)
yoffset = k * self.inv.height - 0.5 * drc["minwidth_metal1"]
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[power_line_index],
yoffset],
width=self.x_off_nand - self.rails_x_offset[power_line_index],
height=drc["minwidth_metal1"])
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[power_line_index] + self.gap_between_rails,
yoffset - via_correct.y],
rotate=90)
yoffset = (self.number_of_outputs * self.inv.height
- 0.5 * drc["minwidth_metal1"])
v_metal = self.get_vertical_metal()
via_y = self.get_via_y()
index = self.number_of_inputs + 1
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[index], yoffset],
width=self.x_off_nand - self.rails_x_offset[index],
height=drc["minwidth_metal1"])
self.add_rect(layer=v_metal,
offset=[self.rails_x_offset[index], self.rail_height],
width=drc["minwidth_"+v_metal],
height=yoffset - self.rail_height)
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[index] + self.gap_between_rails,
via_y] - via_correct,
rotate=90)

View File

@ -1,7 +1,6 @@
from tech import drc
import debug
import design
from nand_2 import nand_2
from vector import vector
from hierarchical_predecode import hierarchical_predecode
@ -9,110 +8,46 @@ class hierarchical_predecode2x4(hierarchical_predecode):
"""
Pre 2x4 decoder used in hierarchical_decoder.
"""
def __init__(self, nmos_width, cellname):
hierarchical_predecode.__init__(self, nmos_width, cellname, 2)
def __init__(self):
hierarchical_predecode.__init__(self, 2)
self.add_pins()
self.create_modules()
self.setup_constrains()
self.setup_constraints()
self.create_layout()
self.route()
def create_nand(self):
self.nand = nand_2(nmos_width=self.nmos_width,
height=self.bitcell_height)
def set_rail_height(self):
self.rail_height = (self.number_of_outputs * self.nand.height
- (self.number_of_outputs - 1) * drc["minwidth_metal2"])
self.DRC_LVS()
def create_layout(self):
""" The general organization is from left to right:
1) a set of M2 rails for input signals
2) a set of inverters to invert input signals
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
4) a set of NAND gates for inversion
"""
self.create_rails()
self.add_inv2x4()
self.add_input_inverters()
self.add_output_inverters()
connections =[["A[0]", "A[1]", "Z[3]", "vdd", "gnd"],
["B[0]", "A[1]", "Z[2]", "vdd", "gnd"],
["A[0]", "B[1]", "Z[1]", "vdd", "gnd"],
["B[0]", "B[1]", "Z[0]", "vdd", "gnd"]]
connections =[["in[0]", "in[1]", "Z[3]", "vdd", "gnd"],
["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"],
["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"],
["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"]]
self.add_nand(connections)
def set_height(self):
self.height = 4 * self.nand.height
def add_inv2x4(self):
self.A_positions = []
for inv_2x4 in range(self.number_of_inputs):
name = "Xpre2x4_inv[{0}]".format(inv_2x4)
if (inv_2x4 % 2 == 0):
y_off = inv_2x4 * (self.inv.height)
offset = vector(self.x_off_inv_1, y_off)
mirror = "R0"
A_off = self.inv.A_position.scale(0, 1)
else:
y_off = (inv_2x4 + 1) * (self.inv.height)
offset = vector(self.x_off_inv_1, y_off)
mirror="MX"
A_off = vector(0, - self.inv.A_position.y - drc["minwidth_metal1"])
self.A_positions.append(offset + A_off)
self.add_inst(name=name,
mod=self.inv,
offset=offset,
mirror=mirror)
self.connect_inst(["A[{0}]".format(inv_2x4),
"B[{0}]".format(inv_2x4),
"vdd", "gnd"])
def cal_input_inverters_output(self,setup,output_shift,inv_rout):
y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset = setup
correct = y_dir * (output_shift + drc["minwidth_metal1"])
out_offset = vector(inv_out_offset)
if y_dir == -1:
out_offset.y = inv_vdd_offset.y + output_shift + drc["minwidth_metal1"]
vertical1 = out_offset
vertical2 = vertical1 + vector(0,
(inv_vdd_offset.y - inv_out_offset.y) * y_dir
- output_shift)
horizontal1 = vector(inv_out_offset.x,
inv_vdd_offset.y - correct)
horizontal2 = horizontal1 + vector(self.rails_x_offset[inv_rout + 4] - inv_out_offset.x+ drc["minwidth_metal2"],
0)
return [[vertical1,vertical2],[horizontal1,horizontal2]]
def set_output_shift(self):
return 2 * drc["minwidth_metal1"]
self.route()
def get_nand_input_line_combination(self):
combination = [[4, 5], [6, 5], [4, 7], [6, 7]]
""" These are the decoder connections of the NAND gates to the A,B pins """
combination = [["Abar[0]", "Abar[1]"],
["A[0]", "Abar[1]"],
["Abar[0]", "A[1]"],
["A[0]", "A[1]"]]
return combination
def create_y_offsets(self,k):
# create y offset list
if (k % 2 == 0):
y_off = k * (self.nand.height)
direct = 1
else:
y_off = (k + 1) * (self.nand.height) - drc["minwidth_metal1"]
direct = - 1
correct =[0,0]
yoffset_nand_in = [y_off + direct * self.nand.A_position.y,
y_off + direct * self.nand.B_position.y]
return yoffset_nand_in, correct
def get_via_correct(self):
return vector(0, self.via_shift)
def get_vertical_metal(self):
return "metal1"
def get_via_y(self):
return self.rail_height
def delay(self, slew, load = 0.0 ):
# A -> B
# in -> inbar
a_t_b_delay = self.inv.delay(slew=slew,load = self.nand.input_load())
# out -> z
# inbar -> z
b_t_z_delay = self.nand.delay(slew=a_t_b_delay.slew,load = self.inv.input_load())
result = a_t_b_delay + b_t_z_delay
@ -122,4 +57,4 @@ class hierarchical_predecode2x4(hierarchical_predecode):
return result
def input_load(self):
return self.inv.input_load()
return self.nand.input_load()

View File

@ -1,105 +1,69 @@
from tech import drc
import debug
import design
from nand_3 import nand_3
from vector import vector
from hierarchical_predecode import hierarchical_predecode
class hierarchical_predecode3x8(hierarchical_predecode):
"""
Pre 3x8 decoder used in hierarchical_decoder.
"""
def __init__(self, nmos_width, cellname):
hierarchical_predecode.__init__(self, nmos_width, cellname, 3)
def __init__(self):
hierarchical_predecode.__init__(self, 3)
self.add_pins()
self.create_modules()
self.setup_constrains()
self.setup_constraints()
self.create_layout()
self.DRC_LVS()
def create_layout(self):
""" The general organization is from left to right:
1) a set of M2 rails for input signals
2) a set of inverters to invert input signals
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
4) a set of NAND gates for inversion
"""
self.create_rails()
self.add_input_inverters()
self.add_output_inverters()
connections=[["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"],
["in[0]", "in[1]", "inbar[2]", "Z[6]", "vdd", "gnd"],
["in[0]", "inbar[1]", "in[2]", "Z[5]", "vdd", "gnd"],
["in[0]", "inbar[1]", "inbar[2]", "Z[4]", "vdd", "gnd"],
["inbar[0]", "in[1]", "in[2]", "Z[3]", "vdd", "gnd"],
["inbar[0]", "in[1]", "inbar[2]", "Z[2]", "vdd", "gnd"],
["inbar[0]", "inbar[1]", "in[2]", "Z[1]", "vdd", "gnd"],
["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"]]
self.add_nand(connections)
self.route()
def create_nand(self):
self.nand = nand_3(nmos_width=self.nmos_width,
height=self.bitcell_height)
def set_rail_height(self):
self.rail_height = (self.number_of_outputs * self.nand.height
- 1.5 * drc["minwidth_metal2"])
def create_layout(self):
self.create_rails()
self.add_output_inverters()
connections=[["A[0]", "A[1]", "A[2]", "Z[7]", "vdd", "gnd"],
["A[0]", "A[1]", "B[2]", "Z[6]", "vdd", "gnd"],
["A[0]", "B[1]", "A[2]", "Z[5]", "vdd", "gnd"],
["A[0]", "B[1]", "B[2]", "Z[4]", "vdd", "gnd"],
["B[0]", "A[1]", "A[2]", "Z[3]", "vdd", "gnd"],
["B[0]", "A[1]", "B[2]", "Z[2]", "vdd", "gnd"],
["B[0]", "B[1]", "A[2]", "Z[1]", "vdd", "gnd"],
["B[0]", "B[1]", "B[2]", "Z[0]", "vdd", "gnd"]]
self.add_nand(connections)
def set_height(self):
self.height = 8 * self.nand.height
def cal_input_inverters_output(self,setup,output_shift,inv_rout):
y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset = setup
correct = y_dir * (output_shift + drc["minwidth_metal1"])
out_offset = inv_out_offset + vector(0, output_shift + correct)
vertical1 = out_offset
vertical2 = (vertical1.scale(1, 0) + inv_vdd_offset.scale(0, 1)
+ vector(0, - correct))
horizontal1 = vertical1
horizontal2 = vector(self.rails_x_offset[inv_rout + 5] + drc["minwidth_metal2"],
vertical2.y)
return [[vertical1,vertical2],[horizontal1,horizontal2]]
def set_output_shift(self):
return 1.5 * drc["minwidth_metal1"]
def get_nand_input_line_combination(self):
combination = [[5, 6, 7], [5, 6, 10],
[5, 9, 7], [5, 9, 10],
[8, 6, 7], [8, 6, 10],
[8, 9, 7], [8, 9, 10]]
""" These are the decoder connections of the NAND gates to the A,B,C pins """
combination = [["Abar[0]", "Abar[1]", "Abar[2]"],
["Abar[0]", "Abar[1]", "A[2]"],
["Abar[0]", "A[1]", "Abar[2]"],
["Abar[0]", "A[1]", "A[2]"],
["A[0]", "Abar[1]", "Abar[2]"],
["A[0]", "Abar[1]", "A[2]"],
["A[0]", "A[1]", "Abar[2]"],
["A[0]", "A[1]", "A[2]"]]
return combination
def create_y_offsets(self,k):
if (k % 2 == 0):
y_off = k * (self.nand.height)
y_dir =1
correct = [0,0,self.contact_shift]
else:
y_off = 2 * self.inv.height - drc["minwidth_metal1"] + (k - 1) * (self.nand.height)
y_dir = -1
correct = [0,self.contact_shift,0]
yoffset_nand_in = [y_off + y_dir*self.nand.A_position[1],
y_off + y_dir*self.nand.B_position[1],
y_off + y_dir*self.nand.C_position[1]]
return yoffset_nand_in, correct
def get_via_correct(self):
return vector(0, self.via_shift+self.contact_shift)
def get_vertical_metal(self):
return "metal2"
def get_via_y(self):
yoffset = (self.number_of_outputs * self.inv.height
- 0.5 * drc["minwidth_metal1"])
return yoffset
def delay(self, slew, load = 0.0 ):
# A -> z
b_t_z_delay = self.nand.delay(slew=slew,
load = self.input_load())
# A -> Abar
a_t_b_delay = self.inv.delay(slew=slew,load = self.nand.input_load())
# Abar -> z
b_t_z_delay = self.nand.delay(slew=a_t_b_delay.slew,load = self.inv.input_load())
result = a_t_b_delay + b_t_z_delay
# Z -> out
a_t_out_delay = self.inv.delay(slew=b_t_z_delay.slew,
load = load)
result = b_t_z_delay + a_t_out_delay
a_t_out_delay = self.inv.delay(slew=b_t_z_delay.slew,load = load)
result = result + a_t_out_delay
return result
def input_load(self):
return self.nand.input_load()

View File

@ -6,6 +6,7 @@ from tech import drc, GDS
from tech import layer as techlayer
import os
from vector import vector
from pin_layout import pin_layout
class layout:
"""
@ -21,9 +22,9 @@ class layout:
self.name = name
self.width = None
self.height = None
self.insts = [] # Holds module/cell layout instances
self.objs = [] # Holds all other objects (labels, geometries, etc)
self.insts = [] # Holds module/cell layout instances
self.objs = [] # Holds all other objects (labels, geometries, etc)
self.pin_map = {} # Holds name->(vector,layer) map for all pins
self.visited = False # Flag for traversing the hierarchy
self.gds_read()
@ -38,6 +39,21 @@ class layout:
self.offset_attributes(coordinate)
self.translate(coordinate)
def get_gate_offset(self, x_offset, height, inv_num):
"""Gets the base offset and y orientation of stacked rows of gates
assuming a minwidth metal1 vdd/gnd rail. Input is which gate
in the stack from 0..n
"""
if (inv_num % 2 == 0):
base_offset=vector(x_offset, inv_num * height)
y_dir = 1
else:
# we lose a rail after every 2 gates
base_offset=vector(x_offset, (inv_num+1) * height - (inv_num%2)*drc["minwidth_metal1"])
y_dir = -1
return (base_offset,y_dir)
def find_lowest_coords(self):
@ -99,24 +115,46 @@ class layout:
for inst in self.insts:
inst.offset = vector(inst.offset - coordinate)
# FIXME: Make name optional and pick a random one if not specified
def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0):
"""Adds an instance of a mod to this module"""
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
message = []
for x in self.insts:
message.append(x.name)
debug.info(4, "adding instance" + ",".join(message))
debug.info(4, "adding instance" + ",".join(x.name for x in self.insts))
return self.insts[-1]
def get_inst(self, name):
"""Retrieve an instance by name"""
for inst in self.insts:
if inst.name == name:
return inst
return None
def add_rect(self, layer, offset, width, height):
"""Adds a rectangle on a given layer,offset with width and height"""
# negative layers indicate "unused" layers in a given technology
layerNumber = techlayer[layer]
if layerNumber >= 0:
self.objs.append(geometry.rectangle(layerNumber, offset, width, height))
return self.objs[-1]
return None
def get_pin(self, text):
""" Return the pin or list of pins """
debug.check(len(self.pin_map[text])==1,"Should use a pin iterator since more than one pin.")
# If we have one pin, return it and not the list.
# Otherwise, should use get_pins()
return self.pin_map[text][0]
def add_layout_pin(self, text, layer, offset, width, height):
def get_pins(self, text):
""" Return a pin list (instead of a single pin) """
return self.pin_map[text]
def add_layout_pin(self, text, layer, offset, width=None, height=None):
"""Create a labeled pin"""
if width==None:
width=drc["minwidth_{0}".format(layer)]
if height==None:
height=drc["minwidth_{0}".format(layer)]
self.add_rect(layer=layer,
offset=offset,
width=width,
@ -124,7 +162,12 @@ class layout:
self.add_label(text=text,
layer=layer,
offset=offset)
try:
self.pin_map[text].append(pin_layout(text,vector(offset,offset+vector(width,height)),layer))
except KeyError:
self.pin_map[text] = [pin_layout(text,vector(offset,offset+vector(width,height)),layer)]
def add_label(self, text, layer, offset=[0,0],zoom=-1):
"""Adds a text label on the given layer,offset, and zoom level"""
@ -132,6 +175,8 @@ class layout:
layerNumber = techlayer[layer]
if layerNumber >= 0:
self.objs.append(geometry.label(text, layerNumber, offset, zoom))
return self.objs[-1]
return None
def add_path(self, layer, coordinates, width=None):

View File

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

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
"""
pins = ["din", "dout", "dout_bar", "clk", "vdd", "gnd"]
chars = utils.auto_measure_libcell(pins, "ms_flop", GDS["unit"], layer["boundary"])
def __init__(self, name):
pin_names = ["din", "dout", "dout_bar", "clk", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("ms_flop", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "ms_flop", GDS["unit"], layer["boundary"])
def __init__(self, name="ms_flop"):
design.design.__init__(self, name)
self.width = ms_flop.chars["width"]
self.height = ms_flop.chars["height"]
self.clk_offset = ms_flop.chars["clk"]
self.din_offset = ms_flop.chars["din"]
self.dout_offset = ms_flop.chars["dout"]
self.dout_bar_offset = ms_flop.chars["dout_bar"]
self.width = ms_flop.width
self.height = ms_flop.height
self.pin_map = ms_flop.pin_map
def delay(self, slew, load = 0.0):
#import pinv
# use inv to mimic the delay
# din -> mout
#ref = pinv.pinv("reference_inv")
#mid_load = ref.input_load()
#din_t_mout_delay = ref.delay(slew = slew, load = mid_load)
# mout -> out
#mout_t_out_delay = ref.delay(slew = slew, load = load)
#result = din_t_mout_delay + mout_t_out_delay
# dont k how to calculate this now, use constant in tech file
# dont know how to calculate this now, use constant in tech file
from tech import spice
result = self.return_delay(spice["msflop_delay"], spice["msflop_slew"])
return result

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@ -8,9 +8,10 @@ import re
class ptx(design.design):
"""
This module generates gds and spice of a parametrically NMOS or PMOS sized transistor.
Creates a simple MOS transistor
Creates a simple MOS transistor. poly_positions are the ll of the poly gate. active_contact_positions
is an array of the positions of the ll of active contacts (left to right)
"""
def __init__(self, width=1, mults=1, tx_type="nmos"):
def __init__(self, width=drc["minwidth_tx"], mults=1, tx_type="nmos"):
name = "{0}_m{1}_w{2}".format(tx_type, mults, width)
# remove periods for newer spice compatibility
name=re.sub('\.','_',name)
@ -243,8 +244,7 @@ class ptx(design.design):
# left_most contact column
contact_xoffset = 0
contact_yoffset = (self.active_height \
- self.active_contact.height) / 2
contact_yoffset = (self.active_height - self.active_contact.height) / 2
offset = vector(contact_xoffset, contact_yoffset)
self.add_contact(layers=("active", "contact", "metal1"),
offset=offset,

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
the technology library. """
pins = ["BL", "BR", "WL", "vdd", "gnd"]
chars = utils.auto_measure_libcell(pins, "replica_cell_6t", GDS["unit"], layer["boundary"])
pin_names = ["BL", "BR", "WL", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"], layer["boundary"])
def __init__(self, name="replica_cell_6t"):
design.design.__init__(self, name)
debug.info(2, "Create bitcell object")
def __init__(self):
design.design.__init__(self, "replica_cell_6t")
debug.info(2, "Create replica bitcell object")
self.width = replica_bitcell.chars["width"]
self.height = replica_bitcell.chars["height"]
self.width = replica_bitcell.width
self.height = replica_bitcell.height
self.pin_map = replica_bitcell.pin_map

View File

@ -16,8 +16,8 @@ class replica_bitline(design.design):
Used for memory timing control
"""
def __init__(self, name, rows):
design.design.__init__(self, "replica_bitline")
def __init__(self, rows, name="replica_bitline"):
design.design.__init__(self, name)
g = reload(__import__(OPTS.config.delay_chain))
self.mod_delay_chain = getattr(g, OPTS.config.delay_chain)
@ -27,386 +27,303 @@ class replica_bitline(design.design):
c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
self.bitcell_chars = self.mod_bitcell.chars
for pin in ["en", "out", "vdd", "gnd"]:
self.add_pin(pin)
self.rows = rows
self.create_modules()
self.cal_modules_offset()
self.calculate_module_offsets()
self.add_modules()
self.route()
self.offset_all_coordinates()
self.add_layout_pins()
self.DRC_LVS()
def cal_modules_offset(self):
pinv_error_offset = 0.025
# leave some room for metal1 routing
margin = 3 * drc["minwidth_metal1"]
# witdth + min_spacing of M1 & M2
m1rail_space = drc["minwidth_metal1"] + drc["metal1_to_metal1"]
m2rail_space = drc["minwidth_metal2"] + drc["metal2_to_metal2"]
# leave some margin as bit cell layout exceeds its own orgin
route_margin = 8 * m2rail_space
well_margin = 2 * drc["pwell_enclose_nwell"]
bitcell_array_spacing = max(route_margin, well_margin)
# now extra space for BL and WL of RBC
gnd_route_margin = 5 * m2rail_space
y_off = (self.inv.height * 2 + pinv_error_offset
+ max(drc["pwell_enclose_nwell"],
m1rail_space * 4))
self.delay_chain_offset = vector(self.delay_chain.height,y_off)
self.en_input_offset = vector(0, y_off - m2rail_space)
self.en_nor_offset = vector(self.nor.width + margin,
self.inv.height * 2)
self.BL_inv_offset = vector(self.en_nor_offset.x - self.inv.width, 0)
self.access_tx_offset = vector(self.en_nor_offset.x - self.nor.width
+ self.access_tx.height + margin,
self.inv.height * 0.5)
self.replica_bitline_offset = vector(self.delay_chain_offset.x
+ bitcell_array_spacing,
self.bitcell_chars["height"]
+ gnd_route_margin)
self.delay_inv_offset = vector(self.delay_chain_offset.x - self.inv.width,
self.inv.height * 2)
self.height = m1rail_space + max(self.delay_chain_offset.y + self.inv.height,
self.replica_bitline_offset.y
+ self.bitline_load.height
+ 0.5 * self.bitcell_chars["height"])
self.width = (self.replica_bitline_offset.x + self.replica_bitcell.width)
def create_modules(self):
""" create module """
self.replica_bitcell = self.mod_replica_bitcell()
self.add_mod(self.replica_bitcell)
# This is the replica bitline load column that is the same height as our array
self.bitline_load = bitcell_array(name="bitline_load",
cols=1,
rows=self.rows)
self.add_mod(self.bitline_load)
# FIXME: This just creates 3 1x inverters
self.delay_chain = self.mod_delay_chain("delay_chain",
[1, 1, 1])
self.add_mod(self.delay_chain)
self.inv = pinv(nmos_width=drc["minwidth_tx"])
self.add_mod(self.inv)
def calculate_module_offsets(self):
""" Calculate all the module offsets """
# These aren't for instantiating, but we use them to get the dimensions
self.active_contact = contact(layer_stack=("active", "contact", "poly"))
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
self.poly_contact_offset = vector(0.5*self.poly_contact.width,0.5*self.poly_contact.height)
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
self.nor = nor_2(nmos_width=drc["minwidth_tx"])
self.add_mod(self.nor)
# M1/M2 routing pitch is based on contacted pitch
self.m1_pitch = max(self.m1m2_via.width,self.m1m2_via.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
# This corrects the offset pitch difference between M2 and M1
self.offset_fix = vector(0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]),0)
self.access_tx = ptx(width=drc["minwidth_tx"],
mults=1,
tx_type="pmos")
# delay chain will be rotated 90, so move it over a width
# we move it up a inv height just for some routing room
self.rbl_inv_offset = vector(self.delay_chain.height, self.inv.width)
# access TX goes right on top of inverter, leave space for an inverter which is
# about the same as a TX. We'll need to add rails though.
self.access_tx_offset = vector(1.5*self.inv.height,self.rbl_inv_offset.y) + vector(0,2.25*self.inv.width)
self.delay_chain_offset = self.rbl_inv_offset + vector(0,4*self.inv.width)
# Replica bitline and such are not rotated, but they must be placed far enough
# away from the delay chain/inverter with space for three M2 tracks
self.bitcell_offset = self.rbl_inv_offset + vector(2*self.m2_pitch, 0) + vector(0, self.bitcell.height + self.inv.width)
self.rbl_offset = self.bitcell_offset
self.height = self.rbl_offset.y + self.rbl.height
self.width = self.rbl_offset.x + self.bitcell.width
def create_modules(self):
""" Create modules for later instantiation """
self.bitcell = self.replica_bitcell = self.mod_replica_bitcell()
self.add_mod(self.bitcell)
# This is the replica bitline load column that is the height of our array
self.rbl = bitcell_array(name="bitline_load", cols=1, rows=self.rows)
self.add_mod(self.rbl)
self.delay_chain = self.mod_delay_chain([1, 1, 1])
self.add_mod(self.delay_chain)
self.inv = pinv()
self.add_mod(self.inv)
self.access_tx = ptx(tx_type="pmos")
self.add_mod(self.access_tx)
def add_modules(self):
"""add mod instance in layout """
self.add_inst(name="BL_inv",
mod=self.inv,
offset=self.BL_inv_offset)
""" Add all of the module instances in the logical netlist """
# This is the threshold detect inverter on the output of the RBL
self.rbl_inv_inst=self.add_inst(name="rbl_inv",
mod=self.inv,
offset=self.rbl_inv_offset+vector(0,self.inv.width),
rotate=270,
mirror="MX")
self.connect_inst(["bl[0]", "out", "vdd", "gnd"])
self.add_inst(name="BL_access_tx",
mod=self.access_tx,
offset=self.access_tx_offset,
rotate=90)
self.tx_inst=self.add_inst(name="rbl_access_tx",
mod=self.access_tx,
offset=self.access_tx_offset,
rotate=90)
# D, G, S, B
self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"])
# add the well and poly contact
self.add_inst(name="delay_chain",
mod=self.delay_chain,
offset=self.delay_chain_offset,
rotate=90)
self.dc_inst=self.add_inst(name="delay_chain",
mod=self.delay_chain,
offset=self.delay_chain_offset,
rotate=90)
self.connect_inst(["en", "delayed_en", "vdd", "gnd"])
self.add_inst(name="bitcell",
mod=self.replica_bitcell,
offset=self.replica_bitline_offset,
mirror="MX")
self.rbc_inst=self.add_inst(name="bitcell",
mod=self.replica_bitcell,
offset=self.bitcell_offset,
mirror="MX")
self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"])
self.add_loads()
self.expan_the_well_to_BL_inv()
self.rbl_inst=self.add_inst(name="load",
mod=self.rbl,
offset=self.rbl_offset)
self.connect_inst(["bl[0]", "br[0]"] + ["gnd"]*self.rows + ["vdd", "gnd"])
def expan_the_well_to_BL_inv(self):
width = self.BL_inv_offset.x - self.access_tx_offset.x + self.inv.width
well_offset = self.access_tx_offset - vector(self.access_tx.width, 0)
for layer in ["nwell", "vtg"]:
self.add_rect(layer=layer,
offset=well_offset,
width=width,
height= 2*self.access_tx.width)
def add_loads(self):
self.add_inst(name="load",
mod=self.bitline_load,
offset=self.replica_bitline_offset)
temp = []
for i in range(1):
temp.append("bl[{0}]".format(i))
temp.append("br[{0}]".format(i))
for j in range(self.rows):
temp.append("gnd".format(j))
temp = temp + ["vdd", "gnd"]
self.connect_inst(temp)
def route(self):
"""connect modules together"""
# calculate pin offset
correct = vector(0, 0.5 * drc["minwidth_metal1"])
self.out_offset = self.BL_inv_offset + self.inv.Z_position + correct
self.add_via(layers=("metal1", "via1", "metal2"),
offset=self.out_offset)
m1_pin_offset = self.out_offset - correct
self.add_rect(layer="metal1",
offset=m1_pin_offset,
width=self.m1m2_via.width,
height=self.m1m2_via.height)
self.add_rect(layer="metal2",
offset=m1_pin_offset,
width=self.m2m3_via.width,
height=self.m2m3_via.height)
BL_inv_in = self.BL_inv_offset + self.inv.A_position + correct
BL_offset = self.replica_bitline_offset + vector(1,0).scale(self.bitcell_chars["BL"])
pin_offset = self.delay_chain.clk_out_offset.rotate_scale(-1,1)
delay_chain_output = self.delay_chain_offset + pin_offset
vdd_offset = vector(self.delay_chain_offset.x + 9 * drc["minwidth_metal2"],
self.height)
self.create_input()
self.route_BL_t_BL_inv(BL_offset, BL_inv_in)
self.route_access_tx(delay_chain_output, BL_inv_in, vdd_offset)
self.route_vdd()
""" Connect all the signals together """
self.route_gnd()
# route loads after gnd and vdd created
self.route_loads(vdd_offset)
self.route_RC(vdd_offset)
self.route_vdd()
self.route_access_tx()
def create_input(self):
# create routing module based on module offset
correct = vector(0.5 * drc["minwidth_metal1"], 0)
pin_offset = self.delay_chain.clk_in_offset.rotate_scale(-1,1)
input_offset = self.delay_chain_offset + pin_offset + correct
mid1 = [input_offset.x, self.en_input_offset.y]
self.add_path("metal1", [self.en_input_offset, mid1, input_offset])
self.add_label(text="en",
layer="metal1",
offset=self.en_input_offset)
def route_access_tx(self):
# GATE ROUTE
# 1. Add the poly contact and nwell enclosure
# Determines the y-coordinate of where to place the gate input poly pin
# (middle in between the pmos and nmos)
def route_BL_t_BL_inv(self, BL_offset, BL_inv_in):
# BL_inv input to M3
mid1 = BL_inv_in - vector(0,
drc["metal2_to_metal2"] + self.m1m2_via.width)
mid2 = vector(self.en_nor_offset.x + 3 * drc["metal1_to_metal1"],
mid1.y)
mid3 = vector(mid2.x,
self.replica_bitline_offset.y - self.replica_bitcell.height
- 0.5 * (self.m1m2_via.height + drc["metal1_to_metal1"])
- 2 * drc["metal1_to_metal1"])
self.add_wire(layers=("metal2", "via1", "metal1"),
coordinates=[BL_inv_in, mid1, mid2, mid3])
# need to fix the mid point as this is done with two wire
# this seems to cover the metal1 error of the wire
offset = mid3 - vector( [0.5 * drc["minwidth_metal1"]] * 2)
self.add_rect(layer="metal1",
offset=offset,
width=drc["minwidth_metal1"],
height=drc["minwidth_metal1"])
mid4 = [BL_offset.x, mid3.y]
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=[BL_offset, mid4, mid3])
def route_access_tx(self, delay_chain_output, BL_inv_in, vdd_offset):
self.route_tx_gate(delay_chain_output)
self.route_tx_drain(vdd_offset)
self.route_tx_source(BL_inv_in)
def route_tx_gate(self, delay_chain_output):
# gate input for access tx
offset = (self.access_tx.poly_positions[0].rotate_scale(0,1)
+ self.access_tx_offset)
width = -6 * drc["minwidth_metal1"]
self.add_rect(layer="poly",
offset=offset,
width=width,
height=drc["minwidth_poly"])
y_off = 0.5 * (drc["minwidth_poly"] - self.poly_contact.height)
offset = offset + vector(width, y_off)
# finds the lower right of the poly gate
poly_offset = self.access_tx_offset + self.access_tx.poly_positions[0].rotate_scale(-1,1)
# This centers the contact on the poly
contact_offset = poly_offset.scale(0,1) + self.dc_inst.get_pin("out").ll().scale(1,0) \
+ vector(-drc["poly_extend_contact"], -0.5*self.poly_contact.height + 0.5*drc["minwidth_poly"])
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset)
# route gate to delay_chain output
gate_offset = offset + vector(0.5 * drc["minwidth_metal1"],
0.5 * self.poly_contact.width)
self.route_access_tx_t_delay_chain(gate_offset, delay_chain_output)
self.route_access_tx_t_WL(gate_offset)
offset=contact_offset)
self.add_rect(layer="poly",
offset=poly_offset,
width=contact_offset.x-poly_offset.x,
height=drc["minwidth_poly"])
nwell_offset = self.rbl_inv_offset + vector(-self.inv.height,self.inv.width)
self.add_rect(layer="nwell",
offset=nwell_offset,
width=0.5*self.inv.height,
height=self.delay_chain_offset.y-nwell_offset.y)
def route_access_tx_t_delay_chain(self, offset, delay_chain_output):
m2rail_space = (drc["minwidth_metal2"] + drc["metal2_to_metal2"])
mid1 = vector(offset.x, self.delay_chain_offset.y - 3 * m2rail_space)
mid2 = [delay_chain_output.x, mid1.y]
# Note the inverted wire stack
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=[offset, mid1, mid2, delay_chain_output])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=delay_chain_output,
mirror="MX")
# 2. Route delay chain output to access tx gate
delay_en_offset = self.dc_inst.get_pin("out").bc()
delay_en_end_offset = contact_offset + vector(self.poly_contact.width,self.poly_contact.height).scale(0.5,0.5)
self.add_path("metal1", [delay_en_offset,delay_en_end_offset])
def route_access_tx_t_WL(self, offset):
m1m2_via_offset = offset - vector(0.5 * self.m1m2_via.width,
0.5 * self.m1m2_via.height)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=m1m2_via_offset)
# route gate to RC WL
RC_WL = self.replica_bitline_offset - vector(0,1).scale(self.bitcell_chars["WL"])
mid1 = vector(offset.x, 0)
mid2 = vector(self.en_nor_offset.x + 3 * drc["metal1_to_metal1"],
mid1.y)
mid3 = vector(RC_WL.x - drc["minwidth_metal1"] - self.m1m2_via.height,
mid1.y)
mid4 = vector(mid3.x, RC_WL.y)
self.add_path("metal2", [offset, mid1, mid2, mid3, mid4])
offset = mid4 - vector([0.5 * drc["minwidth_metal1"]] * 2)
width = RC_WL.x - offset.x
# enter the bit line array with metal1
via_offset = [mid4.x - 0.5 * self.m1m2_via.width,
offset.y
- 0.5 * (self.m1m2_via.height
- drc["minwidth_metal1"])]
self.add_via(layers=("metal1", "via1", "metal2"),
offset=via_offset)
self.add_rect(layer="metal1",
offset=offset,
width=width,
height=drc["minwidth_metal1"])
# 3. Route the mid-point of previous route to the bitcell WL
# route bend of previous net to bitcell WL
wl_offset = self.rbc_inst.get_pin("WL").lc()
wl_mid = vector(delay_en_end_offset.x,wl_offset.y)
self.add_path("metal1", [delay_en_end_offset, wl_mid, wl_offset])
def route_tx_drain(self,vdd_offset):
# route drain to Vdd
active_offset = self.access_tx.active_contact_positions[1].rotate_scale(-1,1)
correct = vector(-0.5 * drc["minwidth_metal1"],
0.5 * self.access_tx.active_contact.width)
drain_offset = self.access_tx_offset + active_offset + correct
close_Vdd_offset = self.BL_inv_offset + vector(0, self.inv.height)
self.add_path("metal1", [drain_offset, close_Vdd_offset])
mid = [vdd_offset.x, close_Vdd_offset.y]
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=[close_Vdd_offset, mid, vdd_offset])
def route_tx_source(self, BL_inv_in):
# route source to BL inv input which is connected to BL
active_offset = self.access_tx.active_contact_positions[0].rotate_scale(-1,1)
correct = vector(-0.5 * drc["minwidth_metal1"],
0.5 * self.access_tx.active_contact.width)
source_offset = self.access_tx_offset + active_offset + correct
self.add_path("metal1", [source_offset, BL_inv_in])
# SOURCE ROUTE
# Route the source to the vdd rail
source_offset = self.access_tx_offset + self.access_tx.active_contact_positions[1].rotate_scale(-1,1)\
+ vector(self.active_contact.width,self.active_contact.height).rotate_scale(-0.5,0.5)
inv_vdd_offset = self.rbl_inv_inst.get_pin("vdd").uc()
vdd_offset = inv_vdd_offset.scale(1,0) + source_offset.scale(0,1)
self.add_path("metal1", [source_offset, vdd_offset])
# DRAIN ROUTE
# Route the drain to the RBL inverter input
drain_offset = self.access_tx_offset + self.access_tx.active_contact_positions[0].rotate_scale(-1,1) \
+ self.poly_contact_offset.rotate_scale(-1,1)
mid1 = drain_offset.scale(1,0) + vector(0,self.rbl_inv_offset.y+self.inv.width+self.m2_pitch)
inv_A_offset = self.rbl_inv_inst.get_pin("A").uc()
mid2 = vector(inv_A_offset.x, mid1.y)
self.add_path("metal1",[drain_offset, mid1, mid2, inv_A_offset])
# Route the connection of the drain route (mid2) to the RBL bitline (left)
drain_offset = mid2
# Route the M2 to the right of the vdd rail between rbl_inv and bitcell
gnd_pin = self.rbl_inv_inst.get_pin("gnd").ll()
mid1 = vector(gnd_pin.x+self.m2_pitch,drain_offset.y)
# Via will go halfway down from the bitcell
bl_offset = self.rbc_inst.get_pin("BL").bc()
via_offset = bl_offset - vector(0,0.5*self.inv.width)
mid2 = vector(mid1.x,via_offset.y)
# self.add_contact(layers=("metal1", "via1", "metal2"),
# offset=via_offset - vector(0.5*drc["minwidth_metal2"],0.5*drc["minwidth_metal1"]))
self.add_wire(("metal1","via1","metal2"),[drain_offset,mid1,mid2,via_offset,bl_offset])
#self.add_path("metal2",[via_offset,bl_offset])
def route_vdd(self):
vdd_offset = vector(0, self.height)
# Add a rail in M2 that is to the right of the inverter gnd pin
# The replica column may not fit in a single standard cell pitch, so add the vdd rail to the
# right of it.
vdd_start = vector(self.bitcell_offset.x + self.bitcell.width + self.m1_pitch,0)
# It is the height of the entire RBL and bitcell
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_offset,
width=self.width,
layer="metal1",
offset=vdd_start,
width=drc["minwidth_metal1"],
height=self.rbl.height+self.bitcell.height+2*self.inv.width+0.5*drc["minwidth_metal1"])
# Connect the vdd pins of the bitcell load directly to vdd
vdd_pins = self.rbl_inst.get_pin("vdd")
for pin in vdd_pins:
offset = vector(vdd_start.x,pin.by())
self.add_rect(layer="metal1",
offset=offset,
width=self.rbl_offset.x-vdd_start.x,
height=drc["minwidth_metal1"])
# Also connect the replica bitcell vdd pin to vdd
pin = self.rbc_inst.get_pin("vdd")
offset = vector(vdd_start.x,pin.by())
self.add_rect(layer="metal1",
offset=offset,
width=self.bitcell_offset.x-vdd_start.x,
height=drc["minwidth_metal1"])
# delay chain vdd to vertical vdd rail and
start = self.delay_chain_offset - vector(0.5 * self.delay_chain.height, 0)
m1rail_space = (drc["minwidth_metal1"] + drc["metal1_to_metal1"])
mid1 = start - vector(0, m1rail_space)
mid2 = vector(self.delay_chain_offset.x + 9 * drc["minwidth_metal2"],
mid1.y)
end = [mid2.x, vdd_offset.y]
self.add_path(layer=("metal1"),
coordinates=[start, mid1, mid2])
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=[mid1, mid2, end])
# Add a second vdd pin. No need for full length. It is must connect at the next level.
inv_vdd_offset = self.rbl_inv_inst.get_pin("vdd").ll()
self.add_layout_pin(text="vdd",
layer="metal1",
offset=inv_vdd_offset.scale(1,0),
width=drc["minwidth_metal1"],
height=self.delay_chain_offset.y)
def route_gnd(self):
"""route gnd of delay chain, en_nor, en_inv and BL_inv"""
# route delay chain gnd to BL_inv gnd
# gnd Node between BL_inv access tx and delay chain, and is below
# en_input
self.gnd_position = self.delay_chain_offset
BL_gnd_offset = self.BL_inv_offset
mid1 = vector(0, self.BL_inv_offset.y)
rail2_space = drc["minwidth_metal2"] + drc["metal2_to_metal2"]
y_off = self.gnd_position.y + self.delay_chain.width + rail2_space
mid2 = vector(mid1.x, y_off)
share_gnd = vector(self.gnd_position.x, mid2.y)
# Note the inverted stacks
lst = [BL_gnd_offset, mid1, mid2, share_gnd, self.gnd_position]
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=lst)
self.add_label(text="gnd",
layer="metal1",
offset=self.gnd_position)
# connect to the metal1 gnd of delay chain
offset = mid2 - vector(0.5 * drc["minwidth_metal1"], 0)
self.add_rect(layer="metal1",
offset=offset,
width=drc["minwidth_metal1"],
height=-self.delay_chain.width)
offset = [offset.x + self.delay_chain.height,
mid2.y]
self.add_rect(layer="metal1",
offset=offset,
width=drc["minwidth_metal1"],
height=-self.delay_chain.width)
""" Route all signals connected to gnd """
# Add a rail in M1 from bottom to two along delay chain
gnd_start = self.rbl_inv_inst.get_pin("gnd").ll() - self.offset_fix
# It is the height of the entire RBL and bitcell
self.add_rect(layer="metal2",
offset=gnd_start,
width=drc["minwidth_metal2"],
height=self.rbl.height+self.bitcell.height+self.inv.width)
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_start.scale(1,0),
width=drc["minwidth_metal2"],
height=2*self.inv.width)
# Connect the WL pins directly to gnd
for row in range(self.rows):
wl = "wl[{}]".format(row)
pin = self.rbl_inst.get_pin(wl)
offset = vector(gnd_start.x,pin.by())
self.add_rect(layer="metal1",
offset=offset,
width=self.rbl_offset.x-gnd_start.x,
height=drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
def route_loads(self,vdd_offset):
"""connect all the loads word line to gnd"""
# Add via for the delay chain
offset = self.delay_chain_offset - vector(0.5*drc["minwidth_metal1"],0) - self.offset_fix
self.add_via(layers=("metal1", "via1", "metal2"),
offset=vdd_offset,
mirror="MX")
gnd_offset = (self.delay_chain_offset
+ vector([drc["minwidth_metal1"]] * 2).scale(-.5,.5))
for i in range(self.rows):
WL_offset = (self.replica_bitline_offset
+ self.bitline_load.WL_positions[i].scale(0,1))
mid = [self.delay_chain_offset.x + 6 * drc["minwidth_metal2"],
gnd_offset.y]
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=[gnd_offset, mid, WL_offset])
if i % 2 == 0:
load_vdd_offset = (self.replica_bitline_offset
+ self.bitline_load.vdd_positions[i])
mid = [vdd_offset.x, load_vdd_offset.y]
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=[vdd_offset, mid, load_vdd_offset])
offset=offset)
def route_RC(self,vdd_offset):
"""route vdd gnd to the replica cell """
# connect vdd
RC_vdd = self.replica_bitline_offset + vector(1,-1).scale(self.bitcell_chars["vdd"])
mid = [vdd_offset.x, RC_vdd.y]
# Note the inverted stacks
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=[vdd_offset, mid, RC_vdd])
# Add via for the inverter
offset = self.rbl_inv_offset - vector(0.5*drc["minwidth_metal1"],self.m1m2_via.height) - self.offset_fix
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
gnd_offset = self.BL_inv_offset - vector(self.inv.width, 0)
load_gnd = self.replica_bitline_offset + vector(self.bitcell_chars["gnd"][0],
self.bitline_load.height)
mid = [load_gnd.x, gnd_offset.y]
self.add_wire(layers=("metal1", "via1", "metal2"),
coordinates=[gnd_offset, mid, load_gnd])
# Connect the bitcell gnd pin to the rail
gnd_pins = self.get_pin("gnd")
gnd_start = self.get_pin("gnd").uc()
rbl_gnd_pins = self.rbl_inst.get_pin("gnd")
# Find the left most rail on M2
gnd_pin = None
for pin in rbl_gnd_pins:
if gnd_pin == None or (pin.layer=="metal2" and pin.lx()<gnd_pin.lx()):
gnd_pin = pin
gnd_end = gnd_pin.uc()
# Add a couple midpoints so that the wire will drop a via and then route horizontal on M1
gnd_mid1 = gnd_start + vector(0,2*drc["metal2_to_metal2"])
gnd_mid2 = gnd_end + vector(0,2*drc["metal2_to_metal2"])
self.add_wire(("metal1","via1","metal2"), [gnd_start, gnd_mid1, gnd_mid2, gnd_end])
load_gnd = self.replica_bitline_offset + vector(0,
self.bitline_load.height)
mid = [load_gnd.x, gnd_offset.y]
self.add_wire(("metal1", "via1", "metal2"), [gnd_offset, mid, load_gnd])
# Add a second gnd pin to the second delay chain rail. No need for full length.
dc_gnd_offset = self.dc_inst.get_pin("gnd")[1].ll()
self.add_layout_pin(text="gnd",
layer="metal1",
offset=dc_gnd_offset.scale(1,0),
width=drc["minwidth_metal1"],
height=self.delay_chain_offset.y)
def add_layout_pins(self):
""" Route the input and output signal """
en_offset = self.delay_chain_offset+self.delay_chain.get_pin("in").ur().rotate_scale(-1,1)
self.add_layout_pin(text="en",
layer="metal1",
offset=en_offset.scale(1,0),
width=drc["minwidth_metal1"],
height=en_offset.y)
out_offset = self.rbl_inv_offset+self.inv.get_pin("Z").ur().rotate_scale(-1,1) - vector(0,self.inv.width)
self.add_layout_pin(text="out",
layer="metal1",
offset=out_offset.scale(1,0),
width=drc["minwidth_metal1"],
height=out_offset.y)

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,28 +20,24 @@ class pinv_test(unittest.TestCase):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import pinv
import tech
# debug.info(2, "Checking min size inverter")
# OPTS.check_lvsdrc = False
# tx = pinv.pinv(nmos_width=tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
# OPTS.check_lvsdrc = True
# self.local_check(tx)
# debug.info(2, "Checking 2x min size inverter")
# OPTS.check_lvsdrc = False
# tx = pinv.pinv(nmos_width=2 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
# OPTS.check_lvsdrc = True
# self.local_check(tx)
debug.info(2, "Checking 5x min size inverter")
OPTS.check_lvsdrc = False
tx = pinv.pinv(nmos_width=5 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
OPTS.check_lvsdrc = True
debug.info(2, "Checking min size inverter")
tx = pinv.pinv(nmos_width=tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
self.local_check(tx)
debug.info(2, "Checking 2x min size inverter")
tx = pinv.pinv(nmos_width=2 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
self.local_check(tx)
debug.info(2, "Checking 5x min size inverter")
tx = pinv.pinv(nmos_width=5 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, tx):
@ -57,6 +53,10 @@ class pinv_test(unittest.TestCase):
os.remove(tempspice)
os.remove(tempgds)
# reset the static duplicate name checker for unit tests
import design
design.design.name_map=[]

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
debug.info(2, "Checking driver")
tx = wordline_driver.wordline_driver(name="Wordline_driver", rows=8)
tx = wordline_driver.wordline_driver(rows=8)
self.local_check(tx)
OPTS.check_lvsdrc = True
self.local_check(tx)
globals.end_openram()
def local_check(self, tx):
tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds"

View File

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

View File

@ -18,50 +18,32 @@ class hierarchical_decoder_test(unittest.TestCase):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import hierarchical_decoder
import tech
debug.info(1, "Testing 4 row sample for hierarchical_decoder")
OPTS.check_lvsdrc = False
a = hierarchical_decoder.hierarchical_decoder(nand2_nmos_width=2 * tech.drc["minwidth_tx"],
nand3_nmos_width=3 * tech.drc["minwidth_tx"],
rows=4)
OPTS.check_lvsdrc = True
a = hierarchical_decoder.hierarchical_decoder(rows=4)
self.local_check(a)
debug.info(1, "Testing 8 row sample for hierarchical_decoder")
OPTS.check_lvsdrc = False
a = hierarchical_decoder.hierarchical_decoder(nand2_nmos_width=2 * tech.drc["minwidth_tx"],
nand3_nmos_width=3 * tech.drc["minwidth_tx"],
rows=8)
OPTS.check_lvsdrc = True
a = hierarchical_decoder.hierarchical_decoder(rows=8)
self.local_check(a)
debug.info(1, "Testing 32 row sample for hierarchical_decoder")
OPTS.check_lvsdrc = False
a = hierarchical_decoder.hierarchical_decoder(nand2_nmos_width=2 * tech.drc["minwidth_tx"],
nand3_nmos_width=3 * tech.drc["minwidth_tx"],
rows=32)
OPTS.check_lvsdrc = True
a = hierarchical_decoder.hierarchical_decoder(rows=32)
self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder")
OPTS.check_lvsdrc = False
a = hierarchical_decoder.hierarchical_decoder(nand2_nmos_width=2 * tech.drc["minwidth_tx"],
nand3_nmos_width=3 * tech.drc["minwidth_tx"],
rows=128)
OPTS.check_lvsdrc = True
a = hierarchical_decoder.hierarchical_decoder(rows=128)
self.local_check(a)
debug.info(1, "Testing 512 row sample for hierarchical_decoder")
OPTS.check_lvsdrc = False
a = hierarchical_decoder.hierarchical_decoder(nand2_nmos_width=2 * tech.drc["minwidth_tx"],
nand3_nmos_width=3 * tech.drc["minwidth_tx"],
rows=512)
OPTS.check_lvsdrc = True
a = hierarchical_decoder.hierarchical_decoder(rows=512)
self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, a):
@ -77,6 +59,11 @@ class hierarchical_decoder_test(unittest.TestCase):
os.remove(tempspice)
os.remove(tempgds)
# reset the static duplicate name checker for unit tests
import design
design.design.name_map=[]
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()

View File

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

View File

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

View File

@ -22,12 +22,20 @@ class single_level_column_mux_test(unittest.TestCase):
OPTS.check_lvsdrc = False
import single_level_column_mux_array
debug.info(1, "Testing sample for columnmux_array")
a = single_level_column_mux_array.single_level_column_mux_array(
rows=32, columns=32, word_size=16)
OPTS.check_lvsdrc = True
debug.info(1, "Testing sample for 2-way column_mux_array")
a = single_level_column_mux_array.single_level_column_mux_array(columns=16, word_size=8)
self.local_check(a)
debug.info(1, "Testing sample for 4-way column_mux_array")
a = single_level_column_mux_array.single_level_column_mux_array(columns=16, word_size=4)
self.local_check(a)
debug.info(1, "Testing sample for 8-way column_mux_array")
a = single_level_column_mux_array.single_level_column_mux_array(columns=32, word_size=4)
self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, a):
@ -43,7 +51,9 @@ class single_level_column_mux_test(unittest.TestCase):
os.remove(tempspice)
os.remove(tempgds)
OPTS.check_lvsdrc = True
# reset the static duplicate name checker for unit tests
import design
design.design.name_map=[]
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":

View File

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

View File

@ -20,16 +20,20 @@ class sense_amp_test(unittest.TestCase):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import sense_amp_array
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2")
OPTS.check_lvsdrc = False
a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2)
OPTS.check_lvsdrc = True
self.local_check(a)
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4")
a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4)
self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, a):
@ -45,6 +49,11 @@ class sense_amp_test(unittest.TestCase):
os.remove(tempspice)
os.remove(tempgds)
# reset the static duplicate name checker for unit tests
import design
design.design.name_map=[]
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()

View File

@ -20,15 +20,19 @@ class write_driver_test(unittest.TestCase):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import write_driver_array
debug.info(2, "Testing write_driver_array for columns=128, word_size=128")
OPTS.check_lvsdrc = False
a = write_driver_array.write_driver_array(columns=16, word_size=16)
OPTS.check_lvsdrc = True
debug.info(2, "Testing write_driver_array for columns=8, word_size=8")
a = write_driver_array.write_driver_array(columns=8, word_size=8)
self.local_check(a)
debug.info(2, "Testing write_driver_array for columns=16, word_size=8")
a = write_driver_array.write_driver_array(columns=16, word_size=8)
self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, a):
@ -44,6 +48,11 @@ class write_driver_test(unittest.TestCase):
os.remove(tempspice)
os.remove(tempgds)
# reset the static duplicate name checker for unit tests
import design
design.design.name_map=[]
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()

View File

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

View File

@ -23,11 +23,15 @@ class tri_gate_array_test(unittest.TestCase):
import tri_gate_array
debug.info(1, "Testing sample for tri_gate_array")
a = tri_gate_array.tri_gate_array(columns=16, word_size=16)
OPTS.check_lvsdrc = True
debug.info(1, "Testing tri_gate_array for columns=8, word_size=8")
a = tri_gate_array.tri_gate_array(columns=8, word_size=8)
self.local_check(a)
debug.info(1, "Testing tri_gate_array for columns=16, word_size=8")
a = tri_gate_array.tri_gate_array(columns=16, word_size=8)
self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, a):
@ -43,6 +47,11 @@ class tri_gate_array_test(unittest.TestCase):
os.remove(tempspice)
os.remove(tempgds)
# reset the static duplicate name checker for unit tests
import design
design.design.name_map=[]
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()

View File

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

View File

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

View File

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

View File

@ -25,16 +25,23 @@ class bank_test(unittest.TestCase):
import bank
# override these from the config file
OPTS.word_size=8
OPTS.num_words=128
OPTS.num_banks=1
debug.info(1, "Testing sample 8bit, 64word BANK")
a = bank.bank(word_size=OPTS.num_words, num_words=OPTS.num_words, words_per_row=2, num_banks=OPTS.num_banks, name="test_sram1")
OPTS.check_lvsdrc = True
debug.info(1, "No column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=2, num_banks=1, name="test_sram1")
self.local_check(a)
debug.info(1, "Two way column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=2, num_banks=1, name="test_sram2")
self.local_check(a)
debug.info(1, "Four way column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="test_sram3")
self.local_check(a)
debug.info(1, "Eight way column mux")
a = bank.bank(word_size=2, num_words=64, words_per_row=8, num_banks=1, name="test_sram4")
self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, a):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,16 +10,22 @@ class tri_gate(design.design):
netlist should be available in the technology library.
"""
pins = ["in", "en", "en_bar", "out", "gnd", "vdd"]
chars = utils.auto_measure_libcell(pins, "tri_gate", GDS["unit"], layer["boundary"])
pin_names = ["in", "en", "en_bar", "out", "gnd", "vdd"]
(width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"], layer["boundary"])
def __init__(self, name):
unique_id = 1
def __init__(self, name=""):
if name=="":
name = "tri{0}".format(tri_gate.unique_id)
tri_gate.unique_id += 1
design.design.__init__(self, name)
debug.info(2, "Create tri_gate object")
self.width = tri_gate.chars["width"]
self.height = tri_gate.chars["height"]
self.width = tri_gate.width
self.height = tri_gate.height
self.pin_map = tri_gate.pin_map
def delay(self, slew, load=0.0):
from tech import spice

View File

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

View File

@ -2,6 +2,8 @@ import os
import gdsMill
import tech
import globals
from vector import vector
from pin_layout import pin_layout
OPTS = globals.OPTS
@ -20,14 +22,18 @@ def snap_to_grid(offset):
out_offset = [xoff, yoff]
return out_offset
def gds_pin_center(gdsPin):
def pin_center(boundary):
"""
This returns the center of a pin shape
This returns the center of a pin shape in the vlsiLayout border format.
"""
boundary = gdsPin[2]
return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])]
def pin_rect(boundary):
"""
This returns a LL,UR point pair.
"""
return [vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])]
def auto_measure_libcell(pin_list, name, units, layer):
"""
Open a GDS file and find the pins in pin_list as text on a given layer.
@ -45,7 +51,49 @@ def auto_measure_libcell(pin_list, name, units, layer):
[cell["width"], cell["height"]] = measure_result
for pin in pin_list:
cell[str(pin)] = gds_pin_center(cell_vlsi.getPinShapeByLabel(str(pin)))
(name,layer,boundary)=cell_vlsi.getPinShapeByLabel(str(pin))
cell[str(pin)] = pin_center(boundary)
return cell
def get_libcell_size(name, units, layer):
"""
Open a GDS file and return the library cell size from either the
bounding box or a border layer.
"""
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(cell_gds)
cell = {}
measure_result = cell_vlsi.getLayoutBorder(layer)
if measure_result == None:
measure_result = cell_vlsi.measureSize(name)
# returns width,height
return measure_result
def get_libcell_pins(pin_list, name, units, layer):
"""
Open a GDS file and find the pins in pin_list as text on a given layer.
Return these as a rectangle layer pair for each pin.
"""
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(cell_gds)
cell = {}
for pin in pin_list:
cell[str(pin)]=[]
label_list=cell_vlsi.getPinShapeByLabel(str(pin))
for label in label_list:
(name,layer,boundary)=label
rect = pin_rect(boundary)
# this is a list because other cells/designs may have must-connect pins
cell[str(pin)].append(pin_layout(pin, rect, layer))
return cell

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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