s8 col mux array

This commit is contained in:
Joey Kunzler 2020-04-22 16:22:34 -07:00
parent 60ba2c1aa5
commit fed1c0bbe1
2 changed files with 85 additions and 75 deletions

View File

@ -5,17 +5,15 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from math import log
import design
import contact
from tech import drc
import debug
import math
from tech import layer
from vector import vector
from sram_factory import factory
from globals import OPTS
import logical_effort
class single_level_column_mux_array(design.design):
"""
Dynamically generated column mux array.
@ -26,13 +24,20 @@ class single_level_column_mux_array(design.design):
design.design.__init__(self, name)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br))
self.columns = columns
self.word_size = word_size
self.words_per_row = int(self.columns / self.word_size)
self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br
if "li" in layer:
self.col_mux_stack = self.li_stack
self.col_mux_stack_pitch = self.li_pitch
else:
self.col_mux_stack = self.m1_stack
self.col_mux_stack_pitch = self.m1_pitch
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -49,20 +54,20 @@ class single_level_column_mux_array(design.design):
self.add_modules()
self.add_pins()
self.create_array()
def create_layout(self):
self.setup_layout_constants()
self.place_array()
self.add_routing()
# Find the highest shapes to determine height before adding well
highest = self.find_highest_coords()
self.height = highest.y
self.height = highest.y
self.add_layout_pins()
self.add_enclosure(self.mux_inst, "pwell")
if "pwell" in layer:
self.add_enclosure(self.mux_inst, "pwell")
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
for i in range(self.columns):
self.add_pin("bl_{}".format(i))
@ -74,23 +79,19 @@ class single_level_column_mux_array(design.design):
self.add_pin("br_out_{}".format(i))
self.add_pin("gnd")
def add_modules(self):
self.mux = factory.create(module_type="single_level_column_mux",
bitcell_bl=self.bitcell_bl,
bitcell_br=self.bitcell_br)
self.add_mod(self.mux)
def setup_layout_constants(self):
self.column_addr_size = num_of_inputs = int(self.words_per_row / 2)
self.column_addr_size = int(self.words_per_row / 2)
self.width = self.columns * self.mux.width
# one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br
# one extra route pitch is to space from the sense amp
self.route_height = (self.words_per_row + 3)*self.m1_pitch
self.route_height = (self.words_per_row + 3) * self.col_mux_stack_pitch
def create_array(self):
self.mux_inst = []
# For every column, add a pass gate
@ -98,11 +99,11 @@ class single_level_column_mux_array(design.design):
name = "XMUX{0}".format(col_num)
self.mux_inst.append(self.add_inst(name=name,
mod=self.mux))
self.connect_inst(["bl_{}".format(col_num),
"br_{}".format(col_num),
"bl_out_{}".format(int(col_num/self.words_per_row)),
"br_out_{}".format(int(col_num/self.words_per_row)),
"bl_out_{}".format(int(col_num / self.words_per_row)),
"br_out_{}".format(int(col_num / self.words_per_row)),
"sel_{}".format(col_num % self.words_per_row),
"gnd"])
@ -117,32 +118,31 @@ class single_level_column_mux_array(design.design):
else:
mirror = ""
name = "XMUX{0}".format(col_num)
offset = vector(xoffset, self.route_height)
self.mux_inst[col_num].place(offset=offset, mirror=mirror)
def add_layout_pins(self):
""" Add the pins after we determine the height. """
# For every column, add a pass gate
for col_num in range(self.columns):
mux_inst = self.mux_inst[col_num]
offset = mux_inst.get_pin("bl").ll()
bl_pin = mux_inst.get_pin("bl")
offset = bl_pin.ll()
self.add_layout_pin(text="bl_{}".format(col_num),
layer="m2",
layer=bl_pin.layer,
offset=offset,
height=self.height-offset.y)
height=self.height - offset.y)
offset = mux_inst.get_pin("br").ll()
br_pin = mux_inst.get_pin("br")
offset = br_pin.ll()
self.add_layout_pin(text="br_{}".format(col_num),
layer="m2",
layer=br_pin.layer,
offset=offset,
height=self.height-offset.y)
height=self.height - offset.y)
for inst in self.mux_inst:
self.copy_layout_pin(inst, "gnd")
def add_routing(self):
self.add_horizontal_input_rail()
self.add_vertical_poly_rail()
@ -151,15 +151,15 @@ class single_level_column_mux_array(design.design):
def add_horizontal_input_rail(self):
""" Create address input rails on M1 below the mux transistors """
for j in range(self.words_per_row):
offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch)
offset = vector(0, self.route_height + (j - self.words_per_row) * self.col_mux_stack_pitch) #edit
self.add_layout_pin(text="sel_{}".format(j),
layer="m1",
layer=self.col_mux_stack[0],
offset=offset,
width=self.mux.width * self.columns)
def add_vertical_poly_rail(self):
""" Connect the poly to the address rails """
# Offset to the first transistor gate in the pass gate
for col in range(self.columns):
# which select bit should this column connect to depends on the position in the word
@ -167,11 +167,12 @@ class single_level_column_mux_array(design.design):
# Add the column x offset to find the right select bit
gate_offset = self.mux_inst[col].get_pin("sel").bc()
# height to connect the gate to the correct horizontal row
sel_height = self.get_pin("sel_{}".format(sel_index)).by()
# sel_height = self.get_pin("sel_{}".format(sel_index)).by()
# use the y offset from the sel pin and the x offset from the gate
offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy())
offset = vector(gate_offset.x,
self.get_pin("sel_{}".format(sel_index)).cy())
# Add the poly contact with a shift to account for the rotation
self.add_via_center(layers=("m1", "contact", "poly"),
self.add_via_center(layers=self.poly_stack,
offset=offset)
self.add_path("poly", [offset, gate_offset])
@ -182,11 +183,11 @@ class single_level_column_mux_array(design.design):
bl_offset = self.mux_inst[j].get_pin("bl_out").bc()
br_offset = self.mux_inst[j].get_pin("br_out").bc()
bl_out_offset = bl_offset - vector(0,(self.words_per_row+1)*self.m1_pitch)
br_out_offset = br_offset - vector(0,(self.words_per_row+2)*self.m1_pitch)
bl_out_offset = bl_offset - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch)
br_out_offset = br_offset - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch)
bl_out_offset_end = bl_out_offset + vector(0,self.route_height)
br_out_offset_end = br_out_offset + vector(0,self.route_height)
bl_out_offset_end = bl_out_offset + vector(0, self.route_height)
br_out_offset_end = br_out_offset + vector(0, self.route_height)
if cell_properties.bitcell.mirror.y and j % 2:
tmp_bl_out_end = br_out_offset_end
@ -208,43 +209,41 @@ class single_level_column_mux_array(design.design):
else:
dist = 0
self.add_path("m1", [bl_out_offset, bl_out_offset+vector(width+dist,0)])
self.add_path("m1", [br_out_offset, br_out_offset+vector(width-dist,0)])
self.add_path(self.col_mux_stack[0], [bl_out_offset, bl_out_offset + vector(width + dist, 0)])
self.add_path(self.col_mux_stack[0], [br_out_offset, br_out_offset + vector(width - dist, 0)])
# Extend the bitline output rails and gnd downward on the first bit of each n-way mux
self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)),
layer="m2",
self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)),
layer=self.col_mux_stack[2],
start=bl_out_offset,
end=tmp_bl_out_end)
self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)),
layer="m2",
self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)),
layer=self.col_mux_stack[2],
start=br_out_offset,
end=tmp_br_out_end)
# This via is on the right of the wire
self.add_via_center(layers=self.m1_stack,
# This via is on the right of the wire
self.add_via_center(layers=self.col_mux_stack,
offset=bl_out_offset)
# This via is on the left of the wire
self.add_via_center(layers=self.m1_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=br_out_offset)
else:
self.add_path("m2", [ bl_out_offset, tmp_bl_out_end])
self.add_path("m2", [ br_out_offset, tmp_br_out_end])
self.add_path(self.col_mux_stack[2], [bl_out_offset, bl_offset])
self.add_path(self.col_mux_stack[2], [br_out_offset, br_offset])
# This via is on the right of the wire
self.add_via_center(layers=self.m1_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=bl_out_offset)
# This via is on the left of the wire
self.add_via_center(layers=self.m1_stack,
# This via is on the left of the wire
self.add_via_center(layers=self.col_mux_stack,
offset=br_out_offset)
def get_drain_cin(self):
"""Get the relative capacitance of the drain of the NMOS pass TX"""
from tech import parameter
#Bitcell drain load being used to estimate mux NMOS drain load
# Bitcell drain load being used to estimate mux NMOS drain load
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
return drain_load
return drain_load

View File

@ -121,13 +121,25 @@ class single_level_column_mux(pgate.pgate):
# height is the distance between the nmos' diffusions, which depends on max(self.active_space,self.poly_space)
offset = self.nmos_lower.get_pin("G").ul() - vector(0,self.poly_extend_active)
height = self.nmos_upper.get_pin("G").by() + self.poly_extend_active - offset.y
self.add_layout_pin(text="sel",
layer="poly",
self.add_rect(layer="poly",
offset=offset,
height=height)
# 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)
def connect_bitlines(self):
""" Connect the bitlines to the mux transistors """
# 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
# These are on metal2
bl_pin = self.get_pin("bl")
br_pin = self.get_pin("br")
@ -140,29 +152,24 @@ class single_level_column_mux(pgate.pgate):
nmos_upper_s_pin = self.nmos_upper.get_pin("S")
nmos_upper_d_pin = self.nmos_upper.get_pin("D")
# If li exists, use li and m1 for the mux, otherwise use m1 and m2
if "li" in layer:
col_mux_stack = self.li_stack
else:
col_mux_stack = self.m1_stack
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
self.add_via_center(layers=col_mux_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=bl_pin.bc(),
directions=("V", "V"))
self.add_via_center(layers=col_mux_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=br_out_pin.uc(),
directions=("V", "V"))
self.add_via_center(layers=col_mux_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=nmos_upper_s_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=col_mux_stack,
self.add_via_center(layers=self.col_mux_stack,
offset=nmos_lower_d_pin.center(),
directions=("V", "V"))
# Add diffusion contacts
# These were previously omitted with the options: add_source_contact=False, add_drain_contact=False
# They are added now and not previously due to a s8 tech special case in which the contacts intersected the mux intraconnect
# They are added now and not previously so that they do not include m1 (which is usually included by default)
# This is only a concern when the local interconnect (li) layer is being used
self.add_via_center(layers=self.active_stack,
offset=nmos_upper_d_pin.center(),
directions=("V", "V"),
@ -186,7 +193,7 @@ class single_level_column_mux(pgate.pgate):
# bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2
self.add_path(col_mux_stack[0],
self.add_path(self.col_mux_stack[0],
[bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()),
nmos_upper_d_pin.center()])
# halfway up, move over
@ -194,12 +201,12 @@ class single_level_column_mux(pgate.pgate):
+ 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(col_mux_stack[2],
self.add_path(self.col_mux_stack[2],
[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.center()])
# br -> nmos_lower/D on metal2
# br_out -> nmos_lower/S on metal1
self.add_path(col_mux_stack[0],
self.add_path(self.col_mux_stack[0],
[br_out_pin.uc(),
vector(nmos_lower_s_pin.cx(), br_out_pin.uy()),
nmos_lower_s_pin.center()])
@ -208,7 +215,7 @@ class single_level_column_mux(pgate.pgate):
+ 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(col_mux_stack[2],
self.add_path(self.col_mux_stack[2],
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()])
def add_wells(self):
@ -225,6 +232,10 @@ class single_level_column_mux(pgate.pgate):
implant_type="p",
well_type="p")
# If there is a li layer, include it in the power stack
self.add_via_center(layers=self.col_mux_stack,
offset=active_pos)
# Add the M1->..->power_grid_layer stack
self.add_power_pin(name = "gnd",
loc = active_pos,