Merge branch 'refactoring' into dev

This commit is contained in:
tangxifan 2020-02-16 16:36:20 -07:00
commit 95accb662b
38 changed files with 4776 additions and 59 deletions

View File

@ -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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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");

View File

@ -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());
}

View File

@ -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);

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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_;
};

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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");

View File

@ -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