Merge branch 'refactoring' into dev
This commit is contained in:
commit
9f13d3bc23
|
@ -59,8 +59,8 @@ class OpenfpgaContext : public Context {
|
|||
const openfpga::FlowManager& flow_manager() const { return flow_manager_; }
|
||||
const openfpga::BitstreamManager& bitstream_manager() const { return bitstream_manager_; }
|
||||
const std::vector<openfpga::ConfigBitId>& fabric_bitstream() const { return fabric_bitstream_; }
|
||||
const openfpga::IoLocationMap& io_location_map() { return io_location_map_; }
|
||||
const std::unordered_map<AtomNetId, t_net_power>& net_activity() { return net_activity_; }
|
||||
const openfpga::IoLocationMap& io_location_map() const { return io_location_map_; }
|
||||
const std::unordered_map<AtomNetId, t_net_power>& net_activity() const { return net_activity_; }
|
||||
public: /* Public mutators */
|
||||
openfpga::Arch& mutable_arch() { return arch_; }
|
||||
openfpga::VprDeviceAnnotation& mutable_vpr_device_annotation() { return vpr_device_annotation_; }
|
||||
|
|
|
@ -17,6 +17,19 @@
|
|||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/************************************************
|
||||
* A generic function to generate the instance name
|
||||
* in the following format:
|
||||
* <instance_name>_<id>_
|
||||
* This is mainly used by module manager to give a default
|
||||
* name for each instance when outputting the module
|
||||
* in Verilog/SPICE format
|
||||
***********************************************/
|
||||
std::string generate_instance_name(const std::string& instance_name,
|
||||
const size_t& instance_id) {
|
||||
return instance_name + std::string("_") + std::to_string(instance_id) + std::string("_");
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Generate the node name for a multiplexing structure
|
||||
* Case 1 : If there is an intermediate buffer followed by,
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
std::string generate_instance_name(const std::string& instance_name,
|
||||
const size_t& instance_id);
|
||||
|
||||
std::string generate_mux_node_name(const size_t& node_level,
|
||||
const bool& add_buffer_postfix);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "circuit_library_utils.h"
|
||||
#include "pnr_sdc_writer.h"
|
||||
#include "analysis_sdc_writer.h"
|
||||
#include "openfpga_sdc.h"
|
||||
|
||||
/* Include global variables of VPR */
|
||||
|
@ -77,4 +78,38 @@ void write_pnr_sdc(OpenfpgaContext& openfpga_ctx,
|
|||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A wrapper function to call the analysis SDC generator of FPGA-SDC
|
||||
*******************************************************************/
|
||||
void write_analysis_sdc(OpenfpgaContext& openfpga_ctx,
|
||||
const Command& cmd, const CommandContext& cmd_context) {
|
||||
|
||||
CommandOptionId opt_output_dir = cmd.option("file");
|
||||
|
||||
/* This is an intermediate data structure which is designed to modularize the FPGA-SDC
|
||||
* Keep it independent from any other outside data structures
|
||||
*/
|
||||
std::string sdc_dir_path = format_dir_path(cmd_context.option_value(cmd, opt_output_dir));
|
||||
|
||||
/* Create directories */
|
||||
create_dir_path(sdc_dir_path.c_str());
|
||||
|
||||
AnalysisSdcOption options(sdc_dir_path);
|
||||
options.set_generate_sdc_analysis(true);
|
||||
|
||||
/* Collect global ports from the circuit library:
|
||||
* TODO: should we place this in the OpenFPGA context?
|
||||
*/
|
||||
std::vector<CircuitPortId> global_ports = find_circuit_library_global_ports(openfpga_ctx.arch().circuit_lib);
|
||||
|
||||
if (true == options.generate_sdc_analysis()) {
|
||||
print_analysis_sdc(options,
|
||||
1./openfpga_ctx.arch().sim_setting.operating_clock_frequency(),
|
||||
g_vpr_ctx,
|
||||
openfpga_ctx,
|
||||
global_ports,
|
||||
openfpga_ctx.flow_manager().compress_routing());
|
||||
}
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
|
|
@ -18,6 +18,9 @@ namespace openfpga {
|
|||
void write_pnr_sdc(OpenfpgaContext& openfpga_ctx,
|
||||
const Command& cmd, const CommandContext& cmd_context);
|
||||
|
||||
void write_analysis_sdc(OpenfpgaContext& openfpga_ctx,
|
||||
const Command& cmd, const CommandContext& cmd_context);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -61,6 +61,36 @@ ShellCommandId add_openfpga_write_pnr_sdc_command(openfpga::Shell<OpenfpgaContex
|
|||
return shell_cmd_id;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* - Add a command to Shell environment: generate PnR SDC
|
||||
* - Add associated options
|
||||
* - Add command dependency
|
||||
*******************************************************************/
|
||||
static
|
||||
ShellCommandId add_openfpga_write_analysis_sdc_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||
const ShellCommandClassId& cmd_class_id,
|
||||
const std::vector<ShellCommandId>& dependent_cmds) {
|
||||
Command shell_cmd("write_analysis_sdc");
|
||||
|
||||
/* Add an option '--file' in short '-f'*/
|
||||
CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for SDC files");
|
||||
shell_cmd.set_option_short_name(output_opt, "f");
|
||||
shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--verbose' */
|
||||
shell_cmd.add_option("verbose", false, "Enable verbose output");
|
||||
|
||||
/* Add command 'write_fabric_verilog' to the Shell */
|
||||
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate SDC files for timing analysis a PnRed FPGA fabric mapped by a benchmark");
|
||||
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||
shell.set_command_execute_function(shell_cmd_id, write_analysis_sdc);
|
||||
|
||||
/* Add command dependency to the Shell */
|
||||
shell.set_command_dependency(shell_cmd_id, dependent_cmds);
|
||||
|
||||
return shell_cmd_id;
|
||||
}
|
||||
|
||||
void add_openfpga_sdc_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
||||
/* Get the unique id of 'build_fabric' command which is to be used in creating the dependency graph */
|
||||
const ShellCommandId& build_fabric_id = shell.command(std::string("build_fabric"));
|
||||
|
@ -77,6 +107,17 @@ void add_openfpga_sdc_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
|||
add_openfpga_write_pnr_sdc_command(shell,
|
||||
openfpga_sdc_cmd_class,
|
||||
pnr_sdc_cmd_dependency);
|
||||
|
||||
/********************************
|
||||
* Command 'write_analysis_sdc'
|
||||
*/
|
||||
/* The 'write_analysis_sdc' command should NOT be executed before 'build_fabric' */
|
||||
std::vector<ShellCommandId> analysis_sdc_cmd_dependency;
|
||||
analysis_sdc_cmd_dependency.push_back(build_fabric_id);
|
||||
add_openfpga_write_analysis_sdc_command(shell,
|
||||
openfpga_sdc_cmd_class,
|
||||
analysis_sdc_cmd_dependency);
|
||||
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
|
|
@ -428,6 +428,12 @@ void add_module_pb_graph_pin_interc(ModuleManager& module_manager,
|
|||
size_t wire_instance = module_manager.num_instance(pb_module, wire_module);
|
||||
module_manager.add_child_module(pb_module, wire_module);
|
||||
|
||||
/* Give an instance name: this name should be consistent with the block name given in SDC generator,
|
||||
* If you want to bind the SDC generation to modules
|
||||
*/
|
||||
std::string wire_instance_name = generate_instance_name(module_manager.module_name(wire_module), wire_instance);
|
||||
module_manager.set_child_instance_name(pb_module, wire_module, wire_instance, wire_instance_name);
|
||||
|
||||
/* 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]));
|
||||
|
|
|
@ -0,0 +1,651 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that are used to write SDC commands
|
||||
* to disable unused ports of grids, such as Configurable Logic Block
|
||||
* (CLBs), heterogeneous blocks, etc.
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
/* Headers from vprutil library */
|
||||
#include "vpr_utils.h"
|
||||
|
||||
#include "openfpga_reserved_words.h"
|
||||
#include "openfpga_naming.h"
|
||||
|
||||
#include "pb_type_utils.h"
|
||||
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "analysis_sdc_writer_utils.h"
|
||||
#include "analysis_sdc_grid_writer.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Recursively visit all the pb_types in the hierarchy
|
||||
* and disable all the ports
|
||||
*
|
||||
* 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_unused_pb_graph_nodes(std::fstream& fp,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& hierarchy_name,
|
||||
t_pb_graph_node* physical_pb_graph_node) {
|
||||
t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type;
|
||||
|
||||
/* Validate file stream */
|
||||
valid_file_stream(fp);
|
||||
|
||||
/* Disable all the ports of current module (parent_module)!
|
||||
* Hierarchy name already includes the instance name of parent_module
|
||||
*/
|
||||
fp << "#######################################" << std::endl;
|
||||
fp << "# Disable all the ports for pb_graph_node " << physical_pb_graph_node->pb_type->name << "[" << physical_pb_graph_node->placement_index << "]" << std::endl;
|
||||
fp << "#######################################" << std::endl;
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << hierarchy_name;
|
||||
fp << "*";
|
||||
fp << std::endl;
|
||||
|
||||
/* Return if this is the primitive pb_type */
|
||||
if (true == is_primitive_pb_type(physical_pb_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Go recursively */
|
||||
t_mode* physical_mode = device_annotation.physical_mode(physical_pb_type);
|
||||
|
||||
/* Disable all the ports by iterating over its instance in the parent module */
|
||||
for (int ichild = 0; ichild < physical_mode->num_pb_type_children; ++ichild) {
|
||||
/* Generate the name of the Verilog module for this child */
|
||||
std::string child_module_name = generate_physical_block_module_name(&(physical_mode->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_mode->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 + child_instance_name + std::string("/");
|
||||
|
||||
rec_print_analysis_sdc_disable_unused_pb_graph_nodes(fp, device_annotation, module_manager, child_module, updated_hierarchy_name,
|
||||
&(physical_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ichild][inst]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* 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,
|
||||
const PhysicalPb& physical_pb,
|
||||
const PhysicalPbId& pb_id) {
|
||||
/* Validate file stream */
|
||||
valid_file_stream(fp);
|
||||
|
||||
/* Identify if the pb_graph_pin has been used or not
|
||||
* TODO: identify if this is a parasitic net
|
||||
*/
|
||||
if (AtomNetId::INVALID() != physical_pb.pb_graph_pin_atom_net(pb_id, pb_graph_pin)) {
|
||||
/* 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 << 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,
|
||||
const PhysicalPb& physical_pb) {
|
||||
const PhysicalPbId& pb_id = physical_pb.find_pb(physical_pb_graph_node);
|
||||
VTR_ASSERT(true == physical_pb.valid_pb_id(pb_id));
|
||||
|
||||
fp << "#######################################" << std::endl;
|
||||
fp << "# Disable unused pins for pb_graph_node " << physical_pb_graph_node->pb_type->name << "[" << physical_pb_graph_node->placement_index << "]" << std::endl;
|
||||
fp << "#######################################" << std::endl;
|
||||
|
||||
/* 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]),
|
||||
physical_pb, pb_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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]),
|
||||
physical_pb, pb_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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]),
|
||||
physical_pb, pb_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* 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 VprDeviceAnnotation& device_annotation,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& hierarchy_name,
|
||||
t_pb_graph_node* physical_pb_graph_node,
|
||||
const PhysicalPb& physical_pb) {
|
||||
|
||||
fp << "#######################################" << std::endl;
|
||||
fp << "# Disable unused mux_inputs for pb_graph_node " << physical_pb_graph_node->pb_type->name << "[" << physical_pb_graph_node->placement_index << "]" << std::endl;
|
||||
fp << "#######################################" << std::endl;
|
||||
|
||||
t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type;
|
||||
|
||||
t_mode* physical_mode = device_annotation.physical_mode(physical_pb_type);
|
||||
|
||||
std::map<std::string, AtomNetId> mux_instance_to_net_map;
|
||||
|
||||
/* Cache the nets for each input pins of each child pb_graph_node */
|
||||
for (int ichild = 0; ichild < physical_mode->num_pb_type_children; ++ichild) {
|
||||
for (int inst = 0; inst < physical_mode->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) {
|
||||
const PhysicalPbId& pb_id = physical_pb.find_pb(child_pb_graph_node);
|
||||
VTR_ASSERT(true == physical_pb.valid_pb_id(pb_id));
|
||||
/* 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] = physical_pb.pb_graph_pin_atom_net(pb_id, &(child_pb_graph_node->input_pins[iport][ipin]));
|
||||
}
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
const PhysicalPbId& pb_id = physical_pb.find_pb(child_pb_graph_node);
|
||||
/* 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] = physical_pb.pb_graph_pin_atom_net(pb_id, &(child_pb_graph_node->clock_pins[iport][ipin]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
const PhysicalPbId& pb_id = physical_pb.find_pb(physical_pb_graph_node);
|
||||
/* 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] = physical_pb.pb_graph_pin_atom_net(pb_id, &(physical_pb_graph_node->output_pins[iport][ipin]));
|
||||
}
|
||||
}
|
||||
|
||||
/* 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));
|
||||
|
||||
const PhysicalPbId& pb_id = physical_pb.find_pb(physical_pb_graph_node);
|
||||
const AtomNetId& mapped_net = physical_pb.pb_graph_pin_atom_net(pb_id, &(physical_pb_graph_node->input_pins[iport][ipin]));
|
||||
|
||||
disable_analysis_module_input_pin_net_sinks(fp, module_manager, parent_module,
|
||||
hierarchy_name,
|
||||
module_port, ipin,
|
||||
mapped_net,
|
||||
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));
|
||||
|
||||
const PhysicalPbId& pb_id = physical_pb.find_pb(physical_pb_graph_node);
|
||||
const AtomNetId& mapped_net = physical_pb.pb_graph_pin_atom_net(pb_id, &(physical_pb_graph_node->clock_pins[iport][ipin]));
|
||||
|
||||
disable_analysis_module_input_pin_net_sinks(fp, module_manager, parent_module,
|
||||
hierarchy_name,
|
||||
module_port, ipin,
|
||||
mapped_net,
|
||||
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_mode->num_pb_type_children; ++ichild) {
|
||||
/* Generate the name of the Verilog module for this child */
|
||||
std::string child_module_name = generate_physical_block_module_name(&(physical_mode->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_mode->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));
|
||||
|
||||
const PhysicalPbId& pb_id = physical_pb.find_pb(child_pb_graph_node);
|
||||
const AtomNetId& mapped_net = physical_pb.pb_graph_pin_atom_net(pb_id, &(child_pb_graph_node->output_pins[iport][ipin]));
|
||||
|
||||
/* Corner case: if the pb_graph_pin has no fan-out we will skip this pin */
|
||||
if (0 == child_pb_graph_node->output_pins[iport][ipin].num_output_edges) {
|
||||
continue;
|
||||
}
|
||||
|
||||
disable_analysis_module_output_pin_net_sinks(fp, module_manager, parent_module,
|
||||
hierarchy_name,
|
||||
child_module, inst,
|
||||
module_port, ipin,
|
||||
mapped_net,
|
||||
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 VprDeviceAnnotation& device_annotation,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const std::string& hierarchy_name,
|
||||
t_pb_graph_node* physical_pb_graph_node,
|
||||
const PhysicalPb& physical_pb) {
|
||||
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, 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, device_annotation,
|
||||
module_manager, parent_module,
|
||||
hierarchy_name, physical_pb_graph_node,
|
||||
physical_pb);
|
||||
|
||||
|
||||
t_mode* physical_mode = device_annotation.physical_mode(physical_pb_type);
|
||||
|
||||
/* Disable all the ports by iterating over its instance in the parent module */
|
||||
for (int ichild = 0; ichild < physical_mode->num_pb_type_children; ++ichild) {
|
||||
/* Generate the name of the Verilog module for this child */
|
||||
std::string child_module_name = generate_physical_block_module_name(&(physical_mode->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 + child_instance_name + std::string("/");
|
||||
|
||||
rec_print_analysis_sdc_disable_pb_graph_node_unused_resources(fp, device_annotation,
|
||||
module_manager, child_module, updated_hierarchy_name,
|
||||
&(physical_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ichild][inst]),
|
||||
physical_pb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* 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_pb_block_unused_resources(std::fstream& fp,
|
||||
t_physical_tile_type_ptr grid_type,
|
||||
const vtr::Point<size_t>& grid_coordinate,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
const ModuleManager& module_manager,
|
||||
const std::string& grid_instance_name,
|
||||
const size_t& grid_z,
|
||||
const PhysicalPb& physical_pb,
|
||||
const bool& unused_block) {
|
||||
/* If the block is partially unused, we should have a physical pb */
|
||||
if (false == unused_block) {
|
||||
VTR_ASSERT(false == physical_pb.empty());
|
||||
}
|
||||
|
||||
VTR_ASSERT(1 == grid_type->equivalent_sites.size());
|
||||
t_pb_graph_node* pb_graph_head = grid_type->equivalent_sites[0]->pb_graph_head;
|
||||
VTR_ASSERT(nullptr != pb_graph_head);
|
||||
|
||||
/* 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()!!!
|
||||
*/
|
||||
/* TODO: validate that the instance name is used in module manager!!! */
|
||||
std::string pb_module_name = generate_physical_block_module_name(pb_graph_head->pb_type);
|
||||
std::string pb_instance_name = generate_physical_block_instance_name(pb_graph_head->pb_type, grid_z);
|
||||
|
||||
ModuleId pb_module = module_manager.find_module(pb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(pb_module));
|
||||
|
||||
/* Print comments */
|
||||
fp << "#######################################" << 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 */
|
||||
if (true == unused_block) {
|
||||
rec_print_analysis_sdc_disable_unused_pb_graph_nodes(fp, device_annotation,
|
||||
module_manager, pb_module, hierarchy_name,
|
||||
pb_graph_head);
|
||||
} else {
|
||||
VTR_ASSERT_SAFE(false == unused_block);
|
||||
rec_print_analysis_sdc_disable_pb_graph_node_unused_resources(fp, device_annotation,
|
||||
module_manager, pb_module, hierarchy_name,
|
||||
pb_graph_head, physical_pb);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* 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_grid(std::fstream& fp,
|
||||
const vtr::Point<size_t>& grid_coordinate,
|
||||
const DeviceGrid& grids,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
const VprClusteringAnnotation& cluster_annotation,
|
||||
const VprPlacementAnnotation& place_annotation,
|
||||
const ModuleManager& module_manager,
|
||||
const e_side& border_side) {
|
||||
/* Validate file stream */
|
||||
valid_file_stream(fp);
|
||||
|
||||
t_physical_tile_type_ptr grid_type = grids[grid_coordinate.x()][grid_coordinate.y()].type;
|
||||
/* Bypass conditions for grids :
|
||||
* 1. EMPTY type, which is by nature unused
|
||||
* 2. Offset > 0, which has already been processed when offset = 0
|
||||
*/
|
||||
if ( (true == is_empty_type(grid_type))
|
||||
|| (0 < grids[grid_coordinate.x()][grid_coordinate.y()].width_offset)
|
||||
|| (0 < grids[grid_coordinate.x()][grid_coordinate.y()].height_offset) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find an unique name to the grid instane
|
||||
* Note: this must be consistent with the instance name we used in build_top_module()!!!
|
||||
*/
|
||||
/* TODO: validate that the instance name is used in module manager!!! */
|
||||
std::string grid_module_name_prefix(GRID_MODULE_NAME_PREFIX);
|
||||
std::string grid_module_name = generate_grid_block_module_name(grid_module_name_prefix, std::string(grid_type->name), is_io_type(grid_type), border_side);
|
||||
std::string grid_instance_name = generate_grid_block_instance_name(grid_module_name_prefix, std::string(grid_type->name), is_io_type(grid_type), border_side, grid_coordinate);
|
||||
|
||||
ModuleId grid_module = module_manager.find_module(grid_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(grid_module));
|
||||
|
||||
/* Print comments */
|
||||
fp << "#######################################" << std::endl;
|
||||
fp << "# Disable Timing for grid[" << grid_coordinate.x() << "][" << grid_coordinate.y() << "]" << std::endl;
|
||||
fp << "#######################################" << std::endl;
|
||||
|
||||
/* For used grid, find the unused rr_node in the local rr_graph
|
||||
* and then disable each port which is not used
|
||||
* as well as the unused inputs of routing multiplexers!
|
||||
*/
|
||||
size_t grid_z = 0;
|
||||
for (const ClusterBlockId& blk_id : place_annotation.grid_blocks(grid_coordinate)) {
|
||||
if (ClusterBlockId::INVALID() != blk_id) {
|
||||
const PhysicalPb& physical_pb = cluster_annotation.physical_pb(blk_id);
|
||||
print_analysis_sdc_disable_pb_block_unused_resources(fp, grid_type, grid_coordinate,
|
||||
device_annotation,
|
||||
module_manager, grid_instance_name, grid_z,
|
||||
physical_pb, false);
|
||||
} else {
|
||||
VTR_ASSERT(ClusterBlockId::INVALID() == blk_id);
|
||||
/* For unused grid, disable all the pins in the physical_pb_type */
|
||||
print_analysis_sdc_disable_pb_block_unused_resources(fp, grid_type, grid_coordinate,
|
||||
device_annotation,
|
||||
module_manager, grid_instance_name, grid_z,
|
||||
PhysicalPb(), true);
|
||||
}
|
||||
grid_z++;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function writes SDC commands to disable unused ports
|
||||
* of grids, such as Configurable Logic Block (CLBs), heterogeneous blocks, etc.
|
||||
*
|
||||
* This function will iterate over all the grids available in the FPGA fabric
|
||||
* It will disable the timing analysis for
|
||||
* 1. Grids, which are totally not used (no logic has been mapped to)
|
||||
* 2. Unused part of grids, including the ports, inputs of routing multiplexers
|
||||
*
|
||||
* Note that it is a must to disable the unused inputs of routing multiplexers
|
||||
* because it will cause unexpected paths in timing analysis
|
||||
* For example:
|
||||
* +---------------------+
|
||||
* inputA (net0) ------->| |
|
||||
* | Routing multiplexer |----> output (net0)
|
||||
* inputB (net1) ------->| |
|
||||
* +---------------------+
|
||||
*
|
||||
* During timing analysis, the path from inputA to output should be considered
|
||||
* while the path from inputB to output should NOT be considered!!!
|
||||
*
|
||||
*******************************************************************/
|
||||
void print_analysis_sdc_disable_unused_grids(std::fstream& fp,
|
||||
const DeviceGrid& grids,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
const VprClusteringAnnotation& cluster_annotation,
|
||||
const VprPlacementAnnotation& place_annotation,
|
||||
const ModuleManager& module_manager) {
|
||||
|
||||
/* Process unused core grids */
|
||||
for (size_t ix = 1; ix < grids.width() - 1; ++ix) {
|
||||
for (size_t iy = 1; iy < grids.height() - 1; ++iy) {
|
||||
/* We should not meet any I/O grid */
|
||||
VTR_ASSERT(false == is_io_type(grids[ix][iy].type));
|
||||
|
||||
print_analysis_sdc_disable_unused_grid(fp, vtr::Point<size_t>(ix, iy),
|
||||
grids, device_annotation, cluster_annotation, place_annotation,
|
||||
module_manager, NUM_SIDES);
|
||||
}
|
||||
}
|
||||
|
||||
/* Instanciate I/O grids */
|
||||
/* Create the coordinate range for each side of FPGA fabric */
|
||||
std::vector<e_side> io_sides{TOP, RIGHT, BOTTOM, LEFT};
|
||||
std::map<e_side, std::vector<vtr::Point<size_t>>> io_coordinates;
|
||||
|
||||
/* TOP side*/
|
||||
for (size_t ix = 1; ix < grids.width() - 1; ++ix) {
|
||||
io_coordinates[TOP].push_back(vtr::Point<size_t>(ix, grids.height() - 1));
|
||||
}
|
||||
|
||||
/* RIGHT side */
|
||||
for (size_t iy = 1; iy < grids.height() - 1; ++iy) {
|
||||
io_coordinates[RIGHT].push_back(vtr::Point<size_t>(grids.width() - 1, iy));
|
||||
}
|
||||
|
||||
/* BOTTOM side*/
|
||||
for (size_t ix = 1; ix < grids.width() - 1; ++ix) {
|
||||
io_coordinates[BOTTOM].push_back(vtr::Point<size_t>(ix, 0));
|
||||
}
|
||||
|
||||
/* LEFT side */
|
||||
for (size_t iy = 1; iy < grids.height() - 1; ++iy) {
|
||||
io_coordinates[LEFT].push_back(vtr::Point<size_t>(0, iy));
|
||||
}
|
||||
|
||||
/* Add instances of I/O grids to top_module */
|
||||
for (const e_side& io_side : io_sides) {
|
||||
for (const vtr::Point<size_t>& io_coordinate : io_coordinates[io_side]) {
|
||||
/* We should not meet any I/O grid */
|
||||
VTR_ASSERT(true == is_io_type(grids[io_coordinate.x()][io_coordinate.y()].type));
|
||||
|
||||
print_analysis_sdc_disable_unused_grid(fp, io_coordinate,
|
||||
grids, device_annotation, cluster_annotation, place_annotation,
|
||||
module_manager, io_side);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef ANALYSIS_SDC_GRID_WRITER_H
|
||||
#define ANALYSIS_SDC_GRID_WRITER_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include "device_grid.h"
|
||||
#include "module_manager.h"
|
||||
#include "vpr_device_annotation.h"
|
||||
#include "vpr_clustering_annotation.h"
|
||||
#include "vpr_placement_annotation.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_analysis_sdc_disable_unused_grids(std::fstream& fp,
|
||||
const DeviceGrid& grids,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
const VprClusteringAnnotation& cluster_annotation,
|
||||
const VprPlacementAnnotation& place_annotation,
|
||||
const ModuleManager& module_manager);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,540 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that are used to output a SDC file
|
||||
* that constrain routing modules of a FPGA fabric (P&Red netlist)
|
||||
* using a benchmark
|
||||
*******************************************************************/
|
||||
#include <map>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
#include "openfpga_side_manager.h"
|
||||
#include "openfpga_port.h"
|
||||
|
||||
#include "openfpga_reserved_words.h"
|
||||
#include "openfpga_naming.h"
|
||||
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "analysis_sdc_writer_utils.h"
|
||||
#include "analysis_sdc_routing_writer.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* This function will disable
|
||||
* 1. all the unused port (unmapped by a benchmark) of a connection block
|
||||
* 2. all the unused inputs (unmapped by a benchmark) of routing multiplexers
|
||||
* in a connection block
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_disable_cb_unused_resources(std::fstream& fp,
|
||||
const AtomContext& atom_ctx,
|
||||
const ModuleManager& module_manager,
|
||||
const RRGraph& rr_graph,
|
||||
const VprRoutingAnnotation& routing_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
/* Validate file stream */
|
||||
valid_file_stream(fp);
|
||||
|
||||
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
|
||||
std::string cb_instance_name = generate_connection_block_module_name(cb_type, gsb_coordinate);
|
||||
|
||||
/* If we use the compact routing hierarchy, we need to find the module name !*/
|
||||
vtr::Point<size_t> cb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
if (true == compact_routing_hierarchy) {
|
||||
vtr::Point<size_t> cb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(cb_type, cb_coord);
|
||||
cb_coordinate.set_x(unique_mirror.get_cb_x(cb_type));
|
||||
cb_coordinate.set_y(unique_mirror.get_cb_y(cb_type));
|
||||
}
|
||||
|
||||
std::string cb_module_name = generate_connection_block_module_name(cb_type, cb_coordinate);
|
||||
|
||||
ModuleId cb_module = module_manager.find_module(cb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(cb_module));
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Disable timing for Connection block " << cb_module_name << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
/* Disable all the input port (routing tracks), which are not used by benchmark */
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) {
|
||||
const RRNodeId& chan_node = rr_gsb.get_chan_node(rr_gsb.get_cb_chan_side(cb_type), itrack);
|
||||
/* Check if this node is used by benchmark */
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(routing_annotation, chan_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Disable both input of the routing track if it is not used! */
|
||||
std::string port_name = generate_cb_module_track_port_name(cb_type,
|
||||
itrack,
|
||||
IN_PORT);
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(cb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, module_port));
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << cb_instance_name << "/";
|
||||
fp << generate_sdc_port(module_manager.module_port(cb_module, module_port));
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/* Disable all the output port (routing tracks), which are not used by benchmark */
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) {
|
||||
const RRNodeId& chan_node = rr_gsb.get_chan_node(rr_gsb.get_cb_chan_side(cb_type), itrack);
|
||||
/* Check if this node is used by benchmark */
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(routing_annotation, chan_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Disable both input of the routing track if it is not used! */
|
||||
std::string port_name = generate_cb_module_track_port_name(cb_type,
|
||||
itrack,
|
||||
OUT_PORT);
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(cb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, module_port));
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << cb_instance_name << "/";
|
||||
fp << generate_sdc_port(module_manager.module_port(cb_module, module_port));
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/* Build a map between mux_instance name and net_num */
|
||||
std::map<std::string, AtomNetId> mux_instance_to_net_map;
|
||||
|
||||
/* Disable all the output port (grid input pins), which are not used by benchmark */
|
||||
std::vector<enum e_side> cb_sides = rr_gsb.get_cb_ipin_sides(cb_type);
|
||||
|
||||
for (size_t side = 0; side < cb_sides.size(); ++side) {
|
||||
enum e_side cb_ipin_side = cb_sides[side];
|
||||
for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) {
|
||||
RRNodeId ipin_node = rr_gsb.get_ipin_node(cb_ipin_side, inode);
|
||||
|
||||
/* Find the MUX instance that drives the IPIN! */
|
||||
std::string mux_instance_name = generate_cb_mux_instance_name(CONNECTION_BLOCK_MUX_INSTANCE_PREFIX, rr_graph.node_side(ipin_node), inode, std::string(""));
|
||||
mux_instance_to_net_map[mux_instance_name] = atom_ctx.lookup.atom_net(routing_annotation.rr_node_net(ipin_node));
|
||||
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(routing_annotation, ipin_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 == std::distance(rr_graph.node_configurable_in_edges(ipin_node).begin(), rr_graph.node_configurable_in_edges(ipin_node).end())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string port_name = generate_cb_module_grid_port_name(cb_ipin_side,
|
||||
rr_graph.node_pin_num(ipin_node));
|
||||
|
||||
/* Find the port in unique mirror! */
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
vtr::Point<size_t> cb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(cb_type, cb_coord);
|
||||
const RRNodeId& unique_mirror_ipin_node = unique_mirror.get_ipin_node(cb_ipin_side, inode);
|
||||
port_name = generate_cb_module_grid_port_name(cb_ipin_side,
|
||||
rr_graph.node_pin_num(unique_mirror_ipin_node));
|
||||
}
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(cb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, module_port));
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << cb_instance_name << "/";
|
||||
fp << generate_sdc_port(module_manager.module_port(cb_module, module_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 the Connection Blocks, 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!
|
||||
*
|
||||
* cb_module
|
||||
* +-----------------------
|
||||
* | MUX instance A
|
||||
* | +-----------
|
||||
* input_port--->|--+---x-->| sink port (disable!)
|
||||
* | | +----------
|
||||
* | | MUX instance B
|
||||
* | | +----------
|
||||
* | +------>| sink port (do not disable!)
|
||||
*/
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) {
|
||||
const RRNodeId& chan_node = rr_gsb.get_chan_node(rr_gsb.get_cb_chan_side(cb_type), itrack);
|
||||
|
||||
/* Disable both input of the routing track if it is not used! */
|
||||
std::string port_name = generate_cb_module_track_port_name(cb_type,
|
||||
itrack,
|
||||
OUT_PORT);
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(cb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(cb_module, module_port));
|
||||
|
||||
AtomNetId mapped_atom_net = atom_ctx.lookup.atom_net(routing_annotation.rr_node_net(chan_node));
|
||||
|
||||
disable_analysis_module_input_port_net_sinks(fp,
|
||||
module_manager, cb_module,
|
||||
cb_instance_name,
|
||||
module_port,
|
||||
mapped_atom_net,
|
||||
mux_instance_to_net_map);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Iterate over all the connection blocks in a device
|
||||
* and disable unused ports for each of them
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_disable_unused_cb_ports(std::fstream& fp,
|
||||
const AtomContext& atom_ctx,
|
||||
const ModuleManager& module_manager,
|
||||
const RRGraph& rr_graph,
|
||||
const VprRoutingAnnotation& routing_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
/* Build unique X-direction connection block modules */
|
||||
vtr::Point<size_t> cb_range = device_rr_gsb.get_gsb_range();
|
||||
|
||||
for (size_t ix = 0; ix < cb_range.x(); ++ix) {
|
||||
for (size_t iy = 0; iy < cb_range.y(); ++iy) {
|
||||
/* Check if the connection block exists in the device!
|
||||
* Some of them do NOT exist due to heterogeneous blocks (height > 1)
|
||||
* We will skip those modules
|
||||
*/
|
||||
const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy);
|
||||
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
print_analysis_sdc_disable_cb_unused_resources(fp,
|
||||
atom_ctx,
|
||||
module_manager,
|
||||
rr_graph,
|
||||
routing_annotation,
|
||||
device_rr_gsb,
|
||||
rr_gsb,
|
||||
cb_type,
|
||||
compact_routing_hierarchy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Iterate over all the connection blocks in a device
|
||||
* and disable unused ports for each of them
|
||||
*******************************************************************/
|
||||
void print_analysis_sdc_disable_unused_cbs(std::fstream& fp,
|
||||
const AtomContext& atom_ctx,
|
||||
const ModuleManager& module_manager,
|
||||
const RRGraph& rr_graph,
|
||||
const VprRoutingAnnotation& routing_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
|
||||
print_analysis_sdc_disable_unused_cb_ports(fp, atom_ctx,
|
||||
module_manager,
|
||||
rr_graph,
|
||||
routing_annotation,
|
||||
device_rr_gsb,
|
||||
CHANX, compact_routing_hierarchy);
|
||||
|
||||
print_analysis_sdc_disable_unused_cb_ports(fp, atom_ctx,
|
||||
module_manager,
|
||||
rr_graph,
|
||||
routing_annotation,
|
||||
device_rr_gsb,
|
||||
CHANY, compact_routing_hierarchy);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* This function will disable
|
||||
* 1. all the unused port (unmapped by a benchmark) of a switch block
|
||||
* 2. all the unused inputs (unmapped by a benchmark) of routing multiplexers
|
||||
* in a switch block
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_disable_sb_unused_resources(std::fstream& fp,
|
||||
const AtomContext& atom_ctx,
|
||||
const ModuleManager& module_manager,
|
||||
const RRGraph& rr_graph,
|
||||
const VprRoutingAnnotation& routing_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const RRGSB& rr_gsb,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
/* Validate file stream */
|
||||
valid_file_stream(fp);
|
||||
|
||||
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
|
||||
std::string sb_instance_name = generate_switch_block_module_name(gsb_coordinate);
|
||||
|
||||
/* If we use the compact routing hierarchy, we need to find the module name !*/
|
||||
vtr::Point<size_t> sb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
if (true == compact_routing_hierarchy) {
|
||||
vtr::Point<size_t> sb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(sb_coord);
|
||||
sb_coordinate.set_x(unique_mirror.get_sb_x());
|
||||
sb_coordinate.set_y(unique_mirror.get_sb_y());
|
||||
}
|
||||
|
||||
std::string sb_module_name = generate_switch_block_module_name(sb_coordinate);
|
||||
|
||||
ModuleId sb_module = module_manager.find_module(sb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Disable timing for Switch block " << sb_module_name << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
/* Build a map between mux_instance name and net_num */
|
||||
std::map<std::string, AtomNetId> mux_instance_to_net_map;
|
||||
|
||||
/* Disable all the input/output port (routing tracks), which are not used by benchmark */
|
||||
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
||||
SideManager side_manager(side);
|
||||
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) {
|
||||
const RRNodeId& chan_node = rr_gsb.get_chan_node(side_manager.get_side(), itrack);
|
||||
|
||||
std::string port_name = generate_sb_module_track_port_name(rr_graph.node_type(rr_gsb.get_chan_node(side_manager.get_side(), itrack)),
|
||||
side_manager.get_side(), itrack,
|
||||
rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack));
|
||||
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
vtr::Point<size_t> sb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(sb_coord);
|
||||
port_name = generate_sb_module_track_port_name(rr_graph.node_type(unique_mirror.get_chan_node(side_manager.get_side(), itrack)),
|
||||
side_manager.get_side(), itrack,
|
||||
unique_mirror.get_chan_node_direction(side_manager.get_side(), itrack));
|
||||
}
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(sb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, module_port));
|
||||
|
||||
/* Cache the net name for routing tracks which are outputs of the switch block */
|
||||
if (OUT_PORT == rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) {
|
||||
/* Generate the name of mux instance related to this output node */
|
||||
std::string mux_instance_name = generate_sb_memory_instance_name(SWITCH_BLOCK_MUX_INSTANCE_PREFIX, side_manager.get_side(), itrack, std::string(""));
|
||||
mux_instance_to_net_map[mux_instance_name] = atom_ctx.lookup.atom_net(routing_annotation.rr_node_net(chan_node));
|
||||
}
|
||||
|
||||
/* Check if this node is used by benchmark */
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(routing_annotation, chan_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << sb_instance_name << "/";
|
||||
fp << generate_sdc_port(module_manager.module_port(sb_module, module_port));
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable all the input port (grid output pins), which are not used by benchmark */
|
||||
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
||||
SideManager side_manager(side);
|
||||
|
||||
for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(side_manager.get_side()); ++inode) {
|
||||
const RRNodeId& opin_node = rr_gsb.get_opin_node(side_manager.get_side(), inode);
|
||||
|
||||
std::string port_name = generate_sb_module_grid_port_name(side_manager.get_side(),
|
||||
rr_graph.node_side(opin_node),
|
||||
rr_graph.node_pin_num(opin_node));
|
||||
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
vtr::Point<size_t> sb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(sb_coord);
|
||||
const RRNodeId& unique_mirror_opin_node = unique_mirror.get_opin_node(side_manager.get_side(), inode);
|
||||
|
||||
port_name = generate_sb_module_grid_port_name(side_manager.get_side(),
|
||||
rr_graph.node_side(unique_mirror_opin_node),
|
||||
rr_graph.node_pin_num(unique_mirror_opin_node));
|
||||
}
|
||||
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(sb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, module_port));
|
||||
|
||||
/* Check if this node is used by benchmark */
|
||||
if (false == is_rr_node_to_be_disable_for_analysis(routing_annotation, opin_node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fp << "set_disable_timing ";
|
||||
fp << sb_instance_name << "/";
|
||||
fp << generate_sdc_port(module_manager.module_port(sb_module, module_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 the Switch Blocks, 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!
|
||||
*
|
||||
* sb_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)
|
||||
*
|
||||
* Because the input ports of a SB module come from
|
||||
* 1. Grid output pins
|
||||
* 2. routing tracks
|
||||
* We will walk through these ports and do conditionally disable_timing
|
||||
*/
|
||||
|
||||
/* Iterate over input ports coming from grid output pins */
|
||||
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
||||
SideManager side_manager(side);
|
||||
|
||||
for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(side_manager.get_side()); ++inode) {
|
||||
const RRNodeId& opin_node = rr_gsb.get_opin_node(side_manager.get_side(), inode);
|
||||
|
||||
std::string port_name = generate_sb_module_grid_port_name(side_manager.get_side(),
|
||||
rr_graph.node_side(opin_node),
|
||||
rr_graph.node_pin_num(opin_node));
|
||||
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
vtr::Point<size_t> sb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(sb_coord);
|
||||
const RRNodeId& unique_mirror_opin_node = unique_mirror.get_opin_node(side_manager.get_side(), inode);
|
||||
|
||||
port_name = generate_sb_module_grid_port_name(side_manager.get_side(),
|
||||
rr_graph.node_side(unique_mirror_opin_node),
|
||||
rr_graph.node_pin_num(unique_mirror_opin_node));
|
||||
}
|
||||
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(sb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, module_port));
|
||||
|
||||
AtomNetId mapped_atom_net = atom_ctx.lookup.atom_net(routing_annotation.rr_node_net(opin_node));
|
||||
|
||||
disable_analysis_module_input_port_net_sinks(fp, module_manager,
|
||||
sb_module,
|
||||
sb_instance_name,
|
||||
module_port,
|
||||
mapped_atom_net,
|
||||
mux_instance_to_net_map);
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate over input ports coming from routing tracks */
|
||||
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
||||
SideManager side_manager(side);
|
||||
|
||||
for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) {
|
||||
/* Skip output ports, they have already been disabled or not */
|
||||
if (OUT_PORT == rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const RRNodeId& chan_node = rr_gsb.get_chan_node(side_manager.get_side(), itrack);
|
||||
|
||||
std::string port_name = generate_sb_module_track_port_name(rr_graph.node_type(chan_node),
|
||||
side_manager.get_side(), itrack,
|
||||
rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack));
|
||||
|
||||
if (true == compact_routing_hierarchy) {
|
||||
/* Note: use GSB coordinate when inquire for unique modules!!! */
|
||||
vtr::Point<size_t> sb_coord(rr_gsb.get_x(), rr_gsb.get_y());
|
||||
const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(sb_coord);
|
||||
const RRNodeId& unique_mirror_chan_node = unique_mirror.get_chan_node(side_manager.get_side(), itrack);
|
||||
|
||||
port_name = generate_sb_module_track_port_name(rr_graph.node_type(unique_mirror_chan_node),
|
||||
side_manager.get_side(), itrack,
|
||||
unique_mirror.get_chan_node_direction(side_manager.get_side(), itrack));
|
||||
}
|
||||
|
||||
|
||||
/* Ensure we have this port in the module! */
|
||||
ModulePortId module_port = module_manager.find_module_port(sb_module, port_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_port_id(sb_module, module_port));
|
||||
|
||||
AtomNetId mapped_atom_net = atom_ctx.lookup.atom_net(routing_annotation.rr_node_net(chan_node));
|
||||
|
||||
disable_analysis_module_input_port_net_sinks(fp, module_manager,
|
||||
sb_module,
|
||||
sb_instance_name,
|
||||
module_port,
|
||||
mapped_atom_net,
|
||||
mux_instance_to_net_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Iterate over all the connection blocks in a device
|
||||
* and disable unused ports for each of them
|
||||
*******************************************************************/
|
||||
void print_analysis_sdc_disable_unused_sbs(std::fstream& fp,
|
||||
const AtomContext& atom_ctx,
|
||||
const ModuleManager& module_manager,
|
||||
const RRGraph& rr_graph,
|
||||
const VprRoutingAnnotation& routing_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
|
||||
/* Build unique X-direction connection block modules */
|
||||
vtr::Point<size_t> sb_range = device_rr_gsb.get_gsb_range();
|
||||
|
||||
for (size_t ix = 0; ix < sb_range.x(); ++ix) {
|
||||
for (size_t iy = 0; iy < sb_range.y(); ++iy) {
|
||||
/* Check if the connection block exists in the device!
|
||||
* Some of them do NOT exist due to heterogeneous blocks (height > 1)
|
||||
* We will skip those modules
|
||||
*/
|
||||
const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy);
|
||||
if (false == rr_gsb.is_sb_exist()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
print_analysis_sdc_disable_sb_unused_resources(fp,
|
||||
atom_ctx,
|
||||
module_manager,
|
||||
rr_graph,
|
||||
routing_annotation,
|
||||
device_rr_gsb,
|
||||
rr_gsb,
|
||||
compact_routing_hierarchy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef ANALYSIS_SDC_ROUTING_WRITER_H
|
||||
#define ANALYSIS_SDC_ROUTING_WRITER_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include "vpr_context.h"
|
||||
#include "module_manager.h"
|
||||
#include "device_rr_gsb.h"
|
||||
#include "vpr_routing_annotation.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_analysis_sdc_disable_unused_cbs(std::fstream& fp,
|
||||
const AtomContext& atom_ctx,
|
||||
const ModuleManager& module_manager,
|
||||
const RRGraph& rr_graph,
|
||||
const VprRoutingAnnotation& routing_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const bool& compact_routing_hierarchy);
|
||||
|
||||
void print_analysis_sdc_disable_unused_sbs(std::fstream& fp,
|
||||
const AtomContext& atom_ctx,
|
||||
const ModuleManager& module_manager,
|
||||
const RRGraph& rr_graph,
|
||||
const VprRoutingAnnotation& routing_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const bool& compact_routing_hierarchy);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,284 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that are used to output a SDC file
|
||||
* that constrain a FPGA fabric (P&Red netlist) using a benchmark
|
||||
*******************************************************************/
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
#include "openfpga_port.h"
|
||||
|
||||
#include "mux_utils.h"
|
||||
|
||||
#include "openfpga_naming.h"
|
||||
#include "openfpga_atom_netlist_utils.h"
|
||||
|
||||
#include "sdc_writer_naming.h"
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "sdc_memory_utils.h"
|
||||
|
||||
#include "analysis_sdc_grid_writer.h"
|
||||
#include "analysis_sdc_routing_writer.h"
|
||||
#include "analysis_sdc_writer.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Generate SDC constaints for inputs and outputs
|
||||
* We consider the top module in formal verification purpose here
|
||||
* which is easier
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_io_delays(std::fstream& fp,
|
||||
const AtomContext& atom_ctx,
|
||||
const PlacementContext& place_ctx,
|
||||
const VprNetlistAnnotation& netlist_annotation,
|
||||
const IoLocationMap& io_location_map,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& top_module,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports,
|
||||
const float& critical_path_delay) {
|
||||
/* Validate the file stream */
|
||||
valid_file_stream(fp);
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Create clock " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
/* Get clock port from the global port */
|
||||
std::vector<BasicPort> operating_clock_ports;
|
||||
for (const CircuitPortId& clock_port : global_ports) {
|
||||
if (CIRCUIT_MODEL_PORT_CLOCK != circuit_lib.port_type(clock_port)) {
|
||||
continue;
|
||||
}
|
||||
/* We only constrain operating clock here! */
|
||||
if (true == circuit_lib.port_is_prog(clock_port)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find the module port and Update the operating port list */
|
||||
ModulePortId module_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(clock_port));
|
||||
operating_clock_ports.push_back(module_manager.module_port(top_module, module_port));
|
||||
}
|
||||
|
||||
for (const BasicPort& operating_clock_port : operating_clock_ports) {
|
||||
/* Reach here, it means a clock port and we need print constraints */
|
||||
fp << "create_clock ";
|
||||
fp << generate_sdc_port(operating_clock_port);
|
||||
fp << " -period " << std::setprecision(10) << critical_path_delay;
|
||||
fp << " -waveform {0 " << std::setprecision(10) << critical_path_delay / 2 << "}";
|
||||
fp << std::endl;
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/* There should be only one operating clock!
|
||||
* TODO: this should be changed when developing multi-clock support!!!
|
||||
*/
|
||||
VTR_ASSERT(1 == operating_clock_ports.size());
|
||||
|
||||
/* In this function, we support only 1 type of I/Os */
|
||||
VTR_ASSERT(1 == module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT).size());
|
||||
BasicPort module_io_port = module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT)[0];
|
||||
|
||||
/* Keep tracking which I/Os have been used */
|
||||
std::vector<bool> io_used(module_io_port.get_width(), false);
|
||||
|
||||
/* Find clock ports in benchmark */
|
||||
std::vector<std::string> benchmark_clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation);
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Create input and output delays for used I/Os " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||
/* Bypass non-I/O atom blocks ! */
|
||||
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
|
||||
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* clock net or constant generator should be disabled in timing analysis */
|
||||
if (benchmark_clock_port_names.end() != std::find(benchmark_clock_port_names.begin(), benchmark_clock_port_names.end(), atom_ctx.nlist.block_name(atom_blk))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find the index of the mapped GPIO in top-level FPGA fabric */
|
||||
size_t io_index = io_location_map.io_index(place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.x,
|
||||
place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.y,
|
||||
place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.z);
|
||||
|
||||
/* Ensure that IO index is in range */
|
||||
BasicPort module_mapped_io_port = module_io_port;
|
||||
/* Set the port pin index */
|
||||
VTR_ASSERT(io_index < module_mapped_io_port.get_width());
|
||||
module_mapped_io_port.set_width(io_index, io_index);
|
||||
|
||||
/* For input I/O, we set an input delay constraint correlated to the operating clock
|
||||
* For output I/O, we set an output delay constraint correlated to the operating clock
|
||||
*/
|
||||
if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) {
|
||||
print_sdc_set_port_input_delay(fp, module_mapped_io_port,
|
||||
operating_clock_ports[0], critical_path_delay);
|
||||
} else {
|
||||
VTR_ASSERT(AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk));
|
||||
print_sdc_set_port_output_delay(fp, module_mapped_io_port,
|
||||
operating_clock_ports[0], critical_path_delay);
|
||||
}
|
||||
|
||||
/* Mark this I/O has been used/wired */
|
||||
io_used[io_index] = true;
|
||||
}
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Disable timing for unused I/Os " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
/* Wire the unused iopads to a constant */
|
||||
for (size_t io_index = 0; io_index < io_used.size(); ++io_index) {
|
||||
/* Bypass used iopads */
|
||||
if (true == io_used[io_index]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Wire to a contant */
|
||||
BasicPort module_unused_io_port = module_io_port;
|
||||
/* Set the port pin index */
|
||||
module_unused_io_port.set_width(io_index, io_index);
|
||||
print_sdc_disable_port_timing(fp, module_unused_io_port);
|
||||
}
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Disable the timing for all the global port except the operating clock ports
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_analysis_sdc_disable_global_ports(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& top_module,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::vector<CircuitPortId>& global_ports) {
|
||||
/* Validate file stream */
|
||||
valid_file_stream(fp);
|
||||
|
||||
/* Print comments */
|
||||
fp << "##################################################" << std::endl;
|
||||
fp << "# Disable timing for global ports " << std::endl;
|
||||
fp << "##################################################" << std::endl;
|
||||
|
||||
for (const CircuitPortId& global_port : global_ports) {
|
||||
/* Skip operating clock here! */
|
||||
if ( (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(global_port))
|
||||
&& (false == circuit_lib.port_is_prog(global_port)) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ModulePortId module_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(global_port));
|
||||
BasicPort port_to_disable = module_manager.module_port(top_module, module_port);
|
||||
|
||||
print_sdc_disable_port_timing(fp, port_to_disable);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function outputs a SDC file
|
||||
* that constrain a FPGA fabric (P&Red netlist) using a benchmark
|
||||
*******************************************************************/
|
||||
void print_analysis_sdc(const AnalysisSdcOption& option,
|
||||
const float& critical_path_delay,
|
||||
const VprContext& vpr_ctx,
|
||||
const OpenfpgaContext& openfpga_ctx,
|
||||
const std::vector<CircuitPortId>& global_ports,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
/* Create the file name for Verilog netlist */
|
||||
std::string sdc_fname(option.sdc_dir() + std::string(SDC_ANALYSIS_FILE_NAME));
|
||||
|
||||
std::string timer_message = std::string("Generating SDC for Timing/Power analysis on the mapped FPGA '")
|
||||
+ sdc_fname
|
||||
+ std::string("'");
|
||||
|
||||
/* Start time count */
|
||||
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* Validate file stream */
|
||||
check_file_stream(sdc_fname.c_str(), fp);
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_sdc_file_header(fp, std::string("Constrain for Timing/Power analysis on the mapped FPGA"));
|
||||
|
||||
/* Find the top_module */
|
||||
ModuleId top_module = openfpga_ctx.module_graph().find_module(generate_fpga_top_module_name());
|
||||
VTR_ASSERT(true == openfpga_ctx.module_graph().valid_module_id(top_module));
|
||||
|
||||
/* Create clock and set I/O ports with input/output delays */
|
||||
print_analysis_sdc_io_delays(fp,
|
||||
vpr_ctx.atom(), vpr_ctx.placement(),
|
||||
openfpga_ctx.vpr_netlist_annotation(), openfpga_ctx.io_location_map(),
|
||||
openfpga_ctx.module_graph(), top_module,
|
||||
openfpga_ctx.arch().circuit_lib, global_ports,
|
||||
critical_path_delay);
|
||||
|
||||
/* Disable the timing for global ports */
|
||||
print_analysis_sdc_disable_global_ports(fp,
|
||||
openfpga_ctx.module_graph(), top_module,
|
||||
openfpga_ctx.arch().circuit_lib, global_ports);
|
||||
|
||||
/* Disable the timing for configuration cells */
|
||||
rec_print_pnr_sdc_disable_configurable_memory_module_output(fp,
|
||||
openfpga_ctx.module_graph(), top_module,
|
||||
format_dir_path(openfpga_ctx.module_graph().module_name(top_module)));
|
||||
|
||||
|
||||
/* Disable timing for unused routing resources in connection blocks */
|
||||
print_analysis_sdc_disable_unused_cbs(fp,
|
||||
vpr_ctx.atom(),
|
||||
openfpga_ctx.module_graph(),
|
||||
vpr_ctx.device().rr_graph,
|
||||
openfpga_ctx.vpr_routing_annotation(),
|
||||
openfpga_ctx.device_rr_gsb(),
|
||||
compact_routing_hierarchy);
|
||||
|
||||
/* Disable timing for unused routing resources in switch blocks */
|
||||
print_analysis_sdc_disable_unused_sbs(fp,
|
||||
vpr_ctx.atom(),
|
||||
openfpga_ctx.module_graph(),
|
||||
vpr_ctx.device().rr_graph,
|
||||
openfpga_ctx.vpr_routing_annotation(),
|
||||
openfpga_ctx.device_rr_gsb(),
|
||||
compact_routing_hierarchy);
|
||||
|
||||
/* Disable timing for unused routing resources in grids (programmable blocks) */
|
||||
print_analysis_sdc_disable_unused_grids(fp,
|
||||
vpr_ctx.device().grid,
|
||||
openfpga_ctx.vpr_device_annotation(),
|
||||
openfpga_ctx.vpr_clustering_annotation(),
|
||||
openfpga_ctx.vpr_placement_annotation(),
|
||||
openfpga_ctx.module_graph());
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef ANALYSIS_SDC_WRITER_H
|
||||
#define ANALYSIS_SDC_WRITER_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "vpr_context.h"
|
||||
#include "openfpga_context.h"
|
||||
#include "analysis_sdc_option.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_analysis_sdc(const AnalysisSdcOption& option,
|
||||
const float& critical_path_delay,
|
||||
const VprContext& vpr_ctx,
|
||||
const OpenfpgaContext& openfpga_ctx,
|
||||
const std::vector<CircuitPortId>& global_ports,
|
||||
const bool& compact_routing_hierarchy);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,237 @@
|
|||
/********************************************************************
|
||||
* 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
|
||||
*******************************************************************/
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "sdc_writer_utils.h"
|
||||
#include "analysis_sdc_writer_utils.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Identify if a node should be disabled during analysis SDC generation
|
||||
*******************************************************************/
|
||||
bool is_rr_node_to_be_disable_for_analysis(const VprRoutingAnnotation& routing_annotation,
|
||||
const RRNodeId& cur_rr_node) {
|
||||
/* Conditions to enable timing analysis for a node
|
||||
* 1st condition: it have a valid net_number
|
||||
* TODO: 2nd condition: it is not an parasitic net
|
||||
*/
|
||||
return ClusterNetId::INVALID() == routing_annotation.rr_node_net(cur_rr_node);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* 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,
|
||||
const AtomNetId& mapped_net,
|
||||
const std::map<std::string, AtomNetId> mux_instance_to_net_map) {
|
||||
/* Validate file stream */
|
||||
valid_file_stream(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 (AtomNetId::INVALID() == mapped_net) {
|
||||
/* Disable all the sinks! */
|
||||
disable_timing = true;
|
||||
} else {
|
||||
std::map<std::string, AtomNetId>::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 (mapped_net != 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]);
|
||||
|
||||
VTR_ASSERT(!sink_instance_name.empty());
|
||||
/* 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,
|
||||
const AtomNetId& mapped_net,
|
||||
const std::map<std::string, AtomNetId> mux_instance_to_net_map) {
|
||||
/* Validate file stream */
|
||||
valid_file_stream(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,
|
||||
mapped_net,
|
||||
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,
|
||||
const AtomNetId& mapped_net,
|
||||
const std::map<std::string, AtomNetId> mux_instance_to_net_map) {
|
||||
/* Validate file stream */
|
||||
valid_file_stream(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 (AtomNetId::INVALID() == mapped_net) {
|
||||
/* Disable all the sinks! */
|
||||
disable_timing = true;
|
||||
} else {
|
||||
std::map<std::string, AtomNetId>::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 (mapped_net != 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]);
|
||||
|
||||
VTR_ASSERT(!sink_instance_name.empty());
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef ANALYSIS_SDC_WRITER_UTILS_H
|
||||
#define ANALYSIS_SDC_WRITER_UTILS_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "module_manager.h"
|
||||
#include "rr_graph_obj.h"
|
||||
#include "atom_netlist_fwd.h"
|
||||
#include "vpr_routing_annotation.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
bool is_rr_node_to_be_disable_for_analysis(const VprRoutingAnnotation& routing_annotation,
|
||||
const RRNodeId& 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,
|
||||
const AtomNetId& mapped_net,
|
||||
const std::map<std::string, AtomNetId> 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,
|
||||
const AtomNetId& mapped_net,
|
||||
const std::map<std::string, AtomNetId> 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,
|
||||
const AtomNetId& mapped_net,
|
||||
const std::map<std::string, AtomNetId> mux_instance_to_net_map);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -16,6 +16,8 @@
|
|||
#include "openfpga_port.h"
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "openfpga_naming.h"
|
||||
|
||||
#include "module_manager_utils.h"
|
||||
#include "verilog_port_types.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
|
@ -371,7 +373,7 @@ void write_verilog_instance_to_file(std::fstream& fp,
|
|||
* if not, we use a default name <name>_<num_instance_in_parent_module>
|
||||
*/
|
||||
if (true == module_manager.instance_name(parent_module, child_module, instance_id).empty()) {
|
||||
fp << module_manager.module_name(child_module) << "_" << instance_id << "_" << " (" << std::endl;
|
||||
fp << generate_instance_name(module_manager.module_name(child_module), instance_id) << " (" << std::endl;
|
||||
} else {
|
||||
fp << module_manager.instance_name(parent_module, child_module, instance_id) << " (" << std::endl;
|
||||
}
|
||||
|
|
|
@ -52,5 +52,8 @@ write_verilog_testbench --file /var/tmp/xtang/openfpga_test_src/SRC --reference_
|
|||
# - Turn on every options here
|
||||
write_pnr_sdc --file /var/tmp/xtang/openfpga_test_src/SDC
|
||||
|
||||
# Write the SDC to run timing analysis for a mapped FPGA fabric
|
||||
write_analysis_sdc --file /var/tmp/xtang/openfpga_test_src/SDC_analysis
|
||||
|
||||
# Finish and exit OpenFPGA
|
||||
exit
|
||||
|
|
Loading…
Reference in New Issue