start refactoring sdc generator, make it geneirc by placing it in parallel to Verilog generator

This commit is contained in:
tangxifan 2019-11-07 22:20:48 -07:00
parent d391983e8c
commit 14e7744fee
15 changed files with 606 additions and 71 deletions

View File

@ -24,9 +24,11 @@
/* Include spice support headers*/
#include "linkedlist.h"
#include "circuit_library_utils.h"
#include "fpga_x2p_utils.h"
#include "fpga_x2p_backannotate_utils.h"
#include "fpga_x2p_setup.h"
#include "fpga_x2p_naming.h"
#include "mux_library_builder.h"
#include "build_device_module.h"
@ -36,6 +38,7 @@
#include "spice_api.h"
#include "verilog_api.h"
#include "sdc_api.h"
#include "fpga_bitstream.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);
}
/* 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 */
if ((TRUE == vpr_setup.FPGA_SPICE_Opts.BitstreamGenOpts.gen_bitstream)
&&(FALSE == vpr_setup.FPGA_SPICE_Opts.SpiceOpts.do_spice)

View File

@ -13,6 +13,8 @@
#include "circuit_library.h"
#include "vpr_types.h"
constexpr char* FPGA_X2P_DEFAULT_SDC_DIR = "SDC";
std::string generate_mux_node_name(const size_t& node_level,
const bool& add_buffer_postfix);

View File

@ -85,26 +85,6 @@ char* format_dir_path(char* dir_path) {
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) {
/* F_OK checks existence and also R_OK, W_OK, X_OK,
* for readable, writable, excutable
@ -251,18 +231,6 @@ char* chomp_file_name_postfix(char* file_name) {
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 */
void fprint_commented_sram_bits(FILE* fp,
int num_sram_bits, int* sram_bits) {
@ -633,21 +601,6 @@ char* my_ito1hot(int in_int, int bin_len) {
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 */
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;
}
/* 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 */
char* my_itobin(int in_int, int bin_len) {
char* ret = (char*) my_calloc (bin_len + 1, sizeof(char));

View File

@ -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;
}

View File

@ -3,16 +3,28 @@
#include <fstream>
#include <vector>
#include <string>
#include "my_free_fwd.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);
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* 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);
@ -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);
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);
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* fpga_spice_create_one_subckt_filename(const char* file_name_prefix,

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -42,7 +42,7 @@ void print_verilog_file_header(std::fstream& fp,
fp << "//-------------------------------------------" << std::endl;
fp << "//----- Time scale -----" << std::endl;
fp << "`timescale 1ns / 1ps" << std::endl;
fp << "\n";
fp << std::endl;
}
/********************************************************************