OpenRAM/compiler/modules/wordline_driver.py

213 lines
8.0 KiB
Python

from tech import drc, parameter
import debug
import design
import contact
from math import log
from math import sqrt
import math
from pinv import pinv
from pnand2 import pnand2
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.offset_all_coordinates()
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.inv_no_output = pinv(route_output=False)
self.add_mod(self.inv_no_output)
self.nand2 = pnand2()
self.add_mod(self.nand2)
def offsets_of_gates(self):
self.x_offset0 = 2*self.m1_width + 5*self.m1_space
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
en_pin=self.add_layout_pin(text="en",
layer="metal2",
offset=[self.m1_width + 2*self.m1_space,0],
width=self.m2_width,
height=self.height)
self.add_layout_pin(text="gnd",
layer="metal1",
offset=[0, -0.5*self.m1_width],
width=self.x_offset0,
height=self.m1_width)
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)
+ self.m1_width)
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 * self.m1_width
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=self.m1_width)
# add inv1 based on the info above
inv1_inst=self.add_inst(name=name_inv1,
mod=self.inv_no_output,
offset=name_inv1_offset,
mirror=inst_mirror )
self.connect_inst(["en",
"en_bar[{0}]".format(row),
"vdd", "gnd"])
# add nand 2
nand_inst=self.add_inst(name=name_nand,
mod=self.nand2,
offset=nand2_offset,
mirror=inst_mirror)
self.connect_inst(["en_bar[{0}]".format(row),
"in[{0}]".format(row),
"net[{0}]".format(row),
"vdd", "gnd"])
# add inv2
inv2_inst=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"])
# en connection
a_pin = inv1_inst.get_pin("A")
a_pos = a_pin.lc()
clk_offset = vector(en_pin.bc().x,a_pos.y)
self.add_segment_center(layer="metal1",
start=clk_offset,
end=a_pos)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=clk_offset)
# first inv to nand2 A
zb_pos = inv1_inst.get_pin("Z").bc()
zu_pos = inv1_inst.get_pin("Z").uc()
bl_pos = nand_inst.get_pin("A").lc()
br_pos = nand_inst.get_pin("A").rc()
self.add_path("metal1", [zb_pos, zu_pos, bl_pos, br_pos])
# Nand2 out to 2nd inv
zr_pos = nand_inst.get_pin("Z").rc()
al_pos = inv2_inst.get_pin("A").lc()
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
# connect the decoder input pin to nand2 B
b_pin = nand_inst.get_pin("B")
b_pos = b_pin.lc()
# needs to move down since B nand input is nearly aligned with A inv input
up_or_down = self.m2_space if row%2 else -self.m2_space
input_offset = vector(0,b_pos.y + up_or_down)
mid_via_offset = vector(clk_offset.x,input_offset.y) + vector(0.5*self.m2_width+self.m2_space+0.5*contact.m1m2.width,0)
# must under the clk line in M1
self.add_layout_pin_segment_center(text="in[{0}]".format(row),
layer="metal1",
start=input_offset,
end=mid_via_offset)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=mid_via_offset)
# now connect to the nand2 B
self.add_path("metal2", [mid_via_offset, b_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=b_pos - vector(0.5*contact.m1m2.height,0),
rotate=90)
# output each WL on the right
wl_offset = inv2_inst.get_pin("Z").rc()
self.add_layout_pin_segment_center(text="wl[{0}]".format(row),
layer="metal1",
start=wl_offset,
end=wl_offset-vector(self.m1_width,0))
def analytical_delay(self, slew, load=0):
# decode -> net
decode_t_net = self.nand2.analytical_delay(slew, self.inv.input_load())
# net -> wl
net_t_wl = self.inv.analytical_delay(decode_t_net.slew, load)
return decode_t_net + net_t_wl
def input_load(self):
return self.nand2.input_load()