Merge branch 'refactoring' into dev

This commit is contained in:
tangxifan 2020-02-27 18:03:21 -07:00
commit fc509aa2c1
40 changed files with 3322 additions and 140 deletions

View File

@ -22,6 +22,7 @@ target_link_libraries(libopenfpga
libarchopenfpga libarchopenfpga
libopenfpgashell libopenfpgashell
libopenfpgautil libopenfpgautil
libini
libvtrutil libvtrutil
libvpr8) libvpr8)

View File

@ -0,0 +1,46 @@
/******************************************************************************
* Memember functions for data structure IoLocationMap
******************************************************************************/
#include "vtr_assert.h"
#include "io_location_map.h"
/* begin namespace openfpga */
namespace openfpga {
/**************************************************
* Public Accessors
*************************************************/
size_t IoLocationMap::io_index(const size_t& x, const size_t& y, const size_t& z) const {
if (x >= io_indices_.size()) {
return size_t(-1);
}
if (y >= io_indices_[x].size()) {
return size_t(-1);
}
if (z >= io_indices_[x][y].size()) {
return size_t(-1);
}
return io_indices_[x][y][z];
}
void IoLocationMap::set_io_index(const size_t& x, const size_t& y, const size_t& z, const size_t& io_index) {
if (x >= io_indices_.size()) {
io_indices_.resize(x + 1);
}
if (y >= io_indices_[x].size()) {
io_indices_[x].resize(y + 1);
}
if (z >= io_indices_[x][y].size()) {
io_indices_[x][y].resize(z + 1);
}
io_indices_[x][y][z] = io_index;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,39 @@
#ifndef IO_LOCATION_MAP_H
#define IO_LOCATION_MAP_H
/********************************************************************
* Include header files required by the data structure definition
*******************************************************************/
#include <vector>
/* Begin namespace openfpga */
namespace openfpga {
/********************************************************************
* I/O location map is a data structure to bridge the index of I/Os
* in the FPGA fabric, i.e., the module graph, and logical location
* of the I/O in VPR coordinate system
*
* For example
* io[0] io[1] io[2]
* +-----------------+ +--------+
* | | | | |
* | I/O | I/O | | I/O |
* | [0][y] | [0][y] | | [1][y] |
* | [0] | [1] | | [0] |
* +-----------------+ +--------+
*
*******************************************************************/
class IoLocationMap {
public: /* Public aggregators */
size_t io_index(const size_t& x, const size_t& y, const size_t& z) const;
public: /* Public mutators */
void set_io_index(const size_t& x, const size_t& y, const size_t& z, const size_t& io_index);
private: /* Internal Data */
/* I/O index fast lookup by [x][y][z] location */
std::vector<std::vector<std::vector<size_t>>> io_indices_;
};
} /* End namespace openfpga*/
#endif

View File

@ -21,43 +21,43 @@ namespace openfpga {
* This function should only be called after the GSB builder is done * This function should only be called after the GSB builder is done
*******************************************************************/ *******************************************************************/
static static
void compress_routing_hierarchy(OpenfpgaContext& openfpga_context, void compress_routing_hierarchy(OpenfpgaContext& openfpga_ctx,
const bool& verbose_output) { const bool& verbose_output) {
vtr::ScopedStartFinishTimer timer("Identify unique General Switch Blocks (GSBs)"); vtr::ScopedStartFinishTimer timer("Identify unique General Switch Blocks (GSBs)");
/* Build unique module lists */ /* Build unique module lists */
openfpga_context.mutable_device_rr_gsb().build_unique_module(g_vpr_ctx.device().rr_graph); openfpga_ctx.mutable_device_rr_gsb().build_unique_module(g_vpr_ctx.device().rr_graph);
/* Report the stats */ /* Report the stats */
VTR_LOGV(verbose_output, VTR_LOGV(verbose_output,
"Detected %lu unique X-direction connection blocks from a total of %d (compression rate=%d%)\n", "Detected %lu unique X-direction connection blocks from a total of %d (compression rate=%d%)\n",
openfpga_context.device_rr_gsb().get_num_cb_unique_module(CHANX), openfpga_ctx.device_rr_gsb().get_num_cb_unique_module(CHANX),
find_device_rr_gsb_num_cb_modules(openfpga_context.device_rr_gsb(), CHANX), find_device_rr_gsb_num_cb_modules(openfpga_ctx.device_rr_gsb(), CHANX),
100 * (openfpga_context.device_rr_gsb().get_num_cb_unique_module(CHANX) / find_device_rr_gsb_num_cb_modules(openfpga_context.device_rr_gsb(), CHANX) - 1)); 100 * (openfpga_ctx.device_rr_gsb().get_num_cb_unique_module(CHANX) / find_device_rr_gsb_num_cb_modules(openfpga_ctx.device_rr_gsb(), CHANX) - 1));
VTR_LOGV(verbose_output, VTR_LOGV(verbose_output,
"Detected %lu unique Y-direction connection blocks from a total of %d (compression rate=%d%)\n", "Detected %lu unique Y-direction connection blocks from a total of %d (compression rate=%d%)\n",
openfpga_context.device_rr_gsb().get_num_cb_unique_module(CHANY), openfpga_ctx.device_rr_gsb().get_num_cb_unique_module(CHANY),
find_device_rr_gsb_num_cb_modules(openfpga_context.device_rr_gsb(), CHANY), find_device_rr_gsb_num_cb_modules(openfpga_ctx.device_rr_gsb(), CHANY),
100 * (openfpga_context.device_rr_gsb().get_num_cb_unique_module(CHANY) / find_device_rr_gsb_num_cb_modules(openfpga_context.device_rr_gsb(), CHANY) - 1)); 100 * (openfpga_ctx.device_rr_gsb().get_num_cb_unique_module(CHANY) / find_device_rr_gsb_num_cb_modules(openfpga_ctx.device_rr_gsb(), CHANY) - 1));
VTR_LOGV(verbose_output, VTR_LOGV(verbose_output,
"Detected %lu unique switch blocks from a total of %d (compression rate=%d%)\n", "Detected %lu unique switch blocks from a total of %d (compression rate=%d%)\n",
openfpga_context.device_rr_gsb().get_num_sb_unique_module(), openfpga_ctx.device_rr_gsb().get_num_sb_unique_module(),
find_device_rr_gsb_num_sb_modules(openfpga_context.device_rr_gsb()), find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb()),
100 * (openfpga_context.device_rr_gsb().get_num_sb_unique_module() / find_device_rr_gsb_num_sb_modules(openfpga_context.device_rr_gsb()) - 1)); 100 * (openfpga_ctx.device_rr_gsb().get_num_sb_unique_module() / find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb()) - 1));
VTR_LOGV(verbose_output, VTR_LOGV(verbose_output,
"Detected %lu unique general switch blocks from a total of %d (compression rate=%d%)\n", "Detected %lu unique general switch blocks from a total of %d (compression rate=%d%)\n",
openfpga_context.device_rr_gsb().get_num_gsb_unique_module(), openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module(),
find_device_rr_gsb_num_gsb_modules(openfpga_context.device_rr_gsb()), find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb()),
100 * (openfpga_context.device_rr_gsb().get_num_gsb_unique_module() / find_device_rr_gsb_num_gsb_modules(openfpga_context.device_rr_gsb()) - 1)); 100 * (openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module() / find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb()) - 1));
} }
/******************************************************************** /********************************************************************
* Build the module graph for FPGA device * Build the module graph for FPGA device
*******************************************************************/ *******************************************************************/
void build_fabric(OpenfpgaContext& openfpga_context, void build_fabric(OpenfpgaContext& openfpga_ctx,
const Command& cmd, const CommandContext& cmd_context) { const Command& cmd, const CommandContext& cmd_context) {
CommandOptionId opt_compress_routing = cmd.option("compress_routing"); CommandOptionId opt_compress_routing = cmd.option("compress_routing");
@ -65,16 +65,17 @@ void build_fabric(OpenfpgaContext& openfpga_context,
CommandOptionId opt_verbose = cmd.option("verbose"); CommandOptionId opt_verbose = cmd.option("verbose");
if (true == cmd_context.option_enable(cmd, opt_compress_routing)) { if (true == cmd_context.option_enable(cmd, opt_compress_routing)) {
compress_routing_hierarchy(openfpga_context, cmd_context.option_enable(cmd, opt_verbose)); compress_routing_hierarchy(openfpga_ctx, cmd_context.option_enable(cmd, opt_verbose));
} }
VTR_LOG("\n"); VTR_LOG("\n");
openfpga_context.mutable_module_graph() = build_device_module_graph(g_vpr_ctx.device(), openfpga_ctx.mutable_module_graph() = build_device_module_graph(openfpga_ctx.mutable_io_location_map(),
const_cast<const OpenfpgaContext&>(openfpga_context), g_vpr_ctx.device(),
cmd_context.option_enable(cmd, opt_compress_routing), const_cast<const OpenfpgaContext&>(openfpga_ctx),
cmd_context.option_enable(cmd, opt_duplicate_grid_pin), cmd_context.option_enable(cmd, opt_compress_routing),
cmd_context.option_enable(cmd, opt_verbose)); cmd_context.option_enable(cmd, opt_duplicate_grid_pin),
cmd_context.option_enable(cmd, opt_verbose));
} }
} /* end namespace openfpga */ } /* end namespace openfpga */

View File

@ -15,7 +15,7 @@
/* begin namespace openfpga */ /* begin namespace openfpga */
namespace openfpga { namespace openfpga {
void build_fabric(OpenfpgaContext& openfpga_context, void build_fabric(OpenfpgaContext& openfpga_ctx,
const Command& cmd, const CommandContext& cmd_context); const Command& cmd, const CommandContext& cmd_context);
} /* end namespace openfpga */ } /* end namespace openfpga */

View File

@ -15,6 +15,7 @@
#include "openfpga_flow_manager.h" #include "openfpga_flow_manager.h"
#include "bitstream_manager.h" #include "bitstream_manager.h"
#include "device_rr_gsb.h" #include "device_rr_gsb.h"
#include "io_location_map.h"
/******************************************************************** /********************************************************************
* This file includes the declaration of the date structure * This file includes the declaration of the date structure
@ -58,6 +59,7 @@ class OpenfpgaContext : public Context {
const openfpga::FlowManager& flow_manager() const { return flow_manager_; } const openfpga::FlowManager& flow_manager() const { return flow_manager_; }
const openfpga::BitstreamManager& bitstream_manager() const { return bitstream_manager_; } const openfpga::BitstreamManager& bitstream_manager() const { return bitstream_manager_; }
const std::vector<openfpga::ConfigBitId>& fabric_bitstream() const { return fabric_bitstream_; } const std::vector<openfpga::ConfigBitId>& fabric_bitstream() const { return fabric_bitstream_; }
const openfpga::IoLocationMap& io_location_map() { return io_location_map_; }
public: /* Public mutators */ public: /* Public mutators */
openfpga::Arch& mutable_arch() { return arch_; } openfpga::Arch& mutable_arch() { return arch_; }
openfpga::VprDeviceAnnotation& mutable_vpr_device_annotation() { return vpr_device_annotation_; } openfpga::VprDeviceAnnotation& mutable_vpr_device_annotation() { return vpr_device_annotation_; }
@ -72,6 +74,7 @@ class OpenfpgaContext : public Context {
openfpga::FlowManager& mutable_flow_manager() { return flow_manager_; } openfpga::FlowManager& mutable_flow_manager() { return flow_manager_; }
openfpga::BitstreamManager& mutable_bitstream_manager() { return bitstream_manager_; } openfpga::BitstreamManager& mutable_bitstream_manager() { return bitstream_manager_; }
std::vector<openfpga::ConfigBitId>& mutable_fabric_bitstream() { return fabric_bitstream_; } std::vector<openfpga::ConfigBitId>& mutable_fabric_bitstream() { return fabric_bitstream_; }
openfpga::IoLocationMap& mutable_io_location_map() { return io_location_map_; }
private: /* Internal data */ private: /* Internal data */
/* Data structure to store information from read_openfpga_arch library */ /* Data structure to store information from read_openfpga_arch library */
openfpga::Arch arch_; openfpga::Arch arch_;
@ -102,6 +105,7 @@ class OpenfpgaContext : public Context {
/* Fabric module graph */ /* Fabric module graph */
openfpga::ModuleManager module_graph_; openfpga::ModuleManager module_graph_;
openfpga::IoLocationMap io_location_map_;
/* Bitstream database */ /* Bitstream database */
openfpga::BitstreamManager bitstream_manager_; openfpga::BitstreamManager bitstream_manager_;

View File

@ -15,7 +15,7 @@
namespace openfpga { namespace openfpga {
/******************************************************************** /********************************************************************
* A wrapper function to call the fabric_verilog function of FPGA-Verilog * A wrapper function to call the fabric Verilog generator of FPGA-Verilog
*******************************************************************/ *******************************************************************/
void write_fabric_verilog(OpenfpgaContext& openfpga_ctx, void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
const Command& cmd, const CommandContext& cmd_context) { const Command& cmd, const CommandContext& cmd_context) {
@ -26,9 +26,6 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
CommandOptionId opt_include_signal_init = cmd.option("include_signal_init"); CommandOptionId opt_include_signal_init = cmd.option("include_signal_init");
CommandOptionId opt_support_icarus_simulator = cmd.option("support_icarus_simulator"); CommandOptionId opt_support_icarus_simulator = cmd.option("support_icarus_simulator");
CommandOptionId opt_print_user_defined_template = cmd.option("print_user_defined_template"); CommandOptionId opt_print_user_defined_template = cmd.option("print_user_defined_template");
CommandOptionId opt_print_top_testbench = cmd.option("print_top_testbench");
CommandOptionId opt_print_formal_verification_top_netlist = cmd.option("print_formal_verification_top_netlist");
CommandOptionId opt_print_autocheck_top_testbench = cmd.option("print_autocheck_top_testbench");
CommandOptionId opt_verbose = cmd.option("verbose"); CommandOptionId opt_verbose = cmd.option("verbose");
/* This is an intermediate data structure which is designed to modularize the FPGA-Verilog /* This is an intermediate data structure which is designed to modularize the FPGA-Verilog
@ -41,9 +38,6 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
options.set_include_signal_init(cmd_context.option_enable(cmd, opt_include_signal_init)); 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_support_icarus_simulator(cmd_context.option_enable(cmd, opt_support_icarus_simulator));
options.set_print_user_defined_template(cmd_context.option_enable(cmd, opt_print_user_defined_template)); options.set_print_user_defined_template(cmd_context.option_enable(cmd, opt_print_user_defined_template));
options.set_print_top_testbench(cmd_context.option_enable(cmd, opt_print_top_testbench));
options.set_print_formal_verification_top_netlist(cmd_context.option_enable(cmd, opt_print_formal_verification_top_netlist));
options.set_print_autocheck_top_testbench(cmd_context.option_value(cmd, opt_print_autocheck_top_testbench));
options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose));
options.set_compress_routing(openfpga_ctx.flow_manager().compress_routing()); options.set_compress_routing(openfpga_ctx.flow_manager().compress_routing());
@ -56,4 +50,43 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
options); options);
} }
/********************************************************************
* A wrapper function to call the Verilog testbench generator of FPGA-Verilog
*******************************************************************/
void write_verilog_testbench(OpenfpgaContext& openfpga_ctx,
const Command& cmd, const CommandContext& cmd_context) {
CommandOptionId opt_output_dir = cmd.option("file");
CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path");
CommandOptionId opt_print_top_testbench = cmd.option("print_top_testbench");
CommandOptionId opt_print_formal_verification_top_netlist = cmd.option("print_formal_verification_top_netlist");
CommandOptionId opt_print_preconfig_top_testbench = cmd.option("print_preconfig_top_testbench");
CommandOptionId opt_print_simulation_ini = cmd.option("print_simulation_ini");
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
*/
VerilogTestbenchOption options;
options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir));
options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark));
options.set_print_formal_verification_top_netlist(cmd_context.option_enable(cmd, opt_print_formal_verification_top_netlist));
options.set_print_preconfig_top_testbench(cmd_context.option_enable(cmd, opt_print_preconfig_top_testbench));
options.set_print_top_testbench(cmd_context.option_enable(cmd, opt_print_top_testbench));
options.set_print_simulation_ini(cmd_context.option_value(cmd, opt_print_simulation_ini));
options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose));
fpga_verilog_testbench(openfpga_ctx.module_graph(),
openfpga_ctx.bitstream_manager(),
openfpga_ctx.fabric_bitstream(),
g_vpr_ctx.atom(),
g_vpr_ctx.placement(),
openfpga_ctx.io_location_map(),
openfpga_ctx.vpr_netlist_annotation(),
openfpga_ctx.arch().circuit_lib,
openfpga_ctx.arch().sim_setting,
openfpga_ctx.arch().config_protocol.type(),
options);
}
} /* end namespace openfpga */ } /* end namespace openfpga */

View File

@ -18,6 +18,9 @@ namespace openfpga {
void write_fabric_verilog(OpenfpgaContext& openfpga_ctx, void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
const Command& cmd, const CommandContext& cmd_context); const Command& cmd, const CommandContext& cmd_context);
void write_verilog_testbench(OpenfpgaContext& openfpga_ctx,
const Command& cmd, const CommandContext& cmd_context);
} /* end namespace openfpga */ } /* end namespace openfpga */
#endif #endif

View File

@ -11,6 +11,98 @@
/* begin namespace openfpga */ /* begin namespace openfpga */
namespace openfpga { namespace openfpga {
/********************************************************************
* - Add a command to Shell environment: generate fabric Verilog
* - Add associated options
* - Add command dependency
*******************************************************************/
static
void add_openfpga_write_fabric_verilog_command(openfpga::Shell<OpenfpgaContext>& shell,
const ShellCommandClassId& cmd_class_id,
const ShellCommandId& shell_cmd_build_fabric_id) {
Command shell_cmd("write_fabric_verilog");
/* Add an option '--file' in short '-f'*/
CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for Verilog netlists");
shell_cmd.set_option_short_name(output_opt, "f");
shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING);
/* Add an option '--explicit_port_mapping' */
shell_cmd.add_option("explicit_port_mapping", false, "Use explicit port mapping in Verilog netlists");
/* Add an option '--include_timing' */
shell_cmd.add_option("include_timing", false, "Enable timing annotation in Verilog netlists");
/* Add an option '--include_signal_init' */
shell_cmd.add_option("include_signal_init", false, "Initialize all the signals in Verilog netlists");
/* Add an option '--support_icarus_simulator' */
shell_cmd.add_option("support_icarus_simulator", false, "Fine-tune Verilog netlists to support icarus simulator");
/* Add an option '--print_user_defined_template' */
shell_cmd.add_option("print_user_defined_template", false, "Generate a template Verilog files for user-defined circuit models");
/* Add an option '--verbose' */
shell_cmd.add_option("verbose", false, "Enable verbose output");
/* Add command 'write_fabric_verilog' to the Shell */
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate Verilog netlists modeling full FPGA fabric");
shell.set_command_class(shell_cmd_id, cmd_class_id);
shell.set_command_execute_function(shell_cmd_id, write_fabric_verilog);
/* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */
std::vector<ShellCommandId> cmd_dependency;
cmd_dependency.push_back(shell_cmd_build_fabric_id);
shell.set_command_dependency(shell_cmd_id, cmd_dependency);
}
/********************************************************************
* - Add a command to Shell environment: write Verilog testbench
* - Add associated options
* - Add command dependency
*******************************************************************/
static
void add_openfpga_write_verilog_testbench_command(openfpga::Shell<OpenfpgaContext>& shell,
const ShellCommandClassId& cmd_class_id,
const ShellCommandId& shell_cmd_build_fabric_id) {
Command shell_cmd("write_verilog_testbench");
/* Add an option '--file' in short '-f'*/
CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for Verilog netlists");
shell_cmd.set_option_short_name(output_opt, "f");
shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING);
/* Add an option '--reference_benchmark_file_path'*/
CommandOptionId ref_bm_opt = shell_cmd.add_option("reference_benchmark_file_path", true, "Specify the file path to the reference Verilog netlist");
shell_cmd.set_option_require_value(ref_bm_opt, openfpga::OPT_STRING);
/* Add an option '--print_top_testbench' */
shell_cmd.add_option("print_top_testbench", false, "Generate a full testbench for top-level fabric module with autocheck capability");
/* Add an option '--print_formal_verification_top_netlist' */
shell_cmd.add_option("print_formal_verification_top_netlist", false, "Generate a top-level module which can be used in formal verification");
/* Add an option '--print_preconfig_top_testbench' */
shell_cmd.add_option("print_preconfig_top_testbench", false, "Generate a pre-configured testbench for top-level fabric module with autocheck capability");
/* Add an option '--print_simulation_ini' */
CommandOptionId sim_ini_opt = shell_cmd.add_option("print_simulation_ini", false, "Generate a .ini file as an exchangeable file to enable HDL simulations");
shell_cmd.set_option_require_value(sim_ini_opt, openfpga::OPT_STRING);
/* Add an option '--verbose' */
shell_cmd.add_option("verbose", false, "Enable verbose output");
/* Add command to the Shell */
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate Verilog testbenches for full FPGA fabric");
shell.set_command_class(shell_cmd_id, cmd_class_id);
shell.set_command_execute_function(shell_cmd_id, write_verilog_testbench);
/* The command should NOT be executed before 'build_fabric' */
std::vector<ShellCommandId> cmd_dependency;
cmd_dependency.push_back(shell_cmd_build_fabric_id);
shell.set_command_dependency(shell_cmd_id, cmd_dependency);
}
void add_openfpga_verilog_commands(openfpga::Shell<OpenfpgaContext>& shell) { void add_openfpga_verilog_commands(openfpga::Shell<OpenfpgaContext>& shell) {
/* Get the unique id of 'build_fabric' command which is to be used in creating the dependency graph */ /* 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")); const ShellCommandId& shell_cmd_build_fabric_id = shell.command(std::string("build_fabric"));
@ -19,42 +111,18 @@ void add_openfpga_verilog_commands(openfpga::Shell<OpenfpgaContext>& shell) {
ShellCommandClassId openfpga_verilog_cmd_class = shell.add_command_class("FPGA-Verilog"); ShellCommandClassId openfpga_verilog_cmd_class = shell.add_command_class("FPGA-Verilog");
/******************************** /********************************
* Command 'wirte_fabric_verilog' * Command 'write_fabric_verilog'
*/ */
Command shell_cmd_write_fabric_verilog("write_fabric_verilog"); add_openfpga_write_fabric_verilog_command(shell,
/* Add an option '--file' in short '-f'*/ openfpga_verilog_cmd_class,
CommandOptionId fabric_verilog_output_opt = shell_cmd_write_fabric_verilog.add_option("file", true, "Specify the output directory for Verilog netlists"); shell_cmd_build_fabric_id);
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 '--print_user_defined_template' */
shell_cmd_write_fabric_verilog.add_option("print_user_defined_template", false, "Generate a template Verilog files for user-defined circuit models");
/* Add an option '--print_top_testbench' */
shell_cmd_write_fabric_verilog.add_option("print_top_testbench", false, "Generate a testbench for top-level fabric module");
/* Add an option '--print_formal_verification_top_netlist' */
shell_cmd_write_fabric_verilog.add_option("print_formal_verification_top_netlist", false, "Generate a top-level module which can be used in formal verification");
/* Add an option '--print_autocheck_top_testbench' */
CommandOptionId fabric_verilog_autocheck_tb_opt = shell_cmd_write_fabric_verilog.add_option("print_autocheck_top_testbench", false, "Generate a testbench for top-level fabric module with autocheck capability");
shell_cmd_write_fabric_verilog.set_option_require_value(fabric_verilog_autocheck_tb_opt, openfpga::OPT_STRING);
/* 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"); * Command 'write_verilog_testbench'
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); add_openfpga_write_verilog_testbench_command(shell,
openfpga_verilog_cmd_class,
/* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */ shell_cmd_build_fabric_id);
std::vector<ShellCommandId> 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 */ } /* end namespace openfpga */

View File

@ -26,7 +26,8 @@ namespace openfpga {
* The main function to be called for building module graphs * The main function to be called for building module graphs
* for a FPGA fabric * for a FPGA fabric
*******************************************************************/ *******************************************************************/
ModuleManager build_device_module_graph(const DeviceContext& vpr_device_ctx, ModuleManager build_device_module_graph(IoLocationMap& io_location_map,
const DeviceContext& vpr_device_ctx,
const OpenfpgaContext& openfpga_ctx, const OpenfpgaContext& openfpga_ctx,
const bool& compress_routing, const bool& compress_routing,
const bool& duplicate_grid_pin, const bool& duplicate_grid_pin,
@ -97,7 +98,8 @@ ModuleManager build_device_module_graph(const DeviceContext& vpr_device_ctx,
} }
/* Build FPGA fabric top-level module */ /* Build FPGA fabric top-level module */
build_top_module(module_manager, openfpga_ctx.arch().circuit_lib, build_top_module(module_manager, io_location_map,
openfpga_ctx.arch().circuit_lib,
vpr_device_ctx.grid, vpr_device_ctx.grid,
vpr_device_ctx.rr_graph, vpr_device_ctx.rr_graph,
openfpga_ctx.device_rr_gsb(), openfpga_ctx.device_rr_gsb(),

View File

@ -14,7 +14,8 @@
/* begin namespace openfpga */ /* begin namespace openfpga */
namespace openfpga { namespace openfpga {
ModuleManager build_device_module_graph(const DeviceContext& vpr_device_ctx, ModuleManager build_device_module_graph(IoLocationMap& io_location_map,
const DeviceContext& vpr_device_ctx,
const OpenfpgaContext& openfpga_ctx, const OpenfpgaContext& openfpga_ctx,
const bool& compress_routing, const bool& compress_routing,
const bool& duplicate_grid_pin, const bool& duplicate_grid_pin,

View File

@ -89,6 +89,7 @@ size_t add_top_module_grid_instance(ModuleManager& module_manager,
static static
vtr::Matrix<size_t> add_top_module_grid_instances(ModuleManager& module_manager, vtr::Matrix<size_t> add_top_module_grid_instances(ModuleManager& module_manager,
const ModuleId& top_module, const ModuleId& top_module,
IoLocationMap& io_location_map,
const DeviceGrid& grids) { const DeviceGrid& grids) {
/* Reserve an array for the instance ids */ /* Reserve an array for the instance ids */
vtr::Matrix<size_t> grid_instance_ids({grids.width(), grids.height()}); vtr::Matrix<size_t> grid_instance_ids({grids.width(), grids.height()});
@ -142,6 +143,7 @@ vtr::Matrix<size_t> add_top_module_grid_instances(ModuleManager& module_manager,
} }
/* Add instances of I/O grids to top_module */ /* Add instances of I/O grids to top_module */
size_t io_counter = 0;
for (const e_side& io_side : io_sides) { for (const e_side& io_side : io_sides) {
for (const vtr::Point<size_t>& io_coordinate : io_coordinates[io_side]) { for (const vtr::Point<size_t>& io_coordinate : io_coordinates[io_side]) {
/* Bypass EMPTY grid */ /* Bypass EMPTY grid */
@ -157,6 +159,21 @@ vtr::Matrix<size_t> add_top_module_grid_instances(ModuleManager& module_manager,
VTR_ASSERT(true == is_io_type(grids[io_coordinate.x()][io_coordinate.y()].type)); VTR_ASSERT(true == is_io_type(grids[io_coordinate.x()][io_coordinate.y()].type));
/* Add a grid module to top_module*/ /* Add a grid module to top_module*/
grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = add_top_module_grid_instance(module_manager, top_module, grids[io_coordinate.x()][io_coordinate.y()].type, io_side, io_coordinate); grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = add_top_module_grid_instance(module_manager, top_module, grids[io_coordinate.x()][io_coordinate.y()].type, io_side, io_coordinate);
/* MUST DO: register in io location mapping!
* I/O location mapping is a critical look-up for testbench generators
* As we add the I/O grid instances to top module by following order:
* TOP -> RIGHT -> BOTTOM -> LEFT
* The I/O index will increase in this way as well.
* This organization I/O indices is also consistent to the way
* that GPIOs are wired in function connect_gpio_module()
*
* Note: if you change the GPIO function, you should update here as well!
*/
for (int z = 0; z < grids[io_coordinate.x()][io_coordinate.y()].type->capacity; ++z) {
io_location_map.set_io_index(io_coordinate.x(), io_coordinate.y(), z, io_counter);
}
io_counter++;
} }
} }
@ -280,6 +297,7 @@ vtr::Matrix<size_t> add_top_module_connection_block_instances(ModuleManager& mod
* 5. Add module nets/submodules to connect configuration ports * 5. Add module nets/submodules to connect configuration ports
*******************************************************************/ *******************************************************************/
void build_top_module(ModuleManager& module_manager, void build_top_module(ModuleManager& module_manager,
IoLocationMap& io_location_map,
const CircuitLibrary& circuit_lib, const CircuitLibrary& circuit_lib,
const DeviceGrid& grids, const DeviceGrid& grids,
const RRGraph& rr_graph, const RRGraph& rr_graph,
@ -301,7 +319,7 @@ void build_top_module(ModuleManager& module_manager,
/* Add sub modules, which are grid, SB and CBX/CBY modules as instances */ /* Add sub modules, which are grid, SB and CBX/CBY modules as instances */
/* Add all the grids across the fabric */ /* Add all the grids across the fabric */
vtr::Matrix<size_t> grid_instance_ids = add_top_module_grid_instances(module_manager, top_module, grids); vtr::Matrix<size_t> grid_instance_ids = add_top_module_grid_instances(module_manager, top_module, io_location_map, grids);
/* Add all the SBs across the fabric */ /* Add all the SBs across the fabric */
vtr::Matrix<size_t> sb_instance_ids = add_top_module_switch_block_instances(module_manager, top_module, device_rr_gsb, compact_routing_hierarchy); vtr::Matrix<size_t> sb_instance_ids = add_top_module_switch_block_instances(module_manager, top_module, device_rr_gsb, compact_routing_hierarchy);
/* Add all the CBX and CBYs across the fabric */ /* Add all the CBX and CBYs across the fabric */

View File

@ -14,6 +14,7 @@
#include "tile_direct.h" #include "tile_direct.h"
#include "arch_direct.h" #include "arch_direct.h"
#include "module_manager.h" #include "module_manager.h"
#include "io_location_map.h"
/******************************************************************** /********************************************************************
* Function declaration * Function declaration
@ -23,6 +24,7 @@
namespace openfpga { namespace openfpga {
void build_top_module(ModuleManager& module_manager, void build_top_module(ModuleManager& module_manager,
IoLocationMap& io_location_map,
const CircuitLibrary& circuit_lib, const CircuitLibrary& circuit_lib,
const DeviceGrid& grids, const DeviceGrid& grids,
const RRGraph& rr_graph, const RRGraph& rr_graph,

View File

@ -3,7 +3,7 @@
******************************************************************************/ ******************************************************************************/
#include "vtr_assert.h" #include "vtr_assert.h"
#include "verilog_options.h" #include "fabric_verilog_options.h"
/* begin namespace openfpga */ /* begin namespace openfpga */
namespace openfpga { namespace openfpga {
@ -52,22 +52,6 @@ bool FabricVerilogOption::compress_routing() const {
return compress_routing_; 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::print_user_defined_template() const { bool FabricVerilogOption::print_user_defined_template() const {
return print_user_defined_template_; return print_user_defined_template_;
} }
@ -103,18 +87,6 @@ void FabricVerilogOption::set_compress_routing(const bool& enabled) {
compress_routing_ = 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_print_user_defined_template(const bool& enabled) { void FabricVerilogOption::set_print_user_defined_template(const bool& enabled) {
print_user_defined_template_ = enabled; print_user_defined_template_ = enabled;
} }

View File

@ -1,5 +1,5 @@
#ifndef VERILOG_OPTIONS_H #ifndef FABRIC_VERILOG_OPTIONS_H
#define VERILOG_OPTIONS_H #define FABRIC_VERILOG_OPTIONS_H
/******************************************************************** /********************************************************************
* Include header files required by the data structure definition * Include header files required by the data structure definition
@ -10,11 +10,7 @@
namespace openfpga { namespace openfpga {
/******************************************************************** /********************************************************************
* FlowManager aims to resolve the dependency between OpenFPGA functional * Options for Fabric Verilog generator
* code blocks
* It can provide flags for downstream modules about if the data structures
* they require have already been constructed
*
*******************************************************************/ *******************************************************************/
class FabricVerilogOption { class FabricVerilogOption {
public: /* Public constructor */ public: /* Public constructor */

View File

@ -0,0 +1,67 @@
/*********************************************************************
* This function includes the writer for generating exchangeable
* information, in order to interface different simulators
********************************************************************/
#include <cmath>
#include <ctime>
#include <map>
#define MINI_CASE_SENSITIVE
#include "ini.h"
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_time.h"
#include "simulation_utils.h"
#include "verilog_constants.h"
#include "simulation_info_writer.h"
/* begin namespace openfpga */
namespace openfpga {
/*********************************************************************
* Top-level function to write an ini file which contains exchangeable
* information, in order to interface different Verilog simulators
********************************************************************/
void print_verilog_simulation_info(const std::string& ini_fname,
const std::string& circuit_name,
const std::string& src_dir,
const size_t& num_program_clock_cycles,
const int& num_operating_clock_cycles,
const float& prog_clock_freq,
const float& op_clock_freq) {
std::string timer_message = std::string("Write exchangeable file containing simulation information '") + ini_fname + std::string("'");
/* Start time count */
vtr::ScopedStartFinishTimer timer(timer_message);
/* Use default name if user does not provide one */
VTR_ASSERT(true != ini_fname.empty());
mINI::INIStructure ini;
// std::map<char, int> units_map;
// units_map['s']=1; // units_map['ms']=1E-3; // units_map['us']=1E-6;
// units_map['ns']=1E-9; // units_map['ps']=1E-12; // units_map['fs']=1E-15;
/* Compute simulation time period */
float simulation_time_period = find_simulation_time_period(1E-3,
num_program_clock_cycles,
1. / prog_clock_freq,
num_operating_clock_cycles,
1. / op_clock_freq);
ini["SIMULATION_DECK"]["PROJECTNAME "] = "ModelSimProject";
ini["SIMULATION_DECK"]["BENCHMARK "] = circuit_name;
ini["SIMULATION_DECK"]["TOP_TB"] = circuit_name + std::string(FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX);
ini["SIMULATION_DECK"]["SIMTIME "] = std::to_string(simulation_time_period);
ini["SIMULATION_DECK"]["UNIT "] = "ms";
ini["SIMULATION_DECK"]["VERILOG_PATH "] = std::string(src_dir);
ini["SIMULATION_DECK"]["VERILOG_FILE1"] = std::string(DEFINES_VERILOG_FILE_NAME);
ini["SIMULATION_DECK"]["VERILOG_FILE2"] = std::string(circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX));
mINI::INIFile file(ini_fname);
file.generate(ini, true);
}
} /* end namespace openfpga */

View File

@ -0,0 +1,26 @@
#ifndef SIMULATION_INFO_WRITER_H
#define SIMULATION_INFO_WRITER_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void print_verilog_simulation_info(const std::string& ini_fname,
const std::string& circuit_name,
const std::string& src_dir,
const size_t& num_program_clock_cycles,
const int& num_operating_clock_cycles,
const float& prog_clock_freq,
const float& op_clock_freq);
} /* end namespace openfpga */
#endif

View File

@ -20,6 +20,11 @@
#include "verilog_grid.h" #include "verilog_grid.h"
#include "verilog_top_module.h" #include "verilog_top_module.h"
#include "verilog_preconfig_top_module.h"
#include "verilog_formal_random_top_testbench.h"
#include "verilog_top_testbench.h"
#include "simulation_info_writer.h"
/* Header file for this source file */ /* Header file for this source file */
#include "verilog_api.h" #include "verilog_api.h"
@ -27,17 +32,18 @@
namespace openfpga { namespace openfpga {
/******************************************************************** /********************************************************************
* Top-level function of FPGA-Verilog * A top-level function of FPGA-Verilog which focuses on fabric Verilog generation
* This function will generate * This function will generate
* 1. primitive modules required by the full fabric * - primitive modules required by the full fabric
* which are LUTs, routing multiplexer, logic gates, transmission-gates etc. * - which are LUTs, routing multiplexer, logic gates, transmission-gates etc.
* 2. Routing modules, which are Switch Blocks (SBs) and Connection Blocks (CBs) * - Routing modules, which are Switch Blocks (SBs) and Connection Blocks (CBs)
* 3. Logic block modules, which are Configuration Logic Blocks (CLBs) * - Logic block modules, which are Configuration Logic Blocks (CLBs)
* 4. FPGA module, which are the full FPGA fabric with configuration protocol * - 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 * Note:
* 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. * - Please do NOT include ANY testbench generation in this function!!!
* 8. Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated * It is about the fabric itself, independent from any implementation
* All the testbench generation should be in the function fpga_testbench_verilog()
* *
* TODO: We should use module manager as a constant here. * TODO: We should use module manager as a constant here.
* All the modification should be done before this writer! * All the modification should be done before this writer!
@ -75,9 +81,6 @@ void fpga_fabric_verilog(ModuleManager& module_manager,
print_verilog_preprocessing_flags_netlist(std::string(src_dir_path), print_verilog_preprocessing_flags_netlist(std::string(src_dir_path),
options); options);
print_verilog_simulation_preprocessing_flags(std::string(src_dir_path),
options);
/* Generate primitive Verilog modules, which are corner stones of FPGA fabric /* Generate primitive Verilog modules, which are corner stones of FPGA fabric
* Note that this function MUST be called before Verilog generation of * Note that this function MUST be called before Verilog generation of
* core logic (i.e., logic blocks and routing resources) !!! * core logic (i.e., logic blocks and routing resources) !!!
@ -121,4 +124,106 @@ void fpga_fabric_verilog(ModuleManager& module_manager,
module_manager.num_modules()); module_manager.num_modules());
} }
/********************************************************************
* A top-level function of FPGA-Verilog which focuses on fabric Verilog generation
* This function will generate
* - A wrapper module, which encapsulate the FPGA module in a Verilog module which have the same port as the input benchmark
* - Testbench, where a FPGA module is configured with a bitstream and then driven by input vectors
* - 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.
* - Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated
********************************************************************/
void fpga_verilog_testbench(const ModuleManager& module_manager,
const BitstreamManager& bitstream_manager,
const std::vector<ConfigBitId>& fabric_bitstream,
const AtomContext& atom_ctx,
const PlacementContext& place_ctx,
const IoLocationMap& io_location_map,
const VprNetlistAnnotation& netlist_annotation,
const CircuitLibrary& circuit_lib,
const SimulationSetting& simulation_setting,
const e_config_protocol_type& config_protocol_type,
const VerilogTestbenchOption& options) {
vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n");
std::string src_dir_path = format_dir_path(options.output_directory());
std::string netlist_name = atom_ctx.nlist.netlist_name();
/* Create directories */
create_dir_path(src_dir_path.c_str());
/* TODO: check if this works here. This function was in fabric generator */
print_verilog_simulation_preprocessing_flags(std::string(src_dir_path),
options);
/* Collect global ports from the circuit library:
* TODO: should we place this in the OpenFPGA context?
*/
std::vector<CircuitPortId> global_ports = find_circuit_library_global_ports(circuit_lib);
/* Generate wrapper module for FPGA fabric (mapped by the input benchmark and pre-configured testbench for verification */
if (true == options.print_formal_verification_top_netlist()) {
std::string formal_verification_top_netlist_file_path = src_dir_path + netlist_name
+ std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX);
print_verilog_preconfig_top_module(module_manager, bitstream_manager,
circuit_lib, global_ports,
atom_ctx, place_ctx, io_location_map,
netlist_annotation,
netlist_name,
formal_verification_top_netlist_file_path,
src_dir_path);
}
if (true == options.print_preconfig_top_testbench()) {
/* Generate top-level testbench using random vectors */
std::string random_top_testbench_file_path = src_dir_path + netlist_name
+ std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX);
print_verilog_random_top_testbench(netlist_name,
random_top_testbench_file_path,
src_dir_path,
atom_ctx,
netlist_annotation,
simulation_setting);
}
/* Generate full testbench for verification, including configuration phase and operating phase */
if (true == options.print_top_testbench()) {
std::string top_testbench_file_path = src_dir_path + netlist_name
+ std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX);
print_verilog_top_testbench(module_manager,
bitstream_manager, fabric_bitstream,
config_protocol_type,
circuit_lib, global_ports,
atom_ctx, place_ctx, io_location_map,
netlist_annotation,
netlist_name,
top_testbench_file_path,
src_dir_path,
simulation_setting);
}
/* Generate exchangeable files which contains simulation settings */
if (true == options.print_simulation_ini()) {
std::string simulation_ini_file_name = options.simulation_ini_path();
VTR_ASSERT(true != options.simulation_ini_path().empty());
print_verilog_simulation_info(simulation_ini_file_name,
netlist_name,
src_dir_path,
bitstream_manager.bits().size(),
simulation_setting.num_clock_cycles(),
simulation_setting.programming_clock_frequency(),
simulation_setting.operating_clock_frequency());
}
/* Generate a Verilog file including all the netlists that have been generated */
print_include_netlists(src_dir_path,
netlist_name,
options.reference_benchmark_file_path(),
circuit_lib);
}
} /* end namespace openfpga */ } /* end namespace openfpga */

View File

@ -7,14 +7,18 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "vpr_types.h"
#include "mux_library.h" #include "mux_library.h"
#include "circuit_library.h" #include "circuit_library.h"
#include "vpr_context.h" #include "vpr_context.h"
#include "vpr_device_annotation.h" #include "vpr_device_annotation.h"
#include "device_rr_gsb.h" #include "device_rr_gsb.h"
#include "module_manager.h" #include "module_manager.h"
#include "verilog_options.h" #include "bitstream_manager.h"
#include "simulation_setting.h"
#include "io_location_map.h"
#include "vpr_netlist_annotation.h"
#include "fabric_verilog_options.h"
#include "verilog_testbench_options.h"
/******************************************************************** /********************************************************************
* Function declaration * Function declaration
@ -31,6 +35,19 @@ void fpga_fabric_verilog(ModuleManager& module_manager,
const DeviceRRGSB& device_rr_gsb, const DeviceRRGSB& device_rr_gsb,
const FabricVerilogOption& options); const FabricVerilogOption& options);
void fpga_verilog_testbench(const ModuleManager& module_manager,
const BitstreamManager& bitstream_manager,
const std::vector<ConfigBitId>& fabric_bitstream,
const AtomContext& atom_ctx,
const PlacementContext& place_ctx,
const IoLocationMap& io_location_map,
const VprNetlistAnnotation& netlist_annotation,
const CircuitLibrary& circuit_lib,
const SimulationSetting& simulation_parameters,
const e_config_protocol_type& config_protocol_type,
const VerilogTestbenchOption& options);
} /* end namespace openfpga */ } /* end namespace openfpga */
#endif #endif

View File

@ -23,7 +23,6 @@ namespace openfpga {
/******************************************************************** /********************************************************************
* Local constant variables * 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 * Print a file that includes all the netlists that have been generated
@ -115,7 +114,7 @@ void print_include_netlists(const std::string& src_dir,
* which are used enable/disable some features in FPGA Verilog modules * which are used enable/disable some features in FPGA Verilog modules
*******************************************************************/ *******************************************************************/
void print_verilog_preprocessing_flags_netlist(const std::string& src_dir, void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
const FabricVerilogOption& fpga_verilog_opts) { const FabricVerilogOption& fabric_verilog_opts) {
std::string verilog_fname = src_dir + std::string(DEFINES_VERILOG_FILE_NAME); std::string verilog_fname = src_dir + std::string(DEFINES_VERILOG_FILE_NAME);
@ -130,25 +129,19 @@ void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable features in FPGA Verilog modules")); print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable features in FPGA Verilog modules"));
/* To enable timing */ /* To enable timing */
if (true == fpga_verilog_opts.include_timing()) { if (true == fabric_verilog_opts.include_timing()) {
print_verilog_define_flag(fp, std::string(VERILOG_TIMING_PREPROC_FLAG), 1); print_verilog_define_flag(fp, std::string(VERILOG_TIMING_PREPROC_FLAG), 1);
fp << std::endl; fp << std::endl;
} }
/* To enable timing */ /* To enable timing */
if (true == fpga_verilog_opts.include_signal_init()) { if (true == fabric_verilog_opts.include_signal_init()) {
print_verilog_define_flag(fp, std::string(VERILOG_SIGNAL_INIT_PREPROC_FLAG), 1); print_verilog_define_flag(fp, std::string(VERILOG_SIGNAL_INIT_PREPROC_FLAG), 1);
fp << std::endl; 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 */ /* To enable functional verfication with Icarus */
if (true == fpga_verilog_opts.support_icarus_simulator()) { if (true == fabric_verilog_opts.support_icarus_simulator()) {
print_verilog_define_flag(fp, std::string(ICARUS_SIMULATOR_FLAG), 1); print_verilog_define_flag(fp, std::string(ICARUS_SIMULATOR_FLAG), 1);
fp << std::endl; fp << std::endl;
} }
@ -161,7 +154,7 @@ void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
* Print a Verilog file containing simulation-related preprocessing flags * Print a Verilog file containing simulation-related preprocessing flags
*******************************************************************/ *******************************************************************/
void print_verilog_simulation_preprocessing_flags(const std::string& src_dir, void print_verilog_simulation_preprocessing_flags(const std::string& src_dir,
const FabricVerilogOption& fpga_verilog_opts) { const VerilogTestbenchOption& verilog_testbench_opts) {
std::string verilog_fname = src_dir + std::string(DEFINES_VERILOG_SIMULATION_FILE_NAME); std::string verilog_fname = src_dir + std::string(DEFINES_VERILOG_SIMULATION_FILE_NAME);
@ -176,19 +169,20 @@ void print_verilog_simulation_preprocessing_flags(const std::string& src_dir,
print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable simulation features")); print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable simulation features"));
/* To enable manualy checked simulation */ /* To enable manualy checked simulation */
if (true == fpga_verilog_opts.print_top_testbench()) { if (true == verilog_testbench_opts.print_top_testbench()) {
print_verilog_define_flag(fp, std::string(INITIAL_SIMULATION_FLAG), 1); print_verilog_define_flag(fp, std::string(INITIAL_SIMULATION_FLAG), 1);
fp << std::endl; fp << std::endl;
} }
/* To enable auto-checked simulation */ /* To enable auto-checked simulation */
if (true == fpga_verilog_opts.print_autocheck_top_testbench()) { if ( (true == verilog_testbench_opts.print_preconfig_top_testbench())
|| (true == verilog_testbench_opts.print_top_testbench()) ) {
print_verilog_define_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG), 1); print_verilog_define_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG), 1);
fp << std::endl; fp << std::endl;
} }
/* To enable pre-configured FPGA simulation */ /* To enable pre-configured FPGA simulation */
if (true == fpga_verilog_opts.print_formal_verification_top_netlist()) { if (true == verilog_testbench_opts.print_formal_verification_top_netlist()) {
print_verilog_define_flag(fp, std::string(FORMAL_SIMULATION_FLAG), 1); print_verilog_define_flag(fp, std::string(FORMAL_SIMULATION_FLAG), 1);
fp << std::endl; fp << std::endl;
} }

View File

@ -6,7 +6,8 @@
*******************************************************************/ *******************************************************************/
#include <string> #include <string>
#include "circuit_library.h" #include "circuit_library.h"
#include "verilog_options.h" #include "fabric_verilog_options.h"
#include "verilog_testbench_options.h"
/******************************************************************** /********************************************************************
* Function declaration * Function declaration
@ -21,10 +22,10 @@ void print_include_netlists(const std::string& src_dir,
const CircuitLibrary& circuit_lib); const CircuitLibrary& circuit_lib);
void print_verilog_preprocessing_flags_netlist(const std::string& src_dir, void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
const FabricVerilogOption& fpga_verilog_opts); const FabricVerilogOption& fabric_verilog_opts);
void print_verilog_simulation_preprocessing_flags(const std::string& src_dir, void print_verilog_simulation_preprocessing_flags(const std::string& src_dir,
const FabricVerilogOption& fpga_verilog_opts); const VerilogTestbenchOption& verilog_testbench_opts);
} /* end namespace openfpga */ } /* end namespace openfpga */

View File

@ -22,6 +22,7 @@ constexpr char* MODELSIM_SIMULATION_TIME_UNIT = "ms";
constexpr char* ICARUS_SIMULATOR_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 // End of Icarus variables and flag
constexpr char* TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v";
constexpr char* VERILOG_TOP_POSTFIX = "_top.v"; constexpr char* VERILOG_TOP_POSTFIX = "_top.v";
constexpr char* FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX = "_top_formal_verification.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* TOP_TESTBENCH_VERILOG_FILE_POSTFIX = "_top_tb.v"; /* !!! must be consist with the modelsim_testbench_module_postfix */
@ -48,4 +49,12 @@ constexpr char* SB_VERILOG_FILE_NAME_PREFIX = "sb_";
constexpr char* LOGICAL_MODULE_VERILOG_FILE_NAME_PREFIX = "logical_tile_"; constexpr char* LOGICAL_MODULE_VERILOG_FILE_NAME_PREFIX = "logical_tile_";
constexpr char* GRID_VERILOG_FILE_NAME_PREFIX = "grid_"; constexpr char* GRID_VERILOG_FILE_NAME_PREFIX = "grid_";
constexpr char* FORMAL_VERIFICATION_TOP_MODULE_POSTFIX = "_top_formal_verification";
constexpr char* FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX = "_fm";
constexpr char* FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME = "U0_formal_verification";
constexpr char* FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX = "_top_formal_verification_random_tb";
#define VERILOG_DEFAULT_SIGNAL_INIT_VALUE 0
#endif #endif

View File

@ -0,0 +1,267 @@
/********************************************************************
* This file includes functions that are used to generate a Verilog
* testbench for the top-level module (FPGA fabric), in purpose of
* running formal verification with random input vectors
*******************************************************************/
#include <fstream>
#include <cstring>
#include <cmath>
#include <iomanip>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
#include "vtr_time.h"
/* Headers from openfpgautil library */
#include "openfpga_port.h"
#include "openfpga_digest.h"
#include "openfpga_atom_netlist_utils.h"
#include "simulation_utils.h"
#include "verilog_constants.h"
#include "verilog_writer_utils.h"
#include "verilog_testbench_utils.h"
#include "verilog_formal_random_top_testbench.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Local variables used only in this file
*******************************************************************/
constexpr char* FPGA_PORT_POSTFIX = "_gfpga";
constexpr char* BENCHMARK_PORT_POSTFIX = "_bench";
constexpr char* CHECKFLAG_PORT_POSTFIX = "_flag";
constexpr char* DEFAULT_CLOCK_NAME = "clk";
constexpr char* BENCHMARK_INSTANCE_NAME = "REF_DUT";
constexpr char* FPGA_INSTANCE_NAME = "FPGA_DUT";
constexpr char* ERROR_COUNTER = "nb_error";
constexpr char* FORMAL_TB_SIM_START_PORT_NAME = "sim_start";
constexpr int MAGIC_NUMBER_FOR_SIMULATION_TIME = 200;
/********************************************************************
* Print the module ports for the Verilog testbench
* using random vectors
* This function generates
* 1. the input ports to drive both input benchmark module and FPGA fabric module
* 2. the output ports for input benchmark module
* 3. the output ports for FPGA fabric module
* 4. the error checking ports
*******************************************************************/
static
void print_verilog_top_random_testbench_ports(std::fstream& fp,
const std::string& circuit_name,
const std::vector<std::string>& clock_port_names,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation) {
/* Validate the file stream */
valid_file_stream(fp);
/* Print the declaration for the module */
fp << "module " << circuit_name << FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX << ";" << std::endl;
/* Create a clock port if the benchmark does not have one!
* The clock is used for counting and synchronizing input stimulus
*/
BasicPort clock_port = generate_verilog_testbench_clock_port(clock_port_names, std::string(DEFAULT_CLOCK_NAME));
print_verilog_comment(fp, std::string("----- Default clock port is added here since benchmark does not contain one -------"));
fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, clock_port) << ";" << std::endl;
/* Add an empty line as splitter */
fp << std::endl;
print_verilog_testbench_shared_ports(fp, atom_ctx, netlist_annotation,
clock_port_names,
std::string(BENCHMARK_PORT_POSTFIX),
std::string(FPGA_PORT_POSTFIX),
std::string(CHECKFLAG_PORT_POSTFIX),
std::string(AUTOCHECKED_SIMULATION_FLAG));
/* Instantiate an integer to count the number of error
* and determine if the simulation succeed or failed
*/
print_verilog_comment(fp, std::string("----- Error counter -------"));
fp << "\tinteger " << ERROR_COUNTER << "= 0;" << std::endl;
/* Add an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Instanciate the input benchmark module
*******************************************************************/
static
void print_verilog_top_random_testbench_benchmark_instance(std::fstream& fp,
const std::string& reference_verilog_top_name,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation) {
/* Validate the file stream */
valid_file_stream(fp);
/* Benchmark is instanciated conditionally: only when a preprocessing flag is enable */
print_verilog_preprocessing_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG));
print_verilog_comment(fp, std::string("----- Reference Benchmark Instanication -------"));
/* Do NOT use explicit port mapping here:
* VPR added a prefix of "out_" to the output ports of input benchmark
*/
print_verilog_testbench_benchmark_instance(fp, reference_verilog_top_name,
std::string(BENCHMARK_INSTANCE_NAME),
std::string(),
std::string(),
std::string(BENCHMARK_PORT_POSTFIX),
atom_ctx, netlist_annotation,
false);
print_verilog_comment(fp, std::string("----- End reference Benchmark Instanication -------"));
/* Add an empty line as splitter */
fp << std::endl;
/* Condition ends for the benchmark instanciation */
print_verilog_endif(fp);
/* Add an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Instanciate the FPGA fabric module
*******************************************************************/
static
void print_verilog_random_testbench_fpga_instance(std::fstream& fp,
const std::string& circuit_name,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation) {
/* Validate the file stream */
valid_file_stream(fp);
print_verilog_comment(fp, std::string("----- FPGA fabric instanciation -------"));
/* Always use explicit port mapping */
print_verilog_testbench_benchmark_instance(fp, std::string(circuit_name + std::string(FORMAL_VERIFICATION_TOP_MODULE_POSTFIX)),
std::string(FPGA_INSTANCE_NAME),
std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX),
std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX),
std::string(FPGA_PORT_POSTFIX),
atom_ctx, netlist_annotation,
true);
print_verilog_comment(fp, std::string("----- End FPGA Fabric Instanication -------"));
/* Add an empty line as splitter */
fp << std::endl;
}
/*********************************************************************
* Top-level function in this file:
* Create a Verilog testbench using random input vectors
* The testbench consists of two modules, i.e., Design Under Test (DUT)
* 1. top-level module of FPGA fabric
* 2. top-level module of users' benchmark,
* i.e., the input benchmark of VPR flow
* +----------+
* | FPGA | +------------+
* +----->| Fabric |------>| |
* | | | | |
* | +----------+ | |
* | | Output |
* random_input_vectors -----+ | Vector |---->Functional correct?
* | | Comparator |
* | +-----------+ | |
* | | Input | | |
* +----->| Benchmark |----->| |
* +-----------+ +------------+
*
* Same input vectors are given to drive both DUTs.
* The output vectors of the DUTs are compared to verify if they
* have the same functionality.
* A flag will be raised to indicate the result
********************************************************************/
void print_verilog_random_top_testbench(const std::string& circuit_name,
const std::string& verilog_fname,
const std::string& verilog_dir,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const SimulationSetting& simulation_parameters) {
std::string timer_message = std::string("Write configuration-skip testbench for FPGA top-level Verilog netlist implemented by '") + circuit_name.c_str() + std::string("'");
/* Start time count */
vtr::ScopedStartFinishTimer timer(timer_message);
/* 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);
/* Generate a brief description on the Verilog file*/
std::string title = std::string("FPGA Verilog Testbench for Formal Top-level netlist of Design: ") + circuit_name;
print_verilog_file_header(fp, title);
/* Print preprocessing flags and external netlists */
print_verilog_include_defines_preproc_file(fp, verilog_dir);
print_verilog_include_netlist(fp, std::string(verilog_dir + std::string(DEFINES_VERILOG_SIMULATION_FILE_NAME)));
/* Preparation: find all the clock ports */
std::vector<std::string> clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation);
/* Start of testbench */
print_verilog_top_random_testbench_ports(fp, circuit_name, clock_port_names, atom_ctx, netlist_annotation);
/* Call defined top-level module */
print_verilog_random_testbench_fpga_instance(fp, circuit_name, atom_ctx, netlist_annotation);
/* Call defined benchmark */
print_verilog_top_random_testbench_benchmark_instance(fp, circuit_name, atom_ctx, netlist_annotation);
/* Find clock port to be used */
BasicPort clock_port = generate_verilog_testbench_clock_port(clock_port_names, std::string(DEFAULT_CLOCK_NAME));
/* Add stimuli for reset, set, clock and iopad signals */
print_verilog_testbench_clock_stimuli(fp, simulation_parameters,
clock_port);
print_verilog_testbench_random_stimuli(fp, atom_ctx,
netlist_annotation,
std::string(CHECKFLAG_PORT_POSTFIX),
clock_port);
print_verilog_testbench_check(fp,
std::string(AUTOCHECKED_SIMULATION_FLAG),
std::string(FORMAL_TB_SIM_START_PORT_NAME),
std::string(BENCHMARK_PORT_POSTFIX),
std::string(FPGA_PORT_POSTFIX),
std::string(CHECKFLAG_PORT_POSTFIX),
std::string(ERROR_COUNTER),
atom_ctx,
netlist_annotation,
clock_port_names,
std::string(DEFAULT_CLOCK_NAME));
int simulation_time = find_operating_phase_simulation_time(MAGIC_NUMBER_FOR_SIMULATION_TIME,
simulation_parameters.num_clock_cycles(),
1./simulation_parameters.operating_clock_frequency(),
VERILOG_SIM_TIMESCALE);
/* Add Icarus requirement */
print_verilog_timeout_and_vcd(fp,
std::string(ICARUS_SIMULATOR_FLAG),
std::string(circuit_name + std::string(FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX)),
std::string(circuit_name + std::string("_formal.vcd")),
std::string(FORMAL_TB_SIM_START_PORT_NAME),
std::string(ERROR_COUNTER),
simulation_time);
/* Testbench ends*/
print_verilog_module_end(fp, std::string(circuit_name) + std::string(FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX));
/* Close the file stream */
fp.close();
}
} /* end namespace openfpga */

View File

@ -0,0 +1,27 @@
#ifndef VERILOG_FORMAL_RANDOM_TOP_TESTBENCH
#define VERILOG_FORMAL_RANDOM_TOP_TESTBENCH
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include "vpr_context.h"
#include "simulation_setting.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void print_verilog_random_top_testbench(const std::string& circuit_name,
const std::string& verilog_fname,
const std::string& verilog_dir,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const SimulationSetting& simulation_parameters);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,452 @@
/********************************************************************
* This file includes functions that are used to generate
* a Verilog module of a pre-configured FPGA fabric
*******************************************************************/
#include <fstream>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
#include "vtr_time.h"
/* Headers from openfpgautil library */
#include "openfpga_port.h"
#include "openfpga_digest.h"
#include "bitstream_manager_utils.h"
#include "openfpga_atom_netlist_utils.h"
#include "openfpga_naming.h"
#include "verilog_constants.h"
#include "verilog_writer_utils.h"
#include "verilog_testbench_utils.h"
#include "verilog_preconfig_top_module.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Print module declaration and ports for the pre-configured
* FPGA top module
* The module ports do exactly match the input benchmark
*******************************************************************/
static
void print_verilog_preconfig_top_module_ports(std::fstream& fp,
const std::string& circuit_name,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation) {
/* Validate the file stream */
valid_file_stream(fp);
/* Module declaration */
fp << "module " << circuit_name << std::string(FORMAL_VERIFICATION_TOP_MODULE_POSTFIX);
fp << " (" << std::endl;
/* Add module ports */
size_t port_counter = 0;
/* Port type-to-type mapping */
std::map<AtomBlockType, enum e_dump_verilog_port_type> port_type2type_map;
port_type2type_map[AtomBlockType::INPAD] = VERILOG_PORT_INPUT;
port_type2type_map[AtomBlockType::OUTPAD] = VERILOG_PORT_OUTPUT;
/* Print all the I/Os of the circuit implementation to be tested*/
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* We only care I/O logical blocks !*/
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
if (0 < port_counter) {
fp << "," << std::endl;
}
/* Both input and output ports have only size of 1 */
BasicPort module_port(std::string(block_name + std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX)), 1);
fp << generate_verilog_port(port_type2type_map[atom_ctx.nlist.block_type(atom_blk)], module_port);
/* Update port counter */
port_counter++;
}
fp << ");" << std::endl;
/* Add an empty line as a splitter */
fp << std::endl;
}
/********************************************************************
* Print internal wires for the pre-configured FPGA top module
* The internal wires are tailored for the ports of FPGA top module
* which will be different in various configuration protocols
*******************************************************************/
static
void print_verilog_preconfig_top_module_internal_wires(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module) {
/* Validate the file stream */
valid_file_stream(fp);
/* Global ports of top-level module */
print_verilog_comment(fp, std::string("----- Local wires for FPGA fabric -----"));
for (const ModulePortId& module_port_id : module_manager.module_ports(top_module)) {
BasicPort module_port = module_manager.module_port(top_module, module_port_id);
fp << generate_verilog_port(VERILOG_PORT_WIRE, module_port) << ";" << std::endl;
}
/* Add an empty line as a splitter */
fp << std::endl;
}
/********************************************************************
* Connect global ports of FPGA top module to constants except:
* 1. operating clock, which should be wired to the clock port of
* this pre-configured FPGA top module
*******************************************************************/
static
void print_verilog_preconfig_top_module_connect_global_ports(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const CircuitLibrary& circuit_lib,
const std::vector<CircuitPortId>& global_ports,
const std::vector<std::string>& benchmark_clock_port_names) {
/* Validate the file stream */
valid_file_stream(fp);
print_verilog_comment(fp, std::string("----- Begin Connect Global ports of FPGA top module -----"));
/* Global ports of the top module in module manager do not carry any attributes,
* such as is_clock, is_set, etc.
* Therefore, for each global port in the top module, we find the circuit port in the circuit library
* which share the same name. We can access to the attributes.
* To gurantee the correct link between global ports in module manager and those in circuit library
* We have performed some critical check in check_circuit_library() for global ports,
* where we guarantee all the global ports share the same name must have the same attributes.
* So that each global port with the same name is unique!
*/
for (const BasicPort& module_global_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GLOBAL_PORT)) {
CircuitPortId linked_circuit_port_id = CircuitPortId::INVALID();
/* Find the circuit port with the same name */
for (const CircuitPortId& circuit_port_id : global_ports) {
if (0 != module_global_port.get_name().compare(circuit_lib.port_prefix(circuit_port_id))) {
continue;
}
linked_circuit_port_id = circuit_port_id;
break;
}
/* Must find one valid circuit port */
VTR_ASSERT(CircuitPortId::INVALID() != linked_circuit_port_id);
/* Port size should match! */
VTR_ASSERT(module_global_port.get_width() == circuit_lib.port_size(linked_circuit_port_id));
/* Now, for operating clock port, we should wire it to the clock of benchmark! */
if ( (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(linked_circuit_port_id))
&& (false == circuit_lib.port_is_prog(linked_circuit_port_id)) ) {
/* Wiring to each pin of the global port: benchmark clock is always 1-bit */
for (const size_t& pin : module_global_port.pins()) {
for (const std::string& clock_port_name : benchmark_clock_port_names) {
BasicPort module_clock_pin(module_global_port.get_name(), pin, pin);
BasicPort benchmark_clock_pin(clock_port_name + std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX), 1);
print_verilog_wire_connection(fp, module_clock_pin, benchmark_clock_pin, false);
}
}
/* Finish, go to the next */
continue;
}
/* For other ports, give an default value */
std::vector<size_t> default_values(module_global_port.get_width(), circuit_lib.port_default_value(linked_circuit_port_id));
print_verilog_wire_constant_values(fp, module_global_port, default_values);
}
print_verilog_comment(fp, std::string("----- End Connect Global ports of FPGA top module -----"));
/* Add an empty line as a splitter */
fp << std::endl;
}
/********************************************************************
* Impose the bitstream on the configuration memories
* This function uses 'assign' syntax to impost the bitstream at mem port
* while uses 'force' syntax to impost the bitstream at mem_inv port
*******************************************************************/
static
void print_verilog_preconfig_top_module_assign_bitstream(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const BitstreamManager& bitstream_manager) {
/* Validate the file stream */
valid_file_stream(fp);
print_verilog_comment(fp, std::string("----- Begin assign bitstream to configuration memories -----"));
for (const ConfigBlockId& config_block_id : bitstream_manager.blocks()) {
/* We only cares blocks with configuration bits */
if (0 == bitstream_manager.block_bits(config_block_id).size()) {
continue;
}
/* Build the hierarchical path of the configuration bit in modules */
std::vector<ConfigBlockId> block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, config_block_id);
/* Drop the first block, which is the top module, it should be replaced by the instance name here */
/* Ensure that this is the module we want to drop! */
VTR_ASSERT(0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0])));
block_hierarchy.erase(block_hierarchy.begin());
/* Build the full hierarchy path */
std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME);
for (const ConfigBlockId& temp_block : block_hierarchy) {
bit_hierarchy_path += std::string(".");
bit_hierarchy_path += bitstream_manager.block_name(temp_block);
}
bit_hierarchy_path += std::string(".");
/* Find the bit index in the parent block */
BasicPort config_data_port(bit_hierarchy_path + generate_configuration_chain_data_out_name(),
bitstream_manager.block_bits(config_block_id).size());
/* Wire it to the configuration bit: access both data out and data outb ports */
std::vector<size_t> config_data_values;
for (const ConfigBitId config_bit : bitstream_manager.block_bits(config_block_id)) {
config_data_values.push_back(bitstream_manager.bit_value(config_bit));
}
print_verilog_wire_constant_values(fp, config_data_port, config_data_values);
}
fp << "initial begin" << std::endl;
for (const ConfigBlockId& config_block_id : bitstream_manager.blocks()) {
/* We only cares blocks with configuration bits */
if (0 == bitstream_manager.block_bits(config_block_id).size()) {
continue;
}
/* Build the hierarchical path of the configuration bit in modules */
std::vector<ConfigBlockId> block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, config_block_id);
/* Drop the first block, which is the top module, it should be replaced by the instance name here */
/* Ensure that this is the module we want to drop! */
VTR_ASSERT(0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0])));
block_hierarchy.erase(block_hierarchy.begin());
/* Build the full hierarchy path */
std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME);
for (const ConfigBlockId& temp_block : block_hierarchy) {
bit_hierarchy_path += std::string(".");
bit_hierarchy_path += bitstream_manager.block_name(temp_block);
}
bit_hierarchy_path += std::string(".");
/* Find the bit index in the parent block */
BasicPort config_datab_port(bit_hierarchy_path + generate_configuration_chain_inverted_data_out_name(),
bitstream_manager.block_bits(config_block_id).size());
std::vector<size_t> config_datab_values;
for (const ConfigBitId config_bit : bitstream_manager.block_bits(config_block_id)) {
config_datab_values.push_back(!bitstream_manager.bit_value(config_bit));
}
print_verilog_force_wire_constant_values(fp, config_datab_port, config_datab_values);
}
fp << "end" << std::endl;
print_verilog_comment(fp, std::string("----- End assign bitstream to configuration memories -----"));
}
/********************************************************************
* Impose the bitstream on the configuration memories
* This function uses '$deposit' syntax to do so
*******************************************************************/
static
void print_verilog_preconfig_top_module_deposit_bitstream(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const BitstreamManager& bitstream_manager) {
/* Validate the file stream */
valid_file_stream(fp);
print_verilog_comment(fp, std::string("----- Begin deposit bitstream to configuration memories -----"));
fp << "initial begin" << std::endl;
for (const ConfigBlockId& config_block_id : bitstream_manager.blocks()) {
/* We only cares blocks with configuration bits */
if (0 == bitstream_manager.block_bits(config_block_id).size()) {
continue;
}
/* Build the hierarchical path of the configuration bit in modules */
std::vector<ConfigBlockId> block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, config_block_id);
/* Drop the first block, which is the top module, it should be replaced by the instance name here */
/* Ensure that this is the module we want to drop! */
VTR_ASSERT(0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0])));
block_hierarchy.erase(block_hierarchy.begin());
/* Build the full hierarchy path */
std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME);
for (const ConfigBlockId& temp_block : block_hierarchy) {
bit_hierarchy_path += std::string(".");
bit_hierarchy_path += bitstream_manager.block_name(temp_block);
}
bit_hierarchy_path += std::string(".");
/* Find the bit index in the parent block */
BasicPort config_data_port(bit_hierarchy_path + generate_configuration_chain_data_out_name(),
bitstream_manager.block_bits(config_block_id).size());
BasicPort config_datab_port(bit_hierarchy_path + generate_configuration_chain_inverted_data_out_name(),
bitstream_manager.block_bits(config_block_id).size());
/* Wire it to the configuration bit: access both data out and data outb ports */
std::vector<size_t> config_data_values;
for (const ConfigBitId config_bit : bitstream_manager.block_bits(config_block_id)) {
config_data_values.push_back(bitstream_manager.bit_value(config_bit));
}
print_verilog_deposit_wire_constant_values(fp, config_data_port, config_data_values);
std::vector<size_t> config_datab_values;
for (const ConfigBitId config_bit : bitstream_manager.block_bits(config_block_id)) {
config_datab_values.push_back(!bitstream_manager.bit_value(config_bit));
}
print_verilog_deposit_wire_constant_values(fp, config_datab_port, config_datab_values);
}
fp << "end" << std::endl;
print_verilog_comment(fp, std::string("----- End deposit bitstream to configuration memories -----"));
}
/********************************************************************
* Impose the bitstream on the configuration memories
* We branch here for different simulators:
* 1. iVerilog Icarus prefers using 'assign' syntax to force the values
* 2. Mentor Modelsim prefers using '$deposit' syntax to do so
*******************************************************************/
static
void print_verilog_preconfig_top_module_load_bitstream(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const BitstreamManager& bitstream_manager) {
print_verilog_comment(fp, std::string("----- Begin load bitstream to configuration memories -----"));
print_verilog_preprocessing_flag(fp, std::string(ICARUS_SIMULATOR_FLAG));
/* Use assign syntax for Icarus simulator */
print_verilog_preconfig_top_module_assign_bitstream(fp, module_manager, top_module, bitstream_manager);
fp << "`else" << std::endl;
/* Use assign syntax for Icarus simulator */
print_verilog_preconfig_top_module_deposit_bitstream(fp, module_manager, top_module, bitstream_manager);
print_verilog_endif(fp);
print_verilog_comment(fp, std::string("----- End load bitstream to configuration memories -----"));
}
/********************************************************************
* Top-level function to generate a Verilog module of
* a pre-configured FPGA fabric.
*
* Pre-configured FPGA fabric
* +--------------------------------------------
* |
* | FPGA fabric
* | +-------------------------------+
* | | |
* | 0/1---->|FPGA global ports |
* | | |
* benchmark_clock----->|--------->|FPGA_clock |
* | | |
* benchmark_inputs---->|--------->|FPGA mapped I/Os |
* | | |
* benchmark_outputs<---|<---------|FPGA mapped I/Os |
* | | |
* | 0/1---->|FPGA unmapped I/Os |
* | | |
* fabric_bitstream---->|--------->|Internal_configuration_ports |
* | +-------------------------------+
* |
* +-------------------------------------------
*
* Note: we do NOT put this module in the module manager.
* Because, it is not a standard module, where we force configuration signals
* This module is a wrapper for the FPGA fabric to be compatible in
* the port map of input benchmark.
* It includes wires to force constant values to part of FPGA datapath I/Os
* All these are hard to implement as a module in module manager
*******************************************************************/
void print_verilog_preconfig_top_module(const ModuleManager& module_manager,
const BitstreamManager& bitstream_manager,
const CircuitLibrary& circuit_lib,
const std::vector<CircuitPortId>& global_ports,
const AtomContext& atom_ctx,
const PlacementContext& place_ctx,
const IoLocationMap& io_location_map,
const VprNetlistAnnotation& netlist_annotation,
const std::string& circuit_name,
const std::string& verilog_fname,
const std::string& verilog_dir) {
std::string timer_message = std::string("Write pre-configured FPGA top-level Verilog netlist for design '") + circuit_name + std::string("'");
/* Start time count */
vtr::ScopedStartFinishTimer timer(timer_message);
/* 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);
/* Generate a brief description on the Verilog file*/
std::string title = std::string("Verilog netlist for pre-configured FPGA fabric by design: ") + circuit_name;
print_verilog_file_header(fp, title);
/* Print preprocessing flags and external netlists */
print_verilog_include_defines_preproc_file(fp, verilog_dir);
print_verilog_include_netlist(fp, std::string(verilog_dir + std::string(DEFINES_VERILOG_SIMULATION_FILE_NAME)));
/* Print module declaration and ports */
print_verilog_preconfig_top_module_ports(fp, circuit_name, atom_ctx, netlist_annotation);
/* Find the top_module */
ModuleId top_module = module_manager.find_module(generate_fpga_top_module_name());
VTR_ASSERT(true == module_manager.valid_module_id(top_module));
/* Print internal wires */
print_verilog_preconfig_top_module_internal_wires(fp, module_manager, top_module);
/* Instanciate FPGA top-level module */
print_verilog_testbench_fpga_instance(fp, module_manager, top_module,
std::string(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME));
/* Find clock ports in benchmark */
std::vector<std::string> benchmark_clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation);
/* Connect FPGA top module global ports to constant or benchmark global signals! */
print_verilog_preconfig_top_module_connect_global_ports(fp, module_manager, top_module,
circuit_lib, global_ports,
benchmark_clock_port_names);
/* Connect I/Os to benchmark I/Os or constant driver */
print_verilog_testbench_connect_fpga_ios(fp, module_manager, top_module,
atom_ctx, place_ctx, io_location_map,
netlist_annotation,
std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX),
std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX),
(size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE);
/* Assign FPGA internal SRAM/Memory ports to bitstream values */
print_verilog_preconfig_top_module_load_bitstream(fp, module_manager, top_module,
bitstream_manager);
/* Testbench ends*/
print_verilog_module_end(fp, std::string(circuit_name) + std::string(FORMAL_VERIFICATION_TOP_MODULE_POSTFIX));
/* Close the file stream */
fp.close();
}
} /* end namespace openfpga */

View File

@ -0,0 +1,37 @@
#ifndef VERILOG_PRECONFIG_TOP_MODULE_H
#define VERILOG_PRECONFIG_TOP_MODULE_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <vector>
#include <string>
#include "circuit_library.h"
#include "vpr_context.h"
#include "module_manager.h"
#include "bitstream_manager.h"
#include "io_location_map.h"
#include "vpr_netlist_annotation.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void print_verilog_preconfig_top_module(const ModuleManager& module_manager,
const BitstreamManager& bitstream_manager,
const CircuitLibrary& circuit_lib,
const std::vector<CircuitPortId>& global_ports,
const AtomContext& atom_ctx,
const PlacementContext& place_ctx,
const IoLocationMap& io_location_map,
const VprNetlistAnnotation& netlist_annotation,
const std::string& circuit_name,
const std::string& verilog_fname,
const std::string& verilog_dir);
} /* end namespace openfpga */
#endif

View File

@ -6,7 +6,7 @@
*******************************************************************/ *******************************************************************/
#include "module_manager.h" #include "module_manager.h"
#include "mux_library.h" #include "mux_library.h"
#include "verilog_options.h" #include "fabric_verilog_options.h"
/******************************************************************** /********************************************************************
* Function declaration * Function declaration

View File

@ -0,0 +1,104 @@
/******************************************************************************
* Memember functions for data structure VerilogTestbenchOption
******************************************************************************/
#include "vtr_assert.h"
#include "vtr_log.h"
#include "verilog_testbench_options.h"
/* begin namespace openfpga */
namespace openfpga {
/**************************************************
* Public Constructors
*************************************************/
VerilogTestbenchOption::VerilogTestbenchOption() {
output_directory_.clear();
reference_benchmark_file_path_.clear();
print_preconfig_top_testbench_ = false;
print_formal_verification_top_netlist_ = false;
print_top_testbench_ = false;
simulation_ini_path_.clear();
verbose_output_ = false;
}
/**************************************************
* Public Accessors
*************************************************/
std::string VerilogTestbenchOption::output_directory() const {
return output_directory_;
}
std::string VerilogTestbenchOption::reference_benchmark_file_path() const {
return reference_benchmark_file_path_;
}
bool VerilogTestbenchOption::print_formal_verification_top_netlist() const {
return print_formal_verification_top_netlist_;
}
bool VerilogTestbenchOption::print_preconfig_top_testbench() const {
return print_preconfig_top_testbench_;
}
bool VerilogTestbenchOption::print_top_testbench() const {
return print_top_testbench_;
}
bool VerilogTestbenchOption::print_simulation_ini() const {
return !simulation_ini_path_.empty();
}
std::string VerilogTestbenchOption::simulation_ini_path() const {
return simulation_ini_path_;
}
bool VerilogTestbenchOption::verbose_output() const {
return verbose_output_;
}
/******************************************************************************
* Private Mutators
******************************************************************************/
void VerilogTestbenchOption::set_output_directory(const std::string& output_dir) {
output_directory_ = output_dir;
}
void VerilogTestbenchOption::set_reference_benchmark_file_path(const std::string& reference_benchmark_file_path) {
reference_benchmark_file_path_ = reference_benchmark_file_path;
/* Chain effect on other options:
* Enable/disable the print_preconfig_top_testbench and print_top_testbench
*/
set_print_preconfig_top_testbench(print_preconfig_top_testbench_);
set_print_top_testbench(print_top_testbench_);
}
void VerilogTestbenchOption::set_print_formal_verification_top_netlist(const bool& enabled) {
print_formal_verification_top_netlist_ = enabled;
}
void VerilogTestbenchOption::set_print_preconfig_top_testbench(const bool& enabled) {
print_preconfig_top_testbench_ = enabled
&& (!reference_benchmark_file_path_.empty());
/* Enable print formal verification top_netlist if this is enabled */
if (true == print_preconfig_top_testbench_) {
if (false == print_formal_verification_top_netlist_) {
VTR_LOG_WARN("Forcely enable to print top-level Verilog netlist in formal verification purpose as print pre-configured top-level Verilog testbench is enabled\n");
print_formal_verification_top_netlist_ = true;
}
}
}
void VerilogTestbenchOption::set_print_top_testbench(const bool& enabled) {
print_top_testbench_ = enabled && (!reference_benchmark_file_path_.empty());
}
void VerilogTestbenchOption::set_print_simulation_ini(const std::string& simulation_ini_path) {
simulation_ini_path_ = simulation_ini_path;
}
void VerilogTestbenchOption::set_verbose_output(const bool& enabled) {
verbose_output_ = enabled;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,62 @@
#ifndef VERILOG_TESTBENCH_OPTIONS_H
#define VERILOG_TESTBENCH_OPTIONS_H
/********************************************************************
* Include header files required by the data structure definition
*******************************************************************/
#include <string>
/* Begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Options for Verilog Testbench generator
* Typicall usage:
* VerilogTestbench verilog_tb_opt();
* // Set options
* ...
*
*******************************************************************/
class VerilogTestbenchOption {
public: /* Public constructor */
/* Set default options */
VerilogTestbenchOption();
public: /* Public accessors */
std::string output_directory() const;
std::string reference_benchmark_file_path() const;
bool print_formal_verification_top_netlist() const;
bool print_preconfig_top_testbench() const;
bool print_top_testbench() const;
bool print_simulation_ini() const;
std::string simulation_ini_path() const;
bool verbose_output() const;
public: /* Public validator */
bool validate() const;
public: /* Public mutators */
void set_output_directory(const std::string& output_dir);
/* The reference verilog file path is the key parameters that will have an impact on other options:
* - print_preconfig_top_testbench
* - print_top_testbench
* If the file path is empty, the above testbench generation will not be enabled
*/
void set_reference_benchmark_file_path(const std::string& reference_benchmark_file_path);
void set_print_formal_verification_top_netlist(const bool& enabled);
/* The preconfig top testbench generation can be enabled only when formal verification top netlist is enabled */
void set_print_preconfig_top_testbench(const bool& enabled);
void set_print_top_testbench(const bool& enabled);
void set_print_simulation_ini(const std::string& simulation_ini_path);
void set_verbose_output(const bool& enabled);
private: /* Internal Data */
std::string output_directory_;
std::string reference_benchmark_file_path_;
bool print_formal_verification_top_netlist_;
bool print_preconfig_top_testbench_;
bool print_top_testbench_;
/* Print simulation ini is enabled only when the path is not empty */
std::string simulation_ini_path_;
bool verbose_output_;
};
} /* End namespace openfpga*/
#endif

View File

@ -0,0 +1,652 @@
/********************************************************************
* This file includes most utilized functions that are used to create
* Verilog testbenches
*
* Note: please try to avoid using global variables in this file
* so that we can make it free to use anywhere
*******************************************************************/
#include <iomanip>
/* Headers from vtrutil library */
#include "vtr_assert.h"
/* Headers from openfpgautil library */
#include "openfpga_port.h"
#include "openfpga_digest.h"
#include "verilog_port_types.h"
#include "verilog_constants.h"
#include "verilog_writer_utils.h"
#include "verilog_testbench_utils.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Print an instance of the FPGA top-level module
*******************************************************************/
void print_verilog_testbench_fpga_instance(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const std::string& top_instance_name) {
/* Validate the file stream */
valid_file_stream(fp);
/* Include defined top-level module */
print_verilog_comment(fp, std::string("----- FPGA top-level module to be capsulated -----"));
/* Create an empty port-to-port name mapping, because we use default names */
std::map<std::string, BasicPort> port2port_name_map;
/* Use explicit port mapping for a clean instanciation */
print_verilog_module_instance(fp, module_manager, top_module,
top_instance_name,
port2port_name_map, true);
/* Add an empty line as a splitter */
fp << std::endl;
}
/********************************************************************
* Instanciate the input benchmark module
*******************************************************************/
void print_verilog_testbench_benchmark_instance(std::fstream& fp,
const std::string& module_name,
const std::string& instance_name,
const std::string& module_input_port_postfix,
const std::string& module_output_port_postfix,
const std::string& output_port_postfix,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const bool& use_explicit_port_map) {
/* Validate the file stream */
valid_file_stream(fp);
fp << "\t" << module_name << " " << instance_name << "(" << std::endl;
size_t port_counter = 0;
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* Bypass non-I/O atom blocks ! */
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
/* The first port does not need a comma */
if(0 < port_counter){
fp << "," << std::endl;
}
/* Input port follows the logical block name while output port requires a special postfix */
if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) {
fp << "\t\t";
if (true == use_explicit_port_map) {
fp << "." << block_name << module_input_port_postfix << "(";
}
fp << block_name;
if (true == use_explicit_port_map) {
fp << ")";
}
} else {
VTR_ASSERT_SAFE(AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk));
fp << "\t\t";
if (true == use_explicit_port_map) {
fp << "." << block_name << module_output_port_postfix << "(";
}
fp << block_name << output_port_postfix;
if (true == use_explicit_port_map) {
fp << ")";
}
}
/* Update the counter */
port_counter++;
}
fp << "\t);" << std::endl;
}
/********************************************************************
* This function adds stimuli to I/Os of FPGA fabric
* 1. For mapped I/Os, this function will wire them to the input ports
* of the pre-configured FPGA top module
* 2. For unmapped I/Os, this function will assign a constant value
* by default
*******************************************************************/
void print_verilog_testbench_connect_fpga_ios(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const AtomContext& atom_ctx,
const PlacementContext& place_ctx,
const IoLocationMap& io_location_map,
const VprNetlistAnnotation& netlist_annotation,
const std::string& io_input_port_name_postfix,
const std::string& io_output_port_name_postfix,
const size_t& unused_io_value) {
/* Validate the file stream */
valid_file_stream(fp);
/* In this function, we support only 1 type of I/Os */
VTR_ASSERT(1 == module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT).size());
BasicPort module_io_port = module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT)[0];
/* Keep tracking which I/Os have been used */
std::vector<bool> io_used(module_io_port.get_width(), false);
/* See if this I/O should be wired to a benchmark input/output */
/* Add signals from blif benchmark and short-wire them to FPGA I/O PADs
* This brings convenience to checking functionality
*/
print_verilog_comment(fp, std::string("----- Link BLIF Benchmark I/Os to FPGA I/Os -----"));
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* Bypass non-I/O atom blocks ! */
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
continue;
}
/* Find the index of the mapped GPIO in top-level FPGA fabric */
size_t io_index = io_location_map.io_index(place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.x,
place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.y,
place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.z);
/* Ensure that IO index is in range */
BasicPort module_mapped_io_port = module_io_port;
/* Set the port pin index */
VTR_ASSERT(io_index < module_mapped_io_port.get_width());
module_mapped_io_port.set_width(io_index, io_index);
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
/* Create the port for benchmark I/O, due to BLIF benchmark, each I/O always has a size of 1
* In addition, the input and output ports may have different postfix in naming
* due to verification context! Here, we give full customization on naming
*/
BasicPort benchmark_io_port;
if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) {
benchmark_io_port.set_name(std::string(block_name + io_input_port_name_postfix));
benchmark_io_port.set_width(1);
print_verilog_comment(fp, std::string("----- Blif Benchmark input " + block_name + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----"));
print_verilog_wire_connection(fp, module_mapped_io_port, benchmark_io_port, false);
} else {
VTR_ASSERT(AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk));
benchmark_io_port.set_name(std::string(block_name + io_output_port_name_postfix));
benchmark_io_port.set_width(1);
print_verilog_comment(fp, std::string("----- Blif Benchmark output " + block_name + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----"));
print_verilog_wire_connection(fp, benchmark_io_port, module_mapped_io_port, false);
}
/* Mark this I/O has been used/wired */
io_used[io_index] = true;
}
/* Add an empty line as a splitter */
fp << std::endl;
/* Wire the unused iopads to a constant */
print_verilog_comment(fp, std::string("----- Wire unused FPGA I/Os to constants -----"));
for (size_t io_index = 0; io_index < io_used.size(); ++io_index) {
/* Bypass used iopads */
if (true == io_used[io_index]) {
continue;
}
/* Wire to a contant */
BasicPort module_unused_io_port = module_io_port;
/* Set the port pin index */
module_unused_io_port.set_width(io_index, io_index);
std::vector<size_t> default_values(module_unused_io_port.get_width(), unused_io_value);
print_verilog_wire_constant_values(fp, module_unused_io_port, default_values);
}
/* Add an empty line as a splitter */
fp << std::endl;
}
/********************************************************************
* Print Verilog codes to set up a timeout for the simulation
* and dump the waveform to VCD files
*
* Note that: these codes are tuned for Icarus simulator!!!
*******************************************************************/
void print_verilog_timeout_and_vcd(std::fstream& fp,
const std::string& icarus_preprocessing_flag,
const std::string& module_name,
const std::string& vcd_fname,
const std::string& simulation_start_counter_name,
const std::string& error_counter_name,
const int& simulation_time) {
/* Validate the file stream */
valid_file_stream(fp);
/* The following verilog codes are tuned for Icarus */
print_verilog_preprocessing_flag(fp, icarus_preprocessing_flag);
print_verilog_comment(fp, std::string("----- Begin Icarus requirement -------"));
fp << "\tinitial begin" << std::endl;
fp << "\t\t$dumpfile(\"" << vcd_fname << "\");" << std::endl;
fp << "\t\t$dumpvars(1, " << module_name << ");" << std::endl;
fp << "\tend" << std::endl;
/* Condition ends for the Icarus requirement */
print_verilog_endif(fp);
print_verilog_comment(fp, std::string("----- END Icarus requirement -------"));
/* Add an empty line as splitter */
fp << std::endl;
BasicPort sim_start_port(simulation_start_counter_name, 1);
fp << "initial begin" << std::endl;
fp << "\t" << generate_verilog_port(VERILOG_PORT_CONKT, sim_start_port) << " <= 1'b1;" << std::endl;
fp << "\t$timeformat(-9, 2, \"ns\", 20);" << std::endl;
fp << "\t$display(\"Simulation start\");" << std::endl;
print_verilog_comment(fp, std::string("----- Can be changed by the user for his/her need -------"));
fp << "\t#" << simulation_time << std::endl;
fp << "\tif(" << error_counter_name << " == 0) begin" << std::endl;
fp << "\t\t$display(\"Simulation Succeed\");" << std::endl;
fp << "\tend else begin" << std::endl;
fp << "\t\t$display(\"Simulation Failed with " << std::string("%d") << " error(s)\", " << error_counter_name << ");" << std::endl;
fp << "\tend" << std::endl;
fp << "\t$finish;" << std::endl;
fp << "end" << std::endl;
/* Add an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Generate the clock port name to be used in this testbench
*
* Restrictions:
* Assume this is a single clock benchmark
*******************************************************************/
BasicPort generate_verilog_testbench_clock_port(const std::vector<std::string>& clock_port_names,
const std::string& default_clock_name) {
if (0 == clock_port_names.size()) {
return BasicPort(default_clock_name, 1);
}
VTR_ASSERT(1 == clock_port_names.size());
return BasicPort(clock_port_names[0], 1);
}
/********************************************************************
* Print Verilog codes to check the equivalence of output vectors
*
* Restriction: this function only supports single clock benchmarks!
*******************************************************************/
void print_verilog_testbench_check(std::fstream& fp,
const std::string& autochecked_preprocessing_flag,
const std::string& simulation_start_counter_name,
const std::string& benchmark_port_postfix,
const std::string& fpga_port_postfix,
const std::string& check_flag_port_postfix,
const std::string& error_counter_name,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const std::vector<std::string>& clock_port_names,
const std::string& default_clock_name) {
/* Validate the file stream */
valid_file_stream(fp);
/* Add output autocheck conditionally: only when a preprocessing flag is enable */
print_verilog_preprocessing_flag(fp, autochecked_preprocessing_flag);
print_verilog_comment(fp, std::string("----- Begin checking output vectors -------"));
BasicPort clock_port = generate_verilog_testbench_clock_port(clock_port_names, default_clock_name);
print_verilog_comment(fp, std::string("----- Skip the first falling edge of clock, it is for initialization -------"));
BasicPort sim_start_port(simulation_start_counter_name, 1);
fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, sim_start_port) << ";" << std::endl;
fp << std::endl;
fp << "\talways@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << ") begin" << std::endl;
fp << "\t\tif (1'b1 == " << generate_verilog_port(VERILOG_PORT_CONKT, sim_start_port) << ") begin" << std::endl;
fp << "\t\t";
print_verilog_register_connection(fp, sim_start_port, sim_start_port, true);
fp << "\t\tend else begin" << std::endl;
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* Bypass non-I/O atom blocks ! */
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
if (AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk)) {
fp << "\t\t\tif(!(" << block_name << fpga_port_postfix;
fp << " === " << block_name << benchmark_port_postfix;
fp << ") && !(" << block_name << benchmark_port_postfix;
fp << " === 1'bx)) begin" << std::endl;
fp << "\t\t\t\t" << block_name << check_flag_port_postfix << " <= 1'b1;" << std::endl;
fp << "\t\t\tend else begin" << std::endl;
fp << "\t\t\t\t" << block_name << check_flag_port_postfix << "<= 1'b0;" << std::endl;
fp << "\t\t\tend" << std::endl;
}
}
fp << "\t\tend" << std::endl;
fp << "\tend" << std::endl;
/* Add an empty line as splitter */
fp << std::endl;
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* Only care about output atom blocks ! */
if (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
fp << "\talways@(posedge " << block_name << check_flag_port_postfix << ") begin" << std::endl;
fp << "\t\tif(" << block_name << check_flag_port_postfix << ") begin" << std::endl;
fp << "\t\t\t" << error_counter_name << " = " << error_counter_name << " + 1;" << std::endl;
fp << "\t\t\t$display(\"Mismatch on " << block_name << fpga_port_postfix << " at time = " << std::string("%t") << "\", $realtime);" << std::endl;
fp << "\t\tend" << std::endl;
fp << "\tend" << std::endl;
/* Add an empty line as splitter */
fp << std::endl;
}
/* Condition ends */
print_verilog_endif(fp);
/* Add an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Generate random stimulus for the clock port
* This function is designed to drive the clock port of a benchmark module
* If there is no clock port found, we will give a default clock name
* In such case, this clock will not be wired to the benchmark module
* but be only used as a synchronizer in verification
*******************************************************************/
void print_verilog_testbench_clock_stimuli(std::fstream& fp,
const SimulationSetting& simulation_parameters,
const BasicPort& clock_port) {
/* Validate the file stream */
valid_file_stream(fp);
print_verilog_comment(fp, std::string("----- Clock Initialization -------"));
fp << "\tinitial begin" << std::endl;
/* Create clock stimuli */
fp << "\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << " <= 1'b0;" << std::endl;
fp << "\t\twhile(1) begin" << std::endl;
fp << "\t\t\t#" << std::setprecision(10) << ((0.5/simulation_parameters.operating_clock_frequency())/VERILOG_SIM_TIMESCALE) << std::endl;
fp << "\t\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, clock_port);
fp << " <= !";
fp << generate_verilog_port(VERILOG_PORT_CONKT, clock_port);
fp << ";" << std::endl;
fp << "\t\tend" << std::endl;
fp << "\tend" << std::endl;
/* Add an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Generate random stimulus for the input ports (non-clock signals)
* For clock signals, please use print_verilog_testbench_clock_stimuli
*******************************************************************/
void print_verilog_testbench_random_stimuli(std::fstream& fp,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const std::string& check_flag_port_postfix,
const BasicPort& clock_port) {
/* Validate the file stream */
valid_file_stream(fp);
print_verilog_comment(fp, std::string("----- Input Initialization -------"));
fp << "\tinitial begin" << std::endl;
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* Bypass non-I/O atom blocks ! */
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
/* TODO: find the clock inputs will be initialized later */
if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) {
fp << "\t\t" << block_name << " <= 1'b0;" << std::endl;
}
}
/* Add an empty line as splitter */
fp << std::endl;
/* Set 0 to registers for checking flags */
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* Bypass non-I/O atom blocks ! */
if (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
/* Each logical block assumes a single-width port */
BasicPort output_port(std::string(block_name + check_flag_port_postfix), 1);
fp << "\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, output_port) << " <= 1'b0;" << std::endl;
}
fp << "\tend" << std::endl;
/* Finish initialization */
/* Add an empty line as splitter */
fp << std::endl;
// Not ready yet to determine if input is reset
/*
fprintf(fp, "//----- Reset Stimulis\n");
fprintf(fp, " initial begin\n");
fprintf(fp, " #%.3f\n",(rand() % 10) + 0.001);
fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name);
fprintf(fp, " #%.3f\n",(rand() % 10) + 0.001);
fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name);
fprintf(fp, " while(1) begin\n");
fprintf(fp, " #%.3f\n", (rand() % 15) + 0.5);
fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name);
fprintf(fp, " #%.3f\n", (rand() % 10000) + 200);
fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name);
fprintf(fp, " end\n");
fprintf(fp, " end\n\n");
*/
print_verilog_comment(fp, std::string("----- Input Stimulus -------"));
fp << "\talways@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << ") begin" << std::endl;
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* Bypass non-I/O atom blocks ! */
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
/* TODO: find the clock inputs will be initialized later */
if (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk)) {
fp << "\t\t" << block_name << " <= $random;" << std::endl;
}
}
fp << "\tend" << std::endl;
/* Add an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Print Verilog declaration of shared ports appear in testbenches
* which are
* 1. the shared input ports (registers) to drive both
* FPGA fabric and benchmark instance
* 2. the output ports (wires) for both FPGA fabric and benchmark instance
* 3. the checking flag ports to evaluate if outputs matches under the
* same input vectors
*******************************************************************/
void print_verilog_testbench_shared_ports(std::fstream& fp,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const std::vector<std::string>& clock_port_names,
const std::string& benchmark_output_port_postfix,
const std::string& fpga_output_port_postfix,
const std::string& check_flag_port_postfix,
const std::string& autocheck_preprocessing_flag) {
/* Validate the file stream */
valid_file_stream(fp);
/* Instantiate register for inputs stimulis */
print_verilog_comment(fp, std::string("----- Shared inputs -------"));
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* We care only those logic blocks which are input I/Os */
if (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk)) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
/* TODO: Skip clocks because they are handled in another function */
if (clock_port_names.end() != std::find(clock_port_names.begin(), clock_port_names.end(), block_name)) {
continue;
}
/* Each logical block assumes a single-width port */
BasicPort input_port(block_name, 1);
fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, input_port) << ";" << std::endl;
}
/* Add an empty line as splitter */
fp << std::endl;
/* Instantiate wires for FPGA fabric outputs */
print_verilog_comment(fp, std::string("----- FPGA fabric outputs -------"));
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* We care only those logic blocks which are output I/Os */
if (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
/* Each logical block assumes a single-width port */
BasicPort output_port(std::string(block_name + fpga_output_port_postfix), 1);
fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, output_port) << ";" << std::endl;
}
/* Add an empty line as splitter */
fp << std::endl;
/* Benchmark is instanciated conditionally: only when a preprocessing flag is enable */
print_verilog_preprocessing_flag(fp, std::string(autocheck_preprocessing_flag));
/* Add an empty line as splitter */
fp << std::endl;
/* Instantiate wire for benchmark output */
print_verilog_comment(fp, std::string("----- Benchmark outputs -------"));
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* We care only those logic blocks which are output I/Os */
if (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
/* Each logical block assumes a single-width port */
BasicPort output_port(std::string(block_name + benchmark_output_port_postfix), 1);
fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, output_port) << ";" << std::endl;
}
/* Add an empty line as splitter */
fp << std::endl;
/* Instantiate register for output comparison */
print_verilog_comment(fp, std::string("----- Output vectors checking flags -------"));
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
/* We care only those logic blocks which are output I/Os */
if (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) {
continue;
}
/* The block may be renamed as it contains special characters which violate Verilog syntax */
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
/* Each logical block assumes a single-width port */
BasicPort output_port(std::string(block_name + check_flag_port_postfix), 1);
fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, output_port) << ";" << std::endl;
}
/* Add an empty line as splitter */
fp << std::endl;
/* Condition ends for the benchmark instanciation */
print_verilog_endif(fp);
/* Add an empty line as splitter */
fp << std::endl;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,93 @@
#ifndef VERILOG_TESTBENCH_UTILS_H
#define VERILOG_TESTBENCH_UTILS_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <fstream>
#include <string>
#include <vector>
#include "module_manager.h"
#include "vpr_context.h"
#include "io_location_map.h"
#include "vpr_netlist_annotation.h"
#include "simulation_setting.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void print_verilog_testbench_fpga_instance(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const std::string& top_instance_name);
void print_verilog_testbench_benchmark_instance(std::fstream& fp,
const std::string& module_name,
const std::string& instance_name,
const std::string& module_input_port_postfix,
const std::string& module_output_port_postfix,
const std::string& output_port_postfix,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const bool& use_explicit_port_map);
void print_verilog_testbench_connect_fpga_ios(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const AtomContext& atom_ctx,
const PlacementContext& place_ctx,
const IoLocationMap& io_location_map,
const VprNetlistAnnotation& netlist_annotation,
const std::string& io_input_port_name_postfix,
const std::string& io_output_port_name_postfix,
const size_t& unused_io_value);
void print_verilog_timeout_and_vcd(std::fstream& fp,
const std::string& icarus_preprocessing_flag,
const std::string& module_name,
const std::string& vcd_fname,
const std::string& simulation_start_counter_name,
const std::string& error_counter_name,
const int& simulation_time);
BasicPort generate_verilog_testbench_clock_port(const std::vector<std::string>& clock_port_names,
const std::string& default_clock_name);
void print_verilog_testbench_check(std::fstream& fp,
const std::string& autochecked_preprocessing_flag,
const std::string& simulation_start_counter_name,
const std::string& benchmark_port_postfix,
const std::string& fpga_port_postfix,
const std::string& check_flag_port_postfix,
const std::string& error_counter_name,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const std::vector<std::string>& clock_port_names,
const std::string& default_clock_name);
void print_verilog_testbench_clock_stimuli(std::fstream& fp,
const SimulationSetting& simulation_parameters,
const BasicPort& clock_port);
void print_verilog_testbench_random_stimuli(std::fstream& fp,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const std::string& check_flag_port_postfix,
const BasicPort& clock_port);
void print_verilog_testbench_shared_ports(std::fstream& fp,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const std::vector<std::string>& clock_port_names,
const std::string& benchmark_output_port_postfix,
const std::string& fpga_output_port_postfix,
const std::string& check_flag_port_postfix,
const std::string& autocheck_preprocessing_flag);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,899 @@
/********************************************************************
* This file includes functions that are used to create
* an auto-check top-level testbench for a FPGA fabric
*******************************************************************/
#include <fstream>
#include <iomanip>
#include <algorithm>
/* Headers from vtrutil library */
#include "vtr_log.h"
#include "vtr_assert.h"
#include "vtr_time.h"
/* Headers from openfpgautil library */
#include "openfpga_port.h"
#include "openfpga_digest.h"
#include "bitstream_manager_utils.h"
#include "openfpga_naming.h"
#include "simulation_utils.h"
#include "openfpga_atom_netlist_utils.h"
#include "verilog_constants.h"
#include "verilog_writer_utils.h"
#include "verilog_testbench_utils.h"
#include "verilog_top_testbench.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Local variables used only in this file
*******************************************************************/
constexpr char* TOP_TESTBENCH_REFERENCE_INSTANCE_NAME = "REF_DUT";
constexpr char* TOP_TESTBENCH_FPGA_INSTANCE_NAME = "FPGA_DUT";
constexpr char* TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX = "_benchmark";
constexpr char* TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX = "_fpga";
constexpr char* TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX = "_flag";
constexpr char* TOP_TESTBENCH_CC_PROG_TASK_NAME = "prog_cycle_task";
constexpr char* TOP_TESTBENCH_SIM_START_PORT_NAME = "sim_start";
constexpr int TOP_TESTBENCH_MAGIC_NUMBER_FOR_SIMULATION_TIME = 200;
constexpr char* TOP_TESTBENCH_ERROR_COUNTER = "nb_error";
constexpr char* TOP_TB_RESET_PORT_NAME = "greset";
constexpr char* TOP_TB_SET_PORT_NAME = "gset";
constexpr char* TOP_TB_PROG_RESET_PORT_NAME = "prog_reset";
constexpr char* TOP_TB_PROG_SET_PORT_NAME = "prog_set";
constexpr char* TOP_TB_CONFIG_DONE_PORT_NAME = "config_done";
constexpr char* TOP_TB_OP_CLOCK_PORT_NAME = "op_clock";
constexpr char* TOP_TB_PROG_CLOCK_PORT_NAME = "prog_clock";
constexpr char* TOP_TB_INOUT_REG_POSTFIX = "_reg";
constexpr char* TOP_TB_CLOCK_REG_POSTFIX = "_reg";
constexpr char* AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX = "_autocheck_top_tb";
/********************************************************************
* Print local wires for configuration chain protocols
*******************************************************************/
static
void print_verilog_top_testbench_config_chain_port(std::fstream& fp) {
/* Validate the file stream */
valid_file_stream(fp);
/* Print the head of configuraion-chains here */
print_verilog_comment(fp, std::string("---- Configuration-chain head -----"));
BasicPort config_chain_head_port(generate_configuration_chain_head_name(), 1);
fp << generate_verilog_port(VERILOG_PORT_REG, config_chain_head_port) << ";" << std::endl;
/* Print the tail of configuration-chains here */
print_verilog_comment(fp, std::string("---- Configuration-chain tail -----"));
BasicPort config_chain_tail_port(generate_configuration_chain_tail_name(), 1);
fp << generate_verilog_port(VERILOG_PORT_WIRE, config_chain_tail_port) << ";" << std::endl;
}
/********************************************************************
* Print local wires for different types of configuration protocols
*******************************************************************/
static
void print_verilog_top_testbench_config_protocol_port(std::fstream& fp,
const e_config_protocol_type& sram_orgz_type) {
switch(sram_orgz_type) {
case CONFIG_MEM_STANDALONE:
/* TODO */
break;
case CONFIG_MEM_SCAN_CHAIN:
print_verilog_top_testbench_config_chain_port(fp);
break;
case CONFIG_MEM_MEMORY_BANK:
/* TODO */
break;
default:
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid type of SRAM organization!\n");
exit(1);
}
}
/********************************************************************
* Wire the global ports of FPGA fabric to local wires
*******************************************************************/
static
void print_verilog_top_testbench_global_ports_stimuli(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const CircuitLibrary& circuit_lib,
const std::vector<CircuitPortId>& global_ports) {
/* Validate the file stream */
valid_file_stream(fp);
print_verilog_comment(fp, std::string("----- Begin connecting global ports of FPGA fabric to stimuli -----"));
/* Connect global clock ports to operating or programming clock signal */
for (const CircuitPortId& model_global_port : global_ports) {
if (CIRCUIT_MODEL_PORT_CLOCK != circuit_lib.port_type(model_global_port)) {
continue;
}
/* Reach here, it means we have a global clock to deal with:
* 1. if the port is identified as a programming clock,
* connect it to the local wire of programming clock
* 2. if the port is identified as an operating clock
* connect it to the local wire of operating clock
*/
/* Find the module port */
ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port));
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
BasicPort stimuli_clock_port;
if (true == circuit_lib.port_is_prog(model_global_port)) {
stimuli_clock_port.set_name(std::string(TOP_TB_PROG_CLOCK_PORT_NAME));
stimuli_clock_port.set_width(1);
} else {
VTR_ASSERT_SAFE(false == circuit_lib.port_is_prog(model_global_port));
stimuli_clock_port.set_name(std::string(TOP_TB_OP_CLOCK_PORT_NAME));
stimuli_clock_port.set_width(1);
}
/* Wire the port to the input stimuli:
* The wiring will be inverted if the default value of the global port is 1
* Otherwise, the wiring will not be inverted!
*/
print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port),
stimuli_clock_port,
1 == circuit_lib.port_default_value(model_global_port));
}
/* Connect global configuration done ports to configuration done signal */
for (const CircuitPortId& model_global_port : global_ports) {
/* Bypass clock signals, they have been processed */
if (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) {
continue;
}
if (false == circuit_lib.port_is_config_enable(model_global_port)) {
continue;
}
/* Reach here, it means we have a configuration done port to deal with */
/* Find the module port */
ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port));
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
BasicPort stimuli_config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1);
/* Wire the port to the input stimuli:
* The wiring will be inverted if the default value of the global port is 1
* Otherwise, the wiring will not be inverted!
*/
print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port),
stimuli_config_done_port,
1 == circuit_lib.port_default_value(model_global_port));
}
/* Connect global reset ports to operating or programming reset signal */
for (const CircuitPortId& model_global_port : global_ports) {
/* Bypass clock signals, they have been processed */
if (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) {
continue;
}
/* Bypass config_done signals, they have been processed */
if (true == circuit_lib.port_is_config_enable(model_global_port)) {
continue;
}
if (false == circuit_lib.port_is_reset(model_global_port)) {
continue;
}
/* Reach here, it means we have a reset port to deal with */
/* Find the module port */
ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port));
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
BasicPort stimuli_reset_port;
if (true == circuit_lib.port_is_prog(model_global_port)) {
stimuli_reset_port.set_name(std::string(TOP_TB_PROG_RESET_PORT_NAME));
stimuli_reset_port.set_width(1);
} else {
VTR_ASSERT_SAFE(false == circuit_lib.port_is_prog(model_global_port));
stimuli_reset_port.set_name(std::string(TOP_TB_RESET_PORT_NAME));
stimuli_reset_port.set_width(1);
}
/* Wire the port to the input stimuli:
* The wiring will be inverted if the default value of the global port is 1
* Otherwise, the wiring will not be inverted!
*/
print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port),
stimuli_reset_port,
1 == circuit_lib.port_default_value(model_global_port));
}
/* Connect global set ports to operating or programming set signal */
for (const CircuitPortId& model_global_port : global_ports) {
/* Bypass clock signals, they have been processed */
if (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) {
continue;
}
/* Bypass config_done signals, they have been processed */
if (true == circuit_lib.port_is_config_enable(model_global_port)) {
continue;
}
/* Bypass reset signals, they have been processed */
if (true == circuit_lib.port_is_reset(model_global_port)) {
continue;
}
if (false == circuit_lib.port_is_set(model_global_port)) {
continue;
}
/* Reach here, it means we have a set port to deal with */
/* Find the module port */
ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port));
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
BasicPort stimuli_set_port;
if (true == circuit_lib.port_is_prog(model_global_port)) {
stimuli_set_port.set_name(std::string(TOP_TB_PROG_SET_PORT_NAME));
stimuli_set_port.set_width(1);
} else {
VTR_ASSERT_SAFE(false == circuit_lib.port_is_prog(model_global_port));
stimuli_set_port.set_name(std::string(TOP_TB_SET_PORT_NAME));
stimuli_set_port.set_width(1);
}
/* Wire the port to the input stimuli:
* The wiring will be inverted if the default value of the global port is 1
* Otherwise, the wiring will not be inverted!
*/
print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port),
stimuli_set_port,
1 == circuit_lib.port_default_value(model_global_port));
}
/* For the rest of global ports, wire them to constant signals */
for (const CircuitPortId& model_global_port : global_ports) {
/* Bypass clock signals, they have been processed */
if (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) {
continue;
}
/* Bypass config_done signals, they have been processed */
if (true == circuit_lib.port_is_config_enable(model_global_port)) {
continue;
}
/* Bypass reset signals, they have been processed */
if (true == circuit_lib.port_is_reset(model_global_port)) {
continue;
}
/* Bypass set signals, they have been processed */
if (true == circuit_lib.port_is_set(model_global_port)) {
continue;
}
/* Reach here, it means we have a port to deal with */
/* Find the module port and wire it to constant values */
ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port));
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
BasicPort module_port = module_manager.module_port(top_module, module_global_port);
std::vector<size_t> default_values(module_port.get_width(), circuit_lib.port_default_value(model_global_port));
print_verilog_wire_constant_values(fp, module_port, default_values);
}
print_verilog_comment(fp, std::string("----- End connecting global ports of FPGA fabric to stimuli -----"));
}
/********************************************************************
* This function prints the top testbench module declaration
* and internal wires/port declaration
* Ports can be classified in two categories:
* 1. General-purpose ports, which are datapath I/Os, clock signals
* for the FPGA fabric and input benchmark
* 2. Fabric-featured ports, which are required by configuration
* protocols.
* Due the difference in configuration protocols, the internal
* wires and ports will be different:
* (a) configuration-chain: we will have two ports,
* a head and a tail for the configuration chain,
* in addition to the regular ports.
* (b) memory-decoders: we will have a few ports to drive
* address lines for decoders and a bit input port to feed
* configuration bits
*******************************************************************/
static
void print_verilog_top_testbench_ports(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& top_module,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation,
const std::vector<std::string>& clock_port_names,
const e_config_protocol_type& sram_orgz_type,
const std::string& circuit_name){
/* Validate the file stream */
valid_file_stream(fp);
/* Print module definition */
fp << "module " << circuit_name << std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX);
fp << ";" << std::endl;
/* Print regular local wires:
* 1. global ports, i.e., reset, set and clock signals
* 2. datapath I/O signals
*/
/* Global ports of top-level module */
print_verilog_comment(fp, std::string("----- Local wires for global ports of FPGA fabric -----"));
for (const BasicPort& module_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GLOBAL_PORT)) {
fp << generate_verilog_port(VERILOG_PORT_WIRE, module_port) << ";" << std::endl;
}
/* Add an empty line as a splitter */
fp << std::endl;
/* Datapath I/Os of top-level module */
print_verilog_comment(fp, std::string("----- Local wires for I/Os of FPGA fabric -----"));
for (const BasicPort& module_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT)) {
fp << generate_verilog_port(VERILOG_PORT_WIRE, module_port) << ";" << std::endl;
}
/* Add an empty line as a splitter */
fp << std::endl;
/* Add local wires/registers that drive stimulus
* We create these general purpose ports here,
* and then wire them to the ports of FPGA fabric depending on their usage
*/
/* Configuration done port */
BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1);
fp << generate_verilog_port(VERILOG_PORT_REG, config_done_port) << ";" << std::endl;
/* Programming clock */
BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1);
fp << generate_verilog_port(VERILOG_PORT_WIRE, prog_clock_port) << ";" << std::endl;
BasicPort prog_clock_register_port(std::string(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1);
fp << generate_verilog_port(VERILOG_PORT_REG, prog_clock_register_port) << ";" << std::endl;
/* Operating clock */
BasicPort op_clock_port(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1);
fp << generate_verilog_port(VERILOG_PORT_WIRE, op_clock_port) << ";" << std::endl;
BasicPort op_clock_register_port(std::string(std::string(TOP_TB_OP_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1);
fp << generate_verilog_port(VERILOG_PORT_REG, op_clock_register_port) << ";" << std::endl;
/* Programming set and reset */
BasicPort prog_reset_port(std::string(TOP_TB_PROG_RESET_PORT_NAME), 1);
fp << generate_verilog_port(VERILOG_PORT_REG, prog_reset_port) << ";" << std::endl;
BasicPort prog_set_port(std::string(TOP_TB_PROG_SET_PORT_NAME), 1);
fp << generate_verilog_port(VERILOG_PORT_REG, prog_set_port) << ";" << std::endl;
/* Global set and reset */
BasicPort reset_port(std::string(TOP_TB_RESET_PORT_NAME), 1);
fp << generate_verilog_port(VERILOG_PORT_REG, reset_port) << ";" << std::endl;
BasicPort set_port(std::string(TOP_TB_SET_PORT_NAME), 1);
fp << generate_verilog_port(VERILOG_PORT_REG, set_port) << ";" << std::endl;
/* Configuration ports depend on the organization of SRAMs */
print_verilog_top_testbench_config_protocol_port(fp, sram_orgz_type);
/* Create a clock port if the benchmark have one but not in the default name!
* We will wire the clock directly to the operating clock directly
*/
for (const std::string clock_port_name : clock_port_names) {
if (0 == clock_port_name.compare(op_clock_port.get_name())) {
continue;
}
/* Ensure the clock port name is not a duplication of global ports of the FPGA module */
bool print_clock_port = true;
for (const BasicPort& module_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GLOBAL_PORT)) {
if (0 == clock_port_name.compare(module_port.get_name())) {
print_clock_port = false;
}
}
if (false == print_clock_port) {
continue;
}
/* Print the clock and wire it to op_clock */
print_verilog_comment(fp, std::string("----- Create a clock for benchmark and wire it to op_clock -------"));
BasicPort clock_port(clock_port_name, 1);
fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, clock_port) << ";" << std::endl;
print_verilog_wire_connection(fp, clock_port, op_clock_port, false);
}
print_verilog_testbench_shared_ports(fp, atom_ctx, netlist_annotation,
clock_port_names,
std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX),
std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX),
std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX),
std::string(AUTOCHECKED_SIMULATION_FLAG));
/* Instantiate an integer to count the number of error and
* determine if the simulation succeed or failed
*/
print_verilog_comment(fp, std::string("----- Error counter -----"));
fp << "\tinteger " << TOP_TESTBENCH_ERROR_COUNTER << "= 0;" << std::endl;
}
/********************************************************************
* Instanciate the input benchmark module
*******************************************************************/
static
void print_verilog_top_testbench_benchmark_instance(std::fstream& fp,
const std::string& reference_verilog_top_name,
const AtomContext& atom_ctx,
const VprNetlistAnnotation& netlist_annotation) {
/* Validate the file stream */
valid_file_stream(fp);
/* Benchmark is instanciated conditionally: only when a preprocessing flag is enable */
print_verilog_preprocessing_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG));
print_verilog_comment(fp, std::string("----- Reference Benchmark Instanication -------"));
/* Do NOT use explicit port mapping here:
* VPR added a prefix of "out_" to the output ports of input benchmark
*/
print_verilog_testbench_benchmark_instance(fp, reference_verilog_top_name,
std::string(TOP_TESTBENCH_REFERENCE_INSTANCE_NAME),
std::string(),
std::string(),
std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX),
atom_ctx, netlist_annotation,
false);
print_verilog_comment(fp, std::string("----- End reference Benchmark Instanication -------"));
/* Add an empty line as splitter */
fp << std::endl;
/* Condition ends for the benchmark instanciation */
print_verilog_endif(fp);
/* Add an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Print tasks (processes) in Verilog format,
* which is very useful in generating stimuli for each clock cycle
* This function is tuned for configuration-chain manipulation:
* During each programming cycle, we feed the input of scan chain with a memory bit
*******************************************************************/
static
void print_verilog_top_testbench_load_bitstream_task_configuration_chain(std::fstream& fp) {
/* Validate the file stream */
valid_file_stream(fp);
BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1);
BasicPort cc_head_port(generate_configuration_chain_head_name(), 1);
BasicPort cc_head_value(generate_configuration_chain_head_name() + std::string("_val"), 1);
/* Add an empty line as splitter */
fp << std::endl;
/* Feed the scan-chain input at each falling edge of programming clock
* It aims at avoid racing the programming clock (scan-chain data changes at the rising edge).
*/
print_verilog_comment(fp, std::string("----- Task: input values during a programming clock cycle -----"));
fp << "task " << std::string(TOP_TESTBENCH_CC_PROG_TASK_NAME) << ";" << std::endl;
fp << generate_verilog_port(VERILOG_PORT_INPUT, cc_head_value) << ";" << std::endl;
fp << "\tbegin" << std::endl;
fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl;
fp << "\t\t\t";
fp << generate_verilog_port(VERILOG_PORT_CONKT, cc_head_port);
fp << " = ";
fp << generate_verilog_port(VERILOG_PORT_CONKT, cc_head_value);
fp << ";" << std::endl;
fp << "\tend" << std::endl;
fp << "endtask" << std::endl;
/* Add an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Print tasks, which is very useful in generating stimuli for each clock cycle
*******************************************************************/
static
void print_verilog_top_testbench_load_bitstream_task(std::fstream& fp,
const e_config_protocol_type& sram_orgz_type) {
switch (sram_orgz_type) {
case CONFIG_MEM_STANDALONE:
break;
case CONFIG_MEM_SCAN_CHAIN:
print_verilog_top_testbench_load_bitstream_task_configuration_chain(fp);
break;
case CONFIG_MEM_MEMORY_BANK:
/* TODO:
dump_verilog_top_testbench_stimuli_serial_version_tasks_memory_bank(cur_sram_orgz_info, fp);
*/
break;
default:
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid type of SRAM organization!\n");
exit(1);
}
}
/********************************************************************
* Print generatic input stimuli for the top testbench
* include:
* 1. configuration done signal
* 2. programming clock
* 3. operating clock
* 4. programming reset signal
* 5. programming set signal
* 6. reset signal
* 7. set signal
*******************************************************************/
static
void print_verilog_top_testbench_generic_stimulus(std::fstream& fp,
const size_t& num_config_clock_cycles,
const float& prog_clock_period,
const float& op_clock_period,
const float& timescale) {
/* Validate the file stream */
valid_file_stream(fp);
print_verilog_comment(fp, std::string("----- Number of clock cycles in configuration phase: " + std::to_string(num_config_clock_cycles) + " -----"));
BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1);
BasicPort op_clock_port(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1);
BasicPort op_clock_register_port(std::string(std::string(TOP_TB_OP_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1);
BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1);
BasicPort prog_clock_register_port(std::string(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1);
BasicPort prog_reset_port(std::string(TOP_TB_PROG_RESET_PORT_NAME), 1);
BasicPort prog_set_port(std::string(TOP_TB_PROG_SET_PORT_NAME), 1);
BasicPort reset_port(std::string(TOP_TB_RESET_PORT_NAME), 1);
BasicPort set_port(std::string(TOP_TB_SET_PORT_NAME), 1);
/* Generate stimuli waveform for configuration done signals */
print_verilog_comment(fp, "----- Begin configuration done signal generation -----");
print_verilog_pulse_stimuli(fp, config_done_port,
0, /* Initial value */
num_config_clock_cycles * prog_clock_period / timescale, 0);
print_verilog_comment(fp, "----- End configuration done signal generation -----");
fp << std::endl;
/* Generate stimuli waveform for programming clock signals */
print_verilog_comment(fp, "----- Begin raw programming clock signal generation -----");
print_verilog_clock_stimuli(fp, prog_clock_register_port,
0, /* Initial value */
0.5 * prog_clock_period / timescale,
std::string());
print_verilog_comment(fp, "----- End raw programming clock signal generation -----");
fp << std::endl;
/* Programming clock should be only enabled during programming phase.
* When configuration is done (config_done is enabled), programming clock should be always zero.
*/
print_verilog_comment(fp, std::string("----- Actual programming clock is triggered only when " + config_done_port.get_name() + " and " + prog_reset_port.get_name() + " are disabled -----"));
fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port);
fp << " = " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_register_port);
fp << " & (~" << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port) << ")";
fp << " & (~" << generate_verilog_port(VERILOG_PORT_CONKT, prog_reset_port) << ")";
fp << ";" << std::endl;
fp << std::endl;
/* Generate stimuli waveform for operating clock signals */
print_verilog_comment(fp, "----- Begin raw operating clock signal generation -----");
print_verilog_clock_stimuli(fp, op_clock_register_port,
0, /* Initial value */
0.5 * op_clock_period / timescale,
std::string("~" + reset_port.get_name()));
print_verilog_comment(fp, "----- End raw operating clock signal generation -----");
/* Operation clock should be enabled after programming phase finishes.
* Before configuration is done (config_done is enabled), operation clock should be always zero.
*/
print_verilog_comment(fp, std::string("----- Actual operating clock is triggered only when " + config_done_port.get_name() + " is enabled -----"));
fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, op_clock_port);
fp << " = " << generate_verilog_port(VERILOG_PORT_CONKT, op_clock_register_port);
fp << " & " << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port);
fp << ";" << std::endl;
fp << std::endl;
/* Reset signal for configuration circuit:
* only enable during the first clock cycle in programming phase
*/
print_verilog_comment(fp, "----- Begin programming reset signal generation -----");
print_verilog_pulse_stimuli(fp, prog_reset_port,
1, /* Initial value */
prog_clock_period / timescale, 0);
print_verilog_comment(fp, "----- End programming reset signal generation -----");
fp << std::endl;
/* Programming set signal for configuration circuit : always disabled */
print_verilog_comment(fp, "----- Begin programming set signal generation: always disabled -----");
print_verilog_pulse_stimuli(fp, prog_set_port,
0, /* Initial value */
prog_clock_period / timescale, 0);
print_verilog_comment(fp, "----- End programming set signal generation: always disabled -----");
fp << std::endl;
/* Operating reset signals: only enabled during the first clock cycle in operation phase */
std::vector<float> reset_pulse_widths;
reset_pulse_widths.push_back(op_clock_period / timescale);
reset_pulse_widths.push_back(2 * op_clock_period / timescale);
std::vector<size_t> reset_flip_values;
reset_flip_values.push_back(1);
reset_flip_values.push_back(0);
print_verilog_comment(fp, "----- Begin operating reset signal generation -----");
print_verilog_comment(fp, "----- Reset signal is enabled until the first clock cycle in operation phase -----");
print_verilog_pulse_stimuli(fp, reset_port,
1,
reset_pulse_widths,
reset_flip_values,
config_done_port.get_name());
print_verilog_comment(fp, "----- End operating reset signal generation -----");
/* Operating set signal for configuration circuit : always disabled */
print_verilog_comment(fp, "----- Begin operating set signal generation: always disabled -----");
print_verilog_pulse_stimuli(fp, set_port,
0, /* Initial value */
op_clock_period / timescale, 0);
print_verilog_comment(fp, "----- End operating set signal generation: always disabled -----");
fp << std::endl;
}
/********************************************************************
* Print stimulus for a FPGA fabric with a configuration chain protocol
* where configuration bits are programming in serial (one by one)
* Task list:
* 1. For clock signal, we should create voltage waveforms for two types of clock signals:
* a. operation clock
* b. programming clock
* 2. For Set/Reset, we reset the chip after programming phase ends
* and before operation phase starts
* 3. For input/output clb nets (mapped to I/O grids),
* we should create voltage waveforms only after programming phase
*******************************************************************/
static
void print_verilog_top_testbench_configuration_chain_bitstream(std::fstream& fp,
const BitstreamManager& bitstream_manager,
const std::vector<ConfigBitId>& fabric_bitstream) {
/* Validate the file stream */
valid_file_stream(fp);
/* Initial value should be the first configuration bits
* In the rest of programming cycles,
* configuration bits are fed at the falling edge of programming clock.
* We do not care the value of scan_chain head during the first programming cycle
* It is reset anyway
*/
BasicPort config_chain_head_port(generate_configuration_chain_head_name(), 1);
std::vector<size_t> initial_values(config_chain_head_port.get_width(), 0);
print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----");
fp << "initial" << std::endl;
fp << "\tbegin" << std::endl;
print_verilog_comment(fp, "----- Configuration chain default input -----");
fp << "\t\t";
fp << generate_verilog_port_constant_values(config_chain_head_port, initial_values);
fp << ";";
fp << std::endl;
/* Attention: the configuration chain protcol requires the last configuration bit is fed first
* We will visit the fabric bitstream in a reverse way
*/
std::vector<ConfigBitId> cc_bitstream = fabric_bitstream;
std::reverse(cc_bitstream.begin(), cc_bitstream.end());
for (const ConfigBitId& bit_id : cc_bitstream) {
fp << "\t\t" << std::string(TOP_TESTBENCH_CC_PROG_TASK_NAME);
fp << "(1'b" << (size_t)bitstream_manager.bit_value(bit_id) << ");" << std::endl;
}
/* Raise the flag of configuration done when bitstream loading is complete */
BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1);
fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl;
BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1);
fp << "\t\t\t";
fp << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port);
fp << " <= ";
std::vector<size_t> config_done_enable_values(config_done_port.get_width(), 1);
fp << generate_verilog_constant_values(config_done_enable_values);
fp << ";" << std::endl;
fp << "\tend" << std::endl;
print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----");
}
/********************************************************************
* Generate the stimuli for the top-level testbench
* The simulation consists of two phases: configuration phase and operation phase
* Configuration bits are loaded serially.
* This is actually what we do for a physical FPGA
*******************************************************************/
static
void print_verilog_top_testbench_bitstream(std::fstream& fp,
const e_config_protocol_type& sram_orgz_type,
const BitstreamManager& bitstream_manager,
const std::vector<ConfigBitId>& fabric_bitstream) {
/* Branch on the type of configuration protocol */
switch (sram_orgz_type) {
case CONFIG_MEM_STANDALONE:
/* TODO */
break;
case CONFIG_MEM_SCAN_CHAIN:
print_verilog_top_testbench_configuration_chain_bitstream(fp, bitstream_manager, fabric_bitstream);
break;
case CONFIG_MEM_MEMORY_BANK:
/* TODO */
break;
default:
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid SRAM organization type!\n");
exit(1);
}
}
/********************************************************************
* The top-level function to generate a testbench, in order to verify:
* 1. Configuration phase of the FPGA fabric, where the bitstream is
* loaded to the configuration protocol of the FPGA fabric
* 2. Operating phase of the FPGA fabric, where input stimuli are
* fed to the I/Os of the FPGA fabric
* +----------+
* | FPGA | +------------+
* +----->| Fabric |------>| |
* | | | | |
* | +----------+ | |
* | | Output |
* random_input_vectors -----+ | Vector |---->Functional correct?
* | | Comparator |
* | +-----------+ | |
* | | Input | | |
* +----->| Benchmark |----->| |
* +-----------+ +------------+
*
*******************************************************************/
void print_verilog_top_testbench(const ModuleManager& module_manager,
const BitstreamManager& bitstream_manager,
const std::vector<ConfigBitId>& fabric_bitstream,
const e_config_protocol_type& sram_orgz_type,
const CircuitLibrary& circuit_lib,
const std::vector<CircuitPortId>& global_ports,
const AtomContext& atom_ctx,
const PlacementContext& place_ctx,
const IoLocationMap& io_location_map,
const VprNetlistAnnotation& netlist_annotation,
const std::string& circuit_name,
const std::string& verilog_fname,
const std::string& verilog_dir,
const SimulationSetting& simulation_parameters) {
std::string timer_message = std::string("Write autocheck testbench for FPGA top-level Verilog netlist for '") + circuit_name + std::string("'");
/* Start time count */
vtr::ScopedStartFinishTimer timer(timer_message);
/* 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);
/* Generate a brief description on the Verilog file*/
std::string title = std::string("FPGA Verilog Testbench for Top-level netlist of Design: ") + circuit_name;
print_verilog_file_header(fp, title);
/* Print preprocessing flags and external netlists */
print_verilog_include_defines_preproc_file(fp, verilog_dir);
/* Find the top_module */
ModuleId top_module = module_manager.find_module(generate_fpga_top_module_name());
VTR_ASSERT(true == module_manager.valid_module_id(top_module));
/* Preparation: find all the clock ports */
std::vector<std::string> clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation);
/* Start of testbench */
print_verilog_top_testbench_ports(fp, module_manager, top_module,
atom_ctx, netlist_annotation, clock_port_names,
sram_orgz_type, circuit_name);
/* Find the clock period */
float prog_clock_period = (1./simulation_parameters.programming_clock_frequency());
float op_clock_period = (1./simulation_parameters.operating_clock_frequency());
/* Estimate the number of configuration clock cycles
* by traversing the linked-list and count the number of SRAM=1 or BL=1&WL=1 in it.
* We plus 1 additional config clock cycle here because we need to reset everything during the first clock cycle
*/
size_t num_config_clock_cycles = 1 + fabric_bitstream.size();
/* Generate stimuli for general control signals */
print_verilog_top_testbench_generic_stimulus(fp,
num_config_clock_cycles,
prog_clock_period,
op_clock_period,
VERILOG_SIM_TIMESCALE);
/* Generate stimuli for global ports or connect them to existed signals */
print_verilog_top_testbench_global_ports_stimuli(fp,
module_manager, top_module,
circuit_lib, global_ports);
/* Instanciate FPGA top-level module */
print_verilog_testbench_fpga_instance(fp, module_manager, top_module,
std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME));
/* Connect I/Os to benchmark I/Os or constant driver */
print_verilog_testbench_connect_fpga_ios(fp, module_manager, top_module,
atom_ctx, place_ctx, io_location_map,
netlist_annotation,
std::string(),
std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX),
(size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE);
/* Instanciate input benchmark */
print_verilog_top_testbench_benchmark_instance(fp,
circuit_name,
atom_ctx,
netlist_annotation);
/* Print tasks used for loading bitstreams */
print_verilog_top_testbench_load_bitstream_task(fp, sram_orgz_type);
/* load bitstream to FPGA fabric in a configuration phase */
print_verilog_top_testbench_bitstream(fp, sram_orgz_type,
bitstream_manager, fabric_bitstream);
/* Add stimuli for reset, set, clock and iopad signals */
print_verilog_testbench_random_stimuli(fp, atom_ctx,
netlist_annotation,
std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX),
BasicPort(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1));
/* Add output autocheck */
print_verilog_testbench_check(fp,
std::string(AUTOCHECKED_SIMULATION_FLAG),
std::string(TOP_TESTBENCH_SIM_START_PORT_NAME),
std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX),
std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX),
std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX),
std::string(TOP_TESTBENCH_ERROR_COUNTER),
atom_ctx,
netlist_annotation,
clock_port_names,
std::string(TOP_TB_OP_CLOCK_PORT_NAME));
/* Find simulation time */
float simulation_time = find_simulation_time_period(VERILOG_SIM_TIMESCALE,
num_config_clock_cycles,
1./simulation_parameters.programming_clock_frequency(),
simulation_parameters.num_clock_cycles(),
1./simulation_parameters.operating_clock_frequency());
/* Add Icarus requirement */
print_verilog_timeout_and_vcd(fp,
std::string(ICARUS_SIMULATOR_FLAG),
std::string(circuit_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX)),
std::string(circuit_name + std::string("_formal.vcd")),
std::string(TOP_TESTBENCH_SIM_START_PORT_NAME),
std::string(TOP_TESTBENCH_ERROR_COUNTER),
(int)simulation_time);
/* Testbench ends*/
print_verilog_module_end(fp, std::string(circuit_name) + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX));
/* Close the file stream */
fp.close();
}
} /* end namespace openfpga */

View File

@ -0,0 +1,41 @@
#ifndef VERILOG_TOP_TESTBENCH
#define VERILOG_TOP_TESTBENCH
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include <vector>
#include "module_manager.h"
#include "bitstream_manager.h"
#include "circuit_library.h"
#include "vpr_context.h"
#include "io_location_map.h"
#include "vpr_netlist_annotation.h"
#include "simulation_setting.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
void print_verilog_top_testbench(const ModuleManager& module_manager,
const BitstreamManager& bitstream_manager,
const std::vector<ConfigBitId>& fabric_bitstream,
const e_config_protocol_type& sram_orgz_type,
const CircuitLibrary& circuit_lib,
const std::vector<CircuitPortId>& global_ports,
const AtomContext& atom_ctx,
const PlacementContext& place_ctx,
const IoLocationMap& io_location_map,
const VprNetlistAnnotation& netlist_annotation,
const std::string& circuit_name,
const std::string& verilog_fname,
const std::string& verilog_dir,
const SimulationSetting& simulation_parameters);
} /* end namespace openfpga */
#endif

View File

@ -14,6 +14,7 @@
#include <string> #include <string>
#include "openfpga_port.h" #include "openfpga_port.h"
#include "verilog_port_types.h" #include "verilog_port_types.h"
#include "circuit_library.h"
#include "module_manager.h" #include "module_manager.h"
/******************************************************************** /********************************************************************

View File

@ -0,0 +1,41 @@
/***************************************************************************************
* This file includes most utilized functions that are used to acquire data from
* VPR atom netlist (users' netlist to implement)
***************************************************************************************/
/* Headers from vtrutil library */
#include "vtr_log.h"
#include "vtr_assert.h"
#include "vtr_time.h"
/* Headers from vtrutil library */
#include "atom_netlist_utils.h"
#include "openfpga_atom_netlist_utils.h"
/* begin namespace openfpga */
namespace openfpga {
/***************************************************************************************
* Find the names of all the atom blocks that drive clock nets
* This function will find if the block has been renamed due to contain sensitive characters
* that violates the Verilog syntax
***************************************************************************************/
std::vector<std::string> find_atom_netlist_clock_port_names(const AtomNetlist& atom_nlist,
const VprNetlistAnnotation& netlist_annotation) {
std::vector<std::string> clock_names;
std::set<AtomPinId> clock_pins = find_netlist_logical_clock_drivers(atom_nlist);
for (const AtomPinId& clock_pin : clock_pins) {
const AtomBlockId& atom_blk = atom_nlist.port_block(atom_nlist.pin_port(clock_pin));
std::string block_name = atom_nlist.block_name(atom_blk);
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
block_name = netlist_annotation.block_name(atom_blk);
}
clock_names.push_back(block_name);
}
return clock_names;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,24 @@
#ifndef OPENFPGA_ATOM_NETLIST_UTILS_H
#define OPENFPGA_ATOM_NETLIST_UTILS_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <vector>
#include <string>
#include "atom_netlist.h"
#include "vpr_netlist_annotation.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
std::vector<std::string> find_atom_netlist_clock_port_names(const AtomNetlist& atom_nlist,
const VprNetlistAnnotation& netlist_annotation);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,47 @@
/********************************************************************
* This file include most utilized functions in generating simulations
* Note: function placed here MUST be generic enough for both SPICE
* and Verilog simulations!
*******************************************************************/
#include <cmath>
#include "simulation_utils.h"
/* begin namespace openfpga */
namespace openfpga {
/********************************************************************
* Compute the time period for the simulation
*******************************************************************/
int find_operating_phase_simulation_time(const int& factor,
const int& num_op_clock_cycles,
const float& op_clock_period,
const float& timescale) {
/* Take into account the prog_reset and reset cycles
* 1e9 is to change the unit to ns rather than second
*/
return (factor * num_op_clock_cycles * op_clock_period) / timescale;
}
/********************************************************************
* Find the the full time period of a simulation, including
* both the programming time and operating time
* This is a generic function that can be used to generate simulation
* time period for SPICE/Verilog simulators
*******************************************************************/
float find_simulation_time_period(const float &time_unit,
const int &num_prog_clock_cycles,
const float &prog_clock_period,
const int &num_op_clock_cycles,
const float &op_clock_period) {
float total_time_period = 0.;
/* Take into account the prog_reset and reset cycles */
total_time_period = (num_prog_clock_cycles + 2) * prog_clock_period + num_op_clock_cycles * op_clock_period;
total_time_period = total_time_period / time_unit;
return total_time_period;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,28 @@
#ifndef SIMULATION_UTILS_H
#define SIMULATION_UTILS_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
int find_operating_phase_simulation_time(const int& factor,
const int& num_op_clock_cycles,
const float& op_clock_period,
const float& timescale);
float find_simulation_time_period(const float& time_unit,
const int& num_prog_clock_cycles,
const float& prog_clock_period,
const int& num_op_clock_cycles,
const float& op_clock_period);
} /* end namespace openfpga */
#endif

View File

@ -27,15 +27,18 @@ build_fabric --compress_routing --duplicate_grid_pin #--verbose
# Repack the netlist to physical pbs # Repack the netlist to physical pbs
# This must be done before bitstream generator and testbench generation # This must be done before bitstream generator and testbench generation
# Strongly recommend it is done after all the fix-up have been applied # Strongly recommend it is done after all the fix-up have been applied
repack --verbose repack #--verbose
# Build the bitstream # Build the bitstream
# - Output the fabric-independent bitstream to a file # - Output the fabric-independent bitstream to a file
fpga_bitstream --verbose --file /var/tmp/xtang/openfpga_test_src/fabric_indepenent_bitstream.xml fpga_bitstream --verbose --file /var/tmp/xtang/openfpga_test_src/fabric_indepenent_bitstream.xml
# Write the Verilog netlit for FPGA fabric # Write the Verilog netlist for FPGA fabric
# - Enable the use of explicit port mapping in Verilog netlist # - 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 --print_user_defined_template --verbose write_fabric_verilog --file /var/tmp/xtang/openfpga_test_src --explicit_port_mapping --include_timing --include_signal_init --support_icarus_simulator --print_user_defined_template --verbose
# Write the Verilog testbench for FPGA fabric
write_verilog_testbench --file /var/tmp/xtang/openfpga_test_src --reference_benchmark_file_path /var/tmp/xtang/s298.v --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini /var/tmp/xtang/openfpga_test_src/simulation_deck.ini
# Finish and exit OpenFPGA # Finish and exit OpenFPGA
exit exit

View File

@ -479,7 +479,6 @@ void print_verilog_testbench_shared_ports(std::fstream& fp,
/* Validate the file stream */ /* Validate the file stream */
check_file_handler(fp); check_file_handler(fp);
/* Instantiate register for inputs stimulis */ /* Instantiate register for inputs stimulis */
print_verilog_comment(fp, std::string("----- Shared inputs -------")); print_verilog_comment(fp, std::string("----- Shared inputs -------"));
for (const t_logical_block& lb : L_logical_blocks) { for (const t_logical_block& lb : L_logical_blocks) {