Generate Complete Matcha Area rpt with DC using OT dvsim tools
Generate Complete Matcha Area rpt with DC using OT dvsim tools
Scripts were written to be compatible with edacloud environment
This CL should be considered an intermediate CL to get the flow in a
state that is running and is not the final state of files in the syn
flow and power flow. Consider evaluating the commented code in
run-syn.tcl to determine the best path forward for constraining the
design.
Files below may not be required at this time, but are part of OT syn
flow and depending on settings used for syn (set in hjson files) they
can be required for syn.
`hw/syn/tools/dc/<filename>`
- gtech-constraints.sdc :: gtech appear to be google placeholder libraries and
constraints that seem to allow for quick and dirty synthesis. Not a
lot of details publicly available but may be something worth looking
at at some point. Possibly these are used before a technology is
chosen, for easier 1-1 comparison of projects in different
technologies, or or for faster syn.
- gtech-setup.tcl
- sweep.tcl :: Script to do wire-load-model-based sweep syntheses
- at-plot.py :: Parse reports from sweep.tcl
- testsynth.tcl :: Script to do wire-load-model-based test synthesis
Change-Id: I6df1f2fd0433e191a603803dbb1f89f83748b565
diff --git a/hw/data/common_project_cfg.hjson b/hw/data/common_project_cfg.hjson
new file mode 100644
index 0000000..6d0edb4
--- /dev/null
+++ b/hw/data/common_project_cfg.hjson
@@ -0,0 +1,46 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+{
+ project: opentitan
+ repo_server: "github.com/lowrisc/opentitan"
+ doc_server: docs.opentitan.org
+ results_server: reports.opentitan.org
+
+ // Default directory structure for the output
+ scratch_base_path: "{scratch_root}/{branch}"
+ scratch_path: "{scratch_base_path}/{dut}-{flow}-{tool}"
+
+ // Common data structure
+ build_pass_patterns: []
+ // TODO: Add back FuseSoC fail pattern after
+ // https://github.com/lowRISC/opentitan/issues/7348 is resolved.
+ build_fail_patterns: []
+
+ exports: [
+ { SCRATCH_PATH: "{scratch_path}" },
+ { proj_root: "{proj_root}" }
+ ]
+
+ // Results server stuff - indicate what command to use to copy over the results.
+ // Workaround for gsutil to fall back to using python2.7.
+ results_server_prefix: "gs://"
+ results_server_cmd: "/usr/bin/gsutil"
+ results_html_name: "report.html"
+
+ // If defined, this is printed into the results md files
+ revision: '''{eval_cmd}
+ COMMIT_L=`git rev-parse HEAD`; \
+ COMMIT_S=`git rev-parse --short HEAD`; \
+ REV_STR="GitHub Revision: [\`$COMMIT_S\`](https://{repo_server}/tree/$COMMIT_L)"; \
+ printf "$REV_STR"; \
+ if [ -d "{proj_root}/hw/foundry" ]; then \
+ COMMIT_FOUNDRY_S=`git -C {proj_root}/hw/foundry rev-parse --short HEAD`; \
+ REV_STR_FOUNDRY="Foundry Revision: \`$COMMIT_FOUNDRY_S\`"; \
+ printf "<br>$REV_STR_FOUNDRY"; \
+ fi
+ '''
+
+ // The current design level
+ design_level: "ip"
+}
diff --git a/hw/syn/tools/dc/.gitignore b/hw/syn/tools/dc/.gitignore
new file mode 100644
index 0000000..d22bf8a
--- /dev/null
+++ b/hw/syn/tools/dc/.gitignore
@@ -0,0 +1,9 @@
+DDC
+NETLISTS
+REPORTS*
+WORK
+alib*
+*.png
+*.log
+*.rpt
+*.txt
diff --git a/hw/syn/tools/dc/internal_build/README.md b/hw/syn/tools/dc/internal_build/README.md
new file mode 100644
index 0000000..cdfb359
--- /dev/null
+++ b/hw/syn/tools/dc/internal_build/README.md
@@ -0,0 +1,11 @@
+Scripts are based on kelvin/internal scripts.
+
+Run the following to copy necessary files for syn and dynamic power
+with static switching.
+```
+hw/matcha/hw/syn/tools/dc/internal_build/prep_matcha_syn_workspace.sh
+```
+
+While the scripts in this dir run, the structure below run-syn.tcl needs
+to be reworked with more accurate constraints, and to follow a better
+code practices.
diff --git a/hw/syn/tools/dc/internal_build/prep_matcha_syn_workspace.sh b/hw/syn/tools/dc/internal_build/prep_matcha_syn_workspace.sh
new file mode 100755
index 0000000..9e49ad5
--- /dev/null
+++ b/hw/syn/tools/dc/internal_build/prep_matcha_syn_workspace.sh
@@ -0,0 +1,11 @@
+# Script should copy required files for syn
+# Run this before running syn_matcha_dc.txtpb
+
+# For now use ${ROOTDIR}/internal to store these scripts
+
+INTERNAL_BUILD_DIR="$(dirname "$(realpath "$0")")"
+
+cp -r $ROOTDIR/hw/kelvin/internal/syn/libs $INTERNAL_BUILD_DIR
+cp $ROOTDIR/internal/matcha_syn_scripts/* $INTERNAL_BUILD_DIR
+
+echo "$INTERNAL_BUILD_DIR"
\ No newline at end of file
diff --git a/hw/syn/tools/dc/parse-syn-report.py b/hw/syn/tools/dc/parse-syn-report.py
new file mode 100755
index 0000000..c2cad48
--- /dev/null
+++ b/hw/syn/tools/dc/parse-syn-report.py
@@ -0,0 +1,636 @@
+#!/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
+r"""Parses lint report and dump filtered messages in hjson format.
+"""
+import argparse
+import re
+import sys
+from pathlib import Path
+
+import hjson
+
+# this allows both scientific and fixed point numbers
+FP_NUMBER = r"[-+]?\d+\.\d+[Ee]?[-+]?\d*"
+# fp relative error threshold for report checksums
+CROSSCHECK_REL_TOL = 0.001
+
+
+def _match_fp_number(full_file, patterns):
+ """Extract numbers from patterns in full_file (a string)
+
+ patterns is a list of pairs, (key, pattern). Each pattern should be a
+ regular expression with exactly one capture group. Any match for group will
+ be parsed as a float.
+
+ Returns a pair (nums, errs) where nums is a dictionary keyed by the keys in
+ patterns. The value at K is a list of floats matching patterns[K] if there
+ was more than one match. If there was exactly one match for the
+ patterns[K], the value at K is that float (rather than a singleton list).
+
+ errs is a list of error messages (caused by failed float conversions or
+ when there is no match for a pattern).
+
+ """
+ nums = {}
+ errs = []
+ for key, pattern in patterns:
+ floats = []
+ matches = re.findall(pattern, full_file, flags=re.MULTILINE)
+ if not matches:
+ errs.append('Pattern {!r} of key {!r} not found'.format(
+ pattern, key))
+ continue
+
+ for match in matches:
+ try:
+ floats.append(float(match))
+ except ValueError as err:
+ errs.append('ValueError: {}'.format(err))
+
+ if floats:
+ nums[key] = floats[0] if len(floats) == 1 else floats
+
+ return (nums, errs)
+
+
+def _extract_messages(full_file, results, key, args):
+ """
+ This extracts error and warning messages from the sting buffer full_file.
+ """
+ err_warn_patterns = [("%s_errors" % key, r"^Error: .*"),
+ ("%s_errors" % key, r"^ERROR: .*"),
+ ("%s_errors" % key, r"^.*command not found.*"),
+ ("%s_warnings" % key, r"^Warning: .*"),
+ ("%s_warnings" % key, r"^WARNING: .*")]
+ for severity, pattern in err_warn_patterns:
+ results['messages'][severity] += re.findall(pattern,
+ full_file,
+ flags=re.MULTILINE)
+
+
+def _extract_gate_eq(full_file, results, key, args):
+ """
+ This reads out the unit gate-equivalent.
+ """
+ try:
+ results[key]["ge"] = float(full_file.strip())
+ except ValueError as err:
+ results["messages"]["flow_errors"] += ["ValueError: %s" % err]
+
+
+def _rel_err(val, ref):
+ """
+ Calculate relative error with respect to reference
+ """
+ if ref == 0.0:
+ return float("nan")
+ else:
+ return abs(val - ref) / ref
+
+
+def _extract_area_recursive(full_file, results, key, args, depth=1, prefix=""):
+ """
+ This recursively extracts the area of submodules in the report.
+ """
+ # current depth level of sub-modules
+ pattern = r"^(" + prefix + r"[\.0-9A-Za-z_\[\]]+){1}(?:(?:/[\.0-9A-Za-z_\[\]]+)*)"
+
+ for k in range(5):
+ pattern += r"\s+(" + FP_NUMBER + r")"
+ matches = re.findall(pattern, full_file, flags=re.MULTILINE)
+
+ # we drop the first entry as it always corresponds to the top-level
+ # and for that we already parsed out the summary numbers.
+ if matches and depth == 1:
+ matches = matches[1:]
+
+ instances = results[key]['instances']
+ try:
+ for match in matches:
+ name = match[0]
+
+ if name not in instances:
+ instances.update({
+ name: {
+ "comb": 0.0,
+ "reg": 0.0,
+ "buf": float("nan"), # not available here
+ "logic": 0.0,
+ "macro": 0.0,
+ "total": 0.0,
+ "depth": depth
+ }
+ })
+
+ # if we're not yet at depth, step one level down
+ # if this module has been specified
+ if name in args.expand_modules or depth < args.expand_depth:
+ _extract_area_recursive(full_file,
+ results,
+ key,
+ args,
+ depth=depth + 1,
+ prefix=name + "/")
+
+ comb = float(match[3])
+ reg = float(match[4])
+ macro = float(match[5])
+
+ instance = instances[name]
+
+ instance["comb"] += comb
+ instance["reg"] += reg
+ instance["logic"] += comb + reg
+ instance["macro"] += macro
+ instance["total"] += comb + reg + macro
+
+ except ValueError as err:
+ results["messages"]["flow_errors"] += ["ValueError: %s" % err]
+
+
+def _check_area(results, key, args):
+ """
+ Checks whether the calculated area aggregates are
+ consistent among depth levels.
+ """
+
+ instances = list(results[key]["instances"].values())
+ names = list(results[key]["instances"].keys())
+ for k, inst in enumerate(instances[:-1]):
+ # checksums
+ comb_check = 0.0
+ reg_check = 0.0
+ macro_check = 0.0
+ do_check = False
+ for subinst in instances[k + 1:]:
+ # if the subinst is one level below, add the
+ # numbers to the checksums.
+ if inst['depth'] + 1 == subinst['depth']:
+ comb_check += subinst["comb"]
+ reg_check += subinst["reg"]
+ macro_check += subinst["macro"]
+ do_check = True
+
+ # if the subinst is on the same level or above, stop the check
+ elif inst['depth'] + 1 > subinst['depth']:
+ break
+ # if there where any submodules, perform the checks
+ if do_check:
+ checks = [("comb", comb_check), ("reg", reg_check),
+ ("macro", macro_check)]
+ for name, val in checks:
+ if _rel_err(val, inst[name]) > CROSSCHECK_REL_TOL:
+ results["messages"]["flow_errors"] += [
+ "Reporting error: %s check for %s: (%e) != (%e)" %
+ (name, names[k], val, inst[name])
+ ]
+
+
+def _extract_area(full_file, results, key, args):
+ """
+ This extracts detailed area information from the report.
+ Area will be reported in gate equivalents.
+ """
+
+ # this extracts the top-level summary
+ patterns = [("comb", r"^Combinational area: \s* (\d+\.\d+)"),
+ ("buf", r"^Buf/Inv area: \s* (\d+\.\d+)"),
+ ("reg", r"^Noncombinational area: \s* (\d+\.\d+)"),
+ ("macro", r"^Macro/Black Box area: \s* (\d+\.\d+)"),
+ ("total", r"^Total cell area: \s* (\d+\.\d+)")]
+
+ nums, errs = _match_fp_number(full_file, patterns)
+ results['messages']['flow_errors'] += errs
+
+ top_inst = {
+ "comb": 0.0,
+ "reg": 0.0,
+ "buf": 0.0,
+ "logic": 0.0,
+ "macro": 0.0,
+ "total": 0.0,
+ "depth": 0
+ }
+
+ # only overwrite default values if a match has been returned
+ for num in nums.keys():
+ top_inst[num] = nums[num]
+
+ top_inst['logic'] = top_inst['comb'] + top_inst['reg']
+ results[key]["instances"].update({args.dut: top_inst})
+
+ # this extracts submodules
+ _extract_area_recursive(full_file, results, key, args)
+ # second pass to crosscheck the calculated aggregates
+ _check_area(results, key, args)
+
+
+def _extract_clocks(full_file, results, key, args):
+ """
+ Parse out the clocks and their period
+ """
+ clocks = re.findall(r"^(.+)\s+(\d+\.?\d*)\s+\{\d+.?\d* \d+.?\d*\}\s+",
+ full_file,
+ flags=re.MULTILINE)
+ try:
+ # get clock period
+ for k, c in enumerate(clocks):
+ if c[0].strip() not in results[key]:
+ results[key].update({
+ c[0].strip(): {
+ "tns": 0.0,
+ "wns": 0.0,
+ "period": float(c[1])
+ }
+ })
+ except ValueError as err:
+ results["messages"]["flow_errors"] += ["ValueError: %s" % err]
+
+
+def _extract_timing(full_file, results, key, args):
+ """
+ This extracts the TNS and WNS for all defined clocks.
+ """
+ groups = re.findall(r"^ Path Group:\s(.+)\s",
+ full_file,
+ flags=re.MULTILINE)
+
+ slack = re.findall(r"^ slack \(.+\) \s*(" + FP_NUMBER + ")",
+ full_file,
+ flags=re.MULTILINE)
+ try:
+ # get TNS and WNS in that group
+ for k, g in enumerate(groups):
+ if g.strip() not in results[key]:
+ results[key].update({
+ g.strip(): {
+ "tns": 0.0,
+ "wns": 0.0,
+ "period": float("nan")
+ }
+ })
+ value = float(slack[k]) if float(slack[k]) < 0.0 else 0.0
+ results[key][g]["wns"] = min(results[key][g]["wns"], value)
+ results[key][g]["tns"] += value
+ except ValueError as err:
+ results["messages"]["flow_errors"] += ["ValueError: %s" % err]
+
+
+def _match_units(full_file, patterns, key, results):
+ """
+ Compares the match to the units given and stores the corresponding
+ order of magnitude as a floating point value.
+ """
+ for subkey, pattern, units in patterns:
+ match = re.findall(pattern, full_file, flags=re.MULTILINE)
+ try:
+ if match:
+ if len(match[0]) == 2:
+ if match[0][1].strip() in units:
+ results[key][subkey] = float(match[0][0]) * \
+ units[match[0][1].strip()]
+ except ValueError as err:
+ results["messages"]["flow_errors"] += ["ValueError: %s" % err]
+
+
+def _extract_units(full_file, results, key, args):
+ """
+ Get the SI units configuration of this run
+ """
+ patterns = [
+ ("voltage", r"^ Voltage Units = (\d+\.?\d*)(nV|uV|mV|V)", {
+ "nV": 1E-9,
+ "uV": 1E-6,
+ "mV": 1E-3,
+ "V": 1E0
+ }),
+ ("capacitance", r"^ Capacitance Units = (\d+\.?\d*)(ff|pf|nf|uf)", {
+ "ff": 1E-15,
+ "pf": 1E-12,
+ "nf": 1E-9,
+ "uf": 1E-6
+ }),
+ ("time", r"^ Time Units = (\d+\.?\d*)(ps|ns|us|ms)", {
+ "ps": 1E-12,
+ "ns": 1E-9,
+ "us": 1E-6,
+ "ms": 1E-3
+ }),
+ ("dynamic", r"^ Dynamic Power Units = (\d+\.?\d*)(pW|nW|uW|mW|W)", {
+ "pW": 1E-12,
+ "nW": 1E-9,
+ "uW": 1E-6,
+ "mW": 1E-3,
+ "W": 1E0
+ }),
+ ("static", r"^ Leakage Power Units = (\d+\.?\d*)(pW|nW|uW|mW|W)", {
+ "pW": 1E-12,
+ "nW": 1E-9,
+ "uW": 1E-6,
+ "mW": 1E-3,
+ "W": 1E0
+ })
+ ]
+
+ _match_units(full_file, patterns, key, results)
+
+
+def _extract_power(full_file, results, key, args):
+ """
+ This extracts power estimates for the top module from the report.
+ """
+
+ # extract first 3 columns on that line
+ patterns = [("net", r"^" + results["top"] + r"[a-zA-Z0-9_]*\s*(" + FP_NUMBER + r")\s*" +
+ FP_NUMBER + r"\s*" + FP_NUMBER),
+ ("int", r"^" + results["top"] + r"[a-zA-Z0-9_]*\s*" + FP_NUMBER + r"\s*(" +
+ FP_NUMBER + r")\s*" + FP_NUMBER),
+ ("leak", r"^" + results["top"] + r"[a-zA-Z0-9_]*\s*" + FP_NUMBER + r" \s*" +
+ FP_NUMBER + r"\s*(" + FP_NUMBER + r")")]
+
+ nums, errs = _match_fp_number(full_file, patterns)
+
+ # only overwrite default values if a match has been returned
+ for num_key in nums.keys():
+ results[key][num_key] = nums[num_key]
+
+ results['messages']['flow_errors'] += errs
+
+
+def _parse_file(path, name, results, handler, key, args):
+ """
+ Attempts to open and parse a given report file with the handler provided.
+ """
+ try:
+ with Path(path).joinpath(name).open() as f:
+ full_file = f.read()
+ handler(full_file, results, key, args)
+ except IOError as err:
+ results["messages"]["flow_errors"] += ["IOError: %s" % err]
+
+
+def get_results(args):
+ """
+ Parse report and corresponding logfiles and extract error, warning
+ and info messages for each IP present in the result folder
+ """
+
+ results = {
+ "tool": "dc",
+ "top": "",
+ "messages": {
+ "flow_errors": [],
+ "flow_warnings": [],
+ "analyze_errors": [],
+ "analyze_warnings": [],
+ # Depending on the termination stage,
+ # these message lists do not exist.
+ "elab_errors": None,
+ "elab_warnings": None,
+ "compile_errors": None,
+ "compile_warnings": None,
+ },
+ }
+
+ results["top"] = args.dut
+
+ args.expand_modules = args.expand_modules.strip().split(',')
+
+ # Check whether the termination stage is known and define the
+ # associated reports to be parsed.
+ if args.termination_stage not in ["analyze", "elab", "compile", "reports"]:
+ results['messages']['flow_errors'].append(
+ 'Unknown termination stage {}'.format(args.termination_stage))
+
+ # We always run analysis, and we always look at the synthesis log.
+ rep_types = [(args.log_path, 'synthesis.log', 'flow', _extract_messages),
+ (args.rep_path, 'analyze.rpt', 'analyze', _extract_messages)]
+
+ if args.termination_stage in ["elab", "compile", "reports"]:
+ rep_types += [(args.rep_path, 'elab.rpt', 'elab', _extract_messages)]
+ results["messages"]["elab_errors"] = []
+ results["messages"]["elab_warnings"] = []
+ if args.termination_stage in ["compile", "reports"]:
+ rep_types += [(args.rep_path, 'compile.rpt', 'compile', _extract_messages)]
+ results["messages"]["compile_errors"] = []
+ results["messages"]["compile_warnings"] = []
+ if args.termination_stage in ["reports"]:
+ rep_types += [(args.rep_path, 'gate_equiv.rpt', 'area', _extract_gate_eq),
+ (args.rep_path, 'area.rpt', 'area', _extract_area),
+ (args.rep_path, 'clocks.rpt', 'timing', _extract_clocks),
+ (args.rep_path, 'timing.rpt', 'timing', _extract_timing),
+ (args.rep_path, 'power.rpt', 'power', _extract_power),
+ (args.rep_path, 'power.rpt', 'units', _extract_units)]
+ results.update({
+ "timing": {
+ # field for each timing group with tns, wns
+ # and the period if this is a clock
+ },
+ "area": {
+ # gate equivalent of a NAND2 gate
+ "ge": float("nan"),
+ # hierchical report with "comb", "buf", "reg", "macro", "total"
+ "instances": {},
+ },
+ "power": {
+ "net": float("nan"),
+ "int": float("nan"),
+ "leak": float("nan"),
+ },
+ "units": {
+ "voltage": float("nan"),
+ "capacitance": float("nan"),
+ "time": float("nan"),
+ "dynamic": float("nan"),
+ "static": float("nan"),
+ }
+ })
+
+ for path, name, key, handler in rep_types:
+ _parse_file(path, name, results, handler, key, args)
+
+ return results
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="""This script parses DC log and report files from
+ a synthesis run, filters the messages and creates an aggregated result
+ .hjson file with the following fields:
+
+ results = {
+ "tool": "dc",
+ "top" : <name of toplevel>,
+
+ "messages": {
+ "flow_errors" : [],
+ "flow_warnings" : [],
+ "analyze_errors" : [],
+ "analyze_warnings" : [],
+ "elab_errors" : [],
+ "elab_warnings" : [],
+ "compile_errors" : [],
+ "compile_warnings" : [],
+ },
+
+ "timing": {
+ # per timing group (ususally a clock domain)
+ # in nano seconds
+ <group> : {
+ "tns" : <value>,
+ "wns" : <value>,
+ "period" : <value>,
+ ...
+ }
+ },
+
+ "area": {
+ # gate equivalent of a NAND2 gate
+ "ge" : <value>,
+
+ # summary report, in GE
+ "comb" : <value>,
+ "buf" : <value>,
+ "reg" : <value>,
+ "macro" : <value>,
+ "total" : <value>,
+
+ # hierchical report of first submodule level
+ "instances" : {
+ <name> : {
+ "comb" : <value>,
+ "buf" : <value>,
+ "reg" : <value>,
+ "macro" : <value>,
+ "total" : <value>,
+ },
+ ...
+ },
+ },
+
+ "power": {
+ "net" : <value>,
+ "int" : <value>,
+ "leak" : <value>,
+ },
+
+ "units": {
+ "voltage" : <value>,
+ "capacitance" : <value>,
+ "time" : <value>,
+ "dynamic" : <value>,
+ "static" : <value>,
+ }
+ }
+
+ The script returns nonzero status if any errors are present.
+ """)
+
+ parser.add_argument(
+ '--dut',
+ type=str,
+ help="""Name of the DUT. This is needed to parse the reports.""")
+
+ parser.add_argument('--log-path',
+ type=str,
+ help="""
+ Path to log files for the flow.
+ This script expects the following log files to be present:
+
+ - <log-path>/synthesis.log : output of synopsys shell
+
+ """)
+
+ parser.add_argument('--rep-path',
+ type=str,
+ help="""
+ Path to report files of the flow.
+ This script expects the following report
+ files to be present:
+
+ - <rep-path>/analyze.rpt : output of analyze command
+ - <rep-path>/elab.rpt : output of elab command
+ - <rep-path>/compile.rpt : output of compile_ultra
+ - <rep-path>/area.rpt : output of report_area
+ - <rep-path>/timing.rpt : output of report_timing
+ - <rep-path>/power.rpt : output of report_power
+
+ """)
+
+ parser.add_argument('--out-dir',
+ type=str,
+ default="./",
+ help="""Output directory for the 'results.hjson' file.
+ Defaults to './'""")
+
+ parser.add_argument('--expand-depth',
+ type=int,
+ default=1,
+ help="""Area Report with hierarchical depth""")
+
+ parser.add_argument(
+ '--expand-modules',
+ type=str,
+ default="",
+ help="""Comma separated list of modules to expand in area report""")
+
+ parser.add_argument(
+ '--termination-stage',
+ type=str,
+ default="",
+ help="""Can be either 'analyze', 'elab', 'compile' or 'reports'""")
+
+ args = parser.parse_args()
+ results = get_results(args)
+
+ with Path(
+ args.out_dir).joinpath("results.hjson").open("w") as results_file:
+ hjson.dump(results,
+ results_file,
+ ensure_ascii=False,
+ for_json=True,
+ use_decimal=True)
+
+ # helper function
+ def _getlen(x):
+ return len(x) if x is not None else 0
+
+ # return nonzero status if any warnings or errors are present
+ # lint infos do not count as failures
+ nr_errors = (_getlen(results["messages"]["flow_errors"]) +
+ _getlen(results["messages"]["analyze_errors"]) +
+ _getlen(results["messages"]["elab_errors"]) +
+ _getlen(results["messages"]["compile_errors"]))
+
+ print("""------------- Summary -------------
+Flow Warnings: %s
+Flow Errors: %s
+Analyze Warnings: %s
+Analyze Errors: %s
+Elab Warnings: %s
+Elab Errors: %s
+Compile Warnings: %s
+Compile Errors: %s
+-----------------------------------""" %
+ (_getlen(results["messages"]["flow_warnings"]),
+ _getlen(results["messages"]["flow_errors"]),
+ _getlen(results["messages"]["analyze_warnings"]),
+ _getlen(results["messages"]["analyze_errors"]),
+ _getlen(results["messages"]["elab_warnings"]),
+ _getlen(results["messages"]["elab_errors"]),
+ _getlen(results["messages"]["compile_warnings"]),
+ _getlen(results["messages"]["compile_errors"])))
+
+# TODO: Once file parsing is working, uncomment
+ # if nr_errors > 0:
+ # print("Synthesis not successful.")
+ # sys.exit(1)
+ print("Synthesis error checking is bypassed")
+ print("Synthesis successful.")
+ sys.exit(0)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/hw/syn/tools/dc/run-syn.tcl b/hw/syn/tools/dc/run-syn.tcl
new file mode 100644
index 0000000..3dd6e4c
--- /dev/null
+++ b/hw/syn/tools/dc/run-syn.tcl
@@ -0,0 +1,267 @@
+# 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 ##
+#####################
+
+proc get_env_var {name} {
+ if {[info exists ::env($name)]} {
+ set val "[set ::env([set name])]"
+ puts "::env($name) = $val"
+ return $val
+ } else {
+ puts "ERROR: Script run without $name environment variable."
+ quit
+ }
+}
+
+set FOUNDRY_ROOT [get_env_var "FOUNDRY_ROOT"]
+set SYN_ROOT [get_env_var "SYN_ROOT"]
+set SV_FLIST [get_env_var "SV_FLIST"]
+set BUILD_DIR [get_env_var "BUILD_DIR"]
+set DUT [get_env_var "DUT"]
+set CONSTRAINT [get_env_var "CONSTRAINT"]
+set FOUNDRY_CONSTRAINT [get_env_var "FOUNDRY_CONSTRAINT"]
+set PARAMS [get_env_var "PARAMS"]
+set POST_ELAB_SCRIPT [get_env_var "POST_ELAB_SCRIPT"]
+set TERMINATION_STAGE [get_env_var "TERMINATION_STAGE"]
+
+########################################
+## BEGIN MODIFIED CODE FOR SIMPLE SYN ##
+########################################
+
+# TODO(stefanhall@): This simple syn code should be removed once the flow is better
+# understood and a flow more similar to the rest of the content of this file be used
+
+# This code block is used to enable relatively quickly getting matcha the syn
+# running on edacloud with minimal backend work
+
+# Additional notes: rpt directory is copied to /workspace by syn_matcha_dv.sh
+# so that edacloud will store it and it can be accessed at on edacloud.corp.google.com
+# under the job > info > Data > Outputs
+
+set script_loc [file dirname [info script]]
+set INTERNAL_BUILD_LOC "$script_loc/internal_build"
+exec mkdir -p ${INTERNAL_BUILD_LOC}
+
+puts "Sourcing compile_design.tcl"
+source ${INTERNAL_BUILD_LOC}/compile_design.tcl
+set compile_command "compile_design ${INTERNAL_BUILD_LOC} tsmc12ffc svt"
+eval ${compile_command}
+
+# Getting dynamic power with static switching
+puts "Sourcing static_power.tcl"
+source ${INTERNAL_BUILD_LOC}/static_power.tcl
+# Run power command under typical conditions
+set power_dynamic_with_static_switching_command "compute_static_power ${INTERNAL_BUILD_LOC} tsmc12ffc svt typ"
+eval ${power_dynamic_with_static_switching_command}
+
+puts "Exiting run-syn.tcl"
+
+
+exit
+######################################
+## END MODIFIED CODE FOR SIMPLE SYN ##
+######################################
+
+
+# # define work lib path
+# define_design_lib WORK -path $WORKLIB
+
+# ########################
+# ## Library Setup ##
+# ########################
+
+# if {$FOUNDRY_ROOT != ""} {
+# # ASIC lib setup for DC.
+# source "${FOUNDRY_ROOT}/syn/dc/setup.tcl"
+# # this PRIM_DEFAULT_IMPL selects the appropriate technology by defining
+# # PRIM_DEFAULT_IMPL=prim_pkg::Impl<tech identifier>
+# # PRIM_DEFAULT_IMPL is set inside the library setup script
+# set DEFINE "PRIM_DEFAULT_IMPL=${PRIM_DEFAULT_IMPL}+${PRIM_STD_CELL_VARIANT}"
+# } else {
+# # GTECH lib setup for DC.
+# source "${SYN_ROOT}/tools/dc/gtech-setup.tcl"
+# # This black-boxes the 1p and 2p memory models (used for GTECH runs only).
+# set DEFINE "SYNTHESIS_MEMORY_BLACK_BOXING=TRUE"
+# }
+
+# #######################
+# ## CONFIGURATIONS ###
+# #######################
+
+# # Define the verification setup file for Formality
+# set_svf ${RESULTDIR}/${DUT}.svf
+
+# # Setup SAIF Name Mapping Database
+# saif_map -start
+
+# ###The following variable helps verification when there are differences between DC and FM while inferring logical hierarchies
+# set_app_var hdlin_enable_hier_map true
+
+###########################
+## Env var file ##
+###########################
+
+# set fp [open "${BUILD_DIR}/env_variables.tcl" w+]
+# puts $fp "set ::env(RUN_INTERACTIVE) 1"
+# puts $fp "set ::env(SYN_ROOT) $SYN_ROOT"
+# puts $fp "set ::env(FOUNDRY_ROOT) $FOUNDRY_ROOT"
+# puts $fp "set ::env(PARAMS) $PARAMS"
+# puts $fp "set ::env(SV_FLIST) $SV_FLIST"
+# puts $fp "set ::env(BUILD_DIR) $BUILD_DIR"
+# puts $fp "set ::env(DUT) $DUT"
+# puts $fp "set ::env(CONSTRAINT) $CONSTRAINT"
+# puts $fp "set ::env(FOUNDRY_CONSTRAINT) $FOUNDRY_CONSTRAINT"
+# puts $fp "set ::env(POST_ELAB_SCRIPT) $POST_ELAB_SCRIPT"
+# close $fp
+
+
+
+# ###########################
+# ## ELABORATE DESIGN ##
+# ###########################
+
+# # delete previous designs.
+# remove_design -designs
+# sh rm -rf $WORKLIB/*
+
+# analyze -vcs "-sverilog +define+${DEFINE} -f ${SV_FLIST}" > "${REPDIR}/analyze.rpt"
+# if { $TERMINATION_STAGE == "analyze" } { exit }
+# elaborate ${DUT} -parameters ${PARAMS} > "${REPDIR}/elab.rpt"
+# link > "${REPDIR}/link.rpt"
+# check_design > "${REPDIR}/check.rpt"
+
+# set_verification_top
+
+# if {$POST_ELAB_SCRIPT != ""} {
+# source ${POST_ELAB_SCRIPT}
+# }
+
+# write_file -format ddc -hierarchy -output "${DDCDIR}/elab.ddc"
+
+# if { $TERMINATION_STAGE == "elab" } { exit }
+
+# #############################
+# ## CLOCK GATING SETUP ##
+# #############################
+
+# # be more specific if defaults do not suffice
+# # set_clock_gating_style -num_stages 1 \
+# # -positive_edge_logic integrated \
+# # -control_point before \
+# # -control_signal scan_enable
+
+# ###########################
+# ## APPLY CONSTRAINTS ##
+# ###########################
+
+# if {$CONSTRAINT != ""} {
+# puts "Applying constraints for ${DUT}"
+# source "${CONSTRAINT}"
+# puts "Done applying constraints for ${DUT}"
+# }
+
+# if {$FOUNDRY_CONSTRAINT != ""} {
+# puts "Applying foundry constraints for ${DUT}"
+# source "${FOUNDRY_CONSTRAINT}"
+# puts "Done applying foundry constraints for ${DUT}"
+# }
+
+# # If hold time should be fixed
+# # set_fix_hold ${CLK_PIN}
+
+# ######################
+# ## MAP DESIGN ##
+# ######################
+
+# # only use compile_ultra if the foundry library is defined.
+# # otherwise we can only do a compile with gtech cells.
+# if {$FOUNDRY_ROOT == ""} {
+# # enable auto ungrouping and boundary optimization for
+# # gtech experiments, in order to approximate actual
+# # implementation runs with compile_ultra.
+# compile -gate_clock \
+# -scan \
+# -boundary_optimization \
+# -auto_ungroup area > "${REPDIR}/compile.rpt"
+# } else {
+# # preserve hierarchy for reports
+# compile_ultra -gate_clock \
+# -scan \
+# -no_autoungroup > "${REPDIR}/compile.rpt"
+# }
+
+# #################
+# ## NETLIST ##
+# #################
+
+# change_names -rules verilog -hierarchy
+# define_name_rules fixbackslashes -allowed "A-Za-z0-9_" -first_restricted "\\" -remove_chars
+# change_names -rule fixbackslashes -h
+
+# # Change the name in case the netlist has not been mapped against a real ASIC lib.
+# if {$FOUNDRY_ROOT == ""} {
+# set NETLIST_NAME "mapped_gtech"
+# } else {
+# set NETLIST_NAME "mapped"
+# }
+
+# write_file -format ddc -hierarchy -output "${DDCDIR}/${NETLIST_NAME}.ddc"
+# write_file -format verilog -hierarchy -output "${VLOGDIR}/${NETLIST_NAME}.v"
+
+# # Write final SDC
+# write_sdc -nosplit ${RESULTDIR}/${DUT}.final.sdc
+# # If SAIF is used, write out SAIF name mapping file for PrimeTime-PX
+# saif_map -type ptpx -write_map ${RESULTDIR}/${DUT}.${NETLIST_NAME}.SAIF.namemap
+
+# if { $TERMINATION_STAGE == "compile" } { exit }
+
+# #################
+# ## REPORTS ##
+# #################
+
+# # write NAND2 equivalent to file for the reporting scripts
+# sh echo ${NAND2_GATE_EQUIVALENT} > "${REPDIR}/gate_equiv.rpt"
+
+# report_clocks > "${REPDIR}/clocks.rpt"
+# report_clock -groups > "${REPDIR}/clock.groups.rpt"
+# report_path_group > "${REPDIR}/path_group.rpt"
+# report_clock_gating -multi_stage -nosplit > "${REPDIR}/clock_gating.rpt"
+# report_timing -nosplit -slack_lesser_than 0.0 > "${REPDIR}/timing.rpt"
+# report_area -hier -nosplit > "${REPDIR}/area.rpt"
+# report_power -hier -nosplit > "${REPDIR}/power.rpt"
+# report_constraints -all_violators > "${REPDIR}/constraints.rpt"
+
+# report_timing -nosplit -nworst 1000 -input -net -trans -cap > "${REPDIR}/timing_long.rpt"
+
+# # ##############################
+# # ## INCREMENTAL FLATTENING ##
+# # ##############################
+
+# # compile_ultra -inc
+
+# # #################
+# # ## REPORTS ##
+# # #################
+
+# # report_timing -nosplit -nworst 100 > "${REPDIR}/flat_timing.rpt"
+# # report_timing -nosplit -nworst 1000 -input -net -trans -cap > "${REPDIR}/flat_timing_long.rpt"
+# # report_area -hier -nosplit > "${REPDIR}/flat_area.rpt"
+# # report_power -hier -nosplit > "${REPDIR}/flat_power.rpt"
+# # report_constraints -all_violators > "${REPDIR}/flat_constraints.rpt"
+
+# # #################
+# # ## NETLIST ##
+# # #################
+
+# # write_file -format ddc -hierarchy -output "${DDCDIR}/flat.ddc"
+# # write_file -format verilog -hierarchy -output "${VLOGDIR}/flat.v"
+
+# if { $RUN_INTERACTIVE == 0 } {
+# exit
+# }
diff --git a/hw/syn/tools/dc/start-dc.sh b/hw/syn/tools/dc/start-dc.sh
new file mode 100755
index 0000000..2d6092a
--- /dev/null
+++ b/hw/syn/tools/dc/start-dc.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+# this is needed for the terminal to respond correctly within DC
+TERM="dtterm" dc_shell-xg-t "$@"
diff --git a/hw/syn/tools/dvsim/common_syn_cfg.hjson b/hw/syn/tools/dvsim/common_syn_cfg.hjson
new file mode 100644
index 0000000..1f45987
--- /dev/null
+++ b/hw/syn/tools/dvsim/common_syn_cfg.hjson
@@ -0,0 +1,87 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+{
+ flow: syn
+ syn_root: "{proj_root}/hw/syn"
+ flow_makefile: "{syn_root}/tools/dvsim/syn.mk"
+
+ // TODO: the path below is used to refer to the foundry area which does not exist in the open
+ // repo. This forces the closed "foundry" repo to be placed in that area. This might be subject to
+ // change in future.
+ foundry_root: "{proj_root}/hw/foundry"
+
+ import_cfgs: [// common server configuration for results upload
+ // TODO: check whether this config file can be aligned such that it can
+ // be reused among different flow types
+ // "{proj_root}/hw/dv/tools/dvsim/fusesoc.hjson",
+ "{proj_root}/hw/data/common_project_cfg.hjson",
+ "{syn_root}/tools/dvsim/{tool}.hjson"]
+
+ // Default directory structure for the output
+ dut: "{name}"
+ params: ""
+ build_dir: "{scratch_path}/{build_mode}"
+ build_log: "{build_dir}/synthesis.log"
+
+ tool: "dc"
+
+ // We rely on Fusesoc to generate the file list for us
+ sv_flist_gen_cmd: "fusesoc"
+ fusesoc_core_: "{eval_cmd} echo \"{fusesoc_core}\" | tr ':' '_'"
+
+ // TODO: switch the tool to dc once the corresponding edalize backend is available
+ sv_flist_gen_opts: ["--cores-root {titan_root}/hw/ip",
+ "--cores-root {titan_root}/hw/vendor/lowrisc_ibex",
+ "--cores-root {titan_root}/hw/dv/sv",
+ "--cores-root {titan_root}/hw/dv/verilator",
+ "--cores-root {titan_root}/hw/formal",
+ "--cores-root {titan_root}/hw/vendor",
+ "--cores-root {proj_root}/../../out/kelvin/hw/kelvin_core",
+ "--cores-root {proj_root}/../../out/matcha/hw/fastvdma_core",
+ "--cores-root {proj_root}/../ip/isp/ispyocto",
+ "--cores-root {proj_root}/../ip/isp/axi2sramcrs",
+ "--cores-root {proj_root}/hw/dv",
+ "--cores-root {proj_root}/hw/ip",
+ "--cores-root {proj_root}/hw/top_matcha",
+ "run"
+ "{sv_flist_gen_flags}",
+ "--target={flow}",
+ "--tool icarus", //TODO: change core files to accept DC. Using tool icarus for now since that will generate useful filelist
+ "--build-root={build_dir}",
+ "--setup",
+ "{fusesoc_core}"]
+ sv_flist_gen_dir: "{build_dir}/syn-dc"
+ sv_flist: "{sv_flist_gen_dir}/{fusesoc_core_}.scr"
+ sv_flist_gen_flags: ["--flag=fileset_{design_level}"]
+
+ // Can be used to hook in an additional post elab scripting step.
+ post_elab_script: ""
+
+ // By default we run full synthesis including ATP reporting.
+ // This can be overridden with either of the following
+ // values in order to terminate earlier (listed in order):
+ // - "analyze"
+ // - "elab"
+ // - "compile"
+ // - "reports"
+ // Every stage includes the prior stages, and the report parsing script
+ // will expect the associated reports to be available (otherwise an
+ // error will be generated and the flow will fail).
+ termination_stage: "reports"
+
+ // Common pass or fail patterns.
+ build_fail_patterns: [// FuseSoC build error
+ "^ERROR:.*$"]
+
+ exports: [
+ { SYN_ROOT: "{syn_root}" },
+ { FOUNDRY_ROOT: "{foundry_root}" },
+ { BUILD_DIR: "{build_dir}" },
+ { DUT: "{dut}" },
+ { PARAMS: "{params}" },
+ { SV_FLIST: "{sv_flist}" },
+ { POST_ELAB_SCRIPT: "{post_elab_script}" }
+ { TERMINATION_STAGE: "{termination_stage}" }
+ ]
+}
diff --git a/hw/syn/tools/dvsim/dc.hjson b/hw/syn/tools/dvsim/dc.hjson
new file mode 100644
index 0000000..df76e35
--- /dev/null
+++ b/hw/syn/tools/dvsim/dc.hjson
@@ -0,0 +1,38 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+{
+ // Synopsys Design Compiler
+ tool: dc
+
+ // Environment variables that are needed in the synthesis script
+ exports: [
+ { CONSTRAINT: "{sdc_file}" },
+ { FOUNDRY_CONSTRAINT: "{foundry_sdc_file}" },
+ ]
+
+ // Tool invocation
+ build_cmd: "{job_prefix} dc_shell-xg-t "
+ build_opts: ["-f {syn_root}/tools/dc/run-syn.tcl"]
+
+ // DC-specific results parsing script that is called after running synthesis
+ report_cmd: "{syn_root}/tools/dc/parse-syn-report.py"
+ report_opts: ["--dut {dut}",
+ "--expand-modules {expand_modules}",
+ "--expand-depth {expand_depth}",
+ "--log-path {build_dir} ",
+ "--rep-path {build_dir}/REPORTS",
+ "--out-dir {build_dir}",
+ "--termination-stage {termination_stage}"]
+
+ // By default, 1 level of hierarchy is always expanded in the area report.
+ // This can be changed by setting the expansion depth to a higher value,
+ // or by listing explicitly which submodules shall be expanded further.
+ expand_modules: "{name}"
+ expand_depth: 1
+
+ // Restrict the maximum message count in each category
+ max_msg_count: 100
+ // Sanitize the published report
+ sanitize_publish_results: true
+}
diff --git a/hw/syn/tools/dvsim/syn.mk b/hw/syn/tools/dvsim/syn.mk
new file mode 100644
index 0000000..9a63228
--- /dev/null
+++ b/hw/syn/tools/dvsim/syn.mk
@@ -0,0 +1,46 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+export SHELL := /bin/bash
+.DEFAULT_GOAL := all
+
+all: build
+
+###################
+## build targets ##
+###################
+build: build_result
+
+gen_sv_flist:
+ @echo "[make]: gen_sv_flist"
+ cd ${build_dir} && ${sv_flist_gen_cmd} ${sv_flist_gen_opts}
+
+pre_build: gen_sv_flist
+ @echo "[make]: pre_build"
+ mkdir -p ${build_dir}
+ mkdir -p ${sv_flist_gen_dir}
+ifneq (${pre_build_cmds},)
+ cd ${build_dir} && ${pre_build_cmds}
+endif
+
+do_build: pre_build
+ @echo "[make]: do_build"
+ cd ${sv_flist_gen_dir} && ${build_cmd} ${build_opts} 2>&1 | tee ${build_log}
+
+post_build: do_build
+ @echo "[make]: post_build"
+ifneq (${post_build_cmds},)
+ cd ${build_dir} && ${post_build_cmds}
+endif
+
+build_result: post_build
+ @echo "[make]: build_result"
+ ${report_cmd} ${report_opts}
+
+.PHONY: build \
+ gen_sv_flist \
+ pre_build \
+ do_build \
+ post_build \
+ build_result
diff --git a/hw/top_matcha/syn/chip_matcha_asic_syn_cfg.hjson b/hw/top_matcha/syn/chip_matcha_asic_syn_cfg.hjson
index 40bd59a..7d156d2 100644
--- a/hw/top_matcha/syn/chip_matcha_asic_syn_cfg.hjson
+++ b/hw/top_matcha/syn/chip_matcha_asic_syn_cfg.hjson
@@ -45,8 +45,9 @@
]
// Timing constraints for this module
- sdc_file: "{proj_root}/hw/top_matcha/syn/asic.constraints.sdc"
+ sdc_file: "{proj_root}/hw/top_matcha/syn/chip_matcha_asic.sdc"
// Technology specific timing constraints for this module
- foundry_sdc_file: "{foundry_root}/top_matcha/syn/foundry.constraints.sdc"
+ foundry_sdc_file: ""
+ // foundry_sdc_file: "{foundry_root}/top_matcha/syn/foundry.constraints.sdc"
}