2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2021-01-22 20:23:28 +01:00
|
|
|
# Copyright (c) 2016-2021 Regents of the University of California and The Board
|
2019-06-14 17:43:41 +02:00
|
|
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
|
|
|
# (acting for and on behalf of Oklahoma State University)
|
|
|
|
|
# All rights reserved.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2019-04-26 20:57:29 +02:00
|
|
|
import pgate
|
2016-11-08 18:57:35 +01:00
|
|
|
import debug
|
2020-04-21 05:22:46 +02:00
|
|
|
from tech import drc, layer
|
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
|
2020-10-28 17:54:15 +01:00
|
|
|
from tech import cell_properties as cell_props
|
2020-11-06 20:09:50 +01:00
|
|
|
from globals import OPTS
|
2019-10-06 19:30:16 +02:00
|
|
|
|
2020-06-04 00:47:03 +02:00
|
|
|
|
2020-10-05 17:56:51 +02:00
|
|
|
class column_mux(pgate.pgate):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
|
|
|
|
This module implements the columnmux bitline cell used in the design.
|
2020-10-05 17:56:51 +02:00
|
|
|
Creates a single column mux cell with the given integer size relative
|
2018-11-08 01:03:48 +01:00
|
|
|
to minimum size. Default is 8x. Per Samira and Hodges-Jackson book:
|
2019-10-06 19:30:16 +02:00
|
|
|
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"):
|
2020-04-21 05:22:46 +02:00
|
|
|
|
2019-04-26 20:57:29 +02:00
|
|
|
debug.info(2, "creating single column mux cell: {0}".format(name))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-11-08 00:43:08 +01:00
|
|
|
self.tx_size = int(tx_size)
|
2018-09-10 07:06:29 +02:00
|
|
|
self.bitcell_bl = bitcell_bl
|
|
|
|
|
self.bitcell_br = bitcell_br
|
2020-04-21 05:22:46 +02:00
|
|
|
|
2020-08-06 20:33:26 +02:00
|
|
|
super().__init__(name)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-02-12 14:04:05 +01:00
|
|
|
def get_bl_names(self):
|
|
|
|
|
return "bl"
|
|
|
|
|
|
|
|
|
|
def get_br_names(self):
|
|
|
|
|
return "br"
|
|
|
|
|
|
2018-08-27 23:18:32 +02:00
|
|
|
def create_netlist(self):
|
2018-08-28 19:24:09 +02:00
|
|
|
self.add_pins()
|
2017-12-12 23:53:19 +01:00
|
|
|
self.add_ptx()
|
2020-04-21 05:22:46 +02:00
|
|
|
|
2018-08-27 23:18:32 +02:00
|
|
|
def create_layout(self):
|
2020-04-21 05:22:46 +02:00
|
|
|
|
2020-06-04 00:47:03 +02:00
|
|
|
# If li exists, use li and m1 for the mux, otherwise use m1 and m2
|
|
|
|
|
if "li" in layer:
|
|
|
|
|
self.col_mux_stack = self.li_stack
|
|
|
|
|
else:
|
|
|
|
|
self.col_mux_stack = self.m1_stack
|
|
|
|
|
self.pin_layer = self.bitcell.get_pin(self.bitcell_bl).layer
|
|
|
|
|
self.pin_pitch = getattr(self, "{}_pitch".format(self.pin_layer))
|
|
|
|
|
self.pin_width = getattr(self, "{}_width".format(self.pin_layer))
|
|
|
|
|
self.pin_height = 2 * self.pin_width
|
2020-07-14 00:51:46 +02:00
|
|
|
|
|
|
|
|
self.place_ptx()
|
|
|
|
|
|
2021-05-03 06:49:09 +02:00
|
|
|
cell = factory.create(module_type=OPTS.bitcell)
|
|
|
|
|
if(cell_props.use_strap == True and OPTS.num_ports == 1):
|
|
|
|
|
strap = factory.create(module_type=cell_props.strap_module, version=cell_props.strap_version)
|
|
|
|
|
precharge_width = cell.width + strap.width
|
|
|
|
|
else:
|
|
|
|
|
precharge_width = cell.width
|
|
|
|
|
self.width = precharge_width
|
2018-06-29 20:35:29 +02:00
|
|
|
self.height = self.nmos_upper.uy() + self.pin_height
|
2020-07-13 23:59:31 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.connect_poly()
|
2017-12-12 23:53:19 +01:00
|
|
|
self.add_bitline_pins()
|
|
|
|
|
self.connect_bitlines()
|
2020-05-13 23:46:42 +02:00
|
|
|
self.add_pn_wells()
|
2018-08-28 19:24:09 +02:00
|
|
|
|
2020-07-14 00:51:46 +02:00
|
|
|
def add_ptx(self):
|
2020-11-06 20:09:50 +01:00
|
|
|
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
2018-08-28 19:24:09 +02:00
|
|
|
|
|
|
|
|
# Adds nmos_lower,nmos_upper to the module
|
2019-10-06 19:30:16 +02:00
|
|
|
self.ptx_width = self.tx_size * drc("minwidth_tx")
|
2020-04-21 07:08:29 +02:00
|
|
|
self.nmos = factory.create(module_type="ptx",
|
2020-06-04 00:47:03 +02:00
|
|
|
width=self.ptx_width)
|
2018-08-28 19:24:09 +02:00
|
|
|
self.add_mod(self.nmos)
|
2020-04-21 05:22:46 +02:00
|
|
|
|
2020-07-14 00:51:46 +02:00
|
|
|
# Space it in the center
|
|
|
|
|
self.nmos_lower = self.add_inst(name="mux_tx1",
|
|
|
|
|
mod=self.nmos)
|
|
|
|
|
self.connect_inst(["bl", "sel", "bl_out", "gnd"])
|
|
|
|
|
|
|
|
|
|
# This aligns it directly above the other tx with gates abutting
|
|
|
|
|
self.nmos_upper = self.add_inst(name="mux_tx2",
|
|
|
|
|
mod=self.nmos)
|
|
|
|
|
self.connect_inst(["br", "sel", "br_out", "gnd"])
|
|
|
|
|
|
2018-08-28 19:24:09 +02:00
|
|
|
def add_pins(self):
|
|
|
|
|
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
|
|
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
def add_bitline_pins(self):
|
|
|
|
|
""" Add the top and bottom pins to this cell """
|
|
|
|
|
|
2020-06-04 00:47:03 +02:00
|
|
|
bl_pos = vector(self.pin_pitch, 0)
|
|
|
|
|
br_pos = vector(self.width - self.pin_pitch, 0)
|
2020-05-06 22:02:33 +02:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
# bl and br
|
|
|
|
|
self.add_layout_pin(text="bl",
|
2020-06-04 00:47:03 +02:00
|
|
|
layer=self.pin_layer,
|
2019-10-06 19:30:16 +02:00
|
|
|
offset=bl_pos + vector(0, self.height - self.pin_height),
|
2018-02-03 00:17:21 +01:00
|
|
|
height=self.pin_height)
|
2017-12-12 23:53:19 +01:00
|
|
|
self.add_layout_pin(text="br",
|
2020-06-04 00:47:03 +02:00
|
|
|
layer=self.pin_layer,
|
2019-10-06 19:30:16 +02:00
|
|
|
offset=br_pos + vector(0, self.height - self.pin_height),
|
2018-02-03 00:17:21 +01:00
|
|
|
height=self.pin_height)
|
2020-04-21 05:22:46 +02:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
# bl_out and br_out
|
|
|
|
|
self.add_layout_pin(text="bl_out",
|
2020-06-04 00:47:03 +02:00
|
|
|
layer=self.pin_layer,
|
2017-12-12 23:53:19 +01:00
|
|
|
offset=bl_pos,
|
2018-02-03 00:17:21 +01:00
|
|
|
height=self.pin_height)
|
2017-12-12 23:53:19 +01:00
|
|
|
self.add_layout_pin(text="br_out",
|
2020-06-04 00:47:03 +02:00
|
|
|
layer=self.pin_layer,
|
2017-12-12 23:53:19 +01:00
|
|
|
offset=br_pos,
|
2018-02-03 00:17:21 +01:00
|
|
|
height=self.pin_height)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-07-13 23:59:31 +02:00
|
|
|
def place_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.place(nmos_lower_position)
|
|
|
|
|
|
|
|
|
|
# This aligns it directly above the other tx with gates abutting
|
|
|
|
|
nmos_upper_position = nmos_lower_position \
|
|
|
|
|
+ vector(0, self.nmos.active_height + max(self.active_space, self.poly_space))
|
|
|
|
|
self.nmos_upper.place(nmos_upper_position)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-10-28 17:54:15 +01:00
|
|
|
if cell_props.pgate.add_implants:
|
|
|
|
|
self.extend_implants()
|
2020-11-03 15:29:17 +01: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 """
|
2020-04-21 05:22:46 +02:00
|
|
|
|
2020-04-22 00:38:19 +02:00
|
|
|
# offset is the top of the lower nmos' diffusion
|
|
|
|
|
# height is the distance between the nmos' diffusions, which depends on max(self.active_space,self.poly_space)
|
2020-06-15 19:08:07 +02:00
|
|
|
offset = self.nmos_lower.get_pin("G").ul() - vector(0, self.poly_extend_active)
|
2020-04-22 00:38:19 +02:00
|
|
|
height = self.nmos_upper.get_pin("G").by() + self.poly_extend_active - offset.y
|
2020-04-23 01:22:34 +02:00
|
|
|
self.add_rect(layer="poly",
|
2020-04-22 00:38:19 +02:00
|
|
|
offset=offset,
|
|
|
|
|
height=height)
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2020-04-23 01:22:34 +02:00
|
|
|
# Add the sel pin to the bottom of the mux
|
|
|
|
|
self.add_layout_pin(text="sel",
|
|
|
|
|
layer="poly",
|
|
|
|
|
offset=self.nmos_lower.get_pin("G").ll(),
|
|
|
|
|
height=self.poly_extend_active)
|
|
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
def connect_bitlines(self):
|
|
|
|
|
""" Connect the bitlines to the mux transistors """
|
2020-05-06 22:02:33 +02:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
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")
|
|
|
|
|
|
2018-06-29 20:35:29 +02:00
|
|
|
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")
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2018-06-29 20:35:29 +02:00
|
|
|
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
|
2020-06-04 00:47:03 +02:00
|
|
|
self.add_via_stack_center(from_layer=bl_pin.layer,
|
|
|
|
|
to_layer=self.col_mux_stack[0],
|
|
|
|
|
offset=bl_pin.bc())
|
|
|
|
|
self.add_via_stack_center(from_layer=br_out_pin.layer,
|
|
|
|
|
to_layer=self.col_mux_stack[0],
|
|
|
|
|
offset=br_out_pin.uc())
|
|
|
|
|
self.add_via_stack_center(from_layer=nmos_upper_s_pin.layer,
|
|
|
|
|
to_layer=self.col_mux_stack[2],
|
|
|
|
|
offset=nmos_upper_s_pin.center())
|
|
|
|
|
self.add_via_stack_center(from_layer=nmos_lower_d_pin.layer,
|
|
|
|
|
to_layer=self.col_mux_stack[2],
|
|
|
|
|
offset=nmos_lower_d_pin.center())
|
2020-04-21 07:08:29 +02:00
|
|
|
|
2018-06-29 20:35:29 +02:00
|
|
|
# bl -> nmos_upper/D on metal1
|
|
|
|
|
# bl_out -> nmos_upper/S on metal2
|
2020-04-23 01:22:34 +02:00
|
|
|
self.add_path(self.col_mux_stack[0],
|
2019-10-06 19:30:16 +02:00
|
|
|
[bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()),
|
|
|
|
|
nmos_upper_d_pin.center()])
|
2017-12-12 23:53:19 +01:00
|
|
|
# halfway up, move over
|
2019-10-06 19:30:16 +02:00
|
|
|
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) \
|
2020-04-21 05:22:46 +02:00
|
|
|
+ nmos_upper_s_pin.bc().scale(1, 0.4)
|
2020-04-23 01:22:34 +02:00
|
|
|
self.add_path(self.col_mux_stack[2],
|
2020-04-21 05:22:46 +02:00
|
|
|
[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.center()])
|
|
|
|
|
|
2018-06-29 20:35:29 +02:00
|
|
|
# br -> nmos_lower/D on metal2
|
|
|
|
|
# br_out -> nmos_lower/S on metal1
|
2020-04-23 01:22:34 +02:00
|
|
|
self.add_path(self.col_mux_stack[0],
|
2019-10-06 19:30:16 +02:00
|
|
|
[br_out_pin.uc(),
|
|
|
|
|
vector(nmos_lower_s_pin.cx(), br_out_pin.uy()),
|
|
|
|
|
nmos_lower_s_pin.center()])
|
2017-12-12 23:53:19 +01:00
|
|
|
# halfway up, move over
|
2020-06-15 19:08:07 +02:00
|
|
|
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)
|
2020-04-23 01:22:34 +02:00
|
|
|
self.add_path(self.col_mux_stack[2],
|
2020-04-21 05:22:46 +02:00
|
|
|
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()])
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-28 17:54:15 +01:00
|
|
|
def extend_implants(self):
|
2020-06-15 19:08:07 +02:00
|
|
|
"""
|
|
|
|
|
Add top-to-bottom implants for adjacency issues in s8.
|
|
|
|
|
"""
|
|
|
|
|
# Route to the bottom
|
|
|
|
|
ll = (self.nmos_lower.ll() - vector(2 * [self.implant_enclose_active])).scale(1, 0)
|
|
|
|
|
# Don't route to the top
|
|
|
|
|
ur = self.nmos_upper.ur() + vector(self.implant_enclose_active, 0)
|
|
|
|
|
self.add_rect("nimplant",
|
|
|
|
|
ll,
|
|
|
|
|
ur.x - ll.x,
|
|
|
|
|
ur.y - ll.y)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-05-13 23:46:42 +02:00
|
|
|
def add_pn_wells(self):
|
2019-10-06 19:30:16 +02:00
|
|
|
"""
|
2018-04-06 18:50:13 +02:00
|
|
|
Add a well and implant over the whole cell. Also, add the
|
2019-10-06 19:30:16 +02:00
|
|
|
pwell contact (if it exists)
|
2018-04-06 18:50:13 +02:00
|
|
|
"""
|
2021-05-03 21:52:07 +02:00
|
|
|
if(cell_props.use_strap == True and OPTS.num_ports == 1):
|
|
|
|
|
strap = factory.create(module_type=cell_props.strap_module, version=cell_props.strap_version)
|
|
|
|
|
rbc_width = self.bitcell.width + strap.width
|
|
|
|
|
else:
|
2021-05-03 22:08:04 +02:00
|
|
|
rbc_width = self.bitcell.width
|
2018-04-06 18:50:13 +02:00
|
|
|
# Add it to the right, aligned in between the two tx
|
2021-05-03 21:52:07 +02:00
|
|
|
active_pos = vector(rbc_width,
|
2019-10-06 19:30:16 +02:00
|
|
|
self.nmos_upper.by() - 0.5 * self.poly_space)
|
2021-05-03 21:52:07 +02:00
|
|
|
|
2019-12-13 23:56:14 +01:00
|
|
|
self.add_via_center(layers=self.active_stack,
|
2019-10-06 19:30:16 +02:00
|
|
|
offset=active_pos,
|
|
|
|
|
implant_type="p",
|
|
|
|
|
well_type="p")
|
|
|
|
|
|
2020-04-23 01:22:34 +02:00
|
|
|
# If there is a li layer, include it in the power stack
|
|
|
|
|
self.add_via_center(layers=self.col_mux_stack,
|
|
|
|
|
offset=active_pos)
|
|
|
|
|
|
2019-12-23 16:37:16 +01:00
|
|
|
# Add the M1->..->power_grid_layer stack
|
2020-06-15 19:08:07 +02:00
|
|
|
self.add_power_pin(name="gnd",
|
|
|
|
|
loc=active_pos,
|
2019-12-23 16:37:16 +01:00
|
|
|
start_layer="m1")
|
2018-06-29 20:35:29 +02:00
|
|
|
|
|
|
|
|
# Add well enclosure over all the tx and contact
|
2020-04-21 05:22:46 +02:00
|
|
|
if "pwell" in layer:
|
|
|
|
|
self.add_rect(layer="pwell",
|
|
|
|
|
offset=vector(0, 0),
|
2021-05-03 21:52:07 +02:00
|
|
|
width=rbc_width,
|
2020-04-21 05:22:46 +02:00
|
|
|
height=self.height)
|