mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into multibank
This commit is contained in:
commit
aefe46394c
|
|
@ -13,3 +13,6 @@ technology/freepdk45/ncsu_basekit
|
||||||
technology/sky130/*_lib
|
technology/sky130/*_lib
|
||||||
technology/sky130/tech/.magicrc
|
technology/sky130/tech/.magicrc
|
||||||
.idea
|
.idea
|
||||||
|
compiler/tests/results/
|
||||||
|
sky*/
|
||||||
|
open_pdks/
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class design(hierarchy_design):
|
||||||
for pin_name in self.pins:
|
for pin_name in self.pins:
|
||||||
pins = self.get_pins(pin_name)
|
pins = self.get_pins(pin_name)
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
print(pin_name, pin)
|
debug.info(0, "{0} {1}".format(pin_name, pin))
|
||||||
|
|
||||||
def setup_multiport_constants(self):
|
def setup_multiport_constants(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -141,30 +141,28 @@ class layout():
|
||||||
layout.active_space)
|
layout.active_space)
|
||||||
|
|
||||||
# These are for debugging previous manual rules
|
# These are for debugging previous manual rules
|
||||||
if False:
|
level=99
|
||||||
print("poly_width", layout.poly_width)
|
debug.info(level, "poly_width".format(layout.poly_width))
|
||||||
print("poly_space", layout.poly_space)
|
debug.info(level, "poly_space".format(layout.poly_space))
|
||||||
print("m1_width", layout.m1_width)
|
debug.info(level, "m1_width".format(layout.m1_width))
|
||||||
print("m1_space", layout.m1_space)
|
debug.info(level, "m1_space".format(layout.m1_space))
|
||||||
print("m2_width", layout.m2_width)
|
debug.info(level, "m2_width".format(layout.m2_width))
|
||||||
print("m2_space", layout.m2_space)
|
debug.info(level, "m2_space".format(layout.m2_space))
|
||||||
print("m3_width", layout.m3_width)
|
debug.info(level, "m3_width".format(layout.m3_width))
|
||||||
print("m3_space", layout.m3_space)
|
debug.info(level, "m3_space".format(layout.m3_space))
|
||||||
print("m4_width", layout.m4_width)
|
debug.info(level, "m4_width".format(layout.m4_width))
|
||||||
print("m4_space", layout.m4_space)
|
debug.info(level, "m4_space".format(layout.m4_space))
|
||||||
print("active_width", layout.active_width)
|
debug.info(level, "active_width".format(layout.active_width))
|
||||||
print("active_space", layout.active_space)
|
debug.info(level, "active_space".format(layout.active_space))
|
||||||
print("contact_width", layout.contact_width)
|
debug.info(level, "contact_width".format(layout.contact_width))
|
||||||
print("poly_to_active", layout.poly_to_active)
|
debug.info(level, "poly_to_active".format(layout.poly_to_active))
|
||||||
print("poly_extend_active", layout.poly_extend_active)
|
debug.info(level, "poly_extend_active".format(layout.poly_extend_active))
|
||||||
print("poly_to_contact", layout.poly_to_contact)
|
debug.info(level, "poly_to_contact".format(layout.poly_to_contact))
|
||||||
print("active_contact_to_gate", layout.active_contact_to_gate)
|
debug.info(level, "active_contact_to_gate".format(layout.active_contact_to_gate))
|
||||||
print("poly_contact_to_gate", layout.poly_contact_to_gate)
|
debug.info(level, "poly_contact_to_gate".format(layout.poly_contact_to_gate))
|
||||||
print("well_enclose_active", layout.well_enclose_active)
|
debug.info(level, "well_enclose_active".format(layout.well_enclose_active))
|
||||||
print("implant_enclose_active", layout.implant_enclose_active)
|
debug.info(level, "implant_enclose_active".format(layout.implant_enclose_active))
|
||||||
print("implant_space", layout.implant_space)
|
debug.info(level, "implant_space".format(layout.implant_space))
|
||||||
import sys
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_layer_constants(layout):
|
def setup_layer_constants(layout):
|
||||||
|
|
@ -202,21 +200,19 @@ class layout():
|
||||||
"{}_nonpref_pitch".format(layer_id),
|
"{}_nonpref_pitch".format(layer_id),
|
||||||
layout.compute_pitch(layer_id, False))
|
layout.compute_pitch(layer_id, False))
|
||||||
|
|
||||||
if False:
|
level=99
|
||||||
for name in tech_layer_indices:
|
for name in tech_layer_indices:
|
||||||
if name == "active":
|
if name == "active":
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
print("{0} width {1} space {2}".format(name,
|
debug.info(level, "{0} width {1} space {2}".format(name,
|
||||||
getattr(layout, "{}_width".format(name)),
|
getattr(layout, "{}_width".format(name)),
|
||||||
getattr(layout, "{}_space".format(name))))
|
getattr(layout, "{}_space".format(name))))
|
||||||
|
|
||||||
print("pitch {0} nonpref {1}".format(getattr(layout, "{}_pitch".format(name)),
|
debug.info(level, "pitch {0} nonpref {1}".format(getattr(layout, "{}_pitch".format(name)),
|
||||||
getattr(layout, "{}_nonpref_pitch".format(name))))
|
getattr(layout, "{}_nonpref_pitch".format(name))))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
import sys
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def compute_pitch(layer, preferred=True):
|
def compute_pitch(layer, preferred=True):
|
||||||
|
|
|
||||||
|
|
@ -220,4 +220,3 @@ class lef:
|
||||||
round(item[1],
|
round(item[1],
|
||||||
self.round_grid)))
|
self.round_grid)))
|
||||||
self.lef.write(" ;\n")
|
self.lef.write(" ;\n")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,4 +153,3 @@ class timing_graph():
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
|
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,5 +118,3 @@ class cacti(simulation):
|
||||||
debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
|
debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
|
||||||
debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
|
debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
|
||||||
return power
|
return power
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,4 +40,3 @@ class linear_regression(regression_model):
|
||||||
|
|
||||||
pred = model.predict(features)
|
pred = model.predict(features)
|
||||||
return pred
|
return pred
|
||||||
|
|
||||||
|
|
@ -211,4 +211,3 @@ class voltage_at_measure(spice_measurement):
|
||||||
meas_name = self.name
|
meas_name = self.name
|
||||||
targ_name = self.targ_name_no_port
|
targ_name = self.targ_name_no_port
|
||||||
return (meas_name, targ_name, time_at)
|
return (meas_name, targ_name, time_at)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -448,6 +448,3 @@ class model_check(delay):
|
||||||
name_dict[self.sae_model_name] = name_dict["sae_measures"]
|
name_dict[self.sae_model_name] = name_dict["sae_measures"]
|
||||||
|
|
||||||
return name_dict
|
return name_dict
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,3 @@ class neural_network(regression_model):
|
||||||
pred = model.predict(features)
|
pred = model.predict(features)
|
||||||
reshape_pred = np.reshape(pred, (len(pred),1))
|
reshape_pred = np.reshape(pred, (len(pred),1))
|
||||||
return reshape_pred
|
return reshape_pred
|
||||||
|
|
||||||
|
|
@ -205,4 +205,3 @@ class regression_model(simulation):
|
||||||
OPTS.model_dict[model_name+"_coef"] = list(model.coef_[0])
|
OPTS.model_dict[model_name+"_coef"] = list(model.coef_[0])
|
||||||
debug.info(1,"Coefs of {}:{}".format(model_name,OPTS.model_dict[model_name+"_coef"]))
|
debug.info(1,"Coefs of {}:{}".format(model_name,OPTS.model_dict[model_name+"_coef"]))
|
||||||
OPTS.model_dict[model_name+"_intercept"] = float(model.intercept_)
|
OPTS.model_dict[model_name+"_intercept"] = float(model.intercept_)
|
||||||
|
|
||||||
|
|
@ -97,6 +97,10 @@ log.create_file = True
|
||||||
|
|
||||||
def info(lev, str):
|
def info(lev, str):
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
# 99 is a special never print level
|
||||||
|
if lev == 99:
|
||||||
|
return
|
||||||
|
|
||||||
if (OPTS.verbose_level >= lev):
|
if (OPTS.verbose_level >= lev):
|
||||||
frm = inspect.stack()[1]
|
frm = inspect.stack()[1]
|
||||||
mod = inspect.getmodule(frm[0])
|
mod = inspect.getmodule(frm[0])
|
||||||
|
|
@ -134,4 +138,3 @@ def bp():
|
||||||
and whenever you encounter the debug.bp() they won't be "reset".
|
and whenever you encounter the debug.bp() they won't be "reset".
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -290,4 +290,3 @@ class cell_properties():
|
||||||
@property
|
@property
|
||||||
def row_cap_2port(self):
|
def row_cap_2port(self):
|
||||||
return self._row_cap_2port
|
return self._row_cap_2port
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import getpass
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
VERSION = "1.1.18"
|
VERSION = "1.2.0"
|
||||||
NAME = "OpenRAM v{}".format(VERSION)
|
NAME = "OpenRAM v{}".format(VERSION)
|
||||||
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
|
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -267,27 +267,3 @@ if __name__ == "__main__":
|
||||||
input_dir_path = sys.argv[1]
|
input_dir_path = sys.argv[1]
|
||||||
out_path = sys.argv[2]
|
out_path = sys.argv[2]
|
||||||
gen_model_csv(input_dir_path, out_path)
|
gen_model_csv(input_dir_path, out_path)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,74 +5,22 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from base import design
|
|
||||||
import debug
|
import debug
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import math
|
import math
|
||||||
from base import vector
|
from base import vector
|
||||||
from globals import OPTS
|
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.
|
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=""):
|
def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""):
|
||||||
""" Constructor """
|
""" Constructor """
|
||||||
name = "control_logic_" + port_type
|
super().__init__(num_rows, words_per_row, word_size, spare_columns, sram, port_type, name)
|
||||||
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 add_pins(self):
|
def add_pins(self):
|
||||||
""" Add the pins to the control logic module. """
|
""" 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",
|
self.delay_chain=factory.create(module_type="delay_chain",
|
||||||
fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
|
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):
|
def setup_signal_busses(self):
|
||||||
""" Setup bus names, determine the size of the busses etc """
|
""" Setup bus names, determine the size of the busses etc """
|
||||||
|
|
||||||
|
|
@ -277,17 +138,6 @@ class control_logic(design):
|
||||||
|
|
||||||
self.supply_list = ["vdd", "gnd"]
|
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):
|
def create_instances(self):
|
||||||
""" Create all the instances """
|
""" Create all the instances """
|
||||||
self.create_dffs()
|
self.create_dffs()
|
||||||
|
|
@ -303,21 +153,8 @@ class control_logic(design):
|
||||||
self.create_delay()
|
self.create_delay()
|
||||||
self.create_pen_row()
|
self.create_pen_row()
|
||||||
|
|
||||||
def place_instances(self):
|
def place_logic_rows(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
|
|
||||||
|
|
||||||
row = 0
|
row = 0
|
||||||
# Add the logic on the right of the bus
|
|
||||||
self.place_clk_buf_row(row)
|
self.place_clk_buf_row(row)
|
||||||
row += 1
|
row += 1
|
||||||
self.place_gated_clk_bar_row(row)
|
self.place_gated_clk_bar_row(row)
|
||||||
|
|
@ -336,24 +173,8 @@ class control_logic(design):
|
||||||
self.place_rbl_delay_row(row)
|
self.place_rbl_delay_row(row)
|
||||||
row += 1
|
row += 1
|
||||||
self.place_wlen_row(row)
|
self.place_wlen_row(row)
|
||||||
row += 1
|
|
||||||
|
|
||||||
control_center_y = self.wl_en_inst.uy() + self.m3_pitch
|
self.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
|
|
||||||
|
|
||||||
def route_all(self):
|
def route_all(self):
|
||||||
""" Routing between modules """
|
""" Routing between modules """
|
||||||
|
|
@ -373,24 +194,12 @@ class control_logic(design):
|
||||||
self.route_supplies()
|
self.route_supplies()
|
||||||
|
|
||||||
def create_delay(self):
|
def create_delay(self):
|
||||||
""" Create the replica bitline """
|
""" Create the delay chain """
|
||||||
self.delay_inst=self.add_inst(name="delay_chain",
|
self.delay_inst=self.add_inst(name="delay_chain",
|
||||||
mod=self.delay_chain)
|
mod=self.delay_chain)
|
||||||
# rbl_bl_delay is asserted (1) when the bitline has been discharged
|
# rbl_bl_delay is asserted (1) when the bitline has been discharged
|
||||||
self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"])
|
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):
|
def route_delay(self):
|
||||||
|
|
||||||
out_pos = self.delay_inst.get_pin("out").center()
|
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
|
# Input from RBL goes to the delay line for futher delay
|
||||||
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
|
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):
|
def create_wlen_row(self):
|
||||||
# input pre_p_en, output: wl_en
|
# input pre_p_en, output: wl_en
|
||||||
self.wl_en_inst=self.add_inst(name="buf_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_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus)
|
||||||
|
|
||||||
self.connect_output(self.w_en_gate_inst, "Z", "w_en")
|
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))
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
@ -213,8 +213,3 @@ class grid:
|
||||||
"""
|
"""
|
||||||
path.set_path(False)
|
path.set_path(False)
|
||||||
path.set_blocked(True)
|
path.set_blocked(True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,4 +49,3 @@ class grid_cell:
|
||||||
type_string += "P"
|
type_string += "P"
|
||||||
|
|
||||||
return type_string
|
return type_string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -805,7 +805,7 @@ class router(router_tech):
|
||||||
try:
|
try:
|
||||||
x = track[0]*self.track_width - 0.5*self.track_width
|
x = track[0]*self.track_width - 0.5*self.track_width
|
||||||
except TypeError:
|
except TypeError:
|
||||||
print(track[0], type(track[0]), self.track_width, type(self.track_width))
|
debug.warning("{} {} {} {}".format(track[0], type(track[0]), self.track_width, type(self.track_width)))
|
||||||
y = track[1]*self.track_width - 0.5*self.track_width
|
y = track[1]*self.track_width - 0.5*self.track_width
|
||||||
# offset lowest corner object to to (-track halo,-track halo)
|
# offset lowest corner object to to (-track halo,-track halo)
|
||||||
ll = snap_to_grid(vector(x, y))
|
ll = snap_to_grid(vector(x, y))
|
||||||
|
|
|
||||||
|
|
@ -102,5 +102,3 @@ class signal_escape_router(router):
|
||||||
# breakpoint()
|
# breakpoint()
|
||||||
|
|
||||||
self.write_debug_gds("debug_route.gds", True)
|
self.write_debug_gds("debug_route.gds", True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,5 +77,3 @@ class supply_grid(signal_grid):
|
||||||
wave = wave_path.neighbor(direct)
|
wave = wave_path.neighbor(direct)
|
||||||
|
|
||||||
return wave_path
|
return wave_path
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -392,4 +392,3 @@ class supply_grid_router(router):
|
||||||
debug.info(4, "Blocking supply rail")
|
debug.info(4, "Blocking supply rail")
|
||||||
for rail_name in self.supply_rail_tracks:
|
for rail_name in self.supply_rail_tracks:
|
||||||
self.rg.set_blocked(self.supply_rail_tracks[rail_name])
|
self.rg.set_blocked(self.supply_rail_tracks[rail_name])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,13 +145,14 @@ class supply_tree_router(router):
|
||||||
connections.append((x, y))
|
connections.append((x, y))
|
||||||
|
|
||||||
# Route MST components
|
# Route MST components
|
||||||
|
level=99
|
||||||
for index, (src, dest) in enumerate(connections):
|
for index, (src, dest) in enumerate(connections):
|
||||||
if not (index % 25):
|
if not (index % 25):
|
||||||
debug.info(1, "{0} supply segments routed, {1} remaining.".format(index, len(connections) - index))
|
debug.info(1, "{0} supply segments routed, {1} remaining.".format(index, len(connections) - index))
|
||||||
self.route_signal(pin_name, src, dest)
|
self.route_signal(pin_name, src, dest)
|
||||||
if False and pin_name == "gnd":
|
if False and pin_name == "gnd":
|
||||||
print("\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages))
|
debug.info(level, "\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages))
|
||||||
print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages))
|
debug.info(level, ("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages)))
|
||||||
self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False)
|
self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False)
|
||||||
|
|
||||||
#self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False)
|
#self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False)
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ class code_format_test(openram_test):
|
||||||
continue
|
continue
|
||||||
errors += check_file_format_tab(code)
|
errors += check_file_format_tab(code)
|
||||||
errors += check_file_format_carriage(code)
|
errors += check_file_format_carriage(code)
|
||||||
|
errors += check_file_format_whitespace(code)
|
||||||
|
|
||||||
for code in source_codes:
|
for code in source_codes:
|
||||||
if re.search("gdsMill", code):
|
if re.search("gdsMill", code):
|
||||||
|
|
@ -51,7 +52,7 @@ def setup_files(path):
|
||||||
files = []
|
files = []
|
||||||
for (dir, _, current_files) in os.walk(path):
|
for (dir, _, current_files) in os.walk(path):
|
||||||
for f in current_files:
|
for f in current_files:
|
||||||
files.append(os.getenv("OPENRAM_HOME"))
|
files.append(os.path.join(dir, f))
|
||||||
nametest = re.compile("\.py$", re.IGNORECASE)
|
nametest = re.compile("\.py$", re.IGNORECASE)
|
||||||
select_files = list(filter(nametest.search, files))
|
select_files = list(filter(nametest.search, files))
|
||||||
return select_files
|
return select_files
|
||||||
|
|
@ -92,16 +93,43 @@ def check_file_format_carriage(file_name):
|
||||||
if len(key_positions)>10:
|
if len(key_positions)>10:
|
||||||
line_numbers = key_positions[:10] + [" ..."]
|
line_numbers = key_positions[:10] + [" ..."]
|
||||||
else:
|
else:
|
||||||
line_numbers = key_positoins
|
line_numbers = key_positions
|
||||||
debug.info(0, '\nFound ' + str(len(key_positions)) + ' carriage returns in ' +
|
debug.info(0, '\nFound ' + str(len(key_positions)) + ' carriage returns in ' +
|
||||||
str(file_name) + ' (lines ' + ",".join(str(x) for x in line_numbers) + ')')
|
str(file_name) + ' (lines ' + ",".join(str(x) for x in line_numbers) + ')')
|
||||||
f.close()
|
f.close()
|
||||||
return len(key_positions)
|
return len(key_positions)
|
||||||
|
|
||||||
|
|
||||||
|
def check_file_format_whitespace(file_name):
|
||||||
|
"""
|
||||||
|
Check if file contains a line with whitespace at the end
|
||||||
|
and return the number of these lines.
|
||||||
|
"""
|
||||||
|
|
||||||
|
f = open(file_name, "r")
|
||||||
|
key_positions = []
|
||||||
|
for num, line in enumerate(f.readlines()):
|
||||||
|
if re.match(r".*[ \t]$", line):
|
||||||
|
key_positions.append(num)
|
||||||
|
if len(key_positions) > 0:
|
||||||
|
if len(key_positions) > 10:
|
||||||
|
line_numbers = key_positions[:10] + [" ..."]
|
||||||
|
else:
|
||||||
|
line_numbers = key_positions
|
||||||
|
debug.info(0, "\nFound " + str(len(key_positions)) + " ending whitespace in " +
|
||||||
|
str(file_name) + " (lines " + ",".join(str(x) for x in line_numbers) + ")")
|
||||||
|
f.close()
|
||||||
|
return len(key_positions)
|
||||||
|
|
||||||
|
|
||||||
def check_print_output(file_name):
|
def check_print_output(file_name):
|
||||||
"""Check if any files (except debug.py) call the _print_ function. We should
|
"""Check if any files (except debug.py) call the _print_ function. We should
|
||||||
use the debug output with verbosity instead!"""
|
use the debug output with verbosity instead!"""
|
||||||
|
|
||||||
|
skip_files = ["printGDS.py", "uniquifyGDS.py", "processGDS.py", "model_data_util.py"]
|
||||||
|
base_file_name = os.path.basename(file_name)
|
||||||
|
if base_file_name in skip_files:
|
||||||
|
return(0)
|
||||||
file = open(file_name, "r+b")
|
file = open(file_name, "r+b")
|
||||||
line = file.read().decode('utf-8')
|
line = file.read().decode('utf-8')
|
||||||
# skip comments with a hash
|
# skip comments with a hash
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,8 @@ class model_delay_test(openram_test):
|
||||||
else:
|
else:
|
||||||
self.assertTrue(False) # other techs fail
|
self.assertTrue(False) # other techs fail
|
||||||
|
|
||||||
print('spice_delays', spice_delays)
|
debug.info(3, 'spice_delays {}'.fomrat(spice_delays))
|
||||||
print('model_delays', model_delays)
|
debug.info(3, 'model_delays {}'.format(model_delays))
|
||||||
|
|
||||||
# Check if no too many or too few results
|
# Check if no too many or too few results
|
||||||
self.assertTrue(len(spice_delays.keys())==len(model_delays.keys()))
|
self.assertTrue(len(spice_delays.keys())==len(model_delays.keys()))
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ RUN apt-get --no-install-recommends -y upgrade
|
||||||
# General tools for building etc.
|
# General tools for building etc.
|
||||||
RUN apt-get install --no-install-recommends -y build-essential git ssh vim gosu autoconf automake libtool bison flex
|
RUN apt-get install --no-install-recommends -y build-essential git ssh vim gosu autoconf automake libtool bison flex
|
||||||
# Use bash instead of dash
|
# Use bash instead of dash
|
||||||
|
# Must be on one line or else ln won't work without a shell!
|
||||||
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
|
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
|
||||||
# Needed by OpenRAM
|
# Needed by OpenRAM
|
||||||
RUN apt-get install --no-install-recommends -y python3 python3-numpy python3-scipy python3-pip python3-matplotlib python3-venv python3-sklearn python3-subunit python3-coverage
|
RUN apt-get install --no-install-recommends -y python3 python3-numpy python3-scipy python3-pip python3-matplotlib python3-venv python3-sklearn python3-subunit python3-coverage
|
||||||
|
|
@ -30,8 +31,8 @@ WORKDIR /root
|
||||||
RUN git clone https://github.com/KLayout/klayout
|
RUN git clone https://github.com/KLayout/klayout
|
||||||
WORKDIR /root/klayout
|
WORKDIR /root/klayout
|
||||||
RUN git checkout ${KLAYOUT_COMMIT}
|
RUN git checkout ${KLAYOUT_COMMIT}
|
||||||
RUN ./build.sh -qt5 -debug -j 8 \
|
RUN ./build.sh -qt5 -debug -j$(nproc)
|
||||||
&& cp -r bin-debug /usr/local/klayout
|
RUN cp -r bin-debug /usr/local/klayout
|
||||||
RUN rm -rf /root/klayout
|
RUN rm -rf /root/klayout
|
||||||
|
|
||||||
### Trilinos ###
|
### Trilinos ###
|
||||||
|
|
@ -99,15 +100,15 @@ RUN ../configure CXXFLAGS="-O3 -std=c++11" \
|
||||||
RUN make -j 4 install
|
RUN make -j 4 install
|
||||||
|
|
||||||
### Ngspice ###
|
### Ngspice ###
|
||||||
ARG NGSPICE_COMIT=032b1c32c4dbad45ff132bcfac1dbecadbd8abb0
|
ARG NGSPICE_COMMIT=032b1c32c4dbad45ff132bcfac1dbecadbd8abb0
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
RUN git clone git://git.code.sf.net/p/ngspice/ngspice
|
RUN git clone git://git.code.sf.net/p/ngspice/ngspice
|
||||||
WORKDIR /root/ngspice
|
WORKDIR /root/ngspice
|
||||||
RUN git checkout ${NGSPICE_COMMIT}
|
RUN git checkout ${NGSPICE_COMMIT}
|
||||||
RUN ./autogen.sh \
|
RUN ./autogen.sh
|
||||||
&& ./configure --enable-openmp --with-readline \
|
RUN ./configure --enable-openmp --with-readline
|
||||||
&& make \
|
RUN make
|
||||||
&& make install
|
RUN make install
|
||||||
RUN rm -rf /root/ngspice
|
RUN rm -rf /root/ngspice
|
||||||
|
|
||||||
### Netgen ###
|
### Netgen ###
|
||||||
|
|
@ -118,9 +119,9 @@ WORKDIR /root
|
||||||
RUN git clone git://opencircuitdesign.com/netgen netgen
|
RUN git clone git://opencircuitdesign.com/netgen netgen
|
||||||
WORKDIR /root/netgen
|
WORKDIR /root/netgen
|
||||||
RUN git checkout ${NETGEN_COMMIT}
|
RUN git checkout ${NETGEN_COMMIT}
|
||||||
RUN ./configure \
|
RUN ./configure
|
||||||
&& make -j$(nproc) \
|
RUN make -j$(nproc)
|
||||||
&& make install
|
RUN make install
|
||||||
RUN rm -rf /root/netgen
|
RUN rm -rf /root/netgen
|
||||||
|
|
||||||
### iVerilog ###
|
### iVerilog ###
|
||||||
|
|
@ -137,9 +138,9 @@ WORKDIR /root/magic
|
||||||
RUN git checkout ${MAGIC_COMMIT}
|
RUN git checkout ${MAGIC_COMMIT}
|
||||||
COPY mrg.patch /root/magic
|
COPY mrg.patch /root/magic
|
||||||
RUN git apply mrg.patch
|
RUN git apply mrg.patch
|
||||||
RUN ./configure \
|
RUN ./configure
|
||||||
&& make \
|
RUN make
|
||||||
&& make install
|
RUN make install
|
||||||
RUN rm -rf /root/magic
|
RUN rm -rf /root/magic
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue