mirror of https://github.com/VLSIDA/OpenRAM.git
273 lines
13 KiB
Python
273 lines
13 KiB
Python
from math import log
|
|
import design
|
|
from single_level_column_mux import single_level_column_mux
|
|
from contact import contact
|
|
from tech import drc
|
|
import debug
|
|
import math
|
|
from vector import vector
|
|
|
|
|
|
class single_level_column_mux_array(design.design):
|
|
"""
|
|
Dynamically generated column mux array.
|
|
Array of column mux to read the bitlines through the 6T.
|
|
"""
|
|
|
|
def __init__(self, rows, columns, word_size):
|
|
design.design.__init__(self, "columnmux_array")
|
|
debug.info(1, "Creating {0}".format(self.name))
|
|
self.rows = rows
|
|
self.columns = columns
|
|
self.word_size = word_size
|
|
self.words_per_row = self.columns / self.word_size
|
|
self.row_addr_size = self.decoder_inputs = int(math.log(self.rows, 2))
|
|
self.add_pins()
|
|
self.create_layout()
|
|
self.offset_all_coordinates()
|
|
self.DRC_LVS()
|
|
|
|
def add_pins(self):
|
|
for i in range(self.columns):
|
|
self.add_pin("bl[{0}]".format(i))
|
|
self.add_pin("br[{0}]".format(i))
|
|
for i in range(self.columns / self.words_per_row):
|
|
self.add_pin("bl_out[{0}]".format(i * self.words_per_row))
|
|
self.add_pin("br_out[{0}]".format(i * self.words_per_row))
|
|
for i in range(self.words_per_row):
|
|
self.add_pin("sel[{0}]".format(i))
|
|
self.add_pin("gnd")
|
|
|
|
def create_layout(self):
|
|
self.add_modules()
|
|
self.setup_layout_constants()
|
|
self.create_array()
|
|
self.add_routing()
|
|
|
|
def add_modules(self):
|
|
self.mux = single_level_column_mux(name="single_level_column_mux",
|
|
tx_size=8)
|
|
self.single_mux = self.mux
|
|
self.add_mod(self.mux)
|
|
|
|
# This is not instantiated and used for calculations only.
|
|
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
|
|
|
|
|
|
def setup_layout_constants(self):
|
|
self.column_addr_size = num_of_inputs = int(self.words_per_row / 2)
|
|
self.width = (self.columns * self.mux.width)
|
|
self.gnd_positions = []
|
|
self.BL_out_positions = []
|
|
self.BR_out_positions = []
|
|
self.BL_positions = []
|
|
self.BR_positions = []
|
|
self.addr_line_positions = []
|
|
|
|
spacing = self.m1m2_via.width + drc['metal1_to_metal1']
|
|
self.height = self.mux.height + spacing + 4 * drc['metal2_to_metal2']
|
|
if (self.words_per_row > 1):
|
|
# 1 for BL and another for BR
|
|
self.height = self.height + (self.words_per_row + 1) * spacing
|
|
|
|
def create_array(self):
|
|
for i in range(self.columns):
|
|
name = "XMUX{0}".format(i)
|
|
x_off = vector(i * self.mux.width, 0)
|
|
self.add_inst(name=name,
|
|
mod=self.mux,
|
|
offset=x_off)
|
|
|
|
""" draw a vertical m2 rail to extend BL BR & gnd on top of the cell """
|
|
# FIXME: These are just min metal squares, are they needed?
|
|
self.add_rect(layer="metal2",
|
|
offset=x_off + self.mux.BL_position,
|
|
width=drc['minwidth_metal2'],
|
|
height=drc['minwidth_metal2'])
|
|
self.add_rect(layer="metal2",
|
|
offset=x_off + self.mux.BR_position,
|
|
width=drc['minwidth_metal2'],
|
|
height=drc['minwidth_metal2'])
|
|
self.add_rect(layer="metal2",
|
|
offset=x_off + self.mux.gnd_position,
|
|
width=drc['minwidth_metal2'],
|
|
height=drc['minwidth_metal2'])
|
|
|
|
""" add labels for the column_mux array """
|
|
BL = self.mux.BL_position + vector(i * self.mux.width, 0)
|
|
self.BL_positions.append(BL)
|
|
self.add_label(text="bl[{0}]".format(i),
|
|
layer="metal2",
|
|
offset=BL)
|
|
|
|
BR = self.mux.BR_position + vector(i * self.mux.width, 0)
|
|
self.BR_positions.append(BR)
|
|
self.add_label(text="br[{0}]".format(i),
|
|
layer="metal2",
|
|
offset=BR)
|
|
|
|
gnd = self.mux.gnd_position + vector(i * self.mux.width, 0)
|
|
self.gnd_positions.append(gnd)
|
|
self.add_label(text="gnd",
|
|
layer="metal2",
|
|
offset=gnd)
|
|
|
|
for i in range(self.word_size):
|
|
base =vector(i * self.words_per_row * self.mux.width, 0)
|
|
BL_out = base + self.mux.BL_out_position
|
|
BR_out = base + self.mux.BR_out_position
|
|
self.add_label(text="bl_out[{0}]".format(i * self.words_per_row),
|
|
layer="metal2",
|
|
offset=BL_out)
|
|
self.add_label(text="br_out[{0}]".format(i * self.words_per_row),
|
|
layer="metal2",
|
|
offset=BR_out)
|
|
self.BL_out_positions.append(BL_out)
|
|
self.BR_out_positions.append(BR_out)
|
|
|
|
if(self.words_per_row == 2):
|
|
for i in range(self.columns / 2):
|
|
# This will not check that the inst connections match.
|
|
self.connect_inst(args=["bl[{0}]".format(2 * i),
|
|
"br[{0}]".format(2 * i),
|
|
"bl_out[{0}]".format(2 * i),
|
|
"br_out[{0}]".format(2 * i),
|
|
"sel[{0}]".format(0), "gnd"],
|
|
check=False)
|
|
# This will not check that the inst connections match.
|
|
self.connect_inst(args=["bl[{0}]".format(2 * i + 1),
|
|
"br[{0}]".format(2 * i + 1),
|
|
"bl_out[{0}]".format(2 * i),
|
|
"br_out[{0}]".format(2 * i),
|
|
"sel[{0}]".format(1), "gnd"],
|
|
check=False)
|
|
if(self.words_per_row == 4):
|
|
for i in range(self.columns / 4):
|
|
# This will not check that the inst connections match.
|
|
self.connect_inst(args=["bl[{0}]".format(4 * i),
|
|
"br[{0}]".format(4 * i),
|
|
"bl_out[{0}]".format(4 * i),
|
|
"br_out[{0}]".format(4 * i),
|
|
"sel[{0}]".format(0), "gnd"],
|
|
check=False)
|
|
# This will not check that the inst connections match.
|
|
self.connect_inst(args=["bl[{0}]".format(4 * i + 1),
|
|
"br[{0}]".format(4 * i + 1),
|
|
"bl_out[{0}]".format(4 * i),
|
|
"br_out[{0}]".format(4 * i),
|
|
"sel[{0}]".format(1), "gnd"],
|
|
check=False)
|
|
# This will not check that the inst connections match.
|
|
self.connect_inst(args=["bl[{0}]".format(4 * i + 2),
|
|
"br[{0}]".format(4 * i + 2),
|
|
"bl_out[{0}]".format(4 * i),
|
|
"br_out[{0}]".format(4 * i),
|
|
"sel[{0}]".format(2), "gnd"],
|
|
check=False)
|
|
# This will not check that the inst connections match.
|
|
self.connect_inst(args=["bl[{0}]".format(4 * i + 3),
|
|
"br[{0}]".format(4 * i + 3),
|
|
"bl_out[{0}]".format(4 * i),
|
|
"br_out[{0}]".format(4 * i),
|
|
"sel[{0}]".format(3), "gnd"],
|
|
check=False)
|
|
|
|
def add_routing(self):
|
|
self.add_horizontal_input_rail()
|
|
self.add_vertical_poly_rail()
|
|
self.routing_BL_BR()
|
|
|
|
def add_horizontal_input_rail(self):
|
|
""" HORIZONTAL ADDRESS INPUTS TO THE COLUMN MUX ARRAY """
|
|
if (self.words_per_row > 1):
|
|
for j in range(self.words_per_row):
|
|
offset = vector(0, -(j + 1) * self.m1m2_via.width
|
|
- j * drc['metal1_to_metal1'])
|
|
self.add_rect(layer="metal1",
|
|
offset=offset,
|
|
width=self.mux.width * self.columns,
|
|
height=self.m1m2_via.width)
|
|
self.addr_line_positions.append(offset)
|
|
|
|
def add_vertical_poly_rail(self):
|
|
""" VERTICAL POLY METAL EXTENSION AND POLY CONTACT """
|
|
for j1 in range(self.columns):
|
|
pattern = math.floor(j1 / self.words_per_row) * self.words_per_row
|
|
height = ((self.m1m2_via.width + drc['metal1_to_metal1'])
|
|
*(pattern - j1))
|
|
nmos1_poly = self.mux.nmos1_position + self.mux.nmos1.poly_positions[0]
|
|
offset = nmos1_poly.scale(1, 0) + vector(j1 * self.mux.width, 0)
|
|
self.add_rect(layer="poly",
|
|
offset=offset,
|
|
width=drc["minwidth_poly"],
|
|
height= height -self.m1m2_via.width)
|
|
|
|
# This is not instantiated and used for calculations only.
|
|
poly_contact = contact(layer_stack=("metal1", "contact", "poly"))
|
|
offset = offset.scale(1, 0) + vector(0, height - poly_contact.width)
|
|
self.add_contact(layers=("metal1", "contact", "poly"),
|
|
offset=offset,
|
|
mirror="MX",
|
|
rotate=90)
|
|
|
|
def routing_BL_BR(self):
|
|
""" OUTPUT BIT-LINE CONNECTIONS (BL_OUT, BR_OUT) """
|
|
if (self.words_per_row > 1):
|
|
for j in range(self.columns / self.words_per_row):
|
|
base = vector(self.mux.width * self.words_per_row * j,
|
|
self.m1m2_via.width + drc['metal1_to_metal1'])
|
|
width = self.m1m2_via.width + self.mux.width * (self.words_per_row - 1)
|
|
self.add_rect(layer="metal1",
|
|
offset=base.scale(1,-self.words_per_row) + self.mux.BL_position.scale(1,0),
|
|
width=width,
|
|
height=-self.m1m2_via.width)
|
|
self.add_rect(layer="metal1",
|
|
offset=base.scale(1,-self.words_per_row-1) + self.mux.BR_position.scale(1,0),
|
|
width=width,
|
|
height=-self.m1m2_via.width)
|
|
|
|
height = base.y * (self.words_per_row + 2) + 3 * drc['metal2_to_metal2']
|
|
base = vector(base.x, - height)
|
|
self.add_rect(layer="metal2",
|
|
offset=base + self.mux.BL_position.scale(1,0),
|
|
width=drc['minwidth_metal2'],
|
|
height=height)
|
|
self.add_rect(layer="metal2",
|
|
offset=base + self.mux.BR_position.scale(1,0),
|
|
width=drc['minwidth_metal2'],
|
|
height=height)
|
|
self.add_rect(layer="metal2",
|
|
offset=base + self.mux.gnd_position.scale(1,0),
|
|
width=drc['minwidth_metal2'],
|
|
height=height)
|
|
|
|
for j in range(self.columns):
|
|
""" adding vertical metal rails to route BL_out and BR_out vertical rails """
|
|
contact_spacing = self.m1m2_via.width + drc['metal1_to_metal1']
|
|
height = self.words_per_row * contact_spacing + self.m1m2_via.width
|
|
offset = vector(self.mux.BL_position.x + self.mux.width * j, 0)
|
|
self.add_rect(layer="metal2",
|
|
offset=offset,
|
|
width=drc['minwidth_metal2'],
|
|
height=-height)
|
|
offset = offset + vector(self.m1m2_via.height, - height)
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=offset,
|
|
rotate=90)
|
|
|
|
offset = vector(self.mux.BR_position.x + self.mux.width * j, 0)
|
|
height = height + contact_spacing
|
|
self.add_rect(layer="metal2",
|
|
offset=offset,
|
|
width=drc['minwidth_metal2'],
|
|
height= - height)
|
|
offset = offset + vector(self.m1m2_via.height/2, - height)
|
|
layer_diff = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width)
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset= offset + vector(layer_diff, 0),
|
|
rotate=90)
|
|
|
|
self.add_label(text="COLUMN_MUX",
|
|
layer="text",
|
|
offset=[self.width / 2.0, self.height / 2.0])
|