diff --git a/openfpga/src/base/io_location_map.cpp b/openfpga/src/base/io_location_map.cpp new file mode 100644 index 000000000..7e0c07dfa --- /dev/null +++ b/openfpga/src/base/io_location_map.cpp @@ -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); + } + + if (y >= io_indices_[x].size()) { + io_indices_[x].resize(y); + } + + if (z >= io_indices_[x][y].size()) { + io_indices_[x][y].resize(z); + } + + io_indices_[x][y][z] = io_index; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/base/io_location_map.h b/openfpga/src/base/io_location_map.h new file mode 100644 index 000000000..de6422d23 --- /dev/null +++ b/openfpga/src/base/io_location_map.h @@ -0,0 +1,39 @@ +#ifndef IO_LOCATION_MAP_H +#define IO_LOCATION_MAP_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include + +/* 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>> io_indices_; +}; + +} /* End namespace openfpga*/ + +#endif diff --git a/openfpga/src/base/openfpga_context.h b/openfpga/src/base/openfpga_context.h index 521a2cf6a..4b6cdf2d2 100644 --- a/openfpga/src/base/openfpga_context.h +++ b/openfpga/src/base/openfpga_context.h @@ -15,6 +15,7 @@ #include "openfpga_flow_manager.h" #include "bitstream_manager.h" #include "device_rr_gsb.h" +#include "io_location_map.h" /******************************************************************** * 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::BitstreamManager& bitstream_manager() const { return bitstream_manager_; } const std::vector& fabric_bitstream() const { return fabric_bitstream_; } + const openfpga::IoLocationMap& io_location_map() { return io_location_map_; } public: /* Public mutators */ openfpga::Arch& mutable_arch() { return arch_; } 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::BitstreamManager& mutable_bitstream_manager() { return bitstream_manager_; } std::vector& mutable_fabric_bitstream() { return fabric_bitstream_; } + openfpga::IoLocationMap& mutable_io_location_map() { return io_location_map_; } private: /* Internal data */ /* Data structure to store information from read_openfpga_arch library */ openfpga::Arch arch_; @@ -102,6 +105,7 @@ class OpenfpgaContext : public Context { /* Fabric module graph */ openfpga::ModuleManager module_graph_; + openfpga::IoLocationMap io_location_map_; /* Bitstream database */ openfpga::BitstreamManager bitstream_manager_; diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp new file mode 100644 index 000000000..e73fe5ee4 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp @@ -0,0 +1,576 @@ +/******************************************************************** + * 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 + +/* 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 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 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 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 << "." << atom_ctx.nlist.block_name(atom_blk) << module_input_port_postfix << "("; + } + fp << atom_ctx.nlist.block_name(atom_blk); + 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 << "." << atom_ctx.nlist.block_name(atom_blk) << module_output_port_postfix << "("; + } + fp << atom_ctx.nlist.block_name(atom_blk) << 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 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 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); + + /* 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(atom_ctx.nlist.block_name(atom_blk) + io_input_port_name_postfix)); + benchmark_io_port.set_width(1); + print_verilog_comment(fp, std::string("----- Blif Benchmark input " + atom_ctx.nlist.block_name(atom_blk) + " 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(atom_ctx.nlist.block_name(atom_blk) + io_output_port_name_postfix)); + benchmark_io_port.set_width(1); + print_verilog_comment(fp, std::string("----- Blif Benchmark output " + atom_ctx.nlist.block_name(atom_blk) + " 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 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& 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 std::vector& 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; + } + + if (AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk)) { + fp << "\t\t\tif(!(" << atom_ctx.nlist.block_name(atom_blk) << fpga_port_postfix; + fp << " === " << atom_ctx.nlist.block_name(atom_blk) << benchmark_port_postfix; + fp << ") && !(" << atom_ctx.nlist.block_name(atom_blk) << benchmark_port_postfix; + fp << " === 1'bx)) begin" << std::endl; + fp << "\t\t\t\t" << atom_ctx.nlist.block_name(atom_blk) << check_flag_port_postfix << " <= 1'b1;" << std::endl; + fp << "\t\t\tend else begin" << std::endl; + fp << "\t\t\t\t" << atom_ctx.nlist.block_name(atom_blk) << 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; + } + + fp << "\talways@(posedge " << atom_ctx.nlist.block_name(atom_blk) << check_flag_port_postfix << ") begin" << std::endl; + fp << "\t\tif(" << atom_ctx.nlist.block_name(atom_blk) << 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 " << atom_ctx.nlist.block_name(atom_blk) << 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 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; + } + + /* TODO: find the clock inputs will be initialized later */ + if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) { + fp << "\t\t" << atom_ctx.nlist.block_name(atom_blk) << " <= 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; + } + + /* Each logical block assumes a single-width port */ + BasicPort output_port(std::string(atom_ctx.nlist.block_name(atom_blk) + 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; + } + + /* TODO: find the clock inputs will be initialized later */ + if (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk)) { + fp << "\t\t" << atom_ctx.nlist.block_name(atom_blk) << " <= $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 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; + } + + /* TODO: Skip clocks because they are handled in another function */ + + /* Each logical block assumes a single-width port */ + BasicPort input_port(atom_ctx.nlist.block_name(atom_blk), 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; + } + + /* Each logical block assumes a single-width port */ + BasicPort output_port(std::string(atom_ctx.nlist.block_name(atom_blk) + 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; + } + + /* Each logical block assumes a single-width port */ + BasicPort output_port(std::string(atom_ctx.nlist.block_name(atom_blk) + 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; + } + + /* Each logical block assumes a single-width port */ + BasicPort output_port(std::string(atom_ctx.nlist.block_name(atom_blk) + 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.h b/openfpga/src/fpga_verilog/verilog_testbench_utils.h new file mode 100644 index 000000000..7de04deeb --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.h @@ -0,0 +1,86 @@ +#ifndef VERILOG_TESTBENCH_UTILS_H +#define VERILOG_TESTBENCH_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include +#include "module_manager.h" +#include "vpr_context.h" +#include "io_location_map.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 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 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& 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 std::vector& 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 std::string& check_flag_port_postfix, + const BasicPort& clock_port); + +void print_verilog_testbench_shared_ports(std::fstream& fp, + const AtomContext& atom_ctx, + 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 diff --git a/openfpga/src/fpga_verilog/verilog_writer_utils.h b/openfpga/src/fpga_verilog/verilog_writer_utils.h index b145d8be6..8e6cddaaa 100644 --- a/openfpga/src/fpga_verilog/verilog_writer_utils.h +++ b/openfpga/src/fpga_verilog/verilog_writer_utils.h @@ -14,6 +14,7 @@ #include #include "openfpga_port.h" #include "verilog_port_types.h" +#include "circuit_library.h" #include "module_manager.h" /******************************************************************** diff --git a/openfpga/test_script/s298_k6_frac.openfpga b/openfpga/test_script/s298_k6_frac.openfpga index 5caf1d24c..0b2e3cc13 100644 --- a/openfpga/test_script/s298_k6_frac.openfpga +++ b/openfpga/test_script/s298_k6_frac.openfpga @@ -27,7 +27,7 @@ build_fabric --compress_routing --duplicate_grid_pin #--verbose # 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 +repack #--verbose # Build the bitstream # - Output the fabric-independent bitstream to a file