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):
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
c = reload(__import__(OPTS.config.control_logic))
|
|
|
|
|
self.mod_control_logic = getattr(c, OPTS.config.control_logic)
|
|
|
|
|
|
|
|
|
|
c = reload(__import__(OPTS.config.ms_flop_array))
|
|
|
|
|
self.mod_ms_flop_array = getattr(c, OPTS.config.ms_flop_array)
|
|
|
|
|
|
|
|
|
|
c = reload(__import__(OPTS.config.bitcell))
|
|
|
|
|
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
|
|
|
|
self.bitcell = self.mod_bitcell()
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-06-02 20:11:57 +02:00
|
|
|
# reset the static duplicate name checker for unit tests
|
|
|
|
|
# in case we create more than one SRAM
|
|
|
|
|
import design
|
|
|
|
|
design.design.name_map=[]
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
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)
|
2017-09-14 00:46:41 +02:00
|
|
|
|
|
|
|
|
# These aren't for instantiating, but we use them to get the dimensions
|
|
|
|
|
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
|
|
|
|
|
self.poly_contact_offset = vector(0.5*self.poly_contact.width,0.5*self.poly_contact.height)
|
|
|
|
|
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
|
|
|
|
|
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
|
|
|
|
|
|
|
|
|
|
# M1/M2 routing pitch is based on contacted pitch
|
|
|
|
|
self.m1_pitch = max(self.m1m2_via.width,self.m1m2_via.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
|
|
|
|
|
self.m2_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
self.control_size = 6
|
|
|
|
|
self.bank_to_bus_distance = 5*drc["minwidth_metal3"]
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.compute_sizes()
|
|
|
|
|
self.add_pins()
|
|
|
|
|
self.create_layout()
|
|
|
|
|
self.DRC_LVS()
|
|
|
|
|
|
|
|
|
|
def compute_sizes(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" Computes the organization of the memory using bitcell size by trying to make it square."""
|
2017-09-14 00:46:41 +02:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.")
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
self.num_words_per_bank = self.num_words/self.num_banks
|
|
|
|
|
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
|
|
|
|
|
self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank
|
2016-11-08 18:57:35 +01:00
|
|
|
self.bank_side_length = math.sqrt(self.bank_area)
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# Estimate the words per row given the height of the bitcell and the square side length
|
|
|
|
|
self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width)
|
|
|
|
|
self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size)
|
|
|
|
|
|
|
|
|
|
# Estimate the number of rows given the tentative words per row
|
|
|
|
|
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)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# Fix the number of columns and rows
|
2016-11-08 18:57:35 +01:00
|
|
|
self.num_cols = self.words_per_row*self.word_size
|
|
|
|
|
self.num_rows = self.num_words_per_bank/self.words_per_row
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
# Compute the address and bank sizes
|
2016-11-08 18:57:35 +01:00
|
|
|
self.row_addr_size = int(log(self.num_rows, 2))
|
|
|
|
|
self.col_addr_size = int(log(self.words_per_row, 2))
|
2017-08-24 00:02:15 +02:00
|
|
|
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))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def estimate_words_per_row(self,tentative_num_cols, word_size):
|
|
|
|
|
"""This provides a heuristic rounded estimate for the number of words
|
|
|
|
|
per row."""
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
if tentative_num_cols < 1.5*word_size:
|
|
|
|
|
return 1
|
|
|
|
|
elif tentative_num_cols > 3*word_size:
|
|
|
|
|
return 4
|
2016-11-08 18:57:35 +01:00
|
|
|
else:
|
2017-08-24 00:02:15 +02:00
|
|
|
return 2
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def amend_words_per_row(self,tentative_num_rows, words_per_row):
|
2017-08-24 00:02:15 +02:00
|
|
|
"""This picks the number of words per row more accurately by limiting
|
|
|
|
|
it to a minimum and maximum.
|
|
|
|
|
"""
|
|
|
|
|
# Recompute the words per row given a hard max
|
2016-11-08 18:57:35 +01:00
|
|
|
if(tentative_num_rows > 512):
|
2017-08-24 00:02:15 +02:00
|
|
|
debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048")
|
|
|
|
|
return words_per_row*tentative_num_rows/512
|
|
|
|
|
# Recompute the words per row given a hard min
|
2016-11-08 18:57:35 +01:00
|
|
|
if(tentative_num_rows < 16):
|
2017-08-24 00:02:15 +02:00
|
|
|
debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
|
|
|
|
|
return words_per_row*tentative_num_rows/16
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
return words_per_row
|
|
|
|
|
|
|
|
|
|
def add_pins(self):
|
2017-09-14 00:46:41 +02:00
|
|
|
""" Add pins for entire SRAM. """
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
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))
|
2017-09-14 00:46:41 +02:00
|
|
|
for pin in ["CSb","WEb","OEb","clk","vdd","gnd"]:
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_pin(pin)
|
|
|
|
|
|
|
|
|
|
def create_layout(self):
|
|
|
|
|
""" Layout creation """
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.create_modules()
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
if self.num_banks == 1:
|
|
|
|
|
self.add_single_bank_modules()
|
|
|
|
|
self.add_single_bank_pins()
|
|
|
|
|
self.route_single_bank()
|
|
|
|
|
else:
|
|
|
|
|
self.add_multi_bank_modules()
|
|
|
|
|
self.route_top_banks()
|
|
|
|
|
if self.num_banks > 2:
|
|
|
|
|
self.route_bottom_banks()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_multi_bank_modules(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
""" Add the multibank address flops and bank decoder """
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.msf_msb_address = self.mod_ms_flop_array(name="msf_msb_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,
|
2017-06-02 20:11:57 +02:00
|
|
|
name="bank")
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_mod(self.bank)
|
|
|
|
|
|
|
|
|
|
# Conditionally create the
|
|
|
|
|
if(self.num_banks > 1):
|
|
|
|
|
self.create_multibank_modules()
|
|
|
|
|
|
|
|
|
|
self.bank_count = 0
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
self.power_rail_width = self.bank.vdd_rail_width
|
|
|
|
|
self.sram_power_rail_gap = 4*self.bank.vdd_rail_width
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_bank(self, position, x_flip, y_flip):
|
2017-09-14 00:46:41 +02:00
|
|
|
""" Place a bank at the given position with orientations """
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
if x_flip == -1 and y_flip == -1:
|
|
|
|
|
bank_rotation = 180
|
|
|
|
|
else:
|
|
|
|
|
bank_rotation = 0
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
if x_flip == y_flip:
|
2016-11-08 18:57:35 +01:00
|
|
|
bank_mirror = "R0"
|
2017-09-14 00:46:41 +02:00
|
|
|
elif x_flip == -1:
|
2016-11-08 18:57:35 +01:00
|
|
|
bank_mirror = "MX"
|
2017-09-14 00:46:41 +02:00
|
|
|
elif y_flip == -1:
|
2016-11-08 18:57:35 +01:00
|
|
|
bank_mirror = "MY"
|
2017-09-14 00:46:41 +02:00
|
|
|
else:
|
|
|
|
|
bank_mirror = "R0"
|
|
|
|
|
|
|
|
|
|
bank_inst=self.add_inst(name="bank{0}".format(self.bank_count),
|
|
|
|
|
mod=self.bank,
|
|
|
|
|
offset=position,
|
|
|
|
|
mirror=bank_mirror,
|
|
|
|
|
rotate=bank_rotation)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
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):
|
2017-09-14 00:46:41 +02:00
|
|
|
temp.append("bank_sel[{0}]".format(self.bank_count))
|
2017-08-24 00:02:15 +02:00
|
|
|
temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en",
|
2017-09-14 00:46:41 +02:00
|
|
|
"clk_bar","clk_buf" , "vdd", "gnd"])
|
2016-11-08 18:57:35 +01:00
|
|
|
self.connect_inst(temp)
|
|
|
|
|
|
|
|
|
|
self.bank_count = self.bank_count + 1
|
2017-09-14 00:46:41 +02:00
|
|
|
|
|
|
|
|
return bank_inst
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def add_control_logic(self, position, rotate):
|
2016-11-08 18:57:35 +01:00
|
|
|
""" Add and place control logic """
|
2017-09-14 00:46:41 +02:00
|
|
|
self.control_logic_inst=self.add_inst(name="control",
|
|
|
|
|
mod=self.control,
|
|
|
|
|
offset=position,
|
|
|
|
|
rotate=rotate)
|
|
|
|
|
temp = ["CSb", "WEb", "OEb", "clk", "s_en", "w_en", "tri_en",
|
|
|
|
|
"tri_en_bar", "clk_bar", "clk_buf", "vdd", "gnd"]
|
2016-11-08 18:57:35 +01:00
|
|
|
self.connect_inst(temp)
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
def add_single_bank_modules(self):
|
|
|
|
|
"""
|
|
|
|
|
This adds the moduels for a single bank SRAM with control
|
|
|
|
|
logic.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# No orientation or offset
|
|
|
|
|
self.bank_inst = self.add_bank([0, 0], 1, 1)
|
|
|
|
|
|
|
|
|
|
# Control logic is placed to the left of the blank even with the
|
|
|
|
|
# decoder bottom. A small gap is in the x-dimension.
|
|
|
|
|
control_gap = 2*drc["minwidth_metal3"]
|
|
|
|
|
pos = vector(-control_gap,
|
|
|
|
|
self.bank.row_decoder_inst.by() + 2*drc["minwidth_metal3"])
|
|
|
|
|
self.add_control_logic(position=pos,
|
|
|
|
|
rotate=90)
|
|
|
|
|
|
|
|
|
|
self.width = self.bank.width + self.control.height + control_gap
|
2016-11-08 18:57:35 +01:00
|
|
|
self.height = self.bank.height
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
def add_single_bank_pins(self):
|
|
|
|
|
"""
|
|
|
|
|
Add the top-level pins for a single bank SRAM with control.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
for i in range(self.word_size):
|
|
|
|
|
self.copy_layout_pin(self.bank_inst, "DATA[{}]".format(i))
|
|
|
|
|
|
|
|
|
|
for i in range(self.addr_size):
|
|
|
|
|
self.copy_layout_pin(self.bank_inst, "ADDR[{}]".format(i))
|
|
|
|
|
|
|
|
|
|
for (old,new) in zip(["csb","web","oeb","clk"],["CSb","WEb","OEb","clk"]):
|
|
|
|
|
self.copy_layout_pin(self.control_logic_inst, old, new)
|
|
|
|
|
|
|
|
|
|
self.copy_layout_pin(self.bank_inst, "vdd")
|
|
|
|
|
self.copy_layout_pin(self.bank_inst, "gnd")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_multi_bank_modules(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
""" 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 \
|
2017-08-24 00:02:15 +02:00
|
|
|
+ self.num_banks + self.num_banks/2
|
2016-11-08 18:57:35 +01:00
|
|
|
self.num_horizontal_line = self.word_size
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
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)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
self.vertical_bus_height = (self.num_banks/2)*(self.bank_h + self.bank_to_bus_distance) \
|
2017-08-24 00:02:15 +02:00
|
|
|
+ self.horizontal_bus_width
|
2016-11-08 18:57:35 +01:00
|
|
|
self.horizontal_bus_height = (2 * (self.bank_w + self.bank_to_bus_distance)
|
2017-08-24 00:02:15 +02:00
|
|
|
+ self.vertical_bus_width)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
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
|
2017-08-24 00:02:15 +02:00
|
|
|
+ self.sram_power_rail_gap
|
|
|
|
|
+ self.horizontal_bus_width)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# 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) \
|
2017-08-24 00:02:15 +02:00
|
|
|
+ self.horizontal_bus_width + self.sram_power_rail_gap
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# 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"])
|
2017-08-24 00:02:15 +02:00
|
|
|
self.add_control_logic(self.control_position, 0)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
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:
|
2017-09-14 00:46:41 +02:00
|
|
|
temp.extend(["bank_sel[1]", "bank_sel[0]"])
|
|
|
|
|
temp.extend(["clk_buf", "vdd", "gnd"])
|
2016-11-08 18:57:35 +01:00
|
|
|
self.connect_inst(temp)
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
self.add_top_banks()
|
|
|
|
|
if self.num_banks == 4:
|
|
|
|
|
self.add_bottom_banks()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# 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])
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
def add_top_banks(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
# 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)
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
def add_bottom_banks(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
# 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")
|
2017-09-14 00:46:41 +02:00
|
|
|
temp = ["msb0", "msb1"]
|
|
|
|
|
for i in range(4):
|
|
|
|
|
temp.append("bank_sel[{}]".format(i))
|
|
|
|
|
temp.extend(["vdd", "gnd"])
|
2016-11-08 18:57:35 +01:00
|
|
|
self.connect_inst(temp)
|
|
|
|
|
|
|
|
|
|
self.control_position = vector(0, self.msb_decoder_position.y
|
|
|
|
|
+ self.msb_decoder.height)
|
2017-08-24 00:02:15 +02:00
|
|
|
self.add_control_logic(self.control_position, 0)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
def route_top_banks(self):
|
|
|
|
|
""" Routing of top two banks """
|
2016-11-08 18:57:35 +01:00
|
|
|
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],
|
2016-11-11 23:33:19 +01:00
|
|
|
width=getattr(self,bank_attr)[right_bank_index].x - getattr(self,bank_attr)[left_bank_index].x,
|
2016-11-08 18:57:35 +01:00
|
|
|
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
|
2017-09-14 00:46:41 +02:00
|
|
|
if self.num_banks == 4:
|
2016-11-08 18:57:35 +01:00
|
|
|
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])
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
def route_bottom_banks(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
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)
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
def connect_rail_from_left_m2m3(self, src_pin, dest_pin):
|
|
|
|
|
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
|
|
|
|
in_pos = src_pin.rc()
|
|
|
|
|
out_pos = vector(dest_pin.cx(), in_pos.y)
|
|
|
|
|
self.add_wire(("metal3","via2","metal2"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
|
|
|
|
|
self.add_via(layers=("metal2","via2","metal3"),
|
|
|
|
|
offset=src_pin.lr(),
|
|
|
|
|
rotate=90)
|
|
|
|
|
|
|
|
|
|
def connect_rail_from_left_m2m1(self, src_pin, dest_pin):
|
|
|
|
|
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
|
|
|
|
in_pos = src_pin.rc()
|
|
|
|
|
out_pos = vector(dest_pin.cx(), in_pos.y)
|
|
|
|
|
self.add_wire(("metal2","via1","metal1"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
|
|
|
|
|
|
|
|
|
|
def route_single_bank(self):
|
|
|
|
|
""" Route a single bank SRAM """
|
|
|
|
|
# left pin is on the control logic, right pin is on the bank
|
|
|
|
|
connections = [("clk_buf", "clk"),
|
|
|
|
|
("tri_en_bar", "tri_en_bar"),
|
|
|
|
|
("tri_en", "tri_en"),
|
|
|
|
|
("clk_bar", "clk_bar"),
|
|
|
|
|
("w_en", "w_en"),
|
|
|
|
|
("s_en", "s_en")]
|
|
|
|
|
for (src,dest) in connections:
|
|
|
|
|
src_pin = self.control_logic_inst.get_pin(src)
|
|
|
|
|
dest_pin = self.bank_inst.get_pin(dest)
|
|
|
|
|
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
|
|
|
|
|
|
|
|
|
|
src_pins = self.control_logic_inst.get_pins("vdd")
|
|
|
|
|
for src_pin in src_pins:
|
|
|
|
|
if src_pin.layer != "metal2":
|
|
|
|
|
continue
|
|
|
|
|
dest_pin = self.bank_inst.get_pins("vdd")[1]
|
|
|
|
|
self.connect_rail_from_left_m2m1(src_pin,dest_pin)
|
|
|
|
|
|
|
|
|
|
src_pins = self.control_logic_inst.get_pins("gnd")
|
|
|
|
|
for src_pin in src_pins:
|
|
|
|
|
if src_pin.layer != "metal2":
|
|
|
|
|
continue
|
|
|
|
|
dest_pin = self.bank_inst.get_pin("gnd")
|
|
|
|
|
self.connect_rail_from_left_m2m3(src_pin,dest_pin)
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def route_bank_and_control(self):
|
|
|
|
|
""" Routing between banks and control """
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
if self.num_banks == 1:
|
|
|
|
|
pass
|
|
|
|
|
elif self.num_banks == 2 or self.num_banks == 4:
|
2016-11-08 18:57:35 +01:00
|
|
|
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],
|
2017-08-24 00:02:15 +02:00
|
|
|
rotate=90)
|
2016-11-08 18:57:35 +01:00
|
|
|
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],
|
2017-08-24 00:02:15 +02:00
|
|
|
rotate=90)
|
2016-11-08 18:57:35 +01:00
|
|
|
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
|
|
|
|
|
|
2016-11-11 23:33:19 +01:00
|
|
|
msf_msb_din = (self.msf_msb_address.din_positions[i].rotate_scale(1, -1)
|
2016-11-08 18:57:35 +01:00
|
|
|
+ self.msf_msb_address_position)
|
|
|
|
|
|
2016-11-11 23:33:19 +01:00
|
|
|
contact_pos = vector(self.vertical_line_positions[msb_line].x,
|
|
|
|
|
msf_msb_din.y - 0.5*self.m2m3_via.width)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_rect(layer="metal3",
|
|
|
|
|
offset=contact_pos,
|
2016-11-11 23:33:19 +01:00
|
|
|
width=msf_msb_din.x - contact_pos.x,
|
2016-11-08 18:57:35 +01:00
|
|
|
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):
|
2016-11-11 23:33:19 +01:00
|
|
|
msb_msf_dout_position = (self.msf_msb_address.dout_positions[i].rotate_scale(1, -1)
|
2016-11-08 18:57:35 +01:00
|
|
|
+ self.msf_msb_address_position)
|
2016-11-11 23:33:19 +01:00
|
|
|
msb_msf_dout_bar_position = (self.msf_msb_address.dout_bar_positions[i].rotate_scale(1, -1)
|
2016-11-08 18:57:35 +01:00
|
|
|
+ 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)
|
2016-11-11 23:33:19 +01:00
|
|
|
end = vector(mid1.x, self.msf_msb_address_position.y
|
2016-11-08 18:57:35 +01:00
|
|
|
+ 4 * (i + 1) * drc["minwidth_metal2"])
|
2016-11-17 23:05:50 +01:00
|
|
|
self.add_wire(("metal2", "via1", "metal1"), [start, mid1, end])
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
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):
|
2016-11-11 23:33:19 +01:00
|
|
|
msb_msf_out_position = (self.msf_msb_address.dout_positions[i].rotate_scale(1, -1)
|
2016-11-08 18:57:35 +01:00
|
|
|
+ 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)
|
2016-11-11 23:33:19 +01:00
|
|
|
end = vector(self.msb_decoder_position.x
|
2016-11-08 18:57:35 +01:00
|
|
|
+ 3*drc["minwidth_metal3"],
|
|
|
|
|
mid2.y)
|
|
|
|
|
|
2016-11-17 23:05:50 +01:00
|
|
|
layer_stack = ("metal2", "via1", "metal1")
|
2016-11-08 18:57:35 +01:00
|
|
|
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),
|
2017-08-24 00:02:15 +02:00
|
|
|
rotate=90)
|
2016-11-08 18:57:35 +01:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
def route_vdd_multi_bank(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
""" 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],
|
2017-08-24 00:02:15 +02:00
|
|
|
rotate=90)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
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],
|
2017-08-24 00:02:15 +02:00
|
|
|
rotate=90)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
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
|
2016-11-11 23:33:19 +01:00
|
|
|
+ self.msf_msb_address.vdd_positions[0].rotate_scale(1,-1))
|
2016-11-08 18:57:35 +01:00
|
|
|
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])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-09-14 00:46:41 +02:00
|
|
|
def route_gnd_multi_bank(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
""" 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"],
|
2016-11-11 23:33:19 +01:00
|
|
|
height=control_gnd_supply.y + drc["minwidth_metal1"]
|
2016-11-08 18:57:35 +01:00
|
|
|
- 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"],
|
2016-11-11 23:33:19 +01:00
|
|
|
control_gnd_supply.y],
|
2017-08-24 00:02:15 +02:00
|
|
|
rotate=90)
|
2016-11-08 18:57:35 +01:00
|
|
|
# Control gnd
|
|
|
|
|
self.add_via(layers=("metal1", "via1", "metal2"),
|
|
|
|
|
offset=[control_gnd_position.x + drc["minwidth_metal2"],
|
|
|
|
|
control_gnd_supply.y],
|
2017-08-24 00:02:15 +02:00
|
|
|
rotate=90)
|
2016-11-08 18:57:35 +01:00
|
|
|
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 """
|
2017-09-14 00:46:41 +02:00
|
|
|
return
|
2016-11-08 18:57:35 +01:00
|
|
|
if (self.num_banks == 1):
|
2017-09-14 00:46:41 +02:00
|
|
|
pass
|
2016-11-08 18:57:35 +01:00
|
|
|
elif (self.num_banks == 2 or self.num_banks == 4):
|
2017-09-14 00:46:41 +02:00
|
|
|
self.route_vdd_multi_bank()
|
|
|
|
|
self.route_gnd_multi_bank()
|
2016-11-08 18:57:35 +01:00
|
|
|
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()
|
2017-05-30 21:50:07 +02:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
def analytical_model(self,slews,loads):
|
|
|
|
|
LH_delay = []
|
|
|
|
|
HL_delay = []
|
|
|
|
|
LH_slew = []
|
|
|
|
|
HL_slew = []
|
|
|
|
|
for slew in slews:
|
|
|
|
|
for load in loads:
|
|
|
|
|
bank_delay = self.bank.delay(slew,load)
|
|
|
|
|
# Convert from ps to ns
|
|
|
|
|
LH_delay.append(bank_delay.delay/1e3)
|
|
|
|
|
HL_delay.append(bank_delay.delay/1e3)
|
|
|
|
|
LH_slew.append(bank_delay.slew/1e3)
|
|
|
|
|
HL_slew.append(bank_delay.slew/1e3)
|
|
|
|
|
|
|
|
|
|
data = {"min_period": 0,
|
|
|
|
|
"delay1": LH_delay,
|
|
|
|
|
"delay0": HL_delay,
|
|
|
|
|
"slew1": LH_slew,
|
|
|
|
|
"slew0": HL_slew,
|
|
|
|
|
"read0_power": 0,
|
|
|
|
|
"read1_power": 0,
|
|
|
|
|
"write0_power": 0,
|
|
|
|
|
"write1_power": 0
|
|
|
|
|
}
|
2017-05-30 21:50:07 +02:00
|
|
|
return data
|