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
|
|
|
#
|
2022-11-27 22:01:20 +01:00
|
|
|
from openram import debug
|
|
|
|
|
from openram.base import design
|
|
|
|
|
from openram.base import vector
|
|
|
|
|
from openram.sram_factory import factory
|
|
|
|
|
from openram.tech import layer, preferred_directions
|
|
|
|
|
from openram.tech import layer_properties as layer_props
|
|
|
|
|
from openram import OPTS
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-04-23 01:22:34 +02:00
|
|
|
|
2022-07-13 19:57:56 +02:00
|
|
|
class column_mux_array(design):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
|
|
|
|
Dynamically generated column mux array.
|
|
|
|
|
Array of column mux to read the bitlines through the 6T.
|
|
|
|
|
"""
|
|
|
|
|
|
2020-09-12 00:36:22 +02:00
|
|
|
def __init__(self, name, columns, word_size, offsets=None, bitcell_bl="bl", bitcell_br="br", column_offset=0):
|
2020-08-06 20:33:26 +02:00
|
|
|
super().__init__(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))
|
2020-04-23 01:22:34 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.columns = columns
|
|
|
|
|
self.word_size = word_size
|
2020-09-12 00:36:22 +02:00
|
|
|
self.offsets = offsets
|
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
|
2020-06-05 20:29:31 +02:00
|
|
|
self.column_offset = column_offset
|
2020-04-23 01:22:34 +02:00
|
|
|
|
2020-10-27 23:11:04 +01:00
|
|
|
self.sel_layer = layer_props.column_mux_array.select_layer
|
2020-12-02 00:20:44 +01:00
|
|
|
self.sel_pitch = getattr(self, self.sel_layer + "_pitch")
|
2020-10-27 23:11:04 +01:00
|
|
|
self.bitline_layer = layer_props.column_mux_array.bitline_layer
|
|
|
|
|
|
2020-06-30 01:23:25 +02:00
|
|
|
if preferred_directions[self.sel_layer] == "V":
|
2020-06-02 20:43:31 +02:00
|
|
|
self.via_directions = ("H", "H")
|
|
|
|
|
else:
|
|
|
|
|
self.via_directions = "pref"
|
2020-04-23 01:22:34 +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
|
|
|
|
2020-02-17 14:09:50 +01:00
|
|
|
def get_bl_name(self):
|
2020-02-12 14:04:05 +01:00
|
|
|
bl_name = self.mux.get_bl_names()
|
2020-02-17 14:09:50 +01:00
|
|
|
return bl_name
|
2020-02-12 14:04:05 +01:00
|
|
|
|
|
|
|
|
def get_br_name(self, port=0):
|
|
|
|
|
br_name = self.mux.get_br_names()
|
2020-02-17 14:09:50 +01:00
|
|
|
return br_name
|
2020-02-12 14:04:05 +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()
|
2020-04-23 01:22:34 +02:00
|
|
|
|
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()
|
2020-04-23 01:22:34 +02:00
|
|
|
self.height = highest.y
|
2018-02-03 00:17:21 +01:00
|
|
|
self.add_layout_pins()
|
2020-04-23 01:22:34 +02:00
|
|
|
if "pwell" in layer:
|
|
|
|
|
self.add_enclosure(self.mux_inst, "pwell")
|
2019-05-28 01:32:38 +02:00
|
|
|
self.add_boundary()
|
2018-08-27 20:13:34 +02:00
|
|
|
self.DRC_LVS()
|
2020-04-23 01:22:34 +02: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")
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def add_modules(self):
|
2020-10-05 17:56:51 +02:00
|
|
|
self.mux = factory.create(module_type="column_mux",
|
2019-01-17 01:15:38 +01:00
|
|
|
bitcell_bl=self.bitcell_bl,
|
|
|
|
|
bitcell_br=self.bitcell_br)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-11-14 01:04:07 +01:00
|
|
|
self.cell = factory.create(module_type=OPTS.bitcell)
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def setup_layout_constants(self):
|
2020-04-23 01:22:34 +02:00
|
|
|
self.column_addr_size = 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
|
2020-06-30 01:23:25 +02:00
|
|
|
self.route_height = (self.words_per_row + 3) * self.sel_pitch
|
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))
|
2020-04-23 01:22:34 +02:00
|
|
|
|
2018-10-11 18:53:08 +02:00
|
|
|
self.connect_inst(["bl_{}".format(col_num),
|
|
|
|
|
"br_{}".format(col_num),
|
2020-04-23 01:22:34 +02:00
|
|
|
"bl_out_{}".format(int(col_num / self.words_per_row)),
|
|
|
|
|
"br_out_{}".format(int(col_num / self.words_per_row)),
|
2018-10-11 18:53:08 +02:00
|
|
|
"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):
|
2020-09-12 00:36:22 +02:00
|
|
|
# Default to single spaced columns
|
|
|
|
|
if not self.offsets:
|
|
|
|
|
self.offsets = [n * self.mux.width for n in range(self.columns)]
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-27 20:13:34 +02:00
|
|
|
# For every column, add a pass gate
|
2020-09-12 02:12:29 +02:00
|
|
|
for col_num, xoffset in enumerate(self.offsets[0:self.columns]):
|
2020-11-14 01:04:07 +01:00
|
|
|
if self.cell.mirror.y and (col_num + self.column_offset) % 2:
|
2020-01-27 17:23:01 +01:00
|
|
|
mirror = "MY"
|
|
|
|
|
xoffset = xoffset + self.mux.width
|
|
|
|
|
else:
|
|
|
|
|
mirror = ""
|
|
|
|
|
|
|
|
|
|
offset = vector(xoffset, self.route_height)
|
|
|
|
|
self.mux_inst[col_num].place(offset=offset, mirror=mirror)
|
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]
|
2020-04-23 01:22:34 +02:00
|
|
|
bl_pin = mux_inst.get_pin("bl")
|
|
|
|
|
offset = bl_pin.ll()
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin(text="bl_{}".format(col_num),
|
2020-04-23 01:22:34 +02:00
|
|
|
layer=bl_pin.layer,
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=offset,
|
2020-04-23 01:22:34 +02:00
|
|
|
height=self.height - offset.y)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-04-23 01:22:34 +02:00
|
|
|
br_pin = mux_inst.get_pin("br")
|
|
|
|
|
offset = br_pin.ll()
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin(text="br_{}".format(col_num),
|
2020-04-23 01:22:34 +02:00
|
|
|
layer=br_pin.layer,
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=offset,
|
2020-04-23 01:22:34 +02:00
|
|
|
height=self.height - offset.y)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2022-05-03 20:45:51 +02:00
|
|
|
def route_supplies(self):
|
|
|
|
|
self.route_horizontal_pins("gnd", self.insts)
|
2018-04-06 18:50:13 +02:00
|
|
|
|
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()
|
2022-05-03 20:45:51 +02:00
|
|
|
self.route_supplies()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def add_horizontal_input_rail(self):
|
2020-06-26 23:27:16 +02:00
|
|
|
""" Create address input rails below the mux transistors """
|
2017-08-24 00:02:15 +02:00
|
|
|
for j in range(self.words_per_row):
|
2020-06-30 01:23:25 +02:00
|
|
|
offset = vector(0, self.route_height + (j - self.words_per_row) * self.sel_pitch)
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin(text="sel_{}".format(j),
|
2020-06-30 01:23:25 +02:00
|
|
|
layer=self.sel_layer,
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=offset,
|
2020-09-14 21:05:45 +02:00
|
|
|
width=self.mux_inst[-1].rx())
|
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 """
|
2020-04-23 01:22:34 +02:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# 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
|
|
|
# use the y offset from the sel pin and the x offset from the gate
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2020-04-23 01:22:34 +02:00
|
|
|
offset = vector(gate_offset.x,
|
|
|
|
|
self.get_pin("sel_{}".format(sel_index)).cy())
|
2021-11-18 02:22:03 +01:00
|
|
|
|
2022-02-24 00:38:11 +01:00
|
|
|
bl_offset = offset.scale(0, 1) + vector((self.mux_inst[col].get_pin("br_out").cx() + self.mux_inst[col].get_pin("bl_out").cx())/2, 0)
|
2020-06-26 23:27:16 +02:00
|
|
|
self.add_via_stack_center(from_layer="poly",
|
2020-06-30 01:23:25 +02:00
|
|
|
to_layer=self.sel_layer,
|
2021-11-18 02:22:03 +01:00
|
|
|
offset=bl_offset,
|
2020-06-26 23:27:16 +02:00
|
|
|
directions=self.via_directions)
|
2021-11-18 02:22:03 +01:00
|
|
|
self.add_path("poly", [offset, gate_offset, bl_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):
|
2020-01-27 17:23:01 +01:00
|
|
|
|
2020-06-02 22:57:41 +02:00
|
|
|
bl_offset_begin = self.mux_inst[j].get_pin("bl_out").bc()
|
|
|
|
|
br_offset_begin = self.mux_inst[j].get_pin("br_out").bc()
|
2020-01-27 17:23:01 +01:00
|
|
|
|
2020-06-30 01:23:25 +02:00
|
|
|
bl_out_offset_begin = bl_offset_begin - vector(0, (self.words_per_row + 1) * self.sel_pitch)
|
|
|
|
|
br_out_offset_begin = br_offset_begin - vector(0, (self.words_per_row + 2) * self.sel_pitch)
|
2020-01-27 17:23:01 +01:00
|
|
|
|
2020-06-02 22:57:41 +02:00
|
|
|
# Add the horizontal wires for the first bit
|
|
|
|
|
if j % self.words_per_row == 0:
|
|
|
|
|
bl_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("bl_out").bc()
|
|
|
|
|
br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc()
|
2020-06-30 01:23:25 +02:00
|
|
|
bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.sel_pitch)
|
|
|
|
|
br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.sel_pitch)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-06-30 01:23:25 +02:00
|
|
|
self.add_path(self.sel_layer, [bl_out_offset_begin, bl_out_offset_end])
|
|
|
|
|
self.add_path(self.sel_layer, [br_out_offset_begin, br_out_offset_end])
|
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
|
2020-04-23 01:22:34 +02:00
|
|
|
self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)),
|
2020-06-30 01:23:25 +02:00
|
|
|
layer=self.bitline_layer,
|
2020-06-02 22:57:41 +02:00
|
|
|
start=bl_offset_begin,
|
|
|
|
|
end=bl_out_offset_begin)
|
2020-04-23 01:22:34 +02:00
|
|
|
self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)),
|
2020-06-30 01:23:25 +02:00
|
|
|
layer=self.bitline_layer,
|
2020-06-02 22:57:41 +02:00
|
|
|
start=br_offset_begin,
|
|
|
|
|
end=br_out_offset_begin)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-06-02 22:57:41 +02:00
|
|
|
else:
|
2020-06-30 01:23:25 +02:00
|
|
|
self.add_path(self.bitline_layer, [bl_out_offset_begin, bl_offset_begin])
|
|
|
|
|
self.add_path(self.bitline_layer, [br_out_offset_begin, br_offset_begin])
|
2019-04-01 23:23:47 +02:00
|
|
|
|
2020-06-02 22:57:41 +02:00
|
|
|
# This via is on the right of the wire
|
2020-06-30 01:23:25 +02:00
|
|
|
self.add_via_stack_center(from_layer=self.bitline_layer,
|
|
|
|
|
to_layer=self.sel_layer,
|
|
|
|
|
offset=bl_out_offset_begin,
|
|
|
|
|
directions=self.via_directions)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-06-02 22:57:41 +02:00
|
|
|
# This via is on the left of the wire
|
2020-06-30 01:23:25 +02:00
|
|
|
self.add_via_stack_center(from_layer=self.bitline_layer,
|
|
|
|
|
to_layer=self.sel_layer,
|
|
|
|
|
offset=br_out_offset_begin,
|
|
|
|
|
directions=self.via_directions)
|
2021-06-14 22:51:52 +02:00
|
|
|
|
|
|
|
|
def graph_exclude_columns(self, column_include_num):
|
|
|
|
|
"""
|
|
|
|
|
Excludes all columns muxes unrelated to the target bit being simulated.
|
|
|
|
|
Each mux in mux_inst corresponds to respective column in bitcell array.
|
|
|
|
|
"""
|
|
|
|
|
for i in range(len(self.mux_inst)):
|
|
|
|
|
if i != column_include_num:
|
|
|
|
|
self.graph_inst_exclude.add(self.mux_inst[i])
|