Skip to content

Commit e79415e

Browse files
[STA] Generating SDC Commands Post-Implementation
Added an option to have VPR generate an SDC file containing the timing commands required for an external timing analysis of the post- implementation netlist to match VPR's timing analysis.
1 parent 43f2a94 commit e79415e

9 files changed

+167
-4
lines changed

doc/src/vpr/command_line_usage.rst

+10
Original file line numberDiff line numberDiff line change
@@ -1710,6 +1710,16 @@ Analysis Options
17101710

17111711
**Default:** ``off``
17121712

1713+
.. option:: --gen_post_implementation_sdc { on | off }
1714+
1715+
Generates an SDC file including a list of constraints that would
1716+
replicate the timing constraints that the timing analysis within
1717+
VPR followed during the flow. This file is expected to be used
1718+
by external timing analyzers to get the same result as VPR's
1719+
timing analysis.
1720+
1721+
**Default:** ``off``
1722+
17131723
.. option:: --post_synth_netlist_unconn_inputs { unconnected | nets | gnd | vcc }
17141724

17151725
Controls how unconnected input cell ports are handled in the post-synthesis netlist

vpr/src/base/SetupVPR.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,7 @@ static void SetupAnalysisOpts(const t_options& Options, t_analysis_opts& analysi
703703

704704
analysis_opts.gen_post_synthesis_netlist = Options.Generate_Post_Synthesis_Netlist;
705705
analysis_opts.gen_post_implementation_merged_netlist = Options.Generate_Post_Implementation_Merged_Netlist;
706+
analysis_opts.gen_post_implementation_sdc = Options.generate_post_implementation_sdc.value();
706707

707708
analysis_opts.timing_report_npaths = Options.timing_report_npaths;
708709
analysis_opts.timing_report_detail = Options.timing_report_detail;

vpr/src/base/ShowSetup.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,8 @@ static void ShowNetlistOpts(const t_netlist_opts& NetlistOpts) {
671671

672672
static void ShowAnalysisOpts(const t_analysis_opts& AnalysisOpts) {
673673
VTR_LOG("AnalysisOpts.gen_post_synthesis_netlist: %s\n", (AnalysisOpts.gen_post_synthesis_netlist) ? "true" : "false");
674+
VTR_LOG("AnalysisOpts.gen_post_implementation_merged_netlist: %s\n", AnalysisOpts.gen_post_implementation_merged_netlist ? "true" : "false");
675+
VTR_LOG("AnalysisOpts.gen_post_implementation_sdc: %s\n", AnalysisOpts.gen_post_implementation_sdc ? "true" : "false");
674676
VTR_LOG("AnalysisOpts.timing_report_npaths: %d\n", AnalysisOpts.timing_report_npaths);
675677
VTR_LOG("AnalysisOpts.timing_report_skew: %s\n", AnalysisOpts.timing_report_skew ? "true" : "false");
676678
VTR_LOG("AnalysisOpts.echo_dot_timing_graph_node: %s\n", AnalysisOpts.echo_dot_timing_graph_node.c_str());

vpr/src/base/netlist_writer.cpp

+135-2
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#include <vector>
7373
#include "atom_netlist.h"
7474
#include "atom_netlist_utils.h"
75+
#include "clock_modeling.h"
7576
#include "globals.h"
7677
#include "logic_vec.h"
7778
#include "netlist_walker.h"
@@ -2644,22 +2645,144 @@ std::string join_identifier(std::string lhs, std::string rhs) {
26442645
return lhs + '_' + rhs;
26452646
}
26462647

2648+
/**
2649+
* @brief Add the original SDC constraints that VPR used during its flow to the
2650+
* given SDC file.
2651+
*/
2652+
void add_original_sdc_to_post_implemented_sdc_file(std::ofstream& sdc_os,
2653+
const t_timing_inf& timing_info) {
2654+
// Open the original SDC file provided to VPR.
2655+
std::ifstream original_sdc_file;
2656+
original_sdc_file.open(timing_info.SDCFile);
2657+
if (!original_sdc_file.is_open()) {
2658+
// TODO: VPR automatically creates SDC constraints by default if no SDC
2659+
// file is provided. These can be replicated here if needed.
2660+
VPR_FATAL_ERROR(VPR_ERROR_IMPL_NETLIST_WRITER,
2661+
"No SDC files provided to VPR, currently cannot generate "
2662+
"post-implementation SDC file without it");
2663+
}
2664+
2665+
// Write a header to declare where these commands came from.
2666+
sdc_os << "\n";
2667+
sdc_os << "#******************************************************************************#\n";
2668+
sdc_os << "# The following SDC commands were provided to VPR from the given SDC file:\n";
2669+
sdc_os << "# \t" << timing_info.SDCFile << "\n";
2670+
sdc_os << "#******************************************************************************#\n";
2671+
2672+
// Append the original SDC file to the post-implementation SDC file.
2673+
sdc_os << original_sdc_file.rdbuf();
2674+
}
2675+
2676+
/**
2677+
* @brief Add propagated clock commands to the given SDC file based on the set
2678+
* clock modeling.
2679+
*
2680+
* This is necessary since VPR decides if clocks are routed or not, which has
2681+
* affects on how timing analysis is performed on the clocks.
2682+
*/
2683+
void add_propagated_clocks_to_sdc_file(std::ofstream& sdc_os,
2684+
e_clock_modeling clock_modeling) {
2685+
2686+
// If all clocks are treated as ideal clocks by VPR, do not add any extra SDC
2687+
// commands. By default, clocks are treated as ideal by many tools.
2688+
if (clock_modeling == e_clock_modeling::IDEAL_CLOCK) {
2689+
return;
2690+
}
2691+
2692+
if (clock_modeling != e_clock_modeling::ROUTED_CLOCK) {
2693+
VPR_FATAL_ERROR(VPR_ERROR_IMPL_NETLIST_WRITER,
2694+
"Only ideal and routed clock modeling are currentlt "
2695+
"supported for post-implementation SDC file generation");
2696+
}
2697+
2698+
// The timing constraints contain information on all the clocks in the circuit
2699+
// (provided by the user-provided SDC file).
2700+
const auto timing_constraints = g_vpr_ctx.timing().constraints;
2701+
2702+
// Collect the non-virtual clocks. Virtual clocks are not routed and
2703+
// do not get propageted.
2704+
std::vector<tatum::DomainId> non_virtual_clocks;
2705+
for (tatum::DomainId clock_domain_id : timing_constraints->clock_domains()) {
2706+
if (!timing_constraints->is_virtual_clock(clock_domain_id)) {
2707+
non_virtual_clocks.push_back(clock_domain_id);
2708+
}
2709+
}
2710+
2711+
// If there are no non-virtual clocks, no extra commands needed, any other
2712+
// clocks will be set to be ideal.
2713+
if (non_virtual_clocks.empty()) {
2714+
return;
2715+
}
2716+
2717+
// Append a header to explain why these commands are added.
2718+
sdc_os << "\n";
2719+
sdc_os << "#******************************************************************************#\n";
2720+
sdc_os << "# The following are clock domains in VPR which have been routed.\n";
2721+
sdc_os << "#\n";
2722+
sdc_os << "# Since these clocks have been routed, they have delays on their signals.\n";
2723+
sdc_os << "# Thus, they should be treated as propageted (non-ideal) clocks.\n";
2724+
sdc_os << "#\n";
2725+
sdc_os << "# Note: Virtual clocks do not get routed and are treated as ideal.\n";
2726+
sdc_os << "#******************************************************************************#\n";
2727+
2728+
// Add the SDC commands to set the non-virtual clocks as propagated (non-ideal);
2729+
for (tatum::DomainId clock_domain_id : non_virtual_clocks) {
2730+
sdc_os << "set_propagated_clock ";
2731+
sdc_os << timing_constraints->clock_domain_name(clock_domain_id);
2732+
sdc_os << "\n";
2733+
}
2734+
}
2735+
2736+
/**
2737+
* @brief Generates a post-implementation SDC file with the given file name
2738+
* based on the timing info and clock modeling set for VPR.
2739+
*/
2740+
void generate_post_implementation_sdc(const std::string& sdc_filename,
2741+
const t_timing_inf& timing_info,
2742+
e_clock_modeling clock_modeling) {
2743+
if (!timing_info.timing_analysis_enabled) {
2744+
VTR_LOG_WARN("Timing analysis is disabled. Post-implementation SDC file "
2745+
"will not be generated.\n");
2746+
return;
2747+
}
2748+
2749+
// Begin writing the post-implementation SDC file.
2750+
std::ofstream sdc_os(sdc_filename);
2751+
2752+
// Print a header declaring that this file is auto-generated and what version
2753+
// of VTR produced it.
2754+
sdc_os << "#******************************************************************************#\n";
2755+
sdc_os << "# SDC automatically generated by VPR from a post-place-and-route implementation.\n";
2756+
sdc_os << "#\tVersion: " << vtr::VERSION << "\n";
2757+
sdc_os << "#******************************************************************************#\n";
2758+
2759+
// Add the original SDC that VPR used during its flow.
2760+
add_original_sdc_to_post_implemented_sdc_file(sdc_os, timing_info);
2761+
2762+
// Add propagated clocks to SDC file if needed.
2763+
add_propagated_clocks_to_sdc_file(sdc_os, clock_modeling);
2764+
}
2765+
26472766
} // namespace
26482767

26492768
//
26502769
// Externally Accessible Functions
26512770
//
26522771

26532772
///@brief Main routine for this file. See netlist_writer.h for details.
2654-
void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> delay_calc, const LogicalModels& models, t_analysis_opts opts) {
2773+
void netlist_writer(const std::string basename,
2774+
std::shared_ptr<const AnalysisDelayCalculator> delay_calc,
2775+
const LogicalModels& models,
2776+
const t_timing_inf& timing_info,
2777+
e_clock_modeling clock_modeling,
2778+
t_analysis_opts opts) {
26552779
std::string verilog_filename = basename + "_post_synthesis.v";
26562780
std::string blif_filename = basename + "_post_synthesis.blif";
26572781
std::string sdf_filename = basename + "_post_synthesis.sdf";
26582782

26592783
VTR_LOG("Writing Implementation Netlist: %s\n", verilog_filename.c_str());
26602784
VTR_LOG("Writing Implementation Netlist: %s\n", blif_filename.c_str());
26612785
VTR_LOG("Writing Implementation SDF : %s\n", sdf_filename.c_str());
2662-
26632786
std::ofstream verilog_os(verilog_filename);
26642787
std::ofstream blif_os(blif_filename);
26652788
std::ofstream sdf_os(sdf_filename);
@@ -2669,6 +2792,16 @@ void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDe
26692792
NetlistWalker nl_walker(visitor);
26702793

26712794
nl_walker.walk();
2795+
2796+
if (opts.gen_post_implementation_sdc) {
2797+
std::string sdc_filename = basename + "_post_synthesis.sdc";
2798+
2799+
VTR_LOG("Writing Implementation SDC : %s\n", sdc_filename.c_str());
2800+
2801+
generate_post_implementation_sdc(sdc_filename,
2802+
timing_info,
2803+
clock_modeling);
2804+
}
26722805
}
26732806

26742807
///@brief Main routine for this file. See netlist_writer.h for details.

vpr/src/base/netlist_writer.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ class LogicalModels;
1616
* All written filenames end in {basename}_post_synthesis.{fmt} where {basename} is the
1717
* basename argument and {fmt} is the file format (e.g. v, blif, sdf)
1818
*/
19-
void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> delay_calc, const LogicalModels& models, t_analysis_opts opts);
19+
void netlist_writer(const std::string basename,
20+
std::shared_ptr<const AnalysisDelayCalculator> delay_calc,
21+
const LogicalModels& models,
22+
const t_timing_inf& timing_info,
23+
e_clock_modeling clock_modeling,
24+
t_analysis_opts opts);
2025

2126
/**
2227
* @brief Writes out the post implementation netlist in Verilog format.

vpr/src/base/read_options.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -2945,6 +2945,16 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio
29452945
.default_value("off")
29462946
.show_in(argparse::ShowIn::HELP_ONLY);
29472947

2948+
analysis_grp.add_argument<bool, ParseOnOff>(args.generate_post_implementation_sdc, "--gen_post_implementation_sdc")
2949+
.help(
2950+
"Generates an SDC file including a list of constraints that would "
2951+
"replicate the timing constraints that the timing analysis within "
2952+
"VPR followed during the flow. This file is expected to be used "
2953+
"by external timing analyzers to get the same result as VPR's "
2954+
"timing analysis.")
2955+
.default_value("off")
2956+
.show_in(argparse::ShowIn::HELP_ONLY);
2957+
29482958
analysis_grp.add_argument(args.timing_report_npaths, "--timing_report_npaths")
29492959
.help("Controls how many timing paths are reported.")
29502960
.default_value("100")

vpr/src/base/read_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ struct t_options {
259259
argparse::ArgValue<bool> full_stats;
260260
argparse::ArgValue<bool> Generate_Post_Synthesis_Netlist;
261261
argparse::ArgValue<bool> Generate_Post_Implementation_Merged_Netlist;
262+
argparse::ArgValue<bool> generate_post_implementation_sdc;
262263
argparse::ArgValue<int> timing_report_npaths;
263264
argparse::ArgValue<e_timing_report_detail> timing_report_detail;
264265
argparse::ArgValue<bool> timing_report_skew;

vpr/src/base/vpr_api.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1468,7 +1468,7 @@ void vpr_analysis(const Netlist<>& net_list,
14681468
//Write the post-synthesis netlist
14691469
if (vpr_setup.AnalysisOpts.gen_post_synthesis_netlist) {
14701470
netlist_writer(atom_ctx.netlist().netlist_name(), analysis_delay_calc,
1471-
Arch.models, vpr_setup.AnalysisOpts);
1471+
Arch.models, vpr_setup.Timing, vpr_setup.clock_modeling, vpr_setup.AnalysisOpts);
14721472
}
14731473

14741474
//Write the post-implementation merged netlist

vpr/src/base/vpr_types.h

+1
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,7 @@ struct t_analysis_opts {
12761276

12771277
bool gen_post_synthesis_netlist;
12781278
bool gen_post_implementation_merged_netlist;
1279+
bool gen_post_implementation_sdc;
12791280
e_post_synth_netlist_unconn_handling post_synth_netlist_unconn_input_handling;
12801281
e_post_synth_netlist_unconn_handling post_synth_netlist_unconn_output_handling;
12811282
bool post_synth_netlist_module_parameters;

0 commit comments

Comments
 (0)