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
|
|
|
|
|
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
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, name="bank_select"):
|
|
|
|
|
design.design.__init__(self, name)
|
|
|
|
|
|
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()
|
|
|
|
|
self.create_modules()
|
|
|
|
|
|
|
|
|
|
def create_layout(self):
|
|
|
|
|
self.calculate_module_offsets()
|
|
|
|
|
self.place_modules()
|
|
|
|
|
self.route_modules()
|
|
|
|
|
|
|
|
|
|
self.DRC_LVS()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_pins(self):
|
|
|
|
|
|
2018-03-05 19:22:51 +01:00
|
|
|
# Number of control lines in the bus
|
2018-08-13 23:47:03 +02:00
|
|
|
self.num_control_lines = 4
|
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-08-19 09:00:42 +02:00
|
|
|
self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"]
|
2018-03-05 19:22:51 +01:00
|
|
|
# These will be outputs of the gaters if this is multibank
|
|
|
|
|
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")
|
|
|
|
|
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 """
|
|
|
|
|
# 1x Inverter
|
|
|
|
|
self.inv = pinv()
|
|
|
|
|
self.add_mod(self.inv)
|
|
|
|
|
|
|
|
|
|
# 4x Inverter
|
|
|
|
|
self.inv4x = pinv(4)
|
|
|
|
|
self.add_mod(self.inv4x)
|
|
|
|
|
|
|
|
|
|
self.nor2 = pnor2()
|
|
|
|
|
self.add_mod(self.nor2)
|
|
|
|
|
|
|
|
|
|
self.nand2 = pnand2()
|
|
|
|
|
self.add_mod(self.nand2)
|
|
|
|
|
|
|
|
|
|
def calculate_module_offsets(self):
|
|
|
|
|
|
|
|
|
|
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_inv = max(self.xoffset_nand + self.nand2.width, self.xoffset_nor + self.nor2.width)
|
|
|
|
|
self.xoffset_bank_sel_inv = 0
|
|
|
|
|
self.xoffset_inputs = 0
|
|
|
|
|
|
|
|
|
|
self.yoffset_maxpoint = self.num_control_lines * self.inv.height
|
2018-03-05 22:49:22 +01:00
|
|
|
# Include the M1 pitches for the supply rails and spacing
|
|
|
|
|
self.height = self.yoffset_maxpoint + 2*self.m1_pitch
|
|
|
|
|
self.width = self.xoffset_inv + self.inv4x.width
|
2018-03-05 19:22:51 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def create_modules(self):
|
2018-03-05 19:22:51 +01:00
|
|
|
|
|
|
|
|
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
|
2018-08-28 01:42:48 +02:00
|
|
|
mod=self.inv)
|
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
|
|
|
|
|
|
|
|
self.logic_inst.append(self.add_inst(name=name_nor,
|
2018-08-28 01:42:48 +02:00
|
|
|
mod=self.nor2))
|
2018-03-05 19:22:51 +01:00
|
|
|
self.connect_inst([input_name,
|
|
|
|
|
"bank_sel_bar",
|
|
|
|
|
gated_name+"_temp_bar",
|
|
|
|
|
"vdd",
|
|
|
|
|
"gnd"])
|
|
|
|
|
|
|
|
|
|
# the rest are AND (nand2+inv) gates
|
|
|
|
|
else:
|
|
|
|
|
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",
|
|
|
|
|
gated_name+"_temp_bar",
|
|
|
|
|
"vdd",
|
|
|
|
|
"gnd"])
|
|
|
|
|
|
|
|
|
|
# They all get inverters on the output
|
|
|
|
|
self.inv_inst.append(self.add_inst(name=name_inv,
|
2018-08-28 01:42:48 +02:00
|
|
|
mod=self.inv4x))
|
2018-03-05 19:22:51 +01:00
|
|
|
self.connect_inst([gated_name+"_temp_bar",
|
|
|
|
|
gated_name,
|
|
|
|
|
"vdd",
|
|
|
|
|
"gnd"])
|
|
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def place_modules(self):
|
|
|
|
|
|
|
|
|
|
# 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]
|
|
|
|
|
|
|
|
|
|
y_offset = self.inv.height * i
|
|
|
|
|
if i%2:
|
|
|
|
|
y_offset += self.inv.height
|
|
|
|
|
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
|
2018-08-28 02:25:39 +02:00
|
|
|
inv_inst.place(offset=[self.xoffset_inv, y_offset],
|
|
|
|
|
mirror=mirror)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2018-03-05 19:22:51 +01:00
|
|
|
|
|
|
|
|
def route_modules(self):
|
|
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
self.add_path("metal2", [bank_sel_line_pos, bank_sel_line_end])
|
|
|
|
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
|
|
|
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",
|
2018-03-05 19:22:51 +01:00
|
|
|
layer="metal3",
|
|
|
|
|
start=bank_sel_pin_pos,
|
|
|
|
|
end=bank_sel_pin_end)
|
|
|
|
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
|
|
|
|
offset=bank_sel_pin_end,
|
|
|
|
|
rotate=90)
|
|
|
|
|
|
|
|
|
|
# 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",
|
|
|
|
|
layer="metal2",
|
2018-03-05 22:49:22 +01:00
|
|
|
offset=vector(xoffset_bank_sel_bar, 0),
|
2018-08-14 00:14:52 +02:00
|
|
|
height=self.inv.height)
|
2018-03-05 19:22:51 +01:00
|
|
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
|
|
|
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]
|
|
|
|
|
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
|
|
|
|
|
pre = logic_inst.get_pin("Z").lc()
|
|
|
|
|
out_position = logic_inst.get_pin("Z").rc() + vector(0.5*self.m1_width,0)
|
|
|
|
|
in_position = inv_inst.get_pin("A").lc() + vector(0.5*self.m1_width,0)
|
|
|
|
|
post = inv_inst.get_pin("A").rc()
|
|
|
|
|
self.add_path("metal1", [pre, out_position, in_position, post])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Connect the logic B input to bank_sel/bank_sel_bar
|
|
|
|
|
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1m2.height,0)
|
|
|
|
|
input_pos = vector(xoffset_bank_signal, logic_pos.y)
|
|
|
|
|
self.add_path("metal2",[logic_pos, input_pos])
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=logic_pos,
|
|
|
|
|
rotate=90)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Connect the logic A input to the input pin
|
|
|
|
|
logic_pos = logic_inst.get_pin("A").lc()
|
|
|
|
|
input_pos = vector(0,logic_pos.y)
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=logic_pos,
|
|
|
|
|
rotate=90)
|
|
|
|
|
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=logic_pos,
|
|
|
|
|
rotate=90)
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text=input_name,
|
2018-03-05 19:22:51 +01:00
|
|
|
layer="metal3",
|
|
|
|
|
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)
|
|
|
|
|
supply_offset = supply_pin.ll().scale(0,1)
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
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())
|
|
|
|
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=pin_pos,
|
|
|
|
|
rotate=90)
|
|
|
|
|
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=pin_pos,
|
|
|
|
|
rotate=90)
|
|
|
|
|
self.add_layout_pin_rect_center(text=n,
|
|
|
|
|
layer="metal3",
|
|
|
|
|
offset=pin_pos)
|
2018-03-05 19:22:51 +01:00
|
|
|
|
|
|
|
|
# Add vdd/gnd supply rails
|
|
|
|
|
gnd_pin = inv_inst.get_pin("gnd")
|
|
|
|
|
left_gnd_pos = vector(0, gnd_pin.cy())
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text="gnd",
|
2018-03-05 19:22:51 +01:00
|
|
|
layer="metal1",
|
|
|
|
|
start=left_gnd_pos,
|
|
|
|
|
end=gnd_pin.rc())
|
|
|
|
|
|
|
|
|
|
vdd_pin = inv_inst.get_pin("vdd")
|
|
|
|
|
left_vdd_pos = vector(0, vdd_pin.cy())
|
2018-03-21 21:20:48 +01:00
|
|
|
self.add_layout_pin_segment_center(text="vdd",
|
2018-03-05 19:22:51 +01:00
|
|
|
layer="metal1",
|
|
|
|
|
start=left_vdd_pos,
|
|
|
|
|
end=vdd_pin.rc())
|