Merge branch 'refactoring' into dev
This commit is contained in:
commit
95accb662b
|
@ -0,0 +1,55 @@
|
|||
/***************************************************************************************
|
||||
* This file includes functions that are used to decode integer to binary vectors
|
||||
* or the reverse operation
|
||||
***************************************************************************************/
|
||||
#include <cmath>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
|
||||
#include "openfpga_decode.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Convert an integer to an one-hot encoding integer array
|
||||
********************************************************************/
|
||||
std::vector<size_t> ito1hot_vec(const size_t& in_int,
|
||||
const size_t& bin_len) {
|
||||
/* Make sure we do not have any overflow! */
|
||||
VTR_ASSERT ( (in_int <= bin_len) );
|
||||
|
||||
/* Initialize */
|
||||
std::vector<size_t> ret(bin_len, 0);
|
||||
|
||||
if (bin_len == in_int) {
|
||||
return ret; /* all zero case */
|
||||
}
|
||||
ret[in_int] = 1; /* Keep a good sequence of bits */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Converter an integer to a binary vector
|
||||
********************************************************************/
|
||||
std::vector<size_t> itobin_vec(const size_t& in_int,
|
||||
const size_t& bin_len) {
|
||||
std::vector<size_t> ret(bin_len, 0);
|
||||
|
||||
/* Make sure we do not have any overflow! */
|
||||
VTR_ASSERT ( (in_int < pow(2., bin_len)) );
|
||||
|
||||
size_t temp = in_int;
|
||||
for (size_t i = 0; i < bin_len; i++) {
|
||||
if (1 == temp % 2) {
|
||||
ret[i] = 1; /* Keep a good sequence of bits */
|
||||
}
|
||||
temp = temp / 2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef OPENFPGA_DECODE_H
|
||||
#define OPENFPGA_DECODE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <vector>
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
/* namespace openfpga begins */
|
||||
namespace openfpga {
|
||||
|
||||
std::vector<size_t> ito1hot_vec(const size_t& in_int,
|
||||
const size_t& bin_len);
|
||||
|
||||
std::vector<size_t> itobin_vec(const size_t& in_int,
|
||||
const size_t& bin_len);
|
||||
|
||||
} /* namespace openfpga ends */
|
||||
|
||||
#endif
|
|
@ -135,7 +135,7 @@ bool create_dir_path(const char* dir_path) {
|
|||
return true;
|
||||
case -1:
|
||||
if (EEXIST == errno) {
|
||||
VTR_LOG_ERROR("Directory '%s' already exists. Will overwrite contents\n",
|
||||
VTR_LOG_WARN("Directory '%s' already exists. Will overwrite contents\n",
|
||||
dir_path);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -905,6 +905,22 @@ std::string generate_mux_sram_port_name(const CircuitLibrary& circuit_lib,
|
|||
return generate_local_sram_port_name(prefix, mux_instance_id, port_type);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate the netlist name of a logical tile
|
||||
**********************************************************************/
|
||||
std::string generate_logical_tile_netlist_name(const std::string& prefix,
|
||||
const t_pb_graph_node* pb_graph_head,
|
||||
const std::string& postfix) {
|
||||
/* This must be the root node */
|
||||
VTR_ASSERT(true == pb_graph_head->is_root());
|
||||
/* Add the name of physical block */
|
||||
std::string module_name = prefix + std::string(pb_graph_head->pb_type->name);
|
||||
|
||||
module_name += postfix;
|
||||
|
||||
return module_name;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate the prefix for naming a grid block netlist or a grid module
|
||||
* This function will consider the io side and add it to the prefix
|
||||
|
|
|
@ -200,6 +200,10 @@ std::string generate_mux_sram_port_name(const CircuitLibrary& circuit_lib,
|
|||
const size_t& mux_instance_id,
|
||||
const e_circuit_model_port_type& port_type);
|
||||
|
||||
std::string generate_logical_tile_netlist_name(const std::string& prefix,
|
||||
const t_pb_graph_node* pb_graph_head,
|
||||
const std::string& postfix);
|
||||
|
||||
std::string generate_grid_block_prefix(const std::string& prefix,
|
||||
const e_side& io_side);
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
|
|||
CommandOptionId opt_include_timing = cmd.option("include_timing");
|
||||
CommandOptionId opt_include_signal_init = cmd.option("include_signal_init");
|
||||
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_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");
|
||||
|
||||
/* This is an intermediate data structure which is designed to modularize the FPGA-Verilog
|
||||
|
@ -36,13 +40,18 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx,
|
|||
options.set_include_timing(cmd_context.option_enable(cmd, opt_include_timing));
|
||||
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_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_compress_routing(openfpga_ctx.flow_manager().compress_routing());
|
||||
|
||||
fpga_fabric_verilog(openfpga_ctx.module_graph(),
|
||||
fpga_fabric_verilog(openfpga_ctx.mutable_module_graph(),
|
||||
openfpga_ctx.arch().circuit_lib,
|
||||
openfpga_ctx.mux_lib(),
|
||||
g_vpr_ctx.device().grid,
|
||||
g_vpr_ctx.device(),
|
||||
openfpga_ctx.vpr_device_annotation(),
|
||||
openfpga_ctx.device_rr_gsb(),
|
||||
options);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,15 @@ void add_openfpga_verilog_commands(openfpga::Shell<OpenfpgaContext>& shell) {
|
|||
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");
|
||||
|
||||
|
|
|
@ -15,12 +15,10 @@
|
|||
#include "device_rr_gsb.h"
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_auxiliary_netlists.h"
|
||||
//#include "verilog_submodules.h"
|
||||
//#include "verilog_routing.h"
|
||||
//#include "verilog_submodules.h"
|
||||
//#include "verilog_grid.h"
|
||||
//#include "verilog_routing.h"
|
||||
//#include "verilog_top_module.h"
|
||||
#include "verilog_submodule.h"
|
||||
#include "verilog_routing.h"
|
||||
#include "verilog_grid.h"
|
||||
#include "verilog_top_module.h"
|
||||
|
||||
/* Header file for this source file */
|
||||
#include "verilog_api.h"
|
||||
|
@ -40,11 +38,17 @@ namespace openfpga {
|
|||
* 6. Testbench, where a FPGA module is configured with a bitstream and then driven by input vectors
|
||||
* 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.
|
||||
* 8. Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated
|
||||
*
|
||||
* TODO: We should use module manager as a constant here.
|
||||
* All the modification should be done before this writer!
|
||||
* The only exception now is the user-defined modules.
|
||||
* We should think clearly about how to handle them for both Verilog and SPICE generators!
|
||||
********************************************************************/
|
||||
void fpga_fabric_verilog(const ModuleManager& module_manager,
|
||||
void fpga_fabric_verilog(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const MuxLibrary& mux_lib,
|
||||
const DeviceGrid& grids,
|
||||
const DeviceContext& device_ctx,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const FabricVerilogOption& options) {
|
||||
|
||||
|
@ -81,35 +85,39 @@ void fpga_fabric_verilog(const ModuleManager& module_manager,
|
|||
* the module manager.
|
||||
* Without the modules in the module manager, core logic generation is not possible!!!
|
||||
*/
|
||||
//print_verilog_submodules(module_manager, mux_lib, sram_verilog_orgz_info, src_dir_path.c_str(), submodule_dir_path.c_str(),
|
||||
// Arch, vpr_setup.FPGA_SPICE_Opts.SynVerilogOpts);
|
||||
print_verilog_submodule(module_manager, mux_lib, circuit_lib,
|
||||
src_dir_path, submodule_dir_path,
|
||||
options);
|
||||
|
||||
/* Generate routing blocks */
|
||||
//if (true == compress_routing) {
|
||||
// print_verilog_unique_routing_modules(module_manager, device_rr_gsb,
|
||||
// src_dir_path, rr_dir_path,
|
||||
// dump_explicit_verilog);
|
||||
//} else {
|
||||
// VTR_ASSERT(false == compress_routing);
|
||||
// print_verilog_flatten_routing_modules(module_manager, device_rr_gsb,
|
||||
// src_dir_path, rr_dir_path,
|
||||
// dump_explicit_verilog);
|
||||
//}
|
||||
if (true == options.compress_routing()) {
|
||||
print_verilog_unique_routing_modules(const_cast<const ModuleManager&>(module_manager),
|
||||
device_rr_gsb,
|
||||
src_dir_path, rr_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
} else {
|
||||
VTR_ASSERT(false == options.compress_routing());
|
||||
print_verilog_flatten_routing_modules(const_cast<const ModuleManager&>(module_manager),
|
||||
device_rr_gsb,
|
||||
src_dir_path, rr_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
/* Generate grids */
|
||||
//print_verilog_grids(module_manager,
|
||||
// src_dir_path, lb_dir_path,
|
||||
// dump_explicit_verilog);
|
||||
print_verilog_grids(const_cast<const ModuleManager&>(module_manager),
|
||||
device_ctx, device_annotation,
|
||||
src_dir_path, lb_dir_path,
|
||||
options.explicit_port_mapping(),
|
||||
options.verbose_output());
|
||||
|
||||
/* Generate FPGA fabric */
|
||||
//print_verilog_top_module(module_manager,
|
||||
// std::string(vpr_setup.FileNameOpts.ArchFile),
|
||||
// src_dir_path,
|
||||
// dump_explicit_verilog);
|
||||
print_verilog_top_module(const_cast<const ModuleManager&>(module_manager),
|
||||
src_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
|
||||
/* Given a brief stats on how many Verilog modules have been written to files */
|
||||
VTR_LOGV(options.verbose_output(),
|
||||
"Outputted %lu Verilog modules in total\n",
|
||||
"Written %lu Verilog modules in total\n",
|
||||
module_manager.num_modules());
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
#include "vpr_types.h"
|
||||
#include "mux_library.h"
|
||||
#include "circuit_library.h"
|
||||
#include "device_grid.h"
|
||||
#include "vpr_context.h"
|
||||
#include "vpr_device_annotation.h"
|
||||
#include "device_rr_gsb.h"
|
||||
#include "module_manager.h"
|
||||
#include "verilog_options.h"
|
||||
|
@ -22,10 +23,11 @@
|
|||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void fpga_fabric_verilog(const ModuleManager& module_manager,
|
||||
void fpga_fabric_verilog(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const MuxLibrary& mux_lib,
|
||||
const DeviceGrid& grids,
|
||||
const DeviceContext& device_ctx,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const FabricVerilogOption& options);
|
||||
|
||||
|
|
|
@ -41,4 +41,11 @@ constexpr char* ESSENTIALS_VERILOG_FILE_NAME = "inv_buf_passgate.v";
|
|||
constexpr char* CONFIG_PERIPHERAL_VERILOG_FILE_NAME = "config_peripherals.v";
|
||||
constexpr char* USER_DEFINED_TEMPLATE_VERILOG_FILE_NAME = "user_defined_templates.v";
|
||||
|
||||
constexpr char* VERILOG_MUX_BASIS_POSTFIX = "_basis";
|
||||
constexpr char* VERILOG_MEM_POSTFIX = "_mem";
|
||||
|
||||
constexpr char* SB_VERILOG_FILE_NAME_PREFIX = "sb_";
|
||||
constexpr char* LOGICAL_MODULE_VERILOG_FILE_NAME_PREFIX = "logical_tile_";
|
||||
constexpr char* GRID_VERILOG_FILE_NAME_PREFIX = "grid_";
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/***************************************************************************************
|
||||
* This file includes functions to generate Verilog modules of decoders
|
||||
***************************************************************************************/
|
||||
/* TODO: merge verilog_decoder.c to this source file and rename to verilog_decoder.cpp */
|
||||
#include <string>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
#include "openfpga_decode.h"
|
||||
|
||||
#include "decoder_library_utils.h"
|
||||
#include "module_manager.h"
|
||||
|
||||
#include "openfpga_naming.h"
|
||||
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
#include "verilog_decoders.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/***************************************************************************************
|
||||
* Create a Verilog module for a decoder with a given output size
|
||||
*
|
||||
* Inputs
|
||||
* | | ... |
|
||||
* v v v
|
||||
* +-----------+
|
||||
* / \
|
||||
* / Decoder \
|
||||
* +-----------------+
|
||||
* | | | ... | | |
|
||||
* v v v v v v
|
||||
* Outputs
|
||||
*
|
||||
* The outputs are assumes to be one-hot codes (at most only one '1' exist)
|
||||
* Considering this fact, there are only num_of_outputs conditions to be encoded.
|
||||
* Therefore, the number of inputs is ceil(log(num_of_outputs)/log(2))
|
||||
***************************************************************************************/
|
||||
static
|
||||
void print_verilog_mux_local_decoder_module(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const DecoderLibrary& decoder_lib,
|
||||
const DecoderId& decoder) {
|
||||
/* Get the number of inputs */
|
||||
size_t addr_size = decoder_lib.addr_size(decoder);
|
||||
size_t data_size = decoder_lib.data_size(decoder);
|
||||
|
||||
/* Validate the FILE handler */
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* TODO: create a name for the local encoder */
|
||||
std::string module_name = generate_mux_local_decoder_subckt_name(addr_size, data_size);
|
||||
|
||||
/* Create a Verilog Module based on the circuit model, and add to module manager */
|
||||
ModuleId module_id = module_manager.find_module(module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(module_id));
|
||||
/* Add module ports */
|
||||
/* Add each input port */
|
||||
BasicPort addr_port(generate_mux_local_decoder_addr_port_name(), addr_size);
|
||||
/* Add each output port */
|
||||
BasicPort data_port(generate_mux_local_decoder_data_port_name(), data_size);
|
||||
/* Data port is registered. It should be outputted as
|
||||
* output reg [lsb:msb] data
|
||||
*/
|
||||
/* Add data_in port */
|
||||
BasicPort data_inv_port(generate_mux_local_decoder_data_inv_port_name(), data_size);
|
||||
VTR_ASSERT(true == decoder_lib.use_data_inv_port(decoder));
|
||||
|
||||
/* dump module definition + ports */
|
||||
print_verilog_module_declaration(fp, module_manager, module_id);
|
||||
/* Finish dumping ports */
|
||||
|
||||
print_verilog_comment(fp, std::string("----- BEGIN Verilog codes for Decoder convert " + std::to_string(addr_size) + "-bit addr to " + std::to_string(data_size) + "-bit data -----"));
|
||||
|
||||
/* Print the truth table of this decoder */
|
||||
/* Internal logics */
|
||||
/* Early exit: Corner case for data size = 1 the logic is very simple:
|
||||
* data = addr;
|
||||
* data_inv = ~data_inv
|
||||
*/
|
||||
if (1 == data_size) {
|
||||
print_verilog_wire_connection(fp, data_port, addr_port, false);
|
||||
print_verilog_wire_connection(fp, data_inv_port, addr_port, true);
|
||||
print_verilog_comment(fp, std::string("----- END Verilog codes for Decoder convert " + std::to_string(addr_size) + "-bit addr to " + std::to_string(data_size) + "-bit data -----"));
|
||||
|
||||
/* Put an end to the Verilog module */
|
||||
print_verilog_module_end(fp, module_name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We use a magic number -1 as the addr=1 should be mapped to ...1
|
||||
* Otherwise addr will map addr=1 to ..10
|
||||
* Note that there should be a range for the shift operators
|
||||
* We should narrow the encoding to be applied to a given set of data
|
||||
* This will lead to that any addr which falls out of the op code of data
|
||||
* will give a all-zero code
|
||||
* For example:
|
||||
* data is 5-bit while addr is 3-bit
|
||||
* data=8'b0_0000 will be encoded to addr=3'b001;
|
||||
* data=8'b0_0001 will be encoded to addr=3'b010;
|
||||
* data=8'b0_0010 will be encoded to addr=3'b011;
|
||||
* data=8'b0_0100 will be encoded to addr=3'b100;
|
||||
* data=8'b0_1000 will be encoded to addr=3'b101;
|
||||
* data=8'b1_0000 will be encoded to addr=3'b110;
|
||||
* The rest of addr codes 3'b110, 3'b111 will be decoded to data=8'b0_0000;
|
||||
*/
|
||||
|
||||
fp << "\t" << "always@(" << generate_verilog_port(VERILOG_PORT_CONKT, addr_port) << ")" << std::endl;
|
||||
fp << "\t" << "case (" << generate_verilog_port(VERILOG_PORT_CONKT, addr_port) << ")" << std::endl;
|
||||
/* Create a string for addr and data */
|
||||
for (size_t i = 0; i < data_size; ++i) {
|
||||
fp << "\t\t" << generate_verilog_constant_values(itobin_vec(i, addr_size));
|
||||
fp << " : ";
|
||||
fp << generate_verilog_port_constant_values(data_port, ito1hot_vec(i, data_size));
|
||||
fp << ";" << std::endl;
|
||||
}
|
||||
fp << "\t\t" << "default : ";
|
||||
fp << generate_verilog_port_constant_values(data_port, ito1hot_vec(data_size - 1, data_size));
|
||||
fp << ";" << std::endl;
|
||||
fp << "\t" << "endcase" << std::endl;
|
||||
|
||||
print_verilog_wire_connection(fp, data_inv_port, data_port, true);
|
||||
|
||||
print_verilog_comment(fp, std::string("----- END Verilog codes for Decoder convert " + std::to_string(addr_size) + "-bit addr to " + std::to_string(data_size) + "-bit data -----"));
|
||||
|
||||
/* Put an end to the Verilog module */
|
||||
print_verilog_module_end(fp, module_name);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
* This function will generate all the unique Verilog modules of local decoders for
|
||||
* the multiplexers used in a FPGA fabric
|
||||
* It will reach the goal in two steps:
|
||||
* 1. Find the unique local decoders w.r.t. the number of inputs/outputs
|
||||
* We will generate the subgraphs from the multiplexing graph of each multiplexers
|
||||
* The number of memory bits is the number of outputs.
|
||||
* From that we can infer the number of inputs of each local decoders.
|
||||
* Here is an illustrative example of how local decoders are interfaced with multi-level MUXes
|
||||
*
|
||||
* +---------+ +---------+
|
||||
* | Local | | Local |
|
||||
* | Decoder | | Decoder |
|
||||
* | A | | B |
|
||||
* +---------+ +---------+
|
||||
* | ... | | ... |
|
||||
* v v v v
|
||||
* +--------------+ +--------------+
|
||||
* | MUX Level 0 |--->| MUX Level 1 |
|
||||
* +--------------+ +--------------+
|
||||
* 2. Generate local decoder Verilog modules using behavioral description.
|
||||
* Note that the implementation of local decoders can be dependent on the technology
|
||||
* and standard cell libraries.
|
||||
* Therefore, behavioral Verilog is used and the local decoders should be synthesized
|
||||
* before running the back-end flow for FPGA fabric
|
||||
* See more details in the function print_verilog_mux_local_decoder() for more details
|
||||
***************************************************************************************/
|
||||
void print_verilog_submodule_mux_local_decoders(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir) {
|
||||
std::string verilog_fname(submodule_dir + std::string(LOCAL_ENCODER_VERILOG_FILE_NAME));
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
/* Print out debugging information for if the file is not opened/created properly */
|
||||
VTR_LOG("Writing Verilog netlist for local decoders for multiplexers '%s'...",
|
||||
verilog_fname.c_str());
|
||||
|
||||
print_verilog_file_header(fp, "Local Decoders for Multiplexers");
|
||||
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Create a library for local encoders with different sizes */
|
||||
DecoderLibrary decoder_lib;
|
||||
|
||||
/* Find unique local decoders for unique branches shared by the multiplexers */
|
||||
for (auto mux : mux_lib.muxes()) {
|
||||
/* Local decoders are need only when users specify them */
|
||||
CircuitModelId mux_circuit_model = mux_lib.mux_circuit_model(mux);
|
||||
/* If this MUX does not need local decoder, we skip it */
|
||||
if (false == circuit_lib.mux_use_local_encoder(mux_circuit_model)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const MuxGraph& mux_graph = mux_lib.mux_graph(mux);
|
||||
/* Create a mux graph for the branch circuit */
|
||||
std::vector<MuxGraph> branch_mux_graphs = mux_graph.build_mux_branch_graphs();
|
||||
/* Add the decoder to the decoder library */
|
||||
for (auto branch_mux_graph : branch_mux_graphs) {
|
||||
/* The decoder size depends on the number of memories of a branch MUX.
|
||||
* Note that only when there are >=2 memories, a decoder is needed
|
||||
*/
|
||||
size_t decoder_data_size = branch_mux_graph.num_memory_bits();
|
||||
if (0 == decoder_data_size) {
|
||||
continue;
|
||||
}
|
||||
/* Try to find if the decoder already exists in the library,
|
||||
* If there is no such decoder, add it to the library
|
||||
*/
|
||||
add_mux_local_decoder_to_library(decoder_lib, decoder_data_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate Verilog modules for the found unique local encoders */
|
||||
for (const auto& decoder : decoder_lib.decoders()) {
|
||||
print_verilog_mux_local_decoder_module(fp, module_manager, decoder_lib, decoder);
|
||||
}
|
||||
|
||||
/* Close the file stream */
|
||||
fp.close();
|
||||
|
||||
/* Add fname to the netlist name list */
|
||||
netlist_names.push_back(verilog_fname);
|
||||
|
||||
VTR_LOG("Done\n");
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef VERILOG_DECODERS_H
|
||||
#define VERILOG_DECODERS_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "circuit_library.h"
|
||||
#include "mux_graph.h"
|
||||
#include "mux_library.h"
|
||||
#include "module_manager.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_submodule_mux_local_decoders(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,585 @@
|
|||
/************************************************
|
||||
* This file includes functions on
|
||||
* outputting Verilog netlists for essential gates
|
||||
* which are inverters, buffers, transmission-gates
|
||||
* logic gates etc.
|
||||
***********************************************/
|
||||
#include <fstream>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_port.h"
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "openfpga_naming.h"
|
||||
#include "module_manager.h"
|
||||
#include "module_manager_utils.h"
|
||||
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
#include "verilog_submodule_utils.h"
|
||||
#include "verilog_essential_gates.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/************************************************
|
||||
* Print Verilog body codes of a power-gated inverter
|
||||
* This function does NOT generate any port map !
|
||||
***********************************************/
|
||||
static
|
||||
void print_verilog_power_gated_invbuf_body(std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model,
|
||||
const CircuitPortId& input_port,
|
||||
const CircuitPortId& output_port,
|
||||
const std::vector<CircuitPortId>& power_gate_ports) {
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
print_verilog_comment(fp, std::string("----- Verilog codes of a power-gated inverter -----"));
|
||||
|
||||
/* Create a sensitive list */
|
||||
fp << "\treg " << circuit_lib.port_prefix(output_port) << "_reg;" << std::endl;
|
||||
|
||||
fp << "\talways @(" << std::endl;
|
||||
/* Power-gate port first*/
|
||||
for (const auto& power_gate_port : power_gate_ports) {
|
||||
/* Skip first comma to dump*/
|
||||
if (0 < &power_gate_port - &power_gate_ports[0]) {
|
||||
fp << ",";
|
||||
}
|
||||
fp << circuit_lib.port_prefix(power_gate_port);
|
||||
}
|
||||
fp << circuit_lib.port_prefix(input_port) << ") begin" << std::endl;
|
||||
|
||||
/* Dump the case of power-gated */
|
||||
fp << "\t\tif (";
|
||||
/* For the first pin, we skip output comma */
|
||||
size_t port_cnt = 0;
|
||||
for (const auto& power_gate_port : power_gate_ports) {
|
||||
for (const auto& power_gate_pin : circuit_lib.pins(power_gate_port)) {
|
||||
if (0 < port_cnt) {
|
||||
fp << std::endl << "\t\t&&";
|
||||
}
|
||||
fp << "(";
|
||||
|
||||
/* Power-gated signal are disable during operating, enabled during configuration,
|
||||
* Therefore, we need to reverse them here
|
||||
*/
|
||||
if (0 == circuit_lib.port_default_value(power_gate_port)) {
|
||||
fp << "~";
|
||||
}
|
||||
|
||||
fp << circuit_lib.port_prefix(power_gate_port) << "[" << power_gate_pin << "])";
|
||||
|
||||
port_cnt++; /* Update port counter*/
|
||||
}
|
||||
}
|
||||
|
||||
fp << ") begin" << std::endl;
|
||||
fp << "\t\t\tassign " << circuit_lib.port_prefix(output_port) << "_reg = ";
|
||||
|
||||
/* Branch on the type of inverter/buffer:
|
||||
* 1. If this is an inverter or an tapered(multi-stage) buffer with odd number of stages,
|
||||
* we invert the input to output
|
||||
* 2. If this is a buffer or an tapere(multi-stage) buffer with even number of stages,
|
||||
* we wire the input to output
|
||||
*/
|
||||
if ( (CIRCUIT_MODEL_BUF_INV == circuit_lib.buffer_type(circuit_model))
|
||||
|| ( (CIRCUIT_MODEL_BUF_BUF == circuit_lib.buffer_type(circuit_model))
|
||||
&& (size_t(-1) != circuit_lib.buffer_num_levels(circuit_model))
|
||||
&& (1 == circuit_lib.buffer_num_levels(circuit_model) % 2 ) ) ) {
|
||||
fp << "~";
|
||||
}
|
||||
|
||||
fp << circuit_lib.port_prefix(input_port) << ";" << std::endl;
|
||||
fp << "\t\tend else begin" << std::endl;
|
||||
fp << "\t\t\tassign " << circuit_lib.port_prefix(output_port) << "_reg = 1'bz;" << std::endl;
|
||||
fp << "\t\tend" << std::endl;
|
||||
fp << "\tend" << std::endl;
|
||||
fp << "\tassign " << circuit_lib.port_prefix(output_port) << " = " << circuit_lib.port_prefix(output_port) << "_reg;" << std::endl;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Print Verilog body codes of a regular inverter
|
||||
* This function does NOT generate any port map !
|
||||
***********************************************/
|
||||
static
|
||||
void print_verilog_invbuf_body(std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model,
|
||||
const CircuitPortId& input_port,
|
||||
const CircuitPortId& output_port) {
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
print_verilog_comment(fp, std::string("----- Verilog codes of a regular inverter -----"));
|
||||
|
||||
fp << "\tassign " << circuit_lib.port_prefix(output_port) << " = (" << circuit_lib.port_prefix(input_port) << " === 1'bz)? $random : ";
|
||||
|
||||
/* Branch on the type of inverter/buffer:
|
||||
* 1. If this is an inverter or an tapered(multi-stage) buffer with odd number of stages,
|
||||
* we invert the input to output
|
||||
* 2. If this is a buffer or an tapere(multi-stage) buffer with even number of stages,
|
||||
* we wire the input to output
|
||||
*/
|
||||
if ( (CIRCUIT_MODEL_BUF_INV == circuit_lib.buffer_type(circuit_model))
|
||||
|| ( (CIRCUIT_MODEL_BUF_BUF == circuit_lib.buffer_type(circuit_model))
|
||||
&& (size_t(-1) != circuit_lib.buffer_num_levels(circuit_model))
|
||||
&& (1 == circuit_lib.buffer_num_levels(circuit_model) % 2 ) ) ) {
|
||||
fp << "~";
|
||||
}
|
||||
|
||||
fp << circuit_lib.port_prefix(input_port) << ";" << std::endl;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Print a Verilog module of inverter or buffer
|
||||
* or tapered buffer to a file
|
||||
***********************************************/
|
||||
static
|
||||
void print_verilog_invbuf_module(const ModuleManager& module_manager,
|
||||
std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model) {
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Find the input port, output port and global inputs*/
|
||||
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
|
||||
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
|
||||
std::vector<CircuitPortId> global_ports = circuit_lib.model_global_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true, true);
|
||||
|
||||
/* Make sure:
|
||||
* There is only 1 input port and 1 output port,
|
||||
* each size of which is 1
|
||||
*/
|
||||
VTR_ASSERT( (1 == input_ports.size()) && (1 == circuit_lib.port_size(input_ports[0])) );
|
||||
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
|
||||
|
||||
/* TODO: move the check codes to check_circuit_library.h */
|
||||
/* If the circuit model is power-gated, we need to find at least one global config_enable signals */
|
||||
if (true == circuit_lib.is_power_gated(circuit_model)) {
|
||||
/* Check all the ports we have are good for a power-gated circuit model */
|
||||
size_t num_err = 0;
|
||||
/* We need at least one global port */
|
||||
if (0 == global_ports.size()) {
|
||||
num_err++;
|
||||
}
|
||||
/* All the global ports should be config_enable */
|
||||
for (const auto& port : global_ports) {
|
||||
if (false == circuit_lib.port_is_config_enable(port)) {
|
||||
num_err++;
|
||||
}
|
||||
}
|
||||
/* Report errors if there are any */
|
||||
if (0 < num_err) {
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Inverter/buffer circuit model '%s' is power-gated. At least one config-enable global port is required!\n",
|
||||
circuit_lib.model_name(circuit_model).c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a Verilog Module based on the circuit model, and add to module manager */
|
||||
ModuleId module_id = module_manager.find_module(circuit_lib.model_name(circuit_model));
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(module_id));
|
||||
|
||||
/* dump module definition + ports */
|
||||
print_verilog_module_declaration(fp, module_manager, module_id);
|
||||
/* Finish dumping ports */
|
||||
|
||||
/* Assign logics : depending on topology */
|
||||
/* Error out for unsupported technology */
|
||||
if ( ( CIRCUIT_MODEL_BUF_INV != circuit_lib.buffer_type(circuit_model))
|
||||
&& ( CIRCUIT_MODEL_BUF_BUF != circuit_lib.buffer_type(circuit_model)) ) {
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Invalid topology for circuit model '%s'!\n",
|
||||
circuit_lib.model_name(circuit_model).c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (true == circuit_lib.is_power_gated(circuit_model)) {
|
||||
/* Output Verilog codes for a power-gated inverter */
|
||||
print_verilog_power_gated_invbuf_body(fp, circuit_lib, circuit_model, input_ports[0], output_ports[0], global_ports);
|
||||
} else {
|
||||
/* Output Verilog codes for a regular inverter */
|
||||
print_verilog_invbuf_body(fp, circuit_lib, circuit_model, input_ports[0], output_ports[0]);
|
||||
}
|
||||
|
||||
/* Print timing info */
|
||||
print_verilog_submodule_timing(fp, circuit_lib, circuit_model);
|
||||
|
||||
/* Print signal initialization */
|
||||
print_verilog_submodule_signal_init(fp, circuit_lib, circuit_model);
|
||||
|
||||
/* Put an end to the Verilog module */
|
||||
print_verilog_module_end(fp, circuit_lib.model_name(circuit_model));
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Print a Verilog module of a pass-gate,
|
||||
* either transmission-gate or pass-transistor
|
||||
***********************************************/
|
||||
static
|
||||
void print_verilog_passgate_module(const ModuleManager& module_manager,
|
||||
std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model) {
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Find the input port, output port*/
|
||||
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
|
||||
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
|
||||
std::vector<CircuitPortId> global_ports = circuit_lib.model_global_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true, true);
|
||||
|
||||
switch (circuit_lib.pass_gate_logic_type(circuit_model)) {
|
||||
case CIRCUIT_MODEL_PASS_GATE_TRANSMISSION:
|
||||
/* Make sure:
|
||||
* There is only 3 input port (in, sel, selb),
|
||||
* each size of which is 1
|
||||
*/
|
||||
VTR_ASSERT( 3 == input_ports.size() );
|
||||
for (const auto& input_port : input_ports) {
|
||||
VTR_ASSERT(1 == circuit_lib.port_size(input_port));
|
||||
}
|
||||
break;
|
||||
case CIRCUIT_MODEL_PASS_GATE_TRANSISTOR:
|
||||
/* Make sure:
|
||||
* There is only 2 input port (in, sel),
|
||||
* each size of which is 1
|
||||
*/
|
||||
VTR_ASSERT( 2 == input_ports.size() );
|
||||
for (const auto& input_port : input_ports) {
|
||||
VTR_ASSERT(1 == circuit_lib.port_size(input_port));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Invalid topology for circuit model '%s'!\n",
|
||||
circuit_lib.model_name(circuit_model).c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Make sure:
|
||||
* There is only 1 output port,
|
||||
* each size of which is 1
|
||||
*/
|
||||
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
|
||||
|
||||
/* Create a Verilog Module based on the circuit model, and add to module manager */
|
||||
ModuleId module_id = module_manager.find_module(circuit_lib.model_name(circuit_model));
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(module_id));
|
||||
|
||||
/* dump module definition + ports */
|
||||
print_verilog_module_declaration(fp, module_manager, module_id);
|
||||
/* Finish dumping ports */
|
||||
|
||||
/* Dump logics: we propagate input to the output when the gate is '1'
|
||||
* the input is blocked from output when the gate is '0'
|
||||
*/
|
||||
fp << "\tassign " << circuit_lib.port_prefix(output_ports[0]) << " = ";
|
||||
fp << circuit_lib.port_prefix(input_ports[1]) << " ? " << circuit_lib.port_prefix(input_ports[0]);
|
||||
fp << " : 1'bz;" << std::endl;
|
||||
|
||||
/* Print timing info */
|
||||
print_verilog_submodule_timing(fp, circuit_lib, circuit_model);
|
||||
|
||||
/* Print signal initialization */
|
||||
print_verilog_submodule_signal_init(fp, circuit_lib, circuit_model);
|
||||
|
||||
/* Put an end to the Verilog module */
|
||||
print_verilog_module_end(fp, circuit_lib.model_name(circuit_model));
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Print Verilog body codes of an N-input AND gate
|
||||
***********************************************/
|
||||
static
|
||||
void print_verilog_and_or_gate_body(std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model,
|
||||
const std::vector<CircuitPortId>& input_ports,
|
||||
const std::vector<CircuitPortId>& output_ports) {
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Find the logic operator for the gate */
|
||||
std::string gate_verilog_operator;
|
||||
switch (circuit_lib.gate_type(circuit_model)) {
|
||||
case CIRCUIT_MODEL_GATE_AND:
|
||||
gate_verilog_operator = "&";
|
||||
break;
|
||||
case CIRCUIT_MODEL_GATE_OR:
|
||||
gate_verilog_operator = "|";
|
||||
break;
|
||||
default:
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Invalid topology for circuit model '%s'!\n",
|
||||
circuit_lib.model_name(circuit_model).c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Output verilog codes */
|
||||
print_verilog_comment(fp, std::string("----- Verilog codes of a " + std::to_string(input_ports.size()) + "-input " + std::to_string(output_ports.size()) + "-output AND gate -----"));
|
||||
|
||||
for (const auto& output_port : output_ports) {
|
||||
for (const auto& output_pin : circuit_lib.pins(output_port)) {
|
||||
BasicPort output_port_info(circuit_lib.port_prefix(output_port), output_pin, output_pin);
|
||||
fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, output_port_info);
|
||||
fp << " = ";
|
||||
|
||||
size_t port_cnt = 0;
|
||||
for (const auto& input_port : input_ports) {
|
||||
for (const auto& input_pin : circuit_lib.pins(input_port)) {
|
||||
/* Do not output AND/OR operator for the first element in the loop */
|
||||
if (0 < port_cnt) {
|
||||
fp << " " << gate_verilog_operator << " ";
|
||||
}
|
||||
|
||||
BasicPort input_port_info(circuit_lib.port_prefix(input_port), input_pin, input_pin);
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, input_port_info);
|
||||
|
||||
/* Increment the counter for port */
|
||||
port_cnt++;
|
||||
}
|
||||
}
|
||||
fp << ";" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Print Verilog body codes of an 2-input MUX gate
|
||||
***********************************************/
|
||||
static
|
||||
void print_verilog_mux2_gate_body(std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model,
|
||||
const std::vector<CircuitPortId>& input_ports,
|
||||
const std::vector<CircuitPortId>& output_ports) {
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* TODO: Move the check codes to check_circuit_library.cpp */
|
||||
size_t num_err = 0;
|
||||
/* Check on the port sequence and map */
|
||||
/* MUX2 should only have 1 output port with size 1 */
|
||||
if (1 != output_ports.size()) {
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"MUX2 circuit model '%s' must have only 1 output!\n",
|
||||
circuit_lib.model_name(circuit_model).c_str());
|
||||
num_err++;
|
||||
}
|
||||
for (const auto& output_port : output_ports) {
|
||||
/* Bypass port size of 1 */
|
||||
if (1 == circuit_lib.port_size(output_port)) {
|
||||
continue;
|
||||
}
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Output port size of a MUX2 circuit model '%s' must be 1!\n",
|
||||
circuit_lib.model_name(circuit_model).c_str());
|
||||
num_err++;
|
||||
}
|
||||
/* MUX2 should only have 3 output port, each of which has a port size of 1 */
|
||||
if (3 != input_ports.size()) {
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"MUX2 circuit model '%s' must have only 3 input!\n",
|
||||
circuit_lib.model_name(circuit_model).c_str());
|
||||
num_err++;
|
||||
}
|
||||
|
||||
for (const auto& input_port : input_ports) {
|
||||
/* Bypass port size of 1 */
|
||||
if (1 == circuit_lib.port_size(input_port)) {
|
||||
continue;
|
||||
}
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Input size MUX2 circuit model '%s' must be 1!\n",
|
||||
circuit_lib.model_name(circuit_model).c_str());
|
||||
num_err++;
|
||||
}
|
||||
if (0 < num_err) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Now, we output the logic of MUX2
|
||||
* IMPORTANT Restriction:
|
||||
* We always assum the first two inputs are data inputs
|
||||
* the third input is the select port
|
||||
*/
|
||||
fp << "\tassign ";
|
||||
BasicPort out_port_info(circuit_lib.port_prefix(output_ports[0]), 0, 0);
|
||||
BasicPort sel_port_info(circuit_lib.port_prefix(input_ports[2]), 0, 0);
|
||||
BasicPort in0_port_info(circuit_lib.port_prefix(input_ports[0]), 0, 0);
|
||||
BasicPort in1_port_info(circuit_lib.port_prefix(input_ports[1]), 0, 0);
|
||||
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, out_port_info);
|
||||
fp << " = ";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, sel_port_info);
|
||||
fp << " ? ";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, in0_port_info);
|
||||
fp << " : ";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, in1_port_info);
|
||||
fp << ";" << std::endl;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Print a Verilog module of a logic gate
|
||||
* which are standard cells
|
||||
* Supported gate types:
|
||||
* 1. N-input AND
|
||||
* 2. N-input OR
|
||||
* 3. 2-input MUX
|
||||
***********************************************/
|
||||
static
|
||||
void print_verilog_gate_module(const ModuleManager& module_manager,
|
||||
std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model) {
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Find the input port, output port*/
|
||||
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
|
||||
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
|
||||
std::vector<CircuitPortId> global_ports = circuit_lib.model_global_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true, true);
|
||||
|
||||
/* Make sure:
|
||||
* There is only 1 output port,
|
||||
* each size of which is 1
|
||||
*/
|
||||
VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) );
|
||||
|
||||
/* Create a Verilog Module based on the circuit model, and add to module manager */
|
||||
ModuleId module_id = module_manager.find_module(circuit_lib.model_name(circuit_model));
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(module_id));
|
||||
|
||||
/* dump module definition + ports */
|
||||
print_verilog_module_declaration(fp, module_manager, module_id);
|
||||
/* Finish dumping ports */
|
||||
|
||||
/* Dump logics */
|
||||
switch (circuit_lib.gate_type(circuit_model)) {
|
||||
case CIRCUIT_MODEL_GATE_AND:
|
||||
case CIRCUIT_MODEL_GATE_OR:
|
||||
print_verilog_and_or_gate_body(fp, circuit_lib, circuit_model, input_ports, output_ports);
|
||||
break;
|
||||
case CIRCUIT_MODEL_GATE_MUX2:
|
||||
print_verilog_mux2_gate_body(fp, circuit_lib, circuit_model, input_ports, output_ports);
|
||||
break;
|
||||
default:
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Invalid topology for circuit model '%s'!\n",
|
||||
circuit_lib.model_name(circuit_model).c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Print timing info */
|
||||
print_verilog_submodule_timing(fp, circuit_lib, circuit_model);
|
||||
|
||||
/* Print signal initialization */
|
||||
print_verilog_submodule_signal_init(fp, circuit_lib, circuit_model);
|
||||
|
||||
/* Put an end to the Verilog module */
|
||||
print_verilog_module_end(fp, circuit_lib.model_name(circuit_model));
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Generate the Verilog netlist for a constant generator,
|
||||
* i.e., either VDD or GND
|
||||
***********************************************/
|
||||
static
|
||||
void print_verilog_constant_generator_module(const ModuleManager& module_manager,
|
||||
std::fstream& fp,
|
||||
const size_t& const_value) {
|
||||
/* Find the module in module manager */
|
||||
std::string module_name = generate_const_value_module_name(const_value);
|
||||
ModuleId const_val_module = module_manager.find_module(module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(const_val_module));
|
||||
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* dump module definition + ports */
|
||||
print_verilog_module_declaration(fp, module_manager, const_val_module);
|
||||
/* Finish dumping ports */
|
||||
|
||||
/* Find the only output*/
|
||||
for (const ModulePortId& module_port_id : module_manager.module_ports(const_val_module)) {
|
||||
BasicPort module_port = module_manager.module_port(const_val_module, module_port_id);
|
||||
print_verilog_wire_constant_values(fp, module_port, std::vector<size_t>(1, const_value));
|
||||
}
|
||||
|
||||
/* Put an end to the Verilog module */
|
||||
print_verilog_module_end(fp, module_name);
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Generate the Verilog netlist for essential gates
|
||||
* include inverters, buffers, transmission-gates,
|
||||
* etc.
|
||||
***********************************************/
|
||||
void print_verilog_submodule_essentials(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir,
|
||||
const CircuitLibrary& circuit_lib) {
|
||||
/* TODO: remove .bak when this part is completed and tested */
|
||||
std::string verilog_fname = submodule_dir + std::string(ESSENTIALS_VERILOG_FILE_NAME);
|
||||
|
||||
std::fstream fp;
|
||||
|
||||
/* Create the file stream */
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
/* Check if the file stream if valid or not */
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
/* Create file */
|
||||
VTR_LOG("Generating Verilog netlist '%s' for essential gates...",
|
||||
verilog_fname.c_str());
|
||||
|
||||
print_verilog_file_header(fp, "Essential gates");
|
||||
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Print constant generators */
|
||||
/* VDD */
|
||||
print_verilog_constant_generator_module(module_manager, fp, 0);
|
||||
/* GND */
|
||||
print_verilog_constant_generator_module(module_manager, fp, 1);
|
||||
|
||||
for (const auto& circuit_model : circuit_lib.models()) {
|
||||
/* By pass user-defined modules */
|
||||
if (!circuit_lib.model_verilog_netlist(circuit_model).empty()) {
|
||||
continue;
|
||||
}
|
||||
if (CIRCUIT_MODEL_INVBUF == circuit_lib.model_type(circuit_model)) {
|
||||
print_verilog_invbuf_module(module_manager, fp, circuit_lib, circuit_model);
|
||||
continue;
|
||||
}
|
||||
if (CIRCUIT_MODEL_PASSGATE == circuit_lib.model_type(circuit_model)) {
|
||||
print_verilog_passgate_module(module_manager, fp, circuit_lib, circuit_model);
|
||||
continue;
|
||||
}
|
||||
if (CIRCUIT_MODEL_GATE == circuit_lib.model_type(circuit_model)) {
|
||||
print_verilog_gate_module(module_manager, fp, circuit_lib, circuit_model);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Close file handler*/
|
||||
fp.close();
|
||||
|
||||
/* Add fname to the netlist name list */
|
||||
netlist_names.push_back(verilog_fname);
|
||||
|
||||
VTR_LOG("Done\n");
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef VERILOG_ESSENTIAL_GATES_H
|
||||
#define VERILOG_ESSENTIAL_GATES_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include "circuit_library.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_submodule_essentials(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir,
|
||||
const CircuitLibrary& circuit_lib);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,411 @@
|
|||
/********************************************************************
|
||||
* This file includes functions to print Verilog modules for a Grid
|
||||
* (CLBs, I/Os, heterogeneous blocks etc.)
|
||||
*******************************************************************/
|
||||
/* System header files */
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_geometry.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from readarch library */
|
||||
#include "physical_types.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
#include "openfpga_side_manager.h"
|
||||
|
||||
/* Headers from vpr library */
|
||||
#include "vpr_utils.h"
|
||||
|
||||
#include "openfpga_reserved_words.h"
|
||||
#include "openfpga_naming.h"
|
||||
#include "pb_type_utils.h"
|
||||
#include "circuit_library_utils.h"
|
||||
#include "module_manager_utils.h"
|
||||
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
#include "verilog_module_writer.h"
|
||||
#include "verilog_grid.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Print Verilog modules of a primitive node in the pb_graph_node graph
|
||||
* This generic function can support all the different types of primitive nodes
|
||||
* i.e., Look-Up Tables (LUTs), Flip-flops (FFs) and hard logic blocks such as adders.
|
||||
*
|
||||
* The Verilog module will consist of two parts:
|
||||
* 1. Logic module of the primitive node
|
||||
* This module performs the logic function of the block
|
||||
* 2. Memory module of the primitive node
|
||||
* This module stores the configuration bits for the logic module
|
||||
* if the logic module is a programmable resource, such as LUT
|
||||
*
|
||||
* Verilog module structure:
|
||||
*
|
||||
* Primitive block
|
||||
* +---------------------------------------+
|
||||
* | |
|
||||
* | +---------+ +---------+ |
|
||||
* in |----->| |--->| |<------|configuration lines
|
||||
* | | Logic |... | Memory | |
|
||||
* out|<-----| |--->| | |
|
||||
* | +---------+ +---------+ |
|
||||
* | |
|
||||
* +---------------------------------------+
|
||||
*
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_verilog_primitive_block(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
t_pb_graph_node* primitive_pb_graph_node,
|
||||
const bool& use_explicit_mapping,
|
||||
const bool& verbose) {
|
||||
/* Ensure a valid file handler */
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Ensure a valid pb_graph_node */
|
||||
if (nullptr == primitive_pb_graph_node) {
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Invalid primitive_pb_graph_node!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Generate the module name for this primitive pb_graph_node*/
|
||||
std::string primitive_module_name = generate_physical_block_module_name(primitive_pb_graph_node->pb_type);
|
||||
|
||||
/* Create a module of the primitive LUT and register it to module manager */
|
||||
ModuleId primitive_module = module_manager.find_module(primitive_module_name);
|
||||
/* Ensure that the module has been created and thus unique! */
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(primitive_module));
|
||||
|
||||
VTR_LOGV(verbose,
|
||||
"Writing Verilog codes of logical tile primitive block '%s'...",
|
||||
module_manager.module_name(primitive_module).c_str());
|
||||
|
||||
/* Write the verilog module */
|
||||
write_verilog_module_to_file(fp, module_manager, primitive_module, use_explicit_mapping);
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
||||
VTR_LOGV(verbose, "Done\n");
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Print Verilog modules of physical blocks inside a grid (CLB, I/O. etc.)
|
||||
* This function will traverse the graph of complex logic block (t_pb_graph_node)
|
||||
* in a recursive way, using a Depth First Search (DFS) algorithm.
|
||||
* As such, primitive physical blocks (LUTs, FFs, etc.), leaf node of the pb_graph
|
||||
* will be printed out first, while the top-level will be printed out in the last
|
||||
*
|
||||
* Note: this function will print a unique Verilog module for each type of
|
||||
* t_pb_graph_node, i.e., t_pb_type, in the graph, in order to enable highly
|
||||
* hierarchical Verilog organization as well as simplify the Verilog file sizes.
|
||||
*
|
||||
* Note: DFS is the right way. Do NOT use BFS.
|
||||
* DFS can guarantee that all the sub-modules can be registered properly
|
||||
* to its parent in module manager
|
||||
*******************************************************************/
|
||||
static
|
||||
void rec_print_verilog_logical_tile(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
t_pb_graph_node* physical_pb_graph_node,
|
||||
const bool& use_explicit_mapping,
|
||||
const bool& verbose) {
|
||||
/* Check the file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Check cur_pb_graph_node*/
|
||||
if (nullptr == physical_pb_graph_node) {
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Invalid physical_pb_graph_node\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Get the pb_type definition related to the node */
|
||||
t_pb_type* physical_pb_type = physical_pb_graph_node->pb_type;
|
||||
|
||||
/* Find the mode that physical implementation of a pb_type */
|
||||
t_mode* physical_mode = device_annotation.physical_mode(physical_pb_type);
|
||||
|
||||
/* For non-leaf node in the pb_type graph:
|
||||
* Recursively Depth-First Generate all the child pb_type at the level
|
||||
*/
|
||||
if (false == is_primitive_pb_type(physical_pb_type)) {
|
||||
for (int ipb = 0; ipb < physical_mode->num_pb_type_children; ++ipb) {
|
||||
/* Go recursive to visit the children */
|
||||
rec_print_verilog_logical_tile(fp,
|
||||
module_manager, device_annotation,
|
||||
&(physical_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][0]),
|
||||
use_explicit_mapping,
|
||||
verbose);
|
||||
}
|
||||
}
|
||||
|
||||
/* For leaf node, a primitive Verilog module will be generated.
|
||||
* Note that the primitive may be mapped to a standard cell, we force to use
|
||||
* explict port mapping. This aims to avoid any port sequence issues!!!
|
||||
*/
|
||||
if (true == is_primitive_pb_type(physical_pb_type)) {
|
||||
print_verilog_primitive_block(fp, module_manager,
|
||||
physical_pb_graph_node,
|
||||
true,
|
||||
verbose);
|
||||
/* Finish for primitive node, return */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Generate the name of the Verilog module for this pb_type */
|
||||
std::string pb_module_name = generate_physical_block_module_name(physical_pb_type);
|
||||
|
||||
/* Register the Verilog module in module manager */
|
||||
ModuleId pb_module = module_manager.find_module(pb_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(pb_module));
|
||||
|
||||
VTR_LOGV(verbose,
|
||||
"Writing Verilog codes of logical tile block '%s'...",
|
||||
module_manager.module_name(pb_module).c_str());
|
||||
|
||||
/* Comment lines */
|
||||
print_verilog_comment(fp, std::string("----- BEGIN Physical programmable logic block Verilog module: " + std::string(physical_pb_type->name) + " -----"));
|
||||
|
||||
/* Write the verilog module */
|
||||
write_verilog_module_to_file(fp, module_manager, pb_module, use_explicit_mapping);
|
||||
|
||||
print_verilog_comment(fp, std::string("----- END Physical programmable logic block Verilog module: " + std::string(physical_pb_type->name) + " -----"));
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
||||
VTR_LOGV(verbose, "Done\n");
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* This function will create a Verilog file and print out a Verilog netlist
|
||||
* for the logical tile (pb_graph/pb_type)
|
||||
*****************************************************************************/
|
||||
static
|
||||
void print_verilog_logical_tile_netlist(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
t_pb_graph_node* pb_graph_head,
|
||||
const bool& use_explicit_mapping,
|
||||
const bool& verbose) {
|
||||
/* Give a name to the Verilog netlist */
|
||||
/* Create the file name for Verilog */
|
||||
std::string verilog_fname(subckt_dir
|
||||
+ generate_logical_tile_netlist_name(std::string(LOGICAL_MODULE_VERILOG_FILE_NAME_PREFIX), pb_graph_head, std::string(VERILOG_NETLIST_FILE_POSTFIX))
|
||||
);
|
||||
|
||||
VTR_LOG("Writing Verilog netlist '%s' for logic tile '%s' ...",
|
||||
verilog_fname.c_str(), pb_graph_head->pb_type->name);
|
||||
VTR_LOGV(verbose, "\n");
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
print_verilog_file_header(fp, std::string("Verilog modules for logical tile: " + std::string(pb_graph_head->pb_type->name) + "]"));
|
||||
|
||||
/* Print preprocessing flags */
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Print Verilog modules for all the pb_types/pb_graph_nodes
|
||||
* use a Depth-First Search Algorithm to print the sub-modules
|
||||
* Note: DFS is the right way. Do NOT use BFS.
|
||||
* DFS can guarantee that all the sub-modules can be registered properly
|
||||
* to its parent in module manager
|
||||
*/
|
||||
/* Print Verilog modules starting from the top-level pb_type/pb_graph_node, and traverse the graph in a recursive way */
|
||||
rec_print_verilog_logical_tile(fp, module_manager,
|
||||
device_annotation,
|
||||
pb_graph_head,
|
||||
use_explicit_mapping,
|
||||
verbose);
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
/* Add fname to the netlist name list */
|
||||
netlist_names.push_back(verilog_fname);
|
||||
|
||||
VTR_LOG("Done\n");
|
||||
VTR_LOG("\n");
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* This function will create a Verilog file and print out a Verilog netlist
|
||||
* for a type of physical block
|
||||
*
|
||||
* For IO blocks:
|
||||
* The param 'border_side' is required, which is specify which side of fabric
|
||||
* the I/O block locates at.
|
||||
*****************************************************************************/
|
||||
static
|
||||
void print_verilog_physical_tile_netlist(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
t_physical_tile_type_ptr phy_block_type,
|
||||
const e_side& border_side,
|
||||
const bool& use_explicit_mapping) {
|
||||
/* Check code: if this is an IO block, the border side MUST be valid */
|
||||
if (true == is_io_type(phy_block_type)) {
|
||||
VTR_ASSERT(NUM_SIDES != border_side);
|
||||
}
|
||||
|
||||
/* Give a name to the Verilog netlist */
|
||||
/* Create the file name for Verilog */
|
||||
std::string verilog_fname(subckt_dir
|
||||
+ generate_grid_block_netlist_name(std::string(phy_block_type->name),
|
||||
is_io_type(phy_block_type),
|
||||
border_side,
|
||||
std::string(VERILOG_NETLIST_FILE_POSTFIX))
|
||||
);
|
||||
|
||||
/* Echo status */
|
||||
if (true == is_io_type(phy_block_type)) {
|
||||
SideManager side_manager(border_side);
|
||||
VTR_LOG("Writing Verilog Netlist '%s' for physical tile '%s' at %s side ...",
|
||||
verilog_fname.c_str(), phy_block_type->name,
|
||||
side_manager.c_str());
|
||||
} else {
|
||||
VTR_LOG("Writing Verilog Netlist '%s' for physical_tile '%s'...",
|
||||
verilog_fname.c_str(), phy_block_type->name);
|
||||
}
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
print_verilog_file_header(fp, std::string("Verilog modules for physical tile: " + std::string(phy_block_type->name) + "]"));
|
||||
|
||||
/* Print preprocessing flags */
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Create a Verilog Module for the top-level physical block, and add to module manager */
|
||||
std::string grid_module_name = generate_grid_block_module_name(std::string(GRID_VERILOG_FILE_NAME_PREFIX), std::string(phy_block_type->name), is_io_type(phy_block_type), border_side);
|
||||
ModuleId grid_module = module_manager.find_module(grid_module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(grid_module));
|
||||
|
||||
/* Write the verilog module */
|
||||
print_verilog_comment(fp, std::string("----- BEGIN Grid Verilog module: " + module_manager.module_name(grid_module) + " -----"));
|
||||
write_verilog_module_to_file(fp, module_manager, grid_module, use_explicit_mapping);
|
||||
|
||||
print_verilog_comment(fp, std::string("----- END Grid Verilog module: " + module_manager.module_name(grid_module) + " -----"));
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
/* Add fname to the netlist name list */
|
||||
netlist_names.push_back(verilog_fname);
|
||||
|
||||
VTR_LOG("Done\n");
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Create logic block modules in a compact way:
|
||||
* 1. Only one module for each I/O on each border side (IO_TYPE)
|
||||
* 2. Only one module for each CLB (FILL_TYPE)
|
||||
* 3. Only one module for each heterogeneous block
|
||||
****************************************************************************/
|
||||
void print_verilog_grids(const ModuleManager& module_manager,
|
||||
const DeviceContext& device_ctx,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
const bool& use_explicit_mapping,
|
||||
const bool& verbose) {
|
||||
/* Create a vector to contain all the Verilog netlist names that have been generated in this function */
|
||||
std::vector<std::string> netlist_names;
|
||||
|
||||
/* Enumerate the types of logical tiles, and build a module for each
|
||||
* Write modules for all the pb_types/pb_graph_nodes
|
||||
* use a Depth-First Search Algorithm to print the sub-modules
|
||||
* Note: DFS is the right way. Do NOT use BFS.
|
||||
* DFS can guarantee that all the sub-modules can be registered properly
|
||||
* to its parent in module manager
|
||||
*/
|
||||
VTR_LOG("Writing logical tiles...");
|
||||
VTR_LOGV(verbose, "\n");
|
||||
for (const t_logical_block_type& logical_tile : device_ctx.logical_block_types) {
|
||||
/* Bypass empty pb_graph */
|
||||
if (nullptr == logical_tile.pb_graph_head) {
|
||||
continue;
|
||||
}
|
||||
print_verilog_logical_tile_netlist(module_manager, netlist_names,
|
||||
device_annotation,
|
||||
verilog_dir, subckt_dir,
|
||||
logical_tile.pb_graph_head,
|
||||
use_explicit_mapping,
|
||||
verbose);
|
||||
}
|
||||
VTR_LOG("Writing logical tiles...");
|
||||
VTR_LOG("Done\n");
|
||||
|
||||
VTR_LOG("\n");
|
||||
|
||||
/* Enumerate the types of physical tiles
|
||||
* Use the logical tile module to build the physical tiles
|
||||
*/
|
||||
VTR_LOG("Building physical tiles...");
|
||||
VTR_LOGV(verbose, "\n");
|
||||
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 if (true == is_io_type(&physical_tile)) {
|
||||
/* Special for I/O block, generate one module for each border side */
|
||||
for (int iside = 0; iside < NUM_SIDES; iside++) {
|
||||
SideManager side_manager(iside);
|
||||
print_verilog_physical_tile_netlist(module_manager, netlist_names,
|
||||
verilog_dir, subckt_dir,
|
||||
&physical_tile,
|
||||
side_manager.get_side(),
|
||||
use_explicit_mapping);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
/* For CLB and heterogenenous blocks */
|
||||
print_verilog_physical_tile_netlist(module_manager, netlist_names,
|
||||
verilog_dir, subckt_dir,
|
||||
&physical_tile,
|
||||
NUM_SIDES,
|
||||
use_explicit_mapping);
|
||||
}
|
||||
}
|
||||
VTR_LOG("Building physical tiles...");
|
||||
VTR_LOG("Done\n");
|
||||
VTR_LOG("\n");
|
||||
|
||||
/* Output a header file for all the logic blocks */
|
||||
std::string grid_verilog_fname(LOGIC_BLOCK_VERILOG_FILE_NAME);
|
||||
VTR_LOG("Writing header file for grid Verilog modules '%s' ...",
|
||||
grid_verilog_fname.c_str());
|
||||
print_verilog_netlist_include_header_file(netlist_names,
|
||||
subckt_dir.c_str(),
|
||||
grid_verilog_fname.c_str());
|
||||
VTR_LOG("Done\n");
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef VERILOG_GRID_H
|
||||
#define VERILOG_GRID_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include "vpr_context.h"
|
||||
#include "module_manager.h"
|
||||
#include "vpr_device_annotation.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_grids(const ModuleManager& module_manager,
|
||||
const DeviceContext& device_ctx,
|
||||
const VprDeviceAnnotation& device_annotation,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
const bool& use_explicit_mapping,
|
||||
const bool& verbose);
|
||||
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,78 @@
|
|||
/********************************************************************
|
||||
* This file includes functions to generate Verilog submodules for LUTs
|
||||
********************************************************************/
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "mux_graph.h"
|
||||
#include "module_manager.h"
|
||||
#include "mux_utils.h"
|
||||
|
||||
#include "openfpga_naming.h"
|
||||
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
#include "verilog_module_writer.h"
|
||||
#include "verilog_lut.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Print Verilog modules for the Look-Up Tables (LUTs)
|
||||
* in the circuit library
|
||||
********************************************************************/
|
||||
void print_verilog_submodule_luts(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir,
|
||||
const bool& use_explicit_port_map) {
|
||||
std::string verilog_fname = submodule_dir + std::string(LUTS_VERILOG_FILE_NAME);
|
||||
|
||||
std::fstream fp;
|
||||
|
||||
/* Create the file stream */
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
/* Check if the file stream if valid or not */
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
/* Create file */
|
||||
VTR_LOG("Writing Verilog netlist for LUTs '%s'...",
|
||||
verilog_fname.c_str());
|
||||
|
||||
print_verilog_file_header(fp, "Look-Up Tables");
|
||||
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Search for each LUT circuit model */
|
||||
for (const auto& lut_model : circuit_lib.models()) {
|
||||
/* Bypass user-defined and non-LUT modules */
|
||||
if ( (!circuit_lib.model_verilog_netlist(lut_model).empty())
|
||||
|| (CIRCUIT_MODEL_LUT != circuit_lib.model_type(lut_model)) ) {
|
||||
continue;
|
||||
}
|
||||
/* Find the module id */
|
||||
ModuleId lut_module = module_manager.find_module(circuit_lib.model_name(lut_model));
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(lut_module));
|
||||
write_verilog_module_to_file(fp, module_manager, lut_module,
|
||||
use_explicit_port_map || circuit_lib.dump_explicit_port_map(lut_model));
|
||||
}
|
||||
|
||||
/* Close the file handler */
|
||||
fp.close();
|
||||
|
||||
/* Add fname to the netlist name list */
|
||||
netlist_names.push_back(verilog_fname);
|
||||
|
||||
VTR_LOG("Done\n");
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef VERILOG_LUT_H
|
||||
#define VERILOG_LUT_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "circuit_library.h"
|
||||
#include "module_manager.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_submodule_luts(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir,
|
||||
const bool& use_explicit_port_map);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,195 @@
|
|||
/*********************************************************************
|
||||
* This file includes functions to generate Verilog submodules for
|
||||
* the memories that are affiliated to multiplexers and other programmable
|
||||
* circuit models, such as IOPADs, LUTs, etc.
|
||||
********************************************************************/
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "mux_graph.h"
|
||||
#include "module_manager.h"
|
||||
#include "circuit_library_utils.h"
|
||||
#include "mux_utils.h"
|
||||
|
||||
#include "openfpga_naming.h"
|
||||
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
#include "verilog_module_writer.h"
|
||||
#include "verilog_memory.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/*********************************************************************
|
||||
* Generate Verilog modules for the memories that are used
|
||||
* by multiplexers
|
||||
*
|
||||
* +----------------+
|
||||
* mem_in --->| Memory Module |---> mem_out
|
||||
* +----------------+
|
||||
* | | ... | |
|
||||
* v v v v SRAM ports of multiplexer
|
||||
* +---------------------+
|
||||
* in--->| Multiplexer Module |---> out
|
||||
* +---------------------+
|
||||
********************************************************************/
|
||||
static
|
||||
void print_verilog_mux_memory_module(const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
std::fstream& fp,
|
||||
const CircuitModelId& mux_model,
|
||||
const MuxGraph& mux_graph,
|
||||
const bool& use_explicit_port_map) {
|
||||
/* Multiplexers built with different technology is in different organization */
|
||||
switch (circuit_lib.design_tech_type(mux_model)) {
|
||||
case CIRCUIT_MODEL_DESIGN_CMOS: {
|
||||
/* Generate module name */
|
||||
std::string 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(VERILOG_MEM_POSTFIX));
|
||||
ModuleId mem_module = module_manager.find_module(module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(mem_module));
|
||||
/* Write the module content in Verilog format */
|
||||
write_verilog_module_to_file(fp, module_manager, mem_module,
|
||||
use_explicit_port_map || circuit_lib.dump_explicit_port_map(mux_model));
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
break;
|
||||
}
|
||||
case CIRCUIT_MODEL_DESIGN_RRAM:
|
||||
/* We do not need a memory submodule for RRAM MUX,
|
||||
* RRAM are embedded in the datapath
|
||||
* TODO: generate local encoders for RRAM-based multiplexers here!!!
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
VTR_LOGF_ERROR(__FILE__, __LINE__,
|
||||
"Invalid design technology of multiplexer '%s'\n",
|
||||
circuit_lib.model_name(mux_model).c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
* Generate Verilog modules for
|
||||
* the memories that are affiliated to multiplexers and other programmable
|
||||
* circuit models, such as IOPADs, LUTs, etc.
|
||||
*
|
||||
* We keep the memory modules separated from the multiplexers and other
|
||||
* programmable circuit models, for the sake of supporting
|
||||
* various configuration schemes.
|
||||
* By following such organiztion, the Verilog modules of the circuit models
|
||||
* implements the functionality (circuit logic) only, while the memory Verilog
|
||||
* modules implements the memory circuits as well as configuration protocols.
|
||||
* For example, the local decoders of multiplexers are implemented in the
|
||||
* memory modules.
|
||||
* Take another example, the memory circuit can implement the scan-chain or
|
||||
* memory-bank organization for the memories.
|
||||
********************************************************************/
|
||||
void print_verilog_submodule_memories(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir,
|
||||
const bool& use_explicit_port_map) {
|
||||
/* Plug in with the mux subckt */
|
||||
std::string verilog_fname(submodule_dir + std::string(MEMORIES_VERILOG_FILE_NAME));
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
/* Print out debugging information for if the file is not opened/created properly */
|
||||
VTR_LOG("Writing Verilog netlist for memories '%s' ...",
|
||||
verilog_fname.c_str());
|
||||
|
||||
print_verilog_file_header(fp, "Memories used in FPGA");
|
||||
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Create the memory circuits for the multiplexer */
|
||||
for (auto mux : mux_lib.muxes()) {
|
||||
const MuxGraph& mux_graph = mux_lib.mux_graph(mux);
|
||||
CircuitModelId mux_model = mux_lib.mux_circuit_model(mux);
|
||||
/* Bypass the non-MUX circuit models (i.e., LUTs).
|
||||
* They should be handled in a different way
|
||||
* Memory circuits of LUT includes both regular and mode-select ports
|
||||
*/
|
||||
if (CIRCUIT_MODEL_MUX != circuit_lib.model_type(mux_model)) {
|
||||
continue;
|
||||
}
|
||||
/* Create a Verilog module for the memories used by the multiplexer */
|
||||
print_verilog_mux_memory_module(module_manager, circuit_lib, fp, mux_model, mux_graph, use_explicit_port_map);
|
||||
}
|
||||
|
||||
/* Create the memory circuits for non-MUX circuit models.
|
||||
* In this case, the memory modules are designed to interface
|
||||
* the mode-select ports
|
||||
*/
|
||||
for (const auto& model : circuit_lib.models()) {
|
||||
/* Bypass MUXes, they have already been considered */
|
||||
if (CIRCUIT_MODEL_MUX == circuit_lib.model_type(model)) {
|
||||
continue;
|
||||
}
|
||||
/* Bypass those modules without any SRAM ports */
|
||||
std::vector<CircuitPortId> sram_ports = circuit_lib.model_ports_by_type(model, CIRCUIT_MODEL_PORT_SRAM, true);
|
||||
if (0 == sram_ports.size()) {
|
||||
continue;
|
||||
}
|
||||
/* Find the name of memory module */
|
||||
/* Get the total number of SRAMs */
|
||||
size_t num_mems = 0;
|
||||
for (const auto& port : sram_ports) {
|
||||
num_mems += circuit_lib.port_size(port);
|
||||
}
|
||||
/* Get the circuit model for the memory circuit used by the multiplexer */
|
||||
std::vector<CircuitModelId> sram_models;
|
||||
for (const auto& port : sram_ports) {
|
||||
CircuitModelId sram_model = circuit_lib.port_tri_state_model(port);
|
||||
VTR_ASSERT(CircuitModelId::INVALID() != sram_model);
|
||||
/* Found in the vector of sram_models, do not update and go to the next */
|
||||
if (sram_models.end() != std::find(sram_models.begin(), sram_models.end(), sram_model)) {
|
||||
continue;
|
||||
}
|
||||
/* sram_model not found in the vector, update the sram_models */
|
||||
sram_models.push_back(sram_model);
|
||||
}
|
||||
/* Should have only 1 SRAM model */
|
||||
VTR_ASSERT( 1 == sram_models.size() );
|
||||
|
||||
/* Create the module name for the memory block */
|
||||
std::string module_name = generate_memory_module_name(circuit_lib, model, sram_models[0], std::string(VERILOG_MEM_POSTFIX));
|
||||
|
||||
ModuleId mem_module = module_manager.find_module(module_name);
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(mem_module));
|
||||
/* Write the module content in Verilog format */
|
||||
write_verilog_module_to_file(fp, module_manager, mem_module,
|
||||
use_explicit_port_map || circuit_lib.dump_explicit_port_map(model));
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/* Close the file stream */
|
||||
fp.close();
|
||||
|
||||
/* Add fname to the netlist name list */
|
||||
netlist_names.push_back(verilog_fname);
|
||||
|
||||
VTR_LOG("Done\n");
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef VERILOG_MEMORY_H
|
||||
#define VERILOG_MEMORY_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
|
||||
#include "circuit_library.h"
|
||||
#include "mux_graph.h"
|
||||
#include "mux_library.h"
|
||||
#include "module_manager.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_submodule_memories(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir,
|
||||
const bool& use_explicit_port_map);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,502 @@
|
|||
/********************************************************************
|
||||
* This file includes functions to write a Verilog module
|
||||
* based on its definition in Module Manager
|
||||
*
|
||||
* Note that Verilog writer functions are just an outputter for the
|
||||
* module definition.
|
||||
* You should NOT modify any content of the module manager
|
||||
* Please use const keyword to restrict this!
|
||||
*******************************************************************/
|
||||
#include <algorithm>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_port.h"
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "module_manager_utils.h"
|
||||
#include "verilog_port_types.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
#include "verilog_module_writer.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Generate the name of a local wire for a undriven port inside Verilog
|
||||
* module
|
||||
*******************************************************************/
|
||||
static
|
||||
std::string generate_verilog_undriven_local_wire_name(const ModuleManager& module_manager,
|
||||
const ModuleId& module,
|
||||
const ModulePortId& module_port_id) {
|
||||
return module_manager.module_port(module, module_port_id).get_name();
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Name a net for a local wire for a verilog module
|
||||
* 1. If this is a local wire, name it after the <src_module_name>_<instance_id>_<src_port_name>
|
||||
* 2. If this is not a local wire, name it after the port name of parent module
|
||||
*
|
||||
* In addition, it will assign the pin index as well
|
||||
*
|
||||
* Restriction: this function requires each net has single driver
|
||||
* which is definitely always true in circuits.
|
||||
*******************************************************************/
|
||||
static
|
||||
BasicPort generate_verilog_port_for_module_net(const ModuleManager& module_manager,
|
||||
const ModuleId& module_id,
|
||||
const ModuleNetId& module_net) {
|
||||
/* Check all the sink modules of the net,
|
||||
* if we have a source module is the current module, this is not local wire
|
||||
*/
|
||||
for (ModuleNetSrcId src_id : module_manager.module_net_sources(module_id, module_net)) {
|
||||
if (module_id == module_manager.net_source_modules(module_id, module_net)[src_id]) {
|
||||
/* Here, this is not a local wire, return the port name of the src_port */
|
||||
ModulePortId net_src_port = module_manager.net_source_ports(module_id, module_net)[src_id];
|
||||
size_t src_pin_index = module_manager.net_source_pins(module_id, module_net)[src_id];
|
||||
return BasicPort(module_manager.module_port(module_id, net_src_port).get_name(), src_pin_index, src_pin_index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check all the sink modules of the net */
|
||||
for (ModuleNetSinkId sink_id : module_manager.module_net_sinks(module_id, module_net)) {
|
||||
if (module_id == module_manager.net_sink_modules(module_id, module_net)[sink_id]) {
|
||||
/* Here, this is not a local wire, return the port name of the sink_port */
|
||||
ModulePortId net_sink_port = module_manager.net_sink_ports(module_id, module_net)[sink_id];
|
||||
size_t sink_pin_index = module_manager.net_sink_pins(module_id, module_net)[sink_id];
|
||||
return BasicPort(module_manager.module_port(module_id, net_sink_port).get_name(), sink_pin_index, sink_pin_index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reach here, this is a local wire */
|
||||
std::string net_name;
|
||||
|
||||
/* Each net must only one 1 source */
|
||||
VTR_ASSERT(1 == module_manager.net_source_modules(module_id, module_net).size());
|
||||
|
||||
/* Get the source module */
|
||||
ModuleId net_src_module = module_manager.net_source_modules(module_id, module_net)[ModuleNetSrcId(0)];
|
||||
/* Get the instance id */
|
||||
size_t net_src_instance = module_manager.net_source_instances(module_id, module_net)[ModuleNetSrcId(0)];
|
||||
/* Get the port id */
|
||||
ModulePortId net_src_port = module_manager.net_source_ports(module_id, module_net)[ModuleNetSrcId(0)];
|
||||
/* Get the pin id */
|
||||
size_t net_src_pin = module_manager.net_source_pins(module_id, module_net)[ModuleNetSrcId(0)];
|
||||
|
||||
/* Load user-defined name if we have it */
|
||||
if (false == module_manager.net_name(module_id, module_net).empty()) {
|
||||
net_name = module_manager.net_name(module_id, module_net);
|
||||
} else {
|
||||
net_name = module_manager.module_name(net_src_module);
|
||||
net_name += std::string("_") + std::to_string(net_src_instance) + std::string("_");
|
||||
net_name += module_manager.module_port(net_src_module, net_src_port).get_name();
|
||||
}
|
||||
|
||||
return BasicPort(net_name, net_src_pin, net_src_pin);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Find all the nets that are going to be local wires
|
||||
* And organize it in a vector of ports
|
||||
* Verilog wire writter function will use the output of this function
|
||||
* to write up local wire declaration in Verilog format
|
||||
*******************************************************************/
|
||||
static
|
||||
std::map<std::string, std::vector<BasicPort>> find_verilog_module_local_wires(const ModuleManager& module_manager,
|
||||
const ModuleId& module_id) {
|
||||
std::map<std::string, std::vector<BasicPort>> local_wires;
|
||||
|
||||
/* Local wires come from the child modules */
|
||||
for (ModuleNetId module_net : module_manager.module_nets(module_id)) {
|
||||
/* Bypass dangling nets:
|
||||
* Xifan Tang: I comment this part because it will shadow our problems in creating module graph
|
||||
* Indeed this make a robust and a smooth Verilog module writing
|
||||
* But I do want the module graph create is nice and clean !!!
|
||||
*/
|
||||
/*
|
||||
if ( (0 == module_manager.net_source_modules(module_id, module_net).size())
|
||||
&& (0 == module_manager.net_source_modules(module_id, module_net).size()) ) {
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
|
||||
/* We only care local wires */
|
||||
if (false == module_net_is_local_wire(module_manager, module_id, module_net)) {
|
||||
continue;
|
||||
}
|
||||
/* Find the name for this local wire */
|
||||
BasicPort local_wire_candidate = generate_verilog_port_for_module_net(module_manager, module_id, module_net);
|
||||
/* Cache the net name, try to find it in the cache.
|
||||
* If you can find one, it means this port may be mergeable, try to do merging. If merge fail, add to the local wire list
|
||||
* If you cannot find one, it means that this port is not mergeable, add to the local wire list immediately.
|
||||
*/
|
||||
std::map<std::string, std::vector<BasicPort>>::iterator it = local_wires.find(local_wire_candidate.get_name());
|
||||
bool merged = false;
|
||||
if (it != local_wires.end()) {
|
||||
/* Try to merge to one the port in the list that can absorb the current local wire */
|
||||
for (BasicPort& local_wire : local_wires[local_wire_candidate.get_name()]) {
|
||||
/* check if the candidate can be combined to an existing local wire */
|
||||
if (true == two_verilog_ports_mergeable(local_wire, local_wire_candidate)) {
|
||||
/* Merge the ports */
|
||||
local_wire = merge_two_verilog_ports(local_wire, local_wire_candidate);
|
||||
merged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If not merged/not found in the cache, push the port to the list */
|
||||
if (false == merged) {
|
||||
local_wires[local_wire_candidate.get_name()].push_back(local_wire_candidate);
|
||||
}
|
||||
}
|
||||
|
||||
/* Local wires could also happen for undriven ports of child module */
|
||||
for (const ModuleId& child : module_manager.child_modules(module_id)) {
|
||||
for (size_t instance : module_manager.child_module_instances(module_id, child)) {
|
||||
for (const ModulePortId& child_port_id : module_manager.module_ports(child)) {
|
||||
BasicPort child_port = module_manager.module_port(child, child_port_id);
|
||||
std::vector<size_t> undriven_pins;
|
||||
for (size_t child_pin : child_port.pins()) {
|
||||
/* Find the net linked to the pin */
|
||||
ModuleNetId net = module_manager.module_instance_port_net(module_id, child, instance,
|
||||
child_port_id, child_pin);
|
||||
/* We only care undriven ports */
|
||||
if (ModuleNetId::INVALID() == net) {
|
||||
undriven_pins.push_back(child_pin);
|
||||
}
|
||||
}
|
||||
if (true == undriven_pins.empty()) {
|
||||
continue;
|
||||
}
|
||||
/* Reach here, we need a local wire, we will create a port only for the undriven pins of the port! */
|
||||
BasicPort instance_port;
|
||||
instance_port.set_name(generate_verilog_undriven_local_wire_name(module_manager, child, child_port_id));
|
||||
/* We give the same port name as child module, this case happens to global ports */
|
||||
instance_port.set_width(*std::min_element(undriven_pins.begin(), undriven_pins.end()),
|
||||
*std::max_element(undriven_pins.begin(), undriven_pins.end()));
|
||||
|
||||
local_wires[instance_port.get_name()].push_back(instance_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return local_wires;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Print a Verilog wire connection
|
||||
* We search all the sinks of the net,
|
||||
* if we find a module output, we try to find the next module output
|
||||
* among the sinks of the net
|
||||
* For each module output (except the first one), we print a wire connection
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_verilog_module_output_short_connection(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& module_id,
|
||||
const ModuleNetId& module_net) {
|
||||
/* Ensure a valid file stream */
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
bool first_port = true;
|
||||
BasicPort src_port;
|
||||
|
||||
/* We have found a module input, now check all the sink modules of the net */
|
||||
for (ModuleNetSinkId net_sink : module_manager.module_net_sinks(module_id, module_net)) {
|
||||
ModuleId sink_module = module_manager.net_sink_modules(module_id, module_net)[net_sink];
|
||||
if (module_id != sink_module) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find the sink port and pin information */
|
||||
ModulePortId sink_port_id = module_manager.net_sink_ports(module_id, module_net)[net_sink];
|
||||
size_t sink_pin = module_manager.net_sink_pins(module_id, module_net)[net_sink];
|
||||
BasicPort sink_port(module_manager.module_port(module_id, sink_port_id).get_name(), sink_pin, sink_pin);
|
||||
|
||||
/* For the first module output, this is the source port, we do nothing and go to the next */
|
||||
if (true == first_port) {
|
||||
src_port = sink_port;
|
||||
/* Flip the flag */
|
||||
first_port = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We need to print a wire connection here */
|
||||
print_verilog_wire_connection(fp, sink_port, src_port, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* Print a Verilog wire connection
|
||||
* We search all the sources of the net,
|
||||
* if we find a module input, we try to find a module output
|
||||
* among the sinks of the net
|
||||
* If we find such a pair, we print a wire connection
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_verilog_module_local_short_connection(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& module_id,
|
||||
const ModuleNetId& module_net) {
|
||||
/* Ensure a valid file stream */
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
for (ModuleNetSrcId net_src : module_manager.module_net_sources(module_id, module_net)) {
|
||||
ModuleId src_module = module_manager.net_source_modules(module_id, module_net)[net_src];
|
||||
if (module_id != src_module) {
|
||||
continue;
|
||||
}
|
||||
/* Find the source port and pin information */
|
||||
print_verilog_comment(fp, std::string("----- Net source id " + std::to_string(size_t(net_src)) + " -----"));
|
||||
ModulePortId src_port_id = module_manager.net_source_ports(module_id, module_net)[net_src];
|
||||
size_t src_pin = module_manager.net_source_pins(module_id, module_net)[net_src];
|
||||
BasicPort src_port(module_manager.module_port(module_id, src_port_id).get_name(), src_pin, src_pin);
|
||||
|
||||
/* We have found a module input, now check all the sink modules of the net */
|
||||
for (ModuleNetSinkId net_sink : module_manager.module_net_sinks(module_id, module_net)) {
|
||||
ModuleId sink_module = module_manager.net_sink_modules(module_id, module_net)[net_sink];
|
||||
if (module_id != sink_module) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find the sink port and pin information */
|
||||
print_verilog_comment(fp, std::string("----- Net sink id " + std::to_string(size_t(net_sink)) + " -----"));
|
||||
ModulePortId sink_port_id = module_manager.net_sink_ports(module_id, module_net)[net_sink];
|
||||
size_t sink_pin = module_manager.net_sink_pins(module_id, module_net)[net_sink];
|
||||
BasicPort sink_port(module_manager.module_port(module_id, sink_port_id).get_name(), sink_pin, sink_pin);
|
||||
|
||||
/* We need to print a wire connection here */
|
||||
print_verilog_wire_connection(fp, sink_port, src_port, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Print short connections inside a Verilog module
|
||||
* The short connection is defined as the direct connection
|
||||
* between an input port of the module and an output port of the module
|
||||
* This type of connection is not covered when printing Verilog instances
|
||||
* Therefore, they are covered in this function
|
||||
*
|
||||
* module
|
||||
* +-----------------------------+
|
||||
* | |
|
||||
* inputA--->|---------------------------->|--->outputB
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* +-----------------------------+
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_verilog_module_local_short_connections(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& module_id) {
|
||||
/* Local wires come from the child modules */
|
||||
for (ModuleNetId module_net : module_manager.module_nets(module_id)) {
|
||||
/* We only care the nets that indicate short connections */
|
||||
if (false == module_net_include_local_short_connection(module_manager, module_id, module_net)) {
|
||||
continue;
|
||||
}
|
||||
print_verilog_comment(fp, std::string("----- Local connection due to Wire " + std::to_string(size_t(module_net)) + " -----"));
|
||||
print_verilog_module_local_short_connection(fp, module_manager, module_id, module_net);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Print output short connections inside a Verilog module
|
||||
* The output short connection is defined as the direct connection
|
||||
* between two output ports of the module
|
||||
* This type of connection is not covered when printing Verilog instances
|
||||
* Therefore, they are covered in this function
|
||||
*
|
||||
* module
|
||||
* +-----------------------------+
|
||||
* |
|
||||
* src------>+--------------->|--->outputA
|
||||
* | |
|
||||
* | |
|
||||
* +--------------->|--->outputB
|
||||
* +-----------------------------+
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_verilog_module_output_short_connections(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& module_id) {
|
||||
/* Local wires come from the child modules */
|
||||
for (ModuleNetId module_net : module_manager.module_nets(module_id)) {
|
||||
/* We only care the nets that indicate short connections */
|
||||
if (false == module_net_include_output_short_connection(module_manager, module_id, module_net)) {
|
||||
continue;
|
||||
}
|
||||
print_verilog_module_output_short_connection(fp, module_manager, module_id, module_net);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Write a Verilog instance to a file
|
||||
* This function will name the input and output connections to
|
||||
* the inputs/output or local wires available in the parent module
|
||||
*
|
||||
* Parent_module
|
||||
* +-----------------------------+
|
||||
* | |
|
||||
* | +--------------+ |
|
||||
* | | | |
|
||||
* | | child_module | |
|
||||
* | | [instance] | |
|
||||
* | +--------------+ |
|
||||
* | |
|
||||
* +-----------------------------+
|
||||
*
|
||||
*******************************************************************/
|
||||
static
|
||||
void write_verilog_instance_to_file(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& parent_module,
|
||||
const ModuleId& child_module,
|
||||
const size_t& instance_id,
|
||||
const bool& use_explicit_port_map) {
|
||||
/* Ensure a valid file stream */
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Print module name */
|
||||
fp << "\t" << module_manager.module_name(child_module) << " ";
|
||||
/* Print instance name:
|
||||
* if we have an instance name, use it;
|
||||
* if not, we use a default name <name>_<num_instance_in_parent_module>
|
||||
*/
|
||||
if (true == module_manager.instance_name(parent_module, child_module, instance_id).empty()) {
|
||||
fp << module_manager.module_name(child_module) << "_" << instance_id << "_" << " (" << std::endl;
|
||||
} else {
|
||||
fp << module_manager.instance_name(parent_module, child_module, instance_id) << " (" << std::endl;
|
||||
}
|
||||
|
||||
/* Print each port with/without explicit port map */
|
||||
/* port type2type mapping */
|
||||
std::map<ModuleManager::e_module_port_type, enum e_dump_verilog_port_type> port_type2type_map;
|
||||
port_type2type_map[ModuleManager::MODULE_GLOBAL_PORT] = VERILOG_PORT_CONKT;
|
||||
port_type2type_map[ModuleManager::MODULE_GPIO_PORT] = VERILOG_PORT_CONKT;
|
||||
port_type2type_map[ModuleManager::MODULE_INOUT_PORT] = VERILOG_PORT_CONKT;
|
||||
port_type2type_map[ModuleManager::MODULE_INPUT_PORT] = VERILOG_PORT_CONKT;
|
||||
port_type2type_map[ModuleManager::MODULE_OUTPUT_PORT] = VERILOG_PORT_CONKT;
|
||||
port_type2type_map[ModuleManager::MODULE_CLOCK_PORT] = VERILOG_PORT_CONKT;
|
||||
|
||||
/* Port sequence: global, inout, input, output and clock ports, */
|
||||
size_t port_cnt = 0;
|
||||
for (const auto& kv : port_type2type_map) {
|
||||
for (const ModulePortId& child_port_id : module_manager.module_port_ids_by_type(child_module, kv.first)) {
|
||||
BasicPort child_port = module_manager.module_port(child_module, child_port_id);
|
||||
if (0 != port_cnt) {
|
||||
/* Do not dump a comma for the first port */
|
||||
fp << "," << std::endl;
|
||||
}
|
||||
/* Print port */
|
||||
fp << "\t\t";
|
||||
/* if explicit port map is required, output the port name */
|
||||
if (true == use_explicit_port_map) {
|
||||
fp << "." << child_port.get_name() << "(";
|
||||
}
|
||||
|
||||
/* Create the port name and width to be used by the instance */
|
||||
std::vector<BasicPort> instance_ports;
|
||||
for (size_t child_pin : child_port.pins()) {
|
||||
/* Find the net linked to the pin */
|
||||
ModuleNetId net = module_manager.module_instance_port_net(parent_module, child_module, instance_id,
|
||||
child_port_id, child_pin);
|
||||
BasicPort instance_port;
|
||||
if (ModuleNetId::INVALID() == net) {
|
||||
/* We give the same port name as child module, this case happens to global ports */
|
||||
instance_port.set_name(generate_verilog_undriven_local_wire_name(module_manager, child_module, child_port_id));
|
||||
instance_port.set_width(child_pin, child_pin);
|
||||
} else {
|
||||
/* Find the name for this child port */
|
||||
instance_port = generate_verilog_port_for_module_net(module_manager, parent_module, net);
|
||||
}
|
||||
/* Create the port information for the net */
|
||||
instance_ports.push_back(instance_port);
|
||||
}
|
||||
/* Try to merge the ports */
|
||||
std::vector<BasicPort> merged_ports = combine_verilog_ports(instance_ports);
|
||||
|
||||
/* Print a verilog port by combining the instance ports */
|
||||
fp << generate_verilog_ports(merged_ports);
|
||||
|
||||
/* if explicit port map is required, output the pair of branket */
|
||||
if (true == use_explicit_port_map) {
|
||||
fp << ")";
|
||||
}
|
||||
port_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print an end to the instance */
|
||||
fp << ");" << std::endl;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Write a Verilog module to a file
|
||||
* This is a key function, maybe most frequently called in our Verilog writer
|
||||
* Note that file stream must be valid
|
||||
*******************************************************************/
|
||||
void write_verilog_module_to_file(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& module_id,
|
||||
const bool& use_explicit_port_map) {
|
||||
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Ensure we have a valid module_id */
|
||||
VTR_ASSERT(module_manager.valid_module_id(module_id));
|
||||
|
||||
/* Print module declaration */
|
||||
print_verilog_module_declaration(fp, module_manager, module_id);
|
||||
|
||||
/* Print an empty line as splitter */
|
||||
fp << std::endl;
|
||||
|
||||
/* Print internal wires */
|
||||
std::map<std::string, std::vector<BasicPort>> local_wires = find_verilog_module_local_wires(module_manager, module_id);
|
||||
for (std::pair<std::string, std::vector<BasicPort>> port_group : local_wires) {
|
||||
for (const BasicPort& local_wire : port_group.second) {
|
||||
fp << generate_verilog_port(VERILOG_PORT_WIRE, local_wire) << ";" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print an empty line as splitter */
|
||||
fp << std::endl;
|
||||
|
||||
/* Print local connection (from module inputs to output! */
|
||||
print_verilog_comment(fp, std::string("----- BEGIN Local short connections -----"));
|
||||
print_verilog_module_local_short_connections(fp, module_manager, module_id);
|
||||
print_verilog_comment(fp, std::string("----- END Local short connections -----"));
|
||||
|
||||
print_verilog_comment(fp, std::string("----- BEGIN Local output short connections -----"));
|
||||
print_verilog_module_output_short_connections(fp, module_manager, module_id);
|
||||
|
||||
print_verilog_comment(fp, std::string("----- END Local output short connections -----"));
|
||||
/* Print an empty line as splitter */
|
||||
fp << std::endl;
|
||||
|
||||
/* Print instances */
|
||||
for (ModuleId child_module : module_manager.child_modules(module_id)) {
|
||||
for (size_t instance : module_manager.child_module_instances(module_id, child_module)) {
|
||||
/* Print an instance */
|
||||
write_verilog_instance_to_file(fp, module_manager, module_id, child_module, instance, use_explicit_port_map);
|
||||
/* Print an empty line as splitter */
|
||||
fp << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print an end for the module */
|
||||
print_verilog_module_end(fp, module_manager.module_name(module_id));
|
||||
|
||||
/* Print an empty line as splitter */
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef VERILOG_MODULE_WRITER_H
|
||||
#define VERILOG_MODULE_WRITER_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include "module_manager.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void write_verilog_module_to_file(std::fstream& fp,
|
||||
const ModuleManager& module_manager,
|
||||
const ModuleId& module_id,
|
||||
const bool& use_explicit_port_map);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,32 @@
|
|||
#ifndef VERILOG_MUX_H
|
||||
#define VERILOG_MUX_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "circuit_library.h"
|
||||
#include "mux_graph.h"
|
||||
#include "mux_library.h"
|
||||
#include "module_manager.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_submodule_muxes(ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir,
|
||||
const bool& use_explicit_port_map);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -8,6 +8,23 @@
|
|||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/**************************************************
|
||||
* Public Constructors
|
||||
*************************************************/
|
||||
FabricVerilogOption::FabricVerilogOption() {
|
||||
output_directory_.clear();
|
||||
support_icarus_simulator_ = false;
|
||||
include_signal_init_ = false;
|
||||
include_timing_ = false;
|
||||
explicit_port_mapping_ = false;
|
||||
compress_routing_ = false;
|
||||
print_top_testbench_ = false;
|
||||
print_formal_verification_top_netlist_ = false;
|
||||
reference_verilog_file_path_.clear();
|
||||
print_user_defined_template_ = false;
|
||||
verbose_output_ = false;
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Public Accessors
|
||||
*************************************************/
|
||||
|
@ -51,6 +68,10 @@ std::string FabricVerilogOption::reference_verilog_file_path() const {
|
|||
return reference_verilog_file_path_;
|
||||
}
|
||||
|
||||
bool FabricVerilogOption::print_user_defined_template() const {
|
||||
return print_user_defined_template_;
|
||||
}
|
||||
|
||||
bool FabricVerilogOption::verbose_output() const {
|
||||
return verbose_output_;
|
||||
}
|
||||
|
@ -94,6 +115,10 @@ void FabricVerilogOption::set_print_autocheck_top_testbench(const std::string& r
|
|||
reference_verilog_file_path_ = reference_verilog_file_path;
|
||||
}
|
||||
|
||||
void FabricVerilogOption::set_print_user_defined_template(const bool& enabled) {
|
||||
print_user_defined_template_ = enabled;
|
||||
}
|
||||
|
||||
void FabricVerilogOption::set_verbose_output(const bool& enabled) {
|
||||
verbose_output_ = enabled;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ namespace openfpga {
|
|||
*
|
||||
*******************************************************************/
|
||||
class FabricVerilogOption {
|
||||
public: /* Public constructor */
|
||||
/* Set default options */
|
||||
FabricVerilogOption();
|
||||
public: /* Public accessors */
|
||||
std::string output_directory() const;
|
||||
bool support_icarus_simulator() const;
|
||||
|
@ -28,6 +31,7 @@ class FabricVerilogOption {
|
|||
bool print_formal_verification_top_netlist() const;
|
||||
bool print_autocheck_top_testbench() const;
|
||||
std::string reference_verilog_file_path() const;
|
||||
bool print_user_defined_template() const;
|
||||
bool verbose_output() const;
|
||||
public: /* Public mutators */
|
||||
void set_output_directory(const std::string& output_dir);
|
||||
|
@ -39,6 +43,7 @@ class FabricVerilogOption {
|
|||
void set_print_top_testbench(const bool& enabled);
|
||||
void set_print_formal_verification_top_netlist(const bool& enabled);
|
||||
void set_print_autocheck_top_testbench(const std::string& reference_verilog_file_path);
|
||||
void set_print_user_defined_template(const bool& enabled);
|
||||
void set_verbose_output(const bool& enabled);
|
||||
private: /* Internal Data */
|
||||
std::string output_directory_;
|
||||
|
@ -51,6 +56,7 @@ class FabricVerilogOption {
|
|||
bool print_formal_verification_top_netlist_;
|
||||
/* print_autocheck_top_testbench will be enabled when reference file path is not empty */
|
||||
std::string reference_verilog_file_path_;
|
||||
bool print_user_defined_template_;
|
||||
bool verbose_output_;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,357 @@
|
|||
/*********************************************************************
|
||||
* This file includes functions that are used for
|
||||
* Verilog generation of FPGA routing architecture (global routing)
|
||||
*********************************************************************/
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
/* Include FPGA-Verilog header files*/
|
||||
#include "openfpga_naming.h"
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
#include "verilog_module_writer.h"
|
||||
#include "verilog_routing.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Print the sub-circuit of a connection Box (Type: [CHANX|CHANY])
|
||||
* Actually it is very similiar to switch box but
|
||||
* the difference is connection boxes connect Grid INPUT Pins to channels
|
||||
* NOTE: direct connection between CLBs should NOT be included inside this
|
||||
* module! They should be added in the top-level module as their connection
|
||||
* is not limited to adjacent CLBs!!!
|
||||
*
|
||||
* Location of a X- and Y-direction Connection Block in FPGA fabric
|
||||
* +------------+ +-------------+
|
||||
* | |------>| |
|
||||
* | CLB |<------| Y-direction |
|
||||
* | | ... | Connection |
|
||||
* | |------>| Block |
|
||||
* +------------+ +-------------+
|
||||
* | ^ ... | | ^ ... |
|
||||
* v | v v | v
|
||||
* +-------------------+ +-------------+
|
||||
* --->| |--->| |
|
||||
* <---| X-direction |<---| Switch |
|
||||
* ...| Connection block |... | Block |
|
||||
* --->| |--->| |
|
||||
* +-------------------+ +-------------+
|
||||
*
|
||||
* Internal structure:
|
||||
* This is an example of a X-direction connection block
|
||||
* Note that middle output ports are shorted wire from inputs of routing tracks,
|
||||
* which are also the inputs of routing multiplexer of the connection block
|
||||
*
|
||||
* CLB Input Pins
|
||||
* (IPINs)
|
||||
* ^ ^ ^
|
||||
* | | ... |
|
||||
* +--------------------------+
|
||||
* | ^ ^ ^ |
|
||||
* | | | ... | |
|
||||
* | +--------------------+ |
|
||||
* | | routing | |
|
||||
* | | multiplexers | |
|
||||
* | +--------------------+ |
|
||||
* | middle outputs |
|
||||
* | of routing channel |
|
||||
* | ^ ^ ^ ^ ^ ^ ^ ^ |
|
||||
* | | | | | ... | | | | |
|
||||
* in[0] -->|------------------------->|---> out[0]
|
||||
* out[1] <--|<-------------------------|<--- in[1]
|
||||
* | ... |
|
||||
* in[W-2] -->|------------------------->|---> out[W-2]
|
||||
* out[W-1] <--|<-------------------------|<--- in[W-1]
|
||||
* +--------------------------+
|
||||
*
|
||||
* W: routing channel width
|
||||
*
|
||||
********************************************************************/
|
||||
static
|
||||
void print_verilog_routing_connection_box_unique_module(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
const RRGSB& rr_gsb,
|
||||
const t_rr_type& cb_type,
|
||||
const bool& use_explicit_port_map) {
|
||||
/* Create the netlist */
|
||||
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type));
|
||||
std::string verilog_fname(subckt_dir + generate_connection_block_netlist_name(cb_type, gsb_coordinate, std::string(VERILOG_NETLIST_FILE_POSTFIX)));
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
print_verilog_file_header(fp, std::string("Verilog modules for Unique Connection Blocks[" + std::to_string(rr_gsb.get_cb_x(cb_type)) + "]["+ std::to_string(rr_gsb.get_cb_y(cb_type)) + "]"));
|
||||
|
||||
/* Print preprocessing flags */
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Create a Verilog Module based on the circuit model, and add to module manager */
|
||||
ModuleId cb_module = module_manager.find_module(generate_connection_block_module_name(cb_type, gsb_coordinate));
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(cb_module));
|
||||
|
||||
/* Write the verilog module */
|
||||
write_verilog_module_to_file(fp, module_manager, cb_module, use_explicit_port_map);
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
/* Add fname to the netlist name list */
|
||||
netlist_names.push_back(verilog_fname);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Generate the Verilog module for a Switch Box.
|
||||
* A Switch Box module consists of following ports:
|
||||
* 1. Channel Y [x][y] inputs
|
||||
* 2. Channel X [x+1][y] inputs
|
||||
* 3. Channel Y [x][y-1] outputs
|
||||
* 4. Channel X [x][y] outputs
|
||||
* 5. Grid[x][y+1] Right side outputs pins
|
||||
* 6. Grid[x+1][y+1] Left side output pins
|
||||
* 7. Grid[x+1][y+1] Bottom side output pins
|
||||
* 8. Grid[x+1][y] Top side output pins
|
||||
* 9. Grid[x+1][y] Left side output pins
|
||||
* 10. Grid[x][y] Right side output pins
|
||||
* 11. Grid[x][y] Top side output pins
|
||||
* 12. Grid[x][y+1] Bottom side output pins
|
||||
*
|
||||
* Location of a Switch Box in FPGA fabric:
|
||||
*
|
||||
* -------------- --------------
|
||||
* | | | |
|
||||
* | Grid | ChanY | Grid |
|
||||
* | [x][y+1] | [x][y+1] | [x+1][y+1] |
|
||||
* | | | |
|
||||
* -------------- --------------
|
||||
* ----------
|
||||
* ChanX | Switch | ChanX
|
||||
* [x][y] | Box | [x+1][y]
|
||||
* | [x][y] |
|
||||
* ----------
|
||||
* -------------- --------------
|
||||
* | | | |
|
||||
* | Grid | ChanY | Grid |
|
||||
* | [x][y] | [x][y] | [x+1][y] |
|
||||
* | | | |
|
||||
* -------------- --------------
|
||||
*
|
||||
* Switch Block pin location map
|
||||
*
|
||||
* Grid[x][y+1] ChanY[x][y+1] Grid[x+1][y+1]
|
||||
* right_pins inputs/outputs left_pins
|
||||
* | ^ |
|
||||
* | | |
|
||||
* v v v
|
||||
* +-----------------------------------------------+
|
||||
* | |
|
||||
* Grid[x][y+1] | | Grid[x+1][y+1]
|
||||
* bottom_pins---->| |<---- bottom_pins
|
||||
* | |
|
||||
* ChanX[x][y] | Switch Box [x][y] | ChanX[x+1][y]
|
||||
* inputs/outputs<--->| |<---> inputs/outputs
|
||||
* | |
|
||||
* Grid[x][y+1] | | Grid[x+1][y+1]
|
||||
* top_pins---->| |<---- top_pins
|
||||
* | |
|
||||
* +-----------------------------------------------+
|
||||
* ^ ^ ^
|
||||
* | | |
|
||||
* | v |
|
||||
* Grid[x][y] ChanY[x][y] Grid[x+1][y]
|
||||
* right_pins inputs/outputs left_pins
|
||||
*
|
||||
*
|
||||
********************************************************************/
|
||||
static
|
||||
void print_verilog_routing_switch_box_unique_module(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
const RRGSB& rr_gsb,
|
||||
const bool& use_explicit_port_map) {
|
||||
/* Create the netlist */
|
||||
vtr::Point<size_t> gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y());
|
||||
std::string verilog_fname(subckt_dir + generate_routing_block_netlist_name(SB_VERILOG_FILE_NAME_PREFIX, gsb_coordinate, std::string(VERILOG_NETLIST_FILE_POSTFIX)));
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
print_verilog_file_header(fp, std::string("Verilog modules for Unique Switch Blocks[" + std::to_string(rr_gsb.get_sb_x()) + "]["+ std::to_string(rr_gsb.get_sb_y()) + "]"));
|
||||
|
||||
/* Print preprocessing flags */
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Create a Verilog Module based on the circuit model, and add to module manager */
|
||||
ModuleId sb_module = module_manager.find_module(generate_switch_block_module_name(gsb_coordinate));
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
||||
|
||||
/* Write the verilog module */
|
||||
write_verilog_module_to_file(fp, module_manager, sb_module, use_explicit_port_map);
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
/* Add fname to the netlist name list */
|
||||
netlist_names.push_back(verilog_fname);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Iterate over all the connection blocks in a device
|
||||
* and build a module for each of them
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_verilog_flatten_connection_block_modules(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
const t_rr_type& cb_type,
|
||||
const bool& use_explicit_port_map) {
|
||||
/* 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 (true != rr_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
print_verilog_routing_connection_box_unique_module(module_manager, netlist_names,
|
||||
verilog_dir,
|
||||
subckt_dir,
|
||||
rr_gsb, cb_type,
|
||||
use_explicit_port_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A top-level function of this file
|
||||
* Print all the modules for global routing architecture of a FPGA fabric
|
||||
* in Verilog format in a flatten way:
|
||||
* Each connection block and switch block will be generated as a unique module
|
||||
* Covering:
|
||||
* 1. Connection blocks
|
||||
* 2. Switch blocks
|
||||
*******************************************************************/
|
||||
void print_verilog_flatten_routing_modules(const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
const bool& use_explicit_port_map) {
|
||||
/* Create a vector to contain all the Verilog netlist names that have been generated in this function */
|
||||
std::vector<std::string> netlist_names;
|
||||
|
||||
vtr::Point<size_t> sb_range = device_rr_gsb.get_gsb_range();
|
||||
|
||||
/* Build unique switch block modules */
|
||||
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 (true != rr_gsb.is_sb_exist()) {
|
||||
continue;
|
||||
}
|
||||
print_verilog_routing_switch_box_unique_module(module_manager, netlist_names,
|
||||
verilog_dir,
|
||||
subckt_dir,
|
||||
rr_gsb,
|
||||
use_explicit_port_map);
|
||||
}
|
||||
}
|
||||
|
||||
print_verilog_flatten_connection_block_modules(module_manager, netlist_names, device_rr_gsb, verilog_dir, subckt_dir, CHANX, use_explicit_port_map);
|
||||
|
||||
print_verilog_flatten_connection_block_modules(module_manager, netlist_names, device_rr_gsb, verilog_dir, subckt_dir, CHANY, use_explicit_port_map);
|
||||
|
||||
VTR_LOG("Writing header file for routing submodules '%s'...",
|
||||
ROUTING_VERILOG_FILE_NAME);
|
||||
print_verilog_netlist_include_header_file(netlist_names,
|
||||
subckt_dir.c_str(),
|
||||
ROUTING_VERILOG_FILE_NAME);
|
||||
VTR_LOG("Done\n");
|
||||
VTR_LOG("\n");
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* A top-level function of this file
|
||||
* Print all the unique modules for global routing architecture of a FPGA fabric
|
||||
* in Verilog format, including:
|
||||
* 1. Connection blocks
|
||||
* 2. Switch blocks
|
||||
*
|
||||
* Note: this function SHOULD be called only when
|
||||
* the option compact_routing_hierarchy is turned on!!!
|
||||
*******************************************************************/
|
||||
void print_verilog_unique_routing_modules(const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
const bool& use_explicit_port_map) {
|
||||
/* Create a vector to contain all the Verilog netlist names that have been generated in this function */
|
||||
std::vector<std::string> netlist_names;
|
||||
|
||||
/* Build unique switch block modules */
|
||||
for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) {
|
||||
const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(isb);
|
||||
print_verilog_routing_switch_box_unique_module(module_manager, netlist_names,
|
||||
verilog_dir,
|
||||
subckt_dir,
|
||||
unique_mirror,
|
||||
use_explicit_port_map);
|
||||
}
|
||||
|
||||
/* Build 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_verilog_routing_connection_box_unique_module(module_manager, netlist_names,
|
||||
verilog_dir,
|
||||
subckt_dir,
|
||||
unique_mirror, CHANX,
|
||||
use_explicit_port_map);
|
||||
}
|
||||
|
||||
/* Build unique X-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_verilog_routing_connection_box_unique_module(module_manager, netlist_names,
|
||||
verilog_dir,
|
||||
subckt_dir,
|
||||
unique_mirror, CHANY,
|
||||
use_explicit_port_map);
|
||||
}
|
||||
|
||||
VTR_LOG("Writing header file for routing submodules '%s'...",
|
||||
ROUTING_VERILOG_FILE_NAME);
|
||||
print_verilog_netlist_include_header_file(netlist_names,
|
||||
subckt_dir.c_str(),
|
||||
ROUTING_VERILOG_FILE_NAME);
|
||||
VTR_LOG("Done\n");
|
||||
VTR_LOG("\n");
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef VERILOG_ROUTING_H
|
||||
#define VERILOG_ROUTING_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
|
||||
#include "mux_library.h"
|
||||
#include "module_manager.h"
|
||||
#include "device_rr_gsb.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_flatten_routing_modules(const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
const bool& use_explicit_port_map);
|
||||
|
||||
void print_verilog_unique_routing_modules(const ModuleManager& module_manager,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& subckt_dir,
|
||||
const bool& use_explicit_port_map);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,101 @@
|
|||
/*********************************************************************
|
||||
* This file includes top-level function to generate Verilog primitive modules
|
||||
* and print them to files
|
||||
********************************************************************/
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
#include "verilog_submodule_utils.h"
|
||||
#include "verilog_essential_gates.h"
|
||||
#include "verilog_decoders.h"
|
||||
#include "verilog_mux.h"
|
||||
#include "verilog_lut.h"
|
||||
#include "verilog_wire.h"
|
||||
#include "verilog_memory.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_submodule.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/*********************************************************************
|
||||
* Top-level function to generate primitive modules:
|
||||
* 1. Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor
|
||||
* 2. Routing multiplexers
|
||||
* 3. Local encoders for routing multiplexers
|
||||
* 4. Wires
|
||||
* 5. Configuration memory blocks
|
||||
* 6. Verilog template
|
||||
********************************************************************/
|
||||
void print_verilog_submodule(ModuleManager& module_manager,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir,
|
||||
const FabricVerilogOption& fpga_verilog_opts) {
|
||||
|
||||
/* Register all the user-defined modules in the module manager
|
||||
* This should be done prior to other steps in this function,
|
||||
* because they will be instanciated by other primitive modules
|
||||
*/
|
||||
//add_user_defined_verilog_modules(module_manager, circuit_lib);
|
||||
|
||||
/* Create a vector to contain all the Verilog netlist names that have been generated in this function */
|
||||
std::vector<std::string> netlist_names;
|
||||
|
||||
|
||||
print_verilog_submodule_essentials(const_cast<const ModuleManager&>(module_manager),
|
||||
netlist_names,
|
||||
verilog_dir,
|
||||
submodule_dir,
|
||||
circuit_lib);
|
||||
|
||||
/* Routing multiplexers */
|
||||
/* NOTE: local decoders generation must go before the MUX generation!!!
|
||||
* because local decoders modules will be instanciated in the MUX modules
|
||||
*/
|
||||
print_verilog_submodule_mux_local_decoders(const_cast<const ModuleManager&>(module_manager),
|
||||
netlist_names,
|
||||
mux_lib, circuit_lib,
|
||||
verilog_dir, submodule_dir);
|
||||
print_verilog_submodule_muxes(module_manager, netlist_names, mux_lib, circuit_lib,
|
||||
verilog_dir, submodule_dir,
|
||||
fpga_verilog_opts.explicit_port_mapping());
|
||||
|
||||
|
||||
/* LUTes */
|
||||
print_verilog_submodule_luts(const_cast<const ModuleManager&>(module_manager),
|
||||
netlist_names, circuit_lib,
|
||||
verilog_dir, submodule_dir,
|
||||
fpga_verilog_opts.explicit_port_mapping());
|
||||
|
||||
/* Hard wires */
|
||||
print_verilog_submodule_wires(const_cast<const ModuleManager&>(module_manager),
|
||||
netlist_names, circuit_lib,
|
||||
verilog_dir, submodule_dir);
|
||||
|
||||
/* 4. Memories */
|
||||
print_verilog_submodule_memories(const_cast<const ModuleManager&>(module_manager),
|
||||
netlist_names,
|
||||
mux_lib, circuit_lib,
|
||||
verilog_dir, submodule_dir,
|
||||
fpga_verilog_opts.explicit_port_mapping());
|
||||
|
||||
/* 5. Dump template for all the modules */
|
||||
if (true == fpga_verilog_opts.print_user_defined_template()) {
|
||||
print_verilog_submodule_templates(const_cast<const ModuleManager&>(module_manager),
|
||||
circuit_lib,
|
||||
verilog_dir, submodule_dir);
|
||||
}
|
||||
|
||||
/* Create a header file to include all the subckts */
|
||||
print_verilog_netlist_include_header_file(netlist_names,
|
||||
submodule_dir.c_str(),
|
||||
SUBMODULE_VERILOG_FILE_NAME);
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef VERILOG_SUBMODULE_H
|
||||
#define VERILOG_SUBMODULE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include "module_manager.h"
|
||||
#include "mux_library.h"
|
||||
#include "verilog_options.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_submodule(ModuleManager& module_manager,
|
||||
const MuxLibrary& mux_lib,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir,
|
||||
const FabricVerilogOption& fpga_verilog_opts);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,249 @@
|
|||
/************************************************
|
||||
* This file includes most utilized functions for
|
||||
* generating Verilog sub-modules
|
||||
* such as timing matrix and signal initialization
|
||||
***********************************************/
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <iomanip>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_port.h"
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
/* Headers from readarchopenfpga library */
|
||||
#include "circuit_types.h"
|
||||
|
||||
#include "module_manager_utils.h"
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
#include "verilog_submodule_utils.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/* All values are printed with this precision value. The higher the
|
||||
* value, the more accurate timing assignment is. Using a number of 6
|
||||
* guarentees that a precision of femtosecond which is sufficent for
|
||||
* electrical simulation (simulation timescale is 10-9
|
||||
*/
|
||||
/* constexpr int FLOAT_PRECISION = std::numeric_limits<float>::max_digits10; */
|
||||
constexpr int FLOAT_PRECISION = 6;
|
||||
|
||||
/************************************************
|
||||
* Print a timing matrix defined in theecircuit model
|
||||
* into a Verilog format.
|
||||
* This function print all the timing edges available
|
||||
* in the circuit model (any pin-to-pin delay)
|
||||
***********************************************/
|
||||
void print_verilog_submodule_timing(std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model) {
|
||||
/* return if there is no delay info */
|
||||
if ( 0 == circuit_lib.num_delay_info(circuit_model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Return if there is no ports */
|
||||
if (0 == circuit_lib.num_model_ports(circuit_model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << std::endl;
|
||||
fp << "`ifdef " << VERILOG_TIMING_PREPROC_FLAG << std::endl;
|
||||
print_verilog_comment(fp, std::string("------ BEGIN Pin-to-pin Timing constraints -----"));
|
||||
fp << "\tspecify" << std::endl;
|
||||
|
||||
/* Read out pin-to-pin delays by finding out all the edges belonging to a circuit model */
|
||||
for (const auto& timing_edge : circuit_lib.timing_edges_by_model(circuit_model)) {
|
||||
CircuitPortId src_port = circuit_lib.timing_edge_src_port(timing_edge);
|
||||
size_t src_pin = circuit_lib.timing_edge_src_pin(timing_edge);
|
||||
BasicPort src_port_info(circuit_lib.port_lib_name(src_port), src_pin, src_pin);
|
||||
|
||||
CircuitPortId sink_port = circuit_lib.timing_edge_sink_port(timing_edge);
|
||||
size_t sink_pin = circuit_lib.timing_edge_sink_pin(timing_edge);
|
||||
BasicPort sink_port_info(circuit_lib.port_lib_name(sink_port), sink_pin, sink_pin);
|
||||
|
||||
fp << "\t\t";
|
||||
fp << "(" << generate_verilog_port(VERILOG_PORT_CONKT, src_port_info);
|
||||
fp << " => ";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, sink_port_info) << ")";
|
||||
fp << " = ";
|
||||
fp << "(" << std::setprecision(FLOAT_PRECISION) << circuit_lib.timing_edge_delay(timing_edge, CIRCUIT_MODEL_DELAY_RISE) / VERILOG_SIM_TIMESCALE;
|
||||
fp << ", ";
|
||||
fp << std::setprecision(FLOAT_PRECISION) << circuit_lib.timing_edge_delay(timing_edge, CIRCUIT_MODEL_DELAY_FALL) / VERILOG_SIM_TIMESCALE << ")";
|
||||
fp << ";" << std::endl;
|
||||
}
|
||||
|
||||
fp << "\tendspecify" << std::endl;
|
||||
print_verilog_comment(fp, std::string("------ END Pin-to-pin Timing constraints -----"));
|
||||
fp << "`endif" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
void print_verilog_submodule_signal_init(std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model) {
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << std::endl;
|
||||
fp << "`ifdef " << VERILOG_SIGNAL_INIT_PREPROC_FLAG << std::endl;
|
||||
print_verilog_comment(fp, std::string("------ BEGIN driver initialization -----"));
|
||||
fp << "\tinitial begin" << std::endl;
|
||||
fp << "\t`ifdef " << VERILOG_FORMAL_VERIFICATION_PREPROC_FLAG << std::endl;
|
||||
|
||||
/* Only for formal verification: deposite a zero signal values */
|
||||
/* Initialize each input port */
|
||||
for (const auto& input_port : circuit_lib.model_input_ports(circuit_model)) {
|
||||
BasicPort input_port_info(circuit_lib.port_lib_name(input_port), circuit_lib.port_size(input_port));
|
||||
fp << "\t\t$deposit(";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, input_port_info);
|
||||
fp << ", " << circuit_lib.port_size(input_port) << "'b" << std::string(circuit_lib.port_size(input_port), '0');
|
||||
fp << ");" << std::endl;
|
||||
}
|
||||
fp << "\t`else" << std::endl;
|
||||
|
||||
/* Regular case: deposite initial signal values: a random value */
|
||||
for (const auto& input_port : circuit_lib.model_input_ports(circuit_model)) {
|
||||
BasicPort input_port_info(circuit_lib.port_lib_name(input_port), circuit_lib.port_size(input_port));
|
||||
fp << "\t\t$deposit(";
|
||||
fp << generate_verilog_port(VERILOG_PORT_CONKT, input_port_info);
|
||||
fp << ", $random);" << std::endl;
|
||||
}
|
||||
|
||||
fp << "\t`endif\n" << std::endl;
|
||||
fp << "\tend" << std::endl;
|
||||
print_verilog_comment(fp, std::string("------ END driver initialization -----"));
|
||||
fp << "`endif" << std::endl;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Register all the user-defined modules in the module manager
|
||||
* Walk through the circuit library and add user-defined circuit models
|
||||
* to the module_manager
|
||||
********************************************************************/
|
||||
void add_user_defined_verilog_modules(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib) {
|
||||
VTR_LOG("Registering user-defined modules...");
|
||||
|
||||
/* Iterate over Verilog modules */
|
||||
for (const auto& model : circuit_lib.models()) {
|
||||
/* We only care about user-defined models */
|
||||
if (true == circuit_lib.model_verilog_netlist(model).empty()) {
|
||||
continue;
|
||||
}
|
||||
/* Skip Routing channel wire models because they need a different name. Do it later */
|
||||
if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(model)) {
|
||||
continue;
|
||||
}
|
||||
/* Reach here, the model requires a user-defined Verilog netlist,
|
||||
* Try to find it in the module manager
|
||||
* If not found, register it in the module_manager
|
||||
*/
|
||||
ModuleId module_id = module_manager.find_module(circuit_lib.model_name(model));
|
||||
if (ModuleId::INVALID() == module_id) {
|
||||
add_circuit_model_to_module_manager(module_manager, circuit_lib, model);
|
||||
VTR_LOG("Registered user-defined circuit model '%s'\n",
|
||||
circuit_lib.model_name(model).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
VTR_LOG("Done\n");
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Print a template for a user-defined circuit model
|
||||
* The template will include just the port declaration of the Verilog module
|
||||
* The template aims to help user to write Verilog codes with a guaranteed
|
||||
* module definition, which can be correctly instanciated (with correct
|
||||
* port mapping) in the FPGA fabric
|
||||
********************************************************************/
|
||||
static
|
||||
void print_one_verilog_template_module(const ModuleManager& module_manager,
|
||||
std::fstream& fp,
|
||||
const std::string& module_name) {
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
print_verilog_comment(fp, std::string("----- Template Verilog module for " + module_name + " -----"));
|
||||
|
||||
/* Find the module in module manager, which should be already registered */
|
||||
/* TODO: routing channel wire model may have a different name! */
|
||||
ModuleId template_module = module_manager.find_module(module_name);
|
||||
VTR_ASSERT(ModuleId::INVALID() != template_module);
|
||||
|
||||
/* dump module definition + ports */
|
||||
print_verilog_module_declaration(fp, module_manager, template_module);
|
||||
/* Finish dumping ports */
|
||||
|
||||
print_verilog_comment(fp, std::string("----- Internal logic should start here -----"));
|
||||
|
||||
/* Add some empty lines as placeholders for the internal logic*/
|
||||
fp << std::endl << std::endl;
|
||||
|
||||
print_verilog_comment(fp, std::string("----- Internal logic should end here -----"));
|
||||
|
||||
/* Put an end to the Verilog module */
|
||||
print_verilog_module_end(fp, module_name);
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Print a template of all the submodules that are user-defined
|
||||
* The template will include just the port declaration of the submodule
|
||||
* The template aims to help user to write Verilog codes with a guaranteed
|
||||
* module definition, which can be correctly instanciated (with correct
|
||||
* port mapping) in the FPGA fabric
|
||||
********************************************************************/
|
||||
void print_verilog_submodule_templates(const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir) {
|
||||
std::string verilog_fname(submodule_dir + USER_DEFINED_TEMPLATE_VERILOG_FILE_NAME);
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
/* Print out debugging information for if the file is not opened/created properly */
|
||||
VTR_LOG("Creating template for user-defined Verilog modules '%s'...",
|
||||
verilog_fname.c_str());
|
||||
|
||||
print_verilog_file_header(fp, "Template for user-defined Verilog modules");
|
||||
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Output essential models*/
|
||||
for (const auto& model : circuit_lib.models()) {
|
||||
/* Focus on user-defined modules, which must have a Verilog netlist defined */
|
||||
if (circuit_lib.model_verilog_netlist(model).empty()) {
|
||||
continue;
|
||||
}
|
||||
/* Skip Routing channel wire models because they need a different name. Do it later */
|
||||
if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(model)) {
|
||||
continue;
|
||||
}
|
||||
/* Print a Verilog template for the circuit model */
|
||||
print_one_verilog_template_module(module_manager, fp, circuit_lib.model_name(model));
|
||||
}
|
||||
|
||||
/* close file stream */
|
||||
fp.close();
|
||||
|
||||
/* No need to add the template to the subckt include files! */
|
||||
VTR_LOG("Done\n");
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef VERILOG_SUBMODULE_UTILS_H
|
||||
#define VERILOG_SUBMODULE_UTILS_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include "module_manager.h"
|
||||
#include "circuit_library.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_submodule_timing(std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model);
|
||||
|
||||
void print_verilog_submodule_signal_init(std::fstream& fp,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& circuit_model);
|
||||
|
||||
void add_user_defined_verilog_modules(ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib);
|
||||
|
||||
void print_verilog_submodule_templates(const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,75 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that are used to print the top-level
|
||||
* module for the FPGA fabric in Verilog format
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "openfpga_naming.h"
|
||||
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
#include "verilog_module_writer.h"
|
||||
#include "verilog_top_module.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Print the top-level module for the FPGA fabric in Verilog format
|
||||
* This function will
|
||||
* 1. name the top-level module
|
||||
* 2. include dependent netlists
|
||||
* - User defined netlists
|
||||
* - Auto-generated netlists
|
||||
* 3. Add the submodules to the top-level graph
|
||||
* 4. Add module nets to connect datapath ports
|
||||
* 5. Add module nets/submodules to connect configuration ports
|
||||
*******************************************************************/
|
||||
void print_verilog_top_module(const ModuleManager& module_manager,
|
||||
const std::string& verilog_dir,
|
||||
const bool& use_explicit_mapping) {
|
||||
/* Create a module as the top-level fabric, and add it to the module manager */
|
||||
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));
|
||||
|
||||
/* Start printing out Verilog netlists */
|
||||
/* Create the file name for Verilog netlist */
|
||||
std::string verilog_fname(verilog_dir + generate_fpga_top_netlist_name(std::string(VERILOG_NETLIST_FILE_POSTFIX)));
|
||||
|
||||
VTR_LOG("Writing Verilog netlist for top-level module of FPGA fabric '%s'...",
|
||||
verilog_fname.c_str());
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
print_verilog_file_header(fp, std::string("Top-level Verilog module for FPGA"));
|
||||
|
||||
/* Print preprocessing flags */
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Write the module content in Verilog format */
|
||||
write_verilog_module_to_file(fp, module_manager, top_module, use_explicit_mapping);
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
||||
VTR_LOG("Done\n");
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef VERILOG_TOP_MODULE_H
|
||||
#define VERILOG_TOP_MODULE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include "module_manager.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_top_module(const ModuleManager& module_manager,
|
||||
const std::string& verilog_dir,
|
||||
const bool& use_explicit_mapping);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,136 @@
|
|||
/***********************************************
|
||||
* This file includes functions to generate
|
||||
* Verilog submodules for wires.
|
||||
**********************************************/
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "module_manager.h"
|
||||
#include "module_manager_utils.h"
|
||||
|
||||
#include "openfpga_naming.h"
|
||||
|
||||
#include "verilog_constants.h"
|
||||
#include "verilog_submodule_utils.h"
|
||||
#include "verilog_writer_utils.h"
|
||||
#include "verilog_wire.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Print a Verilog module of a regular wire segment
|
||||
* Regular wire, which is 1-input and 1-output
|
||||
* This type of wires are used in the local routing architecture
|
||||
* +------+
|
||||
* input --->| wire |---> output
|
||||
* +------+
|
||||
*
|
||||
*******************************************************************/
|
||||
static
|
||||
void print_verilog_wire_module(const ModuleManager& module_manager,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
std::fstream& fp,
|
||||
const CircuitModelId& wire_model) {
|
||||
/* Ensure a valid file handler*/
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Find the input port, output port*/
|
||||
std::vector<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(wire_model, CIRCUIT_MODEL_PORT_INPUT, true);
|
||||
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(wire_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
|
||||
std::vector<CircuitPortId> global_ports = circuit_lib.model_global_ports_by_type(wire_model, CIRCUIT_MODEL_PORT_INPUT, true, true);
|
||||
|
||||
/* Makre sure the port size is what we want */
|
||||
VTR_ASSERT (1 == input_ports.size());
|
||||
VTR_ASSERT (1 == output_ports.size());
|
||||
VTR_ASSERT (1 == circuit_lib.port_size(input_ports[0]));
|
||||
VTR_ASSERT (1 == circuit_lib.port_size(output_ports[0]));
|
||||
|
||||
/* Create a Verilog Module based on the circuit model, and add to module manager */
|
||||
ModuleId wire_module = module_manager.find_module(circuit_lib.model_name(wire_model));
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(wire_module));
|
||||
|
||||
/* dump module definition + ports */
|
||||
print_verilog_module_declaration(fp, module_manager, wire_module);
|
||||
/* Finish dumping ports */
|
||||
|
||||
/* Print the internal logic of Verilog module */
|
||||
/* Find the input port of the module */
|
||||
ModulePortId module_input_port_id = module_manager.find_module_port(wire_module, circuit_lib.port_lib_name(input_ports[0]));
|
||||
VTR_ASSERT(ModulePortId::INVALID() != module_input_port_id);
|
||||
BasicPort module_input_port = module_manager.module_port(wire_module, module_input_port_id);
|
||||
|
||||
/* Find the output port of the module */
|
||||
ModulePortId module_output_port_id = module_manager.find_module_port(wire_module, circuit_lib.port_lib_name(output_ports[0]));
|
||||
VTR_ASSERT(ModulePortId::INVALID() != module_output_port_id);
|
||||
BasicPort module_output_port = module_manager.module_port(wire_module, module_output_port_id);
|
||||
|
||||
/* Print wire declaration for the inputs and outputs */
|
||||
fp << generate_verilog_port(VERILOG_PORT_WIRE, module_input_port) << ";" << std::endl;
|
||||
fp << generate_verilog_port(VERILOG_PORT_WIRE, module_output_port) << ";" << std::endl;
|
||||
|
||||
/* Direct shortcut */
|
||||
print_verilog_wire_connection(fp, module_output_port, module_input_port, false);
|
||||
|
||||
/* Print timing info */
|
||||
print_verilog_submodule_timing(fp, circuit_lib, wire_model);
|
||||
|
||||
/* Put an end to the Verilog module */
|
||||
print_verilog_module_end(fp, circuit_lib.model_name(wire_model));
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Top-level function to print wire modules
|
||||
*******************************************************************/
|
||||
void print_verilog_submodule_wires(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir) {
|
||||
std::string verilog_fname(submodule_dir + std::string(WIRES_VERILOG_FILE_NAME));
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
check_file_stream(verilog_fname.c_str(), fp);
|
||||
|
||||
/* Print out debugging information for if the file is not opened/created properly */
|
||||
VTR_LOG("Writing Verilog netlist for wires '%s'...",
|
||||
verilog_fname.c_str());
|
||||
|
||||
print_verilog_file_header(fp, "Wires");
|
||||
|
||||
print_verilog_include_defines_preproc_file(fp, verilog_dir);
|
||||
|
||||
/* Print Verilog models for regular wires*/
|
||||
print_verilog_comment(fp, std::string("----- BEGIN Verilog modules for regular wires -----"));
|
||||
for (const auto& model : circuit_lib.models_by_type(CIRCUIT_MODEL_WIRE)) {
|
||||
/* Bypass user-defined circuit models */
|
||||
if (!circuit_lib.model_verilog_netlist(model).empty()) {
|
||||
continue;
|
||||
}
|
||||
print_verilog_wire_module(module_manager, circuit_lib, fp, model);
|
||||
}
|
||||
print_verilog_comment(fp, std::string("----- END Verilog modules for regular wires -----"));
|
||||
|
||||
/* Close the file stream */
|
||||
fp.close();
|
||||
|
||||
/* Add fname to the netlist name list */
|
||||
netlist_names.push_back(verilog_fname);
|
||||
|
||||
VTR_LOG("Done\n");
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef VERILOG_WIRE_H
|
||||
#define VERILOG_WIRE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "circuit_library.h"
|
||||
#include "module_manager.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
void print_verilog_submodule_wires(const ModuleManager& module_manager,
|
||||
std::vector<std::string>& netlist_names,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const std::string& verilog_dir,
|
||||
const std::string& submodule_dir);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -32,7 +32,7 @@ namespace openfpga {
|
|||
***********************************************/
|
||||
void print_verilog_file_header(std::fstream& fp,
|
||||
const std::string& usage) {
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
auto end = std::chrono::system_clock::now();
|
||||
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
|
||||
|
@ -54,7 +54,7 @@ void print_verilog_file_header(std::fstream& fp,
|
|||
*******************************************************************/
|
||||
void print_verilog_include_netlist(std::fstream& fp,
|
||||
const std::string& netlist_name) {
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << "`include \"" << netlist_name << "\"" << std::endl;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ void print_verilog_include_netlist(std::fstream& fp,
|
|||
void print_verilog_define_flag(std::fstream& fp,
|
||||
const std::string& flag_name,
|
||||
const int& flag_value) {
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << "`define " << flag_name << " " << flag_value << std::endl;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ void print_verilog_include_defines_preproc_file(std::fstream& fp,
|
|||
***********************************************/
|
||||
void print_verilog_comment(std::fstream& fp,
|
||||
const std::string& comment) {
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << "// " << comment << std::endl;
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ void print_verilog_comment(std::fstream& fp,
|
|||
***********************************************/
|
||||
void print_verilog_preprocessing_flag(std::fstream& fp,
|
||||
const std::string& preproc_flag) {
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << "`ifdef " << preproc_flag << std::endl;
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ void print_verilog_preprocessing_flag(std::fstream& fp,
|
|||
* Print the endif of a Verilog preprocessing flag
|
||||
***********************************************/
|
||||
void print_verilog_endif(std::fstream& fp) {
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << "`endif" << std::endl;
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ void print_verilog_endif(std::fstream& fp) {
|
|||
***********************************************/
|
||||
void print_verilog_module_definition(std::fstream& fp,
|
||||
const ModuleManager& module_manager, const ModuleId& module_id) {
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
print_verilog_comment(fp, std::string("----- Verilog module for " + module_manager.module_name(module_id) + " -----"));
|
||||
|
||||
|
@ -183,7 +183,7 @@ void print_verilog_module_definition(std::fstream& fp,
|
|||
***********************************************/
|
||||
void print_verilog_module_ports(std::fstream& fp,
|
||||
const ModuleManager& module_manager, const ModuleId& module_id) {
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* port type2type mapping */
|
||||
std::map<ModuleManager::e_module_port_type, enum e_dump_verilog_port_type> port_type2type_map;
|
||||
|
@ -292,7 +292,7 @@ void print_verilog_module_ports(std::fstream& fp,
|
|||
***********************************************/
|
||||
void print_verilog_module_declaration(std::fstream& fp,
|
||||
const ModuleManager& module_manager, const ModuleId& module_id) {
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
print_verilog_module_definition(fp, module_manager, module_id);
|
||||
|
||||
|
@ -327,7 +327,7 @@ void print_verilog_module_instance(std::fstream& fp,
|
|||
const std::map<std::string, BasicPort>& port2port_name_map,
|
||||
const bool& use_explicit_port_map) {
|
||||
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Check: all the key ports in the port2port_name_map does exist in the child module */
|
||||
for (const auto& kv : port2port_name_map) {
|
||||
|
@ -418,7 +418,7 @@ void print_verilog_module_instance(std::fstream& fp,
|
|||
***********************************************/
|
||||
void print_verilog_module_end(std::fstream& fp,
|
||||
const std::string& module_name) {
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << "endmodule" << std::endl;
|
||||
print_verilog_comment(fp, std::string("----- END Verilog module for " + module_name + " -----"));
|
||||
|
@ -672,7 +672,7 @@ void print_verilog_wire_constant_values(std::fstream& fp,
|
|||
const BasicPort& output_port,
|
||||
const std::vector<size_t>& const_values) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << "\t";
|
||||
fp << "assign ";
|
||||
|
@ -687,7 +687,7 @@ void print_verilog_deposit_wire_constant_values(std::fstream& fp,
|
|||
const BasicPort& output_port,
|
||||
const std::vector<size_t>& const_values) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << "\t";
|
||||
fp << "$deposit(";
|
||||
|
@ -705,7 +705,7 @@ void print_verilog_force_wire_constant_values(std::fstream& fp,
|
|||
const BasicPort& output_port,
|
||||
const std::vector<size_t>& const_values) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
fp << "\t";
|
||||
fp << "force ";
|
||||
|
@ -722,7 +722,7 @@ void print_verilog_wire_connection(std::fstream& fp,
|
|||
const BasicPort& input_port,
|
||||
const bool& inverted) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Must check: the port width matches */
|
||||
VTR_ASSERT( input_port.get_width() == output_port.get_width() );
|
||||
|
@ -749,7 +749,7 @@ void print_verilog_register_connection(std::fstream& fp,
|
|||
const BasicPort& input_port,
|
||||
const bool& inverted) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Must check: the port width matches */
|
||||
VTR_ASSERT( input_port.get_width() == output_port.get_width() );
|
||||
|
@ -787,7 +787,7 @@ void print_verilog_buffer_instance(std::fstream& fp,
|
|||
const BasicPort& instance_input_port,
|
||||
const BasicPort& instance_output_port) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* To match the context, Buffer should have only 2 non-global ports: 1 input port and 1 output port */
|
||||
std::vector<CircuitPortId> buffer_model_input_ports = circuit_lib.model_ports_by_type(buffer_model, CIRCUIT_MODEL_PORT_INPUT, true);
|
||||
|
@ -903,7 +903,7 @@ void print_verilog_local_sram_wires(std::fstream& fp,
|
|||
const e_config_protocol_type sram_orgz_type,
|
||||
const size_t& port_size) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Port size must be at least one! */
|
||||
if (0 == port_size) {
|
||||
|
@ -1013,7 +1013,7 @@ void print_verilog_local_config_bus(std::fstream& fp,
|
|||
const size_t& instance_id,
|
||||
const size_t& num_conf_bits) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
switch(sram_orgz_type) {
|
||||
case CONFIG_MEM_STANDALONE:
|
||||
|
@ -1081,7 +1081,7 @@ void print_verilog_rram_mux_config_bus(std::fstream& fp,
|
|||
const size_t& num_reserved_conf_bits,
|
||||
const size_t& num_conf_bits) {
|
||||
/* Make sure we have a valid file handler*/
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
switch(sram_orgz_type) {
|
||||
case CONFIG_MEM_STANDALONE:
|
||||
|
@ -1236,7 +1236,7 @@ void print_verilog_pulse_stimuli(std::fstream& fp,
|
|||
const float& pulse_width,
|
||||
const size_t& flip_value) {
|
||||
/* Validate the file stream */
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Config_done signal: indicate when configuration is finished */
|
||||
fp << "initial" << std::endl;
|
||||
|
@ -1280,7 +1280,7 @@ void print_verilog_pulse_stimuli(std::fstream& fp,
|
|||
const std::vector<size_t>& flip_values,
|
||||
const std::string& wait_condition) {
|
||||
/* Validate the file stream */
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Config_done signal: indicate when configuration is finished */
|
||||
fp << "initial" << std::endl;
|
||||
|
@ -1330,7 +1330,7 @@ void print_verilog_clock_stimuli(std::fstream& fp,
|
|||
const float& pulse_width,
|
||||
const std::string& wait_condition) {
|
||||
/* Validate the file stream */
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Config_done signal: indicate when configuration is finished */
|
||||
fp << "initial" << std::endl;
|
||||
|
@ -1374,13 +1374,14 @@ void print_verilog_clock_stimuli(std::fstream& fp,
|
|||
void print_verilog_netlist_include_header_file(const std::vector<std::string>& netlists_to_be_included,
|
||||
const char* subckt_dir,
|
||||
const char* header_file_name) {
|
||||
|
||||
std::string verilog_fname(std::string(subckt_dir) + std::string(header_file_name));
|
||||
|
||||
/* Create the file stream */
|
||||
std::fstream fp;
|
||||
fp.open(verilog_fname, std::fstream::out | std::fstream::trunc);
|
||||
|
||||
valid_file_stream(fp);
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
/* Generate the descriptions*/
|
||||
print_verilog_file_header(fp, "Header file to include other Verilog netlists");
|
||||
|
|
|
@ -23,7 +23,7 @@ build_fabric --compress_routing --duplicate_grid_pin --verbose
|
|||
|
||||
# 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 --verbose
|
||||
write_fabric_verilog --file /var/tmp/xtang/openfpga_test_src --explicit_port_mapping --include_timing --include_signal_init --support_icarus_simulator --print_user_defined_template --verbose
|
||||
|
||||
# Finish and exit OpenFPGA
|
||||
exit
|
||||
|
|
Loading…
Reference in New Issue