2018-07-12 19:30:45 +02:00
|
|
|
import sys
|
|
|
|
|
import datetime
|
|
|
|
|
import getpass
|
|
|
|
|
import debug
|
2018-12-05 18:51:17 +01:00
|
|
|
from datetime import datetime
|
2018-08-31 21:03:28 +02:00
|
|
|
from importlib import reload
|
2018-07-12 19:30:45 +02:00
|
|
|
from vector import vector
|
|
|
|
|
from globals import OPTS, print_time
|
2018-11-09 05:47:34 +01:00
|
|
|
import logical_effort
|
2018-07-12 19:30:45 +02:00
|
|
|
from design import design
|
2019-01-11 23:15:16 +01:00
|
|
|
from verilog import verilog
|
|
|
|
|
from lef import lef
|
2019-01-17 01:15:38 +01:00
|
|
|
from sram_factory import factory
|
2019-01-11 23:15:16 +01:00
|
|
|
|
|
|
|
|
class sram_base(design, verilog, lef):
|
2018-07-12 19:30:45 +02:00
|
|
|
"""
|
|
|
|
|
Dynamically generated SRAM by connecting banks to control logic. The
|
|
|
|
|
number of banks should be 1 , 2 or 4
|
|
|
|
|
"""
|
2018-09-04 19:47:24 +02:00
|
|
|
def __init__(self, name, sram_config):
|
2018-07-12 19:30:45 +02:00
|
|
|
design.__init__(self, name)
|
2019-01-11 23:15:16 +01:00
|
|
|
lef.__init__(self, ["metal1", "metal2", "metal3"])
|
|
|
|
|
verilog.__init__(self)
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-09-04 19:47:24 +02:00
|
|
|
self.sram_config = sram_config
|
|
|
|
|
sram_config.set_local_config(self)
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-09-04 19:47:24 +02:00
|
|
|
self.bank_insts = []
|
2018-11-10 02:14:52 +01:00
|
|
|
|
|
|
|
|
#For logical effort delay calculations.
|
|
|
|
|
self.all_mods_except_control_done = False
|
2018-07-12 19:30:45 +02:00
|
|
|
|
|
|
|
|
def add_pins(self):
|
|
|
|
|
""" Add pins for entire SRAM. """
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.write_ports:
|
2018-09-04 02:44:32 +02:00
|
|
|
for bit in range(self.word_size):
|
2018-09-09 23:14:26 +02:00
|
|
|
self.add_pin("DIN{0}[{1}]".format(port,bit),"INPUT")
|
2018-09-04 02:44:32 +02:00
|
|
|
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-04 02:44:32 +02:00
|
|
|
for bit in range(self.addr_size):
|
|
|
|
|
self.add_pin("ADDR{0}[{1}]".format(port,bit),"INPUT")
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
# These are used to create the physical pins
|
|
|
|
|
self.control_logic_inputs = []
|
|
|
|
|
self.control_logic_outputs = []
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
|
|
|
|
if port in self.readwrite_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
self.control_logic_inputs.append(self.control_logic_rw.get_inputs())
|
|
|
|
|
self.control_logic_outputs.append(self.control_logic_rw.get_outputs())
|
2018-11-08 21:19:40 +01:00
|
|
|
elif port in self.write_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
self.control_logic_inputs.append(self.control_logic_w.get_inputs())
|
|
|
|
|
self.control_logic_outputs.append(self.control_logic_w.get_outputs())
|
|
|
|
|
else:
|
|
|
|
|
self.control_logic_inputs.append(self.control_logic_r.get_inputs())
|
|
|
|
|
self.control_logic_outputs.append(self.control_logic_r.get_outputs())
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
self.add_pin("csb{}".format(port),"INPUT")
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.readwrite_ports:
|
2018-09-09 23:00:51 +02:00
|
|
|
self.add_pin("web{}".format(port),"INPUT")
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
self.add_pin("clk{}".format(port),"INPUT")
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.read_ports:
|
2018-09-04 02:44:32 +02:00
|
|
|
for bit in range(self.word_size):
|
2018-11-08 21:19:40 +01:00
|
|
|
self.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT")
|
2018-07-12 19:30:45 +02:00
|
|
|
|
|
|
|
|
self.add_pin("vdd","POWER")
|
|
|
|
|
self.add_pin("gnd","GROUND")
|
|
|
|
|
|
|
|
|
|
|
2018-08-27 23:18:32 +02:00
|
|
|
def create_netlist(self):
|
2018-08-28 01:42:48 +02:00
|
|
|
""" Netlist creation """
|
2018-12-05 18:51:17 +01:00
|
|
|
|
|
|
|
|
start_time = datetime.now()
|
|
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
# Must create the control logic before pins to get the pins
|
2018-08-27 23:18:32 +02:00
|
|
|
self.add_modules()
|
|
|
|
|
self.add_pins()
|
2018-12-05 18:51:17 +01:00
|
|
|
self.create_modules()
|
2018-08-28 01:42:48 +02:00
|
|
|
|
|
|
|
|
# This is for the lib file if we don't create layout
|
|
|
|
|
self.width=0
|
|
|
|
|
self.height=0
|
2018-12-05 18:51:17 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if not OPTS.is_unit_test:
|
2018-12-06 20:59:20 +01:00
|
|
|
print_time("Submodules",datetime.now(), start_time)
|
2018-09-27 04:10:24 +02:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2018-07-12 19:30:45 +02:00
|
|
|
def create_layout(self):
|
|
|
|
|
""" Layout creation """
|
2018-12-05 18:51:17 +01:00
|
|
|
start_time = datetime.now()
|
2018-11-15 02:05:23 +01:00
|
|
|
self.place_instances()
|
2018-12-05 18:51:17 +01:00
|
|
|
if not OPTS.is_unit_test:
|
|
|
|
|
print_time("Placement",datetime.now(), start_time)
|
2018-11-15 02:05:23 +01:00
|
|
|
|
2018-12-05 18:51:17 +01:00
|
|
|
start_time = datetime.now()
|
2018-11-15 02:05:23 +01:00
|
|
|
self.route_layout()
|
2018-12-05 18:51:17 +01:00
|
|
|
self.route_supplies()
|
|
|
|
|
if not OPTS.is_unit_test:
|
|
|
|
|
print_time("Routing",datetime.now(), start_time)
|
2018-10-06 23:03:00 +02:00
|
|
|
|
2018-07-18 23:29:04 +02:00
|
|
|
self.add_lvs_correspondence_points()
|
2018-08-27 23:18:32 +02:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
self.offset_all_coordinates()
|
2018-10-06 23:03:00 +02:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
highest_coord = self.find_highest_coords()
|
|
|
|
|
self.width = highest_coord[0]
|
|
|
|
|
self.height = highest_coord[1]
|
2018-10-06 23:03:00 +02:00
|
|
|
|
2018-12-05 18:51:17 +01:00
|
|
|
start_time = datetime.now()
|
2018-08-28 01:42:48 +02:00
|
|
|
self.DRC_LVS(final_verification=True)
|
2018-12-05 18:51:17 +01:00
|
|
|
if not OPTS.is_unit_test:
|
|
|
|
|
print_time("Verification",datetime.now(), start_time)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2018-12-05 18:51:17 +01:00
|
|
|
def create_modules(self):
|
|
|
|
|
debug.error("Must override pure virtual function.",-1)
|
|
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
def route_supplies(self):
|
2018-10-06 17:30:38 +02:00
|
|
|
""" Route the supply grid and connect the pins to them. """
|
2018-10-08 18:56:39 +02:00
|
|
|
|
2018-10-06 17:30:38 +02:00
|
|
|
for inst in self.insts:
|
2018-10-08 18:56:39 +02:00
|
|
|
self.copy_power_pins(inst,"vdd")
|
|
|
|
|
self.copy_power_pins(inst,"gnd")
|
2018-10-06 17:30:38 +02:00
|
|
|
|
|
|
|
|
from supply_router import supply_router as router
|
|
|
|
|
layer_stack =("metal3","via3","metal4")
|
|
|
|
|
rtr=router(layer_stack, self)
|
|
|
|
|
rtr.route()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2018-07-12 19:30:45 +02:00
|
|
|
def compute_bus_sizes(self):
|
|
|
|
|
""" Compute the independent bus widths shared between two and four bank SRAMs """
|
|
|
|
|
|
|
|
|
|
# address size + control signals + one-hot bank select signals
|
|
|
|
|
self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks,2) + 1
|
|
|
|
|
# data bus size
|
|
|
|
|
self.num_horizontal_line = self.word_size
|
|
|
|
|
|
|
|
|
|
self.vertical_bus_width = self.m2_pitch*self.num_vertical_line
|
|
|
|
|
# vertical bus height depends on 2 or 4 banks
|
|
|
|
|
|
|
|
|
|
self.data_bus_height = self.m3_pitch*self.num_horizontal_line
|
|
|
|
|
self.data_bus_width = 2*(self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
|
|
|
|
|
|
|
|
|
|
self.control_bus_height = self.m1_pitch*(self.control_size+2)
|
|
|
|
|
self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width
|
|
|
|
|
|
|
|
|
|
self.supply_bus_height = self.m1_pitch*2 # 2 for vdd/gnd placed with control bus
|
|
|
|
|
self.supply_bus_width = self.data_bus_width
|
|
|
|
|
|
|
|
|
|
# Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really)
|
|
|
|
|
debug.check(self.bank.width + self.vertical_bus_width > 0.9*self.control_logic.width,
|
|
|
|
|
"Bank is too small compared to control logic.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_busses(self):
|
|
|
|
|
""" Add the horizontal and vertical busses """
|
|
|
|
|
# Vertical bus
|
|
|
|
|
# The order of the control signals on the control bus:
|
2018-09-27 04:10:24 +02:00
|
|
|
self.control_bus_names = []
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-11-27 03:00:59 +01:00
|
|
|
self.control_bus_names[port] = ["clk_buf{}".format(port)]
|
|
|
|
|
wen = "w_en{}".format(port)
|
|
|
|
|
sen = "s_en{}".format(port)
|
2018-11-27 23:44:55 +01:00
|
|
|
pen = "p_en_bar{}".format(port)
|
2018-11-27 03:00:59 +01:00
|
|
|
if self.port_id[port] == "r":
|
|
|
|
|
self.control_bus_names[port].extend([sen, pen])
|
|
|
|
|
elif self.port_id[port] == "w":
|
|
|
|
|
self.control_bus_names[port].extend([wen])
|
|
|
|
|
else:
|
|
|
|
|
self.control_bus_names[port].extend([sen, wen, pen])
|
2018-09-27 04:10:24 +02:00
|
|
|
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
|
|
|
|
|
pitch=self.m2_pitch,
|
|
|
|
|
offset=self.vertical_bus_offset,
|
|
|
|
|
names=self.control_bus_names[port],
|
|
|
|
|
length=self.vertical_bus_height)
|
|
|
|
|
|
|
|
|
|
self.addr_bus_names=["A{0}[{1}]".format(port,i) for i in range(self.addr_size)]
|
|
|
|
|
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
|
|
|
|
|
pitch=self.m2_pitch,
|
|
|
|
|
offset=self.addr_bus_offset,
|
|
|
|
|
names=self.addr_bus_names,
|
|
|
|
|
length=self.addr_bus_height))
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
|
2018-10-11 18:53:08 +02:00
|
|
|
self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port,i) for i in range(self.num_banks)]
|
2018-09-27 04:10:24 +02:00
|
|
|
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
|
|
|
|
|
pitch=self.m2_pitch,
|
|
|
|
|
offset=self.bank_sel_bus_offset,
|
|
|
|
|
names=self.bank_sel_bus_names,
|
|
|
|
|
length=self.vertical_bus_height))
|
|
|
|
|
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
# Horizontal data bus
|
|
|
|
|
self.data_bus_names = ["DATA{0}[{1}]".format(port,i) for i in range(self.word_size)]
|
|
|
|
|
self.data_bus_positions = self.create_horizontal_pin_bus(layer="metal3",
|
|
|
|
|
pitch=self.m3_pitch,
|
|
|
|
|
offset=self.data_bus_offset,
|
|
|
|
|
names=self.data_bus_names,
|
|
|
|
|
length=self.data_bus_width)
|
|
|
|
|
|
|
|
|
|
# Horizontal control logic bus
|
|
|
|
|
# vdd/gnd in bus go along whole SRAM
|
|
|
|
|
# FIXME: Fatten these wires?
|
|
|
|
|
self.horz_control_bus_positions = self.create_horizontal_bus(layer="metal1",
|
|
|
|
|
pitch=self.m1_pitch,
|
|
|
|
|
offset=self.supply_bus_offset,
|
|
|
|
|
names=["vdd"],
|
|
|
|
|
length=self.supply_bus_width)
|
|
|
|
|
# The gnd rail must not be the entire width since we protrude the right-most vdd rail up for
|
|
|
|
|
# the decoder in 4-bank SRAMs
|
|
|
|
|
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
|
|
|
|
|
pitch=self.m1_pitch,
|
|
|
|
|
offset=self.supply_bus_offset+vector(0,self.m1_pitch),
|
|
|
|
|
names=["gnd"],
|
|
|
|
|
length=self.supply_bus_width))
|
|
|
|
|
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
|
|
|
|
|
pitch=self.m1_pitch,
|
|
|
|
|
offset=self.control_bus_offset,
|
|
|
|
|
names=self.control_bus_names[port],
|
|
|
|
|
length=self.control_bus_width))
|
2018-07-12 19:30:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-08-27 23:18:32 +02:00
|
|
|
def add_multi_bank_modules(self):
|
2018-07-12 19:30:45 +02:00
|
|
|
""" Create the multibank address flops and bank decoder """
|
|
|
|
|
from dff_buf_array import dff_buf_array
|
|
|
|
|
self.msb_address = dff_buf_array(name="msb_address",
|
|
|
|
|
rows=1,
|
|
|
|
|
columns=self.num_banks/2)
|
|
|
|
|
self.add_mod(self.msb_address)
|
|
|
|
|
|
|
|
|
|
if self.num_banks>2:
|
|
|
|
|
self.msb_decoder = self.bank.decoder.pre2_4
|
|
|
|
|
self.add_mod(self.msb_decoder)
|
|
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
|
2018-08-27 23:18:32 +02:00
|
|
|
def add_modules(self):
|
2019-01-17 01:15:38 +01:00
|
|
|
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
2018-07-12 19:30:45 +02:00
|
|
|
|
|
|
|
|
# Create the address and control flops (but not the clk)
|
|
|
|
|
from dff_array import dff_array
|
2018-09-27 04:10:24 +02:00
|
|
|
self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size, columns=1)
|
2018-07-13 23:45:46 +02:00
|
|
|
self.add_mod(self.row_addr_dff)
|
|
|
|
|
|
|
|
|
|
if self.col_addr_size > 0:
|
2018-09-27 04:10:24 +02:00
|
|
|
self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size)
|
2018-07-13 23:45:46 +02:00
|
|
|
self.add_mod(self.col_addr_dff)
|
|
|
|
|
else:
|
|
|
|
|
self.col_addr_dff = None
|
|
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size)
|
2018-07-13 23:45:46 +02:00
|
|
|
self.add_mod(self.data_dff)
|
2018-07-12 19:30:45 +02:00
|
|
|
|
|
|
|
|
# Create the bank module (up to four are instantiated)
|
|
|
|
|
from bank import bank
|
2018-08-31 21:03:28 +02:00
|
|
|
self.bank = bank(self.sram_config,
|
2018-07-12 19:30:45 +02:00
|
|
|
name="bank")
|
|
|
|
|
self.add_mod(self.bank)
|
|
|
|
|
|
|
|
|
|
# Create bank decoder
|
|
|
|
|
if(self.num_banks > 1):
|
2018-08-27 23:18:32 +02:00
|
|
|
self.add_multi_bank_modules()
|
2018-07-12 19:30:45 +02:00
|
|
|
|
|
|
|
|
self.bank_count = 0
|
|
|
|
|
|
|
|
|
|
self.supply_rail_width = self.bank.supply_rail_width
|
|
|
|
|
self.supply_rail_pitch = self.bank.supply_rail_pitch
|
2018-11-10 02:14:52 +01:00
|
|
|
|
|
|
|
|
#The control logic can resize itself based on the other modules. Requires all other modules added before control logic.
|
|
|
|
|
self.all_mods_except_control_done = True
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-11-14 22:53:27 +01:00
|
|
|
c = reload(__import__(OPTS.control_logic))
|
|
|
|
|
self.mod_control_logic = getattr(c, OPTS.control_logic)
|
|
|
|
|
|
2018-11-10 02:14:52 +01:00
|
|
|
# Create the control logic module for each port type
|
2018-11-14 22:53:27 +01:00
|
|
|
if len(self.readwrite_ports)>0:
|
2019-01-23 21:03:52 +01:00
|
|
|
self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows,
|
2018-11-14 22:53:27 +01:00
|
|
|
words_per_row=self.words_per_row,
|
2019-01-23 21:03:52 +01:00
|
|
|
word_size=self.word_size,
|
2018-11-14 22:53:27 +01:00
|
|
|
sram=self,
|
|
|
|
|
port_type="rw")
|
2018-11-10 02:14:52 +01:00
|
|
|
self.add_mod(self.control_logic_rw)
|
2018-11-20 07:20:20 +01:00
|
|
|
if len(self.writeonly_ports)>0:
|
2018-11-14 22:53:27 +01:00
|
|
|
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
|
|
|
|
|
words_per_row=self.words_per_row,
|
2019-01-23 21:03:52 +01:00
|
|
|
word_size=self.word_size,
|
2018-11-14 22:53:27 +01:00
|
|
|
sram=self,
|
|
|
|
|
port_type="w")
|
2018-11-10 02:14:52 +01:00
|
|
|
self.add_mod(self.control_logic_w)
|
2018-11-20 07:20:20 +01:00
|
|
|
if len(self.readonly_ports)>0:
|
2018-11-14 22:53:27 +01:00
|
|
|
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
|
|
|
|
|
words_per_row=self.words_per_row,
|
2019-01-23 21:03:52 +01:00
|
|
|
word_size=self.word_size,
|
2018-11-14 22:53:27 +01:00
|
|
|
sram=self,
|
|
|
|
|
port_type="r")
|
2018-11-10 02:14:52 +01:00
|
|
|
self.add_mod(self.control_logic_r)
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def create_bank(self,bank_num):
|
2018-09-04 02:44:32 +02:00
|
|
|
""" Create a bank """
|
2018-08-27 23:18:32 +02:00
|
|
|
self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num),
|
|
|
|
|
mod=self.bank))
|
|
|
|
|
|
|
|
|
|
temp = []
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.read_ports:
|
2018-09-04 02:44:32 +02:00
|
|
|
for bit in range(self.word_size):
|
2018-11-08 21:19:40 +01:00
|
|
|
temp.append("DOUT{0}[{1}]".format(port,bit))
|
|
|
|
|
for port in self.write_ports:
|
2018-09-04 02:44:32 +02:00
|
|
|
for bit in range(self.word_size):
|
|
|
|
|
temp.append("BANK_DIN{0}[{1}]".format(port,bit))
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-04 02:44:32 +02:00
|
|
|
for bit in range(self.bank_addr_size):
|
|
|
|
|
temp.append("A{0}[{1}]".format(port,bit))
|
2018-08-27 23:18:32 +02:00
|
|
|
if(self.num_banks > 1):
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-04 02:44:32 +02:00
|
|
|
temp.append("bank_sel{0}[{1}]".format(port,bank_num))
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.read_ports:
|
|
|
|
|
temp.append("s_en{0}".format(port))
|
2018-11-27 03:00:59 +01:00
|
|
|
for port in self.read_ports:
|
2018-11-27 23:44:55 +01:00
|
|
|
temp.append("p_en_bar{0}".format(port))
|
2018-11-09 02:40:22 +01:00
|
|
|
for port in self.write_ports:
|
2018-09-04 02:44:32 +02:00
|
|
|
temp.append("w_en{0}".format(port))
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-11-27 03:00:59 +01:00
|
|
|
temp.append("wl_en{0}".format(port))
|
2018-09-27 04:10:24 +02:00
|
|
|
temp.extend(["vdd", "gnd"])
|
2018-08-27 23:18:32 +02:00
|
|
|
self.connect_inst(temp)
|
|
|
|
|
|
|
|
|
|
return self.bank_insts[-1]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def place_bank(self, bank_inst, position, x_flip, y_flip):
|
2018-07-12 19:30:45 +02:00
|
|
|
""" Place a bank at the given position with orientations """
|
|
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
|
|
|
|
|
if x_flip == -1 and y_flip == -1:
|
|
|
|
|
bank_rotation = 180
|
|
|
|
|
else:
|
|
|
|
|
bank_rotation = 0
|
|
|
|
|
|
|
|
|
|
if x_flip == y_flip:
|
|
|
|
|
bank_mirror = "R0"
|
|
|
|
|
elif x_flip == -1:
|
|
|
|
|
bank_mirror = "MX"
|
|
|
|
|
elif y_flip == -1:
|
|
|
|
|
bank_mirror = "MY"
|
|
|
|
|
else:
|
|
|
|
|
bank_mirror = "R0"
|
|
|
|
|
|
2018-08-28 02:25:39 +02:00
|
|
|
bank_inst.place(offset=position,
|
2018-08-27 23:18:32 +02:00
|
|
|
mirror=bank_mirror,
|
|
|
|
|
rotate=bank_rotation)
|
2018-07-12 19:30:45 +02:00
|
|
|
|
|
|
|
|
return bank_inst
|
|
|
|
|
|
2018-08-27 23:18:32 +02:00
|
|
|
|
|
|
|
|
def create_row_addr_dff(self):
|
|
|
|
|
""" Add all address flops for the main decoder """
|
2018-09-27 04:10:24 +02:00
|
|
|
insts = []
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
insts.append(self.add_inst(name="row_address{}".format(port),
|
|
|
|
|
mod=self.row_addr_dff))
|
|
|
|
|
|
|
|
|
|
# inputs, outputs/output/bar
|
|
|
|
|
inputs = []
|
|
|
|
|
outputs = []
|
|
|
|
|
for bit in range(self.row_addr_size):
|
|
|
|
|
inputs.append("ADDR{}[{}]".format(port,bit+self.col_addr_size))
|
|
|
|
|
outputs.append("A{}[{}]".format(port,bit+self.col_addr_size))
|
|
|
|
|
|
|
|
|
|
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
|
|
|
|
|
|
|
|
|
return insts
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-07-13 23:45:46 +02:00
|
|
|
|
2018-08-27 23:18:32 +02:00
|
|
|
def create_col_addr_dff(self):
|
2018-07-13 23:45:46 +02:00
|
|
|
""" Add and place all address flops for the column decoder """
|
2018-09-27 04:10:24 +02:00
|
|
|
insts = []
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
insts.append(self.add_inst(name="col_address{}".format(port),
|
|
|
|
|
mod=self.col_addr_dff))
|
|
|
|
|
|
|
|
|
|
# inputs, outputs/output/bar
|
|
|
|
|
inputs = []
|
|
|
|
|
outputs = []
|
|
|
|
|
for bit in range(self.col_addr_size):
|
|
|
|
|
inputs.append("ADDR{}[{}]".format(port,bit))
|
|
|
|
|
outputs.append("A{}[{}]".format(port,bit))
|
2018-07-12 19:30:45 +02:00
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
|
|
|
|
|
|
|
|
|
return insts
|
|
|
|
|
|
|
|
|
|
|
2018-08-27 23:18:32 +02:00
|
|
|
def create_data_dff(self):
|
2018-07-13 23:45:46 +02:00
|
|
|
""" Add and place all data flops """
|
2018-09-27 04:10:24 +02:00
|
|
|
insts = []
|
2018-11-15 02:05:23 +01:00
|
|
|
for port in self.all_ports:
|
|
|
|
|
if port in self.write_ports:
|
|
|
|
|
insts.append(self.add_inst(name="data_dff{}".format(port),
|
|
|
|
|
mod=self.data_dff))
|
|
|
|
|
else:
|
|
|
|
|
insts.append(None)
|
|
|
|
|
continue
|
2018-09-27 04:10:24 +02:00
|
|
|
|
|
|
|
|
# inputs, outputs/output/bar
|
|
|
|
|
inputs = []
|
|
|
|
|
outputs = []
|
|
|
|
|
for bit in range(self.word_size):
|
|
|
|
|
inputs.append("DIN{}[{}]".format(port,bit))
|
|
|
|
|
outputs.append("BANK_DIN{}[{}]".format(port,bit))
|
2018-07-13 23:45:46 +02:00
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
2018-07-13 23:45:46 +02:00
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
return insts
|
2018-07-13 23:45:46 +02:00
|
|
|
|
2018-08-27 23:18:32 +02:00
|
|
|
|
2018-09-12 09:59:07 +02:00
|
|
|
def create_control_logic(self):
|
2018-11-14 01:05:22 +01:00
|
|
|
""" Add control logic instances """
|
|
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
insts = []
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
|
|
|
|
if port in self.readwrite_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
mod = self.control_logic_rw
|
2018-11-08 21:19:40 +01:00
|
|
|
elif port in self.write_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
mod = self.control_logic_w
|
|
|
|
|
else:
|
|
|
|
|
mod = self.control_logic_r
|
|
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
insts.append(self.add_inst(name="control{}".format(port), mod=mod))
|
2018-11-29 00:30:52 +01:00
|
|
|
|
|
|
|
|
# Inputs
|
2018-09-27 04:10:24 +02:00
|
|
|
temp = ["csb{}".format(port)]
|
2018-11-08 21:19:40 +01:00
|
|
|
if port in self.readwrite_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
temp.append("web{}".format(port))
|
|
|
|
|
temp.append("clk{}".format(port))
|
2018-11-29 00:30:52 +01:00
|
|
|
|
|
|
|
|
# Ouputs
|
2018-11-08 21:19:40 +01:00
|
|
|
if port in self.read_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
temp.append("s_en{}".format(port))
|
2018-11-08 21:19:40 +01:00
|
|
|
if port in self.write_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
temp.append("w_en{}".format(port))
|
2018-11-27 03:00:59 +01:00
|
|
|
if port in self.read_ports:
|
2018-11-27 23:44:55 +01:00
|
|
|
temp.append("p_en_bar{}".format(port))
|
2018-11-27 03:00:59 +01:00
|
|
|
temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"])
|
2018-09-27 04:10:24 +02:00
|
|
|
self.connect_inst(temp)
|
2018-07-17 23:24:07 +02:00
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
return insts
|
2018-07-12 19:30:45 +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()
|
2018-07-16 23:13:41 +02:00
|
|
|
out_pos = dest_pin.center()
|
|
|
|
|
self.add_wire(("metal3","via2","metal2"),[in_pos, vector(out_pos.x,in_pos.y),out_pos])
|
2018-07-12 19:30:45 +02:00
|
|
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
|
|
|
|
offset=src_pin.rc(),
|
|
|
|
|
rotate=90)
|
2018-09-27 04:10:24 +02:00
|
|
|
|
|
|
|
|
|
2018-07-12 19:30:45 +02:00
|
|
|
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 sp_write(self, sp_name):
|
|
|
|
|
# Write the entire spice of the object to the file
|
|
|
|
|
############################################################
|
|
|
|
|
# Spice circuit
|
|
|
|
|
############################################################
|
|
|
|
|
sp = open(sp_name, 'w')
|
|
|
|
|
|
|
|
|
|
sp.write("**************************************************\n")
|
|
|
|
|
sp.write("* OpenRAM generated memory.\n")
|
|
|
|
|
sp.write("* Words: {}\n".format(self.num_words))
|
|
|
|
|
sp.write("* Data bits: {}\n".format(self.word_size))
|
|
|
|
|
sp.write("* Banks: {}\n".format(self.num_banks))
|
|
|
|
|
sp.write("* Column mux: {}:1\n".format(self.words_per_row))
|
|
|
|
|
sp.write("**************************************************\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()
|
|
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
|
2019-03-04 09:42:18 +01:00
|
|
|
def analytical_delay(self, corner, slew,load):
|
2018-07-12 19:30:45 +02:00
|
|
|
""" LH and HL are the same in analytical model. """
|
2019-03-04 09:42:18 +01:00
|
|
|
return self.bank.analytical_delay(corner,slew,load)
|
2018-11-08 09:10:51 +01:00
|
|
|
|
2018-11-15 08:34:53 +01:00
|
|
|
def determine_wordline_stage_efforts(self, inp_is_rise=True):
|
2018-11-10 02:14:52 +01:00
|
|
|
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
|
2018-11-08 09:10:51 +01:00
|
|
|
stage_effort_list = []
|
2018-11-10 02:14:52 +01:00
|
|
|
|
|
|
|
|
#Clk_buf originates from the control logic so only the bank is related to the wordline path
|
2018-11-08 09:10:51 +01:00
|
|
|
external_wordline_cout = 0 #No loading on the wordline other than in the bank.
|
2018-11-15 08:34:53 +01:00
|
|
|
stage_effort_list += self.bank.determine_wordline_stage_efforts(external_wordline_cout, inp_is_rise)
|
2018-11-08 09:10:51 +01:00
|
|
|
|
|
|
|
|
return stage_effort_list
|
|
|
|
|
|
2018-12-06 02:10:11 +01:00
|
|
|
def get_wl_en_cin(self):
|
2018-11-08 09:10:51 +01:00
|
|
|
"""Gets the capacitive load the of clock (clk_buf) for the sram"""
|
2018-12-06 02:10:11 +01:00
|
|
|
#Only the wordline drivers within the bank use this signal
|
2019-01-23 21:03:52 +01:00
|
|
|
return self.bank.get_wl_en_cin()
|
|
|
|
|
|
|
|
|
|
def get_w_en_cin(self):
|
|
|
|
|
"""Gets the capacitive load the of write enable (w_en) for the sram"""
|
|
|
|
|
#Only the write drivers within the bank use this signal
|
|
|
|
|
return self.bank.get_w_en_cin()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_p_en_bar_cin(self):
|
|
|
|
|
"""Gets the capacitive load the of precharge enable (p_en_bar) for the sram"""
|
|
|
|
|
#Only the precharges within the bank use this signal
|
|
|
|
|
return self.bank.get_p_en_bar_cin()
|
|
|
|
|
|
2018-11-09 05:47:34 +01:00
|
|
|
def get_clk_bar_cin(self):
|
|
|
|
|
"""Gets the capacitive load the of clock (clk_buf_bar) for the sram"""
|
|
|
|
|
#As clk_buf_bar is an output of the control logic. The cap for that module is not determined here.
|
|
|
|
|
#Only the precharge cells use this signal (other than the control logic)
|
2019-01-23 21:03:52 +01:00
|
|
|
return self.bank.get_clk_bar_cin()
|
2018-11-09 05:47:34 +01:00
|
|
|
|
|
|
|
|
def get_sen_cin(self):
|
|
|
|
|
"""Gets the capacitive load the of sense amp enable for the sram"""
|
|
|
|
|
#Only the sense_amps use this signal (other than the control logic)
|
2019-01-23 21:03:52 +01:00
|
|
|
return self.bank.get_sen_cin()
|
2018-11-09 05:47:34 +01:00
|
|
|
|
2018-11-10 02:14:52 +01:00
|
|
|
|
2019-01-11 23:15:16 +01:00
|
|
|
|