2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2023-01-29 07:56:27 +01:00
|
|
|
# Copyright (c) 2016-2023 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.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2016-11-08 18:57:35 +01:00
|
|
|
import math
|
2022-11-27 22:01:20 +01:00
|
|
|
from openram import debug
|
|
|
|
|
from openram.base import vector
|
|
|
|
|
from openram.sram_factory import factory
|
|
|
|
|
from openram import OPTS
|
2022-07-27 20:09:10 +02:00
|
|
|
from .control_logic_base import control_logic_base
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-04-15 23:29:43 +02:00
|
|
|
|
2022-07-27 08:22:02 +02:00
|
|
|
class control_logic(control_logic_base):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
|
|
|
|
Dynamically generated Control logic for the total SRAM circuit.
|
|
|
|
|
"""
|
|
|
|
|
|
2020-05-14 12:30:29 +02:00
|
|
|
def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""):
|
2016-11-08 18:57:35 +01:00
|
|
|
""" Constructor """
|
2022-07-27 08:22:02 +02:00
|
|
|
super().__init__(num_rows, words_per_row, word_size, spare_columns, sram, port_type, name)
|
2018-03-15 01:30:41 +01:00
|
|
|
|
2018-03-21 21:20:48 +01:00
|
|
|
def add_pins(self):
|
|
|
|
|
""" Add the pins to the control logic module. """
|
2019-07-12 17:42:36 +02:00
|
|
|
self.add_pin_list(self.input_list + ["clk"] + self.rbl_list, "INPUT")
|
2020-04-15 23:29:43 +02:00
|
|
|
self.add_pin_list(self.output_list, "OUTPUT")
|
|
|
|
|
self.add_pin("vdd", "POWER")
|
|
|
|
|
self.add_pin("gnd", "GROUND")
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def add_modules(self):
|
|
|
|
|
""" Add all the required modules """
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-28 18:36:13 +01:00
|
|
|
self.dff = factory.create(module_type="dff_buf")
|
|
|
|
|
dff_height = self.dff.height
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
self.ctrl_dff_array = factory.create(module_type="dff_buf_array",
|
|
|
|
|
rows=self.num_control_signals,
|
|
|
|
|
columns=1)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-07-27 06:41:27 +02:00
|
|
|
self.and2 = factory.create(module_type="pand2",
|
2020-04-22 22:27:50 +02:00
|
|
|
size=12,
|
2019-01-17 01:15:38 +01:00
|
|
|
height=dff_height)
|
2019-07-16 18:04:58 +02:00
|
|
|
|
2019-07-27 20:09:08 +02:00
|
|
|
self.rbl_driver = factory.create(module_type="pbuf",
|
|
|
|
|
size=self.num_cols,
|
|
|
|
|
height=dff_height)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-04-15 23:29:43 +02:00
|
|
|
# clk_buf drives a flop for every address
|
|
|
|
|
addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
|
2019-08-07 01:29:07 +02:00
|
|
|
# plus data flops and control flops
|
2020-06-03 14:31:30 +02:00
|
|
|
num_flops = addr_flops + self.word_size + self.num_spare_cols + self.num_control_signals
|
2019-08-07 01:29:07 +02:00
|
|
|
# each flop internally has a FO 5 approximately
|
2019-01-25 19:26:31 +01:00
|
|
|
# plus about 5 fanouts for the control logic
|
2020-04-15 23:29:43 +02:00
|
|
|
clock_fanout = 5 * num_flops + 5
|
2019-01-25 23:03:52 +01:00
|
|
|
self.clk_buf_driver = factory.create(module_type="pdriver",
|
|
|
|
|
fanout=clock_fanout,
|
|
|
|
|
height=dff_height)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-08-07 01:29:07 +02:00
|
|
|
# We will use the maximum since this same value is used to size the wl_en
|
|
|
|
|
# and the p_en_bar drivers
|
2020-10-26 23:53:22 +01:00
|
|
|
# max_fanout = max(self.num_rows, self.num_cols)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-01-23 21:03:52 +01:00
|
|
|
# wl_en drives every row in the bank
|
2021-09-03 21:52:17 +02:00
|
|
|
# MRG 9/3/2021: Ensure that this is two stages to prevent race conditions with the write driver
|
|
|
|
|
size_list = [max(int(self.num_rows / 9), 1), max(int(self.num_rows / 3), 1)]
|
2019-01-23 21:03:52 +01:00
|
|
|
self.wl_en_driver = factory.create(module_type="pdriver",
|
2021-09-03 21:52:17 +02:00
|
|
|
size_list=size_list,
|
2019-01-23 21:03:52 +01:00
|
|
|
height=dff_height)
|
|
|
|
|
|
|
|
|
|
# w_en drives every write driver
|
2019-09-27 23:18:49 +02:00
|
|
|
self.wen_and = factory.create(module_type="pand3",
|
2021-11-22 19:51:40 +01:00
|
|
|
size=self.word_size + 8,
|
|
|
|
|
height=dff_height)
|
2019-01-23 21:03:52 +01:00
|
|
|
|
|
|
|
|
# s_en drives every sense amp
|
2019-08-11 01:30:02 +02:00
|
|
|
self.sen_and3 = factory.create(module_type="pand3",
|
2020-06-03 14:31:30 +02:00
|
|
|
size=self.word_size + self.num_spare_cols,
|
2019-07-27 06:41:27 +02:00
|
|
|
height=dff_height)
|
2019-01-23 21:03:52 +01:00
|
|
|
|
2020-04-15 23:29:43 +02:00
|
|
|
# used to generate inverted signals with low fanout
|
2019-01-23 21:03:52 +01:00
|
|
|
self.inv = factory.create(module_type="pinv",
|
2019-08-07 01:29:07 +02:00
|
|
|
size=1,
|
|
|
|
|
height=dff_height)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-07-16 18:04:58 +02:00
|
|
|
# p_en_bar drives every column in the bitcell array
|
2019-08-07 01:29:07 +02:00
|
|
|
# but it is sized the same as the wl_en driver with
|
|
|
|
|
# prepended 3 inverter stages to guarantee it is slower and odd polarity
|
2019-01-23 21:03:52 +01:00
|
|
|
self.p_en_bar_driver = factory.create(module_type="pdriver",
|
2019-08-07 02:17:59 +02:00
|
|
|
fanout=self.num_cols,
|
2019-01-23 21:03:52 +01:00
|
|
|
height=dff_height)
|
2019-07-16 18:04:58 +02:00
|
|
|
|
2019-08-08 02:14:33 +02:00
|
|
|
self.nand2 = factory.create(module_type="pnand2",
|
|
|
|
|
height=dff_height)
|
2019-07-12 17:42:36 +02:00
|
|
|
|
2020-04-15 23:29:43 +02:00
|
|
|
debug.check(OPTS.delay_chain_stages % 2,
|
|
|
|
|
"Must use odd number of delay chain stages for inverting delay chain.")
|
2019-07-12 17:42:36 +02:00
|
|
|
self.delay_chain=factory.create(module_type="delay_chain",
|
2020-04-15 23:29:43 +02:00
|
|
|
fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
|
2020-10-26 23:53:22 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def setup_signal_busses(self):
|
|
|
|
|
""" Setup bus names, determine the size of the busses etc """
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-03-21 21:20:48 +01:00
|
|
|
# List of input control signals
|
2018-10-04 18:31:04 +02:00
|
|
|
if self.port_type == "rw":
|
|
|
|
|
self.input_list = ["csb", "web"]
|
2019-07-12 17:42:36 +02:00
|
|
|
self.rbl_list = ["rbl_bl"]
|
2018-09-26 05:00:25 +02:00
|
|
|
else:
|
2018-10-04 18:31:04 +02:00
|
|
|
self.input_list = ["csb"]
|
2019-08-08 02:14:33 +02:00
|
|
|
self.rbl_list = ["rbl_bl"]
|
2019-07-02 00:51:40 +02:00
|
|
|
|
2018-10-04 18:31:04 +02:00
|
|
|
if self.port_type == "rw":
|
|
|
|
|
self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"]
|
2018-09-26 05:00:25 +02:00
|
|
|
else:
|
2018-10-04 18:31:04 +02:00
|
|
|
self.dff_output_list = ["cs_bar", "cs"]
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-21 21:20:48 +01:00
|
|
|
# list of output control signals (for making a vertical bus)
|
2018-10-04 18:31:04 +02:00
|
|
|
if self.port_type == "rw":
|
2020-06-15 19:25:53 +02:00
|
|
|
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "we_bar", "clk_buf", "cs"]
|
2018-11-28 20:02:24 +01:00
|
|
|
elif self.port_type == "r":
|
2019-09-27 23:18:49 +02:00
|
|
|
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"]
|
2018-10-04 18:31:04 +02:00
|
|
|
else:
|
2019-09-27 23:18:49 +02:00
|
|
|
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
|
2018-07-24 23:15:11 +02:00
|
|
|
# leave space for the bus plus one extra space
|
2020-04-15 23:29:43 +02:00
|
|
|
self.internal_bus_width = (len(self.internal_bus_list) + 1) * self.m2_pitch
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-14 19:09:41 +02:00
|
|
|
# Outputs to the bank
|
2018-11-29 00:30:52 +01:00
|
|
|
if self.port_type == "rw":
|
2019-08-07 01:29:07 +02:00
|
|
|
self.output_list = ["s_en", "w_en"]
|
2018-11-29 00:30:52 +01:00
|
|
|
elif self.port_type == "r":
|
2019-08-07 01:29:07 +02:00
|
|
|
self.output_list = ["s_en"]
|
2018-09-26 05:00:25 +02:00
|
|
|
else:
|
2018-11-29 00:30:52 +01:00
|
|
|
self.output_list = ["w_en"]
|
2019-08-01 21:42:51 +02:00
|
|
|
self.output_list.append("p_en_bar")
|
2018-11-27 03:00:59 +01:00
|
|
|
self.output_list.append("wl_en")
|
2018-09-12 09:59:07 +02:00
|
|
|
self.output_list.append("clk_buf")
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-15 01:30:41 +01:00
|
|
|
self.supply_list = ["vdd", "gnd"]
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
def create_instances(self):
|
|
|
|
|
""" Create all the instances """
|
2018-08-28 01:42:48 +02:00
|
|
|
self.create_dffs()
|
2018-11-28 02:18:03 +01:00
|
|
|
self.create_clk_buf_row()
|
|
|
|
|
self.create_gated_clk_bar_row()
|
|
|
|
|
self.create_gated_clk_buf_row()
|
2018-11-27 03:00:59 +01:00
|
|
|
self.create_wlen_row()
|
2018-10-04 18:31:04 +02:00
|
|
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
2019-09-27 23:18:49 +02:00
|
|
|
self.create_rbl_delay_row()
|
2018-11-27 03:00:59 +01:00
|
|
|
self.create_wen_row()
|
2019-07-16 18:04:58 +02:00
|
|
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
2018-09-26 05:00:25 +02:00
|
|
|
self.create_sen_row()
|
2019-08-08 02:14:33 +02:00
|
|
|
self.create_delay()
|
2020-04-15 23:29:43 +02:00
|
|
|
self.create_pen_row()
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2022-08-01 19:27:57 +02:00
|
|
|
def place_logic_rows(self):
|
2018-09-26 05:00:25 +02:00
|
|
|
row = 0
|
2020-04-15 23:29:43 +02:00
|
|
|
self.place_clk_buf_row(row)
|
2018-11-28 02:18:03 +01:00
|
|
|
row += 1
|
2020-04-15 23:29:43 +02:00
|
|
|
self.place_gated_clk_bar_row(row)
|
2018-11-27 00:29:42 +01:00
|
|
|
row += 1
|
2020-04-15 23:29:43 +02:00
|
|
|
self.place_gated_clk_buf_row(row)
|
2018-11-27 03:00:59 +01:00
|
|
|
row += 1
|
2021-03-24 22:32:10 +01:00
|
|
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
|
|
|
|
self.place_sen_row(row)
|
|
|
|
|
row += 1
|
2018-10-04 18:31:04 +02:00
|
|
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
2018-11-28 02:18:03 +01:00
|
|
|
self.place_wen_row(row)
|
2018-09-26 05:00:25 +02:00
|
|
|
row += 1
|
2019-07-25 19:31:39 +02:00
|
|
|
self.place_pen_row(row)
|
|
|
|
|
row += 1
|
2020-04-15 23:29:43 +02:00
|
|
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
2019-09-27 23:18:49 +02:00
|
|
|
self.place_rbl_delay_row(row)
|
2020-05-07 21:35:21 +02:00
|
|
|
row += 1
|
2021-03-24 22:32:10 +01:00
|
|
|
self.place_wlen_row(row)
|
2021-05-05 01:42:42 +02:00
|
|
|
|
2022-08-01 19:27:57 +02:00
|
|
|
self.control_center_y = self.wl_en_inst.uy() + self.m3_pitch
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def route_all(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
""" Routing between modules """
|
2018-11-18 18:15:03 +01:00
|
|
|
self.route_rails()
|
2018-03-21 21:20:48 +01:00
|
|
|
self.route_dffs()
|
2018-11-27 03:00:59 +01:00
|
|
|
self.route_wlen()
|
2018-10-04 18:31:04 +02:00
|
|
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
2019-09-27 23:18:49 +02:00
|
|
|
self.route_rbl_delay()
|
2018-09-26 05:00:25 +02:00
|
|
|
self.route_wen()
|
2018-10-04 18:31:04 +02:00
|
|
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
2018-09-26 05:00:25 +02:00
|
|
|
self.route_sen()
|
2019-08-08 02:14:33 +02:00
|
|
|
self.route_delay()
|
2019-07-25 19:31:39 +02:00
|
|
|
self.route_pen()
|
2018-11-28 02:18:03 +01:00
|
|
|
self.route_clk_buf()
|
|
|
|
|
self.route_gated_clk_bar()
|
|
|
|
|
self.route_gated_clk_buf()
|
2022-04-05 22:51:55 +02:00
|
|
|
self.route_supplies()
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2019-07-12 17:42:36 +02:00
|
|
|
def create_delay(self):
|
2022-07-27 08:22:02 +02:00
|
|
|
""" Create the delay chain """
|
2019-07-12 17:42:36 +02:00
|
|
|
self.delay_inst=self.add_inst(name="delay_chain",
|
|
|
|
|
mod=self.delay_chain)
|
2019-09-08 08:22:01 +02:00
|
|
|
# rbl_bl_delay is asserted (1) when the bitline has been discharged
|
2019-08-08 02:14:33 +02:00
|
|
|
self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"])
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2019-08-08 02:14:33 +02:00
|
|
|
def route_delay(self):
|
2019-08-10 17:53:02 +02:00
|
|
|
|
2021-05-05 01:42:42 +02:00
|
|
|
out_pos = self.delay_inst.get_pin("out").center()
|
2019-08-10 17:53:02 +02:00
|
|
|
# Connect to the rail level with the vdd rail
|
2021-05-05 01:42:42 +02:00
|
|
|
# Use gated clock since it is in every type of control logic
|
|
|
|
|
vdd_ypos = self.gated_clk_buf_inst.get_pin("vdd").cy() + self.m1_pitch
|
2020-06-08 20:01:14 +02:00
|
|
|
in_pos = vector(self.input_bus["rbl_bl_delay"].cx(), vdd_ypos)
|
2020-04-15 23:29:43 +02:00
|
|
|
mid1 = vector(out_pos.x, in_pos.y)
|
|
|
|
|
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_via_center(layers=self.m1_stack,
|
2019-08-10 17:53:02 +02:00
|
|
|
offset=in_pos)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-08-08 02:14:33 +02:00
|
|
|
# Input from RBL goes to the delay line for futher delay
|
|
|
|
|
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-11-27 03:00:59 +01:00
|
|
|
def create_wlen_row(self):
|
|
|
|
|
# input pre_p_en, output: wl_en
|
2019-07-27 06:41:27 +02:00
|
|
|
self.wl_en_inst=self.add_inst(name="buf_wl_en",
|
2019-01-23 21:03:52 +01:00
|
|
|
mod=self.wl_en_driver)
|
2018-11-28 02:18:03 +01:00
|
|
|
self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"])
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-11-27 03:00:59 +01:00
|
|
|
def place_wlen_row(self, row):
|
2019-08-10 17:53:02 +02:00
|
|
|
x_offset = self.control_x_offset
|
|
|
|
|
|
2020-04-15 23:29:43 +02:00
|
|
|
x_offset = self.place_util(self.wl_en_inst, x_offset, row)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2018-11-27 03:00:59 +01:00
|
|
|
self.row_end_inst.append(self.wl_en_inst)
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2018-11-28 02:18:03 +01:00
|
|
|
def route_wlen(self):
|
|
|
|
|
wlen_map = zip(["A"], ["gated_clk_bar"])
|
2020-06-08 20:01:14 +02:00
|
|
|
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.input_bus)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-11-28 02:18:03 +01:00
|
|
|
self.connect_output(self.wl_en_inst, "Z", "wl_en")
|
2018-08-28 01:42:48 +02:00
|
|
|
|
2018-11-27 01:19:18 +01:00
|
|
|
def create_pen_row(self):
|
2019-08-10 17:53:02 +02:00
|
|
|
self.p_en_bar_nand_inst=self.add_inst(name="nand_p_en_bar",
|
|
|
|
|
mod=self.nand2)
|
2019-09-08 08:22:01 +02:00
|
|
|
# We use the rbl_bl_delay here to ensure that the p_en is only asserted when the
|
|
|
|
|
# bitlines have already been discharged. Otherwise, it is a combination loop.
|
2019-08-08 02:14:33 +02:00
|
|
|
self.connect_inst(["gated_clk_buf", "rbl_bl_delay", "p_en_bar_unbuf", "vdd", "gnd"])
|
|
|
|
|
|
2019-08-10 17:53:02 +02:00
|
|
|
self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar",
|
|
|
|
|
mod=self.p_en_bar_driver)
|
2019-08-08 02:14:33 +02:00
|
|
|
self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"])
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-04-15 23:29:43 +02:00
|
|
|
def place_pen_row(self, row):
|
2019-08-10 17:53:02 +02:00
|
|
|
x_offset = self.control_x_offset
|
|
|
|
|
|
2020-04-15 23:29:43 +02:00
|
|
|
x_offset = self.place_util(self.p_en_bar_nand_inst, x_offset, row)
|
|
|
|
|
x_offset = self.place_util(self.p_en_bar_driver_inst, x_offset, row)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2019-08-10 17:53:02 +02:00
|
|
|
self.row_end_inst.append(self.p_en_bar_driver_inst)
|
2018-11-28 02:18:03 +01:00
|
|
|
|
|
|
|
|
def route_pen(self):
|
2019-08-10 17:53:02 +02:00
|
|
|
in_map = zip(["A", "B"], ["gated_clk_buf", "rbl_bl_delay"])
|
2020-06-08 20:01:14 +02:00
|
|
|
self.connect_vertical_bus(in_map, self.p_en_bar_nand_inst, self.input_bus)
|
2019-08-10 17:53:02 +02:00
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
out_pin = self.p_en_bar_nand_inst.get_pin("Z")
|
|
|
|
|
out_pos = out_pin.center()
|
|
|
|
|
in_pin = self.p_en_bar_driver_inst.get_pin("A")
|
|
|
|
|
in_pos = in_pin.center()
|
|
|
|
|
mid1 = vector(in_pos.x, out_pos.y)
|
|
|
|
|
self.add_path(out_pin.layer, [out_pos, mid1, in_pos])
|
|
|
|
|
self.add_via_stack_center(from_layer=out_pin.layer,
|
|
|
|
|
to_layer=in_pin.layer,
|
|
|
|
|
offset=in_pin.center())
|
2019-08-10 17:53:02 +02:00
|
|
|
|
|
|
|
|
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def create_sen_row(self):
|
|
|
|
|
""" Create the sense enable buffer. """
|
2019-09-08 05:04:48 +02:00
|
|
|
if self.port_type=="rw":
|
|
|
|
|
input_name = "we_bar"
|
|
|
|
|
else:
|
2020-02-19 12:06:11 +01:00
|
|
|
input_name = "cs"
|
2019-07-25 20:19:16 +02:00
|
|
|
# GATE FOR S_EN
|
2019-07-27 06:41:27 +02:00
|
|
|
self.s_en_gate_inst = self.add_inst(name="buf_s_en_and",
|
2019-08-11 01:30:02 +02:00
|
|
|
mod=self.sen_and3)
|
2019-09-08 08:22:01 +02:00
|
|
|
# s_en is asserted in the second half of the cycle during a read.
|
|
|
|
|
# we also must wait until the bitline has been discharged enough for proper sensing
|
|
|
|
|
# hence we use rbl_bl_delay as well.
|
2019-09-08 05:04:48 +02:00
|
|
|
self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"])
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-04-15 23:29:43 +02:00
|
|
|
def place_sen_row(self, row):
|
2019-08-10 17:53:02 +02:00
|
|
|
x_offset = self.control_x_offset
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2019-08-10 17:53:02 +02:00
|
|
|
x_offset = self.place_util(self.s_en_gate_inst, x_offset, row)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-07-27 06:41:27 +02:00
|
|
|
self.row_end_inst.append(self.s_en_gate_inst)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-03-15 01:30:41 +01:00
|
|
|
def route_sen(self):
|
2019-07-25 20:19:16 +02:00
|
|
|
|
2019-09-08 05:04:48 +02:00
|
|
|
if self.port_type=="rw":
|
|
|
|
|
input_name = "we_bar"
|
|
|
|
|
else:
|
2020-02-20 08:32:11 +01:00
|
|
|
input_name = "cs"
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-09-08 05:04:48 +02:00
|
|
|
sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name])
|
2020-06-08 20:01:14 +02:00
|
|
|
self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.input_bus)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-07-27 06:41:27 +02:00
|
|
|
self.connect_output(self.s_en_gate_inst, "Z", "s_en")
|
2019-08-07 01:29:07 +02:00
|
|
|
|
2019-09-27 23:18:49 +02:00
|
|
|
def create_rbl_delay_row(self):
|
|
|
|
|
|
|
|
|
|
self.rbl_bl_delay_inv_inst = self.add_inst(name="rbl_bl_delay_inv",
|
|
|
|
|
mod=self.inv)
|
|
|
|
|
self.connect_inst(["rbl_bl_delay", "rbl_bl_delay_bar", "vdd", "gnd"])
|
|
|
|
|
|
2020-04-15 23:29:43 +02:00
|
|
|
def place_rbl_delay_row(self, row):
|
2019-09-27 23:18:49 +02:00
|
|
|
x_offset = self.control_x_offset
|
|
|
|
|
|
|
|
|
|
x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-09-27 23:18:49 +02:00
|
|
|
self.row_end_inst.append(self.rbl_bl_delay_inv_inst)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-09-27 23:18:49 +02:00
|
|
|
def route_rbl_delay(self):
|
|
|
|
|
# Connect from delay line
|
|
|
|
|
# Connect to rail
|
|
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar")
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-09-27 23:18:49 +02:00
|
|
|
rbl_map = zip(["A"], ["rbl_bl_delay"])
|
2020-06-08 20:01:14 +02:00
|
|
|
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.input_bus)
|
2019-09-27 23:18:49 +02:00
|
|
|
|
2019-08-10 17:53:02 +02:00
|
|
|
def create_wen_row(self):
|
|
|
|
|
|
2018-11-28 02:18:03 +01:00
|
|
|
# input: we (or cs) output: w_en
|
|
|
|
|
if self.port_type == "rw":
|
|
|
|
|
input_name = "we"
|
|
|
|
|
else:
|
|
|
|
|
# No we for write-only reports, so use cs
|
|
|
|
|
input_name = "cs"
|
2018-03-15 01:30:41 +01:00
|
|
|
|
2019-07-25 20:19:16 +02:00
|
|
|
# GATE THE W_EN
|
2019-07-27 20:44:35 +02:00
|
|
|
self.w_en_gate_inst = self.add_inst(name="w_en_and",
|
2019-08-08 02:14:33 +02:00
|
|
|
mod=self.wen_and)
|
2019-09-08 08:22:01 +02:00
|
|
|
# Only drive the writes in the second half of the clock cycle during a write operation.
|
2019-09-27 23:18:49 +02:00
|
|
|
self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"])
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-04-15 23:29:43 +02:00
|
|
|
def place_wen_row(self, row):
|
2019-08-10 17:53:02 +02:00
|
|
|
x_offset = self.control_x_offset
|
|
|
|
|
|
|
|
|
|
x_offset = self.place_util(self.w_en_gate_inst, x_offset, row)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-07-27 06:41:27 +02:00
|
|
|
self.row_end_inst.append(self.w_en_gate_inst)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2018-11-28 02:18:03 +01:00
|
|
|
def route_wen(self):
|
|
|
|
|
if self.port_type == "rw":
|
2019-07-27 00:50:10 +02:00
|
|
|
input_name = "we"
|
2018-11-28 02:18:03 +01:00
|
|
|
else:
|
2018-11-28 20:02:24 +01:00
|
|
|
# No we for write-only reports, so use cs
|
2018-11-28 02:18:03 +01:00
|
|
|
input_name = "cs"
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-09-27 23:18:49 +02:00
|
|
|
wen_map = zip(["A", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"])
|
2020-06-08 20:01:14 +02:00
|
|
|
self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus)
|
2018-07-24 19:35:07 +02:00
|
|
|
|
2019-07-27 06:41:27 +02:00
|
|
|
self.connect_output(self.w_en_gate_inst, "Z", "w_en")
|