2016-11-08 18:57:35 +01:00
|
|
|
from math import log
|
|
|
|
|
import design
|
2017-12-12 23:53:19 +01:00
|
|
|
import contact
|
2016-11-08 18:57:35 +01:00
|
|
|
from tech import drc
|
|
|
|
|
import debug
|
|
|
|
|
import math
|
|
|
|
|
from vector import vector
|
2019-01-17 01:15:38 +01:00
|
|
|
from sram_factory import factory
|
2018-08-28 19:24:09 +02:00
|
|
|
from globals import OPTS
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
class single_level_column_mux_array(design.design):
|
|
|
|
|
"""
|
|
|
|
|
Dynamically generated column mux array.
|
|
|
|
|
Array of column mux to read the bitlines through the 6T.
|
|
|
|
|
"""
|
|
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br"):
|
2018-09-10 07:06:29 +02:00
|
|
|
design.design.__init__(self, name)
|
2016-11-08 18:57:35 +01:00
|
|
|
debug.info(1, "Creating {0}".format(self.name))
|
2019-01-26 00:00:00 +01:00
|
|
|
self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br))
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.columns = columns
|
|
|
|
|
self.word_size = word_size
|
2018-05-12 01:32:00 +02:00
|
|
|
self.words_per_row = int(self.columns / self.word_size)
|
2018-09-10 07:06:29 +02:00
|
|
|
self.bitcell_bl = bitcell_bl
|
|
|
|
|
self.bitcell_br = bitcell_br
|
2018-08-28 19:24:09 +02:00
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
self.create_netlist()
|
2018-08-28 19:24:09 +02:00
|
|
|
if not OPTS.netlist_only:
|
|
|
|
|
self.create_layout()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
def create_netlist(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_modules()
|
2018-08-28 19:24:09 +02:00
|
|
|
self.add_pins()
|
2016-11-08 18:57:35 +01:00
|
|
|
self.create_array()
|
2018-08-27 20:13:34 +02:00
|
|
|
|
|
|
|
|
def create_layout(self):
|
|
|
|
|
self.setup_layout_constants()
|
|
|
|
|
self.place_array()
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_routing()
|
2018-02-03 00:17:21 +01:00
|
|
|
# Find the highest shapes to determine height before adding well
|
|
|
|
|
highest = self.find_highest_coords()
|
|
|
|
|
self.height = highest.y
|
|
|
|
|
self.add_layout_pins()
|
2018-01-31 23:31:50 +01:00
|
|
|
self.add_enclosure(self.mux_inst, "pwell")
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
self.DRC_LVS()
|
2018-02-03 00:17:21 +01:00
|
|
|
|
2018-08-28 19:24:09 +02:00
|
|
|
def add_pins(self):
|
|
|
|
|
for i in range(self.columns):
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_pin("bl_{}".format(i))
|
|
|
|
|
self.add_pin("br_{}".format(i))
|
2018-08-28 19:24:09 +02:00
|
|
|
for i in range(self.words_per_row):
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_pin("sel_{}".format(i))
|
2018-08-28 19:24:09 +02:00
|
|
|
for i in range(self.word_size):
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_pin("bl_out_{}".format(i))
|
|
|
|
|
self.add_pin("br_out_{}".format(i))
|
2018-08-28 19:24:09 +02:00
|
|
|
self.add_pin("gnd")
|
|
|
|
|
|
2018-02-03 00:17:21 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def add_modules(self):
|
2019-01-17 01:15:38 +01:00
|
|
|
self.mux = factory.create(module_type="single_level_column_mux",
|
|
|
|
|
bitcell_bl=self.bitcell_bl,
|
|
|
|
|
bitcell_br=self.bitcell_br)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_mod(self.mux)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def setup_layout_constants(self):
|
|
|
|
|
self.column_addr_size = num_of_inputs = int(self.words_per_row / 2)
|
2017-08-24 00:02:15 +02:00
|
|
|
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
|
2018-02-02 23:26:39 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-01-31 23:31:50 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def create_array(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
self.mux_inst = []
|
|
|
|
|
# For every column, add a pass gate
|
|
|
|
|
for col_num in range(self.columns):
|
|
|
|
|
name = "XMUX{0}".format(col_num)
|
|
|
|
|
self.mux_inst.append(self.add_inst(name=name,
|
2018-08-27 20:13:34 +02:00
|
|
|
mod=self.mux))
|
2018-02-03 00:17:21 +01:00
|
|
|
|
2018-10-11 18:53:08 +02:00
|
|
|
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)),
|
|
|
|
|
"sel_{}".format(col_num % self.words_per_row),
|
2018-02-03 00:17:21 +01:00
|
|
|
"gnd"])
|
|
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
def place_array(self):
|
|
|
|
|
# For every column, add a pass gate
|
|
|
|
|
for col_num in range(self.columns):
|
|
|
|
|
name = "XMUX{0}".format(col_num)
|
|
|
|
|
x_off = vector(col_num * self.mux.width, self.route_height)
|
2018-08-28 02:25:39 +02:00
|
|
|
self.mux_inst[col_num].place(x_off)
|
2018-08-27 20:13:34 +02:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-02-03 00:17:21 +01:00
|
|
|
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()
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin(text="bl_{}".format(col_num),
|
2017-08-24 00:02:15 +02:00
|
|
|
layer="metal2",
|
|
|
|
|
offset=offset,
|
|
|
|
|
height=self.height-offset.y)
|
|
|
|
|
|
2018-02-03 00:17:21 +01:00
|
|
|
offset = mux_inst.get_pin("br").ll()
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin(text="br_{}".format(col_num),
|
2017-08-24 00:02:15 +02:00
|
|
|
layer="metal2",
|
|
|
|
|
offset=offset,
|
|
|
|
|
height=self.height-offset.y)
|
|
|
|
|
|
2018-04-06 18:50:13 +02:00
|
|
|
for inst in self.mux_inst:
|
|
|
|
|
self.copy_layout_pin(inst, "gnd")
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def add_routing(self):
|
|
|
|
|
self.add_horizontal_input_rail()
|
|
|
|
|
self.add_vertical_poly_rail()
|
2017-08-24 00:02:15 +02:00
|
|
|
self.route_bitlines()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def add_horizontal_input_rail(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" Create address input rails on M1 below the mux transistors """
|
|
|
|
|
for j in range(self.words_per_row):
|
2018-03-19 23:11:42 +01:00
|
|
|
offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch)
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin(text="sel_{}".format(j),
|
2017-08-24 00:02:15 +02:00
|
|
|
layer="metal1",
|
|
|
|
|
offset=offset,
|
|
|
|
|
width=self.mux.width * self.columns,
|
2017-12-12 23:53:19 +01:00
|
|
|
height=contact.m1m2.width)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def add_vertical_poly_rail(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" 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
|
|
|
|
|
sel_index = col % self.words_per_row
|
|
|
|
|
# Add the column x offset to find the right select bit
|
2017-12-19 18:01:24 +01:00
|
|
|
gate_offset = self.mux_inst[col].get_pin("sel").bc()
|
2017-08-24 00:02:15 +02:00
|
|
|
# height to connect the gate to the correct horizontal row
|
2018-10-11 18:53:08 +02:00
|
|
|
sel_height = self.get_pin("sel_{}".format(sel_index)).by()
|
2017-08-24 00:02:15 +02:00
|
|
|
# use the y offset from the sel pin and the x offset from the gate
|
2018-10-11 18:53:08 +02:00
|
|
|
offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy())
|
2017-08-24 00:02:15 +02:00
|
|
|
# Add the poly contact with a shift to account for the rotation
|
2017-12-12 23:53:19 +01:00
|
|
|
self.add_via_center(layers=("metal1", "contact", "poly"),
|
|
|
|
|
offset=offset,
|
|
|
|
|
rotate=90)
|
|
|
|
|
self.add_path("poly", [offset, gate_offset])
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def route_bitlines(self):
|
|
|
|
|
""" Connect the output bit-lines to form the appropriate width mux """
|
|
|
|
|
for j in range(self.columns):
|
|
|
|
|
bl_offset = self.mux_inst[j].get_pin("bl_out").ll()
|
|
|
|
|
br_offset = self.mux_inst[j].get_pin("br_out").ll()
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
if (j % self.words_per_row) == 0:
|
|
|
|
|
# Create the metal1 to connect the n-way mux output from the pass gate
|
|
|
|
|
# These will be located below the select lines. Yes, these are M2 width
|
|
|
|
|
# to ensure vias are enclosed and M1 min width rules.
|
2017-12-12 23:53:19 +01:00
|
|
|
width = contact.m1m2.width + self.mux.width * (self.words_per_row - 1)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_rect(layer="metal1",
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=bl_out_offset,
|
2016-11-08 18:57:35 +01:00
|
|
|
width=width,
|
2018-10-12 23:37:51 +02:00
|
|
|
height=drc("minwidth_metal2"))
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_rect(layer="metal1",
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=br_out_offset,
|
2016-11-08 18:57:35 +01:00
|
|
|
width=width,
|
2018-10-12 23:37:51 +02:00
|
|
|
height=drc("minwidth_metal2"))
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# Extend the bitline output rails and gnd downward on the first bit of each n-way mux
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin(text="bl_out_{}".format(int(j/self.words_per_row)),
|
2017-08-24 00:02:15 +02:00
|
|
|
layer="metal2",
|
|
|
|
|
offset=bl_out_offset.scale(1,0),
|
2018-10-12 23:37:51 +02:00
|
|
|
width=drc('minwidth_metal2'),
|
2017-08-24 00:02:15 +02:00
|
|
|
height=self.route_height)
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin(text="br_out_{}".format(int(j/self.words_per_row)),
|
2017-08-24 00:02:15 +02:00
|
|
|
layer="metal2",
|
|
|
|
|
offset=br_out_offset.scale(1,0),
|
2018-10-12 23:37:51 +02:00
|
|
|
width=drc('minwidth_metal2'),
|
2017-08-24 00:02:15 +02:00
|
|
|
height=self.route_height)
|
|
|
|
|
|
|
|
|
|
# This via is on the right of the wire
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
2017-12-12 23:53:19 +01:00
|
|
|
offset=bl_out_offset + vector(contact.m1m2.height,0),
|
2017-08-24 00:02:15 +02:00
|
|
|
rotate=90)
|
|
|
|
|
# This via is on the left of the wire
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset= br_out_offset,
|
|
|
|
|
rotate=90)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
else:
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_rect(layer="metal2",
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=bl_out_offset,
|
2018-10-12 23:37:51 +02:00
|
|
|
width=drc('minwidth_metal2'),
|
2017-08-24 00:02:15 +02:00
|
|
|
height=self.route_height-bl_out_offset.y)
|
|
|
|
|
# This via is on the right of the wire
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
2017-12-12 23:53:19 +01:00
|
|
|
offset=bl_out_offset + vector(contact.m1m2.height,0),
|
2016-11-08 18:57:35 +01:00
|
|
|
rotate=90)
|
|
|
|
|
self.add_rect(layer="metal2",
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=br_out_offset,
|
2018-10-12 23:37:51 +02:00
|
|
|
width=drc('minwidth_metal2'),
|
2017-08-24 00:02:15 +02:00
|
|
|
height=self.route_height-br_out_offset.y)
|
|
|
|
|
# This via is on the left of the wire
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
2017-08-24 00:02:15 +02:00
|
|
|
offset= br_out_offset,
|
2016-11-08 18:57:35 +01:00
|
|
|
rotate=90)
|
|
|
|
|
|
2019-03-04 09:42:18 +01:00
|
|
|
def analytical_delay(self, corner, vdd, slew, load=0.0):
|
2018-10-27 02:37:25 +02:00
|
|
|
from tech import spice, parameter
|
2019-03-05 04:27:53 +01:00
|
|
|
proc,vdd,temp = corner
|
2018-10-23 21:55:54 +02:00
|
|
|
r = spice["min_tx_r"]/(self.mux.ptx_width/parameter["min_tx_size"])
|
|
|
|
|
#Drains of mux transistors make up capacitance.
|
|
|
|
|
c_para = spice["min_tx_drain_c"]*(self.mux.ptx_width/parameter["min_tx_size"])*self.words_per_row#ff
|
2019-03-05 04:27:53 +01:00
|
|
|
volt_swing = spice["v_threshold_typical"]/vdd
|
2018-10-23 21:55:54 +02:00
|
|
|
|
2019-03-04 09:42:18 +01:00
|
|
|
result = self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew, swing = volt_swing)
|
2018-10-23 21:55:54 +02:00
|
|
|
return self.return_delay(result.delay, result.slew)
|
2017-08-24 00:02:15 +02:00
|
|
|
|