OpenRAM/compiler/modules/bank.py

1108 lines
49 KiB
Python
Raw Normal View History

# See LICENSE for licensing information.
#
2021-01-22 20:23:28 +01:00
# Copyright (c) 2016-2021 Regents of the University of California and The Board
2019-06-14 17:43:41 +02:00
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
2016-11-08 18:57:35 +01:00
import debug
import design
2019-01-17 01:15:38 +01:00
from sram_factory import factory
from math import log, ceil, floor
2020-10-27 23:11:04 +01:00
from tech import drc
2016-11-08 18:57:35 +01:00
from vector import vector
from globals import OPTS
2020-10-27 23:11:04 +01:00
from tech import layer_properties as layer_props
2016-11-08 18:57:35 +01:00
2016-11-08 18:57:35 +01:00
class bank(design.design):
"""
Dynamically generated a single bank including bitcell array,
2020-03-05 20:58:36 +01:00
hierarchical_decoder, precharge, (optional column_mux and column decoder),
write driver and sense amplifiers.
2020-03-05 20:58:36 +01:00
This can create up to two ports in any combination: rw, w, r.
2016-11-08 18:57:35 +01:00
"""
2019-07-11 23:47:27 +02:00
def __init__(self, sram_config, name=""):
2016-11-08 18:57:35 +01:00
2019-07-03 22:17:12 +02:00
self.sram_config = sram_config
sram_config.set_local_config(self)
if self.write_size:
self.num_wmasks = int(ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
2020-11-03 15:29:17 +01:00
2020-05-03 17:23:30 +02:00
if not self.num_spare_cols:
self.num_spare_cols = 0
2020-11-03 15:29:17 +01:00
2016-11-08 18:57:35 +01:00
if name == "":
name = "bank_{0}_{1}".format(self.word_size, self.num_words)
2020-08-06 20:33:26 +02:00
super().__init__(name)
2020-03-05 20:58:36 +01:00
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,
self.num_words))
2020-11-03 15:29:17 +01:00
2017-09-14 00:46:41 +02:00
# The local control signals are gated when we have bank select logic,
# so this prefix will be added to all of the input signals to create
# the internal gated signals.
2017-09-14 00:46:41 +02:00
if self.num_banks>1:
self.prefix="gated_"
else:
self.prefix=""
self.create_netlist()
if not OPTS.netlist_only:
2020-03-05 20:58:36 +01:00
debug.check(len(self.all_ports)<=2,
"Bank layout cannot handle more than two ports.")
self.create_layout()
self.add_boundary()
def create_netlist(self):
2016-11-08 18:57:35 +01:00
self.compute_sizes()
self.add_modules()
2019-07-16 18:04:58 +02:00
self.add_pins() # Must create the replica bitcell array first
2018-11-14 01:05:22 +01:00
self.create_instances()
2020-11-03 15:29:17 +01:00
def create_layout(self):
2018-11-14 01:05:22 +01:00
self.place_instances()
self.setup_routing_constraints()
2018-03-05 22:49:22 +01:00
self.route_layout()
2020-11-03 15:29:17 +01:00
# Can remove the following, but it helps for debug!
2020-08-25 23:42:21 +02:00
# self.add_lvs_correspondence_points()
# Remember the bank center for further placement
2021-05-04 00:51:53 +02:00
self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1)
self.bank_array_ur = self.bitcell_array_inst.ur()
self.bank_array_ul = self.bitcell_array_inst.ul()
# These are used for other placements (e.g. address flops)
self.predecoder_top = self.port_address[0].predecoder_height + self.port_address_inst[0].by()
self.predecoder_bottom = self.port_address_inst[0].by()
2016-11-08 18:57:35 +01:00
self.DRC_LVS()
2016-11-08 18:57:35 +01:00
def add_pins(self):
""" Adding pins for Bank module"""
for port in self.read_ports:
2020-05-03 17:23:30 +02:00
for bit in range(self.word_size + self.num_spare_cols):
2020-03-05 20:58:36 +01:00
self.add_pin("dout{0}_{1}".format(port, bit), "OUTPUT")
for port in self.all_ports:
2020-08-25 20:50:44 +02:00
self.add_pin("rbl_bl_{0}_{0}".format(port), "OUTPUT")
for port in self.write_ports:
2020-05-03 17:23:30 +02:00
for bit in range(self.word_size + self.num_spare_cols):
self.add_pin("din{0}_{1}".format(port, bit), "INPUT")
for port in self.all_ports:
for bit in range(self.addr_size):
self.add_pin("addr{0}_{1}".format(port, bit), "INPUT")
2016-11-08 18:57:35 +01:00
# For more than one bank, we have a bank select and name
# the signals gated_*.
2018-03-05 22:49:22 +01:00
if self.num_banks > 1:
for port in self.all_ports:
2020-03-05 20:58:36 +01:00
self.add_pin("bank_sel{}".format(port), "INPUT")
for port in self.read_ports:
self.add_pin("s_en{0}".format(port), "INPUT")
2019-08-01 21:28:21 +02:00
for port in self.all_ports:
self.add_pin("p_en_bar{0}".format(port), "INPUT")
for port in self.write_ports:
self.add_pin("w_en{0}".format(port), "INPUT")
for bit in range(self.num_wmasks):
2020-03-05 20:58:36 +01:00
self.add_pin("bank_wmask{0}_{1}".format(port, bit), "INPUT")
2020-05-03 17:23:30 +02:00
for bit in range(self.num_spare_cols):
self.add_pin("bank_spare_wen{0}_{1}".format(port, bit), "INPUT")
for port in self.all_ports:
2018-11-27 03:00:59 +01:00
self.add_pin("wl_en{0}".format(port), "INPUT")
2020-03-05 20:58:36 +01:00
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
2020-11-03 15:29:17 +01:00
def route_layout(self):
2016-11-08 18:57:35 +01:00
""" Create routing amoung the modules """
self.route_central_bus()
self.route_unused_wordlines()
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
for port in self.all_ports:
self.route_bitlines(port)
2019-07-12 17:42:36 +02:00
self.route_rbl(port)
2019-07-05 18:44:42 +02:00
self.route_port_address(port)
2018-11-14 01:05:22 +01:00
self.route_column_address_lines(port)
self.route_control_lines(port)
if self.num_banks > 1:
self.route_bank_select(port)
2020-11-03 15:29:17 +01:00
self.route_supplies()
2018-11-14 01:05:22 +01:00
2020-03-05 20:58:36 +01:00
def route_rbl(self, port):
2019-07-12 17:42:36 +02:00
""" Route the rbl_bl and rbl_wl """
# Connect the rbl to the port data pin
bl_pin = self.port_data_inst[port].get_pin("rbl_bl")
2020-03-06 18:03:52 +01:00
if port % 2:
pin_pos = bl_pin.uc()
pin_offset = pin_pos + vector(0, self.m3_pitch)
left_right_offset = vector(self.max_x_offset, pin_offset.y)
2020-03-06 18:03:52 +01:00
else:
pin_pos = bl_pin.bc()
pin_offset = pin_pos - vector(0, self.m3_pitch)
left_right_offset = vector(self.min_x_offset, pin_offset.y)
self.add_via_stack_center(from_layer=bl_pin.layer,
to_layer="m3",
offset=pin_offset)
self.add_path(bl_pin.layer, [pin_offset, pin_pos])
2020-08-25 20:50:44 +02:00
self.add_layout_pin_segment_center(text="rbl_bl_{0}_{0}".format(port),
2020-03-06 18:03:52 +01:00
layer="m3",
start=left_right_offset,
end=pin_offset)
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
def route_bitlines(self, port):
""" Route the bitlines depending on the port type rw, w, or r. """
2019-07-03 22:17:12 +02:00
if port in self.write_ports:
self.route_port_data_in(port)
if port in self.read_ports:
self.route_port_data_out(port)
self.route_port_data_to_bitcell_array(port)
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
def create_instances(self):
""" Create the instances of the netlist. """
self.create_bitcell_array()
2019-07-03 22:17:12 +02:00
self.create_port_data()
2019-07-05 18:44:42 +02:00
self.create_port_address()
self.create_column_decoder()
self.create_bank_select()
2018-11-14 01:05:22 +01:00
def compute_instance_offsets(self):
"""
Compute the empty instance offsets for port0 and port1 (if needed)
"""
2020-03-05 20:58:36 +01:00
self.port_data_offsets = [None] * len(self.all_ports)
self.port_address_offsets = [None] * len(self.all_ports)
2019-07-02 01:29:59 +02:00
2020-03-05 20:58:36 +01:00
self.column_decoder_offsets = [None] * len(self.all_ports)
self.bank_select_offsets = [None] * len(self.all_ports)
2019-07-02 01:29:59 +02:00
# The center point for these cells are the upper-right corner of
# the bitcell array.
2019-07-16 20:55:25 +02:00
# The port address decoder/driver logic is placed on the right and mirrored on Y-axis.
# The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
2020-03-05 20:58:36 +01:00
self.bitcell_array_top = self.bitcell_array.height
self.bitcell_array_right = self.bitcell_array.width
2020-11-03 15:29:17 +01:00
# These are the offsets of the main array (excluding dummy and replica rows/cols)
self.main_bitcell_array_top = self.bitcell_array.get_main_array_top()
# Just past the dummy column
self.main_bitcell_array_left = self.bitcell_array.get_main_array_left()
# Just past the dummy column
self.main_bitcell_array_right = self.bitcell_array.get_main_array_right()
# Just past the dummy row and replica row
self.main_bitcell_array_bottom = self.bitcell_array.get_main_array_bottom()
2018-11-14 01:05:22 +01:00
self.compute_instance_port0_offsets()
if len(self.all_ports)==2:
self.compute_instance_port1_offsets()
def get_column_offsets(self):
"""
Return an array of the x offsets of all the regular bits
"""
# Assumes bitcell_array is at 0,0
offsets = self.bitcell_array.get_column_offsets()
return offsets
2018-11-14 01:05:22 +01:00
def compute_instance_port0_offsets(self):
"""
Compute the instance offsets for port0 on the left/bottom of the bank.
"""
2018-11-14 01:05:22 +01:00
port = 0
2020-11-03 15:29:17 +01:00
# UPPER RIGHT QUADRANT
# Bitcell array is placed at (0,0)
2020-03-05 20:58:36 +01:00
self.bitcell_array_offset = vector(0, 0)
# LOWER RIGHT QUADRANT
# Below the bitcell array
self.port_data_offsets[port] = vector(0, 0)
# UPPER LEFT QUADRANT
# To the left of the bitcell array above the predecoders and control logic
x_offset = self.m2_gap + self.port_address[port].width
2020-03-05 20:58:36 +01:00
self.port_address_offsets[port] = vector(-x_offset,
self.main_bitcell_array_bottom)
# LOWER LEFT QUADRANT
2020-03-05 20:58:36 +01:00
# Place the col decoder left aligned with wordline driver
2019-07-16 20:55:25 +02:00
# This is also placed so that it's supply rails do not align with the SRAM-level
# control logic to allow control signals to easily pass over in M3
# by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs
# may be routed in M3 or M4
x_offset = self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width
if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = 1.25 * self.dff.height + self.column_decoder.height
else:
y_offset = 0
2020-03-05 20:58:36 +01:00
self.column_decoder_offsets[port] = vector(-x_offset, -y_offset)
# Bank select gets placed below the column decoder (x_offset doesn't change)
if self.col_addr_size > 0:
2019-07-03 22:17:12 +02:00
y_offset = min(self.column_decoder_offsets[port].y, self.port_data[port].column_mux_offset.y)
else:
2019-07-05 18:44:42 +02:00
y_offset = self.port_address_offsets[port].y
if self.num_banks > 1:
y_offset += self.bank_select.height + drc("well_to_well")
2020-03-05 20:58:36 +01:00
self.bank_select_offsets[port] = vector(-x_offset, -y_offset)
2018-11-14 01:05:22 +01:00
def compute_instance_port1_offsets(self):
"""
Compute the instance offsets for port1 on the right/top of the bank.
2018-11-14 01:05:22 +01:00
"""
port=1
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
# LOWER LEFT QUADRANT
# Bitcell array is placed at (0,0)
2018-11-14 01:05:22 +01:00
# UPPER LEFT QUADRANT
# Above the bitcell array
self.port_data_offsets[port] = vector(0, self.bitcell_array_top)
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
# LOWER RIGHT QUADRANT
2020-08-17 21:32:44 +02:00
# To the right of the bitcell array
x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap
2020-03-05 20:58:36 +01:00
self.port_address_offsets[port] = vector(x_offset,
self.main_bitcell_array_bottom)
2018-11-14 01:05:22 +01:00
# UPPER RIGHT QUADRANT
2020-03-05 20:58:36 +01:00
# Place the col decoder right aligned with wordline driver
# Above the bitcell array with a well spacing
# This is also placed so that it's supply rails do not align with the SRAM-level
# control logic to allow control signals to easily pass over in M3
# by placing 1 1/4 a cell pitch down because both power connections and inputs/outputs
# may be routed in M3 or M4
x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address[port].wordline_driver_array.width
if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = self.bitcell_array_top + 1.25 * self.dff.height + self.column_decoder.height
else:
2019-07-02 01:29:59 +02:00
y_offset = self.bitcell_array_top
2020-03-05 20:58:36 +01:00
self.column_decoder_offsets[port] = vector(x_offset, y_offset)
2018-11-14 01:05:22 +01:00
# Bank select gets placed above the column decoder (x_offset doesn't change)
if self.col_addr_size > 0:
y_offset = max(self.column_decoder_offsets[port].y + self.column_decoder.height,
2019-07-03 22:17:12 +02:00
self.port_data[port].column_mux_offset.y + self.port_data[port].column_mux_array.height)
2018-11-14 01:05:22 +01:00
else:
2019-07-05 18:44:42 +02:00
y_offset = self.port_address_offsets[port].y
2020-03-05 20:58:36 +01:00
self.bank_select_offsets[port] = vector(x_offset, y_offset)
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
def place_instances(self):
""" Place the instances. """
2018-11-14 01:05:22 +01:00
self.compute_instance_offsets()
2020-11-03 15:29:17 +01:00
self.place_bitcell_array(self.bitcell_array_offset)
2016-11-08 18:57:35 +01:00
2019-07-03 22:17:12 +02:00
self.place_port_data(self.port_data_offsets)
2019-07-05 18:44:42 +02:00
self.place_port_address(self.port_address_offsets)
2018-11-14 01:05:22 +01:00
self.place_column_decoder(self.column_decoder_offsets)
self.place_bank_select(self.bank_select_offsets)
2020-11-03 15:29:17 +01:00
2016-11-08 18:57:35 +01:00
def compute_sizes(self):
""" Computes the required sizes to create the bank """
2020-03-05 20:58:36 +01:00
self.num_cols = int(self.words_per_row * self.word_size)
self.num_rows_temp = int(self.num_words / self.words_per_row)
self.num_rows = self.num_rows_temp + self.num_spare_rows
2016-11-08 18:57:35 +01:00
self.row_addr_size = ceil(log(self.num_rows, 2))
2016-11-08 18:57:35 +01:00
self.col_addr_size = int(log(self.words_per_row, 2))
self.addr_size = self.col_addr_size + self.row_addr_size
2020-03-22 22:58:04 +01:00
debug.check(self.num_rows_temp * self.num_cols==self.word_size * self.num_words,
2020-03-05 20:58:36 +01:00
"Invalid bank sizes.")
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,
"Invalid address break down.")
2016-11-08 18:57:35 +01:00
# The order of the control signals on the control bus:
self.input_control_signals = []
port_num = 0
for port in range(OPTS.num_rw_ports):
self.input_control_signals.append(["p_en_bar{}".format(port_num), "s_en{}".format(port_num), "w_en{}".format(port_num)])
port_num += 1
for port in range(OPTS.num_w_ports):
self.input_control_signals.append(["p_en_bar{}".format(port_num), "w_en{}".format(port_num)])
port_num += 1
for port in range(OPTS.num_r_ports):
self.input_control_signals.append(["p_en_bar{}".format(port_num), "s_en{}".format(port_num)])
port_num += 1
# Number of control lines in the bus for each port
self.num_control_lines = [len(x) for x in self.input_control_signals]
2018-11-29 00:30:52 +01:00
# The width of this bus is needed to place other modules (e.g. decoder) for each port
2020-06-30 19:58:09 +02:00
self.central_bus_width = [self.m3_pitch * x + self.m3_width for x in self.num_control_lines]
2018-11-29 00:30:52 +01:00
# These will be outputs of the gaters if this is multibank, if not, normal signals.
self.control_signals = []
for port in self.all_ports:
if self.num_banks > 1:
2020-03-05 20:58:36 +01:00
self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]])
else:
self.control_signals.append(self.input_control_signals[port])
2020-11-03 15:29:17 +01:00
2018-11-29 00:30:52 +01:00
# The central bus is the column address (one hot) and row address (binary)
if self.col_addr_size>0:
self.num_col_addr_lines = 2**self.col_addr_size
else:
2020-03-05 20:58:36 +01:00
self.num_col_addr_lines = 0
self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines
# A space for wells or jogging m2
2020-03-05 20:58:36 +01:00
self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
2021-05-03 06:49:09 +02:00
3 * self.m2_pitch,
drc("nwell_to_nwell"))
2016-11-08 18:57:35 +01:00
def add_modules(self):
2018-11-14 01:05:22 +01:00
""" Add all the modules using the class loader """
2016-11-08 18:57:35 +01:00
2020-08-18 02:19:07 +02:00
2020-11-19 19:48:35 +01:00
local_array_size = OPTS.local_array_size
2020-11-03 15:29:17 +01:00
2020-09-14 23:35:52 +02:00
if local_array_size > 0:
# Find the even multiple that satisfies the fanout with equal sized local arrays
total_cols = self.num_cols + self.num_spare_cols
2020-09-14 23:35:52 +02:00
num_lb = floor(total_cols / local_array_size)
final_size = total_cols - num_lb * local_array_size
cols = [local_array_size] * (num_lb - 1)
# Add the odd bits to the last local array
2020-09-14 23:35:52 +02:00
cols.append(local_array_size + final_size)
self.bitcell_array = factory.create(module_type="global_bitcell_array",
cols=cols,
rows=self.num_rows)
else:
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
cols=self.num_cols + self.num_spare_cols,
rows=self.num_rows)
2019-07-11 23:47:27 +02:00
self.add_mod(self.bitcell_array)
2021-05-04 10:52:51 +02:00
if self.num_spare_cols == 0:
self.num_spare_cols = (self.bitcell_array.column_size % (self.word_size *self.words_per_row))
2021-04-15 00:09:25 +02:00
self.port_address = []
for port in self.all_ports:
self.port_address.append(factory.create(module_type="port_address",
2021-05-03 06:49:09 +02:00
cols=self.bitcell_array.column_size,
2021-04-15 00:09:25 +02:00
rows=self.bitcell_array.row_size,
port=port))
self.add_mod(self.port_address[port])
self.port_data = []
self.bit_offsets = self.get_column_offsets()
for port in self.all_ports:
temp_pre = factory.create(module_type="port_data",
sram_config=self.sram_config,
2021-04-15 00:09:25 +02:00
dimension_override=True,
2021-05-03 06:49:09 +02:00
cols=self.bitcell_array.column_size - self.num_spare_cols,
2021-04-15 00:09:25 +02:00
rows=self.bitcell_array.row_size,
2021-05-03 06:49:09 +02:00
num_spare_cols=self.num_spare_cols,
port=port,
bit_offsets=self.bit_offsets)
self.port_data.append(temp_pre)
self.add_mod(self.port_data[port])
2020-11-03 15:29:17 +01:00
2018-03-05 22:49:22 +01:00
if(self.num_banks > 1):
2019-01-17 01:15:38 +01:00
self.bank_select = factory.create(module_type="bank_select")
2018-03-05 22:49:22 +01:00
self.add_mod(self.bank_select)
2020-11-03 15:29:17 +01:00
def create_bitcell_array(self):
""" Creating Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array)
# Arrays are always:
# word lines (bottom to top)
# bit lines (left to right)
# vdd
# gnd
temp = self.bitcell_array.get_inouts()
temp.append("rbl_wl0")
temp.extend(self.bitcell_array.get_wordline_names())
if len(self.all_ports) > 1:
temp.append("rbl_wl1")
2020-11-03 15:29:17 +01:00
temp.append("vdd")
temp.append("gnd")
2021-05-24 11:42:04 +02:00
if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins:
temp.append('vpb')
temp.append('vnb')
2016-11-08 18:57:35 +01:00
self.connect_inst(temp)
2020-11-03 15:29:17 +01:00
def place_bitcell_array(self, offset):
""" Placing Bitcell Array """
self.bitcell_array_inst.place(offset)
2019-07-03 22:17:12 +02:00
def create_port_data(self):
""" Creating Port Data """
2020-08-25 20:50:44 +02:00
2020-03-05 20:58:36 +01:00
self.port_data_inst = [None] * len(self.all_ports)
for port in self.all_ports:
2019-07-03 22:17:12 +02:00
self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port),
mod=self.port_data[port])
temp = []
2020-08-25 20:50:44 +02:00
temp.extend(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)])
temp.extend(self.bitcell_array.get_bitline_names(port))
2019-07-03 22:17:12 +02:00
if port in self.read_ports:
2021-05-03 06:49:09 +02:00
for bit in range(self.word_size + self.num_spare_cols):
2020-03-05 20:58:36 +01:00
temp.append("dout{0}_{1}".format(port, bit))
2019-07-03 22:17:12 +02:00
if port in self.write_ports:
2021-05-03 06:49:09 +02:00
for bit in range(self.word_size + self.num_spare_cols):
2020-03-05 20:58:36 +01:00
temp.append("din{0}_{1}".format(port, bit))
2019-07-03 22:17:12 +02:00
# Will be empty if no col addr lines
2020-03-05 20:58:36 +01:00
sel_names = ["sel{0}_{1}".format(port, x) for x in range(self.num_col_addr_lines)]
2019-07-03 22:17:12 +02:00
temp.extend(sel_names)
if port in self.read_ports:
temp.append("s_en{0}".format(port))
2019-08-01 21:21:43 +02:00
temp.append("p_en_bar{0}".format(port))
2019-07-03 22:17:12 +02:00
if port in self.write_ports:
temp.append("w_en{0}".format(port))
for bit in range(self.num_wmasks):
temp.append("bank_wmask{0}_{1}".format(port, bit))
2020-05-03 17:23:30 +02:00
for bit in range(self.num_spare_cols):
temp.append("bank_spare_wen{0}_{1}".format(port, bit))
2020-03-05 20:58:36 +01:00
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
2016-11-08 18:57:35 +01:00
2019-07-03 22:17:12 +02:00
def place_port_data(self, offsets):
""" Placing Port Data """
2020-11-03 15:29:17 +01:00
for port in self.all_ports:
2019-07-03 22:17:12 +02:00
# Top one is unflipped, bottom is flipped along X direction
2020-03-05 20:58:36 +01:00
if port % 2 == 1:
2018-11-14 01:05:22 +01:00
mirror = "R0"
else:
mirror = "MX"
2019-07-03 22:17:12 +02:00
self.port_data_inst[port].place(offset=offsets[port], mirror=mirror)
2019-07-05 18:44:42 +02:00
def create_port_address(self):
""" Create the hierarchical row decoder """
2020-11-03 15:29:17 +01:00
2020-03-05 20:58:36 +01:00
self.port_address_inst = [None] * len(self.all_ports)
for port in self.all_ports:
2020-03-05 20:58:36 +01:00
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
mod=self.port_address[port])
2016-11-08 18:57:35 +01:00
temp = []
2021-04-15 00:09:25 +02:00
for bit in range(ceil(log(self.bitcell_array.row_size, 2))):
2020-03-05 20:58:36 +01:00
temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size))
temp.append("wl_en{}".format(port))
wordline_names = self.bitcell_array.get_wordline_names(port)
temp.extend(wordline_names)
temp.append("rbl_wl{}".format(port))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
2020-11-03 15:29:17 +01:00
2019-07-05 18:44:42 +02:00
def place_port_address(self, offsets):
""" Place the hierarchical row decoder """
2016-11-08 18:57:35 +01:00
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.")
2020-11-03 15:29:17 +01:00
2020-03-05 20:58:36 +01:00
# The address and control bus will be in between decoder and the main memory array
# This bus will route address bits to the decoder input and column mux inputs.
# The wires are actually routed after we placed the stuff on both sides.
# The predecoder is below the x-axis and the main decoder is above the x-axis
# The address flop and decoder are aligned in the x coord.
2020-11-03 15:29:17 +01:00
for port in self.all_ports:
2020-03-05 20:58:36 +01:00
if port % 2:
2018-11-14 01:05:22 +01:00
mirror = "MY"
else:
mirror = "R0"
2019-07-05 18:44:42 +02:00
self.port_address_inst[port].place(offset=offsets[port], mirror=mirror)
2020-11-03 15:29:17 +01:00
def create_column_decoder(self):
2020-03-05 20:58:36 +01:00
"""
Create a 2:4 or 3:8 column address decoder.
"""
2019-01-17 01:15:38 +01:00
self.dff =factory.create(module_type="dff")
2020-11-03 15:29:17 +01:00
if self.col_addr_size == 0:
return
elif self.col_addr_size == 1:
self.column_decoder = factory.create(module_type="pinvbuf",
height=self.dff.height)
elif self.col_addr_size == 2:
self.column_decoder = factory.create(module_type="hierarchical_predecode2x4",
column_decoder=True,
height=self.dff.height)
elif self.col_addr_size == 3:
self.column_decoder = factory.create(module_type="hierarchical_predecode3x8",
column_decoder=True,
height=self.dff.height)
2020-10-07 01:27:02 +02:00
elif self.col_addr_size == 4:
self.column_decoder = factory.create(module_type="hierarchical_predecode4x16",
column_decoder=True,
2020-10-07 01:27:02 +02:00
height=self.dff.height)
else:
# No error checking before?
2020-03-05 20:58:36 +01:00
debug.error("Invalid column decoder?", -1)
self.add_mod(self.column_decoder)
2020-11-03 15:29:17 +01:00
2020-03-05 20:58:36 +01:00
self.column_decoder_inst = [None] * len(self.all_ports)
for port in self.all_ports:
2020-03-05 20:58:36 +01:00
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
2018-11-14 01:05:22 +01:00
mod=self.column_decoder)
temp = []
for bit in range(self.col_addr_size):
2020-03-05 20:58:36 +01:00
temp.append("addr{0}_{1}".format(port, bit))
for bit in range(self.num_col_addr_lines):
2020-03-05 20:58:36 +01:00
temp.append("sel{0}_{1}".format(port, bit))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
2020-11-03 15:29:17 +01:00
def place_column_decoder(self, offsets):
2020-03-05 20:58:36 +01:00
"""
Place a 2:4 or 3:8 column address decoder.
"""
if self.col_addr_size == 0:
return
2020-11-03 15:29:17 +01:00
2020-03-05 20:58:36 +01:00
debug.check(len(offsets)>=len(self.all_ports),
"Insufficient offsets to place column decoder.")
for port in self.all_ports:
2020-03-05 20:58:36 +01:00
if port % 2 == 1:
mirror = "XY"
2018-11-14 01:05:22 +01:00
else:
mirror = "R0"
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
2020-11-03 15:29:17 +01:00
def create_bank_select(self):
""" Create the bank select logic. """
if not self.num_banks > 1:
return
2020-03-05 20:58:36 +01:00
self.bank_select_inst = [None] * len(self.all_ports)
for port in self.all_ports:
2018-11-14 01:05:22 +01:00
self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port),
mod=self.bank_select)
2020-11-03 15:29:17 +01:00
temp = []
temp.extend(self.input_control_signals[port])
2018-09-04 02:35:00 +02:00
temp.append("bank_sel{}".format(port))
temp.extend(self.control_signals[port])
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
2017-09-14 00:46:41 +02:00
def place_bank_select(self, offsets):
""" Place the bank select logic. """
if not self.num_banks > 1:
return
2020-03-05 20:58:36 +01:00
debug.check(len(offsets)>=len(self.all_ports),
"Insufficient offsets to place bank select logic.")
for port in self.all_ports:
self.bank_select_inst[port].place(offsets[port])
2020-11-03 15:29:17 +01:00
def route_supplies(self):
2018-04-16 19:15:36 +02:00
""" Propagate all vdd/gnd pins up to this level for all modules """
# Copy only the power pins already on the power layer
# (this won't add vias to internal bitcell pins, for example)
2018-10-08 18:56:39 +02:00
for inst in self.insts:
self.copy_power_pins(inst, "vdd", add_vias=False)
self.copy_power_pins(inst, "gnd", add_vias=False)
2018-04-16 19:15:36 +02:00
2021-05-24 11:42:04 +02:00
#if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins:
# for pin_name, supply_name in zip(['vpb','vnb'],['vdd','gnd']):
# self.copy_power_pins(self.bitcell_array_inst, pin_name, new_name=supply_name)
2020-06-30 01:23:25 +02:00
# If we use the pinvbuf as the decoder, we need to add power pins.
# Other decoders already have them.
if self.col_addr_size == 1:
for port in self.all_ports:
inst = self.column_decoder_inst[port]
for pin_name in ["vdd", "gnd"]:
pin_list = inst.get_pins(pin_name)
for pin in pin_list:
2021-01-13 19:57:12 +01:00
self.copy_power_pin(pin, pin.center())
2020-06-30 01:23:25 +02:00
2018-11-14 01:05:22 +01:00
def route_bank_select(self, port):
2018-03-05 22:49:22 +01:00
""" Route the bank select logic. """
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
if self.port_id[port] == "rw":
bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"]
2018-11-14 01:05:22 +01:00
elif self.port_id[port] == "w":
2019-08-01 21:21:43 +02:00
bank_sel_signals = ["clk_buf", "w_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_p_en_bar"]
2018-11-14 01:05:22 +01:00
else:
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
2020-11-03 15:29:17 +01:00
2020-03-05 20:58:36 +01:00
copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)]
2018-11-14 01:05:22 +01:00
for signal in range(len(copy_control_signals)):
self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal])
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
for signal in range(len(gated_bank_sel_signals)):
# Connect the inverter output to the central bus
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
name = self.control_signals[port][signal]
bus_pos = vector(self.bus_pins[port][name].cx(), out_pos.y)
2020-03-05 20:58:36 +01:00
self.add_path("m3", [out_pos, bus_pos])
self.add_via_center(layers=self.m2_stack,
offset=bus_pos)
self.add_via_center(layers=self.m1_stack,
offset=out_pos)
self.add_via_center(layers=self.m2_stack,
offset=out_pos)
2020-11-03 15:29:17 +01:00
def setup_routing_constraints(self):
2020-03-05 20:58:36 +01:00
"""
After the modules are instantiated, find the dimensions for the
2020-03-05 20:58:36 +01:00
control bus, power ring, etc.
"""
2017-09-14 00:46:41 +02:00
2020-03-05 20:58:36 +01:00
self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width
2018-11-14 01:05:22 +01:00
self.min_y_offset = min([x.by() for x in self.insts])
2020-11-03 15:29:17 +01:00
2020-03-05 20:58:36 +01:00
self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width
2018-11-14 01:05:22 +01:00
self.min_x_offset = min([x.lx() for x in self.insts])
2018-04-21 00:51:19 +02:00
# # Create the core bbox for the power rings
ur = vector(self.max_x_offset, self.max_y_offset)
ll = vector(self.min_x_offset, self.min_y_offset)
self.core_bbox = [ll, ur]
2020-11-03 15:29:17 +01:00
2018-04-21 00:51:19 +02:00
self.height = ur.y - ll.y
self.width = ur.x - ll.x
2016-11-08 18:57:35 +01:00
def route_central_bus(self):
""" Create the address, supply, and control signal central bus lines. """
# Overall central bus width. It includes all the column mux lines,
# and control lines.
2018-11-14 01:05:22 +01:00
self.bus_pins = [None] * len(self.all_ports)
2018-11-14 01:05:22 +01:00
# Port 0
# The bank is at (0,0), so this is to the left of the y-axis.
2020-03-05 20:58:36 +01:00
# 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - 2 * self.m3_pitch, self.min_y_offset)
2018-11-29 00:30:52 +01:00
# The control bus is routed up to two pitches below the bitcell array
2020-09-15 18:49:00 +02:00
control_bus_length = self.port_data_inst[0].uy() - self.min_y_offset
self.bus_pins[0] = self.create_bus(layer="m2",
offset=control_bus_offset,
names=self.control_signals[0],
length=control_bus_length,
vertical=True,
2020-06-30 19:58:09 +02:00
make_pins=(self.num_banks==1),
pitch=self.m3_pitch)
2020-11-03 15:29:17 +01:00
self.copy_layout_pin(self.port_address_inst[0], "wl_en", self.prefix + "wl_en0")
2018-11-14 01:05:22 +01:00
# Port 1
if len(self.all_ports)==2:
2018-11-29 00:30:52 +01:00
# The other control bus is routed up to two pitches above the bitcell array
2020-09-15 18:49:00 +02:00
control_bus_length = self.max_y_offset - self.port_data_inst[1].by()
2020-08-17 21:32:44 +02:00
control_bus_offset = vector(self.bitcell_array_right + 2.5 * self.m3_pitch,
self.max_y_offset - control_bus_length)
2019-07-16 18:04:58 +02:00
# The bus for the right port is reversed so that the rbl_wl is closest to the array
self.bus_pins[1] = self.create_bus(layer="m2",
offset=control_bus_offset,
names=list(reversed(self.control_signals[1])),
length=control_bus_length,
vertical=True,
2020-06-30 19:58:09 +02:00
make_pins=(self.num_banks==1),
pitch=self.m3_pitch)
self.copy_layout_pin(self.port_address_inst[1], "wl_en", self.prefix + "wl_en1")
2019-07-03 22:17:12 +02:00
def route_port_data_to_bitcell_array(self, port):
""" Routing of BL and BR between port data and bitcell array """
2018-04-18 18:37:33 +02:00
2019-07-11 23:47:27 +02:00
# Connect the regular bitlines
2019-07-03 22:17:12 +02:00
inst2 = self.port_data_inst[port]
2018-11-14 01:05:22 +01:00
inst1 = self.bitcell_array_inst
2020-08-25 20:50:44 +02:00
inst1_bl_name = [x for x in self.bitcell_array.get_bitline_names(port) if "bl" in x]
inst1_br_name = [x for x in self.bitcell_array.get_bitline_names(port) if "br" in x]
inst2_bl_name = []
inst2_br_name = []
for col in range(self.num_cols):
inst2_bl_name.append(inst2.mod.get_bl_names() + "_{}".format(col))
inst2_br_name.append(inst2.mod.get_br_names() + "_{}".format(col))
for col in range(self.num_spare_cols):
inst2_bl_name.append("spare" + inst2.mod.get_bl_names() + "_{}".format(col))
inst2_br_name.append("spare" + inst2.mod.get_br_names() + "_{}".format(col))
2020-03-06 18:03:52 +01:00
self.connect_bitlines(inst1=inst1,
inst2=inst2,
inst1_bl_name=inst1_bl_name,
inst1_br_name=inst1_br_name,
inst2_bl_name=inst2_bl_name,
inst2_br_name=inst2_br_name)
2020-11-03 15:29:17 +01:00
2019-07-11 23:47:27 +02:00
# Connect the replica bitlines
for (array_name, data_name) in zip(["rbl_bl_{0}_{0}".format(port), "rbl_br_{0}_{0}".format(port)], ["rbl_bl", "rbl_br"]):
self.connect_bitline(inst1, inst2, array_name, data_name)
2020-11-03 15:29:17 +01:00
2019-07-03 22:17:12 +02:00
def route_port_data_out(self, port):
""" Add pins for the port data out """
2018-04-18 18:37:33 +02:00
2020-05-03 17:23:30 +02:00
for bit in range(self.word_size + self.num_spare_cols):
2019-07-03 22:17:12 +02:00
data_pin = self.port_data_inst[port].get_pin("dout_{0}".format(bit))
2020-03-05 20:58:36 +01:00
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port, bit),
layer=data_pin.layer,
2018-11-14 01:05:22 +01:00
offset=data_pin.center(),
height=data_pin.height(),
width=data_pin.width())
2020-11-03 15:29:17 +01:00
2019-07-05 18:44:42 +02:00
def route_port_address_in(self, port):
""" Routes the row decoder inputs and supplies """
# Create inputs for the row address lines
2018-11-14 01:05:22 +01:00
for row in range(self.row_addr_size):
addr_idx = row + self.col_addr_size
decoder_name = "addr_{}".format(row)
2020-03-05 20:58:36 +01:00
addr_name = "addr{0}_{1}".format(port, addr_idx)
2019-07-05 18:44:42 +02:00
self.copy_layout_pin(self.port_address_inst[port], decoder_name, addr_name)
2019-07-03 22:17:12 +02:00
def route_port_data_in(self, port):
""" Connecting port data in """
2020-05-03 17:23:30 +02:00
for row in range(self.word_size + self.num_spare_cols):
2019-07-03 22:17:12 +02:00
data_name = "din_{}".format(row)
2020-03-05 20:58:36 +01:00
din_name = "din{0}_{1}".format(port, row)
2019-07-03 22:17:12 +02:00
self.copy_layout_pin(self.port_data_inst[port], data_name, din_name)
if self.write_size:
for row in range(self.num_wmasks):
wmask_name = "bank_wmask_{}".format(row)
bank_wmask_name = "bank_wmask{0}_{1}".format(port, row)
self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
2020-11-03 15:29:17 +01:00
2020-05-03 17:23:30 +02:00
for col in range(self.num_spare_cols):
sparecol_name = "bank_spare_wen{}".format(col)
bank_sparecol_name = "bank_spare_wen{0}_{1}".format(port, col)
2020-05-03 17:23:30 +02:00
self.copy_layout_pin(self.port_data_inst[port], sparecol_name, bank_sparecol_name)
2020-11-03 15:29:17 +01:00
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
"""
Route the bl and br of two modules using the channel router.
"""
2020-11-03 15:29:17 +01:00
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name)
(top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name)
else:
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name)
(top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name)
# Channel route each mux separately since we don't minimize the number
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
2020-03-05 20:58:36 +01:00
offset = bottom_inst.ul() + vector(0, self.m1_pitch)
for bit in range(num_bits):
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))]
2020-03-05 20:58:36 +01:00
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
2020-11-03 15:29:17 +01:00
2019-07-11 23:47:27 +02:00
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name):
"""
2020-03-05 20:58:36 +01:00
Connect two pins of two modules.
This assumes that they have sufficient space to create a jog
2018-11-14 01:05:22 +01:00
in the middle between the two modules (if needed).
"""
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
2019-07-11 23:47:27 +02:00
(bottom_inst, bottom_name) = (inst1, inst1_name)
(top_inst, top_name) = (inst2, inst2_name)
2018-11-14 01:05:22 +01:00
else:
2019-07-11 23:47:27 +02:00
(bottom_inst, bottom_name) = (inst2, inst2_name)
(top_inst, top_name) = (inst1, inst1_name)
bottom_pin = bottom_inst.get_pin(bottom_name)
top_pin = top_inst.get_pin(top_name)
debug.check(bottom_pin.layer == top_pin.layer, "Pin layers do not match.")
2020-11-03 15:29:17 +01:00
2019-07-11 23:47:27 +02:00
bottom_loc = bottom_pin.uc()
top_loc = top_pin.bc()
2020-11-03 15:29:17 +01:00
2020-03-05 20:58:36 +01:00
yoffset = 0.5 * (top_loc.y + bottom_loc.y)
self.add_path(top_pin.layer,
[bottom_loc,
vector(bottom_loc.x, yoffset),
vector(top_loc.x, yoffset),
top_loc])
2020-11-03 15:29:17 +01:00
2020-08-25 20:50:44 +02:00
def connect_bitlines(self, inst1, inst2,
inst1_bl_name, inst1_br_name,
inst2_bl_name, inst2_br_name):
2019-07-11 23:47:27 +02:00
"""
Connect the bl and br of two modules.
"""
2020-08-25 20:50:44 +02:00
for (name1, name2) in zip(inst1_bl_name, inst2_bl_name):
self.connect_bitline(inst1, inst2, name1, name2)
for (name1, name2) in zip(inst1_br_name, inst2_br_name):
self.connect_bitline(inst1, inst2, name1, name2)
2018-11-14 01:05:22 +01:00
2019-07-05 18:44:42 +02:00
def route_port_address(self, port):
2018-11-14 01:05:22 +01:00
""" Connect Wordline driver to bitcell array wordline """
2019-07-05 18:44:42 +02:00
self.route_port_address_in(port)
2020-11-03 15:29:17 +01:00
2020-03-05 20:58:36 +01:00
if port % 2:
self.route_port_address_out(port, "right")
2018-11-14 01:05:22 +01:00
else:
self.route_port_address_out(port, "left")
2020-11-03 15:29:17 +01:00
def route_port_address_out(self, port, side="left"):
2016-11-08 18:57:35 +01:00
""" Connecting Wordline driver output to Bitcell WL connection """
2018-11-14 01:05:22 +01:00
driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"]
rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)[port]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port) + [rbl_wl_name]):
2018-11-14 01:05:22 +01:00
# The mid guarantees we exit the input cell to the right.
2020-08-25 20:50:44 +02:00
driver_wl_pin = self.port_address_inst[port].get_pin(driver_name)
if side == "left":
driver_wl_pos = driver_wl_pin.rc()
else:
driver_wl_pos = driver_wl_pin.lc()
2020-08-25 20:50:44 +02:00
bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name)
2020-11-03 15:29:17 +01:00
if side == "left":
bitcell_wl_pos = bitcell_wl_pin.lc()
port_address_pos = self.port_address_inst[port].rx()
bitcell_array_pos = self.bitcell_array_inst.lx()
else:
bitcell_wl_pos = bitcell_wl_pin.rc()
port_address_pos = self.port_address_inst[port].lx()
bitcell_array_pos = self.bitcell_array_inst.rx()
2020-11-03 15:29:17 +01:00
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * port_address_pos + 0.5 * bitcell_array_pos, 0)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
if driver_wl_pin.layer != bitcell_wl_pin.layer:
2020-12-01 20:33:55 +01:00
self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1])
self.add_via_stack_center(from_layer=driver_wl_pin.layer,
to_layer=bitcell_wl_pin.layer,
2020-12-01 20:33:55 +01:00
offset=mid1)
self.add_path(bitcell_wl_pin.layer, [mid1, mid2, bitcell_wl_pos])
else:
self.add_path(bitcell_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
2020-11-03 15:29:17 +01:00
2019-07-05 18:44:42 +02:00
def route_port_address_right(self, port):
2018-11-14 01:05:22 +01:00
""" Connecting Wordline driver output to Bitcell WL connection """
driver_names = ["wl_{}".format(x) for x in range(self.num_rows)] + ["rbl_wl"]
rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)[port]
for (driver_name, array_name) in zip(driver_names, self.bitcell_array.get_wordline_names(port) + [rbl_wl_name]):
2018-11-14 01:05:22 +01:00
# The mid guarantees we exit the input cell to the right.
2020-08-25 20:50:44 +02:00
driver_wl_pin = self.port_address_inst[port].get_pin(driver_name)
driver_wl_pos = driver_wl_pin.lc()
2020-08-25 20:50:44 +02:00
bitcell_wl_pin = self.bitcell_array_inst.get_pin(array_name)
bitcell_wl_pos = bitcell_wl_pin.rc()
2020-03-05 20:58:36 +01:00
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].lx() + 0.5 * self.bitcell_array_inst.rx(), 0)
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2])
self.add_via_stack_center(from_layer=driver_wl_pin.layer,
to_layer=bitcell_wl_pin.layer,
offset=mid2)
self.add_path(bitcell_wl_pin.layer, [mid2, bitcell_wl_pos])
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
def route_column_address_lines(self, port):
2016-11-08 18:57:35 +01:00
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
2020-10-28 17:21:36 +01:00
stack = getattr(self, layer_props.bank.stack)
pitch = getattr(self, layer_props.bank.pitch)
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
if self.col_addr_size == 1:
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
# The Address LSB
2020-03-05 20:58:36 +01:00
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out_{}".format(i))
2018-11-14 01:05:22 +01:00
for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i)
2020-03-05 20:58:36 +01:00
addr_name = "addr{0}_{1}".format(port, i)
2018-11-14 01:05:22 +01:00
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
2020-03-05 20:58:36 +01:00
if port % 2:
2020-06-30 01:47:34 +02:00
offset = self.column_decoder_inst[port].ll() - vector((self.num_col_addr_lines + 1) * pitch, 0)
else:
2020-06-30 01:23:25 +02:00
offset = self.column_decoder_inst[port].lr() + vector(pitch, 0)
2018-11-14 01:05:22 +01:00
decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names]
2018-11-14 01:05:22 +01:00
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
2019-07-03 22:17:12 +02:00
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
2020-11-03 15:29:17 +01:00
route_map = list(zip(decode_pins, column_mux_pins))
self.create_vertical_channel_route(route_map,
offset,
2020-06-30 01:23:25 +02:00
stack)
2016-11-08 18:57:35 +01:00
def add_lvs_correspondence_points(self):
2020-03-05 20:58:36 +01:00
"""
This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
2020-08-25 20:50:44 +02:00
for pin_name in self.bitcell_array.get_all_bitline_names():
self.copy_layout_pin(self.bitcell_array, pin_name)
# Add the wordline names
2020-08-25 20:50:44 +02:00
# for i in range(self.num_rows):
# wl_name = "wl_{}".format(i)
# wl_pin = self.bitcell_array_inst.get_pin(wl_name)
# self.add_label(text=wl_name,
# layer="m1",
# offset=wl_pin.center())
2020-11-03 15:29:17 +01:00
2020-08-25 20:50:44 +02:00
# # Add the bitline names
# for i in range(self.num_cols):
# bl_name = "bl_{}".format(i)
# br_name = "br_{}".format(i)
# bl_pin = self.bitcell_array_inst.get_pin(bl_name)
# br_pin = self.bitcell_array_inst.get_pin(br_name)
# self.add_label(text=bl_name,
# layer="m2",
# offset=bl_pin.center())
# self.add_label(text=br_name,
# layer="m2",
# offset=br_pin.center())
2020-03-05 20:58:36 +01:00
# # Add the data output names to the sense amp output
# for i in range(self.word_size):
# data_name = "data_{}".format(i)
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
# self.add_label(text="sa_out_{}".format(i),
2020-03-05 20:58:36 +01:00
# layer="m2",
# offset=data_pin.center())
# Add labels on the decoder
2020-08-25 20:50:44 +02:00
# for port in self.write_ports:
# for i in range(self.word_size):
# data_name = "dec_out_{}".format(i)
# pin_name = "in_{}".format(i)
# data_pin = self.wordline_driver_inst[port].get_pin(pin_name)
# self.add_label(text=data_name,
# layer="m1",
# offset=data_pin.center())
def route_unused_wordlines(self):
""" Connect the unused RBL and dummy wordlines to gnd """
gnd_wl_names = []
return
# Connect unused RBL WL to gnd
2020-08-25 20:50:44 +02:00
# All RBL WL names
array_rbl_names = set(self.bitcell_array.get_rbl_wordline_names())
# List of used RBL WL names
rbl_wl_names = set()
for port in self.all_ports:
rbl_wl_names.add(self.bitcell_array.get_rbl_wordline_names(port)[port])
gnd_wl_names = list((array_rbl_names - rbl_wl_names))
for wl_name in gnd_wl_names:
pin = self.bitcell_array_inst.get_pin(wl_name)
pin_layer = pin.layer
2020-08-14 23:23:40 +02:00
layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
left_pin_loc = pin.lc()
right_pin_loc = pin.rc()
# Place the pins a track outside of the array
left_loc = left_pin_loc - vector(layer_pitch, 0)
right_loc = right_pin_loc + vector(layer_pitch, 0)
self.add_power_pin("gnd", left_loc, directions=("H", "H"))
self.add_power_pin("gnd", right_loc, directions=("H", "H"))
# Add a path to connect to the array
self.add_path(pin_layer, [left_loc, left_pin_loc])
self.add_path(pin_layer, [right_loc, right_pin_loc])
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
def route_control_lines(self, port):
""" Route the control lines of the entire bank """
2020-11-03 15:29:17 +01:00
# Make a list of tuples that we will connect.
2020-03-05 20:58:36 +01:00
# From control signal to the module pin
# Connection from the central bus to the main control block crosses
# pre-decoder and this connection is in metal3
2018-11-14 01:05:22 +01:00
connection = []
connection.append((self.prefix + "p_en_bar{}".format(port),
self.port_data_inst[port].get_pin("p_en_bar")))
2019-07-12 17:42:36 +02:00
2018-11-14 01:05:22 +01:00
if port in self.write_ports:
connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en")))
2020-11-03 15:29:17 +01:00
2018-11-14 01:05:22 +01:00
if port in self.read_ports:
connection.append((self.prefix + "s_en{}".format(port),
self.port_data_inst[port].get_pin("s_en")))
for (control_signal, pin) in connection:
control_pin = self.bus_pins[port][control_signal]
control_pos = vector(control_pin.cx(), pin.cy())
# If the y doesn't overlap the bus, add a segment
if pin.cy() < control_pin.by():
self.add_path("m2", [control_pos, control_pin.bc()])
elif pin.cy() > control_pin.uy():
self.add_path("m2", [control_pos, control_pin.uc()])
self.add_path(pin.layer, [control_pos, pin.center()])
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m2",
offset=control_pos)
2020-11-03 15:29:17 +01:00
def graph_exclude_precharge(self):
"""
Precharge adds a loop between bitlines, can be excluded to reduce complexity
"""
2019-07-03 23:46:20 +02:00
for port in self.read_ports:
if self.port_data[port]:
self.port_data[port].graph_exclude_precharge()
2020-11-03 15:29:17 +01:00
2019-05-21 03:35:52 +02:00
def get_cell_name(self, inst_name, row, col):
"""
Gets the spice name of the target bitcell.
"""
2020-03-05 20:58:36 +01:00
return self.bitcell_array_inst.mod.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name,
row,
col)
2019-07-03 23:05:28 +02:00
def graph_exclude_bits(self, targ_row, targ_col):
"""
Excludes bits in column from being added to graph except target
"""
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
def clear_exclude_bits(self):
2020-11-03 15:29:17 +01:00
"""
Clears the bit exclusions
"""
self.bitcell_array.clear_exclude_bits()
2020-11-03 15:29:17 +01:00