From 4c0953415b87984e969a9a1d4161fc89b3721dc7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Tue, 12 May 2020 20:44:53 -0600 Subject: [PATCH] add configuration chain sdc writer --- openfpga/src/base/openfpga_sdc.cpp | 36 ++++ openfpga/src/base/openfpga_sdc.h | 3 + openfpga/src/base/openfpga_sdc_command.cpp | 49 +++++ .../configuration_chain_sdc_writer.cpp | 175 ++++++++++++++++++ .../fpga_sdc/configuration_chain_sdc_writer.h | 26 +++ openfpga/src/fpga_sdc/sdc_writer_utils.cpp | 12 +- 6 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp create mode 100644 openfpga/src/fpga_sdc/configuration_chain_sdc_writer.h diff --git a/openfpga/src/base/openfpga_sdc.cpp b/openfpga/src/base/openfpga_sdc.cpp index d492e653a..93bd46959 100644 --- a/openfpga/src/base/openfpga_sdc.cpp +++ b/openfpga/src/base/openfpga_sdc.cpp @@ -15,6 +15,7 @@ #include "circuit_library_utils.h" #include "pnr_sdc_writer.h" #include "analysis_sdc_writer.h" +#include "configuration_chain_sdc_writer.h" #include "openfpga_sdc.h" /* Include global variables of VPR */ @@ -102,6 +103,41 @@ int write_pnr_sdc(OpenfpgaContext& openfpga_ctx, return CMD_EXEC_SUCCESS; } +/******************************************************************** + * A wrapper function to call the PnR SDC generator on configuration chain + * of FPGA-SDC + *******************************************************************/ +int write_configuration_chain_sdc(const OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context) { + /* If the configuration protocol is not a configuration chain, we will not write anything */ + if (CONFIG_MEM_SCAN_CHAIN != openfpga_ctx.arch().config_protocol.type()) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Configuration protocol is %s. Expected %s to write SDC!\n", + CONFIG_PROTOCOL_TYPE_STRING[openfpga_ctx.arch().config_protocol.type()], + CONFIG_PROTOCOL_TYPE_STRING[CONFIG_MEM_SCAN_CHAIN]); + return CMD_EXEC_FATAL_ERROR; + } + + /* Get command options */ + CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_time_unit = cmd.option("time_unit"); + CommandOptionId opt_min_delay = cmd.option("min_delay"); + CommandOptionId opt_max_delay = cmd.option("max_delay"); + + std::string sdc_dir_path = format_dir_path(cmd_context.option_value(cmd, opt_output_dir)); + + float time_unit = string_to_time_unit(cmd_context.option_value(cmd, opt_time_unit)); + + /* Write the SDC for configuration chain */ + print_pnr_sdc_constrain_configurable_chain(cmd_context.option_value(cmd, opt_output_dir), + time_unit, + std::stof(cmd_context.option_value(cmd, opt_max_delay)), + std::stof(cmd_context.option_value(cmd, opt_min_delay)), + openfpga_ctx.module_graph()); + + 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 99a87f899..060c304bc 100644 --- a/openfpga/src/base/openfpga_sdc.h +++ b/openfpga/src/base/openfpga_sdc.h @@ -18,6 +18,9 @@ namespace openfpga { int write_pnr_sdc(OpenfpgaContext& openfpga_ctx, const Command& cmd, const CommandContext& cmd_context); +int write_configuration_chain_sdc(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 a0f578786..faa488931 100644 --- a/openfpga/src/base/openfpga_sdc_command.cpp +++ b/openfpga/src/base/openfpga_sdc_command.cpp @@ -80,6 +80,45 @@ ShellCommandId add_openfpga_write_pnr_sdc_command(openfpga::Shell& shell, + const ShellCommandClassId& cmd_class_id, + const std::vector& dependent_cmds) { + Command shell_cmd("write_configuration_chain_sdc"); + + /* Add an option '--file' in short '-f'*/ + CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the SDC file to constrain configuration chain"); + shell_cmd.set_option_short_name(output_opt, "f"); + shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING); + + /* Add an option '--time_unit' */ + CommandOptionId time_unit_opt = shell_cmd.add_option("time_unit", false, "Specify the time unit in SDC files. Acceptable is [a|f|p|n|u|m|k|M]s"); + shell_cmd.set_option_require_value(time_unit_opt, openfpga::OPT_STRING); + + /* Add an option '--min_delay' */ + CommandOptionId min_dly_opt = shell_cmd.add_option("min_delay", false, "Specify the minimum delay to be used."); + shell_cmd.set_option_require_value(min_dly_opt, openfpga::OPT_STRING); + + /* Add an option '--max_delay' */ + CommandOptionId max_dly_opt = shell_cmd.add_option("max_delay", false, "Specify the maximum delay to be used."); + shell_cmd.set_option_require_value(max_dly_opt, openfpga::OPT_STRING); + + /* Add command 'write_configuration_chain_sdc' to the Shell */ + ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate SDC files to constrain the configuration chain for FPGA fabric"); + shell.set_command_class(shell_cmd_id, cmd_class_id); + shell.set_command_const_execute_function(shell_cmd_id, write_configuration_chain_sdc); + + /* 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 @@ -134,6 +173,16 @@ void add_openfpga_sdc_commands(openfpga::Shell& shell) { openfpga_sdc_cmd_class, pnr_sdc_cmd_dependency); + /******************************** + * Command 'write_configuration_chain_sdc' + */ + /* The 'write_configuration_chain_sdc' command should NOT be executed before 'build_fabric' */ + std::vector cc_sdc_cmd_dependency; + cc_sdc_cmd_dependency.push_back(build_fabric_id); + add_openfpga_write_configuration_chain_sdc_command(shell, + openfpga_sdc_cmd_class, + cc_sdc_cmd_dependency); + /******************************** * Command 'write_analysis_sdc' */ diff --git a/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp b/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp new file mode 100644 index 000000000..ce41e6851 --- /dev/null +++ b/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.cpp @@ -0,0 +1,175 @@ +/******************************************************************** + * 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 openfpgautil library */ +#include "openfpga_scale.h" +#include "openfpga_port.h" +#include "openfpga_digest.h" + +#include "openfpga_naming.h" + +#include "sdc_writer_utils.h" +#include "configuration_chain_sdc_writer.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print SDC commands to constrain the timing between outputs and inputs + * of all the configurable memory modules + * + * |<------Max/Min delay-->| + * | | + * +------+ out in +------+ + * | CCFF |---------------------->| CCFF | + * +------+ +------+ + * + * 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 + * + *******************************************************************/ +static +void rec_print_pnr_sdc_constrain_configurable_chain(std::fstream& fp, + const float& tmax, + const float& tmin, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const std::string& parent_module_path, + std::string& previous_module_path, + ModuleId& previous_module) { + + /* For each configurable child, we will go one level down in priority */ + for (size_t child_index = 0; child_index < module_manager.configurable_children(parent_module).size(); ++child_index) { + std::string child_module_path = parent_module_path; + ModuleId child_module_id = module_manager.configurable_children(parent_module)[child_index]; + size_t child_instance_id = module_manager.configurable_child_instances(parent_module)[child_index]; + std::string child_instance_name; + if (true == module_manager.instance_name(parent_module, child_module_id, child_instance_id).empty()) { + child_instance_name = generate_instance_name(module_manager.module_name(child_module_id), child_instance_id); + } else { + child_instance_name = module_manager.instance_name(parent_module, child_module_id, child_instance_id); + } + + child_module_path += child_instance_name; + + child_module_path = format_dir_path(child_module_path); + + rec_print_pnr_sdc_constrain_configurable_chain(fp, + tmax, tmin, + module_manager, + child_module_id, + child_module_path, + previous_module_path, + previous_module); + } + + /* If there is no configurable children any more, this is a leaf module, print a SDC command for disable timing */ + if (0 < module_manager.configurable_children(parent_module).size()) { + return; + } + + /* Validate file stream */ + valid_file_stream(fp); + + /* Disable timing for each output port of this module */ + if (!previous_module_path.empty()) { + bool first_port = true; + for (const BasicPort& output_port : module_manager.module_ports_by_type(previous_module, ModuleManager::MODULE_OUTPUT_PORT)) { + /* Only the first output port will be considered, + * being consistent with build_memory_module.cpp:395 + */ + if (false == first_port) { + continue; + } + + for (const BasicPort& input_port : module_manager.module_ports_by_type(parent_module, ModuleManager::MODULE_INPUT_PORT)) { + print_pnr_sdc_constrain_max_delay(fp, + previous_module_path, + generate_sdc_port(output_port), + parent_module_path, + generate_sdc_port(input_port), + tmax); + + print_pnr_sdc_constrain_min_delay(fp, + previous_module_path, + generate_sdc_port(output_port), + parent_module_path, + generate_sdc_port(input_port), + tmin); + } + + first_port = false; + } + } + + /* Update previous module */ + previous_module_path = parent_module_path; + previous_module = parent_module; +} + + +/******************************************************************** + * Break combinational loops in FPGA fabric, which mainly come from + * configurable memory cells. + * To handle this, we disable the outputs of memory cells + *******************************************************************/ +void print_pnr_sdc_constrain_configurable_chain(const std::string& sdc_fname, + const float& time_unit, + const float& max_delay, + const float& min_delay, + 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 constrain configurable chain 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("Timing constraints for configurable chains used in PnR")); + + /* Print time unit for the SDC file */ + print_sdc_timescale(fp, time_unit_to_string(time_unit)); + + 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)); + + /* Go recursively in the module manager, starting from the top-level module: instance id of the top-level module is 0 by default */ + std::string previous_module_path; + ModuleId previous_module = ModuleId::INVALID(); + rec_print_pnr_sdc_constrain_configurable_chain(fp, + max_delay/time_unit, min_delay/time_unit, + module_manager, top_module, + format_dir_path(module_manager.module_name(top_module)), + previous_module_path, + previous_module); + + /* Close file handler */ + fp.close(); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.h b/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.h new file mode 100644 index 000000000..1e636c8a9 --- /dev/null +++ b/openfpga/src/fpga_sdc/configuration_chain_sdc_writer.h @@ -0,0 +1,26 @@ +#ifndef CONFIGURATION_CHAIN_SDC_WRITER_H +#define CONFIGURATION_CHAIN_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 { + +void print_pnr_sdc_constrain_configurable_chain(const std::string& sdc_fname, + const float& time_unit, + const float& max_delay, + const float& min_delay, + const ModuleManager& module_manager); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_sdc/sdc_writer_utils.cpp b/openfpga/src/fpga_sdc/sdc_writer_utils.cpp index 85ff6edcb..93a3a94aa 100644 --- a/openfpga/src/fpga_sdc/sdc_writer_utils.cpp +++ b/openfpga/src/fpga_sdc/sdc_writer_utils.cpp @@ -95,14 +95,14 @@ void print_pnr_sdc_constrain_max_delay(std::fstream& fp, fp << " -from "; if (!src_instance_name.empty()) { - fp << src_instance_name << "/"; + fp << format_dir_path(src_instance_name); } fp << src_port_name; fp << " -to "; if (!des_instance_name.empty()) { - fp << des_instance_name << "/"; + fp << format_dir_path(des_instance_name); } fp << des_port_name; @@ -130,7 +130,7 @@ void print_pnr_sdc_regexp_constrain_max_delay(std::fstream& fp, fp << " -from "; fp << "[get_pins -regexp \""; if (!src_instance_name.empty()) { - fp << src_instance_name << "/"; + fp << format_dir_path(src_instance_name); } fp << src_port_name; @@ -140,7 +140,7 @@ void print_pnr_sdc_regexp_constrain_max_delay(std::fstream& fp, fp << "[get_pins -regexp \""; if (!des_instance_name.empty()) { - fp << des_instance_name << "/"; + fp << format_dir_path(des_instance_name); } fp << des_port_name; @@ -167,14 +167,14 @@ void print_pnr_sdc_constrain_min_delay(std::fstream& fp, fp << " -from "; if (!src_instance_name.empty()) { - fp << src_instance_name << "/"; + fp << format_dir_path(src_instance_name); } fp << src_port_name; fp << " -to "; if (!des_instance_name.empty()) { - fp << des_instance_name << "/"; + fp << format_dir_path(des_instance_name); } fp << des_port_name;