OpenRAM/compiler/modules/replica_bitline.py

548 lines
23 KiB
Python
Raw Normal View History

2016-11-08 18:57:35 +01:00
import debug
import design
from tech import drc
2016-11-08 18:57:35 +01:00
from pinv import pinv
import contact
2016-11-08 18:57:35 +01:00
from bitcell_array import bitcell_array
from ptx import ptx
from vector import vector
from globals import OPTS
class replica_bitline(design.design):
"""
Generate a module that simulates the delay of control logic
and bit line charging. Stages is the depth of the delay
line and rows is the height of the replica bit loads.
2016-11-08 18:57:35 +01:00
"""
def __init__(self, delay_stages, delay_fanout, bitcell_loads, name="replica_bitline"):
design.design.__init__(self, name)
2016-11-08 18:57:35 +01:00
g = reload(__import__(OPTS.delay_chain))
self.mod_delay_chain = getattr(g, OPTS.delay_chain)
2016-11-08 18:57:35 +01:00
g = reload(__import__(OPTS.replica_bitcell))
self.mod_replica_bitcell = getattr(g, OPTS.replica_bitcell)
2016-11-08 18:57:35 +01:00
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
2016-11-08 18:57:35 +01:00
for pin in ["en", "out", "vdd", "gnd"]:
self.add_pin(pin)
self.bitcell_loads = bitcell_loads
self.delay_stages = delay_stages
self.delay_fanout = delay_fanout
2016-11-08 18:57:35 +01:00
self.create_modules()
self.calculate_module_offsets()
2016-11-08 18:57:35 +01:00
self.add_modules()
self.route()
self.offset_all_coordinates()
self.add_layout_pins()
#self.add_lvs_correspondence_points()
2016-11-08 18:57:35 +01:00
self.DRC_LVS()
def calculate_module_offsets(self):
""" Calculate all the module offsets """
# These aren't for instantiating, but we use them to get the dimensions
self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height)
# M1/M2 routing pitch is based on contacted pitch
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space,self.m2_space)
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
# Quadrant 1: Replica bitline and such are not rotated, but they must be placed far enough
# away from the delay chain/inverter with space for three M2 tracks
self.bitcell_offset = vector(0,self.replica_bitcell.height)
self.rbl_offset = self.bitcell_offset
# Quadrant 4: with some space below it and tracks on the right for vdd/gnd
self.delay_chain_offset = vector(-self.delay_chain.width-4*self.m2_pitch,self.replica_bitcell.height)
# Will be flipped vertically below the delay chain
self.rbl_inv_offset = self.delay_chain_offset + vector(0.5*self.delay_chain.width, 0)
# Placed next to the replica bitcell
self.access_tx_offset = vector(-4*self.m2_pitch-self.access_tx.width-self.inv.width, 0.5*self.inv.height)
2016-11-08 18:57:35 +01:00
def create_modules(self):
""" Create modules for later instantiation """
self.bitcell = self.replica_bitcell = self.mod_replica_bitcell()
self.add_mod(self.bitcell)
# This is the replica bitline load column that is the height of our array
self.rbl = bitcell_array(name="bitline_load", cols=1, rows=self.bitcell_loads)
self.add_mod(self.rbl)
# FIXME: The FO and depth of this should be tuned
self.delay_chain = self.mod_delay_chain([self.delay_fanout]*self.delay_stages)
2016-11-08 18:57:35 +01:00
self.add_mod(self.delay_chain)
self.inv = pinv()
2016-11-08 18:57:35 +01:00
self.add_mod(self.inv)
self.access_tx = ptx(tx_type="pmos")
2016-11-08 18:57:35 +01:00
self.add_mod(self.access_tx)
def add_modules(self):
""" Add all of the module instances in the logical netlist """
# This is the threshold detect inverter on the output of the RBL
self.rbl_inv_inst=self.add_inst(name="rbl_inv",
mod=self.inv,
offset=self.rbl_inv_offset,
rotate=180)
2016-11-08 18:57:35 +01:00
self.connect_inst(["bl[0]", "out", "vdd", "gnd"])
self.tx_inst=self.add_inst(name="rbl_access_tx",
mod=self.access_tx,
offset=self.access_tx_offset)
2016-11-08 18:57:35 +01:00
# D, G, S, B
self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"])
# add the well and poly contact
2016-11-08 18:57:35 +01:00
self.dc_inst=self.add_inst(name="delay_chain",
mod=self.delay_chain,
offset=self.delay_chain_offset)
2016-11-08 18:57:35 +01:00
self.connect_inst(["en", "delayed_en", "vdd", "gnd"])
self.rbc_inst=self.add_inst(name="bitcell",
mod=self.replica_bitcell,
offset=self.bitcell_offset,
mirror="MX")
2016-11-08 18:57:35 +01:00
self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"])
self.rbl_inst=self.add_inst(name="load",
mod=self.rbl,
offset=self.rbl_offset)
self.connect_inst(["bl[0]", "br[0]"] + ["gnd"]*self.bitcell_loads + ["vdd", "gnd"])
self.height = max(self.rbl_inst.uy(),self.dc_inst.uy()) + self.m2_pitch
self.width = self.rbl_inst.rx() + self.m2_width
2016-11-08 18:57:35 +01:00
def route(self):
""" Connect all the signals together """
2016-11-08 18:57:35 +01:00
self.route_gnd()
self.route_vdd()
self.route_access_tx()
2016-11-08 18:57:35 +01: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
poly_pin = self.tx_inst.get_pin("G")
poly_offset = poly_pin.uc()
# This centers the contact above the poly by one pitch
contact_offset = poly_offset + vector(0,self.m2_pitch)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=contact_offset)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=contact_offset)
self.add_segment_center(layer="poly",
start=poly_offset,
end=contact_offset)
nwell_offset = self.rbl_inv_offset + vector(-self.inv.height,self.inv.width)
# self.add_rect(layer="nwell",
# offset=nwell_offset,
# width=0.5*self.inv.height,
# height=self.delay_chain_offset.y-nwell_offset.y)
# 2. Route delay chain output to access tx gate
delay_en_offset = self.dc_inst.get_pin("out").bc()
self.add_path("metal2", [delay_en_offset,contact_offset])
# 3. Route the contact of previous route to the bitcell WL
# route bend of previous net to bitcell WL
wl_offset = self.rbc_inst.get_pin("WL").lc()
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", [contact_offset, wl_mid1, wl_mid2, wl_offset])
# DRAIN ROUTE
# Route the drain to the vdd rail
drain_offset = self.tx_inst.get_pin("D").center()
self.add_path("metal1", [drain_offset, drain_offset.scale(1,0)])
# SOURCE ROUTE
# 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)
# Via will go halfway down from the bitcell
bl_offset = self.rbc_inst.get_pin("BL").bc()
self.add_path("metal3",[source_offset, bl_offset])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=source_offset)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=source_offset)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=bl_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)
def route_vdd(self):
""" Route all signals connected to vdd """
# Route the vdd lines from left to right
# Add via for the delay chain
left_vdd_start = self.dc_inst.ll().scale(1,0) - vector(2*self.m2_pitch,2*self.m2_pitch)
left_vdd_end = vector(left_vdd_start.x, self.rbl_inst.uy()+2*self.m2_pitch)
left_vdd_pin=self.add_layout_pin_center_segment(text="vdd",
layer="metal2",
start=left_vdd_start,
end=left_vdd_end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left_vdd_start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left_vdd_end,
rotate=90)
# Vdd line to the left of the replica bitline
center_vdd_start = self.rbc_inst.ll() - vector(3*self.m2_pitch,2*self.m2_pitch)
center_vdd_end = vector(center_vdd_start.x, self.rbl_inst.uy()+2*self.m2_pitch)
center_vdd_pin=self.add_layout_pin_center_segment(text="vdd",
layer="metal2",
start=center_vdd_start,
end=center_vdd_end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=center_vdd_start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=center_vdd_end,
rotate=90)
# Vdd line to the right of the replica bitline
right_vdd_start = self.rbc_inst.lr() + vector(2*self.m2_pitch,-2*self.m2_pitch)
right_vdd_end = vector(right_vdd_start.x, self.rbl_inst.uy()+2*self.m2_pitch)
right_vdd_pin=self.add_layout_pin_center_segment(text="vdd",
layer="metal2",
start=right_vdd_start,
end=right_vdd_end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=right_vdd_start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=right_vdd_end,
rotate=90)
# Connect the WL and vdd pins directly to the center and right vdd rails
# Connect RBL vdd pins to center and right rails
rbl_vdd_pins = self.rbl_inst.get_pins("vdd")
for pin in rbl_vdd_pins:
if pin.layer != "metal1":
continue
start = vector(center_vdd_pin.cx(),pin.cy())
end = vector(right_vdd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
# Connect the vdd pins of the delay chain to the left rails
dc_vdd_pins = self.dc_inst.get_pins("vdd")
for pin in dc_vdd_pins:
if pin.layer != "metal1":
continue
start = vector(left_vdd_pin.cx(),pin.cy())
# Note, we don't connect to center because of via conflicts
# with the RBL pins
#end = vector(center_vdd_pin.cx(),pin.cy())
end = pin.rc()
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=end,
#rotate=90)
# Add via for the inverter
pin = self.rbl_inv_inst.get_pin("vdd")
start = vector(left_vdd_pin.cx(),pin.cy())
end = vector(center_vdd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
# Add via for the RBC
pin = self.rbc_inst.get_pin("vdd")
start = pin.lc()
end = vector(right_vdd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
# Connect the RBL rails at the top and bottom
rbl_pins = self.rbl_inst.get_pins("vdd")
for pin in rbl_pins:
if pin.layer != "metal2":
continue
end = vector(pin.cx(),right_vdd_pin.uy())
self.add_segment_center(layer="metal2",
start=pin.uc(),
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
start = vector(pin.cx(),right_vdd_pin.by())
self.add_segment_center(layer="metal2",
start=pin.bc(),
end=start)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
# Connect the rails at the top and bottom
self.add_segment_center(layer="metal1",
start=left_vdd_end,
end=right_vdd_end)
self.add_segment_center(layer="metal1",
start=left_vdd_start,
end=right_vdd_start)
2016-11-08 18:57:35 +01:00
def route_gnd(self):
""" Route all signals connected to gnd """
# Route the gnd lines from left to right
# Add via for the delay chain
left_gnd_start = self.dc_inst.ll().scale(1,0) - vector(self.m2_pitch,self.m2_pitch)
left_gnd_end = vector(left_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch)
left_gnd_pin=self.add_layout_pin_center_segment(text="gnd",
layer="metal2",
start=left_gnd_start,
end=left_gnd_end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left_gnd_start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=left_gnd_end,
rotate=90)
# Gnd line to the left of the replica bitline
center_gnd_start = self.rbc_inst.ll().scale(1,0) - vector(2*self.m2_pitch,self.m2_pitch)
center_gnd_end = vector(center_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch)
center_gnd_pin=self.add_layout_pin_center_segment(text="gnd",
layer="metal2",
start=center_gnd_start,
end=center_gnd_end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=center_gnd_start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=center_gnd_end,
rotate=90)
# Gnd line to the right of the replica bitline
right_gnd_start = self.rbc_inst.lr().scale(1,0) + vector(self.m2_pitch,-self.m2_pitch)
right_gnd_end = vector(right_gnd_start.x, self.rbl_inst.uy()+self.m2_pitch)
right_gnd_pin=self.add_layout_pin_center_segment(text="gnd",
layer="metal2",
start=right_gnd_start,
end=right_gnd_end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=right_gnd_start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=right_gnd_end,
rotate=90)
# Connect the WL and gnd pins directly to the center and right gnd rails
for row in range(self.bitcell_loads):
wl = "wl[{}]".format(row)
pin = self.rbl_inst.get_pin(wl)
if pin.layer != "metal1":
continue
start = vector(center_gnd_pin.cx(),pin.cy())
end = vector(right_gnd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
2016-11-08 18:57:35 +01:00
2017-09-14 00:46:41 +02:00
rbl_gnd_pins = self.rbl_inst.get_pins("gnd")
2018-02-02 21:27:24 +01:00
# Add L shapes to each vertical gnd rail
for pin in rbl_gnd_pins:
if pin.layer != "metal1":
2018-02-02 21:27:24 +01:00
continue
start = vector(center_gnd_pin.cx(),pin.cy())
end = vector(right_gnd_pin.cx(),pin.cy())
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
# Connect the gnd pins of the delay chain to the left rails
dc_gnd_pins = self.dc_inst.get_pins("gnd")
for pin in dc_gnd_pins:
if pin.layer != "metal1":
continue
start = vector(left_gnd_pin.cx(),pin.cy())
# Note, we don't connect to the center rails because of
# via conflicts with the RBL
#end = vector(center_gnd_pin.cx(),pin.cy())
end = pin.rc()
self.add_segment_center(layer="metal1",
start=start,
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=end,
#rotate=90)
# Add via for the inverter
# pin = self.rbl_inv_inst.get_pin("gnd")
# start = vector(left_gnd_pin.cx(),pin.cy())
# end = vector(center_gnd_pin.cx(),pin.cy())
# self.add_segment_center(layer="metal1",
# start=start,
# end=end)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=start,
#rotate=90)
# self.add_via_center(layers=("metal1", "via1", "metal2"),
# offset=end,
#rotate=90)
# Connect the RBL rails at the top and bottom
rbl_pins = self.rbl_inst.get_pins("gnd")
for pin in rbl_pins:
if pin.layer != "metal2":
continue
end = vector(pin.cx(),right_gnd_pin.uy())
self.add_segment_center(layer="metal2",
start=pin.uc(),
end=end)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=end,
rotate=90)
start = vector(pin.cx(),right_gnd_pin.by())
self.add_segment_center(layer="metal2",
start=pin.bc(),
end=start)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=start,
rotate=90)
# Connect the rails at the top and bottom
self.add_segment_center(layer="metal1",
start=left_gnd_end,
end=right_gnd_end)
self.add_segment_center(layer="metal1",
start=left_gnd_start,
end=right_gnd_start)
def add_layout_pins(self):
""" Route the input and output signal """
en_offset = self.dc_inst.get_pin("in").bc()
self.add_layout_pin_center_segment(text="en",
layer="metal2",
start=en_offset,
end=en_offset.scale(1,0))
out_offset = self.rbl_inv_inst.get_pin("Z").center()
self.add_layout_pin_center_segment(text="out",
layer="metal2",
start=out_offset,
end=out_offset.scale(1,0))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=out_offset)
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())
pin = self.dc_inst.get_pin("out")
self.add_label_pin(text="delayed_en",
layer=pin.layer,
offset=pin.ll(),
height=pin.height(),
width=pin.width())