Merge remote-tracking branch 'lnis_origin/dev' into ganesh_dev
This commit is contained in:
commit
ae4b0a6f9c
|
@ -22,6 +22,7 @@ target_link_libraries(libopenfpga
|
||||||
libarchopenfpga
|
libarchopenfpga
|
||||||
libopenfpgashell
|
libopenfpgashell
|
||||||
libopenfpgautil
|
libopenfpgautil
|
||||||
|
libini
|
||||||
libvtrutil
|
libvtrutil
|
||||||
libvpr8)
|
libvpr8)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* Memember functions for data structure IoLocationMap
|
||||||
|
******************************************************************************/
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
|
||||||
|
#include "io_location_map.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/**************************************************
|
||||||
|
* Public Accessors
|
||||||
|
*************************************************/
|
||||||
|
size_t IoLocationMap::io_index(const size_t& x, const size_t& y, const size_t& z) const {
|
||||||
|
if (x >= io_indices_.size()) {
|
||||||
|
return size_t(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y >= io_indices_[x].size()) {
|
||||||
|
return size_t(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z >= io_indices_[x][y].size()) {
|
||||||
|
return size_t(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return io_indices_[x][y][z];
|
||||||
|
}
|
||||||
|
|
||||||
|
void IoLocationMap::set_io_index(const size_t& x, const size_t& y, const size_t& z, const size_t& io_index) {
|
||||||
|
if (x >= io_indices_.size()) {
|
||||||
|
io_indices_.resize(x + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y >= io_indices_[x].size()) {
|
||||||
|
io_indices_[x].resize(y + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z >= io_indices_[x][y].size()) {
|
||||||
|
io_indices_[x][y].resize(z + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
io_indices_[x][y][z] = io_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef IO_LOCATION_MAP_H
|
||||||
|
#define IO_LOCATION_MAP_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files required by the data structure definition
|
||||||
|
*******************************************************************/
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/* Begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* I/O location map is a data structure to bridge the index of I/Os
|
||||||
|
* in the FPGA fabric, i.e., the module graph, and logical location
|
||||||
|
* of the I/O in VPR coordinate system
|
||||||
|
*
|
||||||
|
* For example
|
||||||
|
* io[0] io[1] io[2]
|
||||||
|
* +-----------------+ +--------+
|
||||||
|
* | | | | |
|
||||||
|
* | I/O | I/O | | I/O |
|
||||||
|
* | [0][y] | [0][y] | | [1][y] |
|
||||||
|
* | [0] | [1] | | [0] |
|
||||||
|
* +-----------------+ +--------+
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
class IoLocationMap {
|
||||||
|
public: /* Public aggregators */
|
||||||
|
size_t io_index(const size_t& x, const size_t& y, const size_t& z) const;
|
||||||
|
public: /* Public mutators */
|
||||||
|
void set_io_index(const size_t& x, const size_t& y, const size_t& z, const size_t& io_index);
|
||||||
|
private: /* Internal Data */
|
||||||
|
/* I/O index fast lookup by [x][y][z] location */
|
||||||
|
std::vector<std::vector<std::vector<size_t>>> io_indices_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* End namespace openfpga*/
|
||||||
|
|
||||||
|
#endif
|
|
@ -5,6 +5,9 @@
|
||||||
#include "vtr_time.h"
|
#include "vtr_time.h"
|
||||||
#include "vtr_log.h"
|
#include "vtr_log.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
#include "build_device_bitstream.h"
|
#include "build_device_bitstream.h"
|
||||||
#include "bitstream_writer.h"
|
#include "bitstream_writer.h"
|
||||||
#include "build_fabric_bitstream.h"
|
#include "build_fabric_bitstream.h"
|
||||||
|
@ -30,6 +33,11 @@ void fpga_bitstream(OpenfpgaContext& openfpga_ctx,
|
||||||
cmd_context.option_enable(cmd, opt_verbose));
|
cmd_context.option_enable(cmd, opt_verbose));
|
||||||
|
|
||||||
if (true == cmd_context.option_enable(cmd, opt_file)) {
|
if (true == cmd_context.option_enable(cmd, opt_file)) {
|
||||||
|
std::string src_dir_path = find_path_dir_name(cmd_context.option_value(cmd, opt_file));
|
||||||
|
|
||||||
|
/* Create directories */
|
||||||
|
create_dir_path(src_dir_path.c_str());
|
||||||
|
|
||||||
write_arch_independent_bitstream_to_xml_file(openfpga_ctx.bitstream_manager(),
|
write_arch_independent_bitstream_to_xml_file(openfpga_ctx.bitstream_manager(),
|
||||||
cmd_context.option_value(cmd, opt_file));
|
cmd_context.option_value(cmd, opt_file));
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,43 +21,43 @@ namespace openfpga {
|
||||||
* This function should only be called after the GSB builder is done
|
* This function should only be called after the GSB builder is done
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
static
|
static
|
||||||
void compress_routing_hierarchy(OpenfpgaContext& openfpga_context,
|
void compress_routing_hierarchy(OpenfpgaContext& openfpga_ctx,
|
||||||
const bool& verbose_output) {
|
const bool& verbose_output) {
|
||||||
vtr::ScopedStartFinishTimer timer("Identify unique General Switch Blocks (GSBs)");
|
vtr::ScopedStartFinishTimer timer("Identify unique General Switch Blocks (GSBs)");
|
||||||
|
|
||||||
/* Build unique module lists */
|
/* Build unique module lists */
|
||||||
openfpga_context.mutable_device_rr_gsb().build_unique_module(g_vpr_ctx.device().rr_graph);
|
openfpga_ctx.mutable_device_rr_gsb().build_unique_module(g_vpr_ctx.device().rr_graph);
|
||||||
|
|
||||||
/* Report the stats */
|
/* Report the stats */
|
||||||
VTR_LOGV(verbose_output,
|
VTR_LOGV(verbose_output,
|
||||||
"Detected %lu unique X-direction connection blocks from a total of %d (compression rate=%d%)\n",
|
"Detected %lu unique X-direction connection blocks from a total of %d (compression rate=%d%)\n",
|
||||||
openfpga_context.device_rr_gsb().get_num_cb_unique_module(CHANX),
|
openfpga_ctx.device_rr_gsb().get_num_cb_unique_module(CHANX),
|
||||||
find_device_rr_gsb_num_cb_modules(openfpga_context.device_rr_gsb(), CHANX),
|
find_device_rr_gsb_num_cb_modules(openfpga_ctx.device_rr_gsb(), CHANX),
|
||||||
100 * (openfpga_context.device_rr_gsb().get_num_cb_unique_module(CHANX) / find_device_rr_gsb_num_cb_modules(openfpga_context.device_rr_gsb(), CHANX) - 1));
|
100 * (openfpga_ctx.device_rr_gsb().get_num_cb_unique_module(CHANX) / find_device_rr_gsb_num_cb_modules(openfpga_ctx.device_rr_gsb(), CHANX) - 1));
|
||||||
|
|
||||||
VTR_LOGV(verbose_output,
|
VTR_LOGV(verbose_output,
|
||||||
"Detected %lu unique Y-direction connection blocks from a total of %d (compression rate=%d%)\n",
|
"Detected %lu unique Y-direction connection blocks from a total of %d (compression rate=%d%)\n",
|
||||||
openfpga_context.device_rr_gsb().get_num_cb_unique_module(CHANY),
|
openfpga_ctx.device_rr_gsb().get_num_cb_unique_module(CHANY),
|
||||||
find_device_rr_gsb_num_cb_modules(openfpga_context.device_rr_gsb(), CHANY),
|
find_device_rr_gsb_num_cb_modules(openfpga_ctx.device_rr_gsb(), CHANY),
|
||||||
100 * (openfpga_context.device_rr_gsb().get_num_cb_unique_module(CHANY) / find_device_rr_gsb_num_cb_modules(openfpga_context.device_rr_gsb(), CHANY) - 1));
|
100 * (openfpga_ctx.device_rr_gsb().get_num_cb_unique_module(CHANY) / find_device_rr_gsb_num_cb_modules(openfpga_ctx.device_rr_gsb(), CHANY) - 1));
|
||||||
|
|
||||||
VTR_LOGV(verbose_output,
|
VTR_LOGV(verbose_output,
|
||||||
"Detected %lu unique switch blocks from a total of %d (compression rate=%d%)\n",
|
"Detected %lu unique switch blocks from a total of %d (compression rate=%d%)\n",
|
||||||
openfpga_context.device_rr_gsb().get_num_sb_unique_module(),
|
openfpga_ctx.device_rr_gsb().get_num_sb_unique_module(),
|
||||||
find_device_rr_gsb_num_sb_modules(openfpga_context.device_rr_gsb()),
|
find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb()),
|
||||||
100 * (openfpga_context.device_rr_gsb().get_num_sb_unique_module() / find_device_rr_gsb_num_sb_modules(openfpga_context.device_rr_gsb()) - 1));
|
100 * (openfpga_ctx.device_rr_gsb().get_num_sb_unique_module() / find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb()) - 1));
|
||||||
|
|
||||||
VTR_LOGV(verbose_output,
|
VTR_LOGV(verbose_output,
|
||||||
"Detected %lu unique general switch blocks from a total of %d (compression rate=%d%)\n",
|
"Detected %lu unique general switch blocks from a total of %d (compression rate=%d%)\n",
|
||||||
openfpga_context.device_rr_gsb().get_num_gsb_unique_module(),
|
openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module(),
|
||||||
find_device_rr_gsb_num_gsb_modules(openfpga_context.device_rr_gsb()),
|
find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb()),
|
||||||
100 * (openfpga_context.device_rr_gsb().get_num_gsb_unique_module() / find_device_rr_gsb_num_gsb_modules(openfpga_context.device_rr_gsb()) - 1));
|
100 * (openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module() / find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb()) - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Build the module graph for FPGA device
|
* Build the module graph for FPGA device
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
void build_fabric(OpenfpgaContext& openfpga_context,
|
void build_fabric(OpenfpgaContext& openfpga_ctx,
|
||||||
const Command& cmd, const CommandContext& cmd_context) {
|
const Command& cmd, const CommandContext& cmd_context) {
|
||||||
|
|
||||||
CommandOptionId opt_compress_routing = cmd.option("compress_routing");
|
CommandOptionId opt_compress_routing = cmd.option("compress_routing");
|
||||||
|
@ -65,16 +65,17 @@ void build_fabric(OpenfpgaContext& openfpga_context,
|
||||||
CommandOptionId opt_verbose = cmd.option("verbose");
|
CommandOptionId opt_verbose = cmd.option("verbose");
|
||||||
|
|
||||||
if (true == cmd_context.option_enable(cmd, opt_compress_routing)) {
|
if (true == cmd_context.option_enable(cmd, opt_compress_routing)) {
|
||||||
compress_routing_hierarchy(openfpga_context, cmd_context.option_enable(cmd, opt_verbose));
|
compress_routing_hierarchy(openfpga_ctx, cmd_context.option_enable(cmd, opt_verbose));
|
||||||
}
|
}
|
||||||
|
|
||||||
VTR_LOG("\n");
|
VTR_LOG("\n");
|
||||||
|
|
||||||
openfpga_context.mutable_module_graph() = build_device_module_graph(g_vpr_ctx.device(),
|
openfpga_ctx.mutable_module_graph() = build_device_module_graph(openfpga_ctx.mutable_io_location_map(),
|
||||||
const_cast<const OpenfpgaContext&>(openfpga_context),
|
g_vpr_ctx.device(),
|
||||||
cmd_context.option_enable(cmd, opt_compress_routing),
|
const_cast<const OpenfpgaContext&>(openfpga_ctx),
|
||||||
cmd_context.option_enable(cmd, opt_duplicate_grid_pin),
|
cmd_context.option_enable(cmd, opt_compress_routing),
|
||||||
cmd_context.option_enable(cmd, opt_verbose));
|
cmd_context.option_enable(cmd, opt_duplicate_grid_pin),
|
||||||
|
cmd_context.option_enable(cmd, opt_verbose));
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
/* begin namespace openfpga */
|
/* begin namespace openfpga */
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
|
||||||
void build_fabric(OpenfpgaContext& openfpga_context,
|
void build_fabric(OpenfpgaContext& openfpga_ctx,
|
||||||
const Command& cmd, const CommandContext& cmd_context);
|
const Command& cmd, const CommandContext& cmd_context);
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "openfpga_flow_manager.h"
|
#include "openfpga_flow_manager.h"
|
||||||
#include "bitstream_manager.h"
|
#include "bitstream_manager.h"
|
||||||
#include "device_rr_gsb.h"
|
#include "device_rr_gsb.h"
|
||||||
|
#include "io_location_map.h"
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* This file includes the declaration of the date structure
|
* This file includes the declaration of the date structure
|
||||||
|
@ -58,6 +59,8 @@ class OpenfpgaContext : public Context {
|
||||||
const openfpga::FlowManager& flow_manager() const { return flow_manager_; }
|
const openfpga::FlowManager& flow_manager() const { return flow_manager_; }
|
||||||
const openfpga::BitstreamManager& bitstream_manager() const { return bitstream_manager_; }
|
const openfpga::BitstreamManager& bitstream_manager() const { return bitstream_manager_; }
|
||||||
const std::vector<openfpga::ConfigBitId>& fabric_bitstream() const { return fabric_bitstream_; }
|
const std::vector<openfpga::ConfigBitId>& fabric_bitstream() const { return fabric_bitstream_; }
|
||||||
|
const openfpga::IoLocationMap& io_location_map() { return io_location_map_; }
|
||||||
|
const std::unordered_map<AtomNetId, t_net_power>& net_activity() { return net_activity_; }
|
||||||
public: /* Public mutators */
|
public: /* Public mutators */
|
||||||
openfpga::Arch& mutable_arch() { return arch_; }
|
openfpga::Arch& mutable_arch() { return arch_; }
|
||||||
openfpga::VprDeviceAnnotation& mutable_vpr_device_annotation() { return vpr_device_annotation_; }
|
openfpga::VprDeviceAnnotation& mutable_vpr_device_annotation() { return vpr_device_annotation_; }
|
||||||
|
@ -72,6 +75,8 @@ class OpenfpgaContext : public Context {
|
||||||
openfpga::FlowManager& mutable_flow_manager() { return flow_manager_; }
|
openfpga::FlowManager& mutable_flow_manager() { return flow_manager_; }
|
||||||
openfpga::BitstreamManager& mutable_bitstream_manager() { return bitstream_manager_; }
|
openfpga::BitstreamManager& mutable_bitstream_manager() { return bitstream_manager_; }
|
||||||
std::vector<openfpga::ConfigBitId>& mutable_fabric_bitstream() { return fabric_bitstream_; }
|
std::vector<openfpga::ConfigBitId>& mutable_fabric_bitstream() { return fabric_bitstream_; }
|
||||||
|
openfpga::IoLocationMap& mutable_io_location_map() { return io_location_map_; }
|
||||||
|
std::unordered_map<AtomNetId, t_net_power>& mutable_net_activity() { return net_activity_; }
|
||||||
private: /* Internal data */
|
private: /* Internal data */
|
||||||
/* Data structure to store information from read_openfpga_arch library */
|
/* Data structure to store information from read_openfpga_arch library */
|
||||||
openfpga::Arch arch_;
|
openfpga::Arch arch_;
|
||||||
|
@ -102,10 +107,14 @@ class OpenfpgaContext : public Context {
|
||||||
|
|
||||||
/* Fabric module graph */
|
/* Fabric module graph */
|
||||||
openfpga::ModuleManager module_graph_;
|
openfpga::ModuleManager module_graph_;
|
||||||
|
openfpga::IoLocationMap io_location_map_;
|
||||||
|
|
||||||
/* Bitstream database */
|
/* Bitstream database */
|
||||||
openfpga::BitstreamManager bitstream_manager_;
|
openfpga::BitstreamManager bitstream_manager_;
|
||||||
std::vector<openfpga::ConfigBitId> fabric_bitstream_;
|
std::vector<openfpga::ConfigBitId> fabric_bitstream_;
|
||||||
|
|
||||||
|
/* Net activities of users' implementation */
|
||||||
|
std::unordered_map<AtomNetId, t_net_power> net_activity_;
|
||||||
|
|
||||||
/* Flow status */
|
/* Flow status */
|
||||||
openfpga::FlowManager flow_manager_;
|
openfpga::FlowManager flow_manager_;
|
||||||
|
|
|
@ -2,11 +2,20 @@
|
||||||
* This file includes functions to read an OpenFPGA architecture file
|
* This file includes functions to read an OpenFPGA architecture file
|
||||||
* which are built on the libarchopenfpga library
|
* which are built on the libarchopenfpga library
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
|
#include <cmath>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
/* Headers from vtrutil library */
|
/* Headers from vtrutil library */
|
||||||
#include "vtr_time.h"
|
#include "vtr_time.h"
|
||||||
#include "vtr_assert.h"
|
#include "vtr_assert.h"
|
||||||
#include "vtr_log.h"
|
#include "vtr_log.h"
|
||||||
|
|
||||||
|
/* Headers from vpr library */
|
||||||
|
#include "timing_info.h"
|
||||||
|
#include "AnalysisDelayCalculator.h"
|
||||||
|
#include "net_delay.h"
|
||||||
|
#include "read_activity.h"
|
||||||
|
|
||||||
#include "vpr_device_annotation.h"
|
#include "vpr_device_annotation.h"
|
||||||
#include "pb_type_utils.h"
|
#include "pb_type_utils.h"
|
||||||
#include "annotate_pb_types.h"
|
#include "annotate_pb_types.h"
|
||||||
|
@ -46,6 +55,163 @@ bool is_vpr_rr_graph_supported(const RRGraph& rr_graph) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Find the number of clock cycles in simulation based on the average signal density
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
size_t recommend_num_sim_clock_cycle(const AtomContext& atom_ctx,
|
||||||
|
const std::unordered_map<AtomNetId, t_net_power>& net_activity,
|
||||||
|
const float& sim_window_size) {
|
||||||
|
size_t recmd_num_sim_clock_cycle = 0;
|
||||||
|
|
||||||
|
float avg_density = 0.;
|
||||||
|
size_t net_cnt = 0;
|
||||||
|
|
||||||
|
float weighted_avg_density = 0.;
|
||||||
|
size_t weighted_net_cnt = 0;
|
||||||
|
|
||||||
|
/* get the average density of all the nets */
|
||||||
|
for (const AtomNetId& atom_net : atom_ctx.nlist.nets()) {
|
||||||
|
/* Skip the nets without any activity annotation */
|
||||||
|
if (0 == net_activity.count(atom_net)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only care non-zero density nets */
|
||||||
|
if (0. == net_activity.at(atom_net).density) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
avg_density += net_activity.at(atom_net).density;
|
||||||
|
net_cnt++;
|
||||||
|
|
||||||
|
/* Consider the weight of fan-out */
|
||||||
|
size_t net_weight;
|
||||||
|
if (0 == std::distance(atom_ctx.nlist.net_sinks(atom_net).begin(), atom_ctx.nlist.net_sinks(atom_net).end())) {
|
||||||
|
net_weight = 1;
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT(0 < std::distance(atom_ctx.nlist.net_sinks(atom_net).begin(), atom_ctx.nlist.net_sinks(atom_net).end()));
|
||||||
|
net_weight = std::distance(atom_ctx.nlist.net_sinks(atom_net).begin(), atom_ctx.nlist.net_sinks(atom_net).end());
|
||||||
|
}
|
||||||
|
weighted_avg_density += net_activity.at(atom_net).density* net_weight;
|
||||||
|
weighted_net_cnt += net_weight;
|
||||||
|
}
|
||||||
|
avg_density = avg_density / net_cnt;
|
||||||
|
weighted_avg_density = weighted_avg_density / weighted_net_cnt;
|
||||||
|
|
||||||
|
/* Sort the net density */
|
||||||
|
std::vector<float> net_densities;
|
||||||
|
net_densities.reserve(net_cnt);
|
||||||
|
for (const AtomNetId& atom_net : atom_ctx.nlist.nets()) {
|
||||||
|
/* Skip the nets without any activity annotation */
|
||||||
|
if (0 == net_activity.count(atom_net)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only care non-zero density nets */
|
||||||
|
if (0. == net_activity.at(atom_net).density) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
net_densities.push_back(net_activity.at(atom_net).density);
|
||||||
|
}
|
||||||
|
std::sort(net_densities.begin(), net_densities.end());
|
||||||
|
/* Get the median */
|
||||||
|
float median_density = 0.;
|
||||||
|
/* check for even case */
|
||||||
|
if (net_cnt % 2 != 0) {
|
||||||
|
median_density = net_densities[size_t(net_cnt / 2)];
|
||||||
|
} else {
|
||||||
|
median_density = 0.5 * (net_densities[size_t((net_cnt - 1) / 2)] + net_densities[size_t((net_cnt - 1) / 2)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It may be more reasonable to use median
|
||||||
|
* But, if median density is 0, we use average density
|
||||||
|
*/
|
||||||
|
if ((0. == median_density) && (0. == avg_density)) {
|
||||||
|
recmd_num_sim_clock_cycle = 1;
|
||||||
|
VTR_LOG_WARN("All the signal density is zero!\nNumber of clock cycles in simulations are set to be %ld!\n",
|
||||||
|
recmd_num_sim_clock_cycle);
|
||||||
|
} else if (0. == avg_density) {
|
||||||
|
recmd_num_sim_clock_cycle = (int)round(1 / median_density);
|
||||||
|
} else if (0. == median_density) {
|
||||||
|
recmd_num_sim_clock_cycle = (int)round(1 / avg_density);
|
||||||
|
} else {
|
||||||
|
/* add a sim window size to balance the weight of average density and median density
|
||||||
|
* In practice, we find that there could be huge difference between avereage and median values
|
||||||
|
* For a reasonable number of simulation clock cycles, we do this window size.
|
||||||
|
*/
|
||||||
|
recmd_num_sim_clock_cycle = (int)round(1 / (sim_window_size * avg_density + (1 - sim_window_size) * median_density ));
|
||||||
|
}
|
||||||
|
|
||||||
|
VTR_ASSERT(0 < recmd_num_sim_clock_cycle);
|
||||||
|
|
||||||
|
VTR_LOG("Average net density: %.2f\n", avg_density);
|
||||||
|
VTR_LOG("Median net density: %.2f\n", median_density);
|
||||||
|
VTR_LOG("Average net density after weighting: %.2f\n", weighted_avg_density);
|
||||||
|
VTR_LOG("Window size set for Simulation: %.2f\n", sim_window_size);
|
||||||
|
VTR_LOG("Net density after Window size : %.2f\n",
|
||||||
|
(sim_window_size * avg_density + (1 - sim_window_size) * median_density));
|
||||||
|
VTR_LOG("Recommend no. of clock cycles: %ld\n", recmd_num_sim_clock_cycle);
|
||||||
|
|
||||||
|
return recmd_num_sim_clock_cycle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Annotate simulation setting based on VPR results
|
||||||
|
* - If the operating clock frequency is set to follow the vpr timing results,
|
||||||
|
* we will set a new operating clock frequency here
|
||||||
|
* - If the number of clock cycles in simulation is set to be automatically determined,
|
||||||
|
* we will infer the number based on the average signal density
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void annotate_simulation_setting(const AtomContext& atom_ctx,
|
||||||
|
const std::unordered_map<AtomNetId, t_net_power>& net_activity,
|
||||||
|
SimulationSetting& sim_setting) {
|
||||||
|
|
||||||
|
/* Find if the operating frequency is binded to vpr results */
|
||||||
|
if (0. == sim_setting.operating_clock_frequency()) {
|
||||||
|
VTR_LOG("User specified the operating clock frequency to use VPR results\n");
|
||||||
|
/* Run timing analysis and collect critical path delay
|
||||||
|
* This code is copied from function vpr_analysis() in vpr_api.h
|
||||||
|
* Should keep updated to latest VPR code base
|
||||||
|
* Note:
|
||||||
|
* - MUST mention in documentation that VPR should be run in timing enabled mode
|
||||||
|
*/
|
||||||
|
vtr::vector<ClusterNetId, float*> net_delay;
|
||||||
|
vtr::t_chunk net_delay_ch;
|
||||||
|
/* Load the net delays */
|
||||||
|
net_delay = alloc_net_delay(&net_delay_ch);
|
||||||
|
load_net_delay_from_routing(net_delay);
|
||||||
|
|
||||||
|
/* Do final timing analysis */
|
||||||
|
auto analysis_delay_calc = std::make_shared<AnalysisDelayCalculator>(atom_ctx.nlist, atom_ctx.lookup, net_delay);
|
||||||
|
auto timing_info = make_setup_hold_timing_info(analysis_delay_calc);
|
||||||
|
timing_info->update();
|
||||||
|
|
||||||
|
/* Get critical path delay. Update simulation settings */
|
||||||
|
float T_crit = timing_info->least_slack_critical_path().delay() * (1. + sim_setting.operating_clock_frequency_slack());
|
||||||
|
sim_setting.set_operating_clock_frequency(1 / T_crit);
|
||||||
|
VTR_LOG("Use VPR critical path delay %g [ns] with a %g [%] slack in OpenFPGA.\n",
|
||||||
|
T_crit / 1e9, sim_setting.operating_clock_frequency_slack() * 100);
|
||||||
|
}
|
||||||
|
VTR_LOG("Will apply operating clock frequency %g [MHz] to simulations\n",
|
||||||
|
sim_setting.operating_clock_frequency() / 1e6);
|
||||||
|
|
||||||
|
if (0. == sim_setting.num_clock_cycles()) {
|
||||||
|
/* Find the number of clock cycles to be used in simulation by average over the signal activity */
|
||||||
|
|
||||||
|
VTR_LOG("User specified the number of operating clock cycles to be inferred from signal activities\n");
|
||||||
|
size_t num_clock_cycles = recommend_num_sim_clock_cycle(atom_ctx,
|
||||||
|
net_activity,
|
||||||
|
0.5);
|
||||||
|
sim_setting.set_num_clock_cycles(num_clock_cycles);
|
||||||
|
|
||||||
|
VTR_LOG("Will apply %lu operating clock cycles to simulations\n",
|
||||||
|
sim_setting.num_clock_cycles());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Top-level function to link openfpga architecture to VPR, including:
|
* Top-level function to link openfpga architecture to VPR, including:
|
||||||
* - physical pb_type
|
* - physical pb_type
|
||||||
|
@ -59,6 +225,7 @@ void link_arch(OpenfpgaContext& openfpga_ctx,
|
||||||
|
|
||||||
vtr::ScopedStartFinishTimer timer("Link OpenFPGA architecture to VPR architecture");
|
vtr::ScopedStartFinishTimer timer("Link OpenFPGA architecture to VPR architecture");
|
||||||
|
|
||||||
|
CommandOptionId opt_activity_file = cmd.option("activity_file");
|
||||||
CommandOptionId opt_verbose = cmd.option("verbose");
|
CommandOptionId opt_verbose = cmd.option("verbose");
|
||||||
|
|
||||||
/* Annotate pb_type graphs
|
/* Annotate pb_type graphs
|
||||||
|
@ -118,6 +285,22 @@ void link_arch(OpenfpgaContext& openfpga_ctx,
|
||||||
g_vpr_ctx.clustering(),
|
g_vpr_ctx.clustering(),
|
||||||
g_vpr_ctx.placement(),
|
g_vpr_ctx.placement(),
|
||||||
openfpga_ctx.mutable_vpr_placement_annotation());
|
openfpga_ctx.mutable_vpr_placement_annotation());
|
||||||
|
|
||||||
|
/* Read activity file is manadatory in the following flow-run settings
|
||||||
|
* - When users specify that number of clock cycles
|
||||||
|
* should be inferred from FPGA implmentation
|
||||||
|
* - When FPGA-SPICE is enabled
|
||||||
|
*/
|
||||||
|
openfpga_ctx.mutable_net_activity() = read_activity(g_vpr_ctx.atom().nlist,
|
||||||
|
cmd_context.option_value(cmd, opt_activity_file).c_str());
|
||||||
|
|
||||||
|
/* TODO: Annotate the number of clock cycles and clock frequency by following VPR results
|
||||||
|
* We SHOULD create a new simulation setting for OpenFPGA use only
|
||||||
|
* Avoid overwrite the raw data achieved when parsing!!!
|
||||||
|
*/
|
||||||
|
annotate_simulation_setting(g_vpr_ctx.atom(),
|
||||||
|
openfpga_ctx.net_activity(),
|
||||||
|
openfpga_ctx.mutable_arch().sim_setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file includes functions to compress the hierachy of routing architecture
|
||||||
|
*******************************************************************/
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_time.h"
|
||||||
|
#include "vtr_log.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
|
#include "circuit_library_utils.h"
|
||||||
|
#include "pnr_sdc_writer.h"
|
||||||
|
#include "openfpga_sdc.h"
|
||||||
|
|
||||||
|
/* Include global variables of VPR */
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* A wrapper function to call the PnR SDC generator of FPGA-SDC
|
||||||
|
*******************************************************************/
|
||||||
|
void write_pnr_sdc(OpenfpgaContext& openfpga_ctx,
|
||||||
|
const Command& cmd, const CommandContext& cmd_context) {
|
||||||
|
|
||||||
|
CommandOptionId opt_output_dir = cmd.option("file");
|
||||||
|
CommandOptionId opt_constrain_global_port = cmd.option("constrain_global_port");
|
||||||
|
CommandOptionId opt_constrain_grid = cmd.option("constrain_grid");
|
||||||
|
CommandOptionId opt_constrain_sb = cmd.option("constrain_sb");
|
||||||
|
CommandOptionId opt_constrain_cb = cmd.option("constrain_cb");
|
||||||
|
CommandOptionId opt_constrain_configurable_memory_outputs = cmd.option("constrain_configurable_memory_outputs");
|
||||||
|
CommandOptionId opt_constrain_routing_multiplexer_outputs = cmd.option("constrain_routing_multiplexer_outputs");
|
||||||
|
CommandOptionId opt_constrain_switch_block_outputs = cmd.option("constrain_switch_block_outputs");
|
||||||
|
|
||||||
|
/* This is an intermediate data structure which is designed to modularize the FPGA-SDC
|
||||||
|
* Keep it independent from any other outside data structures
|
||||||
|
*/
|
||||||
|
std::string sdc_dir_path = format_dir_path(cmd_context.option_value(cmd, opt_output_dir));
|
||||||
|
|
||||||
|
/* Create directories */
|
||||||
|
create_dir_path(sdc_dir_path.c_str());
|
||||||
|
|
||||||
|
PnrSdcOption options(sdc_dir_path);
|
||||||
|
|
||||||
|
options.set_constrain_global_port(cmd_context.option_enable(cmd, opt_constrain_global_port));
|
||||||
|
options.set_constrain_grid(cmd_context.option_enable(cmd, opt_constrain_grid));
|
||||||
|
options.set_constrain_sb(cmd_context.option_enable(cmd, opt_constrain_sb));
|
||||||
|
options.set_constrain_cb(cmd_context.option_enable(cmd, opt_constrain_cb));
|
||||||
|
options.set_constrain_configurable_memory_outputs(cmd_context.option_enable(cmd, opt_constrain_configurable_memory_outputs));
|
||||||
|
options.set_constrain_routing_multiplexer_outputs(cmd_context.option_enable(cmd, opt_constrain_routing_multiplexer_outputs));
|
||||||
|
options.set_constrain_switch_block_outputs(cmd_context.option_enable(cmd, opt_constrain_switch_block_outputs));
|
||||||
|
|
||||||
|
/* We first turn on default sdc option and then disable part of them by following users' options */
|
||||||
|
if (false == options.generate_sdc_pnr()) {
|
||||||
|
options.set_generate_sdc_pnr(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Collect global ports from the circuit library:
|
||||||
|
* TODO: should we place this in the OpenFPGA context?
|
||||||
|
*/
|
||||||
|
std::vector<CircuitPortId> global_ports = find_circuit_library_global_ports(openfpga_ctx.arch().circuit_lib);
|
||||||
|
|
||||||
|
/* Execute only when sdc is enabled */
|
||||||
|
if (true == options.generate_sdc_pnr()) {
|
||||||
|
print_pnr_sdc(options,
|
||||||
|
1./openfpga_ctx.arch().sim_setting.programming_clock_frequency(),
|
||||||
|
1./openfpga_ctx.arch().sim_setting.operating_clock_frequency(),
|
||||||
|
g_vpr_ctx.device(),
|
||||||
|
openfpga_ctx.vpr_device_annotation(),
|
||||||
|
openfpga_ctx.device_rr_gsb(),
|
||||||
|
openfpga_ctx.module_graph(),
|
||||||
|
openfpga_ctx.mux_lib(),
|
||||||
|
openfpga_ctx.arch().circuit_lib,
|
||||||
|
global_ports,
|
||||||
|
openfpga_ctx.flow_manager().compress_routing());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef OPENFPGA_SDC_H
|
||||||
|
#define OPENFPGA_SDC_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include "command.h"
|
||||||
|
#include "command_context.h"
|
||||||
|
#include "openfpga_context.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void write_pnr_sdc(OpenfpgaContext& openfpga_ctx,
|
||||||
|
const Command& cmd, const CommandContext& cmd_context);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,79 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Add commands to the OpenFPGA shell interface,
|
||||||
|
* in purpose of generate SDC files
|
||||||
|
* - write_pnr_sdc : generate SDC to constrain the back-end flow for FPGA fabric
|
||||||
|
* - write_analysis_sdc: TODO: generate SDC based on users' implementations
|
||||||
|
*******************************************************************/
|
||||||
|
#include "openfpga_sdc.h"
|
||||||
|
#include "openfpga_sdc_command.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: generate PnR SDC
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void add_openfpga_write_pnr_sdc_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const ShellCommandId& shell_cmd_build_fabric_id) {
|
||||||
|
Command shell_cmd("write_pnr_sdc");
|
||||||
|
|
||||||
|
/* Add an option '--file' in short '-f'*/
|
||||||
|
CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for SDC files");
|
||||||
|
shell_cmd.set_option_short_name(output_opt, "f");
|
||||||
|
shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add an option '--constrain_global_port' */
|
||||||
|
shell_cmd.add_option("constrain_global_port", false, "Constrain all the global ports of FPGA fabric");
|
||||||
|
|
||||||
|
/* Add an option '--constrain_grid' */
|
||||||
|
shell_cmd.add_option("constrain_grid", false, "Constrain all the grids of FPGA fabric");
|
||||||
|
|
||||||
|
/* Add an option '--constrain_sb' */
|
||||||
|
shell_cmd.add_option("constrain_sb", false, "Constrain all the switch blocks of FPGA fabric");
|
||||||
|
|
||||||
|
/* Add an option '--constrain_cb' */
|
||||||
|
shell_cmd.add_option("constrain_cb", false, "Constrain all the connection blocks of FPGA fabric");
|
||||||
|
|
||||||
|
/* Add an option '--constrain_configurable_memory_outputs' */
|
||||||
|
shell_cmd.add_option("constrain_configurable_memory_outputs", false, "Constrain all the outputs of configurable memories of FPGA fabric");
|
||||||
|
|
||||||
|
/* Add an option '--constrain_routing_multiplexer_outputs' */
|
||||||
|
shell_cmd.add_option("constrain_routing_multiplexer_outputs", false, "Constrain all the outputs of routing multiplexer of FPGA fabric");
|
||||||
|
|
||||||
|
/* Add an option '--constrain_switch_block_outputs' */
|
||||||
|
shell_cmd.add_option("constrain_switch_block_outputs", false, "Constrain all the outputs of switch blocks of FPGA fabric");
|
||||||
|
|
||||||
|
/* Add an option '--verbose' */
|
||||||
|
shell_cmd.add_option("verbose", false, "Enable verbose output");
|
||||||
|
|
||||||
|
/* Add command 'write_fabric_verilog' to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate SDC files to constrain the backend flow for FPGA fabric");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_execute_function(shell_cmd_id, write_pnr_sdc);
|
||||||
|
|
||||||
|
/* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */
|
||||||
|
std::vector<ShellCommandId> cmd_dependency;
|
||||||
|
cmd_dependency.push_back(shell_cmd_build_fabric_id);
|
||||||
|
shell.set_command_dependency(shell_cmd_id, cmd_dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_openfpga_sdc_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& shell_cmd_build_fabric_id = shell.command(std::string("build_fabric"));
|
||||||
|
|
||||||
|
/* Add a new class of commands */
|
||||||
|
ShellCommandClassId openfpga_sdc_cmd_class = shell.add_command_class("FPGA-SDC");
|
||||||
|
|
||||||
|
/********************************
|
||||||
|
* Command 'write_fabric_verilog'
|
||||||
|
*/
|
||||||
|
add_openfpga_write_pnr_sdc_command(shell,
|
||||||
|
openfpga_sdc_cmd_class,
|
||||||
|
shell_cmd_build_fabric_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef OPENFPGA_SDC_COMMAND_H
|
||||||
|
#define OPENFPGA_SDC_COMMAND_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include "shell.h"
|
||||||
|
#include "openfpga_context.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void add_openfpga_sdc_commands(openfpga::Shell<OpenfpgaContext>& shell);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -14,9 +14,199 @@
|
||||||
/* begin namespace openfpga */
|
/* begin namespace openfpga */
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: read_openfpga_arch
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
ShellCommandId add_openfpga_read_arch_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id) {
|
||||||
|
Command shell_cmd("read_openfpga_arch");
|
||||||
|
|
||||||
|
/* Add an option '--file' in short '-f'*/
|
||||||
|
CommandOptionId opt_arch_file = shell_cmd.add_option("file", true, "file path to the architecture XML");
|
||||||
|
shell_cmd.set_option_short_name(opt_arch_file, "f");
|
||||||
|
shell_cmd.set_option_require_value(opt_arch_file, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add command 'read_openfpga_arch' to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "read OpenFPGA architecture file");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_execute_function(shell_cmd_id, read_arch);
|
||||||
|
|
||||||
|
return shell_cmd_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: write_openfpga_arch
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
ShellCommandId add_openfpga_write_arch_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const std::vector<ShellCommandId>& dependent_cmds) {
|
||||||
|
Command shell_cmd("write_openfpga_arch");
|
||||||
|
/* Add an option '--file' in short '-f'*/
|
||||||
|
CommandOptionId opt_file = shell_cmd.add_option("file", true, "file path to the architecture XML");
|
||||||
|
shell_cmd.set_option_short_name(opt_file, "f");
|
||||||
|
shell_cmd.set_option_require_value(opt_file, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add command 'write_openfpga_arch' to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "write OpenFPGA architecture file");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_const_execute_function(shell_cmd_id, write_arch);
|
||||||
|
|
||||||
|
/* The 'write_openfpga_arch' command should NOT be executed before 'read_openfpga_arch' */
|
||||||
|
shell.set_command_dependency(shell_cmd_id, dependent_cmds);
|
||||||
|
|
||||||
|
return shell_cmd_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: link_openfpga_arch
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
ShellCommandId add_openfpga_link_arch_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const std::vector<ShellCommandId>& dependent_cmds) {
|
||||||
|
Command shell_cmd("link_openfpga_arch");
|
||||||
|
|
||||||
|
/* Add an option '--activity_file'*/
|
||||||
|
CommandOptionId opt_act_file = shell_cmd.add_option("activity_file", true, "file path to the signal activity");
|
||||||
|
shell_cmd.set_option_require_value(opt_act_file, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add an option '--verbose' */
|
||||||
|
shell_cmd.add_option("verbose", false, "Show verbose outputs");
|
||||||
|
|
||||||
|
/* Add command 'link_openfpga_arch' to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Bind OpenFPGA architecture to VPR");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_execute_function(shell_cmd_id, link_arch);
|
||||||
|
|
||||||
|
/* The 'link_openfpga_arch' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */
|
||||||
|
shell.set_command_dependency(shell_cmd_id, dependent_cmds);
|
||||||
|
|
||||||
|
return shell_cmd_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: check_netlist_naming_conflict
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
ShellCommandId add_openfpga_check_netlist_naming_conflict_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const std::vector<ShellCommandId>& dependent_cmds) {
|
||||||
|
|
||||||
|
Command shell_cmd("check_netlist_naming_conflict");
|
||||||
|
|
||||||
|
/* Add an option '--fix' */
|
||||||
|
shell_cmd.add_option("fix", false, "Apply correction to any conflicts found");
|
||||||
|
|
||||||
|
/* Add an option '--report' */
|
||||||
|
CommandOptionId opt_rpt = shell_cmd.add_option("report", false, "Output a report file about what any correction applied");
|
||||||
|
shell_cmd.set_option_require_value(opt_rpt, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add command 'check_netlist_naming_conflict' to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Check any block/net naming in users' BLIF netlist violates the syntax of fabric generator");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_execute_function(shell_cmd_id, check_netlist_naming_conflict);
|
||||||
|
|
||||||
|
/* The 'link_openfpga_arch' command should NOT be executed before 'vpr' */
|
||||||
|
shell.set_command_dependency(shell_cmd_id, dependent_cmds);
|
||||||
|
|
||||||
|
return shell_cmd_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: pb_pin_fixup
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
ShellCommandId add_openfpga_pb_pin_fixup_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const std::vector<ShellCommandId>& dependent_cmds) {
|
||||||
|
|
||||||
|
Command shell_cmd("pb_pin_fixup");
|
||||||
|
/* Add an option '--verbose' */
|
||||||
|
shell_cmd.add_option("verbose", false, "Show verbose outputs");
|
||||||
|
|
||||||
|
/* Add command 'pb_pin_fixup' to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Fix up the packing results due to pin swapping during routing stage");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_execute_function(shell_cmd_id, pb_pin_fixup);
|
||||||
|
|
||||||
|
/* The 'pb_pin_fixup' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */
|
||||||
|
shell.set_command_dependency(shell_cmd_id, dependent_cmds);
|
||||||
|
|
||||||
|
return shell_cmd_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: lut_truth_table_fixup
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
ShellCommandId add_openfpga_lut_truth_table_fixup_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const std::vector<ShellCommandId>& dependent_cmds) {
|
||||||
|
|
||||||
|
Command shell_cmd("lut_truth_table_fixup");
|
||||||
|
/* Add an option '--verbose' */
|
||||||
|
shell_cmd.add_option("verbose", false, "Show verbose outputs");
|
||||||
|
|
||||||
|
/* Add command 'lut_truth_table_fixup' to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Fix up the truth table of Look-Up Tables due to pin swapping during packing stage");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_execute_function(shell_cmd_id, lut_truth_table_fixup);
|
||||||
|
|
||||||
|
/* The 'lut_truth_table_fixup' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */
|
||||||
|
shell.set_command_dependency(shell_cmd_id, dependent_cmds);
|
||||||
|
|
||||||
|
return shell_cmd_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: build_fabric
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
ShellCommandId add_openfpga_build_fabric_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const std::vector<ShellCommandId>& dependent_cmds) {
|
||||||
|
|
||||||
|
Command shell_cmd("build_fabric");
|
||||||
|
|
||||||
|
/* Add an option '--compress_routing' */
|
||||||
|
shell_cmd.add_option("compress_routing", false, "Compress the number of unique routing modules by identifying the unique GSBs");
|
||||||
|
|
||||||
|
/* Add an option '--duplicate_grid_pin' */
|
||||||
|
shell_cmd.add_option("duplicate_grid_pin", false, "Duplicate the pins on the same side of a grid");
|
||||||
|
|
||||||
|
/* Add an option '--verbose' */
|
||||||
|
shell_cmd.add_option("verbose", false, "Show verbose outputs");
|
||||||
|
|
||||||
|
/* Add command 'compact_routing_hierarchy' to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Build the FPGA fabric in a graph of modules");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_execute_function(shell_cmd_id, build_fabric);
|
||||||
|
|
||||||
|
/* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */
|
||||||
|
shell.set_command_dependency(shell_cmd_id, dependent_cmds);
|
||||||
|
|
||||||
|
return shell_cmd_id;
|
||||||
|
}
|
||||||
|
|
||||||
void add_openfpga_setup_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
void add_openfpga_setup_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
||||||
/* Get the unique id of 'vpr' command which is to be used in creating the dependency graph */
|
/* Get the unique id of 'vpr' command which is to be used in creating the dependency graph */
|
||||||
const ShellCommandId& shell_cmd_vpr_id = shell.command(std::string("vpr"));
|
const ShellCommandId& vpr_cmd_id = shell.command(std::string("vpr"));
|
||||||
|
|
||||||
/* Add a new class of commands */
|
/* Add a new class of commands */
|
||||||
ShellCommandClassId openfpga_setup_cmd_class = shell.add_command_class("OpenFPGA setup");
|
ShellCommandClassId openfpga_setup_cmd_class = shell.add_command_class("OpenFPGA setup");
|
||||||
|
@ -24,119 +214,62 @@ void add_openfpga_setup_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
||||||
/********************************
|
/********************************
|
||||||
* Command 'read_openfpga_arch'
|
* Command 'read_openfpga_arch'
|
||||||
*/
|
*/
|
||||||
Command shell_cmd_read_arch("read_openfpga_arch");
|
ShellCommandId read_arch_cmd_id = add_openfpga_read_arch_command(shell,
|
||||||
/* Add an option '--file' in short '-f'*/
|
openfpga_setup_cmd_class);
|
||||||
CommandOptionId read_arch_opt_file = shell_cmd_read_arch.add_option("file", true, "file path to the architecture XML");
|
|
||||||
shell_cmd_read_arch.set_option_short_name(read_arch_opt_file, "f");
|
|
||||||
shell_cmd_read_arch.set_option_require_value(read_arch_opt_file, openfpga::OPT_STRING);
|
|
||||||
|
|
||||||
/* Add command 'read_openfpga_arch' to the Shell */
|
|
||||||
ShellCommandId shell_cmd_read_arch_id = shell.add_command(shell_cmd_read_arch, "read OpenFPGA architecture file");
|
|
||||||
shell.set_command_class(shell_cmd_read_arch_id, openfpga_setup_cmd_class);
|
|
||||||
shell.set_command_execute_function(shell_cmd_read_arch_id, read_arch);
|
|
||||||
|
|
||||||
/********************************
|
/********************************
|
||||||
* Command 'write_openfpga_arch'
|
* Command 'write_openfpga_arch'
|
||||||
*/
|
*/
|
||||||
Command shell_cmd_write_arch("write_openfpga_arch");
|
std::vector<ShellCommandId> write_arch_dependent_cmds(1, read_arch_cmd_id);
|
||||||
/* Add an option '--file' in short '-f'*/
|
add_openfpga_write_arch_command(shell,
|
||||||
CommandOptionId write_arch_opt_file = shell_cmd_write_arch.add_option("file", true, "file path to the architecture XML");
|
openfpga_setup_cmd_class,
|
||||||
shell_cmd_write_arch.set_option_short_name(write_arch_opt_file, "f");
|
write_arch_dependent_cmds);
|
||||||
shell_cmd_write_arch.set_option_require_value(write_arch_opt_file, openfpga::OPT_STRING);
|
|
||||||
|
|
||||||
/* Add command 'write_openfpga_arch' to the Shell */
|
|
||||||
ShellCommandId shell_cmd_write_arch_id = shell.add_command(shell_cmd_write_arch, "write OpenFPGA architecture file");
|
|
||||||
shell.set_command_class(shell_cmd_write_arch_id, openfpga_setup_cmd_class);
|
|
||||||
shell.set_command_const_execute_function(shell_cmd_write_arch_id, write_arch);
|
|
||||||
/* The 'write_openfpga_arch' command should NOT be executed before 'read_openfpga_arch' */
|
|
||||||
shell.set_command_dependency(shell_cmd_write_arch_id, std::vector<ShellCommandId>(1, shell_cmd_read_arch_id));
|
|
||||||
|
|
||||||
/********************************
|
/********************************
|
||||||
* Command 'link_openfpga_arch'
|
* Command 'link_openfpga_arch'
|
||||||
*/
|
*/
|
||||||
Command shell_cmd_link_openfpga_arch("link_openfpga_arch");
|
std::vector<ShellCommandId> link_arch_dependent_cmds;
|
||||||
/* Add an option '--verbose' */
|
link_arch_dependent_cmds.push_back(read_arch_cmd_id);
|
||||||
shell_cmd_link_openfpga_arch.add_option("verbose", false, "Show verbose outputs");
|
link_arch_dependent_cmds.push_back(vpr_cmd_id);
|
||||||
|
ShellCommandId link_arch_cmd_id = add_openfpga_link_arch_command(shell,
|
||||||
/* Add command 'link_openfpga_arch' to the Shell */
|
openfpga_setup_cmd_class,
|
||||||
ShellCommandId shell_cmd_link_openfpga_arch_id = shell.add_command(shell_cmd_link_openfpga_arch, "Bind OpenFPGA architecture to VPR");
|
link_arch_dependent_cmds);
|
||||||
shell.set_command_class(shell_cmd_link_openfpga_arch_id, openfpga_setup_cmd_class);
|
|
||||||
shell.set_command_execute_function(shell_cmd_link_openfpga_arch_id, link_arch);
|
|
||||||
/* The 'link_openfpga_arch' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */
|
|
||||||
std::vector<ShellCommandId> cmd_dependency_link_openfpga_arch;
|
|
||||||
cmd_dependency_link_openfpga_arch.push_back(shell_cmd_read_arch_id);
|
|
||||||
cmd_dependency_link_openfpga_arch.push_back(shell_cmd_vpr_id);
|
|
||||||
shell.set_command_dependency(shell_cmd_link_openfpga_arch_id, cmd_dependency_link_openfpga_arch);
|
|
||||||
|
|
||||||
/*******************************************
|
/*******************************************
|
||||||
* Command 'check_netlist_naming_conflict'
|
* Command 'check_netlist_naming_conflict'
|
||||||
*/
|
*/
|
||||||
Command shell_cmd_check_netlist_naming_conflict("check_netlist_naming_conflict");
|
std::vector<ShellCommandId> nlist_naming_dependent_cmds;
|
||||||
/* Add an option '--fix' */
|
nlist_naming_dependent_cmds.push_back(vpr_cmd_id);
|
||||||
shell_cmd_check_netlist_naming_conflict.add_option("fix", false, "Apply correction to any conflicts found");
|
add_openfpga_check_netlist_naming_conflict_command(shell,
|
||||||
/* Add an option '--report' */
|
openfpga_setup_cmd_class,
|
||||||
CommandOptionId check_netlist_opt_rpt = shell_cmd_check_netlist_naming_conflict.add_option("report", false, "Output a report file about what any correction applied");
|
nlist_naming_dependent_cmds);
|
||||||
shell_cmd_check_netlist_naming_conflict.set_option_require_value(check_netlist_opt_rpt, openfpga::OPT_STRING);
|
|
||||||
|
|
||||||
/* Add command 'check_netlist_naming_conflict' to the Shell */
|
|
||||||
ShellCommandId shell_cmd_check_netlist_naming_conflict_id = shell.add_command(shell_cmd_check_netlist_naming_conflict, "Check any block/net naming in users' BLIF netlist violates the syntax of fabric generator");
|
|
||||||
shell.set_command_class(shell_cmd_check_netlist_naming_conflict_id, openfpga_setup_cmd_class);
|
|
||||||
shell.set_command_execute_function(shell_cmd_check_netlist_naming_conflict_id, check_netlist_naming_conflict);
|
|
||||||
/* The 'link_openfpga_arch' command should NOT be executed before 'vpr' */
|
|
||||||
std::vector<ShellCommandId> cmd_dependency_check_netlist_naming_conflict(1, shell_cmd_vpr_id);
|
|
||||||
shell.set_command_dependency(shell_cmd_link_openfpga_arch_id, cmd_dependency_check_netlist_naming_conflict);
|
|
||||||
|
|
||||||
/********************************
|
/********************************
|
||||||
* Command 'pb_pin_fixup'
|
* Command 'pb_pin_fixup'
|
||||||
*/
|
*/
|
||||||
Command shell_cmd_pb_pin_fixup("pb_pin_fixup");
|
std::vector<ShellCommandId> pb_pin_fixup_dependent_cmds;
|
||||||
/* Add an option '--verbose' */
|
pb_pin_fixup_dependent_cmds.push_back(read_arch_cmd_id);
|
||||||
shell_cmd_pb_pin_fixup.add_option("verbose", false, "Show verbose outputs");
|
pb_pin_fixup_dependent_cmds.push_back(vpr_cmd_id);
|
||||||
|
add_openfpga_pb_pin_fixup_command(shell,
|
||||||
/* Add command 'pb_pin_fixup' to the Shell */
|
openfpga_setup_cmd_class,
|
||||||
ShellCommandId shell_cmd_pb_pin_fixup_id = shell.add_command(shell_cmd_pb_pin_fixup, "Fix up the packing results due to pin swapping during routing stage");
|
pb_pin_fixup_dependent_cmds);
|
||||||
shell.set_command_class(shell_cmd_pb_pin_fixup_id, openfpga_setup_cmd_class);
|
|
||||||
shell.set_command_execute_function(shell_cmd_pb_pin_fixup_id, pb_pin_fixup);
|
|
||||||
/* The 'pb_pin_fixup' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */
|
|
||||||
std::vector<ShellCommandId> cmd_dependency_pb_pin_fixup;
|
|
||||||
cmd_dependency_pb_pin_fixup.push_back(shell_cmd_read_arch_id);
|
|
||||||
cmd_dependency_pb_pin_fixup.push_back(shell_cmd_vpr_id);
|
|
||||||
shell.set_command_dependency(shell_cmd_pb_pin_fixup_id, cmd_dependency_pb_pin_fixup);
|
|
||||||
|
|
||||||
/********************************
|
/********************************
|
||||||
* Command 'lut_truth_table_fixup'
|
* Command 'lut_truth_table_fixup'
|
||||||
*/
|
*/
|
||||||
Command shell_cmd_lut_truth_table_fixup("lut_truth_table_fixup");
|
std::vector<ShellCommandId> lut_tt_fixup_dependent_cmds;
|
||||||
/* Add an option '--verbose' */
|
lut_tt_fixup_dependent_cmds.push_back(read_arch_cmd_id);
|
||||||
shell_cmd_lut_truth_table_fixup.add_option("verbose", false, "Show verbose outputs");
|
lut_tt_fixup_dependent_cmds.push_back(vpr_cmd_id);
|
||||||
|
add_openfpga_lut_truth_table_fixup_command(shell,
|
||||||
/* Add command 'lut_truth_table_fixup' to the Shell */
|
openfpga_setup_cmd_class,
|
||||||
ShellCommandId shell_cmd_lut_truth_table_fixup_id = shell.add_command(shell_cmd_lut_truth_table_fixup, "Fix up the truth table of Look-Up Tables due to pin swapping during packing stage");
|
lut_tt_fixup_dependent_cmds);
|
||||||
shell.set_command_class(shell_cmd_lut_truth_table_fixup_id, openfpga_setup_cmd_class);
|
|
||||||
shell.set_command_execute_function(shell_cmd_lut_truth_table_fixup_id, lut_truth_table_fixup);
|
|
||||||
/* The 'lut_truth_table_fixup' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */
|
|
||||||
std::vector<ShellCommandId> cmd_dependency_lut_truth_table_fixup;
|
|
||||||
cmd_dependency_lut_truth_table_fixup.push_back(shell_cmd_read_arch_id);
|
|
||||||
cmd_dependency_lut_truth_table_fixup.push_back(shell_cmd_vpr_id);
|
|
||||||
shell.set_command_dependency(shell_cmd_lut_truth_table_fixup_id, cmd_dependency_lut_truth_table_fixup);
|
|
||||||
|
|
||||||
/********************************
|
/********************************
|
||||||
* Command 'build_fabric'
|
* Command 'build_fabric'
|
||||||
*/
|
*/
|
||||||
Command shell_cmd_build_fabric("build_fabric");
|
std::vector<ShellCommandId> build_fabric_dependent_cmds;
|
||||||
/* Add an option '--verbose' */
|
build_fabric_dependent_cmds.push_back(link_arch_cmd_id);
|
||||||
shell_cmd_build_fabric.add_option("compress_routing", false, "Compress the number of unique routing modules by identifying the unique GSBs");
|
add_openfpga_build_fabric_command(shell,
|
||||||
shell_cmd_build_fabric.add_option("duplicate_grid_pin", false, "Duplicate the pins on the same side of a grid");
|
openfpga_setup_cmd_class,
|
||||||
shell_cmd_build_fabric.add_option("verbose", false, "Show verbose outputs");
|
build_fabric_dependent_cmds);
|
||||||
|
|
||||||
/* Add command 'compact_routing_hierarchy' to the Shell */
|
|
||||||
ShellCommandId shell_cmd_build_fabric_id = shell.add_command(shell_cmd_build_fabric, "Build the FPGA fabric in a graph of modules");
|
|
||||||
shell.set_command_class(shell_cmd_build_fabric_id, openfpga_setup_cmd_class);
|
|
||||||
shell.set_command_execute_function(shell_cmd_build_fabric_id, build_fabric);
|
|
||||||
/* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */
|
|
||||||
std::vector<ShellCommandId> cmd_dependency_build_fabric;
|
|
||||||
cmd_dependency_build_fabric.push_back(shell_cmd_link_openfpga_arch_id);
|
|
||||||
shell.set_command_dependency(shell_cmd_build_fabric_id, cmd_dependency_build_fabric);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* A wrapper function to call the fabric_verilog function of FPGA-Verilog
|
* A wrapper function to call the fabric Verilog generator of FPGA-Verilog
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
|
void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
|
||||||
const Command& cmd, const CommandContext& cmd_context) {
|
const Command& cmd, const CommandContext& cmd_context) {
|
||||||
|
@ -26,9 +26,6 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
|
||||||
CommandOptionId opt_include_signal_init = cmd.option("include_signal_init");
|
CommandOptionId opt_include_signal_init = cmd.option("include_signal_init");
|
||||||
CommandOptionId opt_support_icarus_simulator = cmd.option("support_icarus_simulator");
|
CommandOptionId opt_support_icarus_simulator = cmd.option("support_icarus_simulator");
|
||||||
CommandOptionId opt_print_user_defined_template = cmd.option("print_user_defined_template");
|
CommandOptionId opt_print_user_defined_template = cmd.option("print_user_defined_template");
|
||||||
CommandOptionId opt_print_top_testbench = cmd.option("print_top_testbench");
|
|
||||||
CommandOptionId opt_print_formal_verification_top_netlist = cmd.option("print_formal_verification_top_netlist");
|
|
||||||
CommandOptionId opt_print_autocheck_top_testbench = cmd.option("print_autocheck_top_testbench");
|
|
||||||
CommandOptionId opt_verbose = cmd.option("verbose");
|
CommandOptionId opt_verbose = cmd.option("verbose");
|
||||||
|
|
||||||
/* This is an intermediate data structure which is designed to modularize the FPGA-Verilog
|
/* This is an intermediate data structure which is designed to modularize the FPGA-Verilog
|
||||||
|
@ -41,9 +38,6 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
|
||||||
options.set_include_signal_init(cmd_context.option_enable(cmd, opt_include_signal_init));
|
options.set_include_signal_init(cmd_context.option_enable(cmd, opt_include_signal_init));
|
||||||
options.set_support_icarus_simulator(cmd_context.option_enable(cmd, opt_support_icarus_simulator));
|
options.set_support_icarus_simulator(cmd_context.option_enable(cmd, opt_support_icarus_simulator));
|
||||||
options.set_print_user_defined_template(cmd_context.option_enable(cmd, opt_print_user_defined_template));
|
options.set_print_user_defined_template(cmd_context.option_enable(cmd, opt_print_user_defined_template));
|
||||||
options.set_print_top_testbench(cmd_context.option_enable(cmd, opt_print_top_testbench));
|
|
||||||
options.set_print_formal_verification_top_netlist(cmd_context.option_enable(cmd, opt_print_formal_verification_top_netlist));
|
|
||||||
options.set_print_autocheck_top_testbench(cmd_context.option_value(cmd, opt_print_autocheck_top_testbench));
|
|
||||||
options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose));
|
options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose));
|
||||||
options.set_compress_routing(openfpga_ctx.flow_manager().compress_routing());
|
options.set_compress_routing(openfpga_ctx.flow_manager().compress_routing());
|
||||||
|
|
||||||
|
@ -56,4 +50,43 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
|
||||||
options);
|
options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* A wrapper function to call the Verilog testbench generator of FPGA-Verilog
|
||||||
|
*******************************************************************/
|
||||||
|
void write_verilog_testbench(OpenfpgaContext& openfpga_ctx,
|
||||||
|
const Command& cmd, const CommandContext& cmd_context) {
|
||||||
|
|
||||||
|
CommandOptionId opt_output_dir = cmd.option("file");
|
||||||
|
CommandOptionId opt_reference_benchmark = cmd.option("reference_benchmark_file_path");
|
||||||
|
CommandOptionId opt_print_top_testbench = cmd.option("print_top_testbench");
|
||||||
|
CommandOptionId opt_print_formal_verification_top_netlist = cmd.option("print_formal_verification_top_netlist");
|
||||||
|
CommandOptionId opt_print_preconfig_top_testbench = cmd.option("print_preconfig_top_testbench");
|
||||||
|
CommandOptionId opt_print_simulation_ini = cmd.option("print_simulation_ini");
|
||||||
|
CommandOptionId opt_verbose = cmd.option("verbose");
|
||||||
|
|
||||||
|
/* This is an intermediate data structure which is designed to modularize the FPGA-Verilog
|
||||||
|
* Keep it independent from any other outside data structures
|
||||||
|
*/
|
||||||
|
VerilogTestbenchOption options;
|
||||||
|
options.set_output_directory(cmd_context.option_value(cmd, opt_output_dir));
|
||||||
|
options.set_reference_benchmark_file_path(cmd_context.option_value(cmd, opt_reference_benchmark));
|
||||||
|
options.set_print_formal_verification_top_netlist(cmd_context.option_enable(cmd, opt_print_formal_verification_top_netlist));
|
||||||
|
options.set_print_preconfig_top_testbench(cmd_context.option_enable(cmd, opt_print_preconfig_top_testbench));
|
||||||
|
options.set_print_top_testbench(cmd_context.option_enable(cmd, opt_print_top_testbench));
|
||||||
|
options.set_print_simulation_ini(cmd_context.option_value(cmd, opt_print_simulation_ini));
|
||||||
|
options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose));
|
||||||
|
|
||||||
|
fpga_verilog_testbench(openfpga_ctx.module_graph(),
|
||||||
|
openfpga_ctx.bitstream_manager(),
|
||||||
|
openfpga_ctx.fabric_bitstream(),
|
||||||
|
g_vpr_ctx.atom(),
|
||||||
|
g_vpr_ctx.placement(),
|
||||||
|
openfpga_ctx.io_location_map(),
|
||||||
|
openfpga_ctx.vpr_netlist_annotation(),
|
||||||
|
openfpga_ctx.arch().circuit_lib,
|
||||||
|
openfpga_ctx.arch().sim_setting,
|
||||||
|
openfpga_ctx.arch().config_protocol.type(),
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -18,6 +18,9 @@ namespace openfpga {
|
||||||
void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
|
void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
|
||||||
const Command& cmd, const CommandContext& cmd_context);
|
const Command& cmd, const CommandContext& cmd_context);
|
||||||
|
|
||||||
|
void write_verilog_testbench(OpenfpgaContext& openfpga_ctx,
|
||||||
|
const Command& cmd, const CommandContext& cmd_context);
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -11,6 +11,98 @@
|
||||||
/* begin namespace openfpga */
|
/* begin namespace openfpga */
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: generate fabric Verilog
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void add_openfpga_write_fabric_verilog_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const ShellCommandId& shell_cmd_build_fabric_id) {
|
||||||
|
Command shell_cmd("write_fabric_verilog");
|
||||||
|
|
||||||
|
/* Add an option '--file' in short '-f'*/
|
||||||
|
CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for Verilog netlists");
|
||||||
|
shell_cmd.set_option_short_name(output_opt, "f");
|
||||||
|
shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add an option '--explicit_port_mapping' */
|
||||||
|
shell_cmd.add_option("explicit_port_mapping", false, "Use explicit port mapping in Verilog netlists");
|
||||||
|
|
||||||
|
/* Add an option '--include_timing' */
|
||||||
|
shell_cmd.add_option("include_timing", false, "Enable timing annotation in Verilog netlists");
|
||||||
|
|
||||||
|
/* Add an option '--include_signal_init' */
|
||||||
|
shell_cmd.add_option("include_signal_init", false, "Initialize all the signals in Verilog netlists");
|
||||||
|
|
||||||
|
/* Add an option '--support_icarus_simulator' */
|
||||||
|
shell_cmd.add_option("support_icarus_simulator", false, "Fine-tune Verilog netlists to support icarus simulator");
|
||||||
|
|
||||||
|
/* Add an option '--print_user_defined_template' */
|
||||||
|
shell_cmd.add_option("print_user_defined_template", false, "Generate a template Verilog files for user-defined circuit models");
|
||||||
|
|
||||||
|
/* Add an option '--verbose' */
|
||||||
|
shell_cmd.add_option("verbose", false, "Enable verbose output");
|
||||||
|
|
||||||
|
/* Add command 'write_fabric_verilog' to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate Verilog netlists modeling full FPGA fabric");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_execute_function(shell_cmd_id, write_fabric_verilog);
|
||||||
|
|
||||||
|
/* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */
|
||||||
|
std::vector<ShellCommandId> cmd_dependency;
|
||||||
|
cmd_dependency.push_back(shell_cmd_build_fabric_id);
|
||||||
|
shell.set_command_dependency(shell_cmd_id, cmd_dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* - Add a command to Shell environment: write Verilog testbench
|
||||||
|
* - Add associated options
|
||||||
|
* - Add command dependency
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void add_openfpga_write_verilog_testbench_command(openfpga::Shell<OpenfpgaContext>& shell,
|
||||||
|
const ShellCommandClassId& cmd_class_id,
|
||||||
|
const ShellCommandId& shell_cmd_build_fabric_id) {
|
||||||
|
Command shell_cmd("write_verilog_testbench");
|
||||||
|
|
||||||
|
/* Add an option '--file' in short '-f'*/
|
||||||
|
CommandOptionId output_opt = shell_cmd.add_option("file", true, "Specify the output directory for Verilog netlists");
|
||||||
|
shell_cmd.set_option_short_name(output_opt, "f");
|
||||||
|
shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add an option '--reference_benchmark_file_path'*/
|
||||||
|
CommandOptionId ref_bm_opt = shell_cmd.add_option("reference_benchmark_file_path", true, "Specify the file path to the reference Verilog netlist");
|
||||||
|
shell_cmd.set_option_require_value(ref_bm_opt, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add an option '--print_top_testbench' */
|
||||||
|
shell_cmd.add_option("print_top_testbench", false, "Generate a full testbench for top-level fabric module with autocheck capability");
|
||||||
|
|
||||||
|
/* Add an option '--print_formal_verification_top_netlist' */
|
||||||
|
shell_cmd.add_option("print_formal_verification_top_netlist", false, "Generate a top-level module which can be used in formal verification");
|
||||||
|
|
||||||
|
/* Add an option '--print_preconfig_top_testbench' */
|
||||||
|
shell_cmd.add_option("print_preconfig_top_testbench", false, "Generate a pre-configured testbench for top-level fabric module with autocheck capability");
|
||||||
|
|
||||||
|
/* Add an option '--print_simulation_ini' */
|
||||||
|
CommandOptionId sim_ini_opt = shell_cmd.add_option("print_simulation_ini", false, "Generate a .ini file as an exchangeable file to enable HDL simulations");
|
||||||
|
shell_cmd.set_option_require_value(sim_ini_opt, openfpga::OPT_STRING);
|
||||||
|
|
||||||
|
/* Add an option '--verbose' */
|
||||||
|
shell_cmd.add_option("verbose", false, "Enable verbose output");
|
||||||
|
|
||||||
|
/* Add command to the Shell */
|
||||||
|
ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "generate Verilog testbenches for full FPGA fabric");
|
||||||
|
shell.set_command_class(shell_cmd_id, cmd_class_id);
|
||||||
|
shell.set_command_execute_function(shell_cmd_id, write_verilog_testbench);
|
||||||
|
|
||||||
|
/* The command should NOT be executed before 'build_fabric' */
|
||||||
|
std::vector<ShellCommandId> cmd_dependency;
|
||||||
|
cmd_dependency.push_back(shell_cmd_build_fabric_id);
|
||||||
|
shell.set_command_dependency(shell_cmd_id, cmd_dependency);
|
||||||
|
}
|
||||||
|
|
||||||
void add_openfpga_verilog_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
void add_openfpga_verilog_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
||||||
/* Get the unique id of 'build_fabric' command which is to be used in creating the dependency graph */
|
/* Get the unique id of 'build_fabric' command which is to be used in creating the dependency graph */
|
||||||
const ShellCommandId& shell_cmd_build_fabric_id = shell.command(std::string("build_fabric"));
|
const ShellCommandId& shell_cmd_build_fabric_id = shell.command(std::string("build_fabric"));
|
||||||
|
@ -19,42 +111,18 @@ void add_openfpga_verilog_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
||||||
ShellCommandClassId openfpga_verilog_cmd_class = shell.add_command_class("FPGA-Verilog");
|
ShellCommandClassId openfpga_verilog_cmd_class = shell.add_command_class("FPGA-Verilog");
|
||||||
|
|
||||||
/********************************
|
/********************************
|
||||||
* Command 'wirte_fabric_verilog'
|
* Command 'write_fabric_verilog'
|
||||||
*/
|
*/
|
||||||
Command shell_cmd_write_fabric_verilog("write_fabric_verilog");
|
add_openfpga_write_fabric_verilog_command(shell,
|
||||||
/* Add an option '--file' in short '-f'*/
|
openfpga_verilog_cmd_class,
|
||||||
CommandOptionId fabric_verilog_output_opt = shell_cmd_write_fabric_verilog.add_option("file", true, "Specify the output directory for Verilog netlists");
|
shell_cmd_build_fabric_id);
|
||||||
shell_cmd_write_fabric_verilog.set_option_short_name(fabric_verilog_output_opt, "f");
|
|
||||||
shell_cmd_write_fabric_verilog.set_option_require_value(fabric_verilog_output_opt, openfpga::OPT_STRING);
|
|
||||||
/* Add an option '--explicit_port_mapping' */
|
|
||||||
shell_cmd_write_fabric_verilog.add_option("explicit_port_mapping", false, "Use explicit port mapping in Verilog netlists");
|
|
||||||
/* Add an option '--include_timing' */
|
|
||||||
shell_cmd_write_fabric_verilog.add_option("include_timing", false, "Enable timing annotation in Verilog netlists");
|
|
||||||
/* Add an option '--include_signal_init' */
|
|
||||||
shell_cmd_write_fabric_verilog.add_option("include_signal_init", false, "Initialize all the signals in Verilog netlists");
|
|
||||||
/* Add an option '--support_icarus_simulator' */
|
|
||||||
shell_cmd_write_fabric_verilog.add_option("support_icarus_simulator", false, "Fine-tune Verilog netlists to support icarus simulator");
|
|
||||||
/* Add an option '--print_user_defined_template' */
|
|
||||||
shell_cmd_write_fabric_verilog.add_option("print_user_defined_template", false, "Generate a template Verilog files for user-defined circuit models");
|
|
||||||
/* Add an option '--print_top_testbench' */
|
|
||||||
shell_cmd_write_fabric_verilog.add_option("print_top_testbench", false, "Generate a testbench for top-level fabric module");
|
|
||||||
/* Add an option '--print_formal_verification_top_netlist' */
|
|
||||||
shell_cmd_write_fabric_verilog.add_option("print_formal_verification_top_netlist", false, "Generate a top-level module which can be used in formal verification");
|
|
||||||
/* Add an option '--print_autocheck_top_testbench' */
|
|
||||||
CommandOptionId fabric_verilog_autocheck_tb_opt = shell_cmd_write_fabric_verilog.add_option("print_autocheck_top_testbench", false, "Generate a testbench for top-level fabric module with autocheck capability");
|
|
||||||
shell_cmd_write_fabric_verilog.set_option_require_value(fabric_verilog_autocheck_tb_opt, openfpga::OPT_STRING);
|
|
||||||
/* Add an option '--verbose' */
|
|
||||||
shell_cmd_write_fabric_verilog.add_option("verbose", false, "Enable verbose output");
|
|
||||||
|
|
||||||
/* Add command 'write_fabric_verilog' to the Shell */
|
|
||||||
ShellCommandId shell_cmd_write_fabric_verilog_id = shell.add_command(shell_cmd_write_fabric_verilog, "generate Verilog netlists modeling full FPGA fabric");
|
|
||||||
shell.set_command_class(shell_cmd_write_fabric_verilog_id, openfpga_verilog_cmd_class);
|
|
||||||
shell.set_command_execute_function(shell_cmd_write_fabric_verilog_id, write_fabric_verilog);
|
|
||||||
|
|
||||||
/* The 'build_fabric' command should NOT be executed before 'link_openfpga_arch' */
|
/********************************
|
||||||
std::vector<ShellCommandId> cmd_dependency_write_fabric_verilog;
|
* Command 'write_verilog_testbench'
|
||||||
cmd_dependency_write_fabric_verilog.push_back(shell_cmd_build_fabric_id);
|
*/
|
||||||
shell.set_command_dependency(shell_cmd_write_fabric_verilog_id, cmd_dependency_write_fabric_verilog);
|
add_openfpga_write_verilog_testbench_command(shell,
|
||||||
|
openfpga_verilog_cmd_class,
|
||||||
|
shell_cmd_build_fabric_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -26,7 +26,8 @@ namespace openfpga {
|
||||||
* The main function to be called for building module graphs
|
* The main function to be called for building module graphs
|
||||||
* for a FPGA fabric
|
* for a FPGA fabric
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
ModuleManager build_device_module_graph(const DeviceContext& vpr_device_ctx,
|
ModuleManager build_device_module_graph(IoLocationMap& io_location_map,
|
||||||
|
const DeviceContext& vpr_device_ctx,
|
||||||
const OpenfpgaContext& openfpga_ctx,
|
const OpenfpgaContext& openfpga_ctx,
|
||||||
const bool& compress_routing,
|
const bool& compress_routing,
|
||||||
const bool& duplicate_grid_pin,
|
const bool& duplicate_grid_pin,
|
||||||
|
@ -97,7 +98,8 @@ ModuleManager build_device_module_graph(const DeviceContext& vpr_device_ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build FPGA fabric top-level module */
|
/* Build FPGA fabric top-level module */
|
||||||
build_top_module(module_manager, openfpga_ctx.arch().circuit_lib,
|
build_top_module(module_manager, io_location_map,
|
||||||
|
openfpga_ctx.arch().circuit_lib,
|
||||||
vpr_device_ctx.grid,
|
vpr_device_ctx.grid,
|
||||||
vpr_device_ctx.rr_graph,
|
vpr_device_ctx.rr_graph,
|
||||||
openfpga_ctx.device_rr_gsb(),
|
openfpga_ctx.device_rr_gsb(),
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
/* begin namespace openfpga */
|
/* begin namespace openfpga */
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
|
||||||
ModuleManager build_device_module_graph(const DeviceContext& vpr_device_ctx,
|
ModuleManager build_device_module_graph(IoLocationMap& io_location_map,
|
||||||
|
const DeviceContext& vpr_device_ctx,
|
||||||
const OpenfpgaContext& openfpga_ctx,
|
const OpenfpgaContext& openfpga_ctx,
|
||||||
const bool& compress_routing,
|
const bool& compress_routing,
|
||||||
const bool& duplicate_grid_pin,
|
const bool& duplicate_grid_pin,
|
||||||
|
|
|
@ -1103,7 +1103,6 @@ void build_grid_modules(ModuleManager& module_manager,
|
||||||
duplicate_grid_pin,
|
duplicate_grid_pin,
|
||||||
verbose);
|
verbose);
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
} else {
|
} else {
|
||||||
/* For CLB and heterogenenous blocks */
|
/* For CLB and heterogenenous blocks */
|
||||||
build_physical_tile_module(module_manager, circuit_lib,
|
build_physical_tile_module(module_manager, circuit_lib,
|
||||||
|
|
|
@ -89,6 +89,7 @@ size_t add_top_module_grid_instance(ModuleManager& module_manager,
|
||||||
static
|
static
|
||||||
vtr::Matrix<size_t> add_top_module_grid_instances(ModuleManager& module_manager,
|
vtr::Matrix<size_t> add_top_module_grid_instances(ModuleManager& module_manager,
|
||||||
const ModuleId& top_module,
|
const ModuleId& top_module,
|
||||||
|
IoLocationMap& io_location_map,
|
||||||
const DeviceGrid& grids) {
|
const DeviceGrid& grids) {
|
||||||
/* Reserve an array for the instance ids */
|
/* Reserve an array for the instance ids */
|
||||||
vtr::Matrix<size_t> grid_instance_ids({grids.width(), grids.height()});
|
vtr::Matrix<size_t> grid_instance_ids({grids.width(), grids.height()});
|
||||||
|
@ -142,6 +143,7 @@ vtr::Matrix<size_t> add_top_module_grid_instances(ModuleManager& module_manager,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add instances of I/O grids to top_module */
|
/* Add instances of I/O grids to top_module */
|
||||||
|
size_t io_counter = 0;
|
||||||
for (const e_side& io_side : io_sides) {
|
for (const e_side& io_side : io_sides) {
|
||||||
for (const vtr::Point<size_t>& io_coordinate : io_coordinates[io_side]) {
|
for (const vtr::Point<size_t>& io_coordinate : io_coordinates[io_side]) {
|
||||||
/* Bypass EMPTY grid */
|
/* Bypass EMPTY grid */
|
||||||
|
@ -157,6 +159,21 @@ vtr::Matrix<size_t> add_top_module_grid_instances(ModuleManager& module_manager,
|
||||||
VTR_ASSERT(true == is_io_type(grids[io_coordinate.x()][io_coordinate.y()].type));
|
VTR_ASSERT(true == is_io_type(grids[io_coordinate.x()][io_coordinate.y()].type));
|
||||||
/* Add a grid module to top_module*/
|
/* Add a grid module to top_module*/
|
||||||
grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = add_top_module_grid_instance(module_manager, top_module, grids[io_coordinate.x()][io_coordinate.y()].type, io_side, io_coordinate);
|
grid_instance_ids[io_coordinate.x()][io_coordinate.y()] = add_top_module_grid_instance(module_manager, top_module, grids[io_coordinate.x()][io_coordinate.y()].type, io_side, io_coordinate);
|
||||||
|
|
||||||
|
/* MUST DO: register in io location mapping!
|
||||||
|
* I/O location mapping is a critical look-up for testbench generators
|
||||||
|
* As we add the I/O grid instances to top module by following order:
|
||||||
|
* TOP -> RIGHT -> BOTTOM -> LEFT
|
||||||
|
* The I/O index will increase in this way as well.
|
||||||
|
* This organization I/O indices is also consistent to the way
|
||||||
|
* that GPIOs are wired in function connect_gpio_module()
|
||||||
|
*
|
||||||
|
* Note: if you change the GPIO function, you should update here as well!
|
||||||
|
*/
|
||||||
|
for (int z = 0; z < grids[io_coordinate.x()][io_coordinate.y()].type->capacity; ++z) {
|
||||||
|
io_location_map.set_io_index(io_coordinate.x(), io_coordinate.y(), z, io_counter);
|
||||||
|
io_counter++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,6 +297,7 @@ vtr::Matrix<size_t> add_top_module_connection_block_instances(ModuleManager& mod
|
||||||
* 5. Add module nets/submodules to connect configuration ports
|
* 5. Add module nets/submodules to connect configuration ports
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
void build_top_module(ModuleManager& module_manager,
|
void build_top_module(ModuleManager& module_manager,
|
||||||
|
IoLocationMap& io_location_map,
|
||||||
const CircuitLibrary& circuit_lib,
|
const CircuitLibrary& circuit_lib,
|
||||||
const DeviceGrid& grids,
|
const DeviceGrid& grids,
|
||||||
const RRGraph& rr_graph,
|
const RRGraph& rr_graph,
|
||||||
|
@ -301,7 +319,7 @@ void build_top_module(ModuleManager& module_manager,
|
||||||
|
|
||||||
/* Add sub modules, which are grid, SB and CBX/CBY modules as instances */
|
/* Add sub modules, which are grid, SB and CBX/CBY modules as instances */
|
||||||
/* Add all the grids across the fabric */
|
/* Add all the grids across the fabric */
|
||||||
vtr::Matrix<size_t> grid_instance_ids = add_top_module_grid_instances(module_manager, top_module, grids);
|
vtr::Matrix<size_t> grid_instance_ids = add_top_module_grid_instances(module_manager, top_module, io_location_map, grids);
|
||||||
/* Add all the SBs across the fabric */
|
/* Add all the SBs across the fabric */
|
||||||
vtr::Matrix<size_t> sb_instance_ids = add_top_module_switch_block_instances(module_manager, top_module, device_rr_gsb, compact_routing_hierarchy);
|
vtr::Matrix<size_t> sb_instance_ids = add_top_module_switch_block_instances(module_manager, top_module, device_rr_gsb, compact_routing_hierarchy);
|
||||||
/* Add all the CBX and CBYs across the fabric */
|
/* Add all the CBX and CBYs across the fabric */
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "tile_direct.h"
|
#include "tile_direct.h"
|
||||||
#include "arch_direct.h"
|
#include "arch_direct.h"
|
||||||
#include "module_manager.h"
|
#include "module_manager.h"
|
||||||
|
#include "io_location_map.h"
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Function declaration
|
* Function declaration
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
|
||||||
void build_top_module(ModuleManager& module_manager,
|
void build_top_module(ModuleManager& module_manager,
|
||||||
|
IoLocationMap& io_location_map,
|
||||||
const CircuitLibrary& circuit_lib,
|
const CircuitLibrary& circuit_lib,
|
||||||
const DeviceGrid& grids,
|
const DeviceGrid& grids,
|
||||||
const RRGraph& rr_graph,
|
const RRGraph& rr_graph,
|
||||||
|
|
|
@ -168,7 +168,8 @@ void build_physical_block_pin_interc_bitstream(BitstreamManager& bitstream_manag
|
||||||
for (t_pb_graph_pin* src_pb_graph_pin : pb_graph_pin_inputs(des_pb_graph_pin, cur_interc)) {
|
for (t_pb_graph_pin* src_pb_graph_pin : pb_graph_pin_inputs(des_pb_graph_pin, cur_interc)) {
|
||||||
const PhysicalPbId& src_pb_id = physical_pb.find_pb(src_pb_graph_pin->parent_node);
|
const PhysicalPbId& src_pb_id = physical_pb.find_pb(src_pb_graph_pin->parent_node);
|
||||||
/* If the src pb id is not valid, we bypass it */
|
/* If the src pb id is not valid, we bypass it */
|
||||||
if ( (true != physical_pb.valid_pb_id(src_pb_id))
|
if ( (true == physical_pb.valid_pb_id(src_pb_id))
|
||||||
|
&& (AtomNetId::INVALID() != physical_pb.pb_graph_pin_atom_net(des_pb_id, des_pb_graph_pin))
|
||||||
&& (physical_pb.pb_graph_pin_atom_net(src_pb_id, src_pb_graph_pin) == physical_pb.pb_graph_pin_atom_net(des_pb_id, des_pb_graph_pin))) {
|
&& (physical_pb.pb_graph_pin_atom_net(src_pb_id, src_pb_graph_pin) == physical_pb.pb_graph_pin_atom_net(des_pb_id, des_pb_graph_pin))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Member functions for a data structure which includes all the options for the SDC generator
|
||||||
|
********************************************************************/
|
||||||
|
#include "analysis_sdc_option.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Public Constructors
|
||||||
|
********************************************************************/
|
||||||
|
AnalysisSdcOption::AnalysisSdcOption(const std::string& sdc_dir) {
|
||||||
|
sdc_dir_ = sdc_dir;
|
||||||
|
generate_sdc_analysis_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Public accessors
|
||||||
|
********************************************************************/
|
||||||
|
std::string AnalysisSdcOption::sdc_dir() const {
|
||||||
|
return sdc_dir_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnalysisSdcOption::generate_sdc_analysis() const {
|
||||||
|
return generate_sdc_analysis_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Public mutators
|
||||||
|
********************************************************************/
|
||||||
|
void AnalysisSdcOption::set_sdc_dir(const std::string& sdc_dir) {
|
||||||
|
sdc_dir_ = sdc_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnalysisSdcOption::set_generate_sdc_analysis(const bool& generate_sdc_analysis) {
|
||||||
|
generate_sdc_analysis_ = generate_sdc_analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef ANALYSIS_SDC_OPTION_H
|
||||||
|
#define ANALYSIS_SDC_OPTION_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* A data structure to include all the options for the SDC generator
|
||||||
|
* in purpose of analyzing users' implementations
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
class AnalysisSdcOption {
|
||||||
|
public: /* Public Constructors */
|
||||||
|
AnalysisSdcOption(const std::string& sdc_dir);
|
||||||
|
public: /* Public accessors */
|
||||||
|
std::string sdc_dir() const;
|
||||||
|
bool generate_sdc_analysis() const;
|
||||||
|
public: /* Public mutators */
|
||||||
|
void set_sdc_dir(const std::string& sdc_dir);
|
||||||
|
void set_generate_sdc_analysis(const bool& generate_sdc_analysis);
|
||||||
|
private: /* Internal data */
|
||||||
|
std::string sdc_dir_;
|
||||||
|
bool generate_sdc_analysis_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,346 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file includes functions that print SDC (Synopsys Design Constraint)
|
||||||
|
* files in physical design tools, i.e., Place & Route (PnR) tools
|
||||||
|
* The SDC files are used to constrain the physical design for each grid
|
||||||
|
* (CLBs, heterogeneous blocks etc.)
|
||||||
|
*
|
||||||
|
* Note that this is different from the SDC to constrain VPR Place&Route
|
||||||
|
* engine! These SDCs are designed for PnR to generate FPGA layouts!!!
|
||||||
|
*******************************************************************/
|
||||||
|
#include <ctime>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_log.h"
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "vtr_time.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_port.h"
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
#include "openfpga_side_manager.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "openfpga_interconnect_types.h"
|
||||||
|
#include "vpr_utils.h"
|
||||||
|
#include "mux_utils.h"
|
||||||
|
|
||||||
|
#include "openfpga_reserved_words.h"
|
||||||
|
#include "openfpga_naming.h"
|
||||||
|
#include "pb_type_utils.h"
|
||||||
|
#include "pb_graph_utils.h"
|
||||||
|
|
||||||
|
#include "sdc_writer_naming.h"
|
||||||
|
#include "sdc_writer_utils.h"
|
||||||
|
#include "pnr_sdc_grid_writer.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print pin-to-pin timing constraints for a given interconnection
|
||||||
|
* at an output port of a pb_graph node
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_constrain_pb_pin_interc_timing(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& parent_module,
|
||||||
|
t_pb_graph_pin* des_pb_graph_pin,
|
||||||
|
t_mode* physical_mode) {
|
||||||
|
|
||||||
|
/* Validate file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* 1. identify pin interconnection type,
|
||||||
|
* 2. Identify the number of fan-in (Consider interconnection edges of only selected mode)
|
||||||
|
* 3. Print SDC timing constraints
|
||||||
|
*/
|
||||||
|
t_interconnect* cur_interc = pb_graph_pin_interc(des_pb_graph_pin, physical_mode);
|
||||||
|
size_t fan_in = pb_graph_pin_inputs(des_pb_graph_pin, cur_interc).size();
|
||||||
|
if ((nullptr == cur_interc) || (0 == fan_in)) {
|
||||||
|
/* No interconnection matched */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print pin-to-pin SDC contraint here */
|
||||||
|
/* For more than one mode defined, the direct interc has more than one input_edge ,
|
||||||
|
* We need to find which edge is connected the pin we want
|
||||||
|
*/
|
||||||
|
for (int iedge = 0; iedge < des_pb_graph_pin->num_input_edges; iedge++) {
|
||||||
|
if (cur_interc != des_pb_graph_pin->input_edges[iedge]->interconnect) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Source pin, node, pb_type*/
|
||||||
|
t_pb_graph_pin* src_pb_graph_pin = des_pb_graph_pin->input_edges[iedge]->input_pins[0];
|
||||||
|
t_pb_graph_node* src_pb_graph_node = src_pb_graph_pin->parent_node;
|
||||||
|
/* Des pin, node, pb_type */
|
||||||
|
t_pb_graph_node* des_pb_graph_node = des_pb_graph_pin->parent_node;
|
||||||
|
|
||||||
|
/* Find the src module in module manager */
|
||||||
|
std::string src_module_name = generate_physical_block_module_name(src_pb_graph_pin->parent_node->pb_type);
|
||||||
|
ModuleId src_module = module_manager.find_module(src_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(src_module));
|
||||||
|
|
||||||
|
ModulePortId src_module_port_id = module_manager.find_module_port(src_module, generate_pb_type_port_name(src_pb_graph_pin->port));
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_port_id(src_module, src_module_port_id));
|
||||||
|
|
||||||
|
/* Generate the name of the des instance name
|
||||||
|
* If des module is not the parent module, it is a child module.
|
||||||
|
* We should find the instance id
|
||||||
|
*/
|
||||||
|
std::string src_instance_name = src_module_name;
|
||||||
|
if (parent_module != src_module) {
|
||||||
|
src_instance_name = module_manager.module_name(parent_module) + std::string("/");
|
||||||
|
/* Instance id is actually the placement index */
|
||||||
|
size_t instance_id = src_pb_graph_node->placement_index;
|
||||||
|
if (true == module_manager.instance_name(parent_module, src_module, instance_id).empty()) {
|
||||||
|
src_instance_name += src_module_name;
|
||||||
|
src_instance_name += "_";
|
||||||
|
src_instance_name += std::to_string(instance_id);
|
||||||
|
src_instance_name += "_";
|
||||||
|
} else {
|
||||||
|
src_instance_name += module_manager.instance_name(parent_module, src_module, instance_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate src port information */
|
||||||
|
BasicPort src_port = module_manager.module_port(src_module, src_module_port_id);
|
||||||
|
src_port.set_width(src_pb_graph_pin->pin_number, src_pb_graph_pin->pin_number);
|
||||||
|
|
||||||
|
/* Find the des module in module manager */
|
||||||
|
std::string des_module_name = generate_physical_block_module_name(des_pb_graph_pin->parent_node->pb_type);
|
||||||
|
ModuleId des_module = module_manager.find_module(des_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(des_module));
|
||||||
|
ModulePortId des_module_port_id = module_manager.find_module_port(des_module, generate_pb_type_port_name(des_pb_graph_pin->port));
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_port_id(des_module, des_module_port_id));
|
||||||
|
|
||||||
|
/* Generate the name of the des instance name
|
||||||
|
* If des module is not the parent module, it is a child module.
|
||||||
|
* We should find the instance id
|
||||||
|
*/
|
||||||
|
std::string des_instance_name = des_module_name;
|
||||||
|
if (parent_module != des_module) {
|
||||||
|
des_instance_name = module_manager.module_name(parent_module) + std::string("/");
|
||||||
|
/* Instance id is actually the placement index */
|
||||||
|
size_t instance_id = des_pb_graph_node->placement_index;
|
||||||
|
if (true == module_manager.instance_name(parent_module, des_module, instance_id).empty()) {
|
||||||
|
des_instance_name += des_module_name;
|
||||||
|
des_instance_name += "_";
|
||||||
|
des_instance_name += std::to_string(instance_id);
|
||||||
|
des_instance_name += "_";
|
||||||
|
} else {
|
||||||
|
des_instance_name += module_manager.instance_name(parent_module, des_module, instance_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate des port information */
|
||||||
|
BasicPort des_port = module_manager.module_port(des_module, des_module_port_id);
|
||||||
|
des_port.set_width(des_pb_graph_pin->pin_number, des_pb_graph_pin->pin_number);
|
||||||
|
|
||||||
|
/* Print a SDC timing constraint */
|
||||||
|
print_pnr_sdc_constrain_max_delay(fp,
|
||||||
|
src_instance_name,
|
||||||
|
generate_sdc_port(src_port),
|
||||||
|
des_instance_name,
|
||||||
|
generate_sdc_port(des_port),
|
||||||
|
des_pb_graph_pin->input_edges[iedge]->delay_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print port-to-port timing constraints which source from
|
||||||
|
* an output port of a pb_graph node
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_constrain_pb_interc_timing(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& parent_module,
|
||||||
|
t_pb_graph_node* des_pb_graph_node,
|
||||||
|
const e_circuit_pb_port_type& pb_port_type,
|
||||||
|
t_mode* physical_mode) {
|
||||||
|
/* Validate file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
switch (pb_port_type) {
|
||||||
|
case CIRCUIT_PB_PORT_INPUT: {
|
||||||
|
for (int iport = 0; iport < des_pb_graph_node->num_input_ports; ++iport) {
|
||||||
|
for (int ipin = 0; ipin < des_pb_graph_node->num_input_pins[iport]; ++ipin) {
|
||||||
|
/* If this is a idle block, we set 0 to the selected edge*/
|
||||||
|
/* Get the selected edge of current pin*/
|
||||||
|
print_pnr_sdc_constrain_pb_pin_interc_timing(fp,
|
||||||
|
module_manager, parent_module,
|
||||||
|
&(des_pb_graph_node->input_pins[iport][ipin]),
|
||||||
|
physical_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CIRCUIT_PB_PORT_OUTPUT: {
|
||||||
|
for (int iport = 0; iport < des_pb_graph_node->num_output_ports; ++iport) {
|
||||||
|
for (int ipin = 0; ipin < des_pb_graph_node->num_output_pins[iport]; ++ipin) {
|
||||||
|
print_pnr_sdc_constrain_pb_pin_interc_timing(fp,
|
||||||
|
module_manager, parent_module,
|
||||||
|
&(des_pb_graph_node->output_pins[iport][ipin]),
|
||||||
|
physical_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CIRCUIT_PB_PORT_CLOCK: {
|
||||||
|
/* Do NOT constrain clock here, it should be handled by Clock Tree Synthesis */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||||
|
"Invalid pb port type!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* This function will generate a SDC file for each pb_type,
|
||||||
|
* constraining the pin-to-pin timing between
|
||||||
|
* 1. input port of parent_pb_graph_node and input port of child_pb_graph_nodes
|
||||||
|
* 2. output port of parent_pb_graph_node and output port of child_pb_graph_nodes
|
||||||
|
* 3. output port of child_pb_graph_node and input port of child_pb_graph_nodes
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_constrain_pb_graph_node_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
t_pb_graph_node* parent_pb_graph_node,
|
||||||
|
t_mode* physical_mode) {
|
||||||
|
|
||||||
|
/* Get the pb_type definition related to the node */
|
||||||
|
t_pb_type* physical_pb_type = parent_pb_graph_node->pb_type;
|
||||||
|
std::string pb_module_name = generate_physical_block_module_name(physical_pb_type);
|
||||||
|
|
||||||
|
/* Find the pb module in module manager */
|
||||||
|
ModuleId pb_module = module_manager.find_module(pb_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(pb_module));
|
||||||
|
|
||||||
|
/* Create the file name for SDC */
|
||||||
|
std::string sdc_fname(sdc_dir + pb_module_name + std::string(SDC_FILE_NAME_POSTFIX));
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
check_file_stream(sdc_fname.c_str(), fp);
|
||||||
|
|
||||||
|
/* Generate the descriptions*/
|
||||||
|
print_sdc_file_header(fp, std::string("Timing constraints for Grid " + pb_module_name + " in PnR"));
|
||||||
|
|
||||||
|
/* We check output_pins of cur_pb_graph_node and its the input_edges
|
||||||
|
* Built the interconnections between outputs of cur_pb_graph_node and outputs of child_pb_graph_node
|
||||||
|
* child_pb_graph_node.output_pins -----------------> cur_pb_graph_node.outpins
|
||||||
|
* /|\
|
||||||
|
* |
|
||||||
|
* input_pins, edges, output_pins
|
||||||
|
*/
|
||||||
|
print_pnr_sdc_constrain_pb_interc_timing(fp,
|
||||||
|
module_manager, pb_module,
|
||||||
|
parent_pb_graph_node,
|
||||||
|
CIRCUIT_PB_PORT_OUTPUT,
|
||||||
|
physical_mode);
|
||||||
|
|
||||||
|
/* We check input_pins of child_pb_graph_node and its the input_edges
|
||||||
|
* Built the interconnections between inputs of cur_pb_graph_node and inputs of child_pb_graph_node
|
||||||
|
* cur_pb_graph_node.input_pins -----------------> child_pb_graph_node.input_pins
|
||||||
|
* /|\
|
||||||
|
* |
|
||||||
|
* input_pins, edges, output_pins
|
||||||
|
*/
|
||||||
|
for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) {
|
||||||
|
for (int jpb = 0; jpb < physical_mode->pb_type_children[ipb].num_pb; ++jpb) {
|
||||||
|
t_pb_graph_node* child_pb_graph_node = &(parent_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][jpb]);
|
||||||
|
/* For each child_pb_graph_node input pins*/
|
||||||
|
print_pnr_sdc_constrain_pb_interc_timing(fp,
|
||||||
|
module_manager, pb_module,
|
||||||
|
child_pb_graph_node,
|
||||||
|
CIRCUIT_PB_PORT_INPUT,
|
||||||
|
physical_mode);
|
||||||
|
/* Do NOT constrain clock here, it should be handled by Clock Tree Synthesis */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Recursively print SDC timing constraints for a pb_type
|
||||||
|
* This function will generate a SDC file for each pb_type,
|
||||||
|
* constraining the pin-to-pin timing
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void rec_print_pnr_sdc_constrain_pb_graph_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const VprDeviceAnnotation& device_annotation,
|
||||||
|
t_pb_graph_node* parent_pb_graph_node) {
|
||||||
|
/* Validate pb_graph node */
|
||||||
|
if (nullptr == parent_pb_graph_node) {
|
||||||
|
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||||
|
"Invalid parent_pb_graph_node.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the pb_type */
|
||||||
|
t_pb_type* parent_pb_type = parent_pb_graph_node->pb_type;
|
||||||
|
|
||||||
|
/* No need to constrain the primitive node */
|
||||||
|
if (true == is_primitive_pb_type(parent_pb_type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note we only go through the graph through the physical modes.
|
||||||
|
* which we build the modules
|
||||||
|
*/
|
||||||
|
t_mode* physical_mode = device_annotation.physical_mode(parent_pb_type);
|
||||||
|
|
||||||
|
/* Write a SDC file for this pb_type */
|
||||||
|
print_pnr_sdc_constrain_pb_graph_node_timing(sdc_dir,
|
||||||
|
module_manager,
|
||||||
|
parent_pb_graph_node,
|
||||||
|
physical_mode);
|
||||||
|
|
||||||
|
/* Go recursively to the lower level in the pb_graph
|
||||||
|
* Note that we assume a full hierarchical P&R, we will only visit pb_graph_node of unique pb_type
|
||||||
|
*/
|
||||||
|
for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) {
|
||||||
|
rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, module_manager,
|
||||||
|
device_annotation,
|
||||||
|
&(parent_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Top-level function to print timing constraints for pb_types
|
||||||
|
*******************************************************************/
|
||||||
|
void print_pnr_sdc_constrain_grid_timing(const std::string& sdc_dir,
|
||||||
|
const DeviceContext& device_ctx,
|
||||||
|
const VprDeviceAnnotation& device_annotation,
|
||||||
|
const ModuleManager& module_manager) {
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
vtr::ScopedStartFinishTimer timer("Write SDC for constraining grid timing for P&R flow");
|
||||||
|
|
||||||
|
for (const t_physical_tile_type& physical_tile : device_ctx.physical_tile_types) {
|
||||||
|
/* Bypass empty type or nullptr */
|
||||||
|
if (true == is_empty_type(&physical_tile)) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT(1 == physical_tile.equivalent_sites.size());
|
||||||
|
t_pb_graph_node* pb_graph_head = physical_tile.equivalent_sites[0]->pb_graph_head;
|
||||||
|
if (nullptr == pb_graph_head) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Special for I/O block, generate one module for each border side */
|
||||||
|
rec_print_pnr_sdc_constrain_pb_graph_timing(sdc_dir, module_manager,
|
||||||
|
device_annotation,
|
||||||
|
pb_graph_head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef PNR_SDC_GRID_WRITER_H
|
||||||
|
#define PNR_SDC_GRID_WRITER_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "vpr_context.h"
|
||||||
|
#include "vpr_device_annotation.h"
|
||||||
|
#include "module_manager.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void print_pnr_sdc_constrain_grid_timing(const std::string& sdc_dir,
|
||||||
|
const DeviceContext& device_ctx,
|
||||||
|
const VprDeviceAnnotation& device_annotation,
|
||||||
|
const ModuleManager& module_manager);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,113 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Member functions for a data structure which includes all the options for the SDC generator
|
||||||
|
********************************************************************/
|
||||||
|
#include "pnr_sdc_option.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Public Constructors
|
||||||
|
********************************************************************/
|
||||||
|
PnrSdcOption::PnrSdcOption(const std::string& sdc_dir) {
|
||||||
|
sdc_dir_ = sdc_dir;
|
||||||
|
constrain_global_port_ = false;
|
||||||
|
constrain_grid_ = false;
|
||||||
|
constrain_sb_ = false;
|
||||||
|
constrain_cb_ = false;
|
||||||
|
constrain_configurable_memory_outputs_ = false;
|
||||||
|
constrain_routing_multiplexer_outputs_ = false;
|
||||||
|
constrain_switch_block_outputs_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Public accessors
|
||||||
|
********************************************************************/
|
||||||
|
std::string PnrSdcOption::sdc_dir() const {
|
||||||
|
return sdc_dir_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PnrSdcOption::generate_sdc_pnr() const {
|
||||||
|
return constrain_global_port_
|
||||||
|
|| constrain_grid_
|
||||||
|
|| constrain_sb_
|
||||||
|
|| constrain_cb_
|
||||||
|
|| constrain_configurable_memory_outputs_
|
||||||
|
|| constrain_routing_multiplexer_outputs_
|
||||||
|
|| constrain_switch_block_outputs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PnrSdcOption::constrain_global_port() const {
|
||||||
|
return constrain_global_port_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PnrSdcOption::constrain_grid() const {
|
||||||
|
return constrain_grid_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PnrSdcOption::constrain_sb() const {
|
||||||
|
return constrain_sb_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PnrSdcOption::constrain_cb() const {
|
||||||
|
return constrain_cb_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PnrSdcOption::constrain_configurable_memory_outputs() const {
|
||||||
|
return constrain_configurable_memory_outputs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PnrSdcOption::constrain_routing_multiplexer_outputs() const {
|
||||||
|
return constrain_routing_multiplexer_outputs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PnrSdcOption::constrain_switch_block_outputs() const {
|
||||||
|
return constrain_switch_block_outputs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Public mutators
|
||||||
|
********************************************************************/
|
||||||
|
void PnrSdcOption::set_sdc_dir(const std::string& sdc_dir) {
|
||||||
|
sdc_dir_ = sdc_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PnrSdcOption::set_generate_sdc_pnr(const bool& generate_sdc_pnr) {
|
||||||
|
constrain_global_port_ = generate_sdc_pnr;
|
||||||
|
constrain_grid_ = generate_sdc_pnr;
|
||||||
|
constrain_sb_ = generate_sdc_pnr;
|
||||||
|
constrain_cb_ = generate_sdc_pnr;
|
||||||
|
constrain_configurable_memory_outputs_ = generate_sdc_pnr;
|
||||||
|
constrain_routing_multiplexer_outputs_ = generate_sdc_pnr;
|
||||||
|
constrain_switch_block_outputs_ = generate_sdc_pnr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PnrSdcOption::set_constrain_global_port(const bool& constrain_global_port) {
|
||||||
|
constrain_global_port_ = constrain_global_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PnrSdcOption::set_constrain_grid(const bool& constrain_grid) {
|
||||||
|
constrain_grid_ = constrain_grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PnrSdcOption::set_constrain_sb(const bool& constrain_sb) {
|
||||||
|
constrain_sb_ = constrain_sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PnrSdcOption::set_constrain_cb(const bool& constrain_cb) {
|
||||||
|
constrain_cb_ = constrain_cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PnrSdcOption::set_constrain_configurable_memory_outputs(const bool& constrain_config_mem_outputs) {
|
||||||
|
constrain_configurable_memory_outputs_ = constrain_config_mem_outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PnrSdcOption::set_constrain_routing_multiplexer_outputs(const bool& constrain_routing_mux_outputs) {
|
||||||
|
constrain_routing_multiplexer_outputs_ = constrain_routing_mux_outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PnrSdcOption::set_constrain_switch_block_outputs(const bool& constrain_sb_outputs) {
|
||||||
|
constrain_switch_block_outputs_ = constrain_sb_outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef PNR_SDC_OPTION_H
|
||||||
|
#define PNR_SDC_OPTION_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* A data structure to include all the options for the SDC generator
|
||||||
|
* in purpose of constraining physical design of FPGA fabric in back-end flow
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
class PnrSdcOption {
|
||||||
|
public: /* Public Constructors */
|
||||||
|
PnrSdcOption(const std::string& sdc_dir);
|
||||||
|
public: /* Public accessors */
|
||||||
|
std::string sdc_dir() const;
|
||||||
|
bool generate_sdc_pnr() const;
|
||||||
|
bool constrain_global_port() const;
|
||||||
|
bool constrain_grid() const;
|
||||||
|
bool constrain_sb() const;
|
||||||
|
bool constrain_cb() const;
|
||||||
|
bool constrain_configurable_memory_outputs() const;
|
||||||
|
bool constrain_routing_multiplexer_outputs() const;
|
||||||
|
bool constrain_switch_block_outputs() const;
|
||||||
|
public: /* Public mutators */
|
||||||
|
void set_sdc_dir(const std::string& sdc_dir);
|
||||||
|
void set_generate_sdc_pnr(const bool& generate_sdc_pnr);
|
||||||
|
void set_constrain_global_port(const bool& constrain_global_port);
|
||||||
|
void set_constrain_grid(const bool& constrain_grid);
|
||||||
|
void set_constrain_sb(const bool& constrain_sb);
|
||||||
|
void set_constrain_cb(const bool& constrain_cb);
|
||||||
|
void set_constrain_configurable_memory_outputs(const bool& constrain_config_mem_outputs);
|
||||||
|
void set_constrain_routing_multiplexer_outputs(const bool& constrain_routing_mux_outputs);
|
||||||
|
void set_constrain_switch_block_outputs(const bool& constrain_sb_outputs);
|
||||||
|
private: /* Internal data */
|
||||||
|
std::string sdc_dir_;
|
||||||
|
bool constrain_global_port_;
|
||||||
|
bool constrain_grid_;
|
||||||
|
bool constrain_sb_;
|
||||||
|
bool constrain_cb_;
|
||||||
|
bool constrain_configurable_memory_outputs_;
|
||||||
|
bool constrain_routing_multiplexer_outputs_;
|
||||||
|
bool constrain_switch_block_outputs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,411 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file includes functions that print SDC (Synopsys Design Constraint)
|
||||||
|
* files in physical design tools, i.e., Place & Route (PnR) tools
|
||||||
|
* The SDC files are used to constrain the physical design for each routing modules
|
||||||
|
* in FPGA fabric, such as Switch Blocks (SBs) and Connection Blocks (CBs)
|
||||||
|
*
|
||||||
|
* Note that this is different from the SDC to constrain VPR Place&Route
|
||||||
|
* engine! These SDCs are designed for PnR to generate FPGA layouts!!!
|
||||||
|
*******************************************************************/
|
||||||
|
#include <ctime>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_log.h"
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "vtr_time.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_port.h"
|
||||||
|
#include "openfpga_side_manager.h"
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
|
#include "mux_utils.h"
|
||||||
|
|
||||||
|
#include "openfpga_naming.h"
|
||||||
|
|
||||||
|
#include "openfpga_rr_graph_utils.h"
|
||||||
|
#include "build_routing_module_utils.h"
|
||||||
|
|
||||||
|
#include "sdc_writer_naming.h"
|
||||||
|
#include "sdc_writer_utils.h"
|
||||||
|
#include "pnr_sdc_routing_writer.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Find the timing constraints between the inputs and outputs of a routing
|
||||||
|
* multiplexer in a Switch Block
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
float find_pnr_sdc_switch_tmax(const t_rr_switch_inf& switch_inf) {
|
||||||
|
return switch_inf.R * switch_inf.Cout + switch_inf.Tdel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Set timing constraints between the inputs and outputs of a routing
|
||||||
|
* multiplexer in a Switch Block
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_constrain_sb_mux_timing(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& sb_module,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const RRGSB& rr_gsb,
|
||||||
|
const e_side& output_node_side,
|
||||||
|
const RRNodeId& output_rr_node) {
|
||||||
|
/* Validate file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
VTR_ASSERT( ( CHANX == rr_graph.node_type(output_rr_node) )
|
||||||
|
|| ( CHANY == rr_graph.node_type(output_rr_node) ));
|
||||||
|
|
||||||
|
/* Find the module port corresponding to the output rr_node */
|
||||||
|
ModulePortId module_output_port = find_switch_block_module_chan_port(module_manager,
|
||||||
|
sb_module,
|
||||||
|
rr_graph,
|
||||||
|
rr_gsb,
|
||||||
|
output_node_side,
|
||||||
|
output_rr_node,
|
||||||
|
OUT_PORT);
|
||||||
|
|
||||||
|
/* Find the module port corresponding to the fan-in rr_nodes of the output rr_node */
|
||||||
|
std::vector<ModulePortId> module_input_ports = find_switch_block_module_input_ports(module_manager,
|
||||||
|
sb_module,
|
||||||
|
rr_graph,
|
||||||
|
rr_gsb,
|
||||||
|
get_rr_graph_configurable_driver_nodes(rr_graph, output_rr_node));
|
||||||
|
|
||||||
|
/* Find timing constraints for each path (edge) */
|
||||||
|
std::map<ModulePortId, float> switch_delays;
|
||||||
|
size_t edge_counter = 0;
|
||||||
|
for (const RREdgeId& edge : rr_graph.node_configurable_in_edges(output_rr_node)) {
|
||||||
|
/* Get the switch delay */
|
||||||
|
const RRSwitchId& driver_switch = rr_graph.edge_switch(edge);
|
||||||
|
switch_delays[module_input_ports[edge_counter]] = find_pnr_sdc_switch_tmax(rr_graph.get_switch(driver_switch));
|
||||||
|
edge_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the starting points */
|
||||||
|
for (const ModulePortId& module_input_port : module_input_ports) {
|
||||||
|
/* Constrain a path */
|
||||||
|
print_pnr_sdc_constrain_port2port_timing(fp,
|
||||||
|
module_manager,
|
||||||
|
sb_module, module_input_port,
|
||||||
|
sb_module, module_output_port,
|
||||||
|
switch_delays[module_input_port]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Set timing constraints between the inputs and outputs of SBs,
|
||||||
|
* which are connected by routing multiplexers with the given delays
|
||||||
|
* specified in architectural XML file
|
||||||
|
*
|
||||||
|
* To enable block by block timing constraining, we generate the SDC
|
||||||
|
* file for each unique SB module
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_constrain_sb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const RRGSB& rr_gsb) {
|
||||||
|
|
||||||
|
/* Create the file name for Verilog netlist */
|
||||||
|
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||||
|
std::string sdc_fname(sdc_dir + generate_switch_block_module_name(gsb_coordinate) + std::string(SDC_FILE_NAME_POSTFIX));
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
/* Validate file stream */
|
||||||
|
check_file_stream(sdc_fname.c_str(), fp);
|
||||||
|
|
||||||
|
std::string sb_module_name = generate_switch_block_module_name(gsb_coordinate);
|
||||||
|
ModuleId sb_module = module_manager.find_module(sb_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
||||||
|
|
||||||
|
/* Generate the descriptions*/
|
||||||
|
print_sdc_file_header(fp, std::string("Constrain timing of Switch Block " + sb_module_name + " for PnR"));
|
||||||
|
|
||||||
|
for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) {
|
||||||
|
SideManager side_manager(side);
|
||||||
|
for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) {
|
||||||
|
const RRNodeId& chan_rr_node = rr_gsb.get_chan_node(side_manager.get_side(), itrack);
|
||||||
|
/* We only care the output port and it should indicate a SB mux */
|
||||||
|
if (OUT_PORT != rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Constrain thru wires */
|
||||||
|
if (false != rr_gsb.is_sb_node_passing_wire(rr_graph, side_manager.get_side(), itrack)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* This is a MUX, constrain all the paths from an input to an output */
|
||||||
|
print_pnr_sdc_constrain_sb_mux_timing(fp,
|
||||||
|
module_manager, sb_module,
|
||||||
|
rr_graph,
|
||||||
|
rr_gsb,
|
||||||
|
side_manager.get_side(),
|
||||||
|
chan_rr_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print SDC timing constraints for Switch blocks
|
||||||
|
* This function is designed for flatten routing hierarchy
|
||||||
|
*******************************************************************/
|
||||||
|
void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const DeviceRRGSB& device_rr_gsb) {
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
vtr::ScopedStartFinishTimer timer("Write SDC for constrain Switch Block timing for P&R flow");
|
||||||
|
|
||||||
|
/* Get the range of SB array */
|
||||||
|
vtr::Point<size_t> sb_range = device_rr_gsb.get_gsb_range();
|
||||||
|
/* Go for each SB */
|
||||||
|
for (size_t ix = 0; ix < sb_range.x(); ++ix) {
|
||||||
|
for (size_t iy = 0; iy < sb_range.y(); ++iy) {
|
||||||
|
const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy);
|
||||||
|
if (false == rr_gsb.is_sb_exist()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
print_pnr_sdc_constrain_sb_timing(sdc_dir,
|
||||||
|
module_manager,
|
||||||
|
rr_graph,
|
||||||
|
rr_gsb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print SDC timing constraints for Switch blocks
|
||||||
|
* This function is designed for compact routing hierarchy
|
||||||
|
*******************************************************************/
|
||||||
|
void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const DeviceRRGSB& device_rr_gsb) {
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
vtr::ScopedStartFinishTimer timer("Write SDC for constrain Switch Block timing for P&R flow");
|
||||||
|
|
||||||
|
for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) {
|
||||||
|
const RRGSB& rr_gsb = device_rr_gsb.get_sb_unique_module(isb);
|
||||||
|
if (false == rr_gsb.is_sb_exist()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
print_pnr_sdc_constrain_sb_timing(sdc_dir,
|
||||||
|
module_manager,
|
||||||
|
rr_graph,
|
||||||
|
rr_gsb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Set timing constraints between the inputs and outputs of a routing
|
||||||
|
* multiplexer in a Connection Block
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_constrain_cb_mux_timing(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& cb_module,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const RRGSB& rr_gsb,
|
||||||
|
const t_rr_type& cb_type,
|
||||||
|
const RRNodeId& output_rr_node) {
|
||||||
|
/* Validate file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
VTR_ASSERT(IPIN == rr_graph.node_type(output_rr_node));
|
||||||
|
|
||||||
|
/* We have OPINs since we may have direct connections:
|
||||||
|
* These connections should be handled by other functions in the compact_netlist.c
|
||||||
|
* So we just return here for OPINs
|
||||||
|
*/
|
||||||
|
if (0 == get_rr_graph_configurable_driver_nodes(rr_graph, output_rr_node).size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the module port corresponding to the output rr_node */
|
||||||
|
ModulePortId module_output_port = find_connection_block_module_ipin_port(module_manager,
|
||||||
|
cb_module,
|
||||||
|
rr_graph,
|
||||||
|
rr_gsb,
|
||||||
|
output_rr_node);
|
||||||
|
|
||||||
|
/* Find the module port corresponding to the fan-in rr_nodes of the output rr_node */
|
||||||
|
std::vector<ModulePortId> module_input_ports = find_connection_block_module_input_ports(module_manager,
|
||||||
|
cb_module,
|
||||||
|
rr_graph,
|
||||||
|
rr_gsb,
|
||||||
|
cb_type,
|
||||||
|
get_rr_graph_configurable_driver_nodes(rr_graph, output_rr_node));
|
||||||
|
|
||||||
|
/* Find timing constraints for each path (edge) */
|
||||||
|
std::map<ModulePortId, float> switch_delays;
|
||||||
|
size_t edge_counter = 0;
|
||||||
|
for (const RREdgeId& edge : rr_graph.node_configurable_in_edges(output_rr_node)) {
|
||||||
|
/* Get the switch delay */
|
||||||
|
const RRSwitchId& driver_switch = rr_graph.edge_switch(edge);
|
||||||
|
switch_delays[module_input_ports[edge_counter]] = find_pnr_sdc_switch_tmax(rr_graph.get_switch(driver_switch));
|
||||||
|
edge_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the starting points */
|
||||||
|
for (const ModulePortId& module_input_port : module_input_ports) {
|
||||||
|
/* Constrain a path */
|
||||||
|
print_pnr_sdc_constrain_port2port_timing(fp,
|
||||||
|
module_manager,
|
||||||
|
cb_module, module_input_port,
|
||||||
|
cb_module, module_output_port,
|
||||||
|
switch_delays[module_input_port]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print SDC timing constraints for a Connection block
|
||||||
|
* This function is designed for compact routing hierarchy
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_constrain_cb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const RRGSB& rr_gsb,
|
||||||
|
const t_rr_type& cb_type) {
|
||||||
|
/* Create the netlist */
|
||||||
|
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||||
|
|
||||||
|
/* Find the module name and create a SDC file for it */
|
||||||
|
std::string sdc_fname(sdc_dir + generate_connection_block_module_name(cb_type, gsb_coordinate) + std::string(SDC_FILE_NAME_POSTFIX));
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
/* Validate file stream */
|
||||||
|
check_file_stream(sdc_fname.c_str(), fp);
|
||||||
|
|
||||||
|
std::string cb_module_name = generate_connection_block_module_name(cb_type, gsb_coordinate);
|
||||||
|
ModuleId cb_module = module_manager.find_module(cb_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(cb_module));
|
||||||
|
|
||||||
|
/* Generate the descriptions*/
|
||||||
|
print_sdc_file_header(fp, std::string("Constrain timing of Connection Block " + cb_module_name + " for PnR"));
|
||||||
|
|
||||||
|
std::vector<enum e_side> cb_sides = rr_gsb.get_cb_ipin_sides(cb_type);
|
||||||
|
|
||||||
|
for (size_t side = 0; side < cb_sides.size(); ++side) {
|
||||||
|
enum e_side cb_ipin_side = cb_sides[side];
|
||||||
|
SideManager side_manager(cb_ipin_side);
|
||||||
|
for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) {
|
||||||
|
const RRNodeId& ipin_rr_node = rr_gsb.get_ipin_node(cb_ipin_side, inode);
|
||||||
|
print_pnr_sdc_constrain_cb_mux_timing(fp,
|
||||||
|
module_manager, cb_module,
|
||||||
|
rr_graph, rr_gsb, cb_type,
|
||||||
|
ipin_rr_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Iterate over all the connection blocks in a device
|
||||||
|
* and print SDC file for each of them
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const DeviceRRGSB& device_rr_gsb,
|
||||||
|
const t_rr_type& cb_type) {
|
||||||
|
/* Build unique X-direction connection block modules */
|
||||||
|
vtr::Point<size_t> cb_range = device_rr_gsb.get_gsb_range();
|
||||||
|
|
||||||
|
for (size_t ix = 0; ix < cb_range.x(); ++ix) {
|
||||||
|
for (size_t iy = 0; iy < cb_range.y(); ++iy) {
|
||||||
|
/* Check if the connection block exists in the device!
|
||||||
|
* Some of them do NOT exist due to heterogeneous blocks (height > 1)
|
||||||
|
* We will skip those modules
|
||||||
|
*/
|
||||||
|
const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy);
|
||||||
|
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
print_pnr_sdc_constrain_cb_timing(sdc_dir,
|
||||||
|
module_manager,
|
||||||
|
rr_graph,
|
||||||
|
rr_gsb,
|
||||||
|
cb_type);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Iterate over all the connection blocks in a device
|
||||||
|
* and print SDC file for each of them
|
||||||
|
*******************************************************************/
|
||||||
|
void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const DeviceRRGSB& device_rr_gsb) {
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
vtr::ScopedStartFinishTimer timer("Write SDC for constrain Connection Block timing for P&R flow");
|
||||||
|
|
||||||
|
print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_dir, module_manager,
|
||||||
|
rr_graph,
|
||||||
|
device_rr_gsb,
|
||||||
|
CHANX);
|
||||||
|
|
||||||
|
print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_dir, module_manager,
|
||||||
|
rr_graph,
|
||||||
|
device_rr_gsb,
|
||||||
|
CHANY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print SDC timing constraints for Connection blocks
|
||||||
|
* This function is designed for compact routing hierarchy
|
||||||
|
*******************************************************************/
|
||||||
|
void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const DeviceRRGSB& device_rr_gsb) {
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
vtr::ScopedStartFinishTimer timer("Write SDC for constrain Connection Block timing for P&R flow");
|
||||||
|
|
||||||
|
/* Print SDC for unique X-direction connection block modules */
|
||||||
|
for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) {
|
||||||
|
const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANX, icb);
|
||||||
|
print_pnr_sdc_constrain_cb_timing(sdc_dir,
|
||||||
|
module_manager,
|
||||||
|
rr_graph,
|
||||||
|
unique_mirror,
|
||||||
|
CHANX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print SDC for unique Y-direction connection block modules */
|
||||||
|
for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) {
|
||||||
|
const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANY, icb);
|
||||||
|
print_pnr_sdc_constrain_cb_timing(sdc_dir,
|
||||||
|
module_manager,
|
||||||
|
rr_graph,
|
||||||
|
unique_mirror,
|
||||||
|
CHANY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef PNR_SDC_ROUTING_WRITER_H
|
||||||
|
#define PNR_SDC_ROUTING_WRITER_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "module_manager.h"
|
||||||
|
#include "device_rr_gsb.h"
|
||||||
|
#include "rr_graph_obj.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void print_pnr_sdc_flatten_routing_constrain_sb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const DeviceRRGSB& device_rr_gsb);
|
||||||
|
|
||||||
|
void print_pnr_sdc_compact_routing_constrain_sb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const DeviceRRGSB& device_rr_gsb);
|
||||||
|
|
||||||
|
void print_pnr_sdc_flatten_routing_constrain_cb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const DeviceRRGSB& device_rr_gsb);
|
||||||
|
|
||||||
|
void print_pnr_sdc_compact_routing_constrain_cb_timing(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const RRGraph& rr_graph,
|
||||||
|
const DeviceRRGSB& device_rr_gsb);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,431 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file includes functions that print SDC (Synopsys Design Constraint)
|
||||||
|
* files in physical design tools, i.e., Place & Route (PnR) tools
|
||||||
|
* The SDC files are used to constrain the physical design for each module
|
||||||
|
* in FPGA fabric, such as Configurable Logic Blocks (CLBs),
|
||||||
|
* Heterogeneous blocks, Switch Blocks (SBs) and Connection Blocks (CBs)
|
||||||
|
*
|
||||||
|
* Note that this is different from the SDC to constrain VPR Place&Route
|
||||||
|
* engine! These SDCs are designed for PnR to generate FPGA layouts!!!
|
||||||
|
*******************************************************************/
|
||||||
|
#include <ctime>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "vtr_time.h"
|
||||||
|
#include "vtr_log.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_port.h"
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
|
#include "mux_utils.h"
|
||||||
|
|
||||||
|
#include "openfpga_naming.h"
|
||||||
|
|
||||||
|
#include "sdc_writer_naming.h"
|
||||||
|
#include "sdc_writer_utils.h"
|
||||||
|
#include "sdc_memory_utils.h"
|
||||||
|
#include "pnr_sdc_routing_writer.h"
|
||||||
|
#include "pnr_sdc_grid_writer.h"
|
||||||
|
#include "pnr_sdc_writer.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print a SDC file to constrain the global ports of FPGA fabric
|
||||||
|
* in particular clock ports
|
||||||
|
*
|
||||||
|
* For programming clock, we give a fixed period, while for operating
|
||||||
|
* clock, we constrain with critical path delay
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_global_ports(const std::string& sdc_dir,
|
||||||
|
const float& programming_critical_path_delay,
|
||||||
|
const float& operating_critical_path_delay,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports) {
|
||||||
|
|
||||||
|
/* Create the file name for Verilog netlist */
|
||||||
|
std::string sdc_fname(sdc_dir + std::string(SDC_GLOBAL_PORTS_FILE_NAME));
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
std::string timer_message = std::string("Write SDC for constraining clocks for P&R flow '") + sdc_fname + std::string("'");
|
||||||
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
check_file_stream(sdc_fname.c_str(), fp);
|
||||||
|
|
||||||
|
/* Generate the descriptions*/
|
||||||
|
print_sdc_file_header(fp, std::string("Clock contraints for PnR"));
|
||||||
|
|
||||||
|
/* Get clock port from the global port */
|
||||||
|
for (const CircuitPortId& clock_port : global_ports) {
|
||||||
|
if (CIRCUIT_MODEL_PORT_CLOCK != circuit_lib.port_type(clock_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Reach here, it means a clock port and we need print constraints */
|
||||||
|
float clock_period = operating_critical_path_delay;
|
||||||
|
|
||||||
|
/* For programming clock, we give a fixed period */
|
||||||
|
if (true == circuit_lib.port_is_prog(clock_port)) {
|
||||||
|
clock_period = programming_critical_path_delay;
|
||||||
|
/* Print comments */
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
fp << "# Create programmable clock " << std::endl;
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
} else {
|
||||||
|
/* Print comments */
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
fp << "# Create clock " << std::endl;
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const size_t& pin : circuit_lib.pins(clock_port)) {
|
||||||
|
BasicPort port_to_constrain(circuit_lib.port_prefix(clock_port), pin, pin);
|
||||||
|
|
||||||
|
fp << "create_clock ";
|
||||||
|
fp << generate_sdc_port(port_to_constrain) << "-period ";
|
||||||
|
fp << std::setprecision(10) << clock_period;
|
||||||
|
fp << " -waveform {0 ";
|
||||||
|
fp << std::setprecision(10) << clock_period / 2;
|
||||||
|
fp << "}" << std::endl;
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For non-clock port from the global port: give a fixed period */
|
||||||
|
for (const CircuitPortId& global_port : global_ports) {
|
||||||
|
if (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print comments */
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
fp << "# Constrain other global ports " << std::endl;
|
||||||
|
fp << "##################################################" << std::endl;
|
||||||
|
|
||||||
|
/* Reach here, it means a non-clock global port and we need print constraints */
|
||||||
|
float clock_period = operating_critical_path_delay;
|
||||||
|
for (const size_t& pin : circuit_lib.pins(global_port)) {
|
||||||
|
BasicPort port_to_constrain(circuit_lib.port_prefix(global_port), pin, pin);
|
||||||
|
fp << "create_clock ";
|
||||||
|
fp << generate_sdc_port(port_to_constrain) << "-period ";
|
||||||
|
fp << std::setprecision(10) << clock_period;
|
||||||
|
fp << " -waveform {0 ";
|
||||||
|
fp << std::setprecision(10) << clock_period / 2;
|
||||||
|
fp << "} ";
|
||||||
|
fp << "[list [get_ports { " << generate_sdc_port(port_to_constrain) << "}]]" << std::endl;
|
||||||
|
|
||||||
|
fp << "set_drive 0 " << generate_sdc_port(port_to_constrain) << std::endl;
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Break combinational loops in FPGA fabric, which mainly come from
|
||||||
|
* configurable memory cells.
|
||||||
|
* To handle this, we disable the outputs of memory cells
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_constrain_configurable_memory_outputs(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module) {
|
||||||
|
|
||||||
|
/* Create the file name for Verilog netlist */
|
||||||
|
std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_CONFIG_MEM_OUTPUTS_FILE_NAME));
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
std::string timer_message = std::string("Write SDC to disable configurable memory outputs for P&R flow '") + sdc_fname + std::string("'");
|
||||||
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
check_file_stream(sdc_fname.c_str(), fp);
|
||||||
|
|
||||||
|
/* Generate the descriptions*/
|
||||||
|
print_sdc_file_header(fp, std::string("Disable configurable memory outputs for PnR"));
|
||||||
|
|
||||||
|
/* Go recursively in the module manager, starting from the top-level module: instance id of the top-level module is 0 by default */
|
||||||
|
rec_print_pnr_sdc_disable_configurable_memory_module_output(fp, module_manager, top_module,
|
||||||
|
format_dir_path(module_manager.module_name(top_module)));
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Break combinational loops in FPGA fabric, which mainly come from
|
||||||
|
* loops of multiplexers.
|
||||||
|
* To handle this, we disable the timing at outputs of routing multiplexers
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_sdc_disable_routing_multiplexer_outputs(const std::string& sdc_dir,
|
||||||
|
const MuxLibrary& mux_lib,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const ModuleManager& module_manager) {
|
||||||
|
/* Create the file name for Verilog netlist */
|
||||||
|
std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_MUX_OUTPUTS_FILE_NAME));
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
std::string timer_message = std::string("Write SDC to disable routing multiplexer outputs for P&R flow '") + sdc_fname + std::string("'");
|
||||||
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
check_file_stream(sdc_fname.c_str(), fp);
|
||||||
|
|
||||||
|
/* Generate the descriptions*/
|
||||||
|
print_sdc_file_header(fp, std::string("Disable routing multiplexer outputs for PnR"));
|
||||||
|
|
||||||
|
/* Iterate over the MUX modules */
|
||||||
|
for (const MuxId& mux_id : mux_lib.muxes()) {
|
||||||
|
const CircuitModelId& mux_model = mux_lib.mux_circuit_model(mux_id);
|
||||||
|
|
||||||
|
/* Skip LUTs, we only care about multiplexers here */
|
||||||
|
if (CIRCUIT_MODEL_MUX != circuit_lib.model_type(mux_model)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MuxGraph& mux_graph = mux_lib.mux_graph(mux_id);
|
||||||
|
std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model,
|
||||||
|
find_mux_num_datapath_inputs(circuit_lib, mux_model, mux_graph.num_inputs()),
|
||||||
|
std::string(""));
|
||||||
|
/* Find the module name in module manager */
|
||||||
|
ModuleId mux_module = module_manager.find_module(mux_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(mux_module));
|
||||||
|
|
||||||
|
/* Disable the timing for the output ports */
|
||||||
|
for (const BasicPort& output_port : module_manager.module_ports_by_type(mux_module, ModuleManager::MODULE_OUTPUT_PORT)) {
|
||||||
|
fp << "set_disable_timing [get_pins -filter \"name =~ " << output_port.get_name() << "*\" ";
|
||||||
|
fp << "-of [get_cells -hier -filter \"ref_lib_cell_name == " << mux_module_name << "\"]]" << std::endl;
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Break combinational loops in FPGA fabric, which mainly come from
|
||||||
|
* loops of multiplexers.
|
||||||
|
* To handle this, we disable the timing at outputs of Switch blocks
|
||||||
|
* This function is designed for flatten routing hierarchy
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_flatten_routing_disable_switch_block_outputs(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const DeviceRRGSB& device_rr_gsb) {
|
||||||
|
/* Create the file name for Verilog netlist */
|
||||||
|
std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME));
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
std::string timer_message = std::string("Write SDC to disable switch block outputs for P&R flow '") + sdc_fname + std::string("'");
|
||||||
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
check_file_stream(sdc_fname.c_str(), fp);
|
||||||
|
|
||||||
|
/* Generate the descriptions*/
|
||||||
|
print_sdc_file_header(fp, std::string("Disable Switch Block outputs for PnR"));
|
||||||
|
|
||||||
|
/* Get the range of SB array */
|
||||||
|
vtr::Point<size_t> sb_range = device_rr_gsb.get_gsb_range();
|
||||||
|
/* Go for each SB */
|
||||||
|
for (size_t ix = 0; ix < sb_range.x(); ++ix) {
|
||||||
|
for (size_t iy = 0; iy < sb_range.y(); ++iy) {
|
||||||
|
const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy);
|
||||||
|
|
||||||
|
if (false == rr_gsb.is_sb_exist()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||||
|
std::string sb_instance_name = generate_switch_block_module_name(gsb_coordinate);
|
||||||
|
|
||||||
|
ModuleId sb_module = module_manager.find_module(sb_instance_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
||||||
|
|
||||||
|
/* Disable the outputs of the module */
|
||||||
|
for (const BasicPort& output_port : module_manager.module_ports_by_type(sb_module, ModuleManager::MODULE_OUTPUT_PORT)) {
|
||||||
|
fp << "set_disable_timing " << sb_instance_name << "/" << output_port.get_name() << std::endl;
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Break combinational loops in FPGA fabric, which mainly come from
|
||||||
|
* loops of multiplexers.
|
||||||
|
* To handle this, we disable the timing at outputs of Switch blocks
|
||||||
|
* This function is designed for compact routing hierarchy
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_pnr_sdc_compact_routing_disable_switch_block_outputs(const std::string& sdc_dir,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const DeviceRRGSB& device_rr_gsb) {
|
||||||
|
/* Create the file name for Verilog netlist */
|
||||||
|
std::string sdc_fname(sdc_dir + std::string(SDC_DISABLE_SB_OUTPUTS_FILE_NAME));
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
std::string timer_message = std::string("Write SDC to disable switch block outputs for P&R flow '") + sdc_fname + std::string("'");
|
||||||
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(sdc_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
check_file_stream(sdc_fname.c_str(), fp);
|
||||||
|
|
||||||
|
/* Generate the descriptions*/
|
||||||
|
print_sdc_file_header(fp, std::string("Disable Switch Block outputs for PnR"));
|
||||||
|
|
||||||
|
/* Build unique switch block modules */
|
||||||
|
for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) {
|
||||||
|
const RRGSB& rr_gsb = device_rr_gsb.get_sb_unique_module(isb);
|
||||||
|
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||||
|
std::string sb_module_name = generate_switch_block_module_name(gsb_coordinate);
|
||||||
|
|
||||||
|
ModuleId sb_module = module_manager.find_module(sb_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
||||||
|
|
||||||
|
/* Find all the instances in the top-level module */
|
||||||
|
for (const size_t& instance_id : module_manager.child_module_instances(top_module, sb_module)) {
|
||||||
|
std::string sb_instance_name = module_manager.instance_name(top_module, sb_module, instance_id);
|
||||||
|
/* Disable the outputs of the module */
|
||||||
|
for (const BasicPort& output_port : module_manager.module_ports_by_type(sb_module, ModuleManager::MODULE_OUTPUT_PORT)) {
|
||||||
|
fp << "set_disable_timing " << sb_instance_name << "/" << output_port.get_name() << std::endl;
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close file handler */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Top-level function to print a number of SDC files in different purpose
|
||||||
|
* This function will generate files upon the options provided by users
|
||||||
|
* 1. Design constraints for CLBs
|
||||||
|
* 2. Design constraints for Switch Blocks
|
||||||
|
* 3. Design constraints for Connection Blocks
|
||||||
|
* 4. Design constraints for breaking the combinational loops in FPGA fabric
|
||||||
|
*******************************************************************/
|
||||||
|
void print_pnr_sdc(const PnrSdcOption& sdc_options,
|
||||||
|
const float& programming_critical_path_delay,
|
||||||
|
const float& operating_critical_path_delay,
|
||||||
|
const DeviceContext& device_ctx,
|
||||||
|
const VprDeviceAnnotation& device_annotation,
|
||||||
|
const DeviceRRGSB& device_rr_gsb,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const MuxLibrary& mux_lib,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports,
|
||||||
|
const bool& compact_routing_hierarchy) {
|
||||||
|
|
||||||
|
/* Constrain global ports */
|
||||||
|
if (true == sdc_options.constrain_global_port()) {
|
||||||
|
print_pnr_sdc_global_ports(sdc_options.sdc_dir(),
|
||||||
|
programming_critical_path_delay,
|
||||||
|
operating_critical_path_delay,
|
||||||
|
circuit_lib, global_ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string top_module_name = generate_fpga_top_module_name();
|
||||||
|
ModuleId top_module = module_manager.find_module(top_module_name);
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(top_module));
|
||||||
|
|
||||||
|
/* Output Design Constraints to disable outputs of memory cells */
|
||||||
|
if (true == sdc_options.constrain_configurable_memory_outputs()) {
|
||||||
|
print_pnr_sdc_constrain_configurable_memory_outputs(sdc_options.sdc_dir(), module_manager, top_module);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Break loops from Multiplexer Output */
|
||||||
|
if (true == sdc_options.constrain_routing_multiplexer_outputs()) {
|
||||||
|
print_sdc_disable_routing_multiplexer_outputs(sdc_options.sdc_dir(),
|
||||||
|
mux_lib, circuit_lib,
|
||||||
|
module_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Break loops from any SB output */
|
||||||
|
if (true == sdc_options.constrain_switch_block_outputs()) {
|
||||||
|
if (true == compact_routing_hierarchy) {
|
||||||
|
print_pnr_sdc_compact_routing_disable_switch_block_outputs(sdc_options.sdc_dir(),
|
||||||
|
module_manager, top_module,
|
||||||
|
device_rr_gsb);
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT_SAFE (false == compact_routing_hierarchy);
|
||||||
|
print_pnr_sdc_flatten_routing_disable_switch_block_outputs(sdc_options.sdc_dir(),
|
||||||
|
module_manager,
|
||||||
|
device_rr_gsb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Output routing constraints for Switch Blocks */
|
||||||
|
if (true == sdc_options.constrain_sb()) {
|
||||||
|
if (true == compact_routing_hierarchy) {
|
||||||
|
print_pnr_sdc_compact_routing_constrain_sb_timing(sdc_options.sdc_dir(),
|
||||||
|
module_manager,
|
||||||
|
device_ctx.rr_graph,
|
||||||
|
device_rr_gsb);
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT_SAFE (false == compact_routing_hierarchy);
|
||||||
|
print_pnr_sdc_flatten_routing_constrain_sb_timing(sdc_options.sdc_dir(),
|
||||||
|
module_manager,
|
||||||
|
device_ctx.rr_graph,
|
||||||
|
device_rr_gsb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Output routing constraints for Connection Blocks */
|
||||||
|
if (true == sdc_options.constrain_cb()) {
|
||||||
|
if (true == compact_routing_hierarchy) {
|
||||||
|
print_pnr_sdc_compact_routing_constrain_cb_timing(sdc_options.sdc_dir(),
|
||||||
|
module_manager,
|
||||||
|
device_ctx.rr_graph,
|
||||||
|
device_rr_gsb);
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT_SAFE (false == compact_routing_hierarchy);
|
||||||
|
print_pnr_sdc_flatten_routing_constrain_cb_timing(sdc_options.sdc_dir(),
|
||||||
|
module_manager,
|
||||||
|
device_ctx.rr_graph,
|
||||||
|
device_rr_gsb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Output Timing constraints for Programmable blocks */
|
||||||
|
if (true == sdc_options.constrain_grid()) {
|
||||||
|
print_pnr_sdc_constrain_grid_timing(sdc_options.sdc_dir(),
|
||||||
|
device_ctx,
|
||||||
|
device_annotation,
|
||||||
|
module_manager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef PNR_SDC_WRITER_H
|
||||||
|
#define PNR_SDC_WRITER_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "vpr_context.h"
|
||||||
|
#include "vpr_device_annotation.h"
|
||||||
|
#include "device_rr_gsb.h"
|
||||||
|
#include "module_manager.h"
|
||||||
|
#include "mux_library.h"
|
||||||
|
#include "circuit_library.h"
|
||||||
|
#include "pnr_sdc_option.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void print_pnr_sdc(const PnrSdcOption& sdc_options,
|
||||||
|
const float& programming_critical_path_delay,
|
||||||
|
const float& operating_critical_path_delay,
|
||||||
|
const DeviceContext& device_ctx,
|
||||||
|
const VprDeviceAnnotation& device_annotation,
|
||||||
|
const DeviceRRGSB& device_rr_gsb,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const MuxLibrary& mux_lib,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports,
|
||||||
|
const bool& compact_routing_hierarchy);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,69 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Most utilized function used to constrain memory cells in FPGA
|
||||||
|
* fabric using SDC commands
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
|
#include "sdc_writer_utils.h"
|
||||||
|
|
||||||
|
#include "sdc_memory_utils.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print SDC commands to disable outputs of all the configurable memory modules
|
||||||
|
* in a given module
|
||||||
|
* This function will be executed in a recursive way,
|
||||||
|
* using a Depth-First Search (DFS) strategy
|
||||||
|
* It will iterate over all the configurable children under each module
|
||||||
|
* and print a SDC command to disable its outputs
|
||||||
|
*******************************************************************/
|
||||||
|
void rec_print_pnr_sdc_disable_configurable_memory_module_output(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& parent_module,
|
||||||
|
const std::string& parent_module_path) {
|
||||||
|
|
||||||
|
/* For each configurable child, we will go one level down in priority */
|
||||||
|
for (size_t child_index = 0; child_index < module_manager.configurable_children(parent_module).size(); ++child_index) {
|
||||||
|
std::string child_module_path = parent_module_path;
|
||||||
|
ModuleId child_module_id = module_manager.configurable_children(parent_module)[child_index];
|
||||||
|
size_t child_instance_id = module_manager.configurable_child_instances(parent_module)[child_index];
|
||||||
|
if (true == module_manager.instance_name(parent_module, child_module_id, child_instance_id).empty()) {
|
||||||
|
/* Give a default name <module_name>_<instance_id>_ */
|
||||||
|
child_module_path += module_manager.module_name(child_module_id);
|
||||||
|
child_module_path += "_";
|
||||||
|
child_module_path += std::to_string(child_instance_id);
|
||||||
|
child_module_path += "_";
|
||||||
|
} else {
|
||||||
|
child_module_path += module_manager.instance_name(parent_module, child_module_id, child_instance_id);
|
||||||
|
}
|
||||||
|
child_module_path = format_dir_path(child_module_path);
|
||||||
|
|
||||||
|
rec_print_pnr_sdc_disable_configurable_memory_module_output(fp, module_manager,
|
||||||
|
child_module_id,
|
||||||
|
child_module_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there is no configurable children any more, this is a leaf module, print a SDC command for disable timing */
|
||||||
|
if (0 < module_manager.configurable_children(parent_module).size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Disable timing for each output port of this module */
|
||||||
|
for (const BasicPort& output_port : module_manager.module_ports_by_type(parent_module, ModuleManager::MODULE_OUTPUT_PORT)) {
|
||||||
|
for (const size_t& pin : output_port.pins()) {
|
||||||
|
BasicPort output_pin(output_port.get_name(), pin, pin);
|
||||||
|
fp << "set_disable_timing ";
|
||||||
|
fp << parent_module_path << generate_sdc_port(output_pin);
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef SDC_MEMORY_UTILS_H
|
||||||
|
#define SDC_MEMORY_UTILS_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include "module_manager.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void rec_print_pnr_sdc_disable_configurable_memory_module_output(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& parent_module,
|
||||||
|
const std::string& parent_module_path);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef SDC_WRITER_NAMING_H
|
||||||
|
#define SDC_WRITER_NAMING_H
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
constexpr char* SDC_FILE_NAME_POSTFIX = ".sdc";
|
||||||
|
|
||||||
|
constexpr char* SDC_GLOBAL_PORTS_FILE_NAME = "global_ports.sdc";
|
||||||
|
constexpr char* SDC_BENCHMARK_ANALYSIS_FILE_NAME= "fpga_top_analysis.sdc";
|
||||||
|
constexpr char* SDC_DISABLE_CONFIG_MEM_OUTPUTS_FILE_NAME = "disable_configurable_memory_outputs.sdc";
|
||||||
|
constexpr char* SDC_DISABLE_MUX_OUTPUTS_FILE_NAME = "disable_routing_multiplexer_outputs.sdc";
|
||||||
|
constexpr char* SDC_DISABLE_SB_OUTPUTS_FILE_NAME = "disable_sb_outputs.sdc";
|
||||||
|
constexpr char* SDC_CB_FILE_NAME = "cb.sdc";
|
||||||
|
|
||||||
|
constexpr char* SDC_ANALYSIS_FILE_NAME = "fpga_top_analysis.sdc";
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,202 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file include most utilized functions to be used in SDC writers
|
||||||
|
*******************************************************************/
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
|
#include "sdc_writer_utils.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Write a head (description) in SDC file
|
||||||
|
*******************************************************************/
|
||||||
|
void print_sdc_file_header(std::fstream& fp,
|
||||||
|
const std::string& usage) {
|
||||||
|
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
auto end = std::chrono::system_clock::now();
|
||||||
|
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
|
||||||
|
|
||||||
|
fp << "#############################################" << std::endl;
|
||||||
|
fp << "#\tSynopsys Design Constraints (SDC)" << std::endl;
|
||||||
|
fp << "#\tFor FPGA fabric " << std::endl;
|
||||||
|
fp << "#\tDescription: " << usage << std::endl;
|
||||||
|
fp << "#\tAuthor: Xifan TANG " << std::endl;
|
||||||
|
fp << "#\tOrganization: University of Utah " << std::endl;
|
||||||
|
fp << "#\tDate: " << std::ctime(&end_time);
|
||||||
|
fp << "#############################################" << std::endl;
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Write a port in SDC format
|
||||||
|
*******************************************************************/
|
||||||
|
std::string generate_sdc_port(const BasicPort& port) {
|
||||||
|
std::string sdc_line;
|
||||||
|
|
||||||
|
std::string size_str = "[" + std::to_string(port.get_lsb()) + ":" + std::to_string(port.get_msb()) + "]";
|
||||||
|
|
||||||
|
/* Only connection require a format of <port_name>[<lsb>:<msb>]
|
||||||
|
* others require a format of <port_type> [<lsb>:<msb>] <port_name>
|
||||||
|
*/
|
||||||
|
/* When LSB == MSB, we can use a simplified format <port_type>[<lsb>]*/
|
||||||
|
if ( 1 == port.get_width()) {
|
||||||
|
size_str = "[" + std::to_string(port.get_lsb()) + "]";
|
||||||
|
}
|
||||||
|
sdc_line = port.get_name() + size_str;
|
||||||
|
|
||||||
|
return sdc_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Constrain a path between two ports of a module with a given maximum timing value
|
||||||
|
*******************************************************************/
|
||||||
|
void print_pnr_sdc_constrain_max_delay(std::fstream& fp,
|
||||||
|
const std::string& src_instance_name,
|
||||||
|
const std::string& src_port_name,
|
||||||
|
const std::string& des_instance_name,
|
||||||
|
const std::string& des_port_name,
|
||||||
|
const float& delay) {
|
||||||
|
/* Validate file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
fp << "set_max_delay";
|
||||||
|
|
||||||
|
fp << " -from ";
|
||||||
|
if (!src_instance_name.empty()) {
|
||||||
|
fp << src_instance_name << "/";
|
||||||
|
}
|
||||||
|
fp << src_port_name;
|
||||||
|
|
||||||
|
fp << " -to ";
|
||||||
|
|
||||||
|
if (!des_instance_name.empty()) {
|
||||||
|
fp << des_instance_name << "/";
|
||||||
|
}
|
||||||
|
fp << des_port_name;
|
||||||
|
|
||||||
|
fp << " " << std::setprecision(10) << delay;
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Constrain a path between two ports of a module with a given timing value
|
||||||
|
* Note: this function uses set_max_delay !!!
|
||||||
|
*******************************************************************/
|
||||||
|
void print_pnr_sdc_constrain_module_port2port_timing(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& input_parent_module_id,
|
||||||
|
const ModulePortId& module_input_port_id,
|
||||||
|
const ModuleId& output_parent_module_id,
|
||||||
|
const ModulePortId& module_output_port_id,
|
||||||
|
const float& tmax) {
|
||||||
|
print_pnr_sdc_constrain_max_delay(fp,
|
||||||
|
module_manager.module_name(input_parent_module_id),
|
||||||
|
generate_sdc_port(module_manager.module_port(input_parent_module_id, module_input_port_id)),
|
||||||
|
module_manager.module_name(output_parent_module_id),
|
||||||
|
generate_sdc_port(module_manager.module_port(output_parent_module_id, module_output_port_id)),
|
||||||
|
tmax);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Constrain a path between two ports of a module with a given timing value
|
||||||
|
* This function will NOT output the module name
|
||||||
|
* Note: this function uses set_max_delay !!!
|
||||||
|
*******************************************************************/
|
||||||
|
void print_pnr_sdc_constrain_port2port_timing(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& input_parent_module_id,
|
||||||
|
const ModulePortId& module_input_port_id,
|
||||||
|
const ModuleId& output_parent_module_id,
|
||||||
|
const ModulePortId& module_output_port_id,
|
||||||
|
const float& tmax) {
|
||||||
|
print_pnr_sdc_constrain_max_delay(fp,
|
||||||
|
std::string(),
|
||||||
|
generate_sdc_port(module_manager.module_port(input_parent_module_id, module_input_port_id)),
|
||||||
|
std::string(),
|
||||||
|
generate_sdc_port(module_manager.module_port(output_parent_module_id, module_output_port_id)),
|
||||||
|
tmax);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Disable timing for a port
|
||||||
|
*******************************************************************/
|
||||||
|
void print_sdc_disable_port_timing(std::fstream& fp,
|
||||||
|
const BasicPort& port) {
|
||||||
|
/* Validate file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
fp << "set_disable_timing ";
|
||||||
|
|
||||||
|
fp << generate_sdc_port(port);
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Set the input delay for a port in SDC format
|
||||||
|
* Note that the input delay will be bounded by a clock port
|
||||||
|
*******************************************************************/
|
||||||
|
void print_sdc_set_port_input_delay(std::fstream& fp,
|
||||||
|
const BasicPort& port,
|
||||||
|
const BasicPort& clock_port,
|
||||||
|
const float& delay) {
|
||||||
|
/* Validate file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
fp << "set_input_delay ";
|
||||||
|
|
||||||
|
fp << "-clock ";
|
||||||
|
|
||||||
|
fp << generate_sdc_port(clock_port);
|
||||||
|
|
||||||
|
fp << " -max ";
|
||||||
|
|
||||||
|
fp << std::setprecision(10) << delay;
|
||||||
|
|
||||||
|
fp << " ";
|
||||||
|
|
||||||
|
fp << generate_sdc_port(port);
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Set the output delay for a port in SDC format
|
||||||
|
* Note that the output delay will be bounded by a clock port
|
||||||
|
*******************************************************************/
|
||||||
|
void print_sdc_set_port_output_delay(std::fstream& fp,
|
||||||
|
const BasicPort& port,
|
||||||
|
const BasicPort& clock_port,
|
||||||
|
const float& delay) {
|
||||||
|
/* Validate file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
fp << "set_output_delay ";
|
||||||
|
|
||||||
|
fp << "-clock ";
|
||||||
|
|
||||||
|
fp << generate_sdc_port(clock_port);
|
||||||
|
|
||||||
|
fp << " -max ";
|
||||||
|
|
||||||
|
fp << std::setprecision(10) << delay;
|
||||||
|
|
||||||
|
fp << " ";
|
||||||
|
|
||||||
|
fp << generate_sdc_port(port);
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,62 @@
|
||||||
|
#ifndef SDC_WRITER_UTILS_H
|
||||||
|
#define SDC_WRITER_UTILS_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include "openfpga_port.h"
|
||||||
|
#include "module_manager.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void print_sdc_file_header(std::fstream& fp,
|
||||||
|
const std::string& usage);
|
||||||
|
|
||||||
|
std::string generate_sdc_port(const BasicPort& port);
|
||||||
|
|
||||||
|
void print_pnr_sdc_constrain_max_delay(std::fstream& fp,
|
||||||
|
const std::string& src_instance_name,
|
||||||
|
const std::string& src_port_name,
|
||||||
|
const std::string& des_instance_name,
|
||||||
|
const std::string& des_port_name,
|
||||||
|
const float& delay);
|
||||||
|
|
||||||
|
void print_pnr_sdc_constrain_module_port2port_timing(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& input_parent_module_id,
|
||||||
|
const ModulePortId& module_input_port_id,
|
||||||
|
const ModuleId& output_parent_module_id,
|
||||||
|
const ModulePortId& module_output_port_id,
|
||||||
|
const float& tmax);
|
||||||
|
|
||||||
|
void print_pnr_sdc_constrain_port2port_timing(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& input_parent_module_id,
|
||||||
|
const ModulePortId& module_input_port_id,
|
||||||
|
const ModuleId& output_parent_module_id,
|
||||||
|
const ModulePortId& module_output_port_id,
|
||||||
|
const float& tmax);
|
||||||
|
|
||||||
|
void print_sdc_disable_port_timing(std::fstream& fp,
|
||||||
|
const BasicPort& port);
|
||||||
|
|
||||||
|
void print_sdc_set_port_input_delay(std::fstream& fp,
|
||||||
|
const BasicPort& port,
|
||||||
|
const BasicPort& clock_port,
|
||||||
|
const float& delay);
|
||||||
|
|
||||||
|
void print_sdc_set_port_output_delay(std::fstream& fp,
|
||||||
|
const BasicPort& port,
|
||||||
|
const BasicPort& clock_port,
|
||||||
|
const float& delay);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,7 +3,7 @@
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
#include "vtr_assert.h"
|
#include "vtr_assert.h"
|
||||||
|
|
||||||
#include "verilog_options.h"
|
#include "fabric_verilog_options.h"
|
||||||
|
|
||||||
/* begin namespace openfpga */
|
/* begin namespace openfpga */
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
@ -52,22 +52,6 @@ bool FabricVerilogOption::compress_routing() const {
|
||||||
return compress_routing_;
|
return compress_routing_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FabricVerilogOption::print_top_testbench() const {
|
|
||||||
return print_top_testbench_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FabricVerilogOption::print_formal_verification_top_netlist() const {
|
|
||||||
return print_formal_verification_top_netlist_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FabricVerilogOption::print_autocheck_top_testbench() const {
|
|
||||||
return false == reference_verilog_file_path_.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string FabricVerilogOption::reference_verilog_file_path() const {
|
|
||||||
return reference_verilog_file_path_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FabricVerilogOption::print_user_defined_template() const {
|
bool FabricVerilogOption::print_user_defined_template() const {
|
||||||
return print_user_defined_template_;
|
return print_user_defined_template_;
|
||||||
}
|
}
|
||||||
|
@ -103,18 +87,6 @@ void FabricVerilogOption::set_compress_routing(const bool& enabled) {
|
||||||
compress_routing_ = enabled;
|
compress_routing_ = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FabricVerilogOption::set_print_top_testbench(const bool& enabled) {
|
|
||||||
print_top_testbench_ = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FabricVerilogOption::set_print_formal_verification_top_netlist(const bool& enabled) {
|
|
||||||
print_formal_verification_top_netlist_ = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FabricVerilogOption::set_print_autocheck_top_testbench(const std::string& reference_verilog_file_path) {
|
|
||||||
reference_verilog_file_path_ = reference_verilog_file_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FabricVerilogOption::set_print_user_defined_template(const bool& enabled) {
|
void FabricVerilogOption::set_print_user_defined_template(const bool& enabled) {
|
||||||
print_user_defined_template_ = enabled;
|
print_user_defined_template_ = enabled;
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef VERILOG_OPTIONS_H
|
#ifndef FABRIC_VERILOG_OPTIONS_H
|
||||||
#define VERILOG_OPTIONS_H
|
#define FABRIC_VERILOG_OPTIONS_H
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Include header files required by the data structure definition
|
* Include header files required by the data structure definition
|
||||||
|
@ -10,11 +10,7 @@
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* FlowManager aims to resolve the dependency between OpenFPGA functional
|
* Options for Fabric Verilog generator
|
||||||
* code blocks
|
|
||||||
* It can provide flags for downstream modules about if the data structures
|
|
||||||
* they require have already been constructed
|
|
||||||
*
|
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
class FabricVerilogOption {
|
class FabricVerilogOption {
|
||||||
public: /* Public constructor */
|
public: /* Public constructor */
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*********************************************************************
|
||||||
|
* This function includes the writer for generating exchangeable
|
||||||
|
* information, in order to interface different simulators
|
||||||
|
********************************************************************/
|
||||||
|
#include <cmath>
|
||||||
|
#include <ctime>
|
||||||
|
#include <map>
|
||||||
|
#define MINI_CASE_SENSITIVE
|
||||||
|
#include "ini.h"
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "vtr_time.h"
|
||||||
|
|
||||||
|
#include "simulation_utils.h"
|
||||||
|
|
||||||
|
#include "verilog_constants.h"
|
||||||
|
#include "simulation_info_writer.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* Top-level function to write an ini file which contains exchangeable
|
||||||
|
* information, in order to interface different Verilog simulators
|
||||||
|
********************************************************************/
|
||||||
|
void print_verilog_simulation_info(const std::string& ini_fname,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const std::string& src_dir,
|
||||||
|
const size_t& num_program_clock_cycles,
|
||||||
|
const int& num_operating_clock_cycles,
|
||||||
|
const float& prog_clock_freq,
|
||||||
|
const float& op_clock_freq) {
|
||||||
|
|
||||||
|
std::string timer_message = std::string("Write exchangeable file containing simulation information '") + ini_fname + std::string("'");
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
|
|
||||||
|
/* Use default name if user does not provide one */
|
||||||
|
VTR_ASSERT(true != ini_fname.empty());
|
||||||
|
|
||||||
|
mINI::INIStructure ini;
|
||||||
|
// std::map<char, int> units_map;
|
||||||
|
// units_map['s']=1; // units_map['ms']=1E-3; // units_map['us']=1E-6;
|
||||||
|
// units_map['ns']=1E-9; // units_map['ps']=1E-12; // units_map['fs']=1E-15;
|
||||||
|
|
||||||
|
/* Compute simulation time period */
|
||||||
|
float simulation_time_period = find_simulation_time_period(1E-3,
|
||||||
|
num_program_clock_cycles,
|
||||||
|
1. / prog_clock_freq,
|
||||||
|
num_operating_clock_cycles,
|
||||||
|
1. / op_clock_freq);
|
||||||
|
ini["SIMULATION_DECK"]["PROJECTNAME "] = "ModelSimProject";
|
||||||
|
ini["SIMULATION_DECK"]["BENCHMARK "] = circuit_name;
|
||||||
|
ini["SIMULATION_DECK"]["TOP_TB"] = circuit_name + std::string(FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX);
|
||||||
|
ini["SIMULATION_DECK"]["SIMTIME "] = std::to_string(simulation_time_period);
|
||||||
|
ini["SIMULATION_DECK"]["UNIT "] = "ms";
|
||||||
|
ini["SIMULATION_DECK"]["VERILOG_PATH "] = std::string(src_dir);
|
||||||
|
ini["SIMULATION_DECK"]["VERILOG_FILE1"] = std::string(DEFINES_VERILOG_FILE_NAME);
|
||||||
|
ini["SIMULATION_DECK"]["VERILOG_FILE2"] = std::string(circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX));
|
||||||
|
|
||||||
|
mINI::INIFile file(ini_fname);
|
||||||
|
file.generate(ini, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef SIMULATION_INFO_WRITER_H
|
||||||
|
#define SIMULATION_INFO_WRITER_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void print_verilog_simulation_info(const std::string& ini_fname,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const std::string& src_dir,
|
||||||
|
const size_t& num_program_clock_cycles,
|
||||||
|
const int& num_operating_clock_cycles,
|
||||||
|
const float& prog_clock_freq,
|
||||||
|
const float& op_clock_freq);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -20,6 +20,11 @@
|
||||||
#include "verilog_grid.h"
|
#include "verilog_grid.h"
|
||||||
#include "verilog_top_module.h"
|
#include "verilog_top_module.h"
|
||||||
|
|
||||||
|
#include "verilog_preconfig_top_module.h"
|
||||||
|
#include "verilog_formal_random_top_testbench.h"
|
||||||
|
#include "verilog_top_testbench.h"
|
||||||
|
#include "simulation_info_writer.h"
|
||||||
|
|
||||||
/* Header file for this source file */
|
/* Header file for this source file */
|
||||||
#include "verilog_api.h"
|
#include "verilog_api.h"
|
||||||
|
|
||||||
|
@ -27,17 +32,18 @@
|
||||||
namespace openfpga {
|
namespace openfpga {
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Top-level function of FPGA-Verilog
|
* A top-level function of FPGA-Verilog which focuses on fabric Verilog generation
|
||||||
* This function will generate
|
* This function will generate
|
||||||
* 1. primitive modules required by the full fabric
|
* - primitive modules required by the full fabric
|
||||||
* which are LUTs, routing multiplexer, logic gates, transmission-gates etc.
|
* - which are LUTs, routing multiplexer, logic gates, transmission-gates etc.
|
||||||
* 2. Routing modules, which are Switch Blocks (SBs) and Connection Blocks (CBs)
|
* - Routing modules, which are Switch Blocks (SBs) and Connection Blocks (CBs)
|
||||||
* 3. Logic block modules, which are Configuration Logic Blocks (CLBs)
|
* - Logic block modules, which are Configuration Logic Blocks (CLBs)
|
||||||
* 4. FPGA module, which are the full FPGA fabric with configuration protocol
|
* - FPGA module, which are the full FPGA fabric with configuration protocol
|
||||||
* 5. A wrapper module, which encapsulate the FPGA module in a Verilog module which have the same port as the input benchmark
|
*
|
||||||
* 6. Testbench, where a FPGA module is configured with a bitstream and then driven by input vectors
|
* Note:
|
||||||
* 7. Pre-configured testbench, which can skip the configuration phase and pre-configure the FPGA module. This testbench is created for quick verification and formal verification purpose.
|
* - Please do NOT include ANY testbench generation in this function!!!
|
||||||
* 8. Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated
|
* It is about the fabric itself, independent from any implementation
|
||||||
|
* All the testbench generation should be in the function fpga_testbench_verilog()
|
||||||
*
|
*
|
||||||
* TODO: We should use module manager as a constant here.
|
* TODO: We should use module manager as a constant here.
|
||||||
* All the modification should be done before this writer!
|
* All the modification should be done before this writer!
|
||||||
|
@ -75,9 +81,6 @@ void fpga_fabric_verilog(ModuleManager& module_manager,
|
||||||
print_verilog_preprocessing_flags_netlist(std::string(src_dir_path),
|
print_verilog_preprocessing_flags_netlist(std::string(src_dir_path),
|
||||||
options);
|
options);
|
||||||
|
|
||||||
print_verilog_simulation_preprocessing_flags(std::string(src_dir_path),
|
|
||||||
options);
|
|
||||||
|
|
||||||
/* Generate primitive Verilog modules, which are corner stones of FPGA fabric
|
/* Generate primitive Verilog modules, which are corner stones of FPGA fabric
|
||||||
* Note that this function MUST be called before Verilog generation of
|
* Note that this function MUST be called before Verilog generation of
|
||||||
* core logic (i.e., logic blocks and routing resources) !!!
|
* core logic (i.e., logic blocks and routing resources) !!!
|
||||||
|
@ -121,4 +124,106 @@ void fpga_fabric_verilog(ModuleManager& module_manager,
|
||||||
module_manager.num_modules());
|
module_manager.num_modules());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* A top-level function of FPGA-Verilog which focuses on fabric Verilog generation
|
||||||
|
* This function will generate
|
||||||
|
* - A wrapper module, which encapsulate the FPGA module in a Verilog module which have the same port as the input benchmark
|
||||||
|
* - Testbench, where a FPGA module is configured with a bitstream and then driven by input vectors
|
||||||
|
* - Pre-configured testbench, which can skip the configuration phase and pre-configure the FPGA module.
|
||||||
|
* This testbench is created for quick verification and formal verification purpose.
|
||||||
|
* - Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated
|
||||||
|
********************************************************************/
|
||||||
|
void fpga_verilog_testbench(const ModuleManager& module_manager,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const std::vector<ConfigBitId>& fabric_bitstream,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const SimulationSetting& simulation_setting,
|
||||||
|
const e_config_protocol_type& config_protocol_type,
|
||||||
|
const VerilogTestbenchOption& options) {
|
||||||
|
|
||||||
|
vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n");
|
||||||
|
|
||||||
|
std::string src_dir_path = format_dir_path(options.output_directory());
|
||||||
|
|
||||||
|
std::string netlist_name = atom_ctx.nlist.netlist_name();
|
||||||
|
|
||||||
|
/* Create directories */
|
||||||
|
create_dir_path(src_dir_path.c_str());
|
||||||
|
|
||||||
|
/* TODO: check if this works here. This function was in fabric generator */
|
||||||
|
print_verilog_simulation_preprocessing_flags(std::string(src_dir_path),
|
||||||
|
options);
|
||||||
|
|
||||||
|
/* Collect global ports from the circuit library:
|
||||||
|
* TODO: should we place this in the OpenFPGA context?
|
||||||
|
*/
|
||||||
|
std::vector<CircuitPortId> global_ports = find_circuit_library_global_ports(circuit_lib);
|
||||||
|
|
||||||
|
/* Generate wrapper module for FPGA fabric (mapped by the input benchmark and pre-configured testbench for verification */
|
||||||
|
if (true == options.print_formal_verification_top_netlist()) {
|
||||||
|
std::string formal_verification_top_netlist_file_path = src_dir_path + netlist_name
|
||||||
|
+ std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX);
|
||||||
|
print_verilog_preconfig_top_module(module_manager, bitstream_manager,
|
||||||
|
circuit_lib, global_ports,
|
||||||
|
atom_ctx, place_ctx, io_location_map,
|
||||||
|
netlist_annotation,
|
||||||
|
netlist_name,
|
||||||
|
formal_verification_top_netlist_file_path,
|
||||||
|
src_dir_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true == options.print_preconfig_top_testbench()) {
|
||||||
|
/* Generate top-level testbench using random vectors */
|
||||||
|
std::string random_top_testbench_file_path = src_dir_path + netlist_name
|
||||||
|
+ std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX);
|
||||||
|
print_verilog_random_top_testbench(netlist_name,
|
||||||
|
random_top_testbench_file_path,
|
||||||
|
src_dir_path,
|
||||||
|
atom_ctx,
|
||||||
|
netlist_annotation,
|
||||||
|
simulation_setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate full testbench for verification, including configuration phase and operating phase */
|
||||||
|
if (true == options.print_top_testbench()) {
|
||||||
|
std::string top_testbench_file_path = src_dir_path + netlist_name
|
||||||
|
+ std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX);
|
||||||
|
print_verilog_top_testbench(module_manager,
|
||||||
|
bitstream_manager, fabric_bitstream,
|
||||||
|
config_protocol_type,
|
||||||
|
circuit_lib, global_ports,
|
||||||
|
atom_ctx, place_ctx, io_location_map,
|
||||||
|
netlist_annotation,
|
||||||
|
netlist_name,
|
||||||
|
top_testbench_file_path,
|
||||||
|
src_dir_path,
|
||||||
|
simulation_setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate exchangeable files which contains simulation settings */
|
||||||
|
if (true == options.print_simulation_ini()) {
|
||||||
|
std::string simulation_ini_file_name = options.simulation_ini_path();
|
||||||
|
VTR_ASSERT(true != options.simulation_ini_path().empty());
|
||||||
|
print_verilog_simulation_info(simulation_ini_file_name,
|
||||||
|
netlist_name,
|
||||||
|
src_dir_path,
|
||||||
|
bitstream_manager.bits().size(),
|
||||||
|
simulation_setting.num_clock_cycles(),
|
||||||
|
simulation_setting.programming_clock_frequency(),
|
||||||
|
simulation_setting.operating_clock_frequency());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate a Verilog file including all the netlists that have been generated */
|
||||||
|
print_include_netlists(src_dir_path,
|
||||||
|
netlist_name,
|
||||||
|
options.reference_benchmark_file_path(),
|
||||||
|
circuit_lib);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
|
@ -7,14 +7,18 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "vpr_types.h"
|
|
||||||
#include "mux_library.h"
|
#include "mux_library.h"
|
||||||
#include "circuit_library.h"
|
#include "circuit_library.h"
|
||||||
#include "vpr_context.h"
|
#include "vpr_context.h"
|
||||||
#include "vpr_device_annotation.h"
|
#include "vpr_device_annotation.h"
|
||||||
#include "device_rr_gsb.h"
|
#include "device_rr_gsb.h"
|
||||||
#include "module_manager.h"
|
#include "module_manager.h"
|
||||||
#include "verilog_options.h"
|
#include "bitstream_manager.h"
|
||||||
|
#include "simulation_setting.h"
|
||||||
|
#include "io_location_map.h"
|
||||||
|
#include "vpr_netlist_annotation.h"
|
||||||
|
#include "fabric_verilog_options.h"
|
||||||
|
#include "verilog_testbench_options.h"
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Function declaration
|
* Function declaration
|
||||||
|
@ -31,6 +35,19 @@ void fpga_fabric_verilog(ModuleManager& module_manager,
|
||||||
const DeviceRRGSB& device_rr_gsb,
|
const DeviceRRGSB& device_rr_gsb,
|
||||||
const FabricVerilogOption& options);
|
const FabricVerilogOption& options);
|
||||||
|
|
||||||
|
void fpga_verilog_testbench(const ModuleManager& module_manager,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const std::vector<ConfigBitId>& fabric_bitstream,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const SimulationSetting& simulation_parameters,
|
||||||
|
const e_config_protocol_type& config_protocol_type,
|
||||||
|
const VerilogTestbenchOption& options);
|
||||||
|
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -23,7 +23,6 @@ namespace openfpga {
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Local constant variables
|
* Local constant variables
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
constexpr char* TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v";
|
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Print a file that includes all the netlists that have been generated
|
* Print a file that includes all the netlists that have been generated
|
||||||
|
@ -115,7 +114,7 @@ void print_include_netlists(const std::string& src_dir,
|
||||||
* which are used enable/disable some features in FPGA Verilog modules
|
* which are used enable/disable some features in FPGA Verilog modules
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
|
void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
|
||||||
const FabricVerilogOption& fpga_verilog_opts) {
|
const FabricVerilogOption& fabric_verilog_opts) {
|
||||||
|
|
||||||
std::string verilog_fname = src_dir + std::string(DEFINES_VERILOG_FILE_NAME);
|
std::string verilog_fname = src_dir + std::string(DEFINES_VERILOG_FILE_NAME);
|
||||||
|
|
||||||
|
@ -130,25 +129,19 @@ void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
|
||||||
print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable features in FPGA Verilog modules"));
|
print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable features in FPGA Verilog modules"));
|
||||||
|
|
||||||
/* To enable timing */
|
/* To enable timing */
|
||||||
if (true == fpga_verilog_opts.include_timing()) {
|
if (true == fabric_verilog_opts.include_timing()) {
|
||||||
print_verilog_define_flag(fp, std::string(VERILOG_TIMING_PREPROC_FLAG), 1);
|
print_verilog_define_flag(fp, std::string(VERILOG_TIMING_PREPROC_FLAG), 1);
|
||||||
fp << std::endl;
|
fp << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To enable timing */
|
/* To enable timing */
|
||||||
if (true == fpga_verilog_opts.include_signal_init()) {
|
if (true == fabric_verilog_opts.include_signal_init()) {
|
||||||
print_verilog_define_flag(fp, std::string(VERILOG_SIGNAL_INIT_PREPROC_FLAG), 1);
|
print_verilog_define_flag(fp, std::string(VERILOG_SIGNAL_INIT_PREPROC_FLAG), 1);
|
||||||
fp << std::endl;
|
fp << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To enable formal verfication flag */
|
|
||||||
if (true == fpga_verilog_opts.print_formal_verification_top_netlist()) {
|
|
||||||
print_verilog_define_flag(fp, std::string(VERILOG_FORMAL_VERIFICATION_PREPROC_FLAG), 1);
|
|
||||||
fp << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* To enable functional verfication with Icarus */
|
/* To enable functional verfication with Icarus */
|
||||||
if (true == fpga_verilog_opts.support_icarus_simulator()) {
|
if (true == fabric_verilog_opts.support_icarus_simulator()) {
|
||||||
print_verilog_define_flag(fp, std::string(ICARUS_SIMULATOR_FLAG), 1);
|
print_verilog_define_flag(fp, std::string(ICARUS_SIMULATOR_FLAG), 1);
|
||||||
fp << std::endl;
|
fp << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -161,7 +154,7 @@ void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
|
||||||
* Print a Verilog file containing simulation-related preprocessing flags
|
* Print a Verilog file containing simulation-related preprocessing flags
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
void print_verilog_simulation_preprocessing_flags(const std::string& src_dir,
|
void print_verilog_simulation_preprocessing_flags(const std::string& src_dir,
|
||||||
const FabricVerilogOption& fpga_verilog_opts) {
|
const VerilogTestbenchOption& verilog_testbench_opts) {
|
||||||
|
|
||||||
std::string verilog_fname = src_dir + std::string(DEFINES_VERILOG_SIMULATION_FILE_NAME);
|
std::string verilog_fname = src_dir + std::string(DEFINES_VERILOG_SIMULATION_FILE_NAME);
|
||||||
|
|
||||||
|
@ -176,19 +169,26 @@ void print_verilog_simulation_preprocessing_flags(const std::string& src_dir,
|
||||||
print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable simulation features"));
|
print_verilog_file_header(fp, std::string("Preprocessing flags to enable/disable simulation features"));
|
||||||
|
|
||||||
/* To enable manualy checked simulation */
|
/* To enable manualy checked simulation */
|
||||||
if (true == fpga_verilog_opts.print_top_testbench()) {
|
if (true == verilog_testbench_opts.print_top_testbench()) {
|
||||||
print_verilog_define_flag(fp, std::string(INITIAL_SIMULATION_FLAG), 1);
|
print_verilog_define_flag(fp, std::string(INITIAL_SIMULATION_FLAG), 1);
|
||||||
fp << std::endl;
|
fp << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To enable auto-checked simulation */
|
/* To enable auto-checked simulation */
|
||||||
if (true == fpga_verilog_opts.print_autocheck_top_testbench()) {
|
if ( (true == verilog_testbench_opts.print_preconfig_top_testbench())
|
||||||
|
|| (true == verilog_testbench_opts.print_top_testbench()) ) {
|
||||||
print_verilog_define_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG), 1);
|
print_verilog_define_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG), 1);
|
||||||
fp << std::endl;
|
fp << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To enable pre-configured FPGA simulation */
|
/* To enable pre-configured FPGA simulation */
|
||||||
if (true == fpga_verilog_opts.print_formal_verification_top_netlist()) {
|
if (true == verilog_testbench_opts.print_formal_verification_top_netlist()) {
|
||||||
|
print_verilog_define_flag(fp, std::string(VERILOG_FORMAL_VERIFICATION_PREPROC_FLAG), 1);
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To enable pre-configured FPGA simulation */
|
||||||
|
if (true == verilog_testbench_opts.print_preconfig_top_testbench()) {
|
||||||
print_verilog_define_flag(fp, std::string(FORMAL_SIMULATION_FLAG), 1);
|
print_verilog_define_flag(fp, std::string(FORMAL_SIMULATION_FLAG), 1);
|
||||||
fp << std::endl;
|
fp << std::endl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "circuit_library.h"
|
#include "circuit_library.h"
|
||||||
#include "verilog_options.h"
|
#include "fabric_verilog_options.h"
|
||||||
|
#include "verilog_testbench_options.h"
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Function declaration
|
* Function declaration
|
||||||
|
@ -21,10 +22,10 @@ void print_include_netlists(const std::string& src_dir,
|
||||||
const CircuitLibrary& circuit_lib);
|
const CircuitLibrary& circuit_lib);
|
||||||
|
|
||||||
void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
|
void print_verilog_preprocessing_flags_netlist(const std::string& src_dir,
|
||||||
const FabricVerilogOption& fpga_verilog_opts);
|
const FabricVerilogOption& fabric_verilog_opts);
|
||||||
|
|
||||||
void print_verilog_simulation_preprocessing_flags(const std::string& src_dir,
|
void print_verilog_simulation_preprocessing_flags(const std::string& src_dir,
|
||||||
const FabricVerilogOption& fpga_verilog_opts);
|
const VerilogTestbenchOption& verilog_testbench_opts);
|
||||||
|
|
||||||
} /* end namespace openfpga */
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ constexpr char* MODELSIM_SIMULATION_TIME_UNIT = "ms";
|
||||||
constexpr char* ICARUS_SIMULATOR_FLAG = "ICARUS_SIMULATOR"; // the flag to enable specific Verilog code in testbenches
|
constexpr char* ICARUS_SIMULATOR_FLAG = "ICARUS_SIMULATOR"; // the flag to enable specific Verilog code in testbenches
|
||||||
// End of Icarus variables and flag
|
// End of Icarus variables and flag
|
||||||
|
|
||||||
|
constexpr char* TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v";
|
||||||
constexpr char* VERILOG_TOP_POSTFIX = "_top.v";
|
constexpr char* VERILOG_TOP_POSTFIX = "_top.v";
|
||||||
constexpr char* FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX = "_top_formal_verification.v";
|
constexpr char* FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX = "_top_formal_verification.v";
|
||||||
constexpr char* TOP_TESTBENCH_VERILOG_FILE_POSTFIX = "_top_tb.v"; /* !!! must be consist with the modelsim_testbench_module_postfix */
|
constexpr char* TOP_TESTBENCH_VERILOG_FILE_POSTFIX = "_top_tb.v"; /* !!! must be consist with the modelsim_testbench_module_postfix */
|
||||||
|
@ -48,4 +49,12 @@ constexpr char* SB_VERILOG_FILE_NAME_PREFIX = "sb_";
|
||||||
constexpr char* LOGICAL_MODULE_VERILOG_FILE_NAME_PREFIX = "logical_tile_";
|
constexpr char* LOGICAL_MODULE_VERILOG_FILE_NAME_PREFIX = "logical_tile_";
|
||||||
constexpr char* GRID_VERILOG_FILE_NAME_PREFIX = "grid_";
|
constexpr char* GRID_VERILOG_FILE_NAME_PREFIX = "grid_";
|
||||||
|
|
||||||
|
constexpr char* FORMAL_VERIFICATION_TOP_MODULE_POSTFIX = "_top_formal_verification";
|
||||||
|
constexpr char* FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX = "_fm";
|
||||||
|
constexpr char* FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME = "U0_formal_verification";
|
||||||
|
|
||||||
|
constexpr char* FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX = "_top_formal_verification_random_tb";
|
||||||
|
|
||||||
|
#define VERILOG_DEFAULT_SIGNAL_INIT_VALUE 0
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file includes functions that are used to generate a Verilog
|
||||||
|
* testbench for the top-level module (FPGA fabric), in purpose of
|
||||||
|
* running formal verification with random input vectors
|
||||||
|
*******************************************************************/
|
||||||
|
#include <fstream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cmath>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "vtr_log.h"
|
||||||
|
#include "vtr_time.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_port.h"
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
|
#include "openfpga_atom_netlist_utils.h"
|
||||||
|
#include "simulation_utils.h"
|
||||||
|
|
||||||
|
#include "verilog_constants.h"
|
||||||
|
#include "verilog_writer_utils.h"
|
||||||
|
#include "verilog_testbench_utils.h"
|
||||||
|
#include "verilog_formal_random_top_testbench.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Local variables used only in this file
|
||||||
|
*******************************************************************/
|
||||||
|
constexpr char* FPGA_PORT_POSTFIX = "_gfpga";
|
||||||
|
constexpr char* BENCHMARK_PORT_POSTFIX = "_bench";
|
||||||
|
constexpr char* CHECKFLAG_PORT_POSTFIX = "_flag";
|
||||||
|
constexpr char* DEFAULT_CLOCK_NAME = "clk";
|
||||||
|
constexpr char* BENCHMARK_INSTANCE_NAME = "REF_DUT";
|
||||||
|
constexpr char* FPGA_INSTANCE_NAME = "FPGA_DUT";
|
||||||
|
constexpr char* ERROR_COUNTER = "nb_error";
|
||||||
|
constexpr char* FORMAL_TB_SIM_START_PORT_NAME = "sim_start";
|
||||||
|
constexpr int MAGIC_NUMBER_FOR_SIMULATION_TIME = 200;
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print the module ports for the Verilog testbench
|
||||||
|
* using random vectors
|
||||||
|
* This function generates
|
||||||
|
* 1. the input ports to drive both input benchmark module and FPGA fabric module
|
||||||
|
* 2. the output ports for input benchmark module
|
||||||
|
* 3. the output ports for FPGA fabric module
|
||||||
|
* 4. the error checking ports
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_random_testbench_ports(std::fstream& fp,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const std::vector<std::string>& clock_port_names,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Print the declaration for the module */
|
||||||
|
fp << "module " << circuit_name << FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX << ";" << std::endl;
|
||||||
|
|
||||||
|
/* Create a clock port if the benchmark does not have one!
|
||||||
|
* The clock is used for counting and synchronizing input stimulus
|
||||||
|
*/
|
||||||
|
BasicPort clock_port = generate_verilog_testbench_clock_port(clock_port_names, std::string(DEFAULT_CLOCK_NAME));
|
||||||
|
print_verilog_comment(fp, std::string("----- Default clock port is added here since benchmark does not contain one -------"));
|
||||||
|
fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, clock_port) << ";" << std::endl;
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
print_verilog_testbench_shared_ports(fp, atom_ctx, netlist_annotation,
|
||||||
|
clock_port_names,
|
||||||
|
std::string(BENCHMARK_PORT_POSTFIX),
|
||||||
|
std::string(FPGA_PORT_POSTFIX),
|
||||||
|
std::string(CHECKFLAG_PORT_POSTFIX),
|
||||||
|
std::string(AUTOCHECKED_SIMULATION_FLAG));
|
||||||
|
|
||||||
|
/* Instantiate an integer to count the number of error
|
||||||
|
* and determine if the simulation succeed or failed
|
||||||
|
*/
|
||||||
|
print_verilog_comment(fp, std::string("----- Error counter -------"));
|
||||||
|
fp << "\tinteger " << ERROR_COUNTER << "= 0;" << std::endl;
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Instanciate the input benchmark module
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_random_testbench_benchmark_instance(std::fstream& fp,
|
||||||
|
const std::string& reference_verilog_top_name,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Benchmark is instanciated conditionally: only when a preprocessing flag is enable */
|
||||||
|
print_verilog_preprocessing_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG));
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Reference Benchmark Instanication -------"));
|
||||||
|
|
||||||
|
/* Do NOT use explicit port mapping here:
|
||||||
|
* VPR added a prefix of "out_" to the output ports of input benchmark
|
||||||
|
*/
|
||||||
|
print_verilog_testbench_benchmark_instance(fp, reference_verilog_top_name,
|
||||||
|
std::string(BENCHMARK_INSTANCE_NAME),
|
||||||
|
std::string(),
|
||||||
|
std::string(),
|
||||||
|
std::string(BENCHMARK_PORT_POSTFIX),
|
||||||
|
atom_ctx, netlist_annotation,
|
||||||
|
false);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- End reference Benchmark Instanication -------"));
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Condition ends for the benchmark instanciation */
|
||||||
|
print_verilog_endif(fp);
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Instanciate the FPGA fabric module
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_random_testbench_fpga_instance(std::fstream& fp,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- FPGA fabric instanciation -------"));
|
||||||
|
|
||||||
|
/* Always use explicit port mapping */
|
||||||
|
print_verilog_testbench_benchmark_instance(fp, std::string(circuit_name + std::string(FORMAL_VERIFICATION_TOP_MODULE_POSTFIX)),
|
||||||
|
std::string(FPGA_INSTANCE_NAME),
|
||||||
|
std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX),
|
||||||
|
std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX),
|
||||||
|
std::string(FPGA_PORT_POSTFIX),
|
||||||
|
atom_ctx, netlist_annotation,
|
||||||
|
true);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- End FPGA Fabric Instanication -------"));
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
* Top-level function in this file:
|
||||||
|
* Create a Verilog testbench using random input vectors
|
||||||
|
* The testbench consists of two modules, i.e., Design Under Test (DUT)
|
||||||
|
* 1. top-level module of FPGA fabric
|
||||||
|
* 2. top-level module of users' benchmark,
|
||||||
|
* i.e., the input benchmark of VPR flow
|
||||||
|
* +----------+
|
||||||
|
* | FPGA | +------------+
|
||||||
|
* +----->| Fabric |------>| |
|
||||||
|
* | | | | |
|
||||||
|
* | +----------+ | |
|
||||||
|
* | | Output |
|
||||||
|
* random_input_vectors -----+ | Vector |---->Functional correct?
|
||||||
|
* | | Comparator |
|
||||||
|
* | +-----------+ | |
|
||||||
|
* | | Input | | |
|
||||||
|
* +----->| Benchmark |----->| |
|
||||||
|
* +-----------+ +------------+
|
||||||
|
*
|
||||||
|
* Same input vectors are given to drive both DUTs.
|
||||||
|
* The output vectors of the DUTs are compared to verify if they
|
||||||
|
* have the same functionality.
|
||||||
|
* A flag will be raised to indicate the result
|
||||||
|
********************************************************************/
|
||||||
|
void print_verilog_random_top_testbench(const std::string& circuit_name,
|
||||||
|
const std::string& verilog_fname,
|
||||||
|
const std::string& verilog_dir,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const SimulationSetting& simulation_parameters) {
|
||||||
|
std::string timer_message = std::string("Write configuration-skip testbench for FPGA top-level Verilog netlist implemented by '") + circuit_name.c_str() + std::string("'");
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
/* Validate the file stream */
|
||||||
|
check_file_stream(verilog_fname.c_str(), fp);
|
||||||
|
|
||||||
|
/* Generate a brief description on the Verilog file*/
|
||||||
|
std::string title = std::string("FPGA Verilog Testbench for Formal Top-level netlist of Design: ") + circuit_name;
|
||||||
|
print_verilog_file_header(fp, title);
|
||||||
|
|
||||||
|
/* Print preprocessing flags and external netlists */
|
||||||
|
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||||
|
|
||||||
|
print_verilog_include_netlist(fp, std::string(verilog_dir + std::string(DEFINES_VERILOG_SIMULATION_FILE_NAME)));
|
||||||
|
|
||||||
|
/* Preparation: find all the clock ports */
|
||||||
|
std::vector<std::string> clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation);
|
||||||
|
|
||||||
|
/* Start of testbench */
|
||||||
|
print_verilog_top_random_testbench_ports(fp, circuit_name, clock_port_names, atom_ctx, netlist_annotation);
|
||||||
|
|
||||||
|
/* Call defined top-level module */
|
||||||
|
print_verilog_random_testbench_fpga_instance(fp, circuit_name, atom_ctx, netlist_annotation);
|
||||||
|
|
||||||
|
/* Call defined benchmark */
|
||||||
|
print_verilog_top_random_testbench_benchmark_instance(fp, circuit_name, atom_ctx, netlist_annotation);
|
||||||
|
|
||||||
|
/* Find clock port to be used */
|
||||||
|
BasicPort clock_port = generate_verilog_testbench_clock_port(clock_port_names, std::string(DEFAULT_CLOCK_NAME));
|
||||||
|
|
||||||
|
/* Add stimuli for reset, set, clock and iopad signals */
|
||||||
|
print_verilog_testbench_clock_stimuli(fp, simulation_parameters,
|
||||||
|
clock_port);
|
||||||
|
print_verilog_testbench_random_stimuli(fp, atom_ctx,
|
||||||
|
netlist_annotation,
|
||||||
|
clock_port_names,
|
||||||
|
std::string(CHECKFLAG_PORT_POSTFIX),
|
||||||
|
clock_port);
|
||||||
|
|
||||||
|
print_verilog_testbench_check(fp,
|
||||||
|
std::string(AUTOCHECKED_SIMULATION_FLAG),
|
||||||
|
std::string(FORMAL_TB_SIM_START_PORT_NAME),
|
||||||
|
std::string(BENCHMARK_PORT_POSTFIX),
|
||||||
|
std::string(FPGA_PORT_POSTFIX),
|
||||||
|
std::string(CHECKFLAG_PORT_POSTFIX),
|
||||||
|
std::string(ERROR_COUNTER),
|
||||||
|
atom_ctx,
|
||||||
|
netlist_annotation,
|
||||||
|
clock_port_names,
|
||||||
|
std::string(DEFAULT_CLOCK_NAME));
|
||||||
|
|
||||||
|
int simulation_time = find_operating_phase_simulation_time(MAGIC_NUMBER_FOR_SIMULATION_TIME,
|
||||||
|
simulation_parameters.num_clock_cycles(),
|
||||||
|
1./simulation_parameters.operating_clock_frequency(),
|
||||||
|
VERILOG_SIM_TIMESCALE);
|
||||||
|
|
||||||
|
/* Add Icarus requirement */
|
||||||
|
print_verilog_timeout_and_vcd(fp,
|
||||||
|
std::string(ICARUS_SIMULATOR_FLAG),
|
||||||
|
std::string(circuit_name + std::string(FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX)),
|
||||||
|
std::string(circuit_name + std::string("_formal.vcd")),
|
||||||
|
std::string(FORMAL_TB_SIM_START_PORT_NAME),
|
||||||
|
std::string(ERROR_COUNTER),
|
||||||
|
simulation_time);
|
||||||
|
|
||||||
|
/* Testbench ends*/
|
||||||
|
print_verilog_module_end(fp, std::string(circuit_name) + std::string(FORMAL_RANDOM_TOP_TESTBENCH_POSTFIX));
|
||||||
|
|
||||||
|
/* Close the file stream */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef VERILOG_FORMAL_RANDOM_TOP_TESTBENCH
|
||||||
|
#define VERILOG_FORMAL_RANDOM_TOP_TESTBENCH
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <string>
|
||||||
|
#include "vpr_context.h"
|
||||||
|
#include "simulation_setting.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void print_verilog_random_top_testbench(const std::string& circuit_name,
|
||||||
|
const std::string& verilog_fname,
|
||||||
|
const std::string& verilog_dir,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const SimulationSetting& simulation_parameters);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,452 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file includes functions that are used to generate
|
||||||
|
* a Verilog module of a pre-configured FPGA fabric
|
||||||
|
*******************************************************************/
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "vtr_log.h"
|
||||||
|
#include "vtr_time.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_port.h"
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
|
#include "bitstream_manager_utils.h"
|
||||||
|
#include "openfpga_atom_netlist_utils.h"
|
||||||
|
|
||||||
|
#include "openfpga_naming.h"
|
||||||
|
|
||||||
|
#include "verilog_constants.h"
|
||||||
|
#include "verilog_writer_utils.h"
|
||||||
|
#include "verilog_testbench_utils.h"
|
||||||
|
#include "verilog_preconfig_top_module.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print module declaration and ports for the pre-configured
|
||||||
|
* FPGA top module
|
||||||
|
* The module ports do exactly match the input benchmark
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_preconfig_top_module_ports(std::fstream& fp,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation) {
|
||||||
|
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Module declaration */
|
||||||
|
fp << "module " << circuit_name << std::string(FORMAL_VERIFICATION_TOP_MODULE_POSTFIX);
|
||||||
|
fp << " (" << std::endl;
|
||||||
|
|
||||||
|
/* Add module ports */
|
||||||
|
size_t port_counter = 0;
|
||||||
|
|
||||||
|
/* Port type-to-type mapping */
|
||||||
|
std::map<AtomBlockType, enum e_dump_verilog_port_type> port_type2type_map;
|
||||||
|
port_type2type_map[AtomBlockType::INPAD] = VERILOG_PORT_INPUT;
|
||||||
|
port_type2type_map[AtomBlockType::OUTPAD] = VERILOG_PORT_OUTPUT;
|
||||||
|
|
||||||
|
/* Print all the I/Os of the circuit implementation to be tested*/
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* We only care I/O logical blocks !*/
|
||||||
|
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
|
||||||
|
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 < port_counter) {
|
||||||
|
fp << "," << std::endl;
|
||||||
|
}
|
||||||
|
/* Both input and output ports have only size of 1 */
|
||||||
|
BasicPort module_port(std::string(block_name + std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX)), 1);
|
||||||
|
fp << generate_verilog_port(port_type2type_map[atom_ctx.nlist.block_type(atom_blk)], module_port);
|
||||||
|
|
||||||
|
/* Update port counter */
|
||||||
|
port_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp << ");" << std::endl;
|
||||||
|
|
||||||
|
/* Add an empty line as a splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print internal wires for the pre-configured FPGA top module
|
||||||
|
* The internal wires are tailored for the ports of FPGA top module
|
||||||
|
* which will be different in various configuration protocols
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_preconfig_top_module_internal_wires(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Global ports of top-level module */
|
||||||
|
print_verilog_comment(fp, std::string("----- Local wires for FPGA fabric -----"));
|
||||||
|
for (const ModulePortId& module_port_id : module_manager.module_ports(top_module)) {
|
||||||
|
BasicPort module_port = module_manager.module_port(top_module, module_port_id);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_WIRE, module_port) << ";" << std::endl;
|
||||||
|
}
|
||||||
|
/* Add an empty line as a splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Connect global ports of FPGA top module to constants except:
|
||||||
|
* 1. operating clock, which should be wired to the clock port of
|
||||||
|
* this pre-configured FPGA top module
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_preconfig_top_module_connect_global_ports(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports,
|
||||||
|
const std::vector<std::string>& benchmark_clock_port_names) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Begin Connect Global ports of FPGA top module -----"));
|
||||||
|
|
||||||
|
/* Global ports of the top module in module manager do not carry any attributes,
|
||||||
|
* such as is_clock, is_set, etc.
|
||||||
|
* Therefore, for each global port in the top module, we find the circuit port in the circuit library
|
||||||
|
* which share the same name. We can access to the attributes.
|
||||||
|
* To gurantee the correct link between global ports in module manager and those in circuit library
|
||||||
|
* We have performed some critical check in check_circuit_library() for global ports,
|
||||||
|
* where we guarantee all the global ports share the same name must have the same attributes.
|
||||||
|
* So that each global port with the same name is unique!
|
||||||
|
*/
|
||||||
|
for (const BasicPort& module_global_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GLOBAL_PORT)) {
|
||||||
|
CircuitPortId linked_circuit_port_id = CircuitPortId::INVALID();
|
||||||
|
/* Find the circuit port with the same name */
|
||||||
|
for (const CircuitPortId& circuit_port_id : global_ports) {
|
||||||
|
if (0 != module_global_port.get_name().compare(circuit_lib.port_prefix(circuit_port_id))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
linked_circuit_port_id = circuit_port_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Must find one valid circuit port */
|
||||||
|
VTR_ASSERT(CircuitPortId::INVALID() != linked_circuit_port_id);
|
||||||
|
/* Port size should match! */
|
||||||
|
VTR_ASSERT(module_global_port.get_width() == circuit_lib.port_size(linked_circuit_port_id));
|
||||||
|
/* Now, for operating clock port, we should wire it to the clock of benchmark! */
|
||||||
|
if ( (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(linked_circuit_port_id))
|
||||||
|
&& (false == circuit_lib.port_is_prog(linked_circuit_port_id)) ) {
|
||||||
|
/* Wiring to each pin of the global port: benchmark clock is always 1-bit */
|
||||||
|
for (const size_t& pin : module_global_port.pins()) {
|
||||||
|
for (const std::string& clock_port_name : benchmark_clock_port_names) {
|
||||||
|
BasicPort module_clock_pin(module_global_port.get_name(), pin, pin);
|
||||||
|
BasicPort benchmark_clock_pin(clock_port_name + std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX), 1);
|
||||||
|
print_verilog_wire_connection(fp, module_clock_pin, benchmark_clock_pin, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Finish, go to the next */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For other ports, give an default value */
|
||||||
|
std::vector<size_t> default_values(module_global_port.get_width(), circuit_lib.port_default_value(linked_circuit_port_id));
|
||||||
|
print_verilog_wire_constant_values(fp, module_global_port, default_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- End Connect Global ports of FPGA top module -----"));
|
||||||
|
|
||||||
|
/* Add an empty line as a splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Impose the bitstream on the configuration memories
|
||||||
|
* This function uses 'assign' syntax to impost the bitstream at mem port
|
||||||
|
* while uses 'force' syntax to impost the bitstream at mem_inv port
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_preconfig_top_module_assign_bitstream(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const BitstreamManager& bitstream_manager) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Begin assign bitstream to configuration memories -----"));
|
||||||
|
|
||||||
|
for (const ConfigBlockId& config_block_id : bitstream_manager.blocks()) {
|
||||||
|
/* We only cares blocks with configuration bits */
|
||||||
|
if (0 == bitstream_manager.block_bits(config_block_id).size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Build the hierarchical path of the configuration bit in modules */
|
||||||
|
std::vector<ConfigBlockId> block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, config_block_id);
|
||||||
|
/* Drop the first block, which is the top module, it should be replaced by the instance name here */
|
||||||
|
/* Ensure that this is the module we want to drop! */
|
||||||
|
VTR_ASSERT(0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0])));
|
||||||
|
block_hierarchy.erase(block_hierarchy.begin());
|
||||||
|
/* Build the full hierarchy path */
|
||||||
|
std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME);
|
||||||
|
for (const ConfigBlockId& temp_block : block_hierarchy) {
|
||||||
|
bit_hierarchy_path += std::string(".");
|
||||||
|
bit_hierarchy_path += bitstream_manager.block_name(temp_block);
|
||||||
|
}
|
||||||
|
bit_hierarchy_path += std::string(".");
|
||||||
|
|
||||||
|
/* Find the bit index in the parent block */
|
||||||
|
BasicPort config_data_port(bit_hierarchy_path + generate_configuration_chain_data_out_name(),
|
||||||
|
bitstream_manager.block_bits(config_block_id).size());
|
||||||
|
|
||||||
|
/* Wire it to the configuration bit: access both data out and data outb ports */
|
||||||
|
std::vector<size_t> config_data_values;
|
||||||
|
for (const ConfigBitId config_bit : bitstream_manager.block_bits(config_block_id)) {
|
||||||
|
config_data_values.push_back(bitstream_manager.bit_value(config_bit));
|
||||||
|
}
|
||||||
|
print_verilog_wire_constant_values(fp, config_data_port, config_data_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp << "initial begin" << std::endl;
|
||||||
|
|
||||||
|
for (const ConfigBlockId& config_block_id : bitstream_manager.blocks()) {
|
||||||
|
/* We only cares blocks with configuration bits */
|
||||||
|
if (0 == bitstream_manager.block_bits(config_block_id).size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Build the hierarchical path of the configuration bit in modules */
|
||||||
|
std::vector<ConfigBlockId> block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, config_block_id);
|
||||||
|
/* Drop the first block, which is the top module, it should be replaced by the instance name here */
|
||||||
|
/* Ensure that this is the module we want to drop! */
|
||||||
|
VTR_ASSERT(0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0])));
|
||||||
|
block_hierarchy.erase(block_hierarchy.begin());
|
||||||
|
/* Build the full hierarchy path */
|
||||||
|
std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME);
|
||||||
|
for (const ConfigBlockId& temp_block : block_hierarchy) {
|
||||||
|
bit_hierarchy_path += std::string(".");
|
||||||
|
bit_hierarchy_path += bitstream_manager.block_name(temp_block);
|
||||||
|
}
|
||||||
|
bit_hierarchy_path += std::string(".");
|
||||||
|
|
||||||
|
/* Find the bit index in the parent block */
|
||||||
|
BasicPort config_datab_port(bit_hierarchy_path + generate_configuration_chain_inverted_data_out_name(),
|
||||||
|
bitstream_manager.block_bits(config_block_id).size());
|
||||||
|
|
||||||
|
std::vector<size_t> config_datab_values;
|
||||||
|
for (const ConfigBitId config_bit : bitstream_manager.block_bits(config_block_id)) {
|
||||||
|
config_datab_values.push_back(!bitstream_manager.bit_value(config_bit));
|
||||||
|
}
|
||||||
|
print_verilog_force_wire_constant_values(fp, config_datab_port, config_datab_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp << "end" << std::endl;
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- End assign bitstream to configuration memories -----"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Impose the bitstream on the configuration memories
|
||||||
|
* This function uses '$deposit' syntax to do so
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_preconfig_top_module_deposit_bitstream(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const BitstreamManager& bitstream_manager) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Begin deposit bitstream to configuration memories -----"));
|
||||||
|
|
||||||
|
fp << "initial begin" << std::endl;
|
||||||
|
|
||||||
|
for (const ConfigBlockId& config_block_id : bitstream_manager.blocks()) {
|
||||||
|
/* We only cares blocks with configuration bits */
|
||||||
|
if (0 == bitstream_manager.block_bits(config_block_id).size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Build the hierarchical path of the configuration bit in modules */
|
||||||
|
std::vector<ConfigBlockId> block_hierarchy = find_bitstream_manager_block_hierarchy(bitstream_manager, config_block_id);
|
||||||
|
/* Drop the first block, which is the top module, it should be replaced by the instance name here */
|
||||||
|
/* Ensure that this is the module we want to drop! */
|
||||||
|
VTR_ASSERT(0 == module_manager.module_name(top_module).compare(bitstream_manager.block_name(block_hierarchy[0])));
|
||||||
|
block_hierarchy.erase(block_hierarchy.begin());
|
||||||
|
/* Build the full hierarchy path */
|
||||||
|
std::string bit_hierarchy_path(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME);
|
||||||
|
for (const ConfigBlockId& temp_block : block_hierarchy) {
|
||||||
|
bit_hierarchy_path += std::string(".");
|
||||||
|
bit_hierarchy_path += bitstream_manager.block_name(temp_block);
|
||||||
|
}
|
||||||
|
bit_hierarchy_path += std::string(".");
|
||||||
|
|
||||||
|
/* Find the bit index in the parent block */
|
||||||
|
BasicPort config_data_port(bit_hierarchy_path + generate_configuration_chain_data_out_name(),
|
||||||
|
bitstream_manager.block_bits(config_block_id).size());
|
||||||
|
|
||||||
|
BasicPort config_datab_port(bit_hierarchy_path + generate_configuration_chain_inverted_data_out_name(),
|
||||||
|
bitstream_manager.block_bits(config_block_id).size());
|
||||||
|
|
||||||
|
/* Wire it to the configuration bit: access both data out and data outb ports */
|
||||||
|
std::vector<size_t> config_data_values;
|
||||||
|
for (const ConfigBitId config_bit : bitstream_manager.block_bits(config_block_id)) {
|
||||||
|
config_data_values.push_back(bitstream_manager.bit_value(config_bit));
|
||||||
|
}
|
||||||
|
print_verilog_deposit_wire_constant_values(fp, config_data_port, config_data_values);
|
||||||
|
|
||||||
|
std::vector<size_t> config_datab_values;
|
||||||
|
for (const ConfigBitId config_bit : bitstream_manager.block_bits(config_block_id)) {
|
||||||
|
config_datab_values.push_back(!bitstream_manager.bit_value(config_bit));
|
||||||
|
}
|
||||||
|
print_verilog_deposit_wire_constant_values(fp, config_datab_port, config_datab_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp << "end" << std::endl;
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- End deposit bitstream to configuration memories -----"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Impose the bitstream on the configuration memories
|
||||||
|
* We branch here for different simulators:
|
||||||
|
* 1. iVerilog Icarus prefers using 'assign' syntax to force the values
|
||||||
|
* 2. Mentor Modelsim prefers using '$deposit' syntax to do so
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_preconfig_top_module_load_bitstream(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const BitstreamManager& bitstream_manager) {
|
||||||
|
print_verilog_comment(fp, std::string("----- Begin load bitstream to configuration memories -----"));
|
||||||
|
|
||||||
|
print_verilog_preprocessing_flag(fp, std::string(ICARUS_SIMULATOR_FLAG));
|
||||||
|
|
||||||
|
/* Use assign syntax for Icarus simulator */
|
||||||
|
print_verilog_preconfig_top_module_assign_bitstream(fp, module_manager, top_module, bitstream_manager);
|
||||||
|
|
||||||
|
fp << "`else" << std::endl;
|
||||||
|
|
||||||
|
/* Use assign syntax for Icarus simulator */
|
||||||
|
print_verilog_preconfig_top_module_deposit_bitstream(fp, module_manager, top_module, bitstream_manager);
|
||||||
|
|
||||||
|
print_verilog_endif(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- End load bitstream to configuration memories -----"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Top-level function to generate a Verilog module of
|
||||||
|
* a pre-configured FPGA fabric.
|
||||||
|
*
|
||||||
|
* Pre-configured FPGA fabric
|
||||||
|
* +--------------------------------------------
|
||||||
|
* |
|
||||||
|
* | FPGA fabric
|
||||||
|
* | +-------------------------------+
|
||||||
|
* | | |
|
||||||
|
* | 0/1---->|FPGA global ports |
|
||||||
|
* | | |
|
||||||
|
* benchmark_clock----->|--------->|FPGA_clock |
|
||||||
|
* | | |
|
||||||
|
* benchmark_inputs---->|--------->|FPGA mapped I/Os |
|
||||||
|
* | | |
|
||||||
|
* benchmark_outputs<---|<---------|FPGA mapped I/Os |
|
||||||
|
* | | |
|
||||||
|
* | 0/1---->|FPGA unmapped I/Os |
|
||||||
|
* | | |
|
||||||
|
* fabric_bitstream---->|--------->|Internal_configuration_ports |
|
||||||
|
* | +-------------------------------+
|
||||||
|
* |
|
||||||
|
* +-------------------------------------------
|
||||||
|
*
|
||||||
|
* Note: we do NOT put this module in the module manager.
|
||||||
|
* Because, it is not a standard module, where we force configuration signals
|
||||||
|
* This module is a wrapper for the FPGA fabric to be compatible in
|
||||||
|
* the port map of input benchmark.
|
||||||
|
* It includes wires to force constant values to part of FPGA datapath I/Os
|
||||||
|
* All these are hard to implement as a module in module manager
|
||||||
|
*******************************************************************/
|
||||||
|
void print_verilog_preconfig_top_module(const ModuleManager& module_manager,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const std::string& verilog_fname,
|
||||||
|
const std::string& verilog_dir) {
|
||||||
|
std::string timer_message = std::string("Write pre-configured FPGA top-level Verilog netlist for design '") + circuit_name + std::string("'");
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
/* Validate the file stream */
|
||||||
|
check_file_stream(verilog_fname.c_str(), fp);
|
||||||
|
|
||||||
|
/* Generate a brief description on the Verilog file*/
|
||||||
|
std::string title = std::string("Verilog netlist for pre-configured FPGA fabric by design: ") + circuit_name;
|
||||||
|
print_verilog_file_header(fp, title);
|
||||||
|
|
||||||
|
/* Print preprocessing flags and external netlists */
|
||||||
|
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||||
|
|
||||||
|
print_verilog_include_netlist(fp, std::string(verilog_dir + std::string(DEFINES_VERILOG_SIMULATION_FILE_NAME)));
|
||||||
|
|
||||||
|
/* Print module declaration and ports */
|
||||||
|
print_verilog_preconfig_top_module_ports(fp, circuit_name, atom_ctx, netlist_annotation);
|
||||||
|
|
||||||
|
/* Find the top_module */
|
||||||
|
ModuleId top_module = module_manager.find_module(generate_fpga_top_module_name());
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(top_module));
|
||||||
|
|
||||||
|
/* Print internal wires */
|
||||||
|
print_verilog_preconfig_top_module_internal_wires(fp, module_manager, top_module);
|
||||||
|
|
||||||
|
/* Instanciate FPGA top-level module */
|
||||||
|
print_verilog_testbench_fpga_instance(fp, module_manager, top_module,
|
||||||
|
std::string(FORMAL_VERIFICATION_TOP_MODULE_UUT_NAME));
|
||||||
|
|
||||||
|
/* Find clock ports in benchmark */
|
||||||
|
std::vector<std::string> benchmark_clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation);
|
||||||
|
|
||||||
|
/* Connect FPGA top module global ports to constant or benchmark global signals! */
|
||||||
|
print_verilog_preconfig_top_module_connect_global_ports(fp, module_manager, top_module,
|
||||||
|
circuit_lib, global_ports,
|
||||||
|
benchmark_clock_port_names);
|
||||||
|
|
||||||
|
/* Connect I/Os to benchmark I/Os or constant driver */
|
||||||
|
print_verilog_testbench_connect_fpga_ios(fp, module_manager, top_module,
|
||||||
|
atom_ctx, place_ctx, io_location_map,
|
||||||
|
netlist_annotation,
|
||||||
|
std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX),
|
||||||
|
std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX),
|
||||||
|
(size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE);
|
||||||
|
|
||||||
|
/* Assign FPGA internal SRAM/Memory ports to bitstream values */
|
||||||
|
print_verilog_preconfig_top_module_load_bitstream(fp, module_manager, top_module,
|
||||||
|
bitstream_manager);
|
||||||
|
|
||||||
|
/* Testbench ends*/
|
||||||
|
print_verilog_module_end(fp, std::string(circuit_name) + std::string(FORMAL_VERIFICATION_TOP_MODULE_POSTFIX));
|
||||||
|
|
||||||
|
/* Close the file stream */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef VERILOG_PRECONFIG_TOP_MODULE_H
|
||||||
|
#define VERILOG_PRECONFIG_TOP_MODULE_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include "circuit_library.h"
|
||||||
|
#include "vpr_context.h"
|
||||||
|
#include "module_manager.h"
|
||||||
|
#include "bitstream_manager.h"
|
||||||
|
#include "io_location_map.h"
|
||||||
|
#include "vpr_netlist_annotation.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void print_verilog_preconfig_top_module(const ModuleManager& module_manager,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const std::string& verilog_fname,
|
||||||
|
const std::string& verilog_dir);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -6,7 +6,7 @@
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
#include "module_manager.h"
|
#include "module_manager.h"
|
||||||
#include "mux_library.h"
|
#include "mux_library.h"
|
||||||
#include "verilog_options.h"
|
#include "fabric_verilog_options.h"
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
* Function declaration
|
* Function declaration
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* Memember functions for data structure VerilogTestbenchOption
|
||||||
|
******************************************************************************/
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "vtr_log.h"
|
||||||
|
|
||||||
|
#include "verilog_testbench_options.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/**************************************************
|
||||||
|
* Public Constructors
|
||||||
|
*************************************************/
|
||||||
|
VerilogTestbenchOption::VerilogTestbenchOption() {
|
||||||
|
output_directory_.clear();
|
||||||
|
reference_benchmark_file_path_.clear();
|
||||||
|
print_preconfig_top_testbench_ = false;
|
||||||
|
print_formal_verification_top_netlist_ = false;
|
||||||
|
print_top_testbench_ = false;
|
||||||
|
simulation_ini_path_.clear();
|
||||||
|
verbose_output_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************
|
||||||
|
* Public Accessors
|
||||||
|
*************************************************/
|
||||||
|
std::string VerilogTestbenchOption::output_directory() const {
|
||||||
|
return output_directory_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VerilogTestbenchOption::reference_benchmark_file_path() const {
|
||||||
|
return reference_benchmark_file_path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerilogTestbenchOption::print_formal_verification_top_netlist() const {
|
||||||
|
return print_formal_verification_top_netlist_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerilogTestbenchOption::print_preconfig_top_testbench() const {
|
||||||
|
return print_preconfig_top_testbench_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerilogTestbenchOption::print_top_testbench() const {
|
||||||
|
return print_top_testbench_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerilogTestbenchOption::print_simulation_ini() const {
|
||||||
|
return !simulation_ini_path_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VerilogTestbenchOption::simulation_ini_path() const {
|
||||||
|
return simulation_ini_path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerilogTestbenchOption::verbose_output() const {
|
||||||
|
return verbose_output_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* Private Mutators
|
||||||
|
******************************************************************************/
|
||||||
|
void VerilogTestbenchOption::set_output_directory(const std::string& output_dir) {
|
||||||
|
output_directory_ = output_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerilogTestbenchOption::set_reference_benchmark_file_path(const std::string& reference_benchmark_file_path) {
|
||||||
|
reference_benchmark_file_path_ = reference_benchmark_file_path;
|
||||||
|
/* Chain effect on other options:
|
||||||
|
* Enable/disable the print_preconfig_top_testbench and print_top_testbench
|
||||||
|
*/
|
||||||
|
set_print_preconfig_top_testbench(print_preconfig_top_testbench_);
|
||||||
|
set_print_top_testbench(print_top_testbench_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerilogTestbenchOption::set_print_formal_verification_top_netlist(const bool& enabled) {
|
||||||
|
print_formal_verification_top_netlist_ = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerilogTestbenchOption::set_print_preconfig_top_testbench(const bool& enabled) {
|
||||||
|
print_preconfig_top_testbench_ = enabled
|
||||||
|
&& (!reference_benchmark_file_path_.empty());
|
||||||
|
/* Enable print formal verification top_netlist if this is enabled */
|
||||||
|
if (true == print_preconfig_top_testbench_) {
|
||||||
|
if (false == print_formal_verification_top_netlist_) {
|
||||||
|
VTR_LOG_WARN("Forcely enable to print top-level Verilog netlist in formal verification purpose as print pre-configured top-level Verilog testbench is enabled\n");
|
||||||
|
print_formal_verification_top_netlist_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerilogTestbenchOption::set_print_top_testbench(const bool& enabled) {
|
||||||
|
print_top_testbench_ = enabled && (!reference_benchmark_file_path_.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerilogTestbenchOption::set_print_simulation_ini(const std::string& simulation_ini_path) {
|
||||||
|
simulation_ini_path_ = simulation_ini_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerilogTestbenchOption::set_verbose_output(const bool& enabled) {
|
||||||
|
verbose_output_ = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,62 @@
|
||||||
|
#ifndef VERILOG_TESTBENCH_OPTIONS_H
|
||||||
|
#define VERILOG_TESTBENCH_OPTIONS_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files required by the data structure definition
|
||||||
|
*******************************************************************/
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/* Begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Options for Verilog Testbench generator
|
||||||
|
* Typicall usage:
|
||||||
|
* VerilogTestbench verilog_tb_opt();
|
||||||
|
* // Set options
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
class VerilogTestbenchOption {
|
||||||
|
public: /* Public constructor */
|
||||||
|
/* Set default options */
|
||||||
|
VerilogTestbenchOption();
|
||||||
|
public: /* Public accessors */
|
||||||
|
std::string output_directory() const;
|
||||||
|
std::string reference_benchmark_file_path() const;
|
||||||
|
bool print_formal_verification_top_netlist() const;
|
||||||
|
bool print_preconfig_top_testbench() const;
|
||||||
|
bool print_top_testbench() const;
|
||||||
|
bool print_simulation_ini() const;
|
||||||
|
std::string simulation_ini_path() const;
|
||||||
|
bool verbose_output() const;
|
||||||
|
public: /* Public validator */
|
||||||
|
bool validate() const;
|
||||||
|
public: /* Public mutators */
|
||||||
|
void set_output_directory(const std::string& output_dir);
|
||||||
|
/* The reference verilog file path is the key parameters that will have an impact on other options:
|
||||||
|
* - print_preconfig_top_testbench
|
||||||
|
* - print_top_testbench
|
||||||
|
* If the file path is empty, the above testbench generation will not be enabled
|
||||||
|
*/
|
||||||
|
void set_reference_benchmark_file_path(const std::string& reference_benchmark_file_path);
|
||||||
|
void set_print_formal_verification_top_netlist(const bool& enabled);
|
||||||
|
/* The preconfig top testbench generation can be enabled only when formal verification top netlist is enabled */
|
||||||
|
void set_print_preconfig_top_testbench(const bool& enabled);
|
||||||
|
void set_print_top_testbench(const bool& enabled);
|
||||||
|
void set_print_simulation_ini(const std::string& simulation_ini_path);
|
||||||
|
void set_verbose_output(const bool& enabled);
|
||||||
|
private: /* Internal Data */
|
||||||
|
std::string output_directory_;
|
||||||
|
std::string reference_benchmark_file_path_;
|
||||||
|
bool print_formal_verification_top_netlist_;
|
||||||
|
bool print_preconfig_top_testbench_;
|
||||||
|
bool print_top_testbench_;
|
||||||
|
/* Print simulation ini is enabled only when the path is not empty */
|
||||||
|
std::string simulation_ini_path_;
|
||||||
|
bool verbose_output_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* End namespace openfpga*/
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,664 @@
|
||||||
|
/********************************************************************
|
||||||
|
* 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 <algorithm>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_port.h"
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
|
#include "verilog_port_types.h"
|
||||||
|
|
||||||
|
#include "verilog_constants.h"
|
||||||
|
#include "verilog_writer_utils.h"
|
||||||
|
#include "verilog_testbench_utils.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print an instance of the FPGA top-level module
|
||||||
|
*******************************************************************/
|
||||||
|
void print_verilog_testbench_fpga_instance(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const std::string& top_instance_name) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Include defined top-level module */
|
||||||
|
print_verilog_comment(fp, std::string("----- FPGA top-level module to be capsulated -----"));
|
||||||
|
|
||||||
|
/* Create an empty port-to-port name mapping, because we use default names */
|
||||||
|
std::map<std::string, BasicPort> port2port_name_map;
|
||||||
|
|
||||||
|
/* Use explicit port mapping for a clean instanciation */
|
||||||
|
print_verilog_module_instance(fp, module_manager, top_module,
|
||||||
|
top_instance_name,
|
||||||
|
port2port_name_map, true);
|
||||||
|
|
||||||
|
/* Add an empty line as a splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Instanciate the input benchmark module
|
||||||
|
*******************************************************************/
|
||||||
|
void print_verilog_testbench_benchmark_instance(std::fstream& fp,
|
||||||
|
const std::string& module_name,
|
||||||
|
const std::string& instance_name,
|
||||||
|
const std::string& module_input_port_postfix,
|
||||||
|
const std::string& module_output_port_postfix,
|
||||||
|
const std::string& output_port_postfix,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const bool& use_explicit_port_map) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
fp << "\t" << module_name << " " << instance_name << "(" << std::endl;
|
||||||
|
|
||||||
|
size_t port_counter = 0;
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* Bypass non-I/O atom blocks ! */
|
||||||
|
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
|
||||||
|
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The first port does not need a comma */
|
||||||
|
if(0 < port_counter){
|
||||||
|
fp << "," << std::endl;
|
||||||
|
}
|
||||||
|
/* Input port follows the logical block name while output port requires a special postfix */
|
||||||
|
if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
fp << "\t\t";
|
||||||
|
if (true == use_explicit_port_map) {
|
||||||
|
fp << "." << block_name << module_input_port_postfix << "(";
|
||||||
|
}
|
||||||
|
fp << block_name;
|
||||||
|
if (true == use_explicit_port_map) {
|
||||||
|
fp << ")";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT_SAFE(AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk));
|
||||||
|
fp << "\t\t";
|
||||||
|
if (true == use_explicit_port_map) {
|
||||||
|
fp << "." << block_name << module_output_port_postfix << "(";
|
||||||
|
}
|
||||||
|
fp << block_name << output_port_postfix;
|
||||||
|
if (true == use_explicit_port_map) {
|
||||||
|
fp << ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Update the counter */
|
||||||
|
port_counter++;
|
||||||
|
}
|
||||||
|
fp << "\t);" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* This function adds stimuli to I/Os of FPGA fabric
|
||||||
|
* 1. For mapped I/Os, this function will wire them to the input ports
|
||||||
|
* of the pre-configured FPGA top module
|
||||||
|
* 2. For unmapped I/Os, this function will assign a constant value
|
||||||
|
* by default
|
||||||
|
*******************************************************************/
|
||||||
|
void print_verilog_testbench_connect_fpga_ios(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::string& io_input_port_name_postfix,
|
||||||
|
const std::string& io_output_port_name_postfix,
|
||||||
|
const size_t& unused_io_value) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* In this function, we support only 1 type of I/Os */
|
||||||
|
VTR_ASSERT(1 == module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT).size());
|
||||||
|
BasicPort module_io_port = module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT)[0];
|
||||||
|
|
||||||
|
/* Keep tracking which I/Os have been used */
|
||||||
|
std::vector<bool> io_used(module_io_port.get_width(), false);
|
||||||
|
|
||||||
|
/* See if this I/O should be wired to a benchmark input/output */
|
||||||
|
/* Add signals from blif benchmark and short-wire them to FPGA I/O PADs
|
||||||
|
* This brings convenience to checking functionality
|
||||||
|
*/
|
||||||
|
print_verilog_comment(fp, std::string("----- Link BLIF Benchmark I/Os to FPGA I/Os -----"));
|
||||||
|
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* Bypass non-I/O atom blocks ! */
|
||||||
|
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
|
||||||
|
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the index of the mapped GPIO in top-level FPGA fabric */
|
||||||
|
size_t io_index = io_location_map.io_index(place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.x,
|
||||||
|
place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.y,
|
||||||
|
place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.z);
|
||||||
|
|
||||||
|
/* Ensure that IO index is in range */
|
||||||
|
BasicPort module_mapped_io_port = module_io_port;
|
||||||
|
/* Set the port pin index */
|
||||||
|
VTR_ASSERT(io_index < module_mapped_io_port.get_width());
|
||||||
|
module_mapped_io_port.set_width(io_index, io_index);
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the port for benchmark I/O, due to BLIF benchmark, each I/O always has a size of 1
|
||||||
|
* In addition, the input and output ports may have different postfix in naming
|
||||||
|
* due to verification context! Here, we give full customization on naming
|
||||||
|
*/
|
||||||
|
BasicPort benchmark_io_port;
|
||||||
|
if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
benchmark_io_port.set_name(std::string(block_name + io_input_port_name_postfix));
|
||||||
|
benchmark_io_port.set_width(1);
|
||||||
|
print_verilog_comment(fp, std::string("----- Blif Benchmark input " + block_name + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----"));
|
||||||
|
print_verilog_wire_connection(fp, module_mapped_io_port, benchmark_io_port, false);
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT(AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk));
|
||||||
|
benchmark_io_port.set_name(std::string(block_name + io_output_port_name_postfix));
|
||||||
|
benchmark_io_port.set_width(1);
|
||||||
|
print_verilog_comment(fp, std::string("----- Blif Benchmark output " + block_name + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----"));
|
||||||
|
print_verilog_wire_connection(fp, benchmark_io_port, module_mapped_io_port, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark this I/O has been used/wired */
|
||||||
|
io_used[io_index] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add an empty line as a splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Wire the unused iopads to a constant */
|
||||||
|
print_verilog_comment(fp, std::string("----- Wire unused FPGA I/Os to constants -----"));
|
||||||
|
for (size_t io_index = 0; io_index < io_used.size(); ++io_index) {
|
||||||
|
/* Bypass used iopads */
|
||||||
|
if (true == io_used[io_index]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wire to a contant */
|
||||||
|
BasicPort module_unused_io_port = module_io_port;
|
||||||
|
/* Set the port pin index */
|
||||||
|
module_unused_io_port.set_width(io_index, io_index);
|
||||||
|
|
||||||
|
std::vector<size_t> default_values(module_unused_io_port.get_width(), unused_io_value);
|
||||||
|
print_verilog_wire_constant_values(fp, module_unused_io_port, default_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add an empty line as a splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print Verilog codes to set up a timeout for the simulation
|
||||||
|
* and dump the waveform to VCD files
|
||||||
|
*
|
||||||
|
* Note that: these codes are tuned for Icarus simulator!!!
|
||||||
|
*******************************************************************/
|
||||||
|
void print_verilog_timeout_and_vcd(std::fstream& fp,
|
||||||
|
const std::string& icarus_preprocessing_flag,
|
||||||
|
const std::string& module_name,
|
||||||
|
const std::string& vcd_fname,
|
||||||
|
const std::string& simulation_start_counter_name,
|
||||||
|
const std::string& error_counter_name,
|
||||||
|
const int& simulation_time) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* The following verilog codes are tuned for Icarus */
|
||||||
|
print_verilog_preprocessing_flag(fp, icarus_preprocessing_flag);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Begin Icarus requirement -------"));
|
||||||
|
|
||||||
|
fp << "\tinitial begin" << std::endl;
|
||||||
|
fp << "\t\t$dumpfile(\"" << vcd_fname << "\");" << std::endl;
|
||||||
|
fp << "\t\t$dumpvars(1, " << module_name << ");" << std::endl;
|
||||||
|
fp << "\tend" << std::endl;
|
||||||
|
|
||||||
|
/* Condition ends for the Icarus requirement */
|
||||||
|
print_verilog_endif(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- END Icarus requirement -------"));
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
BasicPort sim_start_port(simulation_start_counter_name, 1);
|
||||||
|
|
||||||
|
fp << "initial begin" << std::endl;
|
||||||
|
fp << "\t" << generate_verilog_port(VERILOG_PORT_CONKT, sim_start_port) << " <= 1'b1;" << std::endl;
|
||||||
|
fp << "\t$timeformat(-9, 2, \"ns\", 20);" << std::endl;
|
||||||
|
fp << "\t$display(\"Simulation start\");" << std::endl;
|
||||||
|
print_verilog_comment(fp, std::string("----- Can be changed by the user for his/her need -------"));
|
||||||
|
fp << "\t#" << simulation_time << std::endl;
|
||||||
|
fp << "\tif(" << error_counter_name << " == 0) begin" << std::endl;
|
||||||
|
fp << "\t\t$display(\"Simulation Succeed\");" << std::endl;
|
||||||
|
fp << "\tend else begin" << std::endl;
|
||||||
|
fp << "\t\t$display(\"Simulation Failed with " << std::string("%d") << " error(s)\", " << error_counter_name << ");" << std::endl;
|
||||||
|
fp << "\tend" << std::endl;
|
||||||
|
fp << "\t$finish;" << std::endl;
|
||||||
|
fp << "end" << std::endl;
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Generate the clock port name to be used in this testbench
|
||||||
|
*
|
||||||
|
* Restrictions:
|
||||||
|
* Assume this is a single clock benchmark
|
||||||
|
*******************************************************************/
|
||||||
|
BasicPort generate_verilog_testbench_clock_port(const std::vector<std::string>& clock_port_names,
|
||||||
|
const std::string& default_clock_name) {
|
||||||
|
if (0 == clock_port_names.size()) {
|
||||||
|
return BasicPort(default_clock_name, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
VTR_ASSERT(1 == clock_port_names.size());
|
||||||
|
return BasicPort(clock_port_names[0], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print Verilog codes to check the equivalence of output vectors
|
||||||
|
*
|
||||||
|
* Restriction: this function only supports single clock benchmarks!
|
||||||
|
*******************************************************************/
|
||||||
|
void print_verilog_testbench_check(std::fstream& fp,
|
||||||
|
const std::string& autochecked_preprocessing_flag,
|
||||||
|
const std::string& simulation_start_counter_name,
|
||||||
|
const std::string& benchmark_port_postfix,
|
||||||
|
const std::string& fpga_port_postfix,
|
||||||
|
const std::string& check_flag_port_postfix,
|
||||||
|
const std::string& error_counter_name,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::vector<std::string>& clock_port_names,
|
||||||
|
const std::string& default_clock_name) {
|
||||||
|
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Add output autocheck conditionally: only when a preprocessing flag is enable */
|
||||||
|
print_verilog_preprocessing_flag(fp, autochecked_preprocessing_flag);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Begin checking output vectors -------"));
|
||||||
|
|
||||||
|
BasicPort clock_port = generate_verilog_testbench_clock_port(clock_port_names, default_clock_name);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Skip the first falling edge of clock, it is for initialization -------"));
|
||||||
|
|
||||||
|
BasicPort sim_start_port(simulation_start_counter_name, 1);
|
||||||
|
|
||||||
|
fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, sim_start_port) << ";" << std::endl;
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
fp << "\talways@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << ") begin" << std::endl;
|
||||||
|
fp << "\t\tif (1'b1 == " << generate_verilog_port(VERILOG_PORT_CONKT, sim_start_port) << ") begin" << std::endl;
|
||||||
|
fp << "\t\t";
|
||||||
|
print_verilog_register_connection(fp, sim_start_port, sim_start_port, true);
|
||||||
|
fp << "\t\tend else begin" << std::endl;
|
||||||
|
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* Bypass non-I/O atom blocks ! */
|
||||||
|
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
|
||||||
|
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
fp << "\t\t\tif(!(" << block_name << fpga_port_postfix;
|
||||||
|
fp << " === " << block_name << benchmark_port_postfix;
|
||||||
|
fp << ") && !(" << block_name << benchmark_port_postfix;
|
||||||
|
fp << " === 1'bx)) begin" << std::endl;
|
||||||
|
fp << "\t\t\t\t" << block_name << check_flag_port_postfix << " <= 1'b1;" << std::endl;
|
||||||
|
fp << "\t\t\tend else begin" << std::endl;
|
||||||
|
fp << "\t\t\t\t" << block_name << check_flag_port_postfix << "<= 1'b0;" << std::endl;
|
||||||
|
fp << "\t\t\tend" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fp << "\t\tend" << std::endl;
|
||||||
|
fp << "\tend" << std::endl;
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* Only care about output atom blocks ! */
|
||||||
|
if (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp << "\talways@(posedge " << block_name << check_flag_port_postfix << ") begin" << std::endl;
|
||||||
|
fp << "\t\tif(" << block_name << check_flag_port_postfix << ") begin" << std::endl;
|
||||||
|
fp << "\t\t\t" << error_counter_name << " = " << error_counter_name << " + 1;" << std::endl;
|
||||||
|
fp << "\t\t\t$display(\"Mismatch on " << block_name << fpga_port_postfix << " at time = " << std::string("%t") << "\", $realtime);" << std::endl;
|
||||||
|
fp << "\t\tend" << std::endl;
|
||||||
|
fp << "\tend" << std::endl;
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Condition ends */
|
||||||
|
print_verilog_endif(fp);
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Generate random stimulus for the clock port
|
||||||
|
* This function is designed to drive the clock port of a benchmark module
|
||||||
|
* If there is no clock port found, we will give a default clock name
|
||||||
|
* In such case, this clock will not be wired to the benchmark module
|
||||||
|
* but be only used as a synchronizer in verification
|
||||||
|
*******************************************************************/
|
||||||
|
void print_verilog_testbench_clock_stimuli(std::fstream& fp,
|
||||||
|
const SimulationSetting& simulation_parameters,
|
||||||
|
const BasicPort& clock_port) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Clock Initialization -------"));
|
||||||
|
|
||||||
|
fp << "\tinitial begin" << std::endl;
|
||||||
|
/* Create clock stimuli */
|
||||||
|
fp << "\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << " <= 1'b0;" << std::endl;
|
||||||
|
fp << "\t\twhile(1) begin" << std::endl;
|
||||||
|
fp << "\t\t\t#" << std::setprecision(10) << ((0.5/simulation_parameters.operating_clock_frequency())/VERILOG_SIM_TIMESCALE) << std::endl;
|
||||||
|
fp << "\t\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, clock_port);
|
||||||
|
fp << " <= !";
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_CONKT, clock_port);
|
||||||
|
fp << ";" << std::endl;
|
||||||
|
fp << "\t\tend" << std::endl;
|
||||||
|
|
||||||
|
fp << "\tend" << std::endl;
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Generate random stimulus for the input ports (non-clock signals)
|
||||||
|
* For clock signals, please use print_verilog_testbench_clock_stimuli
|
||||||
|
*******************************************************************/
|
||||||
|
void print_verilog_testbench_random_stimuli(std::fstream& fp,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::vector<std::string>& clock_port_names,
|
||||||
|
const std::string& check_flag_port_postfix,
|
||||||
|
const BasicPort& clock_port) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Input Initialization -------"));
|
||||||
|
|
||||||
|
fp << "\tinitial begin" << std::endl;
|
||||||
|
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* Bypass non-I/O atom blocks ! */
|
||||||
|
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
|
||||||
|
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bypass clock ports */
|
||||||
|
if (clock_port_names.end() != std::find(clock_port_names.begin(), clock_port_names.end(), block_name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: find the clock inputs will be initialized later */
|
||||||
|
if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
fp << "\t\t" << block_name << " <= 1'b0;" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Set 0 to registers for checking flags */
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* Bypass non-I/O atom blocks ! */
|
||||||
|
if (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each logical block assumes a single-width port */
|
||||||
|
BasicPort output_port(std::string(block_name + check_flag_port_postfix), 1);
|
||||||
|
fp << "\t\t" << generate_verilog_port(VERILOG_PORT_CONKT, output_port) << " <= 1'b0;" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp << "\tend" << std::endl;
|
||||||
|
/* Finish initialization */
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
// Not ready yet to determine if input is reset
|
||||||
|
/*
|
||||||
|
fprintf(fp, "//----- Reset Stimulis\n");
|
||||||
|
fprintf(fp, " initial begin\n");
|
||||||
|
fprintf(fp, " #%.3f\n",(rand() % 10) + 0.001);
|
||||||
|
fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name);
|
||||||
|
fprintf(fp, " #%.3f\n",(rand() % 10) + 0.001);
|
||||||
|
fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name);
|
||||||
|
fprintf(fp, " while(1) begin\n");
|
||||||
|
fprintf(fp, " #%.3f\n", (rand() % 15) + 0.5);
|
||||||
|
fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name);
|
||||||
|
fprintf(fp, " #%.3f\n", (rand() % 10000) + 200);
|
||||||
|
fprintf(fp, " %s <= !%s;\n", reset_input_name, reset_input_name);
|
||||||
|
fprintf(fp, " end\n");
|
||||||
|
fprintf(fp, " end\n\n");
|
||||||
|
*/
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Input Stimulus -------"));
|
||||||
|
fp << "\talways@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, clock_port) << ") begin" << std::endl;
|
||||||
|
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* Bypass non-I/O atom blocks ! */
|
||||||
|
if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
|
||||||
|
&& (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bypass clock ports */
|
||||||
|
if (clock_port_names.end() != std::find(clock_port_names.begin(), clock_port_names.end(), block_name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: find the clock inputs will be initialized later */
|
||||||
|
if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
fp << "\t\t" << block_name << " <= $random;" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fp << "\tend" << std::endl;
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print Verilog declaration of shared ports appear in testbenches
|
||||||
|
* which are
|
||||||
|
* 1. the shared input ports (registers) to drive both
|
||||||
|
* FPGA fabric and benchmark instance
|
||||||
|
* 2. the output ports (wires) for both FPGA fabric and benchmark instance
|
||||||
|
* 3. the checking flag ports to evaluate if outputs matches under the
|
||||||
|
* same input vectors
|
||||||
|
*******************************************************************/
|
||||||
|
void print_verilog_testbench_shared_ports(std::fstream& fp,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::vector<std::string>& clock_port_names,
|
||||||
|
const std::string& benchmark_output_port_postfix,
|
||||||
|
const std::string& fpga_output_port_postfix,
|
||||||
|
const std::string& check_flag_port_postfix,
|
||||||
|
const std::string& autocheck_preprocessing_flag) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Instantiate register for inputs stimulis */
|
||||||
|
print_verilog_comment(fp, std::string("----- Shared inputs -------"));
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* We care only those logic blocks which are input I/Os */
|
||||||
|
if (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Skip clocks because they are handled in another function */
|
||||||
|
if (clock_port_names.end() != std::find(clock_port_names.begin(), clock_port_names.end(), block_name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each logical block assumes a single-width port */
|
||||||
|
BasicPort input_port(block_name, 1);
|
||||||
|
fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, input_port) << ";" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Instantiate wires for FPGA fabric outputs */
|
||||||
|
print_verilog_comment(fp, std::string("----- FPGA fabric outputs -------"));
|
||||||
|
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* We care only those logic blocks which are output I/Os */
|
||||||
|
if (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each logical block assumes a single-width port */
|
||||||
|
BasicPort output_port(std::string(block_name + fpga_output_port_postfix), 1);
|
||||||
|
fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, output_port) << ";" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Benchmark is instanciated conditionally: only when a preprocessing flag is enable */
|
||||||
|
print_verilog_preprocessing_flag(fp, std::string(autocheck_preprocessing_flag));
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Instantiate wire for benchmark output */
|
||||||
|
print_verilog_comment(fp, std::string("----- Benchmark outputs -------"));
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* We care only those logic blocks which are output I/Os */
|
||||||
|
if (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each logical block assumes a single-width port */
|
||||||
|
BasicPort output_port(std::string(block_name + benchmark_output_port_postfix), 1);
|
||||||
|
fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, output_port) << ";" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Instantiate register for output comparison */
|
||||||
|
print_verilog_comment(fp, std::string("----- Output vectors checking flags -------"));
|
||||||
|
for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
|
||||||
|
/* We care only those logic blocks which are output I/Os */
|
||||||
|
if (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The block may be renamed as it contains special characters which violate Verilog syntax */
|
||||||
|
std::string block_name = atom_ctx.nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each logical block assumes a single-width port */
|
||||||
|
BasicPort output_port(std::string(block_name + check_flag_port_postfix), 1);
|
||||||
|
fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, output_port) << ";" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Condition ends for the benchmark instanciation */
|
||||||
|
print_verilog_endif(fp);
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,94 @@
|
||||||
|
#ifndef VERILOG_TESTBENCH_UTILS_H
|
||||||
|
#define VERILOG_TESTBENCH_UTILS_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "module_manager.h"
|
||||||
|
#include "vpr_context.h"
|
||||||
|
#include "io_location_map.h"
|
||||||
|
#include "vpr_netlist_annotation.h"
|
||||||
|
#include "simulation_setting.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void print_verilog_testbench_fpga_instance(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const std::string& top_instance_name);
|
||||||
|
|
||||||
|
void print_verilog_testbench_benchmark_instance(std::fstream& fp,
|
||||||
|
const std::string& module_name,
|
||||||
|
const std::string& instance_name,
|
||||||
|
const std::string& module_input_port_postfix,
|
||||||
|
const std::string& module_output_port_postfix,
|
||||||
|
const std::string& output_port_postfix,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const bool& use_explicit_port_map);
|
||||||
|
|
||||||
|
void print_verilog_testbench_connect_fpga_ios(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::string& io_input_port_name_postfix,
|
||||||
|
const std::string& io_output_port_name_postfix,
|
||||||
|
const size_t& unused_io_value);
|
||||||
|
|
||||||
|
void print_verilog_timeout_and_vcd(std::fstream& fp,
|
||||||
|
const std::string& icarus_preprocessing_flag,
|
||||||
|
const std::string& module_name,
|
||||||
|
const std::string& vcd_fname,
|
||||||
|
const std::string& simulation_start_counter_name,
|
||||||
|
const std::string& error_counter_name,
|
||||||
|
const int& simulation_time);
|
||||||
|
|
||||||
|
BasicPort generate_verilog_testbench_clock_port(const std::vector<std::string>& clock_port_names,
|
||||||
|
const std::string& default_clock_name);
|
||||||
|
|
||||||
|
void print_verilog_testbench_check(std::fstream& fp,
|
||||||
|
const std::string& autochecked_preprocessing_flag,
|
||||||
|
const std::string& simulation_start_counter_name,
|
||||||
|
const std::string& benchmark_port_postfix,
|
||||||
|
const std::string& fpga_port_postfix,
|
||||||
|
const std::string& check_flag_port_postfix,
|
||||||
|
const std::string& error_counter_name,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::vector<std::string>& clock_port_names,
|
||||||
|
const std::string& default_clock_name);
|
||||||
|
|
||||||
|
void print_verilog_testbench_clock_stimuli(std::fstream& fp,
|
||||||
|
const SimulationSetting& simulation_parameters,
|
||||||
|
const BasicPort& clock_port);
|
||||||
|
|
||||||
|
void print_verilog_testbench_random_stimuli(std::fstream& fp,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::vector<std::string>& clock_port_names,
|
||||||
|
const std::string& check_flag_port_postfix,
|
||||||
|
const BasicPort& clock_port);
|
||||||
|
|
||||||
|
void print_verilog_testbench_shared_ports(std::fstream& fp,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::vector<std::string>& clock_port_names,
|
||||||
|
const std::string& benchmark_output_port_postfix,
|
||||||
|
const std::string& fpga_output_port_postfix,
|
||||||
|
const std::string& check_flag_port_postfix,
|
||||||
|
const std::string& autocheck_preprocessing_flag);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,900 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file includes functions that are used to create
|
||||||
|
* an auto-check top-level testbench for a FPGA fabric
|
||||||
|
*******************************************************************/
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_log.h"
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "vtr_time.h"
|
||||||
|
|
||||||
|
/* Headers from openfpgautil library */
|
||||||
|
#include "openfpga_port.h"
|
||||||
|
#include "openfpga_digest.h"
|
||||||
|
|
||||||
|
#include "bitstream_manager_utils.h"
|
||||||
|
|
||||||
|
#include "openfpga_naming.h"
|
||||||
|
#include "simulation_utils.h"
|
||||||
|
#include "openfpga_atom_netlist_utils.h"
|
||||||
|
|
||||||
|
#include "verilog_constants.h"
|
||||||
|
#include "verilog_writer_utils.h"
|
||||||
|
#include "verilog_testbench_utils.h"
|
||||||
|
#include "verilog_top_testbench.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Local variables used only in this file
|
||||||
|
*******************************************************************/
|
||||||
|
constexpr char* TOP_TESTBENCH_REFERENCE_INSTANCE_NAME = "REF_DUT";
|
||||||
|
constexpr char* TOP_TESTBENCH_FPGA_INSTANCE_NAME = "FPGA_DUT";
|
||||||
|
constexpr char* TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX = "_benchmark";
|
||||||
|
constexpr char* TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX = "_fpga";
|
||||||
|
|
||||||
|
constexpr char* TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX = "_flag";
|
||||||
|
|
||||||
|
constexpr char* TOP_TESTBENCH_CC_PROG_TASK_NAME = "prog_cycle_task";
|
||||||
|
|
||||||
|
constexpr char* TOP_TESTBENCH_SIM_START_PORT_NAME = "sim_start";
|
||||||
|
|
||||||
|
constexpr int TOP_TESTBENCH_MAGIC_NUMBER_FOR_SIMULATION_TIME = 200;
|
||||||
|
constexpr char* TOP_TESTBENCH_ERROR_COUNTER = "nb_error";
|
||||||
|
|
||||||
|
constexpr char* TOP_TB_RESET_PORT_NAME = "greset";
|
||||||
|
constexpr char* TOP_TB_SET_PORT_NAME = "gset";
|
||||||
|
constexpr char* TOP_TB_PROG_RESET_PORT_NAME = "prog_reset";
|
||||||
|
constexpr char* TOP_TB_PROG_SET_PORT_NAME = "prog_set";
|
||||||
|
constexpr char* TOP_TB_CONFIG_DONE_PORT_NAME = "config_done";
|
||||||
|
constexpr char* TOP_TB_OP_CLOCK_PORT_NAME = "op_clock";
|
||||||
|
constexpr char* TOP_TB_PROG_CLOCK_PORT_NAME = "prog_clock";
|
||||||
|
constexpr char* TOP_TB_INOUT_REG_POSTFIX = "_reg";
|
||||||
|
constexpr char* TOP_TB_CLOCK_REG_POSTFIX = "_reg";
|
||||||
|
|
||||||
|
constexpr char* AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX = "_autocheck_top_tb";
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print local wires for configuration chain protocols
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_testbench_config_chain_port(std::fstream& fp) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Print the head of configuraion-chains here */
|
||||||
|
print_verilog_comment(fp, std::string("---- Configuration-chain head -----"));
|
||||||
|
BasicPort config_chain_head_port(generate_configuration_chain_head_name(), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_REG, config_chain_head_port) << ";" << std::endl;
|
||||||
|
|
||||||
|
/* Print the tail of configuration-chains here */
|
||||||
|
print_verilog_comment(fp, std::string("---- Configuration-chain tail -----"));
|
||||||
|
BasicPort config_chain_tail_port(generate_configuration_chain_tail_name(), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_WIRE, config_chain_tail_port) << ";" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print local wires for different types of configuration protocols
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_testbench_config_protocol_port(std::fstream& fp,
|
||||||
|
const e_config_protocol_type& sram_orgz_type) {
|
||||||
|
switch(sram_orgz_type) {
|
||||||
|
case CONFIG_MEM_STANDALONE:
|
||||||
|
/* TODO */
|
||||||
|
break;
|
||||||
|
case CONFIG_MEM_SCAN_CHAIN:
|
||||||
|
print_verilog_top_testbench_config_chain_port(fp);
|
||||||
|
break;
|
||||||
|
case CONFIG_MEM_MEMORY_BANK:
|
||||||
|
/* TODO */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||||
|
"Invalid type of SRAM organization!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Wire the global ports of FPGA fabric to local wires
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_testbench_global_ports_stimuli(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Begin connecting global ports of FPGA fabric to stimuli -----"));
|
||||||
|
|
||||||
|
/* Connect global clock ports to operating or programming clock signal */
|
||||||
|
for (const CircuitPortId& model_global_port : global_ports) {
|
||||||
|
if (CIRCUIT_MODEL_PORT_CLOCK != circuit_lib.port_type(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Reach here, it means we have a global clock to deal with:
|
||||||
|
* 1. if the port is identified as a programming clock,
|
||||||
|
* connect it to the local wire of programming clock
|
||||||
|
* 2. if the port is identified as an operating clock
|
||||||
|
* connect it to the local wire of operating clock
|
||||||
|
*/
|
||||||
|
/* Find the module port */
|
||||||
|
ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port));
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
|
||||||
|
|
||||||
|
BasicPort stimuli_clock_port;
|
||||||
|
if (true == circuit_lib.port_is_prog(model_global_port)) {
|
||||||
|
stimuli_clock_port.set_name(std::string(TOP_TB_PROG_CLOCK_PORT_NAME));
|
||||||
|
stimuli_clock_port.set_width(1);
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT_SAFE(false == circuit_lib.port_is_prog(model_global_port));
|
||||||
|
stimuli_clock_port.set_name(std::string(TOP_TB_OP_CLOCK_PORT_NAME));
|
||||||
|
stimuli_clock_port.set_width(1);
|
||||||
|
}
|
||||||
|
/* Wire the port to the input stimuli:
|
||||||
|
* The wiring will be inverted if the default value of the global port is 1
|
||||||
|
* Otherwise, the wiring will not be inverted!
|
||||||
|
*/
|
||||||
|
print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port),
|
||||||
|
stimuli_clock_port,
|
||||||
|
1 == circuit_lib.port_default_value(model_global_port));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect global configuration done ports to configuration done signal */
|
||||||
|
for (const CircuitPortId& model_global_port : global_ports) {
|
||||||
|
/* Bypass clock signals, they have been processed */
|
||||||
|
if (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (false == circuit_lib.port_is_config_enable(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Reach here, it means we have a configuration done port to deal with */
|
||||||
|
/* Find the module port */
|
||||||
|
ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port));
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
|
||||||
|
|
||||||
|
BasicPort stimuli_config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1);
|
||||||
|
/* Wire the port to the input stimuli:
|
||||||
|
* The wiring will be inverted if the default value of the global port is 1
|
||||||
|
* Otherwise, the wiring will not be inverted!
|
||||||
|
*/
|
||||||
|
print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port),
|
||||||
|
stimuli_config_done_port,
|
||||||
|
1 == circuit_lib.port_default_value(model_global_port));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect global reset ports to operating or programming reset signal */
|
||||||
|
for (const CircuitPortId& model_global_port : global_ports) {
|
||||||
|
/* Bypass clock signals, they have been processed */
|
||||||
|
if (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Bypass config_done signals, they have been processed */
|
||||||
|
if (true == circuit_lib.port_is_config_enable(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false == circuit_lib.port_is_reset(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Reach here, it means we have a reset port to deal with */
|
||||||
|
/* Find the module port */
|
||||||
|
ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port));
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
|
||||||
|
|
||||||
|
BasicPort stimuli_reset_port;
|
||||||
|
if (true == circuit_lib.port_is_prog(model_global_port)) {
|
||||||
|
stimuli_reset_port.set_name(std::string(TOP_TB_PROG_RESET_PORT_NAME));
|
||||||
|
stimuli_reset_port.set_width(1);
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT_SAFE(false == circuit_lib.port_is_prog(model_global_port));
|
||||||
|
stimuli_reset_port.set_name(std::string(TOP_TB_RESET_PORT_NAME));
|
||||||
|
stimuli_reset_port.set_width(1);
|
||||||
|
}
|
||||||
|
/* Wire the port to the input stimuli:
|
||||||
|
* The wiring will be inverted if the default value of the global port is 1
|
||||||
|
* Otherwise, the wiring will not be inverted!
|
||||||
|
*/
|
||||||
|
print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port),
|
||||||
|
stimuli_reset_port,
|
||||||
|
1 == circuit_lib.port_default_value(model_global_port));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect global set ports to operating or programming set signal */
|
||||||
|
for (const CircuitPortId& model_global_port : global_ports) {
|
||||||
|
/* Bypass clock signals, they have been processed */
|
||||||
|
if (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Bypass config_done signals, they have been processed */
|
||||||
|
if (true == circuit_lib.port_is_config_enable(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bypass reset signals, they have been processed */
|
||||||
|
if (true == circuit_lib.port_is_reset(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false == circuit_lib.port_is_set(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Reach here, it means we have a set port to deal with */
|
||||||
|
/* Find the module port */
|
||||||
|
ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port));
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
|
||||||
|
|
||||||
|
BasicPort stimuli_set_port;
|
||||||
|
if (true == circuit_lib.port_is_prog(model_global_port)) {
|
||||||
|
stimuli_set_port.set_name(std::string(TOP_TB_PROG_SET_PORT_NAME));
|
||||||
|
stimuli_set_port.set_width(1);
|
||||||
|
} else {
|
||||||
|
VTR_ASSERT_SAFE(false == circuit_lib.port_is_prog(model_global_port));
|
||||||
|
stimuli_set_port.set_name(std::string(TOP_TB_SET_PORT_NAME));
|
||||||
|
stimuli_set_port.set_width(1);
|
||||||
|
}
|
||||||
|
/* Wire the port to the input stimuli:
|
||||||
|
* The wiring will be inverted if the default value of the global port is 1
|
||||||
|
* Otherwise, the wiring will not be inverted!
|
||||||
|
*/
|
||||||
|
print_verilog_wire_connection(fp, module_manager.module_port(top_module, module_global_port),
|
||||||
|
stimuli_set_port,
|
||||||
|
1 == circuit_lib.port_default_value(model_global_port));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For the rest of global ports, wire them to constant signals */
|
||||||
|
for (const CircuitPortId& model_global_port : global_ports) {
|
||||||
|
/* Bypass clock signals, they have been processed */
|
||||||
|
if (CIRCUIT_MODEL_PORT_CLOCK == circuit_lib.port_type(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Bypass config_done signals, they have been processed */
|
||||||
|
if (true == circuit_lib.port_is_config_enable(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bypass reset signals, they have been processed */
|
||||||
|
if (true == circuit_lib.port_is_reset(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bypass set signals, they have been processed */
|
||||||
|
if (true == circuit_lib.port_is_set(model_global_port)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reach here, it means we have a port to deal with */
|
||||||
|
/* Find the module port and wire it to constant values */
|
||||||
|
ModulePortId module_global_port = module_manager.find_module_port(top_module, circuit_lib.port_prefix(model_global_port));
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, module_global_port));
|
||||||
|
|
||||||
|
BasicPort module_port = module_manager.module_port(top_module, module_global_port);
|
||||||
|
std::vector<size_t> default_values(module_port.get_width(), circuit_lib.port_default_value(model_global_port));
|
||||||
|
print_verilog_wire_constant_values(fp, module_port, default_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- End connecting global ports of FPGA fabric to stimuli -----"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* This function prints the top testbench module declaration
|
||||||
|
* and internal wires/port declaration
|
||||||
|
* Ports can be classified in two categories:
|
||||||
|
* 1. General-purpose ports, which are datapath I/Os, clock signals
|
||||||
|
* for the FPGA fabric and input benchmark
|
||||||
|
* 2. Fabric-featured ports, which are required by configuration
|
||||||
|
* protocols.
|
||||||
|
* Due the difference in configuration protocols, the internal
|
||||||
|
* wires and ports will be different:
|
||||||
|
* (a) configuration-chain: we will have two ports,
|
||||||
|
* a head and a tail for the configuration chain,
|
||||||
|
* in addition to the regular ports.
|
||||||
|
* (b) memory-decoders: we will have a few ports to drive
|
||||||
|
* address lines for decoders and a bit input port to feed
|
||||||
|
* configuration bits
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_testbench_ports(std::fstream& fp,
|
||||||
|
const ModuleManager& module_manager,
|
||||||
|
const ModuleId& top_module,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::vector<std::string>& clock_port_names,
|
||||||
|
const e_config_protocol_type& sram_orgz_type,
|
||||||
|
const std::string& circuit_name){
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Print module definition */
|
||||||
|
fp << "module " << circuit_name << std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX);
|
||||||
|
fp << ";" << std::endl;
|
||||||
|
|
||||||
|
/* Print regular local wires:
|
||||||
|
* 1. global ports, i.e., reset, set and clock signals
|
||||||
|
* 2. datapath I/O signals
|
||||||
|
*/
|
||||||
|
/* Global ports of top-level module */
|
||||||
|
print_verilog_comment(fp, std::string("----- Local wires for global ports of FPGA fabric -----"));
|
||||||
|
for (const BasicPort& module_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GLOBAL_PORT)) {
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_WIRE, module_port) << ";" << std::endl;
|
||||||
|
}
|
||||||
|
/* Add an empty line as a splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Datapath I/Os of top-level module */
|
||||||
|
print_verilog_comment(fp, std::string("----- Local wires for I/Os of FPGA fabric -----"));
|
||||||
|
for (const BasicPort& module_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT)) {
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_WIRE, module_port) << ";" << std::endl;
|
||||||
|
}
|
||||||
|
/* Add an empty line as a splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Add local wires/registers that drive stimulus
|
||||||
|
* We create these general purpose ports here,
|
||||||
|
* and then wire them to the ports of FPGA fabric depending on their usage
|
||||||
|
*/
|
||||||
|
/* Configuration done port */
|
||||||
|
BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_REG, config_done_port) << ";" << std::endl;
|
||||||
|
|
||||||
|
/* Programming clock */
|
||||||
|
BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_WIRE, prog_clock_port) << ";" << std::endl;
|
||||||
|
BasicPort prog_clock_register_port(std::string(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_REG, prog_clock_register_port) << ";" << std::endl;
|
||||||
|
|
||||||
|
/* Operating clock */
|
||||||
|
BasicPort op_clock_port(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_WIRE, op_clock_port) << ";" << std::endl;
|
||||||
|
BasicPort op_clock_register_port(std::string(std::string(TOP_TB_OP_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_REG, op_clock_register_port) << ";" << std::endl;
|
||||||
|
|
||||||
|
/* Programming set and reset */
|
||||||
|
BasicPort prog_reset_port(std::string(TOP_TB_PROG_RESET_PORT_NAME), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_REG, prog_reset_port) << ";" << std::endl;
|
||||||
|
BasicPort prog_set_port(std::string(TOP_TB_PROG_SET_PORT_NAME), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_REG, prog_set_port) << ";" << std::endl;
|
||||||
|
|
||||||
|
/* Global set and reset */
|
||||||
|
BasicPort reset_port(std::string(TOP_TB_RESET_PORT_NAME), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_REG, reset_port) << ";" << std::endl;
|
||||||
|
BasicPort set_port(std::string(TOP_TB_SET_PORT_NAME), 1);
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_REG, set_port) << ";" << std::endl;
|
||||||
|
|
||||||
|
/* Configuration ports depend on the organization of SRAMs */
|
||||||
|
print_verilog_top_testbench_config_protocol_port(fp, sram_orgz_type);
|
||||||
|
|
||||||
|
/* Create a clock port if the benchmark have one but not in the default name!
|
||||||
|
* We will wire the clock directly to the operating clock directly
|
||||||
|
*/
|
||||||
|
for (const std::string clock_port_name : clock_port_names) {
|
||||||
|
if (0 == clock_port_name.compare(op_clock_port.get_name())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Ensure the clock port name is not a duplication of global ports of the FPGA module */
|
||||||
|
bool print_clock_port = true;
|
||||||
|
for (const BasicPort& module_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GLOBAL_PORT)) {
|
||||||
|
if (0 == clock_port_name.compare(module_port.get_name())) {
|
||||||
|
print_clock_port = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (false == print_clock_port) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print the clock and wire it to op_clock */
|
||||||
|
print_verilog_comment(fp, std::string("----- Create a clock for benchmark and wire it to op_clock -------"));
|
||||||
|
BasicPort clock_port(clock_port_name, 1);
|
||||||
|
fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, clock_port) << ";" << std::endl;
|
||||||
|
print_verilog_wire_connection(fp, clock_port, op_clock_port, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_verilog_testbench_shared_ports(fp, atom_ctx, netlist_annotation,
|
||||||
|
clock_port_names,
|
||||||
|
std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX),
|
||||||
|
std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX),
|
||||||
|
std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX),
|
||||||
|
std::string(AUTOCHECKED_SIMULATION_FLAG));
|
||||||
|
|
||||||
|
/* Instantiate an integer to count the number of error and
|
||||||
|
* determine if the simulation succeed or failed
|
||||||
|
*/
|
||||||
|
print_verilog_comment(fp, std::string("----- Error counter -----"));
|
||||||
|
fp << "\tinteger " << TOP_TESTBENCH_ERROR_COUNTER << "= 0;" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Instanciate the input benchmark module
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_testbench_benchmark_instance(std::fstream& fp,
|
||||||
|
const std::string& reference_verilog_top_name,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Benchmark is instanciated conditionally: only when a preprocessing flag is enable */
|
||||||
|
print_verilog_preprocessing_flag(fp, std::string(AUTOCHECKED_SIMULATION_FLAG));
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Reference Benchmark Instanication -------"));
|
||||||
|
|
||||||
|
/* Do NOT use explicit port mapping here:
|
||||||
|
* VPR added a prefix of "out_" to the output ports of input benchmark
|
||||||
|
*/
|
||||||
|
print_verilog_testbench_benchmark_instance(fp, reference_verilog_top_name,
|
||||||
|
std::string(TOP_TESTBENCH_REFERENCE_INSTANCE_NAME),
|
||||||
|
std::string(),
|
||||||
|
std::string(),
|
||||||
|
std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX),
|
||||||
|
atom_ctx, netlist_annotation,
|
||||||
|
false);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- End reference Benchmark Instanication -------"));
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Condition ends for the benchmark instanciation */
|
||||||
|
print_verilog_endif(fp);
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print tasks (processes) in Verilog format,
|
||||||
|
* which is very useful in generating stimuli for each clock cycle
|
||||||
|
* This function is tuned for configuration-chain manipulation:
|
||||||
|
* During each programming cycle, we feed the input of scan chain with a memory bit
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_testbench_load_bitstream_task_configuration_chain(std::fstream& fp) {
|
||||||
|
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1);
|
||||||
|
BasicPort cc_head_port(generate_configuration_chain_head_name(), 1);
|
||||||
|
BasicPort cc_head_value(generate_configuration_chain_head_name() + std::string("_val"), 1);
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Feed the scan-chain input at each falling edge of programming clock
|
||||||
|
* It aims at avoid racing the programming clock (scan-chain data changes at the rising edge).
|
||||||
|
*/
|
||||||
|
print_verilog_comment(fp, std::string("----- Task: input values during a programming clock cycle -----"));
|
||||||
|
fp << "task " << std::string(TOP_TESTBENCH_CC_PROG_TASK_NAME) << ";" << std::endl;
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_INPUT, cc_head_value) << ";" << std::endl;
|
||||||
|
fp << "\tbegin" << std::endl;
|
||||||
|
fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl;
|
||||||
|
fp << "\t\t\t";
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_CONKT, cc_head_port);
|
||||||
|
fp << " = ";
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_CONKT, cc_head_value);
|
||||||
|
fp << ";" << std::endl;
|
||||||
|
|
||||||
|
fp << "\tend" << std::endl;
|
||||||
|
fp << "endtask" << std::endl;
|
||||||
|
|
||||||
|
/* Add an empty line as splitter */
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print tasks, which is very useful in generating stimuli for each clock cycle
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_testbench_load_bitstream_task(std::fstream& fp,
|
||||||
|
const e_config_protocol_type& sram_orgz_type) {
|
||||||
|
switch (sram_orgz_type) {
|
||||||
|
case CONFIG_MEM_STANDALONE:
|
||||||
|
break;
|
||||||
|
case CONFIG_MEM_SCAN_CHAIN:
|
||||||
|
print_verilog_top_testbench_load_bitstream_task_configuration_chain(fp);
|
||||||
|
break;
|
||||||
|
case CONFIG_MEM_MEMORY_BANK:
|
||||||
|
/* TODO:
|
||||||
|
dump_verilog_top_testbench_stimuli_serial_version_tasks_memory_bank(cur_sram_orgz_info, fp);
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||||
|
"Invalid type of SRAM organization!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print generatic input stimuli for the top testbench
|
||||||
|
* include:
|
||||||
|
* 1. configuration done signal
|
||||||
|
* 2. programming clock
|
||||||
|
* 3. operating clock
|
||||||
|
* 4. programming reset signal
|
||||||
|
* 5. programming set signal
|
||||||
|
* 6. reset signal
|
||||||
|
* 7. set signal
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_testbench_generic_stimulus(std::fstream& fp,
|
||||||
|
const size_t& num_config_clock_cycles,
|
||||||
|
const float& prog_clock_period,
|
||||||
|
const float& op_clock_period,
|
||||||
|
const float& timescale) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, std::string("----- Number of clock cycles in configuration phase: " + std::to_string(num_config_clock_cycles) + " -----"));
|
||||||
|
|
||||||
|
BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1);
|
||||||
|
|
||||||
|
BasicPort op_clock_port(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1);
|
||||||
|
BasicPort op_clock_register_port(std::string(std::string(TOP_TB_OP_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1);
|
||||||
|
|
||||||
|
BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1);
|
||||||
|
BasicPort prog_clock_register_port(std::string(std::string(TOP_TB_PROG_CLOCK_PORT_NAME) + std::string(TOP_TB_CLOCK_REG_POSTFIX)), 1);
|
||||||
|
|
||||||
|
BasicPort prog_reset_port(std::string(TOP_TB_PROG_RESET_PORT_NAME), 1);
|
||||||
|
BasicPort prog_set_port(std::string(TOP_TB_PROG_SET_PORT_NAME), 1);
|
||||||
|
|
||||||
|
BasicPort reset_port(std::string(TOP_TB_RESET_PORT_NAME), 1);
|
||||||
|
BasicPort set_port(std::string(TOP_TB_SET_PORT_NAME), 1);
|
||||||
|
|
||||||
|
/* Generate stimuli waveform for configuration done signals */
|
||||||
|
print_verilog_comment(fp, "----- Begin configuration done signal generation -----");
|
||||||
|
print_verilog_pulse_stimuli(fp, config_done_port,
|
||||||
|
0, /* Initial value */
|
||||||
|
num_config_clock_cycles * prog_clock_period / timescale, 0);
|
||||||
|
print_verilog_comment(fp, "----- End configuration done signal generation -----");
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Generate stimuli waveform for programming clock signals */
|
||||||
|
print_verilog_comment(fp, "----- Begin raw programming clock signal generation -----");
|
||||||
|
print_verilog_clock_stimuli(fp, prog_clock_register_port,
|
||||||
|
0, /* Initial value */
|
||||||
|
0.5 * prog_clock_period / timescale,
|
||||||
|
std::string());
|
||||||
|
print_verilog_comment(fp, "----- End raw programming clock signal generation -----");
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Programming clock should be only enabled during programming phase.
|
||||||
|
* When configuration is done (config_done is enabled), programming clock should be always zero.
|
||||||
|
*/
|
||||||
|
print_verilog_comment(fp, std::string("----- Actual programming clock is triggered only when " + config_done_port.get_name() + " and " + prog_reset_port.get_name() + " are disabled -----"));
|
||||||
|
fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port);
|
||||||
|
fp << " = " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_register_port);
|
||||||
|
fp << " & (~" << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port) << ")";
|
||||||
|
fp << " & (~" << generate_verilog_port(VERILOG_PORT_CONKT, prog_reset_port) << ")";
|
||||||
|
fp << ";" << std::endl;
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Generate stimuli waveform for operating clock signals */
|
||||||
|
print_verilog_comment(fp, "----- Begin raw operating clock signal generation -----");
|
||||||
|
print_verilog_clock_stimuli(fp, op_clock_register_port,
|
||||||
|
0, /* Initial value */
|
||||||
|
0.5 * op_clock_period / timescale,
|
||||||
|
std::string("~" + reset_port.get_name()));
|
||||||
|
print_verilog_comment(fp, "----- End raw operating clock signal generation -----");
|
||||||
|
|
||||||
|
/* Operation clock should be enabled after programming phase finishes.
|
||||||
|
* Before configuration is done (config_done is enabled), operation clock should be always zero.
|
||||||
|
*/
|
||||||
|
print_verilog_comment(fp, std::string("----- Actual operating clock is triggered only when " + config_done_port.get_name() + " is enabled -----"));
|
||||||
|
fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, op_clock_port);
|
||||||
|
fp << " = " << generate_verilog_port(VERILOG_PORT_CONKT, op_clock_register_port);
|
||||||
|
fp << " & " << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port);
|
||||||
|
fp << ";" << std::endl;
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Reset signal for configuration circuit:
|
||||||
|
* only enable during the first clock cycle in programming phase
|
||||||
|
*/
|
||||||
|
print_verilog_comment(fp, "----- Begin programming reset signal generation -----");
|
||||||
|
print_verilog_pulse_stimuli(fp, prog_reset_port,
|
||||||
|
1, /* Initial value */
|
||||||
|
prog_clock_period / timescale, 0);
|
||||||
|
print_verilog_comment(fp, "----- End programming reset signal generation -----");
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Programming set signal for configuration circuit : always disabled */
|
||||||
|
print_verilog_comment(fp, "----- Begin programming set signal generation: always disabled -----");
|
||||||
|
print_verilog_pulse_stimuli(fp, prog_set_port,
|
||||||
|
0, /* Initial value */
|
||||||
|
prog_clock_period / timescale, 0);
|
||||||
|
print_verilog_comment(fp, "----- End programming set signal generation: always disabled -----");
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Operating reset signals: only enabled during the first clock cycle in operation phase */
|
||||||
|
std::vector<float> reset_pulse_widths;
|
||||||
|
reset_pulse_widths.push_back(op_clock_period / timescale);
|
||||||
|
reset_pulse_widths.push_back(2 * op_clock_period / timescale);
|
||||||
|
|
||||||
|
std::vector<size_t> reset_flip_values;
|
||||||
|
reset_flip_values.push_back(1);
|
||||||
|
reset_flip_values.push_back(0);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, "----- Begin operating reset signal generation -----");
|
||||||
|
print_verilog_comment(fp, "----- Reset signal is enabled until the first clock cycle in operation phase -----");
|
||||||
|
print_verilog_pulse_stimuli(fp, reset_port,
|
||||||
|
1,
|
||||||
|
reset_pulse_widths,
|
||||||
|
reset_flip_values,
|
||||||
|
config_done_port.get_name());
|
||||||
|
print_verilog_comment(fp, "----- End operating reset signal generation -----");
|
||||||
|
|
||||||
|
/* Operating set signal for configuration circuit : always disabled */
|
||||||
|
print_verilog_comment(fp, "----- Begin operating set signal generation: always disabled -----");
|
||||||
|
print_verilog_pulse_stimuli(fp, set_port,
|
||||||
|
0, /* Initial value */
|
||||||
|
op_clock_period / timescale, 0);
|
||||||
|
print_verilog_comment(fp, "----- End operating set signal generation: always disabled -----");
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Print stimulus for a FPGA fabric with a configuration chain protocol
|
||||||
|
* where configuration bits are programming in serial (one by one)
|
||||||
|
* Task list:
|
||||||
|
* 1. For clock signal, we should create voltage waveforms for two types of clock signals:
|
||||||
|
* a. operation clock
|
||||||
|
* b. programming clock
|
||||||
|
* 2. For Set/Reset, we reset the chip after programming phase ends
|
||||||
|
* and before operation phase starts
|
||||||
|
* 3. For input/output clb nets (mapped to I/O grids),
|
||||||
|
* we should create voltage waveforms only after programming phase
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_testbench_configuration_chain_bitstream(std::fstream& fp,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const std::vector<ConfigBitId>& fabric_bitstream) {
|
||||||
|
/* Validate the file stream */
|
||||||
|
valid_file_stream(fp);
|
||||||
|
|
||||||
|
/* Initial value should be the first configuration bits
|
||||||
|
* In the rest of programming cycles,
|
||||||
|
* configuration bits are fed at the falling edge of programming clock.
|
||||||
|
* We do not care the value of scan_chain head during the first programming cycle
|
||||||
|
* It is reset anyway
|
||||||
|
*/
|
||||||
|
BasicPort config_chain_head_port(generate_configuration_chain_head_name(), 1);
|
||||||
|
std::vector<size_t> initial_values(config_chain_head_port.get_width(), 0);
|
||||||
|
|
||||||
|
print_verilog_comment(fp, "----- Begin bitstream loading during configuration phase -----");
|
||||||
|
fp << "initial" << std::endl;
|
||||||
|
fp << "\tbegin" << std::endl;
|
||||||
|
print_verilog_comment(fp, "----- Configuration chain default input -----");
|
||||||
|
fp << "\t\t";
|
||||||
|
fp << generate_verilog_port_constant_values(config_chain_head_port, initial_values);
|
||||||
|
fp << ";";
|
||||||
|
|
||||||
|
fp << std::endl;
|
||||||
|
|
||||||
|
/* Attention: the configuration chain protcol requires the last configuration bit is fed first
|
||||||
|
* We will visit the fabric bitstream in a reverse way
|
||||||
|
*/
|
||||||
|
std::vector<ConfigBitId> cc_bitstream = fabric_bitstream;
|
||||||
|
std::reverse(cc_bitstream.begin(), cc_bitstream.end());
|
||||||
|
for (const ConfigBitId& bit_id : cc_bitstream) {
|
||||||
|
fp << "\t\t" << std::string(TOP_TESTBENCH_CC_PROG_TASK_NAME);
|
||||||
|
fp << "(1'b" << (size_t)bitstream_manager.bit_value(bit_id) << ");" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Raise the flag of configuration done when bitstream loading is complete */
|
||||||
|
BasicPort prog_clock_port(std::string(TOP_TB_PROG_CLOCK_PORT_NAME), 1);
|
||||||
|
fp << "\t\t@(negedge " << generate_verilog_port(VERILOG_PORT_CONKT, prog_clock_port) << ");" << std::endl;
|
||||||
|
|
||||||
|
BasicPort config_done_port(std::string(TOP_TB_CONFIG_DONE_PORT_NAME), 1);
|
||||||
|
fp << "\t\t\t";
|
||||||
|
fp << generate_verilog_port(VERILOG_PORT_CONKT, config_done_port);
|
||||||
|
fp << " <= ";
|
||||||
|
std::vector<size_t> config_done_enable_values(config_done_port.get_width(), 1);
|
||||||
|
fp << generate_verilog_constant_values(config_done_enable_values);
|
||||||
|
fp << ";" << std::endl;
|
||||||
|
|
||||||
|
fp << "\tend" << std::endl;
|
||||||
|
print_verilog_comment(fp, "----- End bitstream loading during configuration phase -----");
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Generate the stimuli for the top-level testbench
|
||||||
|
* The simulation consists of two phases: configuration phase and operation phase
|
||||||
|
* Configuration bits are loaded serially.
|
||||||
|
* This is actually what we do for a physical FPGA
|
||||||
|
*******************************************************************/
|
||||||
|
static
|
||||||
|
void print_verilog_top_testbench_bitstream(std::fstream& fp,
|
||||||
|
const e_config_protocol_type& sram_orgz_type,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const std::vector<ConfigBitId>& fabric_bitstream) {
|
||||||
|
/* Branch on the type of configuration protocol */
|
||||||
|
switch (sram_orgz_type) {
|
||||||
|
case CONFIG_MEM_STANDALONE:
|
||||||
|
/* TODO */
|
||||||
|
break;
|
||||||
|
case CONFIG_MEM_SCAN_CHAIN:
|
||||||
|
print_verilog_top_testbench_configuration_chain_bitstream(fp, bitstream_manager, fabric_bitstream);
|
||||||
|
break;
|
||||||
|
case CONFIG_MEM_MEMORY_BANK:
|
||||||
|
/* TODO */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||||
|
"Invalid SRAM organization type!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* The top-level function to generate a testbench, in order to verify:
|
||||||
|
* 1. Configuration phase of the FPGA fabric, where the bitstream is
|
||||||
|
* loaded to the configuration protocol of the FPGA fabric
|
||||||
|
* 2. Operating phase of the FPGA fabric, where input stimuli are
|
||||||
|
* fed to the I/Os of the FPGA fabric
|
||||||
|
* +----------+
|
||||||
|
* | FPGA | +------------+
|
||||||
|
* +----->| Fabric |------>| |
|
||||||
|
* | | | | |
|
||||||
|
* | +----------+ | |
|
||||||
|
* | | Output |
|
||||||
|
* random_input_vectors -----+ | Vector |---->Functional correct?
|
||||||
|
* | | Comparator |
|
||||||
|
* | +-----------+ | |
|
||||||
|
* | | Input | | |
|
||||||
|
* +----->| Benchmark |----->| |
|
||||||
|
* +-----------+ +------------+
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
void print_verilog_top_testbench(const ModuleManager& module_manager,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const std::vector<ConfigBitId>& fabric_bitstream,
|
||||||
|
const e_config_protocol_type& sram_orgz_type,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const std::string& verilog_fname,
|
||||||
|
const std::string& verilog_dir,
|
||||||
|
const SimulationSetting& simulation_parameters) {
|
||||||
|
|
||||||
|
std::string timer_message = std::string("Write autocheck testbench for FPGA top-level Verilog netlist for '") + circuit_name + std::string("'");
|
||||||
|
|
||||||
|
/* Start time count */
|
||||||
|
vtr::ScopedStartFinishTimer timer(timer_message);
|
||||||
|
|
||||||
|
/* Create the file stream */
|
||||||
|
std::fstream fp;
|
||||||
|
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||||
|
|
||||||
|
/* Validate the file stream */
|
||||||
|
check_file_stream(verilog_fname.c_str(), fp);
|
||||||
|
|
||||||
|
/* Generate a brief description on the Verilog file*/
|
||||||
|
std::string title = std::string("FPGA Verilog Testbench for Top-level netlist of Design: ") + circuit_name;
|
||||||
|
print_verilog_file_header(fp, title);
|
||||||
|
|
||||||
|
/* Print preprocessing flags and external netlists */
|
||||||
|
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||||
|
|
||||||
|
/* Find the top_module */
|
||||||
|
ModuleId top_module = module_manager.find_module(generate_fpga_top_module_name());
|
||||||
|
VTR_ASSERT(true == module_manager.valid_module_id(top_module));
|
||||||
|
|
||||||
|
/* Preparation: find all the clock ports */
|
||||||
|
std::vector<std::string> clock_port_names = find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation);
|
||||||
|
|
||||||
|
/* Start of testbench */
|
||||||
|
print_verilog_top_testbench_ports(fp, module_manager, top_module,
|
||||||
|
atom_ctx, netlist_annotation, clock_port_names,
|
||||||
|
sram_orgz_type, circuit_name);
|
||||||
|
|
||||||
|
/* Find the clock period */
|
||||||
|
float prog_clock_period = (1./simulation_parameters.programming_clock_frequency());
|
||||||
|
float op_clock_period = (1./simulation_parameters.operating_clock_frequency());
|
||||||
|
/* Estimate the number of configuration clock cycles
|
||||||
|
* by traversing the linked-list and count the number of SRAM=1 or BL=1&WL=1 in it.
|
||||||
|
* We plus 1 additional config clock cycle here because we need to reset everything during the first clock cycle
|
||||||
|
*/
|
||||||
|
size_t num_config_clock_cycles = 1 + fabric_bitstream.size();
|
||||||
|
|
||||||
|
/* Generate stimuli for general control signals */
|
||||||
|
print_verilog_top_testbench_generic_stimulus(fp,
|
||||||
|
num_config_clock_cycles,
|
||||||
|
prog_clock_period,
|
||||||
|
op_clock_period,
|
||||||
|
VERILOG_SIM_TIMESCALE);
|
||||||
|
|
||||||
|
/* Generate stimuli for global ports or connect them to existed signals */
|
||||||
|
print_verilog_top_testbench_global_ports_stimuli(fp,
|
||||||
|
module_manager, top_module,
|
||||||
|
circuit_lib, global_ports);
|
||||||
|
|
||||||
|
/* Instanciate FPGA top-level module */
|
||||||
|
print_verilog_testbench_fpga_instance(fp, module_manager, top_module,
|
||||||
|
std::string(TOP_TESTBENCH_FPGA_INSTANCE_NAME));
|
||||||
|
|
||||||
|
/* Connect I/Os to benchmark I/Os or constant driver */
|
||||||
|
print_verilog_testbench_connect_fpga_ios(fp, module_manager, top_module,
|
||||||
|
atom_ctx, place_ctx, io_location_map,
|
||||||
|
netlist_annotation,
|
||||||
|
std::string(),
|
||||||
|
std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX),
|
||||||
|
(size_t)VERILOG_DEFAULT_SIGNAL_INIT_VALUE);
|
||||||
|
|
||||||
|
/* Instanciate input benchmark */
|
||||||
|
print_verilog_top_testbench_benchmark_instance(fp,
|
||||||
|
circuit_name,
|
||||||
|
atom_ctx,
|
||||||
|
netlist_annotation);
|
||||||
|
|
||||||
|
/* Print tasks used for loading bitstreams */
|
||||||
|
print_verilog_top_testbench_load_bitstream_task(fp, sram_orgz_type);
|
||||||
|
|
||||||
|
/* load bitstream to FPGA fabric in a configuration phase */
|
||||||
|
print_verilog_top_testbench_bitstream(fp, sram_orgz_type,
|
||||||
|
bitstream_manager, fabric_bitstream);
|
||||||
|
|
||||||
|
/* Add stimuli for reset, set, clock and iopad signals */
|
||||||
|
print_verilog_testbench_random_stimuli(fp, atom_ctx,
|
||||||
|
netlist_annotation,
|
||||||
|
clock_port_names,
|
||||||
|
std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX),
|
||||||
|
BasicPort(std::string(TOP_TB_OP_CLOCK_PORT_NAME), 1));
|
||||||
|
|
||||||
|
/* Add output autocheck */
|
||||||
|
print_verilog_testbench_check(fp,
|
||||||
|
std::string(AUTOCHECKED_SIMULATION_FLAG),
|
||||||
|
std::string(TOP_TESTBENCH_SIM_START_PORT_NAME),
|
||||||
|
std::string(TOP_TESTBENCH_REFERENCE_OUTPUT_POSTFIX),
|
||||||
|
std::string(TOP_TESTBENCH_FPGA_OUTPUT_POSTFIX),
|
||||||
|
std::string(TOP_TESTBENCH_CHECKFLAG_PORT_POSTFIX),
|
||||||
|
std::string(TOP_TESTBENCH_ERROR_COUNTER),
|
||||||
|
atom_ctx,
|
||||||
|
netlist_annotation,
|
||||||
|
clock_port_names,
|
||||||
|
std::string(TOP_TB_OP_CLOCK_PORT_NAME));
|
||||||
|
|
||||||
|
/* Find simulation time */
|
||||||
|
float simulation_time = find_simulation_time_period(VERILOG_SIM_TIMESCALE,
|
||||||
|
num_config_clock_cycles,
|
||||||
|
1./simulation_parameters.programming_clock_frequency(),
|
||||||
|
simulation_parameters.num_clock_cycles(),
|
||||||
|
1./simulation_parameters.operating_clock_frequency());
|
||||||
|
|
||||||
|
|
||||||
|
/* Add Icarus requirement */
|
||||||
|
print_verilog_timeout_and_vcd(fp,
|
||||||
|
std::string(ICARUS_SIMULATOR_FLAG),
|
||||||
|
std::string(circuit_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX)),
|
||||||
|
std::string(circuit_name + std::string("_formal.vcd")),
|
||||||
|
std::string(TOP_TESTBENCH_SIM_START_PORT_NAME),
|
||||||
|
std::string(TOP_TESTBENCH_ERROR_COUNTER),
|
||||||
|
(int)simulation_time);
|
||||||
|
|
||||||
|
|
||||||
|
/* Testbench ends*/
|
||||||
|
print_verilog_module_end(fp, std::string(circuit_name) + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_MODULE_POSTFIX));
|
||||||
|
|
||||||
|
/* Close the file stream */
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef VERILOG_TOP_TESTBENCH
|
||||||
|
#define VERILOG_TOP_TESTBENCH
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "module_manager.h"
|
||||||
|
#include "bitstream_manager.h"
|
||||||
|
#include "circuit_library.h"
|
||||||
|
#include "vpr_context.h"
|
||||||
|
#include "io_location_map.h"
|
||||||
|
#include "vpr_netlist_annotation.h"
|
||||||
|
#include "simulation_setting.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
void print_verilog_top_testbench(const ModuleManager& module_manager,
|
||||||
|
const BitstreamManager& bitstream_manager,
|
||||||
|
const std::vector<ConfigBitId>& fabric_bitstream,
|
||||||
|
const e_config_protocol_type& sram_orgz_type,
|
||||||
|
const CircuitLibrary& circuit_lib,
|
||||||
|
const std::vector<CircuitPortId>& global_ports,
|
||||||
|
const AtomContext& atom_ctx,
|
||||||
|
const PlacementContext& place_ctx,
|
||||||
|
const IoLocationMap& io_location_map,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation,
|
||||||
|
const std::string& circuit_name,
|
||||||
|
const std::string& verilog_fname,
|
||||||
|
const std::string& verilog_dir,
|
||||||
|
const SimulationSetting& simulation_parameters);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -14,6 +14,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "openfpga_port.h"
|
#include "openfpga_port.h"
|
||||||
#include "verilog_port_types.h"
|
#include "verilog_port_types.h"
|
||||||
|
#include "circuit_library.h"
|
||||||
#include "module_manager.h"
|
#include "module_manager.h"
|
||||||
|
|
||||||
/********************************************************************
|
/********************************************************************
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "openfpga_setup_command.h"
|
#include "openfpga_setup_command.h"
|
||||||
#include "openfpga_verilog_command.h"
|
#include "openfpga_verilog_command.h"
|
||||||
#include "openfpga_bitstream_command.h"
|
#include "openfpga_bitstream_command.h"
|
||||||
|
#include "openfpga_sdc_command.h"
|
||||||
#include "basic_command.h"
|
#include "basic_command.h"
|
||||||
|
|
||||||
#include "openfpga_title.h"
|
#include "openfpga_title.h"
|
||||||
|
@ -60,6 +61,9 @@ int main(int argc, char** argv) {
|
||||||
/* Add openfpga bitstream commands */
|
/* Add openfpga bitstream commands */
|
||||||
openfpga::add_openfpga_bitstream_commands(shell);
|
openfpga::add_openfpga_bitstream_commands(shell);
|
||||||
|
|
||||||
|
/* Add openfpga sdc commands */
|
||||||
|
openfpga::add_openfpga_sdc_commands(shell);
|
||||||
|
|
||||||
/* Add basic commands: exit, help, etc.
|
/* Add basic commands: exit, help, etc.
|
||||||
* Note:
|
* Note:
|
||||||
* This MUST be the last command group to be added!
|
* This MUST be the last command group to be added!
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/***************************************************************************************
|
||||||
|
* This file includes most utilized functions that are used to acquire data from
|
||||||
|
* VPR atom netlist (users' netlist to implement)
|
||||||
|
***************************************************************************************/
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "vtr_log.h"
|
||||||
|
#include "vtr_assert.h"
|
||||||
|
#include "vtr_time.h"
|
||||||
|
|
||||||
|
/* Headers from vtrutil library */
|
||||||
|
#include "atom_netlist_utils.h"
|
||||||
|
|
||||||
|
#include "openfpga_atom_netlist_utils.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
/***************************************************************************************
|
||||||
|
* Find the names of all the atom blocks that drive clock nets
|
||||||
|
* This function will find if the block has been renamed due to contain sensitive characters
|
||||||
|
* that violates the Verilog syntax
|
||||||
|
***************************************************************************************/
|
||||||
|
std::vector<std::string> find_atom_netlist_clock_port_names(const AtomNetlist& atom_nlist,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation) {
|
||||||
|
std::vector<std::string> clock_names;
|
||||||
|
|
||||||
|
std::set<AtomPinId> clock_pins = find_netlist_logical_clock_drivers(atom_nlist);
|
||||||
|
for (const AtomPinId& clock_pin : clock_pins) {
|
||||||
|
const AtomBlockId& atom_blk = atom_nlist.port_block(atom_nlist.pin_port(clock_pin));
|
||||||
|
std::string block_name = atom_nlist.block_name(atom_blk);
|
||||||
|
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||||
|
block_name = netlist_annotation.block_name(atom_blk);
|
||||||
|
}
|
||||||
|
clock_names.push_back(block_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return clock_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef OPENFPGA_ATOM_NETLIST_UTILS_H
|
||||||
|
#define OPENFPGA_ATOM_NETLIST_UTILS_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include "atom_netlist.h"
|
||||||
|
#include "vpr_netlist_annotation.h"
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
std::vector<std::string> find_atom_netlist_clock_port_names(const AtomNetlist& atom_nlist,
|
||||||
|
const VprNetlistAnnotation& netlist_annotation);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,47 @@
|
||||||
|
/********************************************************************
|
||||||
|
* This file include most utilized functions in generating simulations
|
||||||
|
* Note: function placed here MUST be generic enough for both SPICE
|
||||||
|
* and Verilog simulations!
|
||||||
|
*******************************************************************/
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "simulation_utils.h"
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Compute the time period for the simulation
|
||||||
|
*******************************************************************/
|
||||||
|
int find_operating_phase_simulation_time(const int& factor,
|
||||||
|
const int& num_op_clock_cycles,
|
||||||
|
const float& op_clock_period,
|
||||||
|
const float& timescale) {
|
||||||
|
/* Take into account the prog_reset and reset cycles
|
||||||
|
* 1e9 is to change the unit to ns rather than second
|
||||||
|
*/
|
||||||
|
return (factor * num_op_clock_cycles * op_clock_period) / timescale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Find the the full time period of a simulation, including
|
||||||
|
* both the programming time and operating time
|
||||||
|
* This is a generic function that can be used to generate simulation
|
||||||
|
* time period for SPICE/Verilog simulators
|
||||||
|
*******************************************************************/
|
||||||
|
float find_simulation_time_period(const float &time_unit,
|
||||||
|
const int &num_prog_clock_cycles,
|
||||||
|
const float &prog_clock_period,
|
||||||
|
const int &num_op_clock_cycles,
|
||||||
|
const float &op_clock_period) {
|
||||||
|
float total_time_period = 0.;
|
||||||
|
|
||||||
|
/* Take into account the prog_reset and reset cycles */
|
||||||
|
total_time_period = (num_prog_clock_cycles + 2) * prog_clock_period + num_op_clock_cycles * op_clock_period;
|
||||||
|
total_time_period = total_time_period / time_unit;
|
||||||
|
|
||||||
|
return total_time_period;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef SIMULATION_UTILS_H
|
||||||
|
#define SIMULATION_UTILS_H
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Include header files that are required by function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/********************************************************************
|
||||||
|
* Function declaration
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/* begin namespace openfpga */
|
||||||
|
namespace openfpga {
|
||||||
|
|
||||||
|
int find_operating_phase_simulation_time(const int& factor,
|
||||||
|
const int& num_op_clock_cycles,
|
||||||
|
const float& op_clock_period,
|
||||||
|
const float& timescale);
|
||||||
|
|
||||||
|
float find_simulation_time_period(const float& time_unit,
|
||||||
|
const int& num_prog_clock_cycles,
|
||||||
|
const float& prog_clock_period,
|
||||||
|
const int& num_op_clock_cycles,
|
||||||
|
const float& op_clock_period);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,8 @@
|
||||||
|
.model top
|
||||||
|
.inputs a b
|
||||||
|
.outputs c
|
||||||
|
|
||||||
|
.names a b c
|
||||||
|
11 1
|
||||||
|
|
||||||
|
.end
|
|
@ -0,0 +1,14 @@
|
||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
module top(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c);
|
||||||
|
|
||||||
|
input wire a;
|
||||||
|
input wire b;
|
||||||
|
output wire c;
|
||||||
|
|
||||||
|
assign c = a & b;
|
||||||
|
|
||||||
|
endmodule
|
|
@ -1,90 +0,0 @@
|
||||||
# Benchmark "s298" written by ABC on Tue Mar 12 09:40:31 2019
|
|
||||||
.model s298
|
|
||||||
.inputs clock G0 G1 G2
|
|
||||||
.outputs G117 G132 G66 G118 G133 G67
|
|
||||||
|
|
||||||
.latch n21 G10 re clock 0
|
|
||||||
.latch n26 G11 re clock 0
|
|
||||||
.latch n31 G12 re clock 0
|
|
||||||
.latch n36 G13 re clock 0
|
|
||||||
.latch n41 G14 re clock 0
|
|
||||||
.latch n46 G15 re clock 0
|
|
||||||
.latch n51 G66 re clock 0
|
|
||||||
.latch n55 G67 re clock 0
|
|
||||||
.latch n59 G117 re clock 0
|
|
||||||
.latch n63 G118 re clock 0
|
|
||||||
.latch n67 G132 re clock 0
|
|
||||||
.latch n71 G133 re clock 0
|
|
||||||
.latch n75 G22 re clock 0
|
|
||||||
.latch n80 G23 re clock 0
|
|
||||||
|
|
||||||
.names n56 n57 G10 n63
|
|
||||||
0-0 1
|
|
||||||
11- 1
|
|
||||||
.names G15 G11 G13 G22 G14 G12 n56
|
|
||||||
01---- 1
|
|
||||||
0-0--- 1
|
|
||||||
0--0-- 1
|
|
||||||
0---1- 1
|
|
||||||
0----1 1
|
|
||||||
-11000 1
|
|
||||||
.names G14 G13 G12 G118 G11 n57
|
|
||||||
01--- 1
|
|
||||||
100-0 1
|
|
||||||
1-11- 1
|
|
||||||
-1-1- 1
|
|
||||||
.names n56 n59_1 G10 n67
|
|
||||||
0-0 1
|
|
||||||
11- 1
|
|
||||||
.names G14 G13 G12 G132 G11 n59_1
|
|
||||||
100-0 1
|
|
||||||
11-1- 1
|
|
||||||
1-11- 1
|
|
||||||
.names G0 G10 n21
|
|
||||||
00 1
|
|
||||||
.names G10 G11 G0 G12 G13 n26
|
|
||||||
010-- 1
|
|
||||||
1001- 1
|
|
||||||
100-0 1
|
|
||||||
.names G12 G0 G11 G10 n31
|
|
||||||
0011 1
|
|
||||||
100- 1
|
|
||||||
10-0 1
|
|
||||||
.names G13 G0 G11 G12 G10 n36
|
|
||||||
00111 1
|
|
||||||
1001- 1
|
|
||||||
1010- 1
|
|
||||||
10--0 1
|
|
||||||
.names n65 G14 G0 n41
|
|
||||||
000 1
|
|
||||||
110 1
|
|
||||||
.names G23 G10 G13 G11 G12 n65
|
|
||||||
1---- 0
|
|
||||||
-1100 0
|
|
||||||
.names G0 n56 n46
|
|
||||||
00 1
|
|
||||||
.names n56 G66 G14 G13 G12 n51
|
|
||||||
111-1 1
|
|
||||||
11-1- 1
|
|
||||||
1-01- 1
|
|
||||||
.names n56 G13 G14 G11 G67 G12 n55
|
|
||||||
1000-- 1
|
|
||||||
10-1-0 1
|
|
||||||
111-1- 1
|
|
||||||
1-1-11 1
|
|
||||||
.names n56 G13 G117 G14 G12 G11 n59
|
|
||||||
10-0-- 1
|
|
||||||
10--01 1
|
|
||||||
1111-- 1
|
|
||||||
1-111- 1
|
|
||||||
.names n56 G14 G12 G13 G133 G11 n71
|
|
||||||
1010-1 1
|
|
||||||
111-1- 1
|
|
||||||
11-11- 1
|
|
||||||
.names G2 G22 G0 n75
|
|
||||||
010 1
|
|
||||||
100 1
|
|
||||||
.names G1 G23 G0 n80
|
|
||||||
010 1
|
|
||||||
100 1
|
|
||||||
.end
|
|
|
@ -168,11 +168,11 @@
|
||||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||||
<port type="input" prefix="pReset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
<port type="input" prefix="pReset" lib_name="reset" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||||
<port type="input" prefix="D" size="1"/>
|
<port type="input" prefix="D" size="1"/>
|
||||||
<port type="output" prefix="Q" size="1"/>
|
<port type="output" prefix="Q" size="1"/>
|
||||||
<port type="output" prefix="Qb" size="1"/>
|
<port type="output" prefix="Qb" size="1"/>
|
||||||
<port type="clock" prefix="prog_clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
<port type="clock" prefix="prog_clk" lib_name="clk" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||||
</circuit_model>
|
</circuit_model>
|
||||||
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
<circuit_model type="iopad" name="iopad" prefix="iopad" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/SpiceNetlists/io.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/VerilogNetlists/io.v">
|
||||||
<design_technology type="cmos"/>
|
<design_technology type="cmos"/>
|
||||||
|
@ -232,6 +232,7 @@
|
||||||
</openfpga_architecture>
|
</openfpga_architecture>
|
||||||
<openfpga_simulation_setting>
|
<openfpga_simulation_setting>
|
||||||
<clock_setting>
|
<clock_setting>
|
||||||
|
<!--operating frequency="auto" num_cycles="auto" slack="0.2"/-->
|
||||||
<operating frequency="200e6" num_cycles="auto" slack="0.2"/>
|
<operating frequency="200e6" num_cycles="auto" slack="0.2"/>
|
||||||
<programming frequency="10e6"/>
|
<programming frequency="10e6"/>
|
||||||
</clock_setting>
|
</clock_setting>
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Run VPR for the s298 design
|
||||||
|
vpr ./test_vpr_arch/k6_frac_N10_40nm.xml ./test_blif/and.blif --clock_modeling route #--write_rr_graph example_rr_graph.xml
|
||||||
|
|
||||||
|
# Read OpenFPGA architecture definition
|
||||||
|
read_openfpga_arch -f ./test_openfpga_arch/k6_frac_N10_40nm_openfpga.xml
|
||||||
|
|
||||||
|
# Write out the architecture XML as a proof
|
||||||
|
#write_openfpga_arch -f ./arch_echo.xml
|
||||||
|
|
||||||
|
# Annotate the OpenFPGA architecture to VPR data base
|
||||||
|
link_openfpga_arch --activity_file ./test_blif/and.act #--verbose
|
||||||
|
|
||||||
|
# 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 #--verbose
|
||||||
|
|
||||||
|
# Build the module graph
|
||||||
|
# - Enabled compression on routing architecture modules
|
||||||
|
# - Enable pin duplication on grid modules
|
||||||
|
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
|
||||||
|
|
||||||
|
# Build the bitstream
|
||||||
|
# - Output the fabric-independent bitstream to a file
|
||||||
|
fpga_bitstream --verbose --file /var/tmp/xtang/openfpga_test_src/fabric_indepenent_bitstream.xml
|
||||||
|
|
||||||
|
# Write the Verilog netlist for FPGA fabric
|
||||||
|
# - Enable the use of explicit port mapping in Verilog netlist
|
||||||
|
write_fabric_verilog --file /var/tmp/xtang/openfpga_test_src/SRC --explicit_port_mapping --include_timing --include_signal_init --support_icarus_simulator --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_verilog_testbench --file /var/tmp/xtang/openfpga_test_src/SRC --reference_benchmark_file_path /var/tmp/xtang/and.v --print_top_testbench --print_preconfig_top_testbench --print_simulation_ini /var/tmp/xtang/openfpga_test_src/simulation_deck.ini
|
||||||
|
|
||||||
|
# Write the SDC files for PnR backend
|
||||||
|
# - Turn on every options here
|
||||||
|
write_pnr_sdc --file /var/tmp/xtang/openfpga_test_src/SDC
|
||||||
|
|
||||||
|
# Finish and exit OpenFPGA
|
||||||
|
exit
|
|
@ -1,14 +0,0 @@
|
||||||
# Run VPR for the s298 design
|
|
||||||
vpr ./test_vpr_arch/k6_N10_40nm.xml ./test_blif/s298.blif
|
|
||||||
|
|
||||||
# Read OpenFPGA architecture definition
|
|
||||||
read_openfpga_arch -f ./test_openfpga_arch/k6_N10_40nm_openfpga.xml
|
|
||||||
|
|
||||||
# Annotate the OpenFPGA architecture to VPR data base
|
|
||||||
link_openfpga_arch
|
|
||||||
|
|
||||||
# Check and correct any naming conflicts in the BLIF netlist
|
|
||||||
check_netlist_naming_conflict --fix --report ./netlist_renaming.xml
|
|
||||||
|
|
||||||
# Finish and exit OpenFPGA
|
|
||||||
exit
|
|
|
@ -1,41 +0,0 @@
|
||||||
# Run VPR for the s298 design
|
|
||||||
vpr ./test_vpr_arch/k6_frac_N10_40nm.xml ./test_blif/s298.blif --write_rr_graph example_rr_graph.xml
|
|
||||||
|
|
||||||
# Read OpenFPGA architecture definition
|
|
||||||
read_openfpga_arch -f ./test_openfpga_arch/k6_frac_N10_40nm_openfpga.xml
|
|
||||||
|
|
||||||
# Write out the architecture XML as a proof
|
|
||||||
#write_openfpga_arch -f ./arch_echo.xml
|
|
||||||
|
|
||||||
# Annotate the OpenFPGA architecture to VPR data base
|
|
||||||
link_openfpga_arch #--verbose
|
|
||||||
|
|
||||||
# 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 #--verbose
|
|
||||||
|
|
||||||
# Build the module graph
|
|
||||||
# - Enabled compression on routing architecture modules
|
|
||||||
# - Enable pin duplication on grid modules
|
|
||||||
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
|
|
||||||
|
|
||||||
# Build the bitstream
|
|
||||||
# - Output the fabric-independent bitstream to a file
|
|
||||||
fpga_bitstream --verbose --file /var/tmp/xtang/openfpga_test_src/fabric_indepenent_bitstream.xml
|
|
||||||
|
|
||||||
# Write the Verilog netlit for FPGA fabric
|
|
||||||
# - Enable the use of explicit port mapping in Verilog netlist
|
|
||||||
write_fabric_verilog --file /var/tmp/xtang/openfpga_test_src --explicit_port_mapping --include_timing --include_signal_init --support_icarus_simulator --print_user_defined_template --verbose
|
|
||||||
|
|
||||||
# Finish and exit OpenFPGA
|
|
||||||
exit
|
|
|
@ -45,19 +45,22 @@
|
||||||
</model>
|
</model>
|
||||||
</models>
|
</models>
|
||||||
<tiles>
|
<tiles>
|
||||||
|
<!-- Do NOT add clock pins to I/O here!!! VPR does not build clock network in the way that OpenFPGA can support
|
||||||
|
If you need to register the I/O, define clocks in the circuit models
|
||||||
|
These clocks can be handled in back-end
|
||||||
|
-->
|
||||||
<tile name="io" capacity="8" area="0">
|
<tile name="io" capacity="8" area="0">
|
||||||
<equivalent_sites>
|
<equivalent_sites>
|
||||||
<site pb_type="io"/>
|
<site pb_type="io"/>
|
||||||
</equivalent_sites>
|
</equivalent_sites>
|
||||||
<input name="outpad" num_pins="1"/>
|
<input name="outpad" num_pins="1"/>
|
||||||
<output name="inpad" num_pins="1"/>
|
<output name="inpad" num_pins="1"/>
|
||||||
<clock name="clock" num_pins="1"/>
|
|
||||||
<fc in_type="frac" in_val="0.15" out_type="frac" out_val="0.10"/>
|
<fc in_type="frac" in_val="0.15" out_type="frac" out_val="0.10"/>
|
||||||
<pinlocations pattern="custom">
|
<pinlocations pattern="custom">
|
||||||
<loc side="left">io.outpad io.inpad io.clock</loc>
|
<loc side="left">io.outpad io.inpad</loc>
|
||||||
<loc side="top">io.outpad io.inpad io.clock</loc>
|
<loc side="top">io.outpad io.inpad</loc>
|
||||||
<loc side="right">io.outpad io.inpad io.clock</loc>
|
<loc side="right">io.outpad io.inpad</loc>
|
||||||
<loc side="bottom">io.outpad io.inpad io.clock</loc>
|
<loc side="bottom">io.outpad io.inpad</loc>
|
||||||
</pinlocations>
|
</pinlocations>
|
||||||
</tile>
|
</tile>
|
||||||
<tile name="clb" area="53894">
|
<tile name="clb" area="53894">
|
||||||
|
@ -145,7 +148,10 @@
|
||||||
<pb_type name="io">
|
<pb_type name="io">
|
||||||
<input name="outpad" num_pins="1"/>
|
<input name="outpad" num_pins="1"/>
|
||||||
<output name="inpad" num_pins="1"/>
|
<output name="inpad" num_pins="1"/>
|
||||||
<clock name="clock" num_pins="1"/>
|
<!-- Do NOT add clock pins to I/O here!!! VPR does not build clock network in the way that OpenFPGA can support
|
||||||
|
If you need to register the I/O, define clocks in the circuit models
|
||||||
|
These clocks can be handled in back-end
|
||||||
|
-->
|
||||||
<!-- A mode denotes the physical implementation of an I/O
|
<!-- A mode denotes the physical implementation of an I/O
|
||||||
This mode will be not packable but is mainly used for fabric verilog generation
|
This mode will be not packable but is mainly used for fabric verilog generation
|
||||||
-->
|
-->
|
||||||
|
|
|
@ -479,7 +479,6 @@ void print_verilog_testbench_shared_ports(std::fstream& fp,
|
||||||
/* Validate the file stream */
|
/* Validate the file stream */
|
||||||
check_file_handler(fp);
|
check_file_handler(fp);
|
||||||
|
|
||||||
|
|
||||||
/* Instantiate register for inputs stimulis */
|
/* Instantiate register for inputs stimulis */
|
||||||
print_verilog_comment(fp, std::string("----- Shared inputs -------"));
|
print_verilog_comment(fp, std::string("----- Shared inputs -------"));
|
||||||
for (const t_logical_block& lb : L_logical_blocks) {
|
for (const t_logical_block& lb : L_logical_blocks) {
|
||||||
|
|
Loading…
Reference in New Issue