Merge branch 'dev' into multibank

This commit is contained in:
Bugra Onal
2022-08-12 21:45:26 -07:00
87 changed files with 1314 additions and 1306 deletions
+1 -1
View File
@@ -31,5 +31,5 @@ class bitcell_1port(bitcell_base):
def is_non_inverting(self):
"""Return input to output polarity for module"""
return False
+1 -1
View File
@@ -102,5 +102,5 @@ class bitcell_2port(bitcell_base):
def is_non_inverting(self):
"""Return input to output polarity for module"""
return False
+27 -27
View File
@@ -169,7 +169,7 @@ class bitcell_base(design):
"""
return
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl"]
@@ -207,39 +207,39 @@ class bitcell_base(design):
is_nchannel = True
stack = 2 # for access and inv tx
is_cell = False
return self.tr_r_on(drc["minwidth_tx"], is_nchannel, stack, is_cell)
return self.tr_r_on(drc["minwidth_tx"], is_nchannel, stack, is_cell)
def get_input_capacitance(self):
"""Input cap of input, passes width of gates to gate cap function"""
# Input cap of both access TX connected to the wordline
return self.gate_c(2*parameter["6T_access_size"])
return self.gate_c(2*parameter["6T_access_size"])
def get_intrinsic_capacitance(self):
"""Get the drain capacitances of the TXs in the gate."""
stack = 1
mult = 1
# FIXME: Need to define TX sizes of bitcell storage node. Using
# FIXME: Need to define TX sizes of bitcell storage node. Using
# min_width as a temp value
# Add the inverter drain Cap and the bitline TX drain Cap
nmos_drain_c = self.drain_c_(drc["minwidth_tx"]*mult,
stack,
mult)
pmos_drain_c = self.drain_c_(drc["minwidth_tx"]*mult,
stack,
mult)
bl_nmos_drain_c = self.drain_c_(parameter["6T_access_size"],
stack,
mult)
return nmos_drain_c + pmos_drain_c + bl_nmos_drain_c
# Add the inverter drain Cap and the bitline TX drain Cap
nmos_drain_c = self.drain_c_(drc["minwidth_tx"]*mult,
stack,
mult)
pmos_drain_c = self.drain_c_(drc["minwidth_tx"]*mult,
stack,
mult)
bl_nmos_drain_c = self.drain_c_(parameter["6T_access_size"],
stack,
mult)
return nmos_drain_c + pmos_drain_c + bl_nmos_drain_c
def module_wire_c(self):
"""Capacitance of bitline"""
# FIXME: entire bitline cap is calculated here because of the current
# graph implementation so array dims are all re-calculated here. May
# be incorrect if dim calculations change
# be incorrect if dim calculations change
cells_in_col = OPTS.num_words/OPTS.words_per_row
return cells_in_col*self.height*spice["wire_c_per_um"]
@@ -247,15 +247,15 @@ class bitcell_base(design):
"""Resistance of bitline"""
# FIXME: entire bitline r is calculated here because of the current
# graph implementation so array dims are all re-calculated. May
# be incorrect if dim calculations change
# be incorrect if dim calculations change
cells_in_col = OPTS.num_words/OPTS.words_per_row
return cells_in_col*self.height*spice["wire_r_per_um"]
def cacti_rc_delay(self, inputramptime, tf, vs1, vs2, rise, extra_param_dict):
return cells_in_col*self.height*spice["wire_r_per_um"]
def cacti_rc_delay(self, inputramptime, tf, vs1, vs2, rise, extra_param_dict):
""" Special RC delay function used by CACTI for bitline delay
"""
import math
vdd = extra_param_dict['vdd']
vdd = extra_param_dict['vdd']
m = vdd / inputramptime #v_wl = vdd for OpenRAM
# vdd == V_b_pre in OpenRAM. Bitline swing is assumed 10% of vdd
tstep = tf * math.log(vdd/(vdd - 0.1*vdd))
@@ -264,4 +264,4 @@ class bitcell_base(design):
else:
delay = math.sqrt(2*tstep*(vdd-spice["nom_threshold"])/m)
return delay
return delay
+2 -2
View File
@@ -128,7 +128,7 @@ class bitcell_base_array(design):
if len(self.all_ports) > 1:
temp.extend(self.get_rbl_wordline_names(1))
return temp
def add_bitline_pins(self):
bitline_names = self.cell.get_all_bitline_names()
for col in range(self.column_size):
@@ -165,7 +165,7 @@ class bitcell_base_array(design):
""" Add the layout pins """
self.add_bitline_pins()
self.add_wl_pins()
def _adjust_x_offset(self, xoffset, col, col_offset):
tempx = xoffset
dir_y = False
+1 -1
View File
@@ -12,7 +12,7 @@ from .bitcell_base import bitcell_base
class col_cap_bitcell_1port(bitcell_base):
"""
Column end cap cell.
Column end cap cell.
"""
def __init__(self, name="col_cap_bitcell_1port"):
+1 -1
View File
@@ -12,7 +12,7 @@ from .bitcell_base import bitcell_base
class col_cap_bitcell_2port(bitcell_base):
"""
Column end cap cell.
Column end cap cell.
"""
def __init__(self, name="col_cap_bitcell_2port"):
+1 -1
View File
@@ -175,7 +175,7 @@ class column_mux_array(design):
# Add the column x offset to find the right select bit
gate_offset = self.mux_inst[col].get_pin("sel").bc()
# use the y offset from the sel pin and the x offset from the gate
offset = vector(gate_offset.x,
self.get_pin("sel_{}".format(sel_index)).cy())
+6 -491
View File
@@ -5,74 +5,22 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from base import design
import debug
from sram_factory import factory
import math
from base import vector
from globals import OPTS
from base import logical_effort
from .control_logic_base import control_logic_base
class control_logic(design):
class control_logic(control_logic_base):
"""
Dynamically generated Control logic for the total SRAM circuit.
"""
def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""):
""" Constructor """
name = "control_logic_" + port_type
super().__init__(name)
debug.info(1, "Creating {}".format(name))
self.add_comment("num_rows: {0}".format(num_rows))
self.add_comment("words_per_row: {0}".format(words_per_row))
self.add_comment("word_size {0}".format(word_size))
self.sram=sram
self.num_rows = num_rows
self.words_per_row = words_per_row
self.word_size = word_size
self.port_type = port_type
if not spare_columns:
self.num_spare_cols = 0
else:
self.num_spare_cols = spare_columns
self.num_cols = word_size * words_per_row + self.num_spare_cols
self.num_words = num_rows * words_per_row
self.enable_delay_chain_resizing = False
self.inv_parasitic_delay = logical_effort.pinv
# Determines how much larger the sen delay should be. Accounts for possible error in model.
# FIXME: This should be made a parameter
self.wl_timing_tolerance = 1
self.wl_stage_efforts = None
self.sen_stage_efforts = None
if self.port_type == "rw":
self.num_control_signals = 2
else:
self.num_control_signals = 1
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.setup_signal_busses()
self.add_pins()
self.add_modules()
self.create_instances()
def create_layout(self):
""" Create layout and route between modules """
self.place_instances()
self.route_all()
# self.add_lvs_correspondence_points()
self.add_boundary()
self.DRC_LVS()
super().__init__(num_rows, words_per_row, word_size, spare_columns, sram, port_type, name)
def add_pins(self):
""" Add the pins to the control logic module. """
@@ -151,93 +99,6 @@ class control_logic(design):
self.delay_chain=factory.create(module_type="delay_chain",
fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
from math import ceil
previous_delay_chain_delay = (previous_fanout + 1 + self.inv_parasitic_delay) * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
# This can be anything >=2
delay_fanout = 3
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay = self.wl_delay * self.wl_timing_tolerance - (self.sen_delay - previous_delay_chain_delay)
debug.check(required_delay > 0, "Cannot size delay chain to have negative delay")
delay_per_stage = delay_fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
# force an even number of stages.
if delay_stages % 2 == 1:
delay_stages += 1
# Fanout can be varied as well but is a little more complicated but potentially optimal.
debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
return (delay_stages, delay_fanout)
def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay
previous_delay_chain_delay = previous_delay_per_stage * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
fanout_rise = fanout_fall = 2 # This can be anything >=2
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay_fall = self.wl_delay_fall * self.wl_timing_tolerance - \
(self.sen_delay_fall - previous_delay_chain_delay / 2)
required_delay_rise = self.wl_delay_rise * self.wl_timing_tolerance - \
(self.sen_delay_rise - previous_delay_chain_delay / 2)
debug.info(2,
"Required delays from chain: fall={}, rise={}".format(required_delay_fall,
required_delay_rise))
# If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
WARNING_FANOUT_DIFF = 5
stages_close = False
# The stages need to be equal (or at least a even number of stages with matching rise/fall delays)
while True:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,
fanout_fall)
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,
fanout_rise)
debug.info(1,
"Fall stages={}, rise stages={}".format(stages_fall,
stages_rise))
if abs(stages_fall - stages_rise) == 1 and not stages_close:
stages_close = True
safe_fanout_rise = fanout_rise
safe_fanout_fall = fanout_fall
if stages_fall == stages_rise:
break
elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise):
debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.")
fanout_rise = safe_fanout_rise
fanout_fall = safe_fanout_fall
break
# There should also be a condition to make sure the fanout does not get too large.
# Otherwise, increase the fanout of delay with the most stages, calculate new stages
elif stages_fall>stages_rise:
fanout_fall+=1
else:
fanout_rise+=1
total_stages = max(stages_fall, stages_rise) * 2
debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall))
# Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage.
stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)]
return stage_list
def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
from math import ceil
# Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay
# 3 is the minimum delay per stage (with pinv=0).
if required_delay <= 3 + self.inv_parasitic_delay:
return 1
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
return delay_stages
def setup_signal_busses(self):
""" Setup bus names, determine the size of the busses etc """
@@ -277,17 +138,6 @@ class control_logic(design):
self.supply_list = ["vdd", "gnd"]
def route_rails(self):
""" Add the input signal inverted tracks """
height = self.control_logic_center.y - self.m2_pitch
# DFF spacing plus the power routing
offset = vector(self.ctrl_dff_array.width + self.m4_pitch, 0)
self.input_bus = self.create_vertical_bus("m2",
offset,
self.internal_bus_list,
height)
def create_instances(self):
""" Create all the instances """
self.create_dffs()
@@ -303,21 +153,8 @@ class control_logic(design):
self.create_delay()
self.create_pen_row()
def place_instances(self):
""" Place all the instances """
# Keep track of all right-most instances to determine row boundary
# and add the vdd/gnd pins
self.row_end_inst = []
# Add the control flops on the left of the bus
self.place_dffs()
# All of the control logic is placed to the right of the DFFs and bus
# as well as the power supply stripe
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + self.m4_pitch
def place_logic_rows(self):
row = 0
# Add the logic on the right of the bus
self.place_clk_buf_row(row)
row += 1
self.place_gated_clk_bar_row(row)
@@ -336,24 +173,8 @@ class control_logic(design):
self.place_rbl_delay_row(row)
row += 1
self.place_wlen_row(row)
row += 1
control_center_y = self.wl_en_inst.uy() + self.m3_pitch
# Delay chain always gets placed at row 4
self.place_delay(4)
height = self.delay_inst.uy()
# This offset is used for placement of the control logic in the SRAM level.
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y)
# Extra pitch on top and right
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"):
self.width = max(self.delay_inst.rx(), self.width)
self.width += self.m2_pitch
self.control_center_y = self.wl_en_inst.uy() + self.m3_pitch
def route_all(self):
""" Routing between modules """
@@ -373,24 +194,12 @@ class control_logic(design):
self.route_supplies()
def create_delay(self):
""" Create the replica bitline """
""" Create the delay chain """
self.delay_inst=self.add_inst(name="delay_chain",
mod=self.delay_chain)
# rbl_bl_delay is asserted (1) when the bitline has been discharged
self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"])
def place_delay(self, row):
""" Place the replica bitline """
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
# 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):
out_pos = self.delay_inst.get_pin("out").center()
@@ -406,109 +215,6 @@ class control_logic(design):
# Input from RBL goes to the delay line for futher delay
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """
self.clk_buf_inst = self.add_inst(name="clkbuf",
mod=self.clk_buf_driver)
self.connect_inst(["clk", "clk_buf", "vdd", "gnd"])
def place_clk_buf_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_buf_inst, x_offset, row)
self.row_end_inst.append(self.clk_buf_inst)
def route_clk_buf(self):
clk_pin = self.clk_buf_inst.get_pin("A")
clk_pos = clk_pin.center()
self.add_layout_pin_rect_center(text="clk",
layer="m2",
offset=clk_pos)
self.add_via_stack_center(from_layer=clk_pin.layer,
to_layer="m2",
offset=clk_pos)
self.route_output_to_bus_jogged(self.clk_buf_inst,
"clk_buf")
self.connect_output(self.clk_buf_inst, "Z", "clk_buf")
def create_gated_clk_bar_row(self):
self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
mod=self.inv)
self.connect_inst(["clk_buf", "clk_bar", "vdd", "gnd"])
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
mod=self.and2)
self.connect_inst(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
def place_gated_clk_bar_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_bar_inst, x_offset, row)
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
self.row_end_inst.append(self.gated_clk_bar_inst)
def route_gated_clk_bar(self):
clkbuf_map = zip(["A"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.input_bus)
out_pin = self.clk_bar_inst.get_pin("Z")
out_pos = out_pin.center()
in_pin = self.gated_clk_bar_inst.get_pin("A")
in_pos = in_pin.center()
self.add_zjog(out_pin.layer, out_pos, in_pos)
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer=in_pin.layer,
offset=in_pos)
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["B"], ["cs"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst,
self.input_bus,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
b_pin = self.gated_clk_bar_inst.get_pin("B")
self.add_via_stack_center(from_layer=b_pin.layer,
to_layer="m3",
offset=b_pin.center())
# This is the second gate over, so it needs to be on M3
self.route_output_to_bus_jogged(self.gated_clk_bar_inst,
"gated_clk_bar")
def create_gated_clk_buf_row(self):
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
mod=self.and2)
self.connect_inst(["clk_buf", "cs", "gated_clk_buf", "vdd", "gnd"])
def place_gated_clk_buf_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
self.row_end_inst.append(self.gated_clk_buf_inst)
def route_gated_clk_buf(self):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_buf_inst,
self.input_bus)
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_buf_inst,
self.input_bus,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
z_pin = self.gated_clk_buf_inst.get_pin("Z")
self.add_via_stack_center(from_layer=z_pin.layer,
to_layer="m2",
offset=z_pin.center())
def create_wlen_row(self):
# input pre_p_en, output: wl_en
self.wl_en_inst=self.add_inst(name="buf_wl_en",
@@ -651,194 +357,3 @@ class control_logic(design):
self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus)
self.connect_output(self.w_en_gate_inst, "Z", "w_en")
def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
inst_pins = self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list
self.connect_inst(inst_pins)
def place_dffs(self):
self.ctrl_dff_inst.place(vector(0, 0))
def route_dffs(self):
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"])
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])
# Connect the clock rail to the other clock rail
# by routing in the supply rail track to avoid channel conflicts
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
mid_pos = vector(in_pos.x, self.gated_clk_buf_inst.get_pin("vdd").cy() - self.m1_pitch)
rail_pos = vector(self.input_bus["clk_buf"].cx(), mid_pos.y)
self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb")
if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
def get_offset(self, row):
""" Compute the y-offset and mirroring """
y_off = row * self.and2.height
if row % 2:
y_off += self.and2.height
mirror="MX"
else:
mirror="R0"
return (y_off, mirror)
def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name)
out_pos = out_pin.center()
right_pos = out_pos + vector(self.width - out_pin.cx(), 0)
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2",
offset=out_pos)
self.add_layout_pin_segment_center(text=out_name,
layer="m2",
start=out_pos,
end=right_pos)
def route_supplies(self):
""" Add vdd and gnd to the instance cells """
pin_layer = self.dff.get_pin("vdd").layer
supply_layer = self.supply_stack[2]
# FIXME: We should be able to replace this with route_vertical_pins instead
# but we may have to make the logic gates a separate module so that they
# have row pins of the same width
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
min_row_x_loc = self.control_x_offset
vdd_pin_locs = []
gnd_pin_locs = []
last_via = None
for inst in self.row_end_inst:
pins = inst.get_pins("vdd")
for pin in pins:
if pin.layer == pin_layer:
row_loc = pin.rc()
pin_loc = vector(max_row_x_loc, pin.rc().y)
vdd_pin_locs.append(pin_loc)
last_via = self.add_via_stack_center(from_layer=pin_layer,
to_layer=supply_layer,
offset=pin_loc,
min_area=True)
self.add_path(pin_layer, [row_loc, pin_loc])
pins = inst.get_pins("gnd")
for pin in pins:
if pin.layer == pin_layer:
row_loc = pin.rc()
pin_loc = vector(min_row_x_loc, pin.rc().y)
gnd_pin_locs.append(pin_loc)
last_via = self.add_via_stack_center(from_layer=pin_layer,
to_layer=supply_layer,
offset=pin_loc,
min_area=True)
self.add_path(pin_layer, [row_loc, pin_loc])
if last_via:
via_height=last_via.mod.second_layer_height
via_width=last_via.mod.second_layer_width
else:
via_height=None
via_width=0
min_y = min([x.y for x in vdd_pin_locs])
max_y = max([x.y for x in vdd_pin_locs])
bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height)
top_pos = vector(max_row_x_loc, max_y + 0.5 * via_height)
self.add_layout_pin_segment_center(text="vdd",
layer=supply_layer,
start=bot_pos,
end=top_pos,
width=via_width)
min_y = min([x.y for x in gnd_pin_locs])
max_y = max([x.y for x in gnd_pin_locs])
bot_pos = vector(min_row_x_loc, min_y - 0.5 * via_height)
top_pos = vector(min_row_x_loc, max_y + 0.5 * via_height)
self.add_layout_pin_segment_center(text="gnd",
layer=supply_layer,
start=bot_pos,
end=top_pos,
width=via_width)
self.copy_layout_pin(self.delay_inst, "gnd")
self.copy_layout_pin(self.delay_inst, "vdd")
self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
def add_lvs_correspondence_points(self):
""" 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.
"""
# pin=self.clk_inv1.get_pin("Z")
# self.add_label_pin(text="clk1_bar",
# layer="m1",
# offset=pin.ll(),
# height=pin.height(),
# width=pin.width())
# pin=self.clk_inv2.get_pin("Z")
# self.add_label_pin(text="clk2",
# layer="m1",
# offset=pin.ll(),
# height=pin.height(),
# width=pin.width())
pin=self.delay_inst.get_pin("out")
self.add_label_pin(text="out",
layer=pin.layer,
offset=pin.ll(),
height=pin.height(),
width=pin.width())
def graph_exclude_dffs(self):
"""Exclude dffs from graph as they do not represent critical path"""
self.graph_inst_exclude.add(self.ctrl_dff_inst)
if self.port_type=="rw" or self.port_type=="w":
self.graph_inst_exclude.add(self.w_en_gate_inst)
def place_util(self, inst, x_offset, row):
""" Utility to place a row and compute the next offset """
(y_offset, mirror) = self.get_offset(row)
offset = vector(x_offset, y_offset)
inst.place(offset, mirror)
return x_offset + inst.width
def route_output_to_bus_jogged(self, inst, name):
# Connect this at the bottom of the buffer
out_pin = inst.get_pin("Z")
out_pos = out_pin.center()
mid1 = vector(out_pos.x, out_pos.y - 0.3 * inst.mod.height)
mid2 = vector(self.input_bus[name].cx(), mid1.y)
bus_pos = self.input_bus[name].center()
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2",
offset=out_pos)
def get_left_pins(self, name):
"""
Return the left side supply pins to connect to a vertical stripe.
"""
return(self.cntrl_dff_inst.get_pins(name) + self.delay_inst.get_pins(name))
+508
View File
@@ -0,0 +1,508 @@
# 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.
#
from base import design
import debug
from sram_factory import factory
import math
from base import vector
from globals import OPTS
from base import logical_effort
class control_logic_base(design):
"""
Generic base class for SRAM control logic.
"""
def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""):
""" Constructor """
name = "control_logic_" + port_type
super().__init__(name)
debug.info(1, "Creating {}".format(name))
self.add_comment("num_rows: {0}".format(num_rows))
self.add_comment("words_per_row: {0}".format(words_per_row))
self.add_comment("word_size {0}".format(word_size))
self.sram=sram
self.num_rows = num_rows
self.words_per_row = words_per_row
self.word_size = word_size
self.port_type = port_type
if not spare_columns:
self.num_spare_cols = 0
else:
self.num_spare_cols = spare_columns
self.num_cols = word_size * words_per_row + self.num_spare_cols
self.num_words = num_rows * words_per_row
self.enable_delay_chain_resizing = False
self.inv_parasitic_delay = logical_effort.pinv
# Determines how much larger the sen delay should be. Accounts for possible error in model.
# FIXME: This should be made a parameter
self.wl_timing_tolerance = 1
self.wl_stage_efforts = None
self.sen_stage_efforts = None
if self.port_type == "rw":
self.num_control_signals = 2
else:
self.num_control_signals = 1
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.setup_signal_busses()
self.add_pins()
self.add_modules()
self.create_instances()
def create_layout(self):
""" Create layout and route between modules """
self.place_instances()
self.route_all()
# self.add_lvs_correspondence_points()
self.add_boundary()
self.DRC_LVS()
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
from math import ceil
previous_delay_chain_delay = (previous_fanout + 1 + self.inv_parasitic_delay) * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
# This can be anything >=2
delay_fanout = 3
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay = self.wl_delay * self.wl_timing_tolerance - (self.sen_delay - previous_delay_chain_delay)
debug.check(required_delay > 0, "Cannot size delay chain to have negative delay")
delay_per_stage = delay_fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
# force an even number of stages.
if delay_stages % 2 == 1:
delay_stages += 1
# Fanout can be varied as well but is a little more complicated but potentially optimal.
debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
return (delay_stages, delay_fanout)
def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay
previous_delay_chain_delay = previous_delay_per_stage * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
fanout_rise = fanout_fall = 2 # This can be anything >=2
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay_fall = self.wl_delay_fall * self.wl_timing_tolerance - \
(self.sen_delay_fall - previous_delay_chain_delay / 2)
required_delay_rise = self.wl_delay_rise * self.wl_timing_tolerance - \
(self.sen_delay_rise - previous_delay_chain_delay / 2)
debug.info(2,
"Required delays from chain: fall={}, rise={}".format(required_delay_fall,
required_delay_rise))
# If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
WARNING_FANOUT_DIFF = 5
stages_close = False
# The stages need to be equal (or at least a even number of stages with matching rise/fall delays)
while True:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,
fanout_fall)
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,
fanout_rise)
debug.info(1,
"Fall stages={}, rise stages={}".format(stages_fall,
stages_rise))
if abs(stages_fall - stages_rise) == 1 and not stages_close:
stages_close = True
safe_fanout_rise = fanout_rise
safe_fanout_fall = fanout_fall
if stages_fall == stages_rise:
break
elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise):
debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.")
fanout_rise = safe_fanout_rise
fanout_fall = safe_fanout_fall
break
# There should also be a condition to make sure the fanout does not get too large.
# Otherwise, increase the fanout of delay with the most stages, calculate new stages
elif stages_fall>stages_rise:
fanout_fall+=1
else:
fanout_rise+=1
total_stages = max(stages_fall, stages_rise) * 2
debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall))
# Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage.
stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)]
return stage_list
def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
from math import ceil
# Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay
# 3 is the minimum delay per stage (with pinv=0).
if required_delay <= 3 + self.inv_parasitic_delay:
return 1
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
return delay_stages
def route_rails(self):
""" Add the input signal inverted tracks """
height = self.control_logic_center.y - self.m2_pitch
# DFF spacing plus the power routing
offset = vector(self.ctrl_dff_array.width + self.m4_pitch, 0)
self.input_bus = self.create_vertical_bus("m2",
offset,
self.internal_bus_list,
height)
def place_instances(self):
""" Place all the instances """
# Keep track of all right-most instances to determine row boundary
# and add the vdd/gnd pins
self.row_end_inst = []
# Add the control flops on the left of the bus
self.place_dffs()
# All of the control logic is placed to the right of the DFFs and bus
# as well as the power supply stripe
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + self.m4_pitch
self.place_logic_rows()
# Delay chain always gets placed at row 4
self.place_delay(4)
height = self.delay_inst.uy()
# This offset is used for placement of the control logic in the SRAM level.
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), self.control_center_y)
# Extra pitch on top and right
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"):
self.width = max(self.delay_inst.rx(), self.width)
self.width += self.m2_pitch
def place_delay(self, row):
""" 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
# Add to the right of the control rows and routing channel
offset = vector(0, y_off)
self.delay_inst.place(offset, mirror="MX")
def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """
self.clk_buf_inst = self.add_inst(name="clkbuf",
mod=self.clk_buf_driver)
self.connect_inst(["clk", "clk_buf", "vdd", "gnd"])
def place_clk_buf_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_buf_inst, x_offset, row)
self.row_end_inst.append(self.clk_buf_inst)
def route_clk_buf(self):
clk_pin = self.clk_buf_inst.get_pin("A")
clk_pos = clk_pin.center()
self.add_layout_pin_rect_center(text="clk",
layer="m2",
offset=clk_pos)
self.add_via_stack_center(from_layer=clk_pin.layer,
to_layer="m2",
offset=clk_pos)
self.route_output_to_bus_jogged(self.clk_buf_inst,
"clk_buf")
self.connect_output(self.clk_buf_inst, "Z", "clk_buf")
def create_gated_clk_bar_row(self):
self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
mod=self.inv)
self.connect_inst(["clk_buf", "clk_bar", "vdd", "gnd"])
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
mod=self.and2)
self.connect_inst(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
def place_gated_clk_bar_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_bar_inst, x_offset, row)
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
self.row_end_inst.append(self.gated_clk_bar_inst)
def route_gated_clk_bar(self):
clkbuf_map = zip(["A"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.input_bus)
out_pin = self.clk_bar_inst.get_pin("Z")
out_pos = out_pin.center()
in_pin = self.gated_clk_bar_inst.get_pin("A")
in_pos = in_pin.center()
self.add_zjog(out_pin.layer, out_pos, in_pos)
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer=in_pin.layer,
offset=in_pos)
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["B"], ["cs"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst,
self.input_bus,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
b_pin = self.gated_clk_bar_inst.get_pin("B")
self.add_via_stack_center(from_layer=b_pin.layer,
to_layer="m3",
offset=b_pin.center())
# This is the second gate over, so it needs to be on M3
self.route_output_to_bus_jogged(self.gated_clk_bar_inst,
"gated_clk_bar")
def create_gated_clk_buf_row(self):
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
mod=self.and2)
self.connect_inst(["clk_buf", "cs", "gated_clk_buf", "vdd", "gnd"])
def place_gated_clk_buf_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
self.row_end_inst.append(self.gated_clk_buf_inst)
def route_gated_clk_buf(self):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_buf_inst,
self.input_bus)
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_buf_inst,
self.input_bus,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
z_pin = self.gated_clk_buf_inst.get_pin("Z")
self.add_via_stack_center(from_layer=z_pin.layer,
to_layer="m2",
offset=z_pin.center())
def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
inst_pins = self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list
self.connect_inst(inst_pins)
def place_dffs(self):
self.ctrl_dff_inst.place(vector(0, 0))
def route_dffs(self):
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"])
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])
# Connect the clock rail to the other clock rail
# by routing in the supply rail track to avoid channel conflicts
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
mid_pos = vector(in_pos.x, self.gated_clk_buf_inst.get_pin("vdd").cy() - self.m1_pitch)
rail_pos = vector(self.input_bus["clk_buf"].cx(), mid_pos.y)
self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb")
if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
def get_offset(self, row):
""" Compute the y-offset and mirroring """
y_off = row * self.and2.height
if row % 2:
y_off += self.and2.height
mirror="MX"
else:
mirror="R0"
return (y_off, mirror)
def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name)
out_pos = out_pin.center()
right_pos = out_pos + vector(self.width - out_pin.cx(), 0)
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2",
offset=out_pos)
self.add_layout_pin_segment_center(text=out_name,
layer="m2",
start=out_pos,
end=right_pos)
def route_supplies(self):
""" Add vdd and gnd to the instance cells """
pin_layer = self.dff.get_pin("vdd").layer
supply_layer = self.supply_stack[2]
# FIXME: We should be able to replace this with route_vertical_pins instead
# but we may have to make the logic gates a separate module so that they
# have row pins of the same width
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
min_row_x_loc = self.control_x_offset
vdd_pin_locs = []
gnd_pin_locs = []
last_via = None
for inst in self.row_end_inst:
pins = inst.get_pins("vdd")
for pin in pins:
if pin.layer == pin_layer:
row_loc = pin.rc()
pin_loc = vector(max_row_x_loc, pin.rc().y)
vdd_pin_locs.append(pin_loc)
last_via = self.add_via_stack_center(from_layer=pin_layer,
to_layer=supply_layer,
offset=pin_loc,
min_area=True)
self.add_path(pin_layer, [row_loc, pin_loc])
pins = inst.get_pins("gnd")
for pin in pins:
if pin.layer == pin_layer:
row_loc = pin.rc()
pin_loc = vector(min_row_x_loc, pin.rc().y)
gnd_pin_locs.append(pin_loc)
last_via = self.add_via_stack_center(from_layer=pin_layer,
to_layer=supply_layer,
offset=pin_loc,
min_area=True)
self.add_path(pin_layer, [row_loc, pin_loc])
if last_via:
via_height=last_via.mod.second_layer_height
via_width=last_via.mod.second_layer_width
else:
via_height=None
via_width=0
min_y = min([x.y for x in vdd_pin_locs])
max_y = max([x.y for x in vdd_pin_locs])
bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height)
top_pos = vector(max_row_x_loc, max_y + 0.5 * via_height)
self.add_layout_pin_segment_center(text="vdd",
layer=supply_layer,
start=bot_pos,
end=top_pos,
width=via_width)
min_y = min([x.y for x in gnd_pin_locs])
max_y = max([x.y for x in gnd_pin_locs])
bot_pos = vector(min_row_x_loc, min_y - 0.5 * via_height)
top_pos = vector(min_row_x_loc, max_y + 0.5 * via_height)
self.add_layout_pin_segment_center(text="gnd",
layer=supply_layer,
start=bot_pos,
end=top_pos,
width=via_width)
self.copy_layout_pin(self.delay_inst, "gnd")
self.copy_layout_pin(self.delay_inst, "vdd")
self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
def add_lvs_correspondence_points(self):
""" 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.
"""
# pin=self.clk_inv1.get_pin("Z")
# self.add_label_pin(text="clk1_bar",
# layer="m1",
# offset=pin.ll(),
# height=pin.height(),
# width=pin.width())
# pin=self.clk_inv2.get_pin("Z")
# self.add_label_pin(text="clk2",
# layer="m1",
# offset=pin.ll(),
# height=pin.height(),
# width=pin.width())
pin=self.delay_inst.get_pin("out")
self.add_label_pin(text="out",
layer=pin.layer,
offset=pin.ll(),
height=pin.height(),
width=pin.width())
def graph_exclude_dffs(self):
"""Exclude dffs from graph as they do not represent critical path"""
self.graph_inst_exclude.add(self.ctrl_dff_inst)
if self.port_type=="rw" or self.port_type=="w":
self.graph_inst_exclude.add(self.w_en_gate_inst)
def place_util(self, inst, x_offset, row):
""" Utility to place a row and compute the next offset """
(y_offset, mirror) = self.get_offset(row)
offset = vector(x_offset, y_offset)
inst.place(offset, mirror)
return x_offset + inst.width
def route_output_to_bus_jogged(self, inst, name):
# Connect this at the bottom of the buffer
out_pin = inst.get_pin("Z")
out_pos = out_pin.center()
mid1 = vector(out_pos.x, out_pos.y - 0.3 * inst.mod.height)
mid2 = vector(self.input_bus[name].cx(), mid1.y)
bus_pos = self.input_bus[name].center()
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2",
offset=out_pos)
def get_left_pins(self, name):
"""
Return the left side supply pins to connect to a vertical stripe.
"""
return(self.cntrl_dff_inst.get_pins(name) + self.delay_inst.get_pins(name))
+1 -1
View File
@@ -597,7 +597,7 @@ class hierarchical_decoder(design):
for inst in all_insts:
self.copy_layout_pin(inst, "vdd")
self.copy_layout_pin(inst, "gnd")
self.route_vertical_pins("vdd", self.and_inst, xside="rx",)
self.route_vertical_pins("gnd", self.and_inst, xside="lx",)
+1 -1
View File
@@ -172,7 +172,7 @@ class local_bitcell_array(bitcell_base_array):
if len(self.all_ports) > 1:
wl_offset = vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing,
self.bitcell_array.get_replica_bottom() + self.wl_array.height + self.cell.height)
self.wl_insts[1].place(wl_offset,
self.wl_insts[1].place(wl_offset,
mirror="XY")
self.height = self.bitcell_array.height
+7 -7
View File
@@ -80,20 +80,20 @@ class nand2_dec(design):
is_nchannel = True
stack = 2
is_cell = False
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
def get_input_capacitance(self):
"""Input cap of input, passes width of gates to gate cap function"""
return self.gate_c(self.nmos_width+self.pmos_width)
return self.gate_c(self.nmos_width+self.pmos_width)
def get_intrinsic_capacitance(self):
"""Get the drain capacitances of the TXs in the gate."""
nmos_stack = 2
mult = 1
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
nmos_stack,
mult)
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
1,
mult)
return nmos_drain_c + pmos_drain_c
mult)
return nmos_drain_c + pmos_drain_c
+7 -7
View File
@@ -80,20 +80,20 @@ class nand3_dec(design):
is_nchannel = True
stack = 3
is_cell = False
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
def get_input_capacitance(self):
"""Input cap of input, passes width of gates to gate cap function"""
return self.gate_c(self.nmos_width+self.pmos_width)
return self.gate_c(self.nmos_width+self.pmos_width)
def get_intrinsic_capacitance(self):
"""Get the drain capacitances of the TXs in the gate."""
nmos_stack = 3
mult = 1
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
nmos_stack,
mult)
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
1,
mult)
return nmos_drain_c + pmos_drain_c
mult)
return nmos_drain_c + pmos_drain_c
+7 -7
View File
@@ -80,20 +80,20 @@ class nand4_dec(design):
is_nchannel = True
stack = 4
is_cell = False
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
def get_input_capacitance(self):
"""Input cap of input, passes width of gates to gate cap function"""
return self.gate_c(self.nmos_width+self.pmos_width)
return self.gate_c(self.nmos_width+self.pmos_width)
def get_intrinsic_capacitance(self):
"""Get the drain capacitances of the TXs in the gate."""
nmos_stack = 4
mult = 1
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
nmos_stack,
mult)
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
1,
mult)
return nmos_drain_c + pmos_drain_c
mult)
return nmos_drain_c + pmos_drain_c
+1 -1
View File
@@ -317,7 +317,7 @@ class pgate(design):
contact_xoffset = nmos_pos.x + nmos.active_width \
+ self.active_space
# Allow an nimplant below it under the rail
contact_yoffset = max(0.5 * self.implant_width + self.implant_enclose_active,
contact_yoffset = max(0.5 * self.implant_width + self.implant_enclose_active,
self.get_tx_insts("nmos")[0].by())
contact_offset = vector(contact_xoffset, contact_yoffset)
+7 -7
View File
@@ -368,19 +368,19 @@ class pnand4(pgate):
is_nchannel = True
stack = 4
is_cell = False
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
def get_input_capacitance(self):
"""Input cap of input, passes width of gates to gate cap function"""
return self.gate_c(self.nmos_width+self.pmos_width)
return self.gate_c(self.nmos_width+self.pmos_width)
def get_intrinsic_capacitance(self):
"""Get the drain capacitances of the TXs in the gate."""
nmos_stack = 4
nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults,
nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults,
nmos_stack,
self.tx_mults)
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
1,
self.tx_mults)
return nmos_drain_c + pmos_drain_c
self.tx_mults)
return nmos_drain_c + pmos_drain_c
+1 -1
View File
@@ -88,7 +88,7 @@ class port_address(design):
self.copy_layout_pin(self.row_decoder_inst, "vdd")
self.copy_layout_pin(self.row_decoder_inst, "gnd")
# Also connect the B input of the RBL and_dec to vdd
if OPTS.local_array_size == 0:
rbl_b_pin = self.rbl_driver_inst.get_pin("B")
+1 -1
View File
@@ -51,5 +51,5 @@ class replica_bitcell_1port(bitcell_base):
def is_non_inverting(self):
"""Return input to output polarity for module"""
return False
+1 -1
View File
@@ -52,5 +52,5 @@ class replica_bitcell_2port(bitcell_base):
def is_non_inverting(self):
"""Return input to output polarity for module"""
return False
+1 -1
View File
@@ -12,7 +12,7 @@ from .bitcell_base import bitcell_base
class row_cap_bitcell_1port(bitcell_base):
"""
Row end cap cell.
Row end cap cell.
"""
def __init__(self, name="row_cap_bitcell_1port"):
+1 -1
View File
@@ -12,7 +12,7 @@ from .bitcell_base import bitcell_base
class row_cap_bitcell_2port(bitcell_base):
"""
Row end cap cell.
Row end cap cell.
"""
def __init__(self, name="row_cap_bitcell_2port"):
+16 -16
View File
@@ -73,48 +73,48 @@ class sense_amp(design):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
def is_non_inverting(self):
"""Return input to output polarity for module"""
#FIXME: This only applied to bl/br -> dout and not s_en->dout
return True
return True
def get_on_resistance(self):
"""On resistance of pinv, defined by single nmos"""
is_nchannel = True
stack = 1
is_cell = False
return self.tr_r_on(parameter["sa_inv_nmos_size"], is_nchannel, stack, is_cell)
is_cell = False
return self.tr_r_on(parameter["sa_inv_nmos_size"], is_nchannel, stack, is_cell)
def get_input_capacitance(self):
"""Input cap of input, passes width of gates to gate cap function"""
return self.gate_c(parameter["sa_inv_nmos_size"])
return self.gate_c(parameter["sa_inv_nmos_size"])
def get_intrinsic_capacitance(self):
"""Get the drain capacitances of the TXs in the gate."""
stack = 1
mult = 1
# Add the inverter drain Cap and the bitline TX drain Cap
nmos_drain_c = self.drain_c_(parameter["sa_inv_nmos_size"]*mult,
nmos_drain_c = self.drain_c_(parameter["sa_inv_nmos_size"]*mult,
stack,
mult)
pmos_drain_c = self.drain_c_(parameter["sa_inv_pmos_size"]*mult,
pmos_drain_c = self.drain_c_(parameter["sa_inv_pmos_size"]*mult,
stack,
mult)
bitline_pmos_size = 8
bl_pmos_drain_c = self.drain_c_(drc("minwidth_tx")*bitline_pmos_size,
bl_pmos_drain_c = self.drain_c_(drc("minwidth_tx")*bitline_pmos_size,
stack,
mult)
mult)
return nmos_drain_c + pmos_drain_c + bl_pmos_drain_c
def cacti_rc_delay(self, inputramptime, tf, vs1, vs2, rise, extra_param_dict):
def cacti_rc_delay(self, inputramptime, tf, vs1, vs2, rise, extra_param_dict):
""" Special RC delay function used by CACTI for sense amp delay
"""
import math
c_senseamp = extra_param_dict['load']
vdd = extra_param_dict['vdd']
vdd = extra_param_dict['vdd']
tau = c_senseamp/spice["sa_transconductance"]
return tau*math.log(vdd/(0.1*vdd))
+1 -1
View File
@@ -80,7 +80,7 @@ class sram():
def save(self):
""" Save all the output files while reporting time to do it as well. """
# Import this at the last minute so that the proper tech file
# Import this at the last minute so that the proper tech file
# is loaded and the right tools are selected
import verify
+1 -1
View File
@@ -1094,7 +1094,7 @@ class sram_1bank(design, verilog, lef):
self.add_dnwell(inflate=2.5)
# Route the supplies together and/or to the ring/stripes.
# This is done with the original bbox since the escape routes need to
# This is done with the original bbox since the escape routes need to
# be outside of the ring for OpenLane
rt = router_tech(self.supply_stack, 1)
init_bbox = self.get_bbox(side="ring",
+1 -1
View File
@@ -124,7 +124,7 @@ class wordline_driver_array(design):
en_pin = self.add_layout_pin_segment_center(text="en",
layer="m2",
start=en_bottom_pos,
end=en_top_pos)
end=en_top_pos)
for row in range(self.rows):
and_inst = self.wld_inst[row]