add most functions needed for delay control logic, fix multi-delay pin order issue

This commit is contained in:
samuelkcrow 2022-06-01 09:18:27 -07:00
parent 45239ca2a9
commit 7d4b718344
3 changed files with 75 additions and 264 deletions

View File

@ -77,7 +77,7 @@ class control_logic_delay(design.design):
def add_pins(self):
""" Add the pins to the control logic module. """
self.add_pin_list(self.input_list + ["clk"] + self.rbl_list, "INPUT")
self.add_pin_list(self.input_list + ["clk"], "INPUT")
self.add_pin_list(self.output_list, "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
@ -250,10 +250,8 @@ class control_logic_delay(design.design):
# List of input control signals
if self.port_type == "rw":
self.input_list = ["csb", "web"]
self.rbl_list = ["rbl_bl"]
else:
self.input_list = ["csb"]
self.rbl_list = ["rbl_bl"]
if self.port_type == "rw":
self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"]
@ -262,11 +260,9 @@ class control_logic_delay(design.design):
# list of output control signals (for making a vertical bus)
if self.port_type == "rw":
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "we_bar", "clk_buf", "cs"]
elif self.port_type == "r":
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"]
self.internal_bus_list = ["glitch1_bar", "glitch2_bar", "glitch3_bar", "pre_sen", "delay1", "delay2", "delay3", "delay4", "gated_clk_bar", "gated_clk_buf", "we", "we_bar", "clk_buf", "cs"]
else:
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
self.internal_bus_list = ["glitch1_bar", "glitch2_bar", "glitch3_bar", "pre_sen", "delay1", "delay2", "delay3", "delay4", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
# leave space for the bus plus one extra space
self.internal_bus_width = (len(self.internal_bus_list) + 1) * self.m2_pitch
@ -302,7 +298,7 @@ class control_logic_delay(design.design):
self.create_gated_clk_buf_row()
self.create_delay()
self.create_glitches()
self.create_glitch3_additional_invs()
self.create_pre_sen_invs()
self.create_wlen_row()
if (self.port_type == "rw") or (self.port_type == "w"):
self.create_wen_row()
@ -338,13 +334,18 @@ class control_logic_delay(design.design):
row += 1
self.place_pen_row(row)
row += 1
if (self.port_type == "rw") or (self.port_type == "w"):
self.place_rbl_delay_row(row)
row += 1
self.place_wlen_row(row)
row += 1
self.place_glitch1_row(row)
row += 1
self.place_glitch2_row(row)
row += 1
self.place_glitch3_row(row)
row += 1
self.place_pre_sen_row(row)
row += 1
control_center_y = self.wl_en_inst.uy() + self.m3_pitch
control_center_y = self.pre_sen_inv_inst.uy() + self.m3_pitch
# Delay chain always gets placed at row 4
self.place_delay(4)
@ -357,7 +358,8 @@ class control_logic_delay(design.design):
self.height = height + 2 * self.m1_pitch
# Max of modules or logic rows
self.width = max([inst.rx() for inst in self.row_end_inst])
if (self.port_type == "rw") or (self.port_type == "r"):
if (self.port_type == "rw") or (self.port_type == "r"):
# TODO: why not w ports here?
self.width = max(self.delay_inst.rx(), self.width)
self.width += self.m2_pitch
@ -367,7 +369,6 @@ class control_logic_delay(design.design):
self.route_dffs()
self.route_wlen()
if (self.port_type == "rw") or (self.port_type == "w"):
self.route_rbl_delay()
self.route_wen()
if (self.port_type == "rw") or (self.port_type == "r"):
self.route_sen()
@ -382,25 +383,24 @@ class control_logic_delay(design.design):
""" Create the delay chain """
self.delay_inst=self.add_inst(name="multi_delay_chain",
mod=self.multi_delay_chain)
self.connect_inst(["gated_clk_buf", "out1", "out6", "out7", "out14", "vdd", "gnd"])
self.connect_inst(["gated_clk_buf", "delay1", "delay2", "delay3", "delay4", "vdd", "gnd"])
def place_delay(self, row):
""" Place the replica bitline """
""" Place the delay chain """
debug.check(row % 2 == 0, "Must place delay chain at even row for supply alignment.")
# It is flipped on X axis
y_off = row * self.and2.height + self.delay_chain.height
y_off = row * self.and2.height + self.multi_delay_chain.height
# Add the RBL above the rows
# Add to the right of the control rows and routing channel
offset = vector(0, y_off)
self.delay_inst.place(offset, mirror="MX")
def route_delay(self):
def route_delay(self): # TODO: this
out_pos = self.delay_inst.get_pin("out").center()
# Connect to the rail level with the vdd rail
# Use gated clock since it is in every type of control logic # TODO: git blame whoever wrote this, do they mean every type made by others or is this foreshadowing our future addition of delay/async logic. If the former, seems a bit odd to include something basically explanatory. If the latter, seems odd I'm hearing about it now for the first time, because it begs the question if there are other steps that have been taken to lay groundwork for control logic varieties.
# 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
in_pos = vector(self.input_bus["rbl_bl_delay"].cx(), vdd_ypos)
mid1 = vector(out_pos.x, in_pos.y)
@ -416,19 +416,19 @@ class control_logic_delay(design.design):
def create_glitches(self):
self.glitch1_inv_inst = self.add_inst(name="inv_glitch1",
mod=self.inv)
self.connect_inst(["out6", "g1_end", "vdd", "gnd"])
self.connect_inst(["delay2", "g1_end", "vdd", "gnd"])
self.glitch2_inv_inst = self.add_inst(name="inv_glitch2",
mod=self.inv)
self.connect_inst(["out7", "g2_end", "vdd", "gnd"])
self.connect_inst(["delay3", "g2_end", "vdd", "gnd"])
self.glitch3_inv_inst = self.add_inst(name="inv_glitch3",
mod=self.inv)
self.connect_inst(["out14", "g3_end", "vdd", "gnd"])
self.connect_inst(["delay4", "g3_end", "vdd", "gnd"])
self.glitch1_nand_inst = self.add_inst(name="nand2_glitch1",
mod=self.nand2)
self.connect_inst(["out1", "g1_end", "glitch1_bar", "vdd", "gnd"])
self.connect_inst(["delay1", "g1_end", "glitch1_bar", "vdd", "gnd"])
self.glitch2_nand_inst = self.add_inst(name="nand2_glitch2",
mod=self.nand2)
@ -436,7 +436,38 @@ class control_logic_delay(design.design):
self.glitch3_nand_inst = self.add_inst(name="nand2_glitch3",
mod=self.nand2)
self.connect_inst(["out6", "g3_end", "glitch3_bar", "vdd", "gnd"])
self.connect_inst(["delay2", "g3_end", "glitch3_bar", "vdd", "gnd"])
def place_glitch1_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.glitch1_inv_inst, x_offset, row)
x_offset = self.place_util(self.glitch1_nand_inst, x_offset, row)
self.row_end_inst.append(self.glitch1_nand_inst)
def place_glitch2_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.glitch2_inv_inst, x_offset, row)
x_offset = self.place_util(self.glitch2_nand_inst, x_offset, row)
self.row_end_inst.append(self.glitch3_nand_inst)
def place_glitch3_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.glitch3_inv_inst, x_offset, row)
x_offset = self.place_util(self.glitch3_nand_inst, x_offset, row)
self.row_end_inst.append(self.glitch3_nand_inst)
def route_glitches(self): # TODO: this
glitch1_map = zip(["A"], ["gated_clk_bar"])
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.input_bus)
self.connect_output(self.wl_en_inst, "Z", "wl_en")
def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """
@ -444,7 +475,7 @@ class control_logic_delay(design.design):
mod=self.clk_buf_driver)
self.connect_inst(["clk", "clk_buf", "vdd", "gnd"])
def create_cs_buf_row(self):
def create_cs_buf_row(self): # TODO: place and route
""" Create the multistage and gated chip select buffer """
self.cs_buf_inst = self.add_inst(name="csbuf",
mod=self.clk_buf_driver)
@ -570,10 +601,6 @@ class control_logic_delay(design.design):
self.connect_output(self.wl_en_inst, "Z", "wl_en")
def create_pen_row(self):
# FIXME: comment below
# 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.
self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar",
mod=self.p_en_bar_driver)
self.connect_inst(["glitch1_bar", "p_en_bar", "vdd", "gnd"])
@ -602,15 +629,23 @@ class control_logic_delay(design.design):
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
def create_glitch3_additional_invs(self):
def create_pre_sen_invs(self):
""" A pair of inverters to create additional signals from and buffer glitch 3"""
self.glitch3_buf_inv_inst = self.add_inst(name="inv_glitch3_buf",
mod=self.inv)
self.connect_inst(["glitch3_bar", "glitch3_buf", "vdd", "gnd"])
self.pre_sen_inv = self.add_inst(name="inv_pre_sen",
self.pre_sen_inv_inst = self.add_inst(name="inv_pre_sen",
mod=self.inv)
self.connect_inst(["gltich3_buf", "pre_sen", "vdd", "gnd"])
self.connect_inst(["glitch3_buf", "pre_sen", "vdd", "gnd"])
def place_pre_sen_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.glitch3_buf_inv_inst, x_offset, row)
x_offset = self.place_util(self.pre_sen_inv_inst, x_offset, row)
self.row_end_inst.append(self.pre_sen_inv_inst)
def create_sen_row(self):
""" Create the sense enable buffer. """
@ -621,10 +656,6 @@ class control_logic_delay(design.design):
# GATE FOR S_EN
self.s_en_gate_inst = self.add_inst(name="and_s_en",
mod=self.sen_and3)
# FIXME: comment below
# 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.
self.connect_inst(["pre_sen", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"])
def place_sen_row(self, row):
@ -693,7 +724,7 @@ class control_logic_delay(design.design):
if self.port_type == "rw":
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
elif self.port_type == "r":
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
dff_out_map = zip(["dout_bar_0"], ["cs"])
else:
dff_out_map = zip(["dout_bar_0"], ["cs"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.input_bus, self.m2_stack[::-1])

View File

@ -1,220 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 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
import design
from vector import vector
from globals import OPTS
from sram_factory import factory
class delay_chain(design.design):
"""
Generate a delay chain with the given number of stages, fanout, and output pins.
Fanout list contains the electrical effort (fanout) of each stage.
Usually, this will be constant, but it could have varied fanout.
Pinout list contains the inverter stages which have an output pin attached.
Supplying an empty pinout list will result in an output on the last stage.
"""
def __init__(self, name, fanout_list, pinout_list):
"""init function"""
super().__init__(name)
debug.info(1, "creating delay chain {0}".format(str(fanout_list)))
self.add_comment("fanouts: {0}".format(str(fanout_list)))
# Two fanouts are needed so that we can route the vdd/gnd connections
for f in fanout_list:
debug.check(f>=2, "Must have >=2 fanouts for each stage.")
# number of inverters including any fanout loads.
self.fanout_list = fanout_list
self.rows = len(self.fanout_list)
# defaults to signle output at end of delay chain
if len(self.pinout_list) == 0:
self.pinout_list = [self.rows] # TODO: check for off-by-one here
else:
self.pinout_list = pinout_list
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_inverters()
def create_layout(self):
# Each stage is a a row
self.height = self.rows * self.inv.height
# The width is determined by the largest fanout plus the driver
self.width = (max(self.fanout_list) + 1) * self.inv.width
self.place_inverters()
self.route_inverters()
self.route_supplies()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
""" Add the pins of the delay chain"""
self.add_pin("in", "INPUT")
for pin_stage in self.pinout_list:
self.add_pin("out{}".format(pin_stage), "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
self.dff = factory.create(module_type="dff_buf")
dff_height = self.dff.height
self.inv = factory.create(module_type="pinv",
height=dff_height)
def create_inverters(self):
""" Create the inverters and connect them based on the stage list """
self.driver_inst_list = []
self.load_inst_map = {}
for stage_num, fanout_size in zip(range(self.rows), self.fanout_list):
# Add the inverter
cur_driver=self.add_inst(name="dinv{}".format(stage_num),
mod=self.inv)
# keep track of the inverter instances so we can use them to get the pins
self.driver_inst_list.append(cur_driver)
# Hook up the driver
stageout_name = "out{}".format(stage_num + 1) # TODO: check for off-by-one here
if stage_num == 0:
stagein_name = "in"
else:
stagein_name = "out{}".format(stage_num)
self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"])
# Now add the dummy loads to the right
self.load_inst_map[cur_driver]=[]
for i in range(fanout_size):
cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num, i),
mod=self.inv)
# Fanout stage is always driven by driver and output is disconnected
disconnect_name = "n_{0}_{1}".format(stage_num, i)
self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"])
# Keep track of all the loads to connect their inputs as a load
self.load_inst_map[cur_driver].append(cur_load)
def place_inverters(self):
""" Place the inverters and connect them based on the stage list """
for stage_num, fanout_size in zip(range(self.rows), self.fanout_list):
if stage_num % 2:
inv_mirror = "MX"
inv_offset = vector(0, (stage_num + 1) * self.inv.height)
else:
inv_mirror = "R0"
inv_offset = vector(0, stage_num * self.inv.height)
# Add the inverter
cur_driver=self.driver_inst_list[stage_num]
cur_driver.place(offset=inv_offset,
mirror=inv_mirror)
# Now add the dummy loads to the right
load_list = self.load_inst_map[cur_driver]
for i in range(fanout_size):
inv_offset += vector(self.inv.width, 0)
load_list[i].place(offset=inv_offset,
mirror=inv_mirror)
def add_route(self, pin1, pin2):
""" This guarantees that we route from the top to bottom row correctly. """
pin1_pos = pin1.center()
pin2_pos = pin2.center()
if pin1_pos.y == pin2_pos.y:
self.add_path("m2", [pin1_pos, pin2_pos])
else:
mid_point = vector(pin2_pos.x, 0.5 * (pin1_pos.y + pin2_pos.y))
# Written this way to guarantee it goes right first if we are switching rows
self.add_path("m2", [pin1_pos, vector(pin1_pos.x, mid_point.y), mid_point, vector(mid_point.x, pin2_pos.y), pin2_pos])
def route_inverters(self):
""" Add metal routing for each of the fanout stages """
for i in range(len(self.driver_inst_list)):
inv = self.driver_inst_list[i]
for load in self.load_inst_map[inv]:
# Drop a via on each A pin
a_pin = load.get_pin("A")
self.add_via_stack_center(from_layer=a_pin.layer,
to_layer="m3",
offset=a_pin.center())
# Route an M3 horizontal wire to the furthest
z_pin = inv.get_pin("Z")
a_pin = inv.get_pin("A")
a_max = self.load_inst_map[inv][-1].get_pin("A")
self.add_via_stack_center(from_layer=a_pin.layer,
to_layer="m2",
offset=a_pin.center())
self.add_via_stack_center(from_layer=z_pin.layer,
to_layer="m3",
offset=z_pin.center())
self.add_path("m3", [z_pin.center(), a_max.center()])
# Route Z to the A of the next stage
if i + 1 < len(self.driver_inst_list):
z_pin = inv.get_pin("Z")
next_inv = self.driver_inst_list[i + 1]
next_a_pin = next_inv.get_pin("A")
y_mid = (z_pin.cy() + next_a_pin.cy()) / 2
mid1_point = vector(z_pin.cx(), y_mid)
mid2_point = vector(next_a_pin.cx(), y_mid)
self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
def route_supplies(self):
# Add power and ground to all the cells except:
# the fanout driver, the right-most load
# The routing to connect the loads is over the first and last cells
# We have an even number of drivers and must only do every other
# supply rail
for inst in self.driver_inst_list:
load_list = self.load_inst_map[inst]
for pin_name in ["vdd", "gnd"]:
pin = load_list[0].get_pin(pin_name)
self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0))
pin = load_list[-2].get_pin(pin_name)
self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0))
def add_layout_pins(self):
# input is A pin of first inverter
# It gets routed to the left a bit to prevent pin access errors
# due to the output pin when going up to M3
a_pin = self.driver_inst_list[0].get_pin("A")
mid_loc = vector(a_pin.cx() - self.m3_pitch, a_pin.cy())
self.add_via_stack_center(from_layer=a_pin.layer,
to_layer="m2",
offset=mid_loc)
self.add_path(a_pin.layer, [a_pin.center(), mid_loc])
self.add_layout_pin_rect_center(text="in",
layer="m2",
offset=mid_loc)
# output is A pin of last load/fanout inverter
last_driver_inst = self.driver_inst_list[-1]
a_pin = self.load_inst_map[last_driver_inst][-1].get_pin("A")
self.add_via_stack_center(from_layer=a_pin.layer,
to_layer="m1",
offset=a_pin.center())
self.add_layout_pin_rect_center(text="out",
layer="m1",
offset=a_pin.center())

View File

@ -39,14 +39,14 @@ class multi_delay_chain(design.design):
if not pinout_list:
self.pinout_list = [self.rows] # TODO: check for off-by-one here
else:
# Set() to sort in ascending order and remove duplicates
self.pinout_list = list(set(pinout_list))
self.pinout_list = pinout_list
#would like to sort and check pinout list for valid format but don't have time now
# Check pinout bounds
debug.check(self.pinout_list[-1] <= self.rows,
"Ouput pin cannot exceed delay chain length.")
debug.check(self.pinout_list[0] > 0,
"Delay chain output pin numbers must be positive")
# debug.check(self.pinout_list[-1] <= self.rows,
# "Ouput pin cannot exceed delay chain length.")
# debug.check(self.pinout_list[0] > 0,
# "Delay chain output pin numbers must be positive")
self.create_netlist()
if not OPTS.netlist_only: