start refactoring sdc generator, make it geneirc by placing it in parallel to Verilog generator
This commit is contained in:
parent
d391983e8c
commit
14e7744fee
|
@ -24,9 +24,11 @@
|
||||||
|
|
||||||
/* Include spice support headers*/
|
/* Include spice support headers*/
|
||||||
#include "linkedlist.h"
|
#include "linkedlist.h"
|
||||||
|
#include "circuit_library_utils.h"
|
||||||
#include "fpga_x2p_utils.h"
|
#include "fpga_x2p_utils.h"
|
||||||
#include "fpga_x2p_backannotate_utils.h"
|
#include "fpga_x2p_backannotate_utils.h"
|
||||||
#include "fpga_x2p_setup.h"
|
#include "fpga_x2p_setup.h"
|
||||||
|
#include "fpga_x2p_naming.h"
|
||||||
|
|
||||||
#include "mux_library_builder.h"
|
#include "mux_library_builder.h"
|
||||||
#include "build_device_module.h"
|
#include "build_device_module.h"
|
||||||
|
@ -36,6 +38,7 @@
|
||||||
|
|
||||||
#include "spice_api.h"
|
#include "spice_api.h"
|
||||||
#include "verilog_api.h"
|
#include "verilog_api.h"
|
||||||
|
#include "sdc_api.h"
|
||||||
#include "fpga_bitstream.h"
|
#include "fpga_bitstream.h"
|
||||||
|
|
||||||
#include "fpga_x2p_reserved_words.h"
|
#include "fpga_x2p_reserved_words.h"
|
||||||
|
@ -139,6 +142,28 @@ void vpr_fpga_x2p_tool_suites(t_vpr_setup vpr_setup,
|
||||||
vpr_setup, Arch, vpr_setup.FileNameOpts.CircuitName);
|
vpr_setup, Arch, vpr_setup.FileNameOpts.CircuitName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Run SDC Generator */
|
||||||
|
std::string src_dir = find_path_dir_name(std::string(vpr_setup.FileNameOpts.CircuitName));
|
||||||
|
|
||||||
|
/* Use current directory if there is not dir path given */
|
||||||
|
if (true == src_dir.empty()) {
|
||||||
|
src_dir = std::string("./");
|
||||||
|
} else {
|
||||||
|
src_dir = format_dir_path(src_dir);
|
||||||
|
}
|
||||||
|
SdcOption sdc_options(format_dir_path(src_dir + std::string(FPGA_X2P_DEFAULT_SDC_DIR)));
|
||||||
|
sdc_options.set_generate_sdc_pnr(TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.print_sdc_pnr);
|
||||||
|
sdc_options.set_generate_sdc_analysis(TRUE == vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts.print_sdc_analysis);
|
||||||
|
|
||||||
|
if (true == sdc_options.generate_sdc()) {
|
||||||
|
std::vector<CircuitPortId> global_ports = find_circuit_library_global_ports(Arch.spice->circuit_lib);
|
||||||
|
/* TODO: the critical path delay unit should be explicit! */
|
||||||
|
fpga_sdc_generator(sdc_options,
|
||||||
|
Arch.spice->spice_params.stimulate_params.vpr_crit_path_delay / 1e-9,
|
||||||
|
Arch.spice->circuit_lib, global_ports);
|
||||||
|
}
|
||||||
|
|
||||||
/* Xifan Tang: Bitstream Generator */
|
/* Xifan Tang: Bitstream Generator */
|
||||||
if ((TRUE == vpr_setup.FPGA_SPICE_Opts.BitstreamGenOpts.gen_bitstream)
|
if ((TRUE == vpr_setup.FPGA_SPICE_Opts.BitstreamGenOpts.gen_bitstream)
|
||||||
&&(FALSE == vpr_setup.FPGA_SPICE_Opts.SpiceOpts.do_spice)
|
&&(FALSE == vpr_setup.FPGA_SPICE_Opts.SpiceOpts.do_spice)
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include "circuit_library.h"
|
#include "circuit_library.h"
|
||||||
#include "vpr_types.h"
|
#include "vpr_types.h"
|
||||||
|
|
||||||
|
constexpr char* FPGA_X2P_DEFAULT_SDC_DIR = "SDC";
|
||||||
|
|
||||||
std::string generate_mux_node_name(const size_t& node_level,
|
std::string generate_mux_node_name(const size_t& node_level,
|
||||||
const bool& add_buffer_postfix);
|
const bool& add_buffer_postfix);
|
||||||
|
|
||||||
|
|
|
@ -85,26 +85,6 @@ char* format_dir_path(char* dir_path) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************
|
|
||||||
* Format a path of directory:
|
|
||||||
* 1. Replace "\" with "/"
|
|
||||||
* 2. add a "/" if the string does not end with a "/"
|
|
||||||
***********************************************/
|
|
||||||
std::string format_dir_path(const std::string& dir_path) {
|
|
||||||
std::string ret = dir_path;
|
|
||||||
|
|
||||||
/* Replace "\" with "/" */
|
|
||||||
std::replace(ret.begin(), ret.end(), '\\', '/');
|
|
||||||
|
|
||||||
/* Complete the string with a "/" if it does not end with that */
|
|
||||||
if ('/' != ret.back()) {
|
|
||||||
ret.push_back('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int try_access_file(char* file_path) {
|
int try_access_file(char* file_path) {
|
||||||
/* F_OK checks existence and also R_OK, W_OK, X_OK,
|
/* F_OK checks existence and also R_OK, W_OK, X_OK,
|
||||||
* for readable, writable, excutable
|
* for readable, writable, excutable
|
||||||
|
@ -251,18 +231,6 @@ char* chomp_file_name_postfix(char* file_name) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_file_handler(std::fstream& fp) {
|
|
||||||
/* Make sure we have a valid file handler*/
|
|
||||||
/* Print out debugging information for if the file is not opened/created properly */
|
|
||||||
if (!fp.is_open() || !fp.good()) {
|
|
||||||
vpr_printf(TIO_MESSAGE_ERROR,
|
|
||||||
"(FILE:%s,LINE[%d])Failure in create file!\n",
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Print SRAM bits, typically in a comment line */
|
/* Print SRAM bits, typically in a comment line */
|
||||||
void fprint_commented_sram_bits(FILE* fp,
|
void fprint_commented_sram_bits(FILE* fp,
|
||||||
int num_sram_bits, int* sram_bits) {
|
int num_sram_bits, int* sram_bits) {
|
||||||
|
@ -633,21 +601,6 @@ char* my_ito1hot(int in_int, int bin_len) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert an integer to an one-hot encoding integer array */
|
|
||||||
std::vector<size_t> my_ito1hot_vec(const size_t& in_int, const size_t& bin_len) {
|
|
||||||
/* Make sure we do not have any overflow! */
|
|
||||||
VTR_ASSERT ( (in_int <= bin_len) );
|
|
||||||
|
|
||||||
/* Initialize */
|
|
||||||
std::vector<size_t> ret(bin_len, 0);
|
|
||||||
|
|
||||||
if (bin_len == in_int) {
|
|
||||||
return ret; /* all zero case */
|
|
||||||
}
|
|
||||||
ret[in_int] = 1; /* Keep a good sequence of bits */
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Converter an integer to a binary string */
|
/* Converter an integer to a binary string */
|
||||||
int* my_itobin_int(int in_int, int bin_len) {
|
int* my_itobin_int(int in_int, int bin_len) {
|
||||||
|
@ -669,24 +622,6 @@ int* my_itobin_int(int in_int, int bin_len) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Converter an integer to a binary vector */
|
|
||||||
std::vector<size_t> my_itobin_vec(const size_t& in_int, const size_t& bin_len) {
|
|
||||||
std::vector<size_t> ret(bin_len, 0);
|
|
||||||
|
|
||||||
/* Make sure we do not have any overflow! */
|
|
||||||
VTR_ASSERT ( (in_int < pow(2., bin_len)) );
|
|
||||||
|
|
||||||
size_t temp = in_int;
|
|
||||||
for (size_t i = 0; i < bin_len; i++) {
|
|
||||||
if (1 == temp % 2) {
|
|
||||||
ret[i] = 1; /* Keep a good sequence of bits */
|
|
||||||
}
|
|
||||||
temp = temp / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Converter an integer to a binary string */
|
/* Converter an integer to a binary string */
|
||||||
char* my_itobin(int in_int, int bin_len) {
|
char* my_itobin(int in_int, int bin_len) {
|
||||||
char* ret = (char*) my_calloc (bin_len + 1, sizeof(char));
|
char* ret = (char*) my_calloc (bin_len + 1, sizeof(char));
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Most utilized functions in FPGA X2P framework
|
||||||
|
*******************************************************************/
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "fpga_x2p_utils.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Format a directory path:
|
||||||
|
* 1. Replace "\" with "/"
|
||||||
|
* 2. add a "/" if the string does not end with a "/"
|
||||||
|
*******************************************************************/
|
||||||
|
std::string format_dir_path(const std::string& dir_path_to_format) {
|
||||||
|
std::string formatted_dir_path = dir_path_to_format;
|
||||||
|
|
||||||
|
char illegal_back_slash = '\\';
|
||||||
|
char legal_back_slash = '/';
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* For windows OS, replace any '/' with '\' */
|
||||||
|
char illegal_back_slash = '/';
|
||||||
|
char legal_back_slash = '\\';
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Replace "\" with "/" */
|
||||||
|
std::replace(formatted_dir_path.begin(), formatted_dir_path.end(), illegal_back_slash, legal_back_slash);
|
||||||
|
|
||||||
|
/* Add a back slash the string is not ended like this! */
|
||||||
|
if (legal_back_slash != formatted_dir_path.back()) {
|
||||||
|
formatted_dir_path.push_back(legal_back_slash);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatted_dir_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Extract full file name from a full path of file
|
||||||
|
* For example: <dir_path>/<file_name>
|
||||||
|
* This function will return <file_name>
|
||||||
|
********************************************************************/
|
||||||
|
std::string find_path_file_name(const std::string& file_name) {
|
||||||
|
|
||||||
|
char back_slash = '/';
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* For windows OS, replace any '/' with '\' */
|
||||||
|
char back_slash = '\\';
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Find the last '/' in the string and return the left part */
|
||||||
|
size_t found = file_name.rfind(back_slash);
|
||||||
|
if (found != std::string::npos) {
|
||||||
|
return file_name.substr(found + 1);
|
||||||
|
}
|
||||||
|
/* Not found, return an empty string */
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Extract full directory path from a full path of file
|
||||||
|
* For example: <dir_path>/<file_name>
|
||||||
|
* This function will return <dir_path>
|
||||||
|
********************************************************************/
|
||||||
|
std::string find_path_dir_name(const std::string& file_name) {
|
||||||
|
|
||||||
|
char back_slash = '/';
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* For windows OS, replace any '/' with '\' */
|
||||||
|
char back_slash = '\\';
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Find the last '/' in the string and return the left part */
|
||||||
|
size_t found = file_name.rfind(back_slash);
|
||||||
|
if (found != std::string::npos) {
|
||||||
|
return file_name.substr(0, found);
|
||||||
|
}
|
||||||
|
/* Not found, return an empty string */
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Check if the file stream is valid
|
||||||
|
********************************************************************/
|
||||||
|
void check_file_handler(std::fstream& fp) {
|
||||||
|
/* Make sure we have a valid file handler*/
|
||||||
|
/* Print out debugging information for if the file is not opened/created properly */
|
||||||
|
if (!fp.is_open() || !fp.good()) {
|
||||||
|
vpr_printf(TIO_MESSAGE_ERROR,
|
||||||
|
"(FILE:%s,LINE[%d])Failure in create file!\n",
|
||||||
|
__FILE__, __LINE__);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Convert an integer to an one-hot encoding integer array
|
||||||
|
********************************************************************/
|
||||||
|
std::vector<size_t> my_ito1hot_vec(const size_t& in_int, const size_t& bin_len) {
|
||||||
|
/* Make sure we do not have any overflow! */
|
||||||
|
VTR_ASSERT ( (in_int <= bin_len) );
|
||||||
|
|
||||||
|
/* Initialize */
|
||||||
|
std::vector<size_t> ret(bin_len, 0);
|
||||||
|
|
||||||
|
if (bin_len == in_int) {
|
||||||
|
return ret; /* all zero case */
|
||||||
|
}
|
||||||
|
ret[in_int] = 1; /* Keep a good sequence of bits */
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Converter an integer to a binary vector
|
||||||
|
********************************************************************/
|
||||||
|
std::vector<size_t> my_itobin_vec(const size_t& in_int, const size_t& bin_len) {
|
||||||
|
std::vector<size_t> ret(bin_len, 0);
|
||||||
|
|
||||||
|
/* Make sure we do not have any overflow! */
|
||||||
|
VTR_ASSERT ( (in_int < pow(2., bin_len)) );
|
||||||
|
|
||||||
|
size_t temp = in_int;
|
||||||
|
for (size_t i = 0; i < bin_len; i++) {
|
||||||
|
if (1 == temp % 2) {
|
||||||
|
ret[i] = 1; /* Keep a good sequence of bits */
|
||||||
|
}
|
||||||
|
temp = temp / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -3,16 +3,28 @@
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "my_free_fwd.h"
|
#include "my_free_fwd.h"
|
||||||
#include "rr_blocks_naming.h"
|
#include "rr_blocks_naming.h"
|
||||||
|
|
||||||
|
std::string format_dir_path(const std::string& dir_path_to_format);
|
||||||
|
|
||||||
void check_file_handler(std::fstream& fp);
|
void check_file_handler(std::fstream& fp);
|
||||||
|
|
||||||
|
std::vector<size_t> my_ito1hot_vec(const size_t& in_int, const size_t& bin_len);
|
||||||
|
|
||||||
|
std::string find_path_dir_name(const std::string& file_name);
|
||||||
|
|
||||||
|
std::string find_path_file_name(const std::string& file_name);
|
||||||
|
|
||||||
|
std::vector<size_t> my_itobin_vec(const size_t& in_int, const size_t& bin_len);
|
||||||
|
|
||||||
|
/* Old functions */
|
||||||
|
|
||||||
char* my_gettime();
|
char* my_gettime();
|
||||||
|
|
||||||
char* format_dir_path(char* dir_path); /* TODO: TO BE REMOVED !!! */
|
char* format_dir_path(char* dir_path); /* TODO: TO BE REMOVED !!! */
|
||||||
std::string format_dir_path(const std::string& dir_path);
|
|
||||||
|
|
||||||
int try_access_file(char* file_path);
|
int try_access_file(char* file_path);
|
||||||
|
|
||||||
|
@ -66,14 +78,10 @@ t_spice_transistor_type* find_mosfet_tech_lib(t_spice_tech_lib tech_lib,
|
||||||
|
|
||||||
char* my_ito1hot(int in_int, int bin_len);
|
char* my_ito1hot(int in_int, int bin_len);
|
||||||
|
|
||||||
std::vector<size_t> my_ito1hot_vec(const size_t& in_int, const size_t& bin_len);
|
|
||||||
|
|
||||||
char* my_itobin(int in_int, int bin_len);
|
char* my_itobin(int in_int, int bin_len);
|
||||||
|
|
||||||
int* my_itobin_int(int in_int, int bin_len);
|
int* my_itobin_int(int in_int, int bin_len);
|
||||||
|
|
||||||
std::vector<size_t> my_itobin_vec(const size_t& in_int, const size_t& bin_len);
|
|
||||||
|
|
||||||
char* my_itoa(int input);
|
char* my_itoa(int input);
|
||||||
|
|
||||||
char* fpga_spice_create_one_subckt_filename(const char* file_name_prefix,
|
char* fpga_spice_create_one_subckt_filename(const char* file_name_prefix,
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file includes functions that print SDC (Synopsys Design Constraint)
|
||||||
|
* files in physical design tools, i.e., Place & Route (PnR) tools
|
||||||
|
* The SDC files are used to constrain the physical design for each module
|
||||||
|
* in FPGA fabric, such as Configurable Logic Blocks (CLBs),
|
||||||
|
* Heterogeneous blocks, Switch Blocks (SBs) and Connection Blocks (CBs)
|
||||||
|
*
|
||||||
|
* Note that this is different from the SDC to constrain VPR Place&Route
|
||||||
|
* engine! These SDCs are designed for PnR to generate FPGA layouts!!!
|
||||||
|
*******************************************************************/
|
||||||
|
#include <ctime>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "device_port.h"
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "fpga_x2p_utils.h"
|
||||||
|
|
||||||
|
#include "sdc_writer_naming.h"
|
||||||
|
#include "sdc_writer_utils.h"
|
||||||
|
#include "pnr_sdc_writer.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Local variables
|
||||||
|
*******************************************************************/
|
||||||
|
constexpr float SDC_FIXED_PROG_CLOCK_PERIOD = 100;
|
||||||
|
constexpr float SDC_FIXED_CLOCK_PERIOD = 10;
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print a SDC file to constrain the global ports of FPGA fabric
|
||||||
|
* in particular clock ports
|
||||||
|
*
|
||||||
|
* For programming clock, we give a fixed period, while for operating
|
||||||
|
* clock, we constrain with critical path delay
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_global_ports(const std::string& sdc_dir,
|
||||||
|
const float& critical_path_delay,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports) {
|
||||||
|
|
||||||
|
/* Create the file name for Verilog netlist */
|
||||||
|
std::string sdc_fname(sdc_dir + std::string(SDC_CLOCK_FILE_NAME));
|
||||||
|
|
||||||
|
vpr_printf(TIO_MESSAGE_INFO,
|
||||||
|
"Generating SDC for constraining clocks for P&R flow: %s ...",
|
||||||
|
sdc_fname.c_str());
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
clock_t t_start = clock();
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
check_file_handler(fp);
|
||||||
|
|
||||||
|
/* Generate the descriptions*/
|
||||||
|
print_sdc_file_header(fp, std::string("Clock contraints for PnR"));
|
||||||
|
|
||||||
|
/* Get clock port from the global port */
|
||||||
|
for (const CircuitPortId& clock_port : global_ports) {
|
||||||
|
if (SPICE_MODEL_PORT_CLOCK != circuit_lib.port_type(clock_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Reach here, it means a clock port and we need print constraints */
|
||||||
|
float clock_period = critical_path_delay;
|
||||||
|
|
||||||
|
/* For programming clock, we give a fixed period */
|
||||||
|
if (true == circuit_lib.port_is_prog(clock_port)) {
|
||||||
|
clock_period = SDC_FIXED_PROG_CLOCK_PERIOD;
|
||||||
|
/* Print comments */
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
fp << "# Create programmable clock " << std::endl;
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
} else {
|
||||||
|
/* Print comments */
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
fp << "# Create clock " << std::endl;
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const size_t& pin : circuit_lib.pins(clock_port)) {
|
||||||
|
BasicPort port_to_constrain(circuit_lib.port_prefix(clock_port), pin, pin);
|
||||||
|
|
||||||
|
fp << "create_clock ";
|
||||||
|
fp << generate_sdc_port(port_to_constrain) << "-period ";
|
||||||
|
fp << std::setprecision(10) << clock_period;
|
||||||
|
fp << " -waveform {0 ";
|
||||||
|
fp << std::setprecision(10) << clock_period / 2;
|
||||||
|
fp << "}" << std::endl;
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For non-clock port from the global port: give a fixed period */
|
||||||
|
for (const CircuitPortId& global_port : global_ports) {
|
||||||
|
if (SPICE_MODEL_PORT_CLOCK == circuit_lib.port_type(global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print comments */
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
fp << "# Constrain other global ports " << std::endl;
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
|
||||||
|
/* Reach here, it means a non-clock global port and we need print constraints */
|
||||||
|
float clock_period = SDC_FIXED_CLOCK_PERIOD;
|
||||||
|
for (const size_t& pin : circuit_lib.pins(global_port)) {
|
||||||
|
BasicPort port_to_constrain(circuit_lib.port_prefix(global_port), pin, pin);
|
||||||
|
fp << "create_clock ";
|
||||||
|
fp << generate_sdc_port(port_to_constrain) << "-period ";
|
||||||
|
fp << std::setprecision(10) << clock_period;
|
||||||
|
fp << " -waveform {0 ";
|
||||||
|
fp << std::setprecision(10) << clock_period / 2;
|
||||||
|
fp << "} ";
|
||||||
|
fp << "[list [get_ports { " << generate_sdc_port(port_to_constrain) << "}]]" << std::endl;
|
||||||
|
|
||||||
|
fp << "set_drive 0 " << generate_sdc_port(port_to_constrain) << std::endl;
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
|
||||||
|
/* End time count */
|
||||||
|
clock_t t_end = clock();
|
||||||
|
|
||||||
|
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||||
|
vpr_printf(TIO_MESSAGE_INFO,
|
||||||
|
"took %g seconds\n",
|
||||||
|
run_time_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Top-level function to print a number of SDC files in different purpose
|
||||||
|
* This function will generate files upon the options provided by users
|
||||||
|
* 1. Design constraints for CLBs
|
||||||
|
* 2. Design constraints for Switch Blocks
|
||||||
|
* 3. Design constraints for Connection Blocks
|
||||||
|
* 4. Design constraints for breaking the combinational loops in FPGA fabric
|
||||||
|
*******************************************************************/
|
||||||
|
void print_pnr_sdc(const SdcOption& sdc_options,
|
||||||
|
const float& critical_path_delay,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports) {
|
||||||
|
|
||||||
|
/* Part 1. Constrain global ports */
|
||||||
|
if (true == sdc_options.constrain_global_port()) {
|
||||||
|
print_pnr_sdc_global_ports(sdc_options.sdc_dir(), critical_path_delay, circuit_lib, global_ports);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef PNR_SDC_WRITER_H
|
||||||
|
#define PNR_SDC_WRITER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "vtr_geometry.h"
|
||||||
|
#include "vpr_types.h"
|
||||||
|
#include "rr_blocks.h"
|
||||||
|
#include "sdc_option.h"
|
||||||
|
|
||||||
|
void print_pnr_sdc(const SdcOption& sdc_options,
|
||||||
|
const float& critical_path_delay,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,34 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Useful APIs for SDC generator
|
||||||
|
*******************************************************************/
|
||||||
|
#include <ctime>
|
||||||
|
#include "pnr_sdc_writer.h"
|
||||||
|
|
||||||
|
#include "sdc_api.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Top-level function to launch SDC generator
|
||||||
|
*******************************************************************/
|
||||||
|
void fpga_sdc_generator(const SdcOption& sdc_options,
|
||||||
|
const float& critical_path_delay,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports) {
|
||||||
|
vpr_printf(TIO_MESSAGE_INFO,
|
||||||
|
"SDC generator starts...");
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
clock_t t_start = clock();
|
||||||
|
|
||||||
|
if (true == sdc_options.generate_sdc_pnr()) {
|
||||||
|
print_pnr_sdc(sdc_options.sdc_dir(), critical_path_delay, circuit_lib, global_ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End time count */
|
||||||
|
clock_t t_end = clock();
|
||||||
|
|
||||||
|
float run_time_sec = (float)(t_end - t_start) / CLOCKS_PER_SEC;
|
||||||
|
vpr_printf(TIO_MESSAGE_INFO,
|
||||||
|
"SDC generation took %g seconds\n",
|
||||||
|
run_time_sec);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef SDC_API_H
|
||||||
|
#define SDC_API_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "sdc_option.h"
|
||||||
|
#include "circuit_library.h"
|
||||||
|
|
||||||
|
void fpga_sdc_generator(const SdcOption& sdc_options,
|
||||||
|
const float& critical_path_delay,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,92 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Member functions for a data structure which includes all the options for the SDC generator
|
||||||
|
********************************************************************/
|
||||||
|
#include "sdc_option.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Public Constructors
|
||||||
|
********************************************************************/
|
||||||
|
SdcOption::SdcOption(const std::string& sdc_dir) {
|
||||||
|
sdc_dir_ = sdc_dir;
|
||||||
|
constrain_global_port_ = true;
|
||||||
|
constrain_grid_ = true;
|
||||||
|
constrain_sb_ = true;
|
||||||
|
constrain_cb_ = true;
|
||||||
|
break_loop_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Public accessors
|
||||||
|
********************************************************************/
|
||||||
|
std::string SdcOption::sdc_dir() const {
|
||||||
|
return sdc_dir_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdcOption::generate_sdc() const {
|
||||||
|
return generate_sdc_pnr_ && generate_sdc_analysis_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdcOption::generate_sdc_pnr() const {
|
||||||
|
return generate_sdc_pnr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdcOption::generate_sdc_analysis() const {
|
||||||
|
return generate_sdc_analysis_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdcOption::constrain_global_port() const {
|
||||||
|
return constrain_global_port_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdcOption::constrain_grid() const {
|
||||||
|
return constrain_grid_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdcOption::constrain_sb() const {
|
||||||
|
return constrain_sb_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdcOption::constrain_cb() const {
|
||||||
|
return constrain_cb_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdcOption::break_loop() const {
|
||||||
|
return break_loop_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Public mutators
|
||||||
|
********************************************************************/
|
||||||
|
void SdcOption::set_sdc_dir(const std::string& sdc_dir) {
|
||||||
|
sdc_dir_ = sdc_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdcOption::set_generate_sdc_pnr(const bool& generate_sdc_pnr) {
|
||||||
|
generate_sdc_pnr_ = generate_sdc_pnr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdcOption::set_generate_sdc_analysis(const bool& generate_sdc_analysis) {
|
||||||
|
generate_sdc_analysis_ = generate_sdc_analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdcOption::set_constrain_global_port(const bool& constrain_global_port) {
|
||||||
|
constrain_global_port_ = constrain_global_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdcOption::set_constrain_grid(const bool& constrain_grid) {
|
||||||
|
constrain_grid_ = constrain_grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdcOption::set_constrain_sb(const bool& constrain_sb) {
|
||||||
|
constrain_sb_ = constrain_sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdcOption::set_constrain_cb(const bool& constrain_cb) {
|
||||||
|
constrain_cb_ = constrain_cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdcOption::set_break_loop(const bool& break_loop) {
|
||||||
|
break_loop_ = break_loop;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef SDC_OPTION_H
|
||||||
|
#define SDC_OPTION_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* A data structure to include all the options for the SDC generator
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class SdcOption {
|
||||||
|
public: /* Public Constructors */
|
||||||
|
SdcOption(const std::string& sdc_dir);
|
||||||
|
public: /* Public accessors */
|
||||||
|
std::string sdc_dir() const;
|
||||||
|
bool generate_sdc() const;
|
||||||
|
bool generate_sdc_pnr() const;
|
||||||
|
bool generate_sdc_analysis() const;
|
||||||
|
bool constrain_global_port() const;
|
||||||
|
bool constrain_grid() const;
|
||||||
|
bool constrain_sb() const;
|
||||||
|
bool constrain_cb() const;
|
||||||
|
bool break_loop() const;
|
||||||
|
public: /* Public mutators */
|
||||||
|
void set_sdc_dir(const std::string& sdc_dir);
|
||||||
|
void set_generate_sdc_pnr(const bool& generate_sdc_pnr);
|
||||||
|
void set_generate_sdc_analysis(const bool& generate_sdc_analysis);
|
||||||
|
void set_constrain_global_port(const bool& constrain_global_port);
|
||||||
|
void set_constrain_grid(const bool& constrain_grid);
|
||||||
|
void set_constrain_sb(const bool& constrain_sb);
|
||||||
|
void set_constrain_cb(const bool& constrain_cb);
|
||||||
|
void set_break_loop(const bool& break_loop);
|
||||||
|
private: /* Internal data */
|
||||||
|
std::string sdc_dir_;
|
||||||
|
bool generate_sdc_pnr_;
|
||||||
|
bool constrain_global_port_;
|
||||||
|
bool constrain_grid_;
|
||||||
|
bool constrain_sb_;
|
||||||
|
bool constrain_cb_;
|
||||||
|
bool break_loop_;
|
||||||
|
bool generate_sdc_analysis_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef SDC_WRITER_NAMING_H
|
||||||
|
#define SDC_WRITER_NAMING_H
|
||||||
|
|
||||||
|
constexpr char* SDC_CLOCK_FILE_NAME = "clb_clock.sdc";
|
||||||
|
constexpr char* SDC_BENCHMARK_ANALYSIS_FILE_NAME= "fpga_top_analysis.sdc";
|
||||||
|
constexpr char* SDC_BREAK_COMB_LOOP_FILE_NAME = "break_loop.sdc";
|
||||||
|
constexpr char* SDC_CB_FILE_NAME = "cb.sdc";
|
||||||
|
constexpr char* SDC_SB_FILE_NAME = "sb.sdc";
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,52 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file include most utilized functions to be used in SDC writers
|
||||||
|
*******************************************************************/
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
#include "fpga_x2p_utils.h"
|
||||||
|
|
||||||
|
#include "sdc_writer_utils.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Write a head (description) in SDC file
|
||||||
|
*******************************************************************/
|
||||||
|
void print_sdc_file_header(std::fstream& fp,
|
||||||
|
const std::string& usage) {
|
||||||
|
|
||||||
|
check_file_handler(fp);
|
||||||
|
|
||||||
|
auto end = std::chrono::system_clock::now();
|
||||||
|
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
|
||||||
|
|
||||||
|
fp << "#############################################" << std::endl;
|
||||||
|
fp << "#\tSynopsys Design Constraints (SDC)" << std::endl;
|
||||||
|
fp << "#\tFor FPGA fabric " << std::endl;
|
||||||
|
fp << "#\tDescription: " << usage << std::endl;
|
||||||
|
fp << "#\tAuthor: Xifan TANG " << std::endl;
|
||||||
|
fp << "#\tOrganization: University of Utah " << std::endl;
|
||||||
|
fp << "#\tDate: " << std::ctime(&end_time);
|
||||||
|
fp << "#############################################" << std::endl;
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Write a port in SDC format
|
||||||
|
*******************************************************************/
|
||||||
|
std::string generate_sdc_port(const BasicPort& port) {
|
||||||
|
std::string sdc_line;
|
||||||
|
|
||||||
|
std::string size_str = "[" + std::to_string(port.get_lsb()) + ":" + std::to_string(port.get_msb()) + "]";
|
||||||
|
|
||||||
|
/* Only connection require a format of <port_name>[<lsb>:<msb>]
|
||||||
|
* others require a format of <port_type> [<lsb>:<msb>] <port_name>
|
||||||
|
*/
|
||||||
|
/* When LSB == MSB, we can use a simplified format <port_type>[<lsb>]*/
|
||||||
|
if ( 1 == port.get_width()) {
|
||||||
|
size_str = "[" + std::to_string(port.get_lsb()) + "]";
|
||||||
|
}
|
||||||
|
sdc_line = port.get_name() + size_str;
|
||||||
|
|
||||||
|
return sdc_line;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef SDC_WRITER_UTILS_H
|
||||||
|
#define SDC_WRITER_UTILS_H
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include "device_port.h"
|
||||||
|
|
||||||
|
void print_sdc_file_header(std::fstream& fp,
|
||||||
|
const std::string& usage);
|
||||||
|
|
||||||
|
std::string generate_sdc_port(const BasicPort& port);
|
||||||
|
|
||||||
|
#endif
|
|
@ -42,7 +42,7 @@ void print_verilog_file_header(std::fstream& fp,
|
||||||
fp << "//-------------------------------------------" << std::endl;
|
fp << "//-------------------------------------------" << std::endl;
|
||||||
fp << "//----- Time scale -----" << std::endl;
|
fp << "//----- Time scale -----" << std::endl;
|
||||||
fp << "`timescale 1ns / 1ps" << std::endl;
|
fp << "`timescale 1ns / 1ps" << std::endl;
|
||||||
fp << "\n";
|
fp << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
|
|
Loading…
Reference in New Issue