2019-04-26 21:21:50 +02:00
|
|
|
# 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.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2018-07-10 23:17:09 +02:00
|
|
|
import debug
|
|
|
|
|
from vector import vector
|
2018-07-12 19:30:45 +02:00
|
|
|
from sram_base import sram_base
|
2020-01-30 02:45:33 +01:00
|
|
|
from contact import m2_via
|
2020-06-15 01:44:10 +02:00
|
|
|
from globals import OPTS
|
2020-07-20 22:26:05 +02:00
|
|
|
import channel_route
|
|
|
|
|
|
2018-07-10 23:17:09 +02:00
|
|
|
|
2018-07-12 19:30:45 +02:00
|
|
|
class sram_1bank(sram_base):
|
2018-07-10 23:17:09 +02:00
|
|
|
"""
|
2018-07-12 19:30:45 +02:00
|
|
|
Procedures specific to a one bank SRAM.
|
2018-07-10 23:17:09 +02:00
|
|
|
"""
|
2018-09-04 19:47:24 +02:00
|
|
|
def __init__(self, name, sram_config):
|
|
|
|
|
sram_base.__init__(self, name, sram_config)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-27 23:18:32 +02:00
|
|
|
def create_modules(self):
|
2020-03-06 01:13:49 +01:00
|
|
|
"""
|
2018-08-27 23:18:32 +02:00
|
|
|
This adds the modules for a single bank SRAM with control
|
2020-03-06 01:13:49 +01:00
|
|
|
logic.
|
2018-08-27 23:18:32 +02:00
|
|
|
"""
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
self.bank_inst=self.create_bank(0)
|
2018-08-27 23:18:32 +02:00
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
self.control_logic_insts = self.create_control_logic()
|
2018-08-27 23:18:32 +02:00
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
self.row_addr_dff_insts = self.create_row_addr_dff()
|
2018-08-27 23:18:32 +02:00
|
|
|
|
|
|
|
|
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
|
|
|
|
2019-08-21 23:29:57 +02:00
|
|
|
if self.write_size:
|
2019-07-04 19:34:14 +02:00
|
|
|
self.wmask_dff_insts = self.create_wmask_dff()
|
2019-08-21 19:07:20 +02:00
|
|
|
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
|
|
|
|
2020-06-03 14:31:30 +02:00
|
|
|
if self.num_spare_cols:
|
|
|
|
|
self.spare_wen_dff_insts = self.create_spare_wen_dff()
|
|
|
|
|
else:
|
|
|
|
|
self.num_spare_cols = 0
|
|
|
|
|
|
2018-11-15 02:05:23 +01:00
|
|
|
def place_instances(self):
|
2020-03-06 01:13:49 +01:00
|
|
|
"""
|
2018-11-15 02:05:23 +01:00
|
|
|
This places the instances for a single bank SRAM with control
|
|
|
|
|
logic and up to 2 ports.
|
2018-07-10 23:17:09 +02:00
|
|
|
"""
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-07-10 23:17:09 +02:00
|
|
|
# No orientation or offset
|
2018-08-27 23:18:32 +02:00
|
|
|
self.place_bank(self.bank_inst, [0, 0], 1, 1)
|
2018-07-10 23:17:09 +02:00
|
|
|
|
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
|
2018-08-14 19:09:41 +02:00
|
|
|
# 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-07-20 22:26:05 +02:00
|
|
|
self.control_pos = [None] * len(self.all_ports)
|
|
|
|
|
self.row_addr_pos = [None] * len(self.all_ports)
|
|
|
|
|
|
|
|
|
|
# DFFs are placd on their own
|
|
|
|
|
self.col_addr_pos = [None] * len(self.all_ports)
|
|
|
|
|
self.wmask_pos = [None] * len(self.all_ports)
|
|
|
|
|
self.spare_wen_pos = [None] * len(self.all_ports)
|
|
|
|
|
self.data_pos = [None] * len(self.all_ports)
|
2018-11-15 02:05:23 +01:00
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
# 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
|
2020-06-29 14:54:30 +02:00
|
|
|
|
|
|
|
|
# Spare wen are on a separate layer so not included
|
2020-06-30 19:58:24 +02:00
|
|
|
# Start with 1 track minimum
|
|
|
|
|
self.data_bus_size = [1] * len(self.all_ports)
|
2020-07-20 22:26:05 +02:00
|
|
|
self.col_addr_bus_size = [1] * len(self.all_ports)
|
2020-06-29 14:54:30 +02:00
|
|
|
for port in self.all_ports:
|
2020-07-20 22:26:05 +02:00
|
|
|
# The column address wires are routed separately from the data bus and will always be smaller.
|
2020-06-29 14:54:30 +02:00
|
|
|
# All ports need the col addr flops
|
2020-07-20 22:26:05 +02:00
|
|
|
self.col_addr_bus_size[port] = self.col_addr_size * self.m4_nonpref_pitch
|
2020-06-29 14:54:30 +02:00
|
|
|
# Write ports need the data input flops and write mask flops
|
|
|
|
|
if port in self.write_ports:
|
|
|
|
|
self.data_bus_size[port] += self.num_wmasks + self.word_size
|
2020-07-01 23:44:01 +02:00
|
|
|
# This is for the din pins that get routed in the same channel
|
|
|
|
|
# when we have dout and din together
|
|
|
|
|
if port in self.readwrite_ports:
|
|
|
|
|
self.data_bus_size[port] += self.word_size
|
2020-06-29 14:54:30 +02:00
|
|
|
# Convert to length
|
|
|
|
|
self.data_bus_size[port] *= self.m4_nonpref_pitch
|
|
|
|
|
# Add the gap in unit length
|
|
|
|
|
self.data_bus_size[port] += self.data_bus_gap
|
2018-11-29 18:54:29 +01:00
|
|
|
|
2020-07-20 22:26:05 +02:00
|
|
|
# The control and row addr flops are independent of any bus widths.
|
|
|
|
|
self.place_control()
|
|
|
|
|
self.place_row_addr_dffs()
|
|
|
|
|
|
|
|
|
|
# Place with an initial wide channel (from above)
|
|
|
|
|
self.place_dffs()
|
|
|
|
|
# Route the channel and set to the new data bus size
|
|
|
|
|
self.route_dffs(add_routes=False)
|
|
|
|
|
# Re-place with the new channel size
|
|
|
|
|
self.place_dffs()
|
|
|
|
|
# Now route the channel
|
|
|
|
|
self.route_dffs()
|
|
|
|
|
|
|
|
|
|
def place_row_addr_dffs(self):
|
|
|
|
|
"""
|
|
|
|
|
Must be run after place control logic.
|
|
|
|
|
"""
|
2018-11-15 02:05:23 +01:00
|
|
|
port = 0
|
|
|
|
|
# The row address bits are placed above the control logic aligned on the right.
|
2018-11-19 17:41:26 +01:00
|
|
|
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-07-21 02:57:38 +02:00
|
|
|
y_offset = max(self.control_logic_insts[port].uy(), self.bank.predecoder_height)
|
2020-07-20 22:26:05 +02:00
|
|
|
self.row_addr_pos[port] = vector(x_offset, y_offset)
|
|
|
|
|
self.row_addr_dff_insts[port].place(self.row_addr_pos[port])
|
2018-11-29 18:54:29 +01:00
|
|
|
|
2020-07-20 22:26:05 +02:00
|
|
|
if len(self.all_ports)>1:
|
|
|
|
|
port = 1
|
|
|
|
|
# The row address bits are placed above the control logic aligned on the left.
|
|
|
|
|
x_offset = self.control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width
|
2020-07-21 02:57:38 +02:00
|
|
|
# If it can be placed above the predecoder and below the control logic, do it
|
|
|
|
|
y_offset = self.bank.bank_array_ll.y
|
2020-07-20 22:26:05 +02:00
|
|
|
self.row_addr_pos[port] = vector(x_offset, y_offset)
|
|
|
|
|
self.row_addr_dff_insts[port].place(self.row_addr_pos[port], mirror="XY")
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-07-20 22:26:05 +02:00
|
|
|
def place_control(self):
|
|
|
|
|
port = 0
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-11-29 18:54:29 +01:00
|
|
|
# This includes 2 M2 pitches for the row addr clock line.
|
2020-06-28 16:15:06 +02:00
|
|
|
# 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-07-20 22:26:05 +02:00
|
|
|
self.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(self.control_pos[port])
|
|
|
|
|
if len(self.all_ports) > 1:
|
|
|
|
|
port = 1
|
|
|
|
|
# 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.
|
|
|
|
|
self.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)
|
|
|
|
|
self.control_logic_insts[port].place(self.control_pos[port], mirror="XY")
|
|
|
|
|
|
|
|
|
|
def place_dffs(self):
|
|
|
|
|
"""
|
|
|
|
|
Place the col addr, data, wmask, and spare data DFFs.
|
|
|
|
|
This can be run more than once after we recompute the channel width.
|
|
|
|
|
"""
|
2018-11-15 02:05:23 +01:00
|
|
|
|
2020-07-20 22:26:05 +02:00
|
|
|
port = 0
|
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
|
2020-07-29 19:31:18 +02:00
|
|
|
# Place it a data bus below the x-axis, but at least as low as the control logic to not block
|
|
|
|
|
# the control logic signals
|
|
|
|
|
y_offset = min(-self.data_bus_size[port] - self.dff.height,
|
|
|
|
|
self.control_logic_insts[port].by())
|
2020-06-27 17:23:12 +02:00
|
|
|
if self.col_addr_dff:
|
2020-07-20 22:26:05 +02:00
|
|
|
self.col_addr_pos[port] = vector(x_offset,
|
|
|
|
|
y_offset)
|
|
|
|
|
self.col_addr_dff_insts[port].place(self.col_addr_pos[port])
|
2020-06-27 17:23:12 +02:00
|
|
|
x_offset = self.col_addr_dff_insts[port].rx()
|
|
|
|
|
else:
|
2020-07-20 22:26:05 +02:00
|
|
|
self.col_addr_pos[port] = vector(x_offset, 0)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-06-27 17:23:12 +02:00
|
|
|
if port in self.write_ports:
|
|
|
|
|
if self.write_size:
|
|
|
|
|
# Add the write mask flops below the write mask AND array.
|
2020-07-20 22:26:05 +02:00
|
|
|
self.wmask_pos[port] = vector(x_offset,
|
|
|
|
|
y_offset)
|
|
|
|
|
self.wmask_dff_insts[port].place(self.wmask_pos[port])
|
2020-06-27 17:23:12 +02:00
|
|
|
x_offset = self.wmask_dff_insts[port].rx()
|
2018-11-29 18:54:29 +01:00
|
|
|
|
2020-06-28 23:28:43 +02:00
|
|
|
# Add the data flops below the write mask flops.
|
2020-07-20 22:26:05 +02:00
|
|
|
self.data_pos[port] = vector(x_offset,
|
|
|
|
|
y_offset)
|
|
|
|
|
self.data_dff_insts[port].place(self.data_pos[port])
|
2020-06-28 23:28:43 +02:00
|
|
|
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:
|
2020-07-20 22:26:05 +02:00
|
|
|
self.spare_wen_pos[port] = vector(x_offset,
|
|
|
|
|
y_offset)
|
|
|
|
|
self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port])
|
2020-06-27 17:23:12 +02:00
|
|
|
x_offset = self.spare_wen_dff_insts[port].rx()
|
2019-08-14 20:45:22 +02:00
|
|
|
|
2020-06-27 17:23:12 +02:00
|
|
|
else:
|
2020-07-20 22:26:05 +02:00
|
|
|
self.wmask_pos[port] = vector(x_offset, y_offset)
|
|
|
|
|
self.data_pos[port] = vector(x_offset, y_offset)
|
|
|
|
|
self.spare_wen_pos[port] = vector(x_offset, y_offset)
|
2020-06-03 14:31:30 +02:00
|
|
|
|
2020-07-20 22:26:05 +02:00
|
|
|
if len(self.all_ports) > 1:
|
2018-11-15 02:11:23 +01:00
|
|
|
port = 1
|
2020-11-03 15:29:17 +01:00
|
|
|
|
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
|
2020-07-29 19:31:18 +02:00
|
|
|
# Place it a data bus below the x-axis, but at least as high as the control logic to not block
|
|
|
|
|
# the control logic signals
|
|
|
|
|
y_offset = max(self.bank.height + self.data_bus_size[port] + self.dff.height,
|
|
|
|
|
self.control_logic_insts[port].uy() - self.dff.height)
|
2020-06-27 17:23:12 +02:00
|
|
|
if self.col_addr_dff:
|
2020-07-20 22:26:05 +02:00
|
|
|
self.col_addr_pos[port] = vector(x_offset,
|
|
|
|
|
y_offset)
|
|
|
|
|
self.col_addr_dff_insts[port].place(self.col_addr_pos[port], mirror="XY")
|
2020-06-27 17:23:12 +02:00
|
|
|
x_offset = self.col_addr_dff_insts[port].lx()
|
|
|
|
|
else:
|
2020-07-20 22:26:05 +02:00
|
|
|
self.col_addr_pos[port] = vector(x_offset, y_offset)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-06-27 17:23:12 +02:00
|
|
|
if port in self.write_ports:
|
2020-06-28 23:28:43 +02:00
|
|
|
# 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:
|
2020-07-20 22:26:05 +02:00
|
|
|
self.spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width,
|
|
|
|
|
y_offset)
|
|
|
|
|
self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port], mirror="MX")
|
2020-06-28 23:28:43 +02:00
|
|
|
x_offset = self.spare_wen_dff_insts[port].lx()
|
2019-07-16 18:04:58 +02:00
|
|
|
|
2020-06-27 17:23:12 +02:00
|
|
|
if self.write_size:
|
|
|
|
|
# Add the write mask flops below the write mask AND array.
|
2020-07-20 22:26:05 +02:00
|
|
|
self.wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width,
|
|
|
|
|
y_offset)
|
|
|
|
|
self.wmask_dff_insts[port].place(self.wmask_pos[port], mirror="MX")
|
2020-06-27 17:23:12 +02:00
|
|
|
x_offset = self.wmask_dff_insts[port].lx()
|
|
|
|
|
|
|
|
|
|
# Add the data flops below the write mask flops.
|
2020-07-20 22:26:05 +02:00
|
|
|
self.data_pos[port] = vector(x_offset - self.data_dff_insts[port].width,
|
|
|
|
|
y_offset)
|
|
|
|
|
self.data_dff_insts[port].place(self.data_pos[port], mirror="MX")
|
2020-06-27 17:23:12 +02:00
|
|
|
else:
|
2020-07-20 22:26:05 +02:00
|
|
|
self.wmask_pos[port] = vector(x_offset, y_offset)
|
|
|
|
|
self.data_pos[port] = vector(x_offset, y_offset)
|
|
|
|
|
self.spare_wen_pos[port] = vector(x_offset, y_offset)
|
|
|
|
|
|
2018-07-13 18:30:14 +02:00
|
|
|
def add_layout_pins(self):
|
2018-07-10 23:17:09 +02:00
|
|
|
"""
|
|
|
|
|
Add the top-level pins for a single bank SRAM with control.
|
|
|
|
|
"""
|
2020-06-15 00:52:09 +02:00
|
|
|
highest_coord = self.find_highest_coords()
|
|
|
|
|
lowest_coord = self.find_lowest_coords()
|
|
|
|
|
bbox = [lowest_coord, highest_coord]
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2020-06-15 00:52:09 +02:00
|
|
|
# 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"
|
2018-09-27 04:10:24 +02:00
|
|
|
|
|
|
|
|
# Connect the control pins as inputs
|
2020-06-15 00:52:09 +02:00
|
|
|
for signal in self.control_logic_inputs[port]:
|
|
|
|
|
if signal == "clk":
|
|
|
|
|
continue
|
2020-08-25 23:42:21 +02:00
|
|
|
if signal.startswith("rbl"):
|
|
|
|
|
continue
|
2020-06-15 01:44:10 +02:00
|
|
|
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))
|
2020-06-15 00:52:09 +02:00
|
|
|
|
2020-06-15 01:44:10 +02:00
|
|
|
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))
|
2018-09-27 04:10:24 +02:00
|
|
|
|
2020-07-01 23:44:01 +02:00
|
|
|
# Data input pins go to BOTTOM/TOP
|
|
|
|
|
din_ports = []
|
|
|
|
|
if port in self.write_ports:
|
|
|
|
|
for bit in range(self.word_size + self.num_spare_cols):
|
|
|
|
|
if OPTS.perimeter_pins:
|
|
|
|
|
p = self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit),
|
|
|
|
|
pin=self.data_dff_insts[port].get_pin("din_{0}".format(bit)),
|
|
|
|
|
side=bottom_or_top,
|
|
|
|
|
bbox=bbox)
|
|
|
|
|
din_ports.append(p)
|
|
|
|
|
else:
|
2020-07-02 00:10:20 +02:00
|
|
|
self.copy_layout_pin(self.data_dff_insts[port],
|
|
|
|
|
"din_{}".format(bit),
|
2020-07-01 23:44:01 +02:00
|
|
|
"din{0}[{1}]".format(port, bit))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-06-15 00:52:09 +02:00
|
|
|
# Data output pins go to BOTTOM/TOP
|
2020-07-01 23:44:01 +02:00
|
|
|
if port in self.readwrite_ports and OPTS.perimeter_pins:
|
|
|
|
|
for bit in range(self.word_size + self.num_spare_cols):
|
|
|
|
|
# This should be routed next to the din pin
|
|
|
|
|
p = din_ports[bit]
|
|
|
|
|
self.add_layout_pin_rect_center(text="dout{0}[{1}]".format(port, bit),
|
|
|
|
|
layer=p.layer,
|
|
|
|
|
offset=p.center() + vector(self.m3_pitch, 0),
|
|
|
|
|
width=p.width(),
|
|
|
|
|
height=p.height())
|
|
|
|
|
elif port in self.read_ports:
|
2020-05-14 12:30:29 +02:00
|
|
|
for bit in range(self.word_size + self.num_spare_cols):
|
2020-06-15 01:44:10 +02:00
|
|
|
if OPTS.perimeter_pins:
|
2020-07-01 23:44:01 +02:00
|
|
|
# This should have a clear route to the perimeter if there are no din routes
|
2020-06-15 01:44:10 +02:00
|
|
|
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:
|
2020-07-02 00:10:20 +02:00
|
|
|
self.copy_layout_pin(self.bank_inst,
|
|
|
|
|
"dout{0}_{1}".format(port, bit),
|
2020-06-15 01:44:10 +02:00
|
|
|
"dout{0}[{1}]".format(port, bit))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
|
|
|
|
|
2018-09-27 04:10:24 +02:00
|
|
|
|
2020-06-15 00:52:09 +02:00
|
|
|
# Lower address bits go to BOTTOM/TOP
|
2018-09-27 04:10:24 +02:00
|
|
|
for bit in range(self.col_addr_size):
|
2020-06-15 01:44:10 +02:00
|
|
|
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))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-06-15 00:52:09 +02:00
|
|
|
# Upper address bits go to LEFT/RIGHT
|
2018-09-27 04:10:24 +02:00
|
|
|
for bit in range(self.row_addr_size):
|
2020-06-15 01:44:10 +02:00
|
|
|
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))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-06-15 00:52:09 +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:
|
2019-08-14 20:45:22 +02:00
|
|
|
for bit in range(self.num_wmasks):
|
2020-06-15 01:44:10 +02:00
|
|
|
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))
|
2020-06-15 00:52:09 +02:00
|
|
|
|
|
|
|
|
# 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):
|
2020-06-15 01:44:10 +02:00
|
|
|
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))
|
2019-08-14 20:45:22 +02:00
|
|
|
|
2018-11-15 02:05:23 +01:00
|
|
|
def route_layout(self):
|
2018-07-10 23:17:09 +02:00
|
|
|
""" Route a single bank SRAM """
|
|
|
|
|
|
2018-07-13 18:30:14 +02:00
|
|
|
self.add_layout_pins()
|
2018-07-17 23:24:44 +02:00
|
|
|
|
|
|
|
|
self.route_clk()
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-07-17 23:24:44 +02:00
|
|
|
self.route_control_logic()
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-07-17 23:24:44 +02:00
|
|
|
self.route_row_addr_dff()
|
|
|
|
|
|
2020-07-20 22:26:05 +02:00
|
|
|
def route_dffs(self, add_routes=True):
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-06-27 17:23:12 +02:00
|
|
|
for port in self.all_ports:
|
2020-07-20 22:26:05 +02:00
|
|
|
self.route_dff(port, add_routes)
|
2020-06-03 14:31:30 +02:00
|
|
|
|
2020-07-20 22:26:05 +02:00
|
|
|
def route_dff(self, port, add_routes):
|
2020-06-27 17:23:12 +02:00
|
|
|
|
|
|
|
|
route_map = []
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-07-20 21:02:59 +02:00
|
|
|
# column mux dff is routed on it's own since it is to the far end
|
|
|
|
|
# decoder inputs are min pitch M2, so need to use lower layer stack
|
2020-06-27 17:23:12 +02:00
|
|
|
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)))
|
2020-07-20 21:02:59 +02:00
|
|
|
|
2020-06-27 17:23:12 +02:00
|
|
|
# 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:
|
2020-07-01 23:44:01 +02:00
|
|
|
# synchronized inputs from data dff
|
2020-06-27 17:23:12 +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]
|
|
|
|
|
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)))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-07-01 23:44:01 +02:00
|
|
|
if port in self.readwrite_ports and OPTS.perimeter_pins:
|
|
|
|
|
# outputs from sense amp
|
|
|
|
|
# These are the output pins which had their pin placed on the perimeter, so route from the
|
|
|
|
|
# sense amp which should not align with write driver input
|
|
|
|
|
sram_names = ["dout{0}[{1}]".format(port, x) for x in range(self.word_size + self.num_spare_cols)]
|
|
|
|
|
sram_pins = [self.get_pin(x) for x in sram_names]
|
|
|
|
|
bank_names = ["dout{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, sram_pins)))
|
2018-07-17 23:24:44 +02:00
|
|
|
|
2020-06-29 01:42:25 +02:00
|
|
|
# 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)))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-06-27 17:23:12 +02:00
|
|
|
if len(route_map) > 0:
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-07-20 22:26:05 +02:00
|
|
|
if self.num_wmasks > 0 and port in self.write_ports:
|
|
|
|
|
layer_stack = self.m3_stack
|
|
|
|
|
else:
|
|
|
|
|
layer_stack = self.m1_stack
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-07-20 21:02:59 +02:00
|
|
|
if port == 0:
|
|
|
|
|
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
|
2020-12-17 20:39:17 +01:00
|
|
|
- self.data_bus_size[port] + 2 * self.m3_pitch)
|
2020-07-20 21:02:59 +02:00
|
|
|
cr = channel_route.channel_route(netlist=route_map,
|
2020-06-27 17:23:12 +02:00
|
|
|
offset=offset,
|
2020-07-20 21:02:59 +02:00
|
|
|
layer_stack=layer_stack,
|
|
|
|
|
parent=self)
|
2020-07-20 22:26:05 +02:00
|
|
|
if add_routes:
|
2020-12-16 01:37:23 +01:00
|
|
|
# This causes problem in magic since it sometimes cannot extract connectivity of isntances
|
|
|
|
|
# with no active devices.
|
|
|
|
|
# self.add_inst(cr.name, cr)
|
|
|
|
|
# self.connect_inst([])
|
|
|
|
|
self.add_flat_inst(cr.name, cr)
|
2020-07-20 22:26:05 +02:00
|
|
|
else:
|
2020-07-20 22:43:57 +02:00
|
|
|
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
|
2020-07-20 21:02:59 +02:00
|
|
|
else:
|
|
|
|
|
offset = vector(0,
|
2020-12-17 20:39:17 +01:00
|
|
|
self.bank.height + self.m3_pitch)
|
2020-07-20 22:26:05 +02:00
|
|
|
cr = channel_route.channel_route(netlist=route_map,
|
|
|
|
|
offset=offset,
|
|
|
|
|
layer_stack=layer_stack,
|
|
|
|
|
parent=self)
|
|
|
|
|
if add_routes:
|
2020-12-16 01:37:23 +01:00
|
|
|
# This causes problem in magic since it sometimes cannot extract connectivity of isntances
|
|
|
|
|
# with no active devices.
|
|
|
|
|
# self.add_inst(cr.name, cr)
|
|
|
|
|
# self.connect_inst([])
|
|
|
|
|
self.add_flat_inst(cr.name, cr)
|
2020-07-20 22:26:05 +02:00
|
|
|
else:
|
2020-07-20 22:43:57 +02:00
|
|
|
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-07-17 23:24:44 +02:00
|
|
|
def route_clk(self):
|
|
|
|
|
""" Route the clock network """
|
2018-08-14 19:09:41 +02:00
|
|
|
|
|
|
|
|
# This is the actual input to the SRAM
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
# 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()
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-11-15 19:30:38 +01:00
|
|
|
# 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")
|
2020-05-07 21:35:21 +02:00
|
|
|
if port % 2:
|
2018-11-15 19:30:38 +01:00
|
|
|
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)
|
2020-05-07 21:35:21 +02:00
|
|
|
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)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-12-06 21:45:45 +01:00
|
|
|
# Note, the via to the control logic is taken care of above
|
2020-05-07 21:35:21 +02:00
|
|
|
self.add_wire(self.m2_stack[::-1],
|
2020-03-06 01:13:49 +01:00
|
|
|
[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
|
2020-11-03 15:29:17 +01:00
|
|
|
|
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)
|
2020-05-07 21:35:21 +02:00
|
|
|
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)
|
2018-12-06 20:58:57 +01:00
|
|
|
# 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))
|
2020-05-07 21:35:21 +02:00
|
|
|
self.add_wire(self.m2_stack[::-1],
|
2020-03-06 01:13:49 +01:00
|
|
|
[data_dff_clk_pos, mid_pos, clk_steiner_pos])
|
2019-08-14 20:45:22 +02:00
|
|
|
|
2018-07-17 23:24:44 +02:00
|
|
|
def route_control_logic(self):
|
2020-09-29 21:15:42 +02:00
|
|
|
"""
|
|
|
|
|
Route the control logic pins that are not inputs
|
|
|
|
|
"""
|
2019-07-16 18:04:58 +02:00
|
|
|
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
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))
|
2020-05-07 21:35:21 +02:00
|
|
|
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-08-25 20:50:44 +02:00
|
|
|
dest_pin = self.bank_inst.get_pin("rbl_bl_{0}_{0}".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()])
|
2020-07-01 18:22:59 +02:00
|
|
|
self.add_via_stack_center(from_layer=src_pin.layer,
|
|
|
|
|
to_layer="m2",
|
|
|
|
|
offset=src_pin.center())
|
|
|
|
|
self.add_via_stack_center(from_layer=dest_pin.layer,
|
|
|
|
|
to_layer="m2",
|
|
|
|
|
offset=dest_pin.center())
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-07-17 23:24:44 +02:00
|
|
|
def route_row_addr_dff(self):
|
2020-11-03 15:29:17 +01:00
|
|
|
"""
|
2020-09-29 21:15:42 +02:00
|
|
|
Connect the output of the row flops to the bank pins
|
|
|
|
|
"""
|
2018-11-08 21:19:40 +01:00
|
|
|
for port in self.all_ports:
|
2018-09-27 04:10:24 +02:00
|
|
|
for bit in range(self.row_addr_size):
|
2018-10-11 18:53:08 +02:00
|
|
|
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)
|
2018-09-27 04:10:24 +02:00
|
|
|
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)
|
2020-05-07 21:35:21 +02:00
|
|
|
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-10 23:17:09 +02:00
|
|
|
|
2018-07-18 23:29:04 +02:00
|
|
|
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.
|
2018-07-18 23:29:04 +02:00
|
|
|
These should probably be turned off by default though, since extraction
|
|
|
|
|
will show these as ports in the extracted netlist.
|
|
|
|
|
"""
|
2020-08-25 23:42:21 +02:00
|
|
|
return
|
2018-09-27 04:10:24 +02:00
|
|
|
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)
|
2018-07-18 23:29:04 +02:00
|
|
|
self.add_label(text=n,
|
2018-07-19 00:10:57 +02:00
|
|
|
layer=pin.layer,
|
|
|
|
|
offset=pin.center())
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-04-24 23:23:22 +02:00
|
|
|
def graph_exclude_data_dff(self):
|
2020-09-29 21:15:42 +02:00
|
|
|
"""
|
|
|
|
|
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.
|
2019-04-24 23:23:22 +02:00
|
|
|
for inst in self.data_dff_insts:
|
|
|
|
|
self.graph_inst_exclude.add(inst)
|
2019-08-21 23:29:57 +02:00
|
|
|
if self.write_size:
|
2019-07-04 19:34:14 +02:00
|
|
|
for inst in self.wmask_dff_insts:
|
|
|
|
|
self.graph_inst_exclude.add(inst)
|
2020-06-03 14:31:30 +02:00
|
|
|
if self.num_spare_cols:
|
|
|
|
|
for inst in self.spare_wen_dff_insts:
|
|
|
|
|
self.graph_inst_exclude.add(inst)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-04-24 23:23:22 +02:00
|
|
|
def graph_exclude_addr_dff(self):
|
2020-09-29 21:15:42 +02:00
|
|
|
"""
|
|
|
|
|
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
|
2019-04-24 23:23:22 +02:00
|
|
|
for inst in self.row_addr_dff_insts:
|
|
|
|
|
self.graph_inst_exclude.add(inst)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-04-24 23:23:22 +02:00
|
|
|
if self.col_addr_dff:
|
|
|
|
|
for inst in self.col_addr_dff_insts:
|
2019-04-25 08:51:09 +02:00
|
|
|
self.graph_inst_exclude.add(inst)
|
|
|
|
|
|
|
|
|
|
def graph_exclude_ctrl_dffs(self):
|
2020-09-29 21:15:42 +02:00
|
|
|
"""
|
|
|
|
|
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
|
2019-04-25 08:51:09 +02:00
|
|
|
for inst in self.control_logic_insts:
|
|
|
|
|
inst.mod.graph_exclude_dffs()
|
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):
|
2020-09-29 21:15:42 +02:00
|
|
|
"""
|
|
|
|
|
Gets the spice name of the target bitcell.
|
|
|
|
|
"""
|
2020-03-06 01:13:49 +01:00
|
|
|
# Sanity check in case it was forgotten
|
2020-09-29 21:15:42 +02:00
|
|
|
if inst_name.find("x") != 0:
|
|
|
|
|
inst_name = "x" + inst_name
|
|
|
|
|
return self.bank_inst.mod.get_cell_name(inst_name + ".x" + self.bank_inst.name, row, col)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-01-16 12:51:29 +01:00
|
|
|
def get_bank_num(self, inst_name, row, col):
|
2020-09-29 21:15:42 +02:00
|
|
|
return 0
|