text: /*********************************************************************
   *  The following code is part of the power modelling feature of VTR.
   *
   * For support:
   * http://code.google.com/p/vtr-verilog-to-routing/wiki/Power
   *
   * or email:
   * vtr.power.estimation@gmail.com
   *
   * If you are using power estimation for your researach please cite:
   *
   * Jeffrey Goeders and Steven Wilton.  VersaPower: Power Estimation
   * for Diverse FPGA Architectures.  In International Conference on
   * Field Programmable Technology, 2012.
   *
   ********************************************************************/
  
  /**
   * This is the top-level file for power estimation in VTR
   */
  
  /************************* INCLUDES *********************************/
  #include <cstdio>
  #include <cstdlib>
  #include <cstring>
  #include <csignal>
  #include <ctime>
  #include <cmath>
  #include <ctype.h>
  
  #include "vtr_util.h"
  #include "vtr_path.h"
  #include "vtr_log.h"
  #include "vtr_assert.h"
  #include "vtr_memory.h"
  
  #include "power.h"
  #include "power_components.h"
  #include "power_util.h"
  #include "power_lowlevel.h"
  #include "power_sizing.h"
  #include "power_callibrate.h"
  #include "power_cmos_tech.h"
  
  #include "physical_types.h"
  #include "globals.h"
  #include "rr_graph.h"
  #include "vpr_utils.h"
  
  /************************* DEFINES **********************************/
  #define CONVERT_NM_PER_M 1000000000
  #define CONVERT_UM_PER_M 1000000
  
  /************************* ENUMS ************************************/
  typedef enum {
      POWER_BREAKDOWN_ENTRY_TYPE_TITLE = 0,
      POWER_BREAKDOWN_ENTRY_TYPE_MODE,
      POWER_BREAKDOWN_ENTRY_TYPE_COMPONENT,
      POWER_BREAKDOWN_ENTRY_TYPE_PB,
      POWER_BREAKDOWN_ENTRY_TYPE_INTERC,
      POWER_BREAKDOWN_ENTRY_TYPE_BUFS_WIRES
  } e_power_breakdown_entry_type;
  
  /************************* File Scope **********************************/
  static t_rr_node_power* rr_node_power;
  
  /************************* Function Declarations ********************/
  /* Routing */
  static void power_usage_routing(t_power_usage* power_usage,
                                  const t_det_routing_arch* routing_arch,
                                  const std::vector<t_segment_inf>& segment_inf);
  
  /* Tiles */
  static void power_usage_blocks(t_power_usage* power_usage);
  static void power_usage_pb(t_power_usage* power_usage, t_pb* pb, t_pb_graph_node* pb_node, ClusterBlockId iblk);
  static void power_usage_primitive(t_power_usage* power_usage, t_pb* pb, t_pb_graph_node* pb_graph_node, ClusterBlockId iblk);
  static void power_reset_tile_usage();
  static void power_reset_pb_type(t_pb_type* pb_type);
  static void power_usage_local_buffers_and_wires(t_power_usage* power_usage,
                                                  t_pb* pb,
                                                  t_pb_graph_node* pb_node,
                                                  ClusterBlockId iblk);
  
  /* Clock */
  static void power_usage_clock(t_power_usage* power_usage,
                                t_clock_arch* clock_arch);
  static void power_usage_clock_single(t_power_usage* power_usage,
                                       t_clock_network* clock_inf);
  
  /* Init/Uninit */
  static void dealloc_mux_graph(t_mux_node* node);
  static void dealloc_mux_graph_rec(t_mux_node* node);
  
  /* Printing */
  static void power_print_breakdown_pb_rec(FILE* fp, t_pb_type* pb_type, int indent);
  static void power_print_summary(FILE* fp, const t_vpr_setup& vpr_setup);
  //static void power_print_stats(FILE * fp);
  static void power_print_breakdown_summary(FILE* fp);
  static void power_print_breakdown_entry(FILE* fp, int indent, e_power_breakdown_entry_type type, const char* name, float power, float total_power, float perc_dyn, const char* method);
  static void power_print_breakdown_component(FILE* fp, const char* name, e_power_component_type type, int indent_level);
  static void power_print_breakdown_pb(FILE* fp);
  
  static const char* power_estimation_method_name(e_power_estimation_method power_method);
  
  void power_usage_local_pin_toggle(t_power_usage* power_usage, t_pb* pb, t_pb_graph_pin* pin, ClusterBlockId iblk);
  void power_usage_local_pin_buffer_and_wire(t_power_usage* power_usage,
                                             t_pb* pb,
                                             t_pb_graph_pin* pin,
                                             ClusterBlockId iblk);
  void power_alloc_and_init_pb_pin(t_pb_graph_pin* pin);
  void power_uninit_pb_pin(t_pb_graph_pin* pin);
  void power_init_pb_pins_rec(t_pb_graph_node* pb_node);
  void power_uninit_pb_pins_rec(t_pb_graph_node* pb_node);
  void power_pb_pins_init();
  void power_pb_pins_uninit();
  void power_routing_init(const t_det_routing_arch* routing_arch);
  
  /************************* FUNCTION DEFINITIONS *********************/
  /**
   *  This function calculates the power of primitives (ff, lut, etc),
   *  by calling the appropriate primitive function.
   *  - power_usage: (Return value)
   *  - pb: The pysical block
   *  - pb_graph_node: The physical block graph node
   *  - calc_dynamic: Calculate dynamic power? Otherwise ignore
   *  - calc_static: Calculate static power? Otherwise ignore
   */
  static void power_usage_primitive(t_power_usage* power_usage, t_pb* pb, t_pb_graph_node* pb_graph_node, ClusterBlockId iblk) {
      t_power_usage sub_power_usage;
  
      power_zero_usage(power_usage);
      power_zero_usage(&sub_power_usage);
  
      auto& atom_ctx = g_vpr_ctx.atom();
      auto& device_ctx = g_vpr_ctx.device();
      auto& power_ctx = g_vpr_ctx.power();
  
      if (strcmp(pb_graph_node->pb_type->blif_model, MODEL_NAMES) == 0) {
          /* LUT */
  
          char* SRAM_values;
          float* input_probabilities;
          float* input_densities;
          int LUT_size;
          int pin_idx;
  
          VTR_ASSERT(pb_graph_node->num_input_ports == 1);
  
          LUT_size = pb_graph_node->num_input_pins[0];
  
          input_probabilities = (float*)vtr::calloc(LUT_size, sizeof(float));
          input_densities = (float*)vtr::calloc(LUT_size, sizeof(float));
  
          for (pin_idx = 0; pin_idx < LUT_size; pin_idx++) {
              t_pb_graph_pin* pin = &pb_graph_node->input_pins[0][pin_idx];
  
              input_probabilities[pin_idx] = pin_prob(pb, pin, iblk);
              input_densities[pin_idx] = pin_dens(pb, pin, iblk);
          }
  
          if (pb) {
              AtomBlockId blk_id = atom_ctx.lookup.pb_atom(pb);
              SRAM_values = alloc_SRAM_values_from_truth_table(LUT_size,
                                                               atom_ctx.nlist.block_truth_table(blk_id));
          } else {
              SRAM_values = alloc_SRAM_values_from_truth_table(LUT_size, AtomNetlist::TruthTable());
          }
          power_usage_lut(&sub_power_usage, LUT_size,
                          power_ctx.arch->LUT_transistor_size, SRAM_values,
                          input_probabilities, input_densities, power_ctx.solution_inf.T_crit);
          power_add_usage(power_usage, &sub_power_usage);
          free(SRAM_values);
          free(input_probabilities);
          free(input_densities);
      } else if (strcmp(pb_graph_node->pb_type->blif_model, MODEL_LATCH) == 0) {
          /* Flip-Flop */
  
          t_pb_graph_pin* D_pin = &pb_graph_node->input_pins[0][0];
          t_pb_graph_pin* Q_pin = &pb_graph_node->output_pins[0][0];
  
          float D_dens = 0.;
          float D_prob = 0.;
          float Q_prob = 0.;
          float Q_dens = 0.;
          float clk_dens = 0.;
          float clk_prob = 0.;
  
          D_dens = pin_dens(pb, D_pin, iblk);
          D_prob = pin_prob(pb, D_pin, iblk);
          Q_dens = pin_dens(pb, Q_pin, iblk);
          Q_prob = pin_prob(pb, Q_pin, iblk);
  
          clk_prob = device_ctx.clock_arch->clock_inf[0].prob;
          clk_dens = device_ctx.clock_arch->clock_inf[0].dens;
  
          power_usage_ff(&sub_power_usage, power_ctx.arch->FF_size, D_prob, D_dens,
                         Q_prob, Q_dens, clk_prob, clk_dens, power_ctx.solution_inf.T_crit);
          power_add_usage(power_usage, &sub_power_usage);
  
      } else {
          char msg[vtr::bufsize];
          sprintf(msg, "No dynamic power defined for BLIF model: %s",
                  pb_graph_node->pb_type->blif_model);
          power_log_msg(POWER_LOG_WARNING, msg);
  
          sprintf(msg, "No leakage power defined for BLIF model: %s",
                  pb_graph_node->pb_type->blif_model);
          power_log_msg(POWER_LOG_WARNING, msg);
      }
  }
  
  void power_usage_local_pin_toggle(t_power_usage* power_usage, t_pb* pb, t_pb_graph_pin* pin, ClusterBlockId iblk) {
      float scale_factor;
      auto& power_ctx = g_vpr_ctx.power();
  
      power_zero_usage(power_usage);
  
      if (pin->pin_power->scaled_by_pin) {
          scale_factor = pin_prob(pb, pin->pin_power->scaled_by_pin, iblk);
          if (pin->port->port_power->reverse_scaled) {
              scale_factor = 1 - scale_factor;
          }
      } else {
          scale_factor = 1.0;
      }
  
      /* Divide by 2 because density is switches/cycle, but a toggle is 2 switches */
      power_usage->dynamic += scale_factor
                              * pin->port->port_power->energy_per_toggle * pin_dens(pb, pin, iblk) / 2.0
                              / power_ctx.solution_inf.T_crit;
  }
  
  void power_usage_local_pin_buffer_and_wire(t_power_usage* power_usage,
                                             t_pb* pb,
                                             t_pb_graph_pin* pin,
                                             ClusterBlockId iblk) {
      t_power_usage sub_power_usage;
      float buffer_size = 0.;
      double C_wire;
      auto& power_ctx = g_vpr_ctx.power();
  
      power_zero_usage(power_usage);
  
      /* Wire switching */
      C_wire = pin->pin_power->C_wire;
      power_usage_wire(&sub_power_usage, C_wire, pin_dens(pb, pin, iblk),
                       power_ctx.solution_inf.T_crit);
      power_add_usage(power_usage, &sub_power_usage);
  
      /* Buffer power */
      buffer_size = pin->pin_power->buffer_size;
      if (buffer_size) {
          power_usage_buffer(&sub_power_usage, buffer_size, pin_prob(pb, pin, iblk),
                             pin_dens(pb, pin, iblk), false, power_ctx.solution_inf.T_crit);
          power_add_usage(power_usage, &sub_power_usage);
      }
  }
  
  static void power_usage_local_buffers_and_wires(t_power_usage* power_usage,
                                                  t_pb* pb,
                                                  t_pb_graph_node* pb_node,
                                                  ClusterBlockId iblk) {
      int port_idx;
      int pin_idx;
      t_power_usage pin_power;
  
      power_zero_usage(power_usage);
  
      /* Input pins */
      for (port_idx = 0; port_idx < pb_node->num_input_ports; port_idx++) {
          for (pin_idx = 0; pin_idx < pb_node->num_input_pins[port_idx];
               pin_idx++) {
              power_usage_local_pin_buffer_and_wire(&pin_power, pb,
                                                    &pb_node->input_pins[port_idx][pin_idx], iblk);
              power_add_usage(power_usage, &pin_power);
          }
      }
  
      /* Output pins */
      for (port_idx = 0; port_idx < pb_node->num_output_ports; port_idx++) {
          for (pin_idx = 0; pin_idx < pb_node->num_output_pins[port_idx];
               pin_idx++) {
              power_usage_local_pin_buffer_and_wire(&pin_power, pb,
                                                    &pb_node->output_pins[port_idx][pin_idx], iblk);
              power_add_usage(power_usage, &pin_power);
          }
      }
  
      /* Clock pins */
      for (port_idx = 0; port_idx < pb_node->num_clock_ports; port_idx++) {
          for (pin_idx = 0; pin_idx < pb_node->num_clock_pins[port_idx];
               pin_idx++) {
              power_usage_local_pin_buffer_and_wire(&pin_power, pb,
                                                    &pb_node->clock_pins[port_idx][pin_idx], iblk);
              power_add_usage(power_usage, &pin_power);
          }
      }
  }
  
  /** Calculates the power of a pb:
   * First checks if dynamic/static power is provided by user in arch file.  If not:
   * - Calculate power of all interconnect
   * - Call recursively for children
   * - If no children, must be a primitive.  Call primitive hander.
   */
  static void power_usage_pb(t_power_usage* power_usage, t_pb* pb, t_pb_graph_node* pb_node, ClusterBlockId iblk) {
      t_power_usage power_usage_bufs_wires;
      t_power_usage power_usage_local_muxes;
      t_power_usage power_usage_children;
      t_power_usage power_usage_pin_toggle;
      t_power_usage power_usage_sub;
  
      int pb_type_idx;
      int pb_idx;
      int interc_idx;
      int pb_mode;
      int port_idx;
      int pin_idx;
      float dens_avg;
      int num_pins;
  
      auto& power_ctx = g_vpr_ctx.power();
  
      power_zero_usage(power_usage);
  
      t_pb_type* pb_type = pb_node->pb_type;
      t_pb_type_power* pb_power = pb_node->pb_type->pb_type_power;
  
      bool estimate_buffers_and_wire = false;
      bool estimate_multiplexers = false;
      bool estimate_primitives = false;
      bool recursive_children;
  
      /* Get mode */
      if (pb) {
          pb_mode = pb->mode;
      } else {
          /* Default mode if not initialized (will only affect leakage power) */
          pb_mode = pb_type->pb_type_power->leakage_default_mode;
      }
  
      recursive_children = power_method_is_recursive(pb_node->pb_type->pb_type_power->estimation_method);
  
      power_zero_usage(&power_usage_sub);
  
      switch (pb_node->pb_type->pb_type_power->estimation_method) {
          case POWER_METHOD_IGNORE:
          case POWER_METHOD_SUM_OF_CHILDREN:
              break;
  
          case POWER_METHOD_ABSOLUTE:
              power_add_usage(power_usage, &pb_power->absolute_power_per_instance);
              power_component_add_usage(&pb_power->absolute_power_per_instance,
                                        POWER_COMPONENT_PB_OTHER);
              break;
  
          case POWER_METHOD_C_INTERNAL:
              power_zero_usage(&power_usage_sub);
  
              /* Just take the average density of inputs pins and use
               * that with user-defined block capacitance and leakage */
  
              /* Average the activity of all pins */
              num_pins = 0;
              dens_avg = 0.;
              for (port_idx = 0; port_idx < pb_node->num_input_ports; port_idx++) {
                  for (pin_idx = 0; pin_idx < pb_node->num_input_pins[port_idx];
                       pin_idx++) {
                      dens_avg += pin_dens(pb,
                                           &pb_node->input_pins[port_idx][pin_idx], iblk);
                      num_pins++;
                  }
              }
              if (num_pins != 0) {
                  dens_avg = dens_avg / num_pins;
              }
              power_usage_sub.dynamic += power_calc_node_switching(pb_power->C_internal,
                                                                   dens_avg,
                                                                   power_ctx.solution_inf.T_crit);
  
              /* Leakage is an absolute */
              power_usage_sub.leakage += pb_power->absolute_power_per_instance.leakage;
  
              /* Add to power of this PB */
              power_add_usage(power_usage, &power_usage_sub);
  
              // Add to component type
              power_component_add_usage(&power_usage_sub, POWER_COMPONENT_PB_OTHER);
              break;
  
          case POWER_METHOD_TOGGLE_PINS:
              power_zero_usage(&power_usage_pin_toggle);
  
              /* Add toggle power of each input pin */
              for (port_idx = 0; port_idx < pb_node->num_input_ports; port_idx++) {
                  for (pin_idx = 0; pin_idx < pb_node->num_input_pins[port_idx];
                       pin_idx++) {
                      t_power_usage pin_power;
                      power_usage_local_pin_toggle(&pin_power, pb,
                                                   &pb_node->input_pins[port_idx][pin_idx], iblk);
                      power_add_usage(&power_usage_pin_toggle, &pin_power);
                  }
              }
  
              /* Add toggle power of each output pin */
              for (port_idx = 0; port_idx < pb_node->num_output_ports; port_idx++) {
                  for (pin_idx = 0; pin_idx < pb_node->num_output_pins[port_idx];
                       pin_idx++) {
                      t_power_usage pin_power;
                      power_usage_local_pin_toggle(&pin_power, pb,
                                                   &pb_node->output_pins[port_idx][pin_idx], iblk);
                      power_add_usage(&power_usage_pin_toggle, &pin_power);
                  }
              }
  
              /* Add toggle power of each clock pin */
              for (port_idx = 0; port_idx < pb_node->num_clock_ports; port_idx++) {
                  for (pin_idx = 0; pin_idx < pb_node->num_clock_pins[port_idx];
                       pin_idx++) {
                      t_power_usage pin_power;
                      power_usage_local_pin_toggle(&pin_power, pb,
                                                   &pb_node->clock_pins[port_idx][pin_idx], iblk);
                      power_add_usage(&power_usage_pin_toggle, &pin_power);
                  }
              }
  
              /* Static is supplied as an absolute */
              power_usage_pin_toggle.leakage += pb_power->absolute_power_per_instance.leakage;
  
              // Add to this PB power
              power_add_usage(power_usage, &power_usage_pin_toggle);
  
              // Add to component type power
              power_component_add_usage(&power_usage_pin_toggle,
                                        POWER_COMPONENT_PB_OTHER);
              break;
          case POWER_METHOD_SPECIFY_SIZES:
              estimate_buffers_and_wire = true;
              estimate_multiplexers = true;
              estimate_primitives = true;
              break;
          case POWER_METHOD_AUTO_SIZES:
              estimate_buffers_and_wire = true;
              estimate_multiplexers = true;
              estimate_primitives = true;
              break;
          case POWER_METHOD_UNDEFINED:
          default:
              VTR_ASSERT(0);
              break;
      }
  
      if (pb_node->pb_type->class_type == LUT_CLASS) {
          /* LUTs will have a child node that is used to indicate pin
           * equivalence for routing purposes.
           * There is a crossbar to the child node; however,
           * this interconnect does not exist in FPGA hardware and should
           * be ignored for power calculations. */
          estimate_buffers_and_wire = false;
          estimate_multiplexers = false;
      }
  
      if (pb_node->is_primitive()) {
          /* This is a leaf node, which is a primitive (lut, ff, etc) */
          if (estimate_primitives) {
              VTR_ASSERT(pb_node->pb_type->blif_model);
              power_usage_primitive(&power_usage_sub, pb, pb_node, iblk);
  
              // Add to power of this PB
              power_add_usage(power_usage, &power_usage_sub);
  
              // Add to power of component type
              power_component_add_usage(&power_usage_sub,
                                        POWER_COMPONENT_PB_PRIMITIVES);
          }
  
      } else {
          /* This node had children.  The power of this node is the sum of:
           *  - Buffers/Wires in Interconnect from Parent to children
           *  - Multiplexers in Interconnect from Parent to children
           *  - Child nodes
           */
  
          if (estimate_buffers_and_wire) {
              /* Check pins of all interconnect */
              power_usage_local_buffers_and_wires(&power_usage_bufs_wires, pb,
                                                  pb_node, iblk);
              power_component_add_usage(&power_usage_bufs_wires,
                                        POWER_COMPONENT_PB_BUFS_WIRE);
              power_add_usage(&pb_node->pb_type->pb_type_power->power_usage_bufs_wires,
                              &power_usage_bufs_wires);
              power_add_usage(power_usage, &power_usage_bufs_wires);
          }
  
          /* Interconnect Structures (multiplexers) */
          if (estimate_multiplexers) {
              power_zero_usage(&power_usage_local_muxes);
              for (interc_idx = 0;
                   interc_idx < pb_type->modes[pb_mode].num_interconnect;
                   interc_idx++) {
                  power_usage_local_interc_mux(&power_usage_sub, pb,
                                               &pb_node->interconnect_pins[pb_mode][interc_idx], iblk);
                  power_add_usage(&power_usage_local_muxes, &power_usage_sub);
              }
              // Add to power of this PB
              power_add_usage(power_usage, &power_usage_local_muxes);
  
              // Add to component type power
              power_component_add_usage(&power_usage_local_muxes,
                                        POWER_COMPONENT_PB_INTERC_MUXES);
  
              // Add to power of this mode
              power_add_usage(&pb_node->pb_type->modes[pb_mode].mode_power->power_usage,
                              &power_usage_local_muxes);
          }
  
          /* Add power for children */
          if (recursive_children) {
              power_zero_usage(&power_usage_children);
              for (pb_type_idx = 0;
                   pb_type_idx
                   < pb_node->pb_type->modes[pb_mode].num_pb_type_children;
                   pb_type_idx++) {
                  for (pb_idx = 0;
                       pb_idx
                       < pb_node->pb_type->modes[pb_mode].pb_type_children[pb_type_idx].num_pb;
                       pb_idx++) {
                      t_pb* child_pb = nullptr;
                      t_pb_graph_node* child_pb_graph_node;
  
                      if (pb && pb->child_pbs[pb_type_idx][pb_idx].name) {
                          /* Child is initialized */
                          child_pb = &pb->child_pbs[pb_type_idx][pb_idx];
                      }
                      child_pb_graph_node = &pb_node->child_pb_graph_nodes[pb_mode][pb_type_idx][pb_idx];
  
                      power_usage_pb(&power_usage_sub, child_pb,
                                     child_pb_graph_node, iblk);
                      power_add_usage(&power_usage_children, &power_usage_sub);
                  }
              }
              // Add to power of this PB
              power_add_usage(power_usage, &power_usage_children);
  
              // Add to power of this mode
              power_add_usage(&pb_node->pb_type->modes[pb_mode].mode_power->power_usage,
                              &power_usage_children);
          }
      }
  
      power_add_usage(&pb_node->pb_type->pb_type_power->power_usage, power_usage);
  }
  
  /* Resets the power stats for all physical blocks */
  static void power_reset_pb_type(t_pb_type* pb_type) {
      int mode_idx;
      int child_idx;
      int interc_idx;
  
      power_zero_usage(&pb_type->pb_type_power->power_usage);
      power_zero_usage(&pb_type->pb_type_power->power_usage_bufs_wires);
  
      for (mode_idx = 0; mode_idx < pb_type->num_modes; mode_idx++) {
          power_zero_usage(&pb_type->modes[mode_idx].mode_power->power_usage);
  
          for (child_idx = 0;
               child_idx < pb_type->modes[mode_idx].num_pb_type_children;
               child_idx++) {
              power_reset_pb_type(&pb_type->modes[mode_idx].pb_type_children[child_idx]);
          }
          for (interc_idx = 0;
               interc_idx < pb_type->modes[mode_idx].num_interconnect;
               interc_idx++) {
              power_zero_usage(&pb_type->modes[mode_idx].interconnect[interc_idx].interconnect_power->power_usage);
          }
      }
  }
  
  /**
   * Resets the power usage for all tile types
   */
  static void power_reset_tile_usage() {
      auto& device_ctx = g_vpr_ctx.device();
  
      for (const auto& type : device_ctx.logical_block_types) {
          if (type.pb_type) {
              power_reset_pb_type(type.pb_type);
          }
      }
  }
  
  /*
   * Calcultes the power usage of all tiles in the FPGA
   */
  static void power_usage_blocks(t_power_usage* power_usage) {
      auto& device_ctx = g_vpr_ctx.device();
      auto& cluster_ctx = g_vpr_ctx.clustering();
      auto& place_ctx = g_vpr_ctx.placement();
  
      power_zero_usage(power_usage);
  
      power_reset_tile_usage();
  
      t_logical_block_type_ptr logical_block;
  
      /* Loop through all grid locations */
      for (size_t x = 0; x < device_ctx.grid.width(); x++) {
          for (size_t y = 0; y < device_ctx.grid.height(); y++) {
              auto physical_tile = device_ctx.grid[x][y].type;
  
              if ((device_ctx.grid[x][y].width_offset != 0)
                  || (device_ctx.grid[x][y].height_offset != 0)
                  || is_empty_type(physical_tile)) {
                  continue;
              }
  
              for (int z = 0; z < physical_tile->capacity; z++) {
                  t_pb* pb = nullptr;
                  t_power_usage pb_power;
  
                  ClusterBlockId iblk = place_ctx.grid_blocks[x][y].blocks[z];
  
                  if (iblk != EMPTY_BLOCK_ID && iblk != INVALID_BLOCK_ID) {
                      pb = cluster_ctx.clb_nlist.block_pb(iblk);
                      logical_block = cluster_ctx.clb_nlist.block_type(iblk);
                  } else {
                      logical_block = pick_best_logical_type(physical_tile);
                  }
  
                  /* Calculate power of this CLB */
                  power_usage_pb(&pb_power, pb, logical_block->pb_graph_head, iblk);
                  power_add_usage(power_usage, &pb_power);
              }
          }
      }
      return;
  }
  
  /**
   * Calculates the total power usage from the clock network
   */
  static void power_usage_clock(t_power_usage* power_usage,
                                t_clock_arch* clock_arch) {
      int clock_idx;
      auto& power_ctx = g_vpr_ctx.power();
  
      /* Initialization */
      power_usage->dynamic = 0.;
      power_usage->leakage = 0.;
  
      /* if no global clock, then return */
      if (clock_arch->num_global_clocks == 0) {
          return;
      }
  
      for (clock_idx = 0; clock_idx < clock_arch->num_global_clocks;
           clock_idx++) {
          t_power_usage clock_power;
  
          /* Assume the global clock is active even for combinational circuits */
          if (clock_arch->num_global_clocks == 1) {
              if (clock_arch->clock_inf[clock_idx].dens == 0) {
                  clock_arch->clock_inf[clock_idx].dens = 2;
                  clock_arch->clock_inf[clock_idx].prob = 0.5;
  
                  // This will need to change for multi-clock
                  clock_arch->clock_inf[clock_idx].period = power_ctx.solution_inf.T_crit;
              }
          }
          /* find the power dissipated by each clock network */
          power_usage_clock_single(&clock_power,
                                   &clock_arch->clock_inf[clock_idx]);
          power_add_usage(power_usage, &clock_power);
      }
  
      return;
  }
  
  /**
   * Calculates the power from a single spine-and-rib global clock
   */
  static void power_usage_clock_single(t_power_usage* power_usage,
                                       t_clock_network* single_clock) {
      /*
       *
       * The following code assumes a spine-and-rib clock network as shown below.
       * This is comprised of 3 main combonents:
       * 	1. A single wire from the io pad to the center of the chip
       * 	2. A H-structure which provides a 'spine' to all 4 quadrants
       * 	3. Ribs connect each spine with an entire column of blocks
       *
       * ___________________
       * |                 |
       * | |_|_|_2__|_|_|_ |
       * | | | |  | | | |  |
       * | |3| |  | | | |  |
       * |        |        |
       * | | | |  | | | |  |
       * | |_|_|__|_|_|_|_ |
       * | | | |  | | | |  |
       * |_______1|________|
       * It is assumed that there are a single-inverter buffers placed along each wire,
       * with spacing equal to the FPGA block size (1 buffer/block) */
      t_power_usage clock_buffer_power;
      int length;
      t_power_usage buffer_power;
      t_power_usage wire_power;
      float C_segment;
      float buffer_size;
      auto& power_ctx = g_vpr_ctx.power();
      auto& device_ctx = g_vpr_ctx.device();
  
      power_usage->dynamic = 0.;
      power_usage->leakage = 0.;
  
      /* Check if this clock is active - this is used for calculating leakage */
      if (single_clock->dens) {
      } else {
          VTR_ASSERT(0);
      }
  
      C_segment = power_ctx.commonly_used->tile_length * single_clock->C_wire;
      if (single_clock->autosize_buffer) {
          buffer_size = 1 + C_segment / power_ctx.commonly_used->INV_1X_C_in;
      } else {
          buffer_size = single_clock->buffer_size;
      }
  
      /* Calculate the capacitance and leakage power for the clock buffer */
      power_usage_inverter(&clock_buffer_power, single_clock->dens,
                           single_clock->prob, buffer_size, single_clock->period);
  
      length = 0;
  
      /* 1. IO to chip center */
      length += device_ctx.grid.height() / 2;
  
      /* 2. H-Tree to 4 quadrants */
      length += device_ctx.grid.height() / 2; //Vertical component of H
      length += 2 * device_ctx.grid.width();  //Horizontal horizontal component of H (two rows)
  
      /* 3. Ribs - to */
      length += device_ctx.grid.width() / 2 * device_ctx.grid.height(); //Each rib spand 1/2 of width, two rows of ribs
  
      buffer_power.dynamic = length * clock_buffer_power.dynamic;
      buffer_power.leakage = length * clock_buffer_power.leakage;
  
      power_add_usage(power_usage, &buffer_power);
      power_component_add_usage(&buffer_power, POWER_COMPONENT_CLOCK_BUFFER);
  
      power_usage_wire(&wire_power, length * C_segment, single_clock->dens,
                       single_clock->period);
      power_add_usage(power_usage, &wire_power);
      power_component_add_usage(&wire_power, POWER_COMPONENT_CLOCK_WIRE);
  
      return;
  }
  
  /* Frees a multiplexer graph */
  static void dealloc_mux_graph(t_mux_node* node) {
      dealloc_mux_graph_rec(node);
      free(node);
  }
  
  static void dealloc_mux_graph_rec(t_mux_node* node) {
      int child_idx;
  
      /* Dealloc Children */
      if (node->level != 0) {
          for (child_idx = 0; child_idx < node->num_inputs; child_idx++) {
              dealloc_mux_graph_rec(&node->children[child_idx]);
          }
          free(node->children);
      }
  }
  
  /**
   * Calculates the power of the entire routing fabric (not local routing
   */
  static void power_usage_routing(t_power_usage* power_usage,
                                  const t_det_routing_arch* routing_arch,
                                  const std::vector<t_segment_inf>& segment_inf) {
      auto& power_ctx = g_vpr_ctx.power();
      auto& cluster_ctx = g_vpr_ctx.clustering();
      auto& device_ctx = g_vpr_ctx.device();
      auto& route_ctx = g_vpr_ctx.routing();
  
      power_zero_usage(power_usage);
  
      /* Reset routing statistics */
      power_ctx.commonly_used->num_sb_buffers = 0;
      power_ctx.commonly_used->total_sb_buffer_size = 0.;
      power_ctx.commonly_used->num_cb_buffers = 0;
      power_ctx.commonly_used->total_cb_buffer_size = 0.;
  
      /* Reset rr graph net indices */
      for (size_t rr_node_idx = 0; rr_node_idx < device_ctx.rr_nodes.size(); rr_node_idx++) {
          rr_node_power[rr_node_idx].net_num = ClusterNetId::INVALID();
          rr_node_power[rr_node_idx].num_inputs = 0;
          rr_node_power[rr_node_idx].selected_input = 0;
      }
  
      /* Populate net indices into rr graph */
      for (auto net_id : cluster_ctx.clb_nlist.nets()) {
          t_trace* trace;
  
          for (trace = route_ctx.trace[net_id].head; trace != nullptr; trace = trace->next) {
              rr_node_power[trace->index].visited = false;
              rr_node_power[trace->index].net_num = net_id;
          }
      }
  
      /* Populate net indices into rr graph */
      for (auto net_id : cluster_ctx.clb_nlist.nets()) {
          t_trace* trace;
  
          for (trace = route_ctx.trace[net_id].head; trace != nullptr; trace = trace->next) {
              auto node = &device_ctx.rr_nodes[trace->index];
              t_rr_node_power* node_power = &rr_node_power[trace->index];
  
              if (node_power->visited) {
                  continue;
              }
  
              for (t_edge_size edge_idx = 0; edge_idx < node->num_edges(); edge_idx++) {
                  if (node->edge_sink_node(edge_idx) != OPEN) {
                      auto next_node = &device_ctx.rr_nodes[node->edge_sink_node(edge_idx)];
                      t_rr_node_power* next_node_power = &rr_node_power[node->edge_sink_node(edge_idx)];
  
                      switch (next_node->type()) {
                          case CHANX:
                          case CHANY:
                          case IPIN:
                              if (next_node_power->net_num == node_power->net_num) {
                                  next_node_power->selected_input = next_node_power->num_inputs;
                              }
                              next_node_power->in_dens[next_node_power->num_inputs] = clb_net_density(node_power->net_num);
                              next_node_power->in_prob[next_node_power->num_inputs] = clb_net_prob(node_power->net_num);
                              next_node_power->num_inputs++;
                              if (next_node_power->num_inputs > next_node->fan_in()) {
                                  VTR_LOG("%d %d\n", next_node_power->num_inputs,
                                          next_node->fan_in());
                                  fflush(nullptr);
                                  VTR_ASSERT(0);
                              }
                              break;
                          default:
                              /* Do nothing */
                              break;
                      }
                  }
              }
              node_power->visited = true;
          }
      }
  
      /* Calculate power of all routing entities */
      for (size_t rr_node_idx = 0; rr_node_idx < device_ctx.rr_nodes.size(); rr_node_idx++) {
          t_power_usage sub_power_usage;
          auto node = &device_ctx.rr_nodes[rr_node_idx];
          t_rr_node_power* node_power = &rr_node_power[rr_node_idx];
          float C_wire;
          float buffer_size;
          int connectionbox_fanout;
          int switchbox_fanout;
          //float C_per_seg_split;
          int wire_length;
  
          switch (node->type()) {
              case SOURCE:
              case SINK:
              case OPIN:
                  /* No power usage for these types */
                  break;
              case IPIN:
                  /* This is part of the connectionbox.  The connection box is comprised of:
                   *  - Driver (accounted for at end of CHANX/Y - see below)
                   *  - Multiplexor */
  
                  if (node->fan_in()) {
                      VTR_ASSERT(node_power->in_dens);
                      VTR_ASSERT(node_power->in_prob);
  
                      /* Multiplexor */
                      power_usage_mux_multilevel(&sub_power_usage,
                                                 power_get_mux_arch(node->fan_in(),
                                                                    power_ctx.arch->mux_transistor_size),
                                                 node_power->in_prob, node_power->in_dens,
                                                 node_power->selected_input, true,
                                                 power_ctx.solution_inf.T_crit);
                      power_add_usage(power_usage, &sub_power_usage);
                      power_component_add_usage(&sub_power_usage,
                                                POWER_COMPONENT_ROUTE_CB);
                  }
                  break;
              case CHANX:
              case CHANY:
                  /* This is a wire driven by a switchbox, which includes:
                   * 	- The Multiplexor at the beginning of the wire
                   * 	- A buffer, after the mux to drive the wire
                   * 	- The wire itself
                   * 	- A buffer at the end of the wire, going to switchbox/connectionbox */
                  VTR_ASSERT(node_power->in_dens);
                  VTR_ASSERT(node_power->in_prob);
  
                  wire_length = 0;
                  if (node->type() == CHANX) {
                      wire_length = node->xhigh() - node->xlow() + 1;
                  } else if (node->type() == CHANY) {
                      wire_length = node->yhigh() - node->ylow() + 1;
                  }
                  C_wire = wire_length
                           * segment_inf[device_ctx.rr_indexed_data[node->cost_index()].seg_index].Cmetal;
                  //(double)power_ctx.commonly_used->tile_length);
                  VTR_ASSERT(node_power->selected_input < node->fan_in());
  
                  /* Multiplexor */
                  power_usage_mux_multilevel(&sub_power_usage,
                                             power_get_mux_arch(node->fan_in(),
                                                                power_ctx.arch->mux_transistor_size),
                                             node_power->in_prob, node_power->in_dens,
                                             node_power->selected_input, true, power_ctx.solution_inf.T_crit);
                  power_add_usage(power_usage, &sub_power_usage);
                  power_component_add_usage(&sub_power_usage,
                                            POWER_COMPONENT_ROUTE_SB);
  
                  /* Buffer Size */
                  switch (device_ctx.rr_switch_inf[node_power->driver_switch_type].power_buffer_type) {
                      case POWER_BUFFER_TYPE_AUTO:
                          /*
                           * C_per_seg_split = ((float) node->num_edges
                           * power_ctx.commonly_used->INV_1X_C_in + C_wire);
                           * // / (float) power_ctx.arch->seg_buffer_split;
                           * buffer_size = power_buffer_size_from_logical_effort(
                           * C_per_seg_split);
                           * buffer_size = std::max(buffer_size, 1.0F);
                           */
                          buffer_size = power_calc_buffer_size_from_Cout(device_ctx.rr_switch_inf[node_power->driver_switch_type].Cout);
                          break;
                      case POWER_BUFFER_TYPE_ABSOLUTE_SIZE:
                          buffer_size = device_ctx.rr_switch_inf[node_power->driver_switch_type].power_buffer_size;
                          buffer_size = std::max(buffer_size, 1.0F);
                          break;
                      case POWER_BUFFER_TYPE_NONE:
                          buffer_size = 0.;
                          break;
                      default:
                          buffer_size = 0.;
                          VTR_ASSERT(0);
                          break;
                  }
  
                  power_ctx.commonly_used->num_sb_buffers++;
                  power_ctx.commonly_used->total_sb_buffer_size += buffer_size;
  
                  /*
                   * power_ctx.commonly_used->num_sb_buffers +=
                   * power_ctx.arch->seg_buffer_split;
                   * power_ctx.commonly_used->total_sb_buffer_size += buffer_size
                   * power_ctx.arch->seg_buffer_split;
                   */
  
                  /* Buffer */
                  power_usage_buffer(&sub_power_usage, buffer_size,
                                     node_power->in_prob[node_power->selected_input],
                                     node_power->in_dens[node_power->selected_input], true,
                                     power_ctx.solution_inf.T_crit);
                  power_add_usage(power_usage, &sub_power_usage);
                  power_component_add_usage(&sub_power_usage,
                                            POWER_COMPONENT_ROUTE_SB);
  
                  /* Wire Capacitance */
                  power_usage_wire(&sub_power_usage, C_wire,
                                   clb_net_density(node_power->net_num), power_ctx.solution_inf.T_crit);
                  power_add_usage(power_usage, &sub_power_usage);
                  power_component_add_usage(&sub_power_usage,
                                            POWER_COMPONENT_ROUTE_GLB_WIRE);
  
                  /* Determine types of switches that this wire drives */
                  connectionbox_fanout = 0;
                  switchbox_fanout = 0;
                  for (t_edge_size iedge = 0; iedge < node->num_edges(); iedge++) {
                      if (node->edge_switch(iedge) == routing_arch->wire_to_rr_ipin_switch) {
                          connectionbox_fanout++;
                      } else if (node->edge_switch(iedge) == routing_arch->delayless_switch) {
                          /* Do nothing */
                      } else {
                          switchbox_fanout++;
                      }
                  }
  
                  /* Buffer to next Switchbox */
                  if (switchbox_fanout) {
                      buffer_size = power_buffer_size_from_logical_effort(switchbox_fanout * power_ctx.commonly_used->NMOS_1X_C_d);
                      power_usage_buffer(&sub_power_usage, buffer_size,
                                         1 - node_power->in_prob[node_power->selected_input],
                                         node_power->in_dens[node_power->selected_input], false,
                                         power_ctx.solution_inf.T_crit);
                      power_add_usage(power_usage, &sub_power_usage);
                      power_component_add_usage(&sub_power_usage,
                                                POWER_COMPONENT_ROUTE_SB);
                  }
  
                  /* Driver for ConnectionBox */
                  if (connectionbox_fanout) {
                      buffer_size = power_buffer_size_from_logical_effort(connectionbox_fanout * power_ctx.commonly_used->NMOS_1X_C_d);
  
                      power_usage_buffer(&sub_power_usage, buffer_size,
                                         1 - node_power->in_prob[node_power->selected_input],
                                         node_power->in_dens[node_power->selected_input],
                                         false, power_ctx.solution_inf.T_crit);
                      power_add_usage(power_usage, &sub_power_usage);
                      power_component_add_usage(&sub_power_usage,
                                                POWER_COMPONENT_ROUTE_CB);
  
                      power_ctx.commonly_used->num_cb_buffers++;
                      power_ctx.commonly_used->total_cb_buffer_size += buffer_size;
                  }
                  break;
              default:
                  power_log_msg(POWER_LOG_WARNING,
                                "The global routing-resource graph contains an unknown node type.");
                  break;
          }
      }
  }
  
  void power_alloc_and_init_pb_pin(t_pb_graph_pin* pin) {
      int port_idx;
      t_port* port_to_find;
      t_pb_graph_node* node = pin->parent_node;
      bool found;
  
      pin->pin_power = (t_pb_graph_pin_power*)vtr::malloc(sizeof(t_pb_graph_pin_power));
      pin->pin_power->C_wire = 0.;
      pin->pin_power->buffer_size = 0.;
      pin->pin_power->scaled_by_pin = nullptr;
  
      if (pin->port->port_power->scaled_by_port) {
          port_to_find = pin->port->port_power->scaled_by_port;
  
          /*pin->pin_power->scaled_by_pin =
           * get_pb_graph_node_pin_from_model_port_pin(
           * port_to_find->model_port,
           * pin->port->port_power->scaled_by_port_pin_idx,
           * pin->parent_node);*/
          /* Search input, output, clock ports */
  
          found = false;
          for (port_idx = 0; port_idx < node->num_input_ports; port_idx++) {
              if (node->input_pins[port_idx][0].port == port_to_find) {
                  pin->pin_power->scaled_by_pin = &node->input_pins[port_idx][pin->port->port_power->scaled_by_port_pin_idx];
                  found = true;
                  break;
              }
          }
          if (!found) {
              for (port_idx = 0; port_idx < node->num_output_ports; port_idx++) {
                  if (node->output_pins[port_idx][0].port == port_to_find) {
                      pin->pin_power->scaled_by_pin = &node->output_pins[port_idx][pin->port->port_power->scaled_by_port_pin_idx];
                      found = true;
                      break;
                  }
              }
          }
          if (!found) {
              for (port_idx = 0; port_idx < node->num_clock_ports; port_idx++) {
                  if (node->clock_pins[port_idx][0].port == port_to_find) {
                      pin->pin_power->scaled_by_pin = &node->clock_pins[port_idx][pin->port->port_power->scaled_by_port_pin_idx];
                      found = true;
                      break;
                  }
              }
          }
          VTR_ASSERT(found);
  
          VTR_ASSERT(pin->pin_power->scaled_by_pin);
      }
  }
  
  void power_uninit_pb_pin(t_pb_graph_pin* pin) {
      vtr::free(pin->pin_power);
      pin->pin_power = nullptr;
  }
  
  void power_init_pb_pins_rec(t_pb_graph_node* pb_node) {
      int mode;
      int type;
      int pb;
      int port_idx;
      int pin_idx;
  
      for (port_idx = 0; port_idx < pb_node->num_input_ports; port_idx++) {
          for (pin_idx = 0; pin_idx < pb_node->num_input_pins[port_idx]; pin_idx++) {
              power_alloc_and_init_pb_pin(&pb_node->input_pins[port_idx][pin_idx]);
          }
      }
  
      for (port_idx = 0; port_idx < pb_node->num_output_ports; port_idx++) {
          for (pin_idx = 0; pin_idx < pb_node->num_output_pins[port_idx]; pin_idx++) {
              power_alloc_and_init_pb_pin(&pb_node->output_pins[port_idx][pin_idx]);
          }
      }
  
      for (port_idx = 0; port_idx < pb_node->num_clock_ports; port_idx++) {
          for (pin_idx = 0; pin_idx < pb_node->num_clock_pins[port_idx]; pin_idx++) {
              power_alloc_and_init_pb_pin(&pb_node->clock_pins[port_idx][pin_idx]);
          }
      }
  
      for (mode = 0; mode < pb_node->pb_type->num_modes; mode++) {
          for (type = 0; type < pb_node->pb_type->modes[mode].num_pb_type_children; type++) {
              for (pb = 0; pb < pb_node->pb_type->modes[mode].pb_type_children[type].num_pb; pb++) {
                  power_init_pb_pins_rec(&pb_node->child_pb_graph_nodes[mode][type][pb]);
              }
          }
      }
  }
  
  void power_uninit_pb_pins_rec(t_pb_graph_node* pb_node) {
      int mode;
      int type;
      int pb;
      int port_idx;
      int pin_idx;
  
      for (port_idx = 0; port_idx < pb_node->num_input_ports; port_idx++) {
          for (pin_idx = 0; pin_idx < pb_node->num_input_pins[port_idx]; pin_idx++) {
              power_uninit_pb_pin(&pb_node->input_pins[port_idx][pin_idx]);
          }
      }
  
      for (port_idx = 0; port_idx < pb_node->num_output_ports; port_idx++) {
          for (pin_idx = 0; pin_idx < pb_node->num_output_pins[port_idx]; pin_idx++) {
              power_uninit_pb_pin(&pb_node->output_pins[port_idx][pin_idx]);
          }
      }
  
      for (port_idx = 0; port_idx < pb_node->num_clock_ports; port_idx++) {
          for (pin_idx = 0; pin_idx < pb_node->num_clock_pins[port_idx]; pin_idx++) {
              power_uninit_pb_pin(&pb_node->clock_pins[port_idx][pin_idx]);
          }
      }
  
      for (mode = 0; mode < pb_node->pb_type->num_modes; mode++) {
          for (type = 0; type < pb_node->pb_type->modes[mode].num_pb_type_children; type++) {
              for (pb = 0; pb < pb_node->pb_type->modes[mode].pb_type_children[type].num_pb; pb++) {
                  power_uninit_pb_pins_rec(&pb_node->child_pb_graph_nodes[mode][type][pb]);
              }
          }
      }
  }
  
  void power_pb_pins_init() {
      auto& device_ctx = g_vpr_ctx.device();
  
      for (const auto& type : device_ctx.logical_block_types) {
          if (type.pb_graph_head) {
              power_init_pb_pins_rec(type.pb_graph_head);
          }
      }
  }
  
  void power_pb_pins_uninit() {
      auto& device_ctx = g_vpr_ctx.device();
  
      for (const auto& type : device_ctx.logical_block_types) {
          if (type.pb_graph_head) {
              power_uninit_pb_pins_rec(type.pb_graph_head);
          }
      }
  }
  
  void power_routing_init(const t_det_routing_arch* routing_arch) {
      int max_fanin;
      int max_IPIN_fanin;
      int max_seg_to_IPIN_fanout;
      int max_seg_to_seg_fanout;
      auto& power_ctx = g_vpr_ctx.mutable_power();
      auto& device_ctx = g_vpr_ctx.device();
      auto& cluster_ctx = g_vpr_ctx.clustering();
      auto& atom_ctx = g_vpr_ctx.atom();
  
      /* Copy probability/density values to new netlist */
      if (power_ctx.clb_net_power.size() == 0) {
          power_ctx.clb_net_power.resize(cluster_ctx.clb_nlist.nets().size());
      }
      for (auto net_id : cluster_ctx.clb_nlist.nets()) {
          power_ctx.clb_net_power[net_id].probability = power_ctx.atom_net_power[atom_ctx.lookup.atom_net(net_id)].probability;
          power_ctx.clb_net_power[net_id].density = power_ctx.atom_net_power[atom_ctx.lookup.atom_net(net_id)].density;
      }
  
      /* Initialize RR Graph Structures */
      rr_node_power = (t_rr_node_power*)vtr::calloc(device_ctx.rr_nodes.size(),
                                                    sizeof(t_rr_node_power));
      for (size_t rr_node_idx = 0; rr_node_idx < device_ctx.rr_nodes.size(); rr_node_idx++) {
          rr_node_power[rr_node_idx].driver_switch_type = OPEN;
      }
  
      /* Initialize Mux Architectures */
      max_fanin = 0;
      max_IPIN_fanin = 0;
      max_seg_to_seg_fanout = 0;
      max_seg_to_IPIN_fanout = 0;
      for (size_t rr_node_idx = 0; rr_node_idx < device_ctx.rr_nodes.size(); rr_node_idx++) {
          int fanout_to_IPIN = 0;
          int fanout_to_seg = 0;
          auto node = &device_ctx.rr_nodes[rr_node_idx];
          t_rr_node_power* node_power = &rr_node_power[rr_node_idx];
  
          switch (node->type()) {
              case IPIN:
                  max_IPIN_fanin = std::max(max_IPIN_fanin,
                                            static_cast<int>(node->fan_in()));
                  max_fanin = std::max(max_fanin, static_cast<int>(node->fan_in()));
  
                  node_power->in_dens = (float*)vtr::calloc(node->fan_in(),
                                                            sizeof(float));
                  node_power->in_prob = (float*)vtr::calloc(node->fan_in(),
                                                            sizeof(float));
                  break;
              case CHANX:
              case CHANY:
                  for (t_edge_size iedge = 0; iedge < node->num_edges(); iedge++) {
                      if (node->edge_switch(iedge) == routing_arch->wire_to_rr_ipin_switch) {
                          fanout_to_IPIN++;
                      } else if (node->edge_switch(iedge) != routing_arch->delayless_switch) {
                          fanout_to_seg++;
                      }
                  }
                  max_seg_to_IPIN_fanout = std::max(max_seg_to_IPIN_fanout,
                                                    fanout_to_IPIN);
                  max_seg_to_seg_fanout = std::max(max_seg_to_seg_fanout, fanout_to_seg);
                  max_fanin = std::max(max_fanin, static_cast<int>(node->fan_in()));
  
                  node_power->in_dens = (float*)vtr::calloc(node->fan_in(),
                                                            sizeof(float));
                  node_power->in_prob = (float*)vtr::calloc(node->fan_in(),
                                                            sizeof(float));
                  break;
              default:
                  /* Do nothing */
                  break;
          }
      }
      power_ctx.commonly_used->max_routing_mux_size = max_fanin;
      power_ctx.commonly_used->max_IPIN_fanin = max_IPIN_fanin;
      power_ctx.commonly_used->max_seg_to_seg_fanout = max_seg_to_seg_fanout;
      power_ctx.commonly_used->max_seg_to_IPIN_fanout = max_seg_to_IPIN_fanout;
  
  #ifdef PRINT_SPICE_COMPARISON
      power_ctx.commonly_used->max_routing_mux_size = std::max(power_ctx.commonly_used->max_routing_mux_size, 26);
  #endif
  
      /* Populate driver switch type */
      for (size_t rr_node_idx = 0; rr_node_idx < device_ctx.rr_nodes.size(); rr_node_idx++) {
          auto node = &device_ctx.rr_nodes[rr_node_idx];
  
          for (t_edge_size edge_idx = 0; edge_idx < node->num_edges(); edge_idx++) {
              if (node->edge_sink_node(edge_idx) != OPEN) {
                  if (rr_node_power[node->edge_sink_node(edge_idx)].driver_switch_type == OPEN) {
                      rr_node_power[node->edge_sink_node(edge_idx)].driver_switch_type = node->edge_switch(edge_idx);
                  } else {
                      VTR_ASSERT(rr_node_power[node->edge_sink_node(edge_idx)].driver_switch_type == node->edge_switch(edge_idx));
                  }
              }
          }
      }
  
      /* Find Max Fanout of Routing Buffer	 */
      t_edge_size max_seg_fanout = 0;
      for (size_t rr_node_idx = 0; rr_node_idx < device_ctx.rr_nodes.size(); rr_node_idx++) {
          auto node = &device_ctx.rr_nodes[rr_node_idx];
  
          switch (node->type()) {
              case CHANX:
              case CHANY:
                  if (node->num_edges() > max_seg_fanout) {
                      max_seg_fanout = node->num_edges();
                  }
                  break;
              default:
                  /* Do nothing */
                  break;
          }
      }
      power_ctx.commonly_used->max_seg_fanout = max_seg_fanout;
  }
  
  /**
   * Initialization for all power-related functions
   */
  bool power_init(const char* power_out_filepath,
                  const char* cmos_tech_behavior_filepath,
                  const t_arch* arch,
                  const t_det_routing_arch* routing_arch) {
      auto& power_ctx = g_vpr_ctx.mutable_power();
      bool error = false;
  
      /* Set global power architecture & options */
      power_ctx.arch = arch->power;
      power_ctx.commonly_used = new t_power_commonly_used;
      power_ctx.tech = (t_power_tech*)vtr::malloc(sizeof(t_power_tech));
      power_ctx.output = (t_power_output*)vtr::malloc(sizeof(t_power_output));
  
      /* Set up Logs */
      power_ctx.output->num_logs = POWER_LOG_NUM_TYPES;
      power_ctx.output->logs = (t_log*)vtr::calloc(power_ctx.output->num_logs,
                                                   sizeof(t_log));
      power_ctx.output->logs[POWER_LOG_ERROR].name = vtr::strdup("Errors");
      power_ctx.output->logs[POWER_LOG_WARNING].name = vtr::strdup("Warnings");
  
      /* Initialize output file */
      if (!error) {
          power_ctx.output->out = nullptr;
          power_ctx.output->out = vtr::fopen(power_out_filepath, "w");
          if (!power_ctx.output->out) {
              error = true;
          }
      }
  
      /* Load technology properties */
      power_tech_init(cmos_tech_behavior_filepath);
  
      /* Low-Level Initialization */
      power_lowlevel_init();
  
      /* Initialize sub-modules */
      power_components_init();
  
      /* Perform callibration */
      power_callibrate();
  
      /* Initialize routing information */
      power_routing_init(routing_arch);
  
      // Allocates power structures for each pb pin
      power_pb_pins_init();
  
      /* Size all components */
      power_sizing_init(arch);
  
      //power_print_spice_comparison();
      //	power_print_callibration();
  
      return error;
  }
  
  /**
   * Uninitialize power module
   */
  bool power_uninit() {
      int mux_size;
      int log_idx;
      int msg_idx;
      auto& device_ctx = g_vpr_ctx.device();
      auto& power_ctx = g_vpr_ctx.power();
      bool error = false;
  
      for (size_t rr_node_idx = 0; rr_node_idx < device_ctx.rr_nodes.size(); rr_node_idx++) {
          auto node = &device_ctx.rr_nodes[rr_node_idx];
          t_rr_node_power* node_power = &rr_node_power[rr_node_idx];
  
          switch (node->type()) {
              case CHANX:
              case CHANY:
              case IPIN:
                  if (node->fan_in()) {
                      free(node_power->in_dens);
                      free(node_power->in_prob);
                  }
                  break;
              default:
                  /* Do nothing */
                  break;
          }
      }
      free(rr_node_power);
  
      /* Free mux architectures */
      for (std::map<float, t_power_mux_info*>::iterator it = power_ctx.commonly_used->mux_info.begin();
           it != power_ctx.commonly_used->mux_info.end(); it++) {
          t_power_mux_info* mux_info = it->second;
          for (mux_size = 1; mux_size <= mux_info->mux_arch_max_size; mux_size++) {
              dealloc_mux_graph(mux_info->mux_arch[mux_size].mux_graph_head);
          }
          free(mux_info->mux_arch);
          delete mux_info;
      }
      /* Free components */
      for (int i = 0; i < POWER_CALLIB_COMPONENT_MAX; ++i) {
          delete power_ctx.commonly_used->component_callibration[i];
      }
      free(power_ctx.commonly_used->component_callibration);
  
      delete power_ctx.commonly_used;
  
      /* Free logs */
      if (power_ctx.output->out) {
          fclose(power_ctx.output->out);
      }
      for (log_idx = 0; log_idx < power_ctx.output->num_logs; log_idx++) {
          for (msg_idx = 0; msg_idx < power_ctx.output->logs[log_idx].num_messages;
               msg_idx++) {
              free(power_ctx.output->logs[log_idx].messages[msg_idx]);
          }
          free(power_ctx.output->logs[log_idx].messages);
          free(power_ctx.output->logs[log_idx].name);
      }
      free(power_ctx.output->logs);
      free(power_ctx.output);
  
      power_pb_pins_uninit();
  
      return error;
  }
  
  #if 0
  /**
   * Prints the power of all pb structures, in an xml format that matches the archicture file
   */
  static void power_print_pb_usage_recursive(FILE * fp, t_pb_type * type,
  		int indent_level, float parent_power, float total_power) {
  	int mode_idx;
  	int mode_indent;
  	int child_idx;
  	int interc_idx;
  	float pb_type_power;
  
  	pb_type_power = type->pb_type_power->power_usage.dynamic
  	+ type->pb_type_power->power_usage.leakage;
  
  	print_tabs(fp, indent_level);
  	fprintf(fp,
  			"<pb_type name=\"%s\" P=\"%.4g\" P_parent=\"%.3g\" P_total=\"%.3g\" P_dyn=\"%.3g\" >\n",
  			type->name, pb_type_power, pb_type_power / parent_power * 100,
  			pb_type_power / total_power * 100,
  			type->pb_type_power->power_usage.dynamic / pb_type_power);
  
  	mode_indent = 0;
  	if (type->num_modes > 1) {
  		mode_indent = 1;
  	}
  
  	for (mode_idx = 0; mode_idx < type->num_modes; mode_idx++) {
  		float mode_power;
  		mode_power = type->modes[mode_idx].mode_power->power_usage.dynamic
  		+ type->modes[mode_idx].mode_power->power_usage.leakage;
  
  		if (type->num_modes > 1) {
  			print_tabs(fp, indent_level + mode_indent);
  			fprintf(fp,
  					"<mode name=\"%s\" P=\"%.4g\" P_parent=\"%.3g\" P_total=\"%.3g\" P_dyn=\"%.3g\">\n",
  					type->modes[mode_idx].name, mode_power,
  					mode_power / pb_type_power * 100,
  					mode_power / total_power * 100,
  					type->modes[mode_idx].mode_power->power_usage.dynamic
  					/ mode_power);
  		}
  
  		if (type->modes[mode_idx].num_interconnect) {
  			/* Sum the interconnect power */
  			t_power_usage interc_power_usage;
  			float interc_total_power;
  
  			power_zero_usage(&interc_power_usage);
  			for (interc_idx = 0;
  					interc_idx < type->modes[mode_idx].num_interconnect;
  					interc_idx++) {
  				power_add_usage(&interc_power_usage,
  						&type->modes[mode_idx].interconnect[interc_idx].interconnect_power->power_usage);
  			}
  			interc_total_power = interc_power_usage.dynamic
  			+ interc_power_usage.leakage;
  
  			/* All interconnect */
  			print_tabs(fp, indent_level + mode_indent + 1);
  			fprintf(fp,
  					"<interconnect P=\"%.4g\" P_parent=\"%.3g\" P_total=\"%.3g\" P_dyn=\"%.3g\">\n",
  					interc_total_power, interc_total_power / mode_power * 100,
  					interc_total_power / total_power * 100,
  					interc_power_usage.dynamic / interc_total_power);
  			for (interc_idx = 0;
  					interc_idx < type->modes[mode_idx].num_interconnect;
  					interc_idx++) {
  				float interc_power =
  				type->modes[mode_idx].interconnect[interc_idx].interconnect_power->power_usage.dynamic
  				+ type->modes[mode_idx].interconnect[interc_idx].interconnect_power->power_usage.leakage;
  				/* Each interconnect */
  				print_tabs(fp, indent_level + mode_indent + 2);
  				fprintf(fp,
  						"<%s name=\"%s\" P=\"%.4g\" P_parent=\"%.3g\" P_total=\"%.3g\" P_dyn=\"%.3g\"/>\n",
  						interconnect_type_name(
  								type->modes[mode_idx].interconnect[interc_idx].type),
  						type->modes[mode_idx].interconnect[interc_idx].name,
  						interc_power, interc_power / interc_total_power * 100,
  						interc_power / total_power * 100,
  						type->modes[mode_idx].interconnect[interc_idx].interconnect_power->power_usage.dynamic
  						/ interc_power);
  			}
  			print_tabs(fp, indent_level + mode_indent + 1);
  			fprintf(fp, "</interconnect>\n");
  		}
  
  		for (child_idx = 0;
  				child_idx < type->modes[mode_idx].num_pb_type_children;
  				child_idx++) {
  			power_print_pb_usage_recursive(fp,
  					&type->modes[mode_idx].pb_type_children[child_idx],
  					indent_level + mode_indent + 1,
  					type->modes[mode_idx].mode_power->power_usage.dynamic
  					+ type->modes[mode_idx].mode_power->power_usage.leakage,
  					total_power);
  		}
  
  		if (type->num_modes > 1) {
  			print_tabs(fp, indent_level + mode_indent);
  			fprintf(fp, "</mode>\n");
  		}
  	}
  
  	print_tabs(fp, indent_level);
  	fprintf(fp, "</pb_type>\n");
  }
  
  static void power_print_clb_detailed(FILE * fp) {
  	int type_idx;
      auto& device_ctx = g_vpr_ctx.device();
  
  	float clb_power_total = power_component_get_usage_sum(
  			POWER_COMPONENT_PB);
  	for (type_idx = 0; type_idx < device_ctx.num_block_types; type_idx++) {
  		if (!device_ctx.block_types[type_idx].pb_type) {
  			continue;
  		}
  
  		power_print_pb_usage_recursive(fp, device_ctx.block_types[type_idx].pb_type,
  				0, clb_power_total, clb_power_total);
  	}
  }
  #endif
  
  /*
   * static void power_print_stats(FILE * fp) {
   * auto& power_ctx = g_vpr_ctx.power();
   * fprintf(fp, "Max Segment Fanout: %d\n",
   * power_ctx.commonly_used->max_seg_fanout);
   * fprintf(fp, "Max Segment->Segment Fanout: %d\n",
   * power_ctx.commonly_used->max_seg_to_seg_fanout);
   * fprintf(fp, "Max Segment->IPIN Fanout: %d\n",
   * power_ctx.commonly_used->max_seg_to_IPIN_fanout);
   * fprintf(fp, "Max IPIN fanin: %d\n", power_ctx.commonly_used->max_IPIN_fanin);
   * fprintf(fp, "Average SB Buffer Size: %.1f\n",
   * power_ctx.commonly_used->total_sb_buffer_size
   * / (float) power_ctx.commonly_used->num_sb_buffers);
   * fprintf(fp, "SB Buffer Transistors: %g\n",
   * power_count_transistors_buffer(
   * power_ctx.commonly_used->total_sb_buffer_size
   * / (float) power_ctx.commonly_used->num_sb_buffers));
   * fprintf(fp, "Average CB Buffer Size: %.1f\n",
   * power_ctx.commonly_used->total_cb_buffer_size
   * / (float) power_ctx.commonly_used->num_cb_buffers);
   * fprintf(fp, "Tile length (um): %.2f\n",
   * power_ctx.commonly_used->tile_length * CONVERT_UM_PER_M);
   * fprintf(fp, "1X Inverter C_in: %g\n", power_ctx.commonly_used->INV_1X_C_in);
   * fprintf(fp, "\n");
   * }
   */
  
  static const char* power_estimation_method_name(e_power_estimation_method power_method) {
      switch (power_method) {
          case POWER_METHOD_UNDEFINED:
              return "Undefined";
          case POWER_METHOD_IGNORE:
              return "Ignore";
          case POWER_METHOD_AUTO_SIZES:
              return "Transistor Auto-Size";
          case POWER_METHOD_SPECIFY_SIZES:
              return "Transistor Specify-Size";
          case POWER_METHOD_TOGGLE_PINS:
              return "Pin-Toggle";
          case POWER_METHOD_C_INTERNAL:
              return "C-Internal";
          case POWER_METHOD_ABSOLUTE:
              return "Absolute";
          case POWER_METHOD_SUM_OF_CHILDREN:
              return "Sum of Children";
          default:
              return "Unkown";
      }
  }
  
  static void power_print_breakdown_pb_rec(FILE* fp, t_pb_type* pb_type, int indent) {
      int mode_idx;
      int child_idx;
      int i;
      char buf[51];
      int child_indent;
      int interc_idx;
      t_mode* mode;
      t_power_usage interc_usage;
      e_power_estimation_method est_method = pb_type->pb_type_power->estimation_method;
      float total_power = power_component_get_usage_sum(POWER_COMPONENT_TOTAL);
      t_pb_type_power* pb_power = pb_type->pb_type_power;
  
      for (i = 0; i < indent; i++) {
          buf[i] = ' ';
      }
      strncpy(buf + indent, pb_type->name, 50 - indent);
      buf[50] = '\0';
      buf[strlen((pb_type->name)) + indent] = '\0';
      power_print_breakdown_entry(fp, indent, POWER_BREAKDOWN_ENTRY_TYPE_PB,
                                  pb_type->name, power_sum_usage(&pb_power->power_usage), total_power,
                                  power_perc_dynamic(&pb_power->power_usage),
                                  power_estimation_method_name(pb_type->pb_type_power->estimation_method));
  
      if (power_method_is_transistor_level(pb_type->pb_type_power->estimation_method)) {
          /* Local bufs and wires */
          power_print_breakdown_entry(fp, indent + 1,
                                      POWER_BREAKDOWN_ENTRY_TYPE_BUFS_WIRES, "Bufs/Wires",
                                      power_sum_usage(&pb_power->power_usage_bufs_wires), total_power,
                                      power_perc_dynamic(&pb_power->power_usage_bufs_wires), nullptr);
      }
  
      if (power_method_is_recursive(est_method)) {
          if (pb_type->num_modes > 1) {
              child_indent = indent + 2;
          } else {
              child_indent = indent + 1;
          }
  
          for (mode_idx = 0; mode_idx < pb_type->num_modes; mode_idx++) {
              mode = &pb_type->modes[mode_idx];
  
              if (pb_type->num_modes > 1) {
                  power_print_breakdown_entry(fp, indent + 1,
                                              POWER_BREAKDOWN_ENTRY_TYPE_MODE, mode->name,
                                              power_sum_usage(&mode->mode_power->power_usage),
                                              total_power,
                                              power_perc_dynamic(&mode->mode_power->power_usage),
                                              nullptr);
              }
  
              /* Interconnect Power */
              power_zero_usage(&interc_usage);
  
              /* Sum the interconnect */
              if (power_method_is_transistor_level(est_method)) {
                  for (interc_idx = 0; interc_idx < mode->num_interconnect;
                       interc_idx++) {
                      power_add_usage(&interc_usage,
                                      &mode->interconnect[interc_idx].interconnect_power->power_usage);
                  }
                  if (mode->num_interconnect) {
                      power_print_breakdown_entry(fp, child_indent,
                                                  POWER_BREAKDOWN_ENTRY_TYPE_INTERC, "Interc:",
                                                  power_sum_usage(&interc_usage), total_power,
                                                  power_perc_dynamic(&interc_usage), nullptr);
                  }
  
                  /* Print Interconnect Breakdown */
                  for (interc_idx = 0; interc_idx < mode->num_interconnect;
                       interc_idx++) {
                      t_interconnect* interc = &mode->interconnect[interc_idx];
                      if (interc->type == DIRECT_INTERC) {
                          // no power - skip
                      } else {
                          power_print_breakdown_entry(fp, child_indent + 1,
                                                      POWER_BREAKDOWN_ENTRY_TYPE_INTERC, interc->name,
                                                      power_sum_usage(&interc->interconnect_power->power_usage),
                                                      total_power,
                                                      power_perc_dynamic(&interc->interconnect_power->power_usage),
                                                      nullptr);
                      }
                  }
              }
  
              for (child_idx = 0;
                   child_idx < pb_type->modes[mode_idx].num_pb_type_children;
                   child_idx++) {
                  power_print_breakdown_pb_rec(fp,
                                               &pb_type->modes[mode_idx].pb_type_children[child_idx],
                                               child_indent);
              }
          }
      }
  }
  
  static void power_print_summary(FILE* fp, const t_vpr_setup& vpr_setup) {
      auto& power_ctx = g_vpr_ctx.power();
      auto& device_ctx = g_vpr_ctx.device();
  
      fprintf(power_ctx.output->out, "Circuit: %s\n",
              vpr_setup.FileNameOpts.CircuitName.c_str());
      fprintf(power_ctx.output->out, "Architecture: %s\n", vtr::basename(vpr_setup.FileNameOpts.ArchFile).c_str());
      fprintf(fp, "Technology (nm): %.0f\n",
              power_ctx.tech->tech_size * CONVERT_NM_PER_M);
      fprintf(fp, "Voltage: %.2f\n", power_ctx.tech->Vdd);
      fprintf(fp, "Temperature: %g\n", power_ctx.tech->temperature);
      fprintf(fp, "Critical Path: %g\n", power_ctx.solution_inf.T_crit);
      fprintf(fp, "Size of FPGA: %zu x %zu\n", device_ctx.grid.width(), device_ctx.grid.height());
      fprintf(fp, "Channel Width: %d\n", power_ctx.solution_inf.channel_width);
      fprintf(fp, "\n");
  }
  
  /*
   * Top-level function for the power module.
   * Calculates the average power of the entire FPGA (watts),
   * and prints it to the output file
   * - run_time_s: (Return value) The total runtime in seconds (us accuracy)
   */
  e_power_ret_code power_total(float* run_time_s, const t_vpr_setup& vpr_setup, const t_arch* arch, const t_det_routing_arch* routing_arch) {
      t_power_usage total_power;
      t_power_usage sub_power_usage;
      clock_t t_start;
      clock_t t_end;
      t_power_usage clb_power_usage;
      auto& power_ctx = g_vpr_ctx.power();
  
      t_start = clock();
  
      power_zero_usage(&total_power);
  
      if (routing_arch->directionality == BI_DIRECTIONAL) {
          power_log_msg(POWER_LOG_ERROR,
                        "Cannot calculate routing power for bi-directional architectures");
          return POWER_RET_CODE_ERRORS;
      }
  
      /* Calculate Power */
      /* Routing */
      power_usage_routing(&sub_power_usage, routing_arch, arch->Segments);
      power_add_usage(&total_power, &sub_power_usage);
      power_component_add_usage(&sub_power_usage, POWER_COMPONENT_ROUTING);
  
      /* Clock  */
      power_usage_clock(&sub_power_usage, arch->clocks);
      power_add_usage(&total_power, &sub_power_usage);
      power_component_add_usage(&sub_power_usage, POWER_COMPONENT_CLOCK);
  
      /* CLBs */
      power_usage_blocks(&clb_power_usage);
      power_add_usage(&total_power, &clb_power_usage);
      power_component_add_usage(&clb_power_usage, POWER_COMPONENT_PB);
  
      power_component_add_usage(&total_power, POWER_COMPONENT_TOTAL);
  
      power_print_title(power_ctx.output->out, "Summary");
      power_print_summary(power_ctx.output->out, vpr_setup);
  
      /* Print Error & Warning Logs */
      output_logs(power_ctx.output->out, power_ctx.output->logs,
                  power_ctx.output->num_logs);
  
      //power_print_title(power_ctx.output->out, "Statistics");
      //power_print_stats(power_ctx.output->out);
  
      power_print_title(power_ctx.output->out, "Power Breakdown");
      power_print_breakdown_summary(power_ctx.output->out);
  
      power_print_title(power_ctx.output->out, "Power Breakdown by PB");
      power_print_breakdown_pb(power_ctx.output->out);
  
      //power_print_title(power_ctx.output->out, "Spice Comparison");
      //power_print_spice_comparison();
  
      t_end = clock();
  
      *run_time_s = (float)(t_end - t_start) / CLOCKS_PER_SEC;
  
      /* Return code */
      if (power_ctx.output->logs[POWER_LOG_ERROR].num_messages) {
          return POWER_RET_CODE_ERRORS;
      } else if (power_ctx.output->logs[POWER_LOG_WARNING].num_messages) {
          return POWER_RET_CODE_WARNINGS;
      } else {
          return POWER_RET_CODE_SUCCESS;
      }
  }
  
  /**
   * Prints the power usage for all components
   * - fp: File descripter to print out to
   */
  static void power_print_breakdown_summary(FILE* fp) {
      power_print_breakdown_entry(fp, 0, POWER_BREAKDOWN_ENTRY_TYPE_TITLE, nullptr,
                                  0., 0., 0., nullptr);
      power_print_breakdown_component(fp, "Total", POWER_COMPONENT_TOTAL, 0);
      fprintf(fp, "\n");
  }
  
  static void power_print_breakdown_pb(FILE* fp) {
      fprintf(fp,
              "This sections provides a detailed breakdown of power usage by PB (physical\n"
              "block). For each PB, the power is listed, which is the sum power of all\n"
              "instances of the block.  It also indicates its percentage of total power (entire\n"
              "FPGA), as well as the percentage of its power that is dynamic (vs. static).  It\n"
              "also indicates the method used for power estimation.\n\n"
              "The data includes:\n"
              "\tModes:\t\tWhen a pb contains multiple modes, each mode is "
              "listed, with\n\t\t\t\tits power statistics.\n"
              "\tBufs/Wires:\tPower of all local "
              "buffers and local wire switching\n"
              "\t\t\t\t(transistor-level estimation only).\n"
              "\tInterc:\t\tPower of local interconnect multiplexers (transistor-\n"
              "\t\t\t\tlevel estimation only)\n\n"
              "Description of Estimation Methods:\n"
              "\tTransistor Auto-Size: Transistor-level power estimation. Local buffers and\n"
              "\t\twire lengths are automatically sized. This is the default estimation\n"
              "\t\tmethod.\n"
              "\tTransistor Specify-Size: Transistor-level power estimation. Local buffers\n"
              "\t\tand wire lengths are only inserted where specified by the user in the\n"
              "\t\tarchitecture file.\n"
              "\tPin-Toggle: Dynamic power is calculated using enery-per-toggle of the PB\n"
              "\t\tinput pins. Static power is absolute.\n"
              "\tC-Internal: Dynamic power is calculated using an internal equivalent\n"
              "\t\tcapacitance for PB type. Static power is absolute.\n"
              "\tAbsolute: Dynamic and static power are absolutes from the architecture file.\n"
              "\tSum of Children: Power of PB is only the sum of all child PBs; interconnect\n"
              "\t\tbetween the PB and its children is ignored.\n"
              "\tIgnore: Power of PB is ignored.\n\n\n");
  
      power_print_breakdown_entry(fp, 0, POWER_BREAKDOWN_ENTRY_TYPE_TITLE, nullptr,
                                  0., 0., 0., nullptr);
  
      auto& device_ctx = g_vpr_ctx.device();
  
      for (const auto& type : device_ctx.logical_block_types) {
          if (type.pb_type) {
              power_print_breakdown_pb_rec(fp, type.pb_type, 0);
          }
      }
      fprintf(fp, "\n");
  }
  
  /**
   * Internal recurseive function, used by power_component_print_usage
   */
  static void power_print_breakdown_component(FILE* fp, const char* name, e_power_component_type type, int indent_level) {
      auto& power_ctx = g_vpr_ctx.power();
      power_print_breakdown_entry(fp, indent_level,
                                  POWER_BREAKDOWN_ENTRY_TYPE_COMPONENT, name,
                                  power_sum_usage(&power_ctx.by_component.components[type]),
                                  power_sum_usage(&power_ctx.by_component.components[POWER_COMPONENT_TOTAL]),
                                  power_perc_dynamic(&power_ctx.by_component.components[type]), nullptr);
  
      switch (type) {
          case (POWER_COMPONENT_TOTAL):
              power_print_breakdown_component(fp, "Routing", POWER_COMPONENT_ROUTING,
                                              indent_level + 1);
              power_print_breakdown_component(fp, "PB Types", POWER_COMPONENT_PB,
                                              indent_level + 1);
              power_print_breakdown_component(fp, "Clock", POWER_COMPONENT_CLOCK,
                                              indent_level + 1);
              break;
          case (POWER_COMPONENT_ROUTING):
              power_print_breakdown_component(fp, "Switch Box",
                                              POWER_COMPONENT_ROUTE_SB, indent_level + 1);
              power_print_breakdown_component(fp, "Connection Box",
                                              POWER_COMPONENT_ROUTE_CB, indent_level + 1);
              power_print_breakdown_component(fp, "Global Wires",
                                              POWER_COMPONENT_ROUTE_GLB_WIRE, indent_level + 1);
              break;
          case (POWER_COMPONENT_CLOCK):
              /*
               * power_print_breakdown_component(fp, "Clock Buffers",
               * POWER_COMPONENT_CLOCK_BUFFER, indent_level + 1);
               * power_print_breakdown_component(fp, "Clock Wires",
               * POWER_COMPONENT_CLOCK_WIRE, indent_level + 1);
               */
              break;
          case (POWER_COMPONENT_PB):
              power_print_breakdown_component(fp, "Primitives",
                                              POWER_COMPONENT_PB_PRIMITIVES, indent_level + 1);
              power_print_breakdown_component(fp, "Interc Structures",
                                              POWER_COMPONENT_PB_INTERC_MUXES, indent_level + 1);
              power_print_breakdown_component(fp, "Buffers and Wires",
                                              POWER_COMPONENT_PB_BUFS_WIRE, indent_level + 1);
              power_print_breakdown_component(fp, "Other Estimation Methods",
                                              POWER_COMPONENT_PB_OTHER, indent_level + 1);
              break;
          default:
              break;
      }
  }
  
  static void power_print_breakdown_entry(FILE* fp, int indent, e_power_breakdown_entry_type type, const char* name, float power, float total_power, float perc_dyn, const char* method) {
      const int buf_size = 32;
      char buf[buf_size];
  
      switch (type) {
          case POWER_BREAKDOWN_ENTRY_TYPE_TITLE:
              fprintf(fp, "%-*s%-12s%-12s%-12s%-12s\n\n", buf_size, "Component",
                      "Power (W)", "%-Total", "%-Dynamic", "Method");
              break;
          case POWER_BREAKDOWN_ENTRY_TYPE_MODE:
              for (int i = 0; i < indent; i++)
                  buf[i] = ' ';
              strcpy(buf + indent, "Mode:");
              strncpy(buf + indent + 5, name, buf_size - indent - 6);
              fprintf(fp, "%-*s%-12.4g%-12.4g%-12.4g\n", buf_size, buf, power,
                      power / total_power, perc_dyn);
              break;
          case POWER_BREAKDOWN_ENTRY_TYPE_COMPONENT:
          case POWER_BREAKDOWN_ENTRY_TYPE_INTERC:
          case POWER_BREAKDOWN_ENTRY_TYPE_BUFS_WIRES:
              for (int i = 0; i < indent; i++)
                  buf[i] = ' ';
              strncpy(buf + indent, name, buf_size - indent - 1);
              buf[buf_size - 1] = '\0';
  
              fprintf(fp, "%-*s%-12.4g%-12.4g%-12.4g\n", buf_size, buf, power,
                      power / total_power, perc_dyn);
              break;
          case POWER_BREAKDOWN_ENTRY_TYPE_PB:
              for (int i = 0; i < indent; i++)
                  buf[i] = ' ';
              strncpy(buf + indent, name, buf_size - indent - 1);
              buf[buf_size - 1] = '\0';
  
              fprintf(fp, "%-*s%-12.4g%-12.4g%-12.4g%-12s\n", buf_size, buf, power,
                      power / total_power, perc_dyn, method);
              break;
          default:
              break;
      }
  }
  