OpenRAM/compiler/pgates/single_level_column_mux.py

213 lines
8.4 KiB
Python
Raw Normal View History

# See LICENSE for licensing information.
#
2019-06-14 17:43:41 +02:00
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import pgate
2016-11-08 18:57:35 +01:00
import debug
from tech import drc
2016-11-08 18:57:35 +01:00
from vector import vector
2019-01-17 01:15:38 +01:00
from sram_factory import factory
import logical_effort
2016-11-08 18:57:35 +01:00
class single_level_column_mux(pgate.pgate):
2016-11-08 18:57:35 +01:00
"""
This module implements the columnmux bitline cell used in the design.
Creates a single columnmux cell with the given integer size relative
to minimum size. Default is 8x. Per Samira and Hodges-Jackson book:
Column-mux transistors driven by the decoder must be sized
for optimal speed
2016-11-08 18:57:35 +01:00
"""
2019-01-17 01:15:38 +01:00
def __init__(self, name, tx_size=8, bitcell_bl="bl", bitcell_br="br"):
debug.info(2, "creating single column mux cell: {0}".format(name))
2016-11-08 18:57:35 +01:00
self.tx_size = int(tx_size)
self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br
pgate.pgate.__init__(self, name)
2016-11-08 18:57:35 +01:00
def create_netlist(self):
self.add_modules()
self.add_pins()
self.add_ptx()
def create_layout(self):
self.pin_height = 2 * self.m2_width
self.width = self.bitcell.width
self.height = self.nmos_upper.uy() + self.pin_height
2016-11-08 18:57:35 +01:00
self.connect_poly()
self.add_bitline_pins()
self.connect_bitlines()
self.add_wells()
def add_modules(self):
2019-01-17 01:15:38 +01:00
self.bitcell = factory.create(module_type="bitcell")
# Adds nmos_lower,nmos_upper to the module
self.ptx_width = self.tx_size * drc("minwidth_tx")
self.nmos = factory.create(module_type="ptx", width=self.ptx_width)
self.add_mod(self.nmos)
def add_pins(self):
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
def add_bitline_pins(self):
""" Add the top and bottom pins to this cell """
bl_pos = vector(self.bitcell.get_pin(self.bitcell_bl).lx(), 0)
br_pos = vector(self.bitcell.get_pin(self.bitcell_br).lx(), 0)
# bl and br
self.add_layout_pin(text="bl",
layer="m2",
offset=bl_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
self.add_layout_pin(text="br",
layer="m2",
offset=br_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
# bl_out and br_out
self.add_layout_pin(text="bl_out",
layer="m2",
offset=bl_pos,
height=self.pin_height)
self.add_layout_pin(text="br_out",
layer="m2",
offset=br_pos,
height=self.pin_height)
def add_ptx(self):
""" Create the two pass gate NMOS transistors to switch the bitlines"""
# Space it in the center
nmos_lower_position = self.nmos.active_offset.scale(0,1) \
+ vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0)
self.nmos_lower = self.add_inst(name="mux_tx1",
mod=self.nmos,
offset=nmos_lower_position)
2016-11-08 18:57:35 +01:00
self.connect_inst(["bl", "sel", "bl_out", "gnd"])
# This aligns it directly above the other tx with gates abutting
nmos_upper_position = nmos_lower_position \
+ vector(0, self.nmos.active_height + self.poly_space)
self.nmos_upper = self.add_inst(name="mux_tx2",
mod=self.nmos,
offset=nmos_upper_position)
2016-11-08 18:57:35 +01:00
self.connect_inst(["br", "sel", "br_out", "gnd"])
def connect_poly(self):
""" Connect the poly gate of the two pass transistors """
height = self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by()
self.add_layout_pin(text="sel",
layer="poly",
offset=self.nmos_lower.get_pin("G").ll(),
height=height)
def connect_bitlines(self):
""" Connect the bitlines to the mux transistors """
# These are on metal2
bl_pin = self.get_pin("bl")
br_pin = self.get_pin("br")
bl_out_pin = self.get_pin("bl_out")
br_out_pin = self.get_pin("br_out")
# These are on metal1
nmos_lower_s_pin = self.nmos_lower.get_pin("S")
nmos_lower_d_pin = self.nmos_lower.get_pin("D")
nmos_upper_s_pin = self.nmos_upper.get_pin("S")
nmos_upper_d_pin = self.nmos_upper.get_pin("D")
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
self.add_via_center(layers=self.m1_stack,
offset=bl_pin.bc(),
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
offset=br_out_pin.uc(),
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
offset=nmos_upper_s_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
offset=nmos_lower_d_pin.center(),
directions=("V", "V"))
# bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2
self.add_path("m1",
[bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()),
nmos_upper_d_pin.center()])
# halfway up, move over
mid1 = bl_out_pin.uc().scale(1, 0.4) \
+ nmos_upper_s_pin.bc().scale(0, 0.4)
mid2 = bl_out_pin.uc().scale(0, 0.4) \
+ nmos_upper_s_pin.bc().scale(1, 0.4)
self.add_path("m2",
[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()])
# br -> nmos_lower/D on metal2
# br_out -> nmos_lower/S on metal1
self.add_path("m1",
[br_out_pin.uc(),
vector(nmos_lower_s_pin.cx(), br_out_pin.uy()),
nmos_lower_s_pin.center()])
# halfway up, move over
mid1 = br_pin.bc().scale(1,0.5) \
+ nmos_lower_d_pin.uc().scale(0,0.5)
mid2 = br_pin.bc().scale(0,0.5) \
+ nmos_lower_d_pin.uc().scale(1,0.5)
self.add_path("m2",
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()])
def add_wells(self):
"""
Add a well and implant over the whole cell. Also, add the
pwell contact (if it exists)
"""
# Add it to the right, aligned in between the two tx
active_pos = vector(self.bitcell.width,
self.nmos_upper.by() - 0.5 * self.poly_space)
2019-12-13 23:56:14 +01:00
self.add_via_center(layers=self.active_stack,
offset=active_pos,
implant_type="p",
well_type="p")
# Add the M1->M2->M3 stack
self.add_via_center(layers=self.m1_stack,
offset=active_pos)
self.add_via_center(layers=self.m2_stack,
offset=active_pos)
self.add_layout_pin_rect_center(text="gnd",
layer="m3",
offset=active_pos)
# Add well enclosure over all the tx and contact
self.add_rect(layer="pwell",
offset=vector(0, 0),
width=self.bitcell.width,
height=self.height)
def get_stage_effort(self, corner, slew, load):
"""
Returns relative delay that the column mux.
Difficult to convert to LE model.
"""
parasitic_delay = 1
# This is not CMOS, so using this may be incorrect.
cin = 2 * self.tx_size
return logical_effort.logical_effort("column_mux",
self.tx_size,
cin,
load,
parasitic_delay,
False)