diff --git a/openfpga/src/base/openfpga_sdc.cpp b/openfpga/src/base/openfpga_sdc.cpp index 93bd46959..bba115d3c 100644 --- a/openfpga/src/base/openfpga_sdc.cpp +++ b/openfpga/src/base/openfpga_sdc.cpp @@ -16,6 +16,7 @@ #include "pnr_sdc_writer.h" #include "analysis_sdc_writer.h" #include "configuration_chain_sdc_writer.h" +#include "configure_port_sdc_writer.h" #include "openfpga_sdc.h" /* Include global variables of VPR */ @@ -138,6 +139,32 @@ int write_configuration_chain_sdc(const OpenfpgaContext& openfpga_ctx, 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 *******************************************************************/ diff --git a/openfpga/src/base/openfpga_sdc.h b/openfpga/src/base/openfpga_sdc.h index 060c304bc..03f4af03f 100644 --- a/openfpga/src/base/openfpga_sdc.h +++ b/openfpga/src/base/openfpga_sdc.h @@ -21,6 +21,9 @@ int write_pnr_sdc(OpenfpgaContext& openfpga_ctx, int write_configuration_chain_sdc(const OpenfpgaContext& openfpga_ctx, 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, const Command& cmd, const CommandContext& cmd_context); diff --git a/openfpga/src/base/openfpga_sdc_command.cpp b/openfpga/src/base/openfpga_sdc_command.cpp index faa488931..17910a711 100644 --- a/openfpga/src/base/openfpga_sdc_command.cpp +++ b/openfpga/src/base/openfpga_sdc_command.cpp @@ -119,6 +119,36 @@ ShellCommandId add_openfpga_write_configuration_chain_sdc_command(openfpga::Shel 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& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& 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 associated options @@ -183,6 +213,16 @@ void add_openfpga_sdc_commands(openfpga::Shell& shell) { openfpga_sdc_cmd_class, 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 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' */ diff --git a/openfpga/src/fpga_sdc/configure_port_sdc_writer.cpp b/openfpga/src/fpga_sdc/configure_port_sdc_writer.cpp new file mode 100644 index 000000000..c38a2d725 --- /dev/null +++ b/openfpga/src/fpga_sdc/configure_port_sdc_writer.cpp @@ -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 +#include +#include + +/* 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 */ diff --git a/openfpga/src/fpga_sdc/configure_port_sdc_writer.h b/openfpga/src/fpga_sdc/configure_port_sdc_writer.h new file mode 100644 index 000000000..ef66af749 --- /dev/null +++ b/openfpga/src/fpga_sdc/configure_port_sdc_writer.h @@ -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 +#include +#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 diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp index 3ef29330d..c02988462 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp @@ -75,69 +75,6 @@ void print_pnr_sdc_constrain_configurable_memory_outputs(const std::string& sdc_ 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 * loops of multiplexers. diff --git a/openfpga/src/fpga_sdc/sdc_mux_utils.cpp b/openfpga/src/fpga_sdc/sdc_mux_utils.cpp index 7f7acc41f..0d2770566 100644 --- a/openfpga/src/fpga_sdc/sdc_mux_utils.cpp +++ b/openfpga/src/fpga_sdc/sdc_mux_utils.cpp @@ -6,13 +6,19 @@ /* Headers from vtrutil library */ #include "vtr_assert.h" #include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" /* Headers from openfpgautil library */ -#include "openfpga_wildcard_string.h" #include "openfpga_digest.h" #include "openfpga_naming.h" +#include "mux_utils.h" + +#include "sdc_writer_naming.h" #include "sdc_writer_utils.h" #include "sdc_mux_utils.h" @@ -21,93 +27,137 @@ namespace openfpga { /******************************************************************** - * Print SDC commands to disable outputs of routing multiplexer 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 - * - * 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 + * 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 *******************************************************************/ -void rec_print_pnr_sdc_disable_routing_multiplexer_outputs(std::fstream& fp, - const bool& flatten_names, - const ModuleManager& module_manager, - const ModuleId& parent_module, - const ModuleId& mux_module, - const std::string& parent_module_path) { +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)); - /* 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> wildcard_names; + /* 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); - /* For each child, we will go one level down in priority */ - for (const ModuleId& child_module : module_manager.child_modules(parent_module)) { + /* Create the file stream */ + std::fstream fp; + fp.open(sdc_fname, std::fstream::out | std::fstream::trunc); - /* 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; + check_file_stream(sdc_fname.c_str(), fp); - 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); - } + /* Generate the descriptions*/ + print_sdc_file_header(fp, std::string("Disable routing multiplexer outputs for PnR")); - 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; - } + /* 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; + } - 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); + 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("")); - /* If this is NOT the MUX module we want, we go recursively */ - if (mux_module != child_module) { - rec_print_pnr_sdc_disable_routing_multiplexer_outputs(fp, flatten_names, - module_manager, - child_module, - mux_module, - child_module_path); - continue; - } + /* 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)); - /* Validate file stream */ - valid_file_stream(fp); - - /* 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; - } + /* 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 BasicPort& output_port : module_manager.module_ports_by_type(mux_module, ModuleManager::MODULE_OUTPUT_PORT)) { + 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)), + output_port.get_name()); } } + + /* 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 */ diff --git a/openfpga/src/fpga_sdc/sdc_mux_utils.h b/openfpga/src/fpga_sdc/sdc_mux_utils.h index 70089aa93..a63a42bb3 100644 --- a/openfpga/src/fpga_sdc/sdc_mux_utils.h +++ b/openfpga/src/fpga_sdc/sdc_mux_utils.h @@ -6,6 +6,8 @@ *******************************************************************/ #include #include +#include "mux_library.h" +#include "circuit_library.h" #include "module_manager.h" /******************************************************************** @@ -15,12 +17,19 @@ /* begin namespace openfpga */ namespace openfpga { -void rec_print_pnr_sdc_disable_routing_multiplexer_outputs(std::fstream& fp, - const bool& flatten_names, - const ModuleManager& module_manager, - const ModuleId& parent_module, - const ModuleId& mux_module, - const std::string& parent_module_path); +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); + +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 */ diff --git a/openfpga/src/fpga_sdc/sdc_writer_utils.cpp b/openfpga/src/fpga_sdc/sdc_writer_utils.cpp index 93a3a94aa..e448d806e 100644 --- a/openfpga/src/fpga_sdc/sdc_writer_utils.cpp +++ b/openfpga/src/fpga_sdc/sdc_writer_utils.cpp @@ -4,12 +4,16 @@ #include #include #include +#include /* Headers from vtrutil library */ #include "vtr_assert.h" /* Headers from openfpgautil library */ #include "openfpga_digest.h" +#include "openfpga_wildcard_string.h" + +#include "openfpga_naming.h" #include "sdc_writer_utils.h" @@ -295,4 +299,111 @@ void print_sdc_set_port_output_delay(std::fstream& fp, 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> 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 */ diff --git a/openfpga/src/fpga_sdc/sdc_writer_utils.h b/openfpga/src/fpga_sdc/sdc_writer_utils.h index 4b353045b..7a3861737 100644 --- a/openfpga/src/fpga_sdc/sdc_writer_utils.h +++ b/openfpga/src/fpga_sdc/sdc_writer_utils.h @@ -74,6 +74,14 @@ void print_sdc_set_port_output_delay(std::fstream& fp, const BasicPort& clock_port, 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 */ #endif diff --git a/openfpga_flow/OpenFPGAShellScripts/configuration_chain_example_script.openfpga b/openfpga_flow/OpenFPGAShellScripts/configuration_chain_example_script.openfpga index 326bdba6a..8b523f4ae 100644 --- a/openfpga_flow/OpenFPGAShellScripts/configuration_chain_example_script.openfpga +++ b/openfpga_flow/OpenFPGAShellScripts/configuration_chain_example_script.openfpga @@ -58,6 +58,9 @@ write_pnr_sdc --file ./SDC # 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 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_analysis_sdc --file ./SDC_analysis diff --git a/openfpga_flow/OpenFPGAShellScripts/example_script.openfpga b/openfpga_flow/OpenFPGAShellScripts/example_script.openfpga index 1ccd8418d..f1dc80820 100644 --- a/openfpga_flow/OpenFPGAShellScripts/example_script.openfpga +++ b/openfpga_flow/OpenFPGAShellScripts/example_script.openfpga @@ -55,6 +55,9 @@ write_verilog_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE # - Turn on every options here 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_analysis_sdc --file ./SDC_analysis