OpenRAM/compiler/wordline_driver.py

209 lines
8.1 KiB
Python

from tech import drc, parameter
import debug
import design
from math import log
from math import sqrt
import math
from pinv import pinv
from nand_2 import nand_2
from vector import vector
from globals import OPTS
class wordline_driver(design.design):
"""
Creates a Wordline Driver
Generates the wordline-driver to drive the bitcell
"""
def __init__(self, rows):
design.design.__init__(self, "wordline_driver")
self.rows = rows
self.add_pins()
self.design_layout()
self.DRC_LVS()
def add_pins(self):
# inputs to wordline_driver.
for i in range(self.rows):
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("en")
self.add_pin("vdd")
self.add_pin("gnd")
def design_layout(self):
self.add_layout()
self.offsets_of_gates()
self.create_layout()
def add_layout(self):
self.inv = pinv()
self.add_mod(self.inv)
self.nand2 = nand_2()
self.add_mod(self.nand2)
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.width = self.x_offset2 + self.inv.width
self.height = self.inv.height * self.rows
def create_layout(self):
# 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 = "wl_driver_inv_en{}".format(row)
name_nand = "wl_driver_nand{}".format(row)
name_inv2 = "wl_driver_inv{}".format(row)
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)
inst_mirror = "MX"
cell_dir = vector(0,-1)
m1tm2_rotate=270
m1tm2_mirror="R0"
else:
y_offset = self.inv.height*row
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(["en", "en_bar[{0}]".format(row),
"vdd", "gnd"])
# add nand 2
self.add_inst(name=name_nand,
mod=self.nand2,
offset=nand2_offset,
mirror=inst_mirror)
self.connect_inst(["in[{0}]".format(row),
"en_bar[{0}]".format(row),
"net[{0}]".format(row),
"vdd", "gnd"])
# add inv2
self.add_inst(name=name_inv2,
mod=self.inv,
offset=inv2_offset,
mirror=inst_mirror)
self.connect_inst(["net[{0}]".format(row),
"wl[{0}]".format(row),
"vdd", "gnd"])
# clk connection
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.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.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"])
# 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=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.get_pin("A").by()],
rotate=m1tm2_rotate,
mirror=m1tm2_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)
def delay(self, slew, load=0):
# 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)
result = decode_t_net + net_t_wl
return result
def input_load(self):
return self.nand2.input_load()