diff --git a/src/dbSta/include/db_sta/dbSta.hh b/src/dbSta/include/db_sta/dbSta.hh index 4adf3e62703..ae55165349d 100644 --- a/src/dbSta/include/db_sta/dbSta.hh +++ b/src/dbSta/include/db_sta/dbSta.hh @@ -35,7 +35,11 @@ #pragma once +#include #include +#include +#include +#include #include "odb/db.h" #include "odb/dbBlockCallBackObj.h" @@ -191,6 +195,30 @@ class dbSta : public Sta, public ord::OpenRoadObserver InstType getInstanceType(odb::dbInst* inst); void report_cell_usage(odb::dbModule* module, bool verbose); + // Creates a cell usage snapshot for the given module at the given stage. + // The snapshot is written to the given path as a JSON file. + void CreateCellUsageSnapshot( + odb::dbModule* module, std::string_view path, std::string_view stage); + + // Holds the usage information of a specific cell. + struct CellUsageInfo { + // Name of the cell. + std::string name; + + // Number of instances of the cell. + int count = 0; + + // Area of the cell in microns^2. Total area of the cell instances can be + // calculated by multiplying the count by this value. + double area = 0.0; + }; + + // Holds a snapshot of cell usage information at a given stage. + struct CellUsageSnapshot { + std::string stage; + std::vector cells_usage_info; + }; + BufferUse getBufferUse(sta::LibertyCell* buffer); using Sta::netSlack; diff --git a/src/dbSta/src/dbSta.cc b/src/dbSta/src/dbSta.cc index 135ddbb4d3b..996263961b4 100644 --- a/src/dbSta/src/dbSta.cc +++ b/src/dbSta/src/dbSta.cc @@ -44,11 +44,21 @@ #include #include // min +#include +#include +#include +#include #include #include +#include +#include +#include #include "AbstractPathRenderer.h" #include "AbstractPowerDensityDataSource.h" +#include "boost/algorithm/string/join.hpp" +#include "boost/json.hpp" +#include "boost/json/src.hpp" #include "dbSdcNetwork.hh" #include "db_sta/MakeDbSta.hh" #include "db_sta/dbNetwork.hh" @@ -80,8 +90,54 @@ dbSta* makeDbSta() } } // namespace ord +namespace boost::json { +void tag_invoke(json::value_from_tag, json::value& json_value, + ord::dbSta::CellUsageInfo const& cell_usage_info) { + json_value = {{"name", cell_usage_info.name}, + {"count", cell_usage_info.count}, + {"area", cell_usage_info.area}}; +} + +void tag_invoke(json::value_from_tag, json::value& json_value, + ord::dbSta::CellUsageSnapshot const& cell_usage_snapshot) { + json_value = { + {"stage", cell_usage_snapshot.stage}, + {"cell_usage_info", + boost::json::value_from(cell_usage_snapshot.cells_usage_info)}}; +} + +ord::dbSta::CellUsageInfo tag_invoke(value_to_tag, + value const& json_value) { + return {value_to(json_value.at("name")), + value_to(json_value.at("count")), + value_to(json_value.at("area"))}; +} + +ord::dbSta::CellUsageSnapshot tag_invoke( + value_to_tag, value const& json_value) { + return {value_to(json_value.at("stage")), + value_to>( + json_value.at("cell_usage_info"))}; +} + +} // namespace boost::json + namespace sta { +namespace { + +constexpr std::string_view kCellUsageSnapshotPrefix = "cell_usage_snapshot"; +constexpr std::string_view kCellUsageSnapshotFileExtension = "json"; + +std::string GetCellSnapshotName(const std::string& module, + const std::string& stage) { + std::vector components = {std::string(kCellUsageSnapshotPrefix), + module, stage}; + return boost::algorithm::join(components, "-"); +} + +} // namespace + using utl::Logger; using utl::STA; @@ -579,6 +635,43 @@ void dbSta::report_cell_usage(odb::dbModule* module, const bool verbose) } } +void dbSta::CreateCellUsageSnapshot(odb::dbModule* module, + std::string_view path, + std::string_view stage) { + std::map name_to_cell_usage_info; + dbBlock* block = db_->getChip()->getBlock(); + const std::vector insts = module->getLeafInsts(); + const double area_to_microns = std::pow(block->getDbUnitsPerMicron(), 2); + for (const dbInst* inst : insts) { + const std::string& cell_name = inst->getMaster()->getName(); + auto [it, inserted] = name_to_cell_usage_info.insert( + {cell_name, CellUsageInfo{ + .name = cell_name, + .count = 1, + .area = inst->getMaster()->getArea() / area_to_microns, + }}); + if (!inserted) { + it->second.count++; + } + } + + CellUsageSnapshot cell_usage_snapshot{.stage = std::string(stage)}; + cell_usage_snapshot.cells_usage_info.reserve(name_to_cell_usage_info.size()); + for (const auto& [cell_name, cell_usage_info] : name_to_cell_usage_info) { + cell_usage_snapshot.cells_usage_info.push_back(cell_usage_info); + } + boost::json::value output = boost::json::value_from(cell_usage_snapshot); + logger_->report("{}", output.as_object()); + + std::ofstream file; + file.open( + std::string(path) + "/" + + GetCellSnapshotName(std::string(module->getName()), std::string(stage)) + + "." + std::string(kCellUsageSnapshotFileExtension)); + file << output.as_object(); + file.close(); +} + BufferUse dbSta::getBufferUse(sta::LibertyCell* buffer) { return buffer_use_analyser_->getBufferUse(buffer); diff --git a/src/dbSta/src/dbSta.i b/src/dbSta/src/dbSta.i index 1034f0c409b..b572646e313 100644 --- a/src/dbSta/src/dbSta.i +++ b/src/dbSta/src/dbSta.i @@ -173,6 +173,16 @@ report_cell_usage_cmd(odb::dbModule* mod, const bool verbose) sta->report_cell_usage(mod, verbose); } + +void +create_cell_usage_snapshot_cmd( + odb::dbModule* module, const char* path, const char* stage) { + cmdLinkedNetwork(); + ord::OpenRoad *openroad = ord::getOpenRoad(); + sta::dbSta *sta = openroad->getSta(); + sta->CreateCellUsageSnapshot(module, std::string(path), std::string(stage)); +} + // Copied from sta/verilog/Verilog.i because we don't want sta::read_verilog // that is in the same file. void diff --git a/src/dbSta/src/dbSta.tcl b/src/dbSta/src/dbSta.tcl index 74b2ccbf99e..60885a7fe36 100644 --- a/src/dbSta/src/dbSta.tcl +++ b/src/dbSta/src/dbSta.tcl @@ -103,6 +103,36 @@ proc report_cell_usage { args } { report_cell_usage_cmd $module [info exists flags(-verbose)] } + +define_cmd_args "create_cell_usage_snapshot" { \ + [-path path]\ + [-stage stage] + } + +proc create_cell_usage_snapshot { args } { + parse_key_args "create_cell_usage_snapshot" args keys {-path -stage} \ + flags {} + + if { [ord::get_db_block] == "NULL" } { + sta_error 1001 "No design block found." + } + set module_name [[ord::get_db_block] getTopModule] + + if { [info exists keys(-path)] } { + set path_name $keys(-path) + } else { + sta_error 1002 "Missing argument -path" + } + + if { [info exists keys(-stage)] } { + set stage_name $keys(-stage) + } else { + sta_error 1003 "Missing argument -stage" + } + + create_cell_usage_snapshot_cmd $module_name $path_name $stage_name +} + # redefine sta::sta_warn/error to call utl::warn/error proc sta_error { id msg } { utl::error STA $id $msg diff --git a/src/dbSta/test/CMakeLists.txt b/src/dbSta/test/CMakeLists.txt index 02735fd942f..2318d7fb290 100644 --- a/src/dbSta/test/CMakeLists.txt +++ b/src/dbSta/test/CMakeLists.txt @@ -3,6 +3,7 @@ or_integration_tests( TESTS block_sta1 constant1 + create_cell_usage_snapshot find_clks1 find_clks2 hier2 diff --git a/src/dbSta/test/cell_usage_snapshot-top-test_stage.jsonok b/src/dbSta/test/cell_usage_snapshot-top-test_stage.jsonok new file mode 100644 index 00000000000..ba1662ea163 --- /dev/null +++ b/src/dbSta/test/cell_usage_snapshot-top-test_stage.jsonok @@ -0,0 +1 @@ +{"stage":"test_stage","cell_usage_info":[{"name":"snl_bufx1","count":4,"area":1E3},{"name":"snl_ffqx1","count":2,"area":1E3}]} \ No newline at end of file diff --git a/src/dbSta/test/create_cell_usage_snapshot.tcl b/src/dbSta/test/create_cell_usage_snapshot.tcl new file mode 100644 index 00000000000..67d2047cb8e --- /dev/null +++ b/src/dbSta/test/create_cell_usage_snapshot.tcl @@ -0,0 +1,17 @@ +# Test for creating a cell usage snapshot. + +# Test environment setup. +set snapshot_file_name cell_usage_snapshot-top-test_stage.json +source "helpers.tcl" +make_result_dir + +# Populate DB with the test design. +read_lef liberty1.lef +read_liberty liberty1.lib +read_verilog hier1.v +link_design top + +# Create the cell usage snapshot and compare it to the golden file. +create_cell_usage_snapshot -path $result_dir -stage test_stage +diff_files cell_usage_snapshot-top-test_stage.jsonok $result_dir/$snapshot_file_name + diff --git a/test/helpers.tcl b/test/helpers.tcl index a0787225501..9c79325ba63 100644 --- a/test/helpers.tcl +++ b/test/helpers.tcl @@ -3,6 +3,13 @@ set test_dir [file dirname [file normalize [info script]]] set result_dir [file join $test_dir "results"] +proc make_result_dir {} { + variable result_dir + if { ![file exists $result_dir] } { + file mkdir $result_dir + } +} + proc make_result_file { filename } { variable result_dir if { ![file exists $result_dir] } {