mirror of https://github.com/VLSIDA/OpenRAM.git
697 lines
31 KiB
Python
697 lines
31 KiB
Python
from math import log
|
|
import design
|
|
from tech import drc, parameter
|
|
import debug
|
|
from ms_flop_array import ms_flop_array
|
|
from wordline_driver import wordline_driver
|
|
from contact import contact
|
|
from pinv import pinv
|
|
from nand_2 import nand_2
|
|
from nand_3 import nand_3
|
|
from nor_2 import nor_2
|
|
from replica_bitline import replica_bitline
|
|
import math
|
|
from vector import vector
|
|
from globals import OPTS
|
|
|
|
class control_logic(design.design):
|
|
"""
|
|
Dynamically generated Control logic for the total SRAM circuit.
|
|
"""
|
|
|
|
def __init__(self, num_rows):
|
|
""" Constructor """
|
|
design.design.__init__(self, "control_logic")
|
|
debug.info(1, "Creating %s" % self.name)
|
|
|
|
self.num_rows = num_rows
|
|
self.create_layout()
|
|
self.DRC_LVS()
|
|
|
|
def create_layout(self):
|
|
""" Create layout and route between modules """
|
|
self.create_modules()
|
|
self.setup_layout_offsets()
|
|
self.add_modules()
|
|
self.add_routing()
|
|
self.add_pin_labels()
|
|
|
|
def create_modules(self):
|
|
""" add all the required modules """
|
|
c = reload(__import__(OPTS.config.ms_flop))
|
|
self.mod_ms_flop = getattr(c, OPTS.config.ms_flop)
|
|
self.ms_flop = self.mod_ms_flop("ms_flop")
|
|
self.add_mod(self.ms_flop)
|
|
self.inv = pinv(name="pinv",
|
|
nmos_width=drc["minwidth_tx"],
|
|
beta=parameter["pinv_beta"])
|
|
|
|
self.add_mod(self.inv)
|
|
self.nand2 = nand_2(name="nand2",
|
|
nmos_width=2 * drc["minwidth_tx"])
|
|
self.add_mod(self.nand2)
|
|
self.NAND3 = nand_3(name="NAND3",
|
|
nmos_width=3 * drc["minwidth_tx"])
|
|
self.add_mod(self.NAND3)
|
|
|
|
# Special gates: 4x Inverter
|
|
self.inv4 = pinv(name="pinv4",
|
|
nmos_width=4 * drc["minwidth_tx"],
|
|
beta=parameter["pinv_beta"])
|
|
self.add_mod(self.inv4)
|
|
|
|
self.nor2 = nor_2(name="nor2",
|
|
nmos_width=drc["minwidth_tx"])
|
|
self.add_mod(self.nor2)
|
|
|
|
self.msf_control = ms_flop_array(name="msf_control",
|
|
array_type="data_in",
|
|
columns=3,
|
|
word_size=3)
|
|
self.add_mod(self.msf_control)
|
|
|
|
self.replica_bitline = replica_bitline("replica_bitline",
|
|
int(math.ceil(self.num_rows / 10.0)))
|
|
self.add_mod(self.replica_bitline)
|
|
|
|
def add_pin_labels(self):
|
|
""" Add pins and labels after everything is done """
|
|
input_lst =["CSb","WEb","OEb"]
|
|
output_lst = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar"]
|
|
clk =["clk"]
|
|
rails = ["vdd", "gnd"]
|
|
pin_lst = input_lst + output_lst + clk + rails
|
|
for pin in pin_lst:
|
|
self.add_pin(pin)
|
|
|
|
# add label of input, output and clk in metal3 layer
|
|
input_lst =["CSb","WEb","OEb"]
|
|
output_lst = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar"]
|
|
for pin in input_lst + output_lst + ["clk"]:
|
|
self.add_label(text=pin,
|
|
layer="metal3",
|
|
offset=getattr(self, pin+"_position"))
|
|
# add label of vdd and gnd manually cause non-uniformed names and layers
|
|
self.add_label(text="vdd",
|
|
layer="metal1",
|
|
offset=self.vdd1_position)
|
|
self.add_label(text="vdd",
|
|
layer="metal2",
|
|
offset=self.vdd2_position)
|
|
self.add_label(text="gnd",
|
|
layer="metal2",
|
|
offset=self.gnd_position)
|
|
|
|
def setup_layout_offsets(self):
|
|
""" Setup layout offsets, determine the size of the busses etc """
|
|
# This isn't for instantiating, but we use it to get the dimensions
|
|
m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
|
|
|
|
# Vertical metal rail gap definition
|
|
self.metal2_extend_contact = (m1m2_via.second_layer_height - m1m2_via.contact_width) / 2
|
|
self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"]
|
|
self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"]
|
|
self.via_shift = (m1m2_via.second_layer_width - m1m2_via.first_layer_width) / 2
|
|
|
|
# used to shift contact when connecting to NAND3 C pin down
|
|
self.contact_shift = (m1m2_via.first_layer_width - m1m2_via.contact_width) / 2
|
|
|
|
# Common parameters for rails
|
|
self.rail_width = drc["minwidth_metal2"]
|
|
self.rail_gap = 2 * drc["metal2_to_metal2"]
|
|
self.rail_offset_gap = self.rail_width + self.rail_gap
|
|
|
|
# First RAIL Parameters
|
|
self.num_rails_1 = 6
|
|
self.overall_rail_1_gap = (self.num_rails_1 + 1) * self.rail_offset_gap
|
|
self.rail_1_x_offsets = []
|
|
|
|
# Second RAIL Parameters
|
|
self.num_rails_2 = 4
|
|
self.overall_rail_2_gap = (self.num_rails_2 + 1) * self.rail_offset_gap
|
|
self.rail_2_x_offsets = []
|
|
|
|
# GAP between main control and REPLICA BITLINE
|
|
self.replica_bitline_gap = self.rail_offset_gap * 2
|
|
|
|
self.output_port_gap = 3 * drc["minwidth_metal3"]
|
|
self.logic_height = max(self.replica_bitline.width, 4 * self.inv.height)
|
|
|
|
def add_modules(self):
|
|
""" Place all the modules """
|
|
self.add_msf_control()
|
|
self.set_msf_control_pins()
|
|
self.add_1st_row(self.output_port_gap)
|
|
self.add_2nd_row(self.output_port_gap + 2 * self.inv.height)
|
|
|
|
# Height and width
|
|
self.height = self.logic_height + self.output_port_gap
|
|
self.width = self.offset_replica_bitline[0] + self.replica_bitline.height
|
|
|
|
def add_routing(self):
|
|
""" Routing between modules """
|
|
self.add_msf_control_routing()
|
|
self.add_1st_row_routing()
|
|
self.add_2nd_row_routing()
|
|
self.add_vdd_routing()
|
|
self.add_gnd_routing()
|
|
self.add_input_routing()
|
|
self.add_output_routing()
|
|
|
|
def add_msf_control(self):
|
|
""" ADD ARRAY OF MS_FLOP"""
|
|
self.offset_msf_control = vector(0, self.logic_height + self.output_port_gap)
|
|
self.add_inst(name="msf_control",
|
|
mod=self.msf_control,
|
|
offset=self.offset_msf_control,
|
|
mirror="R270")
|
|
# don't change this order. This pins are meant for internal connection of msf array inside the control logic.
|
|
# These pins are connecting the msf_array inside of control_logic.
|
|
temp = ["CSb", "WEb", "OEb", "CS_bar", "CS", "WE_bar",
|
|
"WE", "OE_bar", "OE", "clk", "vdd", "gnd"]
|
|
self.connect_inst(temp)
|
|
|
|
def set_msf_control_pins(self):
|
|
# msf_control inputs
|
|
correct = vector(0, 0.5 * drc["minwidth_metal2"])
|
|
def translate_inputs(pt1,pt2):
|
|
return pt1 + pt2.rotate().scale(1,-1) - correct
|
|
|
|
# msf_control outputs
|
|
def translate_outputs(pt1,pt2):
|
|
return pt1 - correct + vector(self.msf_control.height,- pt2[0])
|
|
|
|
# set CSS WE OE signal groups(in, out, bar)
|
|
pt1 = self.offset_msf_control
|
|
pin_set = ["CSb","WEb","OEb"]
|
|
pt2in = self.msf_control.din_positions[0:len(pin_set)]
|
|
pt2out = self.msf_control.dout_positions[0:len(pin_set)]
|
|
pt2bar = self.msf_control.dout_bar_positions[0:len(pin_set)]
|
|
for i in range(len(pin_set)):
|
|
value = translate_inputs(pt1,pt2in[i])
|
|
setattr(self,"msf_control_"+pin_set[i]+"_position",value)
|
|
value = translate_outputs(pt1,pt2out[i])
|
|
setattr(self,"msf_control_"+pin_set[i][0:2]+"_bar_position",value)
|
|
value = translate_outputs(pt1,pt2bar[i])
|
|
setattr(self,"msf_control_"+pin_set[i][0:2]+"_position",value)
|
|
|
|
# clk , vdd
|
|
base = self.offset_msf_control - vector(0.5 * drc["minwidth_metal2"], 0)
|
|
msf_clk = self.msf_control.clk_positions[0].rotate().scale(1,-1)
|
|
self.msf_control_clk_position = base + msf_clk
|
|
msf_vdd = self.msf_control.vdd_positions[0].rotate().scale(1,-1)
|
|
self.msf_control_vdd_position = base + msf_vdd
|
|
|
|
# gnd
|
|
self.msf_control_gnd_positions = []
|
|
for gnd_offset in self.msf_control.gnd_positions:
|
|
offset = self.offset_msf_control + vector(self.msf_control.height,
|
|
- gnd_offset[0])
|
|
self.msf_control_gnd_positions.append(offset - correct)
|
|
|
|
def add_1st_row(self,y_off):
|
|
# inv1 with clk as gate input.
|
|
msf_control_rotate_x = self.offset_msf_control[0] + self.msf_control.height
|
|
self.offset_inv1 = vector(msf_control_rotate_x - self.inv4.width, y_off)
|
|
self.add_inst(name="clk_inverter",
|
|
mod=self.inv4,
|
|
offset=self.offset_inv1)
|
|
self.connect_inst(["clk", "clk_bar", "vdd", "gnd"])
|
|
# set pin offset as attr
|
|
self.inv1_A_position = self.offset_inv1 + self.inv4.A_position.scale(0,1)
|
|
base = self.offset_inv1 + vector(self.inv4.width, 0)
|
|
for pin in ["Z_position", "vdd_position", "gnd_position"]:
|
|
setattr(self, "inv1_"+pin, base + getattr(self.inv4, pin).scale(0,1))
|
|
|
|
# nor2
|
|
self.offset_nor2 = vector(self.nor2.width + 2 * drc["minwidth_metal3"],
|
|
y_off)
|
|
self.add_inst(name="nor2",
|
|
mod=self.nor2,
|
|
offset=self.offset_nor2,
|
|
mirror="MY")
|
|
self.connect_inst(["clk", "OE_bar", "tri_en", "vdd", "gnd"])
|
|
self.set_nand2_nor2_pin("nor2",[-1,1])
|
|
|
|
x_off = msf_control_rotate_x + self.overall_rail_1_gap
|
|
self.nand_array_position = vector(x_off, y_off)
|
|
|
|
# nand2_1 input: OE, clk_bar output: tri_en_bar
|
|
self.offset_nand2 = self.nand_array_position
|
|
self.add_inst(name="nand2_tri_en",
|
|
mod=self.nand2,
|
|
offset=self.offset_nand2)
|
|
self.connect_inst(["OE", "clk_bar", "tri_en_bar", "vdd", "gnd"])
|
|
# set pin offset as attr
|
|
self.set_nand2_nor2_pin("nand2",[1,1])
|
|
|
|
# REPLICA BITLINE
|
|
base_x = self.nand_array_position[0] + self.NAND3.width + 3 * self.inv.width
|
|
total_rail_gap = self.rail_offset_gap + self.overall_rail_2_gap
|
|
x_off = base_x + total_rail_gap + self.replica_bitline_gap
|
|
self.offset_replica_bitline = vector(x_off, y_off)
|
|
self.add_inst(name="replica_bitline",
|
|
mod=self.replica_bitline,
|
|
offset=self.offset_replica_bitline,
|
|
mirror="MX",
|
|
rotate=90)
|
|
self.connect_inst(["rblk", "pre_s_en", "vdd", "gnd"])
|
|
|
|
# BUFFER INVERTERS FOR S_EN
|
|
# inv_4 input: input: pre_s_en_bar, output: s_en
|
|
self.offset_inv4 = vector(base_x - 2 * self.inv.width, y_off)
|
|
self.add_inst(name="inv_s_en1",
|
|
mod=self.inv,
|
|
offset=self.offset_inv4,
|
|
mirror="MY")
|
|
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
|
|
self.set_inv2345_pins(inv_name="inv4", inv_scale=[-1, 1])
|
|
|
|
# inv_5 input: pre_s_en, output: pre_s_en_bar
|
|
self.offset_inv5 = vector(base_x - self.inv.width, y_off)
|
|
self.add_inst(name="inv_s_en2",
|
|
mod=self.inv,
|
|
offset=self.offset_inv5,
|
|
mirror="MY")
|
|
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
|
|
self.set_inv2345_pins(inv_name="inv5", inv_scale=[-1, 1])
|
|
|
|
# set pin offset as attr
|
|
pin_offset = self.replica_bitline.en_input_offset.rotate()
|
|
self.replica_en_offset = self.offset_replica_bitline + pin_offset
|
|
pin_offset = self.replica_bitline.out_offset.rotate()
|
|
self.replica_out_offset = self.offset_replica_bitline + pin_offset
|
|
|
|
def add_2nd_row(self, y_off):
|
|
# Nand3_1 input: OE, clk_bar,CS output: rblk_bar
|
|
self.offset_nand3_1 = vector(self.nand_array_position[0], y_off)
|
|
self.add_inst(name="NAND3_rblk_bar",
|
|
mod=self.NAND3,
|
|
offset=self.offset_nand3_1,
|
|
mirror="MX")
|
|
self.connect_inst(["clk_bar", "OE", "CS", "rblk_bar", "vdd", "gnd"])
|
|
# set pin offset as attr
|
|
self.set_Nand3_pins(nand_name = "nand3_1",nand_scale = [0,-1])
|
|
|
|
# Nand3_2 input: WE, clk_bar,CS output: w_en_bar
|
|
self.offset_nand3_2 = vector(self.nand_array_position[0], y_off)
|
|
self.add_inst(name="NAND3_w_en_bar",
|
|
mod=self.NAND3,
|
|
offset=self.offset_nand3_2,
|
|
mirror="RO")
|
|
self.connect_inst(["clk_bar", "WE", "CS", "w_en_bar", "vdd", "gnd"])
|
|
# set pin offset as attr
|
|
self.set_Nand3_pins(nand_name = "nand3_2",nand_scale = [0,1])
|
|
|
|
# connect nand2 and nand3 to inv
|
|
nand3_to_inv_connection_height = self.NAND3.Z_position[1] - self.inv.A_position[1] + drc["minwidth_metal1"]
|
|
self.add_rect(layer="metal1",
|
|
offset=self.nand3_1_Z_position,
|
|
width=drc["minwidth_metal1"],
|
|
height=nand3_to_inv_connection_height)
|
|
self.add_rect(layer="metal1",
|
|
offset=self.nand3_2_Z_position + vector(0,drc["minwidth_metal1"]),
|
|
width=drc["minwidth_metal1"],
|
|
height=-nand3_to_inv_connection_height)
|
|
|
|
# inv_2 input: rblk_bar, output: rblk
|
|
x_off = self.nand_array_position[0] + self.NAND3.width
|
|
self.offset_inv2 = vector(x_off, y_off)
|
|
self.add_inst(name="inv_rblk",
|
|
mod=self.inv,
|
|
offset=self.offset_inv2,
|
|
mirror="MX")
|
|
self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"])
|
|
# set pin offset as attr
|
|
self.set_inv2345_pins(inv_name="inv2", inv_scale=[1,-1])
|
|
|
|
# inv_3 input: w_en_bar, output: pre_w_en
|
|
self.offset_inv3 = self.offset_inv2
|
|
self.add_inst(name="inv_w_en",
|
|
mod=self.inv,
|
|
offset=self.offset_inv3,
|
|
mirror="RO")
|
|
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
|
|
# set pin offset as attr
|
|
self.set_inv2345_pins(inv_name="inv3", inv_scale=[1, 1])
|
|
|
|
# BUFFER INVERTERS FOR W_EN
|
|
x_off = self.nand_array_position[0] + self.NAND3.width + self.inv.width
|
|
self.offset_inv6 = vector(x_off, y_off)
|
|
self.add_inst(name="inv_w_en1",
|
|
mod=self.inv,
|
|
offset=self.offset_inv6,
|
|
mirror="RO")
|
|
self.connect_inst(["pre_w_en", "pre_w_en1", "vdd", "gnd"])
|
|
|
|
x_off = self.nand_array_position[0] + self.NAND3.width + 2 * self.inv.width
|
|
self.offset_inv7 = [x_off, y_off]
|
|
self.add_inst(name="inv_w_en2",
|
|
mod=self.inv,
|
|
offset=self.offset_inv7,
|
|
mirror="RO")
|
|
self.connect_inst(["pre_w_en1", "w_en", "vdd", "gnd"])
|
|
# set pin offset as attr
|
|
self.inv7_Z_position = self.offset_inv7 + vector(self.inv.width,
|
|
self.inv.Z_position[1])
|
|
|
|
def set_nand2_nor2_pin(self,mod,scale):
|
|
offset = getattr (self, "offset_"+mod)
|
|
for pin in ["A","B"]:
|
|
pin_xy = getattr(getattr(self, mod), pin+"_position")
|
|
setattr(self, mod+"_1_"+pin+"_position", offset + pin_xy.scale(0,1))
|
|
base = offset + vector(getattr(self, mod).width,0).scale(scale[0],0)
|
|
for pin in ["Z","vdd","gnd"]:
|
|
pin_xy = getattr(getattr(self, mod), pin+"_position")
|
|
setattr(self, mod+"_1_"+pin+"_position", base + pin_xy.scale(0,1))
|
|
|
|
def set_Nand3_pins(self,nand_name,nand_scale):
|
|
base = getattr(self, "offset_"+nand_name)
|
|
extra = vector(0, drc["minwidth_metal1"]* (1 - nand_scale[1]) *0.5)
|
|
off1 = base - extra
|
|
off2 = base - extra + vector(self.NAND3.width, 0)
|
|
self.set_Nand3_pins_sub(nand_name,["A","B","C"],off1,[0,nand_scale[1]])
|
|
self.set_Nand3_pins_sub(nand_name,["Z","vdd","gnd"],off2,[0,nand_scale[1]])
|
|
|
|
def set_Nand3_pins_sub(self,nand_name,pin_lst,base,nand_scale):
|
|
for pin in pin_lst:
|
|
pin_xy = getattr(self.NAND3, pin+"_position").scale(0,nand_scale[1])
|
|
setattr(self, nand_name+"_"+pin+"_position", base + pin_xy)
|
|
|
|
def set_inv2345_pins(self,inv_name,inv_scale):
|
|
base_xy = getattr(self, "offset_"+inv_name)
|
|
correct= vector(0, (1-inv_scale[1]) * 0.5 * drc["minwidth_metal1"])
|
|
# pin A
|
|
pin_xy = vector(0, self.inv4.A_position.y).scale(0,inv_scale[1])
|
|
setattr(self, inv_name+"_A_position", base_xy + pin_xy - correct)
|
|
# Z, vdd, gnd
|
|
for pin in ["Z_position", "vdd_position", "gnd_position"]:
|
|
pin_xy = getattr(self.inv, pin).scale(0,inv_scale[1])
|
|
rotated_pin_xy = vector(self.inv.width * inv_scale[0], 0) + pin_xy
|
|
setattr(self, inv_name+"_"+pin, base_xy + rotated_pin_xy - correct)
|
|
|
|
def add_msf_control_routing(self):
|
|
# FIRST RAIL : MSF_CONTROL OUTPUT RAIL
|
|
rail1_start = vector(self.msf_control_WE_position[0],
|
|
self.output_port_gap)
|
|
for i in range(self.num_rails_1):
|
|
correct = vector((i+1) * self.rail_offset_gap, 0)
|
|
offset = rail1_start + correct
|
|
self.add_rect(layer="metal2",
|
|
offset=offset,
|
|
width=drc["minwidth_metal2"],
|
|
height=self.logic_height)
|
|
self.rail_1_x_offsets.append(offset[0])
|
|
|
|
rail2_start_x = (self.nand_array_position[0] + self.NAND3.width
|
|
+ 3 * self.inv.width + self.rail_offset_gap)
|
|
for i in range(self.num_rails_2):
|
|
offset = [rail2_start_x + i * self.rail_offset_gap,
|
|
self.output_port_gap]
|
|
self.add_rect(layer="metal2",
|
|
offset=offset,
|
|
width=drc["minwidth_metal2"],
|
|
height=self.logic_height)
|
|
self.rail_2_x_offsets.append(offset[0])
|
|
|
|
def add_1st_row_routing(self):
|
|
# First rail routing left
|
|
left_side = []
|
|
for pin in ["OE_bar","OE","CS","WE"]:
|
|
left_side.append(getattr(self,"msf_control_"+pin+"_position"))
|
|
line_indices = [1, 2, 3, 4]
|
|
for i in range(len(left_side)):
|
|
offset = left_side[i]
|
|
line_x_offset = self.rail_1_x_offsets[line_indices[i]]
|
|
self.add_rect(layer="metal1",
|
|
offset=offset,
|
|
width=line_x_offset - offset[0] + drc["minwidth_metal2"],
|
|
height=drc["minwidth_metal1"])
|
|
correct1 = vector(self.gap_between_rails, - self.via_shift)
|
|
correct2 = vector(self.contact_shift + drc["minwidth_metal2"],0)
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=offset + correct1 - correct2,
|
|
rotate=90)
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=vector(line_x_offset, offset[1]) + correct1,
|
|
rotate=90)
|
|
# First rail routing Right
|
|
right_side = []
|
|
right_side.append(self.nand2_1_A_position)
|
|
right_side.append(self.nand2_1_B_position)
|
|
for size in ["1","2"]:
|
|
for pin in ["A","B","C"]:
|
|
right_side.append(getattr(self,"nand3_"+size+"_"+pin+"_position"))
|
|
|
|
line_indices = [2, 5, 5, 2, 3, 5, 4, 3]
|
|
for i in range(len(right_side)):
|
|
offset = right_side[i]
|
|
line_x_offset = self.rail_1_x_offsets[line_indices[i]]
|
|
base = vector(line_x_offset, offset[1])
|
|
self.add_rect(layer="metal1",
|
|
offset=base,
|
|
width=offset[0] - line_x_offset,
|
|
height=drc["minwidth_metal1"])
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=base + correct1,
|
|
rotate=90)
|
|
|
|
# OE_bar [Bus # 1] to nor2 B input
|
|
layer_stack = ("metal2", "via1", "metal1")
|
|
start = self.nor2_1_B_position
|
|
mid1 = [self.nor2_1_B_position[0] + 2 * drc["minwidth_metal2"], start[1]]
|
|
mid2 = [mid1[0], self.nor2_1_gnd_position[1] - 2 * drc["minwidth_metal1"]]
|
|
mid3 = [self.rail_1_x_offsets[1] + 0.5 * drc["minwidth_metal2"], mid2[1]]
|
|
end = [mid3[0], self.output_port_gap]
|
|
self.add_wire(layer_stack, [start, mid1, mid2, mid3, end])
|
|
|
|
layer_stack = ("metal1")
|
|
start = [self.inv1_Z_position[0], self.inv1_Z_position[1] + 0.5 * drc["minwidth_metal1"]]
|
|
mid1 = [start[0] + drc["minwidth_metal2"], start[1]]
|
|
mid2 = [mid1[0], self.nand2_1_B_position
|
|
[1] + 0.5 * drc["minwidth_metal1"]]
|
|
end = [self.nand2_1_B_position[0], mid2[1]]
|
|
self.add_path(layer_stack, [start, mid1, mid2, end])
|
|
|
|
def add_2nd_row_routing(self):
|
|
# Second rail routing
|
|
left_side = []
|
|
left_side.append(self.inv2_Z_position)
|
|
left_side.append(self.inv7_Z_position)
|
|
left_side.append(self.inv5_A_position)
|
|
line_indices = [1, 0, 2]
|
|
#line_indices = [1,2]
|
|
for i in range(len(left_side)):
|
|
offset = left_side[i]
|
|
line_x_offset = self.rail_2_x_offsets[line_indices[i]]
|
|
self.add_rect(layer="metal1",
|
|
offset=offset,
|
|
width=line_x_offset - offset[0] + drc["minwidth_metal2"],
|
|
height=drc["minwidth_metal1"])
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=[line_x_offset + self.gap_between_rails,
|
|
offset[1] - self.via_shift],
|
|
rotate=90)
|
|
|
|
# Replica bitline (rblk to replica bitline input)
|
|
layer_stack = ("metal2", "via1", "metal1")
|
|
start = [self.rail_2_x_offsets[1] + 0.5 * drc["minwidth_metal2"],
|
|
self.output_port_gap]
|
|
mid1 = [start[0], 0.5 * drc["minwidth_metal1"]]
|
|
end = [self.replica_en_offset[0], mid1[1]]
|
|
|
|
self.add_wire(layer_stack, [start, mid1, end])
|
|
|
|
height = self.replica_en_offset[1] - end[1] + 0.5 * drc["minwidth_metal1"]
|
|
|
|
self.add_rect(layer="metal1",
|
|
offset=end - vector([0.5 * drc["minwidth_metal1"]] * 2),
|
|
width=drc["minwidth_metal1"],
|
|
height=height)
|
|
|
|
# Replica bitline (replica bitline output to the buffer [inv4,inv5])
|
|
start = [self.rail_2_x_offsets[2], self.replica_out_offset[1]]
|
|
end = self.replica_out_offset - vector(0.5 * drc["minwidth_metal1"],0)
|
|
self.add_rect(layer="metal3",
|
|
offset=start,
|
|
width=self.replica_out_offset[0] - self.rail_2_x_offsets[2],
|
|
height=drc["minwidth_metal3"])
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
offset=start)
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
offset=end)
|
|
|
|
def add_vdd_routing(self):
|
|
""" VDD routing between modules """
|
|
vdd_rail_index = self.num_rails_2 - 1
|
|
rail_2_x = self.rail_2_x_offsets[vdd_rail_index]
|
|
|
|
# Connection between nor2 vdd to nand3 vdd
|
|
self.add_rect(layer="metal1",
|
|
offset=self.nor2_1_vdd_position,
|
|
width=rail_2_x + drc["minwidth_metal2"],
|
|
height=drc["minwidth_metal1"])
|
|
|
|
# Connection between top AND_Array vdd to the last line on rail2
|
|
self.add_rect(layer="metal1",
|
|
offset=self.nand3_2_vdd_position,
|
|
width=(rail_2_x + drc["minwidth_metal2"]
|
|
- self.nand3_2_vdd_position[0]),
|
|
height=drc["minwidth_metal1"])
|
|
|
|
# Connection in horizontal metal2 vdd rail
|
|
base = vector(rail_2_x + self.gap_between_rails,
|
|
- self.via_shift)
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=base + self.nand2_1_vdd_position.scale(0, 1),
|
|
rotate=90)
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=base + self.nand3_2_vdd_position.scale(0, 1),
|
|
rotate=90)
|
|
|
|
# Connection of msf_vdd to inv1 vdd
|
|
self.add_rect(layer="metal1",
|
|
offset=[self.msf_control_vdd_position[0],
|
|
self.inv1_vdd_position[1]],
|
|
width=drc["minwidth_metal1"],
|
|
height=self.msf_control_vdd_position[1] - self.inv1_vdd_position[1])
|
|
|
|
vdd_offset = vector(self.replica_bitline.height,3 * drc["minwidth_metal1"])
|
|
|
|
self.vdd1_position = vdd_offset + self.offset_replica_bitline
|
|
self.vdd2_position = vector(rail_2_x, self.output_port_gap)
|
|
|
|
def add_gnd_routing(self):
|
|
""" GND routing """
|
|
self.gnd_position = self.offset_replica_bitline
|
|
|
|
# Connection of msf_control gnds to the metal2 gnd rail
|
|
for gnd_offset in self.msf_control_gnd_positions:
|
|
self.add_rect(layer="metal2",
|
|
offset=gnd_offset,
|
|
width=(self.rail_1_x_offsets[0] - gnd_offset[0]
|
|
+ drc["minwidth_metal2"]),
|
|
height=drc["minwidth_metal2"])
|
|
|
|
# Connect msf_control gnd to nand3 gnd
|
|
self.add_rect(layer="metal1",
|
|
offset=self.nor2_1_gnd_position,
|
|
width=self.offset_replica_bitline[0],
|
|
height=drc["minwidth_metal1"])
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=[self.rail_1_x_offsets[0] + self.gap_between_rails,
|
|
self.nor2_1_gnd_position[1] - self.via_shift],
|
|
rotate=90)
|
|
|
|
# nand3 gnd to replica bitline gnd
|
|
self.add_rect(layer="metal1",
|
|
offset=self.nand3_2_gnd_position,
|
|
width=(self.offset_replica_bitline[0]
|
|
- self.nand3_2_gnd_position[0]),
|
|
height=drc["minwidth_metal1"])
|
|
|
|
def add_input_routing(self):
|
|
""" Input pin routing """
|
|
# WEb, CEb, OEb assign from msf_control pin
|
|
self.WEb_position = self.msf_control_WEb_position
|
|
self.CSb_position = self.msf_control_CSb_position
|
|
self.OEb_position = self.msf_control_OEb_position
|
|
|
|
# Clk
|
|
clk_y = self.inv1_vdd_position[1] + 6 * drc["minwidth_metal1"]
|
|
self.clk_position = vector(0, clk_y)
|
|
|
|
# clk port to inv1 A
|
|
layer_stack = ("metal2", "via1", "metal1")
|
|
start = self.inv1_A_position + vector(0, 0.5 * drc["minwidth_metal1"])
|
|
mid1 = vector(self.inv1_A_position[0] - 2 * drc["minwidth_metal2"],
|
|
start.y)
|
|
mid2 = vector(mid1.x, clk_y)
|
|
self.clk_position = vector(0, mid2[1])
|
|
|
|
self.add_wire(layer_stack, [start, mid1, mid2, self.clk_position])
|
|
|
|
|
|
# clk line to msf_control_clk
|
|
self.add_rect(layer="metal1",
|
|
offset=[self.msf_control_clk_position[0],
|
|
self.clk_position[1]],
|
|
width=drc["minwidth_metal1"],
|
|
height=(self.msf_control_clk_position[1]
|
|
- self.clk_position[1]))
|
|
|
|
# clk connection to nor2 A input
|
|
start = [self.inv1_A_position[0] - 2 * drc["minwidth_metal2"],
|
|
self.inv1_A_position[1] + 0.5 * drc["minwidth_metal1"]]
|
|
mid1 = [start[0] - 3 * drc["minwidth_metal2"], start[1]]
|
|
mid2 = [mid1[0], self.nor2_1_A_position[1]]
|
|
|
|
self.add_path("metal1", [start, mid1, mid2, self.nor2_1_A_position])
|
|
|
|
correct = vector(0, 0.5 * drc["minwidth_metal1"])
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=self.clk_position + correct,
|
|
rotate=270)
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
offset=self.clk_position + correct,
|
|
rotate=270)
|
|
|
|
def add_output_routing(self):
|
|
""" Output pin routing """
|
|
# clk_bar
|
|
self.clk_bar_position = vector(self.rail_1_x_offsets[self.num_rails_1 - 1],
|
|
0)
|
|
self.add_rect(layer="metal2",
|
|
offset=self.clk_bar_position,
|
|
width=drc["minwidth_metal2"],
|
|
height=self.output_port_gap)
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
offset=self.clk_bar_position)
|
|
|
|
|
|
# tri_en
|
|
correct = vector (0, 0.5 * drc["minwidth_metal1"])
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=self.nor2_1_Z_position + correct,
|
|
rotate=270)
|
|
self.add_rect(layer="metal2",
|
|
offset=self.nor2_1_Z_position.scale(1, 0),
|
|
width=drc["minwidth_metal2"],
|
|
height=self.nor2_1_Z_position.y + correct.y)
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
offset=self.nor2_1_Z_position.scale(1, 0))
|
|
self.tri_en_position = vector(self.nor2_1_Z_position[0], 0)
|
|
|
|
# tri_en_bar
|
|
correct = vector(drc["minwidth_metal2"], 0)
|
|
self.tri_en_bar_position = self.nand2_1_Z_position.scale(1, 0) - correct
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=self.nand2_1_Z_position - correct)
|
|
self.add_rect(layer="metal2",
|
|
offset=self.tri_en_bar_position,
|
|
width=drc["minwidth_metal2"],
|
|
height=self.nand2_1_Z_position[1] + drc["minwidth_metal1"])
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
offset=self.tri_en_bar_position)
|
|
|
|
# w_en
|
|
self.w_en_position = vector(self.rail_2_x_offsets[0], 0)
|
|
self.add_rect(layer="metal2",
|
|
offset=self.w_en_position,
|
|
width=drc["minwidth_metal2"],
|
|
height=self.output_port_gap)
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
offset=self.w_en_position)
|
|
|
|
# s_en
|
|
self.s_en_position = self.inv4_Z_position.scale(1,0)
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
offset=self.inv4_Z_position)
|
|
self.add_rect(layer="metal2",
|
|
offset=self.s_en_position,
|
|
width=drc["minwidth_metal2"],
|
|
height=self.inv4_Z_position[1] + drc["minwidth_metal1"])
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
offset=self.s_en_position)
|