diff --git a/doc/src/vpr/command_line_usage.rst b/doc/src/vpr/command_line_usage.rst index 5a38fdc26b..61dd775abb 100644 --- a/doc/src/vpr/command_line_usage.rst +++ b/doc/src/vpr/command_line_usage.rst @@ -1830,6 +1830,16 @@ Analysis Options **Default:** ``off`` +.. option:: --gen_post_implementation_sdc { on | off } + + Generates an SDC file including a list of constraints that would + replicate the timing constraints that the timing analysis within + VPR followed during the flow. This can be helpful for flows that + use external timing analysis tools that have additional capabilities + or more detailed delay models than what VPR uses. + + **Default:** ``off`` + .. option:: --post_synth_netlist_unconn_inputs { unconnected | nets | gnd | vcc } Controls how unconnected input cell ports are handled in the post-synthesis netlist diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 07a826598b..dc692cdcd1 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -704,6 +704,7 @@ static void SetupAnalysisOpts(const t_options& Options, t_analysis_opts& analysi analysis_opts.gen_post_synthesis_netlist = Options.Generate_Post_Synthesis_Netlist; analysis_opts.gen_post_implementation_merged_netlist = Options.Generate_Post_Implementation_Merged_Netlist; + analysis_opts.gen_post_implementation_sdc = Options.generate_post_implementation_sdc.value(); analysis_opts.timing_report_npaths = Options.timing_report_npaths; analysis_opts.timing_report_detail = Options.timing_report_detail; diff --git a/vpr/src/base/ShowSetup.cpp b/vpr/src/base/ShowSetup.cpp index f35c3f9308..fa81fa9f1a 100644 --- a/vpr/src/base/ShowSetup.cpp +++ b/vpr/src/base/ShowSetup.cpp @@ -677,6 +677,8 @@ static void ShowNetlistOpts(const t_netlist_opts& NetlistOpts) { static void ShowAnalysisOpts(const t_analysis_opts& AnalysisOpts) { VTR_LOG("AnalysisOpts.gen_post_synthesis_netlist: %s\n", (AnalysisOpts.gen_post_synthesis_netlist) ? "true" : "false"); + VTR_LOG("AnalysisOpts.gen_post_implementation_merged_netlist: %s\n", AnalysisOpts.gen_post_implementation_merged_netlist ? "true" : "false"); + VTR_LOG("AnalysisOpts.gen_post_implementation_sdc: %s\n", AnalysisOpts.gen_post_implementation_sdc ? "true" : "false"); VTR_LOG("AnalysisOpts.timing_report_npaths: %d\n", AnalysisOpts.timing_report_npaths); VTR_LOG("AnalysisOpts.timing_report_skew: %s\n", AnalysisOpts.timing_report_skew ? "true" : "false"); VTR_LOG("AnalysisOpts.echo_dot_timing_graph_node: %s\n", AnalysisOpts.echo_dot_timing_graph_node.c_str()); diff --git a/vpr/src/base/netlist_writer.cpp b/vpr/src/base/netlist_writer.cpp index f12bfdedf3..8a02b9a7be 100644 --- a/vpr/src/base/netlist_writer.cpp +++ b/vpr/src/base/netlist_writer.cpp @@ -72,6 +72,7 @@ #include #include "atom_netlist.h" #include "atom_netlist_utils.h" +#include "clock_modeling.h" #include "globals.h" #include "logic_vec.h" #include "netlist_walker.h" @@ -2644,6 +2645,147 @@ std::string join_identifier(std::string lhs, std::string rhs) { return lhs + '_' + rhs; } +/** + * @brief Add the original SDC constraints that VPR used during its flow to the + * given SDC file. + * + * @param sdc_os + * Output stream for the target SDC file. The original SDC file passed into + * VPR will be appended to this file. + * @param timing_info + * Information on the timing within VPR. This is used to get the file path + * to the original SDC file. + */ +void add_original_sdc_to_post_implemented_sdc_file(std::ofstream& sdc_os, + const t_timing_inf& timing_info) { + // Open the original SDC file provided to VPR. + std::ifstream original_sdc_file; + original_sdc_file.open(timing_info.SDCFile); + if (!original_sdc_file.is_open()) { + // TODO: VPR automatically creates SDC constraints by default if no SDC + // file is provided. These can be replicated here if needed. + VPR_FATAL_ERROR(VPR_ERROR_IMPL_NETLIST_WRITER, + "No SDC files provided to VPR; currently cannot generate " + "post-implementation SDC file without it"); + } + + // Write a header to declare where these commands came from. + sdc_os << "\n"; + sdc_os << "#******************************************************************************#\n"; + sdc_os << "# The following SDC commands were provided to VPR from the given SDC file:\n"; + sdc_os << "# \t" << timing_info.SDCFile << "\n"; + sdc_os << "#******************************************************************************#\n"; + + // Append the original SDC file to the post-implementation SDC file. + sdc_os << original_sdc_file.rdbuf(); +} + +/** + * @brief Add propagated clock commands to the given SDC file based on the set + * clock modeling. + * + * This is necessary since VPR decides if clocks are routed or not, which has + * affects on how timing analysis is performed on the clocks. + * + * @param sdc_os + * The file stream to add the propagated clock commands to. + * @param clock_modeling + * The type of clock modeling used by VPR during the CAD flow. + */ +void add_propagated_clocks_to_sdc_file(std::ofstream& sdc_os, + e_clock_modeling clock_modeling) { + + // Ideal and routed clocks are handled by the code below. Other clock models + // like dedicated routing are not supported yet. + // TODO: Supporting dedicated routing should be simple; however it should + // be investigated. Tried quickly but found that the delays produced + // were off by 0.003 ns. Need to investigate why. + if (clock_modeling != e_clock_modeling::ROUTED_CLOCK && clock_modeling != e_clock_modeling::IDEAL_CLOCK) { + VPR_FATAL_ERROR(VPR_ERROR_IMPL_NETLIST_WRITER, + "Only ideal and routed clock modeling are currentlt " + "supported for post-implementation SDC file generation"); + } + + // The timing constraints contain information on all the clocks in the circuit + // (provided by the user-provided SDC file). + const auto timing_constraints = g_vpr_ctx.timing().constraints; + + // Collect the non-virtual clocks. Virtual clocks are not routed and + // do not get propageted. + std::vector non_virtual_clocks; + for (tatum::DomainId clock_domain_id : timing_constraints->clock_domains()) { + if (!timing_constraints->is_virtual_clock(clock_domain_id)) { + non_virtual_clocks.push_back(clock_domain_id); + } + } + + // If there are no non-virtual clocks, no extra commands needed. Virtual + // clocks are ideal. + if (non_virtual_clocks.empty()) { + return; + } + + // Append a header to explain why these commands are added. + sdc_os << "\n"; + sdc_os << "#******************************************************************************#\n"; + sdc_os << "# The following are clock domains in VPR which have delays on their edges.\n"; + sdc_os << "#\n"; + sdc_os << "# Any non-virtual clock has its delay determined and written out as part of a"; + sdc_os << "# propagated clock command. If VPR was instructed not to route the clock, this"; + sdc_os << "# delay will be an underestimate.\n"; + sdc_os << "#\n"; + sdc_os << "# Note: Virtual clocks do not get routed and are treated as ideal.\n"; + sdc_os << "#******************************************************************************#\n"; + + // Add the SDC commands to set the non-virtual clocks as propagated (non-ideal); + // Note: It was decided that "ideal" (dont route) clock modeling in VPR should still + // set the clocks as propagated to allow for the input pad delays of + // clocks to be included. The SDF delay annotations on clock signals + // should make this safe to do. + for (tatum::DomainId clock_domain_id : non_virtual_clocks) { + sdc_os << "set_propagated_clock "; + sdc_os << timing_constraints->clock_domain_name(clock_domain_id); + sdc_os << "\n"; + } +} + +/** + * @brief Generates a post-implementation SDC file with the given file name + * based on the timing info and clock modeling set for VPR. + * + * @param sdc_filename + * The file name of the SDC file to generate. + * @param timing_info + * Information on the timing used in the VPR flow. + * @param clock_modeling + * The type of clock modeling used by VPR during its flow. + */ +void generate_post_implementation_sdc(const std::string& sdc_filename, + const t_timing_inf& timing_info, + e_clock_modeling clock_modeling) { + if (!timing_info.timing_analysis_enabled) { + VTR_LOG_WARN("Timing analysis is disabled. Post-implementation SDC file " + "will not be generated.\n"); + return; + } + + // Begin writing the post-implementation SDC file. + std::ofstream sdc_os(sdc_filename); + + // Print a header declaring that this file is auto-generated and what version + // of VTR produced it. + sdc_os << "#******************************************************************************#\n"; + sdc_os << "# SDC automatically generated by VPR from a post-place-and-route implementation.\n"; + sdc_os << "#\tVersion: " << vtr::VERSION << "\n"; + sdc_os << "#******************************************************************************#\n"; + + // Add the original SDC that VPR used during its flow. + add_original_sdc_to_post_implemented_sdc_file(sdc_os, timing_info); + + // Add propagated clocks to SDC file if needed. + add_propagated_clocks_to_sdc_file(sdc_os, clock_modeling); +} + } // namespace // @@ -2651,7 +2793,12 @@ std::string join_identifier(std::string lhs, std::string rhs) { // ///@brief Main routine for this file. See netlist_writer.h for details. -void netlist_writer(const std::string basename, std::shared_ptr delay_calc, const LogicalModels& models, t_analysis_opts opts) { +void netlist_writer(const std::string basename, + std::shared_ptr delay_calc, + const LogicalModels& models, + const t_timing_inf& timing_info, + e_clock_modeling clock_modeling, + t_analysis_opts opts) { std::string verilog_filename = basename + "_post_synthesis.v"; std::string blif_filename = basename + "_post_synthesis.blif"; std::string sdf_filename = basename + "_post_synthesis.sdf"; @@ -2659,7 +2806,6 @@ void netlist_writer(const std::string basename, std::shared_ptr delay_calc, const LogicalModels& models, t_analysis_opts opts); +void netlist_writer(const std::string basename, + std::shared_ptr delay_calc, + const LogicalModels& models, + const t_timing_inf& timing_info, + e_clock_modeling clock_modeling, + t_analysis_opts opts); /** * @brief Writes out the post implementation netlist in Verilog format. diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index 82de8cc0ef..d4b7089258 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -3012,6 +3012,16 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); + analysis_grp.add_argument(args.generate_post_implementation_sdc, "--gen_post_implementation_sdc") + .help( + "Generates an SDC file including a list of constraints that would " + "replicate the timing constraints that the timing analysis within " + "VPR followed during the flow. This can be helpful for flows that " + "use external timing analysis tools that have additional capabilities " + "or more detailed delay models than what VPR uses") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + analysis_grp.add_argument(args.timing_report_npaths, "--timing_report_npaths") .help("Controls how many timing paths are reported.") .default_value("100") diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 9056cdbf9f..61051940b3 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -263,6 +263,7 @@ struct t_options { argparse::ArgValue full_stats; argparse::ArgValue Generate_Post_Synthesis_Netlist; argparse::ArgValue Generate_Post_Implementation_Merged_Netlist; + argparse::ArgValue generate_post_implementation_sdc; argparse::ArgValue timing_report_npaths; argparse::ArgValue timing_report_detail; argparse::ArgValue timing_report_skew; diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 97f8f16463..560b3e2d52 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -1467,7 +1467,7 @@ void vpr_analysis(const Netlist<>& net_list, //Write the post-synthesis netlist if (vpr_setup.AnalysisOpts.gen_post_synthesis_netlist) { netlist_writer(atom_ctx.netlist().netlist_name(), analysis_delay_calc, - Arch.models, vpr_setup.AnalysisOpts); + Arch.models, vpr_setup.Timing, vpr_setup.clock_modeling, vpr_setup.AnalysisOpts); } //Write the post-implementation merged netlist diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index d19496411f..8fc929ed06 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1342,6 +1342,7 @@ struct t_analysis_opts { bool gen_post_synthesis_netlist; bool gen_post_implementation_merged_netlist; + bool gen_post_implementation_sdc; e_post_synth_netlist_unconn_handling post_synth_netlist_unconn_input_handling; e_post_synth_netlist_unconn_handling post_synth_netlist_unconn_output_handling; bool post_synth_netlist_module_parameters;