[Tool] Add a new command write_full_testbench which outputs self-testable full testbench which loads external bitstream file; Currently only support configuration chain without fast configuration technique
This commit is contained in:
parent
537b73ba6e
commit
ae6a46cd60
|
@ -119,4 +119,54 @@ int write_verilog_testbench(OpenfpgaContext& openfpga_ctx,
|
||||||
options);
|
options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* A wrapper function to call the full testbench generator of FPGA-Verilog
|
||||||
|
*******************************************************************/
|
||||||
|
int write_full_testbench(OpenfpgaContext& openfpga_ctx,
|
||||||
|
const Command& cmd, const CommandContext& cmd_context) {
|
||||||
|
|
||||||
|
CommandOptionId opt_output_dir = cmd.option("file");
|
||||||
|
CommandOptionId opt_bitstream = cmd.option("bitstream");
|
||||||
|
CommandOptionId opt_fabric_netlist = cmd.option("fabric_netlist_file_path");
|
||||||
|
CommandOptionId opt_pcf = cmd.option("pin_constraints_file");
|
||||||
|
CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path");
|
||||||
|
CommandOptionId opt_explicit_port_mapping = cmd.option("explicit_port_mapping");
|
||||||
|
CommandOptionId opt_include_signal_init = cmd.option("include_signal_init");
|
||||||
|
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_fabric_netlist_file_path(cmd_context.option_value(cmd, opt_fabric_netlist));
|
||||||
|
options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark));
|
||||||
|
options.set_explicit_port_mapping(cmd_context.option_enable(cmd, opt_explicit_port_mapping));
|
||||||
|
options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose));
|
||||||
|
options.set_print_top_testbench(true);
|
||||||
|
options.set_include_signal_init(cmd_context.option_enable(cmd, opt_include_signal_init));
|
||||||
|
|
||||||
|
/* If pin constraints are enabled by command options, read the file */
|
||||||
|
PinConstraints pin_constraints;
|
||||||
|
if (true == cmd_context.option_enable(cmd, opt_pcf)) {
|
||||||
|
pin_constraints = read_xml_pin_constraints(cmd_context.option_value(cmd, opt_pcf).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return fpga_verilog_full_testbench(openfpga_ctx.module_graph(),
|
||||||
|
openfpga_ctx.bitstream_manager(),
|
||||||
|
openfpga_ctx.fabric_bitstream(),
|
||||||
|
g_vpr_ctx.atom(),
|
||||||
|
g_vpr_ctx.placement(),
|
||||||
|
pin_constraints,
|
||||||
|
cmd_context.option_value(cmd, opt_bitstream),
|
||||||
|
openfpga_ctx.io_location_map(),
|
||||||
|
openfpga_ctx.fabric_global_port_info(),
|
||||||
|
openfpga_ctx.vpr_netlist_annotation(),
|
||||||
|
openfpga_ctx.arch().circuit_lib,
|
||||||
|
openfpga_ctx.simulation_setting(),
|
||||||
|
openfpga_ctx.arch().config_protocol,
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -21,6 +21,9 @@ int write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
|
||||||
int write_verilog_testbench(OpenfpgaContext& openfpga_ctx,
|
int write_verilog_testbench(OpenfpgaContext& openfpga_ctx,
|
||||||
const Command& cmd, const CommandContext& cmd_context);
|
const Command& cmd, const CommandContext& cmd_context);
|
||||||
|
|
||||||
|
int write_full_testbench(OpenfpgaContext& openfpga_ctx,
|
||||||
|
const Command& cmd, const CommandContext& cmd_context);
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -122,6 +122,59 @@ ShellCommandId add_openfpga_write_verilog_testbench_command(openfpga::Shell<Open
|
||||||
return shell_cmd_id;
|
return shell_cmd_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: write full testbench
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
ShellCommandId add_openfpga_write_full_testbench_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const std::vector<ShellCommandId>& dependent_cmds) {
|
||||||
|
Command shell_cmd("write_full_testbench");
|
||||||
|
|
||||||
|
/* Add an option '--file' in short '-f'*/
|
||||||
|
CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for HDL netlists");
|
||||||
|
shell_cmd.set_option_short_name(output_opt, "f");
|
||||||
|
shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add an option '--bitstream'*/
|
||||||
|
CommandOptionId bitstream_opt = shell_cmd.add_option("bitstream", true, "Specify the bitstream to be loaded in the testbench");
|
||||||
|
shell_cmd.set_option_require_value(bitstream_opt, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add an option '--fabric_netlist_file_path'*/
|
||||||
|
CommandOptionId fabric_netlist_opt = shell_cmd.add_option("fabric_netlist_file_path", false, "Specify the file path to the fabric HDL netlist");
|
||||||
|
shell_cmd.set_option_require_value(fabric_netlist_opt, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add an option '--pin_constraints_file in short '-pcf' */
|
||||||
|
CommandOptionId pcf_opt = shell_cmd.add_option("pin_constraints_file", false, "Specify the file path to the pin constraints");
|
||||||
|
shell_cmd.set_option_short_name(pcf_opt, "pcf");
|
||||||
|
shell_cmd.set_option_require_value(pcf_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 '--explicit_port_mapping' */
|
||||||
|
shell_cmd.add_option("explicit_port_mapping", false, "Use explicit port mapping in Verilog netlists");
|
||||||
|
|
||||||
|
/* Add an option '--include_signal_init' */
|
||||||
|
shell_cmd.add_option("include_signal_init", false, "Initialize all the signals in Verilog testbenches");
|
||||||
|
|
||||||
|
/* 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 full testbenches for an FPGA fabric");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_execute_function(shell_cmd_id, write_full_testbench);
|
||||||
|
|
||||||
|
/* Add command dependency to the Shell */
|
||||||
|
shell.set_command_dependency(shell_cmd_id, dependent_cmds);
|
||||||
|
|
||||||
|
return shell_cmd_id;
|
||||||
|
}
|
||||||
|
|
||||||
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& build_fabric_cmd_id = shell.command(std::string("build_fabric"));
|
const ShellCommandId& build_fabric_cmd_id = shell.command(std::string("build_fabric"));
|
||||||
|
@ -148,6 +201,16 @@ void add_openfpga_verilog_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
||||||
add_openfpga_write_verilog_testbench_command(shell,
|
add_openfpga_write_verilog_testbench_command(shell,
|
||||||
openfpga_verilog_cmd_class,
|
openfpga_verilog_cmd_class,
|
||||||
verilog_testbench_dependent_cmds);
|
verilog_testbench_dependent_cmds);
|
||||||
|
|
||||||
|
/********************************
|
||||||
|
* Command 'write_full_testbench'
|
||||||
|
*/
|
||||||
|
/* The command 'write_full_testbench' should NOT be executed before 'build_fabric' */
|
||||||
|
std::vector<ShellCommandId> full_testbench_dependent_cmds;
|
||||||
|
full_testbench_dependent_cmds.push_back(build_fabric_cmd_id);
|
||||||
|
add_openfpga_write_full_testbench_command(shell,
|
||||||
|
openfpga_verilog_cmd_class,
|
||||||
|
full_testbench_dependent_cmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -32,8 +32,7 @@
|
||||||
#include "verilog_api.h"
|
#include "verilog_api.h"
|
||||||
|
|
||||||
/* begin namespace openfpga */
|
/* begin namespace openfpga */
|
||||||
namespace openfpga
|
namespace openfpga {
|
||||||
{
|
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* A top-level function of FPGA-Verilog which focuses on fabric Verilog generation
|
* A top-level function of FPGA-Verilog which focuses on fabric Verilog generation
|
||||||
|
@ -253,4 +252,65 @@ int fpga_verilog_testbench(const ModuleManager &module_manager,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* A top-level function of FPGA-Verilog which focuses on full testbench generation
|
||||||
|
* This function will generate
|
||||||
|
* - Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated
|
||||||
|
********************************************************************/
|
||||||
|
int fpga_verilog_full_testbench(const ModuleManager &module_manager,
|
||||||
|
const BitstreamManager &bitstream_manager,
|
||||||
|
const FabricBitstream &fabric_bitstream,
|
||||||
|
const AtomContext &atom_ctx,
|
||||||
|
const PlacementContext &place_ctx,
|
||||||
|
const PinConstraints& pin_constraints,
|
||||||
|
const std::string& bitstream_file,
|
||||||
|
const IoLocationMap &io_location_map,
|
||||||
|
const FabricGlobalPortInfo &fabric_global_port_info,
|
||||||
|
const VprNetlistAnnotation &netlist_annotation,
|
||||||
|
const CircuitLibrary &circuit_lib,
|
||||||
|
const SimulationSetting &simulation_setting,
|
||||||
|
const ConfigProtocol &config_protocol,
|
||||||
|
const VerilogTestbenchOption &options) {
|
||||||
|
|
||||||
|
vtr::ScopedStartFinishTimer timer("Write Verilog full 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();
|
||||||
|
|
||||||
|
int status = CMD_EXEC_SUCCESS;
|
||||||
|
|
||||||
|
/* Create directories */
|
||||||
|
create_directory(src_dir_path);
|
||||||
|
|
||||||
|
/* Output preprocessing flags for HDL simulations */
|
||||||
|
print_verilog_simulation_preprocessing_flags(std::string(src_dir_path),
|
||||||
|
options);
|
||||||
|
|
||||||
|
/* Generate full testbench for verification, including configuration phase and operating phase */
|
||||||
|
std::string top_testbench_file_path = src_dir_path + netlist_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX);
|
||||||
|
print_verilog_full_testbench(module_manager,
|
||||||
|
bitstream_manager, fabric_bitstream,
|
||||||
|
circuit_lib,
|
||||||
|
config_protocol,
|
||||||
|
fabric_global_port_info,
|
||||||
|
atom_ctx, place_ctx,
|
||||||
|
pin_constraints,
|
||||||
|
bitstream_file,
|
||||||
|
io_location_map,
|
||||||
|
netlist_annotation,
|
||||||
|
netlist_name,
|
||||||
|
top_testbench_file_path,
|
||||||
|
simulation_setting,
|
||||||
|
options);
|
||||||
|
|
||||||
|
/* Generate a Verilog file including all the netlists that have been generated */
|
||||||
|
print_verilog_testbench_include_netlists(src_dir_path,
|
||||||
|
netlist_name,
|
||||||
|
options.fabric_netlist_file_path(),
|
||||||
|
options.reference_benchmark_file_path());
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -57,6 +57,20 @@ int fpga_verilog_testbench(const ModuleManager& module_manager,
|
||||||
const ConfigProtocol& config_protocol,
|
const ConfigProtocol& config_protocol,
|
||||||
const VerilogTestbenchOption& options);
|
const VerilogTestbenchOption& options);
|
||||||
|
|
||||||
|
int fpga_verilog_full_testbench(const ModuleManager& module_manager,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const FabricBitstream& fabric_bitstream,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const PinConstraints& pin_constraints,
|
||||||
|
const std::string& bitstream_file,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const FabricGlobalPortInfo &fabric_global_port_info,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const SimulationSetting& simulation_parameters,
|
||||||
|
const ConfigProtocol& config_protocol,
|
||||||
|
const VerilogTestbenchOption& options);
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,10 @@ constexpr char* TOP_TB_OP_CLOCK_PORT_PREFIX = "operating_clk_";
|
||||||
constexpr char* TOP_TB_PROG_CLOCK_PORT_NAME = "prog_clock";
|
constexpr char* TOP_TB_PROG_CLOCK_PORT_NAME = "prog_clock";
|
||||||
constexpr char* TOP_TB_INOUT_REG_POSTFIX = "_reg";
|
constexpr char* TOP_TB_INOUT_REG_POSTFIX = "_reg";
|
||||||
constexpr char* TOP_TB_CLOCK_REG_POSTFIX = "_reg";
|
constexpr char* TOP_TB_CLOCK_REG_POSTFIX = "_reg";
|
||||||
|
constexpr char* TOP_TB_BITSTREAM_LENGTH_VARIABLE = "BITSTREAM_LENGTH";
|
||||||
|
constexpr char* TOP_TB_BITSTREAM_WIDTH_VARIABLE = "BITSTREAM_WIDTH";
|
||||||
|
constexpr char* TOP_TB_BITSTREAM_MEM_REG_NAME = "bit_mem";
|
||||||
|
constexpr char* TOP_TB_BITSTREAM_INDEX_REG_NAME = "bit_index";
|
||||||
|
|
||||||
constexpr char* AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX = "_autocheck_top_tb";
|
constexpr char* AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX = "_autocheck_top_tb";
|
||||||
|
|
||||||
|
@ -1930,6 +1934,164 @@ void print_verilog_top_testbench_bitstream(std::fstream& fp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* 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_full_testbench_configuration_chain_bitstream(std::fstream& fp,
|
||||||
|
const std::string& bitstream_file,
|
||||||
|
const bool& fast_configuration,
|
||||||
|
const bool& bit_value_to_skip,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const FabricBitstream& fabric_bitstream) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----");
|
||||||
|
|
||||||
|
/* Find the longest bitstream */
|
||||||
|
size_t regional_bitstream_max_size = find_fabric_regional_bitstream_max_size(fabric_bitstream);
|
||||||
|
|
||||||
|
/* Define a constant for the bitstream length */
|
||||||
|
print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_LENGTH_VARIABLE), regional_bitstream_max_size);
|
||||||
|
print_verilog_define_flag(fp, std::string(TOP_TB_BITSTREAM_WIDTH_VARIABLE), fabric_bitstream.num_regions());
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
|
ModulePortId cc_head_port_id = module_manager.find_module_port(top_module, generate_configuration_chain_head_name());
|
||||||
|
BasicPort config_chain_head_port = module_manager.module_port(top_module, cc_head_port_id);
|
||||||
|
std::vector<size_t> initial_values(config_chain_head_port.get_width(), 0);
|
||||||
|
|
||||||
|
/* Declare local variables for bitstream loading in Verilog */
|
||||||
|
print_verilog_comment(fp, "----- Virtual memory to store the bitstream from external file -----");
|
||||||
|
fp << "reg [0:`" << TOP_TB_BITSTREAM_WIDTH_VARIABLE << " - 1] ";
|
||||||
|
fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[0:`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << " - 1];";
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
fp << "reg [$clog2(`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE << ") - 1:0] " << TOP_TB_BITSTREAM_INDEX_REG_NAME << ";" << std::endl;
|
||||||
|
|
||||||
|
print_verilog_comment(fp, "----- Preload bitstream file to a virtual memory -----");
|
||||||
|
fp << "initial begin" << std::endl;
|
||||||
|
fp << "\t";
|
||||||
|
fp << "$readmemb(\"" << bitstream_file << "\", " << TOP_TB_BITSTREAM_MEM_REG_NAME << ");";
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
print_verilog_comment(fp, "----- Configuration chain default input -----");
|
||||||
|
fp << "\t";
|
||||||
|
fp << generate_verilog_port_constant_values(config_chain_head_port, initial_values, true);
|
||||||
|
fp << ";";
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
fp << "\t";
|
||||||
|
fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " <= 0";
|
||||||
|
fp << ";";
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
fp << "end";
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX), 1);
|
||||||
|
fp << "always";
|
||||||
|
fp << " @(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ")";
|
||||||
|
fp << " begin";
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
fp << "\t";
|
||||||
|
fp << "if (";
|
||||||
|
fp << TOP_TB_BITSTREAM_INDEX_REG_NAME;
|
||||||
|
fp << " >= ";
|
||||||
|
fp << "`" << TOP_TB_BITSTREAM_LENGTH_VARIABLE;
|
||||||
|
fp << ") begin";
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1);
|
||||||
|
fp << "\t\t";
|
||||||
|
std::vector<size_t> config_done_final_values(config_done_port.get_width(), 1);
|
||||||
|
fp << generate_verilog_port_constant_values(config_done_port, config_done_final_values, true);
|
||||||
|
fp << ";" << std::endl;
|
||||||
|
|
||||||
|
fp << "\t";
|
||||||
|
fp << "end else begin";
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
fp << "\t\t";
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_CONKT, config_chain_head_port);
|
||||||
|
fp << " <= ";
|
||||||
|
fp << TOP_TB_BITSTREAM_MEM_REG_NAME << "[" << TOP_TB_BITSTREAM_INDEX_REG_NAME << "]";
|
||||||
|
fp << ";" << std::endl;
|
||||||
|
|
||||||
|
fp << "\t\t";
|
||||||
|
fp << TOP_TB_BITSTREAM_INDEX_REG_NAME;
|
||||||
|
fp << " <= ";
|
||||||
|
fp << TOP_TB_BITSTREAM_INDEX_REG_NAME << " + 1";
|
||||||
|
fp << ";" << std::endl;
|
||||||
|
|
||||||
|
fp << "\t";
|
||||||
|
fp << "end";
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
fp << "end";
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Generate the stimuli for the full 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_full_testbench_bitstream(std::fstream& fp,
|
||||||
|
const std::string& bitstream_file,
|
||||||
|
const e_config_protocol_type& config_protocol_type,
|
||||||
|
const bool& fast_configuration,
|
||||||
|
const bool& bit_value_to_skip,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const FabricBitstream& fabric_bitstream) {
|
||||||
|
|
||||||
|
/* Branch on the type of configuration protocol */
|
||||||
|
switch (config_protocol_type) {
|
||||||
|
case CONFIG_MEM_STANDALONE:
|
||||||
|
break;
|
||||||
|
case CONFIG_MEM_SCAN_CHAIN:
|
||||||
|
print_verilog_full_testbench_configuration_chain_bitstream(fp, bitstream_file,
|
||||||
|
fast_configuration,
|
||||||
|
bit_value_to_skip,
|
||||||
|
module_manager, top_module,
|
||||||
|
bitstream_manager, fabric_bitstream);
|
||||||
|
break;
|
||||||
|
case CONFIG_MEM_MEMORY_BANK:
|
||||||
|
break;
|
||||||
|
case CONFIG_MEM_FRAME_BASED:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||||
|
"Invalid configuration protocol type!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Connect proper stimuli to the reset port
|
* Connect proper stimuli to the reset port
|
||||||
* This function is designed to drive the reset port of a benchmark module
|
* This function is designed to drive the reset port of a benchmark module
|
||||||
|
@ -2017,6 +2179,8 @@ void print_verilog_top_testbench_check(std::fstream& fp,
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
|
* TODO: This top function will be deprecated!!! The only top function is
|
||||||
|
* print_verilog_full_testbench()
|
||||||
* The top-level function to generate a testbench, in order to verify:
|
* The top-level function to generate a testbench, in order to verify:
|
||||||
* 1. Configuration phase of the FPGA fabric, where the bitstream is
|
* 1. Configuration phase of the FPGA fabric, where the bitstream is
|
||||||
* loaded to the configuration protocol of the FPGA fabric
|
* loaded to the configuration protocol of the FPGA fabric
|
||||||
|
@ -2275,4 +2439,269 @@ void print_verilog_top_testbench(const ModuleManager& module_manager,
|
||||||
fp.close();
|
fp.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* The top-level function to generate a full 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 |----->| |
|
||||||
|
* +-----------+ +------------+
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
int print_verilog_full_testbench(const ModuleManager& module_manager,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const FabricBitstream& fabric_bitstream,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const ConfigProtocol& config_protocol,
|
||||||
|
const FabricGlobalPortInfo& global_ports,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const PinConstraints& pin_constraints,
|
||||||
|
const std::string& bitstream_file,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const std::string& verilog_fname,
|
||||||
|
const SimulationSetting& simulation_parameters,
|
||||||
|
const VerilogTestbenchOption& options) {
|
||||||
|
|
||||||
|
bool fast_configuration = options.fast_configuration();
|
||||||
|
bool explicit_port_mapping = options.explicit_port_mapping();
|
||||||
|
|
||||||
|
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 full testbench for top-level netlist of design: ") + circuit_name;
|
||||||
|
print_verilog_file_header(fp, title);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* Preparation: find all the reset/set ports for programming usage */
|
||||||
|
std::vector<FabricGlobalPortId> global_prog_reset_ports = find_fabric_global_programming_reset_ports(global_ports);
|
||||||
|
std::vector<FabricGlobalPortId> global_prog_set_ports = find_fabric_global_programming_set_ports(global_ports);
|
||||||
|
|
||||||
|
/* Identify if we can apply fast configuration */
|
||||||
|
bool apply_fast_configuration = fast_configuration;
|
||||||
|
if ( (global_prog_set_ports.empty() && global_prog_reset_ports.empty())
|
||||||
|
&& (true == fast_configuration)) {
|
||||||
|
VTR_LOG_WARN("None of global reset and set ports are defined for programming purpose. Fast configuration is turned off\n");
|
||||||
|
apply_fast_configuration = false;
|
||||||
|
}
|
||||||
|
bool bit_value_to_skip = find_bit_value_to_skip_for_fast_configuration(config_protocol.type(),
|
||||||
|
apply_fast_configuration,
|
||||||
|
global_prog_reset_ports,
|
||||||
|
global_prog_set_ports,
|
||||||
|
bitstream_manager, fabric_bitstream);
|
||||||
|
|
||||||
|
/* Start of testbench */
|
||||||
|
print_verilog_top_testbench_ports(fp, module_manager, top_module,
|
||||||
|
atom_ctx, netlist_annotation,
|
||||||
|
clock_port_names,
|
||||||
|
pin_constraints,
|
||||||
|
simulation_parameters, config_protocol,
|
||||||
|
circuit_name);
|
||||||
|
|
||||||
|
/* Find the clock period */
|
||||||
|
float prog_clock_period = (1./simulation_parameters.programming_clock_frequency());
|
||||||
|
float default_op_clock_period = (1./simulation_parameters.default_operating_clock_frequency());
|
||||||
|
float max_op_clock_period = 0.;
|
||||||
|
for (const SimulationClockId& clock_id : simulation_parameters.clocks()) {
|
||||||
|
max_op_clock_period = std::max(max_op_clock_period, (float)(1./simulation_parameters.clock_frequency(clock_id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estimate the number of configuration clock cycles */
|
||||||
|
size_t num_config_clock_cycles = calculate_num_config_clock_cycles(config_protocol.type(),
|
||||||
|
apply_fast_configuration,
|
||||||
|
bit_value_to_skip,
|
||||||
|
bitstream_manager,
|
||||||
|
fabric_bitstream);
|
||||||
|
|
||||||
|
/* Generate stimuli for general control signals */
|
||||||
|
print_verilog_top_testbench_generic_stimulus(fp,
|
||||||
|
simulation_parameters,
|
||||||
|
num_config_clock_cycles,
|
||||||
|
prog_clock_period,
|
||||||
|
default_op_clock_period,
|
||||||
|
VERILOG_SIM_TIMESCALE);
|
||||||
|
|
||||||
|
/* Generate stimuli for programming interface */
|
||||||
|
print_verilog_top_testbench_configuration_protocol_stimulus(fp,
|
||||||
|
config_protocol.type(),
|
||||||
|
module_manager, top_module,
|
||||||
|
prog_clock_period,
|
||||||
|
VERILOG_SIM_TIMESCALE);
|
||||||
|
|
||||||
|
/* Identify the stimulus for global reset/set for programming purpose:
|
||||||
|
* - If only reset port is seen we turn on Reset
|
||||||
|
* - If only set port is seen we turn on Reset
|
||||||
|
* - If both reset and set port is defined,
|
||||||
|
* we pick the one which is consistent with the bit value to be skipped
|
||||||
|
*/
|
||||||
|
bool active_global_prog_reset = false;
|
||||||
|
bool active_global_prog_set = false;
|
||||||
|
|
||||||
|
if (!global_prog_reset_ports.empty()) {
|
||||||
|
active_global_prog_reset = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!global_prog_set_ports.empty()) {
|
||||||
|
active_global_prog_set = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure that at most only one of the two switches is activated */
|
||||||
|
if ( (true == active_global_prog_reset)
|
||||||
|
&& (true == active_global_prog_set) ) {
|
||||||
|
/* If we will skip logic '0', we will activate programming reset */
|
||||||
|
active_global_prog_reset = !bit_value_to_skip;
|
||||||
|
/* If we will skip logic '1', we will activate programming set */
|
||||||
|
active_global_prog_set = bit_value_to_skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate stimuli for global ports or connect them to existed signals */
|
||||||
|
print_verilog_top_testbench_global_ports_stimuli(fp,
|
||||||
|
module_manager, top_module,
|
||||||
|
pin_constraints,
|
||||||
|
global_ports,
|
||||||
|
simulation_parameters,
|
||||||
|
active_global_prog_reset,
|
||||||
|
active_global_prog_set);
|
||||||
|
|
||||||
|
/* Instanciate FPGA top-level module */
|
||||||
|
print_verilog_testbench_fpga_instance(fp, module_manager, top_module,
|
||||||
|
std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME),
|
||||||
|
explicit_port_mapping);
|
||||||
|
|
||||||
|
/* 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,
|
||||||
|
explicit_port_mapping);
|
||||||
|
|
||||||
|
/* Print tasks used for loading bitstreams */
|
||||||
|
print_verilog_top_testbench_load_bitstream_task(fp,
|
||||||
|
config_protocol.type(),
|
||||||
|
module_manager, top_module);
|
||||||
|
|
||||||
|
/* load bitstream to FPGA fabric in a configuration phase */
|
||||||
|
print_verilog_full_testbench_bitstream(fp,
|
||||||
|
bitstream_file,
|
||||||
|
config_protocol.type(),
|
||||||
|
apply_fast_configuration,
|
||||||
|
bit_value_to_skip,
|
||||||
|
module_manager, top_module,
|
||||||
|
bitstream_manager, fabric_bitstream);
|
||||||
|
|
||||||
|
/* Add signal initialization:
|
||||||
|
* Bypass writing codes to files due to the autogenerated codes are very large.
|
||||||
|
*/
|
||||||
|
if (true == options.include_signal_init()) {
|
||||||
|
print_verilog_testbench_signal_initialization(fp,
|
||||||
|
std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME),
|
||||||
|
circuit_lib,
|
||||||
|
module_manager,
|
||||||
|
top_module);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Add stimuli for reset, set, clock and iopad signals */
|
||||||
|
print_verilog_top_testbench_reset_stimuli(fp,
|
||||||
|
atom_ctx,
|
||||||
|
netlist_annotation,
|
||||||
|
module_manager,
|
||||||
|
global_ports,
|
||||||
|
pin_constraints,
|
||||||
|
clock_port_names);
|
||||||
|
print_verilog_testbench_random_stimuli(fp, atom_ctx,
|
||||||
|
netlist_annotation,
|
||||||
|
module_manager,
|
||||||
|
global_ports,
|
||||||
|
pin_constraints,
|
||||||
|
clock_port_names,
|
||||||
|
std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX),
|
||||||
|
std::vector<BasicPort>(1, 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));
|
||||||
|
|
||||||
|
/* Add autocheck for configuration phase */
|
||||||
|
print_verilog_top_testbench_check(fp,
|
||||||
|
std::string(AUTOCHECKED_SIMULATION_FLAG),
|
||||||
|
std::string(TOP_TB_CONFIG_DONE_PORT_NAME),
|
||||||
|
std::string(TOP_TESTBENCH_ERROR_COUNTER));
|
||||||
|
|
||||||
|
/* 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.default_operating_clock_frequency());
|
||||||
|
|
||||||
|
|
||||||
|
/* Add Icarus requirement:
|
||||||
|
* Always ceil the simulation time so that we test a sufficient length of period!!!
|
||||||
|
*/
|
||||||
|
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),
|
||||||
|
std::ceil(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();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -42,6 +42,23 @@ void print_verilog_top_testbench(const ModuleManager& module_manager,
|
||||||
const SimulationSetting& simulation_parameters,
|
const SimulationSetting& simulation_parameters,
|
||||||
const VerilogTestbenchOption& options);
|
const VerilogTestbenchOption& options);
|
||||||
|
|
||||||
|
int print_verilog_full_testbench(const ModuleManager& module_manager,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const FabricBitstream& fabric_bitstream,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const ConfigProtocol& config_protocol,
|
||||||
|
const FabricGlobalPortInfo& global_ports,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const PinConstraints& pin_constraints,
|
||||||
|
const std::string& bitstream_file,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const std::string& verilog_fname,
|
||||||
|
const SimulationSetting& simulation_parameters,
|
||||||
|
const VerilogTestbenchOption& options);
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -724,14 +724,20 @@ std::string generate_verilog_constant_values(const std::vector<size_t>& const_va
|
||||||
* Generate a verilog port with a deposite of constant values
|
* Generate a verilog port with a deposite of constant values
|
||||||
********************************************************************/
|
********************************************************************/
|
||||||
std::string generate_verilog_port_constant_values(const BasicPort& output_port,
|
std::string generate_verilog_port_constant_values(const BasicPort& output_port,
|
||||||
const std::vector<size_t>& const_values) {
|
const std::vector<size_t>& const_values,
|
||||||
|
const bool& is_register) {
|
||||||
std::string port_str;
|
std::string port_str;
|
||||||
|
|
||||||
/* Must check: the port width matches */
|
/* Must check: the port width matches */
|
||||||
VTR_ASSERT( const_values.size() == output_port.get_width() );
|
VTR_ASSERT( const_values.size() == output_port.get_width() );
|
||||||
|
|
||||||
port_str = generate_verilog_port(VERILOG_PORT_CONKT, output_port);
|
port_str = generate_verilog_port(VERILOG_PORT_CONKT, output_port);
|
||||||
port_str += " = ";
|
if (is_register) {
|
||||||
|
port_str += " <= ";
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT_SAFE(!is_register);
|
||||||
|
port_str += " = ";
|
||||||
|
}
|
||||||
port_str += generate_verilog_constant_values(const_values);
|
port_str += generate_verilog_constant_values(const_values);
|
||||||
return port_str;
|
return port_str;
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,8 @@ std::string generate_verilog_constant_values(const std::vector<size_t>& const_va
|
||||||
const bool& short_constant = true);
|
const bool& short_constant = true);
|
||||||
|
|
||||||
std::string generate_verilog_port_constant_values(const BasicPort& output_port,
|
std::string generate_verilog_port_constant_values(const BasicPort& output_port,
|
||||||
const std::vector<size_t>& const_values);
|
const std::vector<size_t>& const_values,
|
||||||
|
const bool& is_register = false);
|
||||||
|
|
||||||
void print_verilog_wire_constant_values(std::fstream& fp,
|
void print_verilog_wire_constant_values(std::fstream& fp,
|
||||||
const BasicPort& output_port,
|
const BasicPort& output_port,
|
||||||
|
|
Loading…
Reference in New Issue