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 import OPTS
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-04-22 00:36:38 +02:00
|
|
|
|
2022-07-13 19:57:56 +02:00
|
|
|
class delay_chain(design):
|
2017-08-24 00:02:15 +02:00
|
|
|
"""
|
2018-02-14 00:54:50 +01:00
|
|
|
Generate a delay chain with the given number of stages and fanout.
|
2018-07-19 19:51:20 +02:00
|
|
|
Input is a list contains the electrical effort (fanout) of each stage.
|
|
|
|
|
Usually, this will be constant, but it could have varied fanout.
|
2017-08-24 00:02:15 +02:00
|
|
|
"""
|
|
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
def __init__(self, name, fanout_list):
|
2017-08-24 00:02:15 +02:00
|
|
|
"""init function"""
|
2020-08-06 20:33:26 +02:00
|
|
|
super().__init__(name)
|
2019-01-26 00:00:00 +01:00
|
|
|
debug.info(1, "creating delay chain {0}".format(str(fanout_list)))
|
|
|
|
|
self.add_comment("fanouts: {0}".format(str(fanout_list)))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-07-19 19:51:20 +02:00
|
|
|
# Two fanouts are needed so that we can route the vdd/gnd connections
|
2018-02-16 20:51:01 +01:00
|
|
|
for f in fanout_list:
|
2020-04-22 00:36:38 +02:00
|
|
|
debug.check(f>=2, "Must have >=2 fanouts for each stage.")
|
2018-02-16 20:51:01 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# number of inverters including any fanout loads.
|
|
|
|
|
self.fanout_list = fanout_list
|
2021-05-05 01:42:42 +02:00
|
|
|
self.rows = len(self.fanout_list)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
self.create_netlist()
|
|
|
|
|
if not OPTS.netlist_only:
|
|
|
|
|
self.create_layout()
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
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_inverters()
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def create_layout(self):
|
|
|
|
|
# Each stage is a a row
|
2021-05-05 01:42:42 +02:00
|
|
|
self.height = self.rows * self.inv.height
|
2018-08-28 01:42:48 +02:00
|
|
|
# The width is determined by the largest fanout plus the driver
|
2020-04-22 00:36:38 +02:00
|
|
|
self.width = (max(self.fanout_list) + 1) * self.inv.width
|
2021-11-22 19:51:40 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
self.place_inverters()
|
2018-03-12 21:14:53 +01:00
|
|
|
self.route_inverters()
|
2020-04-22 01:12:54 +02:00
|
|
|
self.route_supplies()
|
2017-08-24 00:02:15 +02:00
|
|
|
self.add_layout_pins()
|
2019-05-28 01:32:38 +02:00
|
|
|
self.add_boundary()
|
2017-08-24 00:02:15 +02:00
|
|
|
self.DRC_LVS()
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def add_pins(self):
|
|
|
|
|
""" Add the pins of the delay chain"""
|
2019-08-06 23:14:09 +02:00
|
|
|
self.add_pin("in", "INPUT")
|
|
|
|
|
self.add_pin("out", "OUTPUT")
|
|
|
|
|
self.add_pin("vdd", "POWER")
|
|
|
|
|
self.add_pin("gnd", "GROUND")
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def add_modules(self):
|
2021-05-06 00:45:28 +02:00
|
|
|
|
|
|
|
|
self.dff = factory.create(module_type="dff_buf")
|
|
|
|
|
dff_height = self.dff.height
|
2021-11-22 19:51:40 +01:00
|
|
|
|
2021-05-06 00:45:28 +02:00
|
|
|
self.inv = factory.create(module_type="pinv",
|
|
|
|
|
height=dff_height)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def create_inverters(self):
|
|
|
|
|
""" Create the inverters and connect them based on the stage list """
|
2018-03-12 21:14:53 +01:00
|
|
|
self.driver_inst_list = []
|
|
|
|
|
self.load_inst_map = {}
|
2021-05-05 01:42:42 +02:00
|
|
|
for stage_num, fanout_size in zip(range(self.rows), self.fanout_list):
|
2018-03-12 21:14:53 +01:00
|
|
|
# Add the inverter
|
|
|
|
|
cur_driver=self.add_inst(name="dinv{}".format(stage_num),
|
2018-08-28 01:42:48 +02:00
|
|
|
mod=self.inv)
|
2017-08-24 00:02:15 +02:00
|
|
|
# keep track of the inverter instances so we can use them to get the pins
|
2018-03-12 21:14:53 +01:00
|
|
|
self.driver_inst_list.append(cur_driver)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-03-12 21:14:53 +01:00
|
|
|
# Hook up the driver
|
2021-05-05 01:42:42 +02:00
|
|
|
if stage_num + 1 == self.rows:
|
2018-03-12 21:14:53 +01:00
|
|
|
stageout_name = "out"
|
2017-08-24 00:02:15 +02:00
|
|
|
else:
|
2020-04-22 00:36:38 +02:00
|
|
|
stageout_name = "dout_{}".format(stage_num + 1)
|
2018-03-12 21:14:53 +01:00
|
|
|
if stage_num == 0:
|
|
|
|
|
stagein_name = "in"
|
2017-08-24 00:02:15 +02:00
|
|
|
else:
|
2020-04-22 00:36:38 +02:00
|
|
|
stagein_name = "dout_{}".format(stage_num)
|
2018-03-12 21:14:53 +01:00
|
|
|
self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"])
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-12 21:14:53 +01:00
|
|
|
# Now add the dummy loads to the right
|
|
|
|
|
self.load_inst_map[cur_driver]=[]
|
|
|
|
|
for i in range(fanout_size):
|
2020-04-22 00:36:38 +02:00
|
|
|
cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num, i),
|
2018-08-28 01:42:48 +02:00
|
|
|
mod=self.inv)
|
2018-03-12 21:14:53 +01:00
|
|
|
# Fanout stage is always driven by driver and output is disconnected
|
2020-04-22 00:36:38 +02:00
|
|
|
disconnect_name = "n_{0}_{1}".format(stage_num, i)
|
2018-03-12 21:14:53 +01:00
|
|
|
self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"])
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-12 21:14:53 +01:00
|
|
|
# Keep track of all the loads to connect their inputs as a load
|
|
|
|
|
self.load_inst_map[cur_driver].append(cur_load)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
|
|
|
|
def place_inverters(self):
|
|
|
|
|
""" Place the inverters and connect them based on the stage list """
|
2021-05-05 01:42:42 +02:00
|
|
|
for stage_num, fanout_size in zip(range(self.rows), self.fanout_list):
|
2018-08-28 01:42:48 +02:00
|
|
|
if stage_num % 2:
|
|
|
|
|
inv_mirror = "MX"
|
2020-04-22 00:36:38 +02:00
|
|
|
inv_offset = vector(0, (stage_num + 1) * self.inv.height)
|
2018-08-28 01:42:48 +02:00
|
|
|
else:
|
|
|
|
|
inv_mirror = "R0"
|
2020-04-22 00:36:38 +02:00
|
|
|
inv_offset = vector(0, stage_num * self.inv.height)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
# Add the inverter
|
2018-08-28 02:25:39 +02:00
|
|
|
cur_driver=self.driver_inst_list[stage_num]
|
|
|
|
|
cur_driver.place(offset=inv_offset,
|
|
|
|
|
mirror=inv_mirror)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
# Now add the dummy loads to the right
|
2018-08-28 02:25:39 +02:00
|
|
|
load_list = self.load_inst_map[cur_driver]
|
2018-08-28 01:42:48 +02:00
|
|
|
for i in range(fanout_size):
|
2020-04-22 00:36:38 +02:00
|
|
|
inv_offset += vector(self.inv.width, 0)
|
2018-08-28 02:25:39 +02:00
|
|
|
load_list[i].place(offset=inv_offset,
|
|
|
|
|
mirror=inv_mirror)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
def add_route(self, pin1, pin2):
|
|
|
|
|
""" This guarantees that we route from the top to bottom row correctly. """
|
|
|
|
|
pin1_pos = pin1.center()
|
|
|
|
|
pin2_pos = pin2.center()
|
|
|
|
|
if pin1_pos.y == pin2_pos.y:
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_path("m2", [pin1_pos, pin2_pos])
|
2017-12-12 23:53:19 +01:00
|
|
|
else:
|
2020-04-22 00:36:38 +02:00
|
|
|
mid_point = vector(pin2_pos.x, 0.5 * (pin1_pos.y + pin2_pos.y))
|
2017-12-12 23:53:19 +01:00
|
|
|
# Written this way to guarantee it goes right first if we are switching rows
|
2020-04-22 00:36:38 +02:00
|
|
|
self.add_path("m2", [pin1_pos, vector(pin1_pos.x, mid_point.y), mid_point, vector(mid_point.x, pin2_pos.y), pin2_pos])
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-12 21:14:53 +01:00
|
|
|
def route_inverters(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" Add metal routing for each of the fanout stages """
|
2018-03-12 21:14:53 +01:00
|
|
|
|
|
|
|
|
for i in range(len(self.driver_inst_list)):
|
|
|
|
|
inv = self.driver_inst_list[i]
|
|
|
|
|
for load in self.load_inst_map[inv]:
|
|
|
|
|
# Drop a via on each A pin
|
2020-04-22 00:36:38 +02:00
|
|
|
a_pin = load.get_pin("A")
|
2020-07-01 18:22:59 +02:00
|
|
|
self.add_via_stack_center(from_layer=a_pin.layer,
|
|
|
|
|
to_layer="m3",
|
|
|
|
|
offset=a_pin.center())
|
2018-03-12 21:14:53 +01:00
|
|
|
|
|
|
|
|
# Route an M3 horizontal wire to the furthest
|
|
|
|
|
z_pin = inv.get_pin("Z")
|
|
|
|
|
a_pin = inv.get_pin("A")
|
2020-04-22 01:12:54 +02:00
|
|
|
a_max = self.load_inst_map[inv][-1].get_pin("A")
|
2020-07-01 18:22:59 +02:00
|
|
|
self.add_via_stack_center(from_layer=a_pin.layer,
|
|
|
|
|
to_layer="m2",
|
|
|
|
|
offset=a_pin.center())
|
|
|
|
|
self.add_via_stack_center(from_layer=z_pin.layer,
|
|
|
|
|
to_layer="m3",
|
|
|
|
|
offset=z_pin.center())
|
2020-04-22 00:36:38 +02:00
|
|
|
self.add_path("m3", [z_pin.center(), a_max.center()])
|
2018-03-12 21:14:53 +01:00
|
|
|
|
|
|
|
|
# Route Z to the A of the next stage
|
2020-04-22 00:36:38 +02:00
|
|
|
if i + 1 < len(self.driver_inst_list):
|
2018-03-12 21:14:53 +01:00
|
|
|
z_pin = inv.get_pin("Z")
|
2020-04-22 00:36:38 +02:00
|
|
|
next_inv = self.driver_inst_list[i + 1]
|
2018-03-12 21:14:53 +01:00
|
|
|
next_a_pin = next_inv.get_pin("A")
|
2020-04-22 00:36:38 +02:00
|
|
|
y_mid = (z_pin.cy() + next_a_pin.cy()) / 2
|
2018-03-12 21:14:53 +01:00
|
|
|
mid1_point = vector(z_pin.cx(), y_mid)
|
|
|
|
|
mid2_point = vector(next_a_pin.cx(), y_mid)
|
2020-04-22 00:36:38 +02:00
|
|
|
self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
|
2018-03-12 21:14:53 +01:00
|
|
|
|
2020-04-22 01:12:54 +02:00
|
|
|
def route_supplies(self):
|
2022-05-03 01:42:14 +02:00
|
|
|
# These pins get routed in one cell from the left/right
|
|
|
|
|
# because the input signal gets routed on M3 and can interfere with the delay input.
|
|
|
|
|
self.route_vertical_pins("vdd", self.driver_inst_list, xside="rx")
|
2022-05-03 00:43:14 +02:00
|
|
|
right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list]
|
2022-05-03 01:42:14 +02:00
|
|
|
self.route_vertical_pins("gnd", right_load_insts, xside="lx")
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-04-22 01:12:54 +02:00
|
|
|
def add_layout_pins(self):
|
2018-07-19 19:37:47 +02:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# input is A pin of first inverter
|
2021-05-06 23:32:47 +02:00
|
|
|
# It gets routed to the left a bit to prevent pin access errors
|
|
|
|
|
# due to the output pin when going up to M3
|
2018-03-12 21:14:53 +01:00
|
|
|
a_pin = self.driver_inst_list[0].get_pin("A")
|
2021-05-06 23:32:47 +02:00
|
|
|
mid_loc = vector(a_pin.cx() - self.m3_pitch, a_pin.cy())
|
2020-07-01 18:22:59 +02:00
|
|
|
self.add_via_stack_center(from_layer=a_pin.layer,
|
2021-05-06 23:32:47 +02:00
|
|
|
to_layer="m2",
|
|
|
|
|
offset=mid_loc)
|
|
|
|
|
self.add_path(a_pin.layer, [a_pin.center(), mid_loc])
|
2021-11-22 19:51:40 +01:00
|
|
|
|
2021-05-05 01:42:42 +02:00
|
|
|
self.add_layout_pin_rect_center(text="in",
|
|
|
|
|
layer="m2",
|
2021-05-06 23:32:47 +02:00
|
|
|
offset=mid_loc)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2021-05-05 01:42:42 +02:00
|
|
|
# output is A pin of last load/fanout inverter
|
2018-03-12 21:14:53 +01:00
|
|
|
last_driver_inst = self.driver_inst_list[-1]
|
2020-04-22 01:12:54 +02:00
|
|
|
a_pin = self.load_inst_map[last_driver_inst][-1].get_pin("A")
|
2020-07-01 18:22:59 +02:00
|
|
|
self.add_via_stack_center(from_layer=a_pin.layer,
|
2021-05-05 01:42:42 +02:00
|
|
|
to_layer="m1",
|
2020-07-01 18:22:59 +02:00
|
|
|
offset=a_pin.center())
|
2021-05-05 01:42:42 +02:00
|
|
|
self.add_layout_pin_rect_center(text="out",
|
|
|
|
|
layer="m1",
|
|
|
|
|
offset=a_pin.center())
|