OpenRAM/compiler/single_level_column_mux.py

204 lines
9.1 KiB
Python
Raw Normal View History

2016-11-08 18:57:35 +01:00
import design
import debug
from tech import drc
2016-11-08 18:57:35 +01:00
from vector import vector
from contact import contact
from ptx import ptx
from globals import OPTS
class single_level_column_mux(design.design):
"""
This module implements the columnmux bitline cell used in the design.
Creates a single columnmux cell.
"""
def __init__(self, name, tx_size):
design.design.__init__(self, name)
debug.info(2, "create single columnmux cell: {0}".format(name))
c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
self.bitcell = self.mod_bitcell()
self.ptx_width = tx_size * drc["minwidth_tx"]
2016-11-08 18:57:35 +01:00
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
self.create_layout()
2016-11-08 18:57:35 +01:00
def create_layout(self):
# This is not instantiated and used for calculations only.
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.well_contact = contact(layer_stack=("active", "contact", "metal1"))
2016-11-08 18:57:35 +01:00
self.create_ptx()
self.width = self.bitcell.width
# The height is bigger than necessary.
self.height = 2*self.nmos.height
2016-11-08 18:57:35 +01:00
self.connect_poly()
self.connect_to_bitlines()
self.add_gnd_rail()
self.add_well_contacts()
2016-11-08 18:57:35 +01:00
def create_ptx(self):
""" Create the two pass gate NMOS transistors to switch the bitlines"""
2016-11-08 18:57:35 +01:00
# Adds nmos1,nmos2 to the module
self.nmos = ptx(width=self.ptx_width)
self.add_mod(self.nmos)
self.nmos1_position = vector(drc["minwidth_metal1"], drc["poly_extend_active"]) \
- vector([drc["well_enclosure_active"]]*2)
self.add_inst(name="mux_tx1",
mod=self.nmos,
2016-11-08 18:57:35 +01:00
offset=self.nmos1_position)
self.connect_inst(["bl", "sel", "bl_out", "gnd"])
nmos2_to_nmos1 = vector(self.nmos.active_width,
self.nmos.active_height + drc["minwidth_poly"] + 2*drc["poly_extend_active"])
2016-11-08 18:57:35 +01:00
self.nmos2_position = self.nmos1_position + nmos2_to_nmos1
self.add_inst(name="mux_tx2",
mod=self.nmos,
2016-11-08 18:57:35 +01:00
offset=self.nmos2_position)
self.connect_inst(["br", "sel", "br_out", "gnd"])
2016-11-08 18:57:35 +01:00
def connect_poly(self):
""" Connect the poly gate of the two pass transistors """
self.poly_offset = self.nmos1_position + self.nmos.poly_positions[0] \
+ vector(0,self.nmos.poly_height)
width=self.nmos2_position.x - self.nmos1_position.x + drc["minwidth_poly"]
self.add_layout_pin(text="col_addr",
layer="poly",
offset=self.poly_offset,
width=width,
height=drc["minwidth_poly"])
2016-11-08 18:57:35 +01:00
def connect_to_bitlines(self):
""" """
2016-11-08 18:57:35 +01:00
# place the contact at the top of the src/drain
offset = self.nmos1_position + vector(self.nmos.active_contact_positions[0].x + 0.5*self.m1m2_via.contact_width
+ 3 * (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width),
self.nmos.active_position.y + self.nmos.active_height - self.m1m2_via.height)
2016-11-08 18:57:35 +01:00
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
2016-11-08 18:57:35 +01:00
bl_offset = vector(self.bitcell.get_pin("BL").lx(),self.height)
self.add_layout_pin(text="bl",
layer="metal2",
offset=bl_offset - vector(0, 2*drc["minwidth_metal2"]),
width=drc["minwidth_metal2"],
height=2*drc["minwidth_metal2"])
# draw an enclosing rectangle for the small jog
start = offset + vector(0.5*self.m1m2_via.width,0.5*self.m1m2_via.height)
end = self.get_pin("bl").bc()
mid1 = vector(start.x,0.5*(start.y+end.y))
mid2 = vector(end.x,mid1.y)
self.add_path("metal2",[start,mid1,mid2,end])
# place the contact at the bottom of the src/drain
offset = self.nmos1_position + self.nmos.active_contact_positions[1]
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
2016-11-08 18:57:35 +01:00
self.add_rect(layer="metal2",
offset=[self.bitcell.get_pin("BL").lx(), offset.y],
width=(offset.x - self.bitcell.get_pin("BL").lx() + 2*drc["minwidth_metal2"]),
height=drc["minwidth_metal2"])
self.add_layout_pin(text="bl_out",
layer="metal2",
offset=[self.bitcell.get_pin("BL").lx(), 0],
2016-11-08 18:57:35 +01:00
width=drc["minwidth_metal2"],
height=drc["minwidth_metal2"] + offset.y)
BL_out_position = vector(self.bitcell.get_pin("BL").lx()- 0.5*self.m1m2_via.width, 0)
2016-11-08 18:57:35 +01:00
# place the contact at the top of the src/drain
offset = self.nmos2_position + vector(self.nmos.active_contact_positions[1].x - 0.5*self.m1m2_via.contact_width
- 2 * (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width),
self.nmos.active_position.y + self.nmos.active_height - self.m1m2_via.height)
2016-11-08 18:57:35 +01:00
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
br_offset = vector(self.bitcell.get_pin("BR").lx(),self.height) - vector(0,2*drc["minwidth_metal2"])
self.add_layout_pin(text="br",
layer="metal2",
offset=br_offset,
width=drc["minwidth_metal2"],
height=2*drc["minwidth_metal2"])
# draw an enclosing rectangle for the small jog
ll = vector(min(offset.x,br_offset.x),min(offset.y,br_offset.y))
ur = vector(max(offset.x+self.m1m2_via.width,br_offset.x+drc["minwidth_metal2"]),
max(offset.y+self.m1m2_via.height,br_offset.y+2*drc["minwidth_metal2"]))
2016-11-08 18:57:35 +01:00
self.add_rect(layer="metal2",
offset=ll,
width=ur.x-ll.x,
height=ur.y-ll.y)
# place the contact in the bottom
offset = self.nmos2_position + self.nmos.active_contact_positions[0]
BR_out_position = vector(self.bitcell.get_pin("BR").lx(), 0)
2016-11-08 18:57:35 +01:00
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
self.add_rect(layer="metal2",
offset=offset,
width=BR_out_position.x - offset.x,
2016-11-08 18:57:35 +01:00
height=drc["minwidth_metal2"])
self.add_layout_pin(text="br_out",
layer="metal2",
offset=[BR_out_position.x, 0],
height=offset.y+ drc["minwidth_metal2"])
2016-11-08 18:57:35 +01:00
def add_gnd_rail(self):
gnd_pins = self.bitcell.get_pins("gnd")
for gnd_pin in gnd_pins:
# only use vertical gnd pins that span the whole cell
if gnd_pin.layer == "metal2" and gnd_pin.height >= self.bitcell.height:
gnd_position = vector(gnd_pin.lx(), 0)
self.add_layout_pin(text="gnd",
layer="metal2",
offset=gnd_position,
height=self.get_pin("bl").uy())
2016-11-08 18:57:35 +01:00
def add_well_contacts(self):
# find right most gnd rail
gnd_pins = self.bitcell.get_pins("gnd")
right_gnd = None
for gnd_pin in gnd_pins:
if right_gnd == None or gnd_pin.lx()>right_gnd.lx():
right_gnd = gnd_pin
# Add to the right (first) gnd rail
m1m2_offset = right_gnd.ll() + vector(-0.5*self.m1m2_via.width,self.nmos.poly_height/2)
2016-11-08 18:57:35 +01:00
self.add_via(layers=("metal1", "via1", "metal2"),
offset=m1m2_offset)
active_offset = right_gnd.ll() + vector(-self.m1m2_via.width,self.nmos.poly_height/2)
2016-11-08 18:57:35 +01:00
self.add_contact(layers=("active", "contact", "metal1"),
offset=active_offset)
offset_implant = active_offset + vector([drc["implant_to_contact"]]*2).scale(-1,-1)
implant_width = 2*drc["implant_to_contact"] + self.well_contact.width
implant_height = 2*drc["implant_to_contact"] + self.well_contact.height
2016-11-08 18:57:35 +01:00
self.add_rect(layer="pimplant",
offset=offset_implant,
width=implant_width,
height=implant_height)
2016-11-08 18:57:35 +01:00
offset_well = self.nmos1_position + vector(self.nmos.width, 0)
2016-11-08 18:57:35 +01:00
self.add_rect(layer="pwell",
offset=offset_well,
width=offset_implant.x + implant_width + drc["well_enclosure_active"] - offset_well.x,
height=self.nmos2_position.y)
2016-11-08 18:57:35 +01:00
self.add_rect(layer="vtg",
offset=offset_well,
width=offset_implant.x + implant_width + drc["well_enclosure_active"] - offset_well.x,
height=self.nmos2_position.y)
2016-11-08 18:57:35 +01:00