2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-05-06 15:50:15 +02:00
|
|
|
#Copyright (c) 2016-2019 Regents of the University of California and The Board
|
2019-04-26 21:21:50 +02:00
|
|
|
#of Regents for the Oklahoma Agricultural and Mechanical College
|
|
|
|
|
#(acting for and on behalf of Oklahoma State University)
|
|
|
|
|
#All rights reserved.
|
|
|
|
|
#
|
2016-11-08 18:57:35 +01:00
|
|
|
import debug
|
|
|
|
|
import design
|
2016-11-09 21:20:52 +01:00
|
|
|
from tech import drc
|
2017-12-12 23:53:19 +01:00
|
|
|
import contact
|
2019-01-17 01:15:38 +01:00
|
|
|
from sram_factory import factory
|
2016-11-08 18:57:35 +01:00
|
|
|
from vector import vector
|
|
|
|
|
from globals import OPTS
|
|
|
|
|
|
|
|
|
|
class replica_bitline(design.design):
|
|
|
|
|
"""
|
2018-02-07 23:54:59 +01:00
|
|
|
Generate a module that simulates the delay of control logic
|
2018-02-16 20:51:01 +01:00
|
|
|
and bit line charging. Stages is the depth of the delay
|
2018-02-07 23:54:59 +01:00
|
|
|
line and rows is the height of the replica bit loads.
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
|
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
def __init__(self, name, delay_fanout_list, bitcell_loads):
|
2017-08-24 00:02:15 +02:00
|
|
|
design.design.__init__(self, name)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 23:54:59 +01:00
|
|
|
self.bitcell_loads = bitcell_loads
|
2018-11-17 01:57:22 +01:00
|
|
|
self.delay_fanout_list = delay_fanout_list
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
self.create_netlist()
|
|
|
|
|
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-11-14 01:05:22 +01:00
|
|
|
self.create_instances()
|
2018-08-28 01:42:48 +02:00
|
|
|
|
|
|
|
|
def create_layout(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
self.calculate_module_offsets()
|
2018-11-14 01:05:22 +01:00
|
|
|
self.place_instances()
|
2016-11-08 18:57:35 +01:00
|
|
|
self.route()
|
2018-08-28 19:24:09 +02:00
|
|
|
self.add_layout_pins()
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
self.offset_all_coordinates()
|
2018-03-15 01:30:41 +01:00
|
|
|
|
2018-03-14 18:53:20 +01:00
|
|
|
#self.add_lvs_correspondence_points()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-14 19:09:41 +02:00
|
|
|
# Extra pitch on top and right
|
2019-04-01 23:23:47 +02:00
|
|
|
self.width = self.replica_column_inst.rx() - self.delay_chain_inst.lx() + self.m2_pitch
|
|
|
|
|
self.height = max(self.replica_column_inst.uy(), self.delay_chain_inst.uy()) + self.m3_pitch
|
2018-03-15 01:30:41 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.DRC_LVS()
|
|
|
|
|
|
2018-08-28 19:24:09 +02:00
|
|
|
def add_pins(self):
|
|
|
|
|
for pin in ["en", "out", "vdd", "gnd"]:
|
|
|
|
|
self.add_pin(pin)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def calculate_module_offsets(self):
|
|
|
|
|
""" Calculate all the module offsets """
|
|
|
|
|
|
|
|
|
|
# These aren't for instantiating, but we use them to get the dimensions
|
2017-12-12 23:53:19 +01:00
|
|
|
self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-03-14 18:53:20 +01:00
|
|
|
# Quadrant 1: Replica bitline and such are not rotated, but they must be placed far enough
|
2017-08-24 00:02:15 +02:00
|
|
|
# away from the delay chain/inverter with space for three M2 tracks
|
2018-03-14 18:53:20 +01:00
|
|
|
self.bitcell_offset = vector(0,self.replica_bitcell.height)
|
|
|
|
|
self.rbl_offset = self.bitcell_offset
|
2018-07-16 21:58:15 +02:00
|
|
|
|
|
|
|
|
# Gap between the delay chain and RBL
|
|
|
|
|
gap_width = 2*self.m2_pitch
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
# Quadrant 4: with some space below it and tracks on the right for vdd/gnd
|
2018-07-16 21:58:15 +02:00
|
|
|
self.delay_chain_offset = vector(-self.delay_chain.width-gap_width,self.replica_bitcell.height)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
# Will be flipped vertically below the delay chain
|
2018-07-19 20:02:13 +02:00
|
|
|
# Align it with the inverters in the delay chain to simplify supply connections
|
|
|
|
|
self.rbl_inv_offset = self.delay_chain_offset + vector(2*self.inv.width, 0)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-03-14 18:53:20 +01:00
|
|
|
# Placed next to the replica bitcell
|
2018-07-16 21:58:15 +02:00
|
|
|
self.access_tx_offset = vector(-gap_width-self.access_tx.width-self.inv.width, 0.5*self.inv.height)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def add_modules(self):
|
|
|
|
|
""" Add the modules for later usage """
|
2018-08-28 19:24:09 +02:00
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
self.replica_bitcell = factory.create(module_type="replica_bitcell")
|
|
|
|
|
self.add_mod(self.replica_bitcell)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
# This is the replica bitline load column that is the height of our array
|
2019-01-17 01:15:38 +01:00
|
|
|
self.rbl = factory.create(module_type="bitcell_array",
|
|
|
|
|
cols=1,
|
|
|
|
|
rows=self.bitcell_loads)
|
2017-08-24 00:02:15 +02:00
|
|
|
self.add_mod(self.rbl)
|
2018-02-07 23:54:59 +01:00
|
|
|
|
|
|
|
|
# FIXME: The FO and depth of this should be tuned
|
2019-01-17 01:15:38 +01:00
|
|
|
self.delay_chain = factory.create(module_type="delay_chain",
|
|
|
|
|
fanout_list=self.delay_fanout_list)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_mod(self.delay_chain)
|
|
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
self.inv = factory.create(module_type="pinv")
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_mod(self.inv)
|
|
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
self.access_tx = factory.create(module_type="ptx",
|
|
|
|
|
tx_type="pmos")
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_mod(self.access_tx)
|
|
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
def create_instances(self):
|
2018-08-28 01:42:48 +02:00
|
|
|
""" Create all of the module instances in the logical netlist """
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# This is the threshold detect inverter on the output of the RBL
|
|
|
|
|
self.rbl_inv_inst=self.add_inst(name="rbl_inv",
|
2018-08-28 01:42:48 +02:00
|
|
|
mod=self.inv)
|
2018-10-11 18:53:08 +02:00
|
|
|
self.connect_inst(["bl0_0", "out", "vdd", "gnd"])
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
self.tx_inst=self.add_inst(name="rbl_access_tx",
|
2018-08-28 01:42:48 +02:00
|
|
|
mod=self.access_tx)
|
2016-11-08 18:57:35 +01:00
|
|
|
# D, G, S, B
|
2018-10-11 18:53:08 +02:00
|
|
|
self.connect_inst(["vdd", "delayed_en", "bl0_0", "vdd"])
|
2017-08-24 00:02:15 +02:00
|
|
|
# add the well and poly contact
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
self.delay_chain_inst=self.add_inst(name="delay_chain",
|
|
|
|
|
mod=self.delay_chain)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.connect_inst(["en", "delayed_en", "vdd", "gnd"])
|
|
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
self.replica_cell_inst=self.add_inst(name="bitcell",
|
|
|
|
|
mod=self.replica_bitcell)
|
2018-09-21 00:21:22 +02:00
|
|
|
temp = []
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-10-11 18:53:08 +02:00
|
|
|
temp.append("bl{}_0".format(port))
|
|
|
|
|
temp.append("br{}_0".format(port))
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-21 00:21:22 +02:00
|
|
|
temp.append("delayed_en")
|
|
|
|
|
temp.append("vdd")
|
|
|
|
|
temp.append("gnd")
|
|
|
|
|
self.connect_inst(temp)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
self.replica_column_inst=self.add_inst(name="load",
|
|
|
|
|
mod=self.rbl)
|
2018-09-09 23:00:51 +02:00
|
|
|
|
|
|
|
|
temp = []
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-10-11 18:53:08 +02:00
|
|
|
temp.append("bl{}_0".format(port))
|
|
|
|
|
temp.append("br{}_0".format(port))
|
2018-09-09 23:00:51 +02:00
|
|
|
for wl in range(self.bitcell_loads):
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-09 23:00:51 +02:00
|
|
|
temp.append("gnd")
|
|
|
|
|
temp.append("vdd")
|
|
|
|
|
temp.append("gnd")
|
|
|
|
|
self.connect_inst(temp)
|
2018-09-10 07:42:52 +02:00
|
|
|
|
|
|
|
|
self.wl_list = self.rbl.cell.list_all_wl_names()
|
2018-11-09 02:02:20 +01:00
|
|
|
self.bl_list = self.rbl.cell.list_all_bl_names()
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
def place_instances(self):
|
2018-08-28 01:42:48 +02:00
|
|
|
""" Add all of the module instances in the logical netlist """
|
|
|
|
|
|
|
|
|
|
# This is the threshold detect inverter on the output of the RBL
|
2018-08-28 02:25:39 +02:00
|
|
|
self.rbl_inv_inst.place(offset=self.rbl_inv_offset,
|
|
|
|
|
rotate=180)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2018-08-28 02:25:39 +02:00
|
|
|
self.tx_inst.place(self.access_tx_offset)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
self.delay_chain_inst.place(self.delay_chain_offset)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
self.replica_cell_inst.place(offset=self.bitcell_offset,
|
2018-08-28 02:25:39 +02:00
|
|
|
mirror="MX")
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
self.replica_column_inst.place(self.rbl_offset)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def route(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" Connect all the signals together """
|
2018-07-16 19:49:43 +02:00
|
|
|
self.route_supplies()
|
|
|
|
|
self.route_wl()
|
2017-08-24 00:02:15 +02:00
|
|
|
self.route_access_tx()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-07-16 19:49:43 +02:00
|
|
|
def route_wl(self):
|
|
|
|
|
""" Connect the RBL word lines to gnd """
|
|
|
|
|
# Connect the WL and gnd pins directly to the center and right gnd rails
|
|
|
|
|
for row in range(self.bitcell_loads):
|
2018-10-11 18:53:08 +02:00
|
|
|
wl = self.wl_list[0]+"_{}".format(row)
|
2019-04-01 23:23:47 +02:00
|
|
|
pin = self.replica_column_inst.get_pin(wl)
|
2018-07-19 22:38:45 +02:00
|
|
|
|
2018-09-13 10:42:06 +02:00
|
|
|
# Route the connection to the right so that it doesn't interfere with the cells
|
2018-10-18 16:05:47 +02:00
|
|
|
# Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions
|
2018-07-19 22:38:45 +02:00
|
|
|
pin_right = pin.rc()
|
2018-10-18 16:05:47 +02:00
|
|
|
pin_extension = pin_right + vector(self.m3_pitch,0)
|
|
|
|
|
|
2018-07-16 19:49:43 +02:00
|
|
|
if pin.layer != "metal1":
|
|
|
|
|
continue
|
2018-11-03 01:16:41 +01:00
|
|
|
pin_width_ydir = pin.uy()-pin.by()
|
|
|
|
|
#Width is set to pin y width to avoid DRC issues with m1 gaps
|
|
|
|
|
self.add_path("metal1", [pin_right, pin_extension], pin_width_ydir)
|
2018-10-18 16:05:47 +02:00
|
|
|
self.add_power_pin("gnd", pin_extension)
|
2018-09-26 05:00:25 +02:00
|
|
|
|
2018-11-01 20:29:49 +01:00
|
|
|
# for multiport, need to short wordlines to each other so they all connect to gnd.
|
2018-11-08 21:19:40 +01:00
|
|
|
wl_last = self.wl_list[-1]+"_{}".format(row)
|
2019-04-01 23:23:47 +02:00
|
|
|
pin_last = self.replica_column_inst.get_pin(wl_last)
|
2018-11-03 01:16:41 +01:00
|
|
|
self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0))
|
2018-11-01 20:29:49 +01:00
|
|
|
|
2018-11-03 01:16:41 +01:00
|
|
|
def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None):
|
2018-11-01 20:29:49 +01:00
|
|
|
"""Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins."""
|
|
|
|
|
#Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord.
|
|
|
|
|
#This is my (Hunter) first time editing layout in openram so this function is likely not optimal.
|
2018-11-08 21:19:40 +01:00
|
|
|
if len(self.all_ports) > 1:
|
2018-11-01 20:29:49 +01:00
|
|
|
#1. Create vertical metal for all the bitlines to connect to
|
|
|
|
|
#m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped
|
|
|
|
|
correct_y = vector(0, 0.5*drc("minwidth_metal1"))
|
|
|
|
|
#x spacing depends on the side being drawn. Unknown to me (Hunter) why the size of the space differs by the side.
|
|
|
|
|
#I assume this is related to how a wire is draw, but I have not investigated the issue.
|
|
|
|
|
if pin_side == "right":
|
|
|
|
|
correct_x = vector(0.5*drc("minwidth_metal1"), 0)
|
2018-11-03 01:16:41 +01:00
|
|
|
if offset_x_vec != None:
|
|
|
|
|
correct_x = offset_x_vec
|
|
|
|
|
else:
|
|
|
|
|
correct_x = vector(1.5*drc("minwidth_metal1"), 0)
|
|
|
|
|
|
2018-11-01 20:29:49 +01:00
|
|
|
if wl_pin_a.uy() > wl_pin_b.uy():
|
|
|
|
|
self.add_path("metal1", [wl_pin_a.rc()+correct_x+correct_y, wl_pin_b.rc()+correct_x-correct_y])
|
|
|
|
|
else:
|
|
|
|
|
self.add_path("metal1", [wl_pin_a.rc()+correct_x-correct_y, wl_pin_b.rc()+correct_x+correct_y])
|
|
|
|
|
elif pin_side == "left":
|
2018-11-03 01:16:41 +01:00
|
|
|
if offset_x_vec != None:
|
|
|
|
|
correct_x = offset_x_vec
|
|
|
|
|
else:
|
|
|
|
|
correct_x = vector(1.5*drc("minwidth_metal1"), 0)
|
|
|
|
|
|
2018-11-01 20:29:49 +01:00
|
|
|
if wl_pin_a.uy() > wl_pin_b.uy():
|
|
|
|
|
self.add_path("metal1", [wl_pin_a.lc()-correct_x+correct_y, wl_pin_b.lc()-correct_x-correct_y])
|
|
|
|
|
else:
|
|
|
|
|
self.add_path("metal1", [wl_pin_a.lc()-correct_x-correct_y, wl_pin_b.lc()-correct_x+correct_y])
|
|
|
|
|
else:
|
|
|
|
|
debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1)
|
2018-09-26 05:00:25 +02:00
|
|
|
|
2018-11-01 20:29:49 +01:00
|
|
|
#2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this.
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-11-03 01:16:41 +01:00
|
|
|
if is_replica_cell:
|
2018-11-01 20:29:49 +01:00
|
|
|
wl = self.wl_list[port]
|
2019-04-01 23:23:47 +02:00
|
|
|
pin = self.replica_cell_inst.get_pin(wl)
|
2018-11-03 01:16:41 +01:00
|
|
|
else:
|
|
|
|
|
wl = self.wl_list[port]+"_{}".format(cell_row)
|
2019-04-01 23:23:47 +02:00
|
|
|
pin = self.replica_column_inst.get_pin(wl)
|
2018-11-03 01:16:41 +01:00
|
|
|
|
|
|
|
|
if pin_side == "left":
|
2018-11-01 20:29:49 +01:00
|
|
|
self.add_path("metal1", [pin.lc()-correct_x, pin.lc()])
|
2018-11-03 01:16:41 +01:00
|
|
|
elif pin_side == "right":
|
|
|
|
|
self.add_path("metal1", [pin.rc()+correct_x, pin.rc()])
|
|
|
|
|
|
|
|
|
|
|
2018-11-01 20:29:49 +01:00
|
|
|
|
2018-07-16 19:49:43 +02:00
|
|
|
def route_supplies(self):
|
2018-05-11 18:15:29 +02:00
|
|
|
""" Propagate all vdd/gnd pins up to this level for all modules """
|
|
|
|
|
|
|
|
|
|
# These are the instances that every bank has
|
2019-04-01 23:23:47 +02:00
|
|
|
top_instances = [self.replica_column_inst,
|
|
|
|
|
self.delay_chain_inst]
|
2018-05-11 18:15:29 +02:00
|
|
|
for inst in top_instances:
|
|
|
|
|
self.copy_layout_pin(inst, "vdd")
|
|
|
|
|
self.copy_layout_pin(inst, "gnd")
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-07-16 19:49:43 +02:00
|
|
|
# Route the inverter supply pin from M1
|
|
|
|
|
# Only vdd is needed because gnd shares a rail with the delay chain
|
|
|
|
|
pin = self.rbl_inv_inst.get_pin("vdd")
|
|
|
|
|
self.add_power_pin("vdd", pin.lc())
|
|
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
for pin in self.replica_cell_inst.get_pins("vdd"):
|
|
|
|
|
self.add_power_pin(name="vdd", loc=pin.center(), vertical=True, start_layer=pin.layer)
|
2018-07-19 23:01:48 +02:00
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
for pin in self.replica_cell_inst.get_pins("gnd"):
|
|
|
|
|
self.add_power_pin("gnd", pin.center(), vertical=True, start_layer=pin.layer)
|
2018-07-16 19:49:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def route_access_tx(self):
|
|
|
|
|
# GATE ROUTE
|
|
|
|
|
# 1. Add the poly contact and nwell enclosure
|
|
|
|
|
# Determines the y-coordinate of where to place the gate input poly pin
|
|
|
|
|
# (middle in between the pmos and nmos)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
poly_pin = self.tx_inst.get_pin("G")
|
2018-03-14 18:53:20 +01:00
|
|
|
poly_offset = poly_pin.uc()
|
|
|
|
|
# This centers the contact above the poly by one pitch
|
|
|
|
|
contact_offset = poly_offset + vector(0,self.m2_pitch)
|
2019-04-01 23:23:47 +02:00
|
|
|
self.add_via_center(layers=("poly", "contact", "metal1"),
|
|
|
|
|
offset=contact_offset)
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=contact_offset)
|
2018-03-14 18:53:20 +01:00
|
|
|
self.add_segment_center(layer="poly",
|
|
|
|
|
start=poly_offset,
|
|
|
|
|
end=contact_offset)
|
2017-08-24 00:02:15 +02:00
|
|
|
nwell_offset = self.rbl_inv_offset + vector(-self.inv.height,self.inv.width)
|
2018-03-14 18:53:20 +01:00
|
|
|
# self.add_rect(layer="nwell",
|
|
|
|
|
# offset=nwell_offset,
|
|
|
|
|
# width=0.5*self.inv.height,
|
|
|
|
|
# height=self.delay_chain_offset.y-nwell_offset.y)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
# 2. Route delay chain output to access tx gate
|
2019-04-01 23:23:47 +02:00
|
|
|
delay_en_offset = self.delay_chain_inst.get_pin("out").bc()
|
2018-03-14 18:53:20 +01:00
|
|
|
self.add_path("metal2", [delay_en_offset,contact_offset])
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-03-14 18:53:20 +01:00
|
|
|
# 3. Route the contact of previous route to the bitcell WL
|
2017-08-24 00:02:15 +02:00
|
|
|
# route bend of previous net to bitcell WL
|
2019-04-01 23:23:47 +02:00
|
|
|
wl_offset = self.replica_cell_inst.get_pin(self.wl_list[0]).lc()
|
2018-10-12 23:37:51 +02:00
|
|
|
wl_mid1 = wl_offset - vector(1.5*drc("minwidth_metal1"), 0)
|
2018-09-13 10:42:06 +02:00
|
|
|
wl_mid2 = vector(wl_mid1.x, contact_offset.y)
|
|
|
|
|
#xmid_point= 0.5*(wl_offset.x+contact_offset.x)
|
|
|
|
|
#wl_mid1 = vector(xmid_point,contact_offset.y)
|
|
|
|
|
#wl_mid2 = vector(xmid_point,wl_offset.y)
|
|
|
|
|
self.add_path("metal1", [wl_offset, wl_mid1, wl_mid2, contact_offset])
|
2018-09-26 05:00:25 +02:00
|
|
|
|
|
|
|
|
# 4. Short wodlines if multiport
|
|
|
|
|
wl = self.wl_list[0]
|
2018-11-08 21:19:40 +01:00
|
|
|
wl_last = self.wl_list[-1]
|
2019-04-01 23:23:47 +02:00
|
|
|
pin = self.replica_cell_inst.get_pin(wl)
|
|
|
|
|
pin_last = self.replica_cell_inst.get_pin(wl_last)
|
2018-11-01 20:29:49 +01:00
|
|
|
x_offset = self.short_wordlines(pin, pin_last, "left", True)
|
2018-09-26 05:00:25 +02:00
|
|
|
|
2018-11-01 20:29:49 +01:00
|
|
|
#correct = vector(0.5*drc("minwidth_metal1"), 0)
|
|
|
|
|
#self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct])
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
# DRAIN ROUTE
|
|
|
|
|
# Route the drain to the vdd rail
|
2018-03-14 18:53:20 +01:00
|
|
|
drain_offset = self.tx_inst.get_pin("D").center()
|
2019-04-01 23:23:47 +02:00
|
|
|
self.add_power_pin("vdd", drain_offset, vertical=True)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
# SOURCE ROUTE
|
2018-03-14 18:53:20 +01:00
|
|
|
# Route the drain to the RBL inverter input
|
|
|
|
|
source_offset = self.tx_inst.get_pin("S").center()
|
|
|
|
|
inv_A_offset = self.rbl_inv_inst.get_pin("A").center()
|
|
|
|
|
self.add_path("metal1",[source_offset, inv_A_offset])
|
|
|
|
|
|
|
|
|
|
# Route the connection of the source route to the RBL bitline (left)
|
2017-08-24 00:02:15 +02:00
|
|
|
# Via will go halfway down from the bitcell
|
2019-04-01 23:23:47 +02:00
|
|
|
bl_offset = self.replica_cell_inst.get_pin(self.bl_list[0]).bc()
|
2018-07-16 19:49:43 +02:00
|
|
|
# Route down a pitch so we can use M2 routing
|
|
|
|
|
bl_down_offset = bl_offset - vector(0, self.m2_pitch)
|
|
|
|
|
self.add_path("metal2",[source_offset, bl_down_offset, bl_offset])
|
2018-03-14 18:53:20 +01:00
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=source_offset)
|
|
|
|
|
|
|
|
|
|
# BODY ROUTE
|
|
|
|
|
# Connect it to the inverter well
|
|
|
|
|
nwell_offset = self.rbl_inv_inst.lr()
|
|
|
|
|
ur_offset = self.tx_inst.ur()
|
|
|
|
|
self.add_rect(layer="nwell",
|
|
|
|
|
offset=nwell_offset,
|
|
|
|
|
width=ur_offset.x-nwell_offset.x,
|
|
|
|
|
height=ur_offset.y-nwell_offset.y)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
def route_vdd(self):
|
2018-03-14 18:53:20 +01:00
|
|
|
""" Route all signals connected to vdd """
|
2018-07-16 19:49:43 +02:00
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
self.copy_layout_pin(self.delay_chain_inst,"vdd")
|
|
|
|
|
self.copy_layout_pin(self.replica_cell_inst,"vdd")
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
# Connect the WL and vdd pins directly to the center and right vdd rails
|
|
|
|
|
# Connect RBL vdd pins to center and right rails
|
2019-04-01 23:23:47 +02:00
|
|
|
rbl_vdd_pins = self.replica_column_inst.get_pins("vdd")
|
2018-03-14 18:53:20 +01:00
|
|
|
for pin in rbl_vdd_pins:
|
|
|
|
|
if pin.layer != "metal1":
|
|
|
|
|
continue
|
2018-03-15 01:30:41 +01:00
|
|
|
start = vector(self.center_vdd_pin.cx(),pin.cy())
|
|
|
|
|
end = vector(self.right_vdd_pin.cx(),pin.cy())
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text="vdd",
|
|
|
|
|
layer="metal1",
|
|
|
|
|
start=start,
|
|
|
|
|
end=end)
|
2018-03-14 18:53:20 +01:00
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=start)
|
2018-03-14 18:53:20 +01:00
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=end)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
# Add via for the inverter
|
|
|
|
|
pin = self.rbl_inv_inst.get_pin("vdd")
|
2018-03-15 01:30:41 +01:00
|
|
|
start = vector(self.left_vdd_pin.cx(),pin.cy())
|
|
|
|
|
end = vector(self.center_vdd_pin.cx(),pin.cy())
|
2018-03-14 18:53:20 +01:00
|
|
|
self.add_segment_center(layer="metal1",
|
|
|
|
|
start=start,
|
|
|
|
|
end=end)
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=start)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=end)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# Add via for the RBC
|
2019-04-01 23:23:47 +02:00
|
|
|
pin = self.replica_cell_inst.get_pin("vdd")
|
2018-03-14 18:53:20 +01:00
|
|
|
start = pin.lc()
|
2018-03-15 01:30:41 +01:00
|
|
|
end = vector(self.right_vdd_pin.cx(),pin.cy())
|
2018-03-14 18:53:20 +01:00
|
|
|
self.add_segment_center(layer="metal1",
|
|
|
|
|
start=start,
|
|
|
|
|
end=end)
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=end)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
2018-03-15 01:30:41 +01:00
|
|
|
# Create the RBL rails too
|
2019-04-01 23:23:47 +02:00
|
|
|
rbl_pins = self.replica_column_inst.get_pins("vdd")
|
2018-03-14 18:53:20 +01:00
|
|
|
for pin in rbl_pins:
|
2018-03-21 21:20:48 +01:00
|
|
|
if pin.layer != "metal1":
|
2018-03-14 18:53:20 +01:00
|
|
|
continue
|
2018-03-21 21:20:48 +01:00
|
|
|
# If above the delay line, route the full width
|
|
|
|
|
left = vector(self.left_vdd_pin.cx(),pin.cy())
|
|
|
|
|
center = vector(self.center_vdd_pin.cx(),pin.cy())
|
2019-04-01 23:23:47 +02:00
|
|
|
if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch:
|
2018-03-21 21:20:48 +01:00
|
|
|
start = left
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=left)
|
2018-03-21 21:20:48 +01:00
|
|
|
else:
|
|
|
|
|
start = center
|
|
|
|
|
end = vector(self.right_vdd_pin.cx()+0.5*self.m1_width,pin.cy())
|
|
|
|
|
self.add_layout_pin_segment_center(text="vdd",
|
|
|
|
|
layer="metal1",
|
2018-03-15 01:30:41 +01:00
|
|
|
start=start,
|
|
|
|
|
end=end)
|
2018-03-21 21:20:48 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def route_gnd(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" Route all signals connected to gnd """
|
2018-02-07 23:15:13 +01:00
|
|
|
|
2018-03-14 18:53:20 +01:00
|
|
|
# Route the gnd lines from left to right
|
|
|
|
|
|
|
|
|
|
# Add via for the delay chain
|
2019-04-01 23:23:47 +02:00
|
|
|
left_gnd_start = self.delay_chain_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0)
|
|
|
|
|
left_gnd_end = vector(left_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch)
|
2018-03-21 21:20:48 +01:00
|
|
|
self.left_gnd_pin=self.add_segment_center(layer="metal2",
|
|
|
|
|
start=left_gnd_start,
|
|
|
|
|
end=left_gnd_end)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
# Gnd line to the left of the replica bitline
|
2019-04-01 23:23:47 +02:00
|
|
|
center_gnd_start = self.replica_cell_inst.ll().scale(1,0) - vector(2*self.m2_pitch,0)
|
|
|
|
|
center_gnd_end = vector(center_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch)
|
2018-03-21 21:20:48 +01:00
|
|
|
self.center_gnd_pin=self.add_segment_center(layer="metal2",
|
|
|
|
|
start=center_gnd_start,
|
|
|
|
|
end=center_gnd_end)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
# Gnd line to the right of the replica bitline
|
2019-04-01 23:23:47 +02:00
|
|
|
right_gnd_start = self.replica_cell_inst.lr().scale(1,0) + vector(self.m2_pitch,0)
|
|
|
|
|
right_gnd_end = vector(right_gnd_start.x, self.replica_column_inst.uy()+self.m2_pitch)
|
2018-03-21 21:20:48 +01:00
|
|
|
self.right_gnd_pin=self.add_segment_center(layer="metal2",
|
|
|
|
|
start=right_gnd_start,
|
|
|
|
|
end=right_gnd_end)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# Connect the WL and gnd pins directly to the center and right gnd rails
|
2018-02-07 23:54:59 +01:00
|
|
|
for row in range(self.bitcell_loads):
|
2018-10-11 18:53:08 +02:00
|
|
|
wl = self.wl_list[0]+"_{}".format(row)
|
2019-04-01 23:23:47 +02:00
|
|
|
pin = self.replica_column_inst.get_pin(wl)
|
2018-03-14 18:53:20 +01:00
|
|
|
if pin.layer != "metal1":
|
|
|
|
|
continue
|
2018-03-21 21:20:48 +01:00
|
|
|
# If above the delay line, route the full width
|
|
|
|
|
left = vector(self.left_gnd_pin.cx(),pin.cy())
|
|
|
|
|
center = vector(self.center_gnd_pin.cx(),pin.cy())
|
2019-04-01 23:23:47 +02:00
|
|
|
if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch:
|
2018-03-21 21:20:48 +01:00
|
|
|
start = left
|
|
|
|
|
else:
|
|
|
|
|
start = center
|
|
|
|
|
end = vector(self.right_gnd_pin.cx(),pin.cy())
|
|
|
|
|
self.add_layout_pin_segment_center(text="gnd",
|
|
|
|
|
layer="metal1",
|
|
|
|
|
start=start,
|
|
|
|
|
end=end)
|
|
|
|
|
if start == left:
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=left)
|
2018-03-14 18:53:20 +01:00
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=center)
|
2018-03-14 18:53:20 +01:00
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=end)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# rbl_gnd_pins = self.replica_column_inst.get_pins("gnd")
|
|
|
|
|
# # Add L shapes to each vertical gnd rail
|
|
|
|
|
# for pin in rbl_gnd_pins:
|
|
|
|
|
# if pin.layer != "metal1":
|
|
|
|
|
# continue
|
|
|
|
|
# # If above the delay line, route the full width
|
|
|
|
|
# left = vector(self.left_gnd_pin.cx(),pin.cy())
|
|
|
|
|
# center = vector(self.center_gnd_pin.cx(),pin.cy())
|
|
|
|
|
# if pin.cy() > self.delay_chain_inst.uy() + self.m1_pitch:
|
|
|
|
|
# start = left
|
|
|
|
|
# else:
|
|
|
|
|
# start = center
|
|
|
|
|
# end = vector(self.right_gnd_pin.cx(),pin.cy())
|
|
|
|
|
# self.add_segment_center(layer="metal1",
|
|
|
|
|
# start=start,
|
|
|
|
|
# end=end)
|
|
|
|
|
# if start == left:
|
|
|
|
|
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
# offset=left)
|
|
|
|
|
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
# offset=center)
|
|
|
|
|
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
# offset=end)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Connect the gnd pins of the delay chain to the left rails
|
2019-04-01 23:23:47 +02:00
|
|
|
dc_gnd_pins = self.delay_chain_inst.get_pins("gnd")
|
2018-03-14 18:53:20 +01:00
|
|
|
for pin in dc_gnd_pins:
|
|
|
|
|
if pin.layer != "metal1":
|
|
|
|
|
continue
|
2018-03-15 01:30:41 +01:00
|
|
|
start = vector(self.left_gnd_pin.cx(),pin.cy())
|
2018-03-14 18:53:20 +01:00
|
|
|
# Note, we don't connect to the center rails because of
|
|
|
|
|
# via conflicts with the RBL
|
2018-03-15 01:30:41 +01:00
|
|
|
#end = vector(self.center_gnd_pin.cx(),pin.cy())
|
2018-03-14 18:53:20 +01:00
|
|
|
end = pin.rc()
|
|
|
|
|
self.add_segment_center(layer="metal1",
|
|
|
|
|
start=start,
|
|
|
|
|
end=end)
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=start)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
# offset=end)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-03-14 18:53:20 +01:00
|
|
|
# Add via for the inverter
|
|
|
|
|
# pin = self.rbl_inv_inst.get_pin("gnd")
|
2018-03-15 01:30:41 +01:00
|
|
|
# start = vector(self.left_gnd_pin.cx(),pin.cy())
|
|
|
|
|
# end = vector(self.center_gnd_pin.cx(),pin.cy())
|
2018-03-14 18:53:20 +01:00
|
|
|
# self.add_segment_center(layer="metal1",
|
|
|
|
|
# start=start,
|
|
|
|
|
# end=end)
|
|
|
|
|
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
# offset=start)
|
2018-03-14 18:53:20 +01:00
|
|
|
# self.add_via_center(layers=("metal1", "via1", "metal2"),
|
2019-04-01 23:23:47 +02:00
|
|
|
# offset=end)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-03-15 01:30:41 +01:00
|
|
|
# Create RBL rails too
|
2019-04-01 23:23:47 +02:00
|
|
|
rbl_pins = self.replica_column_inst.get_pins("gnd")
|
2018-03-14 18:53:20 +01:00
|
|
|
for pin in rbl_pins:
|
|
|
|
|
if pin.layer != "metal2":
|
|
|
|
|
continue
|
2018-03-15 01:30:41 +01:00
|
|
|
start = vector(pin.cx(),self.right_gnd_pin.by())
|
|
|
|
|
end = vector(pin.cx(),self.right_gnd_pin.uy())
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text="gnd",
|
2018-03-15 01:30:41 +01:00
|
|
|
layer="metal2",
|
|
|
|
|
start=start,
|
|
|
|
|
end=end)
|
2018-03-14 18:53:20 +01:00
|
|
|
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
def add_layout_pins(self):
|
|
|
|
|
""" Route the input and output signal """
|
2019-04-01 23:23:47 +02:00
|
|
|
en_offset = self.delay_chain_inst.get_pin("in").bc()
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text="en",
|
2018-03-14 18:53:20 +01:00
|
|
|
layer="metal2",
|
|
|
|
|
start=en_offset,
|
|
|
|
|
end=en_offset.scale(1,0))
|
|
|
|
|
|
|
|
|
|
out_offset = self.rbl_inv_inst.get_pin("Z").center()
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text="out",
|
2018-03-14 18:53:20 +01:00
|
|
|
layer="metal2",
|
|
|
|
|
start=out_offset,
|
|
|
|
|
end=out_offset.scale(1,0))
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=out_offset)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
def add_lvs_correspondence_points(self):
|
|
|
|
|
""" This adds some points for easier debugging if LVS goes wrong.
|
|
|
|
|
These should probably be turned off by default though, since extraction
|
|
|
|
|
will show these as ports in the extracted netlist.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
pin = self.rbl_inv_inst.get_pin("A")
|
|
|
|
|
self.add_label_pin(text="bl[0]",
|
|
|
|
|
layer=pin.layer,
|
|
|
|
|
offset=pin.ll(),
|
|
|
|
|
height=pin.height(),
|
|
|
|
|
width=pin.width())
|
|
|
|
|
|
2019-04-01 23:23:47 +02:00
|
|
|
pin = self.delay_chain_inst.get_pin("out")
|
2017-12-12 23:53:19 +01:00
|
|
|
self.add_label_pin(text="delayed_en",
|
|
|
|
|
layer=pin.layer,
|
|
|
|
|
offset=pin.ll(),
|
|
|
|
|
height=pin.height(),
|
|
|
|
|
width=pin.width())
|
2018-11-09 05:47:34 +01:00
|
|
|
|
|
|
|
|
def get_en_cin(self):
|
|
|
|
|
"""Get the enable input relative capacitance"""
|
|
|
|
|
#The enable is only connected to the delay, get the cin from that module
|
|
|
|
|
en_cin = self.delay_chain.get_cin()
|
|
|
|
|
return en_cin
|
|
|
|
|
|
2018-11-15 08:34:53 +01:00
|
|
|
def determine_sen_stage_efforts(self, ext_cout, inp_is_rise=True):
|
2018-11-09 05:47:34 +01:00
|
|
|
"""Get the stage efforts from the en to s_en. Does not compute the delay for the bitline load."""
|
|
|
|
|
stage_effort_list = []
|
|
|
|
|
#Stage 1 is the delay chain
|
|
|
|
|
stage1_cout = self.get_delayed_en_cin()
|
2018-11-15 08:34:53 +01:00
|
|
|
stage1 = self.delay_chain.determine_delayed_en_stage_efforts(stage1_cout, inp_is_rise)
|
2018-11-09 05:47:34 +01:00
|
|
|
stage_effort_list += stage1
|
|
|
|
|
|
2018-11-15 08:34:53 +01:00
|
|
|
#There is a disconnect between the delay chain and inverter. The rise/fall of the input to the inverter
|
|
|
|
|
#Will be the negation of the previous stage.
|
|
|
|
|
last_stage_is_rise = not stage_effort_list[-1].is_rise
|
|
|
|
|
|
2018-11-09 05:47:34 +01:00
|
|
|
#The delay chain triggers the enable on the replica bitline (rbl). This is used to track the bitline delay whereas this
|
2018-11-15 08:34:53 +01:00
|
|
|
#model is intended to track every but that. Therefore, the next stage is the inverter after the rbl.
|
2019-01-23 21:03:52 +01:00
|
|
|
stage2 = self.inv.get_stage_effort(ext_cout, last_stage_is_rise)
|
2018-11-09 05:47:34 +01:00
|
|
|
stage_effort_list.append(stage2)
|
|
|
|
|
|
|
|
|
|
return stage_effort_list
|
|
|
|
|
|
|
|
|
|
def get_delayed_en_cin(self):
|
2018-12-06 02:10:11 +01:00
|
|
|
"""Get the fanout capacitance (relative) of the delayed enable from the delay chain."""
|
2018-11-09 05:47:34 +01:00
|
|
|
access_tx_cin = self.access_tx.get_cin()
|
|
|
|
|
rbc_cin = self.replica_bitcell.get_wl_cin()
|
2018-12-06 02:10:11 +01:00
|
|
|
return access_tx_cin + rbc_cin
|
2019-01-17 01:15:38 +01:00
|
|
|
|