From d84cd662874c59ee884f80752aac9fe1da993702 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 12 Nov 2019 22:18:13 -0700 Subject: [PATCH] refactored analysis SDC generator for grids --- .../analysis_sdc_grid_writer.cpp | 387 +++++++++++++++++- .../analysis_sdc_routing_writer.cpp | 98 +---- .../backend_assistant/analysis_sdc_writer.cpp | 2 +- .../analysis_sdc_writer_utils.cpp | 235 +++++++++++ .../analysis_sdc_writer_utils.h | 40 ++ .../module_builder/build_grid_modules.cpp | 6 +- 6 files changed, 653 insertions(+), 115 deletions(-) create mode 100644 vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.cpp create mode 100644 vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.h diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_grid_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_grid_writer.cpp index 2a491f129..235e99bee 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_grid_writer.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_grid_writer.cpp @@ -10,6 +10,8 @@ #include "fpga_x2p_utils.h" #include "fpga_x2p_pbtypes_utils.h" +#include "sdc_writer_utils.h" +#include "analysis_sdc_writer_utils.h" #include "analysis_sdc_grid_writer.h" #include "globals.h" @@ -30,6 +32,9 @@ void rec_print_analysis_sdc_disable_unused_pb_graph_nodes(std::fstream& fp, t_pb_graph_node* physical_pb_graph_node, const e_side& border_side) { t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type; + + /* Validate file stream */ + check_file_handler(fp); /* Disable all the ports of current module (parent_module)! * Hierarchy name already includes the instance name of parent_module @@ -72,23 +77,366 @@ void rec_print_analysis_sdc_disable_unused_pb_graph_nodes(std::fstream& fp, } /******************************************************************** + * Disable an unused pin of a pb_graph_node (parent_module) + *******************************************************************/ +static +void disable_pb_graph_node_unused_pin(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& hierarchy_name, + const t_pb_graph_pin& pb_graph_pin, + t_phy_pb* block_physical_pb) { + /* Validate file stream */ + check_file_handler(fp); + + int rr_node_index = pb_graph_pin.rr_node_index_physical_pb; + + /* Identify if the net has been used or not */ + if (false == is_rr_node_to_be_disable_for_analysis(&(block_physical_pb->rr_graph->rr_node[rr_node_index]))) { + /* Used pin; Nothing to do */ + return; + } + /* Reach here, it means that this pin is not used. Disable timing analysis for the pin */ + /* Find the module port by name */ + std::string module_port_name = generate_pb_type_port_name(pb_graph_pin.port); + ModulePortId module_port = module_manager.find_module_port(parent_module, module_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(parent_module, module_port)); + BasicPort port_to_disable = module_manager.module_port(parent_module, module_port); + port_to_disable.set_width(pb_graph_pin.pin_number, pb_graph_pin.pin_number); + + fp << "set_disable_timing "; + fp << hierarchy_name; + fp << "/"; + fp << generate_sdc_port(port_to_disable); + fp << std::endl; +} + +/******************************************************************** + * Disable unused input ports and output ports of this pb_graph_node (parent_module) + * This function will iterate over all the input pins, output pins + * of the physical_pb_graph_node, and check if they are mapped + * For unused pins, we will find the port in parent_module + * and then print SDC commands to disable them + *******************************************************************/ +static +void disable_pb_graph_node_unused_pins(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& hierarchy_name, + t_pb_graph_node* physical_pb_graph_node, + t_phy_pb* block_physical_pb) { + + /* Disable unused input pins */ + for (int iport = 0; iport < physical_pb_graph_node->num_input_ports; ++iport) { + for (int ipin = 0; ipin < physical_pb_graph_node->num_input_pins[iport]; ++ipin) { + disable_pb_graph_node_unused_pin(fp, module_manager, parent_module, + hierarchy_name, + physical_pb_graph_node->input_pins[iport][ipin], + block_physical_pb); + } + } + + /* Disable unused output pins */ + for (int iport = 0; iport < physical_pb_graph_node->num_output_ports; ++iport) { + for (int ipin = 0; ipin < physical_pb_graph_node->num_output_pins[iport]; ++ipin) { + disable_pb_graph_node_unused_pin(fp, module_manager, parent_module, + hierarchy_name, + physical_pb_graph_node->output_pins[iport][ipin], + block_physical_pb); + } + } + + /* Disable unused clock pins */ + for (int iport = 0; iport < physical_pb_graph_node->num_clock_ports; ++iport) { + for (int ipin = 0; ipin < physical_pb_graph_node->num_clock_pins[iport]; ++ipin) { + disable_pb_graph_node_unused_pin(fp, module_manager, parent_module, + hierarchy_name, + physical_pb_graph_node->clock_pins[iport][ipin], + block_physical_pb); + } + } +} + +/******************************************************************** + * Disable unused inputs of routing multiplexers of this pb_graph_node + * This function will first cache the nets for each input and output pins + * and store the results in a mux_name-to-net mapping + *******************************************************************/ +static +void disable_pb_graph_node_unused_mux_inputs(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& hierarchy_name, + t_pb_graph_node* physical_pb_graph_node, + t_phy_pb* block_physical_pb, + const e_side& border_side) { + t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type; + + int physical_mode_index = find_pb_type_physical_mode_index(*physical_pb_type); + + std::map mux_instance_to_net_map; + + /* Cache the nets for each input pins of each child pb_graph_node */ + for (int ichild = 0; ichild < physical_pb_type->modes[physical_mode_index].num_pb_type_children; ++ichild) { + for (int inst = 0; inst < physical_pb_type->modes[physical_mode_index].pb_type_children[ichild].num_pb; ++inst) { + + t_pb_graph_node* child_pb_graph_node = &(physical_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ichild][inst]); + + /* Cache the nets for input pins of the child pb_graph_node */ + for (int iport = 0; iport < child_pb_graph_node->num_input_ports; ++iport) { + for (int ipin = 0; ipin < child_pb_graph_node->num_input_pins[iport]; ++ipin) { + int rr_node_index = child_pb_graph_node->input_pins[iport][ipin].rr_node_index_physical_pb; + /* Generate the mux name */ + std::string mux_instance_name = generate_pb_mux_instance_name(GRID_MUX_INSTANCE_PREFIX, &(child_pb_graph_node->input_pins[iport][ipin]), std::string("")); + /* Cache the net */ + mux_instance_to_net_map[mux_instance_name] = block_physical_pb->rr_graph->rr_node[rr_node_index].vpack_net_num; + } + } + + /* Cache the nets for clock pins of the child pb_graph_node */ + for (int iport = 0; iport < child_pb_graph_node->num_clock_ports; ++iport) { + for (int ipin = 0; ipin < child_pb_graph_node->num_clock_pins[iport]; ++ipin) { + int rr_node_index = child_pb_graph_node->clock_pins[iport][ipin].rr_node_index_physical_pb; + /* Generate the mux name */ + std::string mux_instance_name = generate_pb_mux_instance_name(GRID_MUX_INSTANCE_PREFIX, &(child_pb_graph_node->clock_pins[iport][ipin]), std::string("")); + /* Cache the net */ + mux_instance_to_net_map[mux_instance_name] = block_physical_pb->rr_graph->rr_node[rr_node_index].vpack_net_num; + } + } + + } + } + + /* Cache the nets for each output pins of this pb_graph_node */ + for (int iport = 0; iport < physical_pb_graph_node->num_output_ports; ++iport) { + for (int ipin = 0; ipin < physical_pb_graph_node->num_output_pins[iport]; ++ipin) { + int rr_node_index = physical_pb_graph_node->output_pins[iport][ipin].rr_node_index_physical_pb; + /* Generate the mux name */ + std::string mux_instance_name = generate_pb_mux_instance_name(GRID_MUX_INSTANCE_PREFIX, &(physical_pb_graph_node->output_pins[iport][ipin]), std::string("")); + /* Cache the net */ + mux_instance_to_net_map[mux_instance_name] = block_physical_pb->rr_graph->rr_node[rr_node_index].vpack_net_num; + } + } + + /* Now disable unused inputs of routing multiplexers, by tracing from input pins of the parent_module */ + for (int iport = 0; iport < physical_pb_graph_node->num_input_ports; ++iport) { + for (int ipin = 0; ipin < physical_pb_graph_node->num_input_pins[iport]; ++ipin) { + /* Find the module port by name */ + std::string module_port_name = generate_pb_type_port_name(physical_pb_graph_node->input_pins[iport][ipin].port); + ModulePortId module_port = module_manager.find_module_port(parent_module, module_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(parent_module, module_port)); + + int rr_node_index = physical_pb_graph_node->input_pins[iport][ipin].rr_node_index_physical_pb; + t_rr_node* input_rr_node = &(block_physical_pb->rr_graph->rr_node[rr_node_index]); + + disable_analysis_module_input_pin_net_sinks(fp, module_manager, parent_module, + hierarchy_name, + module_port, ipin, + input_rr_node, + mux_instance_to_net_map); + } + } + + for (int iport = 0; iport < physical_pb_graph_node->num_clock_ports; ++iport) { + for (int ipin = 0; ipin < physical_pb_graph_node->num_clock_pins[iport]; ++ipin) { + /* Find the module port by name */ + std::string module_port_name = generate_pb_type_port_name(physical_pb_graph_node->clock_pins[iport][ipin].port); + ModulePortId module_port = module_manager.find_module_port(parent_module, module_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(parent_module, module_port)); + + int rr_node_index = physical_pb_graph_node->clock_pins[iport][ipin].rr_node_index_physical_pb; + t_rr_node* input_rr_node = &(block_physical_pb->rr_graph->rr_node[rr_node_index]); + + disable_analysis_module_input_pin_net_sinks(fp, module_manager, parent_module, + hierarchy_name, + module_port, ipin, + input_rr_node, + mux_instance_to_net_map); + } + } + + /* Now disable unused inputs of routing multiplexers, by tracing from output pins of the child_module */ + for (int ichild = 0; ichild < physical_pb_type->modes[physical_mode_index].num_pb_type_children; ++ichild) { + /* Generate the name of the Verilog module for this child */ + std::string child_module_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), border_side); + std::string child_module_name = generate_physical_block_module_name(child_module_name_prefix, &(physical_pb_type->modes[physical_mode_index].pb_type_children[ichild])); + + ModuleId child_module = module_manager.find_module(child_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(child_module)); + + for (int inst = 0; inst < physical_pb_type->modes[physical_mode_index].pb_type_children[ichild].num_pb; ++inst) { + + t_pb_graph_node* child_pb_graph_node = &(physical_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ichild][inst]); + + for (int iport = 0; iport < child_pb_graph_node->num_output_ports; ++iport) { + for (int ipin = 0; ipin < child_pb_graph_node->num_output_pins[iport]; ++ipin) { + /* Find the module port by name */ + std::string module_port_name = generate_pb_type_port_name(child_pb_graph_node->output_pins[iport][ipin].port); + ModulePortId module_port = module_manager.find_module_port(child_module, module_port_name); + VTR_ASSERT(true == module_manager.valid_module_port_id(child_module, module_port)); + + int rr_node_index = child_pb_graph_node->output_pins[iport][ipin].rr_node_index_physical_pb; + t_rr_node* output_rr_node = &(block_physical_pb->rr_graph->rr_node[rr_node_index]); + + disable_analysis_module_output_pin_net_sinks(fp, module_manager, parent_module, + hierarchy_name, + child_module, inst, + module_port, ipin, + output_rr_node, + mux_instance_to_net_map); + } + } + } + } +} + +/******************************************************************** + * Recursively visit all the pb_types in the hierarchy + * and disable all the unused resources, including: + * 1. input ports + * 2. output ports + * 3. unused inputs of routing multiplexers + * + * As this function is executed in a recursive way. + * To avoid repeated disable timing for ports, during each run of this function, + * only the unused input ports, output ports of the parent module will be disabled. + * In addition, we will cache all the net ids mapped to the input ports of + * child modules, and the net ids mapped to the output ports of parent module. + * As such, we can trace from + * 1. the input ports of parent module to disable unused inputs of routing multiplexer + * which drives the inputs of child modules + * + * Parent_module + * +--------------------------------------------- + * | MUX child_module + * | +-------------+ +-------- + * input_pin0(netA) --->|-------->| Routing |------>| + * input_pin1(netB) --->|----x--->| Multiplexer | netA | + * | +-------------+ | + * | | + * + * 2. the output ports of child module to disable unused inputs of routing multiplexer + * which drives the outputs of parent modules + * + * Case 1: + * parent_module + * --------------------------------------+ + * child_module | + * -------------+ | + * | +-------------+ | + * output_pin0 (netA) |--->| Routing |----->|----> + * output_pin1 (netB) |-x->| Multiplexer | netA | + * | +-------------+ | + * + * Case 2: + * + * Parent_module + * +--------------------------------------------- + * | + * | +--------------------------------------------+ + * | | MUX child_module | + * | | +-------------+ +-----------+ | + * | +--->| Routing |------>| | | + * input_pin0(netA) --->|----x--->| Multiplexer | netA | output_pin|-----+ + * | +-------------+ | | netA + * | | | + * + * + * Note: it is a must to disable all the ports in all the child pb_types! + * This can prohibit timing analyzer to consider any FF-to-FF path or + * combinatinal path inside an unused grid, when finding critical paths!!! + *******************************************************************/ +static +void rec_print_analysis_sdc_disable_pb_graph_node_unused_resources(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& hierarchy_name, + t_pb_graph_node* physical_pb_graph_node, + t_phy_pb* block_physical_pb, + const e_side& border_side) { + t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type; + + /* Disable unused input ports and output ports of this pb_graph_node (parent_module) */ + disable_pb_graph_node_unused_pins(fp, module_manager, parent_module, + hierarchy_name, physical_pb_graph_node, block_physical_pb); + + /* Return if this is the primitive pb_type + * Note: this must return before we disable any unused inputs of routing multiplexer! + * This is due to that primitive pb_type does NOT contain any routing multiplexers inside!!! + */ + if (TRUE == is_primitive_pb_type(physical_pb_type)) { + return; + } + + /* Disable unused inputs of routing multiplexers of this pb_graph_node */ + disable_pb_graph_node_unused_mux_inputs(fp, module_manager, parent_module, + hierarchy_name, physical_pb_graph_node, block_physical_pb, + border_side); + + + int physical_mode_index = find_pb_type_physical_mode_index(*physical_pb_type); + + /* Disable all the ports by iterating over its instance in the parent module */ + for (int ichild = 0; ichild < physical_pb_type->modes[physical_mode_index].num_pb_type_children; ++ichild) { + /* Generate the name of the Verilog module for this child */ + std::string child_module_name_prefix = generate_grid_block_prefix(std::string(GRID_MODULE_NAME_PREFIX), border_side); + std::string child_module_name = generate_physical_block_module_name(child_module_name_prefix, &(physical_pb_type->modes[physical_mode_index].pb_type_children[ichild])); + + ModuleId child_module = module_manager.find_module(child_module_name); + VTR_ASSERT(true == module_manager.valid_module_id(child_module)); + + /* Each child may exist multiple times in the hierarchy*/ + for (int inst = 0; inst < physical_pb_type->modes[physical_mode_index].pb_type_children[ichild].num_pb; ++inst) { + std::string child_instance_name = module_manager.instance_name(parent_module, child_module, module_manager.child_module_instances(parent_module, child_module)[inst]); + /* Must have a valid instance name!!! */ + VTR_ASSERT(false == child_instance_name.empty()); + + std::string updated_hierarchy_name = hierarchy_name + std::string("/") + child_instance_name + std::string("/"); + + rec_print_analysis_sdc_disable_pb_graph_node_unused_resources(fp, module_manager, child_module, hierarchy_name, + &(physical_pb_graph_node->child_pb_graph_nodes[physical_mode_index][ichild][inst]), + block_physical_pb, border_side); + } + } +} + +/******************************************************************** + * This function can work in two differnt modes: + * 1. For partially unused pb blocks + * --------------------------------- + * Disable the timing for only unused resources in a physical block + * We have to walk through pb_graph node, port by port and pin by pin. + * Identify which pins have not been used, and then disable the timing + * for these ports. + * Plus, for input ports, we will trace the routing multiplexers + * and disable the timing for unused inputs. + * + * 2. For fully unused pb_blocks + * ----------------------------- * Disable the timing for a fully unused grid! * This is very straightforward! * Just walk through each pb_type and disable all the ports using wildcards *******************************************************************/ static -void print_analysis_sdc_disable_unused_pb_block(std::fstream& fp, - t_type_ptr grid_type, - const vtr::Point& grid_coordinate, - const ModuleManager& module_manager, - const std::string& grid_instance_name, - const size_t& grid_z, - const e_side& border_side) { +void print_analysis_sdc_disable_pb_block_unused_resources(std::fstream& fp, + t_type_ptr grid_type, + const vtr::Point& grid_coordinate, + const ModuleManager& module_manager, + const std::string& grid_instance_name, + const size_t& grid_z, + const e_side& border_side, + t_phy_pb* block_physical_pb, + const bool& unused_block) { /* Check code: if this is an IO block, the border side MUST be valid */ if (IO_TYPE == grid_type) { VTR_ASSERT(NUM_SIDES != border_side); } + /* If the block is partially unused, we should have a physical pb */ + if (false == unused_block) { + VTR_ASSERT(NULL != block_physical_pb); + } + /* Find an unique name to the pb instance in this grid * Note: this must be consistent with the instance name we used in build_grid_module()!!! */ @@ -102,13 +450,25 @@ void print_analysis_sdc_disable_unused_pb_block(std::fstream& fp, /* Print comments */ fp << "#######################################" << std::endl; - fp << "# Disable Timing for unused grid[" << grid_coordinate.x() << "][" << grid_coordinate.y() << "][" << grid_z << "]" << std::endl; + + if (true == unused_block) { + fp << "# Disable Timing for unused grid[" << grid_coordinate.x() << "][" << grid_coordinate.y() << "][" << grid_z << "]" << std::endl; + } else { + VTR_ASSERT_SAFE(false == unused_block); + fp << "# Disable Timing for unused resources in grid[" << grid_coordinate.x() << "][" << grid_coordinate.y() << "][" << grid_z << "]" << std::endl; + } + fp << "#######################################" << std::endl; std::string hierarchy_name = grid_instance_name + std::string("/") + pb_instance_name + std::string("/"); /* Go recursively through the pb_graph hierarchy, and disable all the ports level by level */ - rec_print_analysis_sdc_disable_unused_pb_graph_nodes(fp, module_manager, pb_module, hierarchy_name, grid_type->pb_graph_head, border_side); + if (true == unused_block) { + rec_print_analysis_sdc_disable_unused_pb_graph_nodes(fp, module_manager, pb_module, hierarchy_name, grid_type->pb_graph_head, border_side); + } else { + VTR_ASSERT_SAFE(false == unused_block); + rec_print_analysis_sdc_disable_pb_graph_node_unused_resources(fp, module_manager, pb_module, hierarchy_name, grid_type->pb_graph_head, block_physical_pb, border_side); + } } /******************************************************************** @@ -133,7 +493,7 @@ void print_analysis_sdc_disable_unused_grid(std::fstream& fp, */ if ( (NULL == grid_type) || (EMPTY_TYPE == grid_type) - || (0 == L_grids[grid_coordinate.x()][grid_coordinate.y()].offset) ) { + || (0 < L_grids[grid_coordinate.x()][grid_coordinate.y()].offset) ) { return; } @@ -168,6 +528,8 @@ void print_analysis_sdc_disable_unused_grid(std::fstream& fp, /* TODO: verilog_generate_sdc_disable_one_unused_block(fp, &(L_blocks[blk_id])); */ + t_phy_pb* block_phy_pb = (t_phy_pb*) L_blocks[blk_id].phy_pb; + print_analysis_sdc_disable_pb_block_unused_resources(fp, grid_type, grid_coordinate, module_manager, grid_instance_name, iblk, border_side, block_phy_pb, false); } /* For unused grid, disable all the pins in the physical_pb_type */ @@ -176,7 +538,7 @@ void print_analysis_sdc_disable_unused_grid(std::fstream& fp, if (true == grid_usage[iblk]) { continue; } - print_analysis_sdc_disable_unused_pb_block(fp, grid_type, grid_coordinate, module_manager, grid_instance_name, iblk, border_side); + print_analysis_sdc_disable_pb_block_unused_resources(fp, grid_type, grid_coordinate, module_manager, grid_instance_name, iblk, border_side, NULL, true); } } @@ -207,9 +569,6 @@ void print_analysis_sdc_disable_unused_grids(std::fstream& fp, const std::vector>& L_grids, const std::vector& L_blocks, const ModuleManager& module_manager) { - /* TODO: disable inputs of multiplexers - verilog_generate_sdc_disable_unused_grids_muxs(fp, LL_nx, LL_ny, LL_grid, LL_block); - */ /* Process unused core grids */ for (size_t ix = 1; ix < device_size.x() - 1; ++ix) { diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_routing_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_routing_writer.cpp index 312696456..0509c08de 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_routing_writer.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_routing_writer.cpp @@ -14,107 +14,11 @@ #include "fpga_x2p_types.h" #include "sdc_writer_utils.h" +#include "analysis_sdc_writer_utils.h" #include "analysis_sdc_routing_writer.h" #include "globals.h" -/******************************************************************** - * Identify if a node should be disabled during analysis SDC generation - *******************************************************************/ -static -bool is_rr_node_to_be_disable_for_analysis(t_rr_node* cur_rr_node) { - /* Conditions to enable timing analysis for a node - * 1st condition: it have a valid vpack_net_number - * 2nd condition: it is not an parasitic net - * 3rd condition: it is not a global net - */ - if ( (OPEN != cur_rr_node->vpack_net_num) - && (FALSE == cur_rr_node->is_parasitic_net) - && (FALSE == vpack_net[cur_rr_node->vpack_net_num].is_global) - && (FALSE == vpack_net[cur_rr_node->vpack_net_num].is_const_gen) ){ - return false; - } - return true; -} - -/******************************************************************** - * Disable all the unused inputs of routing multiplexers, which are not used by benchmark - * Here, we start from each input of a routing module, and traverse forward to the sink - * port of the module net whose source is the input - * We will find the instance name which is the parent of the sink port, and search the - * net id through the instance_name_to_net_map - * The the net id does not match the net id of this input, we will disable the sink port! - * - * parent_module - * +----------------------- - * | MUX instance A - * | +----------- - * input_port--->|--+---x-->| sink port (disable! net_id = Y) - * (net_id = X) | | +---------- - * | | MUX instance B - * | | +---------- - * | +------>| sink port (do not disable! net_id = X) - * - *******************************************************************/ -static -void disable_analysis_module_input_port_net_sinks(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& parent_module, - const std::string& parent_instance_name, - const ModulePortId& module_input_port, - t_rr_node* input_rr_node, - const std::map mux_instance_to_net_map) { - /* Validate file stream */ - check_file_handler(fp); - - /* Find the module net which sources from this port! */ - for (const size_t& pin : module_manager.module_port(parent_module, module_input_port).pins()) { - ModuleNetId module_net = module_manager.module_instance_port_net(parent_module, parent_module, 0, module_input_port, pin); - VTR_ASSERT(true == module_manager.valid_module_net_id(parent_module, module_net)); - - /* Touch each sink of the net! */ - for (const ModuleNetSinkId& sink_id : module_manager.module_net_sinks(parent_module, module_net)) { - ModuleId sink_module = module_manager.net_sink_modules(parent_module, module_net)[sink_id]; - size_t sink_instance = module_manager.net_sink_instances(parent_module, module_net)[sink_id]; - - /* Skip when sink module is the parent module, - * the output ports of parent modules have been disabled/enabled already! - */ - if (sink_module == parent_module) { - continue; - } - - std::string sink_instance_name = module_manager.instance_name(parent_module, sink_module, sink_instance); - bool disable_timing = false; - /* Check if this node is used by benchmark */ - if (true == is_rr_node_to_be_disable_for_analysis(input_rr_node)) { - /* Disable all the sinks! */ - disable_timing = true; - } else { - /* See if the net id matches. If does not match, we should disable! */ - if (input_rr_node->vpack_net_num != mux_instance_to_net_map.at(sink_instance_name)) { - disable_timing = true; - } - } - - /* Time to write SDC command to disable timing or not */ - if (false == disable_timing) { - continue; - } - - BasicPort sink_port = module_manager.module_port(sink_module, module_manager.net_sink_ports(parent_module, module_net)[sink_id]); - sink_port.set_width(module_manager.net_sink_pins(parent_module, module_net)[sink_id], - module_manager.net_sink_pins(parent_module, module_net)[sink_id]); - /* Get the input id that is used! Disable the unused inputs! */ - fp << "set_disable_timing "; - fp << parent_instance_name << "/"; - fp << sink_instance_name << "/"; - fp << generate_sdc_port(sink_port); - fp << std::endl; - } - } -} - /******************************************************************** * This function will disable * 1. all the unused port (unmapped by a benchmark) of a connection block diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer.cpp index 8aed25fc0..d4297d8dc 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer.cpp @@ -255,7 +255,7 @@ void print_analysis_sdc(const std::string& sdc_dir, L_device_rr_gsb, compact_routing_hierarchy); - /* TODO: Disable timing for unused routing resources in grids (programmable blocks) */ + /* Disable timing for unused routing resources in grids (programmable blocks) */ print_analysis_sdc_disable_unused_grids(fp, device_size, L_grids, L_blocks, module_manager); /* Close file handler */ diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.cpp new file mode 100644 index 000000000..3c2885fd3 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.cpp @@ -0,0 +1,235 @@ +/******************************************************************** + * This file includes most utilized functions + * that are used to output a SDC file + * in order to constrain a FPGA fabric (P&Red netlist) mapped to a benchmark + *******************************************************************/ +#include "vtr_assert.h" + +#include "fpga_x2p_utils.h" + +#include "sdc_writer_utils.h" +#include "analysis_sdc_writer_utils.h" + +#include "globals.h" + +/******************************************************************** + * Identify if a node should be disabled during analysis SDC generation + *******************************************************************/ +bool is_rr_node_to_be_disable_for_analysis(t_rr_node* cur_rr_node) { + /* Conditions to enable timing analysis for a node + * 1st condition: it have a valid vpack_net_number + * 2nd condition: it is not an parasitic net + * 3rd condition: it is not a global net + */ + if ( (OPEN != cur_rr_node->vpack_net_num) + && (FALSE == cur_rr_node->is_parasitic_net) + && (FALSE == vpack_net[cur_rr_node->vpack_net_num].is_global) + && (FALSE == vpack_net[cur_rr_node->vpack_net_num].is_const_gen) ){ + return false; + } + return true; +} + +/******************************************************************** + * Disable all the unused inputs of routing multiplexers, which are not used by benchmark + * Here, we start from each input of a routing module, and traverse forward to the sink + * port of the module net whose source is the input + * We will find the instance name which is the parent of the sink port, and search the + * net id through the instance_name_to_net_map + * The the net id does not match the net id of this input, we will disable the sink port! + * + * parent_module + * +----------------------- + * | MUX instance A + * | +----------- + * input_port--->|--+---x-->| sink port (disable! net_id = Y) + * (net_id = X) | | +---------- + * | | MUX instance B + * | | +---------- + * | +------>| sink port (do not disable! net_id = X) + * + *******************************************************************/ +void disable_analysis_module_input_pin_net_sinks(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& parent_instance_name, + const ModulePortId& module_input_port, + const size_t& module_input_pin, + t_rr_node* input_rr_node, + const std::map mux_instance_to_net_map) { + /* Validate file stream */ + check_file_handler(fp); + + /* Find the module net which sources from this port! */ + ModuleNetId module_net = module_manager.module_instance_port_net(parent_module, parent_module, 0, module_input_port, module_input_pin); + VTR_ASSERT(true == module_manager.valid_module_net_id(parent_module, module_net)); + + /* Touch each sink of the net! */ + for (const ModuleNetSinkId& sink_id : module_manager.module_net_sinks(parent_module, module_net)) { + ModuleId sink_module = module_manager.net_sink_modules(parent_module, module_net)[sink_id]; + size_t sink_instance = module_manager.net_sink_instances(parent_module, module_net)[sink_id]; + + /* Skip when sink module is the parent module, + * the output ports of parent modules have been disabled/enabled already! + */ + if (sink_module == parent_module) { + continue; + } + + std::string sink_instance_name = module_manager.instance_name(parent_module, sink_module, sink_instance); + bool disable_timing = false; + /* Check if this node is used by benchmark */ + if (true == is_rr_node_to_be_disable_for_analysis(input_rr_node)) { + /* Disable all the sinks! */ + disable_timing = true; + } else { + std::map::const_iterator it = mux_instance_to_net_map.find(sink_instance_name); + if (it != mux_instance_to_net_map.end()) { + /* See if the net id matches. If does not match, we should disable! */ + if (input_rr_node->vpack_net_num != mux_instance_to_net_map.at(sink_instance_name)) { + disable_timing = true; + } + } + } + + /* Time to write SDC command to disable timing or not */ + if (false == disable_timing) { + continue; + } + + BasicPort sink_port = module_manager.module_port(sink_module, module_manager.net_sink_ports(parent_module, module_net)[sink_id]); + sink_port.set_width(module_manager.net_sink_pins(parent_module, module_net)[sink_id], + module_manager.net_sink_pins(parent_module, module_net)[sink_id]); + /* Get the input id that is used! Disable the unused inputs! */ + fp << "set_disable_timing "; + fp << parent_instance_name << "/"; + fp << sink_instance_name << "/"; + fp << generate_sdc_port(sink_port); + fp << std::endl; + } +} + + +/******************************************************************** + * Disable all the unused inputs of routing multiplexers, which are not used by benchmark + * Here, we start from each input of a routing module, and traverse forward to the sink + * port of the module net whose source is the input + * We will find the instance name which is the parent of the sink port, and search the + * net id through the instance_name_to_net_map + * The the net id does not match the net id of this input, we will disable the sink port! + * + * parent_module + * +----------------------- + * | MUX instance A + * | +----------- + * input_port--->|--+---x-->| sink port (disable! net_id = Y) + * (net_id = X) | | +---------- + * | | MUX instance B + * | | +---------- + * | +------>| sink port (do not disable! net_id = X) + * + *******************************************************************/ +void disable_analysis_module_input_port_net_sinks(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& parent_instance_name, + const ModulePortId& module_input_port, + t_rr_node* input_rr_node, + const std::map mux_instance_to_net_map) { + /* Validate file stream */ + check_file_handler(fp); + + /* Find the module net which sources from this port! */ + for (const size_t& pin : module_manager.module_port(parent_module, module_input_port).pins()) { + disable_analysis_module_input_pin_net_sinks(fp, module_manager, parent_module, + parent_instance_name, + module_input_port, pin, + input_rr_node, + mux_instance_to_net_map); + } +} + +/******************************************************************** + * Disable all the unused inputs of routing multiplexers, which are not used by benchmark + * Here, we start from each output of a child module, and traverse forward to the sink + * port of the module net whose source is the input + * We will find the instance name which is the parent of the sink port, and search the + * net id through the instance_name_to_net_map + * The the net id does not match the net id of this input, we will disable the sink port! + * + * Parent_module + * +--------------------------------------------- + * | + * | +--------------------------------------------+ + * | | MUX child_module | + * | | +-------------+ +-----------+ | + * | +--->| Routing |------>| | | + * input_pin0(netA) --->|----x--->| Multiplexer | netA | output_pin|-----+ + * | +-------------+ | | netA + * | | | + * + + * + *******************************************************************/ +void disable_analysis_module_output_pin_net_sinks(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& parent_instance_name, + const ModuleId& child_module, + const size_t& child_instance, + const ModulePortId& child_module_port, + const size_t& child_module_pin, + t_rr_node* output_rr_node, + const std::map mux_instance_to_net_map) { + /* Validate file stream */ + check_file_handler(fp); + + /* Find the module net which sources from this port! */ + ModuleNetId module_net = module_manager.module_instance_port_net(parent_module, child_module, child_instance, child_module_port, child_module_pin); + VTR_ASSERT(true == module_manager.valid_module_net_id(parent_module, module_net)); + + /* Touch each sink of the net! */ + for (const ModuleNetSinkId& sink_id : module_manager.module_net_sinks(parent_module, module_net)) { + ModuleId sink_module = module_manager.net_sink_modules(parent_module, module_net)[sink_id]; + size_t sink_instance = module_manager.net_sink_instances(parent_module, module_net)[sink_id]; + + /* Skip when sink module is the parent module, + * the output ports of parent modules have been disabled/enabled already! + */ + if (sink_module == parent_module) { + continue; + } + + std::string sink_instance_name = module_manager.instance_name(parent_module, sink_module, sink_instance); + bool disable_timing = false; + /* Check if this node is used by benchmark */ + if (true == is_rr_node_to_be_disable_for_analysis(output_rr_node)) { + /* Disable all the sinks! */ + disable_timing = true; + } else { + std::map::const_iterator it = mux_instance_to_net_map.find(sink_instance_name); + if (it != mux_instance_to_net_map.end()) { + /* See if the net id matches. If does not match, we should disable! */ + if (output_rr_node->vpack_net_num != mux_instance_to_net_map.at(sink_instance_name)) { + disable_timing = true; + } + } + } + + /* Time to write SDC command to disable timing or not */ + if (false == disable_timing) { + continue; + } + + BasicPort sink_port = module_manager.module_port(sink_module, module_manager.net_sink_ports(parent_module, module_net)[sink_id]); + sink_port.set_width(module_manager.net_sink_pins(parent_module, module_net)[sink_id], + module_manager.net_sink_pins(parent_module, module_net)[sink_id]); + /* Get the input id that is used! Disable the unused inputs! */ + fp << "set_disable_timing "; + fp << parent_instance_name << "/"; + fp << sink_instance_name << "/"; + fp << generate_sdc_port(sink_port); + fp << std::endl; + } +} + diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.h b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.h new file mode 100644 index 000000000..7b5ff9348 --- /dev/null +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/backend_assistant/analysis_sdc_writer_utils.h @@ -0,0 +1,40 @@ +#ifndef ANALYSIS_SDC_WRITER_UTILS_H +#define ANALYSIS_SDC_WRITER_UTILS_H + +#include +#include +#include +#include "module_manager.h" +#include "vpr_types.h" + +bool is_rr_node_to_be_disable_for_analysis(t_rr_node* cur_rr_node); + +void disable_analysis_module_input_pin_net_sinks(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& parent_instance_name, + const ModulePortId& module_input_port, + const size_t& module_input_pin, + t_rr_node* input_rr_node, + const std::map mux_instance_to_net_map); + +void disable_analysis_module_input_port_net_sinks(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& parent_instance_name, + const ModulePortId& module_input_port, + t_rr_node* input_rr_node, + const std::map mux_instance_to_net_map) ; + +void disable_analysis_module_output_pin_net_sinks(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& parent_instance_name, + const ModuleId& child_module, + const size_t& child_instance, + const ModulePortId& child_module_port, + const size_t& child_module_pin, + t_rr_node* output_rr_node, + const std::map mux_instance_to_net_map); + +#endif diff --git a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_grid_modules.cpp b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_grid_modules.cpp index a05d6abf7..f6a940d85 100644 --- a/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_grid_modules.cpp +++ b/vpr7_x2p/vpr/SRC/fpga_x2p/module_builder/build_grid_modules.cpp @@ -483,7 +483,7 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager, } /* Initialize the interconnection type that will be physically implemented in module */ - enum e_interconnect verilog_interc_type = determine_actual_pb_interc_type(cur_interc, fan_in); + enum e_interconnect interc_type = determine_actual_pb_interc_type(cur_interc, fan_in); /* Find input ports of the wire module */ std::vector interc_model_inputs = circuit_lib.model_ports_by_type(cur_interc->circuit_model, SPICE_MODEL_PORT_INPUT, true); /* the last argument to guarantee that we ignore any global inputs */ @@ -497,7 +497,7 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager, /* Branch on the type of physical implementation, * We add instances of programmable interconnection */ - switch (verilog_interc_type) { + switch (interc_type) { case DIRECT_INTERC: { /* Ensure direct interc has only one fan-in */ VTR_ASSERT(1 == fan_in); @@ -521,7 +521,7 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager, /* Get the instance id and add an instance of wire */ size_t wire_instance = module_manager.num_instance(pb_module, wire_module); module_manager.add_child_module(pb_module, wire_module); - + /* Ensure input and output ports of the wire model has only 1 pin respectively */ VTR_ASSERT(1 == circuit_lib.port_size(interc_model_inputs[0])); VTR_ASSERT(1 == circuit_lib.port_size(interc_model_outputs[0]));