Program Listing for File tdm.h

Return to documentation for file (src/tdm/tdm.h)

#ifndef TDM_H
#define TDM_H

#include <spdlog/sinks/basic_file_sink.h>
#include <stdlib.h>

#include <algorithm>
#include <cmath>
#include <cstring>
#include <ctime>
#include <fstream>
#include <future>
#include <iomanip>
#include <iostream>
#include <list>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <thread>
#include <unordered_set>
#include <vector>

#include "delay_lib.h"
#include "routing.h"
#include "tool.h"


class bidir_map {
  int _size = 0;

 public:
  flat_hash_map<pair<int, int>, int> node_map;
  vector<pair<int, int>> reverse_node_map;

  int operator[](const pair<int, int> &key)
  {
    if (node_map.find(key) == node_map.end()) {
      node_map[key] = _size++;
      reverse_node_map.push_back(key);
    }
    return node_map[key];
  }
  int size() const
  {
    return _size;
  }
  const pair<int, int> &at(int id) const
  {
    return reverse_node_map[id];
  }
  bool contains(const pair<int, int> &key) const
  {
    return node_map.find(key) != node_map.end();
  }
  bool try_get(const pair<int, int> &key, int &value) const
  {
    auto it = node_map.find(key);
    if (it != node_map.end()) {
      value = it->second;
      return true;
    }
    return false;
  }
};

struct timing_edge {
  int src;
  int dst;
  int net_id;
  int src_node;
  int dst_node;
  bool is_tdm;
  double delay;
  double tdm_ratio;
  double tdm_ratio_assign;
  int group_idx;
  double tdm_delay;
  double die_delay;
  double cut_delay;
  double clock_period;
  RatioLib lib_choice;
  std::string port_name;
  std::string left_port_name;
  std::string right_port_name;
  DelayScope delay_scope = DelayScope::Default;

  string get_port_name() const;
};

struct hash_pair {
  template <class T1, class T2>
  std::size_t operator()(const std::pair<T1, T2> &p) const
  {
    return std::hash<T1>()(p.first) ^ (std::hash<T2>()(p.second) << 1);
  }
};

struct CutTimingPathInfo {
  int index;
  double cp;
  double old_slack;
  vector<int> ratios;
  int die_hop;
  double new_slack;
  double delay_real_sum;
  double delay_sum;
};

struct EdgeStat {
  int id        = -1;
  double crit   = 0.0;  // criticality (0~1)
  double r_cont = 1.0;  // continuous  ratio (floating)
  int r_disc    = 1;    // discrete     ratio (int, multiple of 4)
};

struct EdgeSnap {
  int eid;
  double ratio_assign;
  double tdm_ratio;
  double delay;
  RatioLib lib_choice;
};

struct TypedSockets {
  vector<string> left_gio, right_gio;
  vector<string> left_mgt, right_mgt;
};

struct MgtSubChannel {
  double clock;
  int used;
};

struct DomainStat {
  double cp        = 0.0;
  double slack_min = std::numeric_limits<double>::max();
};

static inline double saturate(double v, double lo, double hi)
{
  return std::max(lo, std::min(v, hi));
}
class tdm_group {
 public:
  int left_part;
  int right_part;
  int backward_id;
  int capacity;
  int cap_cont = 0;
  vector<int> edges;
  vector<string> left_sockets;
  vector<string> right_sockets;
  int forward_capacity;
  int backward_capacity;
  double lambda        = 0.0;
  double learning_rate = 0.5;
  bool is_mgt          = false;
  int gio_links        = 0;
  int mgt_links        = 0;
  bool channel_capacity_mode = false;
  int gio_channel_capacity = 0;
  int mgt_channel_capacity = 0;

  int fwd_cap_gio = 0, bwd_cap_gio = 0;
  int fwd_cap_mgt = 0, bwd_cap_mgt = 0;
  double cap_cont_gio;
  double cap_cont_mgt;
  double cap_cont_gio_fwd = 0, cap_cont_gio_bwd = 0;
  double cap_cont_mgt_fwd = 0, cap_cont_mgt_bwd = 0;
};

class Tdm {
 public:
  bidir_map tdm_group_map;
  bidir_map node_maps;
  bidir_map edge_map;
  vector<tdm_group> tdm_groups;
  vector<timing_edge> timing_edges;
  set<int> tdm_choices;
  set<int> mgt_ratios;

  const flat_hash_map<int, set<pair<int, int>>> &route_trees;
  vector<vector<int>> die_physical_links;
  vector<vector<int>> die_gio_links;
  vector<vector<int>> die_mgt_links;
  vector<vector<int>> die_graph;
  vector<cut_timing_path> &cut_timing_paths;
  const vector<cut_net> &cut_nets;
  vector<vector<int>> cut_mat;
  const shared_ptr<mulClockAttr> &mul_clock_attr;
  const unordered_map<cut_timing_path, double>
      &cut_to_tp_id;
  const graph &finest;
  std::vector<int> node_to_route_part;
  vector<DieConnection> die_connections;
  vector<std::vector<int>> board_capacity;
  const flat_hash_map<int, vector<RouteTreeEdge>>
      *route_trees_edges;

  double cut_delay;
  double tdm_delay;
  double die_delay;
  double clock_period;
  bool enable_net;
  int channel_grouping_capacity;
  bool is_directed;
  bool delayLibReady = false;
  bool has_die       = false;
  bool has_timing = false;
  bool contest    = false;
  bool debug_mode = false;
  int solver_threads = 1;
  int log_topk       = 1;
  bool tdm_fast_mode = false;
  bool use_worst_slack_objective =
      false;
  bool allow_mgt_on_non_timing_edges =
      false;
  bool use_hierarchy_delay = false;
  std::string tdm_opt_mode = "legacy";
  int board_count_hint     = -1;
  int fpga_per_board_hint  = -1;
  vector<vector<int>>
      fpga_hierarchy_paths;
  const double INIT_LR       = 0.5;
  const double MAX_LR        = 5.0;
  const double MIN_LR        = 1e-3;
  const double TDM_RATIO_EPS = 1e-6;

  double min_ratio;
  double max_ratio;
  const double R_STEP          = 2;
  const double R_MAX           = 256;
  const double R_MIN           = 1;
  const DelayLibrary *delayLib = nullptr;

  double lib_delay(RatioLib lib, double r) const;
  double lib_slope(RatioLib lib, double r) const;
  DelayScope delay_scope_for_parts(int src_part, int dst_part) const;
  double scoped_lib_delay(DelayScope scope, RatioLib lib, double r) const;
  double scoped_lib_slope(DelayScope scope, RatioLib lib, double r) const;
  double lib_rmin(RatioLib lib) const;
  double lib_rmax(RatioLib lib) const;
  bool is_mgt_ratio(int r) const;  // 判断是否为MGT ratio
  bool is_bypass_ratio(int r) const;
  double mgt_benefit_from_ratio(int base_ratio, double mu) const;
  void relief_pack_one_dir(const tdm_group &g_dir,
                           const std::vector<int> &dir_edges,
                           int gio_cap_channels,
                           int mgt_cap_lanes,  // = 8 * mgt_links (lanes)
                           const std::vector<double> &edge_mu,
                           const std::vector<uint8_t> &edge_in_path);
  inline RatioLib lib_from_ratio(int r) const
  {
    return is_mgt_ratio(r) ? RatioLib::MGT : RatioLib::GIO;
  }
  std::vector<int> choices_for(RatioLib lib) const;  // 候选离散比率

  Tdm(const vector<vector<int>> &die_graph,
      vector<vector<int>> die_physical_links, vector<vector<int>> die_gio_links,
      vector<vector<int>> die_mgt_links,
      vector<cut_timing_path> &cut_timing_paths,
      const vector<cut_net> &cut_nets, const vector<vector<int>> &cut_mat,
      const flat_hash_map<int, set<pair<int, int>>> &route_trees,
      const flat_hash_map<int, vector<RouteTreeEdge>> *route_trees_edges,
      double cut_delay, double tdm_delay, double die_delay, double clock_period,
      const shared_ptr<mulClockAttr> &mul_clock_attr, const graph &finest,
      const std::vector<int> &node_to_route_part,
      const unordered_map<cut_timing_path, double> &cut_to_tp_id, int max_iters,
      double convergence_threshold, double convergence_num, double decay_factor,
      double initial_learning_rate, double decay_base, double decay_rate,
      vector<DieConnection> die_connections, double stable_num, bool enable_net,
      vector<std::vector<int>> board_capacity, const DelayLibrary *delayLib,
      bool delayLibReady, bool has_die, bool has_timing, bool debug_mode,
      int solver_threads, int log_topk, bool tdm_fast_mode,
      bool use_worst_slack_objective, bool allow_mgt_on_non_timing_edges,
      const std::string &tdm_opt_mode, bool contest, int board_count_hint = -1,
      int fpga_per_board_hint                  = -1,
      vector<vector<int>> fpga_hierarchy_paths = {});

  void tdm_assign();
  void tdm_assign_optimized_legacy();
  void tdm_assign_optimized();
  void tdm_assign_optimized_contest();
  void tdm_assign_avg_only();
  void tdm_assign_optimized_1();
  void tdm_assign_optimized_2();
  void tdm_assign_optimized_3();

  void init_average();
  vector<int> build_try_ratios_from_lib(RatioLib lib) const;
  void tdm_continuous_assignment_optimized();
  void assign_continuous_paper();
  void discretise_dp_paper();
  std::pair<double, std::vector<double>> compute_RW_net();
  void legalize_per_group();

  void tdm_discretization_dp();
  double compute_RW();
  void init_net_criticality();
  void tdm_assign_paper();
  void refine_continuous_paper();
  // Experimental branches selected by para.test=4/5.  They do not export a
  // standard solution and run the common cut-timing-path slack analyzer last.
  void tdm_assign_synergistic_paper();
  void tdm_assign_near_optimal_paper();
  void enter_paper_experiment_mode();
  void build_paper_timing_path_edges(const std::string &tag);
  void legalize_paper_wire_assignment(bool delay_aware);

  double mgt_benefit(int eid, double mu) const;
  tdm_group make_subgroup_single_dir(const tdm_group &g,
                                     const std::vector<int> &edges) const;
  int required_roots_bestcase(const tdm_group &g_sub, RatioLib lib,
                              double max_bound);
  void pack_subset_assign(const tdm_group &g_sub, RatioLib lib, int cap_roots);
  vector<int> build_try_ratios_from_lib(RatioLib lib, double focus_ratio,
                                        int max_out) const;
  void select_and_pack_one_dir(const tdm_group &g_dir,
                               const std::vector<int> &dir_edges,
                               int gio_cap_roots, int mgt_cap_roots,
                               const std::vector<double> &edge_mu);
  void discretize_group_separate_libs(tdm_group &g,
                                      const std::vector<double> &edge_mu);
  void discretize_group_gio_baseline(tdm_group &g);
  void discretize_pure_gio_then_sequential_mgt(
      const std::vector<double> &edge_mu);
  void improve_all_groups_with_mgt_slack_guided(
      const std::vector<double> &edge_mu);
  bool improve_one_dir_slack_guided(const tdm_group &g_dir,
                                    const std::vector<int> &dir_edges,
                                    int gio_cap_channels, int mgt_cap_lanes,
                                    const std::vector<double> &edge_mu,
                                    const std::vector<uint8_t> &edge_in_path,
                                    double &best_min_slack);
  void set_continuous_capacity_baseline();
  std::vector<uint8_t> build_edge_in_any_cut_path(int edge_count) const;
  void log_timing_vs_non_timing_tdm_usage(
      const std::string &tag, bool append_to_pair_stats_file = false) const;
  int mgt_lanes_needed_same_clock(const std::vector<int> &mgt_edges,
                                  int r_mgt) const;
  bool mgt_assign_feasible_same_clock(const std::vector<int> &mgt_edges,
                                      int r_mgt, int mgt_cap_lanes) const;
  int mgt_lanes_needed_mixed_ratio(const std::vector<int> &mgt_edges) const;
  bool mgt_channel_packable_dir(const std::vector<int> &mgt_edges,
                                int mgt_cap_lanes) const;
  void pack_dir_by_sets(const tdm_group &g_dir, const std::vector<int> &gio_set,
                        const std::vector<int> &mgt_set, int gio_cap_channels,
                        int mgt_cap_lanes);
  bool repack_dir_by_lib_sets(const tdm_group &g_dir,
                              const std::vector<int> &dir_edges,
                              const std::vector<int> &gio_set,
                              const std::vector<int> &mgt_set,
                              int gio_cap_channels, int mgt_cap_lanes);
  void split_group_base(tdm_group &group, double *forward_usage = nullptr,
                        double *backward_usage = nullptr);
  void split_group(tdm_group &group);
  void apply_gio_root_split(tdm_group &group, int fwd_gio_roots);
  bool optimize_group_gio_root_split(tdm_group &group,
                                     const std::vector<double> &edge_mu,
                                     bool mixed_lib);
  bool apply_final_polish(const std::vector<double> &edge_mu,
                          int max_rounds = 3);
  bool apply_gio_capacity_fill(int max_rounds = 3, double active_window = 5.0);
  bool apply_mgt_channel_polish(int max_rounds = 3, double active_window = 5.0);
  bool legalize_all_gio_dir_bucket_overflow(const std::string &tag,
                                            int max_moves = 10000);
  bool apply_score_batch_reallocation(int max_rounds           = 2,
                                      double active_cut_window = 5.0,
                                      int critical_topk        = 50,
                                      int protected_topk       = 200);
  bool apply_score_polish(int max_rounds = 3, double active_window = 2.0,
                          double tau = 1.0);
  bool socket_packable_dir(const tdm_group &g, bool is_forward,
                           const std::vector<int> &dir_edges);
  bool socket_packable_dir_with_map(
      const tdm_group &g, bool is_forward, const std::vector<int> &dir_edges,
      const std::unordered_map<int, int> &ratio_map);
  bool socket_packable_dir(const tdm_group &g, bool is_fwd) const;
  bool update_edge_mu_for_worst_slack(std::vector<double> &edge_mu) const;
  bool tdm_root_split_enabled() const;
  bool tdm_final_polish_enabled() const;
  bool tdm_postprocess_enabled() const;
  bool tdm_mu_slack_enabled() const;
  int pick_choice(int base, double bound, RatioLib lib);
  double get_min_bound_typed(const tdm_group &group, double max_bound,
                             bool is_forward, RatioLib lib,
                             int capacity_groups);
  int get_min_wire_typed(const tdm_group &group, int idx, double bound,
                         bool assign_ratio, bool is_forward, RatioLib lib,
                         int capacity_groups, int &next_idx);
  int get_min_wire_common(const tdm_group &group, int idx, double bound,
                          bool assign_ratio, bool is_forward,
                          const std::vector<int> &choices, int capacity_groups,
                          int &next_idx);
  double get_min_bound_common(const tdm_group &group, double max_bound,
                              bool is_forward, const std::vector<int> &choices,
                              int capacity_groups);
  // 获取group中从idx开始,选择choice个元素的最大索引
  int get_end_idx(const tdm_group &group, int idx, int choice, double bound);
  int get_min_wire(const tdm_group &group, int idx, double bound,
                   bool assign_ratio, bool is_forward);
  double get_min_bound(const tdm_group &group, double max_bound,
                       bool is_forward);
  double get_min_bound_lib(const tdm_group &group, double max_bound,
                           bool is_forward, RatioLib lib,
                           int capacity_channels);
  int get_min_wire_lib(const tdm_group &group, int idx, double bound,
                       bool assign_ratio, bool is_forward, RatioLib lib);

  double eval_worst_delay_discrete() const;
  double eval_worst_net_rw_current() const;
  double eval_worst_net_rw_discrete() const;
  void log_contest_worst_nets(const std::string &stage, bool use_discrete_delay,
                              int top_k) const;
  double eval_min_slack_discrete() const;
  void export_tdm_solution();
  void export_contest_outputs(const std::string &route_filename,
                              const std::string &tdm_filename) const;
  void assign_group_sockets(const vector<DieConnection> &die_connections,
                            bool has_die);

  void build_tdm(int &edge_id, const shared_ptr<mulClockAttr> &mul_clock_attr);
  void buildGraphEdges(vector<std::vector<int>> &inEdges,
                       vector<std::vector<int>> &outEdges, int nodeCount,
                       int &edgeCount);
  vector<int> topological_sort_kahn(const vector<vector<int>> &in_edges,
                                    const vector<vector<int>> &out_edges,
                                    int node_count);
  void tdm_local_search(vector<int> &topo_order, vector<double> &edge_tdm_ratio,
                        vector<double> &edge_arrival_time,
                        vector<double> &node_arrival_time,
                        vector<int> &node_critical_prev,
                        vector<double> &arrival_time_gap,
                        double &best_discrete_arrival_time,
                        const vector<vector<int>> &in_edges,
                        const vector<vector<int>> &out_edges, int dst_node,
                        int node_count, int edge_count);
  void compute_arrival_times(const vector<int> &topo_order,
                             const vector<vector<int>> &out_edges,
                             vector<double> &node_arrival,
                             vector<double> &edge_arrival,
                             bool reset_arrays = true);
  void log_worst_slack_path();
  void log_cut_worst_slack_path(int top_k = 1);
  void log_ratio_comparison(const std::string &filename);
  std::string format_fpga_die(int die_id, int die_per_fpga) const;
  TypedSockets collect_group_sockets_from_connections(
      const tdm_group &group, const std::vector<DieConnection> &conns);
  void populate_timing_edge_ports();

  void project_group_ratios_exact(int g, const std::vector<double> &edge_mu,
                                  double r_min, double r_max);
  void seed_base_ratio_by_mu(const tdm_group &g,
                             const std::vector<double> &edge_mu);
  double edge_delay_from_assign(const timing_edge &e) const;
  void refresh_all_tdm_delay_from_assign();
  void log_worst_cut_path_brief(const std::string &tag,
                                int max_edges_to_print      = 8,
                                bool use_current_edge_delay = false) const;
  std::vector<int> get_worst_cut_path_eids(
      bool use_current_edge_delay = false) const;
  bool try_reduce_critical_edge_ratio(int critical_eid, double &best_min_slack,
                                      const std::vector<uint8_t> &edge_in_path);
  bool try_reduce_critical_edge_ratio_to_target(
      int critical_eid, int target_ratio,
      const std::vector<uint8_t> &edge_in_path, double &out_min_slack);
  bool rescue_single_tdm_edge_worst_path(
      const std::vector<uint8_t> &edge_in_path, double &best_min_slack);
  bool rescue_worst_path_edge_ratio();
  void export_contest_route_result(const std::string &filename) const;
  void export_contest_tdm_result(const std::string &filename,
                                 const std::string &route_filename) const;
  int count_dir_bypass_channels_need(const tdm_group &group,
                                     bool is_forward) const;
  int gio_cap_channels_from_roots(int roots, int bypass_extra) const;

 private:
  int max_iters;
  double convergence_threshold;
  double convergence_num;
  double stable_num;
  double decay_factor;
  double initial_learning_rate;
  double decay_base;
  double decay_rate;

  void prepare_graph_and_topology(int &edge_count, int &node_count,
                                  int &dst_node, vector<vector<int>> &in_edges,
                                  vector<vector<int>> &out_edges,
                                  vector<int> &topo_order);
  double analyze_arrival_and_trace(const vector<int> &topo_order,
                                   const vector<vector<int>> &out_edges,
                                   int node_count, int dst_node,
                                   vector<double> &node_arrival_time);
  void init_dual_state_from_topology(
      const vector<int> &topo_order, const vector<vector<int>> &in_edges,
      const vector<vector<int>> &out_edges, int node_count, int edge_count,
      int dst_node, vector<double> &lambda, vector<double> &learning_rate,
      vector<int> &node_critical_prev, vector<double> &edge_arrival_time,
      vector<double> &arrival_time_gap, vector<double> &edge_tdm_ratio,
      vector<double> &node_pte, vector<double> &edge_pte,
      vector<double> &node_mu, vector<double> &edge_mu,
      vector<double> &lambda_velocity);
  void normalize_and_rescue_tdm_assignments();
  double compute_dst_arrival_from_current_delay(
      const vector<int> &topo_order, const vector<vector<int>> &out_edges,
      int node_count, int dst_node) const;
  RatioLib choose_lib_continuous_for_group(const tdm_group &g) const;

  void project_edges_with_capacity_(const std::vector<int> &E, double capacity,
                                    const std::vector<double> &edge_mu,
                                    double r_min, double r_max);
  inline RatioLib edge_lib(int u, int v);
};

namespace tdm {
void executeTDM(Routing &router, const fpga &fpgas, const params &para,
                DelayLibrary &delayLib);
}  // namespace tdm
#endif