Merge pull request #320 from lnis-uofu/testbench_external_bitstream

Preliminary support on full testbench generator using external bitstream file
This commit is contained in:
tangxifan 2021-06-03 18:43:45 -06:00 committed by GitHub
commit f36425e5d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 3039 additions and 15 deletions

View File

@ -5,7 +5,7 @@ Fabric-dependent Bitstream
.. _file_formats_fabric_bitstream_plain_text:
Plain text (.txt)
Plain text (.bit)
~~~~~~~~~~~~~~~~~
This file format is designed to be directly loaded to an FPGA fabric.

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" viewBox="501 16 702 175" width="702" height="175">
<defs>
<font-face font-family="Helvetica Neue" font-size="20" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.9958" descent="-212.99744" font-weight="400">
<font-face-src>
<font-face-name name="HelveticaNeue"/>
</font-face-src>
</font-face>
<marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" stroke-linejoin="miter" stroke-miterlimit="10" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="black">
<g>
<path d="M 4.8 0 L 0 -1.8 L 0 1.8 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/>
</g>
</marker>
<font-face font-family="Helvetica Neue" font-size="18" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.9958" descent="-212.99744" font-weight="400">
<font-face-src>
<font-face-name name="HelveticaNeue"/>
</font-face-src>
</font-face>
</defs>
<metadata> Produced by OmniGraffle 7.18.5\n2021-06-03 22:42:19 +0000</metadata>
<g id="block_diagram" fill-opacity="1" stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none">
<title>block_diagram</title>
<rect fill="white" x="501" y="16" width="702" height="175"/>
<g id="block_diagram_Layer_1">
<title>Layer 1</title>
<g id="Graphic_91">
<rect x="502.5" y="25.252" width="128.5" height="49" fill="#666"/>
<rect x="502.5" y="25.252" width="128.5" height="49" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
<text transform="translate(507.5 37.972)" fill="white">
<tspan font-family="Helvetica Neue" font-size="20" font-weight="400" fill="white" x=".91" y="19">Bitstream file</tspan>
</text>
</g>
<g id="Line_92">
<line x1="632" y1="49.752" x2="726.244" y2="49.752" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
</g>
</g>
<g id="block_diagram_block_diagram">
<title>block_diagram</title>
<g id="Graphic_89">
<rect x="740.144" y="34" width="92.332" height="68" fill="#4040ff"/>
<rect x="740.144" y="34" width="92.332" height="68" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
<text transform="translate(745.144 44.44)" fill="white">
<tspan font-family="Helvetica Neue" font-size="20" font-weight="400" fill="white" x="14.876" y="19">FPGA </tspan>
<tspan font-family="Helvetica Neue" font-size="20" font-weight="400" fill="white" x="13.206" y="42.56">Fabric</tspan>
</text>
</g>
<g id="Graphic_88">
<rect x="740.144" y="122" width="92.332" height="68" fill="#4040ff"/>
<rect x="740.144" y="122" width="92.332" height="68" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
<text transform="translate(745.144 132.44)" fill="white">
<tspan font-family="Helvetica Neue" font-size="20" font-weight="400" fill="white" x="13.206" y="19">Users </tspan>
<tspan font-family="Helvetica Neue" font-size="20" font-weight="400" fill="white" x="10.236" y="42.56">Design</tspan>
</text>
</g>
<g id="Graphic_87">
<rect x="969.072" y="61" width="92.332" height="98.5" fill="#ff4040"/>
<rect x="969.072" y="61" width="92.332" height="98.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
<text transform="translate(974.072 86.69)" fill="white">
<tspan font-family="Helvetica Neue" font-size="20" font-weight="400" fill="white" x="10.216" y="19">Output </tspan>
<tspan font-family="Helvetica Neue" font-size="20" font-weight="400" fill="white" x="3.7560004" y="42.56">Checker</tspan>
</text>
</g>
<g id="Graphic_86">
<text transform="translate(598.566 99.248)" fill="black">
<tspan font-family="Helvetica Neue" font-size="18" font-weight="400" fill="black" x="45474735e-20" y="17">Input stimulus</tspan>
</text>
</g>
<g id="Line_85">
<line x1="833.476" y1="68" x2="955.172" y2="68" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
</g>
<g id="Line_84">
<line x1="833.476" y1="156" x2="955.172" y2="156" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
</g>
<g id="Line_83">
<line x1="1062.404" y1="110.84803" x2="1091.601" y2="111.21822" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
</g>
<g id="Graphic_82">
<text transform="translate(1109.5 90.496)" fill="black">
<tspan font-family="Helvetica Neue" font-size="18" font-weight="400" fill="black" x="1.0814986" y="17">Number of </tspan>
<tspan font-family="Helvetica Neue" font-size="18" font-weight="400" fill="black" x="20.431499" y="38.504">errors</tspan>
</text>
</g>
<g id="Graphic_81">
<text transform="translate(842 39)" fill="black">
<tspan font-family="Helvetica Neue" font-size="18" font-weight="400" fill="black" x="22737368e-20" y="17">FPGA output vectors</tspan>
</text>
</g>
<g id="Graphic_80">
<text transform="translate(842 163.496)" fill="black">
<tspan font-family="Helvetica Neue" font-size="18" font-weight="400" fill="black" x="42632564e-21" y="17">Expected output vectors</tspan>
</text>
</g>
<g id="Line_79">
<path d="M 655.095 94.248 L 655.095 68 L 726.244 68" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
</g>
<g id="Line_78">
<path d="M 655.095 125.752 L 655.095 156 L 726.244 156" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

View File

@ -16,24 +16,32 @@ In this part, we will introduce the hierarchy, dependency and functionality of e
+-----------------+---------+----------------+---------------+
OpenFPGA can auto-generate two types of Verilog testbenches to validate the correctness of the fabric: full and formal-oriented.
Both testbenches share the same organization, as depicted in :numref:`fig_verilog_testbench_organization` (a).
Both testbenches share the same organization, as depicted in :numref:`fig_verilog_testbench_organization`.
To enable self-testing, the FPGA and user's RTL design (simulate using an HDL simulator) are driven by the same input stimuli, and any mismatch on their outputs will raise an error flag.
.. _fig_verilog_testbench_organization:
.. figure:: figures/verilog_testbench_organization.png
.. figure:: figures/full_testbench_block_diagram.svg
:scale: 50%
:alt: Functional Verification using ModelSim
:alt: Verilog testbench principles
Principles of Verilog testbenches organization: (a) block diagram and (b) waveforms.
Principles of Verilog testbenches: (1) using common input stimuli; (2) applying bitstream; (3) checking output vectors.
.. _fig_verilog_full_testbench_waveform:
.. figure:: figures/full_testbench_waveform.svg
:scale: 50%
:alt: Full testbench waveform
Illustration on the waveforms in full testbench
Full Testbench
~~~~~~~~~~~~~~
Full testbench aims at simulating an entire FPGA operating period, consisting of two phases:
- the **Configuration Phase**, where the synthesized design bitstream is loaded to the programmable fabric, as highlighted by the green rectangle of :numref:`fig_verilog_testbench_organization` (b);
- the **Configuration Phase**, where the synthesized design bitstream is loaded to the programmable fabric, as highlighted by the green rectangle of :numref:`fig_verilog_full_testbench_waveform`;
- the **Operating Phase**, where random input vectors are auto-generated to drive both Devices Under Test (DUTs), as highlighted by the red rectangle of :numref:`fig_verilog_testbench_organization` (b). Using the full testbench, users can validate both the configuration circuits and programming fabric of an FPGA.
- the **Operating Phase**, where random input vectors are auto-generated to drive both Devices Under Test (DUTs), as highlighted by the red rectangle of :numref:`fig_verilog_full_testbench_waveform`. Using the full testbench, users can validate both the configuration circuits and programming fabric of an FPGA.
Formal-oriented Testbench
~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -87,3 +87,39 @@ write_verilog_testbench
.. option:: --explicit_port_mapping
Use explicit port mapping when writing the Verilog netlists
write_full_testbench
~~~~~~~~~~~~~~~~~~~~~~~
Write the full testbench for FPGA fabric in Verilog format
.. option:: --file <string> or -f <string>
The output directory for all the testbench netlists. We suggest the use of same output directory as fabric Verilog netlists. For example, ``--file /temp/testbench``
.. option:: --bitstream <string>
The bitstream file to be loaded to the full testbench, which should be in the same file format that OpenFPGA can outputs (See detailes in :ref:`file_formats_fabric_bitstream_plain_text`). For example, ``--bitstream and2.bit``
.. option:: --fabric_netlist_file_path <string>
Specify the fabric Verilog file if they are not in the same directory as the testbenches to be generated. If not specified, OpenFPGA will assume that the fabric netlists are the in the same directory as testbenches and assign default names. For example, ``--file /temp/fabric/fabric_netlists.v``
.. option:: --reference_benchmark_file_path <string>
Must specify the reference benchmark Verilog file if you want to output any testbenches. For example, ``--reference_benchmark_file_path /temp/benchmark/counter_post_synthesis.v``
.. option:: --pin_constraints_file <string> or -pcf <string>
Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml``
Strongly recommend for multi-clock simulations. See detailed file format about :ref:`file_format_pin_constraints_file`.
.. option:: --explicit_port_mapping
Use explicit port mapping when writing the Verilog netlists
.. option:: --include_signal_init
Output signal initialization to Verilog testbench to smooth convergence in HDL simulation

View File

@ -119,4 +119,54 @@ int write_verilog_testbench(OpenfpgaContext& openfpga_ctx,
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 */

View File

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

View File

@ -122,6 +122,59 @@ ShellCommandId add_openfpga_write_verilog_testbench_command(openfpga::Shell<Open
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) {
/* 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"));
@ -148,6 +201,16 @@ void add_openfpga_verilog_commands(openfpga::Shell<OpenfpgaContext>& shell) {
add_openfpga_write_verilog_testbench_command(shell,
openfpga_verilog_cmd_class,
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 */

View File

@ -32,8 +32,7 @@
#include "verilog_api.h"
/* begin namespace openfpga */
namespace openfpga
{
namespace openfpga {
/********************************************************************
* 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;
}
/********************************************************************
* 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 */

View File

@ -57,6 +57,20 @@ int fpga_verilog_testbench(const ModuleManager& module_manager,
const ConfigProtocol& config_protocol,
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 */

View File

@ -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_INOUT_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";
@ -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
* 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:
* 1. Configuration phase of the FPGA fabric, where the bitstream is
* loaded to the configuration protocol of the FPGA fabric
@ -2275,4 +2439,269 @@ void print_verilog_top_testbench(const ModuleManager& module_manager,
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 */

View File

@ -42,6 +42,23 @@ void print_verilog_top_testbench(const ModuleManager& module_manager,
const SimulationSetting& simulation_parameters,
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 */
#endif

View File

@ -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
********************************************************************/
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;
/* Must check: the port width matches */
VTR_ASSERT( const_values.size() == output_port.get_width() );
port_str = generate_verilog_port(VERILOG_PORT_CONKT, output_port);
if (is_register) {
port_str += " <= ";
} else {
VTR_ASSERT_SAFE(!is_register);
port_str += " = ";
}
port_str += generate_verilog_constant_values(const_values);
return port_str;
}

View File

@ -108,7 +108,8 @@ std::string generate_verilog_constant_values(const std::vector<size_t>& const_va
const bool& short_constant = true);
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,
const BasicPort& output_port,

View File

@ -0,0 +1,74 @@
# Run VPR for the 'and' design
#--write_rr_graph example_rr_graph.xml
vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route ${OPENFPGA_VPR_DEVICE_LAYOUT}
# Read OpenFPGA architecture definition
read_openfpga_arch -f ${OPENFPGA_ARCH_FILE}
# Read OpenFPGA simulation settings
read_openfpga_simulation_setting -f ${OPENFPGA_SIM_SETTING_FILE}
# Annotate the OpenFPGA architecture to VPR data base
# to debug use --verbose options
link_openfpga_arch --activity_file ${ACTIVITY_FILE} --sort_gsb_chan_node_in_edges
# Check and correct any naming conflicts in the BLIF netlist
check_netlist_naming_conflict --fix --report ./netlist_renaming.xml
# Apply fix-up to clustering nets based on routing results
pb_pin_fixup --verbose
# Apply fix-up to Look-Up Table truth tables based on packing results
lut_truth_table_fixup
# Build the module graph
# - Enabled compression on routing architecture modules
# - Enable pin duplication on grid modules
build_fabric --compress_routing #--verbose
# Write the fabric hierarchy of module graph to a file
# This is used by hierarchical PnR flows
write_fabric_hierarchy --file ./fabric_hierarchy.txt
# Repack the netlist to physical pbs
# This must be done before bitstream generator and testbench generation
# Strongly recommend it is done after all the fix-up have been applied
repack #--verbose
# Build the bitstream
# - Output the fabric-independent bitstream to a file
build_architecture_bitstream --verbose --write_file fabric_independent_bitstream.xml
# Build fabric-dependent bitstream
build_fabric_bitstream --verbose
# Write fabric-dependent bitstream
write_fabric_bitstream --file fabric_bitstream.bit --format plain_text
# Write the Verilog netlist for FPGA fabric
# - Enable the use of explicit port mapping in Verilog netlist
write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --print_user_defined_template --verbose
# Write the Verilog testbench for FPGA fabric
# - We suggest the use of same output directory as fabric Verilog netlists
# - Must specify the reference benchmark file if you want to output any testbenches
# - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA
# - Enable pre-configured top-level testbench which is a fast verification skipping programming phase
# - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts
write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} --include_signal_init --explicit_port_mapping --bitstream fabric_bitstream.bit
# Write the SDC files for PnR backend
# - Turn on every options here
write_pnr_sdc --file ./SDC
# Write SDC to disable timing for configure ports
write_sdc_disable_timing_configure_ports --file ./SDC/disable_configure_ports.sdc
# Write the SDC to run timing analysis for a mapped FPGA fabric
write_analysis_sdc --file ./SDC_analysis
# Finish and exit OpenFPGA
exit
# Note :
# To run verification at the end of the flow maintain source in ./SRC directory

View File

@ -16,9 +16,10 @@ timeout_each_job = 20*60
fpga_flow=yosys_vpr
[OpenFPGA_SHELL]
openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/configuration_chain_example_script.openfpga
openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga
openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_cc_openfpga.xml
openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml
openfpga_vpr_device_layout=
[ARCHITECTURES]
arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml

View File

@ -16,10 +16,10 @@ timeout_each_job = 20*60
fpga_flow=yosys_vpr
[OpenFPGA_SHELL]
openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/fix_device_example_script.openfpga
openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_full_testbench_example_script.openfpga
openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k4_N4_40nm_multi_region_cc_openfpga.xml
openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml
openfpga_vpr_device_layout=2x2
openfpga_vpr_device_layout=--device 2x2
[ARCHITECTURES]
arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k4_N4_tileable_40nm.xml