OpenFPGA/openfpga/src/fpga_verilog/verilog_writer_utils.cpp

1667 lines
65 KiB
C++

/************************************************
* Include functions for most frequently
* used Verilog writers
***********************************************/
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <string>
/* Headers from vtrutil library */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from readarchopenfpga library */
#include "circuit_types.h"
/* Headers from openfpgautil library */
#include "circuit_library_utils.h"
#include "openfpga_digest.h"
#include "openfpga_naming.h"
#include "verilog_constants.h"
#include "verilog_writer_utils.h"
/* begin namespace openfpga */
namespace openfpga {
/************************************************
* Generate the declaration for default net type
***********************************************/
void print_verilog_default_net_type_declaration(
std::fstream& fp, const e_verilog_default_net_type& default_net_type) {
VTR_ASSERT(true == valid_file_stream(fp));
fp << "//----- Default net type -----" << std::endl;
fp << "`default_nettype " << VERILOG_DEFAULT_NET_TYPE_STRING[default_net_type]
<< std::endl;
fp << std::endl;
}
/************************************************
* Generate header comments for a Verilog netlist
* include the description
***********************************************/
void print_verilog_file_header(std::fstream& fp, const std::string& usage,
const bool& include_time_stamp,
const bool& include_time_scale) {
VTR_ASSERT(true == valid_file_stream(fp));
fp << "//-------------------------------------------" << std::endl;
fp << "//\tFPGA Synthesizable Verilog Netlist" << std::endl;
fp << "//\tDescription: " << usage << std::endl;
fp << "//\tAuthor: Xifan TANG" << std::endl;
fp << "//\tOrganization: University of Utah" << std::endl;
if (include_time_stamp) {
auto end = std::chrono::system_clock::now();
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
fp << "//\tDate: " << std::ctime(&end_time);
}
fp << "//-------------------------------------------" << std::endl;
if (include_time_scale) {
fp << "//----- Time scale -----" << std::endl;
fp << "`timescale 1ns / 1ps" << std::endl;
fp << std::endl;
}
}
/********************************************************************
* Print Verilog codes to include a netlist
*******************************************************************/
void print_verilog_include_netlist(std::fstream& fp,
const std::string& netlist_name) {
VTR_ASSERT(true == valid_file_stream(fp));
fp << "`include \"" << netlist_name << "\"" << std::endl;
}
/********************************************************************
* Print Verilog codes to define a preprocessing flag
*******************************************************************/
void print_verilog_define_flag(std::fstream& fp, const std::string& flag_name,
const int& flag_value) {
VTR_ASSERT(true == valid_file_stream(fp));
fp << "`define " << flag_name << " " << flag_value << std::endl;
}
/************************************************
* Generate include files for a Verilog netlist
***********************************************/
void print_verilog_include_defines_preproc_file(
std::fstream& fp, const std::string& verilog_dir) {
/* Generate the file name */
std::string include_file_path = format_dir_path(verilog_dir);
include_file_path += std::string(DEFINES_VERILOG_FILE_NAME);
print_verilog_include_netlist(fp, include_file_path);
}
/************************************************
* Print a Verilog comment line
***********************************************/
void print_verilog_comment(std::fstream& fp, const std::string& comment) {
VTR_ASSERT(true == valid_file_stream(fp));
fp << "// " << comment << std::endl;
}
/************************************************
* Print the declaration of a Verilog preprocessing flag
***********************************************/
void print_verilog_preprocessing_flag(std::fstream& fp,
const std::string& preproc_flag) {
VTR_ASSERT(true == valid_file_stream(fp));
fp << "`ifdef " << preproc_flag << std::endl;
}
/************************************************
* Print the endif of a Verilog preprocessing flag
***********************************************/
void print_verilog_endif(std::fstream& fp) {
VTR_ASSERT(true == valid_file_stream(fp));
fp << "`endif" << std::endl;
}
/************************************************
* Print a Verilog module definition
* We use the following format:
* module <module_name> (<ports without directions>);
***********************************************/
void print_verilog_module_definition(std::fstream& fp,
const ModuleManager& module_manager,
const ModuleId& module_id) {
VTR_ASSERT(true == valid_file_stream(fp));
print_verilog_comment(
fp, std::string("----- Verilog module for " +
module_manager.module_name(module_id) + " -----"));
std::string module_head_line =
"module " + module_manager.module_name(module_id) + "(";
fp << module_head_line;
/* 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_GPIN_PORT] = VERILOG_PORT_CONKT;
port_type2type_map[ModuleManager::MODULE_GPOUT_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;
bool printed_ifdef =
false; /* A flag to tell if an ifdef has been printed for the last port */
for (const auto& kv : port_type2type_map) {
for (const auto& port :
module_manager.module_ports_by_type(module_id, kv.first)) {
if (0 != port_cnt) {
/* Do not dump a comma for the first port */
fp << "," << std::endl;
}
if (true == printed_ifdef) {
/* Print an endif to pair the ifdef */
print_verilog_endif(fp);
/* Reset the flag */
printed_ifdef = false;
}
ModulePortId port_id =
module_manager.find_module_port(module_id, port.get_name());
VTR_ASSERT(ModulePortId::INVALID() != port_id);
/* Print pre-processing flag for a port, if defined */
std::string preproc_flag =
module_manager.port_preproc_flag(module_id, port_id);
if (false == preproc_flag.empty()) {
/* Print an ifdef Verilog syntax */
print_verilog_preprocessing_flag(fp, preproc_flag);
/* Raise the flag */
printed_ifdef = true;
}
/* Create a space for "module <module_name>" except the first line! */
if (0 != port_cnt) {
std::string port_whitespace(module_head_line.length(), ' ');
fp << port_whitespace;
}
/* Print port: only the port name is enough */
fp << port.get_name();
/* Increase the counter */
port_cnt++;
}
}
fp << ");" << std::endl;
}
/************************************************
* Print a Verilog module ports based on the module id
***********************************************/
void print_verilog_module_ports(
std::fstream& fp, const ModuleManager& module_manager,
const ModuleId& module_id,
const e_verilog_default_net_type& default_net_type) {
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;
port_type2type_map[ModuleManager::MODULE_GLOBAL_PORT] = VERILOG_PORT_INPUT;
port_type2type_map[ModuleManager::MODULE_GPIN_PORT] = VERILOG_PORT_INPUT;
port_type2type_map[ModuleManager::MODULE_GPOUT_PORT] = VERILOG_PORT_OUTPUT;
port_type2type_map[ModuleManager::MODULE_GPIO_PORT] = VERILOG_PORT_INOUT;
port_type2type_map[ModuleManager::MODULE_INOUT_PORT] = VERILOG_PORT_INOUT;
port_type2type_map[ModuleManager::MODULE_INPUT_PORT] = VERILOG_PORT_INPUT;
port_type2type_map[ModuleManager::MODULE_OUTPUT_PORT] = VERILOG_PORT_OUTPUT;
port_type2type_map[ModuleManager::MODULE_CLOCK_PORT] = VERILOG_PORT_INPUT;
/* Port sequence: global, inout, input, output and clock ports, */
for (const auto& kv : port_type2type_map) {
for (const auto& port :
module_manager.module_ports_by_type(module_id, kv.first)) {
ModulePortId port_id =
module_manager.find_module_port(module_id, port.get_name());
VTR_ASSERT(ModulePortId::INVALID() != port_id);
/* Print pre-processing flag for a port, if defined */
std::string preproc_flag =
module_manager.port_preproc_flag(module_id, port_id);
if (false == preproc_flag.empty()) {
/* Print an ifdef Verilog syntax */
print_verilog_preprocessing_flag(fp, preproc_flag);
}
/* Print port */
fp << "//----- " << module_manager.module_port_type_str(kv.first)
<< " -----" << std::endl;
fp << generate_verilog_port(kv.second, port);
fp << ";" << std::endl;
if (false == preproc_flag.empty()) {
/* Print an endif to pair the ifdef */
print_verilog_endif(fp);
}
}
}
/* Output any port that is also wire connection when default net type is not
* wire! */
if (VERILOG_DEFAULT_NET_TYPE_WIRE != default_net_type) {
fp << std::endl;
fp << "//----- BEGIN wire-connection ports -----" << std::endl;
for (const auto& kv : port_type2type_map) {
for (const auto& port :
module_manager.module_ports_by_type(module_id, kv.first)) {
/* Skip the ports that are not registered */
ModulePortId port_id =
module_manager.find_module_port(module_id, port.get_name());
VTR_ASSERT(ModulePortId::INVALID() != port_id);
if (false == module_manager.port_is_wire(module_id, port_id)) {
continue;
}
/* Print pre-processing flag for a port, if defined */
std::string preproc_flag =
module_manager.port_preproc_flag(module_id, port_id);
if (false == preproc_flag.empty()) {
/* Print an ifdef Verilog syntax */
print_verilog_preprocessing_flag(fp, preproc_flag);
}
/* Print port */
fp << generate_verilog_port(VERILOG_PORT_WIRE, port);
fp << ";" << std::endl;
if (false == preproc_flag.empty()) {
/* Print an endif to pair the ifdef */
print_verilog_endif(fp);
}
}
}
fp << "//----- END wire-connection ports -----" << std::endl;
fp << std::endl;
}
/* Output any port that is registered */
fp << std::endl;
fp << "//----- BEGIN Registered ports -----" << std::endl;
for (const auto& kv : port_type2type_map) {
for (const auto& port :
module_manager.module_ports_by_type(module_id, kv.first)) {
/* Skip the ports that are not registered */
ModulePortId port_id =
module_manager.find_module_port(module_id, port.get_name());
VTR_ASSERT(ModulePortId::INVALID() != port_id);
if (false == module_manager.port_is_register(module_id, port_id)) {
continue;
}
/* Print pre-processing flag for a port, if defined */
std::string preproc_flag =
module_manager.port_preproc_flag(module_id, port_id);
if (false == preproc_flag.empty()) {
/* Print an ifdef Verilog syntax */
print_verilog_preprocessing_flag(fp, preproc_flag);
}
/* Print port */
fp << generate_verilog_port(VERILOG_PORT_REG, port);
fp << ";" << std::endl;
if (false == preproc_flag.empty()) {
/* Print an endif to pair the ifdef */
print_verilog_endif(fp);
}
}
}
fp << "//----- END Registered ports -----" << std::endl;
fp << std::endl;
}
/************************************************
* Print a Verilog module declaration (definition + port list
* We use the following format:
* module <module_name> (<ports without directions>);
* <tab><port definition with direction>
***********************************************/
void print_verilog_module_declaration(
std::fstream& fp, const ModuleManager& module_manager,
const ModuleId& module_id,
const e_verilog_default_net_type& default_net_type) {
VTR_ASSERT(true == valid_file_stream(fp));
/* Apply default net type from user's option */
if (default_net_type != VERILOG_DEFAULT_NET_TYPE_WIRE) {
print_verilog_default_net_type_declaration(fp, default_net_type);
}
print_verilog_module_definition(fp, module_manager, module_id);
print_verilog_module_ports(fp, module_manager, module_id, default_net_type);
}
/********************************************************************
* Print an instance in Verilog format (a generic version)
* This function will require user to provide an instance name
*
* This function will output the port map by referring to a port-to-port
* mapping:
* <module_port_name> -> <instance_port_name>
* The key of the port-to-port mapping is the port name of the module:
* The value of the port-to-port mapping is the port information of the instance
* With link between module and instance, the function can output a Verilog
* instance easily, supporting both explicit port mapping:
* .<module_port_name>(<instance_port_name>)
* and inexplicit port mapping
* <instance_port_name>
*
* Note that, it is not necessary that the port-to-port mapping
* covers all the module ports.
* Any instance/module port which are not specified in the port-to-port
* mapping will be output by the module port name.
*******************************************************************/
void print_verilog_module_instance(
std::fstream& fp, const ModuleManager& module_manager,
const ModuleId& module_id, const std::string& instance_name,
const std::map<std::string, BasicPort>& port2port_name_map,
const bool& use_explicit_port_map) {
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) {
ModulePortId module_port_id =
module_manager.find_module_port(module_id, kv.first);
VTR_ASSERT(ModulePortId::INVALID() != module_port_id);
}
/* Print module name */
fp << "\t" << module_manager.module_name(module_id) << " ";
/* Print instance name */
fp << instance_name << " (" << 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_GPIN_PORT] = VERILOG_PORT_CONKT;
port_type2type_map[ModuleManager::MODULE_GPOUT_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 auto& port :
module_manager.module_ports_by_type(module_id, kv.first)) {
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 << "." << port.get_name() << "(";
}
/* Try to find the instanced port name in the name map */
if (port2port_name_map.find(port.get_name()) !=
port2port_name_map.end()) {
/* Found it, we assign the port name */
/* TODO: make sure the port width matches! */
ModulePortId module_port_id =
module_manager.find_module_port(module_id, port.get_name());
/* Get the port from module */
BasicPort module_port =
module_manager.module_port(module_id, module_port_id);
VTR_ASSERT(module_port.get_width() ==
port2port_name_map.at(port.get_name()).get_width());
fp << generate_verilog_port(kv.second,
port2port_name_map.at(port.get_name()));
} else {
/* Not found, we give the default port name */
fp << generate_verilog_port(kv.second, port);
}
/* 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;
}
/************************************************
* Print an instance for a Verilog module
* This function is a wrapper for the generic version of
* print_verilog_module_instance()
* This function create an instance name based on the index
* of the child module in its parent module
***********************************************/
void print_verilog_module_instance(
std::fstream& fp, const ModuleManager& module_manager,
const ModuleId& parent_module_id, const ModuleId& child_module_id,
const std::map<std::string, BasicPort>& port2port_name_map,
const bool& use_explicit_port_map) {
/* Create instance name, <name>_<num_instance_in_parent_module> */
std::string instance_name = module_manager.module_name(child_module_id) +
"_" +
std::to_string(module_manager.num_instance(
parent_module_id, child_module_id)) +
"_";
print_verilog_module_instance(fp, module_manager, child_module_id,
instance_name, port2port_name_map,
use_explicit_port_map);
}
/************************************************
* Print an end line for a Verilog module
***********************************************/
void print_verilog_module_end(
std::fstream& fp, const std::string& module_name,
const e_verilog_default_net_type& default_net_type) {
VTR_ASSERT(true == valid_file_stream(fp));
fp << "endmodule" << std::endl;
print_verilog_comment(
fp, std::string("----- END Verilog module for " + module_name + " -----"));
fp << std::endl;
/* Reset default net type to be none */
if (default_net_type != VERILOG_DEFAULT_NET_TYPE_WIRE) {
print_verilog_default_net_type_declaration(fp,
VERILOG_DEFAULT_NET_TYPE_WIRE);
}
}
/************************************************
* Generate a string of a Verilog port
***********************************************/
std::string generate_verilog_port(
const enum e_dump_verilog_port_type& verilog_port_type,
const BasicPort& port_info, const bool& must_print_port_size,
const bool& big_endian) {
std::string verilog_line;
/* Ensure the port type is valid */
VTR_ASSERT(verilog_port_type < NUM_VERILOG_PORT_TYPES);
std::string size_str;
if (big_endian) {
size_str = "[" + std::to_string(port_info.get_lsb()) + ":" +
std::to_string(port_info.get_msb()) + "]";
} else {
size_str = "[" + std::to_string(port_info.get_msb()) + ":" +
std::to_string(port_info.get_lsb()) + "]";
}
/* Only connection require a format of <port_name>[<lsb>:<msb>]
* others require a format of <port_type> [<lsb>:<msb>] <port_name>
*/
if (VERILOG_PORT_CONKT == verilog_port_type) {
/* Simplication:
* - When LSB == MSB == 0, we do not need to specify size when the user
* option allows Note that user option is essential, otherwise what could
* happen is that a multi-bit verilog port used in instance port mapping is
* printed as a single-bit net For example, input [1:0] in; inv inv_inst
* (.A(in), .Y(out)); The original port width is the reference to backtrace
* the defintion of the port
* - When LSB == MSB, we can use a simplified format <port_type>[<lsb>]
*/
if ((false == must_print_port_size) && (1 == port_info.get_width()) &&
(0 == port_info.get_lsb()) &&
(1 == port_info.get_origin_port_width())) {
size_str.clear();
} else if ((1 == port_info.get_width())) {
size_str = "[" + std::to_string(port_info.get_lsb()) + "]";
}
verilog_line = port_info.get_name() + size_str;
} else {
verilog_line = VERILOG_PORT_TYPE_STRING[verilog_port_type];
verilog_line += " " + size_str + " " + port_info.get_name();
}
return verilog_line;
}
/********************************************************************
* Evaluate if two Verilog ports can be merged:
* If the port name is same, it can merged
*******************************************************************/
bool two_verilog_ports_mergeable(const BasicPort& portA,
const BasicPort& portB) {
if (0 == portA.get_name().compare(portB.get_name())) {
return true;
}
return false;
}
/********************************************************************
* Merge two Verilog ports, return the merged port
* The ports should have the same name
* The new LSB will be minimum of the LSBs of the two ports
* The new MSB will the maximum of the MSBs of the two ports
*******************************************************************/
BasicPort merge_two_verilog_ports(const BasicPort& portA,
const BasicPort& portB) {
BasicPort merged_port;
VTR_ASSERT(true == two_verilog_ports_mergeable(portA, portB));
merged_port.set_name(portA.get_name());
merged_port.set_lsb(
(size_t)std::min((int)portA.get_lsb(), (int)portB.get_lsb()));
merged_port.set_msb(
(size_t)std::max((int)portA.get_msb(), (int)portB.get_msb()));
return merged_port;
}
/************************************************
* This function takes a list of ports and
* combine the port string by comparing the name
* and width of ports.
* For example, two ports A and B share the same name is
* mergable as long as A's MSB + 1 == B's LSB
* Note that the port sequence really matters!
* This function will NOT change the sequence
* of ports in the list port_info
***********************************************/
std::vector<BasicPort> combine_verilog_ports(
const std::vector<BasicPort>& ports) {
std::vector<BasicPort> merged_ports;
/* Directly return if there are no ports */
if (0 == ports.size()) {
return merged_ports;
}
/* Push the first port to the merged ports */
merged_ports.push_back(ports[0]);
/* Iterate over ports */
for (const auto& port : ports) {
/* Bypass the first port, it is already in the list */
if (&port == &ports[0]) {
continue;
}
/* Identify if the port name can be potentially merged:
* if the port can be merged to the last port in the list, it may be merged
*/
if (false == port.mergeable(merged_ports.back())) {
/* Unable to merge, add the port to merged port list */
merged_ports.push_back(port);
continue;
}
/* May be merged, check LSB of port and MSB of merged_port */
if (merged_ports.back().get_msb() + 1 != port.get_lsb()) {
/* Unable to merge, add the port to merged port list */
merged_ports.push_back(port);
continue;
}
/* Reach here, we should merge the ports,
* LSB of merged_port remains the same,
* MSB of merged_port will be updated
* to the MSB of port
*/
BasicPort& port_to_merge = merged_ports.back();
port_to_merge.set_msb(port.get_msb());
}
return merged_ports;
}
/************************************************
* Generate the string of a list of verilog ports
***********************************************/
std::string generate_verilog_ports(const std::vector<BasicPort>& merged_ports) {
/* Output the string of ports:
* If there is only one port in the merged_port list
* we only output the port.
* If there are more than one port in the merged port list, we output an
* concatenated port:
* {<port1>, <port2>, ... <last_port>}
*/
VTR_ASSERT(0 < merged_ports.size());
if (1 == merged_ports.size()) {
/* Use connection type of verilog port */
return generate_verilog_port(VERILOG_PORT_CONKT, merged_ports[0], false);
}
std::string verilog_line = "{";
for (const auto& port : merged_ports) {
/* The first port does not need a comma */
if (&port != &merged_ports[0]) {
verilog_line += ", ";
}
verilog_line += generate_verilog_port(VERILOG_PORT_CONKT, port, false);
}
verilog_line += "}";
return verilog_line;
}
/********************************************************************
* Generate a bus port (could be used to create a local wire)
* for a list of Verilog ports
* The bus port will be created by aggregating the ports in the list
* A bus port name may be need only there are many ports with
* different names. It is hard to name the bus port
*******************************************************************/
BasicPort generate_verilog_bus_port(const std::vector<BasicPort>& input_ports,
const std::string& bus_port_name) {
/* Try to combine the ports */
std::vector<BasicPort> combined_input_ports =
combine_verilog_ports(input_ports);
/* Create a port data structure that is to be returned */
BasicPort bus_port;
if (1 == combined_input_ports.size()) {
bus_port = combined_input_ports[0];
} else {
/* TODO: the naming could be more flexible? */
bus_port.set_name(bus_port_name);
/* Deposite a [0:0] port */
bus_port.set_width(1);
for (const auto& port : combined_input_ports) {
bus_port.combine(port);
}
}
return bus_port;
}
/********************************************************************
* Generate a bus wire declaration for a list of Verilog ports
* Output ports: the local_wire name
* Input ports: the driving ports
* When there are more than two ports, a bus wiring will be created
* {<port0>, <port1>, ... <last_port>}
*******************************************************************/
std::string generate_verilog_local_wire(
const BasicPort& output_port, const std::vector<BasicPort>& input_ports) {
/* Try to combine the ports */
std::vector<BasicPort> combined_input_ports =
combine_verilog_ports(input_ports);
/* If we have more than 1 port in the combined ports ,
* output a local wire */
VTR_ASSERT(0 < combined_input_ports.size());
/* Must check: the port width matches */
size_t input_ports_width = 0;
for (const auto& port : combined_input_ports) {
/* We must have valid ports! */
VTR_ASSERT(0 < port.get_width());
input_ports_width += port.get_width();
}
VTR_ASSERT(input_ports_width == output_port.get_width());
std::string wire_str;
wire_str += generate_verilog_port(VERILOG_PORT_WIRE, output_port);
wire_str += " = ";
wire_str += generate_verilog_ports(combined_input_ports);
wire_str += ";";
return wire_str;
}
/********************************************************************
* Generate a string for a constant value in Verilog format:
* <#.of bits>'b<binary numbers>
*
* Optimization: short_constant
* When this switch is turned on, we will generate short version
* for all-zero/all-one vectors
* {<length>{1'b<zero/one>}}
*******************************************************************/
std::string generate_verilog_constant_values(
const std::vector<size_t>& const_values, const bool& short_constant) {
VTR_ASSERT(!const_values.empty());
bool same_values = true;
size_t first_val = const_values.back();
if (true == short_constant) {
for (const auto& val : const_values) {
if (first_val != val) {
same_values = false;
break;
}
}
}
if (1 == const_values.size()) {
same_values = false;
}
std::string str;
if ((true == short_constant) && (true == same_values)) {
str = "{" + std::to_string(const_values.size()) + "{1'b" +
std::to_string(first_val) + "}}";
} else {
str = std::to_string(const_values.size());
str += "'b";
for (const auto& val : const_values) {
str += std::to_string(val);
}
}
return str;
}
/********************************************************************
* Generate a verilog port with a deposit of constant values
********************************************************************/
std::string generate_verilog_port_constant_values(
const BasicPort& output_port, const std::vector<size_t>& const_values,
const bool& is_register) {
std::string port_str;
/* Must check: the port width matches */
VTR_ASSERT(const_values.size() == output_port.get_width());
port_str = generate_verilog_port(VERILOG_PORT_CONKT, output_port);
if (is_register) {
port_str += " <= ";
} else {
VTR_ASSERT_SAFE(!is_register);
port_str += " = ";
}
port_str += generate_verilog_constant_values(const_values);
return port_str;
}
/********************************************************************
* Generate a list of verilog ports with a deposit of constant values
********************************************************************/
std::string generate_verilog_ports_constant_values(
const std::vector<BasicPort>& output_ports,
const std::vector<size_t>& const_values, const bool& is_register) {
std::string port_str;
/* Must check: the port width matches */
size_t total_width = 0;
for (const BasicPort& port : output_ports) {
total_width += port.get_width();
}
VTR_ASSERT(const_values.size() == total_width);
port_str = generate_verilog_ports(output_ports);
if (is_register) {
port_str += " <= ";
} else {
VTR_ASSERT_SAFE(!is_register);
port_str += " = ";
}
port_str += generate_verilog_constant_values(const_values);
return port_str;
}
/********************************************************************
* Generate a wire connection, that assigns constant values to a
* Verilog port
*******************************************************************/
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*/
VTR_ASSERT(true == valid_file_stream(fp));
fp << "\t";
fp << "assign ";
fp << generate_verilog_port_constant_values(output_port, const_values);
fp << ";" << std::endl;
}
/********************************************************************
* Deposit constant values to a Verilog port
*******************************************************************/
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*/
VTR_ASSERT(true == valid_file_stream(fp));
fp << "\t";
fp << "$deposit(";
fp << generate_verilog_port(VERILOG_PORT_CONKT, output_port);
fp << ", ";
fp << generate_verilog_constant_values(const_values);
fp << ");" << std::endl;
}
/********************************************************************
* Generate a wire connection, that assigns constant values to a
* Verilog port
*******************************************************************/
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*/
VTR_ASSERT(true == valid_file_stream(fp));
fp << "\t";
fp << "force ";
fp << generate_verilog_port_constant_values(output_port, const_values);
fp << ";" << std::endl;
}
/********************************************************************
* Generate a wire connection for two Verilog ports
* using "assign" syntax
*******************************************************************/
void print_verilog_wire_connection(std::fstream& fp,
const BasicPort& output_port,
const BasicPort& input_port,
const bool& inverted) {
/* Make sure we have a valid file handler*/
VTR_ASSERT(true == valid_file_stream(fp));
/* Must check: the port width matches */
VTR_ASSERT(input_port.get_width() == output_port.get_width());
fp << "\t";
fp << "assign ";
fp << generate_verilog_port(VERILOG_PORT_CONKT, output_port);
fp << " = ";
if (true == inverted) {
fp << "~";
}
fp << generate_verilog_port(VERILOG_PORT_CONKT, input_port);
fp << ";" << std::endl;
}
/********************************************************************
* Generate a wire connection for two Verilog ports
* using "assign" syntax
*******************************************************************/
void print_verilog_register_connection(std::fstream& fp,
const BasicPort& output_port,
const BasicPort& input_port,
const bool& inverted) {
/* Make sure we have a valid file handler*/
VTR_ASSERT(true == valid_file_stream(fp));
/* Must check: the port width matches */
VTR_ASSERT(input_port.get_width() == output_port.get_width());
fp << "\t";
fp << generate_verilog_port(VERILOG_PORT_CONKT, output_port);
fp << " <= ";
if (true == inverted) {
fp << "~";
}
fp << generate_verilog_port(VERILOG_PORT_CONKT, input_port);
fp << ";" << std::endl;
}
/********************************************************************
* Generate an instance of a buffer module
* with given information about the input and output ports of instance
*
* Buffer instance
* +----------------------------------------+
* instance_input_port --->| buffer_input_port buffer_output_port|---->
*instance_output_port
* +----------------------------------------+
*
* Restrictions:
* Buffer must have only 1 input (non-global) port and 1 output (non-global)
*port
*******************************************************************/
void print_verilog_buffer_instance(std::fstream& fp,
ModuleManager& module_manager,
const CircuitLibrary& circuit_lib,
const ModuleId& parent_module_id,
const CircuitModelId& buffer_model,
const BasicPort& instance_input_port,
const BasicPort& instance_output_port) {
/* Make sure we have a valid file handler*/
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);
std::vector<CircuitPortId> buffer_model_output_ports =
circuit_lib.model_ports_by_type(buffer_model, CIRCUIT_MODEL_PORT_OUTPUT,
true);
VTR_ASSERT(1 == buffer_model_input_ports.size());
VTR_ASSERT(1 == buffer_model_output_ports.size());
/* Get the moduleId for the buffer module */
ModuleId buffer_module_id =
module_manager.find_module(circuit_lib.model_name(buffer_model));
/* We must have one */
VTR_ASSERT(ModuleId::INVALID() != buffer_module_id);
/* Create a port-to-port map */
std::map<std::string, BasicPort> buffer_port2port_name_map;
/* Build the link between buffer_input_port[0] and output_node_pre_buffer
* Build the link between buffer_output_port[0] and output_node_bufferred
*/
{ /* Create a code block to accommodate the local variables */
std::string module_input_port_name =
circuit_lib.port_lib_name(buffer_model_input_ports[0]);
buffer_port2port_name_map[module_input_port_name] = instance_input_port;
std::string module_output_port_name =
circuit_lib.port_lib_name(buffer_model_output_ports[0]);
buffer_port2port_name_map[module_output_port_name] = instance_output_port;
}
/* Output an instance of the module */
print_verilog_module_instance(
fp, module_manager, parent_module_id, buffer_module_id,
buffer_port2port_name_map,
circuit_lib.dump_explicit_port_map(buffer_model));
/* IMPORTANT: this update MUST be called after the instance outputting!!!!
* update the module manager with the relationship between the parent and
* child modules
*/
module_manager.add_child_module(parent_module_id, buffer_module_id);
}
/********************************************************************
* Print local wires that are used for SRAM configuration
* The local wires are strongly dependent on the organization of SRAMs.
* Standalone SRAMs:
* -----------------
* No need for local wires, their outputs are port of the module
*
* Module
* +------------------------------+
* | Sub-module |
* | +---------------------+ |
* | | sram_out|---->|---->sram_out
* | | | |
* | | sram_out|---->|---->sram_out
* | | | |
* | +---------------------+ |
* +------------------------------+
*
* Configuration chain-style
* -------------------------
* wire [0:N] config_bus
*
*
* Module
* +--------------------------------------------------------------+
* | config_bus config_bus config_bus config_bus |
* | [0] [1] [2] [N] |
* | | | | | |
* | v v v v |
* ccff_head| ----------+ +---------+ +------------+ +----------------|->
ccff_tail
* | | ^ | ^ | ^ |
* | head v |tail v | v | |
* | +----------+ +----------+ +----------+ |
* | | Memory | | Memory | | Memory | |
* | | Module | | Module | ... | Module | |
* | | [0] | | [1] | | [N] | |
* | +----------+ +----------+ +----------+ |
* | | | | |
* | v v v |
* | +----------+ +----------+ +----------+ |
* | | MUX | | MUX | | MUX | |
* | | Module | | Module | ... | Module | |
* | | [0] | | [1] | | [N] | |
* | +----------+ +----------+ +----------+ |
* | |
* +--------------------------------------------------------------+
*
* Memory bank-style
* -----------------
* two ports will be added, which are regular output and inverted output
* Note that the outputs are the data outputs of SRAMs
* BL/WLs of memory decoders are ports of module but not local wires
*
* Module
* +-------------------------------------------------+
* | |
BL/WL bus --+--------+------------+-----------------+ |
* | | | | |
* | BL/WL v BL/WL v BL/WL v |
* | +----------+ +----------+ +----------+ |
* | | Memory | | Memory | | Memory | |
* | | Module | | Module | ... | Module | |
* | | [0] | | [1] | | [N] | |
* | +----------+ +----------+ +----------+ |
* | | | | |
* | v v v |
* | +----------+ +----------+ +----------+ |
* | | MUX | | MUX | | MUX | |
* | | Module | | Module | ... | Module | |
* | | [0] | | [1] | | [N] | |
* | +----------+ +----------+ +----------+ |
* | |
* +-------------------------------------------------+
*
********************************************************************/
void print_verilog_local_sram_wires(std::fstream& fp,
const CircuitLibrary& circuit_lib,
const CircuitModelId& sram_model,
const e_config_protocol_type sram_orgz_type,
const size_t& port_size) {
/* Make sure we have a valid file handler*/
VTR_ASSERT(true == valid_file_stream(fp));
/* Port size must be at least one! */
if (0 == port_size) {
return;
}
/* Depend on the configuraion style */
switch (sram_orgz_type) {
case CONFIG_MEM_STANDALONE:
/* Nothing to do here */
break;
case CONFIG_MEM_SCAN_CHAIN: {
/* Generate the name of local wire for the CCFF inputs, CCFF output and
* inverted output */
/* [0] => CCFF input */
BasicPort ccff_config_bus_port(generate_local_config_bus_port_name(),
port_size);
fp << generate_verilog_port(VERILOG_PORT_WIRE, ccff_config_bus_port)
<< ";" << std::endl;
/* Connect first CCFF to the head */
/* Head is always a 1-bit port */
BasicPort ccff_head_port(
generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_INPUT), 1);
BasicPort ccff_head_local_port(ccff_config_bus_port.get_name(), 1);
print_verilog_wire_connection(fp, ccff_head_local_port, ccff_head_port,
false);
/* Connect last CCFF to the tail */
/* Tail is always a 1-bit port */
BasicPort ccff_tail_port(
generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_OUTPUT), 1);
BasicPort ccff_tail_local_port(ccff_config_bus_port.get_name(),
ccff_config_bus_port.get_msb(),
ccff_config_bus_port.get_msb());
print_verilog_wire_connection(fp, ccff_tail_local_port, ccff_tail_port,
false);
break;
}
case CONFIG_MEM_QL_MEMORY_BANK:
case CONFIG_MEM_MEMORY_BANK: {
/* Generate the name of local wire for the SRAM output and inverted output
*/
std::vector<BasicPort> sram_ports;
/* [0] => SRAM output */
sram_ports.push_back(BasicPort(
generate_sram_local_port_name(circuit_lib, sram_model, sram_orgz_type,
CIRCUIT_MODEL_PORT_INPUT),
port_size));
/* [1] => SRAM inverted output */
sram_ports.push_back(BasicPort(
generate_sram_local_port_name(circuit_lib, sram_model, sram_orgz_type,
CIRCUIT_MODEL_PORT_OUTPUT),
port_size));
/* Print local wire definition */
for (const auto& sram_port : sram_ports) {
fp << generate_verilog_port(VERILOG_PORT_WIRE, sram_port) << ";"
<< std::endl;
}
break;
}
default:
VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid SRAM organization!\n");
exit(1);
}
}
/*********************************************************************
* Print a number of bus ports which are wired to the configuration
* ports of a CMOS (SRAM-based) routing multiplexer
* This port is supposed to be used locally inside a Verilog/SPICE module
*
* The following shows a few representative examples:
*
* For standalone configuration style:
* ------------------------------------
* No bus needed
*
* Configuration chain-style
* -------------------------
* wire [0:N] config_bus
*
* config_bus config_bus config_bus config_bus
* [0] [1] [2] [N]
* | | | |
* v v v v
* ccff_head ----------+ +---------+ +------------+ +----> ccff_tail
* | ^ | ^ | ^
* head v |tail v | v |
* +----------+ +----------+ +----------+
* | Memory | | Memory | | Memory |
* | Module | | Module | ... | Module |
* | [0] | | [1] | | [N] |
* +----------+ +----------+ +----------+
* | | |
* v v v
* +----------+ +----------+ +----------+
* | MUX | | MUX | | MUX |
* | Module | | Module | ... | Module |
* | [0] | | [1] | | [N] |
* +----------+ +----------+ +----------+
*
* Memory bank-style
* -----------------
* BL/WL bus --+------------+-------------------->
* | | |
* BL/WL v BL/WL v BL/WL v
* +----------+ +----------+ +----------+
* | Memory | | Memory | | Memory |
* | Module | | Module | ... | Module |
* | [0] | | [1] | | [N] |
* +----------+ +----------+ +----------+
* | | |
* v v v
* +----------+ +----------+ +----------+
* | MUX | | MUX | | MUX |
* | Module | | Module | ... | Module |
* | [0] | | [1] | | [N] |
* +----------+ +----------+ +----------+
*
*********************************************************************/
void print_verilog_local_config_bus(
std::fstream& fp, const std::string& prefix,
const e_config_protocol_type& sram_orgz_type, const size_t& instance_id,
const size_t& num_conf_bits) {
/* Make sure we have a valid file handler*/
VTR_ASSERT(true == valid_file_stream(fp));
switch (sram_orgz_type) {
case CONFIG_MEM_STANDALONE:
/* Not need for configuration bus
* The configuration ports of SRAM are directly wired to the ports of
* modules
*/
break;
case CONFIG_MEM_SCAN_CHAIN:
case CONFIG_MEM_QL_MEMORY_BANK:
case CONFIG_MEM_MEMORY_BANK: {
/* Two configuration buses should be outputted
* One for the regular SRAM ports of a routing multiplexer
* The other for the inverted SRAM ports of a routing multiplexer
*/
BasicPort config_port(generate_local_sram_port_name(
prefix, instance_id, CIRCUIT_MODEL_PORT_INPUT),
num_conf_bits);
fp << generate_verilog_port(VERILOG_PORT_WIRE, config_port) << ";"
<< std::endl;
BasicPort inverted_config_port(
generate_local_sram_port_name(prefix, instance_id,
CIRCUIT_MODEL_PORT_OUTPUT),
num_conf_bits);
fp << generate_verilog_port(VERILOG_PORT_WIRE, inverted_config_port)
<< ";" << std::endl;
break;
}
default:
VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid SRAM organization!\n");
exit(1);
}
}
/*********************************************************************
* Print a number of bus ports which are wired to the configuration
* ports of a ReRAM-based routing multiplexer
* This port is supposed to be used locally inside a Verilog/SPICE module
*
* Currently support:
* For memory-bank configuration style:
* ------------------------------------
* Different than CMOS routing multiplexers, ReRAM multiplexers require
* reserved BL/WLs to be grouped in buses
*
* Module Port
* |
* v
* regular/reserved bus_port --+----------------+----> ...
* | |
* bl/wl/../sram_ports v v
* +-----------+ +-----------+
* | Memory | | Memory |
* | Module[0] | | Module[1] | ...
* +-----------+ +-----------+
* | |
* v v
* +-----------+ +-----------+
* | Routing | | Routing |
* | MUX [0] | | MUX[1] | ...
* +-----------+ +-----------+
*
*********************************************************************/
static void print_verilog_rram_mux_config_bus(
std::fstream& fp, const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model, const e_config_protocol_type& sram_orgz_type,
const size_t& mux_size, const size_t& mux_instance_id,
const size_t& num_reserved_conf_bits, const size_t& num_conf_bits) {
/* Make sure we have a valid file handler*/
VTR_ASSERT(true == valid_file_stream(fp));
switch (sram_orgz_type) {
case CONFIG_MEM_STANDALONE:
/* Not need for configuration bus
* The configuration ports of SRAM are directly wired to the ports of
* modules
*/
break;
case CONFIG_MEM_SCAN_CHAIN: {
/* Not supported yet.
* Configuration chain may be only applied to ReRAM-based multiplexers
* with local decoders
*/
break;
}
case CONFIG_MEM_QL_MEMORY_BANK:
case CONFIG_MEM_MEMORY_BANK: {
/* This is currently most used in ReRAM FPGAs */
/* Print configuration bus to group reserved BL/WLs */
BasicPort reserved_bl_bus(
generate_reserved_sram_port_name(CIRCUIT_MODEL_PORT_BL),
num_reserved_conf_bits);
fp << generate_verilog_port(VERILOG_PORT_WIRE, reserved_bl_bus) << ";"
<< std::endl;
BasicPort reserved_wl_bus(
generate_reserved_sram_port_name(CIRCUIT_MODEL_PORT_WL),
num_reserved_conf_bits);
fp << generate_verilog_port(VERILOG_PORT_WIRE, reserved_wl_bus) << ";"
<< std::endl;
/* Print configuration bus to group BL/WLs */
BasicPort bl_bus(generate_mux_config_bus_port_name(circuit_lib, mux_model,
mux_size, 0, false),
num_conf_bits + num_reserved_conf_bits);
fp << generate_verilog_port(VERILOG_PORT_WIRE, bl_bus) << ";"
<< std::endl;
BasicPort wl_bus(generate_mux_config_bus_port_name(circuit_lib, mux_model,
mux_size, 1, false),
num_conf_bits + num_reserved_conf_bits);
fp << generate_verilog_port(VERILOG_PORT_WIRE, wl_bus) << ";"
<< std::endl;
/* Print bus to group SRAM outputs, this is to interface memory cells to
* routing multiplexers */
BasicPort sram_output_bus(
generate_mux_sram_port_name(circuit_lib, mux_model, mux_size,
mux_instance_id, CIRCUIT_MODEL_PORT_INPUT),
num_conf_bits);
fp << generate_verilog_port(VERILOG_PORT_WIRE, sram_output_bus) << ";"
<< std::endl;
BasicPort inverted_sram_output_bus(
generate_mux_sram_port_name(circuit_lib, mux_model, mux_size,
mux_instance_id, CIRCUIT_MODEL_PORT_OUTPUT),
num_conf_bits);
fp << generate_verilog_port(VERILOG_PORT_WIRE, inverted_sram_output_bus)
<< ";" << std::endl;
/* Get the SRAM model of the mux_model */
std::vector<CircuitModelId> sram_models =
find_circuit_sram_models(circuit_lib, mux_model);
/* TODO: maybe later multiplexers may have mode select ports... This
* should be relaxed */
VTR_ASSERT(1 == sram_models.size());
/* Wire the reserved configuration bits to part of bl/wl buses */
BasicPort bl_bus_reserved_bits(bl_bus.get_name(), num_reserved_conf_bits);
print_verilog_wire_connection(fp, bl_bus_reserved_bits, reserved_bl_bus,
false);
BasicPort wl_bus_reserved_bits(wl_bus.get_name(), num_reserved_conf_bits);
print_verilog_wire_connection(fp, wl_bus_reserved_bits, reserved_wl_bus,
false);
/* Connect SRAM BL/WLs to bus */
BasicPort mux_bl_wire(
generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_BL),
num_conf_bits);
BasicPort bl_bus_regular_bits(bl_bus.get_name(), num_reserved_conf_bits,
num_reserved_conf_bits + num_conf_bits - 1);
print_verilog_wire_connection(fp, bl_bus_regular_bits, mux_bl_wire,
false);
BasicPort mux_wl_wire(
generate_sram_port_name(sram_orgz_type, CIRCUIT_MODEL_PORT_WL),
num_conf_bits);
BasicPort wl_bus_regular_bits(wl_bus.get_name(), num_reserved_conf_bits,
num_reserved_conf_bits + num_conf_bits - 1);
print_verilog_wire_connection(fp, wl_bus_regular_bits, mux_wl_wire,
false);
break;
}
default:
VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid SRAM organization!\n");
exit(1);
}
}
/*********************************************************************
* Print a number of bus ports which are wired to the configuration
* ports of a memory module, which consists of a number of configuration
* memory cells, such as SRAMs.
* Note that the configuration bus will only interface the memory
* module, rather than the programming routing multiplexers, LUTs, IOs
* etc. This helps us to keep clean and simple Verilog generation
*********************************************************************/
void print_verilog_mux_config_bus(
std::fstream& fp, const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model, const e_config_protocol_type& sram_orgz_type,
const size_t& mux_size, const size_t& mux_instance_id,
const size_t& num_reserved_conf_bits, const size_t& num_conf_bits) {
/* Depend on the design technology of this MUX:
* bus connections are different
* SRAM MUX: bus is connected to the output ports of SRAM
* RRAM MUX: bus is connected to the BL/WL of MUX
* TODO: Maybe things will become even more complicated,
* the bus connections may depend on the type of configuration circuit...
* Currently, this is fine.
*/
switch (circuit_lib.design_tech_type(mux_model)) {
case CIRCUIT_MODEL_DESIGN_CMOS: {
std::string prefix = generate_mux_subckt_name(circuit_lib, mux_model,
mux_size, std::string());
print_verilog_local_config_bus(fp, prefix, sram_orgz_type,
mux_instance_id, num_conf_bits);
break;
}
case CIRCUIT_MODEL_DESIGN_RRAM:
print_verilog_rram_mux_config_bus(
fp, circuit_lib, mux_model, sram_orgz_type, mux_size, mux_instance_id,
num_reserved_conf_bits, num_conf_bits);
break;
default:
VTR_LOGF_ERROR(__FILE__, __LINE__,
"Invalid design technology for routing multiplexer!\n");
exit(1);
}
}
/*********************************************************************
* Print a wire to connect MUX configuration ports
* This function connects the sram ports to the ports of a Verilog module
* used for formal verification
*
* Note: MSB and LSB of formal verification configuration bus MUST be updated
* before running this function !!!!
*********************************************************************/
void print_verilog_formal_verification_mux_sram_ports_wiring(
std::fstream& fp, const CircuitLibrary& circuit_lib,
const CircuitModelId& mux_model, const size_t& mux_size,
const size_t& mux_instance_id, const size_t& num_conf_bits,
const BasicPort& fm_config_bus) {
BasicPort mux_sram_output(
generate_mux_sram_port_name(circuit_lib, mux_model, mux_size,
mux_instance_id, CIRCUIT_MODEL_PORT_INPUT),
num_conf_bits);
/* Get the SRAM model of the mux_model */
std::vector<CircuitModelId> sram_models =
find_circuit_sram_models(circuit_lib, mux_model);
/* TODO: maybe later multiplexers may have mode select ports... This should be
* relaxed */
VTR_ASSERT(1 == sram_models.size());
BasicPort formal_verification_port;
formal_verification_port.set_name(
generate_formal_verification_sram_port_name(circuit_lib, sram_models[0]));
VTR_ASSERT(num_conf_bits == fm_config_bus.get_width());
formal_verification_port.set_lsb(fm_config_bus.get_lsb());
formal_verification_port.set_msb(fm_config_bus.get_msb());
print_verilog_wire_connection(fp, mux_sram_output, formal_verification_port,
false);
}
/********************************************************************
* Print stimuli for a pulse generation
*
* |<--- pulse width --->|
* +------ flip_value
* |
* initial_value ----------------------+
*
*******************************************************************/
void print_verilog_pulse_stimuli(std::fstream& fp, const BasicPort& port,
const size_t& initial_value,
const float& pulse_width,
const size_t& flip_value) {
/* Validate the file stream */
VTR_ASSERT(true == valid_file_stream(fp));
/* Config_done signal: indicate when configuration is finished */
fp << "initial" << std::endl;
fp << "\tbegin" << std::endl;
fp << "\t";
std::vector<size_t> initial_values(port.get_width(), initial_value);
fp << "\t";
fp << generate_verilog_port_constant_values(port, initial_values);
fp << ";" << std::endl;
/* if flip_value is the same as initial value, we do not need to flip the
* signal ! */
if (flip_value != initial_value) {
fp << "\t"
<< "#" << std::setprecision(10) << pulse_width;
std::vector<size_t> port_flip_values(port.get_width(), flip_value);
fp << "\t";
fp << generate_verilog_port_constant_values(port, port_flip_values);
fp << ";" << std::endl;
}
fp << "\tend" << std::endl;
/* Print an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Print stimuli for a clock pulse generation
* This function supports the delay at the beginning of the waveform
*
* |<-- Initial delay -->|<--- pulse width --->|
* +------ flip_value
* |
* initial_value --------------------------------------------+
*
*******************************************************************/
void print_verilog_shifted_clock_stimuli(std::fstream& fp,
const BasicPort& port,
const float& initial_delay,
const float& pulse_width,
const size_t& initial_value) {
/* Validate the file stream */
VTR_ASSERT(true == valid_file_stream(fp));
/* Config_done signal: indicate when configuration is finished */
fp << "initial" << std::endl;
write_tab_to_file(fp, 1);
fp << "begin" << std::endl;
write_tab_to_file(fp, 1);
std::vector<size_t> initial_values(port.get_width(), initial_value);
write_tab_to_file(fp, 1);
fp << generate_verilog_port_constant_values(port, initial_values);
fp << ";" << std::endl;
write_tab_to_file(fp, 2);
fp << "#" << std::setprecision(10) << initial_delay;
fp << ";" << std::endl;
write_tab_to_file(fp, 2);
fp << "forever ";
fp << generate_verilog_port(VERILOG_PORT_CONKT, port);
fp << " = ";
fp << "#" << std::setprecision(10) << pulse_width;
fp << " ~" << generate_verilog_port(VERILOG_PORT_CONKT, port);
fp << ";" << std::endl;
write_tab_to_file(fp, 1);
fp << "end" << std::endl;
/* Print an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Print stimuli for a pulse generation
* This function supports multiple signal switching under different pulse width
*
* |<-- wait condition -->|
* |<--- pulse width --->|
* +------
*flip_values
* |
* initial_value ------- ... --------------------------------+
*
*******************************************************************/
void print_verilog_pulse_stimuli(std::fstream& fp, const BasicPort& port,
const size_t& initial_value,
const std::vector<float>& pulse_widths,
const std::vector<size_t>& flip_values,
const std::string& wait_condition) {
/* Validate the file stream */
VTR_ASSERT(true == valid_file_stream(fp));
/* Config_done signal: indicate when configuration is finished */
fp << "initial" << std::endl;
fp << "\tbegin" << std::endl;
fp << "\t";
std::vector<size_t> initial_values(port.get_width(), initial_value);
fp << "\t";
fp << generate_verilog_port_constant_values(port, initial_values);
fp << ";" << std::endl;
/* Set a wait condition if specified */
if (false == wait_condition.empty()) {
fp << "\twait(" << wait_condition << ")" << std::endl;
}
/* Number of flip conditions and values should match */
VTR_ASSERT(flip_values.size() == pulse_widths.size());
for (size_t ipulse = 0; ipulse < pulse_widths.size(); ++ipulse) {
fp << "\t"
<< "#" << std::setprecision(10) << pulse_widths[ipulse];
std::vector<size_t> port_flip_value(port.get_width(), flip_values[ipulse]);
fp << "\t";
fp << generate_verilog_port_constant_values(port, port_flip_value);
fp << ";" << std::endl;
}
fp << "\tend" << std::endl;
/* Print an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Print stimuli for a clock signal
* This function can support if the clock signal should wait for a period
* of time and then start
* pulse width
* |<----->|
* +-------+ +-------+
* | | | |
* initial_value --- ... ---+ +-------+ +------ ...
* |<--wait_condition-->|
*
*******************************************************************/
void print_verilog_clock_stimuli(std::fstream& fp, const BasicPort& port,
const size_t& initial_value,
const float& pulse_width,
const std::string& wait_condition) {
/* Validate the file stream */
VTR_ASSERT(true == valid_file_stream(fp));
/* Config_done signal: indicate when configuration is finished */
fp << "initial" << std::endl;
fp << "\tbegin" << std::endl;
std::vector<size_t> initial_values(port.get_width(), initial_value);
fp << "\t\t";
fp << generate_verilog_port_constant_values(port, initial_values);
fp << ";" << std::endl;
fp << "\tend" << std::endl;
fp << "always";
/* Set a wait condition if specified */
if (true == wait_condition.empty()) {
fp << std::endl;
} else {
fp << " wait(" << wait_condition << ")" << std::endl;
}
fp << "\tbegin" << std::endl;
fp << "\t\t"
<< "#" << std::setprecision(10) << pulse_width;
fp << "\t";
fp << generate_verilog_port(VERILOG_PORT_CONKT, port);
fp << " = ";
fp << "~";
fp << generate_verilog_port(VERILOG_PORT_CONKT, port);
fp << ";" << std::endl;
fp << "\tend" << std::endl;
/* Print an empty line as splitter */
fp << std::endl;
}
/********************************************************************
* Output a header file that includes a number of Verilog netlists
* so that it can be easily included in a top-level netlist
********************************************************************/
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,
const bool& include_time_stamp) {
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);
VTR_ASSERT(true == valid_file_stream(fp));
/* Generate the descriptions*/
print_verilog_file_header(fp, "Header file to include other Verilog netlists",
include_time_stamp);
/* Output file names */
for (const std::string& netlist_name : netlists_to_be_included) {
fp << "`include \"" << netlist_name << "\"" << std::endl;
}
/* close file stream */
fp.close();
}
} /* end namespace openfpga */