add new command to disable timing for configure ports of programmable modules
This commit is contained in:
parent
ae9f1fbd90
commit
13f591cacf
|
@ -16,6 +16,7 @@
|
||||||
#include "pnr_sdc_writer.h"
|
#include "pnr_sdc_writer.h"
|
||||||
#include "analysis_sdc_writer.h"
|
#include "analysis_sdc_writer.h"
|
||||||
#include "configuration_chain_sdc_writer.h"
|
#include "configuration_chain_sdc_writer.h"
|
||||||
|
#include "configure_port_sdc_writer.h"
|
||||||
#include "openfpga_sdc.h"
|
#include "openfpga_sdc.h"
|
||||||
|
|
||||||
/* Include global variables of VPR */
|
/* Include global variables of VPR */
|
||||||
|
@ -138,6 +139,32 @@ int write_configuration_chain_sdc(const OpenfpgaContext& openfpga_ctx,
|
||||||
return CMD_EXEC_SUCCESS;
|
return CMD_EXEC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* A wrapper function to call the PnR SDC generator on routing multiplexers
|
||||||
|
* of FPGA-SDC
|
||||||
|
*******************************************************************/
|
||||||
|
int write_sdc_disable_timing_configure_ports(const OpenfpgaContext& openfpga_ctx,
|
||||||
|
const Command& cmd, const CommandContext& cmd_context) {
|
||||||
|
|
||||||
|
/* Get command options */
|
||||||
|
CommandOptionId opt_output_dir = cmd.option("file");
|
||||||
|
CommandOptionId opt_flatten_names = cmd.option("flatten_names");
|
||||||
|
|
||||||
|
std::string sdc_dir_path = format_dir_path(cmd_context.option_value(cmd, opt_output_dir));
|
||||||
|
|
||||||
|
/* Write the SDC for configuration chain */
|
||||||
|
if (CMD_EXEC_FATAL_ERROR ==
|
||||||
|
print_sdc_disable_timing_configure_ports(cmd_context.option_value(cmd, opt_output_dir),
|
||||||
|
cmd_context.option_enable(cmd, opt_flatten_names),
|
||||||
|
openfpga_ctx.mux_lib(),
|
||||||
|
openfpga_ctx.arch().circuit_lib,
|
||||||
|
openfpga_ctx.module_graph())) {
|
||||||
|
return CMD_EXEC_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMD_EXEC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* A wrapper function to call the analysis SDC generator of FPGA-SDC
|
* A wrapper function to call the analysis SDC generator of FPGA-SDC
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
|
|
|
@ -21,6 +21,9 @@ int write_pnr_sdc(OpenfpgaContext& openfpga_ctx,
|
||||||
int write_configuration_chain_sdc(const OpenfpgaContext& openfpga_ctx,
|
int write_configuration_chain_sdc(const OpenfpgaContext& openfpga_ctx,
|
||||||
const Command& cmd, const CommandContext& cmd_context);
|
const Command& cmd, const CommandContext& cmd_context);
|
||||||
|
|
||||||
|
int write_sdc_disable_timing_configure_ports(const OpenfpgaContext& openfpga_ctx,
|
||||||
|
const Command& cmd, const CommandContext& cmd_context);
|
||||||
|
|
||||||
int write_analysis_sdc(OpenfpgaContext& openfpga_ctx,
|
int write_analysis_sdc(OpenfpgaContext& openfpga_ctx,
|
||||||
const Command& cmd, const CommandContext& cmd_context);
|
const Command& cmd, const CommandContext& cmd_context);
|
||||||
|
|
||||||
|
|
|
@ -119,6 +119,36 @@ ShellCommandId add_openfpga_write_configuration_chain_sdc_command(openfpga::Shel
|
||||||
return shell_cmd_id;
|
return shell_cmd_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: generate PnR SDC for configure ports
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
ShellCommandId add_openfpga_write_sdc_disable_timing_configure_ports_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const std::vector<ShellCommandId>& dependent_cmds) {
|
||||||
|
Command shell_cmd("write_sdc_disable_timing_configure_ports");
|
||||||
|
|
||||||
|
/* Add an option '--file' in short '-f'*/
|
||||||
|
CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory");
|
||||||
|
shell_cmd.set_option_short_name(output_opt, "f");
|
||||||
|
shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add an option '--flatten_name' */
|
||||||
|
shell_cmd.add_option("flatten_names", false, "Use flatten names (no wildcards) in SDC files");
|
||||||
|
|
||||||
|
/* Add command 'write_configuration_chain_sdc' to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate SDC files to disable timing for configure ports across FPGA fabric");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_const_execute_function(shell_cmd_id, write_sdc_disable_timing_configure_ports);
|
||||||
|
|
||||||
|
/* Add command dependency to the Shell */
|
||||||
|
shell.set_command_dependency(shell_cmd_id, dependent_cmds);
|
||||||
|
|
||||||
|
return shell_cmd_id;
|
||||||
|
}
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* - Add a command to Shell environment: generate PnR SDC
|
* - Add a command to Shell environment: generate PnR SDC
|
||||||
* - Add associated options
|
* - Add associated options
|
||||||
|
@ -183,6 +213,16 @@ void add_openfpga_sdc_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
||||||
openfpga_sdc_cmd_class,
|
openfpga_sdc_cmd_class,
|
||||||
cc_sdc_cmd_dependency);
|
cc_sdc_cmd_dependency);
|
||||||
|
|
||||||
|
/********************************
|
||||||
|
* Command 'write_sdc_disable_timing_configure_ports'
|
||||||
|
*/
|
||||||
|
/* The 'write_sdc_disable_timing_configure_ports' command should NOT be executed before 'build_fabric' */
|
||||||
|
std::vector<ShellCommandId> config_port_sdc_cmd_dependency;
|
||||||
|
config_port_sdc_cmd_dependency.push_back(build_fabric_id);
|
||||||
|
add_openfpga_write_sdc_disable_timing_configure_ports_command(shell,
|
||||||
|
openfpga_sdc_cmd_class,
|
||||||
|
config_port_sdc_cmd_dependency);
|
||||||
|
|
||||||
/********************************
|
/********************************
|
||||||
* Command 'write_analysis_sdc'
|
* Command 'write_analysis_sdc'
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file includes functions that print SDC (Synopsys Design Constraint)
|
||||||
|
* files in physical design tools, i.e., Place & Route (PnR) tools
|
||||||
|
* The SDC files are used to constrain the timing of configuration chain
|
||||||
|
*
|
||||||
|
* Note that this is different from the SDC to constrain VPR Place&Route
|
||||||
|
* engine! These SDCs are designed for PnR to generate FPGA layouts!!!
|
||||||
|
*******************************************************************/
|
||||||
|
#include <ctime>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "vtr_time.h"
|
||||||
|
#include "vtr_log.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgashell library */
|
||||||
|
#include "command_exit_codes.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_scale.h"
|
||||||
|
#include "openfpga_port.h"
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
|
#include "openfpga_naming.h"
|
||||||
|
|
||||||
|
#include "sdc_writer_naming.h"
|
||||||
|
#include "sdc_writer_utils.h"
|
||||||
|
#include "sdc_mux_utils.h"
|
||||||
|
#include "configure_port_sdc_writer.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Break combinational loops in FPGA fabric, which mainly come from
|
||||||
|
* non-MUX programmable modules
|
||||||
|
* To handle this, we disable the timing at configuration ports
|
||||||
|
*
|
||||||
|
* Return code:
|
||||||
|
* 0: success
|
||||||
|
* 1: fatal error occurred
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
int print_sdc_disable_non_mux_circuit_configure_ports(std::fstream& fp,
|
||||||
|
const bool& flatten_names,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module) {
|
||||||
|
|
||||||
|
if (false == valid_file_stream(fp)) {
|
||||||
|
return CMD_EXEC_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate over the MUX modules */
|
||||||
|
for (const CircuitModelId& model : circuit_lib.models()) {
|
||||||
|
|
||||||
|
/* Skip MUXes, they are handled in another function */
|
||||||
|
if (CIRCUIT_MODEL_MUX == circuit_lib.model_type(model)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We care programmable circuit models only */
|
||||||
|
if (0 == circuit_lib.model_ports_by_type(model, CIRCUIT_MODEL_PORT_SRAM).size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string programmable_module_name = circuit_lib.model_name(model);
|
||||||
|
|
||||||
|
/* Find the module name in module manager */
|
||||||
|
ModuleId programmable_module = module_manager.find_module(programmable_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(programmable_module));
|
||||||
|
|
||||||
|
/* Go recursively in the module manager,
|
||||||
|
* starting from the top-level module: instance id of the top-level module is 0 by default
|
||||||
|
* Disable all the outputs of child modules that matches the mux_module id
|
||||||
|
*/
|
||||||
|
for (const CircuitPortId& sram_port : circuit_lib.model_ports_by_type(model, CIRCUIT_MODEL_PORT_SRAM)) {
|
||||||
|
const std::string& sram_port_name = circuit_lib.port_lib_name(sram_port);
|
||||||
|
if (CMD_EXEC_FATAL_ERROR ==
|
||||||
|
rec_print_sdc_disable_timing_for_module_ports(fp,
|
||||||
|
flatten_names,
|
||||||
|
module_manager,
|
||||||
|
top_module,
|
||||||
|
programmable_module,
|
||||||
|
format_dir_path(module_manager.module_name(top_module)),
|
||||||
|
sram_port_name)) {
|
||||||
|
return CMD_EXEC_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMD_EXEC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Break combinational loops in FPGA fabric, which mainly come from
|
||||||
|
* the configure ports of each programmable module.
|
||||||
|
* To handle this, we disable the configure ports of
|
||||||
|
* - routing multiplexers
|
||||||
|
* - other circuit model that has SRAM ports
|
||||||
|
*******************************************************************/
|
||||||
|
int print_sdc_disable_timing_configure_ports(const std::string& sdc_fname,
|
||||||
|
const bool& flatten_names,
|
||||||
|
const MuxLibrary& mux_lib,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const ModuleManager& module_manager) {
|
||||||
|
/* Create the directory */
|
||||||
|
create_directory(find_path_dir_name(sdc_fname));
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
std::string timer_message = std::string("Write SDC to disable timing on configuration outputs of programmable cells for P&R flow '") + sdc_fname + std::string("'");
|
||||||
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
check_file_stream(sdc_fname.c_str(), fp);
|
||||||
|
|
||||||
|
/* Generate the descriptions*/
|
||||||
|
print_sdc_file_header(fp, std::string("Disable configuration outputs of all the programmable cells for PnR"));
|
||||||
|
|
||||||
|
std::string top_module_name = generate_fpga_top_module_name();
|
||||||
|
ModuleId top_module = module_manager.find_module(top_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(top_module));
|
||||||
|
|
||||||
|
/* Disable timing for the configure ports of all the routing multiplexer */
|
||||||
|
VTR_LOG("Write disable timing for routing multiplexers...");
|
||||||
|
if (CMD_EXEC_FATAL_ERROR == print_sdc_disable_routing_multiplexer_configure_ports(fp,
|
||||||
|
flatten_names,
|
||||||
|
mux_lib,
|
||||||
|
circuit_lib,
|
||||||
|
module_manager,
|
||||||
|
top_module)) {
|
||||||
|
VTR_LOG("Fatal errors occurred\n");
|
||||||
|
return CMD_EXEC_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
VTR_LOG("Done\n");
|
||||||
|
|
||||||
|
/* Disable timing for the other programmable circuit models */
|
||||||
|
VTR_LOG("Write disable timing for other programmable modules...");
|
||||||
|
if (CMD_EXEC_FATAL_ERROR == print_sdc_disable_non_mux_circuit_configure_ports(fp,
|
||||||
|
flatten_names,
|
||||||
|
circuit_lib,
|
||||||
|
module_manager,
|
||||||
|
top_module)) {
|
||||||
|
VTR_LOG("Fatal errors occurred\n");
|
||||||
|
return CMD_EXEC_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
VTR_LOG("Done\n");
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
|
||||||
|
return CMD_EXEC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef CONFIGURE_PORT_SDC_WRITER_H
|
||||||
|
#define CONFIGURE_PORT_SDC_WRITER_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "module_manager.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
int print_sdc_disable_timing_configure_ports(const std::string& sdc_fname,
|
||||||
|
const bool& flatten_names,
|
||||||
|
const MuxLibrary& mux_lib,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const ModuleManager& module_manager);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -75,69 +75,6 @@ void print_pnr_sdc_constrain_configurable_memory_outputs(const std::string& sdc_
|
||||||
fp.close();
|
fp.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************
|
|
||||||
* Break combinational loops in FPGA fabric, which mainly come from
|
|
||||||
* loops of multiplexers.
|
|
||||||
* To handle this, we disable the timing at outputs of routing multiplexers
|
|
||||||
*******************************************************************/
|
|
||||||
static
|
|
||||||
void print_sdc_disable_routing_multiplexer_outputs(const std::string& sdc_dir,
|
|
||||||
const bool& flatten_names,
|
|
||||||
const MuxLibrary& mux_lib,
|
|
||||||
const CircuitLibrary& circuit_lib,
|
|
||||||
const ModuleManager& module_manager,
|
|
||||||
const ModuleId& top_module) {
|
|
||||||
/* Create the file name for Verilog netlist */
|
|
||||||
std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_MUX_OUTPUTS_FILE_NAME));
|
|
||||||
|
|
||||||
/* Start time count */
|
|
||||||
std::string timer_message = std::string("Write SDC to disable routing multiplexer outputs for P&R flow '") + sdc_fname + std::string("'");
|
|
||||||
vtr::ScopedStartFinishTimer timer(timer_message);
|
|
||||||
|
|
||||||
/* Create the file stream */
|
|
||||||
std::fstream fp;
|
|
||||||
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
|
||||||
|
|
||||||
check_file_stream(sdc_fname.c_str(), fp);
|
|
||||||
|
|
||||||
/* Generate the descriptions*/
|
|
||||||
print_sdc_file_header(fp, std::string("Disable routing multiplexer outputs for PnR"));
|
|
||||||
|
|
||||||
/* Iterate over the MUX modules */
|
|
||||||
for (const MuxId& mux_id : mux_lib.muxes()) {
|
|
||||||
const CircuitModelId& mux_model = mux_lib.mux_circuit_model(mux_id);
|
|
||||||
|
|
||||||
/* Skip LUTs, we only care about multiplexers here */
|
|
||||||
if (CIRCUIT_MODEL_MUX != circuit_lib.model_type(mux_model)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MuxGraph& mux_graph = mux_lib.mux_graph(mux_id);
|
|
||||||
std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model,
|
|
||||||
find_mux_num_datapath_inputs(circuit_lib, mux_model, mux_graph.num_inputs()),
|
|
||||||
std::string(""));
|
|
||||||
|
|
||||||
/* Find the module name in module manager */
|
|
||||||
ModuleId mux_module = module_manager.find_module(mux_module_name);
|
|
||||||
VTR_ASSERT(true == module_manager.valid_module_id(mux_module));
|
|
||||||
|
|
||||||
/* Go recursively in the module manager,
|
|
||||||
* starting from the top-level module: instance id of the top-level module is 0 by default
|
|
||||||
* Disable all the outputs of child modules that matches the mux_module id
|
|
||||||
*/
|
|
||||||
rec_print_pnr_sdc_disable_routing_multiplexer_outputs(fp,
|
|
||||||
flatten_names,
|
|
||||||
module_manager,
|
|
||||||
top_module,
|
|
||||||
mux_module,
|
|
||||||
format_dir_path(module_manager.module_name(top_module)));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close file handler */
|
|
||||||
fp.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Break combinational loops in FPGA fabric, which mainly come from
|
* Break combinational loops in FPGA fabric, which mainly come from
|
||||||
* loops of multiplexers.
|
* loops of multiplexers.
|
||||||
|
|
|
@ -6,13 +6,19 @@
|
||||||
/* Headers from vtrutil library */
|
/* Headers from vtrutil library */
|
||||||
#include "vtr_assert.h"
|
#include "vtr_assert.h"
|
||||||
#include "vtr_log.h"
|
#include "vtr_log.h"
|
||||||
|
#include "vtr_time.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgashell library */
|
||||||
|
#include "command_exit_codes.h"
|
||||||
|
|
||||||
/* Headers from openfpgautil library */
|
/* Headers from openfpgautil library */
|
||||||
#include "openfpga_wildcard_string.h"
|
|
||||||
#include "openfpga_digest.h"
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
#include "openfpga_naming.h"
|
#include "openfpga_naming.h"
|
||||||
|
|
||||||
|
#include "mux_utils.h"
|
||||||
|
|
||||||
|
#include "sdc_writer_naming.h"
|
||||||
#include "sdc_writer_utils.h"
|
#include "sdc_writer_utils.h"
|
||||||
|
|
||||||
#include "sdc_mux_utils.h"
|
#include "sdc_mux_utils.h"
|
||||||
|
@ -21,93 +27,137 @@
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Print SDC commands to disable outputs of routing multiplexer modules
|
* Break combinational loops in FPGA fabric, which mainly come from
|
||||||
* in a given module id
|
* loops of multiplexers.
|
||||||
* This function will be executed in a recursive way,
|
* To handle this, we disable the timing at outputs of routing multiplexers
|
||||||
* using a Depth-First Search (DFS) strategy
|
|
||||||
* It will iterate over all the configurable children under each module
|
|
||||||
* and print a SDC command to disable its outputs
|
|
||||||
*
|
|
||||||
* Note:
|
|
||||||
* - When flatten_names is true
|
|
||||||
* this function will not apply any wildcard to names
|
|
||||||
* - When flatten_names is false
|
|
||||||
* It will straightforwardly output the instance name and port name
|
|
||||||
* This function will try to apply wildcard to names
|
|
||||||
* so that SDC file size can be minimal
|
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
void rec_print_pnr_sdc_disable_routing_multiplexer_outputs(std::fstream& fp,
|
void print_sdc_disable_routing_multiplexer_outputs(const std::string& sdc_dir,
|
||||||
const bool& flatten_names,
|
const bool& flatten_names,
|
||||||
const ModuleManager& module_manager,
|
const MuxLibrary& mux_lib,
|
||||||
const ModuleId& parent_module,
|
const CircuitLibrary& circuit_lib,
|
||||||
const ModuleId& mux_module,
|
const ModuleManager& module_manager,
|
||||||
const std::string& parent_module_path) {
|
const ModuleId& top_module) {
|
||||||
|
/* Create the file name for Verilog netlist */
|
||||||
|
std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_MUX_OUTPUTS_FILE_NAME));
|
||||||
|
|
||||||
/* Build wildcard names for the instance names of multiple-instanced-blocks (MIB)
|
/* Start time count */
|
||||||
* We will find all the instance names and see there are common prefix
|
std::string timer_message = std::string("Write SDC to disable routing multiplexer outputs for P&R flow '") + sdc_fname + std::string("'");
|
||||||
* If so, we can use wildcards
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
*/
|
|
||||||
std::map<ModuleId, std::vector<std::string>> wildcard_names;
|
|
||||||
|
|
||||||
/* For each child, we will go one level down in priority */
|
/* Create the file stream */
|
||||||
for (const ModuleId& child_module : module_manager.child_modules(parent_module)) {
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
/* Iterate over the child instances*/
|
check_file_stream(sdc_fname.c_str(), fp);
|
||||||
for (const size_t& child_instance : module_manager.child_module_instances(parent_module, child_module)) {
|
|
||||||
std::string child_module_path = parent_module_path;
|
|
||||||
|
|
||||||
std::string child_instance_name;
|
/* Generate the descriptions*/
|
||||||
if (true == module_manager.instance_name(parent_module, child_module, child_instance).empty()) {
|
print_sdc_file_header(fp, std::string("Disable routing multiplexer outputs for PnR"));
|
||||||
child_instance_name = generate_instance_name(module_manager.module_name(child_module), child_instance);
|
|
||||||
} else {
|
|
||||||
child_instance_name = module_manager.instance_name(parent_module, child_module, child_instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false == flatten_names) {
|
/* Iterate over the MUX modules */
|
||||||
/* Try to adapt to a wildcard name: replace all the numbers with a wildcard character '*' */
|
for (const MuxId& mux_id : mux_lib.muxes()) {
|
||||||
WildCardString wildcard_str(child_instance_name);
|
const CircuitModelId& mux_model = mux_lib.mux_circuit_model(mux_id);
|
||||||
/* If the wildcard name is already in the list, we can skip this
|
|
||||||
* Otherwise, we have to
|
|
||||||
* - output this instance
|
|
||||||
* - record the wildcard name in the map
|
|
||||||
*/
|
|
||||||
if ( (0 < wildcard_names.count(child_module))
|
|
||||||
&& (wildcard_names.at(child_module).end() != std::find(wildcard_names.at(child_module).begin(),
|
|
||||||
wildcard_names.at(child_module).end(),
|
|
||||||
wildcard_str.data())) ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
child_module_path += wildcard_str.data();
|
/* Skip LUTs, we only care about multiplexers here */
|
||||||
|
if (CIRCUIT_MODEL_MUX != circuit_lib.model_type(mux_model)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
wildcard_names[child_module].push_back(wildcard_str.data());
|
const MuxGraph& mux_graph = mux_lib.mux_graph(mux_id);
|
||||||
} else {
|
std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model,
|
||||||
child_module_path += child_instance_name;
|
find_mux_num_datapath_inputs(circuit_lib, mux_model, mux_graph.num_inputs()),
|
||||||
}
|
std::string(""));
|
||||||
|
|
||||||
child_module_path = format_dir_path(child_module_path);
|
/* Find the module name in module manager */
|
||||||
|
ModuleId mux_module = module_manager.find_module(mux_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(mux_module));
|
||||||
|
|
||||||
/* If this is NOT the MUX module we want, we go recursively */
|
/* Go recursively in the module manager,
|
||||||
if (mux_module != child_module) {
|
* starting from the top-level module: instance id of the top-level module is 0 by default
|
||||||
rec_print_pnr_sdc_disable_routing_multiplexer_outputs(fp, flatten_names,
|
* Disable all the outputs of child modules that matches the mux_module id
|
||||||
module_manager,
|
*/
|
||||||
child_module,
|
for (const BasicPort& output_port : module_manager.module_ports_by_type(mux_module, ModuleManager::MODULE_OUTPUT_PORT)) {
|
||||||
mux_module,
|
rec_print_sdc_disable_timing_for_module_ports(fp,
|
||||||
child_module_path);
|
flatten_names,
|
||||||
continue;
|
module_manager,
|
||||||
}
|
top_module,
|
||||||
|
mux_module,
|
||||||
/* Validate file stream */
|
format_dir_path(module_manager.module_name(top_module)),
|
||||||
valid_file_stream(fp);
|
output_port.get_name());
|
||||||
|
|
||||||
/* Reach here, this is the MUX module we want, disable the outputs */
|
|
||||||
for (const BasicPort& output_port : module_manager.module_ports_by_type(mux_module, ModuleManager::MODULE_OUTPUT_PORT)) {
|
|
||||||
fp << "set_disable_timing ";
|
|
||||||
fp << child_module_path << output_port.get_name();
|
|
||||||
fp << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Break combinational loops in FPGA fabric, which mainly come from
|
||||||
|
* loops of multiplexers.
|
||||||
|
* To handle this, we disable the timing at configuration ports of routing multiplexers
|
||||||
|
*
|
||||||
|
* Return code:
|
||||||
|
* 0: success
|
||||||
|
* 1: fatal error occurred
|
||||||
|
*******************************************************************/
|
||||||
|
int print_sdc_disable_routing_multiplexer_configure_ports(std::fstream& fp,
|
||||||
|
const bool& flatten_names,
|
||||||
|
const MuxLibrary& mux_lib,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module) {
|
||||||
|
|
||||||
|
if (false == valid_file_stream(fp)) {
|
||||||
|
return CMD_EXEC_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate over the MUX modules */
|
||||||
|
for (const MuxId& mux_id : mux_lib.muxes()) {
|
||||||
|
const CircuitModelId& mux_model = mux_lib.mux_circuit_model(mux_id);
|
||||||
|
|
||||||
|
const MuxGraph& mux_graph = mux_lib.mux_graph(mux_id);
|
||||||
|
std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model,
|
||||||
|
find_mux_num_datapath_inputs(circuit_lib, mux_model, mux_graph.num_inputs()),
|
||||||
|
std::string(""));
|
||||||
|
|
||||||
|
/* Find the module name in module manager */
|
||||||
|
ModuleId mux_module = module_manager.find_module(mux_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(mux_module));
|
||||||
|
|
||||||
|
/* Go recursively in the module manager,
|
||||||
|
* starting from the top-level module: instance id of the top-level module is 0 by default
|
||||||
|
* Disable all the outputs of child modules that matches the mux_module id
|
||||||
|
*/
|
||||||
|
for (const CircuitPortId& mux_sram_port : circuit_lib.model_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_SRAM)) {
|
||||||
|
const std::string& mux_sram_port_name = circuit_lib.port_prefix(mux_sram_port);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, module_manager.find_module_port(mux_module, mux_sram_port_name)));
|
||||||
|
if (CMD_EXEC_FATAL_ERROR ==
|
||||||
|
rec_print_sdc_disable_timing_for_module_ports(fp,
|
||||||
|
flatten_names,
|
||||||
|
module_manager,
|
||||||
|
top_module,
|
||||||
|
mux_module,
|
||||||
|
format_dir_path(module_manager.module_name(top_module)),
|
||||||
|
mux_sram_port_name)) {
|
||||||
|
return CMD_EXEC_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& mux_sram_inv_port_name = circuit_lib.port_prefix(mux_sram_port) + "_inv";
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, module_manager.find_module_port(mux_module, mux_sram_inv_port_name)));
|
||||||
|
if (CMD_EXEC_FATAL_ERROR ==
|
||||||
|
rec_print_sdc_disable_timing_for_module_ports(fp,
|
||||||
|
flatten_names,
|
||||||
|
module_manager,
|
||||||
|
top_module,
|
||||||
|
mux_module,
|
||||||
|
format_dir_path(module_manager.module_name(top_module)),
|
||||||
|
mux_sram_inv_port_name)) {
|
||||||
|
return CMD_EXEC_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return CMD_EXEC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "mux_library.h"
|
||||||
|
#include "circuit_library.h"
|
||||||
#include "module_manager.h"
|
#include "module_manager.h"
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
|
@ -15,12 +17,19 @@
|
||||||
/* begin namespace openfpga */
|
/* begin namespace openfpga */
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
|
||||||
void rec_print_pnr_sdc_disable_routing_multiplexer_outputs(std::fstream& fp,
|
void print_sdc_disable_routing_multiplexer_outputs(const std::string& sdc_dir,
|
||||||
const bool& flatten_names,
|
const bool& flatten_names,
|
||||||
const ModuleManager& module_manager,
|
const MuxLibrary& mux_lib,
|
||||||
const ModuleId& parent_module,
|
const CircuitLibrary& circuit_lib,
|
||||||
const ModuleId& mux_module,
|
const ModuleManager& module_manager,
|
||||||
const std::string& parent_module_path);
|
const ModuleId& top_module);
|
||||||
|
|
||||||
|
int print_sdc_disable_routing_multiplexer_configure_ports(std::fstream& fp,
|
||||||
|
const bool& flatten_names,
|
||||||
|
const MuxLibrary& mux_lib,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module);
|
||||||
|
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -4,12 +4,16 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
/* Headers from vtrutil library */
|
/* Headers from vtrutil library */
|
||||||
#include "vtr_assert.h"
|
#include "vtr_assert.h"
|
||||||
|
|
||||||
/* Headers from openfpgautil library */
|
/* Headers from openfpgautil library */
|
||||||
#include "openfpga_digest.h"
|
#include "openfpga_digest.h"
|
||||||
|
#include "openfpga_wildcard_string.h"
|
||||||
|
|
||||||
|
#include "openfpga_naming.h"
|
||||||
|
|
||||||
#include "sdc_writer_utils.h"
|
#include "sdc_writer_utils.h"
|
||||||
|
|
||||||
|
@ -295,4 +299,111 @@ void print_sdc_set_port_output_delay(std::fstream& fp,
|
||||||
fp << std::endl;
|
fp << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print SDC commands to disable a given port of modules
|
||||||
|
* in a given module id
|
||||||
|
* This function will be executed in a recursive way,
|
||||||
|
* using a Depth-First Search (DFS) strategy
|
||||||
|
* It will iterate over all the configurable children under each module
|
||||||
|
* and print a SDC command to disable its outputs
|
||||||
|
*
|
||||||
|
* Return code:
|
||||||
|
* 0: success
|
||||||
|
* 1: fatal error occurred
|
||||||
|
*
|
||||||
|
* Note:
|
||||||
|
* - When flatten_names is true
|
||||||
|
* this function will not apply any wildcard to names
|
||||||
|
* - When flatten_names is false
|
||||||
|
* It will straightforwardly output the instance name and port name
|
||||||
|
* This function will try to apply wildcard to names
|
||||||
|
* so that SDC file size can be minimal
|
||||||
|
*******************************************************************/
|
||||||
|
int rec_print_sdc_disable_timing_for_module_ports(std::fstream& fp,
|
||||||
|
const bool& flatten_names,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& parent_module,
|
||||||
|
const ModuleId& module_to_disable,
|
||||||
|
const std::string& parent_module_path,
|
||||||
|
const std::string& disable_port_name) {
|
||||||
|
|
||||||
|
if (false == valid_file_stream(fp)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build wildcard names for the instance names of multiple-instanced-blocks (MIB)
|
||||||
|
* We will find all the instance names and see there are common prefix
|
||||||
|
* If so, we can use wildcards
|
||||||
|
*/
|
||||||
|
std::map<ModuleId, std::vector<std::string>> wildcard_names;
|
||||||
|
|
||||||
|
/* For each child, we will go one level down in priority */
|
||||||
|
for (const ModuleId& child_module : module_manager.child_modules(parent_module)) {
|
||||||
|
|
||||||
|
/* Iterate over the child instances*/
|
||||||
|
for (const size_t& child_instance : module_manager.child_module_instances(parent_module, child_module)) {
|
||||||
|
std::string child_module_path = parent_module_path;
|
||||||
|
|
||||||
|
std::string child_instance_name;
|
||||||
|
if (true == module_manager.instance_name(parent_module, child_module, child_instance).empty()) {
|
||||||
|
child_instance_name = generate_instance_name(module_manager.module_name(child_module), child_instance);
|
||||||
|
} else {
|
||||||
|
child_instance_name = module_manager.instance_name(parent_module, child_module, child_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false == flatten_names) {
|
||||||
|
/* Try to adapt to a wildcard name: replace all the numbers with a wildcard character '*' */
|
||||||
|
WildCardString wildcard_str(child_instance_name);
|
||||||
|
/* If the wildcard name is already in the list, we can skip this
|
||||||
|
* Otherwise, we have to
|
||||||
|
* - output this instance
|
||||||
|
* - record the wildcard name in the map
|
||||||
|
*/
|
||||||
|
if ( (0 < wildcard_names.count(child_module))
|
||||||
|
&& (wildcard_names.at(child_module).end() != std::find(wildcard_names.at(child_module).begin(),
|
||||||
|
wildcard_names.at(child_module).end(),
|
||||||
|
wildcard_str.data())) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
child_module_path += wildcard_str.data();
|
||||||
|
|
||||||
|
wildcard_names[child_module].push_back(wildcard_str.data());
|
||||||
|
} else {
|
||||||
|
child_module_path += child_instance_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
child_module_path = format_dir_path(child_module_path);
|
||||||
|
|
||||||
|
/* If this is NOT the MUX module we want, we go recursively */
|
||||||
|
if (module_to_disable != child_module) {
|
||||||
|
int status = rec_print_sdc_disable_timing_for_module_ports(fp, flatten_names,
|
||||||
|
module_manager,
|
||||||
|
child_module,
|
||||||
|
module_to_disable,
|
||||||
|
child_module_path,
|
||||||
|
disable_port_name);
|
||||||
|
if (1 == status) {
|
||||||
|
return 1; /* FATAL ERRORS */
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Reach here, this is the MUX module we want, disable the outputs */
|
||||||
|
ModulePortId port_to_disable = module_manager.find_module_port(module_to_disable, disable_port_name);
|
||||||
|
if (ModulePortId::INVALID() == port_to_disable) {
|
||||||
|
return 1; /* FATAL ERRORS */
|
||||||
|
}
|
||||||
|
fp << "set_disable_timing ";
|
||||||
|
fp << child_module_path << module_manager.module_port(module_to_disable, port_to_disable).get_name();
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; /* Success */
|
||||||
|
}
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -74,6 +74,14 @@ void print_sdc_set_port_output_delay(std::fstream& fp,
|
||||||
const BasicPort& clock_port,
|
const BasicPort& clock_port,
|
||||||
const float& delay);
|
const float& delay);
|
||||||
|
|
||||||
|
int rec_print_sdc_disable_timing_for_module_ports(std::fstream& fp,
|
||||||
|
const bool& flatten_names,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& parent_module,
|
||||||
|
const ModuleId& module_to_disable,
|
||||||
|
const std::string& parent_module_path,
|
||||||
|
const std::string& disable_port_name);
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -58,6 +58,9 @@ write_pnr_sdc --file ./SDC
|
||||||
# Write SDC to constrain timing of configuration chain
|
# Write SDC to constrain timing of configuration chain
|
||||||
write_configuration_chain_sdc --file ./SDC/ccff_timing.sdc --time_unit ns --max_delay 5 --min_delay 2.5
|
write_configuration_chain_sdc --file ./SDC/ccff_timing.sdc --time_unit ns --max_delay 5 --min_delay 2.5
|
||||||
|
|
||||||
|
# Write SDC to disable timing for configure ports
|
||||||
|
write_sdc_disable_timing_configure_ports --file ./SDC/disable_configure_ports.sdc
|
||||||
|
|
||||||
# Write the SDC to run timing analysis for a mapped FPGA fabric
|
# Write the SDC to run timing analysis for a mapped FPGA fabric
|
||||||
write_analysis_sdc --file ./SDC_analysis
|
write_analysis_sdc --file ./SDC_analysis
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,9 @@ write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE
|
||||||
# - Turn on every options here
|
# - Turn on every options here
|
||||||
write_pnr_sdc --file ./SDC
|
write_pnr_sdc --file ./SDC
|
||||||
|
|
||||||
|
# Write SDC to disable timing for configure ports
|
||||||
|
write_sdc_disable_timing_configure_ports --file ./SDC/disable_configure_ports.sdc
|
||||||
|
|
||||||
# Write the SDC to run timing analysis for a mapped FPGA fabric
|
# Write the SDC to run timing analysis for a mapped FPGA fabric
|
||||||
write_analysis_sdc --file ./SDC_analysis
|
write_analysis_sdc --file ./SDC_analysis
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue