From 622c7826d1f48cf8a457aeda1dadd11ce05fda7f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 15 Feb 2020 15:03:00 -0700 Subject: [PATCH 1/6] start transplanting fpga_verilog --- .../libopenfpgautil/src/openfpga_digest.cpp | 113 +++++++++++++++++ .../libopenfpgautil/src/openfpga_digest.h | 8 ++ openfpga/src/fpga_verilog/verilog_api.cpp | 118 ++++++++++++++++++ openfpga/src/fpga_verilog/verilog_api.h | 36 ++++++ openfpga/src/fpga_verilog/verilog_constants.h | 40 ++++++ .../src/fpga_verilog/verilog_port_types.h | 16 +++ 6 files changed, 331 insertions(+) create mode 100644 openfpga/src/fpga_verilog/verilog_api.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_api.h create mode 100644 openfpga/src/fpga_verilog/verilog_constants.h create mode 100644 openfpga/src/fpga_verilog/verilog_port_types.h diff --git a/libopenfpga/libopenfpgautil/src/openfpga_digest.cpp b/libopenfpga/libopenfpgautil/src/openfpga_digest.cpp index f1b44d788..3340ea8b1 100644 --- a/libopenfpga/libopenfpgautil/src/openfpga_digest.cpp +++ b/libopenfpga/libopenfpgautil/src/openfpga_digest.cpp @@ -2,6 +2,9 @@ * This file includes functions that handles the file outputting * in OpenFPGA framework *******************************************************************/ +#include +#include + /* Headers from vtrutil library */ #include "vtr_log.h" @@ -37,4 +40,114 @@ void check_file_stream(const char* fname, } } +/******************************************************************** + * 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: / + * This function will return + ********************************************************************/ +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. The input is the file name! Return the original string */ + return file_name; +} + +/******************************************************************** + * Extract full directory path from a full path of file + * For example: / + * This function will return + ********************************************************************/ +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(); +} + +/******************************************************************** + * Create a directory with a given path + ********************************************************************/ +bool create_dir_path(const char* dir_path) { + /* Give up if the path is empty */ + if (nullptr == dir_path) { + VTR_LOG_ERROR("dir_path is empty and nothing is created.\n"); + return false; + } + + /* Try to create a directory */ + int ret = mkdir(dir_path, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH); + + /* Analyze the return flag and output status */ + switch (ret) { + case 0: + VTR_LOG("Succeed to create directory '%s'\n", + dir_path); + return true; + case -1: + if (EEXIST == errno) { + VTR_LOG_ERROR("Directory '%s' already exists. Will overwrite contents\n", + dir_path); + return true; + } + break; + default: + VTR_LOG_ERROR("Create directory '%s'...Failed!\n", + dir_path); + exit(1); + return false; + } + + return false; +} + } /* namespace openfpga ends */ diff --git a/libopenfpga/libopenfpgautil/src/openfpga_digest.h b/libopenfpga/libopenfpgautil/src/openfpga_digest.h index 332249d56..90af3dc4e 100644 --- a/libopenfpga/libopenfpgautil/src/openfpga_digest.h +++ b/libopenfpga/libopenfpgautil/src/openfpga_digest.h @@ -17,6 +17,14 @@ bool valid_file_stream(std::fstream& fp); void check_file_stream(const char* fname, std::fstream& fp); +std::string format_dir_path(const std::string& dir_path_to_format); + +std::string find_path_file_name(const std::string& file_name); + +std::string find_path_dir_name(const std::string& file_name); + +bool create_dir_path(const char* dir_path); + } /* namespace openfpga ends */ #endif diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp new file mode 100644 index 000000000..7de1b95e9 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -0,0 +1,118 @@ +/******************************************************************** + * This file include top-level function of FPGA-Verilog + ********************************************************************/ + +/* Headers from vtrutil library */ +#include "vtr_log.h" +#include "vtr_assert.h" +#include "vtr_time.h" + +#include "circuit_library_utils.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "device_rr_gsb.h" +#include "verilog_constants.h" +//#include "verilog_submodules.h" +//#include "verilog_routing.h" +//#include "verilog_submodules.h" +//#include "verilog_grid.h" +//#include "verilog_routing.h" +//#include "verilog_top_module.h" + +/* Header file for this source file */ +#include "verilog_api.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Top-level function of FPGA-Verilog + * This function will generate + * 1. primitive modules required by the full fabric + * which are LUTs, routing multiplexer, logic gates, transmission-gates etc. + * 2. Routing modules, which are Switch Blocks (SBs) and Connection Blocks (CBs) + * 3. Logic block modules, which are Configuration Logic Blocks (CLBs) + * 4. FPGA module, which are the full FPGA fabric with configuration protocol + * 5. A wrapper module, which encapsulate the FPGA module in a Verilog module which have the same port as the input benchmark + * 6. Testbench, where a FPGA module is configured with a bitstream and then driven by input vectors + * 7. Pre-configured testbench, which can skip the configuration phase and pre-configure the FPGA module. This testbench is created for quick verification and formal verification purpose. + * 8. Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated + ********************************************************************/ +void fabric_verilog(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const MuxLibrary& mux_lib, + const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb, + const std::string& output_directory, + const bool& compress_routing, + const bool& dump_explict_verilog, + const bool& verbose) { + + vtr::ScopedStartFinishTimer timer("Generate Verilog netlists for FPGA fabric\n"); + + std::string src_dir_path = format_dir_path(output_directory); + + /* Create directories */ + create_dir_path(src_dir_path.c_str()); + + /* Sub directory under SRC directory to contain all the primitive block netlists */ + std::string submodule_dir_path = src_dir_path + std::string(DEFAULT_SUBMODULE_DIR_NAME); + create_dir_path(submodule_dir_path.c_str()); + + /* Sub directory under SRC directory to contain all the logic block netlists */ + std::string lb_dir_path = src_dir_path + std::string(DEFAULT_LB_DIR_NAME); + create_dir_path(lb_dir_path.c_str()); + + /* Sub directory under SRC directory to contain all the routing block netlists */ + std::string rr_dir_path = src_dir_path + std::string(DEFAULT_RR_DIR_NAME); + create_dir_path(rr_dir_path.c_str()); + + /* Print Verilog files containing preprocessing flags */ + //print_verilog_preprocessing_flags_netlist(std::string(src_dir_path), + // vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts); + + //print_verilog_simulation_preprocessing_flags(std::string(src_dir_path), + // vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts); + + /* Generate primitive Verilog modules, which are corner stones of FPGA fabric + * Note that this function MUST be called before Verilog generation of + * core logic (i.e., logic blocks and routing resources) !!! + * This is because that this function will add the primitive Verilog modules to + * the module manager. + * Without the modules in the module manager, core logic generation is not possible!!! + */ + //print_verilog_submodules(module_manager, mux_lib, sram_verilog_orgz_info, src_dir_path.c_str(), submodule_dir_path.c_str(), + // Arch, vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts); + + /* Generate routing blocks */ + //if (true == compress_routing) { + // print_verilog_unique_routing_modules(module_manager, device_rr_gsb, + // src_dir_path, rr_dir_path, + // dump_explicit_verilog); + //} else { + // VTR_ASSERT(false == compress_routing); + // print_verilog_flatten_routing_modules(module_manager, device_rr_gsb, + // src_dir_path, rr_dir_path, + // dump_explicit_verilog); + //} + + /* Generate grids */ + //print_verilog_grids(module_manager, + // src_dir_path, lb_dir_path, + // dump_explicit_verilog); + + /* Generate FPGA fabric */ + //print_verilog_top_module(module_manager, + // std::string(vpr_setup.FileNameOpts.ArchFile), + // src_dir_path, + // dump_explicit_verilog); + + /* Given a brief stats on how many Verilog modules have been written to files */ + VTR_LOGV(verbose, + "Outputted %lu Verilog modules in total\n", + module_manager.num_modules()); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h new file mode 100644 index 000000000..9d601ee46 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -0,0 +1,36 @@ +#ifndef VERILOG_API_H +#define VERILOG_API_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include +#include +#include "vpr_types.h" +#include "mux_library.h" +#include "circuit_library.h" +#include "device_grid.h" +#include "device_rr_gsb.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void fabric_verilog(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const MuxLibrary& mux_lib, + const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb, + const std::string& output_directory, + const bool& compress_routing, + const bool& dump_explict_verilog, + const bool& verbose); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_verilog/verilog_constants.h b/openfpga/src/fpga_verilog/verilog_constants.h new file mode 100644 index 000000000..10a6c0669 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_constants.h @@ -0,0 +1,40 @@ +#ifndef VERILOG_CONSTANTS_H +#define VERILOG_CONSTANTS_H + +/* global parameters for dumping synthesizable verilog */ + +constexpr char* VERILOG_NETLIST_FILE_POSTFIX = ".v"; +constexpr float VERILOG_SIM_TIMESCALE = 1e-9; // Verilog Simulation time scale (minimum time unit) : 1ns + +constexpr char* VERILOG_TIMING_PREPROC_FLAG = "ENABLE_TIMING"; // the flag to enable timing definition during compilation +constexpr char* VERILOG_SIGNAL_INIT_PREPROC_FLAG = "ENABLE_SIGNAL_INITIALIZATION"; // the flag to enable signal initialization during compilation +constexpr char* VERILOG_FORMAL_VERIFICATGION_PREPROC_FLAG = "ENABLE_FORMAL_VERIFICATION"; // the flag to enable formal verification during compilation +constexpr char* INITIAL_SIMULATION_FLAG = "INITIAL_SIMULATION"; // the flag to enable initial functional verification +constexpr char* AUTOCHECKED_SIMULATION_FLAG = "AUTOCHECKED_SIMULATION"; // the flag to enable autochecked functional verification +constexpr char* FORMAL_SIMULATION_FLAG = "FORMAL_SIMULATION"; // the flag to enable formal functional verification + +constexpr char* DEFAULT_LB_DIR_NAME = "lb/"; +constexpr char* DEFAULT_RR_DIR_NAME = "routing/"; +constexpr char* DEFAULT_SUBMODULE_DIR_NAME = "sub_module/"; +constexpr char* MODELSIM_SIMULATION_TIME_UNIT = "ms"; + +// Icarus variables and flag +constexpr char* ICARUS_SIMULATION_FLAG = "ICARUS_SIMULATOR"; // the flag to enable specific Verilog code in testbenches +// End of Icarus variables and flag + +constexpr char* VERILOG_TOP_POSTFIX = "_top.v"; +constexpr char* DEFINES_VERILOG_FILE_NAME = "fpga_defines.v"; +constexpr char* DEFINES_VERILOG_SIMULATION_FILE_NAME = "define_simulation.v"; +constexpr char* SUBMODULE_VERILOG_FILE_NAME = "sub_module.v"; +constexpr char* LOGIC_BLOCK_VERILOG_FILE_NAME = "logic_blocks.v"; +constexpr char* LUTS_VERILOG_FILE_NAME = "luts.v"; +constexpr char* ROUTING_VERILOG_FILE_NAME = "routing.v"; +constexpr char* MUXES_VERILOG_FILE_NAME = "muxes.v"; +constexpr char* LOCAL_ENCODER_VERILOG_FILE_NAME = "local_encoder.v"; +constexpr char* MEMORIES_VERILOG_FILE_NAME = "memories.v"; +constexpr char* WIRES_VERILOG_FILE_NAME = "wires.v"; +constexpr char* ESSENTIALS_VERILOG_FILE_NAME = "inv_buf_passgate.v"; +constexpr char* CONFIG_PERIPHERAL_VERILOG_FILE_NAME = "config_peripherals.v"; +constexpr char* USER_DEFINED_TEMPLATE_VERILOG_FILE_NAME = "user_defined_templates.v"; + +#endif diff --git a/openfpga/src/fpga_verilog/verilog_port_types.h b/openfpga/src/fpga_verilog/verilog_port_types.h new file mode 100644 index 000000000..2100ba272 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_port_types.h @@ -0,0 +1,16 @@ +#ifndef VERILOG_PORT_TYPES_H +#define VERILOG_PORT_TYPES_H + +enum e_dump_verilog_port_type { + VERILOG_PORT_INPUT, + VERILOG_PORT_OUTPUT, + VERILOG_PORT_INOUT, + VERILOG_PORT_WIRE, + VERILOG_PORT_REG, + VERILOG_PORT_CONKT, + NUM_VERILOG_PORT_TYPES +}; +constexpr std::array VERILOG_PORT_TYPE_STRING = {{"input", "output", "inout", "wire", "reg", ""}}; /* string version of enum e_verilog_port_type */ + +#endif + From 8b0df8632c35332342401e2766a400e3a01962a4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 15 Feb 2020 20:38:45 -0700 Subject: [PATCH 2/6] bring fpga verilog create directory online --- openfpga/src/base/openfpga_context.h | 6 +++ openfpga/src/base/openfpga_flow_manager.cpp | 26 ++++++++++ openfpga/src/base/openfpga_flow_manager.h | 28 ++++++++++ openfpga/src/base/openfpga_setup_command.cpp | 2 +- .../src/base/openfpga_verilog_command.cpp | 51 +++++++++++++++++++ openfpga/src/base/openfpga_verilog_command.h | 21 ++++++++ openfpga/src/fpga_verilog/verilog_api.cpp | 20 ++++---- openfpga/src/fpga_verilog/verilog_api.h | 18 +++---- openfpga/src/main.cpp | 4 ++ openfpga/test_script/s298_k6_frac.openfpga | 4 ++ 10 files changed, 160 insertions(+), 20 deletions(-) create mode 100644 openfpga/src/base/openfpga_flow_manager.cpp create mode 100644 openfpga/src/base/openfpga_flow_manager.h create mode 100644 openfpga/src/base/openfpga_verilog_command.cpp create mode 100644 openfpga/src/base/openfpga_verilog_command.h diff --git a/openfpga/src/base/openfpga_context.h b/openfpga/src/base/openfpga_context.h index ac3cc99c8..5726cbba9 100644 --- a/openfpga/src/base/openfpga_context.h +++ b/openfpga/src/base/openfpga_context.h @@ -10,6 +10,7 @@ #include "mux_library.h" #include "tile_direct.h" #include "module_manager.h" +#include "openfpga_flow_manager.h" #include "device_rr_gsb.h" /******************************************************************** @@ -50,6 +51,7 @@ class OpenfpgaContext : public Context { const openfpga::MuxLibrary& mux_lib() const { return mux_lib_; } const openfpga::TileDirect& tile_direct() const { return tile_direct_; } const openfpga::ModuleManager& module_graph() const { return module_graph_; } + const openfpga::FlowManager& flow_manager() const { return flow_manager_; } public: /* Public mutators */ openfpga::Arch& mutable_arch() { return arch_; } openfpga::VprDeviceAnnotation& mutable_vpr_device_annotation() { return vpr_device_annotation_; } @@ -60,6 +62,7 @@ class OpenfpgaContext : public Context { openfpga::MuxLibrary& mutable_mux_lib() { return mux_lib_; } openfpga::TileDirect& mutable_tile_direct() { return tile_direct_; } openfpga::ModuleManager& mutable_module_graph() { return module_graph_; } + openfpga::FlowManager& mutable_flow_manager() { return flow_manager_; } private: /* Internal data */ /* Data structure to store information from read_openfpga_arch library */ openfpga::Arch arch_; @@ -87,6 +90,9 @@ class OpenfpgaContext : public Context { /* Fabric module graph */ openfpga::ModuleManager module_graph_; + + /* Flow status */ + openfpga::FlowManager flow_manager_; }; #endif diff --git a/openfpga/src/base/openfpga_flow_manager.cpp b/openfpga/src/base/openfpga_flow_manager.cpp new file mode 100644 index 000000000..bc97f291d --- /dev/null +++ b/openfpga/src/base/openfpga_flow_manager.cpp @@ -0,0 +1,26 @@ +/****************************************************************************** + * Memember functions for data structure FlowManager + ******************************************************************************/ +#include "vtr_assert.h" + +#include "openfpga_flow_manager.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/************************************************** + * Public Accessors + *************************************************/ +bool FlowManager::compress_routing() const { + return compress_routing_; +} + +/****************************************************************************** + * Private Mutators + ******************************************************************************/ +void FlowManager::set_compress_routing(const bool& enabled) { + compress_routing_ = enabled; +} + + +} /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_flow_manager.h b/openfpga/src/base/openfpga_flow_manager.h new file mode 100644 index 000000000..6a6ae7c75 --- /dev/null +++ b/openfpga/src/base/openfpga_flow_manager.h @@ -0,0 +1,28 @@ +#ifndef FLOW_MANAGER_H +#define FLOW_MANAGER_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +/* Begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * FlowManager aims to resolve the dependency between OpenFPGA functional + * code blocks + * It can provide flags for downstream modules about if the data structures + * they require have already been constructed + * + *******************************************************************/ +class FlowManager { + public: /* Public accessors */ + bool compress_routing() const; + public: /* Public mutators */ + void set_compress_routing(const bool& enabled); + private: /* Internal Data */ + bool compress_routing_; +}; + +} /* End namespace openfpga*/ + +#endif diff --git a/openfpga/src/base/openfpga_setup_command.cpp b/openfpga/src/base/openfpga_setup_command.cpp index 1b6971167..e84227187 100644 --- a/openfpga/src/base/openfpga_setup_command.cpp +++ b/openfpga/src/base/openfpga_setup_command.cpp @@ -135,7 +135,7 @@ void add_openfpga_setup_commands(openfpga::Shell& shell) { shell.set_command_execute_function(shell_cmd_build_fabric_id, build_fabric); /* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */ std::vector cmd_dependency_build_fabric; - cmd_dependency_lut_truth_table_fixup.push_back(shell_cmd_link_openfpga_arch_id); + cmd_dependency_build_fabric.push_back(shell_cmd_link_openfpga_arch_id); shell.set_command_dependency(shell_cmd_build_fabric_id, cmd_dependency_build_fabric); } diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp new file mode 100644 index 000000000..fa3ff9c4b --- /dev/null +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -0,0 +1,51 @@ +/******************************************************************** + * Add commands to the OpenFPGA shell interface, + * in purpose of generate Verilog netlists modeling the full FPGA fabric + * This is one of the core engine of openfpga, including: + * - generate_fabric_verilog : generate Verilog netlists about FPGA fabric + * - generate_fabric_verilog_testbench : TODO: generate Verilog testbenches + *******************************************************************/ +#include "openfpga_verilog.h" +#include "openfpga_verilog_command.h" + +/* begin namespace openfpga */ +namespace openfpga { + +void add_openfpga_verilog_commands(openfpga::Shell& shell) { + /* Get the unique id of 'build_fabric' command which is to be used in creating the dependency graph */ + const ShellCommandId& shell_cmd_build_fabric_id = shell.command(std::string("build_fabric")); + + /* Add a new class of commands */ + ShellCommandClassId openfpga_verilog_cmd_class = shell.add_command_class("FPGA-Verilog"); + + /******************************** + * Command 'wirte_fabric_verilog' + */ + Command shell_cmd_write_fabric_verilog("write_fabric_verilog"); + /* Add an option '--file' in short '-f'*/ + CommandOptionId fabric_verilog_output_opt = shell_cmd_write_fabric_verilog.add_option("file", true, "Specify the output directory for Verilog netlists"); + shell_cmd_write_fabric_verilog.set_option_short_name(fabric_verilog_output_opt, "f"); + shell_cmd_write_fabric_verilog.set_option_require_value(fabric_verilog_output_opt, openfpga::OPT_STRING); + /* Add an option '--explicit_port_mapping' */ + shell_cmd_write_fabric_verilog.add_option("explicit_port_mapping", false, "Use explicit port mapping in Verilog netlists"); + /* Add an option '--include_timing' */ + shell_cmd_write_fabric_verilog.add_option("include_timing", false, "Enable timing annotation in Verilog netlists"); + /* Add an option '--include_signal_init' */ + shell_cmd_write_fabric_verilog.add_option("include_signal_init", false, "Initialize all the signals in Verilog netlists"); + /* Add an option '--support_icarus_simulator' */ + shell_cmd_write_fabric_verilog.add_option("support_icarus_simulator", false, "Fine-tune Verilog netlists to support icarus simulator"); + /* Add an option '--verbose' */ + shell_cmd_write_fabric_verilog.add_option("verbose", false, "Enable verbose output"); + + /* Add command 'write_fabric_verilog' to the Shell */ + ShellCommandId shell_cmd_write_fabric_verilog_id = shell.add_command(shell_cmd_write_fabric_verilog, "generate Verilog netlists modeling full FPGA fabric"); + shell.set_command_class(shell_cmd_write_fabric_verilog_id, openfpga_verilog_cmd_class); + shell.set_command_execute_function(shell_cmd_write_fabric_verilog_id, write_fabric_verilog); + + /* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */ + std::vector cmd_dependency_write_fabric_verilog; + cmd_dependency_write_fabric_verilog.push_back(shell_cmd_build_fabric_id); + shell.set_command_dependency(shell_cmd_write_fabric_verilog_id, cmd_dependency_write_fabric_verilog); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_verilog_command.h b/openfpga/src/base/openfpga_verilog_command.h new file mode 100644 index 000000000..a99b809dd --- /dev/null +++ b/openfpga/src/base/openfpga_verilog_command.h @@ -0,0 +1,21 @@ +#ifndef OPENFPGA_VERILOG_COMMAND_H +#define OPENFPGA_VERILOG_COMMAND_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "shell.h" +#include "openfpga_context.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void add_openfpga_verilog_commands(openfpga::Shell& shell); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 7de1b95e9..0ad2cecb6 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -40,17 +40,17 @@ namespace openfpga { * 7. Pre-configured testbench, which can skip the configuration phase and pre-configure the FPGA module. This testbench is created for quick verification and formal verification purpose. * 8. Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated ********************************************************************/ -void fabric_verilog(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const DeviceGrid& grids, - const DeviceRRGSB& device_rr_gsb, - const std::string& output_directory, - const bool& compress_routing, - const bool& dump_explict_verilog, - const bool& verbose) { +void fpga_fabric_verilog(const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const MuxLibrary& mux_lib, + const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb, + const std::string& output_directory, + const bool& compress_routing, + const bool& dump_explict_verilog, + const bool& verbose) { - vtr::ScopedStartFinishTimer timer("Generate Verilog netlists for FPGA fabric\n"); + vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n"); std::string src_dir_path = format_dir_path(output_directory); diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index 9d601ee46..91f78e918 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -21,15 +21,15 @@ /* begin namespace openfpga */ namespace openfpga { -void fabric_verilog(ModuleManager& module_manager, - const CircuitLibrary& circuit_lib, - const MuxLibrary& mux_lib, - const DeviceGrid& grids, - const DeviceRRGSB& device_rr_gsb, - const std::string& output_directory, - const bool& compress_routing, - const bool& dump_explict_verilog, - const bool& verbose); +void fpga_fabric_verilog(const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const MuxLibrary& mux_lib, + const DeviceGrid& grids, + const DeviceRRGSB& device_rr_gsb, + const std::string& output_directory, + const bool& compress_routing, + const bool& dump_explict_verilog, + const bool& verbose); } /* end namespace openfpga */ diff --git a/openfpga/src/main.cpp b/openfpga/src/main.cpp index c7c31b00c..792223e3c 100644 --- a/openfpga/src/main.cpp +++ b/openfpga/src/main.cpp @@ -12,6 +12,7 @@ /* Header file from openfpga */ #include "vpr_command.h" #include "openfpga_setup_command.h" +#include "openfpga_verilog_command.h" #include "basic_command.h" #include "openfpga_title.h" @@ -52,6 +53,9 @@ int main(int argc, char** argv) { /* Add openfpga setup commands */ openfpga::add_openfpga_setup_commands(shell); + /* Add openfpga verilog commands */ + openfpga::add_openfpga_verilog_commands(shell); + /* Add basic commands: exit, help, etc. * Note: * This MUST be the last command group to be added! diff --git a/openfpga/test_script/s298_k6_frac.openfpga b/openfpga/test_script/s298_k6_frac.openfpga index 9d4edcced..1dece6d57 100644 --- a/openfpga/test_script/s298_k6_frac.openfpga +++ b/openfpga/test_script/s298_k6_frac.openfpga @@ -21,5 +21,9 @@ lut_truth_table_fixup #--verbose # - Enable pin duplication on grid modules build_fabric --compress_routing --duplicate_grid_pin --verbose +# Write the Verilog netlit for FPGA fabric +# - Enable the use of explicit port mapping in Verilog netlist +write_fabric_verilog --file /var/tmp/xtang/openfpga_test_src --explicit_port_mapping --include_timing --include_signal_init --support_icarus_simulator --verbose + # Finish and exit OpenFPGA exit From da79ef687c53351b4a184f1e7ab02156f05dd9d3 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 15 Feb 2020 20:54:37 -0700 Subject: [PATCH 3/6] add missing files --- openfpga/src/base/openfpga_verilog.cpp | 38 ++++++++++++++++++++++++++ openfpga/src/base/openfpga_verilog.h | 23 ++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 openfpga/src/base/openfpga_verilog.cpp create mode 100644 openfpga/src/base/openfpga_verilog.h diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp new file mode 100644 index 000000000..076127f4a --- /dev/null +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -0,0 +1,38 @@ +/******************************************************************** + * This file includes functions to compress the hierachy of routing architecture + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_time.h" +#include "vtr_log.h" + +#include "verilog_api.h" +#include "openfpga_verilog.h" + +/* Include global variables of VPR */ +#include "globals.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * A wrapper function to call the fabric_verilog function of FPGA-Verilog + *******************************************************************/ +void write_fabric_verilog(OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context) { + + CommandOptionId opt_output_dir = cmd.option("file"); + CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); + CommandOptionId opt_verbose = cmd.option("verbose"); + + fpga_fabric_verilog(openfpga_ctx.module_graph(), + openfpga_ctx.arch().circuit_lib, + openfpga_ctx.mux_lib(), + g_vpr_ctx.device().grid, + openfpga_ctx.device_rr_gsb(), + cmd_context.option_value(cmd, opt_output_dir), + openfpga_ctx.flow_manager().compress_routing(), + cmd_context.option_enable(cmd, opt_explicit_port_mapping), + cmd_context.option_enable(cmd, opt_verbose)); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_verilog.h b/openfpga/src/base/openfpga_verilog.h new file mode 100644 index 000000000..a62d19411 --- /dev/null +++ b/openfpga/src/base/openfpga_verilog.h @@ -0,0 +1,23 @@ +#ifndef OPENFPGA_VERILOG_H +#define OPENFPGA_VERILOG_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "command.h" +#include "command_context.h" +#include "openfpga_context.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void write_fabric_verilog(OpenfpgaContext& openfpga_ctx, + const Command& cmd, const CommandContext& cmd_context); + +} /* end namespace openfpga */ + +#endif From bf54be3d00ac77b0d98e575804b03ef83007db6c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 15 Feb 2020 21:39:47 -0700 Subject: [PATCH 4/6] add option data structure for FPGA Verilog --- openfpga/src/base/openfpga_verilog.cpp | 20 ++++- openfpga/src/fpga_verilog/verilog_api.cpp | 9 +-- openfpga/src/fpga_verilog/verilog_api.h | 6 +- openfpga/src/fpga_verilog/verilog_options.cpp | 73 +++++++++++++++++++ openfpga/src/fpga_verilog/verilog_options.h | 48 ++++++++++++ 5 files changed, 142 insertions(+), 14 deletions(-) create mode 100644 openfpga/src/fpga_verilog/verilog_options.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_options.h diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index 076127f4a..2612fe76a 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -22,17 +22,29 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx, CommandOptionId opt_output_dir = cmd.option("file"); CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping"); + CommandOptionId opt_include_timing = cmd.option("include_timing"); + CommandOptionId opt_include_signal_init = cmd.option("include_signal_init"); + CommandOptionId opt_support_icarus_simulator = cmd.option("support_icarus_simulator"); CommandOptionId opt_verbose = cmd.option("verbose"); + + /* This is an intermediate data structure which is designed to modularize the FPGA-Verilog + * Keep it independent from any other outside data structures + */ + FabricVerilogOption options; + options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir)); + options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping)); + options.set_include_timing(cmd_context.option_enable(cmd, opt_include_timing)); + options.set_include_signal_init(cmd_context.option_enable(cmd, opt_include_signal_init)); + options.set_support_icarus_simulator(cmd_context.option_enable(cmd, opt_support_icarus_simulator)); + options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); + options.set_compress_routing(openfpga_ctx.flow_manager().compress_routing()); fpga_fabric_verilog(openfpga_ctx.module_graph(), openfpga_ctx.arch().circuit_lib, openfpga_ctx.mux_lib(), g_vpr_ctx.device().grid, openfpga_ctx.device_rr_gsb(), - cmd_context.option_value(cmd, opt_output_dir), - openfpga_ctx.flow_manager().compress_routing(), - cmd_context.option_enable(cmd, opt_explicit_port_mapping), - cmd_context.option_enable(cmd, opt_verbose)); + options); } } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 0ad2cecb6..d8dd8dab8 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -45,14 +45,11 @@ void fpga_fabric_verilog(const ModuleManager& module_manager, const MuxLibrary& mux_lib, const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb, - const std::string& output_directory, - const bool& compress_routing, - const bool& dump_explict_verilog, - const bool& verbose) { + const FabricVerilogOption& options) { vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n"); - std::string src_dir_path = format_dir_path(output_directory); + std::string src_dir_path = format_dir_path(options.output_directory()); /* Create directories */ create_dir_path(src_dir_path.c_str()); @@ -110,7 +107,7 @@ void fpga_fabric_verilog(const ModuleManager& module_manager, // dump_explicit_verilog); /* Given a brief stats on how many Verilog modules have been written to files */ - VTR_LOGV(verbose, + VTR_LOGV(options.verbose_output(), "Outputted %lu Verilog modules in total\n", module_manager.num_modules()); } diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index 91f78e918..a24c0a1e8 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -13,6 +13,7 @@ #include "device_grid.h" #include "device_rr_gsb.h" #include "module_manager.h" +#include "verilog_options.h" /******************************************************************** * Function declaration @@ -26,10 +27,7 @@ void fpga_fabric_verilog(const ModuleManager& module_manager, const MuxLibrary& mux_lib, const DeviceGrid& grids, const DeviceRRGSB& device_rr_gsb, - const std::string& output_directory, - const bool& compress_routing, - const bool& dump_explict_verilog, - const bool& verbose); + const FabricVerilogOption& options); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_options.cpp b/openfpga/src/fpga_verilog/verilog_options.cpp new file mode 100644 index 000000000..a0dfd7e2a --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_options.cpp @@ -0,0 +1,73 @@ +/****************************************************************************** + * Memember functions for data structure FabricVerilogOption + ******************************************************************************/ +#include "vtr_assert.h" + +#include "verilog_options.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/************************************************** + * Public Accessors + *************************************************/ +std::string FabricVerilogOption::output_directory() const { + return output_directory_; +} + +bool FabricVerilogOption::support_icarus_simulator() const { + return support_icarus_simulator_; +} + +bool FabricVerilogOption::include_timing() const { + return include_timing_; +} + +bool FabricVerilogOption::include_signal_init() const { + return include_signal_init_; +} + +bool FabricVerilogOption::explicit_port_mapping() const { + return explicit_port_mapping_; +} + +bool FabricVerilogOption::compress_routing() const { + return compress_routing_; +} + +bool FabricVerilogOption::verbose_output() const { + return verbose_output_; +} + +/****************************************************************************** + * Private Mutators + ******************************************************************************/ +void FabricVerilogOption::set_output_directory(const std::string& output_dir) { + output_directory_ = output_dir; +} + +void FabricVerilogOption::set_support_icarus_simulator(const bool& enabled) { + support_icarus_simulator_ = enabled; +} + +void FabricVerilogOption::set_include_timing(const bool& enabled) { + include_timing_ = enabled; +} + +void FabricVerilogOption::set_include_signal_init(const bool& enabled) { + include_signal_init_ = enabled; +} + +void FabricVerilogOption::set_explicit_port_mapping(const bool& enabled) { + explicit_port_mapping_ = enabled; +} + +void FabricVerilogOption::set_compress_routing(const bool& enabled) { + compress_routing_ = enabled; +} + +void FabricVerilogOption::set_verbose_output(const bool& enabled) { + verbose_output_ = enabled; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_options.h b/openfpga/src/fpga_verilog/verilog_options.h new file mode 100644 index 000000000..336ec542a --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_options.h @@ -0,0 +1,48 @@ +#ifndef VERILOG_OPTIONS_H +#define VERILOG_OPTIONS_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include + +/* Begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * FlowManager aims to resolve the dependency between OpenFPGA functional + * code blocks + * It can provide flags for downstream modules about if the data structures + * they require have already been constructed + * + *******************************************************************/ +class FabricVerilogOption { + public: /* Public accessors */ + std::string output_directory() const; + bool support_icarus_simulator() const; + bool include_timing() const; + bool include_signal_init() const; + bool explicit_port_mapping() const; + bool compress_routing() const; + bool verbose_output() const; + public: /* Public mutators */ + void set_output_directory(const std::string& output_dir); + void set_support_icarus_simulator(const bool& enabled); + void set_include_timing(const bool& enabled); + void set_include_signal_init(const bool& enabled); + void set_explicit_port_mapping(const bool& enabled); + void set_compress_routing(const bool& enabled); + void set_verbose_output(const bool& enabled); + private: /* Internal Data */ + std::string output_directory_; + bool support_icarus_simulator_; + bool include_signal_init_; + bool include_timing_; + bool explicit_port_mapping_; + bool compress_routing_; + bool verbose_output_; +}; + +} /* End namespace openfpga*/ + +#endif From 0d5292ad0da38f66440805281288ce6823f1b9ed Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 15 Feb 2020 23:26:59 -0700 Subject: [PATCH 5/6] adapt verilog writer utils --- .../src/fpga_verilog/verilog_writer_utils.cpp | 1397 +++++++++++++++++ .../src/fpga_verilog/verilog_writer_utils.h | 187 +++ 2 files changed, 1584 insertions(+) create mode 100644 openfpga/src/fpga_verilog/verilog_writer_utils.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_writer_utils.h diff --git a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp new file mode 100644 index 000000000..5798b05fc --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp @@ -0,0 +1,1397 @@ +/************************************************ + * Include functions for most frequently + * used Verilog writers + ***********************************************/ +#include +#include +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from readarchopenfpga library */ +#include "circuit_types.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "openfpga_naming.h" +#include "circuit_library_utils.h" +#include "verilog_constants.h" +#include "verilog_writer_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/************************************************ + * Generate header comments for a Verilog netlist + * include the description + ***********************************************/ +void print_verilog_file_header(std::fstream& fp, + const std::string& usage) { + valid_file_stream(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 << "//\tFPGA Synthesizable Verilog Netlist" << 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 << "//----- Time scale -----" << std::endl; + fp << "`timescale 1ns / 1ps" << std::endl; + fp << std::endl; +} + +/******************************************************************** + * Print Verilog codes to include a netlist + *******************************************************************/ +void print_verilog_include_netlist(std::fstream& fp, + const std::string& netlist_name) { + valid_file_stream(fp); + + fp << "`include \"" << netlist_name << "\"" << std::endl; +} + +/******************************************************************** + * Print Verilog codes to define a preprocessing flag + *******************************************************************/ +void print_verilog_define_flag(std::fstream& fp, + const std::string& flag_name, + const int& flag_value) { + valid_file_stream(fp); + + fp << "`define " << flag_name << " " << flag_value << std::endl; +} + +/************************************************ + * Generate include files for a Verilog netlist + ***********************************************/ +void print_verilog_include_defines_preproc_file(std::fstream& fp, + const std::string& verilog_dir) { + + /* Generate the file name */ + std::string include_file_path = format_dir_path(verilog_dir); + include_file_path += std::string(DEFINES_VERILOG_FILE_NAME); + + print_verilog_include_netlist(fp, include_file_path); +} + +/************************************************ + * Print a Verilog comment line + ***********************************************/ +void print_verilog_comment(std::fstream& fp, + const std::string& comment) { + valid_file_stream(fp); + + fp << "// " << comment << std::endl; +} + +/************************************************ + * Print the declaration of a Verilog preprocessing flag + ***********************************************/ +void print_verilog_preprocessing_flag(std::fstream& fp, + const std::string& preproc_flag) { + valid_file_stream(fp); + + fp << "`ifdef " << preproc_flag << std::endl; +} + +/************************************************ + * Print the endif of a Verilog preprocessing flag + ***********************************************/ +void print_verilog_endif(std::fstream& fp) { + valid_file_stream(fp); + + fp << "`endif" << std::endl; +} + +/************************************************ + * Print a Verilog module definition + * We use the following format: + * module (); + ***********************************************/ +void print_verilog_module_definition(std::fstream& fp, + const ModuleManager& module_manager, const ModuleId& module_id) { + valid_file_stream(fp); + + print_verilog_comment(fp, std::string("----- Verilog module for " + module_manager.module_name(module_id) + " -----")); + + std::string module_head_line = "module " + module_manager.module_name(module_id) + "("; + fp << module_head_line; + + /* port type2type mapping */ + std::map port_type2type_map; + port_type2type_map[ModuleManager::MODULE_GLOBAL_PORT] = VERILOG_PORT_CONKT; + port_type2type_map[ModuleManager::MODULE_GPIO_PORT] = VERILOG_PORT_CONKT; + port_type2type_map[ModuleManager::MODULE_INOUT_PORT] = VERILOG_PORT_CONKT; + port_type2type_map[ModuleManager::MODULE_INPUT_PORT] = VERILOG_PORT_CONKT; + port_type2type_map[ModuleManager::MODULE_OUTPUT_PORT] = VERILOG_PORT_CONKT; + port_type2type_map[ModuleManager::MODULE_CLOCK_PORT] = VERILOG_PORT_CONKT; + + /* Port sequence: global, inout, input, output and clock ports, */ + size_t port_cnt = 0; + bool printed_ifdef = false; /* A flag to tell if an ifdef has been printed for the last port */ + for (const auto& kv : port_type2type_map) { + for (const auto& port : module_manager.module_ports_by_type(module_id, kv.first)) { + if (0 != port_cnt) { + /* Do not dump a comma for the first port */ + fp << "," << std::endl; + } + + if (true == printed_ifdef) { + /* Print an endif to pair the ifdef */ + print_verilog_endif(fp); + /* Reset the flag */ + printed_ifdef = false; + } + + ModulePortId port_id = module_manager.find_module_port(module_id, port.get_name()); + VTR_ASSERT(ModulePortId::INVALID() != port_id); + /* Print pre-processing flag for a port, if defined */ + std::string preproc_flag = module_manager.port_preproc_flag(module_id, port_id); + if (false == preproc_flag.empty()) { + /* Print an ifdef Verilog syntax */ + print_verilog_preprocessing_flag(fp, preproc_flag); + /* Raise the flag */ + printed_ifdef = true; + } + + /* Create a space for "module " except the first line! */ + if (0 != port_cnt) { + std::string port_whitespace(module_head_line.length(), ' '); + fp << port_whitespace; + } + /* Print port: only the port name is enough */ + fp << port.get_name(); + + /* Increase the counter */ + port_cnt++; + } + } + fp << ");" << std::endl; +} + +/************************************************ + * Print a Verilog module ports based on the module id + ***********************************************/ +void print_verilog_module_ports(std::fstream& fp, + const ModuleManager& module_manager, const ModuleId& module_id) { + valid_file_stream(fp); + + /* port type2type mapping */ + std::map port_type2type_map; + port_type2type_map[ModuleManager::MODULE_GLOBAL_PORT] = VERILOG_PORT_INPUT; + port_type2type_map[ModuleManager::MODULE_GPIO_PORT] = VERILOG_PORT_INOUT; + port_type2type_map[ModuleManager::MODULE_INOUT_PORT] = VERILOG_PORT_INOUT; + port_type2type_map[ModuleManager::MODULE_INPUT_PORT] = VERILOG_PORT_INPUT; + port_type2type_map[ModuleManager::MODULE_OUTPUT_PORT] = VERILOG_PORT_OUTPUT; + port_type2type_map[ModuleManager::MODULE_CLOCK_PORT] = VERILOG_PORT_INPUT; + + /* Port sequence: global, inout, input, output and clock ports, */ + for (const auto& kv : port_type2type_map) { + for (const auto& port : module_manager.module_ports_by_type(module_id, kv.first)) { + ModulePortId port_id = module_manager.find_module_port(module_id, port.get_name()); + VTR_ASSERT(ModulePortId::INVALID() != port_id); + /* Print pre-processing flag for a port, if defined */ + std::string preproc_flag = module_manager.port_preproc_flag(module_id, port_id); + if (false == preproc_flag.empty()) { + /* Print an ifdef Verilog syntax */ + print_verilog_preprocessing_flag(fp, preproc_flag); + } + + /* Print port */ + fp << "//----- " << module_manager.module_port_type_str(kv.first) << " -----" << std::endl; + fp << generate_verilog_port(kv.second, port); + fp << ";" << std::endl; + + if (false == preproc_flag.empty()) { + /* Print an endif to pair the ifdef */ + print_verilog_endif(fp); + } + } + } + + /* Output any port that is also wire connection */ + fp << std::endl; + fp << "//----- BEGIN wire-connection ports -----" << std::endl; + for (const auto& kv : port_type2type_map) { + for (const auto& port : module_manager.module_ports_by_type(module_id, kv.first)) { + /* Skip the ports that are not registered */ + ModulePortId port_id = module_manager.find_module_port(module_id, port.get_name()); + VTR_ASSERT(ModulePortId::INVALID() != port_id); + if (false == module_manager.port_is_wire(module_id, port_id)) { + continue; + } + + /* Print pre-processing flag for a port, if defined */ + std::string preproc_flag = module_manager.port_preproc_flag(module_id, port_id); + if (false == preproc_flag.empty()) { + /* Print an ifdef Verilog syntax */ + print_verilog_preprocessing_flag(fp, preproc_flag); + } + + /* Print port */ + fp << generate_verilog_port(VERILOG_PORT_WIRE, port); + fp << ";" << std::endl; + + if (false == preproc_flag.empty()) { + /* Print an endif to pair the ifdef */ + print_verilog_endif(fp); + } + } + } + fp << "//----- END wire-connection ports -----" << std::endl; + fp << std::endl; + + + /* Output any port that is registered */ + fp << std::endl; + fp << "//----- BEGIN Registered ports -----" << std::endl; + for (const auto& kv : port_type2type_map) { + for (const auto& port : module_manager.module_ports_by_type(module_id, kv.first)) { + /* Skip the ports that are not registered */ + ModulePortId port_id = module_manager.find_module_port(module_id, port.get_name()); + VTR_ASSERT(ModulePortId::INVALID() != port_id); + if (false == module_manager.port_is_register(module_id, port_id)) { + continue; + } + + /* Print pre-processing flag for a port, if defined */ + std::string preproc_flag = module_manager.port_preproc_flag(module_id, port_id); + if (false == preproc_flag.empty()) { + /* Print an ifdef Verilog syntax */ + print_verilog_preprocessing_flag(fp, preproc_flag); + } + + /* Print port */ + fp << generate_verilog_port(VERILOG_PORT_REG, port); + fp << ";" << std::endl; + + if (false == preproc_flag.empty()) { + /* Print an endif to pair the ifdef */ + print_verilog_endif(fp); + } + } + } + fp << "//----- END Registered ports -----" << std::endl; + fp << std::endl; +} + +/************************************************ + * Print a Verilog module declaration (definition + port list + * We use the following format: + * module (); + * + ***********************************************/ +void print_verilog_module_declaration(std::fstream& fp, + const ModuleManager& module_manager, const ModuleId& module_id) { + valid_file_stream(fp); + + print_verilog_module_definition(fp, module_manager, module_id); + + print_verilog_module_ports(fp, module_manager, module_id); +} + + +/******************************************************************** + * Print an instance in Verilog format (a generic version) + * This function will require user to provide an instance name + * + * This function will output the port map by referring to a port-to-port + * mapping: + * -> + * The key of the port-to-port mapping is the port name of the module: + * The value of the port-to-port mapping is the port information of the instance + * With link between module and instance, the function can output a Verilog + * instance easily, supporting both explicit port mapping: + * .() + * and inexplicit port mapping + * + * + * Note that, it is not necessary that the port-to-port mapping + * covers all the module ports. + * Any instance/module port which are not specified in the port-to-port + * mapping will be output by the module port name. + *******************************************************************/ +void print_verilog_module_instance(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const std::string& instance_name, + const std::map& port2port_name_map, + const bool& use_explicit_port_map) { + + valid_file_stream(fp); + + /* Check: all the key ports in the port2port_name_map does exist in the child module */ + for (const auto& kv : port2port_name_map) { + ModulePortId module_port_id = module_manager.find_module_port(module_id, kv.first); + VTR_ASSERT(ModulePortId::INVALID() != module_port_id); + } + + /* Print module name */ + fp << "\t" << module_manager.module_name(module_id) << " "; + /* Print instance name */ + fp << instance_name << " (" << std::endl; + + /* Print each port with/without explicit port map */ + /* port type2type mapping */ + std::map port_type2type_map; + port_type2type_map[ModuleManager::MODULE_GLOBAL_PORT] = VERILOG_PORT_CONKT; + port_type2type_map[ModuleManager::MODULE_GPIO_PORT] = VERILOG_PORT_CONKT; + port_type2type_map[ModuleManager::MODULE_INOUT_PORT] = VERILOG_PORT_CONKT; + port_type2type_map[ModuleManager::MODULE_INPUT_PORT] = VERILOG_PORT_CONKT; + port_type2type_map[ModuleManager::MODULE_OUTPUT_PORT] = VERILOG_PORT_CONKT; + port_type2type_map[ModuleManager::MODULE_CLOCK_PORT] = VERILOG_PORT_CONKT; + + /* Port sequence: global, inout, input, output and clock ports, */ + size_t port_cnt = 0; + for (const auto& kv : port_type2type_map) { + for (const auto& port : module_manager.module_ports_by_type(module_id, kv.first)) { + if (0 != port_cnt) { + /* Do not dump a comma for the first port */ + fp << "," << std::endl; + } + /* Print port */ + fp << "\t\t"; + /* if explicit port map is required, output the port name */ + if (true == use_explicit_port_map) { + fp << "." << port.get_name() << "("; + } + /* Try to find the instanced port name in the name map */ + if (port2port_name_map.find(port.get_name()) != port2port_name_map.end()) { + /* Found it, we assign the port name */ + /* TODO: make sure the port width matches! */ + ModulePortId module_port_id = module_manager.find_module_port(module_id, port.get_name()); + /* Get the port from module */ + BasicPort module_port = module_manager.module_port(module_id, module_port_id); + VTR_ASSERT(module_port.get_width() == port2port_name_map.at(port.get_name()).get_width()); + fp << generate_verilog_port(kv.second, port2port_name_map.at(port.get_name())); + } else { + /* Not found, we give the default port name */ + fp << generate_verilog_port(kv.second, port); + } + /* if explicit port map is required, output the pair of branket */ + if (true == use_explicit_port_map) { + fp << ")"; + } + port_cnt++; + } + } + + /* Print an end to the instance */ + fp << ");" << std::endl; +} + + +/************************************************ + * Print an instance for a Verilog module + * This function is a wrapper for the generic version of + * print_verilog_module_instance() + * This function create an instance name based on the index + * of the child module in its parent module + ***********************************************/ +void print_verilog_module_instance(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module_id, const ModuleId& child_module_id, + const std::map& port2port_name_map, + const bool& use_explicit_port_map) { + + /* Create instance name, _ */ + std::string instance_name = module_manager.module_name(child_module_id) + + "_" + + std::to_string(module_manager.num_instance(parent_module_id, child_module_id)) + + "_"; + + print_verilog_module_instance(fp, module_manager, child_module_id, instance_name, + port2port_name_map, use_explicit_port_map); +} + +/************************************************ + * Print an end line for a Verilog module + ***********************************************/ +void print_verilog_module_end(std::fstream& fp, + const std::string& module_name) { + valid_file_stream(fp); + + fp << "endmodule" << std::endl; + print_verilog_comment(fp, std::string("----- END Verilog module for " + module_name + " -----")); + fp << std::endl; +} + +/************************************************ + * Generate a string of a Verilog port + ***********************************************/ +std::string generate_verilog_port(const enum e_dump_verilog_port_type& verilog_port_type, + const BasicPort& port_info) { + std::string verilog_line; + + /* Ensure the port type is valid */ + VTR_ASSERT(verilog_port_type < NUM_VERILOG_PORT_TYPES); + + std::string size_str = "[" + std::to_string(port_info.get_lsb()) + ":" + std::to_string(port_info.get_msb()) + "]"; + + /* Only connection require a format of [:] + * others require a format of [:] + */ + if (VERILOG_PORT_CONKT == verilog_port_type) { + /* When LSB == MSB, we can use a simplified format []*/ + if ( 1 == port_info.get_width()) { + size_str = "[" + std::to_string(port_info.get_lsb()) + "]"; + } + verilog_line = port_info.get_name() + size_str; + } else { + verilog_line = VERILOG_PORT_TYPE_STRING[verilog_port_type]; + verilog_line += " " + size_str + " " + port_info.get_name(); + } + + return verilog_line; +} + +/******************************************************************** + * Evaluate if two Verilog ports can be merged: + * If the port name is same, it can merged + *******************************************************************/ +bool two_verilog_ports_mergeable(const BasicPort& portA, + const BasicPort& portB) { + if (0 == portA.get_name().compare(portB.get_name())) { + return true; + } + return false; +} + +/******************************************************************** + * Merge two Verilog ports, return the merged port + * The ports should have the same name + * The new LSB will be minimum of the LSBs of the two ports + * The new MSB will the maximum of the MSBs of the two ports + *******************************************************************/ +BasicPort merge_two_verilog_ports(const BasicPort& portA, + const BasicPort& portB) { + BasicPort merged_port; + + VTR_ASSERT(true == two_verilog_ports_mergeable(portA, portB)); + + merged_port.set_name(portA.get_name()); + merged_port.set_lsb((size_t)std::min((int)portA.get_lsb(), (int)portB.get_lsb())); + merged_port.set_msb((size_t)std::max((int)portA.get_msb(), (int)portB.get_msb())); + + return merged_port; +} + +/************************************************ + * This function takes a list of ports and + * combine the port string by comparing the name + * and width of ports. + * For example, two ports A and B share the same name is + * mergable as long as A's MSB + 1 == B's LSB + * Note that the port sequence really matters! + * This function will NOT change the sequence + * of ports in the list port_info + ***********************************************/ +std::vector combine_verilog_ports(const std::vector& ports) { + std::vector merged_ports; + + /* Directly return if there are no ports */ + if (0 == ports.size()) { + return merged_ports; + } + /* Push the first port to the merged ports */ + merged_ports.push_back(ports[0]); + + /* Iterate over ports */ + for (const auto& port : ports) { + /* Bypass the first port, it is already in the list */ + if (&port == &ports[0]) { + continue; + } + /* Identify if the port name can be potentially merged: if the port name is already in the merged port list, it may be merged */ + bool merged = false; + for (auto& merged_port : merged_ports) { + if (false == port.mergeable(merged_port)) { + /* Unable to merge, Go to next */ + continue; + } + /* May be merged, check LSB of port and MSB of merged_port */ + if (merged_port.get_msb() + 1 != port.get_lsb()) { + /* Unable to merge, Go to next */ + continue; + } + /* Reach here, we should merge the ports, + * LSB of merged_port remains the same, + * MSB of merged_port will be updated + * to the MSB of port + */ + merged_port.set_msb(port.get_msb()); + merged = true; + break; + } + if (false == merged) { + /* Unable to merge, add the port to merged port list */ + merged_ports.push_back(port); + } + } + + return merged_ports; +} + +/************************************************ + * Generate the string of a list of verilog ports + ***********************************************/ +std::string generate_verilog_ports(const std::vector& merged_ports) { + + /* Output the string of ports: + * If there is only one port in the merged_port list + * we only output the port. + * If there are more than one port in the merged port list, we output an concatenated port: + * {, , ... } + */ + VTR_ASSERT(0 < merged_ports.size()); + if ( 1 == merged_ports.size()) { + /* Use connection type of verilog port */ + return generate_verilog_port(VERILOG_PORT_CONKT, merged_ports[0]); + } + + std::string verilog_line = "{"; + for (const auto& port : merged_ports) { + /* The first port does not need a comma */ + if (&port != &merged_ports[0]) { + verilog_line += ", "; + } + verilog_line += generate_verilog_port(VERILOG_PORT_CONKT, port); + } + verilog_line += "}"; + + return verilog_line; +} + +/******************************************************************** + * Generate a bus port (could be used to create a local wire) + * for a list of Verilog ports + * The bus port will be created by aggregating the ports in the list + * A bus port name may be need only there are many ports with + * different names. It is hard to name the bus port + *******************************************************************/ +BasicPort generate_verilog_bus_port(const std::vector& input_ports, + const std::string& bus_port_name) { + /* Try to combine the ports */ + std::vector combined_input_ports = combine_verilog_ports(input_ports); + + /* Create a port data structure that is to be returned */ + BasicPort bus_port; + + if (1 == combined_input_ports.size()) { + bus_port = combined_input_ports[0]; + } else { + /* TODO: the naming could be more flexible? */ + bus_port.set_name(bus_port_name); + /* Deposite a [0:0] port */ + bus_port.set_width(1); + for (const auto& port : combined_input_ports) { + bus_port.combine(port); + } + } + + return bus_port; +} + +/******************************************************************** + * Generate a bus wire declaration for a list of Verilog ports + * Output ports: the local_wire name + * Input ports: the driving ports + * When there are more than two ports, a bus wiring will be created + * {, , ... } + *******************************************************************/ +std::string generate_verilog_local_wire(const BasicPort& output_port, + const std::vector& input_ports) { + /* Try to combine the ports */ + std::vector combined_input_ports = combine_verilog_ports(input_ports); + + /* If we have more than 1 port in the combined ports , + * output a local wire */ + VTR_ASSERT(0 < combined_input_ports.size()); + + /* Must check: the port width matches */ + size_t input_ports_width = 0; + for (const auto& port : combined_input_ports) { + /* We must have valid ports! */ + VTR_ASSERT( 0 < port.get_width() ); + input_ports_width += port.get_width(); + } + VTR_ASSERT( input_ports_width == output_port.get_width() ); + + std::string wire_str; + wire_str += generate_verilog_port(VERILOG_PORT_WIRE, output_port); + wire_str += " = "; + wire_str += generate_verilog_ports(combined_input_ports); + wire_str += ";"; + + return wire_str; +} + +/******************************************************************** + * Generate a string for a constant value in Verilog format: + * <#.of bits>'b + *******************************************************************/ +std::string generate_verilog_constant_values(const std::vector& const_values) { + std::string str = std::to_string(const_values.size()); + str += "'b"; + for (const auto& val : const_values) { + str += std::to_string(val); + } + return str; +} + +/******************************************************************** + * Generate a verilog port with a deposite of constant values + ********************************************************************/ +std::string generate_verilog_port_constant_values(const BasicPort& output_port, + const std::vector& const_values) { + std::string port_str; + + /* Must check: the port width matches */ + VTR_ASSERT( const_values.size() == output_port.get_width() ); + + port_str = generate_verilog_port(VERILOG_PORT_CONKT, output_port); + port_str += " = "; + port_str += generate_verilog_constant_values(const_values); + return port_str; +} + +/******************************************************************** + * Generate a wire connection, that assigns constant values to a + * Verilog port + *******************************************************************/ +void print_verilog_wire_constant_values(std::fstream& fp, + const BasicPort& output_port, + const std::vector& const_values) { + /* Make sure we have a valid file handler*/ + valid_file_stream(fp); + + fp << "\t"; + fp << "assign "; + fp << generate_verilog_port_constant_values(output_port, const_values); + fp << ";" << std::endl; +} + +/******************************************************************** + * Deposit constant values to a Verilog port + *******************************************************************/ +void print_verilog_deposit_wire_constant_values(std::fstream& fp, + const BasicPort& output_port, + const std::vector& const_values) { + /* Make sure we have a valid file handler*/ + valid_file_stream(fp); + + fp << "\t"; + fp << "$deposit("; + fp << generate_verilog_port(VERILOG_PORT_CONKT, output_port); + fp << ", "; + fp << generate_verilog_constant_values(const_values); + fp << ");" << std::endl; +} + +/******************************************************************** + * Generate a wire connection, that assigns constant values to a + * Verilog port + *******************************************************************/ +void print_verilog_force_wire_constant_values(std::fstream& fp, + const BasicPort& output_port, + const std::vector& const_values) { + /* Make sure we have a valid file handler*/ + valid_file_stream(fp); + + fp << "\t"; + fp << "force "; + fp << generate_verilog_port_constant_values(output_port, const_values); + fp << ";" << std::endl; +} + +/******************************************************************** + * Generate a wire connection for two Verilog ports + * using "assign" syntax + *******************************************************************/ +void print_verilog_wire_connection(std::fstream& fp, + const BasicPort& output_port, + const BasicPort& input_port, + const bool& inverted) { + /* Make sure we have a valid file handler*/ + valid_file_stream(fp); + + /* Must check: the port width matches */ + VTR_ASSERT( input_port.get_width() == output_port.get_width() ); + + fp << "\t"; + fp << "assign "; + fp << generate_verilog_port(VERILOG_PORT_CONKT, output_port); + fp << " = "; + + if (true == inverted) { + fp << "~"; + } + + fp << generate_verilog_port(VERILOG_PORT_CONKT, input_port); + fp << ";" << std::endl; +} + +/******************************************************************** + * Generate a wire connection for two Verilog ports + * using "assign" syntax + *******************************************************************/ +void print_verilog_register_connection(std::fstream& fp, + const BasicPort& output_port, + const BasicPort& input_port, + const bool& inverted) { + /* Make sure we have a valid file handler*/ + valid_file_stream(fp); + + /* Must check: the port width matches */ + VTR_ASSERT( input_port.get_width() == output_port.get_width() ); + + fp << "\t"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, output_port); + fp << " <= "; + + if (true == inverted) { + fp << "~"; + } + + fp << generate_verilog_port(VERILOG_PORT_CONKT, input_port); + fp << ";" << std::endl; +} + + +/******************************************************************** + * Generate an instance of a buffer module + * with given information about the input and output ports of instance + * + * Buffer instance + * +----------------------------------------+ + * instance_input_port --->| buffer_input_port buffer_output_port|----> instance_output_port + * +----------------------------------------+ + * + * Restrictions: + * Buffer must have only 1 input (non-global) port and 1 output (non-global) port + *******************************************************************/ +void print_verilog_buffer_instance(std::fstream& fp, + ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const ModuleId& parent_module_id, + const CircuitModelId& buffer_model, + const BasicPort& instance_input_port, + const BasicPort& instance_output_port) { + /* Make sure we have a valid file handler*/ + valid_file_stream(fp); + + /* To match the context, Buffer should have only 2 non-global ports: 1 input port and 1 output port */ + std::vector buffer_model_input_ports = circuit_lib.model_ports_by_type(buffer_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector buffer_model_output_ports = circuit_lib.model_ports_by_type(buffer_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + VTR_ASSERT(1 == buffer_model_input_ports.size()); + VTR_ASSERT(1 == buffer_model_output_ports.size()); + + /* Get the moduleId for the buffer module */ + ModuleId buffer_module_id = module_manager.find_module(circuit_lib.model_name(buffer_model)); + /* We must have one */ + VTR_ASSERT(ModuleId::INVALID() != buffer_module_id); + + /* Create a port-to-port map */ + std::map buffer_port2port_name_map; + + /* Build the link between buffer_input_port[0] and output_node_pre_buffer + * Build the link between buffer_output_port[0] and output_node_bufferred + */ + { /* Create a code block to accommodate the local variables */ + std::string module_input_port_name = circuit_lib.port_lib_name(buffer_model_input_ports[0]); + buffer_port2port_name_map[module_input_port_name] = instance_input_port; + std::string module_output_port_name = circuit_lib.port_lib_name(buffer_model_output_ports[0]); + buffer_port2port_name_map[module_output_port_name] = instance_output_port; + } + + /* Output an instance of the module */ + print_verilog_module_instance(fp, module_manager, parent_module_id, buffer_module_id, buffer_port2port_name_map, circuit_lib.dump_explicit_port_map(buffer_model)); + + /* IMPORTANT: this update MUST be called after the instance outputting!!!! + * update the module manager with the relationship between the parent and child modules + */ + module_manager.add_child_module(parent_module_id, buffer_module_id); +} + +/******************************************************************** + * Print local wires that are used for SRAM configuration + * The local wires are strongly dependent on the organization of SRAMs. + * Standalone SRAMs: + * ----------------- + * No need for local wires, their outputs are port of the module + * + * Module + * +------------------------------+ + * | Sub-module | + * | +---------------------+ | + * | | sram_out|---->|---->sram_out + * | | | | + * | | sram_out|---->|---->sram_out + * | | | | + * | +---------------------+ | + * +------------------------------+ + * + * Configuration chain-style + * ------------------------- + * wire [0:N] config_bus + * + * + * Module + * +--------------------------------------------------------------+ + * | config_bus config_bus config_bus config_bus | + * | [0] [1] [2] [N] | + * | | | | | | + * | v v v v | + * ccff_head| ----------+ +---------+ +------------+ +----------------|-> ccff_tail + * | | ^ | ^ | ^ | + * | head v |tail v | v | | + * | +----------+ +----------+ +----------+ | + * | | Memory | | Memory | | Memory | | + * | | Module | | Module | ... | Module | | + * | | [0] | | [1] | | [N] | | + * | +----------+ +----------+ +----------+ | + * | | | | | + * | v v v | + * | +----------+ +----------+ +----------+ | + * | | MUX | | MUX | | MUX | | + * | | Module | | Module | ... | Module | | + * | | [0] | | [1] | | [N] | | + * | +----------+ +----------+ +----------+ | + * | | + * +--------------------------------------------------------------+ + * + * Memory bank-style + * ----------------- + * two ports will be added, which are regular output and inverted output + * Note that the outputs are the data outputs of SRAMs + * BL/WLs of memory decoders are ports of module but not local wires + * + * Module + * +-------------------------------------------------+ + * | | + BL/WL bus --+--------+------------+-----------------+ | + * | | | | | + * | BL/WL v BL/WL v BL/WL v | + * | +----------+ +----------+ +----------+ | + * | | Memory | | Memory | | Memory | | + * | | Module | | Module | ... | Module | | + * | | [0] | | [1] | | [N] | | + * | +----------+ +----------+ +----------+ | + * | | | | | + * | v v v | + * | +----------+ +----------+ +----------+ | + * | | MUX | | MUX | | MUX | | + * | | Module | | Module | ... | Module | | + * | | [0] | | [1] | | [N] | | + * | +----------+ +----------+ +----------+ | + * | | + * +-------------------------------------------------+ + * + ********************************************************************/ +void print_verilog_local_sram_wires(std::fstream& fp, + const CircuitLibrary& circuit_lib, + const CircuitModelId& sram_model, + const e_config_protocol_type sram_orgz_type, + const size_t& port_size) { + /* Make sure we have a valid file handler*/ + valid_file_stream(fp); + + /* Port size must be at least one! */ + if (0 == port_size) { + return; + } + + /* Depend on the configuraion style */ + switch(sram_orgz_type) { + case CONFIG_MEM_STANDALONE: + /* Nothing to do here */ + break; + case CONFIG_MEM_SCAN_CHAIN: { + /* Generate the name of local wire for the CCFF inputs, CCFF output and inverted output */ + /* [0] => CCFF input */ + BasicPort ccff_config_bus_port(generate_local_config_bus_port_name(), port_size); + fp << generate_verilog_port(VERILOG_PORT_WIRE, ccff_config_bus_port) << ";" << std::endl; + /* Connect first CCFF to the head */ + /* Head is always a 1-bit port */ + BasicPort ccff_head_port(generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_INPUT), 1); + BasicPort ccff_head_local_port(ccff_config_bus_port.get_name(), 1); + print_verilog_wire_connection(fp, ccff_head_local_port, ccff_head_port, false); + /* Connect last CCFF to the tail */ + /* Tail is always a 1-bit port */ + BasicPort ccff_tail_port(generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_OUTPUT), 1); + BasicPort ccff_tail_local_port(ccff_config_bus_port.get_name(), ccff_config_bus_port.get_msb(), ccff_config_bus_port.get_msb()); + print_verilog_wire_connection(fp, ccff_tail_local_port, ccff_tail_port, false); + break; + } + case CONFIG_MEM_MEMORY_BANK: { + /* Generate the name of local wire for the SRAM output and inverted output */ + std::vector sram_ports; + /* [0] => SRAM output */ + sram_ports.push_back(BasicPort(generate_sram_local_port_name(circuit_lib, sram_model, sram_orgz_type, CIRCUIT_MODEL_PORT_INPUT), port_size)); + /* [1] => SRAM inverted output */ + sram_ports.push_back(BasicPort(generate_sram_local_port_name(circuit_lib, sram_model, sram_orgz_type, CIRCUIT_MODEL_PORT_OUTPUT), port_size)); + /* Print local wire definition */ + for (const auto& sram_port : sram_ports) { + fp << generate_verilog_port(VERILOG_PORT_WIRE, sram_port) << ";" << std::endl; + } + + break; + } + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid SRAM organization!\n"); + exit(1); + } +} + +/********************************************************************* + * Print a number of bus ports which are wired to the configuration + * ports of a CMOS (SRAM-based) routing multiplexer + * This port is supposed to be used locally inside a Verilog/SPICE module + * + * The following shows a few representative examples: + * + * For standalone configuration style: + * ------------------------------------ + * No bus needed + * + * Configuration chain-style + * ------------------------- + * wire [0:N] config_bus + * + * config_bus config_bus config_bus config_bus + * [0] [1] [2] [N] + * | | | | + * v v v v + * ccff_head ----------+ +---------+ +------------+ +----> ccff_tail + * | ^ | ^ | ^ + * head v |tail v | v | + * +----------+ +----------+ +----------+ + * | Memory | | Memory | | Memory | + * | Module | | Module | ... | Module | + * | [0] | | [1] | | [N] | + * +----------+ +----------+ +----------+ + * | | | + * v v v + * +----------+ +----------+ +----------+ + * | MUX | | MUX | | MUX | + * | Module | | Module | ... | Module | + * | [0] | | [1] | | [N] | + * +----------+ +----------+ +----------+ + * + * Memory bank-style + * ----------------- + * BL/WL bus --+------------+--------------------> + * | | | + * BL/WL v BL/WL v BL/WL v + * +----------+ +----------+ +----------+ + * | Memory | | Memory | | Memory | + * | Module | | Module | ... | Module | + * | [0] | | [1] | | [N] | + * +----------+ +----------+ +----------+ + * | | | + * v v v + * +----------+ +----------+ +----------+ + * | MUX | | MUX | | MUX | + * | Module | | Module | ... | Module | + * | [0] | | [1] | | [N] | + * +----------+ +----------+ +----------+ + * + *********************************************************************/ +void print_verilog_local_config_bus(std::fstream& fp, + const std::string& prefix, + const e_config_protocol_type& sram_orgz_type, + const size_t& instance_id, + const size_t& num_conf_bits) { + /* Make sure we have a valid file handler*/ + valid_file_stream(fp); + + switch(sram_orgz_type) { + case CONFIG_MEM_STANDALONE: + /* Not need for configuration bus + * The configuration ports of SRAM are directly wired to the ports of modules + */ + break; + case CONFIG_MEM_SCAN_CHAIN: + case CONFIG_MEM_MEMORY_BANK: { + /* Two configuration buses should be outputted + * One for the regular SRAM ports of a routing multiplexer + * The other for the inverted SRAM ports of a routing multiplexer + */ + BasicPort config_port(generate_local_sram_port_name(prefix, instance_id, CIRCUIT_MODEL_PORT_INPUT), + num_conf_bits); + fp << generate_verilog_port(VERILOG_PORT_WIRE, config_port) << ";" << std::endl; + BasicPort inverted_config_port(generate_local_sram_port_name(prefix, instance_id, CIRCUIT_MODEL_PORT_OUTPUT), + num_conf_bits); + fp << generate_verilog_port(VERILOG_PORT_WIRE, inverted_config_port) << ";" << std::endl; + break; + } + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid SRAM organization!\n"); + exit(1); + } +} + +/********************************************************************* + * Print a number of bus ports which are wired to the configuration + * ports of a ReRAM-based routing multiplexer + * This port is supposed to be used locally inside a Verilog/SPICE module + * + * Currently support: + * For memory-bank configuration style: + * ------------------------------------ + * Different than CMOS routing multiplexers, ReRAM multiplexers require + * reserved BL/WLs to be grouped in buses + * + * Module Port + * | + * v + * regular/reserved bus_port --+----------------+----> ... + * | | + * bl/wl/../sram_ports v v + * +-----------+ +-----------+ + * | Memory | | Memory | + * | Module[0] | | Module[1] | ... + * +-----------+ +-----------+ + * | | + * v v + * +-----------+ +-----------+ + * | Routing | | Routing | + * | MUX [0] | | MUX[1] | ... + * +-----------+ +-----------+ + * + *********************************************************************/ +static +void print_verilog_rram_mux_config_bus(std::fstream& fp, + const CircuitLibrary& circuit_lib, + const CircuitModelId& mux_model, + const e_config_protocol_type& sram_orgz_type, + const size_t& mux_size, + const size_t& mux_instance_id, + const size_t& num_reserved_conf_bits, + const size_t& num_conf_bits) { + /* Make sure we have a valid file handler*/ + valid_file_stream(fp); + + switch(sram_orgz_type) { + case CONFIG_MEM_STANDALONE: + /* Not need for configuration bus + * The configuration ports of SRAM are directly wired to the ports of modules + */ + break; + case CONFIG_MEM_SCAN_CHAIN: { + /* Not supported yet. + * Configuration chain may be only applied to ReRAM-based multiplexers with local decoders + */ + break; + } + case CONFIG_MEM_MEMORY_BANK: { + /* This is currently most used in ReRAM FPGAs */ + /* Print configuration bus to group reserved BL/WLs */ + BasicPort reserved_bl_bus(generate_reserved_sram_port_name(CIRCUIT_MODEL_PORT_BL), + num_reserved_conf_bits); + fp << generate_verilog_port(VERILOG_PORT_WIRE, reserved_bl_bus) << ";" << std::endl; + BasicPort reserved_wl_bus(generate_reserved_sram_port_name(CIRCUIT_MODEL_PORT_WL), + num_reserved_conf_bits); + fp << generate_verilog_port(VERILOG_PORT_WIRE, reserved_wl_bus) << ";" << std::endl; + + /* Print configuration bus to group BL/WLs */ + BasicPort bl_bus(generate_mux_config_bus_port_name(circuit_lib, mux_model, mux_size, 0, false), + num_conf_bits + num_reserved_conf_bits); + fp << generate_verilog_port(VERILOG_PORT_WIRE, bl_bus) << ";" << std::endl; + BasicPort wl_bus(generate_mux_config_bus_port_name(circuit_lib, mux_model, mux_size, 1, false), + num_conf_bits + num_reserved_conf_bits); + fp << generate_verilog_port(VERILOG_PORT_WIRE, wl_bus) << ";" << std::endl; + + /* Print bus to group SRAM outputs, this is to interface memory cells to routing multiplexers */ + BasicPort sram_output_bus(generate_mux_sram_port_name(circuit_lib, mux_model, mux_size, mux_instance_id, CIRCUIT_MODEL_PORT_INPUT), + num_conf_bits); + fp << generate_verilog_port(VERILOG_PORT_WIRE, sram_output_bus) << ";" << std::endl; + BasicPort inverted_sram_output_bus(generate_mux_sram_port_name(circuit_lib, mux_model, mux_size, mux_instance_id, CIRCUIT_MODEL_PORT_OUTPUT), + num_conf_bits); + fp << generate_verilog_port(VERILOG_PORT_WIRE, inverted_sram_output_bus) << ";" << std::endl; + + /* Get the SRAM model of the mux_model */ + std::vector sram_models = find_circuit_sram_models(circuit_lib, mux_model); + /* TODO: maybe later multiplexers may have mode select ports... This should be relaxed */ + VTR_ASSERT( 1 == sram_models.size() ); + + /* Wire the reserved configuration bits to part of bl/wl buses */ + BasicPort bl_bus_reserved_bits(bl_bus.get_name(), num_reserved_conf_bits); + print_verilog_wire_connection(fp, bl_bus_reserved_bits, reserved_bl_bus, false); + BasicPort wl_bus_reserved_bits(wl_bus.get_name(), num_reserved_conf_bits); + print_verilog_wire_connection(fp, wl_bus_reserved_bits, reserved_wl_bus, false); + + /* Connect SRAM BL/WLs to bus */ + BasicPort mux_bl_wire(generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_BL), + num_conf_bits); + BasicPort bl_bus_regular_bits(bl_bus.get_name(), num_reserved_conf_bits, num_reserved_conf_bits + num_conf_bits - 1); + print_verilog_wire_connection(fp, bl_bus_regular_bits, mux_bl_wire, false); + BasicPort mux_wl_wire(generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_WL), + num_conf_bits); + BasicPort wl_bus_regular_bits(wl_bus.get_name(), num_reserved_conf_bits, num_reserved_conf_bits + num_conf_bits - 1); + print_verilog_wire_connection(fp, wl_bus_regular_bits, mux_wl_wire, false); + + break; + } + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid SRAM organization!\n"); + exit(1); + } + +} + +/********************************************************************* + * Print a number of bus ports which are wired to the configuration + * ports of a memory module, which consists of a number of configuration + * memory cells, such as SRAMs. + * Note that the configuration bus will only interface the memory + * module, rather than the programming routing multiplexers, LUTs, IOs + * etc. This helps us to keep clean and simple Verilog generation + *********************************************************************/ +void print_verilog_mux_config_bus(std::fstream& fp, + const CircuitLibrary& circuit_lib, + const CircuitModelId& mux_model, + const e_config_protocol_type& sram_orgz_type, + const size_t& mux_size, + const size_t& mux_instance_id, + const size_t& num_reserved_conf_bits, + const size_t& num_conf_bits) { + /* Depend on the design technology of this MUX: + * bus connections are different + * SRAM MUX: bus is connected to the output ports of SRAM + * RRAM MUX: bus is connected to the BL/WL of MUX + * TODO: Maybe things will become even more complicated, + * the bus connections may depend on the type of configuration circuit... + * Currently, this is fine. + */ + switch (circuit_lib.design_tech_type(mux_model)) { + case CIRCUIT_MODEL_DESIGN_CMOS: { + std::string prefix = generate_mux_subckt_name(circuit_lib, mux_model, mux_size, std::string()); + print_verilog_local_config_bus(fp, prefix, sram_orgz_type, mux_instance_id, num_conf_bits); + break; + } + case CIRCUIT_MODEL_DESIGN_RRAM: + print_verilog_rram_mux_config_bus(fp, circuit_lib, mux_model, sram_orgz_type, mux_size, mux_instance_id, num_reserved_conf_bits, num_conf_bits); + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid design technology for routing multiplexer!\n"); + exit(1); + } +} + +/********************************************************************* + * Print a wire to connect MUX configuration ports + * This function connects the sram ports to the ports of a Verilog module + * used for formal verification + * + * Note: MSB and LSB of formal verification configuration bus MUST be updated + * before running this function !!!! + *********************************************************************/ +void print_verilog_formal_verification_mux_sram_ports_wiring(std::fstream& fp, + const CircuitLibrary& circuit_lib, + const CircuitModelId& mux_model, + const size_t& mux_size, + const size_t& mux_instance_id, + const size_t& num_conf_bits, + const BasicPort& fm_config_bus) { + BasicPort mux_sram_output(generate_mux_sram_port_name(circuit_lib, mux_model, mux_size, mux_instance_id, CIRCUIT_MODEL_PORT_INPUT), + num_conf_bits); + /* Get the SRAM model of the mux_model */ + std::vector sram_models = find_circuit_sram_models(circuit_lib, mux_model); + /* TODO: maybe later multiplexers may have mode select ports... This should be relaxed */ + VTR_ASSERT( 1 == sram_models.size() ); + BasicPort formal_verification_port; + formal_verification_port.set_name(generate_formal_verification_sram_port_name(circuit_lib, sram_models[0])); + VTR_ASSERT(num_conf_bits == fm_config_bus.get_width()); + formal_verification_port.set_lsb(fm_config_bus.get_lsb()); + formal_verification_port.set_msb(fm_config_bus.get_msb()); + print_verilog_wire_connection(fp, mux_sram_output, formal_verification_port, false); +} + +/******************************************************************** + * Print stimuli for a pulse generation + * + * |<--- pulse width --->| + * +------ flip_value + * | + * initial_value ----------------------+ + * + *******************************************************************/ +void print_verilog_pulse_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const float& pulse_width, + const size_t& flip_value) { + /* Validate the file stream */ + valid_file_stream(fp); + + /* Config_done signal: indicate when configuration is finished */ + fp << "initial" << std::endl; + fp << "\tbegin" << std::endl; + fp << "\t"; + std::vector initial_values(port.get_width(), initial_value); + fp << "\t"; + fp << generate_verilog_port_constant_values(port, initial_values); + fp << ";" << std::endl; + + /* if flip_value is the same as initial value, we do not need to flip the signal ! */ + if (flip_value != initial_value) { + fp << "\t" << "#" << std::setprecision(10) << pulse_width; + std::vector port_flip_values(port.get_width(), flip_value); + fp << "\t"; + fp << generate_verilog_port_constant_values(port, port_flip_values); + fp << ";" << std::endl; + } + + fp << "\tend" << std::endl; + + /* Print an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Print stimuli for a pulse generation + * This function supports multiple signal switching under different pulse width + * + * |<-- wait condition -->| + * |<--- pulse width --->| + * +------ flip_values + * | + * initial_value ------- ... --------------------------------+ + * + *******************************************************************/ +void print_verilog_pulse_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const std::vector& pulse_widths, + const std::vector& flip_values, + const std::string& wait_condition) { + /* Validate the file stream */ + valid_file_stream(fp); + + /* Config_done signal: indicate when configuration is finished */ + fp << "initial" << std::endl; + fp << "\tbegin" << std::endl; + fp << "\t"; + std::vector initial_values(port.get_width(), initial_value); + fp << "\t"; + fp << generate_verilog_port_constant_values(port, initial_values); + fp << ";" << std::endl; + + /* Set a wait condition if specified */ + if (false == wait_condition.empty()) { + fp << "\twait(" << wait_condition << ")" << std::endl; + } + + /* Number of flip conditions and values should match */ + VTR_ASSERT(flip_values.size() == pulse_widths.size()); + for (size_t ipulse = 0; ipulse < pulse_widths.size(); ++ipulse) { + fp << "\t" << "#" << std::setprecision(10) << pulse_widths[ipulse]; + std::vector port_flip_value(port.get_width(), flip_values[ipulse]); + fp << "\t"; + fp << generate_verilog_port_constant_values(port, port_flip_value); + fp << ";" << std::endl; + } + + fp << "\tend" << std::endl; + + /* Print an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Print stimuli for a clock signal + * This function can support if the clock signal should wait for a period + * of time and then start + * pulse width + * |<----->| + * +-------+ +-------+ + * | | | | + * initial_value --- ... ---+ +-------+ +------ ... + * |<--wait_condition-->| + * + *******************************************************************/ +void print_verilog_clock_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const float& pulse_width, + const std::string& wait_condition) { + /* Validate the file stream */ + valid_file_stream(fp); + + /* Config_done signal: indicate when configuration is finished */ + fp << "initial" << std::endl; + fp << "\tbegin" << std::endl; + + std::vector initial_values(port.get_width(), initial_value); + fp << "\t\t"; + fp << generate_verilog_port_constant_values(port, initial_values); + fp << ";" << std::endl; + + fp << "\tend" << std::endl; + fp << "always"; + + /* Set a wait condition if specified */ + if (true == wait_condition.empty()) { + fp << std::endl; + } else { + fp << " wait(" << wait_condition << ")" << std::endl; + } + + fp << "\tbegin" << std::endl; + fp << "\t\t" << "#" << std::setprecision(10) << pulse_width; + + fp << "\t"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, port); + fp << " = "; + fp << "~"; + fp << generate_verilog_port(VERILOG_PORT_CONKT, port); + fp << ";" << std::endl; + + fp << "\tend" << std::endl; + + /* Print an empty line as splitter */ + fp << std::endl; +} + +/******************************************************************** + * Output a header file that includes a number of Verilog netlists + * so that it can be easily included in a top-level netlist + ********************************************************************/ +void print_verilog_netlist_include_header_file(const std::vector& netlists_to_be_included, + const char* subckt_dir, + const char* header_file_name) { + std::string verilog_fname(std::string(subckt_dir) + std::string(header_file_name)); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + valid_file_stream(fp); + + /* Generate the descriptions*/ + print_verilog_file_header(fp, "Header file to include other Verilog netlists"); + + /* Output file names */ + for (const std::string& netlist_name : netlists_to_be_included) { + fp << "`include \"" << netlist_name << "\"" << std::endl; + } + + /* close file stream */ + fp.close(); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_writer_utils.h b/openfpga/src/fpga_verilog/verilog_writer_utils.h new file mode 100644 index 000000000..b145d8be6 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_writer_utils.h @@ -0,0 +1,187 @@ +/************************************************ + * Header file for verilog_writer_utils.cpp + * Include function declaration for most frequently + * used Verilog writers + ***********************************************/ +#ifndef VERILOG_WRITER_UTILS_H +#define VERILOG_WRITER_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include +#include "openfpga_port.h" +#include "verilog_port_types.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +/* Tips: for naming your function in this header/source file + * If a function outputs to a file, its name should begin with "print_verilog" + * If a function creates a string without outputting to a file, its name should begin with "generate_verilog" + * Please show respect to this naming convention, in order to keep a clean header/source file + * as well maintain a easy way to identify the functions + */ + +void print_verilog_file_header(std::fstream& fp, + const std::string& usage); + +void print_verilog_include_netlist(std::fstream& fp, + const std::string& netlist_name); + +void print_verilog_define_flag(std::fstream& fp, + const std::string& flag_name, + const int& flag_value); + +void print_verilog_include_defines_preproc_file(std::fstream& fp, + const std::string& verilog_dir); + +void print_verilog_comment(std::fstream& fp, + const std::string& comment); + +void print_verilog_preprocessing_flag(std::fstream& fp, + const std::string& preproc_flag); + +void print_verilog_endif(std::fstream& fp); + +void print_verilog_module_definition(std::fstream& fp, + const ModuleManager& module_manager, const ModuleId& module_id); + +void print_verilog_module_ports(std::fstream& fp, + const ModuleManager& module_manager, const ModuleId& module_id); + +void print_verilog_module_declaration(std::fstream& fp, + const ModuleManager& module_manager, const ModuleId& module_id); + +void print_verilog_module_instance(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const std::string& instance_name, + const std::map& port2port_name_map, + const bool& use_explicit_port_map); + +void print_verilog_module_instance(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module_id, const ModuleId& child_module_id, + const std::map& port2port_name_map, + const bool& use_explicit_port_map); + +void print_verilog_module_end(std::fstream& fp, + const std::string& module_name); + +std::string generate_verilog_port(const enum e_dump_verilog_port_type& dump_port_type, + const BasicPort& port_info); + +bool two_verilog_ports_mergeable(const BasicPort& portA, + const BasicPort& portB); + +BasicPort merge_two_verilog_ports(const BasicPort& portA, + const BasicPort& portB); + +std::vector combine_verilog_ports(const std::vector& ports); + +std::string generate_verilog_ports(const std::vector& merged_ports); + +BasicPort generate_verilog_bus_port(const std::vector& input_ports, + const std::string& bus_port_name); + +std::string generate_verilog_local_wire(const BasicPort& output_port, + const std::vector& input_ports); + +std::string generate_verilog_constant_values(const std::vector& const_values); + +std::string generate_verilog_port_constant_values(const BasicPort& output_port, + const std::vector& const_values); + +void print_verilog_wire_constant_values(std::fstream& fp, + const BasicPort& output_port, + const std::vector& const_values); + +void print_verilog_deposit_wire_constant_values(std::fstream& fp, + const BasicPort& output_port, + const std::vector& const_values); + +void print_verilog_force_wire_constant_values(std::fstream& fp, + const BasicPort& output_port, + const std::vector& const_values); + +void print_verilog_wire_connection(std::fstream& fp, + const BasicPort& output_port, + const BasicPort& input_port, + const bool& inverted); + +void print_verilog_register_connection(std::fstream& fp, + const BasicPort& output_port, + const BasicPort& input_port, + const bool& inverted); + +void print_verilog_buffer_instance(std::fstream& fp, + ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const ModuleId& parent_module_id, + const CircuitModelId& buffer_model, + const BasicPort& instance_input_port, + const BasicPort& instance_output_port); + +void print_verilog_local_sram_wires(std::fstream& fp, + const CircuitLibrary& circuit_lib, + const CircuitModelId& sram_model, + const e_config_protocol_type sram_orgz_type, + const size_t& port_size); + +void print_verilog_local_config_bus(std::fstream& fp, + const std::string& prefix, + const e_config_protocol_type& sram_orgz_type, + const size_t& instance_id, + const size_t& num_conf_bits); + +void print_verilog_mux_config_bus(std::fstream& fp, + const CircuitLibrary& circuit_lib, + const CircuitModelId& mux_model, + const e_config_protocol_type& sram_orgz_type, + const size_t& mux_size, + const size_t& mux_instance_id, + const size_t& num_reserved_conf_bits, + const size_t& num_conf_bits); + +void print_verilog_formal_verification_mux_sram_ports_wiring(std::fstream& fp, + const CircuitLibrary& circuit_lib, + const CircuitModelId& mux_model, + const size_t& mux_size, + const size_t& mux_instance_id, + const size_t& num_conf_bits, + const BasicPort& fm_config_bus); + +void print_verilog_pulse_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const float& pulse_width, + const size_t& flip_value); + +void print_verilog_pulse_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const std::vector& pulse_widths, + const std::vector& flip_values, + const std::string& wait_condition); + +void print_verilog_clock_stimuli(std::fstream& fp, + const BasicPort& port, + const size_t& initial_value, + const float& pulse_width, + const std::string& wait_condition); + +void print_verilog_netlist_include_header_file(const std::vector& netlists_to_be_included, + const char* subckt_dir, + const char* header_file_name); + +} /* end namespace openfpga */ + +#endif From 4cb61e2138b74ac840f4f934c64e5d0d5393cc11 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 00:03:24 -0700 Subject: [PATCH 6/6] bring preprocessing flag Verilog netlists online --- openfpga/src/fpga_verilog/verilog_api.cpp | 9 +- .../verilog_auxiliary_netlists.cpp | 200 ++++++++++++++++++ .../fpga_verilog/verilog_auxiliary_netlists.h | 31 +++ openfpga/src/fpga_verilog/verilog_constants.h | 8 +- openfpga/src/fpga_verilog/verilog_options.cpp | 28 +++ openfpga/src/fpga_verilog/verilog_options.h | 11 + 6 files changed, 281 insertions(+), 6 deletions(-) create mode 100644 openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index d8dd8dab8..71ec0708d 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -14,6 +14,7 @@ #include "device_rr_gsb.h" #include "verilog_constants.h" +#include "verilog_auxiliary_netlists.h" //#include "verilog_submodules.h" //#include "verilog_routing.h" //#include "verilog_submodules.h" @@ -67,11 +68,11 @@ void fpga_fabric_verilog(const ModuleManager& module_manager, create_dir_path(rr_dir_path.c_str()); /* Print Verilog files containing preprocessing flags */ - //print_verilog_preprocessing_flags_netlist(std::string(src_dir_path), - // vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts); + print_verilog_preprocessing_flags_netlist(std::string(src_dir_path), + options); - //print_verilog_simulation_preprocessing_flags(std::string(src_dir_path), - // vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts); + print_verilog_simulation_preprocessing_flags(std::string(src_dir_path), + options); /* Generate primitive Verilog modules, which are corner stones of FPGA fabric * Note that this function MUST be called before Verilog generation of diff --git a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp new file mode 100644 index 000000000..f1a12798d --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp @@ -0,0 +1,200 @@ +/******************************************************************** + * This file includes functions that are used to generate Verilog files + * or code blocks, with a focus on + * `include user-defined or auto-generated netlists in Verilog format + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "openfpga_naming.h" +#include "circuit_library_utils.h" +#include "verilog_constants.h" +#include "verilog_writer_utils.h" +#include "verilog_auxiliary_netlists.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Local constant variables + *******************************************************************/ +constexpr char* TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v"; + +/******************************************************************** + * Print a file that includes all the netlists that have been generated + * and user-defined. + * Some netlists are open to compile under specific preprocessing flags + *******************************************************************/ +void print_include_netlists(const std::string& src_dir, + const std::string& circuit_name, + const std::string& reference_benchmark_file, + const CircuitLibrary& circuit_lib) { + std::string verilog_fname = src_dir + circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_stream(verilog_fname.c_str(), fp); + + /* Print the title */ + print_verilog_file_header(fp, std::string("Netlist Summary")); + + /* Print preprocessing flags */ + print_verilog_comment(fp, std::string("------ Include defines: preproc flags -----")); + print_verilog_include_netlist(fp, std::string(src_dir + std::string(DEFINES_VERILOG_FILE_NAME))); + fp << std::endl; + + print_verilog_comment(fp, std::string("------ Include simulation defines -----")); + print_verilog_include_netlist(fp, src_dir + std::string(DEFINES_VERILOG_SIMULATION_FILE_NAME)); + fp << std::endl; + + /* Include all the user-defined netlists */ + for (const std::string& user_defined_netlist : find_circuit_library_unique_verilog_netlists(circuit_lib)) { + print_verilog_include_netlist(fp, user_defined_netlist); + } + + /* Include all the primitive modules */ + print_verilog_include_netlist(fp, src_dir + std::string(DEFAULT_SUBMODULE_DIR_NAME) + std::string(SUBMODULE_VERILOG_FILE_NAME)); + fp << std::endl; + + /* Include all the CLB, heterogeneous block modules */ + print_verilog_include_netlist(fp, src_dir + std::string(DEFAULT_LB_DIR_NAME) + std::string(LOGIC_BLOCK_VERILOG_FILE_NAME)); + fp << std::endl; + + /* Include all the routing architecture modules */ + print_verilog_include_netlist(fp, src_dir + std::string(DEFAULT_RR_DIR_NAME) + std::string(ROUTING_VERILOG_FILE_NAME)); + fp << std::endl; + + /* Include FPGA top module */ + print_verilog_include_netlist(fp, src_dir + generate_fpga_top_netlist_name(std::string(VERILOG_NETLIST_FILE_POSTFIX))); + fp << std::endl; + + /* Include reference benchmark netlist only when auto-check flag is enabled */ + print_verilog_preprocessing_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG)); + fp << "\t"; + print_verilog_include_netlist(fp, std::string(reference_benchmark_file)); + print_verilog_endif(fp); + fp << std::endl; + + /* Include formal verification netlists only when formal verification flag is enable */ + print_verilog_preprocessing_flag(fp, std::string(VERILOG_FORMAL_VERIFICATION_PREPROC_FLAG)); + fp << "\t"; + print_verilog_include_netlist(fp, src_dir + circuit_name + std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX)); + + /* Include formal verification testbench only when formal simulation flag is enabled */ + fp << "\t"; + print_verilog_preprocessing_flag(fp, std::string(FORMAL_SIMULATION_FLAG)); + fp << "\t\t"; + print_verilog_include_netlist(fp, src_dir + circuit_name + std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX)); + fp << "\t"; + print_verilog_endif(fp); + + print_verilog_endif(fp); + fp << std::endl; + + /* Include top-level testbench only when auto-check flag is enabled */ + print_verilog_preprocessing_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG)); + fp << "\t"; + print_verilog_include_netlist(fp, src_dir + circuit_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX)); + print_verilog_endif(fp); + fp << std::endl; + + /* Close the file stream */ + fp.close(); +} + +/******************************************************************** + * Print a Verilog file containing preprocessing flags + * which are used enable/disable some features in FPGA Verilog modules + *******************************************************************/ +void print_verilog_preprocessing_flags_netlist(const std::string& src_dir, + const FabricVerilogOption& fpga_verilog_opts) { + + std::string verilog_fname = src_dir + std::string(DEFINES_VERILOG_FILE_NAME); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_stream(verilog_fname.c_str(), fp); + + /* Print the title */ + print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable features in FPGA Verilog modules")); + + /* To enable timing */ + if (true == fpga_verilog_opts.include_timing()) { + print_verilog_define_flag(fp, std::string(VERILOG_TIMING_PREPROC_FLAG), 1); + fp << std::endl; + } + + /* To enable timing */ + if (true == fpga_verilog_opts.include_signal_init()) { + print_verilog_define_flag(fp, std::string(VERILOG_SIGNAL_INIT_PREPROC_FLAG), 1); + fp << std::endl; + } + + /* To enable formal verfication flag */ + if (true == fpga_verilog_opts.print_formal_verification_top_netlist()) { + print_verilog_define_flag(fp, std::string(VERILOG_FORMAL_VERIFICATION_PREPROC_FLAG), 1); + fp << std::endl; + } + + /* To enable functional verfication with Icarus */ + if (true == fpga_verilog_opts.support_icarus_simulator()) { + print_verilog_define_flag(fp, std::string(ICARUS_SIMULATOR_FLAG), 1); + fp << std::endl; + } + + /* Close the file stream */ + fp.close(); +} + +/******************************************************************** + * Print a Verilog file containing simulation-related preprocessing flags + *******************************************************************/ +void print_verilog_simulation_preprocessing_flags(const std::string& src_dir, + const FabricVerilogOption& fpga_verilog_opts) { + + std::string verilog_fname = src_dir + std::string(DEFINES_VERILOG_SIMULATION_FILE_NAME); + + /* Create the file stream */ + std::fstream fp; + fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_stream(verilog_fname.c_str(), fp); + + /* Print the title */ + print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable simulation features")); + + /* To enable manualy checked simulation */ + if (true == fpga_verilog_opts.print_top_testbench()) { + print_verilog_define_flag(fp, std::string(INITIAL_SIMULATION_FLAG), 1); + fp << std::endl; + } + + /* To enable auto-checked simulation */ + if (true == fpga_verilog_opts.print_autocheck_top_testbench()) { + print_verilog_define_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG), 1); + fp << std::endl; + } + + /* To enable pre-configured FPGA simulation */ + if (true == fpga_verilog_opts.print_formal_verification_top_netlist()) { + print_verilog_define_flag(fp, std::string(FORMAL_SIMULATION_FLAG), 1); + fp << std::endl; + } + + /* Close the file stream */ + fp.close(); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h new file mode 100644 index 000000000..42f3b0042 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h @@ -0,0 +1,31 @@ +#ifndef VERILOG_AUXILIARY_NETLISTS_H +#define VERILOG_AUXILIARY_NETLISTS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "circuit_library.h" +#include "verilog_options.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_include_netlists(const std::string& src_dir, + const std::string& circuit_name, + const std::string& reference_benchmark_file, + const CircuitLibrary& circuit_lib); + +void print_verilog_preprocessing_flags_netlist(const std::string& src_dir, + const FabricVerilogOption& fpga_verilog_opts); + +void print_verilog_simulation_preprocessing_flags(const std::string& src_dir, + const FabricVerilogOption& fpga_verilog_opts); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_verilog/verilog_constants.h b/openfpga/src/fpga_verilog/verilog_constants.h index 10a6c0669..9562bd730 100644 --- a/openfpga/src/fpga_verilog/verilog_constants.h +++ b/openfpga/src/fpga_verilog/verilog_constants.h @@ -8,7 +8,7 @@ constexpr float VERILOG_SIM_TIMESCALE = 1e-9; // Verilog Simulation time scale ( constexpr char* VERILOG_TIMING_PREPROC_FLAG = "ENABLE_TIMING"; // the flag to enable timing definition during compilation constexpr char* VERILOG_SIGNAL_INIT_PREPROC_FLAG = "ENABLE_SIGNAL_INITIALIZATION"; // the flag to enable signal initialization during compilation -constexpr char* VERILOG_FORMAL_VERIFICATGION_PREPROC_FLAG = "ENABLE_FORMAL_VERIFICATION"; // the flag to enable formal verification during compilation +constexpr char* VERILOG_FORMAL_VERIFICATION_PREPROC_FLAG = "ENABLE_FORMAL_VERIFICATION"; // the flag to enable formal verification during compilation constexpr char* INITIAL_SIMULATION_FLAG = "INITIAL_SIMULATION"; // the flag to enable initial functional verification constexpr char* AUTOCHECKED_SIMULATION_FLAG = "AUTOCHECKED_SIMULATION"; // the flag to enable autochecked functional verification constexpr char* FORMAL_SIMULATION_FLAG = "FORMAL_SIMULATION"; // the flag to enable formal functional verification @@ -19,10 +19,14 @@ constexpr char* DEFAULT_SUBMODULE_DIR_NAME = "sub_module/"; constexpr char* MODELSIM_SIMULATION_TIME_UNIT = "ms"; // Icarus variables and flag -constexpr char* ICARUS_SIMULATION_FLAG = "ICARUS_SIMULATOR"; // the flag to enable specific Verilog code in testbenches +constexpr char* ICARUS_SIMULATOR_FLAG = "ICARUS_SIMULATOR"; // the flag to enable specific Verilog code in testbenches // End of Icarus variables and flag constexpr char* VERILOG_TOP_POSTFIX = "_top.v"; +constexpr char* FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX = "_top_formal_verification.v"; +constexpr char* TOP_TESTBENCH_VERILOG_FILE_POSTFIX = "_top_tb.v"; /* !!! must be consist with the modelsim_testbench_module_postfix */ +constexpr char* AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX = "_autocheck_top_tb.v"; /* !!! must be consist with the modelsim_autocheck_testbench_module_postfix */ +constexpr char* RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX = "_formal_random_top_tb.v"; constexpr char* DEFINES_VERILOG_FILE_NAME = "fpga_defines.v"; constexpr char* DEFINES_VERILOG_SIMULATION_FILE_NAME = "define_simulation.v"; constexpr char* SUBMODULE_VERILOG_FILE_NAME = "sub_module.v"; diff --git a/openfpga/src/fpga_verilog/verilog_options.cpp b/openfpga/src/fpga_verilog/verilog_options.cpp index a0dfd7e2a..2539d5e51 100644 --- a/openfpga/src/fpga_verilog/verilog_options.cpp +++ b/openfpga/src/fpga_verilog/verilog_options.cpp @@ -35,6 +35,22 @@ bool FabricVerilogOption::compress_routing() const { return compress_routing_; } +bool FabricVerilogOption::print_top_testbench() const { + return print_top_testbench_; +} + +bool FabricVerilogOption::print_formal_verification_top_netlist() const { + return print_formal_verification_top_netlist_; +} + +bool FabricVerilogOption::print_autocheck_top_testbench() const { + return false == reference_verilog_file_path_.empty(); +} + +std::string FabricVerilogOption::reference_verilog_file_path() const { + return reference_verilog_file_path_; +} + bool FabricVerilogOption::verbose_output() const { return verbose_output_; } @@ -66,6 +82,18 @@ void FabricVerilogOption::set_compress_routing(const bool& enabled) { compress_routing_ = enabled; } +void FabricVerilogOption::set_print_top_testbench(const bool& enabled) { + print_top_testbench_ = enabled; +} + +void FabricVerilogOption::set_print_formal_verification_top_netlist(const bool& enabled) { + print_formal_verification_top_netlist_ = enabled; +} + +void FabricVerilogOption::set_print_autocheck_top_testbench(const std::string& reference_verilog_file_path) { + reference_verilog_file_path_ = reference_verilog_file_path; +} + void FabricVerilogOption::set_verbose_output(const bool& enabled) { verbose_output_ = enabled; } diff --git a/openfpga/src/fpga_verilog/verilog_options.h b/openfpga/src/fpga_verilog/verilog_options.h index 336ec542a..182cabcf2 100644 --- a/openfpga/src/fpga_verilog/verilog_options.h +++ b/openfpga/src/fpga_verilog/verilog_options.h @@ -24,6 +24,10 @@ class FabricVerilogOption { bool include_signal_init() const; bool explicit_port_mapping() const; bool compress_routing() const; + bool print_top_testbench() const; + bool print_formal_verification_top_netlist() const; + bool print_autocheck_top_testbench() const; + std::string reference_verilog_file_path() const; bool verbose_output() const; public: /* Public mutators */ void set_output_directory(const std::string& output_dir); @@ -32,6 +36,9 @@ class FabricVerilogOption { void set_include_signal_init(const bool& enabled); void set_explicit_port_mapping(const bool& enabled); void set_compress_routing(const bool& enabled); + void set_print_top_testbench(const bool& enabled); + void set_print_formal_verification_top_netlist(const bool& enabled); + void set_print_autocheck_top_testbench(const std::string& reference_verilog_file_path); void set_verbose_output(const bool& enabled); private: /* Internal Data */ std::string output_directory_; @@ -40,6 +47,10 @@ class FabricVerilogOption { bool include_timing_; bool explicit_port_mapping_; bool compress_routing_; + bool print_top_testbench_; + bool print_formal_verification_top_netlist_; + /* print_autocheck_top_testbench will be enabled when reference file path is not empty */ + std::string reference_verilog_file_path_; bool verbose_output_; };