diff --git a/libopenfpga/libopenfpgautil/src/openfpga_scale.cpp b/libopenfpga/libopenfpgautil/src/openfpga_scale.cpp new file mode 100644 index 000000000..9baa753d1 --- /dev/null +++ b/libopenfpga/libopenfpgautil/src/openfpga_scale.cpp @@ -0,0 +1,183 @@ +/******************************************************************** + * This file includes functions that convert time/resistance/capacitance + * units to string or vice versa + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "openfpga_scale.h" + +namespace openfpga { + +/* A small ratio for float number comparison + * If the float number B is in the range of the referance A +/- epsilon + * we regard A == B + * A - A * EPSILON <= B <= A + A * EPSILON + */ +#define EPSILON_RATIO 1e-3 + +bool same_float_number(const float& a, + const float& b, + const float& epsilon) { + /* Always use a positive epsilon */ + if ( (a - a * std::abs(epsilon) <= b) + && (b <= a + a * std::abs(epsilon)) ) { + return true; + } + + return false; +} + +/******************************************************************** + * Convert numeric unit to string: + * - 1e12 -> T + * - 1e9 -> B + * - 1e6 -> M + * - 1e3 -> k + * - 1. -> + * - 1e-3 -> m + * - 1e-6 -> u + * - 1e-9 -> n + * - 1e-12 -> p + * - 1e-15 -> f + * - 1e-18 -> a + *******************************************************************/ +std::string unit_to_string(const float& unit) { + if (true == same_float_number(unit, 1., EPSILON_RATIO)) { + return std::string(); + /* Larger than 1 unit */ + } else if (true == same_float_number(unit, 1e3, EPSILON_RATIO)) { + return std::string("k"); + } else if (true == same_float_number(unit, 1e6, EPSILON_RATIO)) { + return std::string("M"); + } else if (true == same_float_number(unit, 1e9, EPSILON_RATIO)) { + return std::string("B"); + } else if (true == same_float_number(unit, 1e12, EPSILON_RATIO)) { + return std::string("T"); + /* Less than 1 unit */ + } else if (true == same_float_number(unit, 1e-3, EPSILON_RATIO)) { + return std::string("m"); + } else if (true == same_float_number(unit, 1e-6, EPSILON_RATIO)) { + return std::string("u"); + } else if (true == same_float_number(unit, 1e-9, EPSILON_RATIO)) { + return std::string("n"); + } else if (true == same_float_number(unit, 1e-12, EPSILON_RATIO)) { + return std::string("p"); + } else if (true == same_float_number(unit, 1e-15, EPSILON_RATIO)) { + return std::string("f"); + } else if (true == same_float_number(unit, 1e-18, EPSILON_RATIO)) { + return std::string("a"); + } + + /* Invalid unit report error */ + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid unit %g!\nAcceptable units are [1e12|1e9|1e6|1e3|1|1e-3|1e-6|1e-9|1e-12|1e-15|1e-18]\n", + unit); + exit(1); +} + +/******************************************************************** + * Convert numeric time unit to string + * e.g. 1e-12 -> ps + *******************************************************************/ +std::string time_unit_to_string(const float& unit) { + /* For larger than 1 unit, we do not accept */ + if (1e6 < unit) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid time unit %g!\nAcceptable units are [1e6|1e3|1|1e-3|1e-6|1e-9|1e-12|1e-15|1e-18]\n", + unit); + exit(1); + } + + return unit_to_string(unit) + std::string("s"); +} + +/******************************************************************** + * Convert string unit to numeric: + * - T -> 1e12 + * - B -> 1e9 + * - M -> 1e6 + * - k -> 1e3 + * - "" -> 1. + * - m -> 1e-3 + * - u -> 1e-6 + * - n -> 1e-9 + * - p -> 1e-12 + * - f -> 1e-15 + * - a -> 1e-18 + *******************************************************************/ +float string_to_unit(const std::string& scale) { + if (true == scale.empty()) { + return 1.; + /* Larger than 1 unit */ + } else if (std::string("T") == scale) { + return 1e12; + } else if (std::string("B") == scale) { + return 1e9; + } else if (std::string("M") == scale) { + return 1e6; + } else if (std::string("k") == scale) { + return 1e3; + /* Less than 1 unit */ + } else if (std::string("m") == scale) { + return 1e-3; + } else if (std::string("u") == scale) { + return 1e-6; + } else if (std::string("n") == scale) { + return 1e-9; + } else if (std::string("p") == scale) { + return 1e-12; + } else if (std::string("f") == scale) { + return 1e-15; + } else if (std::string("a") == scale) { + return 1e-18; + } + + /* Invalid unit report error */ + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid unit %s!\nAcceptable units are [a|f|p|n|u|k|M|B|T] or empty\n", + scale); + exit(1); +} + +/******************************************************************** + * Convert string time unit to numeric + * e.g. ps -> 1e-12 + *******************************************************************/ +float string_to_time_unit(const std::string& scale) { + if ( (1 != scale.length()) + && (2 != scale.length()) ) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Time unit (='%s') must contain only one or two characters!\n", + scale); + } + /* The last character must be 's' */ + if ('s' != scale.back()) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Time unit (='%s') must end with 's'!\n", + scale); + } + + float unit = 1.; + VTR_ASSERT ( (1 == scale.length()) + || (2 == scale.length()) ); + if (2 == scale.length()) { + unit = string_to_unit(scale.substr(0, 1)); + } + + /* For larger than 1 unit, we do not accept */ + if (1e6 < unit) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid time unit %g!\nAcceptable units are [1e6|1e3|1|1e-3|1e-6|1e-9|1e-12|1e-15|1e-18]\n", + unit); + exit(1); + } + + return unit; +} + +} /* namespace openfpga ends */ diff --git a/libopenfpga/libopenfpgautil/src/openfpga_scale.h b/libopenfpga/libopenfpgautil/src/openfpga_scale.h new file mode 100644 index 000000000..197923548 --- /dev/null +++ b/libopenfpga/libopenfpgautil/src/openfpga_scale.h @@ -0,0 +1,29 @@ +#ifndef OPENFPGA_SCALE_H +#define OPENFPGA_SCALE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +/******************************************************************** + * Function declaration + *******************************************************************/ +/* namespace openfpga begins */ +namespace openfpga { + +bool same_float_number(const float& a, + const float& b, + const float& epsilon); + +std::string unit_to_string(const float& unit); + +std::string time_unit_to_string(const float& unit); + +float string_to_unit(const std::string& scale); + +float string_to_time_unit(const std::string& scale); + +} /* namespace openfpga ends */ + +#endif diff --git a/openfpga/src/base/openfpga_sdc.cpp b/openfpga/src/base/openfpga_sdc.cpp index 9fa360cff..d90376839 100644 --- a/openfpga/src/base/openfpga_sdc.cpp +++ b/openfpga/src/base/openfpga_sdc.cpp @@ -9,6 +9,7 @@ #include "command_exit_codes.h" /* Headers from openfpgautil library */ +#include "openfpga_scale.h" #include "openfpga_digest.h" #include "circuit_library_utils.h" @@ -31,6 +32,7 @@ int write_pnr_sdc(OpenfpgaContext& openfpga_ctx, CommandOptionId opt_output_dir = cmd.option("file"); CommandOptionId opt_flatten_names = cmd.option("flatten_names"); CommandOptionId opt_hierarchical = cmd.option("hierarchical"); + CommandOptionId opt_time_unit = cmd.option("time_unit"); CommandOptionId opt_constrain_global_port = cmd.option("constrain_global_port"); CommandOptionId opt_constrain_non_clock_global_port = cmd.option("constrain_non_clock_global_port"); CommandOptionId opt_constrain_grid = cmd.option("constrain_grid"); @@ -53,6 +55,11 @@ int write_pnr_sdc(OpenfpgaContext& openfpga_ctx, options.set_flatten_names(cmd_context.option_enable(cmd, opt_flatten_names)); options.set_hierarchical(cmd_context.option_enable(cmd, opt_hierarchical)); + + if (true == cmd_context.option_enable(cmd, opt_time_unit)) { + options.set_time_unit(string_to_time_unit(cmd_context.option_value(cmd, opt_time_unit))); + } + options.set_constrain_global_port(cmd_context.option_enable(cmd, opt_constrain_global_port)); options.set_constrain_non_clock_global_port(cmd_context.option_enable(cmd, opt_constrain_non_clock_global_port)); options.set_constrain_grid(cmd_context.option_enable(cmd, opt_constrain_grid)); @@ -100,6 +107,7 @@ int write_analysis_sdc(OpenfpgaContext& openfpga_ctx, CommandOptionId opt_output_dir = cmd.option("file"); CommandOptionId opt_flatten_names = cmd.option("flatten_names"); + CommandOptionId opt_time_unit = cmd.option("time_unit"); /* This is an intermediate data structure which is designed to modularize the FPGA-SDC * Keep it independent from any other outside data structures @@ -113,6 +121,10 @@ int write_analysis_sdc(OpenfpgaContext& openfpga_ctx, options.set_generate_sdc_analysis(true); options.set_flatten_names(cmd_context.option_enable(cmd, opt_flatten_names)); + if (true == cmd_context.option_enable(cmd, opt_time_unit)) { + options.set_time_unit(string_to_time_unit(cmd_context.option_value(cmd, opt_time_unit))); + } + /* Collect global ports from the circuit library: * TODO: should we place this in the OpenFPGA context? */ diff --git a/openfpga/src/base/openfpga_sdc_command.cpp b/openfpga/src/base/openfpga_sdc_command.cpp index 479cb8de9..bdf123e8b 100644 --- a/openfpga/src/base/openfpga_sdc_command.cpp +++ b/openfpga/src/base/openfpga_sdc_command.cpp @@ -32,6 +32,10 @@ ShellCommandId add_openfpga_write_pnr_sdc_command(openfpga::Shellinput_edges[iedge]->delay_max); + des_pb_graph_pin->input_edges[iedge]->delay_max / time_unit); } } @@ -161,6 +163,7 @@ void print_pnr_sdc_constrain_pb_pin_interc_timing(std::fstream& fp, *******************************************************************/ static void print_pnr_sdc_constrain_pb_interc_timing(std::fstream& fp, + const float& time_unit, const ModuleManager& module_manager, const ModuleId& parent_module, t_pb_graph_node* des_pb_graph_node, @@ -176,7 +179,8 @@ void print_pnr_sdc_constrain_pb_interc_timing(std::fstream& fp, for (int ipin = 0; ipin < des_pb_graph_node->num_input_pins[iport]; ++ipin) { /* If this is a idle block, we set 0 to the selected edge*/ /* Get the selected edge of current pin*/ - print_pnr_sdc_constrain_pb_pin_interc_timing(fp, + print_pnr_sdc_constrain_pb_pin_interc_timing(fp, + time_unit, module_manager, parent_module, &(des_pb_graph_node->input_pins[iport][ipin]), physical_mode, @@ -189,6 +193,7 @@ void print_pnr_sdc_constrain_pb_interc_timing(std::fstream& fp, for (int iport = 0; iport < des_pb_graph_node->num_output_ports; ++iport) { for (int ipin = 0; ipin < des_pb_graph_node->num_output_pins[iport]; ++ipin) { print_pnr_sdc_constrain_pb_pin_interc_timing(fp, + time_unit, module_manager, parent_module, &(des_pb_graph_node->output_pins[iport][ipin]), physical_mode, @@ -217,6 +222,7 @@ void print_pnr_sdc_constrain_pb_interc_timing(std::fstream& fp, *******************************************************************/ static void print_pnr_sdc_constrain_pb_graph_node_timing(const std::string& sdc_dir, + const float& time_unit, const ModuleManager& module_manager, t_pb_graph_node* parent_pb_graph_node, t_mode* physical_mode, @@ -242,6 +248,9 @@ void print_pnr_sdc_constrain_pb_graph_node_timing(const std::string& sdc_dir, /* Generate the descriptions*/ print_sdc_file_header(fp, std::string("Timing constraints for Grid " + pb_module_name + " in PnR")); + /* Print time unit for the SDC file */ + print_sdc_timescale(fp, time_unit_to_string(time_unit)); + /* We check output_pins of cur_pb_graph_node and its the input_edges * Built the interconnections between outputs of cur_pb_graph_node and outputs of child_pb_graph_node * child_pb_graph_node.output_pins -----------------> cur_pb_graph_node.outpins @@ -250,6 +259,7 @@ void print_pnr_sdc_constrain_pb_graph_node_timing(const std::string& sdc_dir, * input_pins, edges, output_pins */ print_pnr_sdc_constrain_pb_interc_timing(fp, + time_unit, module_manager, pb_module, parent_pb_graph_node, CIRCUIT_PB_PORT_OUTPUT, @@ -268,6 +278,7 @@ void print_pnr_sdc_constrain_pb_graph_node_timing(const std::string& sdc_dir, t_pb_graph_node* child_pb_graph_node = &(parent_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][jpb]); /* For each child_pb_graph_node input pins*/ print_pnr_sdc_constrain_pb_interc_timing(fp, + time_unit, module_manager, pb_module, child_pb_graph_node, CIRCUIT_PB_PORT_INPUT, @@ -291,6 +302,7 @@ void print_pnr_sdc_constrain_pb_graph_node_timing(const std::string& sdc_dir, *******************************************************************/ static void print_pnr_sdc_constrain_primitive_pb_graph_node(const std::string& sdc_dir, + const float& time_unit, const ModuleManager& module_manager, t_pb_graph_node* primitive_pb_graph_node, const bool& constrain_zero_delay_paths) { @@ -350,6 +362,9 @@ void print_pnr_sdc_constrain_primitive_pb_graph_node(const std::string& sdc_dir, /* Generate the descriptions*/ print_sdc_file_header(fp, std::string("Timing constraints for Grid " + pb_module_name + " in PnR")); + /* Print time unit for the SDC file */ + print_sdc_timescale(fp, time_unit_to_string(time_unit)); + /* We traverse the pb_graph pins where we can find pin-to-pin timing annotation * We walk through output pins here, build timing constraints by pair each output to input * Clock pins are not walked through because they will be handled by clock tree synthesis @@ -387,7 +402,7 @@ void print_pnr_sdc_constrain_primitive_pb_graph_node(const std::string& sdc_dir, generate_sdc_port(src_port), pb_module_name, generate_sdc_port(sink_port), - tmax); + tmax / time_unit); } /* Find min delay between src and sink pin */ @@ -400,7 +415,7 @@ void print_pnr_sdc_constrain_primitive_pb_graph_node(const std::string& sdc_dir, generate_sdc_port(src_port), pb_module_name, generate_sdc_port(sink_port), - tmin); + tmin / time_unit); } } } @@ -417,6 +432,7 @@ void print_pnr_sdc_constrain_primitive_pb_graph_node(const std::string& sdc_dir, *******************************************************************/ static void rec_print_pnr_sdc_constrain_pb_graph_timing(const std::string& sdc_dir, + const float& time_unit, const ModuleManager& module_manager, const VprDeviceAnnotation& device_annotation, t_pb_graph_node* parent_pb_graph_node, @@ -433,7 +449,9 @@ void rec_print_pnr_sdc_constrain_pb_graph_timing(const std::string& sdc_dir, /* Constrain the primitive node if a timing matrix is defined */ if (true == is_primitive_pb_type(parent_pb_type)) { - print_pnr_sdc_constrain_primitive_pb_graph_node(sdc_dir, module_manager, + print_pnr_sdc_constrain_primitive_pb_graph_node(sdc_dir, + time_unit, + module_manager, parent_pb_graph_node, constrain_zero_delay_paths); return; @@ -446,6 +464,7 @@ void rec_print_pnr_sdc_constrain_pb_graph_timing(const std::string& sdc_dir, /* Write a SDC file for this pb_type */ print_pnr_sdc_constrain_pb_graph_node_timing(sdc_dir, + time_unit, module_manager, parent_pb_graph_node, physical_mode, @@ -455,7 +474,9 @@ void rec_print_pnr_sdc_constrain_pb_graph_timing(const std::string& sdc_dir, * Note that we assume a full hierarchical P&R, we will only visit pb_graph_node of unique pb_type */ for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) { - rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, module_manager, + rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, + time_unit, + module_manager, device_annotation, &(parent_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][0]), constrain_zero_delay_paths); @@ -466,6 +487,7 @@ void rec_print_pnr_sdc_constrain_pb_graph_timing(const std::string& sdc_dir, * Top-level function to print timing constraints for pb_types *******************************************************************/ void print_pnr_sdc_constrain_grid_timing(const std::string& sdc_dir, + const float& time_unit, const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const ModuleManager& module_manager, @@ -485,7 +507,8 @@ void print_pnr_sdc_constrain_grid_timing(const std::string& sdc_dir, continue; } /* Special for I/O block, generate one module for each border side */ - rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, module_manager, + rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, time_unit, + module_manager, device_annotation, pb_graph_head, constrain_zero_delay_paths); diff --git a/openfpga/src/fpga_sdc/pnr_sdc_grid_writer.h b/openfpga/src/fpga_sdc/pnr_sdc_grid_writer.h index 400ca1616..909caff2a 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_grid_writer.h +++ b/openfpga/src/fpga_sdc/pnr_sdc_grid_writer.h @@ -18,6 +18,7 @@ namespace openfpga { void print_pnr_sdc_constrain_grid_timing(const std::string& sdc_dir, + const float& time_unit, const DeviceContext& device_ctx, const VprDeviceAnnotation& device_annotation, const ModuleManager& module_manager, diff --git a/openfpga/src/fpga_sdc/pnr_sdc_option.cpp b/openfpga/src/fpga_sdc/pnr_sdc_option.cpp index 3b7c417e0..f8ee429f3 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_option.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_option.cpp @@ -13,6 +13,7 @@ PnrSdcOption::PnrSdcOption(const std::string& sdc_dir) { sdc_dir_ = sdc_dir; hierarchical_ = false; flatten_names_ = false; + time_unit_ = 1.; constrain_global_port_ = false; constrain_non_clock_global_port_ = false; constrain_grid_ = false; @@ -39,6 +40,10 @@ bool PnrSdcOption::hierarchical() const { return hierarchical_; } +float PnrSdcOption::time_unit() const { + return time_unit_; +} + bool PnrSdcOption::generate_sdc_pnr() const { return constrain_global_port_ || constrain_grid_ @@ -100,6 +105,10 @@ void PnrSdcOption::set_hierarchical(const bool& hierarchical) { hierarchical_ = hierarchical; } +void PnrSdcOption::set_time_unit(const float& time_unit) { + time_unit_ = time_unit; +} + void PnrSdcOption::set_generate_sdc_pnr(const bool& generate_sdc_pnr) { constrain_global_port_ = generate_sdc_pnr; constrain_grid_ = generate_sdc_pnr; diff --git a/openfpga/src/fpga_sdc/pnr_sdc_option.h b/openfpga/src/fpga_sdc/pnr_sdc_option.h index ff52a75fd..4fe997321 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_option.h +++ b/openfpga/src/fpga_sdc/pnr_sdc_option.h @@ -18,6 +18,7 @@ class PnrSdcOption { std::string sdc_dir() const; bool flatten_names() const; bool hierarchical() const; + float time_unit() const; bool generate_sdc_pnr() const; bool constrain_global_port() const; bool constrain_non_clock_global_port() const; @@ -32,6 +33,7 @@ class PnrSdcOption { void set_sdc_dir(const std::string& sdc_dir); void set_flatten_names(const bool& flatten_names); void set_hierarchical(const bool& hierarchical); + void set_time_unit(const float& time_unit); void set_generate_sdc_pnr(const bool& generate_sdc_pnr); void set_constrain_global_port(const bool& constrain_global_port); void set_constrain_non_clock_global_port(const bool& constrain_non_clock_global_port); @@ -46,6 +48,7 @@ class PnrSdcOption { std::string sdc_dir_; bool flatten_names_; bool hierarchical_; + float time_unit_; bool constrain_global_port_; bool constrain_non_clock_global_port_; bool constrain_grid_; diff --git a/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp index 60e756839..e530e7903 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.cpp @@ -16,6 +16,7 @@ #include "vtr_time.h" /* Headers from openfpgautil library */ +#include "openfpga_scale.h" #include "openfpga_port.h" #include "openfpga_side_manager.h" #include "openfpga_digest.h" @@ -49,6 +50,7 @@ float find_pnr_sdc_switch_tmax(const t_rr_switch_inf& switch_inf) { *******************************************************************/ static void print_pnr_sdc_constrain_sb_mux_timing(std::fstream& fp, + const float& time_unit, const bool& hierarchical, const std::string& module_path, const ModuleManager& module_manager, @@ -104,7 +106,7 @@ void print_pnr_sdc_constrain_sb_mux_timing(std::fstream& fp, generate_sdc_port(module_manager.module_port(sb_module, module_input_port)), module_path, generate_sdc_port(module_manager.module_port(sb_module, module_output_port)), - switch_delays[module_input_port]); + switch_delays[module_input_port] / time_unit); } else { VTR_ASSERT_SAFE(true == hierarchical); @@ -112,7 +114,7 @@ void print_pnr_sdc_constrain_sb_mux_timing(std::fstream& fp, module_manager, sb_module, module_input_port, sb_module, module_output_port, - switch_delays[module_input_port]); + switch_delays[module_input_port] / time_unit); } } } @@ -127,6 +129,7 @@ void print_pnr_sdc_constrain_sb_mux_timing(std::fstream& fp, *******************************************************************/ static void print_pnr_sdc_constrain_sb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const std::string& module_path, const ModuleManager& module_manager, @@ -152,6 +155,9 @@ void print_pnr_sdc_constrain_sb_timing(const std::string& sdc_dir, /* Generate the descriptions*/ print_sdc_file_header(fp, std::string("Constrain timing of Switch Block " + sb_module_name + " for PnR")); + /* Print time unit for the SDC file */ + print_sdc_timescale(fp, time_unit_to_string(time_unit)); + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { SideManager side_manager(side); for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) { @@ -166,6 +172,7 @@ void print_pnr_sdc_constrain_sb_timing(const std::string& sdc_dir, } /* This is a MUX, constrain all the paths from an input to an output */ print_pnr_sdc_constrain_sb_mux_timing(fp, + time_unit, hierarchical, module_path, module_manager, sb_module, @@ -186,6 +193,7 @@ void print_pnr_sdc_constrain_sb_timing(const std::string& sdc_dir, * This function is designed for flatten routing hierarchy *******************************************************************/ void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const ModuleManager& module_manager, const ModuleId& top_module, @@ -223,6 +231,7 @@ void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_di module_path += std::string(")") + std::string("\\"); print_pnr_sdc_constrain_sb_timing(sdc_dir, + time_unit, hierarchical, module_path, module_manager, @@ -238,6 +247,7 @@ void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_di * This function is designed for compact routing hierarchy *******************************************************************/ void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const ModuleManager& module_manager, const ModuleId& top_module, @@ -283,6 +293,7 @@ void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_di module_path += std::string(")") + std::string("\\"); print_pnr_sdc_constrain_sb_timing(sdc_dir, + time_unit, hierarchical, module_path, module_manager, @@ -298,6 +309,7 @@ void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_di *******************************************************************/ static void print_pnr_sdc_constrain_cb_mux_timing(std::fstream& fp, + const float& time_unit, const bool& hierarchical, const std::string& module_path, const ModuleManager& module_manager, @@ -373,7 +385,7 @@ void print_pnr_sdc_constrain_cb_mux_timing(std::fstream& fp, module_manager, cb_module, module_input_port, cb_module, module_output_port, - switch_delays[module_input_port]); + switch_delays[module_input_port] / time_unit); } else { VTR_ASSERT_SAFE(false == hierarchical); print_pnr_sdc_regexp_constrain_max_delay(fp, @@ -381,7 +393,7 @@ void print_pnr_sdc_constrain_cb_mux_timing(std::fstream& fp, generate_sdc_port(module_manager.module_port(cb_module, module_input_port)), std::string(module_path), generate_sdc_port(module_manager.module_port(cb_module, module_output_port)), - switch_delays[module_input_port]); + switch_delays[module_input_port] / time_unit); } } @@ -393,6 +405,7 @@ void print_pnr_sdc_constrain_cb_mux_timing(std::fstream& fp, *******************************************************************/ static void print_pnr_sdc_constrain_cb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const std::string& module_path, const ModuleManager& module_manager, @@ -420,6 +433,9 @@ void print_pnr_sdc_constrain_cb_timing(const std::string& sdc_dir, /* Generate the descriptions*/ print_sdc_file_header(fp, std::string("Constrain timing of Connection Block " + cb_module_name + " for PnR")); + /* Print time unit for the SDC file */ + print_sdc_timescale(fp, time_unit_to_string(time_unit)); + /* Contrain each routing track inside the connection block */ for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { /* Create a port description for the input */ @@ -463,7 +479,7 @@ void print_pnr_sdc_constrain_cb_timing(const std::string& sdc_dir, module_manager, cb_module, input_port_id, cb_module, output_port_id, - routing_segment_delay); + routing_segment_delay / time_unit); } else { VTR_ASSERT_SAFE(false == hierarchical); print_pnr_sdc_regexp_constrain_max_delay(fp, @@ -471,7 +487,7 @@ void print_pnr_sdc_constrain_cb_timing(const std::string& sdc_dir, generate_sdc_port(module_manager.module_port(cb_module, input_port_id)), std::string(module_path), generate_sdc_port(module_manager.module_port(cb_module, output_port_id)), - routing_segment_delay); + routing_segment_delay / time_unit); } } @@ -484,6 +500,7 @@ void print_pnr_sdc_constrain_cb_timing(const std::string& sdc_dir, for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) { const RRNodeId& ipin_rr_node = rr_gsb.get_ipin_node(cb_ipin_side, inode); print_pnr_sdc_constrain_cb_mux_timing(fp, + time_unit, hierarchical, module_path, module_manager, cb_module, rr_graph, rr_gsb, cb_type, @@ -502,6 +519,7 @@ void print_pnr_sdc_constrain_cb_timing(const std::string& sdc_dir, *******************************************************************/ static void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const ModuleManager& module_manager, const ModuleId& top_module, @@ -542,6 +560,7 @@ void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_di module_path += std::string(")") + std::string("\\"); print_pnr_sdc_constrain_cb_timing(sdc_dir, + time_unit, hierarchical, module_path, module_manager, @@ -559,6 +578,7 @@ void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_di * and print SDC file for each of them *******************************************************************/ void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const ModuleManager& module_manager, const ModuleId& top_module, @@ -569,14 +589,16 @@ void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_di /* Start time count */ vtr::ScopedStartFinishTimer timer("Write SDC for constrain Connection Block timing for P&R flow"); - print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_dir, hierarchical, + print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_dir, time_unit, + hierarchical, module_manager, top_module, rr_graph, device_rr_gsb, CHANX, constrain_zero_delay_paths); - print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_dir, hierarchical, + print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_dir, time_unit, + hierarchical, module_manager, top_module, rr_graph, device_rr_gsb, @@ -589,6 +611,7 @@ void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_di * This function is designed for compact routing hierarchy *******************************************************************/ void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const ModuleManager& module_manager, const ModuleId& top_module, @@ -631,6 +654,7 @@ void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_di module_path += std::string(")") + std::string("\\"); print_pnr_sdc_constrain_cb_timing(sdc_dir, + time_unit, hierarchical, module_path, module_manager, @@ -670,6 +694,7 @@ void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_di module_path += std::string(")") + std::string("\\"); print_pnr_sdc_constrain_cb_timing(sdc_dir, + time_unit, hierarchical, module_path, module_manager, diff --git a/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.h b/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.h index a29a7ac56..d95c03a00 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.h +++ b/openfpga/src/fpga_sdc/pnr_sdc_routing_writer.h @@ -18,6 +18,7 @@ namespace openfpga { void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const ModuleManager& module_manager, const ModuleId& top_module, @@ -26,6 +27,7 @@ void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_di const bool& constrain_zero_delay_paths); void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const ModuleManager& module_manager, const ModuleId& top_module, @@ -34,6 +36,7 @@ void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_di const bool& constrain_zero_delay_paths); void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const ModuleManager& module_manager, const ModuleId& top_module, @@ -42,6 +45,7 @@ void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_di const bool& constrain_zero_delay_paths); void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_dir, + const float& time_unit, const bool& hierarchical, const ModuleManager& module_manager, const ModuleId& top_module, diff --git a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp index c88e3e2ef..ae6d3a4b8 100644 --- a/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp +++ b/openfpga/src/fpga_sdc/pnr_sdc_writer.cpp @@ -442,6 +442,7 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, if (true == sdc_options.constrain_sb()) { if (true == compact_routing_hierarchy) { print_pnr_sdc_compact_routing_constrain_sb_timing(sdc_options.sdc_dir(), + sdc_options.time_unit(), sdc_options.hierarchical(), module_manager, top_module, @@ -451,6 +452,7 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, } else { VTR_ASSERT_SAFE (false == compact_routing_hierarchy); print_pnr_sdc_flatten_routing_constrain_sb_timing(sdc_options.sdc_dir(), + sdc_options.time_unit(), sdc_options.hierarchical(), module_manager, top_module, @@ -464,6 +466,7 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, if (true == sdc_options.constrain_cb()) { if (true == compact_routing_hierarchy) { print_pnr_sdc_compact_routing_constrain_cb_timing(sdc_options.sdc_dir(), + sdc_options.time_unit(), sdc_options.hierarchical(), module_manager, top_module, @@ -473,6 +476,7 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, } else { VTR_ASSERT_SAFE (false == compact_routing_hierarchy); print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_options.sdc_dir(), + sdc_options.time_unit(), sdc_options.hierarchical(), module_manager, top_module, @@ -485,6 +489,7 @@ void print_pnr_sdc(const PnrSdcOption& sdc_options, /* Output Timing constraints for Programmable blocks */ if (true == sdc_options.constrain_grid()) { print_pnr_sdc_constrain_grid_timing(sdc_options.sdc_dir(), + sdc_options.time_unit(), device_ctx, device_annotation, module_manager, diff --git a/openfpga/src/fpga_sdc/sdc_writer_utils.cpp b/openfpga/src/fpga_sdc/sdc_writer_utils.cpp index 13b70fec4..0fcf450ad 100644 --- a/openfpga/src/fpga_sdc/sdc_writer_utils.cpp +++ b/openfpga/src/fpga_sdc/sdc_writer_utils.cpp @@ -38,6 +38,21 @@ void print_sdc_file_header(std::fstream& fp, fp << std::endl; } +/******************************************************************** + * Write a timescale definition in SDC file + *******************************************************************/ +void print_sdc_timescale(std::fstream& fp, + const std::string& timescale) { + + valid_file_stream(fp); + + fp << "#############################################" << std::endl; + fp << "#\tDefine time unit " << std::endl; + fp << "#############################################" << std::endl; + fp << "set_units -time " << timescale << std::endl; + fp << std::endl; +} + /******************************************************************** * Write a port in SDC format *******************************************************************/ diff --git a/openfpga/src/fpga_sdc/sdc_writer_utils.h b/openfpga/src/fpga_sdc/sdc_writer_utils.h index c7a7cd3a1..4b353045b 100644 --- a/openfpga/src/fpga_sdc/sdc_writer_utils.h +++ b/openfpga/src/fpga_sdc/sdc_writer_utils.h @@ -19,6 +19,9 @@ namespace openfpga { void print_sdc_file_header(std::fstream& fp, const std::string& usage); +void print_sdc_timescale(std::fstream& fp, + const std::string& timescale); + std::string generate_sdc_port(const BasicPort& port); void print_pnr_sdc_constrain_max_delay(std::fstream& fp,