diff --git a/default.nix b/default.nix index 4a4390b7c..8fb2dd348 100644 --- a/default.nix +++ b/default.nix @@ -22,7 +22,7 @@ libparse, magic-vlsi, netgen, - opensta-stable, + opensta, openroad, ruby, surelog, @@ -84,7 +84,7 @@ ]; includedTools = [ - opensta-stable + opensta yosys-env openroad-env klayout diff --git a/flake.nix b/flake.nix index 49bf552c5..88b9d635f 100644 --- a/flake.nix +++ b/flake.nix @@ -72,10 +72,6 @@ }); colab-env = callPackage ./nix/colab-env.nix {}; opensta = callPackage ./nix/opensta.nix {}; - opensta-stable = callPackage ./nix/opensta.nix { - rev = "cc9eb1f12a0d5030aebc1f1428e4300480e30b40"; - sha256 = "sha256-/ShPD4xWq3lkN0Z3uONKm7i9eqbT+IU41UF7yIvDJy4="; - }; openroad-abc = callPackage ./nix/openroad-abc.nix {}; openroad = callPythonPackage ./nix/openroad.nix { inherit (nix-eda) buildPythonEnvForInterpreter; diff --git a/nix/openroad.nix b/nix/openroad.nix index 5c5ec40d6..d7e9b73f4 100644 --- a/nix/openroad.nix +++ b/nix/openroad.nix @@ -20,6 +20,7 @@ opensta, boost183, eigen, + cudd, tcl, python3, readline, @@ -45,8 +46,8 @@ buildEnv, makeBinaryWrapper, buildPythonEnvForInterpreter, - rev ? "b16bda7e82721d10566ff7e2b68f1ff0be9f9e38", - sha256 ? "sha256-+JGyX81Km2XidptA3k1Y5ZPwv+4Ed39LCsPfIHWd6ac=", + rev ? "edf00dff99f6c40d67a30c0e22a8191c5d2ed9d6", + sha256 ? "sha256-J649SIC/IHtiKiMvY8XrteyFkNM0WeQ6hfKIYdtE81g=", }: let self = clangStdenv.mkDerivation (finalAttrs: { name = "openroad"; @@ -73,6 +74,7 @@ ++ [ "-DUSE_SYSTEM_ABC:BOOL=ON" "-DUSE_SYSTEM_OPENSTA:BOOL=ON" + "-DCMAKE_CXX_FLAGS=-I${eigen}/include/eigen3" "-DOPENSTA_HOME=${opensta}" "-DABC_LIBRARY=${openroad-abc}/lib/libabc.a" ]; @@ -85,12 +87,14 @@ 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 diff --git a/nix/opensta.nix b/nix/opensta.nix index ddf574173..84b154420 100644 --- a/nix/opensta.nix +++ b/nix/opensta.nix @@ -22,10 +22,12 @@ flex, bison, tcl, + tclreadline, + cudd, zlib, eigen, - rev ? "e01d3f163f483f233db00410b6515a767a6ca03b", - sha256 ? "sha256-0LbY5RcM+11oV3iPfAUd7hpyFPwCfCjnG0EE1LkXg5E=", + rev ? "b5f3a02b33b8ae1739ace8a329fde94434711dd6", + sha256 ? "sha256-s9Qn8Hkxuzvx7sZdaa/RX8X4Rp4w/kTVdnrmsRvC8wo=", }: clangStdenv.mkDerivation (finalAttrs: { name = "opensta"; @@ -44,6 +46,8 @@ clangStdenv.mkDerivation (finalAttrs: { ]; buildInputs = [ + cudd + tclreadline eigen tcl zlib diff --git a/openlane/common/metrics/util.py b/openlane/common/metrics/util.py index 0d3adfc75..6909ceaa5 100644 --- a/openlane/common/metrics/util.py +++ b/openlane/common/metrics/util.py @@ -59,7 +59,7 @@ def parse_metric_modifiers(metric_name: str) -> Tuple[str, Mapping[str, str]]: while ":" in mn_mut[-1]: key, value = mn_mut.pop().split(":", maxsplit=1) modifiers[key] = value - return "__".join(mn_mut), modifiers + return "__".join(mn_mut), {k: modifiers[k] for k in reversed(modifiers)} def aggregate_metrics( @@ -85,25 +85,29 @@ def aggregate_metrics( aggregated: Dict[str, Any] = {} for name, value in input.items(): metric_name, modifiers = parse_metric_modifiers(name) - if len(modifiers) != 1: + if len(modifiers) < 1: # No modifiers = final aggregate, don't double-represent in sums - # >1 modifiers = n-level nesting, not supported atm continue - modifier = list(modifiers.keys())[0] - + modifier_names = list(modifiers.keys()) dont_aggregate: Iterable[str] = [] entry = aggregator_by_metric.get(metric_name) if isinstance(entry, Metric): dont_aggregate = entry.dont_aggregate or [] entry = entry.aggregator + if entry is None: continue - if modifier in dont_aggregate: + + if len(set(modifier_names).intersection(set(dont_aggregate))): continue - start, aggregator = entry - current = aggregated.get(metric_name) or start - aggregated[metric_name] = aggregator([current, value]) + + metric_name_so_far = metric_name + for modifier in modifier_names: + start, aggregation_fn = entry + current = aggregated.get(metric_name_so_far) or start + aggregated[metric_name_so_far] = aggregation_fn([current, value]) + metric_name_so_far += f"__{modifier}:{modifiers[modifier]}" final_values = dict(input) final_values.update(aggregated) diff --git a/openlane/flows/classic.py b/openlane/flows/classic.py index 2da9d037a..7526d9390 100644 --- a/openlane/flows/classic.py +++ b/openlane/flows/classic.py @@ -95,6 +95,7 @@ class Classic(SequentialFlow): Odb.ReportWireLength, Checker.WireLength, OpenROAD.FillInsertion, + Odb.CellFrequencyTables, OpenROAD.RCX, OpenROAD.STAPostPNR, OpenROAD.IRDropReport, diff --git a/openlane/scripts/odbpy/cell_frequency.py b/openlane/scripts/odbpy/cell_frequency.py new file mode 100644 index 000000000..e9d663ed9 --- /dev/null +++ b/openlane/scripts/odbpy/cell_frequency.py @@ -0,0 +1,107 @@ +# 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 + +from collections import Counter +from reader import click, click_odb, OdbReader + +import rich +from rich.table import Table +from rich.console import Console + + +@click.command() +@click.option( + "--out-dir", + type=click.Path(file_okay=False, dir_okay=True), + required=True, + help="Directory to output tables to", +) +@click.option( + "--buffer-list", + type=click.Path(file_okay=True, dir_okay=False), + help="List of wildcard strings", +) +@click_odb +def main( + out_dir, + buffer_list, + reader: OdbReader, +): + db = reader.db + block = db.getChip().getBlock() + + pattern = r"^(\S+)__(\S+)_\d+" + compiled_pattern = re.compile(pattern) + + cell_frequency_table = Table( + "Cell", + "Count", + title="Cells by Master", + ) + scl_table = Table( + "SCL", + "Count", + title="Cells by SCL", + ) + cell_fn_table = Table( + "Cell Function", + "Count", + title="Cells by Function", + title_justify="center", + ) + buffer_table = Table( + "Buffer", + "Count", + title="Buffers by Cell Master", + ) + + cells = [instance.getMaster().getName() for instance in block.getInsts()] + buffers = open(buffer_list).read().split() + buffer_frequency = Counter([cell for cell in cells if cell in buffers]) + cell_frequency = Counter(cells) + scl_frequency = Counter() + cell_fn_frequency = Counter() + + for cell in cell_frequency.keys(): + if match := compiled_pattern.search(cell): + scl, cell_type = match[1], match[2] + cell_type_key = f"{scl}__{cell_type}" + scl_frequency[scl] += cell_frequency[cell] + cell_fn_frequency[cell_type_key] += cell_frequency[cell] + + console = Console() + for table, frequency, name in [ + (cell_frequency_table, cell_frequency, "cell"), + (cell_fn_table, cell_fn_frequency, "cell_function"), + (scl_table, scl_frequency, "by_scl"), + (buffer_table, buffer_frequency, "buffers"), + ]: + freqs = sorted(frequency.items(), key=lambda x: x[0]) + for key, value in freqs: + table.add_row(key, str(value)) + + table.min_width = console.width + console.print(table) + + full_table_path = os.path.join(out_dir, f"{name}.rpt") + table.min_width = 160 + with open(full_table_path, "w") as f: + file_console = rich.console.Console(file=f, width=160) + file_console.print(table) + + +if __name__ == "__main__": + main() diff --git a/openlane/scripts/odbpy/disconnected_pins.py b/openlane/scripts/odbpy/disconnected_pins.py index 530986541..f3c950b7d 100644 --- a/openlane/scripts/odbpy/disconnected_pins.py +++ b/openlane/scripts/odbpy/disconnected_pins.py @@ -1,13 +1,10 @@ # Copyright 2022 Efabless Corporation # -# This file is part of the DFFRAM Memory Compiler. -# See https://github.com/Cloud-V/DFFRAM for further info. -# # 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 +# 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, diff --git a/openlane/scripts/odbpy/wire_lengths.py b/openlane/scripts/odbpy/wire_lengths.py index a85e797fc..2e4fbf3c9 100644 --- a/openlane/scripts/odbpy/wire_lengths.py +++ b/openlane/scripts/odbpy/wire_lengths.py @@ -1,13 +1,10 @@ # Copyright 2022 Efabless Corporation # -# This file is part of the DFFRAM Memory Compiler. -# See https://github.com/Cloud-V/DFFRAM for further info. -# # 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 +# 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, diff --git a/openlane/scripts/openroad/buffer_list.tcl b/openlane/scripts/openroad/buffer_list.tcl new file mode 100644 index 000000000..e2b0152b8 --- /dev/null +++ b/openlane/scripts/openroad/buffer_list.tcl @@ -0,0 +1,10 @@ +foreach lib $::env(_PNR_LIBS) { + read_liberty $lib +} + +set cells [get_lib_cells *] +foreach cell $cells { + if { [$cell is_buffer] } { + puts [get_property $cell name] + } +} diff --git a/openlane/scripts/openroad/common/io.tcl b/openlane/scripts/openroad/common/io.tcl index a07ee3645..a77a119a5 100644 --- a/openlane/scripts/openroad/common/io.tcl +++ b/openlane/scripts/openroad/common/io.tcl @@ -1,4 +1,4 @@ -# Copyright 2022 Efabless Corporation +# Copyright 2022-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. @@ -29,6 +29,17 @@ proc env_var_used {file var} { return [string_in_file $file "\$::env($var)"] } +proc set_global_vars {} { + if { [namespace exists ::ord] } { + set ::db [::ord::get_db] + set ::chip [$::db getChip] + set ::tech [$::db getTech] + set ::block [$::chip getBlock] + set ::dbu [$::tech getDbUnitsPerMicron] + set ::libs [$::db getLibs] + } +} + proc read_current_sdc {} { if { ![info exists ::env(_SDC_IN)]} { puts "\[INFO\] _SDC_IN not found. Not reading an SDC file." @@ -129,15 +140,7 @@ proc read_current_netlist {args} { puts "Linking design '$::env(DESIGN_NAME)' from netlist…" link_design $::env(DESIGN_NAME) - if { [namespace exists ::ord] } { - set ::db [::ord::get_db] - set ::chip [$::db getChip] - set ::tech [$::db getTech] - set ::block [$::chip getBlock] - set ::dbu [$::tech getDbUnitsPerMicron] - set ::libs [$::db getLibs] - } - + set_global_vars read_current_sdc } @@ -305,12 +308,7 @@ proc read_current_odb {args} { exit 1 } - set ::db [::ord::get_db] - set ::chip [$::db getChip] - set ::tech [$::db getTech] - set ::block [$::chip getBlock] - set ::dbu [$::tech getDbUnitsPerMicron] - set ::libs [$::db getLibs] + set_global_vars # Read supporting views (if applicable) read_pnr_libs @@ -331,6 +329,10 @@ proc write_views {args} { puts "Setting global connections for newly added cells…" set_global_connections + puts "Updating metrics…" + report_design_area_metrics + report_cell_usage + if { [info exists ::env(SAVE_ODB)] } { puts "Writing OpenROAD database to '$::env(SAVE_ODB)'…" write_db $::env(SAVE_ODB) diff --git a/openlane/scripts/openroad/cts.tcl b/openlane/scripts/openroad/cts.tcl index bfbe7dd20..60faa3441 100755 --- a/openlane/scripts/openroad/cts.tcl +++ b/openlane/scripts/openroad/cts.tcl @@ -72,11 +72,9 @@ source $::env(SCRIPTS_DIR)/openroad/common/dpl.tcl estimate_parasitics -placement + write_views puts "%OL_CREATE_REPORT cts.rpt" report_cts puts "%OL_END_REPORT" - -report_design_area_metrics - diff --git a/openlane/scripts/openroad/fill.tcl b/openlane/scripts/openroad/fill.tcl index b80b43d3e..3575e15ef 100755 --- a/openlane/scripts/openroad/fill.tcl +++ b/openlane/scripts/openroad/fill.tcl @@ -26,4 +26,5 @@ foreach {pattern} $::env(FILL_CELL) { puts $fill_list filler_placement $fill_list -write_views \ No newline at end of file +write_views + diff --git a/openlane/scripts/openroad/rcx.tcl b/openlane/scripts/openroad/rcx.tcl index d6f76d28e..573506797 100644 --- a/openlane/scripts/openroad/rcx.tcl +++ b/openlane/scripts/openroad/rcx.tcl @@ -14,7 +14,7 @@ source $::env(SCRIPTS_DIR)/openroad/common/io.tcl read_lefs "RCX_LEF" read_def $::env(CURRENT_DEF) - +set_global_vars set_propagated_clock [all_clocks] @@ -29,4 +29,4 @@ define_process_corner -ext_model_index 0 CURRENT_CORNER 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..ef457ae10 100644 --- a/openlane/scripts/openroad/repair_design.tcl +++ b/openlane/scripts/openroad/repair_design.tcl @@ -65,7 +65,6 @@ unset_dont_touch_objects source $::env(SCRIPTS_DIR)/openroad/common/set_rc.tcl estimate_parasitics -placement -write_views -report_design_area_metrics +write_views diff --git a/openlane/scripts/openroad/repair_design_postgrt.tcl b/openlane/scripts/openroad/repair_design_postgrt.tcl index d0ad8c94e..54e9ec5fa 100644 --- a/openlane/scripts/openroad/repair_design_postgrt.tcl +++ b/openlane/scripts/openroad/repair_design_postgrt.tcl @@ -44,7 +44,5 @@ if { $::env(GRT_DESIGN_REPAIR_RUN_GRT) } { source $::env(SCRIPTS_DIR)/openroad/common/grt.tcl } -write_views - -report_design_area_metrics +write_views diff --git a/openlane/scripts/openroad/rsz_timing_postcts.tcl b/openlane/scripts/openroad/rsz_timing_postcts.tcl index b46d341ca..9113bfccc 100644 --- a/openlane/scripts/openroad/rsz_timing_postcts.tcl +++ b/openlane/scripts/openroad/rsz_timing_postcts.tcl @@ -55,7 +55,5 @@ unset_dont_touch_objects source $::env(SCRIPTS_DIR)/openroad/common/set_rc.tcl estimate_parasitics -placement -write_views - -report_design_area_metrics +write_views diff --git a/openlane/scripts/openroad/rsz_timing_postgrt.tcl b/openlane/scripts/openroad/rsz_timing_postgrt.tcl index 5c2007a80..f8e423e0d 100644 --- a/openlane/scripts/openroad/rsz_timing_postgrt.tcl +++ b/openlane/scripts/openroad/rsz_timing_postgrt.tcl @@ -57,7 +57,5 @@ if { $::env(GRT_RESIZER_RUN_GRT) } { source $::env(SCRIPTS_DIR)/openroad/common/grt.tcl } -write_views - -report_design_area_metrics +write_views diff --git a/openlane/scripts/openroad/sta/corner.tcl b/openlane/scripts/openroad/sta/corner.tcl index d9352205e..b6795cdba 100644 --- a/openlane/scripts/openroad/sta/corner.tcl +++ b/openlane/scripts/openroad/sta/corner.tcl @@ -347,7 +347,7 @@ puts "%OL_END_REPORT" puts "%OL_CREATE_REPORT unpropagated.rpt" foreach clock [all_clocks] { - if { ![get_property $clock propagated] } { + if { ![get_property $clock is_propagated] } { puts "[get_property $clock full_name]" } } @@ -355,15 +355,46 @@ foreach clock [all_clocks] { puts "%OL_END_REPORT" -# puts "%OL_CREATE_REPORT clock.rpt" +puts "%OL_CREATE_REPORT clock.rpt" -# foreach clock [all_clocks] { -# report_clock_properties $clock -# report_clock_latency -clock $clock -# report_clock_min_period -clocks [get_property $clock name] -# } +foreach clock [all_clocks] { + set source_names "" + set is_generated "no" + set is_virtual "no" + set is_propagated "no" + foreach source [get_property $clock sources] { + set source_names "[get_property $source full_name] $source_names" + } + if { [get_property $clock is_generated] } { + set is_generated "yes" + } + if { [get_property $clock is_virtual] } { + set is_virtual "yes" + } + if { [get_property $clock is_propagated] } { + set is_virtual "yes" + } + puts "Clock: [get_property $clock name]" + puts "Sources: $source_names" + puts "Generated: $is_generated" + puts "Virtual: $is_virtual" + puts "Propagated: $is_propagated" + puts "Period: [get_property $clock period]" + puts "\n===========================================================================" + puts "report_clock_properties" + puts "============================================================================" + report_clock_properties $clock + puts "\n===========================================================================" + puts "report_clock_latency" + puts "============================================================================" + report_clock_latency -clock $clock + puts "\n===========================================================================" + puts "report_clock_min_period" + puts "============================================================================" + report_clock_min_period -clocks [get_property $clock name] +} -# puts "%OL_END_REPORT" +puts "%OL_END_REPORT" write_sdfs write_libs diff --git a/openlane/scripts/openroad/tapcell.tcl b/openlane/scripts/openroad/tapcell.tcl index adcbd929d..355e16355 100755 --- a/openlane/scripts/openroad/tapcell.tcl +++ b/openlane/scripts/openroad/tapcell.tcl @@ -21,7 +21,5 @@ tapcell\ -halo_width_x $::env(FP_MACRO_HORIZONTAL_HALO)\ -halo_width_y $::env(FP_MACRO_VERTICAL_HALO) -write_views - -report_design_area_metrics +write_views diff --git a/openlane/steps/odb.py b/openlane/steps/odb.py index 94f41f855..17a18904d 100644 --- a/openlane/steps/odb.py +++ b/openlane/steps/odb.py @@ -28,6 +28,7 @@ OpenROADOutputProcessor, ) from .openroad import DetailedPlacement, GlobalRouting +from .tclstep import TclStep from .step import ( ViewsUpdate, MetricsUpdate, @@ -857,6 +858,57 @@ class HeuristicDiodeInsertion(CompositeStep): @Step.factory.register() +class CellFrequencyTables(OdbpyStep): + """ + Creates a number of tables to show the cell frequencies by: + + - Cells + - Buffer cells only + - Cell Function* + - Standard Cell Library* + + * These tables only return meaningful info with PDKs distributed in the + Open_PDKs format, i.e., all cells are named ``{scl}__{cell_fn}_{size}``. + """ + + id = "Odb.CellFrequencyTables" + name = "Generate Cell Frequency Tables" + + def get_script_path(self): + return os.path.join( + get_script_dir(), + "odbpy", + "cell_frequency.py", + ) + + def get_buffer_list_file(self): + return os.path.join(self.step_dir, "buffer_list.txt") + + def get_buffer_list_script(self): + return os.path.join(get_script_dir(), "openroad", "buffer_list.tcl") + + def run(self, state_in, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]: + kwargs, env = self.extract_env(kwargs) + + env_copy = env.copy() + lib_list = self.toolbox.filter_views(self.config, self.config["LIB"]) + env_copy["_PNR_LIBS"] = TclStep.value_to_tcl(lib_list) + super().run_subprocess( + ["openroad", "-no_splash", "-exit", self.get_buffer_list_script()], + env=env_copy, + log_to=self.get_buffer_list_file(), + ) + return super().run(state_in, env=env, **kwargs) + + def get_command(self) -> List[str]: + command = super().get_command() + command.append("--buffer-list") + command.append(self.get_buffer_list_file()) + command.append("--out-dir") + command.append(self.step_dir) + return command + + class ManualGlobalPlacement(OdbpyStep): """ This is an step to override the placement of one or more instances at diff --git a/test/designs b/test/designs index 2682d2284..da5ed2cae 160000 --- a/test/designs +++ b/test/designs @@ -1 +1 @@ -Subproject commit 2682d228448208ce45094b65346ba17611a783b2 +Subproject commit da5ed2cae9da72290c6fc016b2d19cd2b8914bae diff --git a/test/steps/all b/test/steps/all index 720aa27aa..cb8661ec1 160000 --- a/test/steps/all +++ b/test/steps/all @@ -1 +1 @@ -Subproject commit 720aa27aa05a1f6977cb6a95926b38dfcba0b164 +Subproject commit cb8661ec1b261f6763af09d09f9372a3ee8f5ba6