Skip to content

[STA] Generating SDC Commands Post-Implementation #3016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/src/vpr/command_line_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1710,6 +1710,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 file is expected to be used
by external timing analyzers to get the same result as VPR's
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd replace the last sentence: This can be helpful for flows that use external timing analysis tools that have additional capabilities or more detailed delay models than VPR uses.

timing analysis.

**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
Expand Down
1 change: 1 addition & 0 deletions vpr/src/base/SetupVPR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,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;
Expand Down
2 changes: 2 additions & 0 deletions vpr/src/base/ShowSetup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,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());
Expand Down
138 changes: 136 additions & 2 deletions vpr/src/base/netlist_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
#include <vector>
#include "atom_netlist.h"
#include "atom_netlist_utils.h"
#include "clock_modeling.h"
#include "globals.h"
#include "logic_vec.h"
#include "netlist_walker.h"
Expand Down Expand Up @@ -2644,22 +2645,145 @@ 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.
*/
void add_original_sdc_to_post_implemented_sdc_file(std::ofstream& sdc_os,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static method? Or move comment to header file.
Comment parameters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment if you append to the sdc_os stream, or expect it to be empty.

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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VPR, currently --> VPR; currently ... (add period at end).

}

// 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.
*/
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.
if (clock_modeling != e_clock_modeling::ROUTED_CLOCK && clock_modeling != e_clock_modeling::IDEAL_CLOCK) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should work. We should already extract the delays and your code path presumably already writes those out as propagated clock delays.
Mustafa Abbas has some tests for routing dedicated clocks; you could find one of those and use it to test this functionality. If it works, remove the if.

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<tatum::DomainId> 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, any other
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this comment mean? virtual clocks are ideal? (clarify)

// clocks will be set to be 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 << "# Clocks that have been routed or whose input signal have been placed to an\n";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can make the comment more general. "Any non-virtual clock has its delay determined and written out as part of a propagated clock command. If VPR was instructed not to route the clock, this delay will be an underestimate."
"propageted" -> "propagated"

sdc_os << "# input pad have delays on their signals. Thus, they should be treated as\n";
sdc_os << "# propageted (non-ideal) clocks.\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" clock modeling in VPR should still
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"ideal (don't route)"

// 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.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make methods static or move comments to the header file.

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

//
// Externally Accessible Functions
//

///@brief Main routine for this file. See netlist_writer.h for details.
void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> delay_calc, const LogicalModels& models, t_analysis_opts opts) {
void netlist_writer(const std::string basename,
std::shared_ptr<const AnalysisDelayCalculator> 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";

VTR_LOG("Writing Implementation Netlist: %s\n", verilog_filename.c_str());
VTR_LOG("Writing Implementation Netlist: %s\n", blif_filename.c_str());
VTR_LOG("Writing Implementation SDF : %s\n", sdf_filename.c_str());

std::ofstream verilog_os(verilog_filename);
std::ofstream blif_os(blif_filename);
std::ofstream sdf_os(sdf_filename);
Expand All @@ -2669,6 +2793,16 @@ void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDe
NetlistWalker nl_walker(visitor);

nl_walker.walk();

if (opts.gen_post_implementation_sdc) {
std::string sdc_filename = basename + "_post_synthesis.sdc";

VTR_LOG("Writing Implementation SDC : %s\n", sdc_filename.c_str());

generate_post_implementation_sdc(sdc_filename,
timing_info,
clock_modeling);
}
}

///@brief Main routine for this file. See netlist_writer.h for details.
Expand Down
7 changes: 6 additions & 1 deletion vpr/src/base/netlist_writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ class LogicalModels;
* All written filenames end in {basename}_post_synthesis.{fmt} where {basename} is the
* basename argument and {fmt} is the file format (e.g. v, blif, sdf)
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have the energy for it, you could document the parameters.

void netlist_writer(const std::string basename, std::shared_ptr<const AnalysisDelayCalculator> delay_calc, const LogicalModels& models, t_analysis_opts opts);
void netlist_writer(const std::string basename,
std::shared_ptr<const AnalysisDelayCalculator> 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.
Expand Down
10 changes: 10 additions & 0 deletions vpr/src/base/read_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2945,6 +2945,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<bool, ParseOnOff>(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 file is expected to be used "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Match the .rst text comment.

"by external timing analyzers to get the same result as VPR's "
"timing analysis.")
.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")
Expand Down
1 change: 1 addition & 0 deletions vpr/src/base/read_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ struct t_options {
argparse::ArgValue<bool> full_stats;
argparse::ArgValue<bool> Generate_Post_Synthesis_Netlist;
argparse::ArgValue<bool> Generate_Post_Implementation_Merged_Netlist;
argparse::ArgValue<bool> generate_post_implementation_sdc;
argparse::ArgValue<int> timing_report_npaths;
argparse::ArgValue<e_timing_report_detail> timing_report_detail;
argparse::ArgValue<bool> timing_report_skew;
Expand Down
2 changes: 1 addition & 1 deletion vpr/src/base/vpr_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1468,7 +1468,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
Expand Down
1 change: 1 addition & 0 deletions vpr/src/base/vpr_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,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;
Expand Down