2016-11-08 18:57:35 +01:00
|
|
|
import math
|
|
|
|
|
import sys
|
2016-11-09 21:20:52 +01:00
|
|
|
from tech import drc, spice
|
2016-11-08 18:57:35 +01:00
|
|
|
import debug
|
|
|
|
|
import design
|
|
|
|
|
from math import log,sqrt,ceil
|
|
|
|
|
from contact import contact
|
|
|
|
|
from bank import bank
|
|
|
|
|
import datetime
|
|
|
|
|
import getpass
|
|
|
|
|
from vector import vector
|
|
|
|
|
from globals import OPTS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class sram(design.design):
|
|
|
|
|
"""
|
|
|
|
|
Dynamically generated SRAM by connecting banks to control logic. The
|
|
|
|
|
number of banks should be 1 , 2 or 4
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, word_size, num_words, num_banks, name):
|
|
|
|
|
mod_list = ["control_logic", "ms_flop_array", "ms_flop", "bitcell"]
|
|
|
|
|
for mod_name in mod_list:
|
|
|
|
|
config_mod_name = getattr(OPTS.config, mod_name)
|
|
|
|
|
class_file = reload(__import__(config_mod_name))
|
|
|
|
|
mod_class = getattr(class_file , config_mod_name)
|
|
|
|
|
setattr (self, "mod_"+mod_name, mod_class)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.ms_flop_chars = self.mod_ms_flop.chars
|
|
|
|
|
self.bitcell_chars = self.mod_bitcell.chars
|
|
|
|
|
|
|
|
|
|
self.word_size = word_size
|
|
|
|
|
self.num_words = num_words
|
|
|
|
|
self.num_banks = num_banks
|
|
|
|
|
|
|
|
|
|
debug.info(2, "create sram of size {0} with {1} num of words".format(self.word_size,
|
|
|
|
|
self.num_words))
|
|
|
|
|
|
|
|
|
|
design.design.__init__(self, name)
|
|
|
|
|
self.ctrl_positions = {}
|
|
|
|
|
|
|
|
|
|
self.compute_sizes()
|
|
|
|
|
self.add_pins()
|
|
|
|
|
|
|
|
|
|
self.create_layout()
|
|
|
|
|
self.DRC_LVS()
|
|
|
|
|
|
|
|
|
|
def compute_sizes(self):
|
|
|
|
|
""" Computes the required sizes to create the memory """
|
|
|
|
|
self.check_num_banks(self.num_banks)
|
|
|
|
|
|
|
|
|
|
self.num_words_per_bank = self.num_words/self.num_banks
|
|
|
|
|
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
|
|
|
|
|
|
|
|
|
|
self.bank_area = self.bitcell_chars["width"]*\
|
|
|
|
|
self.bitcell_chars["height"]*self.num_bits_per_bank
|
|
|
|
|
self.bank_side_length = math.sqrt(self.bank_area)
|
|
|
|
|
|
|
|
|
|
self.tentative_num_cols = int(self.bank_side_length/self.bitcell_chars["width"])
|
|
|
|
|
self.words_per_row = self.cal_words_per_row(self.tentative_num_cols,
|
|
|
|
|
self.word_size)
|
|
|
|
|
self.tentative_num_rows = self.num_bits_per_bank \
|
|
|
|
|
/(self.words_per_row \
|
|
|
|
|
*self.word_size)
|
|
|
|
|
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows,
|
|
|
|
|
self.words_per_row)
|
|
|
|
|
|
|
|
|
|
self.num_cols = self.words_per_row*self.word_size
|
|
|
|
|
self.num_rows = self.num_words_per_bank/self.words_per_row
|
|
|
|
|
|
|
|
|
|
self.row_addr_size = int(log(self.num_rows, 2))
|
|
|
|
|
self.col_addr_size = int(log(self.words_per_row, 2))
|
|
|
|
|
self.bank_addr_size = self.col_addr_size \
|
|
|
|
|
+ self.row_addr_size
|
|
|
|
|
self.addr_size = self.bank_addr_size + \
|
|
|
|
|
int(math.log(self.num_banks, 2))
|
|
|
|
|
|
|
|
|
|
self.control_size = 6
|
|
|
|
|
|
|
|
|
|
self.bank_to_bus_distance = 5*drc["minwidth_metal3"]
|
|
|
|
|
|
|
|
|
|
def check_num_banks(self,num_banks):
|
|
|
|
|
if(num_banks != 1 and num_banks != 2 and num_banks != 4):
|
|
|
|
|
debug.error("Valid number of banks are 1 , 2 and 4.")
|
|
|
|
|
sys.exit(-1)
|
|
|
|
|
|
|
|
|
|
def cal_words_per_row(self,tentative_num_cols, word_size):
|
|
|
|
|
if(tentative_num_cols < 1.5*word_size):
|
|
|
|
|
words_per_row = 1
|
|
|
|
|
elif(tentative_num_cols > 3*word_size):
|
|
|
|
|
words_per_row = 4
|
|
|
|
|
else:
|
|
|
|
|
words_per_row = 2
|
|
|
|
|
return words_per_row
|
|
|
|
|
|
|
|
|
|
def amend_words_per_row(self,tentative_num_rows, words_per_row):
|
|
|
|
|
if(tentative_num_rows > 512):
|
|
|
|
|
if(tentative_num_rows*words_per_row > 2048):
|
|
|
|
|
debug.error("Number of rows exceeds 512")
|
|
|
|
|
sys.exit(-1)
|
|
|
|
|
words_per_row = words_per_row*tentative_num_rows/512
|
|
|
|
|
|
|
|
|
|
if(tentative_num_rows < 16):
|
|
|
|
|
if(tentative_num_rows*words_per_row < 16):
|
|
|
|
|
debug.error("minimum number of rows is 16, but given {0}".format(
|
|
|
|
|
tentative_num_rows))
|
|
|
|
|
sys.exit(-1)
|
|
|
|
|
words_per_row = words_per_row*tentative_num_rows/16
|
|
|
|
|
return words_per_row
|
|
|
|
|
|
|
|
|
|
def add_pins(self):
|
|
|
|
|
""" app pins """
|
|
|
|
|
for i in range(self.word_size):
|
|
|
|
|
self.add_pin("DATA[{0}]".format(i))
|
|
|
|
|
for i in range(self.addr_size):
|
|
|
|
|
self.add_pin("ADDR[{0}]".format(i))
|
|
|
|
|
for pin in ["CSb","WEb","OEb",
|
|
|
|
|
"clk","vdd","gnd"]:
|
|
|
|
|
self.add_pin(pin)
|
|
|
|
|
|
|
|
|
|
def create_layout(self):
|
|
|
|
|
""" Layout creation """
|
|
|
|
|
self.create_modules()
|
|
|
|
|
self.add_modules()
|
|
|
|
|
self.add_routing()
|
|
|
|
|
|
|
|
|
|
def add_routing(self):
|
|
|
|
|
""" Route all of the modules """
|
|
|
|
|
if (self.num_banks == 2 or self.num_banks == 4):
|
|
|
|
|
self.route_2or4_banks()
|
|
|
|
|
if (self.num_banks == 4):
|
|
|
|
|
self.route_4_banks()
|
|
|
|
|
self.route_bank_and_control()
|
|
|
|
|
self.route_supplies()
|
|
|
|
|
|
|
|
|
|
def create_multibank_modules(self):
|
|
|
|
|
""" Add the multibank address flops and bank decoder """
|
|
|
|
|
self.msf_msb_address = self.mod_ms_flop_array(name="msf_msb_address",
|
|
|
|
|
array_type="address",
|
|
|
|
|
columns=self.num_banks/2,
|
|
|
|
|
word_size=self.num_banks/2)
|
|
|
|
|
self.add_mod(self.msf_msb_address)
|
|
|
|
|
|
|
|
|
|
self.msb_decoder = self.bank.decoder.pre2_4
|
|
|
|
|
self.add_mod(self.msb_decoder)
|
|
|
|
|
|
|
|
|
|
def create_modules(self):
|
|
|
|
|
""" Create all the modules that will be used """
|
|
|
|
|
|
|
|
|
|
# Create the control logic module
|
|
|
|
|
self.control = self.mod_control_logic(num_rows=self.num_rows)
|
|
|
|
|
self.add_mod(self.control)
|
|
|
|
|
|
|
|
|
|
# Create the bank module (up to four are instantiated)
|
|
|
|
|
self.bank = bank(word_size=self.word_size,
|
|
|
|
|
num_words=self.num_words_per_bank,
|
|
|
|
|
words_per_row=self.words_per_row,
|
|
|
|
|
num_banks=self.num_banks,
|
|
|
|
|
name="test_bank1")
|
|
|
|
|
self.add_mod(self.bank)
|
|
|
|
|
|
|
|
|
|
# Conditionally create the
|
|
|
|
|
if(self.num_banks > 1):
|
|
|
|
|
self.create_multibank_modules()
|
|
|
|
|
|
|
|
|
|
# These aren't for instantiating, but we use them to get the dimensions
|
|
|
|
|
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
|
|
|
|
|
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
|
|
|
|
|
|
|
|
|
|
self.bank_count = 0
|
|
|
|
|
|
|
|
|
|
self.sram_property = ["bank_clk_positions",
|
|
|
|
|
"bank_clk_bar_positions",
|
|
|
|
|
"bank_tri_en_positions",
|
|
|
|
|
"bank_tri_en_bar_positions",
|
|
|
|
|
"bank_w_en_positions",
|
|
|
|
|
"bank_s_en_positions"]
|
|
|
|
|
self.bank_property = ["clk_position",
|
|
|
|
|
"clk_bar_position",
|
|
|
|
|
"tri_en_position",
|
|
|
|
|
"tri_en_bar_position",
|
|
|
|
|
"w_en_position",
|
|
|
|
|
"s_en_position", ]
|
|
|
|
|
|
|
|
|
|
self.bank_positions = []
|
|
|
|
|
|
|
|
|
|
self.bank_clk_positions = []
|
|
|
|
|
self.bank_clk_bar_positions = []
|
|
|
|
|
self.bank_tri_en_positions = []
|
|
|
|
|
self.bank_tri_en_bar_positions = []
|
|
|
|
|
self.bank_w_en_positions = []
|
|
|
|
|
self.bank_s_en_positions = []
|
|
|
|
|
|
|
|
|
|
# SRAM bank address 3D array
|
|
|
|
|
# 2 keys will return a x,y position pair
|
|
|
|
|
# example key1 = bank_index , key2 = addr_line_index will return [x,y]
|
|
|
|
|
self.sram_bank_adress_positions = []
|
|
|
|
|
|
|
|
|
|
# SRAM data lines 3D array
|
|
|
|
|
# 2 keys will return a x,y position pair
|
|
|
|
|
# example key1 = bank_index , key2 = data_line_index will return [x,y]
|
|
|
|
|
self.sram_bank_data_positions = []
|
|
|
|
|
|
|
|
|
|
# 2D array for bank_select position of banks
|
|
|
|
|
self.sram_bank_select_positions = []
|
|
|
|
|
|
|
|
|
|
# Bank power rail positions
|
|
|
|
|
self.sram_bank_right_vdd_positions = []
|
|
|
|
|
self.sram_bank_left_vdd_positions = []
|
|
|
|
|
self.sram_bank_left_gnd_positions = []
|
|
|
|
|
|
|
|
|
|
self.power_rail_width = self.bank.power_rail_width
|
|
|
|
|
self.sram_power_rail_gap = 4*self.power_rail_width
|
|
|
|
|
|
|
|
|
|
self.vdd_position = vector(0, 2*self.power_rail_width)
|
|
|
|
|
self.gnd_position = vector(0, 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_bank(self, position, x_flip, y_flip):
|
|
|
|
|
""" add and place bank. All the pin position is also
|
|
|
|
|
translated and saved for later use"""
|
|
|
|
|
|
|
|
|
|
# x_flip == 1 --> no flip in x_axis
|
|
|
|
|
# x_flip == -1 --> flip in x_axis
|
|
|
|
|
# y_flip == 1 --> no flip in y_axis
|
|
|
|
|
# y_flip == -1 --> flip in y_axis
|
|
|
|
|
|
|
|
|
|
# x_flip and y_flip are used for position translation
|
|
|
|
|
|
|
|
|
|
bank_rotation = 180 if (x_flip == -1 and y_flip == -1) else 0
|
|
|
|
|
bank_mirror = "R0"
|
|
|
|
|
|
|
|
|
|
if(x_flip == y_flip):
|
|
|
|
|
bank_mirror = "R0"
|
|
|
|
|
elif(x_flip == -1):
|
|
|
|
|
bank_mirror = "MX"
|
|
|
|
|
elif(y_flip == -1):
|
|
|
|
|
bank_mirror = "MY"
|
|
|
|
|
|
|
|
|
|
yMetalShift = drc["minwidth_metal3"] if (x_flip == -1) else 0
|
|
|
|
|
xMetalShift = drc["minwidth_metal3"] if (y_flip == -1) else 0
|
|
|
|
|
|
|
|
|
|
position=vector(position)
|
|
|
|
|
self.add_inst(name="bank{0}".format(self.bank_count),
|
|
|
|
|
mod=self.bank,
|
|
|
|
|
offset=position,
|
|
|
|
|
mirror=bank_mirror,
|
|
|
|
|
rotate=bank_rotation)
|
|
|
|
|
self.bank_positions.append(position)
|
|
|
|
|
|
|
|
|
|
temp = []
|
|
|
|
|
for i in range(self.word_size):
|
|
|
|
|
temp.append("DATA[{0}]".format(i))
|
|
|
|
|
for i in range(self.bank_addr_size):
|
|
|
|
|
temp.append("ADDR[{0}]".format(i))
|
|
|
|
|
if(self.num_banks > 1):
|
|
|
|
|
temp.append("bank_select[{0}]".format(self.bank_count))
|
|
|
|
|
temp = temp + ["s_en" , "w_en", "tri_en_bar", "tri_en",
|
|
|
|
|
"clk_bar", "clk" , "vdd" , "gnd" ]
|
|
|
|
|
self.connect_inst(temp)
|
|
|
|
|
|
|
|
|
|
# Saving control line properties
|
|
|
|
|
for i in range(len(self.sram_property)):
|
|
|
|
|
sub_mod_offset = getattr(self.bank,self.bank_property[i])
|
|
|
|
|
new=(position + vector(y_flip,x_flip).scale(sub_mod_offset)
|
|
|
|
|
- vector(xMetalShift,yMetalShift))
|
|
|
|
|
|
|
|
|
|
pos_list=getattr(self,self.sram_property[i])
|
|
|
|
|
if pos_list is None:
|
|
|
|
|
pos_list=[]
|
|
|
|
|
pos_list.append(new)
|
|
|
|
|
setattr(self,self.sram_property[i],pos_list)
|
|
|
|
|
|
|
|
|
|
# Address input lines
|
|
|
|
|
bank_address_positions = []
|
|
|
|
|
for addr_position in self.bank.address_positions:
|
|
|
|
|
new=(position + vector(y_flip,x_flip).scale(addr_position)
|
|
|
|
|
- vector(xMetalShift,yMetalShift))
|
|
|
|
|
bank_address_positions.append(new)
|
|
|
|
|
self.sram_bank_adress_positions.append(bank_address_positions)
|
|
|
|
|
|
|
|
|
|
# Bank select
|
|
|
|
|
if (self.num_banks > 1):
|
|
|
|
|
new=(position + vector(y_flip,x_flip).scale(self.bank.bank_select_position)
|
|
|
|
|
- vector(xMetalShift,yMetalShift))
|
|
|
|
|
self.sram_bank_select_positions.append(new)
|
|
|
|
|
|
|
|
|
|
# Data input lines
|
|
|
|
|
bank_data_positions = []
|
|
|
|
|
for data_position in self.bank.data_positions:
|
|
|
|
|
new=(position + vector(y_flip,x_flip).scale(data_position)
|
|
|
|
|
- vector(xMetalShift,yMetalShift))
|
|
|
|
|
bank_data_positions.append(new)
|
|
|
|
|
self.sram_bank_data_positions.append(bank_data_positions)
|
|
|
|
|
|
|
|
|
|
# VDD rails
|
|
|
|
|
|
|
|
|
|
yPowerShift = self.power_rail_width if(x_flip == -1) else 0
|
|
|
|
|
xPowerShift = self.power_rail_width if(y_flip == -1) else 0
|
|
|
|
|
|
|
|
|
|
# Right vdd
|
|
|
|
|
new=(position + vector(y_flip,x_flip).scale(self.bank.right_vdd_position)
|
|
|
|
|
- vector(xPowerShift,yPowerShift))
|
|
|
|
|
self.sram_bank_right_vdd_positions.append(new)
|
|
|
|
|
# left vdd
|
|
|
|
|
new=(position + vector(y_flip,x_flip).scale(self.bank.left_vdd_position)
|
|
|
|
|
- vector(xPowerShift,yPowerShift))
|
|
|
|
|
self.sram_bank_left_vdd_positions.append(new)
|
|
|
|
|
# left gnd
|
|
|
|
|
new=(position + vector(y_flip,x_flip).scale(self.bank.left_gnd_position)
|
|
|
|
|
- vector(xPowerShift,yPowerShift))
|
|
|
|
|
self.sram_bank_left_gnd_positions.append(new)
|
|
|
|
|
|
|
|
|
|
self.bank_count = self.bank_count + 1
|
|
|
|
|
|
|
|
|
|
# FIXME: This should be in geometry.py or it's own class since it is
|
|
|
|
|
# reusable
|
|
|
|
|
def create_bus(self, layer, offset, bits, height, rotate):
|
|
|
|
|
""" Create a bus and place it according to rotate and
|
|
|
|
|
return an array of line positions """
|
|
|
|
|
|
|
|
|
|
minwidth = "minwidth_{0}".format(layer)
|
|
|
|
|
m2m = "{0}_to_{0}".format(layer)
|
|
|
|
|
line_width = drc[minwidth]
|
|
|
|
|
line_gap = 2*drc[m2m]
|
|
|
|
|
|
|
|
|
|
line_positions = []
|
|
|
|
|
bus_width = bits*(line_width + line_gap)
|
|
|
|
|
if(rotate == 0):
|
|
|
|
|
for i in range(bits):
|
|
|
|
|
line_offset = offset + vector(i*(line_width + line_gap),0)
|
|
|
|
|
self.add_rect(layer=layer,
|
|
|
|
|
offset=line_offset,
|
|
|
|
|
width=line_width,
|
|
|
|
|
height=height)
|
|
|
|
|
line_positions.append(line_offset)
|
|
|
|
|
elif(rotate == 270):
|
|
|
|
|
for i in range(bits):
|
|
|
|
|
line_offset = offset - vector(0, (i+1)*line_width + i*line_gap)
|
|
|
|
|
self.add_rect(layer=layer,
|
|
|
|
|
offset=line_offset,
|
|
|
|
|
width=height,
|
|
|
|
|
height=line_width)
|
|
|
|
|
line_positions.append(line_offset)
|
|
|
|
|
else:
|
|
|
|
|
debug.error("Unimplemented rotation for create_bus")
|
|
|
|
|
|
|
|
|
|
return line_positions
|
|
|
|
|
|
|
|
|
|
def calculate_bus_width(self, layer, bits):
|
|
|
|
|
""" Calculate the bus width """
|
|
|
|
|
minwidth = "minwidth_{0}".format(layer)
|
|
|
|
|
m2m = "{0}_to_{0}".format(layer)
|
|
|
|
|
line_width = drc[minwidth]
|
|
|
|
|
line_gap = 2*drc[m2m]
|
|
|
|
|
return bits*(line_width + line_gap) - line_gap
|
|
|
|
|
|
|
|
|
|
def add_control_logic(self, position, mirror):
|
|
|
|
|
""" Add and place control logic """
|
|
|
|
|
self.control_position = position
|
|
|
|
|
self.add_inst(name="control",
|
|
|
|
|
mod=self.control,
|
|
|
|
|
offset=self.control_position,
|
|
|
|
|
mirror=mirror)
|
|
|
|
|
temp = ["CSb", "WEb", "OEb", "s_en", "w_en", "tri_en",
|
|
|
|
|
"tri_en_bar", "clk_bar", "clk", "vdd", "gnd"]
|
|
|
|
|
self.connect_inst(temp)
|
|
|
|
|
|
|
|
|
|
def add_singlebank_modules(self):
|
|
|
|
|
""" This adds the moduels for a single bank SRAM with control
|
|
|
|
|
logic. """
|
|
|
|
|
self.add_bank([0, 0], 1, 1)
|
|
|
|
|
# FIXME: document
|
|
|
|
|
loc = vector(- 2 * drc["minwidth_metal3"],
|
|
|
|
|
self.bank_positions[0].y + self.bank.decoder_position.y
|
|
|
|
|
+ 2 * drc["minwidth_metal3"])
|
|
|
|
|
self.add_control_logic(loc, "R90")
|
|
|
|
|
|
|
|
|
|
self.width = self.bank.width + self.control.height + 2*drc["minwidth_metal3"]
|
|
|
|
|
self.height = self.bank.height
|
|
|
|
|
self.control.CSb_position.rotate().scale(-1,1)
|
|
|
|
|
self.CSb_position = (self.control.CSb_position.rotate().scale(-1,1)
|
|
|
|
|
+self.control_position)
|
|
|
|
|
self.OEb_position = (self.control.OEb_position.rotate().scale(-1,1)
|
|
|
|
|
+self.control_position)
|
|
|
|
|
self.WEb_position = (self.control.WEb_position.rotate().scale(-1,1)
|
|
|
|
|
+self.control_position)
|
|
|
|
|
self.clk_position = (self.control.clk_position.rotate().scale(-1,1)
|
|
|
|
|
+self.control_position)
|
|
|
|
|
for i in range(0, self.word_size):
|
|
|
|
|
self.add_label(text="DATA[{0}]".format(i),
|
|
|
|
|
layer="metal3",
|
|
|
|
|
offset=self.bank.data_positions[i])
|
|
|
|
|
|
|
|
|
|
def add_multibank_modules(self):
|
|
|
|
|
""" This creates either a 2 or 4 bank SRAM with control logic
|
|
|
|
|
and bank selection logic."""
|
|
|
|
|
|
|
|
|
|
self.bank_h = self.bank.height
|
|
|
|
|
self.bank_w = self.bank.width
|
|
|
|
|
|
|
|
|
|
self.num_vertical_line = self.bank_addr_size + self.control_size \
|
|
|
|
|
+ self.num_banks + self.num_banks/2
|
|
|
|
|
self.num_horizontal_line = self.word_size
|
|
|
|
|
|
|
|
|
|
self.vertical_bus_width = self.calculate_bus_width("metal2",
|
|
|
|
|
self.num_vertical_line)
|
|
|
|
|
self.horizontal_bus_width = self.calculate_bus_width("metal3",
|
|
|
|
|
self.num_horizontal_line)
|
|
|
|
|
|
|
|
|
|
self.vertical_bus_height = (self.num_banks/2)*(self.bank_h + self.bank_to_bus_distance) \
|
|
|
|
|
+ self.horizontal_bus_width
|
|
|
|
|
self.horizontal_bus_height = (2 * (self.bank_w + self.bank_to_bus_distance)
|
|
|
|
|
+ self.vertical_bus_width)
|
|
|
|
|
|
|
|
|
|
self.vertical_bus_offset = vector(self.bank_w + self.bank_to_bus_distance,
|
|
|
|
|
self.sram_power_rail_gap)
|
|
|
|
|
self.horizontal_bus_offset = vector(0,
|
|
|
|
|
self.bank_h + self.bank_to_bus_distance
|
|
|
|
|
+ self.sram_power_rail_gap
|
|
|
|
|
+ self.horizontal_bus_width)
|
|
|
|
|
|
|
|
|
|
# Vertical bus
|
|
|
|
|
self.vertical_line_positions = self.create_bus(layer="metal2",
|
|
|
|
|
offset=self.vertical_bus_offset,
|
|
|
|
|
bits=self.num_vertical_line,
|
|
|
|
|
height=self.vertical_bus_height,
|
|
|
|
|
rotate=0)
|
|
|
|
|
|
|
|
|
|
# Horizontal bus
|
|
|
|
|
self.horizontal_line_positions = self.create_bus(layer="metal3",
|
|
|
|
|
offset=self.horizontal_bus_offset,
|
|
|
|
|
bits=self.num_horizontal_line,
|
|
|
|
|
height=self.horizontal_bus_height,
|
|
|
|
|
rotate=270)
|
|
|
|
|
for i in range(0, self.word_size):
|
|
|
|
|
self.add_label(text="DATA[{0}]".format(i),
|
|
|
|
|
layer="metal3",
|
|
|
|
|
offset=self.horizontal_line_positions[i])
|
|
|
|
|
|
|
|
|
|
self.width = 2*(self.bank_w + self.bank_to_bus_distance) + self.vertical_bus_width
|
|
|
|
|
self.height = (self.num_banks/2)*(self.bank_h + self.bank_to_bus_distance) \
|
|
|
|
|
+ self.horizontal_bus_width + self.sram_power_rail_gap
|
|
|
|
|
|
|
|
|
|
# Add Control logic for Bank = 2 and Bank =4
|
|
|
|
|
|
|
|
|
|
control_bus_width = self.calculate_bus_width("metal1",
|
|
|
|
|
self.control_size + 2)
|
|
|
|
|
control_bus_height = (self.vertical_line_positions[self.control_size - 1].x
|
|
|
|
|
+ drc["minwidth_metal2"])
|
|
|
|
|
|
|
|
|
|
control_bus_offset = vector(0, self.height + control_bus_width
|
|
|
|
|
+ 4*drc["minwidth_metal3"])
|
|
|
|
|
self.control_bus_line_positions = self.create_bus(layer="metal1",
|
|
|
|
|
offset=control_bus_offset,
|
|
|
|
|
bits=self.control_size + 2,
|
|
|
|
|
height=control_bus_height,
|
|
|
|
|
rotate=270)
|
|
|
|
|
|
|
|
|
|
if (self.num_banks == 2):
|
|
|
|
|
self.control_position = vector(0, control_bus_offset.y
|
|
|
|
|
+ self.ms_flop_chars["width"])
|
|
|
|
|
self.add_control_logic(self.control_position, "R0")
|
|
|
|
|
|
|
|
|
|
self.CSb_position = self.control_position + self.control.CSb_position
|
|
|
|
|
self.OEb_position = self.control_position + self.control.OEb_position
|
|
|
|
|
self.WEb_position = self.control_position + self.control.WEb_position
|
|
|
|
|
self.clk_position = self.control_position + self.control.clk_position
|
|
|
|
|
# Max point
|
|
|
|
|
self.max_point = self.control_position.y + self.ms_flop_chars["width"]
|
|
|
|
|
|
|
|
|
|
# MSB address
|
|
|
|
|
x_off = (self.bank_w + self.vertical_bus_width
|
|
|
|
|
+ 2 * self.bank_to_bus_distance
|
|
|
|
|
+ self.power_rail_width
|
|
|
|
|
+ 4 * drc["minwidth_metal3"])
|
|
|
|
|
y_off = self.height + 2 * self.ms_flop_chars["width"] + 4 * drc["minwidth_metal3"]
|
|
|
|
|
self.msf_msb_address_position = vector(x_off, y_off)
|
|
|
|
|
self.add_inst(name="msf_msb_address",
|
|
|
|
|
mod=self.msf_msb_address,
|
|
|
|
|
offset=self.msf_msb_address_position,
|
|
|
|
|
mirror="RO",
|
|
|
|
|
rotate=270)
|
|
|
|
|
|
|
|
|
|
temp = []
|
|
|
|
|
for i in range(self.num_banks/2):
|
|
|
|
|
temp.append("ADDR[{0}]".format(self.bank.addr_size + i))
|
|
|
|
|
if(self.num_banks == 4):
|
|
|
|
|
for i in range(self.num_banks/2):
|
|
|
|
|
temp.append("msb{0}".format(i))
|
|
|
|
|
temp.append("msb{0}_bar".format(i))
|
|
|
|
|
else:
|
|
|
|
|
temp = temp + ["bank_select[1]", "bank_select[0]"]
|
|
|
|
|
temp = temp + ["clk", "vdd", "gnd"]
|
|
|
|
|
self.connect_inst(temp)
|
|
|
|
|
|
|
|
|
|
self.add_banks_0and1()
|
|
|
|
|
|
|
|
|
|
if (self.num_banks == 4):
|
|
|
|
|
self.add_banks_2and3()
|
|
|
|
|
|
|
|
|
|
# Extension of Vertical Rail
|
|
|
|
|
self.create_bus(layer="metal2",
|
|
|
|
|
offset=[self.vertical_bus_offset.x,
|
|
|
|
|
self.height],
|
|
|
|
|
bits=self.num_vertical_line,
|
|
|
|
|
height=self.max_point - self.height,
|
|
|
|
|
rotate=0)
|
|
|
|
|
|
|
|
|
|
# Add ADDRESS labels to vertical line
|
|
|
|
|
for i in range(self.addr_size - int(math.log(self.num_banks, 2))):
|
|
|
|
|
index = self.control_size + int(math.log(self.num_banks, 2)) + i
|
|
|
|
|
self.add_label(text="ADDR[{}]".format(i),
|
|
|
|
|
layer="metal2",
|
|
|
|
|
offset=[self.vertical_line_positions[index].x,
|
|
|
|
|
self.max_point])
|
|
|
|
|
|
|
|
|
|
for i in range(int(math.log(self.num_banks, 2))):
|
|
|
|
|
self.add_label(text="ADDR[{}]".format(self.addr_size - i - 1),
|
|
|
|
|
layer="metal2",
|
|
|
|
|
offset=[self.vertical_line_positions[self.control_size + i].x,
|
|
|
|
|
self.max_point])
|
|
|
|
|
|
|
|
|
|
def add_modules(self):
|
|
|
|
|
""" add all the modules """
|
|
|
|
|
if (self.num_banks == 1):
|
|
|
|
|
self.add_singlebank_modules()
|
|
|
|
|
elif (self.num_banks == 2 or self.num_banks == 4):
|
|
|
|
|
self.add_multibank_modules()
|
|
|
|
|
|
|
|
|
|
self.add_labels()
|
|
|
|
|
|
|
|
|
|
def add_banks_0and1(self):
|
|
|
|
|
# Placement of bank 0
|
|
|
|
|
self.bank_position_0 = vector(self.bank_w,
|
|
|
|
|
self.bank_h + self.sram_power_rail_gap)
|
|
|
|
|
self.add_bank(self.bank_position_0, -1, -1)
|
|
|
|
|
|
|
|
|
|
# Placement of bank 1
|
|
|
|
|
x_off = self.bank_w + self.vertical_bus_width + 2*self.bank_to_bus_distance
|
|
|
|
|
self.bank_position_1 = vector(x_off, self.bank_position_0.y)
|
|
|
|
|
self.add_bank(self.bank_position_1, -1, 1)
|
|
|
|
|
|
|
|
|
|
def add_banks_2and3(self):
|
|
|
|
|
# Placement of bank 2
|
|
|
|
|
y_off = (self.bank_h + self.horizontal_bus_width
|
|
|
|
|
+2 * self.bank_to_bus_distance
|
|
|
|
|
+ self.sram_power_rail_gap)
|
|
|
|
|
bank_position_2 = vector(self.bank_position_0.x, y_off)
|
|
|
|
|
self.add_bank(bank_position_2, 1, -1)
|
|
|
|
|
|
|
|
|
|
# Placement of bank 3
|
|
|
|
|
bank_position_3 = vector(self.bank_position_1.x, bank_position_2.y)
|
|
|
|
|
self.add_bank(bank_position_3, 1, 1)
|
|
|
|
|
|
|
|
|
|
self.msb_decoder_position = vector(bank_position_3.x + self.power_rail_width
|
|
|
|
|
+ 4 * drc["minwidth_metal3"]
|
|
|
|
|
+ self.msb_decoder.width,
|
|
|
|
|
self.msf_msb_address_position.y
|
|
|
|
|
+ 4 * drc["minwidth_metal3"])
|
|
|
|
|
|
|
|
|
|
self.add_inst(name="msb_decoder",
|
|
|
|
|
mod=self.msb_decoder,
|
|
|
|
|
offset=self.msb_decoder_position,
|
|
|
|
|
mirror="MY")
|
|
|
|
|
temp = ["msb0", "msb1", "bank_select[{0}]".format(0),
|
|
|
|
|
"bank_select[{0}]".format(1), "bank_select[{0}]".format(2),
|
|
|
|
|
"bank_select[{0}]".format(3),
|
|
|
|
|
"vdd", "gnd"]
|
|
|
|
|
self.connect_inst(temp)
|
|
|
|
|
|
|
|
|
|
self.control_position = vector(0, self.msb_decoder_position.y
|
|
|
|
|
+ self.msb_decoder.height)
|
|
|
|
|
self.add_control_logic(self.control_position, "R0")
|
|
|
|
|
|
|
|
|
|
self.CSb_position = self.control_position + self.control.CSb_position
|
|
|
|
|
self.OEb_position = self.control_position + self.control.OEb_position
|
|
|
|
|
self.WEb_position = self.control_position + self.control.WEb_position
|
|
|
|
|
self.clk_position = self.control_position + self.control.clk_position
|
|
|
|
|
|
|
|
|
|
# Max point
|
|
|
|
|
self.max_point = self.msb_decoder_position.y + self.msb_decoder.height
|
|
|
|
|
|
|
|
|
|
def add_labels(self):
|
|
|
|
|
""" Add the top-level labels for control and address """
|
|
|
|
|
for label in ["CSb", "OEb", "WEb", "clk"]:
|
|
|
|
|
offset = getattr(self, label+"_position")
|
|
|
|
|
self.add_label(text=label,
|
|
|
|
|
layer="metal3",
|
|
|
|
|
offset=offset)
|
|
|
|
|
|
|
|
|
|
# add address label
|
|
|
|
|
for addr_pos_lst in self.sram_bank_adress_positions:
|
|
|
|
|
for address, address_positions in enumerate(addr_pos_lst):
|
|
|
|
|
self.add_label(text="ADDR[{0}]".format(address),
|
|
|
|
|
layer="metal3",
|
|
|
|
|
offset=address_positions)
|
|
|
|
|
|
|
|
|
|
def route_2or4_banks(self):
|
|
|
|
|
""" Routing between bank 2 or 4 bank modules """
|
|
|
|
|
addr_start_index = len(self.sram_property) + (self.num_banks / 2)
|
|
|
|
|
bank_select_index = addr_start_index + self.bank.addr_size
|
|
|
|
|
|
|
|
|
|
# control, data , address and bank_select connection
|
|
|
|
|
for i in range(self.num_banks / 2):
|
|
|
|
|
left_bank_index = 2 * i
|
|
|
|
|
right_bank_index = 2 * i + 1
|
|
|
|
|
|
|
|
|
|
for attr_index in range(len(self.sram_property)):
|
|
|
|
|
bank_attr = self.sram_property[attr_index]
|
|
|
|
|
self.add_rect(layer="metal3",
|
|
|
|
|
offset=getattr(self,bank_attr)[left_bank_index],
|
|
|
|
|
width=getattr(self,bank_attr)[right_bank_index].x - getattr(self,bank_attr)[left_bank_index][0],
|
|
|
|
|
height=drc["minwidth_metal3"])
|
|
|
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=[self.vertical_line_positions[attr_index].x,
|
|
|
|
|
getattr(self,bank_attr)[left_bank_index].y])
|
|
|
|
|
|
|
|
|
|
for addr_index in range(self.bank.addr_size):
|
|
|
|
|
line_index = addr_start_index + addr_index
|
|
|
|
|
self.add_rect(layer="metal3",
|
|
|
|
|
offset=self.sram_bank_adress_positions[left_bank_index][addr_index],
|
|
|
|
|
width=self.sram_bank_adress_positions[right_bank_index][addr_index].x \
|
|
|
|
|
- self.sram_bank_adress_positions[left_bank_index][addr_index].x,
|
|
|
|
|
height=drc["minwidth_metal3"])
|
|
|
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=[self.vertical_line_positions[line_index].x,
|
|
|
|
|
self.sram_bank_adress_positions[left_bank_index][addr_index].y])
|
|
|
|
|
|
|
|
|
|
# left bank_select
|
|
|
|
|
self.add_rect(layer="metal3",
|
|
|
|
|
offset=self.sram_bank_select_positions[left_bank_index],
|
|
|
|
|
width=self.vertical_line_positions[bank_select_index + left_bank_index].x \
|
|
|
|
|
- self.sram_bank_select_positions[left_bank_index].x,
|
|
|
|
|
height=drc["minwidth_metal3"])
|
|
|
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=[self.vertical_line_positions[bank_select_index + left_bank_index].x,
|
|
|
|
|
self.sram_bank_select_positions[left_bank_index].y])
|
|
|
|
|
|
|
|
|
|
# right bank select
|
|
|
|
|
x_off = self.vertical_line_positions[bank_select_index + right_bank_index].x
|
|
|
|
|
contact_offset = vector(x_off,
|
|
|
|
|
self.sram_bank_select_positions[right_bank_index].y)
|
|
|
|
|
|
|
|
|
|
self.add_rect(layer="metal3",
|
|
|
|
|
offset=contact_offset,
|
|
|
|
|
width=self.sram_bank_select_positions[right_bank_index].x \
|
|
|
|
|
- contact_offset.x,
|
|
|
|
|
height=drc["minwidth_metal3"])
|
|
|
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=contact_offset)
|
|
|
|
|
|
|
|
|
|
# Data connection on the horizontal bus
|
|
|
|
|
if (self.num_banks == 4):
|
|
|
|
|
data_connection_top = self.sram_bank_data_positions[2][0].y + self.m2m3_via.height
|
|
|
|
|
else:
|
|
|
|
|
data_connection_top=self.horizontal_bus_offset.y
|
|
|
|
|
|
|
|
|
|
data_connection_height = data_connection_top - self.sram_bank_data_positions[0][0].y
|
|
|
|
|
|
|
|
|
|
for i in range(2):
|
|
|
|
|
lower_bank_index = i
|
|
|
|
|
upper_bank_index = i + 2
|
|
|
|
|
|
|
|
|
|
for data_index in range(self.bank.word_size):
|
|
|
|
|
line_index = addr_start_index + addr_index
|
|
|
|
|
self.add_rect(layer="metal2",
|
|
|
|
|
offset=self.sram_bank_data_positions[lower_bank_index][data_index],
|
|
|
|
|
width=drc["minwidth_metal2"],
|
|
|
|
|
height=data_connection_height)
|
|
|
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=[self.sram_bank_data_positions[lower_bank_index][data_index].x,
|
|
|
|
|
self.horizontal_line_positions[data_index].y])
|
|
|
|
|
|
|
|
|
|
def route_4_banks(self):
|
|
|
|
|
for i in range(2):
|
|
|
|
|
lower_bank_index = i
|
|
|
|
|
upper_bank_index = i + 2
|
|
|
|
|
|
|
|
|
|
# Power rail connections
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=self.sram_bank_right_vdd_positions[lower_bank_index],
|
|
|
|
|
width=self.power_rail_width,
|
|
|
|
|
height=self.sram_bank_right_vdd_positions[upper_bank_index].y \
|
|
|
|
|
- self.sram_bank_right_vdd_positions[lower_bank_index].y)
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=self.sram_bank_left_vdd_positions[lower_bank_index],
|
|
|
|
|
width=self.power_rail_width,
|
|
|
|
|
height=self.sram_bank_left_vdd_positions[upper_bank_index].y \
|
|
|
|
|
- self.sram_bank_left_vdd_positions[lower_bank_index].y)
|
|
|
|
|
self.add_rect(layer="metal2",
|
|
|
|
|
offset=self.sram_bank_left_gnd_positions[lower_bank_index],
|
|
|
|
|
width=self.power_rail_width,
|
|
|
|
|
height=self.sram_bank_left_gnd_positions[upper_bank_index].y \
|
|
|
|
|
- self.sram_bank_left_gnd_positions[lower_bank_index].y)
|
|
|
|
|
|
|
|
|
|
def route_bank_and_control(self):
|
|
|
|
|
""" Routing between banks and control """
|
|
|
|
|
|
|
|
|
|
if (self.num_banks == 1):
|
|
|
|
|
|
|
|
|
|
# FIXME what is this? add comments
|
|
|
|
|
# 5 = clk
|
|
|
|
|
# 4 = tri_en_bar
|
|
|
|
|
# 3 = tri_en
|
|
|
|
|
# 2 = clk_bar
|
|
|
|
|
# 1 = w_en
|
|
|
|
|
# 0 = s_en
|
|
|
|
|
|
|
|
|
|
control_side = []
|
|
|
|
|
control_side.append(self.control.clk_position.rotate().scale(-1, 1)
|
|
|
|
|
+ self.control_position)
|
|
|
|
|
control_side.append(self.control.clk_bar_position.rotate().scale(-1, 1)
|
|
|
|
|
+ self.control_position)
|
|
|
|
|
control_side.append(self.control.tri_en_position.rotate().scale(-1, 1)
|
|
|
|
|
+ self.control_position)
|
|
|
|
|
control_side.append(self.control.tri_en_bar_position.rotate().scale(-1, 1)
|
|
|
|
|
+ self.control_position)
|
|
|
|
|
control_side.append(self.control.w_en_position.rotate().scale(-1, 1)
|
|
|
|
|
+ self.control_position)
|
|
|
|
|
control_side.append(self.control.s_en_position.rotate().scale(-1, 1)
|
|
|
|
|
+ self.control_position)
|
|
|
|
|
|
|
|
|
|
bank_side = []
|
|
|
|
|
|
|
|
|
|
for attr_name in (self.sram_property):
|
|
|
|
|
bank_side.append(getattr(self,attr_name)[0])
|
|
|
|
|
|
|
|
|
|
for i in range(len(control_side)):
|
|
|
|
|
self.add_rect(layer="metal3",
|
|
|
|
|
offset=control_side[i],
|
|
|
|
|
width=bank_side[i].x - control_side[i].x,
|
|
|
|
|
height=drc["minwidth_metal3"])
|
|
|
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=[bank_side[i].x + drc["minwidth_metal2"],
|
|
|
|
|
control_side[i].y],
|
|
|
|
|
mirror="R90")
|
|
|
|
|
elif (self.num_banks == 2 or self.num_banks == 4):
|
|
|
|
|
for i in range(self.control_size):
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=[self.vertical_line_positions[i].x + drc["minwidth_metal2"],
|
|
|
|
|
self.control_bus_line_positions[i].y],
|
|
|
|
|
mirror="R90")
|
|
|
|
|
control_attr = self.bank_property[i]
|
|
|
|
|
control_side_line_position = (getattr(self.control,control_attr)
|
|
|
|
|
+self.control_position)
|
|
|
|
|
|
|
|
|
|
self.add_rect(layer="metal2",
|
|
|
|
|
offset=[control_side_line_position.x,
|
|
|
|
|
self.control_bus_line_positions[i].y],
|
|
|
|
|
width=drc["minwidth_metal2"],
|
|
|
|
|
height=control_side_line_position.y
|
|
|
|
|
- self.control_bus_line_positions[i].y)
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=[control_side_line_position.x
|
|
|
|
|
+ drc["minwidth_metal2"],
|
|
|
|
|
self.control_bus_line_positions[i].y],
|
|
|
|
|
mirror="R90")
|
|
|
|
|
for i in range(self.num_banks/2):
|
|
|
|
|
# MSB line connections
|
|
|
|
|
msb_line = self.control_size + self.num_banks/2 - 1 - i
|
|
|
|
|
bank_select_start_line = msb_line + 2 + self.bank_addr_size
|
|
|
|
|
|
|
|
|
|
msf_msb_din = (self.msf_msb_address.din_positions[i].rotate().scale(1, -1)
|
|
|
|
|
+ self.msf_msb_address_position)
|
|
|
|
|
|
|
|
|
|
contact_pos = [self.vertical_line_positions[msb_line].x,
|
|
|
|
|
msf_msb_din.y - 0.5*self.m2m3_via.width]
|
|
|
|
|
self.add_rect(layer="metal3",
|
|
|
|
|
offset=contact_pos,
|
|
|
|
|
width=msf_msb_din[0] - contact_pos[0],
|
|
|
|
|
height=drc["minwidth_metal3"])
|
|
|
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=contact_pos)
|
|
|
|
|
|
|
|
|
|
# msf_msb_address clk connection
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=[self.vertical_line_positions[0].x,
|
|
|
|
|
self.control_bus_line_positions[0].y],
|
|
|
|
|
width=self.msf_msb_address_position.x
|
|
|
|
|
+ self.msf_msb_address.clk_positions[0].y
|
|
|
|
|
- self.vertical_line_positions[0].x,
|
|
|
|
|
height=drc["minwidth_metal1"])
|
|
|
|
|
|
|
|
|
|
if(self.num_banks == 2):
|
|
|
|
|
msb_msf_dout_position = (self.msf_msb_address.dout_positions[i].rotate().scale(1, -1)
|
|
|
|
|
+ self.msf_msb_address_position)
|
|
|
|
|
msb_msf_dout_bar_position = (self.msf_msb_address.dout_bar_positions[i].rotate().scale(1, -1)
|
|
|
|
|
+ self.msf_msb_address_position)
|
|
|
|
|
starts = [msb_msf_dout_bar_position,
|
|
|
|
|
msb_msf_dout_position]
|
|
|
|
|
|
|
|
|
|
for i in range(2):
|
|
|
|
|
bank_select_line = (self.control_size + 1
|
|
|
|
|
+ self.bank_addr_size + i)
|
|
|
|
|
|
|
|
|
|
start = starts[i]
|
|
|
|
|
mid1 = vector(self.msf_msb_address_position.x
|
|
|
|
|
+ self.msf_msb_address.height
|
|
|
|
|
+ 4 * (i + 1) * drc["minwidth_metal2"],
|
|
|
|
|
start.y)
|
|
|
|
|
end = vector(mid1.x, self.msf_msb_address_position[1]
|
|
|
|
|
+ 4 * (i + 1) * drc["minwidth_metal2"])
|
|
|
|
|
self.add_wire(("metal1", "via1", "metal2"), [start, mid1, end])
|
|
|
|
|
|
|
|
|
|
x_off = self.vertical_line_positions[bank_select_line].x
|
|
|
|
|
contact_pos = vector(x_off,
|
|
|
|
|
end.y - drc["minwidth_metal1"])
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=contact_pos,
|
|
|
|
|
width=end.x - contact_pos.x
|
|
|
|
|
+ 0.5 * drc["minwidth_metal1"],
|
|
|
|
|
height=drc["minwidth_metal1"])
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=contact_pos)
|
|
|
|
|
|
|
|
|
|
if(self.num_banks == 4):
|
|
|
|
|
for i in range(2):
|
|
|
|
|
msb_msf_out_position = (self.msf_msb_address.dout_positions[i].rotate().scale(1, -1)
|
|
|
|
|
+ self.msf_msb_address_position)
|
|
|
|
|
msb_decoder_in_position =(self.msb_decoder.A_positions[i].scale(-1, 1)
|
|
|
|
|
+ self.msb_decoder_position
|
|
|
|
|
+ vector(0, 0.5 * drc["minwidth_metal1"]))
|
|
|
|
|
|
|
|
|
|
start = msb_msf_out_position
|
|
|
|
|
mid1 = start + vector(4 * (i + 1) * drc["minwidth_metal1"], 0)
|
|
|
|
|
mid2 = vector(mid1.x, msb_decoder_in_position.y)
|
|
|
|
|
end = vector(self.msb_decoder_position[0]
|
|
|
|
|
+ 3*drc["minwidth_metal3"],
|
|
|
|
|
mid2.y)
|
|
|
|
|
|
|
|
|
|
layer_stack = ("metal1", "via1", "metal2")
|
|
|
|
|
self.add_wire(layer_stack, [start, mid1, mid2, end])
|
|
|
|
|
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=[msb_decoder_in_position.x,
|
|
|
|
|
msb_decoder_in_position.y - 0.5 * drc["minwidth_metal1"]],
|
|
|
|
|
width=end.x - msb_decoder_in_position.x,
|
|
|
|
|
height=drc["minwidth_metal1"])
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=end - vector(0, 0.5 * self.m1m2_via.width),
|
|
|
|
|
mirror="R90")
|
|
|
|
|
for i in range(4):
|
|
|
|
|
bank_select_line = self.control_size + 2 + self.bank_addr_size + i
|
|
|
|
|
msb_decoder_out = (self.msb_decoder_position
|
|
|
|
|
+ self.msb_decoder.decode_out_positions[i].scale(-1, 1)
|
|
|
|
|
+ vector(0, 0.5*drc["minwidth_metal1"]))
|
|
|
|
|
|
|
|
|
|
x_off = self.vertical_line_positions[bank_select_line].x
|
|
|
|
|
contact_pos = vector(x_off,
|
|
|
|
|
msb_decoder_out.y - 0.5*drc["minwidth_metal1"])
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=contact_pos,
|
|
|
|
|
width=msf_msb_din.x - contact_pos.x,
|
|
|
|
|
height=drc["minwidth_metal1"])
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=contact_pos)
|
|
|
|
|
|
|
|
|
|
def route_vdd_singlebank(self):
|
|
|
|
|
""" Route the vdd for 1 bank SRAMs """
|
|
|
|
|
|
|
|
|
|
# left vdd rail of bank
|
|
|
|
|
self.vdd_offset = self.bank.left_vdd_position
|
|
|
|
|
self.add_label(text="vdd",
|
|
|
|
|
layer="metal1",
|
|
|
|
|
offset=self.vdd_offset)
|
|
|
|
|
|
|
|
|
|
# Add label for right vdd rail bank
|
|
|
|
|
self.add_label(text="vdd",
|
|
|
|
|
layer="metal1",
|
|
|
|
|
offset=self.sram_bank_right_vdd_positions[0])
|
|
|
|
|
|
|
|
|
|
# control logic
|
|
|
|
|
self.control_vdd1_position = (self.control_position
|
|
|
|
|
+ self.control.vdd1_position.rotate().scale(-1, 1))
|
|
|
|
|
self.control_vdd2_position = (self.control_position
|
|
|
|
|
+ self.control.vdd2_position.rotate().scale(-1, 1))
|
|
|
|
|
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=self.control_vdd1_position,
|
|
|
|
|
width=self.vdd_offset.x
|
|
|
|
|
- self.control_vdd1_position.x,
|
|
|
|
|
height=drc["minwidth_metal1"])
|
|
|
|
|
|
|
|
|
|
self.add_rect(layer="metal2",
|
|
|
|
|
offset=self.control_vdd2_position,
|
|
|
|
|
width=self.vdd_offset.x
|
|
|
|
|
- self.control_vdd2_position.x,
|
|
|
|
|
height=drc["minwidth_metal2"])
|
|
|
|
|
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=[self.vdd_offset.x,
|
|
|
|
|
self.control_vdd2_position.y],
|
|
|
|
|
size=(2, 1))
|
|
|
|
|
|
|
|
|
|
def route_vdd_multibank(self):
|
|
|
|
|
""" Route the vdd for 2 and 4 bank SRAMs """
|
|
|
|
|
# VDD routing between banks
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=self.vdd_position,
|
|
|
|
|
width=self.width,
|
|
|
|
|
height=self.power_rail_width)
|
|
|
|
|
for bank_index in range(2):
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=[self.sram_bank_right_vdd_positions[bank_index].x,
|
|
|
|
|
self.vdd_position.y],
|
|
|
|
|
width=self.power_rail_width,
|
|
|
|
|
height=self.sram_bank_right_vdd_positions[bank_index].y
|
|
|
|
|
- self.vdd_position.y)
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=[self.sram_bank_left_vdd_positions[bank_index].x,
|
|
|
|
|
self.vdd_position.y],
|
|
|
|
|
width=self.power_rail_width,
|
|
|
|
|
height=self.sram_bank_left_vdd_positions[bank_index].y
|
|
|
|
|
- self.vdd_position.y)
|
|
|
|
|
|
|
|
|
|
# VDD routing to control
|
|
|
|
|
control_vdd_supply = self.control_bus_line_positions[self.control_size + 1]
|
|
|
|
|
control_vdd1_position = self.control_position + self.control.vdd1_position
|
|
|
|
|
control_vdd2_position = self.control_position + self.control.vdd2_position
|
|
|
|
|
|
|
|
|
|
# rail extension
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=self.sram_bank_right_vdd_positions[0],
|
|
|
|
|
width=self.power_rail_width,
|
|
|
|
|
height=control_vdd_supply.y
|
|
|
|
|
- self.sram_bank_right_vdd_positions[0].y)
|
|
|
|
|
|
|
|
|
|
# Control vdd1
|
|
|
|
|
if (self.control.width <= self.bank.width):
|
|
|
|
|
self.add_rect(layer="metal2",
|
|
|
|
|
offset=[control_vdd1_position.x,
|
|
|
|
|
control_vdd_supply.y],
|
|
|
|
|
width=drc["minwidth_metal2"],
|
|
|
|
|
height=control_vdd1_position.y
|
|
|
|
|
- control_vdd_supply.y)
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=control_vdd1_position)
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=[control_vdd1_position.x + drc["minwidth_metal2"],
|
|
|
|
|
control_vdd_supply.y],
|
|
|
|
|
mirror="R90")
|
|
|
|
|
|
|
|
|
|
if (self.control.width > self.bank.width):
|
|
|
|
|
last_bank = self.num_banks - 1
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=control_vdd1_position,
|
|
|
|
|
width=self.sram_bank_right_vdd_positions[last_bank].x
|
|
|
|
|
- control_vdd1_position.x,
|
|
|
|
|
height=drc["minwidth_metal2"])
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=self.sram_bank_right_vdd_positions[last_bank],
|
|
|
|
|
width=10*drc["minwidth_metal2"],
|
|
|
|
|
height=control_vdd1_position.y
|
|
|
|
|
- self.sram_bank_right_vdd_positions[last_bank].y
|
|
|
|
|
+ drc["minwidth_metal2"])
|
|
|
|
|
|
|
|
|
|
# Control vdd2
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=[control_vdd2_position.x
|
|
|
|
|
+ drc["minwidth_metal2"],
|
|
|
|
|
control_vdd_supply.y],
|
|
|
|
|
mirror="R90")
|
|
|
|
|
|
|
|
|
|
self.add_layout_pin(text="vdd",
|
|
|
|
|
layer="metal2",
|
|
|
|
|
offset=[control_vdd2_position.x,
|
|
|
|
|
control_vdd_supply.y],
|
|
|
|
|
width=drc["minwidth_metal2"],
|
|
|
|
|
height=control_vdd2_position.y
|
|
|
|
|
- control_vdd_supply.y)
|
|
|
|
|
|
|
|
|
|
# msf_msb_address
|
|
|
|
|
start = msf_address_vdd_position = (self.msf_msb_address_position
|
|
|
|
|
+ self.msf_msb_address.vdd_positions[0].rotate().scale(1,-1))
|
|
|
|
|
mid1 = vector(start.x,
|
|
|
|
|
self.msf_msb_address_position.y
|
|
|
|
|
- self.msf_msb_address.width
|
|
|
|
|
- 2*drc["minwidth_metal3"])
|
|
|
|
|
end = vector(self.sram_bank_left_vdd_positions[1].x,
|
|
|
|
|
mid1.y)
|
|
|
|
|
|
|
|
|
|
self.add_path("metal1", [start, mid1, end])
|
|
|
|
|
|
|
|
|
|
# rail extension
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=self.sram_bank_left_vdd_positions[1],
|
|
|
|
|
width=self.power_rail_width,
|
|
|
|
|
height=end.y - self.sram_bank_left_vdd_positions[1].y)
|
|
|
|
|
|
|
|
|
|
if(self.num_banks == 4):
|
|
|
|
|
# msf_msb and msb_decoder VDD
|
|
|
|
|
start = (self.msb_decoder_position
|
|
|
|
|
+ self.msb_decoder.vdd_position.scale(-1, 1)
|
|
|
|
|
+ vector(0, 0.5*drc["minwidth_metal1"]))
|
|
|
|
|
mid1 = vector(msf_address_vdd_position.x,
|
|
|
|
|
start.y)
|
|
|
|
|
end = msf_address_vdd_position
|
|
|
|
|
|
|
|
|
|
self.add_path("metal1", [start, mid1, end])
|
|
|
|
|
|
|
|
|
|
# Add vdd labels to horizotal and vertical lines
|
|
|
|
|
self.add_label(text="vdd",
|
|
|
|
|
layer="metal1",
|
|
|
|
|
offset=self.vdd_position)
|
|
|
|
|
self.add_label(text="vdd",
|
|
|
|
|
layer="metal1",
|
|
|
|
|
offset=[self.sram_bank_left_vdd_positions[0].x,
|
|
|
|
|
self.vdd_position.y])
|
|
|
|
|
self.add_label(text="vdd",
|
|
|
|
|
layer="metal1",
|
|
|
|
|
offset=[self.sram_bank_left_vdd_positions[1].x,
|
|
|
|
|
self.vdd_position.y])
|
|
|
|
|
self.add_label(text="vdd",
|
|
|
|
|
layer="metal1",
|
|
|
|
|
offset=[self.sram_bank_right_vdd_positions[1].x,
|
|
|
|
|
self.vdd_position.y])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def route_gnd_singlebank(self):
|
|
|
|
|
""" Route the gnd for 1 bank SRAMs """
|
|
|
|
|
|
|
|
|
|
# left gnd rail of bank
|
|
|
|
|
self.gnd_offset = self.bank.left_gnd_position
|
|
|
|
|
self.add_label(text="gnd",
|
|
|
|
|
layer="metal2",
|
|
|
|
|
offset=self.gnd_offset)
|
|
|
|
|
|
|
|
|
|
self.control_gnd_position = (self.control_position
|
|
|
|
|
+ self.control.gnd_position.rotate().scale(-1,1)
|
|
|
|
|
+ vector(drc["minwidth_metal2"],0))
|
|
|
|
|
|
|
|
|
|
self.add_rect(layer="metal3",
|
|
|
|
|
offset=self.control_gnd_position,
|
|
|
|
|
width=self.gnd_offset.x - self.control_gnd_position.x,
|
|
|
|
|
height=drc["minwidth_metal3"])
|
|
|
|
|
|
|
|
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=[self.gnd_offset.x,
|
|
|
|
|
self.control_gnd_position.y],
|
|
|
|
|
size=(2,1))
|
|
|
|
|
|
|
|
|
|
self.add_via(layers=("metal2", "via2", "metal3"),
|
|
|
|
|
offset=self.control_gnd_position,
|
|
|
|
|
rotate=90)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def route_gnd_multibank(self):
|
|
|
|
|
""" Route the gnd for 2 and 4 bank SRAMs """
|
|
|
|
|
self.add_rect(layer="metal2",
|
|
|
|
|
offset=self.gnd_position,
|
|
|
|
|
width=self.width,
|
|
|
|
|
height=self.power_rail_width)
|
|
|
|
|
|
|
|
|
|
for bank_index in range(2):
|
|
|
|
|
self.add_rect(layer="metal2",
|
|
|
|
|
offset=[self.sram_bank_left_gnd_positions[bank_index].x,
|
|
|
|
|
self.gnd_position.y],
|
|
|
|
|
width=self.power_rail_width,
|
|
|
|
|
height=self.sram_bank_left_gnd_positions[bank_index].y
|
|
|
|
|
- self.gnd_position.y)
|
|
|
|
|
|
|
|
|
|
# gnd routing to control
|
|
|
|
|
control_gnd_supply = self.control_bus_line_positions[self.control_size]
|
|
|
|
|
control_gnd_position = self.control_position + self.control.gnd_position
|
|
|
|
|
|
|
|
|
|
# rail extension
|
|
|
|
|
self.add_rect(layer="metal2",
|
|
|
|
|
offset=self.sram_bank_left_gnd_positions[0],
|
|
|
|
|
width=drc["minwidth_metal2"],
|
|
|
|
|
height=control_gnd_supply[1] + drc["minwidth_metal1"]
|
|
|
|
|
- self.sram_bank_left_gnd_positions[0].y)
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=[self.sram_bank_left_gnd_positions[0].x
|
|
|
|
|
+ drc["minwidth_metal2"],
|
|
|
|
|
control_gnd_supply[1]],
|
|
|
|
|
mirror="R90")
|
|
|
|
|
# Control gnd
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=[control_gnd_position.x + drc["minwidth_metal2"],
|
|
|
|
|
control_gnd_supply.y],
|
|
|
|
|
mirror="R90")
|
|
|
|
|
self.add_layout_pin(text="gnd",
|
|
|
|
|
layer="metal2",
|
|
|
|
|
offset=[control_gnd_position.x,
|
|
|
|
|
control_gnd_supply.y],
|
|
|
|
|
width=drc["minwidth_metal2"],
|
|
|
|
|
height=control_gnd_position.y
|
|
|
|
|
- control_gnd_supply.y)
|
|
|
|
|
|
|
|
|
|
# msf_msb_address , msb_decoder gnd
|
|
|
|
|
|
|
|
|
|
# left gnd rail extension of bank3
|
|
|
|
|
self.add_rect(layer="metal2",
|
|
|
|
|
offset=self.sram_bank_left_gnd_positions[1],
|
|
|
|
|
width=self.power_rail_width,
|
|
|
|
|
height=self.max_point
|
|
|
|
|
- self.sram_bank_left_gnd_positions[1].y)
|
|
|
|
|
|
|
|
|
|
for p in self.msf_msb_address.gnd_positions:
|
|
|
|
|
gnd_position = vector(self.msf_msb_address_position.x
|
|
|
|
|
+ self.msf_msb_address.height,
|
|
|
|
|
self.msf_msb_address_position.y
|
|
|
|
|
- p.x - 0.5*drc["minwidth_metal2"])
|
|
|
|
|
self.add_rect(layer="metal2",
|
|
|
|
|
offset=gnd_position,
|
|
|
|
|
width=(self.sram_bank_left_gnd_positions[1].x
|
|
|
|
|
- gnd_position.x),
|
|
|
|
|
height=drc["minwidth_metal2"])
|
|
|
|
|
|
|
|
|
|
if(self.num_banks == 4):
|
|
|
|
|
# msb Decoder
|
|
|
|
|
msb_decoder_gnd_position = (self.msb_decoder_position
|
|
|
|
|
+ self.msb_decoder.gnd_position.scale(-1,1))
|
|
|
|
|
self.add_rect(layer="metal1",
|
|
|
|
|
offset=msb_decoder_gnd_position,
|
|
|
|
|
width=self.sram_bank_left_gnd_positions[3].x \
|
|
|
|
|
- msb_decoder_gnd_position.x \
|
|
|
|
|
+ self.power_rail_width,
|
|
|
|
|
height=drc["minwidth_metal1"])
|
|
|
|
|
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=[self.sram_bank_left_gnd_positions[3].x,
|
|
|
|
|
msb_decoder_gnd_position.y + drc["minwidth_metal1"]],
|
|
|
|
|
mirror="MX",
|
|
|
|
|
size=(2,1))
|
|
|
|
|
|
|
|
|
|
# Add gnd labels to horizotal and vertical lines
|
|
|
|
|
self.add_label(text="gnd",
|
|
|
|
|
layer="metal2",
|
|
|
|
|
offset=self.gnd_position)
|
|
|
|
|
self.add_label(text="gnd",
|
|
|
|
|
layer="metal2",
|
|
|
|
|
offset=[self.sram_bank_left_gnd_positions[0].x,
|
|
|
|
|
0])
|
|
|
|
|
self.add_label(text="gnd",
|
|
|
|
|
layer="metal2",
|
|
|
|
|
offset=[self.sram_bank_left_gnd_positions[1].x,
|
|
|
|
|
0])
|
|
|
|
|
|
|
|
|
|
def route_supplies(self):
|
|
|
|
|
""" vdd/gnd routing of all modules """
|
|
|
|
|
|
|
|
|
|
if (self.num_banks == 1):
|
|
|
|
|
self.route_vdd_singlebank()
|
|
|
|
|
self.route_gnd_singlebank()
|
|
|
|
|
elif (self.num_banks == 2 or self.num_banks == 4):
|
|
|
|
|
self.route_vdd_multibank()
|
|
|
|
|
self.route_gnd_multibank()
|
|
|
|
|
else:
|
|
|
|
|
debug.error("Incorrect number of banks.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def sp_write(self, sp_name):
|
|
|
|
|
# Write the entire spice of the object to the file
|
|
|
|
|
############################################################
|
|
|
|
|
# Spice circuit
|
|
|
|
|
############################################################
|
|
|
|
|
sp = open(sp_name, 'w')
|
|
|
|
|
|
|
|
|
|
sp.write("* OpenRAM generated memory.\n")
|
|
|
|
|
# This causes unit test mismatch
|
|
|
|
|
#sp.write("* Created: {0}\n".format(datetime.datetime.now()))
|
|
|
|
|
sp.write("* User: {0}\n".format(getpass.getuser()))
|
|
|
|
|
sp.write(".global {0} {1}\n".format(spice["vdd_name"],
|
|
|
|
|
spice["gnd_name"]))
|
|
|
|
|
usedMODS = list()
|
|
|
|
|
self.sp_write_file(sp, usedMODS)
|
|
|
|
|
del usedMODS
|
|
|
|
|
sp.close()
|