2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2022-11-30 23:50:43 +01:00
|
|
|
# Copyright (c) 2016-2022 Regents of the University of California and The Board
|
2019-06-14 17:43:41 +02:00
|
|
|
# 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
|
|
|
#
|
2022-11-27 22:01:20 +01:00
|
|
|
from openram import debug
|
|
|
|
|
from openram.base import design
|
|
|
|
|
from openram.base import vector
|
|
|
|
|
from openram.sram_factory import factory
|
|
|
|
|
from openram.tech import drc, layer
|
|
|
|
|
from openram.tech import layer_properties as layer_props
|
|
|
|
|
from openram import OPTS
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-07-23 23:43:14 +02:00
|
|
|
|
2022-07-13 19:57:56 +02:00
|
|
|
class wordline_driver_array(design):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
|
|
|
|
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):
|
2020-08-06 20:33:26 +02:00
|
|
|
super().__init__(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))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-05-14 20:20:37 +02:00
|
|
|
self.rows = rows
|
|
|
|
|
self.cols = cols
|
2020-04-22 22:27:50 +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()
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def create_layout(self):
|
2020-05-14 20:20:37 +02:00
|
|
|
if "li" in layer:
|
|
|
|
|
self.route_layer = "li"
|
|
|
|
|
else:
|
|
|
|
|
self.route_layer = "m1"
|
2022-03-05 00:44:07 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
self.place_drivers()
|
|
|
|
|
self.route_layout()
|
2022-03-06 18:56:00 +01:00
|
|
|
self.offset_x_coordinates(vector(-2*self.m4_pitch, 0))
|
2022-03-05 00:44:07 +01:00
|
|
|
|
|
|
|
|
# Leave a well gap to separate the bitcell array well from this well
|
|
|
|
|
well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active")
|
|
|
|
|
self.width = self.wld_inst[-1].rx() + well_gap
|
|
|
|
|
self.height = self.wld_inst[-1].uy()
|
|
|
|
|
|
2019-05-28 01:32:38 +02:00
|
|
|
self.add_boundary()
|
2022-04-05 22:51:55 +02:00
|
|
|
self.route_supplies()
|
2016-11-08 18:57:35 +01:00
|
|
|
self.DRC_LVS()
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def add_pins(self):
|
|
|
|
|
# inputs to wordline_driver.
|
2020-05-14 20:20:37 +02:00
|
|
|
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.
|
2020-05-14 20:20:37 +02:00
|
|
|
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):
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-05-14 20:20:37 +02:00
|
|
|
self.wl_driver = factory.create(module_type="wordline_driver",
|
2020-09-17 23:45:49 +02:00
|
|
|
cols=self.cols)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2022-04-05 22:51:55 +02:00
|
|
|
def route_supplies(self):
|
2019-11-06 22:51:21 +01:00
|
|
|
"""
|
2022-03-03 01:29:43 +01:00
|
|
|
Add vertical power rails.
|
2019-11-06 22:19:36 +01:00
|
|
|
"""
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2022-05-03 00:43:14 +02:00
|
|
|
if layer_props.wordline_driver.vertical_supply:
|
|
|
|
|
self.route_vertical_pins("vdd", self.wld_inst)
|
|
|
|
|
self.route_vertical_pins("gnd", self.wld_inst)
|
|
|
|
|
else:
|
|
|
|
|
self.route_vertical_pins("vdd", self.wld_inst, xside="rx",)
|
|
|
|
|
self.route_vertical_pins("gnd", self.wld_inst, xside="lx",)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
|
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
def create_drivers(self):
|
2020-05-14 20:20:37 +02:00
|
|
|
self.wld_inst = []
|
|
|
|
|
for row in range(self.rows):
|
2020-04-22 22:27:50 +02:00
|
|
|
name_and = "wl_driver_and{}".format(row)
|
2018-08-27 20:13:34 +02:00
|
|
|
|
2020-04-22 22:27:50 +02:00
|
|
|
# add and2
|
2020-05-14 20:20:37 +02:00
|
|
|
self.wld_inst.append(self.add_inst(name=name_and,
|
|
|
|
|
mod=self.wl_driver))
|
2022-03-05 00:44:07 +01:00
|
|
|
self.connect_inst(["in_{0}".format(row),
|
|
|
|
|
"en",
|
2018-10-11 18:53:08 +02:00
|
|
|
"wl_{0}".format(row),
|
2018-08-27 20:13:34 +02:00
|
|
|
"vdd", "gnd"])
|
2020-04-22 22:27:50 +02:00
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
def place_drivers(self):
|
2020-05-07 21:35:21 +02:00
|
|
|
|
2020-05-14 20:20:37 +02:00
|
|
|
for row in range(self.rows):
|
2016-11-08 18:57:35 +01:00
|
|
|
if (row % 2):
|
2020-05-14 20:20:37 +02:00
|
|
|
y_offset = self.wl_driver.height * (row + 1)
|
2016-11-08 18:57:35 +01:00
|
|
|
inst_mirror = "MX"
|
|
|
|
|
else:
|
2020-05-14 20:20:37 +02:00
|
|
|
y_offset = self.wl_driver.height * row
|
2016-11-08 18:57:35 +01:00
|
|
|
inst_mirror = "R0"
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-05-14 20:20:37 +02:00
|
|
|
and2_offset = [self.wl_driver.width, y_offset]
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-04-22 22:27:50 +02:00
|
|
|
# add and2
|
2020-05-14 20:20:37 +02:00
|
|
|
self.wld_inst[row].place(offset=and2_offset,
|
|
|
|
|
mirror=inst_mirror)
|
|
|
|
|
|
2018-04-12 01:23:45 +02:00
|
|
|
def route_layout(self):
|
|
|
|
|
""" Route all of the signals """
|
|
|
|
|
|
|
|
|
|
# Wordline enable connection
|
2022-03-03 18:53:24 +01:00
|
|
|
en_pin = self.wld_inst[0].get_pin("B")
|
2022-03-03 01:29:43 +01:00
|
|
|
en_bottom_pos = vector(en_pin.cx(), 0)
|
2022-03-05 00:44:07 +01:00
|
|
|
en_top_pos = vector(en_pin.cx(), self.wld_inst[-1].uy())
|
2022-03-03 01:29:43 +01:00
|
|
|
en_pin = self.add_layout_pin_segment_center(text="en",
|
|
|
|
|
layer="m2",
|
|
|
|
|
start=en_bottom_pos,
|
2022-07-22 18:52:38 +02:00
|
|
|
end=en_top_pos)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-05-14 20:20:37 +02:00
|
|
|
for row in range(self.rows):
|
|
|
|
|
and_inst = self.wld_inst[row]
|
2019-11-06 22:19:36 +01:00
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
# Drop a via
|
2022-03-05 00:44:07 +01:00
|
|
|
b_pin = and_inst.get_pin("B")
|
2020-05-07 21:35:21 +02:00
|
|
|
self.add_via_stack_center(from_layer=b_pin.layer,
|
|
|
|
|
to_layer="m2",
|
|
|
|
|
offset=b_pin.center())
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-04-23 23:43:54 +02:00
|
|
|
# connect the decoder input pin to and2 A
|
2022-03-03 18:53:24 +01:00
|
|
|
self.copy_layout_pin(and_inst, "A", "in_{0}".format(row))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# output each WL on the right
|
2020-04-22 22:27:50 +02:00
|
|
|
wl_offset = and_inst.get_pin("Z").rc()
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin_segment_center(text="wl_{0}".format(row),
|
2020-05-14 20:20:37 +02:00
|
|
|
layer=self.route_layer,
|
2017-11-29 03:13:32 +01:00
|
|
|
start=wl_offset,
|
2019-11-06 22:19:36 +01:00
|
|
|
end=wl_offset - vector(self.m1_width, 0))
|