mirror of
https://github.com/VLSIDA/OpenRAM.git
synced 2026-09-03 08:27:11 +02:00
Merge branch 'dev' into cacti_model
This commit is contained in:
@@ -5,10 +5,11 @@
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
|
||||
import math
|
||||
import tech
|
||||
|
||||
|
||||
class vector():
|
||||
"""
|
||||
This is the vector class to represent the coordinate
|
||||
|
||||
@@ -153,7 +153,7 @@ class verilog:
|
||||
self.vf.write(" wmask{0}_reg = wmask{0};\n".format(port))
|
||||
if self.num_spare_cols:
|
||||
self.vf.write(" spare_wen{0}_reg = spare_wen{0};\n".format(port))
|
||||
self.vf.write(" addr{0}_reg = addr{0};\n".format(port))
|
||||
self.vf.write(" addr{0}_reg = addr{0};\n".format(port))
|
||||
if port in self.read_ports:
|
||||
self.add_write_read_checks(port)
|
||||
|
||||
@@ -190,15 +190,15 @@ class verilog:
|
||||
self.vf.write(" input csb{0}; // active low chip select\n".format(port))
|
||||
if port in self.readwrite_ports:
|
||||
self.vf.write(" input web{0}; // active low write control\n".format(port))
|
||||
|
||||
self.vf.write(" input [ADDR_WIDTH-1:0] addr{0};\n".format(port))
|
||||
if port in self.write_ports:
|
||||
if self.write_size:
|
||||
self.vf.write(" input [NUM_WMASKS-1:0] wmask{0}; // write mask\n".format(port))
|
||||
if self.num_spare_cols == 1:
|
||||
self.vf.write(" input spare_wen{0}; // spare mask\n".format(port))
|
||||
elif self.num_spare_cols > 1:
|
||||
self.vf.write(" input [{1}:0] spare_wen{0}; // spare mask\n".format(port, self.num_spare_cols-1))
|
||||
|
||||
self.vf.write(" input [ADDR_WIDTH-1:0] addr{0};\n".format(port))
|
||||
if port in self.write_ports:
|
||||
self.vf.write(" input [DATA_WIDTH-1:0] din{0};\n".format(port))
|
||||
if port in self.read_ports:
|
||||
self.vf.write(" output [DATA_WIDTH-1:0] dout{0};\n".format(port))
|
||||
|
||||
@@ -31,7 +31,7 @@ class lib:
|
||||
self.gnd_name = spice["ground"]
|
||||
except KeyError:
|
||||
self.gnd_name = "gnd"
|
||||
|
||||
|
||||
self.out_dir = out_dir
|
||||
self.sram = sram
|
||||
self.sp_file = sp_file
|
||||
@@ -60,7 +60,7 @@ class lib:
|
||||
self.load_scales = np.array(OPTS.load_scales)
|
||||
self.load = tech.spice["dff_in_cap"]
|
||||
self.loads = self.load_scales * self.load
|
||||
|
||||
|
||||
|
||||
self.slew_scales = np.array(OPTS.slew_scales)
|
||||
self.slew = tech.spice["rise_time"]
|
||||
@@ -79,7 +79,7 @@ class lib:
|
||||
self.slews.append(slew)
|
||||
self.loads = np.array(self.loads)
|
||||
self.slews = np.array(self.slews)
|
||||
debug.info(1, "Slews: {0}".format(self.slews))
|
||||
debug.info(1, "Slews: {0}".format(self.slews))
|
||||
debug.info(1, "Loads: {0}".format(self.loads))
|
||||
debug.info(1, "self.load_slews : {0}".format(self.load_slews))
|
||||
def create_corners(self):
|
||||
@@ -102,7 +102,7 @@ class lib:
|
||||
|
||||
self.corners = []
|
||||
self.lib_files = []
|
||||
|
||||
|
||||
if OPTS.use_specified_corners == None:
|
||||
# Nominal corner
|
||||
corner_tuples = set()
|
||||
@@ -114,7 +114,7 @@ class lib:
|
||||
for v in self.supply_voltages:
|
||||
for t in self.temperatures:
|
||||
corner_tuples.add((p, v, t))
|
||||
else:
|
||||
else:
|
||||
nom_corner = (nom_process, nom_supply, nom_temperature)
|
||||
corner_tuples.add(nom_corner)
|
||||
if not OPTS.nominal_corner_only:
|
||||
@@ -132,7 +132,7 @@ class lib:
|
||||
corner_tuples.remove(nom_corner)
|
||||
else:
|
||||
corner_tuples = OPTS.use_specified_corners
|
||||
|
||||
|
||||
for corner_tuple in corner_tuples:
|
||||
self.add_corner(*corner_tuple)
|
||||
|
||||
@@ -366,16 +366,16 @@ class lib:
|
||||
self.lib.write(" base_type : array;\n")
|
||||
self.lib.write(" data_type : bit;\n")
|
||||
self.lib.write(" bit_width : {0};\n".format(self.sram.word_size))
|
||||
self.lib.write(" bit_from : 0;\n")
|
||||
self.lib.write(" bit_to : {0};\n".format(self.sram.word_size - 1))
|
||||
self.lib.write(" bit_from : {0};\n".format(self.sram.word_size - 1))
|
||||
self.lib.write(" bit_to : 0;\n")
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
self.lib.write(" type (addr){\n")
|
||||
self.lib.write(" base_type : array;\n")
|
||||
self.lib.write(" data_type : bit;\n")
|
||||
self.lib.write(" bit_width : {0};\n".format(self.sram.addr_size))
|
||||
self.lib.write(" bit_from : 0;\n")
|
||||
self.lib.write(" bit_to : {0};\n".format(self.sram.addr_size - 1))
|
||||
self.lib.write(" bit_from : {0};\n".format(self.sram.addr_size - 1))
|
||||
self.lib.write(" bit_to : 0;\n")
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
if self.sram.write_size:
|
||||
@@ -383,8 +383,8 @@ class lib:
|
||||
self.lib.write(" base_type : array;\n")
|
||||
self.lib.write(" data_type : bit;\n")
|
||||
self.lib.write(" bit_width : {0};\n".format(self.sram.num_wmasks))
|
||||
self.lib.write(" bit_from : 0;\n")
|
||||
self.lib.write(" bit_to : {0};\n".format(self.sram.num_wmasks - 1))
|
||||
self.lib.write(" bit_from : {0};\n".format(self.sram.num_wmasks - 1))
|
||||
self.lib.write(" bit_to : 0;\n")
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
|
||||
@@ -652,21 +652,21 @@ class lib:
|
||||
probe_address = "0" + "1" * (self.sram.addr_size - 1)
|
||||
probe_data = self.sram.word_size - 1
|
||||
char_results = self.d.analyze(probe_address, probe_data, self.load_slews)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
self.char_sram_results, self.char_port_results = char_results
|
||||
if 'sim_time' in self.char_sram_results:
|
||||
self.pred_time = self.char_sram_results['sim_time']
|
||||
# Add to the OPTS to be written out as part of the extended OPTS file
|
||||
# FIXME: Temporarily removed from characterization output
|
||||
# FIXME: Temporarily removed from characterization output
|
||||
# if not self.use_model:
|
||||
# OPTS.sen_path_delays = self.char_sram_results["sen_path_measures"]
|
||||
# OPTS.sen_path_names = self.char_sram_results["sen_path_names"]
|
||||
# OPTS.bl_path_delays = self.char_sram_results["bl_path_measures"]
|
||||
# OPTS.bl_path_names = self.char_sram_results["bl_path_names"]
|
||||
|
||||
|
||||
|
||||
|
||||
def compute_setup_hold(self):
|
||||
""" Do the analysis if we haven't characterized a FF yet """
|
||||
# Do the analysis if we haven't characterized a FF yet
|
||||
@@ -689,23 +689,23 @@ class lib:
|
||||
datasheet = open(datasheet_path +'/datasheet.info', 'w')
|
||||
else:
|
||||
datasheet = open(datasheet_path +'/datasheet.info', 'a+')
|
||||
|
||||
|
||||
self.write_inp_params_datasheet(datasheet, corner, lib_name)
|
||||
self.write_signal_from_ports(datasheet,
|
||||
"din{1}[{0}:0]".format(self.sram.word_size - 1, '{}'),
|
||||
self.write_ports,
|
||||
"din{1}[{0}:0]".format(self.sram.word_size - 1, '{}'),
|
||||
self.write_ports,
|
||||
"setup_times_LH",
|
||||
"setup_times_HL",
|
||||
"setup_times_HL",
|
||||
"hold_times_LH",
|
||||
"hold_times_HL")
|
||||
|
||||
|
||||
# self.write_signal_from_ports(datasheet,
|
||||
# "dout{1}[{0}:0]".format(self.sram.word_size - 1, '{}'),
|
||||
# self.read_ports,
|
||||
# "dout{1}[{0}:0]".format(self.sram.word_size - 1, '{}'),
|
||||
# self.read_ports,
|
||||
# "delay_lh",
|
||||
# "delay_hl",
|
||||
# "delay_hl",
|
||||
# "slew_lh",
|
||||
# "slew_hl")
|
||||
# "slew_hl")
|
||||
for port in self.all_ports:
|
||||
#dout timing
|
||||
if port in self.read_ports:
|
||||
@@ -722,41 +722,41 @@ class lib:
|
||||
|
||||
min(list(map(round_time,self.char_port_results[port]["slew_hl"]))),
|
||||
max(list(map(round_time,self.char_port_results[port]["slew_hl"])))
|
||||
))
|
||||
))
|
||||
|
||||
self.write_signal_from_ports(datasheet,
|
||||
"csb{}",
|
||||
self.all_ports,
|
||||
"csb{}",
|
||||
self.all_ports,
|
||||
"setup_times_LH",
|
||||
"setup_times_HL",
|
||||
"setup_times_HL",
|
||||
"hold_times_LH",
|
||||
"hold_times_HL")
|
||||
|
||||
self.write_signal_from_ports(datasheet,
|
||||
"addr{1}[{0}:0]".format(self.sram.addr_size - 1, '{}'),
|
||||
self.all_ports,
|
||||
"setup_times_LH",
|
||||
"setup_times_HL",
|
||||
"hold_times_LH",
|
||||
"hold_times_HL")
|
||||
|
||||
self.write_signal_from_ports(datasheet,
|
||||
"web{}",
|
||||
self.readwrite_ports,
|
||||
"addr{1}[{0}:0]".format(self.sram.addr_size - 1, '{}'),
|
||||
self.all_ports,
|
||||
"setup_times_LH",
|
||||
"setup_times_HL",
|
||||
"setup_times_HL",
|
||||
"hold_times_LH",
|
||||
"hold_times_HL")
|
||||
"hold_times_HL")
|
||||
|
||||
self.write_signal_from_ports(datasheet,
|
||||
"web{}",
|
||||
self.readwrite_ports,
|
||||
"setup_times_LH",
|
||||
"setup_times_HL",
|
||||
"hold_times_LH",
|
||||
"hold_times_HL")
|
||||
|
||||
self.write_power_datasheet(datasheet)
|
||||
|
||||
self.write_model_params(datasheet, time)
|
||||
|
||||
|
||||
datasheet.write("END\n")
|
||||
datasheet.close()
|
||||
|
||||
def write_inp_params_datasheet(self, datasheet, corner, lib_name):
|
||||
|
||||
|
||||
if OPTS.is_unit_test:
|
||||
git_id = 'FFFFFFFFFFFFFFFFFFFF'
|
||||
|
||||
@@ -776,7 +776,7 @@ class lib:
|
||||
debug.warning("Failed to retrieve git id")
|
||||
git_id = 'Failed to retrieve'
|
||||
current_time = datetime.date.today()
|
||||
|
||||
|
||||
# write static information to be parser later
|
||||
datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},".format(
|
||||
OPTS.output_name,
|
||||
@@ -804,8 +804,8 @@ class lib:
|
||||
|
||||
# write area
|
||||
datasheet.write(str(self.sram.width * self.sram.height) + ',')
|
||||
|
||||
def write_signal_from_ports(self, datasheet, signal, ports, time_pos_1, time_pos_2, time_pos_3, time_pos_4):
|
||||
|
||||
def write_signal_from_ports(self, datasheet, signal, ports, time_pos_1, time_pos_2, time_pos_3, time_pos_4):
|
||||
for port in ports:
|
||||
datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format(
|
||||
signal.format(port),
|
||||
@@ -822,8 +822,8 @@ class lib:
|
||||
max(list(map(round_time,self.times[time_pos_4])))
|
||||
|
||||
))
|
||||
|
||||
def write_power_datasheet(self, datasheet):
|
||||
|
||||
def write_power_datasheet(self, datasheet):
|
||||
# write power information
|
||||
for port in self.all_ports:
|
||||
name = ''
|
||||
@@ -862,34 +862,30 @@ class lib:
|
||||
control_str += ' & csb{0}'.format(i)
|
||||
|
||||
datasheet.write("{0},{1},{2},".format('leak', control_str, self.char_sram_results["leakage_power"]))
|
||||
|
||||
|
||||
def write_model_params(self, datasheet, time):
|
||||
"""Write values which will be used in the analytical model as inputs"""
|
||||
datasheet.write("{0},{1},".format('sim_time', time))
|
||||
datasheet.write("{0},{1},".format('words_per_row', OPTS.words_per_row))
|
||||
datasheet.write("{0},{1},".format('slews', list(self.slews)))
|
||||
datasheet.write("{0},{1},".format('loads', list(self.loads)))
|
||||
|
||||
|
||||
for port in self.read_ports:
|
||||
datasheet.write("{0},{1},".format('cell_rise_{}'.format(port), self.char_port_results[port]["delay_lh"]))
|
||||
datasheet.write("{0},{1},".format('cell_fall_{}'.format(port), self.char_port_results[port]["delay_hl"]))
|
||||
datasheet.write("{0},{1},".format('rise_transition_{}'.format(port), self.char_port_results[port]["slew_lh"]))
|
||||
datasheet.write("{0},{1},".format('fall_transition_{}'.format(port), self.char_port_results[port]["slew_hl"]))
|
||||
|
||||
|
||||
for port in self.write_ports:
|
||||
write1_power = np.mean(self.char_port_results[port]["write1_power"])
|
||||
write0_power = np.mean(self.char_port_results[port]["write0_power"])
|
||||
datasheet.write("{0},{1},".format('write_rise_power_{}'.format(port), write1_power))
|
||||
#FIXME: should be write_fall_power
|
||||
datasheet.write("{0},{1},".format('write_fall_power_{}'.format(port), write0_power))
|
||||
|
||||
|
||||
for port in self.read_ports:
|
||||
read1_power = np.mean(self.char_port_results[port]["read1_power"])
|
||||
read0_power = np.mean(self.char_port_results[port]["read0_power"])
|
||||
datasheet.write("{0},{1},".format('read_rise_power_{}'.format(port), read1_power))
|
||||
#FIXME: should be read_fall_power
|
||||
datasheet.write("{0},{1},".format('read_fall_power_{}'.format(port), read0_power))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import getpass
|
||||
import subprocess
|
||||
|
||||
|
||||
VERSION = "1.1.15"
|
||||
VERSION = "1.1.18"
|
||||
NAME = "OpenRAM v{}".format(VERSION)
|
||||
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
|
||||
|
||||
|
||||
@@ -369,11 +369,9 @@ class bank(design.design):
|
||||
3 * self.m2_pitch,
|
||||
drc("nwell_to_nwell"))
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
""" Add all the modules using the class loader """
|
||||
|
||||
|
||||
local_array_size = OPTS.local_array_size
|
||||
|
||||
if local_array_size > 0:
|
||||
@@ -705,7 +703,7 @@ class bank(design.design):
|
||||
pitch=self.m3_pitch)
|
||||
|
||||
self.copy_layout_pin(self.port_address_inst[0], "wl_en", self.prefix + "wl_en0")
|
||||
|
||||
|
||||
# Port 1
|
||||
if len(self.all_ports)==2:
|
||||
# The other control bus is routed up to two pitches above the bitcell array
|
||||
|
||||
@@ -121,8 +121,10 @@ class control_logic(design.design):
|
||||
# max_fanout = max(self.num_rows, self.num_cols)
|
||||
|
||||
# wl_en drives every row in the bank
|
||||
# MRG 9/3/2021: Ensure that this is two stages to prevent race conditions with the write driver
|
||||
size_list = [max(int(self.num_rows / 9), 1), max(int(self.num_rows / 3), 1)]
|
||||
self.wl_en_driver = factory.create(module_type="pdriver",
|
||||
fanout=self.num_rows,
|
||||
size_list=size_list,
|
||||
height=dff_height)
|
||||
self.add_mod(self.wl_en_driver)
|
||||
|
||||
@@ -348,7 +350,7 @@ class control_logic(design.design):
|
||||
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()
|
||||
@@ -391,7 +393,7 @@ class control_logic(design.design):
|
||||
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
|
||||
|
||||
|
||||
@@ -302,4 +302,3 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||
Clears the bit exclusions
|
||||
"""
|
||||
self.bitcell_array.clear_exclude_bits()
|
||||
|
||||
|
||||
@@ -43,6 +43,21 @@ class sram_config:
|
||||
|
||||
self.compute_sizes()
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
config_items = ["num_banks",
|
||||
"word_size",
|
||||
"num_words",
|
||||
"words_per_row",
|
||||
"write_size",
|
||||
"num_spare_rows",
|
||||
"num_spare_cols"]
|
||||
str = ""
|
||||
for item in config_items:
|
||||
val = getattr(self, item)
|
||||
str += "{} : {}\n".format(item, val)
|
||||
return str
|
||||
|
||||
def set_local_config(self, module):
|
||||
""" Copy all of the member variables to the given module for convenience """
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class model_delay_test(openram_test):
|
||||
debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data))
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
|
||||
|
||||
d = delay(s.s, tempspice, corner)
|
||||
m = elmore(s.s, tempspice, corner)
|
||||
import tech
|
||||
@@ -77,9 +77,9 @@ class model_delay_test(openram_test):
|
||||
debug.info(1,"Model Delays={}".format(model_delays))
|
||||
|
||||
if OPTS.tech_name == "freepdk45":
|
||||
error_tolerance = 0.25
|
||||
error_tolerance = 0.30
|
||||
elif OPTS.tech_name == "scn4m_subm":
|
||||
error_tolerance = 0.25
|
||||
error_tolerance = 0.30
|
||||
else:
|
||||
self.assertTrue(False) # other techs fail
|
||||
|
||||
|
||||
@@ -60,20 +60,20 @@ class timing_sram_test(openram_test):
|
||||
data.update(port_data[0])
|
||||
|
||||
if OPTS.tech_name == "freepdk45":
|
||||
golden_data = {'delay_hl': [0.24042560000000002],
|
||||
'delay_lh': [0.24042560000000002],
|
||||
'disabled_read0_power': [0.8981647999999998],
|
||||
'disabled_read1_power': [0.9101543999999998],
|
||||
'disabled_write0_power': [0.9270382999999998],
|
||||
'disabled_write1_power': [0.9482969999999998],
|
||||
'leakage_power': 2.9792199999999998,
|
||||
golden_data = {'delay_hl': [0.2314011],
|
||||
'delay_lh': [0.2314011],
|
||||
'disabled_read0_power': [0.173459901],
|
||||
'disabled_read1_power': [0.185612201],
|
||||
'disabled_write0_power': [0.202493001],
|
||||
'disabled_write1_power': [0.224080601],
|
||||
'leakage_power': 0.0017065770000000001,
|
||||
'min_period': 0.938,
|
||||
'read0_power': [1.1107930999999998],
|
||||
'read1_power': [1.1143252999999997],
|
||||
'slew_hl': [0.2800772],
|
||||
'slew_lh': [0.2800772],
|
||||
'write0_power': [1.1667769],
|
||||
'write1_power': [1.0986076999999999]}
|
||||
'read0_power': [0.372276201],
|
||||
'read1_power': [0.37621480100000004],
|
||||
'slew_hl': [0.27947489999999997],
|
||||
'slew_lh': [0.27947489999999997],
|
||||
'write0_power': [0.429895901],
|
||||
'write1_power': [0.383337501]}
|
||||
elif OPTS.tech_name == "scn4m_subm":
|
||||
golden_data = {'delay_hl': [1.884186],
|
||||
'delay_lh': [1.884186],
|
||||
|
||||
@@ -36,6 +36,7 @@ class riscv_func_test(openram_test):
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
OPTS.num_r_ports = 0
|
||||
OPTS.local_array_size = 16
|
||||
globals.setup_bitcell()
|
||||
|
||||
# This is a hack to reload the characterizer __init__ with the spice version
|
||||
|
||||
@@ -16,7 +16,7 @@ from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
# @unittest.skip("SKIPPING 50_riscv_phys_test")
|
||||
#@unittest.skip("SKIPPING 50_riscv_phys_test")
|
||||
class riscv_phys_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
@@ -24,8 +24,15 @@ class riscv_phys_test(openram_test):
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
if OPTS.tech_name == "sky130":
|
||||
num_spare_rows = 1
|
||||
num_spare_cols = 1
|
||||
else:
|
||||
num_spare_rows = 0
|
||||
num_spare_cols = 0
|
||||
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_r_ports = 0
|
||||
OPTS.num_w_ports = 0
|
||||
OPTS.local_array_size = 16
|
||||
globals.setup_bitcell()
|
||||
@@ -36,9 +43,9 @@ class riscv_phys_test(openram_test):
|
||||
write_size=8,
|
||||
num_words=32,
|
||||
num_banks=1,
|
||||
num_spare_rows=1,
|
||||
num_spare_cols=1)
|
||||
c.words_per_row=2
|
||||
num_spare_cols=num_spare_cols,
|
||||
num_spare_rows=num_spare_rows)
|
||||
c.words_per_row=1
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram "
|
||||
"with {} bit words, {} words, {} words per "
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# All rights reserved.
|
||||
#
|
||||
from globals import OPTS
|
||||
word_size = 1
|
||||
word_size = 2
|
||||
num_words = 16
|
||||
|
||||
tech_name = OPTS.tech_name
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# All rights reserved.
|
||||
#
|
||||
from globals import OPTS
|
||||
word_size = 1
|
||||
word_size = 2
|
||||
num_words = 16
|
||||
|
||||
tech_name = OPTS.tech_name
|
||||
|
||||
@@ -32,14 +32,16 @@ if not OPTS.check_lvsdrc:
|
||||
# OPTS.magic_exe = None
|
||||
else:
|
||||
debug.info(1, "Finding DRC/LVS/PEX tools.")
|
||||
OPTS.drc_exe = get_tool("DRC", ["calibre", "assura", "magic"], drc_name)
|
||||
OPTS.lvs_exe = get_tool("LVS", ["calibre", "assura", "netgen"], lvs_name)
|
||||
OPTS.pex_exe = get_tool("PEX", ["calibre", "magic"], pex_name)
|
||||
OPTS.drc_exe = get_tool("DRC", ["klayout", "magic", "calibre", "assura"], drc_name)
|
||||
OPTS.lvs_exe = get_tool("LVS", ["klayout", "netgen", "calibre", "assura"], lvs_name)
|
||||
OPTS.pex_exe = get_tool("PEX", ["klayout", "magic", "calibre"], pex_name)
|
||||
# if OPTS.tech_name == "sky130":
|
||||
# OPTS.magic_exe = get_tool("GDS", ["magic"])
|
||||
|
||||
if not OPTS.drc_exe:
|
||||
from .none import run_drc, print_drc_stats, write_drc_script
|
||||
elif "klayout"==OPTS.drc_exe[0]:
|
||||
from .klayout import run_drc, print_drc_stats, write_drc_script
|
||||
elif "calibre"==OPTS.drc_exe[0]:
|
||||
from .calibre import run_drc, print_drc_stats, write_drc_script
|
||||
elif "assura"==OPTS.drc_exe[0]:
|
||||
@@ -52,6 +54,8 @@ else:
|
||||
|
||||
if not OPTS.lvs_exe:
|
||||
from .none import run_lvs, print_lvs_stats, write_lvs_script
|
||||
elif "klayout"==OPTS.lvs_exe[0]:
|
||||
from .klayout import run_lvs, print_lvs_stats, write_lvs_script
|
||||
elif "calibre"==OPTS.lvs_exe[0]:
|
||||
from .calibre import run_lvs, print_lvs_stats, write_lvs_script
|
||||
elif "assura"==OPTS.lvs_exe[0]:
|
||||
@@ -65,6 +69,8 @@ else:
|
||||
|
||||
if not OPTS.pex_exe:
|
||||
from .none import run_pex, print_pex_stats
|
||||
elif "klayout"==OPTS.pex_exe[0]:
|
||||
from .klayout import run_pex, print_pex_stats
|
||||
elif "calibre"==OPTS.pex_exe[0]:
|
||||
from .calibre import run_pex, print_pex_stats
|
||||
elif "magic"==OPTS.pex_exe[0]:
|
||||
@@ -78,4 +84,3 @@ else:
|
||||
# from .magic import filter_gds
|
||||
# else:
|
||||
# debug.warning("Did not find Magic.")
|
||||
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
# 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.
|
||||
#
|
||||
"""
|
||||
This is a DRC/LVS/PEX interface file for klayout.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import debug
|
||||
from globals import OPTS
|
||||
from run_script import *
|
||||
|
||||
# Keep track of statistics
|
||||
num_drc_runs = 0
|
||||
num_lvs_runs = 0
|
||||
num_pex_runs = 0
|
||||
|
||||
|
||||
def write_drc_script(cell_name, gds_name, extract, final_verification, output_path, sp_name=None):
|
||||
"""
|
||||
Write a klayout script to perform DRC and optionally extraction.
|
||||
"""
|
||||
global OPTS
|
||||
|
||||
# DRC:
|
||||
# klayout -b -r drc_FreePDK45.lydrc -rd input=sram_8_256_freepdk45.gds -rd topcell=sram_8_256_freepdk45 -rd output=drc_FreePDK45.lyrdb
|
||||
|
||||
# Copy .lydrc file into the output directory
|
||||
full_drc_file = OPTS.openram_tech + "tech/{}.lydrc".format(OPTS.tech_name)
|
||||
drc_file = os.path.basename(full_drc_file)
|
||||
if os.path.exists(full_drc_file):
|
||||
shutil.copy(full_drc_file, output_path)
|
||||
else:
|
||||
debug.warning("Could not locate file: {}".format(full_drc_file))
|
||||
|
||||
# Create an auxiliary script to run calibre with the runset
|
||||
run_file = output_path + "run_drc.sh"
|
||||
f = open(run_file, "w")
|
||||
f.write("#!/bin/sh\n")
|
||||
cmd = "{0} -b -r {1} -rd input={2} -rd topcell={3} -rd output={3}.drc.report".format(OPTS.drc_exe[1],
|
||||
drc_file,
|
||||
gds_name,
|
||||
cell_name)
|
||||
|
||||
f.write(cmd)
|
||||
f.write("\n")
|
||||
f.close()
|
||||
os.system("chmod u+x {}".format(run_file))
|
||||
|
||||
|
||||
def run_drc(cell_name, gds_name, sp_name=None, extract=True, final_verification=False):
|
||||
"""Run DRC check on a cell which is implemented in gds_name."""
|
||||
|
||||
global num_drc_runs
|
||||
num_drc_runs += 1
|
||||
|
||||
write_drc_script(cell_name, gds_name, extract, final_verification, OPTS.openram_temp, sp_name=sp_name)
|
||||
|
||||
(outfile, errfile, resultsfile) = run_script(cell_name, "drc")
|
||||
|
||||
# Check the result for these lines in the summary:
|
||||
# Total DRC errors found: 0
|
||||
# The count is shown in this format:
|
||||
# Cell replica_cell_6t has 3 error tiles.
|
||||
# Cell tri_gate_array has 8 error tiles.
|
||||
# etc.
|
||||
try:
|
||||
f = open(resultsfile, "r")
|
||||
except FileNotFoundError:
|
||||
debug.error("Unable to load DRC results file from {}. Is klayout set up?".format(resultsfile), 1)
|
||||
|
||||
results = f.readlines()
|
||||
f.close()
|
||||
errors=len([x for x in results if "<visited>" in x])
|
||||
|
||||
# always display this summary
|
||||
result_str = "DRC Errors {0}\t{1}".format(cell_name, errors)
|
||||
if errors > 0:
|
||||
debug.warning(result_str)
|
||||
else:
|
||||
debug.info(1, result_str)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, output_path=None):
|
||||
""" Write a klayout script to perform LVS. """
|
||||
|
||||
# LVS:
|
||||
# klayout -b -rd input=sram_32_2048_freepdk45.gds -rd report=my_report.lyrdb -rd schematic=sram_32_2048_freepdk45.sp -rd target_netlist=sram_32_2048_freepdk45_extracted.cir -r lvs_freepdk45.lvs
|
||||
|
||||
global OPTS
|
||||
|
||||
if not output_path:
|
||||
output_path = OPTS.openram_temp
|
||||
|
||||
# Copy .lylvs file into the output directory
|
||||
full_lvs_file = OPTS.openram_tech + "tech/{}.lylvs".format(OPTS.tech_name)
|
||||
lvs_file = os.path.basename(full_lvs_file)
|
||||
|
||||
if os.path.exists(full_lvs_file):
|
||||
shutil.copy(full_lvs_file, output_path)
|
||||
else:
|
||||
debug.warning("Could not locate file: {}".format(full_lvs_file))
|
||||
|
||||
run_file = output_path + "/run_lvs.sh"
|
||||
f = open(run_file, "w")
|
||||
f.write("#!/bin/sh\n")
|
||||
cmd = "{0} -b -r {1} -rd input={2} -rd report={4}.lvs.report -rd schematic={3} -rd target_netlist={4}.spice".format(OPTS.lvs_exe[1],
|
||||
lvs_file,
|
||||
gds_name,
|
||||
sp_name,
|
||||
cell_name)
|
||||
f.write(cmd)
|
||||
f.write("\n")
|
||||
f.close()
|
||||
os.system("chmod u+x {}".format(run_file))
|
||||
|
||||
|
||||
def run_lvs(cell_name, gds_name, sp_name, final_verification=False, output_path=None):
|
||||
"""Run LVS check on a given top-level name which is
|
||||
implemented in gds_name and sp_name. Final verification will
|
||||
ensure that there are no remaining virtual conections. """
|
||||
|
||||
global num_lvs_runs
|
||||
num_lvs_runs += 1
|
||||
|
||||
if not output_path:
|
||||
output_path = OPTS.openram_temp
|
||||
|
||||
write_lvs_script(cell_name, gds_name, sp_name, final_verification)
|
||||
|
||||
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
|
||||
|
||||
# check the result for these lines in the summary:
|
||||
try:
|
||||
f = open(outfile, "r")
|
||||
except FileNotFoundError:
|
||||
debug.error("Unable to load LVS results from {}".format(outfile), 1)
|
||||
|
||||
results = f.readlines()
|
||||
f.close()
|
||||
# Look for CONGRATULATIONS or ERROR
|
||||
congrats = len([x for x in results if "CONGRATULATIONS" in x])
|
||||
total_errors = len([x for x in results if "ERROR" in x])
|
||||
|
||||
if total_errors>0:
|
||||
debug.error("{0}\tLVS mismatch (results in {1})".format(cell_name, resultsfile))
|
||||
elif congrats>0:
|
||||
debug.info(1, "{0}\tLVS matches".format(cell_name))
|
||||
else:
|
||||
debug.info(1, "{0}\tNo LVS result".format(cell_name))
|
||||
total_errors += 1
|
||||
|
||||
return total_errors
|
||||
|
||||
|
||||
def run_pex(name, gds_name, sp_name, output=None, final_verification=False, output_path=None):
|
||||
"""Run pex on a given top-level name which is
|
||||
implemented in gds_name and sp_name. """
|
||||
|
||||
debug.error("PEX not implemented", -1)
|
||||
|
||||
global num_pex_runs
|
||||
num_pex_runs += 1
|
||||
|
||||
if not output_path:
|
||||
output_path = OPTS.openram_temp
|
||||
|
||||
os.chdir(output_path)
|
||||
|
||||
if not output_path:
|
||||
output_path = OPTS.openram_temp
|
||||
|
||||
if output == None:
|
||||
output = name + ".pex.netlist"
|
||||
|
||||
# check if lvs report has been done
|
||||
# if not run drc and lvs
|
||||
if not os.path.isfile(name + ".lvs.report"):
|
||||
run_drc(name, gds_name)
|
||||
run_lvs(name, gds_name, sp_name)
|
||||
|
||||
# # pex_fix did run the pex using a script while dev orignial method
|
||||
# # use batch mode.
|
||||
# # the dev old code using batch mode does not run and is split into functions
|
||||
# pex_runset = write_script_pex_rule(gds_name, name, sp_name, output)
|
||||
|
||||
# errfile = "{0}{1}.pex.err".format(output_path, name)
|
||||
# outfile = "{0}{1}.pex.out".format(output_path, name)
|
||||
|
||||
# script_cmd = "{0} 2> {1} 1> {2}".format(pex_runset,
|
||||
# errfile,
|
||||
# outfile)
|
||||
# cmd = script_cmd
|
||||
# debug.info(2, cmd)
|
||||
# os.system(cmd)
|
||||
|
||||
# # rename technology models
|
||||
# pex_nelist = open(output, 'r')
|
||||
# s = pex_nelist.read()
|
||||
# pex_nelist.close()
|
||||
# s = s.replace('pfet', 'p')
|
||||
# s = s.replace('nfet', 'n')
|
||||
# f = open(output, 'w')
|
||||
# f.write(s)
|
||||
# f.close()
|
||||
|
||||
# # also check the output file
|
||||
# f = open(outfile, "r")
|
||||
# results = f.readlines()
|
||||
# f.close()
|
||||
# out_errors = find_error(results)
|
||||
# debug.check(os.path.isfile(output), "Couldn't find PEX extracted output.")
|
||||
|
||||
# correct_port(name, output, sp_name)
|
||||
return out_errors
|
||||
|
||||
|
||||
def write_batch_pex_rule(gds_name, name, sp_name, output):
|
||||
"""
|
||||
"""
|
||||
# write the runset file
|
||||
file = OPTS.openram_temp + "pex_runset"
|
||||
f = open(file, "w")
|
||||
|
||||
f.close()
|
||||
return file
|
||||
|
||||
|
||||
def write_script_pex_rule(gds_name, cell_name, sp_name, output):
|
||||
global OPTS
|
||||
run_file = OPTS.openram_temp + "run_pex.sh"
|
||||
f = open(run_file, "w")
|
||||
f.write("#!/bin/sh\n")
|
||||
f.write('export OPENRAM_TECH="{}"\n'.format(os.environ['OPENRAM_TECH']))
|
||||
f.write('echo "$(date): Starting PEX using Klayout {}"\n'.format(OPTS.drc_exe[1]))
|
||||
|
||||
f.write("retcode=$?\n")
|
||||
f.write("mv {0}.spice {1}\n".format(cell_name, output))
|
||||
f.write('echo "$(date): Finished PEX using Klayout {}"\n'.format(OPTS.drc_exe[1]))
|
||||
f.write("exit $retcode\n")
|
||||
|
||||
f.close()
|
||||
os.system("chmod u+x {}".format(run_file))
|
||||
return run_file
|
||||
|
||||
|
||||
def print_drc_stats():
|
||||
debug.info(1, "DRC runs: {0}".format(num_drc_runs))
|
||||
|
||||
|
||||
def print_lvs_stats():
|
||||
debug.info(1, "LVS runs: {0}".format(num_lvs_runs))
|
||||
|
||||
|
||||
def print_pex_stats():
|
||||
debug.info(1, "PEX runs: {0}".format(num_pex_runs))
|
||||
+24
-23
@@ -71,15 +71,15 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
|
||||
global OPTS
|
||||
|
||||
# Copy .magicrc file into the output directory
|
||||
magic_file = os.environ.get('OPENRAM_MAGICRC', None)
|
||||
if not magic_file:
|
||||
magic_file = OPTS.openram_tech + "tech/.magicrc"
|
||||
full_magic_file = os.environ.get('OPENRAM_MAGICRC', None)
|
||||
if not full_magic_file:
|
||||
full_magic_file = OPTS.openram_tech + "tech/.magicrc"
|
||||
|
||||
if os.path.exists(magic_file):
|
||||
shutil.copy(magic_file, output_path + "/.magicrc")
|
||||
if os.path.exists(full_magic_file):
|
||||
shutil.copy(full_magic_file, output_path + "/.magicrc")
|
||||
else:
|
||||
debug.warning("Could not locate .magicrc file: {}".format(magic_file))
|
||||
|
||||
debug.warning("Could not locate .magicrc file: {}".format(full_magic_file))
|
||||
|
||||
run_file = output_path + "run_ext.sh"
|
||||
f = open(run_file, "w")
|
||||
f.write("#!/bin/sh\n")
|
||||
@@ -96,7 +96,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
|
||||
f.write("gds warning default\n")
|
||||
# These two options are temporarily disabled until Tim fixes a bug in magic related
|
||||
# to flattening channel routes and vias (hierarchy with no devices in it). Otherwise,
|
||||
# they appear to be disconnected.
|
||||
# they appear to be disconnected.
|
||||
f.write("gds flatten true\n")
|
||||
f.write("gds ordering true\n")
|
||||
f.write("gds readonly true\n")
|
||||
@@ -106,7 +106,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
|
||||
f.write('puts "Finished loading cell {}"\n'.format(cell_name))
|
||||
f.write("cellname delete \\(UNNAMED\\)\n")
|
||||
f.write("writeall force\n")
|
||||
|
||||
|
||||
# Extract
|
||||
if not sp_name:
|
||||
f.write("port makeall\n")
|
||||
@@ -142,7 +142,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
|
||||
f.write(pre + "ext2spice format ngspice\n")
|
||||
f.write(pre + "ext2spice {}\n".format(cell_name))
|
||||
f.write('puts "Finished ext2spice"\n')
|
||||
|
||||
|
||||
f.write("quit -noprompt\n")
|
||||
f.write("EOF\n")
|
||||
f.write("magic_retcode=$?\n")
|
||||
@@ -165,7 +165,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
|
||||
mag_file = OPTS.openram_tech + "maglef_lib/" + blackbox_cell_name + ".mag"
|
||||
debug.check(os.path.isfile(mag_file), "Could not find blackbox cell {}".format(mag_file))
|
||||
f.write('cp {0} .\n'.format(mag_file))
|
||||
|
||||
|
||||
f.write('echo "$(date): Starting DRC using Magic {}"\n'.format(OPTS.drc_exe[1]))
|
||||
f.write('\n')
|
||||
f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1]))
|
||||
@@ -193,7 +193,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
|
||||
|
||||
f.close()
|
||||
os.system("chmod u+x {}".format(run_file))
|
||||
|
||||
|
||||
|
||||
def run_drc(cell_name, gds_name, sp_name=None, extract=True, final_verification=False):
|
||||
"""Run DRC check on a cell which is implemented in gds_name."""
|
||||
@@ -202,9 +202,9 @@ def run_drc(cell_name, gds_name, sp_name=None, extract=True, final_verification=
|
||||
num_drc_runs += 1
|
||||
|
||||
write_drc_script(cell_name, gds_name, extract, final_verification, OPTS.openram_temp, sp_name=sp_name)
|
||||
|
||||
|
||||
(outfile, errfile, resultsfile) = run_script(cell_name, "ext")
|
||||
|
||||
|
||||
(outfile, errfile, resultsfile) = run_script(cell_name, "drc")
|
||||
|
||||
# Check the result for these lines in the summary:
|
||||
@@ -252,13 +252,14 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out
|
||||
output_path = OPTS.openram_temp
|
||||
|
||||
# Copy setup.tcl file into the output directory
|
||||
setup_file = os.environ.get('OPENRAM_NETGENRC', None)
|
||||
if not setup_file:
|
||||
setup_file = OPTS.openram_tech + "tech/setup.tcl"
|
||||
|
||||
if os.path.exists(setup_file):
|
||||
full_setup_file = os.environ.get('OPENRAM_NETGENRC', None)
|
||||
if not full_setup_file:
|
||||
full_setup_file = OPTS.openram_tech + "tech/setup.tcl"
|
||||
setup_file = os.path.basename(full_setup_file)
|
||||
|
||||
if os.path.exists(full_setup_file):
|
||||
# Copy setup.tcl file into temp dir
|
||||
shutil.copy(setup_file, output_path)
|
||||
shutil.copy(full_setup_file, output_path)
|
||||
else:
|
||||
setup_file = 'nosetup'
|
||||
|
||||
@@ -290,7 +291,7 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False, output_path=
|
||||
|
||||
if not output_path:
|
||||
output_path = OPTS.openram_temp
|
||||
|
||||
|
||||
write_lvs_script(cell_name, gds_name, sp_name, final_verification)
|
||||
|
||||
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
|
||||
@@ -358,10 +359,10 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False, outp
|
||||
|
||||
global num_pex_runs
|
||||
num_pex_runs += 1
|
||||
|
||||
|
||||
if not output_path:
|
||||
output_path = OPTS.openram_temp
|
||||
|
||||
|
||||
os.chdir(output_path)
|
||||
|
||||
if not output_path:
|
||||
|
||||
Reference in New Issue
Block a user