diff --git a/src/OpenRoad.cc b/src/OpenRoad.cc index 6a6c2a594ac..3d7a676919d 100644 --- a/src/OpenRoad.cc +++ b/src/OpenRoad.cc @@ -272,7 +272,8 @@ void OpenRoad::init(Tcl_Interp* tcl_interp, ioPlacer_, opendp_, global_router_, - detailed_router_); + detailed_router_, + sta_); icewall_ = new pad::ICeWall(db_, logger_); dft_ = new dft::Dft(db_, sta_, logger_); example_ = new exa::Example(db_, logger_); diff --git a/src/ram/BUILD b/src/ram/BUILD index 2cb8a34b5e9..adedc96c624 100644 --- a/src/ram/BUILD +++ b/src/ram/BUILD @@ -31,6 +31,7 @@ cc_library( ], deps = [ "//:ord", + "//src/dbSta", "//src/dbSta:dbNetwork", "//src/dpl", "//src/drt", diff --git a/src/ram/include/ram/ram.h b/src/ram/include/ram/ram.h index 1a41ba0d7ba..16b18902299 100644 --- a/src/ram/include/ram/ram.h +++ b/src/ram/include/ram/ram.h @@ -24,6 +24,7 @@ class dbMaster; namespace sta { class dbNetwork; class LibertyPort; +class dbSta; } // namespace sta namespace utl { @@ -92,7 +93,8 @@ class RamGen ppl::IOPlacer* io_placer, dpl::Opendp* opendp, grt::GlobalRouter* global_router, - drt::TritonRoute* detailed_router); + drt::TritonRoute* detailed_router, + sta::dbSta* sta); ~RamGen() = default; void generate(int mask_size, @@ -132,6 +134,9 @@ class RamGen int r_ports, int w_ports); + double reportTiming(); + void reportPower(); + private: void findMasters(); std::map buildPortMap(odb::dbMaster*); @@ -221,6 +226,7 @@ class RamGen dpl::Opendp* opendp_{nullptr}; grt::GlobalRouter* global_router_{nullptr}; drt::TritonRoute* detailed_router_{nullptr}; + sta::dbSta* sta_{nullptr}; odb::dbMaster* storage_cell_{nullptr}; odb::dbMaster* tristate_cell_{nullptr}; @@ -245,6 +251,7 @@ class RamGen std::vector> addr_inputs_; std::vector data_inputs_; + std::vector write_enable_; std::vector> q_outputs_; std::string behavioral_verilog_filename_; std::string aoi22_in_a1_, aoi22_in_a2_, aoi22_in_b1_, aoi22_in_b2_, diff --git a/src/ram/src/ram.cpp b/src/ram/src/ram.cpp index 2d2c5378007..5159a7e8b25 100644 --- a/src/ram/src/ram.cpp +++ b/src/ram/src/ram.cpp @@ -17,6 +17,7 @@ #include #include "db_sta/dbNetwork.hh" +#include "db_sta/dbSta.hh" #include "dpl/Opendp.h" #include "drt/TritonRoute.h" #include "grt/GlobalRouter.h" @@ -30,8 +31,18 @@ #include "sta/ConcreteLibrary.hh" #include "sta/FuncExpr.hh" #include "sta/Liberty.hh" +#include "sta/MinMax.hh" +#include "sta/NetworkClass.hh" +#include "sta/PathEnd.hh" #include "sta/PortDirection.hh" +#include "sta/PowerClass.hh" +#include "sta/Scene.hh" +#include "sta/SdcClass.hh" +#include "sta/SearchClass.hh" #include "sta/Sequential.hh" +#include "sta/StringUtil.hh" +#include "sta/Transition.hh" +#include "sta/Units.hh" #include "utl/Logger.h" namespace ram { @@ -57,7 +68,8 @@ RamGen::RamGen(sta::dbNetwork* network, ppl::IOPlacer* io_placer, dpl::Opendp* opendp, grt::GlobalRouter* global_router, - drt::TritonRoute* detailed_router) + drt::TritonRoute* detailed_router, + sta::dbSta* sta) : network_(network), db_(db), @@ -67,6 +79,7 @@ RamGen::RamGen(sta::dbNetwork* network, opendp_(opendp), global_router_(global_router), detailed_router_(detailed_router), + sta_(sta), ram_grid_(odb::horizontal) { } @@ -1275,6 +1288,7 @@ void RamGen::generate(const int mask_size, auto in_name = fmt::format("we[{}]", slice); write_enable[slice] = makeBTerm(in_name, dbIoType::INPUT); } + write_enable_ = write_enable; // When column_mux_ratio > 1: word_sel_nets[word_idx] is high/active when // word_idx is the addressed word within a physical row. Derived from the @@ -2056,4 +2070,187 @@ endmodule logger_->info(RAM, 24, "Behavioral Verilog written for {}", module_name); } +static bool isClockPin(const sta::Pin* pin, sta::dbNetwork* network) +{ + sta::LibertyPort* lp = network->libertyPort(pin); + if (!lp) { + return false; + } + return lp->isClock() || lp->isRegClk() || lp->isClockGateClock(); +} + +double RamGen::reportTiming() +{ + network_->setBlock(block_); + sta_->updateTiming(false); + + sta::SceneSeq scenes = sta_->makeSceneSeq(sta_->cmdScene()); + sta::StringSeq group_names; + + // Include non clock primary inputs: D, address, write enable pins + auto collect_primary_inputs = [&]() -> sta::PinSet* { + auto* pins = new sta::PinSet(network_); + for (auto* bterm : data_inputs_) { + pins->insert(network_->dbToSta(bterm)); + } + for (auto& port_addrs : addr_inputs_) { + for (auto* bterm : port_addrs) { + pins->insert(network_->dbToSta(bterm)); + } + } + for (auto* bterm : write_enable_) { + pins->insert(network_->dbToSta(bterm)); + } + return pins; + }; + + auto collect_all_outputs = [&]() -> sta::PinSet* { + auto* pins = new sta::PinSet(network_); + for (auto* inst : block_->getInsts()) { + for (auto* iterm : inst->getITerms()) { + auto* pin = network_->dbToSta(iterm); + if (!pin || isClockPin(pin, network_)) { + continue; + } + auto* lp = network_->libertyPort(pin); + if (lp && lp->direction()->isAnyOutput()) { + pins->insert(pin); + } + } + } + for (auto* bterm : block_->getBTerms()) { + auto* pin = network_->dbToSta(bterm); + if (pin && !isClockPin(pin, network_) + && network_->direction(pin)->isOutput()) { + pins->insert(pin); + } + } + return pins; + }; + + // Find worst unconstrained setup path delay/longest path + sta::ExceptionFrom* setup_from + = sta_->makeExceptionFrom(collect_primary_inputs(), + nullptr, + nullptr, + sta::RiseFallBoth::riseFall(), + sta_->cmdSdc()); + + sta::ExceptionTo* setup_to + = sta_->makeExceptionTo(collect_all_outputs(), + nullptr, + nullptr, + sta::RiseFallBoth::riseFall(), + sta::RiseFallBoth::riseFall(), + sta_->cmdSdc()); + + sta::PathEndSeq setup_ends + = sta_->findPathEnds(setup_from, /* from */ + nullptr, /* thrus */ + setup_to, /* to */ + true, /* unconstrained */ + scenes, /* scenes */ + sta::MinMaxAll::max(), /* min_max */ + 1, /* group_path_count */ + 1, /* endpoint_path_count */ + false, /* unique_pins */ + false, /* unique_edges */ + -sta::INF, /* slack_min */ + sta::INF, /* slack_max */ + true, /* sort_by_slack */ + group_names, /* group_names */ + true, /* setup */ + false, /* hold */ + false, /* recovery */ + false, /* removal */ + true, /* clk_gating_setup */ + false); /* clk_gating_hold */ + + float setup_delay + = setup_ends.empty() + ? 0.0 + : static_cast(setup_ends[0]->dataArrivalTime(sta_)); + + // Find worst unconstrained hold path delay/shortest path + sta::ExceptionFrom* hold_from + = sta_->makeExceptionFrom(collect_primary_inputs(), + nullptr, + nullptr, + sta::RiseFallBoth::riseFall(), + sta_->cmdSdc()); + + sta::ExceptionTo* hold_to + = sta_->makeExceptionTo(collect_all_outputs(), + nullptr, + nullptr, + sta::RiseFallBoth::riseFall(), + sta::RiseFallBoth::riseFall(), + sta_->cmdSdc()); + + sta::PathEndSeq hold_ends + = sta_->findPathEnds(hold_from, /* from */ + nullptr, /* thrus */ + hold_to, /* to */ + true, /* unconstrained */ + scenes, /* scenes */ + sta::MinMaxAll::min(), /* min_max */ + 1, /* group_path_count */ + 1, /* endpoint_path_count */ + false, /* unique_pins */ + false, /* unique_edges */ + -sta::INF, /* slack_min */ + sta::INF, /* slack_max */ + true, /* sort_by_slack */ + group_names, /* group_names */ + false, /* setup */ + true, /* hold */ + false, /* recovery */ + false, /* removal */ + false, /* clk_gating_setup */ + true); /* clk_gating_hold */ + + float hold_delay + = hold_ends.empty() + ? 0.0 + : static_cast(hold_ends[0]->dataArrivalTime(sta_)); + + sta::Unit* time_unit = sta_->units()->timeUnit(); + logger_->info(RAM, + 44, + "RAM maximum path delay: {} {}", + time_unit->asString(setup_delay), + time_unit->scaleAbbrevSuffix()); + logger_->info(RAM, + 45, + "RAM minimum path delay: {} {}", + time_unit->asString(hold_delay), + time_unit->scaleAbbrevSuffix()); + + if (setup_delay <= 0.0F) { + return 0.0; + } + logger_->info(RAM, + 47, + "Using maximum path delay of {} {} as the clock period for " + "power estimation", + time_unit->asString(setup_delay), + time_unit->scaleAbbrevSuffix()); + return time_unit->staToUser(setup_delay); +} + +void RamGen::reportPower() +{ + sta_->updateTiming(false); + + sta::PowerResult total, sequential, combinational, clock, macro, pad; + sta_->power( + sta_->cmdScene(), total, sequential, combinational, clock, macro, pad); + sta::Unit* power_unit = sta_->units()->powerUnit(); + logger_->info(RAM, + 46, + "RAM estimated power: {} {}", + power_unit->asString(total.total()), + power_unit->scaleAbbrevSuffix()); +} + } // namespace ram diff --git a/src/ram/src/ram.i b/src/ram/src/ram.i index 8b225fcc890..9cefb9aab90 100644 --- a/src/ram/src/ram.i +++ b/src/ram/src/ram.i @@ -127,6 +127,18 @@ void ram_routing() ram_gen->ramRouting(thread_count); } +double ram_report_timing() +{ + RamGen* ram_gen = ord::getRamGen(); + return ram_gen->reportTiming(); +} + +void ram_report_power() +{ + RamGen* ram_gen = ord::getRamGen(); + ram_gen->reportPower(); +} + void set_behavioral_verilog_filename(const char* filename) { RamGen* ram_gen = ord::getRamGen(); @@ -135,4 +147,4 @@ void set_behavioral_verilog_filename(const char* filename) } //namespace_ram -%} // inline \ No newline at end of file +%} // inline diff --git a/src/ram/src/ram.tcl b/src/ram/src/ram.tcl index 4e58c501636..8c843d3f0d9 100644 --- a/src/ram/src/ram.tcl +++ b/src/ram/src/ram.tcl @@ -297,4 +297,10 @@ proc generate_ram { args } { ram::ram_filler $filler_cells ram::ram_routing + estimate_parasitics -global_routing + set clock_period [ram::ram_report_timing] + if { $clock_period > 0 } { + create_clock -name ram_clk -period $clock_period [get_ports clk] + } + ram::ram_report_power } diff --git a/src/ram/test/make_7x7_2r1w_nangate45.ok b/src/ram/test/make_7x7_2r1w_nangate45.ok index e2b03632f05..a3058ad666c 100644 --- a/src/ram/test/make_7x7_2r1w_nangate45.ok +++ b/src/ram/test/make_7x7_2r1w_nangate45.ok @@ -113,6 +113,10 @@ [INFO DRT-0036] via9 guide region query size = 0. [INFO DRT-0036] metal10 guide region query size = 0. [INFO DRT-0179] Init gr pin query. +[INFO RAM-0044] RAM maximum path delay: 0.284 ns +[INFO RAM-0045] RAM minimum path delay: 0.116 ns +[INFO RAM-0047] Using maximum path delay of 0.284 ns as the clock period for power estimation +[INFO RAM-0046] RAM estimated power: 749876.125 nW No differences found. No differences found. No differences found. diff --git a/src/ram/test/make_7x7_nangate45.ok b/src/ram/test/make_7x7_nangate45.ok index 7c45868bbda..420c371f5f6 100644 --- a/src/ram/test/make_7x7_nangate45.ok +++ b/src/ram/test/make_7x7_nangate45.ok @@ -109,6 +109,10 @@ [INFO DRT-0036] via9 guide region query size = 0. [INFO DRT-0036] metal10 guide region query size = 0. [INFO DRT-0179] Init gr pin query. +[INFO RAM-0044] RAM maximum path delay: 0.282 ns +[INFO RAM-0045] RAM minimum path delay: 0.118 ns +[INFO RAM-0047] Using maximum path delay of 0.282 ns as the clock period for power estimation +[INFO RAM-0046] RAM estimated power: 550985.750 nW No differences found. No differences found. No differences found. diff --git a/src/ram/test/make_8x8_2r1w_sky130.ok b/src/ram/test/make_8x8_2r1w_sky130.ok index fc0abe7aa2f..068370c6955 100644 --- a/src/ram/test/make_8x8_2r1w_sky130.ok +++ b/src/ram/test/make_8x8_2r1w_sky130.ok @@ -67,6 +67,10 @@ [INFO DRT-0036] via4 guide region query size = 0. [INFO DRT-0036] met5 guide region query size = 0. [INFO DRT-0179] Init gr pin query. +[INFO RAM-0044] RAM maximum path delay: 1.131 ns +[INFO RAM-0045] RAM minimum path delay: 0.395 ns +[INFO RAM-0047] Using maximum path delay of 1.131 ns as the clock period for power estimation +[INFO RAM-0046] RAM estimated power: 1161060.500 nW No differences found. No differences found. No differences found. diff --git a/src/ram/test/make_8x8_sky130.ok b/src/ram/test/make_8x8_sky130.ok index a395beb2bbe..d4ff2fb4ca6 100644 --- a/src/ram/test/make_8x8_sky130.ok +++ b/src/ram/test/make_8x8_sky130.ok @@ -67,6 +67,10 @@ [INFO DRT-0036] via4 guide region query size = 0. [INFO DRT-0036] met5 guide region query size = 0. [INFO DRT-0179] Init gr pin query. +[INFO RAM-0044] RAM maximum path delay: 1.114 ns +[INFO RAM-0045] RAM minimum path delay: 0.399 ns +[INFO RAM-0047] Using maximum path delay of 1.114 ns as the clock period for power estimation +[INFO RAM-0046] RAM estimated power: 848681.312 nW No differences found. No differences found. No differences found.