adapt essential gates for submodule generation

This commit is contained in:
tangxifan 2020-02-16 11:57:19 -07:00
parent 2eba882332
commit cf34339e96
2 changed files with 610 additions and 0 deletions

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