[syn] Add testsynthesis and sweep scripts for experiments
Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/hw/syn/tools/dc/at-plot.py b/hw/syn/tools/dc/at-plot.py
new file mode 100755
index 0000000..4b12697
--- /dev/null
+++ b/hw/syn/tools/dc/at-plot.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python3
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Note: this script relies on numpy and matplotlib, which both currently are
+# not standard python requirements of OpenTitan. If you wish to use this script,
+# make sure you install them with "pip3 install --user numpy matplotlib"
+
+import argparse
+import re
+import sys
+from pathlib import Path
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+USAGE = """
+This script parses reports generated with the sweep.tcl DC synthesis
+script and creates an AT-plot with that data.
+
+usage: ./at-plot.py ./REPORTS/<dut1_basename> ./REPORTS/<dut2_basename> ...
+
+In order to get convert area into gate equivalents (GE) the size of a GE
+can be supplied using the -g switch.
+
+The script currently supports plotting up to 10 different result series in
+one plot.
+"""
+
+
+def main():
+ parser = argparse.ArgumentParser(prog="")
+ parser.add_argument('result_series',
+ type=str,
+ nargs='+',
+ help="Path and basename of result series.")
+
+ parser.add_argument(
+ '-g',
+ '--gate_equivalent',
+ type=float,
+ default=1.0,
+ help="Gate equivalent in square micrometre. Defaults to 1.0.")
+ parser.add_argument('-o',
+ '--output',
+ type=str,
+ default="at-plot.png",
+ help='Filename of at plot. Defaults to "at-plot.png"')
+ parser.add_argument('-t',
+ '--title',
+ type=str,
+ default="AT Plot",
+ help='Title of AT plot.')
+ parser.add_argument('--semilogy',
+ action='store_true',
+ help='semilogy plot.')
+
+ l = 0
+ labels = []
+
+ # line style and color setup
+ linestyles = ['--', '--', '--', '--', '--', '--', '--', '--', '--', '--']
+ markers = ['^', 'd', '.', '*', '>', '<', 'v', 's', 'h', 'o']
+ cmap = plt.get_cmap("Set1")
+
+ args = parser.parse_args()
+
+ if len(args.result_series) > 10:
+ print("Only up to ten result series can be plottet at once")
+ sys.exit(1)
+
+ print("Gate Equivalent is %.2f square micrometre" % args.gate_equivalent)
+
+ for basename in args.result_series:
+ report_filebase = Path(basename)
+
+ print("%s" % (report_filebase.name))
+
+ results = np.array([[]])
+
+ for rpt_area in report_filebase.parent.rglob(report_filebase.name +
+ '_*_area.rpt'):
+ tmp_period = Path(rpt_area).name.split('_area')
+ tmp_period = tmp_period[0].split(report_filebase.name + '_')
+ row = np.array([[float(tmp_period[1]), 0.0, 0.0]])
+ if np.any(results):
+ results = np.append(results, row, axis=0)
+ else:
+ results = row
+ try:
+ with open(rpt_area, 'r') as f:
+ full_file = f.read()
+ tmp_area = re.findall(r"^Total cell area:.*",
+ full_file,
+ flags=re.MULTILINE)
+ if tmp_area:
+ tmp_area = tmp_area[0].split("Total cell area:")
+ results[-1, 1] = float(
+ tmp_area[1]) / args.gate_equivalent
+ else:
+ print("Error, could not find total cell area in %s" %
+ (report_area))
+ sys.exit(1)
+
+ except IOError as e:
+ print(str(e))
+ sys.exit(1)
+
+ try:
+ rpt_timing = rpt_area.parent.joinpath(
+ rpt_area.name.replace('_area', '_timing'))
+ with open(rpt_timing, 'r') as f:
+ full_file = f.read()
+ tmp_slack = re.findall(r"^ slack \(.*",
+ full_file,
+ flags=re.MULTILINE)
+ if tmp_slack:
+ tmp_slack = tmp_slack[0].split(")")
+ # adjust period with slack
+ results[-1, 2] = results[-1][0] - float(tmp_slack[1])
+ else:
+ print("Error, could not find slack in %s" %
+ (rpt_timing))
+ sys.exit(1)
+
+ except IOError as e:
+ print(str(e))
+ sys.exit(1)
+
+ if np.any(results):
+
+ results = results[np.argsort(results[:, 0])]
+
+ if args.gate_equivalent == 1.0:
+ print("constraint [ns], achieved [ns], complexity [GE]")
+ else:
+ print("constraint [ns], achieved [ns], complexity [um^2]")
+
+ for k in range(len(results)):
+ print("%.2f, %.2f, %.2f" %
+ (results[k][0], results[k][2], results[k][1]))
+ if args.semilogy:
+ plt.semilogy(results[:, 2],
+ results[:, 1],
+ color=cmap(l),
+ linestyle=linestyles[l],
+ marker=markers[l],
+ linewidth=1.5,
+ markersize=6,
+ markeredgecolor='k')
+ else:
+ plt.plot(results[:, 2],
+ results[:, 1],
+ color=cmap(l),
+ linestyle=linestyles[l],
+ marker=markers[l],
+ linewidth=1.5,
+ markersize=6,
+ markeredgecolor='k')
+
+ l += 1
+ labels += [report_filebase.name]
+
+ print("Parsed %d result series" % l)
+
+ plt.xlabel('Period [ns]')
+ if args.gate_equivalent == 1.0:
+ plt.ylabel('Complexity [um^2]')
+ else:
+ plt.ylabel('Complexity [GE]')
+ plt.grid(which='both')
+ plt.legend(labels)
+ plt.title(args.title)
+ plt.savefig(args.output)
+ plt.show()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/hw/syn/tools/dc/sweep.tcl b/hw/syn/tools/dc/sweep.tcl
new file mode 100644
index 0000000..fd8a9b3
--- /dev/null
+++ b/hw/syn/tools/dc/sweep.tcl
@@ -0,0 +1,115 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Simple tcl script for DC to do some wire-load-model-based sweep syntheses.
+
+#####################
+## PREPARE FLOW ##
+#####################
+
+source ../../../foundry/syn/dc/setup.tcl
+
+# paths
+set WORKLIB "WORK"
+set REPDIR "REPORTS"
+set DDCDIR "DDC"
+set VLOGDIR "NETLISTS"
+
+exec mkdir -p ${REPDIR} ${DDCDIR} ${VLOGDIR} ${WORKLIB}
+
+# define work lib path
+define_design_lib WORK -path $WORKLIB
+
+######################
+## DESIGN SETUP ##
+######################
+
+set DUT "prim_prince"
+# set DUT "prim_present"
+
+lappend search_path "../../../ip/prim/rtl/"
+set SRC { "../../../ip/prim/rtl/prim_assert.sv" \
+ "../../../ip/prim/rtl/prim_present.sv" \
+ "../../../ip/prim/rtl/prim_prince.sv" \
+ }
+
+
+# additional defines
+set DEFINE ""
+
+# additional parameters
+set PARAMS ""
+
+set CLK_PIN clk_i
+set RST_PIN rst_ni
+
+set TCK_SWEEP { 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 }
+set IN_DEL 0.0
+set OUT_DEL 0.0
+
+###########################
+## SWEEP ##
+###########################
+
+foreach TCK $TCK_SWEEP {
+ ###########################
+ ## ELABORATE DESIGN ##
+ ###########################
+
+ # delete previous designs.
+ remove_design -designs
+ sh rm -rf $WORKLIB/*
+
+ analyze -define ${DEFINE} -format sv ${SRC} > "${REPDIR}/${DUT}_${TCK}_analyze.rpt"
+ elaborate ${DUT} -parameters ${PARAMS} > "${REPDIR}/${DUT}_${TCK}_elab.rpt"
+ link > "${REPDIR}/${DUT}_${TCK}_link.rpt"
+ check_design > "${REPDIR}/${DUT}_${TCK}_check.rpt"
+
+ write_file -format ddc -hierarchy -output "${DDCDIR}/${DUT}_${TCK}_elab.ddc"
+ write_file -format verilog -hierarchy -output "${DDCDIR}/${DUT}_${TCK}_elab.v"
+
+ ###########################
+ ## APPLY CONSTRAINTS ##
+ ###########################
+
+ # timing constraint in ns
+ # set timing to 250 MHz
+ set DELAY ${TCK}
+
+ create_clock ${CLK_PIN} -period ${TCK}
+
+ set_ideal_network ${CLK_PIN}
+ set_ideal_network ${RST_PIN}
+
+ set_max_delay ${DELAY} -from [all_inputs] -to [all_outputs]
+ set_input_delay ${IN_DEL} [remove_from_collection [all_inputs] {${CLK_PIN}}] -clock ${CLK_PIN}
+ set_output_delay ${OUT_DEL} [all_outputs] -clock ${CLK_PIN}
+
+ set_driving_cell -no_design_rule -lib_cell ${driving_cell} -pin X [all_inputs]
+ set_load [load_of ${load_lib}/${load_cell}/A] [all_outputs]
+
+ ######################
+ ## MAP DESIGN ##
+ ######################
+
+ compile_ultra -gate_clock -scan > "${REPDIR}/${DUT}_${TCK}_compile.rpt"
+
+ #################
+ ## REPORTS ##
+ #################
+
+ report_timing -nosplit > "${REPDIR}/${DUT}_${TCK}_timing.rpt"
+ report_area -hier -nosplit > "${REPDIR}/${DUT}_${TCK}_area.rpt"
+ report_constraints -all_violators > "${REPDIR}/${DUT}_${TCK}_constraints.rpt"
+
+ report_timing -nosplit -nworst 1000 -input -net -trans -cap > "${REPDIR}/${DUT}_${TCK}_timing_long.rpt"
+
+ #################
+ ## NETLIST ##
+ #################
+
+ change_names -rules verilog -hierarchy
+ write_file -format ddc -hierarchy -output "${DDCDIR}/${DUT}_${TCK}_mapped.ddc"
+ write_file -format verilog -hierarchy -output "${VLOGDIR}/${DUT}_${TCK}_mapped.v"
+}
diff --git a/hw/syn/tools/dc/testsynth.tcl b/hw/syn/tools/dc/testsynth.tcl
new file mode 100644
index 0000000..78ae6b6
--- /dev/null
+++ b/hw/syn/tools/dc/testsynth.tcl
@@ -0,0 +1,113 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Simple tcl script for DC to do some wire-load-model-based test syntheses.
+
+#####################
+## PREPARE FLOW ##
+#####################
+
+# tool setup
+source ../../../foundry/syn/dc/setup.tcl
+
+# paths
+set WORKLIB "WORK"
+set REPDIR "REPORTS"
+set DDCDIR "DDC"
+set VLOGDIR "NETLISTS"
+
+exec mkdir -p ${REPDIR} ${DDCDIR} ${VLOGDIR} ${WORKLIB}
+
+# define work lib path
+define_design_lib WORK -path $WORKLIB
+
+#######################
+## DESIGN SOURCES ###
+#######################
+
+set DUT "rv_plic_target"
+
+lappend search_path "../../../ip/prim/rtl/"
+set SRC { "../../../ip/rv_plic/rtl/rv_plic_target.sv" }
+
+# additional defines
+set DEFINE ""
+
+# additional parameters
+set PARAMS "N_SOURCE=128"
+
+###########################
+## ELABORATE DESIGN ##
+###########################
+
+# delete previous designs.
+remove_design -designs
+sh rm -rf $WORKLIB/*
+
+analyze -define ${DEFINE} -format sv ${SRC} > "${REPDIR}/${DUT}_analyze.rpt"
+elaborate ${DUT} -parameters ${PARAMS} > "${REPDIR}/${DUT}_elab.rpt"
+link > "${REPDIR}/${DUT}_link.rpt"
+check_design > "${REPDIR}/${DUT}_check.rpt"
+
+write_file -format ddc -hierarchy -output "${DDCDIR}/${DUT}_elab.ddc"
+write_file -format verilog -hierarchy -output "${DDCDIR}/${DUT}_elab.v"
+
+###########################
+## APPLY CONSTRAINTS ##
+###########################
+
+set CLK_PIN clk_i
+set RST_PIN rst_ni
+
+# timing constraint in ns
+# set timing to 250 MHz
+set TCK 4.0
+set IN_DEL 1.0
+set OUT_DEL 1.0
+set DELAY ${TCK}
+
+create_clock ${CLK_PIN} -period ${TCK}
+
+set_ideal_network ${CLK_PIN}
+set_ideal_network ${RST_PIN}
+
+set_max_delay ${DELAY} -from [all_inputs] -to [all_outputs]
+set_input_delay ${IN_DEL} [remove_from_collection [all_inputs] {${CLK_PIN}}] -clock ${CLK_PIN}
+set_output_delay ${OUT_DEL} [all_outputs] -clock ${CLK_PIN}
+
+set_driving_cell -no_design_rule -lib_cell ${driving_cell} -pin X [all_inputs]
+set_load [load_of ${load_lib}/${load_cell}/A] [all_outputs]
+
+# set a nonzero critical range to be able to spot the violating paths better
+# in the report
+# set_critical_range 0.2 ${DUT}
+
+######################
+## MAP DESIGN ##
+######################
+
+compile_ultra -gate_clock -scan > "${REPDIR}/${DUT}_compile.rpt"
+
+# preserve hierarchy for reports
+#compile_ultra -gate_clock -scan -no_autoungroup > "${REPDIR}/${DUT}_compile.rpt"
+
+#################
+## REPORTS ##
+#################
+
+report_clocks > "${REPDIR}/${DUT}_clocks.rpt"
+report_timing -nosplit -slack_lesser_than 0.0 > "${REPDIR}/${DUT}_timing.rpt"
+report_area -hier -nosplit > "${REPDIR}/${DUT}_area.rpt"
+report_power -hier -nosplit > "${REPDIR}/${DUT}_power.rpt"
+report_constraints -all_violators > "${REPDIR}/${DUT}_constraints.rpt"
+
+report_timing -nosplit -nworst 1000 -input -net -trans -cap > "${REPDIR}/${DUT}_timing_long.rpt"
+
+#################
+## NETLIST ##
+#################
+
+# change_names -rules verilog -hierarchy
+write_file -format ddc -hierarchy -output "${DDCDIR}/${DUT}_mapped.ddc"
+write_file -format verilog -hierarchy -output "${VLOGDIR}/${DUT}_mapped.v"