2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-06-14 17:43:41 +02:00
|
|
|
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
|
|
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
|
|
|
# (acting for and on behalf of Oklahoma State University)
|
|
|
|
|
# All rights reserved.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2016-11-08 18:57:35 +01:00
|
|
|
import debug
|
|
|
|
|
import design
|
2017-12-12 23:53:19 +01:00
|
|
|
import contact
|
2016-11-08 18:57:35 +01:00
|
|
|
from vector import vector
|
2019-01-17 01:15:38 +01:00
|
|
|
from sram_factory import factory
|
2016-11-08 18:57:35 +01:00
|
|
|
from globals import OPTS
|
|
|
|
|
|
2019-11-06 22:19:36 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
class wordline_driver(design.design):
|
|
|
|
|
"""
|
|
|
|
|
Creates a Wordline Driver
|
|
|
|
|
Generates the wordline-driver to drive the bitcell
|
|
|
|
|
"""
|
|
|
|
|
|
2019-01-24 19:20:23 +01:00
|
|
|
def __init__(self, name, rows, cols):
|
2019-01-17 01:15:38 +01:00
|
|
|
design.design.__init__(self, name)
|
2019-01-26 00:00:00 +01:00
|
|
|
debug.info(1, "Creating {0}".format(self.name))
|
|
|
|
|
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.rows = rows
|
2019-01-24 19:20:23 +01:00
|
|
|
self.cols = cols
|
2018-08-28 19:24:09 +02:00
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
self.create_netlist()
|
2018-08-28 01:42:48 +02:00
|
|
|
if not OPTS.netlist_only:
|
|
|
|
|
self.create_layout()
|
|
|
|
|
|
|
|
|
|
def create_netlist(self):
|
|
|
|
|
self.add_modules()
|
2018-08-28 19:24:09 +02:00
|
|
|
self.add_pins()
|
2018-08-28 01:42:48 +02:00
|
|
|
self.create_drivers()
|
|
|
|
|
|
|
|
|
|
def create_layout(self):
|
|
|
|
|
self.place_drivers()
|
|
|
|
|
self.route_layout()
|
|
|
|
|
self.route_vdd_gnd()
|
2018-03-19 23:11:42 +01:00
|
|
|
self.offset_all_coordinates()
|
2019-05-28 01:32:38 +02:00
|
|
|
self.add_boundary()
|
2016-11-08 18:57:35 +01:00
|
|
|
self.DRC_LVS()
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def add_pins(self):
|
|
|
|
|
# inputs to wordline_driver.
|
|
|
|
|
for i in range(self.rows):
|
2019-08-06 23:14:09 +02:00
|
|
|
self.add_pin("in_{0}".format(i), "INPUT")
|
2016-11-08 18:57:35 +01:00
|
|
|
# Outputs from wordline_driver.
|
|
|
|
|
for i in range(self.rows):
|
2019-08-06 23:14:09 +02:00
|
|
|
self.add_pin("wl_{0}".format(i), "OUTPUT")
|
|
|
|
|
self.add_pin("en", "INPUT")
|
|
|
|
|
self.add_pin("vdd", "POWER")
|
|
|
|
|
self.add_pin("gnd", "GROUND")
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
def add_modules(self):
|
2019-01-24 19:20:23 +01:00
|
|
|
self.inv = factory.create(module_type="pdriver",
|
|
|
|
|
fanout=self.cols,
|
|
|
|
|
neg_polarity=True)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_mod(self.inv)
|
|
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
self.nand2 = factory.create(module_type="pnand2")
|
2017-08-24 00:02:15 +02:00
|
|
|
self.add_mod(self.nand2)
|
2018-09-04 02:31:12 +02:00
|
|
|
|
2018-04-12 01:23:45 +02:00
|
|
|
def route_vdd_gnd(self):
|
2019-11-06 22:19:36 +01:00
|
|
|
"""
|
|
|
|
|
Add a pin for each row of vdd/gnd which
|
|
|
|
|
are must-connects next level up.
|
|
|
|
|
"""
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-04-12 01:23:45 +02:00
|
|
|
# Find the x offsets for where the vias/pins should be placed
|
2018-11-27 01:41:31 +01:00
|
|
|
a_xoffset = self.nand_inst[0].rx()
|
2018-04-12 01:23:45 +02:00
|
|
|
b_xoffset = self.inv2_inst[0].lx()
|
|
|
|
|
for num in range(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
|
2019-11-06 22:19:36 +01:00
|
|
|
(gate_offset, y_dir) = self.get_gate_offset(0,
|
|
|
|
|
self.inv.height,
|
|
|
|
|
num)
|
2018-04-12 01:23:45 +02:00
|
|
|
|
|
|
|
|
# Route both supplies
|
2019-04-01 23:23:47 +02:00
|
|
|
for name in ["vdd", "gnd"]:
|
|
|
|
|
supply_pin = self.inv2_inst[num].get_pin(name)
|
2018-04-12 01:23:45 +02:00
|
|
|
|
|
|
|
|
# Add pins in two locations
|
|
|
|
|
for xoffset in [a_xoffset, b_xoffset]:
|
|
|
|
|
pin_pos = vector(xoffset, supply_pin.cy())
|
2019-04-01 23:23:47 +02:00
|
|
|
self.add_power_pin(name, pin_pos)
|
2018-04-12 01:23:45 +02:00
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
def create_drivers(self):
|
|
|
|
|
self.nand_inst = []
|
|
|
|
|
self.inv2_inst = []
|
|
|
|
|
for row in range(self.rows):
|
|
|
|
|
name_nand = "wl_driver_nand{}".format(row)
|
|
|
|
|
name_inv2 = "wl_driver_inv{}".format(row)
|
|
|
|
|
|
|
|
|
|
# add nand 2
|
|
|
|
|
self.nand_inst.append(self.add_inst(name=name_nand,
|
|
|
|
|
mod=self.nand2))
|
2019-07-05 19:31:05 +02:00
|
|
|
self.connect_inst(["en",
|
2018-10-11 18:53:08 +02:00
|
|
|
"in_{0}".format(row),
|
|
|
|
|
"wl_bar_{0}".format(row),
|
2018-08-27 20:13:34 +02:00
|
|
|
"vdd", "gnd"])
|
|
|
|
|
# add inv2
|
|
|
|
|
self.inv2_inst.append(self.add_inst(name=name_inv2,
|
|
|
|
|
mod=self.inv))
|
2018-10-11 18:53:08 +02:00
|
|
|
self.connect_inst(["wl_bar_{0}".format(row),
|
|
|
|
|
"wl_{0}".format(row),
|
2018-08-27 20:13:34 +02:00
|
|
|
"vdd", "gnd"])
|
2019-11-06 22:19:36 +01:00
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
def place_drivers(self):
|
2018-11-27 01:41:31 +01:00
|
|
|
nand2_xoffset = 2*self.m1_width + 5*self.m1_space
|
2018-04-12 01:23:45 +02:00
|
|
|
inv2_xoffset = nand2_xoffset + self.nand2.width
|
|
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
self.width = inv2_xoffset + self.inv.width
|
2018-09-10 07:06:29 +02:00
|
|
|
self.height = self.inv.height * self.rows
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
for row in range(self.rows):
|
|
|
|
|
if (row % 2):
|
2018-11-14 01:05:22 +01:00
|
|
|
y_offset = self.inv.height*(row + 1)
|
2016-11-08 18:57:35 +01:00
|
|
|
inst_mirror = "MX"
|
|
|
|
|
else:
|
2018-11-14 01:05:22 +01:00
|
|
|
y_offset = self.inv.height*row
|
2016-11-08 18:57:35 +01:00
|
|
|
inst_mirror = "R0"
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2019-11-06 22:19:36 +01:00
|
|
|
nand2_offset = [nand2_xoffset, y_offset]
|
|
|
|
|
inv2_offset = [inv2_xoffset, y_offset]
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
# add nand 2
|
2018-08-28 02:25:39 +02:00
|
|
|
self.nand_inst[row].place(offset=nand2_offset,
|
|
|
|
|
mirror=inst_mirror)
|
2016-11-08 18:57:35 +01:00
|
|
|
# add inv2
|
2018-08-28 02:25:39 +02:00
|
|
|
self.inv2_inst[row].place(offset=inv2_offset,
|
|
|
|
|
mirror=inst_mirror)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-04-12 01:23:45 +02:00
|
|
|
def route_layout(self):
|
|
|
|
|
""" Route all of the signals """
|
|
|
|
|
|
|
|
|
|
# Wordline enable connection
|
2019-11-06 22:19:36 +01:00
|
|
|
en_offset = [self.m1_width + 2 * self.m1_space, 0]
|
|
|
|
|
en_pin = self.add_layout_pin(text="en",
|
|
|
|
|
layer="metal2",
|
|
|
|
|
offset=en_offset,
|
|
|
|
|
width=self.m2_width,
|
|
|
|
|
height=self.height)
|
2018-04-12 01:23:45 +02:00
|
|
|
|
|
|
|
|
for row in range(self.rows):
|
|
|
|
|
nand_inst = self.nand_inst[row]
|
|
|
|
|
inv2_inst = self.inv2_inst[row]
|
|
|
|
|
|
2019-07-05 19:31:05 +02:00
|
|
|
# en connection
|
2018-11-27 01:41:31 +01:00
|
|
|
a_pin = nand_inst.get_pin("A")
|
2017-10-07 00:30:15 +02:00
|
|
|
a_pos = a_pin.lc()
|
2019-11-06 22:19:36 +01:00
|
|
|
clk_offset = vector(en_pin.bc().x, a_pos.y)
|
2017-11-30 21:01:04 +01:00
|
|
|
self.add_segment_center(layer="metal1",
|
2017-11-29 03:13:32 +01:00
|
|
|
start=clk_offset,
|
|
|
|
|
end=a_pos)
|
2018-01-26 21:39:00 +01:00
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=clk_offset)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
# Nand2 out to 2nd inv
|
2017-10-07 00:30:15 +02:00
|
|
|
zr_pos = nand_inst.get_pin("Z").rc()
|
2017-12-12 23:53:19 +01:00
|
|
|
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()
|
2019-11-06 22:19:36 +01:00
|
|
|
# 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)
|
|
|
|
|
base_offset = vector(clk_offset.x, input_offset.y)
|
|
|
|
|
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1m2.width, 0)
|
|
|
|
|
mid_via_offset = base_offset + contact_offset
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# must under the clk line in M1
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin_segment_center(text="in_{0}".format(row),
|
2017-11-29 03:13:32 +01:00
|
|
|
layer="metal1",
|
|
|
|
|
start=input_offset,
|
|
|
|
|
end=mid_via_offset)
|
2017-11-30 21:01:04 +01:00
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=mid_via_offset,
|
2019-11-06 22:19:36 +01:00
|
|
|
directions=("V", "V"))
|
2017-10-07 00:30:15 +02:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
# now connect to the nand2 B
|
|
|
|
|
self.add_path("metal2", [mid_via_offset, b_pos])
|
2019-11-06 22:19:36 +01:00
|
|
|
contact_offset = b_pos - vector(0.5 * contact.m1m2.height, 0)
|
2017-11-30 21:01:04 +01:00
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-11-06 22:19:36 +01:00
|
|
|
offset=contact_offset,
|
|
|
|
|
directions=("H", "H"))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# output each WL on the right
|
2017-10-07 00:30:15 +02:00
|
|
|
wl_offset = inv2_inst.get_pin("Z").rc()
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin_segment_center(text="wl_{0}".format(row),
|
2017-11-29 03:13:32 +01:00
|
|
|
layer="metal1",
|
|
|
|
|
start=wl_offset,
|
2019-11-06 22:19:36 +01:00
|
|
|
end=wl_offset - vector(self.m1_width, 0))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-11-15 08:34:53 +01:00
|
|
|
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
2019-11-06 22:19:36 +01:00
|
|
|
"""
|
|
|
|
|
Follows the clk_buf to a wordline signal adding
|
|
|
|
|
each stages stage effort to a list.
|
|
|
|
|
"""
|
2018-11-08 09:10:51 +01:00
|
|
|
stage_effort_list = []
|
2018-12-06 02:10:11 +01:00
|
|
|
|
|
|
|
|
stage1_cout = self.inv.get_cin()
|
2019-01-23 21:03:52 +01:00
|
|
|
stage1 = self.nand2.get_stage_effort(stage1_cout, inp_is_rise)
|
2018-11-08 09:10:51 +01:00
|
|
|
stage_effort_list.append(stage1)
|
2018-11-15 08:34:53 +01:00
|
|
|
last_stage_is_rise = stage1.is_rise
|
2018-11-08 09:10:51 +01:00
|
|
|
|
2019-01-25 19:26:31 +01:00
|
|
|
stage2 = self.inv.get_stage_efforts(external_cout, last_stage_is_rise)
|
|
|
|
|
stage_effort_list.extend(stage2)
|
2018-11-08 09:10:51 +01:00
|
|
|
|
|
|
|
|
return stage_effort_list
|
|
|
|
|
|
2018-12-06 02:10:11 +01:00
|
|
|
def get_wl_en_cin(self):
|
2019-11-06 22:19:36 +01:00
|
|
|
"""
|
|
|
|
|
Get the relative capacitance of all
|
|
|
|
|
the enable connections in the bank
|
|
|
|
|
"""
|
|
|
|
|
# The enable is connected to a nand2 for every row.
|
2018-12-06 02:10:11 +01:00
|
|
|
total_cin = self.nand2.get_cin() * self.rows
|
2019-01-17 01:15:38 +01:00
|
|
|
return total_cin
|