From 7cd5485ceaf68ef455e13d54219fabe70c16c421 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Wed, 21 Aug 2024 18:55:17 +0300 Subject: [PATCH 01/11] Yosys Step Rewrite, Update `nix-eda` * `Yosys.JsonHeader`, `Yosys.*Synthesis` * **Internal**: * Steps are no longer `TclStep`s: rewritten in Python and now use `libyosys`. While there are no functional changes, this enhances the codebase's consistency and helps avoid tokenization-related security issues. * `Yosys.*Synthesis` * ABC scripts used now created dynamically and dumped as a `.abc` file into the step directory. * Updated `nix-eda` * `yosys` -> `0.44` (+ `-y` patch) * `klayout` -> `0.29.4` * OpenROAD now used with new `withPythonPackages` features to use Python packages specifically for the OpenROAD environment --- default.nix | 30 +- flake.lock | 7 +- flake.nix | 6 +- nix/openroad.nix | 33 +- .../scripts/pyosys/construct_abc_script.py | 177 +++++ openlane/scripts/pyosys/json_header.py | 68 ++ openlane/scripts/pyosys/synthesize.py | 342 ++++++++++ openlane/scripts/pyosys/ys_common.py | 124 ++++ openlane/scripts/yosys/common.tcl | 121 ---- .../scripts/yosys/construct_abc_script.tcl | 116 ---- openlane/scripts/yosys/json_header.tcl | 25 - openlane/scripts/yosys/synthesize.tcl | 235 ------- openlane/steps/pyosys.py | 609 ++++++++++++++++++ openlane/steps/step.py | 2 +- openlane/steps/yosys.py | 472 ++------------ type_stubs/libyosys.pyi | 24 + type_stubs/pyosys.pyi | 3 + 17 files changed, 1453 insertions(+), 941 deletions(-) create mode 100644 openlane/scripts/pyosys/construct_abc_script.py create mode 100644 openlane/scripts/pyosys/json_header.py create mode 100644 openlane/scripts/pyosys/synthesize.py create mode 100644 openlane/scripts/pyosys/ys_common.py delete mode 100644 openlane/scripts/yosys/common.tcl delete mode 100644 openlane/scripts/yosys/construct_abc_script.tcl delete mode 100644 openlane/scripts/yosys/json_header.tcl delete mode 100644 openlane/scripts/yosys/synthesize.tcl create mode 100644 openlane/steps/pyosys.py create mode 100644 type_stubs/libyosys.pyi create mode 100644 type_stubs/pyosys.pyi diff --git a/default.nix b/default.nix index 62039206a..b6656e7d8 100644 --- a/default.nix +++ b/default.nix @@ -19,7 +19,6 @@ nix-gitignore, # Tools klayout, - klayout-pymod, libparse, magic-vlsi, netgen, @@ -57,6 +56,22 @@ ioplace-parser, poetry-core, }: let + yosys-env = + (yosys.withPlugins ([ + yosys-sby + yosys-eqy + yosys-lighter + yosys-synlig-sv + yosys-f4pga-sdc + ] + ++ lib.optionals (system == "x86_64-linux") [yosys-ghdl])); + openroad-env = + (openroad.withPythonPackages(ps: with ps; [ + click + rich + pyyaml + ioplace-parser + ])); self = buildPythonPackage { pname = "openlane"; version = (builtins.fromTOML (builtins.readFile ./pyproject.toml)).tool.poetry.version; @@ -69,16 +84,9 @@ ]; includedTools = [ - (yosys.withPlugins ([ - yosys-sby - yosys-eqy - yosys-lighter - yosys-synlig-sv - yosys-f4pga-sdc - ] - ++ lib.optionals (system == "x86_64-linux") [yosys-ghdl])) + yosys-env opensta - openroad + openroad-env klayout netgen magic-vlsi @@ -104,7 +112,7 @@ deprecated libparse psutil - klayout-pymod + klayout.pymod rapidfuzz ioplace-parser ] diff --git a/flake.lock b/flake.lock index 95f7bd31a..0f33a4b00 100644 --- a/flake.lock +++ b/flake.lock @@ -100,15 +100,16 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1719830786, - "narHash": "sha256-meVPi2d6TI44FjuqQc57Ve3UV1GP12uDVrRfHcACBdg=", + "lastModified": 1724169777, + "narHash": "sha256-nn4ChFRJL1b3GoVku47sUSLSFyfyi6saWDdq5zz7ADk=", "owner": "efabless", "repo": "nix-eda", - "rev": "8fd46e08259a761d5078c6c3b8b19dd58bb69e75", + "rev": "2fc07cbc18bb576227f88049eae3a21919571c56", "type": "github" }, "original": { "owner": "efabless", + "ref": "python_cleanup", "repo": "nix-eda", "type": "github" } diff --git a/flake.nix b/flake.nix index 7b882df84..17e969f6d 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ }; inputs = { - nix-eda.url = github:efabless/nix-eda; + nix-eda.url = github:efabless/nix-eda/python_cleanup; libparse.url = github:efabless/libparse-python; ioplace-parser.url = github:efabless/ioplace_parser; volare.url = github:efabless/volare; @@ -70,7 +70,9 @@ colab-env = callPackage ./nix/colab-env.nix {}; opensta = callPackage ./nix/opensta.nix {}; openroad-abc = callPackage ./nix/openroad-abc.nix {}; - openroad = callPythonPackage ./nix/openroad.nix {}; + openroad = callPythonPackage ./nix/openroad.nix { + inherit (nix-eda) buildPythonEnvForInterpreter; + }; openlane = callPythonPackage ./default.nix {}; sphinx-tippy = callPythonPackage ./nix/sphinx-tippy.nix {}; sphinx-subfigure = callPythonPackage ./nix/sphinx-subfigure.nix {}; diff --git a/nix/openroad.nix b/nix/openroad.nix index 9dcf4151c..98eafe26f 100644 --- a/nix/openroad.nix +++ b/nix/openroad.nix @@ -42,19 +42,12 @@ bison, clang-tools_14, ioplace-parser, + buildEnv, + makeBinaryWrapper, + buildPythonEnvForInterpreter, rev ? "b16bda7e82721d10566ff7e2b68f1ff0be9f9e38", sha256 ? "sha256-+JGyX81Km2XidptA3k1Y5ZPwv+4Ed39LCsPfIHWd6ac=", -}: let - pyenv = python3.withPackages (p: - with p; [ - click - rich - pyyaml - ioplace-parser - ]); - pyenv-sitepackages = "${pyenv}/${pyenv.sitePackages}"; -in - clangStdenv.mkDerivation (finalAttrs: { +}: let self = clangStdenv.mkDerivation (finalAttrs: { name = "openroad"; inherit rev; @@ -98,7 +91,7 @@ in boost183 eigen tcl - pyenv + python3 readline tclreadline spdlog-internal-fmt @@ -131,10 +124,16 @@ in shellHook = '' export DEVSHELL_CMAKE_FLAGS="${builtins.concatStringsSep " " finalAttrs.cmakeFlagsAll}" ''; - - qtWrapperArgs = [ - "--prefix PYTHONPATH : ${pyenv-sitepackages}" - ]; + + passthru = { + inherit python3; + withPythonPackages = buildPythonEnvForInterpreter { + target = self; + inherit lib; + inherit buildEnv; + inherit makeBinaryWrapper; + }; + }; meta = with lib; { description = "OpenROAD's unified application implementing an RTL-to-GDS flow"; @@ -144,4 +143,4 @@ in license = licenses.gpl3Plus; platforms = platforms.linux ++ platforms.darwin; }; - }) + }); in self diff --git a/openlane/scripts/pyosys/construct_abc_script.py b/openlane/scripts/pyosys/construct_abc_script.py new file mode 100644 index 000000000..a9652cb56 --- /dev/null +++ b/openlane/scripts/pyosys/construct_abc_script.py @@ -0,0 +1,177 @@ +# Copyright 2020-2024 Efabless Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import re + + +class ABCScriptCreator: + def __init__(self, config): + self.config = config + + self.rs_K = "resub -K " + self.rs = "resub" + self.rsz = "resub -z" + self.rf = "drf -l" + self.rfz = "drf -l -z" + self.rw = "drw -l" + self.rwz = "drw -l -z" + self.rw_K = "drw -l -K" + + if config["SYNTH_ABC_LEGACY_REFACTOR"]: + self.rf = "refactor" + self.rfz = "refactor -z" + + if config["SYNTH_ABC_LEGACY_REWRITE"]: + self.rw = "rewrite" + self.rwz = "rewrite -z" + self.rw_K = "rewrite -K" + + self.b = "balance" + self.resyn2 = f"{self.b}; {self.rw}; {self.rf}; {self.b}; {self.rw}; {self.rwz}; {self.b}; {self.rfz}; {self.rwz}; {self.b}" + self.share = f"strash; multi -m; {self.resyn2}" + self.resyn2a = f"{self.b};{self.rw};{self.b};{self.rw};{self.rwz};{self.b};{self.rwz};{self.b}" + self.resyn3 = "balance;resub;resub -K 6;balance;resub -z;resub -z -K 6;balance;resub -z -K 5;balance" + self.resyn2rs = f"{self.b};{self.rs_K} 6;{self.rw};{self.rs_K} 6 -N 2;{self.rf};{self.rs_K} 8;{self.rw};{self.rs_K} 10;{self.rwz};{self.rs_K} 10 -N 2;{self.b} {self.rs_K} 12;{self.rfz};{self.rs_K} 12 -N 2;{self.rwz};{self.b}" + + self.choice = f"fraig_store; {self.resyn2}; fraig_store; {self.resyn2}; fraig_store; fraig_restore" + self.choice2 = f"fraig_store; balance; fraig_store; {self.resyn2}; fraig_store; {self.resyn2}; fraig_store; {self.resyn2}; fraig_store; fraig_restore" + + self.area_mfs3 = "" + self.delay_mfs3 = "" + if config["SYNTH_ABC_USE_MFS3"]: + self.area_mfs3 = "mfs3 -aemvz -I 4 -O 2" + self.delay_mfs3 = "mfs3 -emvz -I 4 -O 2" + + self.map_old_area = "map -p -a -B 0.2 -A 0.9 -M 0" + self.map_old_dly = "map -p -B 0.2 -A 0.9 -M 0" + self.retime_area = "retime {D} -M 5" + self.retime_dly = "retime {D} -M 6" + self.map_new_area = "amap -m -Q 0.1 -F 20 -A 20 -C 5000" + + if config["SYNTH_ABC_AREA_USE_NF"]: + self.map_new_area = "&nf -R 1000" + + self.max_fanout = config["MAX_FANOUT_CONSTRAINT"] + self.max_transition = ( + config.get("MAX_TRANSITION_CONSTRAINT") or 0 + ) * 1000 # ns -> ps + self.fine_tune = "" + if config["SYNTH_ABC_BUFFERING"]: + max_tr_arg = "" + if self.max_transition != 0: + max_tr_arg = f" -S {self.max_transition}" + self.fine_tune = ( + f"buffer -N {self.max_fanout}{max_tr_arg};upsize {{D}};dnsize {{D}}" + ) + elif config["SYNTH_SIZING"]: + self.fine_tune = "upsize {D};dnsize {D}" + + def generate_abc_script(self, step_dir, strategy): + strategy_clean = re.sub(r"\s+", "_", strategy) + abc_script_path = os.path.join(step_dir, f"{strategy_clean}.abc") + f = open(abc_script_path, "w") + + if strategy == "AREA 3": + # ORFS Area Script + print("strash", file=f) + print("dch", file=f) + print("map -B 0.9", file=f) + print("topo", file=f) + print("stime -c", file=f) + print(f"buffer -c -N {self.max_fanout}", file=f) + print("upsize -c", file=f) + print("dnsize -c", file=f) + elif strategy == "DELAY 4": + # ORFS Delay Script + def repeated_sequence(f): + print("&st", file=f) + print("&syn2", file=f) + print("&if -g -K 6", file=f) + print("&synch2", file=f) + print("&nf", file=f) + + print("&get -n", file=f) + print("&st", file=f) + print("&dch", file=f) + print("&nf", file=f) + + for _ in range(7): + repeated_sequence(f) + + print("&put", file=f) + print(f"buffer -c -N {self.max_fanout}", file=f) + print("topo", file=f) + print("stime -c", file=f) + print("upsize -c", file=f) + print("dnsize -c", file=f) + else: + print("fx", file=f) + print("mfs", file=f) + print("strash", file=f) + print(self.rf, file=f) + + # Resynth/Retime + if strategy == "AREA 2": + print(self.choice2, file=f) + else: + print(self.resyn2, file=f) + if strategy.startswith("AREA ") or strategy == "DELAY 3": + print(self.retime_area, file=f) + else: + print(self.retime_dly, file=f) + print("scleanup", file=f) + + if strategy == "AREA 4": + print(self.choice, file=f) + else: + print(self.choice2, file=f) + if strategy.startswith("AREA ") or strategy == "DELAY 3": + print(self.map_new_area, file=f) + else: + print(self.map_old_dly, file=f) + + # Area Recovery + if strategy in ["AREA 0", "AREA 1"]: + print(self.choice2, file=f) + print(self.map_new_area, file=f) + elif strategy in ["DELAY 1"]: + print(self.choice2, file=f) + print("map", file=f) + elif strategy in ["DELAY 2"]: + print(self.choice, file=f) + print("map", file=f) + elif strategy in ["DELAY 3"]: + print(self.choice2, file=f) + print(self.map_old_dly, file=f) + + if strategy.startswith("AREA "): + print(self.area_mfs3, file=f) + else: + print(self.delay_mfs3, file=f) + + print("retime {D}", file=f) + + # & space + print("&get -n", file=f) + print("&st", file=f) + print("&dch", file=f) + print("&nf", file=f) + print("&put", file=f) + print(self.fine_tune, file=f) + + # Common Conclusion + print("stime -p", file=f) + print("print_stats -m", file=f) + f.close() + return abc_script_path diff --git a/openlane/scripts/pyosys/json_header.py b/openlane/scripts/pyosys/json_header.py new file mode 100644 index 000000000..faa00c503 --- /dev/null +++ b/openlane/scripts/pyosys/json_header.py @@ -0,0 +1,68 @@ +# Copyright 2020-2024 Efabless Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import json +import argparse +from ys_common import ys + + +def json_header( + output, + config_in, + extra_in, +): + config = json.load(open(config_in)) + extra = json.load(open(extra_in)) + + blackbox_models = extra["blackbox_models"] + + d = ys.Design() + d.add_blackbox_models(blackbox_models) + d.read_verilog_files( + config["VERILOG_FILES"], + top=config["DESIGN_NAME"], + synth_parameters=config["SYNTH_PARAMETERS"] or [], + includes=config["VERILOG_INCLUDE_DIRS"] or [], + defines=(config["VERILOG_DEFINES"] or []) + + [ + f"PDK_{config['PDK']}", + f"SCL_{config['STD_CELL_LIBRARY']}", + "__openlane__", + "__pnr__", + ] + + [config["VERILOG_POWER_DEFINE"]], + use_synlig=config["USE_SYNLIG"], + synlig_defer=config["SYNLIG_DEFER"], + ) + d.run_pass( + "hierarchy", + "-check", + "-top", + config["DESIGN_NAME"], + "-nokeep_prints", + "-nokeep_asserts", + ) + d.run_pass("rename", "-top", config["DESIGN_NAME"]) + d.run_pass("proc") + d.run_pass("flatten") + d.run_pass("opt_clean", "-purge") + d.run_pass("json", "-o", output) + + +if __name__ == "__main__": + ap = argparse.ArgumentParser() + ap.add_argument("--output", required=True) + ap.add_argument("--config-in", required=True) + ap.add_argument("--extra-in", required=True) + + json_header(**ap.parse_args().__dict__) diff --git a/openlane/scripts/pyosys/synthesize.py b/openlane/scripts/pyosys/synthesize.py new file mode 100644 index 000000000..ebdebb6ed --- /dev/null +++ b/openlane/scripts/pyosys/synthesize.py @@ -0,0 +1,342 @@ +# Copyright 2020-2024 Efabless Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Parts of this file adapted from https://github.com/YosysHQ/yosys/blob/master/techlibs/common/synth.cc +# +# Copyright (C) 2012 Claire Xenia Wolf +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import os +import json +import shutil +import argparse + +from ys_common import ys +from construct_abc_script import ABCScriptCreator + + +def openlane_proc(d: ys.Design, report_dir: str): + d.run_pass("proc_clean") # Clean up unused procedures + d.run_pass("proc_rmdead") # Remove dead procedures + d.run_pass("proc_prune") # Prune unused parts of procedures + d.run_pass("proc_init") # Initialize procedures + d.run_pass("proc_arst") # Analyze reset signals in procedures + d.run_pass("proc_rom") # Map ROMs within procedures + d.run_pass("proc_mux") # Optimize multiplexers within procedures + d.tee( + "proc_dlatch", o=os.path.join(report_dir, "latch.rpt") + ) # Report latches after procedure processing + d.run_pass("proc_dff") # Analyze flip-flops within procedures + d.run_pass("proc_memwr") # Analyze memory writes within procedures + d.run_pass("proc_clean") # Clean up after procedure processing + + +def openlane_synth(d, top, flatten, report_dir): + openlane_proc(d, report_dir) + + if flatten: + d.run_pass("flatten") # Flatten the design hierarchy + + d.run_pass("opt_expr") # Optimize expressions + d.run_pass("opt_clean") # Clean up after optimization + + # Perform various logic optimization passes + d.run_pass("opt", "-nodffe", "-nosdff") # Optimize logic excluding flip-flops + d.run_pass("fsm") # Identify and optimize finite state machines + d.run_pass("opt") # Additional logic optimization + d.run_pass("wreduce") # Reduce logic using algebraic rewriting + d.run_pass("peepopt") # Perform local peephole optimization + d.run_pass("opt_clean") # Clean up after optimization + d.run_pass("alumacc") # Optimize arithmetic logic units + d.run_pass("share") # Share logic across the design + d.run_pass("opt") # More logic optimization + + # Memory optimization + d.run_pass("memory", "-nomap") # Analyze memories but don't map them yet + d.run_pass("opt_clean") # Clean up after memory analysis + + # Perform more aggressive optimization with faster runtime + d.run_pass("opt", "-fast", "-full") # Fast and comprehensive optimization + + # Technology mapping + d.run_pass("memory_map") # Map memories to standard cells + d.run_pass("opt", "-full") # More optimization after memory mapping + d.run_pass("techmap") # Map logic to standard cells from the technology library + d.run_pass("opt", "-fast") # Fast optimization after technology mapping + d.run_pass("opt", "-fast") # More fast optimization + + # Utilize the ABC logic synthesis tool for further optimization + d.run_pass("abc", "-fast") # Run ABC with fast settings + + # Additional optimization after ABC + d.run_pass("opt", "-fast") # Fast optimization + + # Check design hierarchy again + d.run_pass("hierarchy", "-check", "-nokeep_prints", "-nokeep_asserts") + + # Generate design statistics + d.run_pass("stat") # Calculate design statistics + + # Perform design rule checking + d.run_pass("check") # Check for design rule violations + + +def synthesize( + output, + config_in, + extra_in, + lighter_dff_map, +): + config = json.load(open(config_in)) + extra = json.load(open(extra_in)) + + blackbox_models = extra["blackbox_models"] + libs = extra["libs_synth"] + + d = ys.Design() + + step_dir = os.path.dirname(output) + report_dir = os.path.join(step_dir, "reports") + os.makedirs(report_dir, exist_ok=True) + + d.add_blackbox_models(blackbox_models) + + clock_period = config["CLOCK_PERIOD"] * 1000 # ns -> ps + + # ABC only supports these two: + # https://github.com/YosysHQ/abc/blob/28d955ca97a1c4be3aed4062aec0241a734fac5d/src/map/scl/sclUtil.c#L257 + sdc_path = os.path.join(step_dir, "synthesis.abc.sdc") + with open(sdc_path, "w") as f: + print(f"set_driving_cell {config['SYNTH_DRIVING_CELL']}", file=f) + print(f"set_load {config['OUTPUT_CAP_LOAD']}", file=f) + + print(f"[INFO] Using SDC file '{sdc_path}' for ABC…") + + d.read_verilog_files( + config["VERILOG_FILES"], + top=config["DESIGN_NAME"], + synth_parameters=config["SYNTH_PARAMETERS"] or [], + includes=config["VERILOG_INCLUDE_DIRS"] or [], + defines=(config["VERILOG_DEFINES"] or []) + + [ + f"PDK_{config['PDK']}", + f"SCL_{config['STD_CELL_LIBRARY']}", + "__openlane__", + "__pnr__", + ], + use_synlig=config["USE_SYNLIG"], + synlig_defer=config["SYNLIG_DEFER"], + ) + d.run_pass( + "hierarchy", + "-check", + "-top", + config["DESIGN_NAME"], + "-nokeep_prints", + "-nokeep_asserts", + ) + d.run_pass("rename", "-top", config["DESIGN_NAME"]) + d.run_pass("select", "-module", config["DESIGN_NAME"]) + try: + d.run_pass( + "show", "-format", "dot", "-prefix", os.path.join(step_dir, "hierarchy") + ) + except Exception: + pass + d.run_pass("select", "-clear") + + lib_arguments = [] + for lib in libs: + lib_arguments.extend(["-liberty", lib]) + + if config["SYNTH_ELABORATE_ONLY"]: + openlane_proc(d, report_dir) + if config["SYNTH_ELABORATE_FLATTEN"]: + d.run_pass("flatten") + d.run_pass("setattr", "-set", "keep", "1") + d.run_pass("splitnets") + d.run_pass("opt_clean", "-purge") + d.tee("check", o=os.path.join(report_dir, "chk.rpt")) + d.tee("stat", "-json", *lib_arguments, o=os.path.join(report_dir, "stat.json")) + d.tee("stat", *lib_arguments, o=os.path.join(report_dir, "stat.rpt")) + + d.run_pass( + "write_verilog", + "-noattr", + "-noexpr", + "-nohex", + "-nodec", + "-defparam", + output, + ) + d.run_pass("write_json", f"{output}.json") + exit(0) + + if config["SYNTH_TRISTATE_MAP"] is not None: + d.run_pass("tribuf") + + adder_type = config["SYNTH_ADDER_TYPE"] + if adder_type not in ["YOSYS", "FA"]: + if mapping := config[f"SYNTH_{adder_type}_MAP"]: + print(f"[INFO] Applying {adder_type} mapping from '{mapping}'…") + d.run_pass("techmap", "-map", mapping) + + if mapping := lighter_dff_map: + print(f"Using Lighter with mapping '{mapping}'…") + d.run_pass("plugin", "-i", "lighter") + d.run_pass("reg_clock_gating", "-map", mapping) + + openlane_synth(d, config["DESIGN_NAME"], not config["SYNTH_NO_FLAT"], report_dir) + + d.run_pass("delete", "/t:$print") + d.run_pass("delete", "/t:$assert") + + try: + d.run_pass( + "show", + "-format", + "dot", + "-prefix", + os.path.join(step_dir, "primitive_techmap"), + ) + except Exception: + pass + + d.run_pass("opt") + d.run_pass("opt_clean", "-purge") + + d.tee( + "stat", "-json", *lib_arguments, o=os.path.join(report_dir, "pre_techmap.json") + ) + d.tee("stat", *lib_arguments, o=os.path.join(report_dir, "pre_techmap.rpt")) + + if tristate_mapping := config["SYNTH_TRISTATE_MAP"]: + print(f"[INFO] Applying tri-state buffer mapping from '{tristate_mapping}'…") + d.run_pass("techmap", "-map", tristate_mapping) + d.run_pass("simplemap") + if fa_mapping := config["SYNTH_FA_MAP"]: + if adder_type == "FA": + print(f"[INFO] Applying full-adder mapping from '{fa_mapping}'…") + d.run_pass("techmap", "-map", fa_mapping) + if latch_mapping := config["SYNTH_LATCH_MAP"]: + print(f"[INFO] Applying latch mapping from '{latch_mapping}'…") + d.run_pass("techmap", "-map", latch_mapping) + d.run_pass("simplemap") + if extra_mapping := config["SYNTH_EXTRA_MAPPING_FILE"]: + print(f"[INFO] Applying extra mappings from '{extra_mapping}'…") + d.run_pass("techmap", "-map", extra_mapping) + + dfflibmap_args = [] + for lib in libs: + dfflibmap_args.extend(["-liberty", lib]) + d.run_pass("dfflibmap", *dfflibmap_args) + + d.tee("stat", "-json", *lib_arguments, o=os.path.join(report_dir, "post_dff.json")) + d.tee("stat", *lib_arguments, o=os.path.join(report_dir, "post_dff.rpt")) + + script_creator = ABCScriptCreator(config) + + def run_strategy(d): + abc_script = script_creator.generate_abc_script( + step_dir, + config["SYNTH_STRATEGY"], + ) + print(f"[INFO] Using generated ABC script '{abc_script}'…") + d.run_pass( + "abc", + "-script", + abc_script, + "-D", + f"{clock_period}", + "-constr", + sdc_path, + "-showtmp", + *lib_arguments, + *(["-dff"] if config["SYNTH_ABC_DFF"] else []), + ) + + d.run_pass("setundef", "-zero") + d.run_pass( + "hilomap", + "-hicell", + config["SYNTH_TIEHI_CELL"], + "-locell", + config["SYNTH_TIELO_CELL"], + ) + + if config["SYNTH_SPLITNETS"]: + d.run_pass("splitnets") + d.run_pass("opt_clean", "-purge") + + if config["SYNTH_DIRECT_WIRE_BUFFERING"]: + d.run_pass("insbuf", "-buf", *config["SYNTH_BUFFER_CELL"].split("/")) + + d.tee("check", o=os.path.join(report_dir, "chk.rpt")) + d.tee("stat", "-json", *lib_arguments, o=os.path.join(report_dir, "stat.json")) + d.tee("stat", *lib_arguments, o=os.path.join(report_dir, "stat.rpt")) + + if config["SYNTH_AUTONAME"]: + # Generate public names for the various nets, resulting in very long + # names that include the full hierarchy, which is preferable to the + # internal names that are simply sequential numbers such as `_000019_`. + # Renamed net names can be very long, such as: + # manual_reset_gf180mcu_fd_sc_mcu7t5v0__dffq_1_Q_D_gf180mcu_ \ + # fd_sc_mcu7t5v0__nor3_1_ZN_A1_gf180mcu_fd_sc_mcu7t5v0__aoi21_ \ + # 1_A2_A1_gf180mcu_fd_sc_mcu7t5v0__nand3_1_ZN_A3_gf180mcu_fd_ \ + # sc_mcu7t5v0__and3_1_A3_Z_gf180mcu_fd_sc_mcu7t5v0__buf_1_I_Z + d.run_pass("autoname") + + d.run_pass( + "write_verilog", + "-noattr", + "-noexpr", + "-nohex", + "-nodec", + "-defparam", + output, + ) + d.run_pass("write_json", f"{output}.json") + + run_strategy(d) + + if config["SYNTH_NO_FLAT"]: + # Resynthesize, flattening + d_flat = ys.Design() + d_flat.add_blackbox_models(blackbox_models) + + shutil.copy(output, f"{output}.hierarchy.nl.v") + d_flat.run_pass("read_verilog", "-sv", output) + d_flat.run_pass("synth", "-flatten") + run_strategy(d_flat) + + +if __name__ == "__main__": + ap = argparse.ArgumentParser() + ap.add_argument("--output", required=True) + ap.add_argument("--config-in", required=True) + ap.add_argument("--extra-in", required=True) + ap.add_argument("--lighter-dff-map", required=False) + + synthesize(**ap.parse_args().__dict__) diff --git a/openlane/scripts/pyosys/ys_common.py b/openlane/scripts/pyosys/ys_common.py new file mode 100644 index 000000000..e9a7d1cf0 --- /dev/null +++ b/openlane/scripts/pyosys/ys_common.py @@ -0,0 +1,124 @@ +# Copyright 2020-2024 Efabless Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from typing import Iterable, List, Union + +try: + import libyosys as ys +except ImportError: + try: + from pyosys import libyosys as ys + except ImportError: + print( + "Could not find pyosys in 'PYTHONPATH'-- make sure Yosys is compiled with ENABLE_PYTHON set to 1." + ) + + +def _Design_run_pass(self, *command): + ys.Pass.call__YOSYS_NAMESPACE_RTLIL_Design__std_vector_string_(self, list(command)) + + +ys.Design.run_pass = _Design_run_pass # type: ignore + + +def _Design_tee(self, *command: Union[List[str], str], o: str): + self.run_pass("tee", "-o", o, *command) + + +ys.Design.tee = _Design_tee # type: ignore + + +def _Design_read_verilog_files( + self: ys.Design, + files: Iterable[str], + *, + top: str, + synth_parameters: Iterable[str], + includes: Iterable[str], + defines: Iterable[str], + use_synlig: bool = False, + synlig_defer: bool = False, +): + files = list(files) # for easier concatenation + include_args = [f"-I{dir}" for dir in includes] + define_args = [f"-D{define}" for define in defines] + chparams = {} + synlig_chparam_args = [] + for chparam in synth_parameters: + param, value = chparam.split("=", maxsplit=1) # validate + chparams[param] = value + synlig_chparam_args.append(f"-P{param}={value}") + + if use_synlig and synlig_defer: + self.run_pass("plugin", "-i", "synlig-sv") + for file in files: + self.run_pass( + "read_systemverilog", + "-defer", + "-sverilog", + *define_args, + *include_args, + file, + ) + self.run_pass( + "read_systemverilog", + "-link", + "-sverilog", + "-top", + top, + *synlig_chparam_args, + ) + elif use_synlig: + self.run_pass("plugin", "-i", "synlig-sv") + self.run_pass( + "read_systemverilog", + "-sverilog", + "-top", + top, + *define_args, + *include_args, + *synlig_chparam_args, + *files, + ) + else: + for file in files: + self.run_pass( + "read_verilog", "-noautowire", "-sv", *include_args, *define_args, file + ) + for param, value in chparams.items(): + self.run_pass("chparam", "-set", param, value, top) + + +ys.Design.read_verilog_files = _Design_read_verilog_files # type: ignore + + +def _Design_add_blackbox_models(self, models: Iterable[str]): + for model in models: + if model.endswith(".v") or model.endswith(".sv"): + self.run_pass("read_verilog", "-sv", "-lib", model) + elif model.endswith(".lib"): + self.run_pass( + "read_liberty", + "-lib", + "-ignore_miss_dir", + "-setattr", + "blackbox", + model, + ) + else: + print( + f"[ERROR] Black-box model '{model}' has an unrecognized file extension." + ) + + +ys.Design.add_blackbox_models = _Design_add_blackbox_models # type: ignore diff --git a/openlane/scripts/yosys/common.tcl b/openlane/scripts/yosys/common.tcl deleted file mode 100644 index 1d25a8c26..000000000 --- a/openlane/scripts/yosys/common.tcl +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright 2020-2023 Efabless Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Parts of this file adapted from https://github.com/YosysHQ/yosys/blob/master/techlibs/common/synth.cc -# -# Copyright (C) 2012 Claire Xenia Wolf -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -source $::env(_TCL_ENV_IN) - -namespace eval yosys_ol { - proc ol_proc {report_dir} { - proc_clean - proc_rmdead - proc_prune - proc_init - proc_arst - proc_rom - proc_mux - tee -o "$report_dir/latch.rpt" proc_dlatch - proc_dff - proc_memwr - proc_clean - tee -o "$report_dir/pre_synth_chk.rpt" check - opt_expr - } - - proc ol_synth {design_name report_dir} { - hierarchy -check -top $design_name -nokeep_prints -nokeep_asserts - yosys_ol::ol_proc $report_dir - if { $::env(SYNTH_NO_FLAT) != 1 } { - flatten - } - opt_expr - opt_clean - opt -nodffe -nosdff - fsm - opt - wreduce - peepopt - opt_clean - alumacc - share - opt - memory -nomap - opt_clean - - opt -fast -full - memory_map - opt -full - techmap - opt -fast - abc -fast - opt -fast - hierarchy -check -nokeep_prints -nokeep_asserts - stat - check - } - - proc read_verilog_files {top_module} { - set verilog_include_args [list] - if {[info exist ::env(VERILOG_INCLUDE_DIRS)]} { - foreach dir $::env(VERILOG_INCLUDE_DIRS) { - lappend verilog_include_args "-I$dir" - } - } - - set synlig_params [list] - - if { [info exists ::env(SYNTH_PARAMETERS) ] } { - foreach define $::env(SYNTH_PARAMETERS) { - set param_and_value [split $define "="] - lassign $param_and_value param value - lappend synlig_params "-P$param=$value" - } - } - - if { $::env(USE_SYNLIG) && $::env(SYNLIG_DEFER) } { - foreach file $::env(VERILOG_FILES) { - read_systemverilog -defer {*}$::_synlig_defines -sverilog {*}$verilog_include_args $file - } - read_systemverilog -link -top $::env(DESIGN_NAME) {*}$synlig_params - } elseif { $::env(USE_SYNLIG) } { - read_systemverilog -top $::env(DESIGN_NAME) {*}$::_synlig_defines {*}$synlig_params -sverilog {*}$verilog_include_args {*}$::env(VERILOG_FILES) - } else { - foreach file $::env(VERILOG_FILES) { - read_verilog -noautowire -sv {*}$verilog_include_args $file - } - - if { [info exists ::env(SYNTH_PARAMETERS) ] } { - foreach define $::env(SYNTH_PARAMETERS) { - set param_and_value [split $define "="] - lassign $param_and_value param value - chparam -set $param $value $top_module - } - } - } - } -} - diff --git a/openlane/scripts/yosys/construct_abc_script.tcl b/openlane/scripts/yosys/construct_abc_script.tcl deleted file mode 100644 index 00b793098..000000000 --- a/openlane/scripts/yosys/construct_abc_script.tcl +++ /dev/null @@ -1,116 +0,0 @@ -namespace eval yosys_ol { - set abc_rs_K "resub,-K," - set abc_rs "resub" - set abc_rsz "resub,-z" - set abc_rf "drf,-l" - set abc_rfz "drf,-l,-z" - set abc_rw "drw,-l" - set abc_rwz "drw,-l,-z" - set abc_rw_K "drw,-l,-K" - if { $::env(SYNTH_ABC_LEGACY_REFACTOR) == "1" } { - set abc_rf "refactor" - set abc_rfz "refactor,-z" - } - if { $::env(SYNTH_ABC_LEGACY_REWRITE) == "1" } { - set abc_rw "rewrite" - set abc_rwz "rewrite,-z" - set abc_rw_K "rewrite,-K" - } - set abc_b "balance" - - set abc_resyn2 "${abc_b}; ${abc_rw}; ${abc_rf}; ${abc_b}; ${abc_rw}; ${abc_rwz}; ${abc_b}; ${abc_rfz}; ${abc_rwz}; ${abc_b}" - set abc_share "strash; multi,-m; ${abc_resyn2}" - set abc_resyn2a "${abc_b};${abc_rw};${abc_b};${abc_rw};${abc_rwz};${abc_b};${abc_rwz};${abc_b}" - set abc_resyn3 "balance;resub;resub,-K,6;balance;resub,-z;resub,-z,-K,6;balance;resub,-z,-K,5;balance" - set abc_resyn2rs "${abc_b};${abc_rs_K},6;${abc_rw};${abc_rs_K},6,-N,2;${abc_rf};${abc_rs_K},8;${abc_rw};${abc_rs_K},10;${abc_rwz};${abc_rs_K},10,-N,2;${abc_b},${abc_rs_K},12;${abc_rfz};${abc_rs_K},12,-N,2;${abc_rwz};${abc_b}" - - set abc_choice "fraig_store; ${abc_resyn2}; fraig_store; ${abc_resyn2}; fraig_store; fraig_restore" - set abc_choice2 "fraig_store; balance; fraig_store; ${abc_resyn2}; fraig_store; ${abc_resyn2}; fraig_store; ${abc_resyn2}; fraig_store; fraig_restore" - - set abc_map_old_cnt "map,-p,-a,-B,0.2,-A,0.9,-M,0" - set abc_map_old_dly "map,-p,-B,0.2,-A,0.9,-M,0" - set abc_retime_area "retime,-D,{D},-M,5" - set abc_retime_dly "retime,-D,{D},-M,6" - set abc_map_new_area "amap,-m,-Q,0.1,-F,20,-A,20,-C,5000" - - set abc_area_recovery_1 "${abc_choice}; map;" - set abc_area_recovery_2 "${abc_choice2}; map;" - - set map_old_cnt "map,-p,-a,-B,0.2,-A,0.9,-M,0" - set map_old_dly "map,-p,-B,0.2,-A,0.9,-M,0" - set abc_retime_area "retime,-D,{D},-M,5" - set abc_retime_dly "retime,-D,{D},-M,6" - set abc_map_new_area "amap,-m,-Q,0.1,-F,20,-A,20,-C,5000" - - if { $::env(SYNTH_ABC_BUFFERING) } { - set max_tr_arg "" - if { $max_TR != 0 } { - set max_tr_arg ",-S,${max_TR}" - } - set abc_fine_tune "buffer,-N,${max_FO}${max_tr_arg};upsize,{D};dnsize,{D}" - } elseif {$::env(SYNTH_SIZING)} { - set abc_fine_tune "upsize,{D};dnsize,{D}" - } else { - set abc_fine_tune "" - } - - set area_scripts [list \ - "+read_constr,${sdc_file};fx;mfs;strash;${abc_rf};${abc_resyn2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};retime,-D,{D};&get,-n;&st;&dch;&nf;&put;${abc_fine_tune};stime,-p;print_stats -m" \ - \ - "+read_constr,${sdc_file};fx;mfs;strash;${abc_rf};${abc_resyn2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};${abc_choice2};${abc_map_new_area};retime,-D,{D};&get,-n;&st;&dch;&nf;&put;${abc_fine_tune};stime,-p;print_stats -m" \ - \ - "+read_constr,${sdc_file};fx;mfs;strash;${abc_rf};${abc_choice2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};${abc_choice2};${abc_map_new_area};retime,-D,{D};&get,-n;&st;&dch;&nf;&put;${abc_fine_tune};stime,-p;print_stats -m" \ - \ - "+read_constr,${sdc_file};strash;dch;map -B 0.9;topo;stime -c;buffer -c -N ${max_FO};upsize -c;dnsize -c;stime,-p;print_stats -m"] - - set delay_scripts [list \ - "+read_constr,${sdc_file};fx;mfs;strash;${abc_rf};${abc_resyn2};${abc_retime_dly}; scleanup;${abc_map_old_dly};retime,-D,{D};&get,-n;&st;&dch;&nf;&put;${abc_fine_tune};stime,-p;print_stats -m" \ - \ - "+read_constr,${sdc_file};fx;mfs;strash;${abc_rf};${abc_resyn2};${abc_retime_dly}; scleanup;${abc_choice2};${abc_map_old_dly};${abc_area_recovery_2}; retime,-D,{D};&get,-n;&st;&dch;&nf;&put;${abc_fine_tune};stime,-p;print_stats -m" \ - \ - "+read_constr,${sdc_file};fx;mfs;strash;${abc_rf};${abc_resyn2};${abc_retime_dly}; scleanup;${abc_choice};${abc_map_old_dly};${abc_area_recovery_1}; retime,-D,{D};&get,-n;&st;&dch;&nf;&put;${abc_fine_tune};stime,-p;print_stats -m" \ - \ - "+read_constr,${sdc_file};fx;mfs;strash;${abc_rf};${abc_resyn2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};${abc_choice2};${abc_map_old_dly};retime,-D,{D};&get,-n;&st;&dch;&nf;&put;${abc_fine_tune};stime,-p;print_stats -m" \ - \ - "+read_constr,${sdc_file};&get -n;&st;&dch;&nf;&put;&get -n;&st;&syn2;&if -g -K 6;&synch2;&nf;&put;&get -n;&st;&syn2;&if -g -K 6;&synch2;&nf;&put;&get -n;&st;&syn2;&if -g -K 6;&synch2;&nf;&put;&get -n;&st;&syn2;&if -g -K 6;&synch2;&nf;&put;&get -n;&st;&syn2;&if -g -K 6;&synch2;&nf;&put;buffer -c -N ${max_FO};topo;stime -c;upsize -c;dnsize -c;;stime,-p;print_stats -m"] - - proc get_abc_script {strategy} { - set strategy_parts [split $strategy] - - proc malformed_strategy {strategy} { - log -stderr "\[ERROR] Misformatted SYNTH_STRATEGY (\"$strategy\")." - log -stderr "\[ERROR] Correct format is \"DELAY 0-[expr [llength $yosys_ol::delay_scripts]-1]|AREA 0-[expr [llength $yosys_ol::area_scripts]-1]\"." - exit 1 - } - - if { [llength $strategy_parts] != 2 } { - malformed_strategy $strategy - } - - set strategy_type [lindex $strategy_parts 0] - set strategy_type_idx [lindex $strategy_parts 1] - - if { $strategy_type != "AREA" && $strategy_type != "DELAY" } { - log -stderr "\[ERROR] AREA|DELAY tokens not found. ($strategy_type)" - malformed_strategy $strategy - } - - if { $strategy_type == "DELAY" && $strategy_type_idx >= [llength $yosys_ol::delay_scripts] } { - log -stderr "\[ERROR] strategy index ($strategy_type_idx) is too high." - malformed_strategy $strategy - } - - if { $strategy_type == "AREA" && $strategy_type_idx >= [llength $yosys_ol::area_scripts] } { - log -stderr "\[ERROR] strategy index ($strategy_type_idx) is too high." - malformed_strategy $strategy - } - - if { $strategy_type == "DELAY" } { - set strategy_script [lindex $yosys_ol::delay_scripts $strategy_type_idx] - } else { - set strategy_script [lindex $yosys_ol::area_scripts $strategy_type_idx] - } - return $strategy_script - } - -} diff --git a/openlane/scripts/yosys/json_header.tcl b/openlane/scripts/yosys/json_header.tcl deleted file mode 100644 index 9d62368ce..000000000 --- a/openlane/scripts/yosys/json_header.tcl +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2020-2023 Efabless Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -yosys -import -source $::env(SCRIPTS_DIR)/yosys/common.tcl - -source $::env(_DEPS_SCRIPT) - -yosys_ol::read_verilog_files $::env(DESIGN_NAME) -hierarchy -check -top $::env(DESIGN_NAME) -nokeep_prints -nokeep_asserts -yosys rename -top $::env(DESIGN_NAME) -yosys proc -flatten -opt_clean -purge -json -o $::env(SAVE_JSON_HEADER) diff --git a/openlane/scripts/yosys/synthesize.tcl b/openlane/scripts/yosys/synthesize.tcl deleted file mode 100644 index 7f4014f8a..000000000 --- a/openlane/scripts/yosys/synthesize.tcl +++ /dev/null @@ -1,235 +0,0 @@ -# Copyright 2020-2023 Efabless Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -yosys -import - -source $::env(SCRIPTS_DIR)/yosys/common.tcl - -set report_dir $::env(STEP_DIR)/reports -file mkdir $report_dir - -# Load Macro Dependencies -source $::env(_DEPS_SCRIPT) - -# Prepare Liberty Flags -set lib_args [list] -foreach lib $::env(SYNTH_LIBS) { - lappend lib_args -liberty $lib -} - -set dfflib $::env(SYNTH_LIBS) -if {[info exists ::env(DFF_LIB_SYNTH)]} { - set dfflib $::env(DFF_LIB_SYNTH) -} - -set dfflib_args [list] -foreach lib $dfflib { - lappend dfflib_args -liberty $lib -} - -set max_FO $::env(MAX_FANOUT_CONSTRAINT) -set max_TR 0 -if { [info exist ::env(MAX_TRANSITION_CONSTRAINT)]} { - set max_TR [expr {$::env(MAX_TRANSITION_CONSTRAINT) * 1000}]; # ns -> ps -} -set clock_period [expr {$::env(CLOCK_PERIOD) * 1000}]; # ns -> ps - -# Create SDC File -set sdc_file $::env(STEP_DIR)/synthesis.sdc -set outfile [open ${sdc_file} w] -puts $outfile "set_driving_cell $::env(SYNTH_DRIVING_CELL)" -puts $outfile "set_load $::env(OUTPUT_CAP_LOAD)" -close $outfile - -source $::env(SCRIPTS_DIR)/yosys/construct_abc_script.tcl -set strategy_name $::env(SYNTH_STRATEGY) -set strategy_script [yosys_ol::get_abc_script $strategy_name] - -# Start Synthesis -if { [info exists ::env(VERILOG_FILES) ]} { - yosys_ol::read_verilog_files $::env(DESIGN_NAME) -} elseif { [info exists ::env(VHDL_FILES)] } { - ghdl {*}$::env(VHDL_FILES) -e $::env(DESIGN_NAME) -} else { - puts "SCRIPT NOT CALLED CORRECTLY: EITHER VERILOG_FILES OR VHDL_FILES MUST BE SET" - exit -1 -} - -hierarchy -check -top $::env(DESIGN_NAME) -nokeep_prints -nokeep_asserts -yosys rename -top $::env(DESIGN_NAME) -select -module $::env(DESIGN_NAME) -catch {show -format dot -prefix $::env(STEP_DIR)/hierarchy} -select -clear - -if { $::env(SYNTH_ELABORATE_ONLY) } { - yosys_ol::ol_proc $report_dir - if { $::env(SYNTH_ELABORATE_FLATTEN) } { - flatten - } - - setattr -set keep 1 - - splitnets - opt_clean -purge - - tee -o "$report_dir/chk.rpt" check - tee -o "$report_dir/stat.json" stat -json {*}$lib_args - tee -o "$report_dir/stat.log" stat {*}$lib_args - - write_verilog -noattr -noexpr -nohex -nodec -defparam "$::env(SAVE_NETLIST)" - write_json "$::env(SAVE_NETLIST).json" - exit 0 -} - -# Add various maps -set tbuf_map false -set fa_map false -if { [info exists ::env(SYNTH_TRISTATE_MAP)] } { - set tbuf_map true - tribuf -} -set adder_type $::env(SYNTH_ADDER_TYPE) -if { !($adder_type in [list "YOSYS" "FA" "RCA" "CSA"]) } { - log -stderr "\[ERROR] Misformatted SYNTH_ADDER_TYPE (\"$::env(SYNTH_ADDER_TYPE)\")." - log -stderr "\[ERROR] Correct format is \"YOSYS|FA|RCA|CSA\"." - exit 1 -} -if { $adder_type == "RCA"} { - if { [info exists ::env(SYNTH_RCA_MAP)] } { - log "\[INFO] Applying ripple carry adder mapping from '$::env(RIPPLE_CARRY_ADDER_MAP)'..." - techmap -map $::env(RIPPLE_CARRY_ADDER_MAP) - } -} elseif { $adder_type == "CSA"} { - if { [info exists ::env(SYNTH_CSA_MAP)] } { - log "\[INFO] Applying carry-select adder mapping from '$::env(SYNTH_CSA_MAP)'..." - techmap -map $::env(SYNTH_CSA_MAP) - } -} elseif { $adder_type == "FA"} { - if { [info exists ::env(SYNTH_FA_MAP)] } { - set fa_map true - log "\[INFO] Applying carry-select adder mapping from '$::env(SYNTH_FA_MAP)'..." - } -} - -if { [info exists ::env(_LIGHTER_DFF_MAP)] } { - puts "Using Lighter with map '$::env(_LIGHTER_DFF_MAP)'…" - reg_clock_gating -map $::env(_LIGHTER_DFF_MAP) -} - -yosys_ol::ol_synth $::env(DESIGN_NAME) $report_dir - -delete t:\$print -delete t:\$assert - -catch {show -format dot -prefix $::env(STEP_DIR)/primitive_techmap} -opt -opt_clean -purge - -tee -o "$report_dir/pre_techmap.json" stat -json {*}$lib_args -tee -o "$report_dir/pre_techmap.log" stat {*}$lib_args - -# Techmaps -if { $tbuf_map } { - log {mapping tbuf} - log "\[INFO] Applying tri-state buffer mapping from '$::env(SYNTH_TRISTATE_MAP)'..." - techmap -map $::env(SYNTH_TRISTATE_MAP) - simplemap -} -if { $fa_map } { - log "\[INFO] Applying full-adder mapping from '$::env(SYNTH_FA_MAP)'..." - techmap -map $::env(SYNTH_FA_MAP) -} -if { [info exists ::env(SYNTH_LATCH_MAP)] } { - log "\[INFO] Applying latch mapping from '$::env(SYNTH_LATCH_MAP)'..." - techmap -map $::env(SYNTH_LATCH_MAP) - simplemap -} -if { [info exists ::env(SYNTH_EXTRA_MAPPING_FILE)] } { - log "\[INFO] Applying extra mappings from '$::env(SYNTH_EXTRA_MAPPING_FILE)'..." - techmap -map $::env(SYNTH_EXTRA_MAPPING_FILE) -} - -dfflibmap {*}$dfflib_args -tee -o "$report_dir/post_dff.json" stat -json {*}$lib_args -tee -o "$report_dir/post_dff.log" stat {*}$lib_args - -proc run_strategy {output script strategy_name {postfix_with_strategy 0}} { - upvar clock_period clock_period - upvar sdc_file sdc_file - upvar report_dir report_dir - upvar lib_args lib_args - - log "\[INFO\] Using strategy \"$strategy_name\"..." - - design -load checkpoint - abc -D "$clock_period" \ - -constr "$sdc_file" \ - -script "$script" \ - -showtmp \ - {*}$lib_args - - setundef -zero - - hilomap -hicell $::env(SYNTH_TIEHI_CELL) -locell $::env(SYNTH_TIELO_CELL) - - if { $::env(SYNTH_SPLITNETS) } { - splitnets - opt_clean -purge - } - - if { $::env(SYNTH_DIRECT_WIRE_BUFFERING) } { - insbuf -buf {*}[split $::env(SYNTH_BUFFER_CELL) "/"] - } - - tee -o "$report_dir/chk.rpt" check - tee -o "$report_dir/stat.json" stat -json {*}$lib_args - tee -o "$report_dir/stat.log" stat {*}$lib_args - - if { $::env(SYNTH_AUTONAME) } { - # Generate public names for the various nets, resulting in very long - # names that include the full hierarchy, which is preferable to the - # internal names that are simply sequential numbers such as `_000019_`. - # Renamed net names can be very long, such as: - # manual_reset_gf180mcu_fd_sc_mcu7t5v0__dffq_1_Q_D_gf180mcu_ \ - # fd_sc_mcu7t5v0__nor3_1_ZN_A1_gf180mcu_fd_sc_mcu7t5v0__aoi21_ \ - # 1_A2_A1_gf180mcu_fd_sc_mcu7t5v0__nand3_1_ZN_A3_gf180mcu_fd_ \ - # sc_mcu7t5v0__and3_1_A3_Z_gf180mcu_fd_sc_mcu7t5v0__buf_1_I_Z - autoname - } - write_verilog -noattr -noexpr -nohex -nodec -defparam $output - write_json "$output.json" - design -reset -} -design -save checkpoint - -run_strategy\ - "$::env(SAVE_NETLIST)"\ - "$strategy_script"\ - "$strategy_name" - -if { $::env(SYNTH_NO_FLAT) } { - design -reset - - source $::env(_DEPS_SCRIPT) - - file copy -force $::env(SAVE_NETLIST) $::env(STEP_DIR)/$::env(DESIGN_NAME).hierarchy.nl.v - read_verilog -sv $::env(SAVE_NETLIST) - synth -flatten - - design -save checkpoint - run_strategy\ - "$::env(SAVE_NETLIST)"\ - "$strategy_script"\ - "$strategy_name" -} - diff --git a/openlane/steps/pyosys.py b/openlane/steps/pyosys.py new file mode 100644 index 000000000..5e747100b --- /dev/null +++ b/openlane/steps/pyosys.py @@ -0,0 +1,609 @@ +# Copyright 2024 Efabless Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import re +import io +import json +import fnmatch +import subprocess +from decimal import Decimal +from abc import abstractmethod +from typing import List, Literal, Optional, Set, Tuple + +from .step import ViewsUpdate, MetricsUpdate, Step + +from ..config import Variable +from ..state import State, DesignFormat +from ..logging import debug, verbose +from ..common import Path, get_script_dir, process_list_file + +verilog_rtl_cfg_vars = [ + Variable( + "VERILOG_FILES", + List[Path], + "The paths of the design's Verilog files.", + ), + Variable( + "VERILOG_DEFINES", + Optional[List[str]], + "Preprocessor defines for input Verilog files.", + deprecated_names=["SYNTH_DEFINES"], + ), + Variable( + "VERILOG_POWER_DEFINE", + str, + "Specifies the name of the define used to guard power and ground connections in the input RTL.", + deprecated_names=["SYNTH_USE_PG_PINS_DEFINES", "SYNTH_POWER_DEFINE"], + default="USE_POWER_PINS", + ), + Variable( + "VERILOG_INCLUDE_DIRS", + Optional[List[str]], + "Specifies the Verilog `include` directories.", + ), + Variable( + "USE_SYNLIG", + bool, + "Use the Synlig plugin to process files, which has better SystemVerilog parsing capabilities but may not be compatible with all Yosys commands and attributes.", + default=False, + ), + Variable( + "SYNLIG_DEFER", + bool, + "Uses -defer flag when reading files the Synlig plugin, which may improve performance by reading each file separately, but is experimental.", + default=False, + ), +] + +starts_with_whitespace = re.compile(r"^\s+.+$") + +yosys_cell_rx = r"cell\s+\S+\s+\((\S+)\)" + + +def _check_any_tristate( + cells: List[str], + tristate_patterns: List[str], +): + for cell in cells: + for tristate_pattern in tristate_patterns: + if fnmatch.fnmatch(cell, tristate_pattern): + return True + + return False + + +def _parse_yosys_check( + report: io.TextIOBase, + tristate_patterns: Optional[List[str]] = None, + tristate_okay: bool = False, + elaborate_only: bool = False, +) -> int: + verbose("Parsing synthesis checks…") + errors_encountered: int = 0 + last_warning = None + current_warning = None + + tristate_patterns = tristate_patterns or [] + + for line in report: + if line.startswith("Warning:") or line.startswith("Found and reported"): + last_warning = current_warning + current_warning = line + if last_warning is None: + continue + + cells = re.findall(yosys_cell_rx, last_warning) + + if elaborate_only and "but has no driver" in last_warning: + debug("Ignoring undriven cell in elaborate-only mode:") + debug(last_warning) + elif tristate_okay and ( + ("tribuf" in last_warning) + or _check_any_tristate(cells, tristate_patterns) + ): + debug("Ignoring tristate-related error:") + debug(last_warning) + else: + debug("Encountered check error:") + debug(last_warning) + errors_encountered += 1 + elif ( + starts_with_whitespace.match(line) is not None + and current_warning is not None + ): + current_warning += line + else: + pass + return errors_encountered + + +verilog_rtl_cfg_vars = [ + Variable( + "VERILOG_FILES", + List[Path], + "The paths of the design's Verilog files.", + ), + Variable( + "VERILOG_DEFINES", + Optional[List[str]], + "Preprocessor defines for input Verilog files.", + deprecated_names=["SYNTH_DEFINES"], + ), + Variable( + "VERILOG_POWER_DEFINE", + str, + "Specifies the name of the define used to guard power and ground connections in the input RTL.", + deprecated_names=["SYNTH_USE_PG_PINS_DEFINES", "SYNTH_POWER_DEFINE"], + default="USE_POWER_PINS", + ), + Variable( + "VERILOG_INCLUDE_DIRS", + Optional[List[str]], + "Specifies the Verilog `include` directories.", + ), + Variable( + "SYNTH_PARAMETERS", + Optional[List[str]], + "Key-value pairs to be `chparam`ed in Yosys, in the format `key1=value1`.", + ), + Variable( + "USE_SYNLIG", + bool, + "Use the Synlig plugin to process files, which has better SystemVerilog parsing capabilities but may not be compatible with all Yosys commands and attributes.", + default=False, + ), + Variable( + "SYNLIG_DEFER", + bool, + "Uses -defer flag when reading files the Synlig plugin, which may improve performance by reading each file separately, but is experimental.", + default=False, + ), +] + + +class PyosysStep(Step): + config_vars = [ + Variable( + "SYNTH_LATCH_MAP", + Optional[Path], + "A path to a file containing the latch mapping for Yosys.", + pdk=True, + ), + Variable( + "SYNTH_TRISTATE_MAP", + Optional[Path], + "A path to a file containing the tri-state buffer mapping for Yosys.", + deprecated_names=["TRISTATE_BUFFER_MAP"], + pdk=True, + ), + Variable( + "SYNTH_CSA_MAP", + Optional[Path], + "A path to a file containing the carry-select adder mapping for Yosys.", + deprecated_names=["CARRY_SELECT_ADDER_MAP"], + pdk=True, + ), + Variable( + "SYNTH_RCA_MAP", + Optional[Path], + "A path to a file containing the ripple-carry adder mapping for Yosys.", + deprecated_names=["RIPPLE_CARRY_ADDER_MAP"], + pdk=True, + ), + Variable( + "SYNTH_FA_MAP", + Optional[Path], + "A path to a file containing the full adder mapping for Yosys.", + deprecated_names=["FULL_ADDER_MAP"], + pdk=True, + ), + Variable( + "SYNTH_MUX_MAP", + Optional[Path], + "A path to a file containing the mux mapping for Yosys.", + pdk=True, + ), + Variable( + "SYNTH_MUX4_MAP", + Optional[Path], + "A path to a file containing the mux4 mapping for Yosys.", + pdk=True, + ), + Variable( + "USE_LIGHTER", + bool, + "Activates Lighter, an experimental plugin that attempts to optimize clock-gated flip-flops.", + default=False, + ), + Variable( + "LIGHTER_DFF_MAP", + Optional[Path], + "An override to the custom DFF map file provided for the given SCL by Lighter.", + ), + Variable( + "YOSYS_LOG_LEVEL", + Literal["ALL", "WARNING", "ERROR"], + "Which log level for Yosys. At WARNING or higher, the initialization splash is also disabled.", + default="ALL", + ), + ] + + @abstractmethod + def get_script_path(self) -> str: + pass + + def get_command(self, state_in: State) -> List[str]: + script_path = self.get_script_path() + cmd = ["yosys", "-y", script_path] + if self.config["YOSYS_LOG_LEVEL"] != "ALL": + cmd += ["-Q"] + if self.config["YOSYS_LOG_LEVEL"] == "WARNING": + cmd += ["-q"] + elif self.config["YOSYS_LOG_LEVEL"] == "ERROR": + cmd += ["-qq"] + cmd += ["--"] + cmd += ["--config", os.path.join(self.step_dir, "config.json")] + return cmd + + def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: + kwargs, env = self.extract_env(kwargs) + + env["PYTHONPATH"] = os.path.join(get_script_dir(), "pyosys") + + cmd = self.get_command(state_in) + + subprocess_result = super().run_subprocess(cmd, env=env, **kwargs) + return {}, subprocess_result["generated_metrics"] + + +class VerilogStep(PyosysStep): + power_defines: bool = False + + config_vars = PyosysStep.config_vars + verilog_rtl_cfg_vars + + def get_command(self, state_in: State) -> List[str]: + cmd = super().get_command(state_in) + + blackbox_models = [] + scl_lib_list = self.toolbox.filter_views(self.config, self.config["LIB"]) + if self.power_defines and self.config["CELL_VERILOG_MODELS"] is not None: + blackbox_models.extend( + [ + self.toolbox.create_blackbox_model( + frozenset(self.config["CELL_VERILOG_MODELS"]), + frozenset(["USE_POWER_PINS"]), + ) + ] + ) + else: + blackbox_models.extend(str(f) for f in scl_lib_list) + + # Priorities from higher to lower + format_list = ( + [ + DesignFormat.VERILOG_HEADER, + DesignFormat.POWERED_NETLIST, + DesignFormat.NETLIST, + DesignFormat.LIB, + ] + if self.power_defines + else [ + DesignFormat.VERILOG_HEADER, + DesignFormat.NETLIST, + DesignFormat.POWERED_NETLIST, + DesignFormat.LIB, + ] + ) + for view, _ in self.toolbox.get_macro_views_by_priority( + self.config, format_list + ): + blackbox_models.append(str(view)) + + if libs := self.config.get("EXTRA_LIBS"): + blackbox_models.extend(str(f) for f in libs) + if models := self.config.get("EXTRA_VERILOG_MODELS"): + blackbox_models.extend(str(f) for f in models) + + excluded_cells: Set[str] = set(self.config["EXTRA_EXCLUDED_CELLS"] or []) + excluded_cells.update( + process_list_file(self.config["SYNTH_EXCLUDED_CELL_FILE"]) + ) + excluded_cells.update(process_list_file(self.config["PNR_EXCLUDED_CELL_FILE"])) + + libs_synth = self.toolbox.remove_cells_from_lib( + frozenset([str(lib) for lib in scl_lib_list]), + excluded_cells=frozenset(excluded_cells), + ) + extra_path = os.path.join(self.step_dir, "extra.json") + with open(extra_path, "w") as f: + json.dump({"blackbox_models": blackbox_models, "libs_synth": libs_synth}, f) + cmd.extend(["--extra-in", extra_path]) + return cmd + + +@Step.factory.register() +class JsonHeader(VerilogStep): + id = "Yosys.JsonHeader" + name = "Generate JSON Header" + long_name = "Generate JSON Header" + + inputs = [] + outputs = [DesignFormat.JSON_HEADER] + + config_vars = PyosysStep.config_vars + verilog_rtl_cfg_vars + + power_defines = True + + def get_script_path(self) -> str: + return os.path.join(get_script_dir(), "pyosys", "json_header.py") + + def get_command(self, state_in: State) -> List[str]: + out_file = os.path.join( + self.step_dir, + f"{self.config['DESIGN_NAME']}.{DesignFormat.JSON_HEADER.value.extension}", + ) + return super().get_command(state_in) + ["--output", out_file] + + def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: + out_file = os.path.join( + self.step_dir, + f"{self.config['DESIGN_NAME']}.{DesignFormat.JSON_HEADER.value.extension}", + ) + views_updates, metrics_updates = super().run(state_in, **kwargs) + views_updates[DesignFormat.JSON_HEADER] = Path(out_file) + return views_updates, metrics_updates + + +class SynthesisCommon(VerilogStep): + inputs = [] # The input RTL is part of the configuration + outputs = [DesignFormat.NETLIST] + + config_vars = PyosysStep.config_vars + [ + Variable( + "SYNTH_CHECKS_ALLOW_TRISTATE", + bool, + "Ignore multiple-driver warnings if they are connected to tri-state buffers on a best-effort basis.", + default=True, + ), + Variable( + "SYNTH_AUTONAME", + bool, + "Generates names for netlist instances. This results in instance names that can be extremely long, but are more human-readable.", + default=False, + ), + Variable( + "SYNTH_STRATEGY", + Literal[ + "AREA 0", + "AREA 1", + "AREA 2", + "AREA 3", + "DELAY 0", + "DELAY 1", + "DELAY 2", + "DELAY 3", + "DELAY 4", + ], + "Strategies for abc logic synthesis and technology mapping. AREA strategies usually result in a more compact design, while DELAY strategies usually result in a design that runs at a higher frequency. Please note that there is no way to know which strategy is the best before trying them.", + default="AREA 0", + ), + Variable( + "SYNTH_ABC_BUFFERING", + bool, + "Enables `abc` cell buffering.", + default=False, + deprecated_names=["SYNTH_BUFFERING"], + ), + Variable( + "SYNTH_ABC_LEGACY_REFACTOR", + bool, + "Replaces the ABC command `drf -l` with `refactor` which matches older versions of OpenLane but is more unstable.", + default=False, + ), + Variable( + "SYNTH_ABC_LEGACY_REWRITE", + bool, + "Replaces the ABC command `drw -l` with `rewrite` which matches older versions of OpenLane but is more unstable.", + default=False, + ), + Variable( + "SYNTH_ABC_DFF", + bool, + "Passes D-flipflop cells through ABC for optimization (which can for example, eliminate identical flip-flops).", + default=False, + ), + Variable( + "SYNTH_ABC_USE_MFS3", + bool, + "Experimental and unstable; attempts a SAT-based remapping in all area and delay strategies before 'retime', which may improve PPA results but may also crash.", + default=False, + ), + Variable( + "SYNTH_ABC_AREA_USE_NF", + bool, + "Experimental and unstable; uses the &nf delay-based mapper with a very high value instead of the amap area mapper, which may be better at recovering area.", + default=False, + ), + Variable( + "SYNTH_DIRECT_WIRE_BUFFERING", + bool, + "Enables inserting buffer cells for directly connected wires.", + default=True, + deprecated_names=["SYNTH_BUFFER_DIRECT_WIRES"], + ), + Variable( + "SYNTH_SPLITNETS", + bool, + "Splits multi-bit nets into single-bit nets. Easier to trace but may not be supported by all tools.", + default=True, + ), + Variable( + "SYNTH_SIZING", + bool, + "Enables `abc` cell sizing (instead of buffering).", + default=False, + ), + Variable( + "SYNTH_NO_FLAT", + bool, + "A flag that disables flattening the hierarchy during synthesis, only flattening it after synthesis, mapping and optimizations.", + default=False, + ), + Variable( + "SYNTH_SHARE_RESOURCES", + bool, + "A flag that enables yosys to reduce the number of cells by determining shareable resources and merging them.", + default=True, + ), + Variable( + "SYNTH_ADDER_TYPE", + Literal["YOSYS", "FA", "RCA", "CSA"], + "Adder type to which the $add and $sub operators are mapped to. Possible values are `YOSYS/FA/RCA/CSA`; where `YOSYS` refers to using Yosys internal adder definition, `FA` refers to full-adder structure, `RCA` refers to ripple carry adder structure, and `CSA` refers to carry select adder.", + default="YOSYS", + ), + Variable( + "SYNTH_EXTRA_MAPPING_FILE", + Optional[Path], + "Points to an extra techmap file for yosys that runs right after yosys `synth` before generic techmap.", + ), + Variable( + "SYNTH_ELABORATE_ONLY", + bool, + '"Elaborate" the design only without attempting any logic mapping. Useful when dealing with structural Verilog netlists.', + default=False, + ), + Variable( + "SYNTH_ELABORATE_FLATTEN", + bool, + "If `SYNTH_ELABORATE_ONLY` is specified, this variable controls whether or not the top level should be flattened.", + default=True, + deprecated_names=["SYNTH_FLAT_TOP"], + ), + # Variable( + # "SYNTH_SDC_FILE", + # Optional[Path], + # "Specifies the SDC file read during all Synthesis steps", + # ), + ] + + def get_script_path(self) -> str: + return os.path.join(get_script_dir(), "pyosys", "synthesize.py") + + def get_command(self, state_in: State) -> List[str]: + out_file = os.path.join( + self.step_dir, + f"{self.config['DESIGN_NAME']}.{DesignFormat.NETLIST.value.extension}", + ) + cmd = super().get_command(state_in) + if self.config["USE_LIGHTER"]: + lighter_dff_map = self.config["LIGHTER_DFF_MAP"] + if lighter_dff_map is None: + scl = self.config["STD_CELL_LIBRARY"] + try: + raw = subprocess.check_output( + ["lighter_files", scl], encoding="utf8" + ) + files = raw.strip().splitlines() + lighter_dff_map = Path(files[0]) + except FileNotFoundError: + self.warn( + "Lighter not found or not set up with OpenLane: If you're using a manual Lighter install, try setting LIGHTER_DFF_MAP explicitly." + ) + except subprocess.CalledProcessError: + self.warn(f"{scl} not supported by Lighter.") + cmd.extend(["--lighter-dff-map", lighter_dff_map]) + return cmd + ["--output", out_file] + + def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: + out_file = os.path.join( + self.step_dir, + f"{self.config['DESIGN_NAME']}.{DesignFormat.NETLIST.value.extension}", + ) + + view_updates, metric_updates = super().run(state_in, **kwargs) + + stats_file = os.path.join(self.step_dir, "reports", "stat.json") + stats_str = open(stats_file).read() + stats = json.loads(stats_str, parse_float=Decimal) + + metric_updates["design__instance__count"] = stats["design"]["num_cells"] + if chip_area := stats["design"].get("area"): # needs nonzero area + metric_updates["design__instance__area"] = chip_area + + cells = stats["design"]["num_cells_by_type"] + safe = ["$assert"] + unmapped_cells = [ + cells[y] for y in cells.keys() if y not in safe and y.startswith("$") + ] + metric_updates["design__instance_unmapped__count"] = sum(unmapped_cells) + + check_error_count_file = os.path.join( + self.step_dir, "reports", "pre_synth_chk.rpt" + ) + metric_updates["synthesis__check_error__count"] = 0 + if os.path.exists(check_error_count_file): + metric_updates["synthesis__check_error__count"] = _parse_yosys_check( + open(check_error_count_file), + self.config["TRISTATE_CELLS"], + self.config["SYNTH_CHECKS_ALLOW_TRISTATE"], + self.config["SYNTH_ELABORATE_ONLY"], + ) + + view_updates[DesignFormat.NETLIST] = Path(out_file) + + return view_updates, metric_updates + + +@Step.factory.register() +class Synthesis(SynthesisCommon): + """ + Performs synthesis and technology mapping on Verilog RTL files + using Yosys and ABC, emitting a netlist. + + Some metrics will also be extracted and updated, namely: + + * ``design__instance__count`` + * ``design__instance_unmapped__count`` + * ``design__instance__area`` + """ + + id = "Yosys.Synthesis" + name = "Synthesis" + + config_vars = SynthesisCommon.config_vars + verilog_rtl_cfg_vars + + +@Step.factory.register() +class VHDLSynthesis(SynthesisCommon): + """ + Performs synthesis and technology mapping on VHDL files + using Yosys, GHDL and ABC, emitting a netlist. + + Some metrics will also be extracted and updated, namely: + + * ``design__instance__count`` + * ``design__instance_unmapped__count`` + * ``design__instance__area`` + """ + + id = "Yosys.VHDLSynthesis" + name = "Synthesis (VHDL)" + + config_vars = SynthesisCommon.config_vars + [ + Variable( + "VHDL_FILES", + List[Path], + "The paths of the design's VHDL files.", + ), + ] diff --git a/openlane/steps/step.py b/openlane/steps/step.py index fcc54c51b..3a58bf84f 100644 --- a/openlane/steps/step.py +++ b/openlane/steps/step.py @@ -1266,7 +1266,7 @@ def run_subprocess( output_processors.append(cls(self, report_dir, silent)) verbose( - f"Logging subprocess to [repr.filename][link=file://{os.path.abspath(log_path)}]{os.path.relpath(log_path)}[/link][/repr.filename]…" + f"Logging subprocess to [repr.filename]'{os.path.relpath(log_path)}'[/repr.filename]…" ) process = _popen_callable( cmd_str, diff --git a/openlane/steps/yosys.py b/openlane/steps/yosys.py index c50548ba0..4a905df42 100644 --- a/openlane/steps/yosys.py +++ b/openlane/steps/yosys.py @@ -12,109 +12,52 @@ # See the License for the specific language governing permissions and # limitations under the License. import os -import re -import io -import json -import fnmatch -import shutil -import tempfile import textwrap import subprocess -from decimal import Decimal from abc import abstractmethod from typing import List, Literal, Optional, Set, Tuple from .tclstep import TclStep from .step import ViewsUpdate, MetricsUpdate, Step +from .pyosys import JsonHeader, verilog_rtl_cfg_vars, Synthesis, VHDLSynthesis from ..config import Variable, Config from ..state import State, DesignFormat -from ..logging import debug, verbose, info -from ..common import Path, get_script_dir, Toolbox, TclUtils, process_list_file - -starts_with_whitespace = re.compile(r"^\s+.+$") - -yosys_cell_rx = r"cell\s+\S+\s+\((\S+)\)" - - -def _check_any_tristate( - cells: List[str], - tristate_patterns: List[str], -): - for cell in cells: - for tristate_pattern in tristate_patterns: - if fnmatch.fnmatch(cell, tristate_pattern): - return True - - return False - - -def _parse_yosys_check( - report: io.TextIOBase, - tristate_patterns: Optional[List[str]] = None, - tristate_okay: bool = False, - elaborate_only: bool = False, -) -> int: - verbose("Parsing synthesis checks…") - errors_encountered: int = 0 - last_warning = None - current_warning = None - - tristate_patterns = tristate_patterns or [] - - for line in report: - if line.startswith("Warning:") or line.startswith("Found and reported"): - last_warning = current_warning - current_warning = line - if last_warning is None: - continue - - cells = re.findall(yosys_cell_rx, last_warning) - - if elaborate_only and "but has no driver" in last_warning: - debug("Ignoring undriven cell in elaborate-only mode:") - debug(last_warning) - elif tristate_okay and ( - ("tribuf" in last_warning) - or _check_any_tristate(cells, tristate_patterns) - ): - debug("Ignoring tristate-related error:") - debug(last_warning) - else: - debug("Encountered check error:") - debug(last_warning) - errors_encountered += 1 - elif ( - starts_with_whitespace.match(line) is not None - and current_warning is not None - ): - current_warning += line - else: - pass - return errors_encountered +from ..logging import info +from ..common import Path, Toolbox, TclUtils, process_list_file + +# Re-export for back-compat +JsonHeader +Synthesis +VHDLSynthesis +# This is now only used by EQY since we moved our Yosys scripts to Python. def _generate_read_deps( config: Config, toolbox: Toolbox, power_defines: bool = False, + tcl: bool = True, ) -> str: - commands = "set ::_synlig_defines [list]\n" + commands = "" synth_defines = [ f"PDK_{config['PDK']}", - f"SCL_{config['STD_CELL_LIBRARY']}\"", + f"SCL_{config['STD_CELL_LIBRARY']}", "__openlane__", "__pnr__", ] synth_defines += ( config.get("VERILOG_DEFINES") or [] ) # VERILOG_DEFINES not defined for VHDLSynthesis + if tcl: + commands += "set ::_synlig_defines [list]\n" for define in synth_defines: commands += f"verilog_defines {TclUtils.escape(f'-D{define}')}\n" - commands += ( - f"lappend ::_synlig_defines {TclUtils.escape(f'+define+{define}')}\n" - ) + if tcl: + commands += ( + f"lappend ::_synlig_defines {TclUtils.escape(f'+define+{define}')}\n" + ) scl_lib_list = toolbox.filter_views(config, config["LIB"]) @@ -123,7 +66,8 @@ def _generate_read_deps( "VERILOG_POWER_DEFINE" ): # VERILOG_POWER_DEFINE not defined for VHDLSynthesis commands += f"verilog_defines {TclUtils.escape(f'-D{power_define}')}\n" - commands += f"lappend ::_synlig_defines {TclUtils.escape(f'+define+{power_define}')}\n" + if tcl: + commands += f"lappend ::_synlig_defines {TclUtils.escape(f'+define+{power_define}')}\n" # Try your best to use powered blackbox models if power_defines is true if power_defines and config["CELL_VERILOG_MODELS"] is not None: @@ -148,7 +92,10 @@ def _generate_read_deps( frozenset([str(lib) for lib in scl_lib_list]), excluded_cells=frozenset(excluded_cells), ) - commands += f"set ::env(SYNTH_LIBS) {TclUtils.escape(TclUtils.join(lib_synth))}\n" + if tcl: + commands += ( + f"set ::env(SYNTH_LIBS) {TclUtils.escape(TclUtils.join(lib_synth))}\n" + ) verilog_include_args = [] if dirs := config.get("VERILOG_INCLUDE_DIRS"): @@ -195,45 +142,7 @@ def _generate_read_deps( return commands -verilog_rtl_cfg_vars = [ - Variable( - "VERILOG_FILES", - List[Path], - "The paths of the design's Verilog files.", - ), - Variable( - "VERILOG_DEFINES", - Optional[List[str]], - "Preprocessor defines for input Verilog files.", - deprecated_names=["SYNTH_DEFINES"], - ), - Variable( - "VERILOG_POWER_DEFINE", - str, - "Specifies the name of the define used to guard power and ground connections in the input RTL.", - deprecated_names=["SYNTH_USE_PG_PINS_DEFINES", "SYNTH_POWER_DEFINE"], - default="USE_POWER_PINS", - ), - Variable( - "VERILOG_INCLUDE_DIRS", - Optional[List[str]], - "Specifies the Verilog `include` directories.", - ), - Variable( - "USE_SYNLIG", - bool, - "Use the Synlig plugin to process files, which has better SystemVerilog parsing capabilities but may not be compatible with all Yosys commands and attributes.", - default=False, - ), - Variable( - "SYNLIG_DEFER", - bool, - "Uses -defer flag when reading files the Synlig plugin, which may improve performance by reading each file separately, but is experimental.", - default=False, - ), -] - - +# No longer used by us, kept for back-compat class YosysStep(TclStep): config_vars = [ Variable( @@ -338,271 +247,7 @@ def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: @Step.factory.register() -class JsonHeader(YosysStep): - id = "Yosys.JsonHeader" - name = "Generate JSON Header" - long_name = "Generate JSON Header" - - inputs = [] - outputs = [DesignFormat.JSON_HEADER] - - def get_script_path(self): - return os.path.join(get_script_dir(), "yosys", "json_header.tcl") - - config_vars = YosysStep.config_vars + verilog_rtl_cfg_vars - - def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: - return super().run(state_in, power_defines=True, **kwargs) - - -class SynthesisCommon(YosysStep): - inputs = [] # The input RTL is part of the configuration - outputs = [DesignFormat.NETLIST] - - config_vars = YosysStep.config_vars + [ - Variable( - "SYNTH_CHECKS_ALLOW_TRISTATE", - bool, - "Ignore multiple-driver warnings if they are connected to tri-state buffers on a best-effort basis.", - default=True, - ), - Variable( - "SYNTH_AUTONAME", - bool, - "Generates names for netlist instances. This results in instance names that can be extremely long, but are more human-readable.", - default=False, - ), - Variable( - "SYNTH_STRATEGY", - Literal[ - "AREA 0", - "AREA 1", - "AREA 2", - "AREA 3", - "DELAY 0", - "DELAY 1", - "DELAY 2", - "DELAY 3", - "DELAY 4", - ], - "Strategies for abc logic synthesis and technology mapping. AREA strategies usually result in a more compact design, while DELAY strategies usually result in a design that runs at a higher frequency. Please note that there is no way to know which strategy is the best before trying them.", - default="AREA 0", - ), - Variable( - "SYNTH_ABC_BUFFERING", - bool, - "Enables `abc` cell buffering.", - default=False, - deprecated_names=["SYNTH_BUFFERING"], - ), - Variable( - "SYNTH_ABC_LEGACY_REFACTOR", - bool, - "Replaces the ABC command `drf -l` with `refactor` which matches older versions of OpenLane but is more unstable.", - default=False, - ), - Variable( - "SYNTH_ABC_LEGACY_REWRITE", - bool, - "Replaces the ABC command `drw -l` with `rewrite` which matches older versions of OpenLane but is more unstable.", - default=False, - ), - Variable( - "SYNTH_DIRECT_WIRE_BUFFERING", - bool, - "Enables inserting buffer cells for directly connected wires.", - default=True, - deprecated_names=["SYNTH_BUFFER_DIRECT_WIRES"], - ), - Variable( - "SYNTH_SPLITNETS", - bool, - "Splits multi-bit nets into single-bit nets. Easier to trace but may not be supported by all tools.", - default=True, - ), - Variable( - "SYNTH_SIZING", - bool, - "Enables `abc` cell sizing (instead of buffering).", - default=False, - ), - Variable( - "SYNTH_NO_FLAT", - bool, - "A flag that disables flattening the hierarchy during synthesis, only flattening it after synthesis, mapping and optimizations.", - default=False, - ), - Variable( - "SYNTH_SHARE_RESOURCES", - bool, - "A flag that enables yosys to reduce the number of cells by determining shareable resources and merging them.", - default=True, - ), - Variable( - "SYNTH_ADDER_TYPE", - Literal["YOSYS", "FA", "RCA", "CSA"], - "Adder type to which the $add and $sub operators are mapped to. Possible values are `YOSYS/FA/RCA/CSA`; where `YOSYS` refers to using Yosys internal adder definition, `FA` refers to full-adder structure, `RCA` refers to ripple carry adder structure, and `CSA` refers to carry select adder.", - default="YOSYS", - ), - Variable( - "SYNTH_EXTRA_MAPPING_FILE", - Optional[Path], - "Points to an extra techmap file for yosys that runs right after yosys `synth` before generic techmap.", - ), - Variable( - "SYNTH_PARAMETERS", - Optional[List[str]], - "Key-value pairs to be `chparam`ed in Yosys, in the format `key1=value1`.", - ), - Variable( - "SYNTH_ELABORATE_ONLY", - bool, - '"Elaborate" the design only without attempting any logic mapping. Useful when dealing with structural Verilog netlists.', - default=False, - ), - Variable( - "SYNTH_ELABORATE_FLATTEN", - bool, - "If `SYNTH_ELABORATE_ONLY` is specified, this variable controls whether or not the top level should be flattened.", - default=True, - deprecated_names=["SYNTH_FLAT_TOP"], - ), - # Variable( - # "SYNTH_SDC_FILE", - # Optional[Path], - # "Specifies the SDC file read during all Synthesis steps", - # ), - ] - - def get_script_path(self): - return os.path.join(get_script_dir(), "yosys", "synthesize.tcl") - - def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: - kwargs, env = self.extract_env(kwargs) - - if self.config["USE_LIGHTER"]: - lighter_dff_map = self.config["LIGHTER_DFF_MAP"] - if lighter_dff_map is None: - scl = self.config["STD_CELL_LIBRARY"] - try: - raw = subprocess.check_output( - ["lighter_files", scl], encoding="utf8" - ) - files = raw.strip().splitlines() - lighter_dff_map = Path(files[0]) - except FileNotFoundError: - self.warn( - "Lighter not found or not set up with OpenLane: If you're using a manual Lighter install, try setting LIGHTER_DFF_MAP explicitly." - ) - except subprocess.CalledProcessError: - self.warn(f"{scl} not supported by Lighter.") - - env["_LIGHTER_DFF_MAP"] = lighter_dff_map - - # env["_SDC_IN"] = ( - # self.config["SYNTH_SDC_FILE"] or self.config["FALLBACK_SDC_FILE"] - # ) - - views_updates, metric_updates = super().run(state_in, env=env, **kwargs) - - # The following is a naive workaround to OpenSTA not accepting defparams. - # It *should* be handled with a fix to the OpenSTA Verilog parser. - # - # This workaround was in OpenLane 1 and turns 4 this November! 🎂 - # https://github.com/The-OpenROAD-Project/OpenLane/commit/e6bc1ea5 - defparam_rx = re.compile(r"^\s*defparam\s+[\s\S]+$") - defparams_found = False - nl_path = str(views_updates[DesignFormat.NETLIST]) - with tempfile.NamedTemporaryFile("w", delete=False) as nodefparams: - for line in open(nl_path, "r"): - if defparam_rx.match(line) is not None: - defparams_found = True - nodefparams.write(f"// removed: {line}") - else: - nodefparams.write(line) - if defparams_found: - shutil.move(nl_path, f"{nl_path}-defparams") - shutil.move(nodefparams.name, nl_path) - else: - os.unlink(nodefparams.name) - - stats_file = os.path.join(self.step_dir, "reports", "stat.json") - stats_str = open(stats_file).read() - stats = json.loads(stats_str, parse_float=Decimal) - - metric_updates = {} - metric_updates["design__instance__count"] = stats["design"]["num_cells"] - if chip_area := stats["design"].get("area"): # needs nonzero area - metric_updates["design__instance__area"] = chip_area - - cells = stats["design"]["num_cells_by_type"] - safe = ["$assert"] - unmapped_cells = [ - cells[y] for y in cells.keys() if y not in safe and y.startswith("$") - ] - metric_updates["design__instance_unmapped__count"] = sum(unmapped_cells) - - check_error_count_file = os.path.join( - self.step_dir, "reports", "pre_synth_chk.rpt" - ) - metric_updates["synthesis__check_error__count"] = 0 - if os.path.exists(check_error_count_file): - metric_updates["synthesis__check_error__count"] = _parse_yosys_check( - open(check_error_count_file), - self.config["TRISTATE_CELLS"], - self.config["SYNTH_CHECKS_ALLOW_TRISTATE"], - self.config["SYNTH_ELABORATE_ONLY"], - ) - - return views_updates, metric_updates - - -@Step.factory.register() -class Synthesis(SynthesisCommon): - """ - Performs synthesis and technology mapping on Verilog RTL files - using Yosys and ABC, emitting a netlist. - - Some metrics will also be extracted and updated, namely: - - * ``design__instance__count`` - * ``design__instance_unmapped__count`` - * ``design__instance__area`` - """ - - id = "Yosys.Synthesis" - name = "Synthesis" - - config_vars = SynthesisCommon.config_vars + verilog_rtl_cfg_vars - - -@Step.factory.register() -class VHDLSynthesis(SynthesisCommon): - """ - Performs synthesis and technology mapping on VHDL files - using Yosys, GHDL and ABC, emitting a netlist. - - Some metrics will also be extracted and updated, namely: - - * ``design__instance__count`` - * ``design__instance_unmapped__count`` - * ``design__instance__area`` - """ - - id = "Yosys.VHDLSynthesis" - name = "Synthesis (VHDL)" - - config_vars = SynthesisCommon.config_vars + [ - Variable( - "VHDL_FILES", - List[Path], - "The paths of the design's VHDL files.", - ), - ] - - -@Step.factory.register() -class EQY(YosysStep): +class EQY(Step): id = "Yosys.EQY" name = "Equivalence Check" long_name = "RTL/Netlist Equivalence Check" @@ -610,32 +255,28 @@ class EQY(YosysStep): inputs = [DesignFormat.NETLIST] outputs = [] - config_vars = YosysStep.config_vars + [ - Variable( - "EQY_SCRIPT", - Optional[Path], - "An optional override for the automatically generated EQY script for more complex designs.", - ), - Variable( - "MACRO_PLACEMENT_CFG", - Optional[Path], - "This step will warn if this deprecated variable is used, as it indicates Macros are used without the new Macro object.", - ), - Variable( - "EQY_FORCE_ACCEPT_PDK", - bool, - "Attempt to run EQY even if the PDK's Verilog models are supported by this step. Will likely result in a failure.", - default=False, - ), - ] - - def get_command(self) -> List[str]: - script_path = self.get_script_path() - work_dir = os.path.join(self.step_dir, "scratch") - return ["eqy", "-f", script_path, "-d", work_dir] - - def get_script_path(self) -> str: - return os.path.join(self.step_dir, f"{self.config['DESIGN_NAME']}.eqy") + config_vars = ( + YosysStep.config_vars + + verilog_rtl_cfg_vars + + [ + Variable( + "EQY_SCRIPT", + Optional[Path], + "An optional override for the automatically generated EQY script for more complex designs.", + ), + Variable( + "MACRO_PLACEMENT_CFG", + Optional[Path], + "This step will warn if this deprecated variable is used, as it indicates Macros are used without the new Macro object.", + ), + Variable( + "EQY_FORCE_ACCEPT_PDK", + bool, + "Attempt to run EQY even if the PDK's Verilog models are supported by this step. Will likely result in a failure.", + default=False, + ), + ] + ) def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: processed_pdk = os.path.join(self.step_dir, "formal_pdk.v") @@ -660,7 +301,11 @@ def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: ) return {}, {} - with open(self.get_script_path(), "w", encoding="utf8") as f: + eqy_script_path = os.path.join( + self.step_dir, f"{self.config['DESIGN_NAME']}.eqy" + ) + + with open(eqy_script_path, "w", encoding="utf8") as f: if eqy_script := self.config["EQY_SCRIPT"]: for line in open(eqy_script, "r", encoding="utf8"): f.write(line) @@ -706,7 +351,9 @@ def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: """ ).format( design_name=self.config["DESIGN_NAME"], - dep_commands=_generate_read_deps(self.config, self.toolbox), + dep_commands=_generate_read_deps( + self.config, self.toolbox, tcl=False + ), files=TclUtils.join( [str(file) for file in self.config["VERILOG_FILES"]] ), @@ -715,5 +362,10 @@ def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: step_dir=self.step_dir, ) f.write(script) + work_dir = os.path.join(self.step_dir, "scratch") + + subprocess_result = self.run_subprocess( + ["eqy", "-f", eqy_script_path, "-d", work_dir] + ) - return super().run(state_in, **kwargs) + return {}, subprocess_result["generated_metrics"] diff --git a/type_stubs/libyosys.pyi b/type_stubs/libyosys.pyi new file mode 100644 index 000000000..6dea64aaf --- /dev/null +++ b/type_stubs/libyosys.pyi @@ -0,0 +1,24 @@ +from typing import List, Iterable + +class Design: + def __init__(self) -> None: ... + def run_pass(self, *command): ... + def tee(self, *command, o: str): ... + def read_verilog_files( + self, + files: Iterable[str], + *, + top: str, + synth_parameters: Iterable[str], + includes: Iterable[str], + defines: Iterable[str], + use_synlig: bool = False, + synlig_defer: bool = False, + ): ... + def add_blackbox_models(self, models: Iterable[str]): ... + +class Pass: + @staticmethod + def call__YOSYS_NAMESPACE_RTLIL_Design__std_vector_string_( + design: Design, cmd: List[str] + ): ... diff --git a/type_stubs/pyosys.pyi b/type_stubs/pyosys.pyi new file mode 100644 index 000000000..353be01c7 --- /dev/null +++ b/type_stubs/pyosys.pyi @@ -0,0 +1,3 @@ +import libyosys + +__all__ = ["libyosys"] From f752ac9b69589e6a6f1ec7ee812427e80aaee25e Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Wed, 21 Aug 2024 19:04:20 +0300 Subject: [PATCH 02/11] Exclude Pyosys Scripts from mypy --- .mypy.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mypy.ini b/.mypy.ini index 76e7863f6..ae9dbda48 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -1,3 +1,3 @@ [mypy] mypy_path = "./mypy_stubs" -exclude = venv|build|odbpy|scripts/klayout|docs|designs|setup.py|test|sandbox +exclude = venv|build|odbpy|scripts/klayout|scripts/pyosys|docs|designs|setup.py|test|sandbox From a5cfc8c1dd891923b6de0a8f2f80074a95478fe8 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Thu, 22 Aug 2024 11:25:25 +0300 Subject: [PATCH 03/11] Fix missing passes in proc/synth --- openlane/scripts/pyosys/synthesize.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/openlane/scripts/pyosys/synthesize.py b/openlane/scripts/pyosys/synthesize.py index ebdebb6ed..8da96c81d 100644 --- a/openlane/scripts/pyosys/synthesize.py +++ b/openlane/scripts/pyosys/synthesize.py @@ -51,15 +51,18 @@ def openlane_proc(d: ys.Design, report_dir: str): d.run_pass("proc_dff") # Analyze flip-flops within procedures d.run_pass("proc_memwr") # Analyze memory writes within procedures d.run_pass("proc_clean") # Clean up after procedure processing + d.tee("check", o=os.path.join(report_dir, "pre_synth_chk.rpt")) + d.run_pass("opt_expr") # Optimize expressions def openlane_synth(d, top, flatten, report_dir): + d.run_pass("hierarchy", "-check", "-top", top, "-nokeep_prints", "-nokeep_asserts") openlane_proc(d, report_dir) if flatten: d.run_pass("flatten") # Flatten the design hierarchy + d.run_pass("opt_expr") # Optimize expressions again - d.run_pass("opt_expr") # Optimize expressions d.run_pass("opt_clean") # Clean up after optimization # Perform various logic optimization passes @@ -87,20 +90,13 @@ def openlane_synth(d, top, flatten, report_dir): d.run_pass("opt", "-fast") # Fast optimization after technology mapping d.run_pass("opt", "-fast") # More fast optimization - # Utilize the ABC logic synthesis tool for further optimization d.run_pass("abc", "-fast") # Run ABC with fast settings + d.run_pass("opt", "-fast") # MORE fast optimization - # Additional optimization after ABC - d.run_pass("opt", "-fast") # Fast optimization - - # Check design hierarchy again - d.run_pass("hierarchy", "-check", "-nokeep_prints", "-nokeep_asserts") - - # Generate design statistics - d.run_pass("stat") # Calculate design statistics - - # Perform design rule checking - d.run_pass("check") # Check for design rule violations + # Checks and Stats + d.run_pass("hierarchy", "-check", "-top", top, "-nokeep_prints", "-nokeep_asserts") + d.run_pass("check") + d.run_pass("stat") def synthesize( From 8082f54f3e35ca4e4a1b189db95481d063117b95 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Thu, 22 Aug 2024 17:57:41 +0300 Subject: [PATCH 04/11] Fix VHDL and Power Defines Issues + Add booth/fix -dff --- default.nix | 2 +- openlane/scripts/pyosys/json_header.py | 26 +++++---- openlane/scripts/pyosys/synthesize.py | 74 ++++++++++++++++++-------- openlane/scripts/pyosys/ys_common.py | 15 +++++- openlane/steps/pyosys.py | 6 +++ type_stubs/libyosys.pyi | 8 ++- 6 files changed, 95 insertions(+), 36 deletions(-) diff --git a/default.nix b/default.nix index b6656e7d8..cbbd9e625 100644 --- a/default.nix +++ b/default.nix @@ -64,7 +64,7 @@ yosys-synlig-sv yosys-f4pga-sdc ] - ++ lib.optionals (system == "x86_64-linux") [yosys-ghdl])); + ++ lib.optionals (builtins.elem system ["x86_64-linux" "x86_64-darwin"]) [yosys-ghdl])); openroad-env = (openroad.withPythonPackages(ps: with ps; [ click diff --git a/openlane/scripts/pyosys/json_header.py b/openlane/scripts/pyosys/json_header.py index faa00c503..adf9df4a6 100644 --- a/openlane/scripts/pyosys/json_header.py +++ b/openlane/scripts/pyosys/json_header.py @@ -26,21 +26,27 @@ def json_header( blackbox_models = extra["blackbox_models"] + includes = config["VERILOG_INCLUDE_DIRS"] or [] + defines = (config["VERILOG_DEFINES"] or []) + [ + f"PDK_{config['PDK']}", + f"SCL_{config['STD_CELL_LIBRARY']}", + "__openlane__", + "__pnr__", + config["VERILOG_POWER_DEFINE"], + ] + d = ys.Design() - d.add_blackbox_models(blackbox_models) + d.add_blackbox_models( + blackbox_models, + includes=includes, + defines=defines, + ) d.read_verilog_files( config["VERILOG_FILES"], top=config["DESIGN_NAME"], synth_parameters=config["SYNTH_PARAMETERS"] or [], - includes=config["VERILOG_INCLUDE_DIRS"] or [], - defines=(config["VERILOG_DEFINES"] or []) - + [ - f"PDK_{config['PDK']}", - f"SCL_{config['STD_CELL_LIBRARY']}", - "__openlane__", - "__pnr__", - ] - + [config["VERILOG_POWER_DEFINE"]], + includes=includes, + defines=defines, use_synlig=config["USE_SYNLIG"], synlig_defer=config["SYNLIG_DEFER"], ) diff --git a/openlane/scripts/pyosys/synthesize.py b/openlane/scripts/pyosys/synthesize.py index 8da96c81d..ff3c7a5f3 100644 --- a/openlane/scripts/pyosys/synthesize.py +++ b/openlane/scripts/pyosys/synthesize.py @@ -29,6 +29,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import os +import sys import json import shutil import argparse @@ -55,13 +56,14 @@ def openlane_proc(d: ys.Design, report_dir: str): d.run_pass("opt_expr") # Optimize expressions -def openlane_synth(d, top, flatten, report_dir): +def openlane_synth(d, top, flatten, report_dir, *, booth=False, abc_dff=False): d.run_pass("hierarchy", "-check", "-top", top, "-nokeep_prints", "-nokeep_asserts") openlane_proc(d, report_dir) if flatten: d.run_pass("flatten") # Flatten the design hierarchy - d.run_pass("opt_expr") # Optimize expressions again + + d.run_pass("opt_expr") # Optimize expressions again d.run_pass("opt_clean") # Clean up after optimization @@ -72,7 +74,9 @@ def openlane_synth(d, top, flatten, report_dir): d.run_pass("wreduce") # Reduce logic using algebraic rewriting d.run_pass("peepopt") # Perform local peephole optimization d.run_pass("opt_clean") # Clean up after optimization - d.run_pass("alumacc") # Optimize arithmetic logic units + if booth: + d.run_pass("booth") + d.run_pass("alumacc") # Optimize arithmetic logic unitsb d.run_pass("share") # Share logic across the design d.run_pass("opt") # More logic optimization @@ -90,7 +94,9 @@ def openlane_synth(d, top, flatten, report_dir): d.run_pass("opt", "-fast") # Fast optimization after technology mapping d.run_pass("opt", "-fast") # More fast optimization - d.run_pass("abc", "-fast") # Run ABC with fast settings + d.run_pass( + "abc", "-fast", *(["-dff"] if abc_dff else []) + ) # Run ABC with fast settings d.run_pass("opt", "-fast") # MORE fast optimization # Checks and Stats @@ -108,6 +114,14 @@ def synthesize( config = json.load(open(config_in)) extra = json.load(open(extra_in)) + includes = config.get("VERILOG_INCLUDE_DIRS") or [] + defines = (config.get("VERILOG_DEFINES") or []) + [ + f"PDK_{config['PDK']}", + f"SCL_{config['STD_CELL_LIBRARY']}", + "__openlane__", + "__pnr__", + ] + blackbox_models = extra["blackbox_models"] libs = extra["libs_synth"] @@ -117,7 +131,7 @@ def synthesize( report_dir = os.path.join(step_dir, "reports") os.makedirs(report_dir, exist_ok=True) - d.add_blackbox_models(blackbox_models) + d.add_blackbox_models(blackbox_models, includes=includes, defines=defines) clock_period = config["CLOCK_PERIOD"] * 1000 # ns -> ps @@ -130,21 +144,26 @@ def synthesize( print(f"[INFO] Using SDC file '{sdc_path}' for ABC…") - d.read_verilog_files( - config["VERILOG_FILES"], - top=config["DESIGN_NAME"], - synth_parameters=config["SYNTH_PARAMETERS"] or [], - includes=config["VERILOG_INCLUDE_DIRS"] or [], - defines=(config["VERILOG_DEFINES"] or []) - + [ - f"PDK_{config['PDK']}", - f"SCL_{config['STD_CELL_LIBRARY']}", - "__openlane__", - "__pnr__", - ], - use_synlig=config["USE_SYNLIG"], - synlig_defer=config["SYNLIG_DEFER"], - ) + if verilog_files := config.get("VERILOG_FILES"): + d.read_verilog_files( + verilog_files, + top=config["DESIGN_NAME"], + synth_parameters=config["SYNTH_PARAMETERS"] or [], + includes=includes, + defines=defines, + use_synlig=config["USE_SYNLIG"], + synlig_defer=config["SYNLIG_DEFER"], + ) + elif vhdl_files := config.get("VHDL_FILES"): + d.run_pass("plugin", "-i", "ghdl") + d.run_pass("ghdl", *vhdl_files, "-e", config["DESIGN_NAME"]) + else: + print( + f"Script called inappropriately: config must include either VERILOG_FILES or VHDL_FILES.", + file=sys.stderr, + ) + exit(1) + d.run_pass( "hierarchy", "-check", @@ -204,7 +223,14 @@ def synthesize( d.run_pass("plugin", "-i", "lighter") d.run_pass("reg_clock_gating", "-map", mapping) - openlane_synth(d, config["DESIGN_NAME"], not config["SYNTH_NO_FLAT"], report_dir) + openlane_synth( + d, + config["DESIGN_NAME"], + not config["SYNTH_NO_FLAT"], + report_dir, + booth=config["SYNTH_MUL_BOOTH"], + abc_dff=config["SYNTH_ABC_DFF"], + ) d.run_pass("delete", "/t:$print") d.run_pass("delete", "/t:$assert") @@ -324,7 +350,11 @@ def run_strategy(d): shutil.copy(output, f"{output}.hierarchy.nl.v") d_flat.run_pass("read_verilog", "-sv", output) - d_flat.run_pass("synth", "-flatten") + d_flat.run_pass( + "synth", + "-flatten", + *(["-booth"] if config["SYNTH_MUL_BOOTH"] else []), + ) run_strategy(d_flat) diff --git a/openlane/scripts/pyosys/ys_common.py b/openlane/scripts/pyosys/ys_common.py index e9a7d1cf0..a15691f81 100644 --- a/openlane/scripts/pyosys/ys_common.py +++ b/openlane/scripts/pyosys/ys_common.py @@ -102,10 +102,21 @@ def _Design_read_verilog_files( ys.Design.read_verilog_files = _Design_read_verilog_files # type: ignore -def _Design_add_blackbox_models(self, models: Iterable[str]): +def _Design_add_blackbox_models( + self, + models: Iterable[str], + *, + includes: Iterable[str], + defines: Iterable[str], +): + include_args = [f"-I{dir}" for dir in includes] + define_args = [f"-D{define}" for define in defines] + for model in models: if model.endswith(".v") or model.endswith(".sv"): - self.run_pass("read_verilog", "-sv", "-lib", model) + self.run_pass( + "read_verilog", "-sv", "-lib", *include_args, *define_args, model + ) elif model.endswith(".lib"): self.run_pass( "read_liberty", diff --git a/openlane/steps/pyosys.py b/openlane/steps/pyosys.py index 5e747100b..26bbc92fe 100644 --- a/openlane/steps/pyosys.py +++ b/openlane/steps/pyosys.py @@ -490,6 +490,12 @@ class SynthesisCommon(VerilogStep): default=True, deprecated_names=["SYNTH_FLAT_TOP"], ), + Variable( + "SYNTH_MUL_BOOTH", + bool, + "Runs the booth pass as part of synthesis: See https://yosyshq.readthedocs.io/projects/yosys/en/latest/cmd/booth.html", + default=False, + ), # Variable( # "SYNTH_SDC_FILE", # Optional[Path], diff --git a/type_stubs/libyosys.pyi b/type_stubs/libyosys.pyi index 6dea64aaf..d93f60a19 100644 --- a/type_stubs/libyosys.pyi +++ b/type_stubs/libyosys.pyi @@ -15,7 +15,13 @@ class Design: use_synlig: bool = False, synlig_defer: bool = False, ): ... - def add_blackbox_models(self, models: Iterable[str]): ... + def add_blackbox_models( + self, + models: Iterable[str], + *, + includes: Iterable[str], + defines: Iterable[str], + ): ... class Pass: @staticmethod From 13b7c3aa2b56eade914076ea8274ce022f1912df Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Thu, 22 Aug 2024 18:37:30 +0300 Subject: [PATCH 05/11] Fix Lint --- openlane/scripts/pyosys/synthesize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlane/scripts/pyosys/synthesize.py b/openlane/scripts/pyosys/synthesize.py index ff3c7a5f3..61a01ad2b 100644 --- a/openlane/scripts/pyosys/synthesize.py +++ b/openlane/scripts/pyosys/synthesize.py @@ -159,7 +159,7 @@ def synthesize( d.run_pass("ghdl", *vhdl_files, "-e", config["DESIGN_NAME"]) else: print( - f"Script called inappropriately: config must include either VERILOG_FILES or VHDL_FILES.", + "Script called inappropriately: config must include either VERILOG_FILES or VHDL_FILES.", file=sys.stderr, ) exit(1) From 5b7225557c1c3ec6b841c0cfec3c829be0b56617 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Thu, 22 Aug 2024 19:24:43 +0300 Subject: [PATCH 06/11] Fix `&nf` usage --- openlane/flows/synth_explore.py | 87 ++++++++++--------- .../scripts/pyosys/construct_abc_script.py | 2 +- openlane/steps/pyosys.py | 4 +- 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/openlane/flows/synth_explore.py b/openlane/flows/synth_explore.py index 9baee4398..5d078fedb 100644 --- a/openlane/flows/synth_explore.py +++ b/openlane/flows/synth_explore.py @@ -79,53 +79,62 @@ def run( options.set_condensed_mode(True) - for strategy in [ - "AREA 0", - "AREA 1", - "AREA 2", - "AREA 3", - "DELAY 0", - "DELAY 1", - "DELAY 2", - "DELAY 3", - "DELAY 4", - ]: - config = self.config.copy(SYNTH_STRATEGY=strategy) - - synth_step = Yosys.Synthesis( - config, - id=f"synthesis-{strategy}", - state_in=initial_state, - ) - synth_future = self.start_step_async(synth_step) - step_list.append(synth_step) - - sdc_step = OpenROAD.CheckSDCFiles( - config, - id=f"sdc-{strategy}", - state_in=synth_future, - ) - sdc_future = self.start_step_async(sdc_step) - step_list.append(sdc_step) + for nf in [True, False]: + for strategy in [ + "AREA 0", + "AREA 1", + "AREA 2", + "AREA 3", + "DELAY 0", + "DELAY 1", + "DELAY 2", + "DELAY 3", + "DELAY 4", + ]: + config = self.config.copy( + SYNTH_ABC_AREA_USE_NF=nf, + SYNTH_STRATEGY=strategy, + ) - sta_step = OpenROAD.STAPrePNR( - config, - state_in=sdc_future, - id=f"sta-{strategy}", - ) + synth_step = Yosys.Synthesis( + config, + id=f"synthesis-{strategy}" + + ("-nf" if config["SYNTH_ABC_AREA_USE_NF"] else ""), + state_in=initial_state, + ) + synth_future = self.start_step_async(synth_step) + step_list.append(synth_step) + + sdc_step = OpenROAD.CheckSDCFiles( + config, + id=f"sdc-{strategy}" + + ("-nf" if config["SYNTH_ABC_AREA_USE_NF"] else ""), + state_in=synth_future, + ) + sdc_future = self.start_step_async(sdc_step) + step_list.append(sdc_step) + + sta_step = OpenROAD.STAPrePNR( + config, + state_in=sdc_future, + id=f"sta-{strategy}" + + ("-nf" if config["SYNTH_ABC_AREA_USE_NF"] else ""), + ) - step_list.append(sta_step) - sta_future = self.start_step_async(sta_step) + step_list.append(sta_step) + sta_future = self.start_step_async(sta_step) - synth_futures.append((config, sta_future)) + synth_futures.append((config, sta_future)) results: Dict[str, Optional[Tuple[Decimal, Decimal, Decimal, Decimal]]] = {} for config, future in synth_futures: - strategy = config["SYNTH_STRATEGY"] - results[strategy] = None + header = config["SYNTH_STRATEGY"] + ( + "-nf" if config["SYNTH_ABC_AREA_USE_NF"] else "" + ) + results[header] = None try: state = future.result() - results[strategy] = ( + results[header] = ( state.metrics["design__instance__count"], state.metrics["design__instance__area"], state.metrics["timing__setup__ws"], diff --git a/openlane/scripts/pyosys/construct_abc_script.py b/openlane/scripts/pyosys/construct_abc_script.py index a9652cb56..db68319eb 100644 --- a/openlane/scripts/pyosys/construct_abc_script.py +++ b/openlane/scripts/pyosys/construct_abc_script.py @@ -60,7 +60,7 @@ def __init__(self, config): self.map_new_area = "amap -m -Q 0.1 -F 20 -A 20 -C 5000" if config["SYNTH_ABC_AREA_USE_NF"]: - self.map_new_area = "&nf -R 1000" + self.map_new_area = "&get -n; &nf -R 1000; &put" self.max_fanout = config["MAX_FANOUT_CONSTRAINT"] self.max_transition = ( diff --git a/openlane/steps/pyosys.py b/openlane/steps/pyosys.py index 26bbc92fe..c89c27650 100644 --- a/openlane/steps/pyosys.py +++ b/openlane/steps/pyosys.py @@ -426,13 +426,13 @@ class SynthesisCommon(VerilogStep): Variable( "SYNTH_ABC_USE_MFS3", bool, - "Experimental and unstable; attempts a SAT-based remapping in all area and delay strategies before 'retime', which may improve PPA results but may also crash.", + "Experimental: attempts a SAT-based remapping in all area and delay strategies before 'retime', which may improve PPA results.", default=False, ), Variable( "SYNTH_ABC_AREA_USE_NF", bool, - "Experimental and unstable; uses the &nf delay-based mapper with a very high value instead of the amap area mapper, which may be better at recovering area.", + "Experimental: uses the &nf delay-based mapper with a very high value instead of the amap area mapper, which may be better in some scenarios at recovering area.", default=False, ), Variable( From 7b36a6ede53313fe4051632937ee7fbb86429723 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Sun, 25 Aug 2024 13:58:00 +0300 Subject: [PATCH 07/11] Fix nonsynth deletes --- openlane/scripts/pyosys/synthesize.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlane/scripts/pyosys/synthesize.py b/openlane/scripts/pyosys/synthesize.py index 61a01ad2b..dda7b7f4d 100644 --- a/openlane/scripts/pyosys/synthesize.py +++ b/openlane/scripts/pyosys/synthesize.py @@ -232,8 +232,8 @@ def synthesize( abc_dff=config["SYNTH_ABC_DFF"], ) - d.run_pass("delete", "/t:$print") - d.run_pass("delete", "/t:$assert") + d.run_pass("delete", "t:$print") + d.run_pass("delete", "t:$assert") try: d.run_pass( From 72875b869c7a1318c680db0e55e8b62d6427b9df Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Sun, 25 Aug 2024 14:55:54 +0300 Subject: [PATCH 08/11] Fix `vh` --- openlane/scripts/pyosys/ys_common.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openlane/scripts/pyosys/ys_common.py b/openlane/scripts/pyosys/ys_common.py index a15691f81..2c5e95463 100644 --- a/openlane/scripts/pyosys/ys_common.py +++ b/openlane/scripts/pyosys/ys_common.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import sys from typing import Iterable, List, Union try: @@ -113,7 +114,7 @@ def _Design_add_blackbox_models( define_args = [f"-D{define}" for define in defines] for model in models: - if model.endswith(".v") or model.endswith(".sv"): + if model.endswith(".v") or model.endswith(".sv") or model.endswith(".vh"): self.run_pass( "read_verilog", "-sv", "-lib", *include_args, *define_args, model ) @@ -128,8 +129,11 @@ def _Design_add_blackbox_models( ) else: print( - f"[ERROR] Black-box model '{model}' has an unrecognized file extension." + f"[ERROR] Black-box model '{model}' has an unrecognized file extension.", + file=sys.stderr, ) + sys.stderr.flush() + exit(-1) ys.Design.add_blackbox_models = _Design_add_blackbox_models # type: ignore From 60bcad1374d459b2a199f6232b66f47f87d10122 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 26 Aug 2024 10:08:54 +0300 Subject: [PATCH 09/11] Add support for Hierarchical RTL Macro Placer * `OpenROAD.*` * Added `log_cmd` from OpenROAD-flow-scripts -- neat idea for consistency * **Internal**: Steps now sensitive to `_OPENROAD_GUI` environment variable -- coupled with `--only`, it runs a step in OpenROAD then doesn't quit so you may inspect the result. * This is not part of the OpenLane stable API and may be broken at any moment. * `OpenROAD.CutRows` * Rows that generate less than a specified number of sites are now removed to help with PDN generation (default: 25) * Created `OpenROAD.HierarchicalMacroPlacer` * Based on OpenROAD `mpl2`-- a macro placer that uses hierarchy information from the RTL to generate a macro placement automatically * Used for macros for which a manual placement has not been provided * Created `OpenROAD.UnplaceAll` * Removes placement status of all instances. * `Yosys.Synthesis` * `SYNTH_NO_FLAT` replaced with `SYNTH_HIERARCHY_MODE` with three options: `flatten` (previously `false`), `deferred_flatten` (previously `true`) and `keep` (new value). When set to `keep`, the netlist generated is hierarchical. ## Flows * Classic * Added `OpenROAD.HierarchicalMacroPlacer` after `Odb.ManualMacroPlacement` * Moved pin placement flow before macro placement as `mpl2` requires pin placement information * Added `OpenROAD.UnplaceAll` after pin placement so rows can be cut for macros without worries about already-placed instances ## Tool Updates * `openroad` -> `49a497a` * Enhanced overlay for `or-tools` to fix a bug where trying to build OpenROAD from a git repo (using nix develop .#openroad) would Cmake always fail after the first `cmake ..` --- nix/openroad.nix | 16 +- nix/opensta.nix | 8 +- nix/overlay.nix | 21 ++- openlane/config/preprocessor.py | 2 +- openlane/flows/classic.py | 10 +- openlane/flows/sequential.py | 14 +- openlane/scripts/openroad/common/dpl.tcl | 2 +- openlane/scripts/openroad/common/grt.tcl | 6 +- openlane/scripts/openroad/common/io.tcl | 64 +++++++ .../common/set_global_connections.tcl | 4 +- .../openroad/common/set_power_nets.tcl | 2 +- openlane/scripts/openroad/cts.tcl | 4 +- openlane/scripts/openroad/cut_rows.tcl | 21 ++- openlane/scripts/openroad/drt.tcl | 4 +- openlane/scripts/openroad/floorplan.tcl | 6 +- openlane/scripts/openroad/gpl.tcl | 13 +- openlane/scripts/openroad/hiertlmp.tcl | 122 +++++++++++++ openlane/scripts/openroad/insert_buffer.tcl | 4 +- openlane/scripts/openroad/ioplacer.tcl | 3 +- openlane/scripts/openroad/irdrop.tcl | 6 +- openlane/scripts/openroad/pdn.tcl | 39 ++-- openlane/scripts/openroad/rcx.tcl | 4 +- openlane/scripts/openroad/repair_design.tcl | 2 +- .../openroad/repair_design_postgrt.tcl | 2 +- .../scripts/openroad/rsz_timing_postcts.tcl | 4 +- .../scripts/openroad/rsz_timing_postgrt.tcl | 4 +- .../openroad/sta/check_macro_instances.tcl | 2 +- openlane/scripts/openroad/tapcell.tcl | 2 +- openlane/scripts/openroad/ungpl.tcl | 23 +++ openlane/scripts/pyosys/synthesize.py | 4 +- openlane/steps/openroad.py | 167 +++++++++++++++--- openlane/steps/pyosys.py | 14 +- 32 files changed, 481 insertions(+), 118 deletions(-) create mode 100644 openlane/scripts/openroad/hiertlmp.tcl create mode 100644 openlane/scripts/openroad/ungpl.tcl diff --git a/nix/openroad.nix b/nix/openroad.nix index 98eafe26f..dfe500299 100644 --- a/nix/openroad.nix +++ b/nix/openroad.nix @@ -20,6 +20,8 @@ opensta, boost183, eigen, + cudd, + ninja, tcl, python3, readline, @@ -45,8 +47,8 @@ buildEnv, makeBinaryWrapper, buildPythonEnvForInterpreter, - rev ? "b16bda7e82721d10566ff7e2b68f1ff0be9f9e38", - sha256 ? "sha256-+JGyX81Km2XidptA3k1Y5ZPwv+4Ed39LCsPfIHWd6ac=", + rev ? "49a497abf0011b2631ce7caab8b78bd1f9d67662", + sha256 ? "sha256-3o+iT+9X8/EIf4c/+FLp6GRxPGmM5kWMwK7beyICRL0=", }: let self = clangStdenv.mkDerivation (finalAttrs: { name = "openroad"; inherit rev; @@ -62,7 +64,6 @@ "-DTCL_LIBRARY=${tcl}/lib/libtcl${clangStdenv.hostPlatform.extensions.sharedLibrary}" "-DTCL_HEADER=${tcl}/include/tcl.h" "-DUSE_SYSTEM_BOOST:BOOL=ON" - "-DCMAKE_CXX_FLAGS=-I${openroad-abc}/include" "-DENABLE_TESTS:BOOL=OFF" "-DVERBOSE=1" ]; @@ -73,23 +74,23 @@ "-DUSE_SYSTEM_ABC:BOOL=ON" "-DUSE_SYSTEM_OPENSTA:BOOL=ON" "-DOPENSTA_HOME=${opensta}" + "-DCMAKE_CXX_FLAGS=-I${eigen}/include/eigen3" "-DABC_LIBRARY=${openroad-abc}/lib/libabc.a" ]; preConfigure = '' sed -i "s/GITDIR-NOTFOUND/${rev}/" ./cmake/GetGitRevisionDescription.cmake patchShebangs ./etc/find_messages.py - - sed -i 's@#include "base/abc/abc.h"@#include @' src/rmp/src/Restructure.cpp - sed -i 's@#include "base/main/abcapis.h"@#include @' src/rmp/src/Restructure.cpp + sed -i 's@# tclReadline@target_link_libraries(openroad readline)@' src/CMakeLists.txt - sed -i 's@%include "../../src/Exception.i"@%include "../../Exception.i"@' src/dbSta/src/dbSta.i + sed -i 's@''${TCL_LIBRARY}@''${TCL_LIBRARY}\n${cudd}/lib/libcudd.a@' src/CMakeLists.txt ''; buildInputs = [ openroad-abc boost183 eigen + cudd tcl python3 readline @@ -114,6 +115,7 @@ swig4 pkg-config cmake + ninja gnumake flex bison diff --git a/nix/opensta.nix b/nix/opensta.nix index 9381bf6c9..ab14e4495 100644 --- a/nix/opensta.nix +++ b/nix/opensta.nix @@ -22,10 +22,12 @@ flex, bison, tcl, + tclreadline, zlib, + cudd, eigen, - rev ? "e01d3f163f483f233db00410b6515a767a6ca03b", - sha256 ? "sha256-0LbY5RcM+11oV3iPfAUd7hpyFPwCfCjnG0EE1LkXg5E=", + rev ? "aafee90f8a21bf7867cef2e159929440cf45b2e5", + sha256 ? "sha256-omFHcgUpZZfyA1vuF/6/cuAhG1ToSm0z35/dQ0QwUUc=", }: clangStdenv.mkDerivation (finalAttrs: { name = "opensta"; @@ -46,6 +48,8 @@ clangStdenv.mkDerivation (finalAttrs: { buildInputs = [ eigen tcl + tclreadline + cudd zlib ]; diff --git a/nix/overlay.nix b/nix/overlay.nix index f6153dcaa..804c698c9 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -17,6 +17,18 @@ new: old: { }; }; + ## Alligned alloc not available on the default SDK for x86_64-darwin (10.12!!) + or-tools = (old.or-tools.override { + stdenv = if old.stdenv.isDarwin then (old.overrideSDK old.stdenv "11.0") else old.stdenv; + }).overrideAttrs(finalAttrs: previousAttrs: { + # Based on https://github.com/google/or-tools/commit/af44f98dbeb905656b5a9fc664b5fdcffcbe1f60 + # Stops CMake going haywire on reconfigures + postPatch = previousAttrs.postPatch + '' + sed -Ei.bak 's/(NOT\s+\w+_FOUND\s+AND\s+)+//' cmake/ortoolsConfig.cmake.in + sed -Ei.bak 's/NOT absl_FOUND/NOT TARGET absl::base/' cmake/ortoolsConfig.cmake.in + ''; + }); + # Platform-specific ## Undeclared Platform clp = @@ -48,13 +60,4 @@ new: old: { stdenv = old.gccStdenv; } else (old.jshon); - - ## Alligned alloc not available on the default SDK for x86_64-darwin (10.12!!) - or-tools = - if old.system == "x86_64-darwin" - then - (old.or-tools.override { - stdenv = old.overrideSDK old.stdenv "11.0"; - }) - else (old.or-tools); } diff --git a/openlane/config/preprocessor.py b/openlane/config/preprocessor.py index 04f7069c8..66f36d5a5 100644 --- a/openlane/config/preprocessor.py +++ b/openlane/config/preprocessor.py @@ -180,7 +180,7 @@ def evaluate(expression: str, symbols: Mapping[str, Any]) -> Decimal: eval_stack.pop() eval_stack.pop() - result = Decimal(0.0) + result = Decimal("0") if token.value == "**": result = number1**number2 elif token.value == "*": diff --git a/openlane/flows/classic.py b/openlane/flows/classic.py index 703403b2a..db1c482b7 100644 --- a/openlane/flows/classic.py +++ b/openlane/flows/classic.py @@ -56,17 +56,19 @@ class Classic(SequentialFlow): OpenROAD.Floorplan, Odb.CheckMacroAntennaProperties, Odb.SetPowerConnections, + OpenROAD.GlobalPlacementSkipIO, + OpenROAD.IOPlacement, + OpenROAD.UnplaceAll, + Odb.CustomIOPlacement, + Odb.ApplyDEFTemplate, Odb.ManualMacroPlacement, + OpenROAD.HierarchicalMacroPlacer, OpenROAD.CutRows, OpenROAD.TapEndcapInsertion, Odb.AddPDNObstructions, OpenROAD.GeneratePDN, Odb.RemovePDNObstructions, Odb.AddRoutingObstructions, - OpenROAD.GlobalPlacementSkipIO, - OpenROAD.IOPlacement, - Odb.CustomIOPlacement, - Odb.ApplyDEFTemplate, OpenROAD.GlobalPlacement, Odb.WriteVerilogHeader, Checker.PowerGridViolations, diff --git a/openlane/flows/sequential.py b/openlane/flows/sequential.py index f34956865..d4c514844 100644 --- a/openlane/flows/sequential.py +++ b/openlane/flows/sequential.py @@ -13,6 +13,7 @@ # limitations under the License. from __future__ import annotations +import json import os import fnmatch from typing import ( @@ -29,7 +30,8 @@ from rapidfuzz import process, fuzz, utils from .flow import Flow, FlowException, FlowError -from ..common import Filter +from ..config import Macro +from ..common import Filter, GenericDictEncoder from ..state import State from ..logging import info, success, debug from ..steps import ( @@ -334,11 +336,6 @@ def resolve_step(matchable: Optional[str], multiple_ok: bool = False): if to_resolved and to_resolved == step.id: executing = False - if len(deferred_errors) != 0: - raise FlowError( - "One or more deferred errors were encountered:\n" - + "\n".join(deferred_errors) - ) assert self.run_dir is not None debug(f"Run concluded ▶ '{self.run_dir}'") @@ -347,5 +344,10 @@ def resolve_step(matchable: Optional[str], multiple_ok: bool = False): current_state.save_snapshot(final_views_path) except Exception as e: raise FlowException(f"Failed to save final views: {e}") + if len(deferred_errors) != 0: + raise FlowError( + "One or more deferred errors were encountered:\n" + + "\n".join(deferred_errors) + ) success("Flow complete.") return (current_state, step_list) diff --git a/openlane/scripts/openroad/common/dpl.tcl b/openlane/scripts/openroad/common/dpl.tcl index cf8158580..66dec0051 100644 --- a/openlane/scripts/openroad/common/dpl.tcl +++ b/openlane/scripts/openroad/common/dpl.tcl @@ -15,7 +15,7 @@ source $::env(SCRIPTS_DIR)/openroad/common/dpl_cell_pad.tcl remove_fillers -detailed_placement\ +log_cmd detailed_placement\ -max_displacement [subst { $::env(PL_MAX_DISPLACEMENT_X) $::env(PL_MAX_DISPLACEMENT_Y) }] if { [info exists ::env(PL_OPTIMIZE_MIRRORING)] && $::env(PL_OPTIMIZE_MIRRORING) } { diff --git a/openlane/scripts/openroad/common/grt.tcl b/openlane/scripts/openroad/common/grt.tcl index 12ba85f7a..96bc24427 100644 --- a/openlane/scripts/openroad/common/grt.tcl +++ b/openlane/scripts/openroad/common/grt.tcl @@ -26,7 +26,7 @@ lappend arg_list -verbose if { $::env(GRT_ALLOW_CONGESTION) == 1 } { lappend arg_list -allow_congestion } -puts $arg_list -global_route {*}$arg_list -write_guide $::env(STEP_DIR)/after_grt.guide \ No newline at end of file +log_cmd global_route {*}$arg_list + +write_guide $::env(STEP_DIR)/after_grt.guide diff --git a/openlane/scripts/openroad/common/io.tcl b/openlane/scripts/openroad/common/io.tcl index f3ba72fa3..4902aa8de 100644 --- a/openlane/scripts/openroad/common/io.tcl +++ b/openlane/scripts/openroad/common/io.tcl @@ -458,3 +458,67 @@ if { [namespace exists utl] } { puts "%OL_METRIC $metric $value" } } + +proc exit_unless_gui {{status 0}} { + if { ![info exists ::env(_OPENROAD_GUI)] || !$::env(_OPENROAD_GUI) } { + exit $status + } +} + + +# Code below adapted from OpenROAD Flow Scripts under the following license: +# +# BSD 3-Clause License +# +# Copyright (c) 2018-2023, The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +proc log_cmd {cmd args} { + puts "+ $cmd [join $args " "]" + $cmd {*}$args +} + +proc find_unfixed_macros {} { + set macros [list] + + foreach inst [$::block getInsts] { + set inst_master [$inst getMaster] + + # BLOCK means MACRO cells + if { ![string match [$inst_master getType] "BLOCK"] } { + continue + } + + if { [$inst isFixed] } { + continue + } + + lappend macros $inst + } + return $macros +} + diff --git a/openlane/scripts/openroad/common/set_global_connections.tcl b/openlane/scripts/openroad/common/set_global_connections.tcl index 13a48b433..cf5dd7627 100644 --- a/openlane/scripts/openroad/common/set_global_connections.tcl +++ b/openlane/scripts/openroad/common/set_global_connections.tcl @@ -45,7 +45,7 @@ proc set_global_connections {} { if { $power_pin == "" || $ground_pin == "" } { puts "PDN_MACRO_CONNECTIONS missing power and ground pin names" - exit -1 + exit_unless_gui 1 } set matched 0 @@ -57,7 +57,7 @@ proc set_global_connections {} { } if { $matched != 1 } { puts "\[ERROR\] No match found for regular expression '$instance_name' defined in PDN_MACRO_CONNECTIONS." - exit 1 + exit_unless_gui 1 } add_global_connection \ diff --git a/openlane/scripts/openroad/common/set_power_nets.tcl b/openlane/scripts/openroad/common/set_power_nets.tcl index 951b5f749..b8b623a73 100644 --- a/openlane/scripts/openroad/common/set_power_nets.tcl +++ b/openlane/scripts/openroad/common/set_power_nets.tcl @@ -17,7 +17,7 @@ if { [info exists ::env(VDD_NETS)] || [info exists ::env(GND_NETS)] } { # current assumption: they cannot have a common ground if { ! [info exists ::env(VDD_NETS)] || ! [info exists ::env(GND_NETS)] } { puts stderr "\[ERROR\] VDD_NETS and GND_NETS must *both* either be defined or undefined" - exit -1 + exit_unless_gui 1 } set ::env(VDD_NET) [lindex $::env(VDD_NETS) 0] set ::env(GND_NET) [lindex $::env(GND_NETS) 0] diff --git a/openlane/scripts/openroad/cts.tcl b/openlane/scripts/openroad/cts.tcl index bfbe7dd20..d54d81a23 100755 --- a/openlane/scripts/openroad/cts.tcl +++ b/openlane/scripts/openroad/cts.tcl @@ -33,7 +33,7 @@ if { [info exists ::env(CTS_MAX_CAP)] } { if { [info exists ::env(CTS_MAX_SLEW)] } { lappend cts_characterization_args -max_slew [expr {$::env(CTS_MAX_SLEW) * 1e-9}]; # ns -> S } -configure_cts_characterization {*}$cts_characterization_args +log_cmd configure_cts_characterization {*}$cts_characterization_args puts "\[INFO\] Performing clock tree synthesis…" puts "\[INFO\] Looking for the following net(s): $::env(CLOCK_NET)" @@ -55,7 +55,7 @@ if { $::env(CTS_DISABLE_POST_PROCESSING) } { lappend arg_list -post_cts_disable } -clock_tree_synthesis {*}$arg_list +log_cmd clock_tree_synthesis {*}$arg_list set_propagated_clock [all_clocks] diff --git a/openlane/scripts/openroad/cut_rows.tcl b/openlane/scripts/openroad/cut_rows.tcl index 076e37a6d..b124b5d40 100644 --- a/openlane/scripts/openroad/cut_rows.tcl +++ b/openlane/scripts/openroad/cut_rows.tcl @@ -14,11 +14,30 @@ source $::env(SCRIPTS_DIR)/openroad/common/io.tcl read_current_odb -cut_rows\ +log_cmd cut_rows\ -endcap_master $::env(ENDCAP_CELL)\ -halo_width_x $::env(FP_MACRO_HORIZONTAL_HALO)\ -halo_width_y $::env(FP_MACRO_VERTICAL_HALO) +# Prune really short rows (<25 sites) so the PDN doesn't scream and complain +## Replace with https://github.com/The-OpenROAD-Project/OpenROAD/issues/5648 when this is available +foreach lib $::libs { + set current_sites [$lib getSites] + foreach site $current_sites { + set name [$site getName] + set ::sites($name) $site + } +} + +set ::default_site $::sites($::env(PLACE_SITE)) +foreach row [$::block getRows] { + set bbox [$row getBBox] + set site_count [expr ([$bbox xMax] - [$bbox xMin]) / [$::default_site getWidth]] + if { $site_count < 25 } { + odb::dbRow_destroy $row + } +} + write_views report_design_area_metrics diff --git a/openlane/scripts/openroad/drt.tcl b/openlane/scripts/openroad/drt.tcl index 5e8d13397..7b912dd12 100755 --- a/openlane/scripts/openroad/drt.tcl +++ b/openlane/scripts/openroad/drt.tcl @@ -26,7 +26,7 @@ if { [info exists ::env(DRT_MAX_LAYER)] } { set max_layer $::env(DRT_MAX_LAYER) } -detailed_route\ +log_cmd detailed_route\ -bottom_routing_layer $min_layer\ -top_routing_layer $max_layer\ -output_drc $::env(STEP_DIR)/$::env(DESIGN_NAME).drc\ @@ -34,4 +34,4 @@ detailed_route\ -or_seed 42\ -verbose 1 -write_views \ No newline at end of file +write_views diff --git a/openlane/scripts/openroad/floorplan.tcl b/openlane/scripts/openroad/floorplan.tcl index f3e750f43..aa1c0e918 100755 --- a/openlane/scripts/openroad/floorplan.tcl +++ b/openlane/scripts/openroad/floorplan.tcl @@ -53,7 +53,7 @@ puts "\[INFO\] Using $::env(FP_SIZING) sizing for the floorplan." if {$::env(FP_SIZING) == "absolute"} { if { [llength $::env(DIE_AREA)] != 4 } { puts stderr "Invalid die area string '$::env(DIE_AREA)'." - exit -1 + exit_unless_gui 1 } if { ! [info exists ::env(CORE_AREA)] } { set die_ll_x [lindex $::env(DIE_AREA) 0] @@ -70,7 +70,7 @@ if {$::env(FP_SIZING) == "absolute"} { } else { if { [llength $::env(CORE_AREA)] != 4 } { puts stderr "Invalid core area string '$::env(CORE_AREA)'." - exit -1 + exit_unless_gui 1 } puts "\[INFO\] Using the set CORE_AREA; ignoring core margin parameters" } @@ -94,7 +94,7 @@ if { [info exists ::env(FP_OBSTRUCTIONS)] } { } } -initialize_floorplan {*}$arg_list +log_cmd initialize_floorplan {*}$arg_list insert_tiecells $::env(SYNTH_TIELO_CELL) -prefix "TIE_ZERO_" insert_tiecells $::env(SYNTH_TIEHI_CELL) -prefix "TIE_ONE_" diff --git a/openlane/scripts/openroad/gpl.tcl b/openlane/scripts/openroad/gpl.tcl index 1652456d3..713f72e02 100755 --- a/openlane/scripts/openroad/gpl.tcl +++ b/openlane/scripts/openroad/gpl.tcl @@ -26,11 +26,11 @@ foreach inst $::insts { } if { !$placement_needed } { - puts stderr "\[WARNING\] All instances are FIXED/FIRM." - puts stderr "\[WARNING\] No need to perform global placement." - puts stderr "\[WARNING\] Skipping…" + puts stderr "\[INFO\] All instances are FIXED/FIRM." + puts stderr "\[INFO\] No need to perform global placement." + puts stderr "\[INFO\] Skipping…" write_views - exit 0 + exit_unless_gui } set arg_list [list] @@ -53,8 +53,7 @@ if { $::env(PL_SKIP_INITIAL_PLACEMENT) } { lappend arg_list -skip_initial_place } - -if { [info exists ::env(__PL_SKIP_IO)] } { +if { $::env(STEP_ID) == "OpenROAD.GlobalPlacementSkipIO" } { lappend arg_list -skip_io } @@ -72,7 +71,7 @@ lappend arg_list -pad_right $cell_pad_side lappend arg_list -pad_left $cell_pad_side lappend arg_list -init_wirelength_coef $::env(PL_WIRE_LENGTH_COEF) -global_placement {*}$arg_list +log_cmd global_placement {*}$arg_list source $::env(SCRIPTS_DIR)/openroad/common/set_rc.tcl diff --git a/openlane/scripts/openroad/hiertlmp.tcl b/openlane/scripts/openroad/hiertlmp.tcl new file mode 100644 index 000000000..c7ae3c746 --- /dev/null +++ b/openlane/scripts/openroad/hiertlmp.tcl @@ -0,0 +1,122 @@ +# Copyright 2024 Efabless Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Code adapted from OpenROAD Flow Scripts under the following license: +# +# BSD 3-Clause License +# +# Copyright (c) 2018-2023, The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +source $::env(SCRIPTS_DIR)/openroad/common/io.tcl +read_current_odb + +if { [llength [find_unfixed_macros]] } { + report_design_area_metrics + set rtlmp_args [list] + file mkdir $::env(STEP_DIR)/reports + + lappend rtlmp_args -report_directory $::env(STEP_DIR)/reports + lappend rtlmp_args -halo_width $::env(FP_MACRO_HORIZONTAL_HALO) + lappend rtlmp_args -halo_height $::env(FP_MACRO_VERTICAL_HALO) + + if { [info exists ::env(RTLMP_MAX_LEVEL)]} { + lappend rtlmp_args -max_num_level $::env(RTLMP_MAX_LEVEL) + } + if { [info exists ::env(RTLMP_MAX_INST)]} { + lappend rtlmp_args -max_num_inst $::env(RTLMP_MAX_INST) + } + if { [info exists ::env(RTLMP_MIN_INST)]} { + lappend rtlmp_args -min_num_inst $::env(RTLMP_MIN_INST) + } + if { [info exists ::env(RTLMP_MAX_MACRO)]} { + lappend rtlmp_args -max_num_macro $::env(RTLMP_MAX_MACRO) + } + if { [info exists ::env(RTLMP_MIN_MACRO)]} { + lappend rtlmp_args -min_num_macro $::env(RTLMP_MIN_MACRO) + } + + if { [info exists ::env(RTLMP_MIN_AR)]} { + lappend rtlmp_args -min_ar $::env(RTLMP_MIN_AR) + } + if { [info exists ::env(RTLMP_SIGNATURE_NET_THRESHOLD)]} { + lappend rtlmp_args -signature_net_threshold $::env(RTLMP_SIGNATURE_NET_THRESHOLD) + } + if { [info exists ::env(RTLMP_AREA_WT)]} { + lappend rtlmp_args -area_weight $::env(RTLMP_AREA_WT) + } + if { [info exists ::env(RTLMP_WIRELENGTH_WT)]} { + lappend rtlmp_args -wirelength_weight $::env(RTLMP_WIRELENGTH_WT) + } + if { [info exists ::env(RTLMP_OUTLINE_WT)]} { + lappend rtlmp_args -outline_weight $::env(RTLMP_OUTLINE_WT) + } + if { [info exists ::env(RTLMP_BOUNDARY_WT)]} { + lappend rtlmp_args -boundary_weight $::env(RTLMP_BOUNDARY_WT) + } + + if { [info exists ::env(RTLMP_NOTCH_WT)]} { + lappend rtlmp_args -notch_weight $::env(RTLMP_NOTCH_WT) + } + + if { [info exists ::env(RTLMP_DEAD_SPACE)]} { + lappend rtlmp_args -dead_space $::env(RTLMP_DEAD_SPACE) + } + + if { [info exists ::env(RTLMP_FENCE_LX)]} { + lappend rtlmp_args -fence_lx $::env(RTLMP_FENCE_LX) + } + if { [info exists ::env(RTLMP_FENCE_LY)]} { + lappend rtlmp_args -fence_ly $::env(RTLMP_FENCE_LY) + } + if { [info exists ::env(RTLMP_FENCE_UX)]} { + lappend rtlmp_args -fence_ux $::env(RTLMP_FENCE_UX) + } + if { [info exists ::env(RTLMP_FENCE_UY)]} { + lappend rtlmp_args -fence_uy $::env(RTLMP_FENCE_UY) + } + + log_cmd rtl_macro_placer {*}$rtlmp_args +} else { + puts "\[INFO\] No macro instances found that are not 'FIXED'." +} + +write_views + +report_design_area_metrics diff --git a/openlane/scripts/openroad/insert_buffer.tcl b/openlane/scripts/openroad/insert_buffer.tcl index e0e41b7b6..032d2118b 100644 --- a/openlane/scripts/openroad/insert_buffer.tcl +++ b/openlane/scripts/openroad/insert_buffer.tcl @@ -121,7 +121,7 @@ if { [info exists ::env(INSERT_BUFFER_COMMAND) ]} { read_current_odb set arg_list [split $::env(INSERT_BUFFER_COMMAND) " "] - insert_buffer {*}$arg_list + log_cmd insert_buffer {*}$arg_list write_views -} \ No newline at end of file +} diff --git a/openlane/scripts/openroad/ioplacer.tcl b/openlane/scripts/openroad/ioplacer.tcl index 6e0aa2ab1..6a8d45a88 100755 --- a/openlane/scripts/openroad/ioplacer.tcl +++ b/openlane/scripts/openroad/ioplacer.tcl @@ -55,8 +55,7 @@ if { $::env(FP_PPL_MODE) == "annealing" } { set HMETAL $::env(FP_IO_HLAYER) set VMETAL $::env(FP_IO_VLAYER) -puts "\[INFO\] place_pins args: $arg_list" -place_pins {*}$arg_list \ +log_cmd place_pins {*}$arg_list \ -random_seed 42 \ -hor_layers $HMETAL \ -ver_layers $VMETAL diff --git a/openlane/scripts/openroad/irdrop.tcl b/openlane/scripts/openroad/irdrop.tcl index f5545707f..60fbeec7c 100644 --- a/openlane/scripts/openroad/irdrop.tcl +++ b/openlane/scripts/openroad/irdrop.tcl @@ -27,7 +27,7 @@ if { [info exists ::env(VSRC_LOC_FILES)] } { lappend arg_list -net $net lappend arg_list -voltage_file $::env(STEP_DIR)/net-$net.csv lappend arg_list -vsrc $vsrc_file - analyze_power_grid {*}$arg_list + log_cmd analyze_power_grid {*}$arg_list } puts "%OL_END_REPORT" } else { @@ -38,14 +38,14 @@ if { [info exists ::env(VSRC_LOC_FILES)] } { lappend arg_list -net $net lappend arg_list -voltage_file $::env(STEP_DIR)/net-$net.csv set_pdnsim_net_voltage -net $net -voltage $::env(LIB_VOLTAGE) - analyze_power_grid {*}$arg_list + log_cmd analyze_power_grid {*}$arg_list } foreach net "$::env(GND_NETS)" { set arg_list [list] lappend arg_list -net $net lappend arg_list -voltage_file $::env(STEP_DIR)/net-$net.csv set_pdnsim_net_voltage -net $net -voltage 0 - analyze_power_grid {*}$arg_list + log_cmd analyze_power_grid {*}$arg_list } puts "%OL_END_REPORT" } diff --git a/openlane/scripts/openroad/pdn.tcl b/openlane/scripts/openroad/pdn.tcl index b89ecae82..dbac05ad8 100644 --- a/openlane/scripts/openroad/pdn.tcl +++ b/openlane/scripts/openroad/pdn.tcl @@ -23,30 +23,31 @@ read_pdn_cfg set arg_list [list] if { $::env(FP_PDN_SKIPTRIM) } { lappend arg_list -skip_trim - puts "adding -skip_trim to pdngen" } # run PDNGEN -if {[catch {pdngen {*}$arg_list} errmsg]} { +if {[catch {log_cmd pdngen {*}$arg_list} errmsg]} { puts stderr $errmsg - exit 1 -} - -write_views -report_design_area_metrics + exit_unless_gui 1 +} else { + write_views + report_design_area_metrics -foreach {net} "$::env(VDD_NETS) $::env(GND_NETS)" { - set report_file $::env(STEP_DIR)/$net-grid-errors.rpt + foreach {net} "$::env(VDD_NETS) $::env(GND_NETS)" { + set report_file $::env(STEP_DIR)/$net-grid-errors.rpt - # For some reason, check_power_grid is… totally okay if no nodes are found - # at all. i.e. PDN generation has completely failed. - # This is a fallback file. - set f [open $report_file "w"] - puts $f "violation type: no nodes" - puts $f " srcs: " - puts $f " - N/A" - close $f + # For some reason, check_power_grid is… totally okay if no nodes are found + # at all. i.e. PDN generation has completely failed. + # This is a fallback file. + set f [open $report_file "w"] + puts $f "violation type: no nodes" + puts $f " srcs: " + puts $f " - N/A" + close $f - if { [catch {check_power_grid -net $net -error_file $report_file} err] } { - puts stderr "\[WARNING\] Grid check for $net failed: $err" + if { [catch {check_power_grid -net $net -error_file $report_file} err] } { + puts stderr "\[WARNING\] Grid check for $net failed: $err" + } } + } + diff --git a/openlane/scripts/openroad/rcx.tcl b/openlane/scripts/openroad/rcx.tcl index d6f76d28e..3001de649 100644 --- a/openlane/scripts/openroad/rcx.tcl +++ b/openlane/scripts/openroad/rcx.tcl @@ -26,7 +26,7 @@ if { !$::env(RCX_MERGE_VIA_WIRE_RES) } { # RCX puts "Using RCX ruleset '$::env(RCX_RULESET)'…" define_process_corner -ext_model_index 0 CURRENT_CORNER -extract_parasitics $rcx_flags\ +log_cmd extract_parasitics $rcx_flags\ -ext_model_file $::env(RCX_RULESET)\ -lef_res -write_views \ No newline at end of file +write_views diff --git a/openlane/scripts/openroad/repair_design.tcl b/openlane/scripts/openroad/repair_design.tcl index 3c2ea31e2..d7a91c7b8 100644 --- a/openlane/scripts/openroad/repair_design.tcl +++ b/openlane/scripts/openroad/repair_design.tcl @@ -43,7 +43,7 @@ if { $::env(DESIGN_REPAIR_BUFFER_OUTPUT_PORTS) } { } # Repair Design -repair_design -verbose \ +log_cmd repair_design -verbose \ -max_wire_length $::env(DESIGN_REPAIR_MAX_WIRE_LENGTH) \ -slew_margin $::env(DESIGN_REPAIR_MAX_SLEW_PCT) \ -cap_margin $::env(DESIGN_REPAIR_MAX_CAP_PCT) diff --git a/openlane/scripts/openroad/repair_design_postgrt.tcl b/openlane/scripts/openroad/repair_design_postgrt.tcl index d0ad8c94e..1f52212e1 100644 --- a/openlane/scripts/openroad/repair_design_postgrt.tcl +++ b/openlane/scripts/openroad/repair_design_postgrt.tcl @@ -32,7 +32,7 @@ source $::env(SCRIPTS_DIR)/openroad/common/grt.tcl estimate_parasitics -global_routing # Repair design -repair_design -verbose \ +log_cmd repair_design -verbose \ -max_wire_length $::env(GRT_DESIGN_REPAIR_MAX_WIRE_LENGTH) \ -slew_margin $::env(GRT_DESIGN_REPAIR_MAX_SLEW_PCT) \ -cap_margin $::env(GRT_DESIGN_REPAIR_MAX_CAP_PCT) diff --git a/openlane/scripts/openroad/rsz_timing_postcts.tcl b/openlane/scripts/openroad/rsz_timing_postcts.tcl index b46d341ca..f194a608f 100644 --- a/openlane/scripts/openroad/rsz_timing_postcts.tcl +++ b/openlane/scripts/openroad/rsz_timing_postcts.tcl @@ -29,7 +29,7 @@ source $::env(SCRIPTS_DIR)/openroad/common/set_rc.tcl estimate_parasitics -placement # Resize -repair_timing -verbose -setup \ +log_cmd repair_timing -verbose -setup \ -setup_margin $::env(PL_RESIZER_SETUP_SLACK_MARGIN) \ -max_buffer_percent $::env(PL_RESIZER_SETUP_MAX_BUFFER_PCT) @@ -45,7 +45,7 @@ if { $::env(PL_RESIZER_ALLOW_SETUP_VIOS) == 1 } { if { $::env(PL_RESIZER_GATE_CLONING) != 1 } { lappend arg_list -skip_gate_cloning } -repair_timing {*}$arg_list +log_cmd repair_timing {*}$arg_list # Legalize source $::env(SCRIPTS_DIR)/openroad/common/dpl.tcl diff --git a/openlane/scripts/openroad/rsz_timing_postgrt.tcl b/openlane/scripts/openroad/rsz_timing_postgrt.tcl index 5c2007a80..1ae5a1818 100644 --- a/openlane/scripts/openroad/rsz_timing_postgrt.tcl +++ b/openlane/scripts/openroad/rsz_timing_postgrt.tcl @@ -32,7 +32,7 @@ source $::env(SCRIPTS_DIR)/openroad/common/grt.tcl estimate_parasitics -global_routing # Resize -repair_timing -verbose -setup \ +log_cmd repair_timing -verbose -setup \ -setup_margin $::env(GRT_RESIZER_SETUP_SLACK_MARGIN) \ -max_buffer_percent $::env(GRT_RESIZER_SETUP_MAX_BUFFER_PCT) @@ -48,7 +48,7 @@ if { $::env(GRT_RESIZER_ALLOW_SETUP_VIOS) == 1 } { if { $::env(GRT_RESIZER_GATE_CLONING) != 1 } { lappend arg_list -skip_gate_cloning } -repair_timing {*}$arg_list +log_cmd repair_timing {*}$arg_list # Re-DPL and GRT source $::env(SCRIPTS_DIR)/openroad/common/dpl.tcl diff --git a/openlane/scripts/openroad/sta/check_macro_instances.tcl b/openlane/scripts/openroad/sta/check_macro_instances.tcl index d6aa20448..057da8c29 100644 --- a/openlane/scripts/openroad/sta/check_macro_instances.tcl +++ b/openlane/scripts/openroad/sta/check_macro_instances.tcl @@ -49,5 +49,5 @@ foreach {instance_name macro_name} $::env(_check_macro_instances) { } if { $error_count != 0 } { - exit -1 + exit_unless_gui 1 } diff --git a/openlane/scripts/openroad/tapcell.tcl b/openlane/scripts/openroad/tapcell.tcl index adcbd929d..8c6443535 100755 --- a/openlane/scripts/openroad/tapcell.tcl +++ b/openlane/scripts/openroad/tapcell.tcl @@ -14,7 +14,7 @@ source $::env(SCRIPTS_DIR)/openroad/common/io.tcl read_current_odb -tapcell\ +log_cmd tapcell\ -distance $::env(FP_TAPCELL_DIST)\ -tapcell_master "$::env(WELLTAP_CELL)"\ -endcap_master "$::env(ENDCAP_CELL)"\ diff --git a/openlane/scripts/openroad/ungpl.tcl b/openlane/scripts/openroad/ungpl.tcl new file mode 100644 index 000000000..a44c301d8 --- /dev/null +++ b/openlane/scripts/openroad/ungpl.tcl @@ -0,0 +1,23 @@ +# Copyright 2024 Efabless Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +source $::env(SCRIPTS_DIR)/openroad/common/io.tcl +read_current_odb + +set ::insts [$::block getInsts] + +foreach inst $::insts { + $inst setPlacementStatus "NONE" +} + +write_views diff --git a/openlane/scripts/pyosys/synthesize.py b/openlane/scripts/pyosys/synthesize.py index dda7b7f4d..e50b9ae44 100644 --- a/openlane/scripts/pyosys/synthesize.py +++ b/openlane/scripts/pyosys/synthesize.py @@ -226,7 +226,7 @@ def synthesize( openlane_synth( d, config["DESIGN_NAME"], - not config["SYNTH_NO_FLAT"], + config["SYNTH_HIERARCHY_MODE"] == "flatten", report_dir, booth=config["SYNTH_MUL_BOOTH"], abc_dff=config["SYNTH_ABC_DFF"], @@ -343,7 +343,7 @@ def run_strategy(d): run_strategy(d) - if config["SYNTH_NO_FLAT"]: + if config["SYNTH_HIERARCHY_MODE"] == "deferred_flatten": # Resynthesize, flattening d_flat = ys.Design() d_flat.add_blackbox_models(blackbox_models) diff --git a/openlane/steps/openroad.py b/openlane/steps/openroad.py index d3cabffd3..83cdfc914 100644 --- a/openlane/steps/openroad.py +++ b/openlane/steps/openroad.py @@ -323,7 +323,7 @@ def get_command(self) -> List[str]: metrics_path = os.path.join(self.step_dir, "or_metrics_out.json") return [ "openroad", - "-exit", + ("-gui" if os.getenv("_OPENROAD_GUI", "0") == "1" else "-exit"), "-no_splash", "-metrics", metrics_path, @@ -1082,16 +1082,16 @@ class TapEndcapInsertion(OpenROADStep): Variable( "FP_MACRO_HORIZONTAL_HALO", Decimal, - "Specify the horizontal halo size around macros while cutting rows.", - default=10, + "Specify the horizontal halo size around macros.", + default=5, units="µm", deprecated_names=["FP_TAP_HORIZONTAL_HALO"], ), Variable( "FP_MACRO_VERTICAL_HALO", Decimal, - "Specify the vertical halo size around macros while cutting rows.", - default=10, + "Specify the horizontal halo size around macros.", + default=5, units="µm", deprecated_names=["FP_TAP_VERTICAL_HALO"], ), @@ -1101,6 +1101,15 @@ def get_script_path(self): return os.path.join(get_script_dir(), "openroad", "tapcell.tcl") +@Step.factory.register() +class UnplaceAll(OpenROADStep): + id = "OpenROAD.UnplaceAll" + name = "Unplace All" + + def get_script_path(self): + return os.path.join(get_script_dir(), "openroad", "ungpl.tcl") + + def get_psm_error_count(rpt: io.TextIOWrapper) -> int: sio = io.StringIO() @@ -1182,7 +1191,7 @@ class _GlobalPlacement(OpenROADStep): "The desired placement density of cells. If not specified, the value will be equal to (`FP_CORE_UTIL` + 5 * `GPL_CELL_PADDING` + 10).", units="%", deprecated_names=[ - ("PL_TARGET_DENSITY", lambda d: Decimal(d) * Decimal(100.0)) + ("PL_TARGET_DENSITY", lambda d: Decimal(d) * Decimal("100")) ], ), Variable( @@ -1276,7 +1285,7 @@ class GlobalPlacement(_GlobalPlacement): @Step.factory.register() class GlobalPlacementSkipIO(_GlobalPlacement): """ - Performs global placement without taking I/O into consideration. + Performs preliminary global placement as a basis for pin placement. This is useful for flows where the: * Cells are placed @@ -1295,6 +1304,11 @@ class GlobalPlacementSkipIO(_GlobalPlacement): default="matching", deprecated_names=[("FP_IO_MODE", _migrate_ppl_mode)], ), + Variable( + "FP_PIN_ORDER_CFG", + Optional[Path], + "Path to a custom pin configuration file.", + ), Variable( "FP_DEF_TEMPLATE", Optional[Path], @@ -1306,37 +1320,140 @@ def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: kwargs, env = self.extract_env(kwargs) if self.config["FP_DEF_TEMPLATE"] is not None: info( - f"I/O pins were loaded from {self.config['FP_DEF_TEMPLATE']}. Skipping the first global placement iteration…" + f"I/O pins were loaded from {self.config['FP_DEF_TEMPLATE']}. Returning state unaltered…" + ) + return {}, {} + if self.config["FP_PIN_ORDER_CFG"] is not None: + info( + f"I/O pins to be placed from {self.config['FP_PIN_ORDER_CFG']}. Returning state unaltered…" ) return {}, {} - env["__PL_SKIP_IO"] = "1" return super().run(state_in, env=env, **kwargs) @Step.factory.register() -class BasicMacroPlacement(OpenROADStep): - id = "OpenROAD.BasicMacroPlacement" - name = "Basic Macro Placement" +class HierarchicalMacroPlacer(OpenROADStep): + id = "OpenROAD.HierarchicalMacroPlacer" + name = "Hierarchical Macro Placement" config_vars = OpenROADStep.config_vars + [ Variable( - "PL_MACRO_HALO", - str, - "Macro placement halo. Format: `{Horizontal} {Vertical}`.", - default="0 0", + "FP_MACRO_HORIZONTAL_HALO", + Decimal, + "Specify the horizontal halo size around macros.", + default=5, + units="µm", + deprecated_names=["FP_TAP_HORIZONTAL_HALO"], + ), + Variable( + "FP_MACRO_VERTICAL_HALO", + Decimal, + "Specify the horizontal halo size around macros.", + default=5, + units="µm", + deprecated_names=["FP_TAP_VERTICAL_HALO"], + ), + # Variable( + # "PL_MACRO_CHANNEL", + # Tuple[Decimal, Decimal], + # "'Halo' (i.e. space left empty) around macros placed using the hierarchical macro placement. Tuple of horizontal and vertical values.", + # default=(Decimal("0"), Decimal("0")), + # units="µm", + # ), + Variable( + "RTLMP_MAX_LEVEL", + Optional[int], + "Maximum depth of the physical hierarchical tree.", + ), + Variable( + "RTLMP_MAX_INST", + Optional[int], + "Maximum number of standard cells in a cluster.", + ), + Variable( + "RTLMP_MIN_INST", + Optional[int], + "Minimum number of standard cells in a cluster.", + ), + Variable( + "RTLMP_MAX_MACRO", + Optional[int], + "Maximum number of macros in a cluster.", + ), + Variable( + "RTLMP_MIN_MACRO", + Optional[int], + "Minimum number of macros in a cluster.", + ), + Variable( + "RTLMP_MIN_AR", + Decimal, + "Minimum aspect ratio of a StandardCellCluster.", + default=Decimal("0.33"), + ), + Variable( + "RTLMP_SIGNATURE_NET_THRESHOLD", + Optional[int], + "Minimum number of connections between two clusters to be identified as connected.", + ), + Variable( + "RTLMP_AREA_WT", + Optional[Decimal], + "Weight for the area of the current floorplan.", + ), + Variable( + "RTLMP_WIRELENGTH_WT", + Optional[Decimal], + "Weight for half-perimeter wirelength.", + ), + Variable( + "RTLMP_OUTLINE_WT", + Optional[Decimal], + "Weight for violating the fixed outline constraint.", + ), + Variable( + "RTLMP_BOUNDARY_WT", + Optional[Decimal], + "Weight for the boundary, or how far the hard macro clusters are from boundaries.", + ), + Variable( + "RTLMP_NOTCH_WT", + Optional[Decimal], + "Weight for the notch, or the existence of dead space that cannot be used for placement & routing.", + ), + Variable( + "RTLMP_DEAD_SPACE", + Optional[Decimal], + "Specifies the target dead space percentage.", + ), + Variable( + "RTLMP_FENCE_LX", + Optional[Decimal], + "Lower X coordinate of the global fence bounding box.", units="µm", ), Variable( - "PL_MACRO_CHANNEL", - str, - "Channel widths between macros. Format: `{Horizontal} {Vertical}`.", - default="0 0", + "RTLMP_FENCE_LY", + Optional[Decimal], + "Lower Y coordinate of the global fence bounding box.", + units="µm", + ), + Variable( + "RTLMP_FENCE_UX", + Optional[Decimal], + "Upper X coordinate of the global fence bounding box.", + units="µm", + ), + Variable( + "RTLMP_FENCE_UY", + Optional[Decimal], + "Upper Y coordinate of the global fence bounding box.", units="µm", ), ] def get_script_path(self): - raise NotImplementedError() + return os.path.join(get_script_dir(), "openroad", "hiertlmp.tcl") @Step.factory.register() @@ -1894,16 +2011,16 @@ class CutRows(OpenROADStep): Variable( "FP_MACRO_HORIZONTAL_HALO", Decimal, - "Specify the horizontal halo size around macros while cutting rows.", - default=10, + "Specify the horizontal halo size around macros.", + default=5, units="µm", deprecated_names=["FP_TAP_HORIZONTAL_HALO"], ), Variable( "FP_MACRO_VERTICAL_HALO", Decimal, - "Specify the vertical halo size around macros while cutting rows.", - default=10, + "Specify the horizontal halo size around macros.", + default=5, units="µm", deprecated_names=["FP_TAP_VERTICAL_HALO"], ), diff --git a/openlane/steps/pyosys.py b/openlane/steps/pyosys.py index c89c27650..e6eab1f29 100644 --- a/openlane/steps/pyosys.py +++ b/openlane/steps/pyosys.py @@ -455,10 +455,16 @@ class SynthesisCommon(VerilogStep): default=False, ), Variable( - "SYNTH_NO_FLAT", - bool, - "A flag that disables flattening the hierarchy during synthesis, only flattening it after synthesis, mapping and optimizations.", - default=False, + "SYNTH_HIERARCHY_MODE", + Literal["flatten", "deferred_flatten", "keep"], + "Affects how hierarchy is maintained throughout and after synthesis. 'flatten' flattens it during and after synthesis. 'deferred_flatten' flattens it after synthesis. 'keep' never flattens it.", + default="flatten", + deprecated_names=[ + ( + "SYNTH_NO_FLAT", + lambda x: "deferred_flatten" if x else "flatten", + ) + ], ), Variable( "SYNTH_SHARE_RESOURCES", From 29b34245c22633bd2627eec7d10c19bb749400d0 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 26 Aug 2024 10:40:25 +0300 Subject: [PATCH 10/11] lint michigan --- openlane/flows/sequential.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openlane/flows/sequential.py b/openlane/flows/sequential.py index d4c514844..2ab63c568 100644 --- a/openlane/flows/sequential.py +++ b/openlane/flows/sequential.py @@ -13,7 +13,6 @@ # limitations under the License. from __future__ import annotations -import json import os import fnmatch from typing import ( @@ -30,8 +29,7 @@ from rapidfuzz import process, fuzz, utils from .flow import Flow, FlowException, FlowError -from ..config import Macro -from ..common import Filter, GenericDictEncoder +from ..common import Filter from ..state import State from ..logging import info, success, debug from ..steps import ( From a6fa5e60255c2c877a1042a24af5e4dc37c383dc Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Tue, 27 Aug 2024 20:15:49 +0300 Subject: [PATCH 11/11] Update OpenROAD again + docs --- nix/openroad.nix | 6 ++---- openlane/steps/openroad.py | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/nix/openroad.nix b/nix/openroad.nix index dfe500299..8bff0cdf1 100644 --- a/nix/openroad.nix +++ b/nix/openroad.nix @@ -43,12 +43,11 @@ flex, bison, clang-tools_14, - ioplace-parser, buildEnv, makeBinaryWrapper, buildPythonEnvForInterpreter, - rev ? "49a497abf0011b2631ce7caab8b78bd1f9d67662", - sha256 ? "sha256-3o+iT+9X8/EIf4c/+FLp6GRxPGmM5kWMwK7beyICRL0=", + rev ? "6b5937db431d2fa1023d3865f21ccd9b65781492", + sha256 ? "sha256-rpv3WkOahFFkW4g+8tRiPBfBFcLwJIqK/hTgWv2tkow=", }: let self = clangStdenv.mkDerivation (finalAttrs: { name = "openroad"; inherit rev; @@ -65,7 +64,6 @@ "-DTCL_HEADER=${tcl}/include/tcl.h" "-DUSE_SYSTEM_BOOST:BOOL=ON" "-DENABLE_TESTS:BOOL=OFF" - "-DVERBOSE=1" ]; cmakeFlags = diff --git a/openlane/steps/openroad.py b/openlane/steps/openroad.py index 83cdfc914..19c33d6b2 100644 --- a/openlane/steps/openroad.py +++ b/openlane/steps/openroad.py @@ -1071,7 +1071,7 @@ def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: @Step.factory.register() class TapEndcapInsertion(OpenROADStep): """ - Places well TAP cells across a floorplan, as well as end-cap cells at the + Places welltap cells across a floorplan, as well as endcap cells at the edges of the floorplan. """ @@ -1103,6 +1103,13 @@ def get_script_path(self): @Step.factory.register() class UnplaceAll(OpenROADStep): + """ + Sets placement status of *all* instances to NONE. + + Useful in flows where a preliminary placement is needed as a pre-requisite + to something else but that placement must be discarded. + """ + id = "OpenROAD.UnplaceAll" name = "Unplace All" @@ -1333,6 +1340,20 @@ def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: @Step.factory.register() class HierarchicalMacroPlacer(OpenROADStep): + """ + Uses the *Hier-RTLMP* automatic macro placer in OpenROAD to place any + unplaced macros. + + This placer is aimed at complex top-level IP integrations where hierarchy + information from the register-transfer level could be leveraged. + + This requires the netlist to keep hierarchy information. OpenLane 2's + Classic flow does not do so by default, see ``Yosys.Synthesis``'s + documentation on how to alter that. + + See this paper for more info: https://arxiv.org/pdf/2304.11761 + """ + id = "OpenROAD.HierarchicalMacroPlacer" name = "Hierarchical Macro Placement"