2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-06-14 17:43:41 +02:00
|
|
|
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
|
|
|
# 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
|
|
|
#
|
2018-03-05 19:22:51 +01:00
|
|
|
import sys
|
|
|
|
|
from tech import drc, parameter
|
|
|
|
|
import debug
|
|
|
|
|
import design
|
|
|
|
|
import contact
|
|
|
|
|
from pinv import pinv
|
|
|
|
|
from pnand2 import pnand2
|
|
|
|
|
from pnor2 import pnor2
|
|
|
|
|
from vector import vector
|
2019-01-17 01:15:38 +01:00
|
|
|
from sram_factory import factory
|
2018-03-05 19:22:51 +01:00
|
|
|
from globals import OPTS
|
|
|
|
|
|
|
|
|
|
class bank_select(design.design):
|
|
|
|
|
"""Create a bank select signal that is combined with an array of
|
|
|
|
|
NOR+INV gates to gate the control signals in case of multiple
|
|
|
|
|
banks are created in upper level SRAM module
|
|
|
|
|
"""
|
|
|
|
|
|
2018-09-26 23:53:55 +02:00
|
|
|
def __init__(self, name="bank_select", port="rw"):
|
2018-03-05 19:22:51 +01:00
|
|
|
design.design.__init__(self, name)
|
|
|
|
|
|
2018-09-26 23:53:55 +02:00
|
|
|
self.port = port
|
|
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
self.create_netlist()
|
|
|
|
|
if not OPTS.netlist_only:
|
|
|
|
|
self.create_layout()
|
|
|
|
|
|
|
|
|
|
def create_netlist(self):
|
|
|
|
|
self.add_pins()
|
|
|
|
|
self.add_modules()
|
2018-11-14 01:05:22 +01:00
|
|
|
self.create_instances()
|
2018-08-28 01:42:48 +02:00
|
|
|
|
|
|
|
|
def create_layout(self):
|
|
|
|
|
self.calculate_module_offsets()
|
2018-11-14 01:05:22 +01:00
|
|
|
self.place_instances()
|
|
|
|
|
self.route_instances()
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2019-12-20 01:19:21 +01:00
|
|
|
self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width
|
|
|
|
|
self.width = max([x.rx() for x in self.inv_inst])
|
|
|
|
|
|
2019-05-28 01:32:38 +02:00
|
|
|
self.add_boundary()
|
2018-08-28 01:42:48 +02:00
|
|
|
self.DRC_LVS()
|
|
|
|
|
|
|
|
|
|
def add_pins(self):
|
|
|
|
|
|
2018-03-05 19:22:51 +01:00
|
|
|
# Number of control lines in the bus
|
2018-09-26 23:53:55 +02:00
|
|
|
if self.port == "rw":
|
|
|
|
|
self.num_control_lines = 4
|
|
|
|
|
else:
|
|
|
|
|
self.num_control_lines = 3
|
2018-03-05 19:22:51 +01:00
|
|
|
# The order of the control signals on the control bus:
|
2018-08-28 01:42:48 +02:00
|
|
|
# FIXME: Update for multiport (these names are not right)
|
2018-09-26 23:53:55 +02:00
|
|
|
self.input_control_signals = ["clk_buf", "clk_buf_bar"]
|
|
|
|
|
if (self.port == "rw") or (self.port == "w"):
|
|
|
|
|
self.input_control_signals.append("w_en")
|
|
|
|
|
if (self.port == "rw") or (self.port == "r"):
|
|
|
|
|
self.input_control_signals.append("s_en")
|
2018-03-05 19:22:51 +01:00
|
|
|
# These will be outputs of the gaters if this is multibank
|
2020-04-14 21:15:56 +02:00
|
|
|
self.control_signals = ["gated_" + str for str in self.input_control_signals]
|
2018-03-17 01:46:29 +01:00
|
|
|
|
|
|
|
|
self.add_pin_list(self.input_control_signals, "INPUT")
|
2018-03-20 00:23:13 +01:00
|
|
|
self.add_pin("bank_sel")
|
2018-03-17 01:46:29 +01:00
|
|
|
self.add_pin_list(self.control_signals, "OUTPUT")
|
2020-04-14 21:15:56 +02:00
|
|
|
self.add_pin("vdd", "POWER")
|
|
|
|
|
self.add_pin("gnd", "GROUND")
|
2018-03-05 19:22:51 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def add_modules(self):
|
2018-03-05 19:22:51 +01:00
|
|
|
""" Create modules for later instantiation """
|
2020-04-14 21:15:56 +02:00
|
|
|
self.dff = factory.create(module_type="dff")
|
|
|
|
|
height = self.dff.height + drc("poly_to_active")
|
2018-09-27 11:01:32 +02:00
|
|
|
|
2018-03-05 19:22:51 +01:00
|
|
|
# 1x Inverter
|
2019-01-17 01:15:38 +01:00
|
|
|
self.inv_sel = factory.create(module_type="pinv", height=height)
|
2018-09-27 11:01:32 +02:00
|
|
|
self.add_mod(self.inv_sel)
|
2018-03-05 19:22:51 +01:00
|
|
|
|
|
|
|
|
# 4x Inverter
|
2019-01-17 01:15:38 +01:00
|
|
|
self.inv4x = factory.create(module_type="pinv", height=height, size=4)
|
2018-03-05 19:22:51 +01:00
|
|
|
self.add_mod(self.inv4x)
|
|
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
self.nor2 = factory.create(module_type="pnor2", height=height)
|
2018-03-05 19:22:51 +01:00
|
|
|
self.add_mod(self.nor2)
|
2018-09-27 11:01:32 +02:00
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4)
|
2018-09-27 11:01:32 +02:00
|
|
|
self.add_mod(self.inv4x_nor)
|
2018-03-05 19:22:51 +01:00
|
|
|
|
2019-07-04 00:12:22 +02:00
|
|
|
self.nand2 = factory.create(module_type="pnand2", height=height)
|
2018-03-05 19:22:51 +01:00
|
|
|
self.add_mod(self.nand2)
|
|
|
|
|
|
|
|
|
|
def calculate_module_offsets(self):
|
|
|
|
|
|
2020-04-14 21:15:56 +02:00
|
|
|
self.xoffset_nand = self.inv4x.width + 2 * self.m2_pitch + drc("pwell_to_nwell")
|
|
|
|
|
self.xoffset_nor = self.inv4x.width + 2 * self.m2_pitch + drc("pwell_to_nwell")
|
|
|
|
|
self.xoffset_bank_sel_inv = 0
|
2018-03-05 19:22:51 +01:00
|
|
|
self.xoffset_inputs = 0
|
2019-01-17 01:15:38 +01:00
|
|
|
self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height
|
2018-03-05 19:22:51 +01:00
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
def create_instances(self):
|
2018-03-05 19:22:51 +01:00
|
|
|
|
2020-04-14 21:15:56 +02:00
|
|
|
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
|
2018-09-27 11:01:32 +02:00
|
|
|
mod=self.inv_sel)
|
2018-03-05 19:22:51 +01:00
|
|
|
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
|
|
|
|
|
|
|
|
|
|
self.logic_inst = []
|
|
|
|
|
self.inv_inst = []
|
|
|
|
|
for i in range(self.num_control_lines):
|
|
|
|
|
input_name = self.input_control_signals[i]
|
|
|
|
|
gated_name = self.control_signals[i]
|
|
|
|
|
name_nand = "nand_{}".format(input_name)
|
|
|
|
|
name_nor = "nor_{}".format(input_name)
|
|
|
|
|
name_inv = "inv_{}".format(input_name)
|
|
|
|
|
|
|
|
|
|
# These require OR (nor2+inv) gates since they are active low.
|
|
|
|
|
# (writes occur on clk low)
|
2018-08-13 23:47:03 +02:00
|
|
|
if input_name in ("clk_buf"):
|
2018-03-05 19:22:51 +01:00
|
|
|
|
2020-04-14 21:15:56 +02:00
|
|
|
self.logic_inst.append(self.add_inst(name=name_nor,
|
|
|
|
|
mod=self.nor2))
|
2018-03-05 19:22:51 +01:00
|
|
|
self.connect_inst([input_name,
|
|
|
|
|
"bank_sel_bar",
|
2020-04-14 21:15:56 +02:00
|
|
|
gated_name + "_temp_bar",
|
2018-03-05 19:22:51 +01:00
|
|
|
"vdd",
|
|
|
|
|
"gnd"])
|
|
|
|
|
|
2018-09-27 11:01:32 +02:00
|
|
|
# They all get inverters on the output
|
2020-04-14 21:15:56 +02:00
|
|
|
self.inv_inst.append(self.add_inst(name=name_inv,
|
2018-09-27 11:01:32 +02:00
|
|
|
mod=self.inv4x_nor))
|
2020-04-14 21:15:56 +02:00
|
|
|
self.connect_inst([gated_name + "_temp_bar",
|
2018-09-27 11:01:32 +02:00
|
|
|
gated_name,
|
|
|
|
|
"vdd",
|
|
|
|
|
"gnd"])
|
|
|
|
|
|
2018-03-05 19:22:51 +01:00
|
|
|
# the rest are AND (nand2+inv) gates
|
|
|
|
|
else:
|
2020-04-14 21:15:56 +02:00
|
|
|
self.logic_inst.append(self.add_inst(name=name_nand,
|
2018-08-28 01:42:48 +02:00
|
|
|
mod=self.nand2))
|
2018-03-05 19:22:51 +01:00
|
|
|
self.connect_inst([input_name,
|
|
|
|
|
"bank_sel",
|
2020-04-14 21:15:56 +02:00
|
|
|
gated_name + "_temp_bar",
|
2018-03-05 19:22:51 +01:00
|
|
|
"vdd",
|
|
|
|
|
"gnd"])
|
|
|
|
|
|
2018-09-27 11:01:32 +02:00
|
|
|
# They all get inverters on the output
|
2020-04-14 21:15:56 +02:00
|
|
|
self.inv_inst.append(self.add_inst(name=name_inv,
|
2018-09-27 11:01:32 +02:00
|
|
|
mod=self.inv4x))
|
2020-04-14 21:15:56 +02:00
|
|
|
self.connect_inst([gated_name + "_temp_bar",
|
2018-09-27 11:01:32 +02:00
|
|
|
gated_name,
|
|
|
|
|
"vdd",
|
|
|
|
|
"gnd"])
|
2018-03-05 19:22:51 +01:00
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
def place_instances(self):
|
2018-08-28 01:42:48 +02:00
|
|
|
|
|
|
|
|
# bank select inverter
|
|
|
|
|
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
|
|
|
|
|
|
|
|
|
|
# bank select inverter (must be made unique if more than one OR)
|
2018-08-28 02:25:39 +02:00
|
|
|
self.bank_sel_inv.place(vector(self.xoffset_bank_sel_inv, 0))
|
2018-08-28 01:42:48 +02:00
|
|
|
|
|
|
|
|
for i in range(self.num_control_lines):
|
2018-08-28 02:25:39 +02:00
|
|
|
|
|
|
|
|
logic_inst = self.logic_inst[i]
|
|
|
|
|
inv_inst = self.inv_inst[i]
|
|
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
input_name = self.input_control_signals[i]
|
|
|
|
|
|
2018-09-27 11:01:32 +02:00
|
|
|
if i == 0:
|
|
|
|
|
y_offset = 0
|
|
|
|
|
else:
|
2020-04-14 21:15:56 +02:00
|
|
|
y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1)
|
2018-09-27 11:01:32 +02:00
|
|
|
|
2020-04-14 21:15:56 +02:00
|
|
|
if i % 2:
|
2019-01-17 01:15:38 +01:00
|
|
|
y_offset += self.inv4x.height
|
2018-08-28 01:42:48 +02:00
|
|
|
mirror = "MX"
|
|
|
|
|
else:
|
|
|
|
|
mirror = ""
|
|
|
|
|
|
|
|
|
|
# These require OR (nor2+inv) gates since they are active low.
|
|
|
|
|
# (writes occur on clk low)
|
|
|
|
|
if input_name in ("clk_buf"):
|
|
|
|
|
|
2018-08-28 02:25:39 +02:00
|
|
|
logic_inst.place(offset=[self.xoffset_nor, y_offset],
|
|
|
|
|
mirror=mirror)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
|
|
|
|
# the rest are AND (nand2+inv) gates
|
|
|
|
|
else:
|
2018-08-28 02:25:39 +02:00
|
|
|
logic_inst.place(offset=[self.xoffset_nand, y_offset],
|
|
|
|
|
mirror=mirror)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
|
|
|
|
# They all get inverters on the output
|
2019-12-20 01:19:21 +01:00
|
|
|
inv_inst.place(offset=[logic_inst.rx(), y_offset],
|
2018-08-28 02:25:39 +02:00
|
|
|
mirror=mirror)
|
2018-03-05 19:22:51 +01:00
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
def route_instances(self):
|
2018-03-05 19:22:51 +01:00
|
|
|
|
|
|
|
|
# bank_sel is vertical wire
|
|
|
|
|
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
|
|
|
|
|
xoffset_bank_sel = bank_sel_inv_pin.lx()
|
2018-03-05 22:49:22 +01:00
|
|
|
bank_sel_line_pos = vector(xoffset_bank_sel, 0)
|
2018-03-05 19:22:51 +01:00
|
|
|
bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint)
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_path("m2", [bank_sel_line_pos, bank_sel_line_end])
|
|
|
|
|
self.add_via_center(layers=self.m1_stack,
|
2018-03-05 19:22:51 +01:00
|
|
|
offset=bank_sel_inv_pin.lc())
|
|
|
|
|
|
|
|
|
|
# Route the pin to the left edge as well
|
2018-03-05 22:49:22 +01:00
|
|
|
bank_sel_pin_pos=vector(0, 0)
|
2018-03-05 19:22:51 +01:00
|
|
|
bank_sel_pin_end=vector(bank_sel_line_pos.x, bank_sel_pin_pos.y)
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text="bank_sel",
|
2019-12-17 20:03:36 +01:00
|
|
|
layer="m3",
|
2018-03-05 19:22:51 +01:00
|
|
|
start=bank_sel_pin_pos,
|
|
|
|
|
end=bank_sel_pin_end)
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_via_center(layers=self.m2_stack,
|
2018-03-05 19:22:51 +01:00
|
|
|
offset=bank_sel_pin_end,
|
2020-04-14 21:15:56 +02:00
|
|
|
directions=("H", "H"))
|
2018-03-05 19:22:51 +01:00
|
|
|
|
|
|
|
|
# bank_sel_bar is vertical wire
|
|
|
|
|
bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z")
|
|
|
|
|
xoffset_bank_sel_bar = bank_sel_bar_pin.rx()
|
|
|
|
|
self.add_label_pin(text="bank_sel_bar",
|
2020-04-14 21:15:56 +02:00
|
|
|
layer="m2",
|
|
|
|
|
offset=vector(xoffset_bank_sel_bar, 0),
|
2019-01-17 01:15:38 +01:00
|
|
|
height=self.inv4x.height)
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_via_center(layers=self.m1_stack,
|
2018-03-05 19:22:51 +01:00
|
|
|
offset=bank_sel_bar_pin.rc())
|
|
|
|
|
|
|
|
|
|
for i in range(self.num_control_lines):
|
|
|
|
|
|
|
|
|
|
logic_inst = self.logic_inst[i]
|
|
|
|
|
inv_inst = self.inv_inst[i]
|
|
|
|
|
|
|
|
|
|
input_name = self.input_control_signals[i]
|
2020-04-14 21:15:56 +02:00
|
|
|
gated_name = self.control_signals[i]
|
2018-08-13 23:47:03 +02:00
|
|
|
if input_name in ("clk_buf"):
|
2018-03-05 19:22:51 +01:00
|
|
|
xoffset_bank_signal = xoffset_bank_sel_bar
|
|
|
|
|
else:
|
|
|
|
|
xoffset_bank_signal = xoffset_bank_sel
|
|
|
|
|
|
|
|
|
|
# Connect the logic output to inverter input
|
2020-04-14 21:15:56 +02:00
|
|
|
out_pin = logic_inst.get_pin("Z")
|
|
|
|
|
out_pos = out_pin.rc()
|
|
|
|
|
in_pin = inv_inst.get_pin("A")
|
|
|
|
|
in_pos = in_pin.lc()
|
|
|
|
|
mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
|
|
|
|
|
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
|
|
|
|
|
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
|
2018-03-05 19:22:51 +01:00
|
|
|
|
2020-04-14 21:15:56 +02:00
|
|
|
# Connect the logic B input to bank_sel / bank_sel_bar
|
|
|
|
|
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5 * contact.m1_via.height, 0)
|
2018-03-05 19:22:51 +01:00
|
|
|
input_pos = vector(xoffset_bank_signal, logic_pos.y)
|
2020-04-14 21:15:56 +02:00
|
|
|
self.add_path("m2", [logic_pos, input_pos])
|
2019-12-13 23:13:41 +01:00
|
|
|
self.add_via_center(layers=self.m1_stack,
|
2018-03-05 19:22:51 +01:00
|
|
|
offset=logic_pos,
|
2020-04-14 21:15:56 +02:00
|
|
|
directions=("H", "H"))
|
2018-03-05 19:22:51 +01:00
|
|
|
|
|
|
|
|
# Connect the logic A input to the input pin
|
|
|
|
|
logic_pos = logic_inst.get_pin("A").lc()
|
2020-04-14 21:15:56 +02:00
|
|
|
input_pos = vector(0, logic_pos.y)
|
2019-12-13 23:13:41 +01:00
|
|
|
self.add_via_center(layers=self.m1_stack,
|
2018-03-05 19:22:51 +01:00
|
|
|
offset=logic_pos,
|
2020-04-14 21:15:56 +02:00
|
|
|
directions=("H", "H"))
|
2019-12-13 23:13:41 +01:00
|
|
|
self.add_via_center(layers=self.m2_stack,
|
2018-03-05 19:22:51 +01:00
|
|
|
offset=logic_pos,
|
2020-04-14 21:15:56 +02:00
|
|
|
directions=("H", "H"))
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text=input_name,
|
2019-12-17 20:03:36 +01:00
|
|
|
layer="m3",
|
2018-03-05 19:22:51 +01:00
|
|
|
start=input_pos,
|
|
|
|
|
end=logic_pos)
|
|
|
|
|
|
|
|
|
|
# Add output pins
|
|
|
|
|
out_pin = inv_inst.get_pin("Z")
|
|
|
|
|
self.add_layout_pin(text=gated_name,
|
|
|
|
|
layer=out_pin.layer,
|
|
|
|
|
offset=out_pin.ll(),
|
|
|
|
|
width=inv_inst.rx() - out_pin.lx(),
|
|
|
|
|
height=out_pin.height())
|
2018-04-12 01:55:09 +02:00
|
|
|
|
|
|
|
|
# Find the x offsets for where the vias/pins should be placed
|
|
|
|
|
a_xoffset = self.logic_inst[0].lx()
|
|
|
|
|
b_xoffset = self.inv_inst[0].lx()
|
|
|
|
|
for num in range(self.num_control_lines):
|
|
|
|
|
# Route both supplies
|
|
|
|
|
for n in ["vdd", "gnd"]:
|
|
|
|
|
supply_pin = self.inv_inst[num].get_pin(n)
|
2020-04-14 21:15:56 +02:00
|
|
|
supply_offset = supply_pin.ll().scale(0, 1)
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_rect(layer="m1",
|
2018-04-12 01:55:09 +02:00
|
|
|
offset=supply_offset,
|
|
|
|
|
width=self.width)
|
|
|
|
|
|
|
|
|
|
# Add pins in two locations
|
|
|
|
|
for xoffset in [a_xoffset, b_xoffset]:
|
|
|
|
|
pin_pos = vector(xoffset, supply_pin.cy())
|
2019-12-13 23:13:41 +01:00
|
|
|
self.add_via_center(layers=self.m1_stack,
|
2018-04-12 01:55:09 +02:00
|
|
|
offset=pin_pos,
|
2020-04-14 21:15:56 +02:00
|
|
|
directions=("H", "H"))
|
2019-12-13 23:13:41 +01:00
|
|
|
self.add_via_center(layers=self.m2_stack,
|
2018-04-12 01:55:09 +02:00
|
|
|
offset=pin_pos,
|
2020-04-14 21:15:56 +02:00
|
|
|
directions=("H", "H"))
|
2018-04-12 01:55:09 +02:00
|
|
|
self.add_layout_pin_rect_center(text=n,
|
2019-12-17 20:03:36 +01:00
|
|
|
layer="m3",
|
2018-04-12 01:55:09 +02:00
|
|
|
offset=pin_pos)
|
2018-03-05 19:22:51 +01:00
|
|
|
|
|
|
|
|
# Add vdd/gnd supply rails
|
2019-12-20 01:19:21 +01:00
|
|
|
gnd_pin = self.inv_inst[num].get_pin("gnd")
|
2018-03-05 19:22:51 +01:00
|
|
|
left_gnd_pos = vector(0, gnd_pin.cy())
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text="gnd",
|
2019-12-17 20:03:36 +01:00
|
|
|
layer="m1",
|
2018-03-05 19:22:51 +01:00
|
|
|
start=left_gnd_pos,
|
|
|
|
|
end=gnd_pin.rc())
|
|
|
|
|
|
2019-12-20 01:19:21 +01:00
|
|
|
vdd_pin = self.inv_inst[num].get_pin("vdd")
|
2018-03-05 19:22:51 +01:00
|
|
|
left_vdd_pos = vector(0, vdd_pin.cy())
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text="vdd",
|
2019-12-17 20:03:36 +01:00
|
|
|
layer="m1",
|
2018-03-05 19:22:51 +01:00
|
|
|
start=left_vdd_pos,
|
|
|
|
|
end=vdd_pin.rc())
|