OpenRAM/compiler/sram/sram_1bank.py

669 lines
33 KiB
Python
Raw Normal View History

# See LICENSE for licensing information.
#
2019-06-14 17:43:41 +02:00
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
from vector import vector
from sram_base import sram_base
2020-01-30 02:45:33 +01:00
from contact import m2_via
from globals import OPTS
class sram_1bank(sram_base):
"""
Procedures specific to a one bank SRAM.
"""
def __init__(self, name, sram_config):
sram_base.__init__(self, name, sram_config)
def create_modules(self):
2020-03-06 01:13:49 +01:00
"""
This adds the modules for a single bank SRAM with control
2020-03-06 01:13:49 +01:00
logic.
"""
self.bank_inst=self.create_bank(0)
2018-11-14 01:05:22 +01:00
self.control_logic_insts = self.create_control_logic()
2018-11-14 01:05:22 +01:00
self.row_addr_dff_insts = self.create_row_addr_dff()
if self.col_addr_dff:
2018-11-14 01:05:22 +01:00
self.col_addr_dff_insts = self.create_col_addr_dff()
2019-07-03 22:17:12 +02:00
if self.write_size:
self.wmask_dff_insts = self.create_wmask_dff()
self.data_dff_insts = self.create_data_dff()
else:
self.data_dff_insts = self.create_data_dff()
2019-07-04 20:14:32 +02:00
if self.num_spare_cols:
self.spare_wen_dff_insts = self.create_spare_wen_dff()
else:
self.num_spare_cols = 0
def place_instances(self):
2020-03-06 01:13:49 +01:00
"""
This places the instances for a single bank SRAM with control
logic and up to 2 ports.
"""
# No orientation or offset
self.place_bank(self.bank_inst, [0, 0], 1, 1)
2018-08-14 19:36:35 +02:00
# The control logic is placed such that the vertical center (between the delay/RBL and
# the actual control logic is aligned with the vertical center of the bank (between
# the sense amps/column mux and cell array)
2018-08-14 19:36:35 +02:00
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
# up to the row address DFFs.
2020-03-06 01:13:49 +01:00
control_pos = [None] * len(self.all_ports)
row_addr_pos = [None] * len(self.all_ports)
col_addr_pos = [None] * len(self.all_ports)
wmask_pos = [None] * len(self.all_ports)
spare_wen_pos = [None] * len(self.all_ports)
2020-03-06 01:13:49 +01:00
data_pos = [None] * len(self.all_ports)
# These positions utilize the channel route sizes.
# FIXME: Auto-compute these rather than manual computation.
# If a horizontal channel, they rely on the vertical channel non-preferred (contacted) pitch.
# If a vertical channel, they rely on the horizontal channel non-preferred (contacted) pitch.
# So, m3 non-pref pitch means that this is routed on the m2 layer.
2020-06-27 17:23:12 +02:00
self.data_bus_gap = self.m4_nonpref_pitch * 2
self.data_bus_size = self.m4_nonpref_pitch * (self.word_size + self.num_spare_cols + self.num_wmasks + self.col_addr_size + self.num_spare_cols) + self.data_bus_gap
# Port 0
port = 0
2018-11-29 18:54:29 +01:00
# This includes 2 M2 pitches for the row addr clock line.
# The delay line is aligned with the bitcell array while the control logic is aligned with the port_data
# using the control_logic_center value.
2020-03-06 01:13:49 +01:00
control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch,
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(control_pos[port])
# The row address bits are placed above the control logic aligned on the right.
x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width
2019-07-16 18:04:58 +02:00
# It is above the control logic but below the top of the bitcell array
2020-06-26 00:34:18 +02:00
y_offset = max(self.control_logic_insts[port].uy(), self.control_logic_insts[port].uy() + self.dff.height)
row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port])
2020-06-27 17:23:12 +02:00
# Add the col address flops below the bank to the right of the control logic
x_offset = self.control_logic_insts[port].rx() + self.dff.width
y_offset = - self.data_bus_size - self.dff.height
if self.col_addr_dff:
col_addr_pos[port] = vector(x_offset,
y_offset)
self.col_addr_dff_insts[port].place(col_addr_pos[port])
x_offset = self.col_addr_dff_insts[port].rx()
else:
col_addr_pos[port] = vector(x_offset, 0)
if port in self.write_ports:
if self.write_size:
# Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(x_offset,
y_offset)
self.wmask_dff_insts[port].place(wmask_pos[port])
x_offset = self.wmask_dff_insts[port].rx()
2018-11-29 18:54:29 +01:00
# Add the data flops below the write mask flops.
data_pos[port] = vector(x_offset,
y_offset)
self.data_dff_insts[port].place(data_pos[port])
x_offset = self.data_dff_insts[port].rx()
# Add spare write enable flops to the right of data flops since the spare columns
# will be on the right
2020-06-27 17:23:12 +02:00
if self.num_spare_cols:
spare_wen_pos[port] = vector(x_offset,
y_offset)
self.spare_wen_dff_insts[port].place(spare_wen_pos[port])
x_offset = self.spare_wen_dff_insts[port].rx()
2020-06-27 17:23:12 +02:00
else:
wmask_pos[port] = vector(x_offset, y_offset)
data_pos[port] = vector(x_offset, y_offset)
spare_wen_pos[port] = vector(x_offset, y_offset)
2020-06-27 17:23:12 +02:00
if len(self.all_ports)>1:
# Port 1
port = 1
2019-07-03 22:17:12 +02:00
# This includes 2 M2 pitches for the row addr clock line
# The delay line is aligned with the bitcell array while the control logic is aligned with the port_data
# using the control_logic_center value.
2020-03-06 01:13:49 +01:00
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch,
self.bank.bank_array_ur.y
+ self.control_logic_insts[port].height
- self.control_logic_insts[port].height
+ self.control_logic_insts[port].mod.control_logic_center.y)
2019-07-03 22:17:12 +02:00
self.control_logic_insts[port].place(control_pos[port], mirror="XY")
# The row address bits are placed above the control logic aligned on the left.
x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width
2019-07-16 18:04:58 +02:00
# It is below the control logic but below the bottom of the bitcell array
2020-06-26 00:34:18 +02:00
y_offset = min(self.control_logic_insts[port].by(), self.control_logic_insts[port].by() - self.dff.height)
2019-07-03 22:17:12 +02:00
row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY")
2020-06-27 17:23:12 +02:00
# Add the col address flops below the bank to the right of the control logic
x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width
y_offset = self.bank.height + self.data_bus_size + self.dff.height
if self.col_addr_dff:
col_addr_pos[port] = vector(x_offset - self.col_addr_dff_insts[port].width,
y_offset)
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
x_offset = self.col_addr_dff_insts[port].lx()
else:
col_addr_pos[port] = vector(x_offset, y_offset)
if port in self.write_ports:
# Add spare write enable flops to the right of the data flops since the spare
# columns will be on the left
if self.num_spare_cols:
spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width,
y_offset)
self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX")
x_offset = self.spare_wen_dff_insts[port].lx()
2020-06-27 17:23:12 +02:00
if self.write_size:
# Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width,
y_offset)
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
x_offset = self.wmask_dff_insts[port].lx()
# Add the data flops below the write mask flops.
data_pos[port] = vector(x_offset - self.data_dff_insts[port].width,
y_offset)
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
2020-06-27 17:23:12 +02:00
else:
wmask_pos[port] = vector(x_offset, y_offset)
data_pos[port] = vector(x_offset, y_offset)
spare_wen_pos[port] = vector(x_offset, y_offset)
def add_layout_pins(self):
"""
Add the top-level pins for a single bank SRAM with control.
"""
highest_coord = self.find_highest_coords()
lowest_coord = self.find_lowest_coords()
bbox = [lowest_coord, highest_coord]
for port in self.all_ports:
# Depending on the port, use the bottom/top or left/right sides
# Port 0 is left/bottom
# Port 1 is right/top
bottom_or_top = "bottom" if port==0 else "top"
left_or_right = "left" if port==0 else "right"
# Connect the control pins as inputs
for signal in self.control_logic_inputs[port]:
if signal == "clk":
continue
if OPTS.perimeter_pins:
self.add_perimeter_pin(name=signal + "{}".format(port),
pin=self.control_logic_insts[port].get_pin(signal),
side=left_or_right,
bbox=bbox)
else:
self.copy_layout_pin(self.control_logic_insts[port],
signal,
signal + "{}".format(port))
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="clk{}".format(port),
pin=self.control_logic_insts[port].get_pin("clk"),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.control_logic_insts[port],
"clk",
"clk{}".format(port))
# Data output pins go to BOTTOM/TOP
if port in self.read_ports:
2020-05-14 12:30:29 +02:00
for bit in range(self.word_size + self.num_spare_cols):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="dout{0}[{1}]".format(port, bit),
pin=self.bank_inst.get_pin("dout{0}_{1}".format(port, bit)),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.bank_inst,
"dout{0}_{1}".format(port, bit),
"dout{0}[{1}]".format(port, bit))
# Lower address bits go to BOTTOM/TOP
for bit in range(self.col_addr_size):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit),
pin=self.col_addr_dff_insts[port].get_pin("din_{}".format(bit)),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.col_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit))
# Upper address bits go to LEFT/RIGHT
for bit in range(self.row_addr_size):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit + self.col_addr_size),
pin=self.row_addr_dff_insts[port].get_pin("din_{}".format(bit)),
side=left_or_right,
bbox=bbox)
else:
self.copy_layout_pin(self.row_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit + self.col_addr_size))
# Data input pins go to BOTTOM/TOP
if port in self.write_ports:
2020-05-14 12:30:29 +02:00
for bit in range(self.word_size + self.num_spare_cols):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit),
pin=self.data_dff_insts[port].get_pin("din_{}".format(bit)),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.data_dff_insts[port],
"din_{}".format(bit),
"din{0}[{1}]".format(port, bit))
2019-08-23 00:01:41 +02:00
# Write mask pins go to BOTTOM/TOP
if port in self.write_ports:
2019-08-23 00:02:52 +02:00
if self.write_size:
for bit in range(self.num_wmasks):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="wmask{0}[{1}]".format(port, bit),
pin=self.wmask_dff_insts[port].get_pin("din_{}".format(bit)),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.wmask_dff_insts[port],
"din_{}".format(bit),
"wmask{0}[{1}]".format(port, bit))
# Spare wen pins go to BOTTOM/TOP
if port in self.write_ports:
2020-05-14 12:30:29 +02:00
for bit in range(self.num_spare_cols):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="spare_wen{0}[{1}]".format(port, bit),
pin=self.spare_wen_dff_insts[port].get_pin("din_{}".format(bit)),
side=left_or_right,
bbox=bbox)
else:
self.copy_layout_pin(self.spare_wen_dff_insts[port],
"din_{}".format(bit),
"spare_wen{0}[{1}]".format(port, bit))
def route_layout(self):
""" Route a single bank SRAM """
self.add_layout_pins()
2018-07-17 23:24:44 +02:00
self.route_clk()
2018-07-17 23:24:44 +02:00
self.route_control_logic()
self.route_row_addr_dff()
2020-06-27 17:23:12 +02:00
# if self.col_addr_dff:
# self.route_col_addr_dff()
2018-07-17 23:24:44 +02:00
2020-06-27 17:23:12 +02:00
# self.route_data_dff()
2019-08-21 20:33:43 +02:00
2020-06-27 17:23:12 +02:00
# if self.write_size:
# self.route_wmask_dff()
2018-07-17 23:24:44 +02:00
2020-06-27 17:23:12 +02:00
# if self.num_spare_cols:
# self.route_spare_wen_dff()
for port in self.all_ports:
self.route_dff(port)
2020-06-27 17:23:12 +02:00
def route_dff(self, port):
route_map = []
# column mux dff
if self.col_addr_size > 0:
dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)]
dff_pins = [self.col_addr_dff_insts[port].get_pin(x) for x in dff_names]
bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, dff_pins)))
# spare wen dff
if self.num_spare_cols > 0 and port in self.write_ports:
dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)]
dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names]
bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, dff_pins)))
# wmask dff
if self.num_wmasks > 0 and port in self.write_ports:
dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)]
dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names]
bank_names = ["bank_wmask{0}_{1}".format(port, x) for x in range(self.num_wmasks)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, dff_pins)))
if port in self.write_ports:
# data dff
dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)]
dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, dff_pins)))
if self.num_wmasks > 0 and port in self.write_ports:
layer_stack = self.m3_stack
else:
layer_stack = self.m1_stack
if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size + 2 * self.m1_pitch)
else:
offset = vector(0,
self.bank.height + 2 * self.m1_space)
if len(route_map) > 0:
self.create_horizontal_channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack)
2018-07-17 23:24:44 +02:00
def route_clk(self):
""" Route the clock network """
# This is the actual input to the SRAM
for port in self.all_ports:
# Connect all of these clock pins to the clock in the central bus
# This is something like a "spine" clock distribution. The two spines
# are clk_buf and clk_buf_bar
2018-11-29 00:30:52 +01:00
control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf")
control_clk_buf_pos = control_clk_buf_pin.center()
# This uses a metal2 track to the right (for port0) of the control/row addr DFF
# to route vertically. For port1, it is to the left.
2018-11-14 01:05:22 +01:00
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
if port % 2:
control_clk_buf_pos = control_clk_buf_pin.lc()
row_addr_clk_pos = row_addr_clk_pin.lc()
mid1_pos = vector(self.row_addr_dff_insts[port].lx() - self.m2_pitch,
row_addr_clk_pos.y)
else:
control_clk_buf_pos = control_clk_buf_pin.rc()
row_addr_clk_pos = row_addr_clk_pin.rc()
mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch,
row_addr_clk_pos.y)
2018-11-29 00:30:52 +01:00
# This is the steiner point where the net branches out
clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y)
self.add_path(control_clk_buf_pin.layer, [control_clk_buf_pos, clk_steiner_pos])
self.add_via_stack_center(from_layer=control_clk_buf_pin.layer,
to_layer="m2",
offset=clk_steiner_pos)
2018-11-29 00:30:52 +01:00
# Note, the via to the control logic is taken care of above
self.add_wire(self.m2_stack[::-1],
2020-03-06 01:13:49 +01:00
[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
2018-11-29 00:30:52 +01:00
if self.col_addr_dff:
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y)
self.add_wire(self.m2_stack[::-1],
2020-03-06 01:13:49 +01:00
[dff_clk_pos, mid_pos, clk_steiner_pos])
2020-06-27 17:23:12 +02:00
elif port in self.write_ports:
2018-11-29 00:30:52 +01:00
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
data_dff_clk_pos = data_dff_clk_pin.center()
mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y)
# In some designs, the steiner via will be too close to the mid_pos via
# so make the wire as wide as the contacts
2020-03-06 01:13:49 +01:00
self.add_path("m2",
[mid_pos, clk_steiner_pos],
width=max(m2_via.width, m2_via.height))
self.add_wire(self.m2_stack[::-1],
2020-03-06 01:13:49 +01:00
[data_dff_clk_pos, mid_pos, clk_steiner_pos])
2018-11-29 00:30:52 +01:00
2018-07-17 23:24:44 +02:00
def route_control_logic(self):
2019-07-16 18:04:58 +02:00
""" Route the control logic pins that are not inputs """
for port in self.all_ports:
for signal in self.control_logic_outputs[port]:
2018-11-29 00:30:52 +01:00
# The clock gets routed separately and is not a part of the bank
if "clk" in signal:
continue
2018-11-14 01:05:22 +01:00
src_pin = self.control_logic_insts[port].get_pin(signal)
2020-03-06 01:13:49 +01:00
dest_pin = self.bank_inst.get_pin(signal + "{}".format(port))
self.connect_vbus(src_pin, dest_pin)
2019-07-16 18:04:58 +02:00
2019-08-10 21:53:07 +02:00
for port in self.all_ports:
2019-07-16 18:04:58 +02:00
# Only input (besides pins) is the replica bitline
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
2020-03-06 01:13:49 +01:00
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
2020-06-04 20:03:39 +02:00
self.add_wire(self.m2_stack[::-1],
[src_pin.center(), vector(src_pin.cx(), dest_pin.cy()), dest_pin.rc()])
# self.connect_hbus(src_pin, dest_pin)
2020-03-06 18:03:52 +01:00
2018-07-17 23:24:44 +02:00
def route_row_addr_dff(self):
""" Connect the output of the row flops to the bank pins """
for port in self.all_ports:
for bit in range(self.row_addr_size):
flop_name = "dout_{}".format(bit)
2020-03-06 01:13:49 +01:00
bank_name = "addr{0}_{1}".format(port, bit + self.col_addr_size)
2018-11-14 01:05:22 +01:00
flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name)
bank_pin = self.bank_inst.get_pin(bank_name)
flop_pos = flop_pin.center()
bank_pos = bank_pin.center()
2020-03-06 01:13:49 +01:00
mid_pos = vector(bank_pos.x, flop_pos.y)
self.add_via_stack_center(from_layer=flop_pin.layer,
to_layer="m3",
offset=flop_pos)
2020-06-26 00:34:18 +02:00
self.add_path("m3", [flop_pos, mid_pos])
self.add_via_stack_center(from_layer=bank_pin.layer,
to_layer="m3",
offset=mid_pos)
self.add_path(bank_pin.layer, [mid_pos, bank_pos])
2018-07-17 23:24:44 +02:00
def route_col_addr_dff(self):
""" Connect the output of the col flops to the bank pins """
for port in self.all_ports:
if port % 2:
offset = self.col_addr_dff_insts[port].ll() - vector(0, self.col_addr_bus_size)
2019-07-03 22:17:12 +02:00
else:
offset = self.col_addr_dff_insts[port].ul() + vector(0, self.col_addr_bus_gap)
2019-07-03 22:17:12 +02:00
2020-03-06 01:13:49 +01:00
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
col_addr_bus_offsets = self.create_horizontal_bus(layer="m1",
2019-07-03 22:17:12 +02:00
offset=offset,
names=bus_names,
2018-11-14 01:05:22 +01:00
length=self.col_addr_dff_insts[port].width)
dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)]
data_dff_map = zip(dff_names, bus_names)
self.connect_horizontal_bus(data_dff_map,
self.col_addr_dff_insts[port],
col_addr_bus_offsets)
2020-03-06 01:13:49 +01:00
bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)]
data_bank_map = zip(bank_names, bus_names)
self.connect_horizontal_bus(data_bank_map,
self.bank_inst,
col_addr_bus_offsets)
2018-07-17 23:24:44 +02:00
def route_data_dff(self):
""" Connect the output of the data flops to the write driver """
# This is where the channel will start (y-dimension at least)
for port in self.write_ports:
if port % 2:
offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size)
else:
offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap)
2020-05-14 12:30:29 +02:00
dff_names = ["dout_{}".format(x) for x in range(self.word_size + self.num_spare_cols)]
dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
if self.write_size or self.num_spare_cols:
for x in dff_names:
pin = self.data_dff_insts[port].get_pin(x)
pin_offset = pin.center()
self.add_via_center(layers=self.m1_stack,
offset=pin_offset,
2020-03-06 01:13:49 +01:00
directions=("V", "V"))
self.add_via_stack_center(from_layer="m2",
to_layer="m4",
offset=pin_offset)
2020-05-14 12:30:29 +02:00
bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
if self.write_size or self.num_spare_cols:
for x in bank_names:
pin = self.bank_inst.get_pin(x)
if port % 2:
pin_offset = pin.uc()
else:
pin_offset = pin.bc()
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m4",
offset=pin_offset)
route_map = list(zip(bank_pins, dff_pins))
if self.write_size or self.num_spare_cols:
layer_stack = self.m3_stack
else:
layer_stack = self.m1_stack
self.create_horizontal_channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack)
def route_wmask_dff(self):
""" Connect the output of the wmask flops to the write mask AND array """
# This is where the channel will start (y-dimension at least)
for port in self.write_ports:
if port % 2:
offset = self.wmask_dff_insts[port].ll() - vector(0, self.wmask_bus_size)
else:
offset = self.wmask_dff_insts[port].ul() + vector(0, self.wmask_bus_gap)
dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)]
dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names]
2019-08-21 20:33:43 +02:00
for x in dff_names:
offset_pin = self.wmask_dff_insts[port].get_pin(x).center()
self.add_via_center(layers=self.m1_stack,
offset=offset_pin,
directions=("V", "V"))
bank_names = ["bank_wmask{0}_{1}".format(port, x) for x in range(self.num_wmasks)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
2019-08-21 20:33:43 +02:00
for x in bank_names:
offset_pin = self.bank_inst.get_pin(x).center()
self.add_via_center(layers=self.m1_stack,
offset=offset_pin)
route_map = list(zip(bank_pins, dff_pins))
self.create_horizontal_channel_route(netlist=route_map,
offset=offset,
layer_stack=self.m1_stack)
def route_spare_wen_dff(self):
""" Connect the output of the spare write enable flops to the spare write drivers """
# This is where the channel will start (y-dimension at least)
for port in self.write_ports:
if port % 2:
# for port 0
offset = self.spare_wen_dff_insts[port].ll() - vector(0, self.spare_wen_bus_size)
else:
offset = self.spare_wen_dff_insts[port].ul() + vector(0, self.spare_wen_bus_gap)
dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)]
dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names]
for x in dff_names:
offset_pin = self.spare_wen_dff_insts[port].get_pin(x).center()
self.add_via_center(layers=self.m1_stack,
offset=offset_pin,
directions=("V", "V"))
bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
for x in bank_names:
offset_pin = self.bank_inst.get_pin(x).center()
self.add_via_center(layers=self.m1_stack,
offset=offset_pin)
route_map = list(zip(bank_pins, dff_pins))
self.create_horizontal_channel_route(netlist=route_map,
offset=offset,
layer_stack=self.m1_stack)
def add_lvs_correspondence_points(self):
2020-03-06 01:13:49 +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.
"""
for n in self.control_logic_outputs[0]:
2018-11-14 01:05:22 +01:00
pin = self.control_logic_insts[0].get_pin(n)
self.add_label(text=n,
layer=pin.layer,
offset=pin.center())
def graph_exclude_data_dff(self):
"""Removes data dff and wmask dff (if applicable) from search graph. """
2020-03-06 01:13:49 +01:00
# Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay.
for inst in self.data_dff_insts:
self.graph_inst_exclude.add(inst)
if self.write_size:
for inst in self.wmask_dff_insts:
self.graph_inst_exclude.add(inst)
if self.num_spare_cols:
for inst in self.spare_wen_dff_insts:
self.graph_inst_exclude.add(inst)
def graph_exclude_addr_dff(self):
"""Removes data dff from search graph. """
2020-03-06 01:13:49 +01:00
# Address is considered not part of the critical path, subjectively removed
for inst in self.row_addr_dff_insts:
self.graph_inst_exclude.add(inst)
if self.col_addr_dff:
for inst in self.col_addr_dff_insts:
self.graph_inst_exclude.add(inst)
def graph_exclude_ctrl_dffs(self):
"""Exclude dffs for CSB, WEB, etc from graph"""
2020-03-06 01:13:49 +01:00
# Insts located in control logic, exclusion function called here
for inst in self.control_logic_insts:
inst.mod.graph_exclude_dffs()
def get_sen_name(self, sram_name, port=0):
"""Returns the s_en spice name."""
2020-03-06 01:13:49 +01:00
# Naming scheme is hardcoded using this function, should be built into the
# graph in someway.
sen_name = "s_en{}".format(port)
control_conns = self.get_conns(self.control_logic_insts[port])
2020-03-06 01:13:49 +01:00
# Sanity checks
if sen_name not in control_conns:
2020-03-06 01:13:49 +01:00
debug.error("Signal={} not contained in control logic connections={}".format(sen_name,
control_conns))
if sen_name in self.pins:
2020-03-06 01:13:49 +01:00
debug.error("Internal signal={} contained in port list. Name defined by the parent.".format(sen_name))
return "X{}.{}".format(sram_name, sen_name)
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-06 01:13:49 +01:00
# Sanity check in case it was forgotten
2019-05-21 03:35:52 +02:00
if inst_name.find('x') != 0:
2020-03-06 01:13:49 +01:00
inst_name = 'x' + inst_name
return self.bank_inst.mod.get_cell_name(inst_name + '.x' + self.bank_inst.name, row, col)