refactored analysis SDC generator for grids

This commit is contained in:
tangxifan 2019-11-12 22:18:13 -07:00
parent 6c58a4dd92
commit d84cd66287
6 changed files with 653 additions and 115 deletions

View File

@ -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<std::string, int> 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<size_t>& 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<size_t>& 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<std::vector<t_grid_tile>>& L_grids,
const std::vector<t_block>& 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) {

View File

@ -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<std::string, int> 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

View File

@ -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 */

View File

@ -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<std::string, int> 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<std::string, int>::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<std::string, int> 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<std::string, int> 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<std::string, int>::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;
}
}

View File

@ -0,0 +1,40 @@
#ifndef ANALYSIS_SDC_WRITER_UTILS_H
#define ANALYSIS_SDC_WRITER_UTILS_H
#include <fstream>
#include <string>
#include <map>
#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<std::string, int> 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<std::string, int> 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<std::string, int> mux_instance_to_net_map);
#endif

View File

@ -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<CircuitPortId> 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]));