2016-11-08 18:57:35 +01:00
|
|
|
import design
|
|
|
|
|
import debug
|
2016-11-09 21:20:52 +01:00
|
|
|
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)
|
2017-08-24 00:02:15 +02:00
|
|
|
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"])
|
2017-08-24 00:02:15 +02:00
|
|
|
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"))
|
2017-08-24 00:02:15 +02:00
|
|
|
self.well_contact = contact(layer_stack=("active", "contact", "metal1"))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
self.create_ptx()
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
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()
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def create_ptx(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" 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
|
2017-08-24 00:02:15 +02:00
|
|
|
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"])
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
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
|
2017-08-24 00:02:15 +02:00
|
|
|
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"])
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def connect_poly(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" 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):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" """
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02: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"),
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=offset)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02: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",
|
2017-08-24 00:02:15 +02:00
|
|
|
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"],
|
2017-08-24 00:02:15 +02:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2017-08-24 00:02:15 +02: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)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
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",
|
2017-08-24 00:02:15 +02:00
|
|
|
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,
|
2017-08-24 00:02:15 +02:00
|
|
|
width=BR_out_position.x - offset.x,
|
2016-11-08 18:57:35 +01:00
|
|
|
height=drc["minwidth_metal2"])
|
2017-08-24 00:02:15 +02:00
|
|
|
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):
|
2017-08-24 00:02:15 +02:00
|
|
|
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):
|
2017-08-24 00:02:15 +02:00
|
|
|
# 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"),
|
2017-08-24 00:02:15 +02:00
|
|
|
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"),
|
2017-08-24 00:02:15 +02:00
|
|
|
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,
|
2017-08-24 00:02:15 +02:00
|
|
|
width=implant_width,
|
|
|
|
|
height=implant_height)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02: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,
|
2017-08-24 00:02:15 +02:00
|
|
|
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,
|
2017-08-24 00:02:15 +02:00
|
|
|
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
|
|
|
|