blob: 95f85fb605a54c63fd665181b270ecfeb9e14360 [file] [log] [blame]
#include "read_options.h"
#include "constant_nets.h"
#include "clock_modeling.h"
#include "vpr_error.h"
#include "argparse.hpp"
#include "vtr_memory.h"
#include "vtr_log.h"
#include "vtr_util.h"
#include "vtr_path.h"
using argparse::ConvertedValue;
using argparse::Provenance;
//Read and process VPR's command-line aruments
t_options read_options(int argc, const char** argv) {
t_options args = t_options(); //Explicitly initialize for zero initialization
auto parser = create_arg_parser(argv[0], args);
parser.parse_args(argc, argv);
set_conditional_defaults(args);
verify_args(args);
return args;
}
struct ParseOnOff {
ConvertedValue<bool> from_str(std::string str) {
ConvertedValue<bool> conv_value;
if (str == "on")
conv_value.set_value(true);
else if (str == "off")
conv_value.set_value(false);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to boolean (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
;
}
ConvertedValue<std::string> to_str(bool val) {
ConvertedValue<std::string> conv_value;
if (val)
conv_value.set_value("on");
else
conv_value.set_value("off");
return conv_value;
}
std::vector<std::string> default_choices() {
return {"on", "off"};
}
};
struct ParseCircuitFormat {
ConvertedValue<e_circuit_format> from_str(std::string str) {
ConvertedValue<e_circuit_format> conv_value;
if (str == "auto")
conv_value.set_value(e_circuit_format::AUTO);
else if (str == "blif")
conv_value.set_value(e_circuit_format::BLIF);
else if (str == "eblif")
conv_value.set_value(e_circuit_format::EBLIF);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_circuit_format (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_circuit_format val) {
ConvertedValue<std::string> conv_value;
if (val == e_circuit_format::AUTO)
conv_value.set_value("auto");
else if (val == e_circuit_format::BLIF)
conv_value.set_value("blif");
else {
VTR_ASSERT(val == e_circuit_format::EBLIF);
conv_value.set_value("eblif");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"auto", "blif", "eblif"};
}
};
struct ParseRoutePredictor {
ConvertedValue<e_routing_failure_predictor> from_str(std::string str) {
ConvertedValue<e_routing_failure_predictor> conv_value;
if (str == "safe")
conv_value.set_value(SAFE);
else if (str == "aggressive")
conv_value.set_value(AGGRESSIVE);
else if (str == "off")
conv_value.set_value(OFF);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_routing_failure_predictor (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_routing_failure_predictor val) {
ConvertedValue<std::string> conv_value;
if (val == SAFE)
conv_value.set_value("safe");
else if (val == AGGRESSIVE)
conv_value.set_value("aggressive");
else {
VTR_ASSERT(val == OFF);
conv_value.set_value("off");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"safe", "aggressive", "off"};
}
};
struct ParseRouterAlgorithm {
ConvertedValue<e_router_algorithm> from_str(std::string str) {
ConvertedValue<e_router_algorithm> conv_value;
if (str == "breadth_first")
conv_value.set_value(BREADTH_FIRST);
else if (str == "timing_driven")
conv_value.set_value(TIMING_DRIVEN);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_router_algorithm (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_router_algorithm val) {
ConvertedValue<std::string> conv_value;
if (val == BREADTH_FIRST)
conv_value.set_value("breadth_first");
else {
VTR_ASSERT(val == TIMING_DRIVEN);
conv_value.set_value("timing_driven");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"breadth_first", "timing_driven"};
}
};
struct RouteBudgetsAlgorithm {
ConvertedValue<e_routing_budgets_algorithm> from_str(std::string str) {
ConvertedValue<e_routing_budgets_algorithm> conv_value;
if (str == "minimax")
conv_value.set_value(MINIMAX);
else if (str == "scale_delay")
conv_value.set_value(SCALE_DELAY);
else if (str == "disable")
conv_value.set_value(DISABLE);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_routing_budget_algorithm (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_routing_budgets_algorithm val) {
ConvertedValue<std::string> conv_value;
if (val == MINIMAX)
conv_value.set_value("minimax");
else if (val == DISABLE)
conv_value.set_value("disable");
else {
VTR_ASSERT(val == SCALE_DELAY);
conv_value.set_value("scale_delay");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"minimax", "scale_delay", "disable"};
}
};
struct ParseRouteType {
ConvertedValue<e_route_type> from_str(std::string str) {
ConvertedValue<e_route_type> conv_value;
if (str == "global")
conv_value.set_value(GLOBAL);
else if (str == "detailed")
conv_value.set_value(DETAILED);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_router_algorithm (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_route_type val) {
ConvertedValue<std::string> conv_value;
if (val == GLOBAL)
conv_value.set_value("global");
else {
VTR_ASSERT(val == DETAILED);
conv_value.set_value("detailed");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"global", "detailed"};
}
};
struct ParseBaseCost {
ConvertedValue<e_base_cost_type> from_str(std::string str) {
ConvertedValue<e_base_cost_type> conv_value;
if (str == "delay_normalized")
conv_value.set_value(DELAY_NORMALIZED);
else if (str == "delay_normalized_length")
conv_value.set_value(DELAY_NORMALIZED_LENGTH);
else if (str == "delay_normalized_frequency")
conv_value.set_value(DELAY_NORMALIZED_FREQUENCY);
else if (str == "delay_normalized_length_frequency")
conv_value.set_value(DELAY_NORMALIZED_LENGTH_FREQUENCY);
else if (str == "demand_only_normalized_length")
conv_value.set_value(DEMAND_ONLY_NORMALIZED_LENGTH);
else if (str == "demand_only")
conv_value.set_value(DEMAND_ONLY);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_router_algorithm (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_base_cost_type val) {
ConvertedValue<std::string> conv_value;
if (val == DELAY_NORMALIZED)
conv_value.set_value("delay_normalized");
else if (val == DELAY_NORMALIZED_LENGTH)
conv_value.set_value("delay_normalized_length");
else if (val == DELAY_NORMALIZED_FREQUENCY)
conv_value.set_value("delay_normalized_frequency");
else if (val == DELAY_NORMALIZED_LENGTH_FREQUENCY)
conv_value.set_value("delay_normalized_length_frequency");
else if (val == DEMAND_ONLY_NORMALIZED_LENGTH)
conv_value.set_value("demand_only_normalized_length");
else {
VTR_ASSERT(val == DEMAND_ONLY);
conv_value.set_value("demand_only");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"demand_only", "demand_only_normalized_length", "delay_normalized", "delay_normalized_length", "delay_normalized_frequency", "delay_normalized_length_frequency"};
}
};
struct ParsePlaceAlgorithm {
ConvertedValue<e_place_algorithm> from_str(std::string str) {
ConvertedValue<e_place_algorithm> conv_value;
if (str == "bounding_box")
conv_value.set_value(BOUNDING_BOX_PLACE);
else if (str == "path_timing_driven")
conv_value.set_value(PATH_TIMING_DRIVEN_PLACE);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_router_algorithm (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_place_algorithm val) {
ConvertedValue<std::string> conv_value;
if (val == BOUNDING_BOX_PLACE)
conv_value.set_value("bounding_box");
else {
VTR_ASSERT(val == PATH_TIMING_DRIVEN_PLACE);
conv_value.set_value("path_timing_driven");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"bounding_box", "path_timing_driven"};
}
};
struct ParseClusterSeed {
ConvertedValue<e_cluster_seed> from_str(std::string str) {
ConvertedValue<e_cluster_seed> conv_value;
if (str == "timing")
conv_value.set_value(e_cluster_seed::TIMING);
else if (str == "max_inputs")
conv_value.set_value(e_cluster_seed::MAX_INPUTS);
else if (str == "blend")
conv_value.set_value(e_cluster_seed::BLEND);
else if (str == "max_pins")
conv_value.set_value(e_cluster_seed::MAX_PINS);
else if (str == "max_input_pins")
conv_value.set_value(e_cluster_seed::MAX_INPUT_PINS);
else if (str == "blend2")
conv_value.set_value(e_cluster_seed::BLEND2);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_router_algorithm (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_cluster_seed val) {
ConvertedValue<std::string> conv_value;
if (val == e_cluster_seed::TIMING)
conv_value.set_value("timing");
else if (val == e_cluster_seed::MAX_INPUTS)
conv_value.set_value("max_inputs");
else if (val == e_cluster_seed::BLEND)
conv_value.set_value("blend");
else if (val == e_cluster_seed::MAX_PINS)
conv_value.set_value("max_pins");
else if (val == e_cluster_seed::MAX_INPUT_PINS)
conv_value.set_value("max_input_pins");
else {
VTR_ASSERT(val == e_cluster_seed::BLEND2);
conv_value.set_value("blend2");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"timing", "max_inputs", "blend", "max_pins", "max_input_pins", "blend2"};
}
};
struct ParseConstantNetMethod {
ConvertedValue<e_constant_net_method> from_str(std::string str) {
ConvertedValue<e_constant_net_method> conv_value;
if (str == "global")
conv_value.set_value(CONSTANT_NET_GLOBAL);
else if (str == "route")
conv_value.set_value(CONSTANT_NET_ROUTE);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_constant_net_method (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_constant_net_method val) {
ConvertedValue<std::string> conv_value;
if (val == CONSTANT_NET_GLOBAL)
conv_value.set_value("global");
else {
VTR_ASSERT(val == CONSTANT_NET_ROUTE);
conv_value.set_value("route");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"global", "route"};
}
};
struct ParseTimingReportDetail {
ConvertedValue<e_timing_report_detail> from_str(std::string str) {
ConvertedValue<e_timing_report_detail> conv_value;
if (str == "netlist")
conv_value.set_value(e_timing_report_detail::NETLIST);
else if (str == "aggregated")
conv_value.set_value(e_timing_report_detail::AGGREGATED);
else if (str == "detailed")
conv_value.set_value(e_timing_report_detail::DETAILED_ROUTING);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_timing_report_detail (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_timing_report_detail val) {
ConvertedValue<std::string> conv_value;
if (val == e_timing_report_detail::NETLIST)
conv_value.set_value("netlist");
else if (val == e_timing_report_detail::AGGREGATED) {
conv_value.set_value("aggregated");
} else if (val == e_timing_report_detail::DETAILED_ROUTING) {
VTR_ASSERT(val == e_timing_report_detail::DETAILED_ROUTING);
conv_value.set_value("detailed");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"netlist", "aggregated", "detailed"};
}
};
struct ParseClockModeling {
ConvertedValue<e_clock_modeling> from_str(std::string str) {
ConvertedValue<e_clock_modeling> conv_value;
if (str == "ideal")
conv_value.set_value(IDEAL_CLOCK);
else if (str == "route")
conv_value.set_value(ROUTED_CLOCK);
else if (str == "dedicated_network")
conv_value.set_value(DEDICATED_NETWORK);
else {
std::stringstream msg;
msg << "Invalid conversion from '"
<< str
<< "' to e_clock_modeling (expected one of: "
<< argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_clock_modeling val) {
ConvertedValue<std::string> conv_value;
if (val == IDEAL_CLOCK)
conv_value.set_value("ideal");
else if (val == ROUTED_CLOCK)
conv_value.set_value("route");
else {
VTR_ASSERT(val == DEDICATED_NETWORK);
conv_value.set_value("dedicated_network");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"ideal", "route", "dedicated_network"};
}
};
struct ParseUnrelatedClustering {
ConvertedValue<e_unrelated_clustering> from_str(std::string str) {
ConvertedValue<e_unrelated_clustering> conv_value;
if (str == "on")
conv_value.set_value(e_unrelated_clustering::ON);
else if (str == "off")
conv_value.set_value(e_unrelated_clustering::OFF);
else if (str == "auto")
conv_value.set_value(e_unrelated_clustering::AUTO);
else {
std::stringstream msg;
msg << "Invalid conversion from '"
<< str
<< "' to e_unrelated_clustering (expected one of: "
<< argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_unrelated_clustering val) {
ConvertedValue<std::string> conv_value;
if (val == e_unrelated_clustering::ON)
conv_value.set_value("on");
else if (val == e_unrelated_clustering::OFF)
conv_value.set_value("off");
else {
VTR_ASSERT(val == e_unrelated_clustering::AUTO);
conv_value.set_value("auto");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"on", "off", "auto"};
}
};
struct ParseBalanceBlockTypeUtil {
ConvertedValue<e_balance_block_type_util> from_str(std::string str) {
ConvertedValue<e_balance_block_type_util> conv_value;
if (str == "on")
conv_value.set_value(e_balance_block_type_util::ON);
else if (str == "off")
conv_value.set_value(e_balance_block_type_util::OFF);
else if (str == "auto")
conv_value.set_value(e_balance_block_type_util::AUTO);
else {
std::stringstream msg;
msg << "Invalid conversion from '"
<< str
<< "' to e_balance_block_type_util (expected one of: "
<< argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_balance_block_type_util val) {
ConvertedValue<std::string> conv_value;
if (val == e_balance_block_type_util::ON)
conv_value.set_value("on");
else if (val == e_balance_block_type_util::OFF)
conv_value.set_value("off");
else {
VTR_ASSERT(val == e_balance_block_type_util::AUTO);
conv_value.set_value("auto");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"on", "off", "auto"};
}
};
struct ParseConstGenInference {
ConvertedValue<e_const_gen_inference> from_str(std::string str) {
ConvertedValue<e_const_gen_inference> conv_value;
if (str == "none")
conv_value.set_value(e_const_gen_inference::NONE);
else if (str == "comb")
conv_value.set_value(e_const_gen_inference::COMB);
else if (str == "comb_seq")
conv_value.set_value(e_const_gen_inference::COMB_SEQ);
else {
std::stringstream msg;
msg << "Invalid conversion from '"
<< str
<< "' to e_const_gen_inference (expected one of: "
<< argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_const_gen_inference val) {
ConvertedValue<std::string> conv_value;
if (val == e_const_gen_inference::NONE)
conv_value.set_value("none");
else if (val == e_const_gen_inference::COMB)
conv_value.set_value("comb");
else {
VTR_ASSERT(val == e_const_gen_inference::COMB_SEQ);
conv_value.set_value("comb_seq");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"none", "comb", "comb_seq"};
}
};
struct ParseIncrRerouteDelayRipup {
ConvertedValue<e_incr_reroute_delay_ripup> from_str(std::string str) {
ConvertedValue<e_incr_reroute_delay_ripup> conv_value;
if (str == "on")
conv_value.set_value(e_incr_reroute_delay_ripup::ON);
else if (str == "off")
conv_value.set_value(e_incr_reroute_delay_ripup::OFF);
else if (str == "auto")
conv_value.set_value(e_incr_reroute_delay_ripup::AUTO);
else {
std::stringstream msg;
msg << "Invalid conversion from '"
<< str
<< "' to e_incr_reroute_delay_ripup (expected one of: "
<< argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_incr_reroute_delay_ripup val) {
ConvertedValue<std::string> conv_value;
if (val == e_incr_reroute_delay_ripup::ON)
conv_value.set_value("on");
else if (val == e_incr_reroute_delay_ripup::OFF)
conv_value.set_value("off");
else {
VTR_ASSERT(val == e_incr_reroute_delay_ripup::AUTO);
conv_value.set_value("auto");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"on", "off", "auto"};
}
};
struct ParseRouteBBUpdate {
ConvertedValue<e_route_bb_update> from_str(std::string str) {
ConvertedValue<e_route_bb_update> conv_value;
if (str == "static")
conv_value.set_value(e_route_bb_update::STATIC);
else if (str == "dynamic")
conv_value.set_value(e_route_bb_update::DYNAMIC);
else {
std::stringstream msg;
msg << "Invalid conversion from '"
<< str
<< "' to e_route_bb_update (expected one of: "
<< argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_route_bb_update val) {
ConvertedValue<std::string> conv_value;
if (val == e_route_bb_update::STATIC)
conv_value.set_value("static");
else {
VTR_ASSERT(val == e_route_bb_update::DYNAMIC);
conv_value.set_value("dynamic");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"static", "dynamic"};
}
};
struct ParseRouterLookahead {
ConvertedValue<e_router_lookahead> from_str(std::string str) {
ConvertedValue<e_router_lookahead> conv_value;
if (str == "classic")
conv_value.set_value(e_router_lookahead::CLASSIC);
else if (str == "map")
conv_value.set_value(e_router_lookahead::MAP);
else {
std::stringstream msg;
msg << "Invalid conversion from '"
<< str
<< "' to e_router_lookahead (expected one of: "
<< argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_router_lookahead val) {
ConvertedValue<std::string> conv_value;
if (val == e_router_lookahead::CLASSIC)
conv_value.set_value("classic");
else {
VTR_ASSERT(val == e_router_lookahead::MAP);
conv_value.set_value("map");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"classic", "map"};
}
};
struct ParsePlaceDelayModel {
ConvertedValue<PlaceDelayModelType> from_str(std::string str) {
ConvertedValue<PlaceDelayModelType> conv_value;
if (str == "delta")
conv_value.set_value(PlaceDelayModelType::DELTA);
else if (str == "delta_override")
conv_value.set_value(PlaceDelayModelType::DELTA_OVERRIDE);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to PlaceDelayModelType (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(PlaceDelayModelType val) {
ConvertedValue<std::string> conv_value;
if (val == PlaceDelayModelType::DELTA)
conv_value.set_value("delta");
else if (val == PlaceDelayModelType::DELTA_OVERRIDE)
conv_value.set_value("delta_override");
else {
std::stringstream msg;
msg << "Unrecognized PlaceDelayModelType";
conv_value.set_error(msg.str());
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"delta", "delta_override"};
}
};
struct ParseReducer {
ConvertedValue<e_reducer> from_str(std::string str) {
ConvertedValue<e_reducer> conv_value;
if (str == "min")
conv_value.set_value(e_reducer::MIN);
else if (str == "max")
conv_value.set_value(e_reducer::MAX);
else if (str == "median")
conv_value.set_value(e_reducer::MEDIAN);
else if (str == "arithmean")
conv_value.set_value(e_reducer::ARITHMEAN);
else if (str == "geomean")
conv_value.set_value(e_reducer::GEOMEAN);
else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_reducer (expected one of: " << argparse::join(default_choices(), ", ") << ")";
conv_value.set_error(msg.str());
}
return conv_value;
}
ConvertedValue<std::string> to_str(e_reducer val) {
ConvertedValue<std::string> conv_value;
if (val == e_reducer::MIN)
conv_value.set_value("min");
else if (val == e_reducer::MAX)
conv_value.set_value("max");
else if (val == e_reducer::MEDIAN)
conv_value.set_value("median");
else if (val == e_reducer::ARITHMEAN)
conv_value.set_value("arithmean");
else {
VTR_ASSERT(val == e_reducer::GEOMEAN);
conv_value.set_value("geomean");
}
return conv_value;
}
std::vector<std::string> default_choices() {
return {"min", "max", "median", "arithmean", "geomean"};
}
};
argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& args) {
std::string description =
"Implements the specified circuit onto the target FPGA architecture"
" by performing packing/placement/routing, and analyzes the result.\n"
"\n"
"Attempts to find the minimum routable channel width, unless a fixed"
" channel width is specified with --route_chan_width.";
auto parser = argparse::ArgumentParser(prog_name, description);
std::string epilog = vtr::replace_all(
"Usage Examples\n"
"--------------\n"
" #Find the minimum routable channel width of my_circuit on my_arch\n"
" {prog} my_arch.xml my_circuit.blif\n"
"\n"
" #Show interactive graphics\n"
" {prog} my_arch.xml my_circuit.blif --disp on\n"
"\n"
" #Implement at a fixed channel width of 100\n"
" {prog} my_arch.xml my_circuit.blif --route_chan_width 100\n"
"\n"
" #Perform packing and placement only\n"
" {prog} my_arch.xml my_circuit.blif --pack --place\n"
"\n"
" #Generate post-implementation netlist\n"
" {prog} my_arch.xml my_circuit.blif --gen_post_synthesis_netlist on\n"
"\n"
" #Write routing-resource graph to a file\n"
" {prog} my_arch.xml my_circuit.blif --write_rr_graph my_rr_graph.xml\n"
"\n"
"\n"
"For additional documentation see: https://docs.verilogtorouting.org",
"{prog}", parser.prog());
parser.epilog(epilog);
auto& pos_grp = parser.add_argument_group("positional arguments");
pos_grp.add_argument(args.ArchFile, "architecture")
.help("FPGA Architecture description file (XML)");
pos_grp.add_argument(args.CircuitName, "circuit")
.help("Circuit file (or circuit name if --circuit_file specified)");
auto& stage_grp = parser.add_argument_group("stage options");
stage_grp.add_argument<bool, ParseOnOff>(args.do_packing, "--pack")
.help("Run packing")
.action(argparse::Action::STORE_TRUE)
.default_value("off");
stage_grp.add_argument<bool, ParseOnOff>(args.do_placement, "--place")
.help("Run placement")
.action(argparse::Action::STORE_TRUE)
.default_value("off");
stage_grp.add_argument<bool, ParseOnOff>(args.do_routing, "--route")
.help("Run routing")
.action(argparse::Action::STORE_TRUE)
.default_value("off");
stage_grp.add_argument<bool, ParseOnOff>(args.do_analysis, "--analysis")
.help("Run analysis")
.action(argparse::Action::STORE_TRUE)
.default_value("off");
stage_grp.epilog(
"If none of the stage options are specified, all stages are run.\n"
"Analysis is always run after routing, unless the implementation\n"
"is illegal.\n"
"\n"
"If the implementation is illegal analysis can be forced by explicitly\n"
"specifying the --analysis option.");
auto& gfx_grp = parser.add_argument_group("graphics options");
gfx_grp.add_argument<bool, ParseOnOff>(args.show_graphics, "--disp")
.help("Enable or disable interactive graphics")
.default_value("off");
gfx_grp.add_argument(args.GraphPause, "--auto")
.help(
"Controls how often VPR pauses for interactive"
" graphics (requiring Proceed to be clicked)."
" Higher values pause less frequently")
.default_value("1")
.choices({"0", "1", "2"})
.show_in(argparse::ShowIn::HELP_ONLY);
gfx_grp.add_argument<bool, ParseOnOff>(args.save_graphics, "--save_graphics")
.help("Save all graphical contents to PDF files")
.default_value("off");
auto& gen_grp = parser.add_argument_group("general options");
gen_grp.add_argument(args.show_help, "--help", "-h")
.help("Show this help message then exit")
.action(argparse::Action::HELP);
gen_grp.add_argument<bool, ParseOnOff>(args.show_version, "--version")
.help("Show version information then exit")
.action(argparse::Action::VERSION);
gen_grp.add_argument<std::string>(args.device_layout, "--device")
.help(
"Controls which device layout/floorplan is used from the architecture file."
" 'auto' uses the smallest device which satisfies the circuit's resource requirements.")
.metavar("DEVICE_NAME")
.default_value("auto");
gen_grp.add_argument<size_t>(args.num_workers, "--num_workers", "-j")
.help(
"Controls how many parallel workers VPR may use:\n"
" * 1 implies VPR will execute serially,\n"
" * >1 implies VPR may execute in parallel with up to the\n"
" specified concurrency, and\n"
" * 0 implies VPR may execute in parallel with up to the\n"
" maximum concurrency supported by the host machine.\n"
"If this option is not specified it may be set from the 'VPR_NUM_WORKERS' "
"environment variable; otherwise the default is used.")
.default_value("1");
gen_grp.add_argument<bool, ParseOnOff>(args.timing_analysis, "--timing_analysis")
.help("Controls whether timing analysis (and timing driven optimizations) are enabled.")
.default_value("on");
gen_grp.add_argument<bool, ParseOnOff>(args.CreateEchoFile, "--echo_file")
.help(
"Generate echo files of key internal data structures."
" Useful for debugging VPR, and typically end in .echo")
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
gen_grp.add_argument<bool, ParseOnOff>(args.verify_file_digests, "--verify_file_digests")
.help(
"Verify that files loaded by VPR (e.g. architecture, netlist,"
" previous packing/placement/routing) are consistent")
.default_value("on")
.show_in(argparse::ShowIn::HELP_ONLY);
gen_grp.add_argument(args.target_device_utilization, "--target_utilization")
.help(
"Sets the target device utilization."
" This corresponds to the maximum target fraction of device grid-tiles to be used."
" A value of 1.0 means the smallest device (which fits the circuit) will be used.")
.default_value("1.0")
.show_in(argparse::ShowIn::HELP_ONLY);
gen_grp.add_argument<e_constant_net_method, ParseConstantNetMethod>(args.constant_net_method, "--constant_net_method")
.help(
"Specifies how constant nets (i.e. those driven to a constant\n"
"value) are handled:\n"
" * global: Treat constant nets as globals (not routed)\n"
" * route : Treat constant nets as normal nets (routed)\n"
" * dedicated_network : Build a dedicated clock network based on the\n"
" clock network specified in the architecture file\n")
.default_value("global")
.show_in(argparse::ShowIn::HELP_ONLY);
gen_grp.add_argument<e_clock_modeling, ParseClockModeling>(args.clock_modeling, "--clock_modeling")
.help(
"Specifies how clock nets are handled\n"
" * ideal: Treat clock pins as ideal\n"
" (i.e. no routing delays on clocks)\n"
" * route: Treat the clock pins as normal nets\n"
" (i.e. routed using inter-block routing)\n")
.default_value("ideal")
.show_in(argparse::ShowIn::HELP_ONLY);
gen_grp.add_argument<bool, ParseOnOff>(args.exit_before_pack, "--exit_before_pack")
.help("Causes VPR to exit before packing starts (useful for statistics collection)")
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
gen_grp.add_argument<bool, ParseOnOff>(args.strict_checks, "--strict_checks")
.help(
"Controls whether VPR enforces some consistency checks strictly (as errors) or treats them as warnings."
" Usually these checks indicate an issue with either the targetted architecture, or consistency issues"
" with VPR's internal data structures/algorithms (possibly harming optimization quality)."
" In specific circumstances on specific architectures these checks may be too restrictive and can be turned off."
" However exercise extreme caution when turning this option off -- be sure you completely understand why the issue"
" is being flagged, and why it is OK to treat as a warning instead of an error.")
.default_value("on")
.show_in(argparse::ShowIn::HELP_ONLY);
gen_grp.add_argument<std::string>(args.disable_errors, "--disable_errors")
.help(
"Parses a list of functions for which the errors are going to be treated as warnings.\n"
"Each function in the list is delimited by `:`\n"
"This option should be only used for development purposes.")
.default_value("");
gen_grp.add_argument<std::string>(args.suppress_warnings, "--suppress_warnings")
.help(
"Parses a list of functions for which the warnings will be suppressed on stdout.\n"
"The first element of the list is the name of the output log file with the suppressed warnings.\n"
"The output log file can be omitted to completely suppress warnings.\n"
"The file name and the list of functions is separated by `,`. If no output log file is specified,\n"
"the comma is not needed.\n"
"Each function in the list is delimited by `:`\n"
"This option should be only used for development purposes.")
.default_value("");
gen_grp.add_argument<bool, ParseOnOff>(args.allow_dangling_combinational_nodes, "--allow_dangling_combinational_nodes")
.help(
"Option to allow dangling combinational nodes in the timing graph.\n"
"This option should normally be off, as dangling combinational nodes are unusual\n"
"in the timing graph and may indicate a problem in the circuit or architecture.\n"
"Unless you understand why your architecture/circuit can have valid dangling combinational nodes, this option should be off.\n"
"In general this is a dev-only option and should not be turned on by the end-user.")
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
auto& file_grp = parser.add_argument_group("file options");
file_grp.add_argument(args.BlifFile, "--circuit_file")
.help("Path to technology mapped circuit")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument<e_circuit_format, ParseCircuitFormat>(args.circuit_format, "--circuit_format")
.help(
"File format for the input atom-level circuit/netlist.\n"
" * auto: infer from file extension\n"
" * blif: Strict structural BLIF format\n"
" * eblif: Structural BLIF format with the extensions:\n"
" .conn - Connection between two wires\n"
" .cname - Custom name for atom primitive\n"
" .param - Parameter on atom primitive\n"
" .attr - Attribute on atom primitive\n")
.default_value("auto")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.NetFile, "--net_file")
.help("Path to packed netlist file")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.PlaceFile, "--place_file")
.help("Path to placement file")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.RouteFile, "--route_file")
.help("Path to routing file")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.SDCFile, "--sdc_file")
.help("Path to timing constraints file in SDC format")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.read_rr_graph_file, "--read_rr_graph")
.help(
"The routing resource graph file to load."
" The loaded routing resource graph overrides any routing architecture specified in the architecture file.")
.metavar("RR_GRAPH_FILE")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.write_rr_graph_file, "--write_rr_graph")
.help("Writes the routing resource graph to the specified file")
.metavar("RR_GRAPH_FILE")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.read_router_lookahead, "--read_router_lookahead")
.help(
"Reads the lookahead data from the specified file instead of computing it.")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.write_router_lookahead, "--write_router_lookahead")
.help("Writes the lookahead data to the specified file.")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.read_placement_delay_lookup, "--read_placement_delay_lookup")
.help(
"Reads the placement delay lookup from the specified file instead of computing it.")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.write_placement_delay_lookup, "--write_placement_delay_lookup")
.help("Writes the placement delay lookup to the specified file.")
.show_in(argparse::ShowIn::HELP_ONLY);
file_grp.add_argument(args.out_file_prefix, "--outfile_prefix")
.help("Prefix for output files")
.show_in(argparse::ShowIn::HELP_ONLY);
auto& netlist_grp = parser.add_argument_group("netlist options");
netlist_grp.add_argument<bool, ParseOnOff>(args.absorb_buffer_luts, "--absorb_buffer_luts")
.help("Controls whether LUTS programmed as buffers are absorbed by downstream logic")
.default_value("on")
.show_in(argparse::ShowIn::HELP_ONLY);
netlist_grp.add_argument<e_const_gen_inference, ParseConstGenInference>(args.const_gen_inference, "--const_gen_inference")
.help(
"Controls how constant generators are detected\n"
" * none : No constant generator inference is performed\n"
" * comb : Only combinational primitives are considered\n"
" for constant generator inference (always safe)\n"
" * comb_seq: Both combinational and sequential primitives\n"
" are considered for constant generator inference\n"
" (usually safe)\n")
.default_value("comb_seq")
.show_in(argparse::ShowIn::HELP_ONLY);
netlist_grp.add_argument<bool, ParseOnOff>(args.sweep_dangling_primary_ios, "--sweep_dangling_primary_ios")
.help("Controls whether dangling primary inputs and outputs are removed from the netlist")
.default_value("on")
.show_in(argparse::ShowIn::HELP_ONLY);
netlist_grp.add_argument<bool, ParseOnOff>(args.sweep_dangling_nets, "--sweep_dangling_nets")
.help("Controls whether dangling nets are removed from the netlist")
.default_value("on")
.show_in(argparse::ShowIn::HELP_ONLY);
netlist_grp.add_argument<bool, ParseOnOff>(args.sweep_dangling_blocks, "--sweep_dangling_blocks")
.help("Controls whether dangling blocks are removed from the netlist")
.default_value("on")
.show_in(argparse::ShowIn::HELP_ONLY);
netlist_grp.add_argument<bool, ParseOnOff>(args.sweep_constant_primary_outputs, "--sweep_constant_primary_outputs")
.help("Controls whether primary outputs driven by constant values are removed from the netlist")
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
netlist_grp.add_argument(args.netlist_verbosity, "--netlist_verbosity")
.help(
"Controls how much detail netlist processing produces about detected netlist"
" characteristics (e.g. constant generator detection) and applied netlist"
" modifications (e.g. swept netlist components)."
" Larger values produce more detail.")
.default_value("1")
.show_in(argparse::ShowIn::HELP_ONLY);
auto& pack_grp = parser.add_argument_group("packing options");
pack_grp.add_argument<bool, ParseOnOff>(args.connection_driven_clustering, "--connection_driven_clustering")
.help(
"Controls whether or not packing prioritizes the absorption of nets with fewer"
" connections into a complex logic block over nets with more connections")
.default_value("on")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument<e_unrelated_clustering, ParseUnrelatedClustering>(args.allow_unrelated_clustering, "--allow_unrelated_clustering")
.help(
"Controls whether primitives with no attraction to a cluster can be packed into it.\n"
"Turning unrelated clustering on can increase packing density (fewer blocks are used), but at the cost of worse routability.\n"
" * on : Unrelated clustering enabled\n"
" * off : Unrelated clustering disabled\n"
" * auto: Dynamically enabled/disabled (based on density)\n")
.default_value("auto")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument(args.alpha_clustering, "--alpha_clustering")
.help(
"Parameter that weights the optimization of timing vs area. 0.0 focuses solely on"
" area, 1.0 solely on timing.")
.default_value("0.75")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument(args.beta_clustering, "--beta_clustering")
.help(
"Parameter that weights the absorption of small nets vs signal sharing."
" 0.0 focuses solely on sharing, 1.0 solely on small net absoprtion."
" Only meaningful if --connection_driven_clustering=on")
.default_value("0.9")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument<bool, ParseOnOff>(args.timing_driven_clustering, "--timing_driven_clustering")
.help("Controls whether custering optimizes for timing")
.default_value("on")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument<e_cluster_seed, ParseClusterSeed>(args.cluster_seed_type, "--cluster_seed_type")
.help(
"Controls how primitives are chosen as seeds."
" (Default: blend2 if timing driven, max_inputs otherwise)")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument<bool, ParseOnOff>(args.enable_clustering_pin_feasibility_filter, "--clustering_pin_feasibility_filter")
.help(
"Controls whether the pin counting feasibility filter is used during clustering."
" When enabled the clustering engine counts the number of available pins in"
" groups/classes of mutually connected pins within a cluster."
" These counts are used to quickly filter out candidate primitives/atoms/molecules"
" for which the cluster has insufficient pins to route (without performing a full routing)."
" This reduces packer run-time")
.default_value("on")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument<e_balance_block_type_util, ParseBalanceBlockTypeUtil>(args.balance_block_type_utilization, "--balance_block_type_utilization")
.help(
"If enabled, when a primitive can potentially be mapped to multiple block types the packer will\n"
"pick the block type which (currently) has the lowest utilization.\n"
" * on : Try to balance block type utilization\n"
" * off : Do not try to balance block type utilization\n"
" * auto: Dynamically enabled/disabled (based on density)\n")
.default_value("auto")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument(args.target_external_pin_util, "--target_ext_pin_util")
.help(
"Sets the external pin utilization target during clustering.\n"
"Value Ranges: [1.0, 0.0]\n"
"* 1.0 : The packer to pack as densely as possible (i.e. try\n"
" to use 100% of cluster external pins)\n"
"* 0.0 : The packer to pack as loosely as possible (i.e. each\n"
" block will contain a single mollecule).\n"
" Values in between trade-off pin usage and\n"
" packing density.\n"
"\n"
"Typically packing less densely improves routability, at\n"
"the cost of using more clusters. Note that these settings are\n"
"only guidelines, the packer will use up to 1.0 utilization if\n"
"a molecule would not otherwise pack into any cluster type.\n"
"\n"
"This option can take multiple specifications in several\n"
"formats:\n"
"* auto (i.e. 'auto'): VPR will determine the target pin\n"
" utilizations automatically\n"
"* Single Value (e.g. '0.7'): the input pin utilization for\n"
" all block types (output pin\n"
" utilization defaults to 1.0)\n"
"* Double Value (e.g. '0.7,0.8'): the input and output pin\n"
" utilization for all block types\n"
"* Block Value (e.g. 'clb:0.7', 'clb:0.7,0.8'): the pin\n"
" utilization for a specific\n"
" block type\n"
"These can be used in combination. For example:\n"
" '--target_ext_pin_util 0.9 clb:0.7'\n"
"would set the input pin utilization of clb blocks to 0.7,\n"
"and all other blocks to 0.9.\n")
.nargs('+')
.default_value({"auto"})
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument<bool, ParseOnOff>(args.pack_prioritize_transitive_connectivity, "--pack_prioritize_transitive_connectivity")
.help("Whether transitive connectivity is prioritized over high-fanout connectivity during packing")
.default_value("on")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument(args.pack_high_fanout_threshold, "--pack_high_fanout_threshold")
.help(
"Sets the high fanout threshold during clustering.\n"
"\n"
"Typically reducing the threshold reduces packing density\n"
"and improves routability."
"\n"
"This option can take multiple specifications in several\n"
"formats:\n"
"* auto (i.e. 'auto'): VPR will determine the target pin\n"
" utilizations automatically\n"
"* Single Value (e.g. '256'): the high fanout threshold\n"
" for all block types\n"
"* Block Value (e.g. 'clb:16'): the high fanout threshold\n"
" for a specific block type\n"
"These can be used in combination. For example:\n"
" '--pack_high_fanout_threshold 256 clb:16'\n"
"would set the high fanout threshold for clb blocks to 16\n"
"and all other blocks to 256\n")
.nargs('+')
.default_value({"auto"})
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument(args.pack_transitive_fanout_threshold, "--pack_transitive_fanout_threshold")
.help("Packer transitive fanout threshold")
.default_value("4")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument(args.pack_feasible_block_array_size, "--pack_feasible_block_array_size")
.help(
"This value is used to determine the max size of the\n"
"priority queue for candidates that pass the early filter\n"
"legality test but not the more detailed routing test\n")
.default_value("30")
.show_in(argparse::ShowIn::HELP_ONLY);
pack_grp.add_argument<int>(args.pack_verbosity, "--pack_verbosity")
.help("Controls how verbose clustering's output is. Higher values produce more output (useful for debugging architecture packing problems)")
.default_value("2")
.show_in(argparse::ShowIn::HELP_ONLY);
auto& place_grp = parser.add_argument_group("placement options");
place_grp.add_argument(args.Seed, "--seed")
.help("Placement random number generator seed")
.default_value("1")
.show_in(argparse::ShowIn::HELP_ONLY);
place_grp.add_argument<bool, ParseOnOff>(args.ShowPlaceTiming, "--enable_timing_computations")
.help("Displays delay statistics even if placement is not timing driven")
.show_in(argparse::ShowIn::HELP_ONLY);
place_grp.add_argument(args.PlaceInnerNum, "--inner_num")
.help("Controls number of moves per temperature: inner_num * num_blocks ^ (4/3)")
.default_value("1.0")
.show_in(argparse::ShowIn::HELP_ONLY);
place_grp.add_argument(args.PlaceInitT, "--init_t")
.help("Initial temperature for manual annealing schedule")
.default_value("100.0")
.show_in(argparse::ShowIn::HELP_ONLY);
place_grp.add_argument(args.PlaceExitT, "--exit_t")
.help("Temperature at which annealing which terminate for manual annealing schedule")
.default_value("0.01")
.show_in(argparse::ShowIn::HELP_ONLY);
place_grp.add_argument(args.PlaceAlphaT, "--alpha_t")
.help(
"Temperature scaling factor for manual annealing schedule."
" Old temperature is multiplied by alpha_t")
.default_value("0.8")
.show_in(argparse::ShowIn::HELP_ONLY);
place_grp.add_argument(args.pad_loc_file, "--fix_pins")
.help(
"Fixes I/O pad locations during placement. Valid options:\n"
" * 'free' allows placement to optimize pad locations\n"
" * 'random' fixes pad locations to arbitraray locations\n"
" * path to a file specifying pad locations (.place format with only pads specified).")
.default_value("free")
.show_in(argparse::ShowIn::HELP_ONLY);
place_grp.add_argument<e_place_algorithm, ParsePlaceAlgorithm>(args.PlaceAlgorithm, "--place_algorithm")
.help("Controls which placement algorithm is used")
.default_value("path_timing_driven")
.choices({"bounding_box", "path_timing_driven"})
.show_in(argparse::ShowIn::HELP_ONLY);
place_grp.add_argument(args.PlaceChanWidth, "--place_chan_width")
.help(
"Sets the assumed channel width during placement. "
"If --place_chan_width is unspecified, but --route_chan_width is specified the "
"--route_chan_width value will be used (otherwise the default value is used).")
.default_value("100")
.show_in(argparse::ShowIn::HELP_ONLY);
place_grp.add_argument(args.place_rlim_escape_fraction, "--place_rlim_escape")
.help(
"The fraction of moves which are allowed to ignore the region limit."
" For example, a value of 0.1 means 10%% of moves are allowed to ignore the region limit.")
.default_value("0.0")
.show_in(argparse::ShowIn::HELP_ONLY);
place_grp.add_argument(args.place_move_stats_file, "--place_move_stats")
.help(
"File to write detailed placer move statistics to")
.default_value("")
.show_in(argparse::ShowIn::HELP_ONLY);
auto& place_timing_grp = parser.add_argument_group("timing-driven placement options");
place_timing_grp.add_argument(args.PlaceTimingTradeoff, "--timing_tradeoff")
.help(
"Trade-off control between delay and wirelength during placement."
" 0.0 focuses completely on wirelength, 1.0 completely on timing")
.default_value("0.5")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.RecomputeCritIter, "--recompute_crit_iter")
.help("Controls how many temperature updates occur between timing analysis during placement")
.default_value("1")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.inner_loop_recompute_divider, "--inner_loop_recompute_divider")
.help("Controls how many timing analysies are perform per temperature during placement")
.default_value("0")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.place_exp_first, "--td_place_exp_first")
.help(
"Controls how critical a connection is as a function of slack at the start of placement."
" A value of zero treats all connections as equally critical (regardless of slack)."
" Values larger than 1.0 cause low slack connections to be treated more critically."
" The value increases to --td_place_exp_last during placement.")
.default_value("1.0")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.place_exp_last, "--td_place_exp_last")
.help("Controls how critical a connection is as a function of slack at the end of placement.")
.default_value("8.0")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument<PlaceDelayModelType, ParsePlaceDelayModel>(args.place_delay_model, "--place_delay_model")
.help(
"This option controls what information is considered and how"
" the placement delay model is constructed.\n"
"Valid options:\n"
" * 'delta' uses differences in position only\n"
" * 'delta_override' uses differences in position with overrides for direct connects\n")
.default_value("delta")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument<e_reducer, ParseReducer>(args.place_delay_model_reducer, "--place_delay_model_reducer")
.help("When calculating delta delays for the placment delay model how are multiple values combined?")
.default_value("min")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.place_delay_offset, "--place_delay_offset")
.help(
"A constant offset (in seconds) applied to the placer's delay model.")
.default_value("0.0")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.place_delay_ramp_delta_threshold, "--place_delay_ramp_delta_threshold")
.help(
"The delta distance beyond which --place_delay_ramp is applied."
" Negative values disable the placer delay ramp.")
.default_value("-1")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.place_delay_ramp_slope, "--place_delay_ramp_slope")
.help("The slope of the ramp (in seconds per grid tile) which is applied to the placer delay model for delta distance beyond --place_delay_ramp_delta_threshold")
.default_value("0.0e-9")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.place_tsu_rel_margin, "--place_tsu_rel_margin")
.help(
"Specifies the scaling factor for cell setup times used by the placer."
" This effectively controls whether the placer should try to achieve extra margin on setup paths."
" For example a value of 1.1 corresponds to requesting 10%% setup margin.")
.default_value("1.0")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.place_tsu_abs_margin, "--place_tsu_abs_margin")
.help(
"Specifies an absolute offest added to cell setup times used by the placer."
" This effectively controls whether the placer should try to achieve extra margin on setup paths."
" For example a value of 500e-12 corresponds to requesting an extra 500ps of setup margin.")
.default_value("0.0")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.post_place_timing_report_file, "--post_place_timing_report")
.help("Name of the post-placement timing report file (not generated if unspecfied)")
.default_value("")
.show_in(argparse::ShowIn::HELP_ONLY);
place_timing_grp.add_argument(args.allowed_tiles_for_delay_model, "--allowed_tiles_for_delay_model")
.help(
"Names of allowed tile types that can be sampled during delay "
"modelling. Default is to allow all tiles. Can be used to "
"exclude specialized tiles from placer delay sampling.")
.default_value("")
.show_in(argparse::ShowIn::HELP_ONLY);
auto& route_grp = parser.add_argument_group("routing options");
route_grp.add_argument(args.max_router_iterations, "--max_router_iterations")
.help(
"Maximum number of Pathfinder-based routing iterations before the circuit is"
" declared unroutable at a given channel width")
.default_value("50")
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument(args.first_iter_pres_fac, "--first_iter_pres_fac")
.help("Sets the present overuse factor for the first routing iteration")
.default_value("0.0")
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument(args.initial_pres_fac, "--initial_pres_fac")
.help("Sets the present overuse factor for the second routing iteration")
.default_value("0.5")
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument(args.pres_fac_mult, "--pres_fac_mult")
.help(
"Sets the growth factor by which the present overuse penalty factor is"
" multiplied after each routing iteration")
.default_value("1.3")
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument(args.acc_fac, "--acc_fac")
.help("Specifies the accumulated overuse factor (historical congestion cost factor)")
.default_value("1.0")
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument(args.bb_factor, "--bb_factor")
.help("Sets the distance (in channels) outside a connection's bounding box which can be explored during routing")
.default_value("3")
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument<e_base_cost_type, ParseBaseCost>(args.base_cost_type, "--base_cost_type")
.help(
"Sets the basic cost of routing resource nodes:\n"
" * demand_only: based on expected demand of node type\n"
" * demand_only_normalized_length: based on expected \n"
" demand of node type normalized by length\n"
" * delay_normalized: like demand_only but normalized\n"
" to magnitude of typical routing resource delay\n"
" * delay_normalized_length: like delay_normalized but\n"
" scaled by routing resource length\n"
" * delay_normalized_freqeuncy: like delay_normalized\n"
" but scaled inversely by segment type frequency\n"
" * delay_normalized_length_freqeuncy: like delay_normalized\n"
" but scaled by routing resource length, and inversely\n"
" by segment type frequency\n"
"(Default: demand_only for breadth-first router,\n"
" delay_normalized_length for timing-driven router)")
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument(args.bend_cost, "--bend_cost")
.help("The cost of a bend. (Default: 1.0 for global routing, 0.0 for detailed routing)")
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument<e_route_type, ParseRouteType>(args.RouteType, "--route_type")
.help("Specifies whether global, or combined global and detailed routing is performed.")
.default_value("detailed")
.choices({"global", "detailed"})
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument(args.RouteChanWidth, "--route_chan_width")
.help(
"Specifies a fixed channel width to route at."
" A value of -1 indicates that the minimum channel width should be determined")
.default_value("-1")
.metavar("CHANNEL_WIDTH");
route_grp.add_argument(args.min_route_chan_width_hint, "--min_route_chan_width_hint")
.help(
"Hint to the router what the minimum routable channel width is."
" Good hints can speed-up determining the minimum channel width.")
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument<bool, ParseOnOff>(args.verify_binary_search, "--verify_binary_search")
.help(
"Force the router to verify the minimum channel width by routing at"
" consecutively lower channel widths until two consecutive failures are observed.")
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument<e_router_algorithm, ParseRouterAlgorithm>(args.RouterAlgorithm, "--router_algorithm")
.help(
"Specifies the router algorithm to use.\n"
" * breadth_first: focuses solely on routability\n"
" * timing_driven: focuses on routability and circuit speed\n")
.default_value("timing_driven")
.choices({"breadth_first", "timing_driven"})
.show_in(argparse::ShowIn::HELP_ONLY);
route_grp.add_argument(args.min_incremental_reroute_fanout, "--min_incremental_reroute_fanout")
.help("The net fanout threshold above which nets will be re-routed incrementally.")
.default_value("16")
.show_in(argparse::ShowIn::HELP_ONLY);
auto& route_timing_grp = parser.add_argument_group("timing-driven routing options");
route_timing_grp.add_argument(args.astar_fac, "--astar_fac")
.help(
"Controls the directedness of the the timing-driven router's exploration."
" Values between 1 and 2 are resonable; higher values trade some quality for reduced run-time")
.default_value("1.2")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument(args.max_criticality, "--max_criticality")
.help(
"Sets the maximum fraction of routing cost derived from delay (vs routability) for any net."
" 0.0 means no attention is paid to delay, 1.0 means nets on the critical path ignore congestion")
.default_value("0.99")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument(args.criticality_exp, "--criticality_exp")
.help(
"Controls the delay-routability trade-off for nets as a function of slack."
" 0.0 implies all nets treated equally regardless of slack."
" At large values (>> 1) only nets on the critical path will consider delay.")
.default_value("1.0")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument(args.router_init_wirelength_abort_threshold, "--router_init_wirelength_abort_threshold")
.help(
"The first routing iteration wirelength abort threshold."
" If the first routing iteration uses more than this fraction of available wirelength routing is aborted.")
.default_value("0.85")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument<e_incr_reroute_delay_ripup, ParseIncrRerouteDelayRipup>(args.incr_reroute_delay_ripup, "--incremental_reroute_delay_ripup")
.help("Controls whether incremental net routing will rip-up (and re-route) a critical connection for delay, even if the routing is legal.")
.default_value("auto")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument<e_routing_failure_predictor, ParseRoutePredictor>(args.routing_failure_predictor, "--routing_failure_predictor")
.help(
"Controls how aggressively the router will predict a routing as unsuccessful"
" and give up early. This can significantly reducing the run-time required"
" to find the minimum channel width.\n"
" * safe: Only abort when it is extremely unlikely a routing will succeed\n"
" * aggressive: Further reduce run-time by giving up earlier. This may increase the reported minimum channel width\n"
" * off: Only abort when the maximum number of iterations is reached\n")
.default_value("safe")
.choices({"safe", "aggressive", "off"})
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument<e_routing_budgets_algorithm, RouteBudgetsAlgorithm>(args.routing_budgets_algorithm, "--routing_budgets_algorithm")
.help(
"Controls how the routing budgets are created.\n"
" * slack: Sets the budgets depending on the amount slack between connections and the current delay values.\n"
" * criticality: Sets the minimum budgets to 0 and the maximum budgets as a function of delay and criticality (net delay/ pin criticality)\n"
" * disable: Removes the routing budgets, use the default VPR and ignore hold time constraints\n")
.default_value("disable")
.choices({"minimax", "scale_delay", "disable"})
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument<bool, ParseOnOff>(args.save_routing_per_iteration, "--save_routing_per_iteration")
.help(
"Controls whether VPR saves the current routing to a file after each routing iteration."
" May be helpful for debugging.")
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument<float>(args.congested_routing_iteration_threshold_frac, "--congested_routing_iteration_threshold")
.help(
"Controls when the router enters a high effort mode to resolve lingering routing congestion."
" Value is the fraction of max_router_iterations beyond which the routing is deemed congested.")
.default_value("1.0")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument<e_route_bb_update, ParseRouteBBUpdate>(args.route_bb_update, "--route_bb_update")
.help(
"Controls how the router's net bounding boxes are updated:\n"
" * static : bounding boxes are never updated\n"
" * dynamic: bounding boxes are updated dynamically as routing progresses\n")
.default_value("dynamic")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument<int>(args.router_high_fanout_threshold, "--router_high_fanout_threshold")
.help(
"Specifies the net fanout beyond which a net is considered high fanout."
" Values less than zero disable special behaviour for high fanout nets")
.default_value("64")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument<e_router_lookahead, ParseRouterLookahead>(args.router_lookahead_type, "--router_lookahead")
.help(
"Controls what lookahead the router uses to calculate cost of completing a connection.\n"
" * classic: The classic VPR lookahead\n"
" * map: A more advanced lookahead which accounts for diverse wire type\n")
.default_value("classic")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument(args.router_max_convergence_count, "--router_max_convergence_count")
.help(
"Controls how many times the router is allowed to converge to a legal routing before halting."
" If multiple legal solutions are found the best quality implementation is used.")
.default_value("1")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument(args.router_reconvergence_cpd_threshold, "--router_reconvergence_cpd_threshold")
.help(
"Specifies the minimum potential CPD improvement for which the router will"
" continue to attempt re-convergent routing."
" For example, a value of 0.99 means the router will not give up on reconvergent"
" routing if it thinks a > 1% CPD reduction is possible.")
.default_value("0.99")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument(args.router_first_iteration_timing_report_file, "--router_first_iter_timing_report")
.help("Name of the post first routing iteration timing report file (not generated if unspecfied)")
.default_value("")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument(args.router_debug_net, "--router_debug_net")
.help(
"Controls when router debugging is enabled.\n"
" * For values >= 0, the value is taken as the net ID for\n"
" which to enable router debug output.\n"
" * For value == -1, router debug output is enabled for\n"
" all nets.\n"
" * For values < -1, all net-sbased router debug output is disabled.\n"
"Note if VPR as compiled without debug logging enabled this will produce only limited output.\n")
.default_value("-2")
.show_in(argparse::ShowIn::HELP_ONLY);
route_timing_grp.add_argument(args.router_debug_sink_rr, "--router_debug_sink_rr")
.help(
"Controls when router debugging is enabled for the specified sink RR.\n"
" * For values >= 0, the value is taken as the sink RR Node ID for\n"
" which to enable router debug output.\n"
" * For values < 0, sink-based router debug output is disabled.\n"
"Note if VPR as compiled without debug logging enabled this will produce only limited output.\n")
.default_value("-2")
.show_in(argparse::ShowIn::HELP_ONLY);
auto& analysis_grp = parser.add_argument_group("analysis options");
analysis_grp.add_argument<bool, ParseOnOff>(args.full_stats, "--full_stats")
.help("Print extra statistics about the circuit and it's routing (useful for wireability analysis)")
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
analysis_grp.add_argument<bool, ParseOnOff>(args.Generate_Post_Synthesis_Netlist, "--gen_post_synthesis_netlist")
.help(
"Generates the post-synthesis netlist (in BLIF and Verilog) along with delay information (in SDF)."
" Used for post-implementation simulation and verification")
.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")
.show_in(argparse::ShowIn::HELP_ONLY);
analysis_grp.add_argument<e_timing_report_detail, ParseTimingReportDetail>(args.timing_report_detail, "--timing_report_detail")
.help(
"Controls how much detail is provided in timing reports.\n"
" * netlist: Shows only netlist pins\n"
" * aggregated: Like 'netlist', but also shows aggregated intra-block/inter-block delays\n"
" * detailed: Lke 'aggregated' but shows detailed routing instead of aggregated inter-block delays\n")
.default_value("netlist")
.show_in(argparse::ShowIn::HELP_ONLY);
analysis_grp.add_argument<bool, ParseOnOff>(args.timing_report_skew, "--timing_report_skew")
.help("Controls whether skew timing reports are generated\n")
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
auto& power_grp = parser.add_argument_group("power analysis options");
power_grp.add_argument<bool, ParseOnOff>(args.do_power, "--power")
.help("Enable power estimation")
.action(argparse::Action::STORE_TRUE)
.default_value("off")
.show_in(argparse::ShowIn::HELP_ONLY);
power_grp.add_argument(args.CmosTechFile, "--tech_properties")
.help("XML file containing CMOS technology properties (see documentation).")
.show_in(argparse::ShowIn::HELP_ONLY);
power_grp.add_argument(args.ActFile, "--activity_file")
.help("Signal activities file for all nets (see documentation).")
.show_in(argparse::ShowIn::HELP_ONLY);
return parser;
}
void set_conditional_defaults(t_options& args) {
//Some arguments are set conditionally based on other options.
//These are resolved here.
/*
* Filenames
*/
//We may have recieved the full circuit filepath in the circuit name,
//remove the extension and any leading path elements
VTR_ASSERT(args.CircuitName.provenance() == Provenance::SPECIFIED);
auto name_ext = vtr::split_ext(args.CircuitName);
if (args.BlifFile.provenance() != Provenance::SPECIFIED) {
//If the blif file wasn't explicitly specified, interpret the circuit name
//as the blif file, and split off the extension
args.CircuitName.set(vtr::basename(name_ext[0]), Provenance::SPECIFIED);
}
std::string default_output_name = args.CircuitName;
if (args.BlifFile.provenance() != Provenance::SPECIFIED) {
//Use the full path specified in the original circuit name,
//and append the expected .blif extension
std::string blif_file = name_ext[0] + name_ext[1];
args.BlifFile.set(blif_file, Provenance::INFERRED);
}
if (args.SDCFile.provenance() != Provenance::SPECIFIED) {
//Use the full path specified in the original circuit name,
//and append the expected .sdc extension
std::string sdc_file = default_output_name + ".sdc";
args.SDCFile.set(sdc_file, Provenance::INFERRED);
}
if (args.NetFile.provenance() != Provenance::SPECIFIED) {
std::string net_file = args.out_file_prefix;
net_file += default_output_name + ".net";
args.NetFile.set(net_file, Provenance::INFERRED);
}
if (args.PlaceFile.provenance() != Provenance::SPECIFIED) {
std::string place_file = args.out_file_prefix;
place_file += default_output_name + ".place";
args.PlaceFile.set(place_file, Provenance::INFERRED);
}
if (args.RouteFile.provenance() != Provenance::SPECIFIED) {
std::string route_file = args.out_file_prefix;
route_file += default_output_name + ".route";
args.RouteFile.set(route_file, Provenance::INFERRED);
}
if (args.ActFile.provenance() != Provenance::SPECIFIED) {
std::string activity_file = args.out_file_prefix;
activity_file += default_output_name + ".act";
args.ActFile.set(activity_file, Provenance::INFERRED);
}
if (args.PowerFile.provenance() != Provenance::SPECIFIED) {
std::string power_file = args.out_file_prefix;
power_file += default_output_name + ".power";
args.PowerFile.set(power_file, Provenance::INFERRED);
}
/*
* Packing
*/
if (args.timing_driven_clustering && !args.timing_analysis) {
if (args.timing_driven_clustering.provenance() == Provenance::SPECIFIED) {
VTR_LOG_WARN("Command-line argument '%s' has no effect since timing analysis is disabled\n",
args.timing_driven_clustering.argument_name().c_str());
}
args.timing_driven_clustering.set(args.timing_analysis, Provenance::INFERRED);
}
if (args.cluster_seed_type.provenance() != Provenance::SPECIFIED) {
if (args.timing_driven_clustering) {
args.cluster_seed_type.set(e_cluster_seed::BLEND2, Provenance::INFERRED);
} else {
args.cluster_seed_type.set(e_cluster_seed::MAX_INPUTS, Provenance::INFERRED);
}
}
/*
* Placement
*/
//Which placement algorithm to use?
if (args.PlaceAlgorithm.provenance() != Provenance::SPECIFIED) {
if (args.timing_analysis) {
args.PlaceAlgorithm.set(PATH_TIMING_DRIVEN_PLACE, Provenance::INFERRED);
} else {
args.PlaceAlgorithm.set(BOUNDING_BOX_PLACE, Provenance::INFERRED);
}
}
//Place chan width follows Route chan width if unspecified
if (args.PlaceChanWidth.provenance() != Provenance::SPECIFIED && args.RouteChanWidth.provenance() == Provenance::SPECIFIED) {
args.PlaceChanWidth.set(args.RouteChanWidth.value(), Provenance::INFERRED);
}
//Do we calculate timing info during placement?
if (args.ShowPlaceTiming.provenance() != Provenance::SPECIFIED) {
args.ShowPlaceTiming.set(args.timing_analysis, Provenance::INFERRED);
}
//Are we using the automatic, or user-specified annealing schedule?
if (args.PlaceInitT.provenance() == Provenance::SPECIFIED
|| args.PlaceExitT.provenance() == Provenance::SPECIFIED
|| args.PlaceAlphaT.provenance() == Provenance::SPECIFIED) {
args.anneal_sched_type.set(USER_SCHED, Provenance::INFERRED);
} else {
args.anneal_sched_type.set(AUTO_SCHED, Provenance::INFERRED);
}
//Are the pad locations specified?
if (std::string(args.pad_loc_file) == "free") {
args.pad_loc_type.set(FREE, Provenance::INFERRED);
args.pad_loc_file.set("", Provenance::SPECIFIED);
} else if (std::string(args.pad_loc_file) == "random") {
args.pad_loc_type.set(RANDOM, Provenance::INFERRED);
args.pad_loc_file.set("", Provenance::SPECIFIED);
} else {
args.pad_loc_type.set(USER, Provenance::INFERRED);
VTR_ASSERT(!args.pad_loc_file.value().empty());
}
/*
* Routing
*/
//Base cost type
if (args.base_cost_type.provenance() != Provenance::SPECIFIED) {
if (args.RouterAlgorithm == BREADTH_FIRST) {
args.base_cost_type.set(DEMAND_ONLY, Provenance::INFERRED);
} else {
VTR_ASSERT(args.RouterAlgorithm == TIMING_DRIVEN);
if (args.RouteType == DETAILED) {
if (args.timing_analysis) {
args.base_cost_type.set(DELAY_NORMALIZED_LENGTH, Provenance::INFERRED);
} else {
args.base_cost_type.set(DEMAND_ONLY_NORMALIZED_LENGTH, Provenance::INFERRED);
}
} else {
VTR_ASSERT(args.RouteType == GLOBAL);
//Global RR graphs don't have valid timing, so use demand base cost
args.base_cost_type.set(DEMAND_ONLY_NORMALIZED_LENGTH, Provenance::INFERRED);
}
}
}
//Bend cost
if (args.bend_cost.provenance() != Provenance::SPECIFIED) {
if (args.RouteType == GLOBAL) {
args.bend_cost.set(1., Provenance::INFERRED);
} else {
VTR_ASSERT(args.RouteType == DETAILED);
args.bend_cost.set(0., Provenance::INFERRED);
}
}
}
bool verify_args(const t_options& args) {
/*
* Check for conflicting paramaters
*/
if (args.read_rr_graph_file.provenance() == Provenance::SPECIFIED
&& args.RouteChanWidth.provenance() != Provenance::SPECIFIED) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"--route_chan_width option must be specified if --read_rr_graph is requested (%s)\n",
args.read_rr_graph_file.argument_name().c_str());
}
if (!args.enable_clustering_pin_feasibility_filter && (args.target_external_pin_util.provenance() == Provenance::SPECIFIED)) {
VPR_FATAL_ERROR(VPR_ERROR_OTHER,
"%s option must be enabled for %s to have any effect\n",
args.enable_clustering_pin_feasibility_filter.argument_name().c_str(),
args.target_external_pin_util.argument_name().c_str());
}
return true;
}