From 9a6d4863056e7ff10fa5f3d016ca91540834133a Mon Sep 17 00:00:00 2001 From: Milad Ebrahimipour Date: Thu, 31 Aug 2023 01:00:45 -0400 Subject: [PATCH 1/2] Adding Number of Logic levels and number of timing graph levels to timing setup and hold reports --- .../libtatum/tatum/TimingReporter.cpp | 22 +-- .../libtatum/tatum/TimingReporter.hpp | 10 +- vpr/src/analysis/timing_reports.cpp | 4 +- vpr/src/base/vpr_api.cpp | 4 +- vpr/src/base/vpr_context.h | 10 ++ vpr/src/util/vpr_utils.cpp | 130 ++++++++++++++++++ vpr/src/util/vpr_utils.h | 19 +++ 7 files changed, 182 insertions(+), 17 deletions(-) diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp index 609b0c0b03e..729aaef20b1 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp @@ -86,32 +86,32 @@ TimingReporter::TimingReporter(const TimingGraphNameResolver& name_resolver, void TimingReporter::report_timing_setup(std::string filename, const SetupTimingAnalyzer& setup_analyzer, - size_t npaths) const { + size_t npaths, const int num_logic_levels) const { std::ofstream os(filename); - report_timing_setup(os, setup_analyzer, npaths); + report_timing_setup(os, setup_analyzer, npaths, num_logic_levels); } void TimingReporter::report_timing_setup(std::ostream& os, const SetupTimingAnalyzer& setup_analyzer, - size_t npaths) const { + size_t npaths, const int num_logic_levels) const { auto paths = path_collector_.collect_worst_setup_timing_paths(timing_graph_, setup_analyzer, npaths); - report_timing(os, paths); + report_timing(os, paths, num_logic_levels); } void TimingReporter::report_timing_hold(std::string filename, const HoldTimingAnalyzer& hold_analyzer, - size_t npaths) const { + size_t npaths, const int num_logic_levels) const { std::ofstream os(filename); - report_timing_hold(os, hold_analyzer, npaths); + report_timing_hold(os, hold_analyzer, npaths, num_logic_levels); } void TimingReporter::report_timing_hold(std::ostream& os, const HoldTimingAnalyzer& hold_analyzer, - size_t npaths) const { + size_t npaths, const int num_logic_levels) const { auto paths = path_collector_.collect_worst_hold_timing_paths(timing_graph_, hold_analyzer, npaths); - report_timing(os, paths); + report_timing(os, paths, num_logic_levels); } void TimingReporter::report_skew_setup(std::string filename, @@ -195,7 +195,7 @@ void TimingReporter::report_unconstrained_hold(std::ostream& os, */ void TimingReporter::report_timing(std::ostream& os, - const std::vector& paths) const { + const std::vector& paths, const int num_logic_levels) const { tatum::OsFormatGuard flag_guard(os); os << "#Timing report of worst " << paths.size() << " path(s)\n"; @@ -203,6 +203,10 @@ void TimingReporter::report_timing(std::ostream& os, os << "# Output precision: " << precision_ << "\n"; os << "\n"; + os << "# Logical Levels: " << num_logic_levels << "\n"; + os << "# Timing Graph Levels: " << timing_graph_.levels().size() << "\n"; + os << "\n"; + size_t i = 0; for(const auto& path : paths) { os << "#Path " << ++i << "\n"; diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp index 1569aa6d704..0234869b4cd 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp @@ -62,11 +62,11 @@ class TimingReporter { float unit_scale=1e-9, size_t precision=3); public: - void report_timing_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; - void report_timing_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; + void report_timing_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS, const int num_logic_levels=0) const; + void report_timing_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS, const int num_logic_levels=0) const; - void report_timing_hold(std::string filename, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; - void report_timing_hold(std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; + void report_timing_hold(std::string filename, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS, const int num_logic_levels=0) const; + void report_timing_hold(std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS, const int num_logic_levels=0) const; void report_skew_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t nworst=REPORT_TIMING_DEFAULT_NPATHS) const; void report_skew_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t nworst=REPORT_TIMING_DEFAULT_NPATHS) const; @@ -94,7 +94,7 @@ class TimingReporter { }; private: - void report_timing(std::ostream& os, const std::vector& paths) const; + void report_timing(std::ostream& os, const std::vector& paths, const int num_logic_levels) const; void report_timing_path(std::ostream& os, const TimingPath& path) const; diff --git a/vpr/src/analysis/timing_reports.cpp b/vpr/src/analysis/timing_reports.cpp index 88762367a95..d3de4a06f58 100644 --- a/vpr/src/analysis/timing_reports.cpp +++ b/vpr/src/analysis/timing_reports.cpp @@ -23,7 +23,7 @@ void generate_setup_timing_stats(const std::string& prefix, const SetupTimingInf tatum::TimingReporter timing_reporter(resolver, *timing_ctx.graph, *timing_ctx.constraints); - timing_reporter.report_timing_setup(prefix + "report_timing.setup.rpt", *timing_info.setup_analyzer(), analysis_opts.timing_report_npaths); + timing_reporter.report_timing_setup(prefix + "report_timing.setup.rpt", *timing_info.setup_analyzer(), analysis_opts.timing_report_npaths, g_vpr_ctx.logic_levels().num_logic_levels()); if (analysis_opts.timing_report_skew) { timing_reporter.report_skew_setup(prefix + "report_skew.setup.rpt", *timing_info.setup_analyzer(), analysis_opts.timing_report_npaths); @@ -43,7 +43,7 @@ void generate_hold_timing_stats(const std::string& prefix, const HoldTimingInfo& tatum::TimingReporter timing_reporter(resolver, *timing_ctx.graph, *timing_ctx.constraints); - timing_reporter.report_timing_hold(prefix + "report_timing.hold.rpt", *timing_info.hold_analyzer(), analysis_opts.timing_report_npaths); + timing_reporter.report_timing_hold(prefix + "report_timing.hold.rpt", *timing_info.hold_analyzer(), analysis_opts.timing_report_npaths, g_vpr_ctx.logic_levels().num_logic_levels()); if (analysis_opts.timing_report_skew) { timing_reporter.report_skew_hold(prefix + "report_skew.hold.rpt", *timing_info.hold_analyzer(), analysis_opts.timing_report_npaths); diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 8cfd23b18be..1e986b2e2b0 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -371,6 +371,8 @@ bool vpr_flow(t_vpr_setup& vpr_setup, t_arch& arch) { return true; } + g_vpr_ctx.mutable_logic_levels().levelize(); + { //Pack bool pack_success = vpr_pack_flow(vpr_setup, arch); @@ -1596,4 +1598,4 @@ void vpr_print_error(const VprError& vpr_error) { VTR_LOG_ERROR("\nType: %s\nFile: %s\nLine: %d\nMessage: %s\n", error_type, filename.c_str(), vpr_error.line(), msg.c_str()); -} +} \ No newline at end of file diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 260c7f7addc..94d7d59851d 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -639,6 +639,9 @@ class VprContext : public Context { const PackingMultithreadingContext& packing_multithreading() const { return packing_multithreading_; } PackingMultithreadingContext& mutable_packing_multithreading() { return packing_multithreading_; } + const Levelized& logic_levels() const {return logic_levels_;} + Levelized& mutable_logic_levels() {return logic_levels_;} + private: DeviceContext device_; @@ -656,6 +659,13 @@ class VprContext : public Context { NocContext noc_; PackingMultithreadingContext packing_multithreading_; + + /** + * @brief The Levelized class represents a graph that has been levelized, allowing efficient traversal and analysis. + * It provides methods to perform graph levelization, check if the graph has been levelized, and access information about + * nodes in each logic level and the total number of logic levels. + */ + Levelized logic_levels_; }; #endif diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 574a6d0fc84..18558ea6860 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -2523,3 +2523,133 @@ void add_pb_child_to_list(std::list& pb_list, const t_pb* parent_pb } } } + + +// Function to perform graph levelization +void Levelized::levelize() { + //Levelizes the graph + const auto& atom_nlist = g_vpr_ctx.atom().nlist; + const auto atom_lookup = g_vpr_ctx.atom().lookup; + + const auto tg_ = g_vpr_ctx.timing().graph; + // vtr::vector_map> level_nodes; //Nodes in each level + + //Allocate space for the first level + level_nodes_.resize(1); + + //Copy the number of input edges per-node + //These will be decremented to know when all a node's upstream parents have been + //placed in a previous level (indicating that the node goes in the current level) + // + //Also initialize the first level (nodes with no fanin) + std::vector node_fanin_remaining(atom_nlist.blocks().size()); + for(auto node_id : atom_nlist.blocks()) { + size_t node_fanin = 0; + for(auto in_pin: atom_nlist.block_input_pins(node_id)){ + auto sink_tnode = atom_lookup.atom_pin_tnode(in_pin); + auto net_id = atom_nlist.pin_net(in_pin); + auto source_pin = atom_nlist.net_driver(net_id); + auto source_tnode = atom_lookup.atom_pin_tnode(source_pin); + auto edge = tg_->find_edge(source_tnode, sink_tnode); + if(tg_->edge_disabled(edge)) continue; + ++node_fanin; + } + node_fanin_remaining[size_t(node_id)] = node_fanin; + + //Initialize the first level + if(node_fanin == 0) { + level_nodes_[0].push_back(node_id); + + // if (atom_nlist.block_type(node_id) == AtomBlockType::INPAD) { + // //We require that all primary inputs (i.e. top-level circuit inputs) to + // //be SOURCEs. Due to disconnected nodes we may have non-SOURCEs which + // //otherwise appear in the first level. + // primary_inputs.push_back(node_id); + // } + } + } + + //Walk the graph from primary inputs (no fanin) to generate a topological sort + // + //We inspect the output edges of each node and decrement the fanin count of the + //target node. Once the fanin count for a node reaches zero it can be added + //to the current level. + int level_idx = 0; + + std::vector last_level; + vtr::vector visited(atom_nlist.blocks().size(), false); + + bool inserted_node_in_level = true; + while(inserted_node_in_level) { //If nothing was inserted we are finished + inserted_node_in_level = false; + + for(const auto node_id : level_nodes_[level_idx]) { + //Inspect the fanout + for(auto out_pin: atom_nlist.block_output_pins(node_id)){ + auto source_tnode = atom_lookup.atom_pin_tnode(out_pin); + auto net_id = atom_nlist.pin_net(out_pin); + for(auto sink_pin: atom_nlist.net_sinks(net_id)){ + auto sink_tnode = atom_lookup.atom_pin_tnode(sink_pin); + auto edge = tg_->find_edge(source_tnode, sink_tnode); + if(tg_->edge_disabled(edge)) continue; + auto blk_id = atom_nlist.pin_block(sink_pin); + VTR_ASSERT((node_fanin_remaining[(size_t)blk_id] > 0 && !visited[blk_id]) || + (node_fanin_remaining[(size_t)blk_id] <= 0 && visited[blk_id])); + node_fanin_remaining[(size_t)blk_id]--; + + if(node_fanin_remaining[(size_t)blk_id] == 0){ + if (atom_nlist.block_type(node_id) != AtomBlockType::OUTPAD){ + level_nodes_.resize(level_idx+2); + level_nodes_[level_idx+1].push_back(blk_id); + inserted_node_in_level = true; + visited[blk_id] = true; + } + else{ + VTR_ASSERT(atom_nlist.block_output_pins(blk_id).size() == 0); + last_level.push_back(blk_id); + } + } + } + + } + } + + if(inserted_node_in_level) { + level_idx++; + } + } + + //Add the last level to the end of the levelization + level_nodes_.emplace_back(last_level); + level_idx++; + + //Add SINK type nodes in the last level to logical outputs + //Note that we only do this for sinks, since non-sink nodes may end up + //in the last level (e.g. due to breaking combinational loops) + // auto is_sink = [this](NodeId id) { + // return this->node_type(id) == NodeType::SINK; + // }; + // std::copy_if(last_level.begin(), last_level.end(), std::back_inserter(logical_outputs_), is_sink); + + //Mark the levelization as valid + is_levelized_ = true; + num_logic_levels_ = level_idx; +} + + +// Function to check if the graph has been levelized +bool Levelized::is_levelized() const{ + return is_levelized_; +} + + +// Function to get a pointer to the map of nodes in each level +vtr::vector_map>* Levelized::level_nodes(){ + return &level_nodes_; +} + + +// Function to get the number of logic levels in the levelized graph +int Levelized::num_logic_levels() const{ + return num_logic_levels_; +} diff --git a/vpr/src/util/vpr_utils.h b/vpr/src/util/vpr_utils.h index dd16e0fd03c..3591269c9c8 100644 --- a/vpr/src/util/vpr_utils.h +++ b/vpr/src/util/vpr_utils.h @@ -317,4 +317,23 @@ void add_pb_child_to_list(std::list& pb_list, const t_pb* parent_pb class VprConstraints; void apply_route_constraints(VprConstraints& constraint); + +// Class definition for a levelized graph +/** + * @brief The Levelized class represents a graph that has been levelized, allowing efficient traversal and analysis. + * It provides methods to perform graph levelization, check if the graph has been levelized, and access information about + * nodes in each logic level and the total number of logic levels. + */ +class Levelized{ + public: + void levelize(); // Function to perform graph levelization + bool is_levelized() const; // Check if the graph has been levelized + vtr::vector_map>* level_nodes(); // Get a pointer to the map of nodes in each level + int num_logic_levels() const; // Get the number of logic levels in the levelized graph + private: + vtr::vector_map> level_nodes_; //Nodes in each level + bool is_levelized_; // Flag to indicate whether graph has been levelized + int num_logic_levels_; // Number of logic levels in the levelized graph +}; + #endif From f77ef50e5ddc39f297032ec7db1064c0dd8dcd4b Mon Sep 17 00:00:00 2001 From: Milad Ebrahimipour Date: Thu, 31 Aug 2023 02:59:47 -0400 Subject: [PATCH 2/2] Adding Number of Logic levels and number of timing graph levels to timing setup and hold reports --- vpr/src/util/vpr_utils.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 18558ea6860..612eb6519e1 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -1182,7 +1182,7 @@ const t_port* find_pb_graph_port(const t_pb_graph_node* pb_gnode, std::string po const t_pb_graph_pin* find_pb_graph_pin(const t_pb_graph_node* pb_gnode, std::string port_name, int index) { for (int iport = 0; iport < pb_gnode->num_input_ports; iport++) { - if (pb_gnode->num_input_pins[iport] < index) continue; + if (pb_gnode->num_input_pins[iport] <= index) continue; const t_pb_graph_pin* gpin = &pb_gnode->input_pins[iport][index]; @@ -1191,7 +1191,7 @@ const t_pb_graph_pin* find_pb_graph_pin(const t_pb_graph_node* pb_gnode, std::st } } for (int iport = 0; iport < pb_gnode->num_output_ports; iport++) { - if (pb_gnode->num_output_pins[iport] < index) continue; + if (pb_gnode->num_output_pins[iport] <= index) continue; const t_pb_graph_pin* gpin = &pb_gnode->output_pins[iport][index]; @@ -1200,7 +1200,7 @@ const t_pb_graph_pin* find_pb_graph_pin(const t_pb_graph_node* pb_gnode, std::st } } for (int iport = 0; iport < pb_gnode->num_clock_ports; iport++) { - if (pb_gnode->num_clock_pins[iport] < index) continue; + if (pb_gnode->num_clock_pins[iport] <= index) continue; const t_pb_graph_pin* gpin = &pb_gnode->clock_pins[iport][index];