From 2eba8823320ad8c74de0816f8dade1c39289d67c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 11:41:20 -0700 Subject: [PATCH 01/13] put verilog submodules online. ready to bring the how submodule writer online --- .../fpga_verilog/verilog_submodule_utils.cpp | 243 ++++++++++++++++++ .../fpga_verilog/verilog_submodule_utils.h | 37 +++ .../src/fpga_verilog/verilog_writer_utils.cpp | 48 ++-- 3 files changed, 304 insertions(+), 24 deletions(-) create mode 100644 openfpga/src/fpga_verilog/verilog_submodule_utils.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_submodule_utils.h diff --git a/openfpga/src/fpga_verilog/verilog_submodule_utils.cpp b/openfpga/src/fpga_verilog/verilog_submodule_utils.cpp new file mode 100644 index 000000000..f4f02472d --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_submodule_utils.cpp @@ -0,0 +1,243 @@ +/************************************************ + * This file includes most utilized functions for + * generating Verilog sub-modules + * such as timing matrix and signal initialization + ***********************************************/ +#include +#include +#include + +/* 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::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) { + /* 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); + } + } +} + +/********************************************************************* + * 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_submodule_utils.h b/openfpga/src/fpga_verilog/verilog_submodule_utils.h new file mode 100644 index 000000000..fedcb63a8 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_submodule_utils.h @@ -0,0 +1,37 @@ +#ifndef VERILOG_SUBMODULE_UTILS_H +#define VERILOG_SUBMODULE_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#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 diff --git a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp index 5798b05fc..28f2ed5db 100644 --- a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp @@ -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 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& 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& 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& 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& 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 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& 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; @@ -1380,7 +1380,7 @@ void print_verilog_netlist_include_header_file(const std::vector& n 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"); From cf34339e96cfbd20c8f93c5e11c2966dfb9142d9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 11:57:19 -0700 Subject: [PATCH 02/13] adapt essential gates for submodule generation --- .../fpga_verilog/verilog_essential_gates.cpp | 585 ++++++++++++++++++ .../fpga_verilog/verilog_essential_gates.h | 25 + 2 files changed, 610 insertions(+) create mode 100644 openfpga/src/fpga_verilog/verilog_essential_gates.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_essential_gates.h diff --git a/openfpga/src/fpga_verilog/verilog_essential_gates.cpp b/openfpga/src/fpga_verilog/verilog_essential_gates.cpp new file mode 100644 index 000000000..c18bece61 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_essential_gates.cpp @@ -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 + +/* 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& 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 input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + std::vector 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 input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + std::vector 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& input_ports, + const std::vector& 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& input_ports, + const std::vector& 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 input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + std::vector 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(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& 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_essential_gates.h b/openfpga/src/fpga_verilog/verilog_essential_gates.h new file mode 100644 index 000000000..33ce49c6c --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_essential_gates.h @@ -0,0 +1,25 @@ +#ifndef VERILOG_ESSENTIAL_GATES_H +#define VERILOG_ESSENTIAL_GATES_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "circuit_library.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_verilog_submodule_essentials(ModuleManager& module_manager, + std::vector& netlist_names, + const std::string& verilog_dir, + const std::string& submodule_dir, + const CircuitLibrary& circuit_lib); + +} /* end namespace openfpga */ + +#endif From 3efd1a2a6d59711e1aadcded3bc3c0bb3e20a742 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 12:04:03 -0700 Subject: [PATCH 03/13] print verilog module writer online --- .../fpga_verilog/verilog_module_writer.cpp | 502 ++++++++++++++++++ .../src/fpga_verilog/verilog_module_writer.h | 24 + 2 files changed, 526 insertions(+) create mode 100644 openfpga/src/fpga_verilog/verilog_module_writer.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_module_writer.h diff --git a/openfpga/src/fpga_verilog/verilog_module_writer.cpp b/openfpga/src/fpga_verilog/verilog_module_writer.cpp new file mode 100644 index 000000000..15a5bc28d --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_module_writer.cpp @@ -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 + +/* 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 __ + * 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> find_verilog_module_local_wires(const ModuleManager& module_manager, + const ModuleId& module_id) { + std::map> 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>::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 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 _ + */ + 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 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 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 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> local_wires = find_verilog_module_local_wires(module_manager, module_id); + for (std::pair> 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_module_writer.h b/openfpga/src/fpga_verilog/verilog_module_writer.h new file mode 100644 index 000000000..1c0391492 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_module_writer.h @@ -0,0 +1,24 @@ +#ifndef VERILOG_MODULE_WRITER_H +#define VERILOG_MODULE_WRITER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#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 From a88c4bc954f38edef0fb29ee7e753b357cab47dd Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 12:21:59 -0700 Subject: [PATCH 04/13] add decode utils to libopenfpga and adapt local decoder writer in Verilog --- .../libopenfpgautil/src/openfpga_decode.cpp | 55 +++++ .../libopenfpgautil/src/openfpga_decode.h | 23 ++ .../src/fpga_verilog/verilog_decoders.cpp | 231 ++++++++++++++++++ openfpga/src/fpga_verilog/verilog_decoders.h | 32 +++ 4 files changed, 341 insertions(+) create mode 100644 libopenfpga/libopenfpgautil/src/openfpga_decode.cpp create mode 100644 libopenfpga/libopenfpgautil/src/openfpga_decode.h create mode 100644 openfpga/src/fpga_verilog/verilog_decoders.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_decoders.h diff --git a/libopenfpga/libopenfpgautil/src/openfpga_decode.cpp b/libopenfpga/libopenfpgautil/src/openfpga_decode.cpp new file mode 100644 index 000000000..9839f13b9 --- /dev/null +++ b/libopenfpga/libopenfpgautil/src/openfpga_decode.cpp @@ -0,0 +1,55 @@ +/*************************************************************************************** + * This file includes functions that are used to decode integer to binary vectors + * or the reverse operation + ***************************************************************************************/ +#include + +/* 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 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 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 itobin_vec(const size_t& in_int, + const size_t& bin_len) { + std::vector 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 */ diff --git a/libopenfpga/libopenfpgautil/src/openfpga_decode.h b/libopenfpga/libopenfpgautil/src/openfpga_decode.h new file mode 100644 index 000000000..c79254f63 --- /dev/null +++ b/libopenfpga/libopenfpgautil/src/openfpga_decode.h @@ -0,0 +1,23 @@ +#ifndef OPENFPGA_DECODE_H +#define OPENFPGA_DECODE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +/******************************************************************** + * Function declaration + *******************************************************************/ +/* namespace openfpga begins */ +namespace openfpga { + +std::vector ito1hot_vec(const size_t& in_int, + const size_t& bin_len); + +std::vector itobin_vec(const size_t& in_int, + const size_t& bin_len); + +} /* namespace openfpga ends */ + +#endif diff --git a/openfpga/src/fpga_verilog/verilog_decoders.cpp b/openfpga/src/fpga_verilog/verilog_decoders.cpp new file mode 100644 index 000000000..7689d3ed6 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_decoders.cpp @@ -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 + +/* 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, + 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(ModuleManager& module_manager, + std::vector& 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 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_decoders.h b/openfpga/src/fpga_verilog/verilog_decoders.h new file mode 100644 index 000000000..41f932932 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_decoders.h @@ -0,0 +1,32 @@ +#ifndef VERILOG_DECODERS_H +#define VERILOG_DECODERS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include + +#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(ModuleManager& module_manager, + std::vector& netlist_names, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::string& verilog_dir, + const std::string& submodule_dir); + +} /* end namespace openfpga */ + +#endif From c9d8120ae09b3c7ffda1d294931562ef4f4c6963 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 12:35:41 -0700 Subject: [PATCH 05/13] adapt Verilog mux writer --- openfpga/src/fpga_verilog/verilog_constants.h | 2 + openfpga/src/fpga_verilog/verilog_mux.cpp | 1281 +++++++++++++++++ openfpga/src/fpga_verilog/verilog_mux.h | 32 + 3 files changed, 1315 insertions(+) create mode 100644 openfpga/src/fpga_verilog/verilog_mux.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_mux.h diff --git a/openfpga/src/fpga_verilog/verilog_constants.h b/openfpga/src/fpga_verilog/verilog_constants.h index 9562bd730..49a73bf6d 100644 --- a/openfpga/src/fpga_verilog/verilog_constants.h +++ b/openfpga/src/fpga_verilog/verilog_constants.h @@ -41,4 +41,6 @@ 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"; + #endif diff --git a/openfpga/src/fpga_verilog/verilog_mux.cpp b/openfpga/src/fpga_verilog/verilog_mux.cpp new file mode 100644 index 000000000..b78a9362a --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_mux.cpp @@ -0,0 +1,1281 @@ +/*********************************************** + * This file includes functions to generate + * Verilog submodules for multiplexers. + * including both fundamental submodules + * such as a branch in a multiplexer + * and the full multiplexer + **********************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from readarch library */ +#include "physical_types.h" + +/* Headers from readarcopenfpga library */ +#include "circuit_types.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "mux_graph.h" +#include "module_manager.h" +#include "mux_utils.h" +#include "circuit_library_utils.h" +#include "decoder_library_utils.h" + +#include "openfpga_naming.h" + +#include "verilog_constants.h" +#include "verilog_writer_utils.h" +#include "verilog_module_writer.h" +#include "verilog_mux.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/********************************************************************* + * Generate behavior-level Verilog codes modeling an branch circuit + * for a multiplexer with the given size + *********************************************************************/ +static +void generate_verilog_cmos_mux_branch_body_behavioral(std::fstream& fp, + const BasicPort& input_port, + const BasicPort& output_port, + const BasicPort& mem_port, + const MuxGraph& mux_graph, + const size_t& default_mem_val) { + /* Make sure we have a valid file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Verilog Behavior description for a MUX */ + print_verilog_comment(fp, std::string("---- Behavioral-level description -----")); + + /* Add an internal register for the output */ + BasicPort outreg_port("out_reg", mux_graph.num_outputs()); + /* Print the port */ + fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, outreg_port) << ";" << std::endl; + + /* Generate the case-switch table */ + fp << "\talways @(" << generate_verilog_port(VERILOG_PORT_CONKT, input_port) << ", " << generate_verilog_port(VERILOG_PORT_CONKT, mem_port) << ")" << std::endl; + fp << "\tcase (" << generate_verilog_port(VERILOG_PORT_CONKT, mem_port) << ")" << std::endl; + + /* Output the netlist following the connections in mux_graph */ + /* Iterate over the inputs */ + for (const auto& mux_input : mux_graph.inputs()) { + BasicPort cur_input_port(input_port.get_name(), size_t(mux_graph.input_id(mux_input)), size_t(mux_graph.input_id(mux_input))); + /* Iterate over the outputs */ + for (const auto& mux_output : mux_graph.outputs()) { + BasicPort cur_output_port(output_port.get_name(), size_t(mux_graph.output_id(mux_output)), size_t(mux_graph.output_id(mux_output))); + /* if there is a connection between the input and output, a tgate will be outputted */ + std::vector edges = mux_graph.find_edges(mux_input, mux_output); + /* There should be only one edge or no edge*/ + VTR_ASSERT((1 == edges.size()) || (0 == edges.size())); + /* No need to output tgates if there are no edges between two nodes */ + if (0 == edges.size()) { + continue; + } + /* For each case, generate the logic levels for all the inputs */ + /* In each case, only one mem is enabled */ + fp << "\t\t" << mem_port.get_width() << "'b"; + std::string case_code(mem_port.get_width(), default_mem_val); + + /* Find the mem_id controlling the edge */ + MuxMemId mux_mem = mux_graph.find_edge_mem(edges[0]); + /* Flip a bit by the mem_id */ + if (false == mux_graph.is_edge_use_inv_mem(edges[0])) { + case_code[size_t(mux_mem)] = '1'; + } else { + case_code[size_t(mux_mem)] = '0'; + } + fp << case_code << ": " << generate_verilog_port(VERILOG_PORT_CONKT, outreg_port) << " <= "; + fp << generate_verilog_port(VERILOG_PORT_CONKT, cur_input_port) << ";" << std::endl; + } + } + + /* Default case: outputs are at high-impedance state 'z' */ + std::string default_case(mux_graph.num_outputs(), 'z'); + fp << "\t\tdefault: " << generate_verilog_port(VERILOG_PORT_CONKT, outreg_port) << " <= "; + fp << mux_graph.num_outputs() << "'b" << default_case << ";" << std::endl; + + /* End the case */ + fp << "\tendcase" << std::endl; + + /* Wire registers to output ports */ + fp << "\tassign " << generate_verilog_port(VERILOG_PORT_CONKT, output_port) << " = "; + fp << generate_verilog_port(VERILOG_PORT_CONKT, outreg_port) << ";" << std::endl; +} + +/********************************************************************* + * Generate Verilog codes modeling an branch circuit + * for a CMOS multiplexer with the given size + * Support structural and behavioral Verilog codes + *********************************************************************/ +static +void print_verilog_cmos_mux_branch_module_behavioral(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& mux_model, + const std::string& module_name, + const MuxGraph& mux_graph) { + /* Get the tgate model */ + CircuitModelId tgate_model = circuit_lib.pass_gate_logic_model(mux_model); + + /* Skip output if the tgate model is a MUX2, it is handled by essential-gate generator */ + if (CIRCUIT_MODEL_GATE == circuit_lib.model_type(tgate_model)) { + VTR_ASSERT(CIRCUIT_MODEL_GATE_MUX2 == circuit_lib.gate_type(tgate_model)); + return; + } + + /* Make sure we have a valid file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Generate the Verilog netlist according to the mux_graph */ + /* Find out the number of inputs */ + size_t num_inputs = mux_graph.num_inputs(); + /* Find out the number of outputs */ + size_t num_outputs = mux_graph.num_outputs(); + /* Find out the number of memory bits */ + size_t num_mems = mux_graph.num_memory_bits(); + + /* Check codes to ensure the port of Verilog netlists will match */ + /* MUX graph must have only 1 output */ + VTR_ASSERT(1 == num_outputs); + /* MUX graph must have only 1 level*/ + VTR_ASSERT(1 == mux_graph.num_levels()); + + /* Create a Verilog Module based on the circuit model, and add to module manager */ + ModuleId mux_module = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); + /* Find module ports */ + /* Find each input port */ + BasicPort input_port("in", num_inputs); + /* Find each output port */ + BasicPort output_port("out", num_outputs); + /* Find each memory port */ + BasicPort mem_port("mem", num_mems); + + /* dump module definition + ports */ + print_verilog_module_declaration(fp, module_manager, mux_module); + + /* Print the internal logic in behavioral Verilog codes */ + /* Get the default value of SRAM ports */ + std::vector regular_sram_ports = find_circuit_regular_sram_ports(circuit_lib, mux_model); + VTR_ASSERT(1 == regular_sram_ports.size()); + std::string mem_default_val = std::to_string(circuit_lib.port_default_value(regular_sram_ports[0])); + /* Mem string must be only 1-bit! */ + VTR_ASSERT(1 == mem_default_val.length()); + generate_verilog_cmos_mux_branch_body_behavioral(fp, input_port, output_port, mem_port, mux_graph, mem_default_val[0]); + + /* Put an end to the Verilog module */ + print_verilog_module_end(fp, module_name); +} + +/********************************************************************* + * Dump a structural verilog for RRAM MUX basis module + * This is only called when structural verilog dumping option is enabled for this spice model + * IMPORTANT: the structural verilog can NOT be used for functionality verification!!! + * TODO: This part is quite restricted to the way we implemented our RRAM FPGA + * Should be reworked to be more generic !!! + * + * By structural the schematic is splitted into two parts: left part and right part + * The left part includes BLB[0..N-1] and WL[0..N-1] signals as well as RRAMs + * The right part includes BLB[N] and WL[N] + * Corresponding Schematic is as follows: + * + * LEFT PART | RIGHT PART + * + * BLB[0] BLB[N] + * | | + * \|/ \|/ + * in[0] ---->RRAM[0]-----+ + * | + * BLB[1] | + * | | + * \|/ | + * in[1] ---->RRAM[1]-----+ + * |-----> out[0] + * ... + * | + * in[N-1] ---->RRAM[N-1]---+ + * /|\ /|\ + * | | + * BLB[N-1] WL[N] + * + * Working principle: + * 1. Set a RRAM[i]: enable BLB[i] and WL[N] + * 2. Reset a RRAM[i]: enable BLB[N] and WL[i] + * 3. Operation: disable all BLBs and WLs + * + * The structure is done in the way we implement the physical layout of RRAM MUX + * It is NOT the only road to the goal!!! + *********************************************************************/ +static +void generate_verilog_rram_mux_branch_body_structural(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const ModuleId& module_id, + const CircuitModelId& circuit_model, + const BasicPort& input_port, + const BasicPort& output_port, + const BasicPort& blb_port, + const BasicPort& wl_port, + const MuxGraph& mux_graph) { + std::string progTE_module_name("PROG_TE"); + std::string progBE_module_name("PROG_BE"); + + /* Make sure we have a valid file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Verilog Behavior description for a MUX */ + print_verilog_comment(fp, std::string("---- Structure-level description of RRAM MUX -----")); + + /* Print internal structure of 4T1R programming structures + * Written in structural Verilog + * The whole structure-level description is divided into two parts: + * 1. Left part consists of N PROG_TE modules, each of which + * includes a PMOS, a NMOS and a RRAM, which is actually the left + * part of a 4T1R programming structure + * 2. Right part includes only a PROG_BE module, which consists + * of a PMOS and a NMOS, which is actually the right part of a + * 4T1R programming sturcture + */ + /* Create a module for the progTE and register it in the module manager + * Structure of progTE + * + * +----------+ + * in--->| | + * BLB-->| progTE |--> out + * WL--->| | + * +----------+ + */ + ModuleId progTE_module_id = module_manager.add_module(progTE_module_name); + /* If there is already such as module inside, we just ned to find the module id */ + if (ModuleId::INVALID() == progTE_module_id) { + progTE_module_id = module_manager.find_module(progTE_module_name); + /* We should have a valid id! */ + VTR_ASSERT(ModuleId::INVALID() != progTE_module_id); + } + /* Add ports to the module */ + /* input port */ + BasicPort progTE_in_port("A", 1); + module_manager.add_port(progTE_module_id, progTE_in_port, ModuleManager::MODULE_INPUT_PORT); + /* WL port */ + BasicPort progTE_wl_port("WL", 1); + module_manager.add_port(progTE_module_id, progTE_wl_port, ModuleManager::MODULE_INPUT_PORT); + /* BLB port */ + BasicPort progTE_blb_port("BLB", 1); + module_manager.add_port(progTE_module_id, progTE_blb_port, ModuleManager::MODULE_INPUT_PORT); + /* output port */ + BasicPort progTE_out_port("Z", 1); + module_manager.add_port(progTE_module_id, progTE_out_port, ModuleManager::MODULE_INPUT_PORT); + + /* LEFT part: Verilog code generation */ + /* Iterate over the inputs */ + for (const auto& mux_input : mux_graph.inputs()) { + BasicPort cur_input_port(input_port.get_name(), size_t(mux_graph.input_id(mux_input)), size_t(mux_graph.input_id(mux_input))); + /* Iterate over the outputs */ + for (const auto& mux_output : mux_graph.outputs()) { + BasicPort cur_output_port(output_port.get_name(), size_t(mux_graph.output_id(mux_output)), size_t(mux_graph.output_id(mux_output))); + /* if there is a connection between the input and output, a tgate will be outputted */ + std::vector edges = mux_graph.find_edges(mux_input, mux_output); + /* There should be only one edge or no edge*/ + VTR_ASSERT((1 == edges.size()) || (0 == edges.size())); + /* No need to output tgates if there are no edges between two nodes */ + if (0 == edges.size()) { + continue; + } + /* Create a port-to-port name map */ + std::map port2port_name_map; + /* input port */ + port2port_name_map[progTE_in_port.get_name()] = cur_input_port; + /* output port */ + port2port_name_map[progTE_out_port.get_name()] = cur_output_port; + /* Find the mem_id controlling the edge */ + MuxMemId mux_mem = mux_graph.find_edge_mem(edges[0]); + BasicPort cur_blb_port(blb_port.get_name(), size_t(mux_mem), size_t(mux_mem)); + BasicPort cur_wl_port(wl_port.get_name(), size_t(mux_mem), size_t(mux_mem)); + /* RRAM configuration port: there should not be any inverted edge in RRAM MUX! */ + VTR_ASSERT( false == mux_graph.is_edge_use_inv_mem(edges[0]) ); + /* wire mem to mem of module, and wire mem_inv to mem_inv of module */ + port2port_name_map[progTE_blb_port.get_name()] = cur_blb_port; + port2port_name_map[progTE_wl_port.get_name()] = cur_wl_port; + /* Output an instance of the module */ + print_verilog_module_instance(fp, module_manager, module_id, progTE_module_id, port2port_name_map, circuit_lib.dump_explicit_port_map(circuit_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(module_id, progTE_module_id); + } + } + + /* Create a module for the progBE and register it in the module manager + * Structure of progBE + * + * +----------+ + * | | + * BLB-->| progBE |<-> out + * WL--->| | + * +----------+ + */ + ModuleId progBE_module_id = module_manager.add_module(progBE_module_name); + /* If there is already such as module inside, we just ned to find the module id */ + if (ModuleId::INVALID() == progBE_module_id) { + progBE_module_id = module_manager.find_module(progBE_module_name); + /* We should have a valid id! */ + VTR_ASSERT(ModuleId::INVALID() != progBE_module_id); + } + /* Add ports to the module */ + /* inout port */ + BasicPort progBE_inout_port("INOUT", 1); + module_manager.add_port(progBE_module_id, progBE_inout_port, ModuleManager::MODULE_INOUT_PORT); + /* WL port */ + BasicPort progBE_wl_port("WL", 1); + module_manager.add_port(progBE_module_id, progBE_wl_port, ModuleManager::MODULE_INPUT_PORT); + /* BLB port */ + BasicPort progBE_blb_port("BLB", 1); + module_manager.add_port(progBE_module_id, progBE_blb_port, ModuleManager::MODULE_INPUT_PORT); + + /* RIGHT part: Verilog code generation */ + /* Iterate over the outputs */ + for (const auto& mux_output : mux_graph.outputs()) { + BasicPort cur_output_port(output_port.get_name(), size_t(mux_graph.output_id(mux_output)), size_t(mux_graph.output_id(mux_output))); + /* Create a port-to-port name map */ + std::map port2port_name_map; + /* Wire the output port to the INOUT port */ + port2port_name_map[progBE_inout_port.get_name()] = cur_output_port; + /* Find the mem_id controlling the edge */ + BasicPort cur_blb_port(blb_port.get_name(), mux_graph.num_memory_bits(), mux_graph.num_memory_bits()); + BasicPort cur_wl_port(wl_port.get_name(), mux_graph.num_memory_bits(), mux_graph.num_memory_bits()); + port2port_name_map[progBE_blb_port.get_name()] = cur_blb_port; + port2port_name_map[progBE_wl_port.get_name()] = cur_wl_port; + /* Output an instance of the module */ + print_verilog_module_instance(fp, module_manager, module_id, progBE_module_id, port2port_name_map, circuit_lib.dump_explicit_port_map(circuit_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(module_id, progBE_module_id); + } +} + +/********************************************************************* + * Generate behavior-level Verilog codes modeling an branch circuit + * for a RRAM-based multiplexer with the given size + * Corresponding Schematic is as follows: + * + * BLB[0] BLB[N] + * | | + * \|/ \|/ + * in[0] ---->RRAM[0]-----+ + * | + * BLB[1] | + * | | + * \|/ | + * in[1] ---->RRAM[1]-----+ + * |-----> out[0] + * ... + * | + * in[N-1] ---->RRAM[N-1]---+ + * /|\ /|\ + * | | + * BLB[N-1] WL[N] + * + * Working principle: + * 1. Set a RRAM[i]: enable BLB[i] and WL[N] + * 2. Reset a RRAM[i]: enable BLB[N] and WL[i] + * 3. Operation: disable all BLBs and WLs + * + * TODO: Elaborate the codes to output the circuit logic + * following the mux_graph! + *********************************************************************/ +static +void generate_verilog_rram_mux_branch_body_behavioral(std::fstream& fp, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const BasicPort& input_port, + const BasicPort& output_port, + const BasicPort& blb_port, + const BasicPort& wl_port, + const MuxGraph& mux_graph) { + /* Make sure we have a valid file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Verilog Behavior description for a MUX */ + print_verilog_comment(fp, std::string("---- Behavioral-level description of RRAM MUX -----")); + + /* Add an internal register for the output */ + BasicPort outreg_port("out_reg", mux_graph.num_inputs()); + /* Print the port */ + fp << "\t" << generate_verilog_port(VERILOG_PORT_REG, outreg_port) << ";" << std::endl; + + /* Print the internal logics */ + fp << "\t" << "always @("; + fp << generate_verilog_port(VERILOG_PORT_CONKT, blb_port); + fp << ", "; + fp << generate_verilog_port(VERILOG_PORT_CONKT, wl_port); + fp << ")"; + fp << " begin" << std::endl; + + /* Only when the last bit of wl is enabled, + * the propagating path can be changed + * (RRAM value can be changed) */ + fp << "\t\t" << "if ("; + BasicPort set_enable_port(wl_port.get_name(), wl_port.get_width() - 1, wl_port.get_width() - 1); + fp << generate_verilog_port(VERILOG_PORT_CONKT, set_enable_port); + /* We need two config-enable ports: prog_EN and prog_ENb */ + bool find_prog_EN = false; + bool find_prog_ENb = false; + for (const auto& port : circuit_lib.model_global_ports(circuit_model, true)) { + /* Bypass non-config-enable ports */ + if (false == circuit_lib.port_is_config_enable(port)) { + continue; + } + /* Reach here, the port should be is_config_enable */ + /* Create a port object */ + fp << " && "; + BasicPort prog_en_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port)); + if ( 0 == circuit_lib.port_default_value(port)) { + /* Default value = 0 means that this is a prog_EN port */ + fp << generate_verilog_port(VERILOG_PORT_CONKT, prog_en_port); + find_prog_EN = true; + } else { + VTR_ASSERT ( 1 == circuit_lib.port_default_value(port)); + /* Default value = 1 means that this is a prog_ENb port, add inversion in the if condition */ + fp << "(~" << generate_verilog_port(VERILOG_PORT_CONKT, prog_en_port) << ")"; + find_prog_ENb = true; + } + } + /* Check if we find any config_enable signals */ + if (false == find_prog_EN) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Unable to find a config_enable signal with default value 0 for a RRAM MUX '%s'!\n", + circuit_lib.model_name(circuit_model).c_str()); + exit(1); + } + if (false == find_prog_ENb) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Unable to find a config_enable signal with default value 1 for a RRAM MUX '%s'!\n", + circuit_lib.model_name(circuit_model).c_str()); + exit(1); + } + + /* Finish the if clause */ + fp << ") begin" << std::endl; + + for (const auto& mux_input : mux_graph.inputs()) { + /* First if clause need tabs */ + if ( 0 == size_t(mux_graph.input_id(mux_input)) ) { + fp << "\t\t\t"; + } + fp << "if (1 == "; + /* Create a temp port of a BLB bit */ + BasicPort cur_blb_port(blb_port.get_name(), size_t(mux_graph.input_id(mux_input)), size_t(mux_graph.input_id(mux_input))); + fp << generate_verilog_port(VERILOG_PORT_CONKT, cur_blb_port); + fp << ") begin" << std::endl; + fp << "\t\t\t\t" << "assign "; + fp << outreg_port.get_name(); + fp << " = " << size_t(mux_graph.input_id(mux_input)) << ";" << std::endl; + fp << "\t\t\t" << "end else "; + } + fp << "begin" << std::endl; + fp << "\t\t\t\t" << "assign "; + fp << outreg_port.get_name(); + fp << " = 0;" << std::endl; + fp << "\t\t\t" << "end" << std::endl; + fp << "\t\t" << "end" << std::endl; + fp << "\t" << "end" << std::endl; + + fp << "\t" << "assign "; + fp << generate_verilog_port(VERILOG_PORT_CONKT, output_port); + fp << " = "; + fp << input_port.get_name() << "["; + fp << outreg_port.get_name(); + fp << "];" << std::endl; +} + +/********************************************************************* + * Generate Verilog codes modeling an branch circuit + * for a RRAM-based multiplexer with the given size + * Support structural and behavioral Verilog codes + *********************************************************************/ +static +void generate_verilog_rram_mux_branch_module(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& circuit_model, + const std::string& module_name, + const MuxGraph& mux_graph, + const bool& use_structural_verilog) { + /* Make sure we have a valid file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Get the input ports from the mux */ + std::vector mux_input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + /* Get the output ports from the mux */ + std::vector mux_output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + /* Get the BL and WL ports from the mux */ + std::vector mux_blb_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_BLB, true); + std::vector mux_wl_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_WL, true); + + /* Generate the Verilog netlist according to the mux_graph */ + /* Find out the number of inputs */ + size_t num_inputs = mux_graph.num_inputs(); + /* Find out the number of outputs */ + size_t num_outputs = mux_graph.num_outputs(); + /* Find out the number of memory bits */ + size_t num_mems = mux_graph.num_memory_bits(); + + /* Check codes to ensure the port of Verilog netlists will match */ + /* MUX graph must have only 1 output */ + VTR_ASSERT(1 == num_outputs); + /* MUX graph must have only 1 level*/ + VTR_ASSERT(1 == mux_graph.num_levels()); + /* MUX graph must have only 1 input and 1 BLB and 1 WL port */ + VTR_ASSERT(1 == mux_input_ports.size()); + VTR_ASSERT(1 == mux_output_ports.size()); + VTR_ASSERT(1 == mux_blb_ports.size()); + VTR_ASSERT(1 == mux_wl_ports.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)); + + /* Find each input port */ + BasicPort input_port(circuit_lib.port_prefix(mux_input_ports[0]), num_inputs); + + /* Find each output port */ + BasicPort output_port(circuit_lib.port_prefix(mux_output_ports[0]), num_outputs); + + /* Find RRAM programming ports, + * RRAM MUXes require one more pair of BLB and WL + * to configure the memories. See schematic for details + */ + BasicPort blb_port(circuit_lib.port_prefix(mux_blb_ports[0]), num_mems + 1); + + BasicPort wl_port(circuit_lib.port_prefix(mux_wl_ports[0]), num_mems + 1); + + /* dump module definition + ports */ + print_verilog_module_declaration(fp, module_manager, module_id); + + /* Print the internal logic in either structural or behavioral Verilog codes */ + if (true == use_structural_verilog) { + generate_verilog_rram_mux_branch_body_structural(module_manager, circuit_lib, fp, module_id, circuit_model, input_port, output_port, blb_port, wl_port, mux_graph); + } else { + generate_verilog_rram_mux_branch_body_behavioral(fp, circuit_lib, circuit_model, input_port, output_port, blb_port, wl_port, mux_graph); + } + + /* Put an end to the Verilog module */ + print_verilog_module_end(fp, module_name); +} + +/*********************************************** + * Generate Verilog codes modeling an branch circuit + * for a multiplexer with the given size + **********************************************/ +static +void generate_verilog_mux_branch_module(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& mux_model, + const size_t& mux_size, + const MuxGraph& mux_graph, + const bool& use_explicit_port_map) { + std::string module_name = generate_mux_branch_subckt_name(circuit_lib, mux_model, mux_size, mux_graph.num_inputs(), VERILOG_MUX_BASIS_POSTFIX); + + /* Multiplexers built with different technology is in different organization */ + switch (circuit_lib.design_tech_type(mux_model)) { + case CIRCUIT_MODEL_DESIGN_CMOS: + /* Skip module writing if the branch subckt is a standard cell! */ + if (true == circuit_lib.valid_model_id(circuit_lib.model(module_name))) { + /* This model must be a MUX2 gate */ + VTR_ASSERT(CIRCUIT_MODEL_GATE == circuit_lib.model_type(circuit_lib.model(module_name))); + VTR_ASSERT(CIRCUIT_MODEL_GATE_MUX2 == circuit_lib.gate_type(circuit_lib.model(module_name))); + break; + } + if (true == circuit_lib.dump_structural_verilog(mux_model)) { + /* Structural verilog can be easily generated by module writer */ + ModuleId mux_module = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); + write_verilog_module_to_file(fp, module_manager, mux_module, + use_explicit_port_map || circuit_lib.dump_explicit_port_map(mux_model)); + /* Add an empty line as a splitter */ + fp << std::endl; + } else { + /* Behavioral verilog requires customized generation */ + print_verilog_cmos_mux_branch_module_behavioral(module_manager, circuit_lib, fp, mux_model, module_name, mux_graph); + } + break; + case CIRCUIT_MODEL_DESIGN_RRAM: + generate_verilog_rram_mux_branch_module(module_manager, circuit_lib, fp, mux_model, module_name, mux_graph, + circuit_lib.dump_structural_verilog(mux_model)); + 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 the input bufferes for a multiplexer or LUT in Verilog codes + * 1. If input are required to be buffered (specified by users), + * buffers will be added to all the datapath inputs. + * 2. If input are required to NOT be buffered (specified by users), + * all the datapath inputs will be short wired to MUX inputs. + * + * For those Multiplexers or LUTs require a constant input: + * the last input of multiplexer will be wired to a constant voltage level + *******************************************************************/ +static +void generate_verilog_cmos_mux_module_input_buffers(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const ModuleId& module_id, + const CircuitModelId& circuit_model, + const MuxGraph& mux_graph) { + /* Make sure we have a valid file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Get the input ports from the mux */ + std::vector mux_input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + /* We should have only 1 input port! */ + VTR_ASSERT(1 == mux_input_ports.size()); + + /* Get the input port from MUX module */ + ModulePortId module_input_port_id = module_manager.find_module_port(module_id, circuit_lib.port_prefix(mux_input_ports[0])); + VTR_ASSERT(ModulePortId::INVALID() != module_input_port_id); + /* Get the port from module */ + BasicPort module_input_port = module_manager.module_port(module_id, module_input_port_id); + + /* Iterate over all the inputs in the MUX graph */ + for (const auto& input_node : mux_graph.inputs()) { + /* Fetch fundamental information from MUX graph w.r.t. the input node */ + MuxInputId input_index = mux_graph.input_id(input_node); + VTR_ASSERT(MuxInputId::INVALID() != input_index); + + size_t input_node_level = mux_graph.node_level(input_node); + size_t input_node_index_at_level = mux_graph.node_index_at_level(input_node); + + /* Create the port information of the MUX input, which is the input of buffer instance */ + BasicPort instance_input_port(module_input_port.get_name(), size_t(input_index), size_t(input_index)); + + /* Create the port information of the MUX graph input, which is the output of buffer instance */ + BasicPort instance_output_port(generate_mux_node_name(input_node_level, false), input_node_index_at_level, input_node_index_at_level); + + /* For last input: + * Add a constant value to the last input, if this MUX needs a constant input + */ + if ( (MuxInputId(mux_graph.num_inputs() - 1) == mux_graph.input_id(input_node)) + && (true == circuit_lib.mux_add_const_input(circuit_model)) ) { + /* Get the constant input value */ + size_t const_value = circuit_lib.mux_const_input_value(circuit_model); + VTR_ASSERT( (0 == const_value) || (1 == const_value) ); + /* For the output of the buffer instance: + * Get the last inputs from the MUX graph and generate the node name in MUX module. + */ + print_verilog_comment(fp, std::string("---- BEGIN short-wire a multiplexing structure input to a constant value -----")); + print_verilog_wire_constant_values(fp, instance_output_port, std::vector(1, const_value)); + print_verilog_comment(fp, std::string("---- END short-wire a multiplexing structure input to a constant value -----")); + fp << std::endl; + continue; /* Finish here */ + } + + /* If the inputs are not supposed to be buffered */ + if (false == circuit_lib.is_input_buffered(circuit_model)) { + print_verilog_comment(fp, std::string("---- BEGIN short-wire a multiplexing structure input to MUX module input -----")); + + /* Short wire all the datapath inputs to the MUX inputs */ + print_verilog_wire_connection(fp, instance_output_port, instance_input_port, false); + + print_verilog_comment(fp, std::string("---- END short-wire a multiplexing structure input to MUX module input -----")); + fp << std::endl; + continue; /* Finish here */ + } + + /* Reach here, we need a buffer, create a port-to-port map and output the buffer instance */ + print_verilog_comment(fp, std::string("---- BEGIN Instanciation of an input buffer module -----")); + + /* Now we need to add intermediate buffers by instanciating the modules */ + CircuitModelId buffer_model = circuit_lib.input_buffer_model(circuit_model); + /* We must have a valid model id */ + VTR_ASSERT(CircuitModelId::INVALID() != buffer_model); + + print_verilog_buffer_instance(fp, module_manager, circuit_lib, module_id, buffer_model, instance_input_port, instance_output_port); + + print_verilog_comment(fp, std::string("---- END Instanciation of an input buffer module -----")); + fp << std::endl; + } +} + +/******************************************************************** + * Generate the output bufferes for a multiplexer or LUT in Verilog codes + * 1. If output are required to be buffered (specified by users), + * buffers will be added to all the outputs. + * 2. If output are required to NOT be buffered (specified by users), + * all the outputs will be short wired to MUX outputs. + *******************************************************************/ +static +void generate_verilog_cmos_mux_module_output_buffers(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const ModuleId& module_id, + const CircuitModelId& circuit_model, + const MuxGraph& mux_graph) { + /* Make sure we have a valid file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Get the output ports from the mux */ + std::vector mux_output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + + /* Iterate over all the outputs in the MUX module */ + for (const auto& output_port : mux_output_ports) { + /* Get the output port from MUX module */ + ModulePortId module_output_port_id = module_manager.find_module_port(module_id, circuit_lib.port_prefix(output_port)); + VTR_ASSERT(ModulePortId::INVALID() != module_output_port_id); + /* Get the port from module */ + BasicPort module_output_port = module_manager.module_port(module_id, module_output_port_id); + + /* Iterate over each pin of the output port */ + for (const auto& pin : circuit_lib.pins(output_port)) { + /* Fetch fundamental information from MUX graph w.r.t. the input node */ + /* Deposite the last level of the graph, which is a default value */ + size_t output_node_level = mux_graph.num_node_levels() - 1; + /* If there is a fracturable level specified for the output, we find the exact level */ + if (size_t(-1) != circuit_lib.port_lut_frac_level(output_port)) { + output_node_level = circuit_lib.port_lut_frac_level(output_port); + } + /* Deposite a zero, which is a default value */ + size_t output_node_index_at_level = 0; + /* If there are output masks, we find the node_index */ + if (!circuit_lib.port_lut_output_mask(output_port).empty()) { + output_node_index_at_level = circuit_lib.port_lut_output_mask(output_port).at(pin); + } + /* Double check the node exists in the Mux Graph */ + VTR_ASSERT(MuxNodeId::INVALID() != mux_graph.node_id(output_node_level, output_node_index_at_level)); + + /* Create the port information of the MUX input, which is the input of buffer instance */ + BasicPort instance_input_port(generate_mux_node_name(output_node_level, false), output_node_index_at_level, output_node_index_at_level); + + /* Create the port information of the module output at the given pin range, which is the output of buffer instance */ + BasicPort instance_output_port(module_output_port.get_name(), pin, pin); + + /* If the output is not supposed to be buffered */ + if (false == circuit_lib.is_output_buffered(circuit_model)) { + print_verilog_comment(fp, std::string("---- BEGIN short-wire a multiplexing structure output to MUX module output -----")); + + /* Short wire all the datapath inputs to the MUX inputs */ + print_verilog_wire_connection(fp, instance_output_port, instance_input_port, false); + + print_verilog_comment(fp, std::string("---- END short-wire a multiplexing structure output to MUX module output -----")); + fp << std::endl; + continue; /* Finish here */ + } + + /* Reach here, we need a buffer, create a port-to-port map and output the buffer instance */ + print_verilog_comment(fp, std::string("---- BEGIN Instanciation of an output buffer module -----")); + + /* Now we need to add intermediate buffers by instanciating the modules */ + CircuitModelId buffer_model = circuit_lib.output_buffer_model(circuit_model); + /* We must have a valid model id */ + VTR_ASSERT(CircuitModelId::INVALID() != buffer_model); + + print_verilog_buffer_instance(fp, module_manager, circuit_lib, module_id, buffer_model, instance_input_port, instance_output_port); + + print_verilog_comment(fp, std::string("---- END Instanciation of an output buffer module -----")); + fp << std::endl; + } + } +} + +/******************************************************************** + * Generate the 4T1R-based internal logic + * (multiplexing structure) for a multiplexer in Verilog codes + * This function will : + * 1. build a multiplexing structure by instanciating the branch circuits + * generated before + * 2. add intermediate buffers between multiplexing stages if specified. + *******************************************************************/ +static +void generate_verilog_rram_mux_module_multiplexing_structure(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const ModuleId& module_id, + const CircuitModelId& circuit_model, + const MuxGraph& mux_graph) { + /* Make sure we have a valid file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Find the actual mux size */ + size_t mux_size = find_mux_num_datapath_inputs(circuit_lib, circuit_model, mux_graph.num_inputs()); + + /* Get the BL and WL ports from the mux */ + std::vector mux_blb_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_BLB, true); + std::vector mux_wl_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_WL, true); + /* MUX graph must have only 1 BLB and 1 WL port */ + VTR_ASSERT(1 == mux_blb_ports.size()); + VTR_ASSERT(1 == mux_wl_ports.size()); + + /* Build the location map of intermediate buffers */ + std::vector inter_buffer_location_map = build_mux_intermediate_buffer_location_map(circuit_lib, circuit_model, mux_graph.num_node_levels()); + + print_verilog_comment(fp, std::string("---- BEGIN Internal Logic of a RRAM-based MUX module -----")); + + print_verilog_comment(fp, std::string("---- BEGIN Internal wires of a RRAM-based MUX module -----")); + /* Print local wires which are the nodes in the mux graph */ + for (size_t level = 0; level < mux_graph.num_levels(); ++level) { + /* Print the internal wires located at this level */ + BasicPort internal_wire_port(generate_mux_node_name(level, false), mux_graph.num_nodes_at_level(level)); + fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, internal_wire_port) << ";" << std::endl; + /* Identify if an intermediate buffer is needed */ + if (false == inter_buffer_location_map[level]) { + continue; + } + BasicPort internal_wire_buffered_port(generate_mux_node_name(level, true), mux_graph.num_nodes_at_level(level)); + fp << "\t" << generate_verilog_port(VERILOG_PORT_WIRE, internal_wire_buffered_port) << std::endl; + } + print_verilog_comment(fp, std::string("---- END Internal wires of a RRAM-based MUX module -----")); + fp << std::endl; + + /* Iterate over all the internal nodes and output nodes in the mux graph */ + for (const auto& node : mux_graph.non_input_nodes()) { + print_verilog_comment(fp, std::string("---- BEGIN Instanciation of a branch RRAM-based MUX module -----")); + /* Get the size of branch circuit + * Instanciate an branch circuit by the size (fan-in) of the node + */ + size_t branch_size = mux_graph.node_in_edges(node).size(); + + /* Get the node level and index in the current level */ + size_t output_node_level = mux_graph.node_level(node); + size_t output_node_index_at_level = mux_graph.node_index_at_level(node); + + /* Get the nodes which drive the root_node */ + std::vector input_nodes; + for (const auto& edge : mux_graph.node_in_edges(node)) { + /* Get the nodes drive the edge */ + for (const auto& src_node : mux_graph.edge_src_nodes(edge)) { + input_nodes.push_back(src_node); + } + } + /* Number of inputs should match the branch_input_size!!! */ + VTR_ASSERT(input_nodes.size() == branch_size); + + /* Get the mems in the branch circuits */ + std::vector mems; + for (const auto& edge : mux_graph.node_in_edges(node)) { + /* Get the mem control the edge */ + MuxMemId mem = mux_graph.find_edge_mem(edge); + /* Add the mem if it is not in the list */ + if (mems.end() == std::find(mems.begin(), mems.end(), mem)) { + mems.push_back(mem); + } + } + + /* Instanciate the branch module which is a tgate-based module + */ + std::string branch_module_name= generate_mux_branch_subckt_name(circuit_lib, circuit_model, mux_size, branch_size, VERILOG_MUX_BASIS_POSTFIX); + /* Get the moduleId for the submodule */ + ModuleId branch_module_id = module_manager.find_module(branch_module_name); + /* We must have one */ + VTR_ASSERT(ModuleId::INVALID() != branch_module_id); + + /* Create a port-to-port map */ + std::map port2port_name_map; + /* TODO: the branch module name should NOT be hard-coded. Use the port lib_name given by users! */ + + /* All the input node names organized in bus */ + std::vector branch_node_input_ports; + for (const auto& input_node : input_nodes) { + /* Generate the port info of each input node */ + size_t input_node_level = mux_graph.node_level(input_node); + size_t input_node_index_at_level = mux_graph.node_index_at_level(input_node); + BasicPort branch_node_input_port(generate_mux_node_name(input_node_level, inter_buffer_location_map[input_node_level]), input_node_index_at_level, input_node_index_at_level); + branch_node_input_ports.push_back(branch_node_input_port); + } + + /* Create the port info for the input */ + /* TODO: the naming could be more flexible? */ + BasicPort instance_input_port = generate_verilog_bus_port(branch_node_input_ports, std::string(generate_mux_node_name(output_node_level, false) + "_in")); + /* If we have more than 1 port in the combined instance ports , + * output a local wire */ + if (1 < combine_verilog_ports(branch_node_input_ports).size()) { + /* Print a local wire for the merged ports */ + fp << "\t" << generate_verilog_local_wire(instance_input_port, branch_node_input_ports) << std::endl; + } else { + /* Safety check */ + VTR_ASSERT(1 == combine_verilog_ports(branch_node_input_ports).size()); + } + + /* Link nodes to input ports for the branch module */ + ModulePortId module_input_port_id = module_manager.find_module_port(branch_module_id, "in"); + VTR_ASSERT(ModulePortId::INVALID() != module_input_port_id); + /* Get the port from module */ + BasicPort module_input_port = module_manager.module_port(branch_module_id, module_input_port_id); + port2port_name_map[module_input_port.get_name()] = instance_input_port; + + /* Link nodes to output ports for the branch module */ + BasicPort instance_output_port(generate_mux_node_name(output_node_level, false), output_node_index_at_level, output_node_index_at_level); + ModulePortId module_output_port_id = module_manager.find_module_port(branch_module_id, "out"); + VTR_ASSERT(ModulePortId::INVALID() != module_output_port_id); + /* Get the port from module */ + BasicPort module_output_port = module_manager.module_port(branch_module_id, module_output_port_id); + port2port_name_map[module_output_port.get_name()] = instance_output_port; + + /* All the mem node names organized in bus + * RRAM-based MUX uses BLB and WL to control memories + */ + std::vector branch_node_blb_ports; + for (const auto& mem : mems) { + /* Generate the port info of each mem node: + */ + BasicPort branch_node_blb_port(circuit_lib.port_prefix(mux_blb_ports[0]), size_t(mem), size_t(mem)); + branch_node_blb_ports.push_back(branch_node_blb_port); + } + /* Every stage, we have an additonal BLB and WL in controlling purpose + * The additional BLB is arranged at the tail of BLB port + * For example: + * The total port width is BLB[0 ... + - 1] + * The regular BLB used by branches are BLB[0 .. - 1] + * The additional BLB used by branches are BLB[ .. + - 1] + * + * output_node_level is always larger than the mem_level by 1 + */ + branch_node_blb_ports.push_back(BasicPort(circuit_lib.port_prefix(mux_blb_ports[0]), + mux_graph.num_memory_bits() + output_node_level - 1, + mux_graph.num_memory_bits() + output_node_level - 1) + ); + + /* Create the port info for the input */ + /* TODO: the naming could be more flexible? */ + BasicPort instance_blb_port = generate_verilog_bus_port(branch_node_blb_ports, std::string(generate_mux_node_name(output_node_level, false) + "_blb")); + /* If we have more than 1 port in the combined instance ports , + * output a local wire */ + if (1 < combine_verilog_ports(branch_node_blb_ports).size()) { + /* Print a local wire for the merged ports */ + fp << "\t" << generate_verilog_local_wire(instance_blb_port, branch_node_blb_ports) << std::endl; + } else { + /* Safety check */ + VTR_ASSERT(1 == combine_verilog_ports(branch_node_blb_ports).size()); + } + + /* Link nodes to BLB ports for the branch module */ + ModulePortId module_blb_port_id = module_manager.find_module_port(branch_module_id, circuit_lib.port_prefix(mux_blb_ports[0])); + VTR_ASSERT(ModulePortId::INVALID() != module_blb_port_id); + /* Get the port from module */ + BasicPort module_blb_port = module_manager.module_port(branch_module_id, module_blb_port_id); + port2port_name_map[module_blb_port.get_name()] = instance_blb_port; + + std::vector branch_node_wl_ports; + for (const auto& mem : mems) { + /* Generate the port info of each mem node: + */ + BasicPort branch_node_blb_port(circuit_lib.port_prefix(mux_wl_ports[0]), size_t(mem), size_t(mem)); + branch_node_wl_ports.push_back(branch_node_blb_port); + } + /* Every stage, we have an additonal BLB and WL in controlling purpose + * The additional BLB is arranged at the tail of BLB port + * For example: + * The total port width is WL[0 ... + - 1] + * The regular BLB used by branches are WL[0 .. - 1] + * The additional BLB used by branches are WL[ .. + - 1] + * + * output_node_level is always larger than the mem_level by 1 + */ + branch_node_wl_ports.push_back(BasicPort(circuit_lib.port_prefix(mux_wl_ports[0]), + mux_graph.num_memory_bits() + output_node_level - 1, + mux_graph.num_memory_bits() + output_node_level - 1) + ); + + /* Create the port info for the WL */ + /* TODO: the naming could be more flexible? */ + BasicPort instance_wl_port = generate_verilog_bus_port(branch_node_wl_ports, std::string(generate_mux_node_name(output_node_level, false) + "_wl")); + /* If we have more than 1 port in the combined instance ports , + * output a local wire */ + if (1 < combine_verilog_ports(branch_node_wl_ports).size()) { + /* Print a local wire for the merged ports */ + fp << "\t" << generate_verilog_local_wire(instance_wl_port, branch_node_wl_ports) << std::endl; + } else { + /* Safety check */ + VTR_ASSERT(1 == combine_verilog_ports(branch_node_wl_ports).size()); + } + + /* Link nodes to BLB ports for the branch module */ + ModulePortId module_wl_port_id = module_manager.find_module_port(branch_module_id, circuit_lib.port_prefix(mux_wl_ports[0])); + VTR_ASSERT(ModulePortId::INVALID() != module_wl_port_id); + /* Get the port from module */ + BasicPort module_wl_port = module_manager.module_port(branch_module_id, module_wl_port_id); + port2port_name_map[module_wl_port.get_name()] = instance_wl_port; + + /* Output an instance of the module */ + print_verilog_module_instance(fp, module_manager, module_id, branch_module_id, port2port_name_map, circuit_lib.dump_explicit_port_map(circuit_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(module_id, branch_module_id); + + print_verilog_comment(fp, std::string("---- END Instanciation of a branch RRAM-based MUX module -----")); + fp << std::endl; + + if (false == inter_buffer_location_map[output_node_level]) { + continue; /* No need for intermediate buffers */ + } + + print_verilog_comment(fp, std::string("---- BEGIN Instanciation of an intermediate buffer modules -----")); + + /* Now we need to add intermediate buffers by instanciating the modules */ + CircuitModelId buffer_model = circuit_lib.lut_intermediate_buffer_model(circuit_model); + /* We must have a valid model id */ + VTR_ASSERT(CircuitModelId::INVALID() != buffer_model); + + BasicPort buffer_instance_input_port(generate_mux_node_name(output_node_level, false), output_node_index_at_level, output_node_index_at_level); + BasicPort buffer_instance_output_port(generate_mux_node_name(output_node_level, true), output_node_index_at_level, output_node_index_at_level); + + print_verilog_buffer_instance(fp, module_manager, circuit_lib, module_id, buffer_model, buffer_instance_input_port, buffer_instance_output_port); + + print_verilog_comment(fp, std::string("---- END Instanciation of an intermediate buffer module -----")); + fp << std::endl; + } + + print_verilog_comment(fp, std::string("---- END Internal Logic of a RRAM-based MUX module -----")); + fp << std::endl; +} + +/********************************************************************* + * Generate Verilog codes modeling a RRAM-based multiplexer with the given size + * The Verilog module will consist of three parts: + * 1. instances of the branch circuits of multiplexers which are generated before + * This builds up the 4T1R-based multiplexing structure + * + * BLB WL + * | | ... + * v v + * +--------+ + * in[0]-->| | BLB WL + * ...| Branch |-----+ | | + * in -->| 0 | | v v + * [N-1] +--------+ | +--------+ + * ... -->| | + * BLBs WLs ...| Branch | + * | | ... -->| X | + * v v +--------+ + * +--------+ | + * -->| | | + * ...| Branch |----+ + * -->| i | + * +--------+ + * + * 2. Input buffers/inverters + * 3. Output buffers/inverters + *********************************************************************/ +static +void generate_verilog_rram_mux_module(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& circuit_model, + const std::string& module_name, + const MuxGraph& mux_graph) { + /* Error out for the conditions where we are not yet supported! */ + if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(circuit_model)) { + /* RRAM LUT is not supported now... */ + VTR_LOGF_ERROR(__FILE__, __LINE__, + "RRAM-based LUT is not supported for circuit model '%s'!\n", + circuit_lib.model_name(circuit_model).c_str()); + exit(1); + } + + /* Get the global ports required by MUX (and any submodules) */ + std::vector mux_global_ports = circuit_lib.model_global_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true, true); + /* Get the input ports from the mux */ + std::vector mux_input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true); + /* Get the output ports from the mux */ + std::vector mux_output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + /* Get the BL and WL ports from the mux */ + std::vector mux_blb_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_BLB, true); + std::vector mux_wl_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_WL, true); + + /* Make sure we have a valid file handler*/ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Generate the Verilog netlist according to the mux_graph */ + /* Find out the number of data-path inputs */ + size_t num_inputs = find_mux_num_datapath_inputs(circuit_lib, circuit_model, mux_graph.num_inputs()); + /* Find out the number of outputs */ + size_t num_outputs = mux_graph.num_outputs(); + /* Find out the number of memory bits */ + size_t num_mems = mux_graph.num_memory_bits(); + + /* Check codes to ensure the port of Verilog netlists will match */ + /* MUX graph must have only 1 input and 1 BLB and 1 WL port */ + VTR_ASSERT(1 == mux_input_ports.size()); + VTR_ASSERT(1 == mux_blb_ports.size()); + VTR_ASSERT(1 == mux_wl_ports.size()); + + /* Create a Verilog Module based on the circuit model, and add to module manager */ + ModuleId module_id = module_manager.add_module(module_name); + VTR_ASSERT(ModuleId::INVALID() != module_id); + /* Add module ports */ + /* Add each global port */ + for (const auto& port : mux_global_ports) { + /* Configure each global port */ + BasicPort global_port(circuit_lib.port_prefix(port), circuit_lib.port_size(port)); + module_manager.add_port(module_id, global_port, ModuleManager::MODULE_GLOBAL_PORT); + } + /* Add each input port */ + size_t input_port_cnt = 0; + for (const auto& port : mux_input_ports) { + BasicPort input_port(circuit_lib.port_prefix(port), num_inputs); + module_manager.add_port(module_id, input_port, ModuleManager::MODULE_INPUT_PORT); + /* Update counter */ + input_port_cnt++; + } + /* Double check: We should have only 1 input port generated here! */ + VTR_ASSERT(1 == input_port_cnt); + + for (const auto& port : mux_output_ports) { + BasicPort output_port(circuit_lib.port_prefix(port), num_outputs); + if (CIRCUIT_MODEL_LUT == circuit_lib.model_type(circuit_model)) { + output_port.set_width(circuit_lib.port_size(port)); + } + module_manager.add_port(module_id, output_port, ModuleManager::MODULE_OUTPUT_PORT); + } + + /* BLB port */ + for (const auto& port : mux_blb_ports) { + /* IMPORTANT: RRAM-based MUX has an additional BLB pin per level + * So, the actual port width of BLB should be added by the number of levels of the MUX graph + */ + BasicPort blb_port(circuit_lib.port_prefix(port), num_mems + mux_graph.num_levels()); + module_manager.add_port(module_id, blb_port, ModuleManager::MODULE_INPUT_PORT); + } + + /* WL port */ + for (const auto& port : mux_wl_ports) { + /* IMPORTANT: RRAM-based MUX has an additional WL pin per level + * So, the actual port width of WL should be added by the number of levels of the MUX graph + */ + BasicPort wl_port(circuit_lib.port_prefix(port), num_mems + mux_graph.num_levels()); + module_manager.add_port(module_id, wl_port, ModuleManager::MODULE_INPUT_PORT); + } + + /* dump module definition + ports */ + print_verilog_module_declaration(fp, module_manager, module_id); + + /* TODO: Print the internal logic in Verilog codes */ + generate_verilog_rram_mux_module_multiplexing_structure(module_manager, circuit_lib, fp, module_id, circuit_model, mux_graph); + + /* Print the input and output buffers in Verilog codes */ + /* TODO, we should rename the follow functions to a generic name? Since they are applicable to both MUXes */ + generate_verilog_cmos_mux_module_input_buffers(module_manager, circuit_lib, fp, module_id, circuit_model, mux_graph); + generate_verilog_cmos_mux_module_output_buffers(module_manager, circuit_lib, fp, module_id, circuit_model, mux_graph); + + /* Put an end to the Verilog module */ + print_verilog_module_end(fp, module_name); +} + + +/*********************************************** + * Generate Verilog codes modeling a multiplexer + * with the given graph-level description + **********************************************/ +static +void generate_verilog_mux_module(ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& mux_model, + const MuxGraph& mux_graph, + const bool& use_explicit_port_map) { + 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("")); + + /* Multiplexers built with different technology is in different organization */ + switch (circuit_lib.design_tech_type(mux_model)) { + case CIRCUIT_MODEL_DESIGN_CMOS: { + /* Use Verilog writer to print the module to file */ + ModuleId mux_module = module_manager.find_module(module_name); + VTR_ASSERT(true == module_manager.valid_module_id(mux_module)); + write_verilog_module_to_file(fp, module_manager, mux_module, + ( use_explicit_port_map + || circuit_lib.dump_explicit_port_map(mux_model) + || circuit_lib.dump_explicit_port_map(circuit_lib.pass_gate_logic_model(mux_model)) ) + ); + /* Add an empty line as a splitter */ + fp << std::endl; + break; + } + case CIRCUIT_MODEL_DESIGN_RRAM: + /* TODO: RRAM-based Multiplexer Verilog module generation */ + generate_verilog_rram_mux_module(module_manager, circuit_lib, fp, mux_model, module_name, mux_graph); + 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 all the unique + * multiplexers in the FPGA device + **********************************************/ +void print_verilog_submodule_muxes(ModuleManager& module_manager, + std::vector& 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) { + + std::string verilog_fname(submodule_dir + std::string(MUXES_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 Multiplexers '%s' ...\n", + verilog_fname.c_str()); + + print_verilog_file_header(fp, "Multiplexers"); + + print_verilog_include_defines_preproc_file(fp, verilog_dir); + + /* Generate basis sub-circuit for unique branches shared by the multiplexers */ + for (auto mux : mux_lib.muxes()) { + const MuxGraph& mux_graph = mux_lib.mux_graph(mux); + CircuitModelId mux_circuit_model = mux_lib.mux_circuit_model(mux); + /* Create a mux graph for the branch circuit */ + std::vector branch_mux_graphs = mux_graph.build_mux_branch_graphs(); + /* Create branch circuits, which are N:1 one-level or 2:1 tree-like MUXes */ + for (auto branch_mux_graph : branch_mux_graphs) { + generate_verilog_mux_branch_module(module_manager, circuit_lib, fp, mux_circuit_model, + find_mux_num_datapath_inputs(circuit_lib, mux_circuit_model, mux_graph.num_inputs()), + branch_mux_graph, use_explicit_port_map); + } + } + + /* Generate unique Verilog modules for the multiplexers */ + for (auto mux : mux_lib.muxes()) { + const MuxGraph& mux_graph = mux_lib.mux_graph(mux); + CircuitModelId mux_circuit_model = mux_lib.mux_circuit_model(mux); + /* Create MUX circuits */ + generate_verilog_mux_module(module_manager, circuit_lib, fp, mux_circuit_model, mux_graph, use_explicit_port_map); + } + + /* 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_mux.h b/openfpga/src/fpga_verilog/verilog_mux.h new file mode 100644 index 000000000..16e6c2f2a --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_mux.h @@ -0,0 +1,32 @@ +#ifndef VERILOG_MUX_H +#define VERILOG_MUX_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +#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& 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 From 105ccabecc4ae7ae0d1b5a9391653afcae6593e2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 12:41:43 -0700 Subject: [PATCH 06/13] adapt memroy writer for verilog --- openfpga/src/fpga_verilog/verilog_constants.h | 1 + openfpga/src/fpga_verilog/verilog_memory.cpp | 195 ++++++++++++++++++ openfpga/src/fpga_verilog/verilog_memory.h | 31 +++ 3 files changed, 227 insertions(+) create mode 100644 openfpga/src/fpga_verilog/verilog_memory.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_memory.h diff --git a/openfpga/src/fpga_verilog/verilog_constants.h b/openfpga/src/fpga_verilog/verilog_constants.h index 49a73bf6d..91f87c475 100644 --- a/openfpga/src/fpga_verilog/verilog_constants.h +++ b/openfpga/src/fpga_verilog/verilog_constants.h @@ -42,5 +42,6 @@ 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"; #endif diff --git a/openfpga/src/fpga_verilog/verilog_memory.cpp b/openfpga/src/fpga_verilog/verilog_memory.cpp new file mode 100644 index 000000000..743647d44 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_memory.cpp @@ -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 +#include + +/* 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(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(ModuleManager& module_manager, + std::vector& 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 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 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_memory.h b/openfpga/src/fpga_verilog/verilog_memory.h new file mode 100644 index 000000000..774e6feb9 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_memory.h @@ -0,0 +1,31 @@ +#ifndef VERILOG_MEMORY_H +#define VERILOG_MEMORY_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#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(ModuleManager& module_manager, + std::vector& 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 From 5cc68b07301860d559c7a090afdb4cf43ed9a7b2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 12:45:58 -0700 Subject: [PATCH 07/13] adapt LUT Verilog writer --- openfpga/src/fpga_verilog/verilog_lut.cpp | 78 +++++++++++++++++++++++ openfpga/src/fpga_verilog/verilog_lut.h | 29 +++++++++ 2 files changed, 107 insertions(+) create mode 100644 openfpga/src/fpga_verilog/verilog_lut.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_lut.h diff --git a/openfpga/src/fpga_verilog/verilog_lut.cpp b/openfpga/src/fpga_verilog/verilog_lut.cpp new file mode 100644 index 000000000..900ef3073 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_lut.cpp @@ -0,0 +1,78 @@ +/******************************************************************** + * This file includes functions to generate Verilog submodules for LUTs + ********************************************************************/ +#include +#include + +/* 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(ModuleManager& module_manager, + std::vector& 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_lut.h b/openfpga/src/fpga_verilog/verilog_lut.h new file mode 100644 index 000000000..f5d1345dc --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_lut.h @@ -0,0 +1,29 @@ +#ifndef VERILOG_LUT_H +#define VERILOG_LUT_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +#include "circuit_library.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_verilog_submodule_luts(ModuleManager& module_manager, + std::vector& 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 From 99c3712b6f649da9fea727b7df3ca01765aec98a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 12:59:37 -0700 Subject: [PATCH 08/13] adapt Verilog wire module writer --- openfpga/src/fpga_verilog/verilog_wire.cpp | 136 +++++++++++++++++++++ openfpga/src/fpga_verilog/verilog_wire.h | 28 +++++ 2 files changed, 164 insertions(+) create mode 100644 openfpga/src/fpga_verilog/verilog_wire.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_wire.h diff --git a/openfpga/src/fpga_verilog/verilog_wire.cpp b/openfpga/src/fpga_verilog/verilog_wire.cpp new file mode 100644 index 000000000..2ac9e7ffa --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_wire.cpp @@ -0,0 +1,136 @@ +/*********************************************** + * This file includes functions to generate + * Verilog submodules for wires. + **********************************************/ +#include +#include + +/* 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(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 input_ports = circuit_lib.model_ports_by_type(wire_model, CIRCUIT_MODEL_PORT_INPUT, true); + std::vector output_ports = circuit_lib.model_ports_by_type(wire_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + std::vector 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(ModuleManager& module_manager, + std::vector& 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_wire.h b/openfpga/src/fpga_verilog/verilog_wire.h new file mode 100644 index 000000000..7d22ae4c8 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_wire.h @@ -0,0 +1,28 @@ +#ifndef VERILOG_WIRE_H +#define VERILOG_WIRE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +#include "circuit_library.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_verilog_submodule_wires(ModuleManager& module_manager, + std::vector& netlist_names, + const CircuitLibrary& circuit_lib, + const std::string& verilog_dir, + const std::string& submodule_dir); + +} /* end namespace openfpga */ + +#endif From c6c3ef71f39654af33522288873dc4bcb440a92f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 13:35:18 -0700 Subject: [PATCH 09/13] adapt all the Verilog submodule writers and bring it onlien --- .../libopenfpgautil/src/openfpga_digest.cpp | 2 +- openfpga/src/base/openfpga_verilog.cpp | 10 +- .../src/base/openfpga_verilog_command.cpp | 9 ++ openfpga/src/fpga_verilog/verilog_api.cpp | 16 ++-- openfpga/src/fpga_verilog/verilog_api.h | 2 +- openfpga/src/fpga_verilog/verilog_mux.cpp | 2 +- openfpga/src/fpga_verilog/verilog_options.cpp | 25 +++++ openfpga/src/fpga_verilog/verilog_options.h | 6 ++ .../src/fpga_verilog/verilog_submodule.cpp | 96 +++++++++++++++++++ openfpga/src/fpga_verilog/verilog_submodule.h | 27 ++++++ .../fpga_verilog/verilog_submodule_utils.cpp | 6 ++ .../src/fpga_verilog/verilog_writer_utils.cpp | 6 ++ openfpga/test_script/s298_k6_frac.openfpga | 2 +- 13 files changed, 198 insertions(+), 11 deletions(-) create mode 100644 openfpga/src/fpga_verilog/verilog_submodule.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_submodule.h diff --git a/libopenfpga/libopenfpgautil/src/openfpga_digest.cpp b/libopenfpga/libopenfpgautil/src/openfpga_digest.cpp index 3340ea8b1..4714700e4 100644 --- a/libopenfpga/libopenfpgautil/src/openfpga_digest.cpp +++ b/libopenfpga/libopenfpgautil/src/openfpga_digest.cpp @@ -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; } diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index 2612fe76a..018fe1249 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -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,10 +40,14 @@ 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, diff --git a/openfpga/src/base/openfpga_verilog_command.cpp b/openfpga/src/base/openfpga_verilog_command.cpp index fa3ff9c4b..7eb7674b9 100644 --- a/openfpga/src/base/openfpga_verilog_command.cpp +++ b/openfpga/src/base/openfpga_verilog_command.cpp @@ -34,6 +34,15 @@ void add_openfpga_verilog_commands(openfpga::Shell& 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"); diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 71ec0708d..baa18abb4 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -15,11 +15,9 @@ #include "device_rr_gsb.h" #include "verilog_constants.h" #include "verilog_auxiliary_netlists.h" -//#include "verilog_submodules.h" +#include "verilog_submodule.h" //#include "verilog_routing.h" -//#include "verilog_submodules.h" //#include "verilog_grid.h" -//#include "verilog_routing.h" //#include "verilog_top_module.h" /* Header file for this source file */ @@ -40,8 +38,13 @@ 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, @@ -81,8 +84,9 @@ 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) { diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index a24c0a1e8..569650f2a 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -22,7 +22,7 @@ /* 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, diff --git a/openfpga/src/fpga_verilog/verilog_mux.cpp b/openfpga/src/fpga_verilog/verilog_mux.cpp index b78a9362a..1e58e3fc8 100644 --- a/openfpga/src/fpga_verilog/verilog_mux.cpp +++ b/openfpga/src/fpga_verilog/verilog_mux.cpp @@ -1240,7 +1240,7 @@ void print_verilog_submodule_muxes(ModuleManager& module_manager, 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 Multiplexers '%s' ...\n", + VTR_LOG("Writing Verilog netlist for Multiplexers '%s' ...", verilog_fname.c_str()); print_verilog_file_header(fp, "Multiplexers"); diff --git a/openfpga/src/fpga_verilog/verilog_options.cpp b/openfpga/src/fpga_verilog/verilog_options.cpp index 2539d5e51..b3b398dd7 100644 --- a/openfpga/src/fpga_verilog/verilog_options.cpp +++ b/openfpga/src/fpga_verilog/verilog_options.cpp @@ -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; } diff --git a/openfpga/src/fpga_verilog/verilog_options.h b/openfpga/src/fpga_verilog/verilog_options.h index 182cabcf2..48e32086b 100644 --- a/openfpga/src/fpga_verilog/verilog_options.h +++ b/openfpga/src/fpga_verilog/verilog_options.h @@ -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_; }; diff --git a/openfpga/src/fpga_verilog/verilog_submodule.cpp b/openfpga/src/fpga_verilog/verilog_submodule.cpp new file mode 100644 index 000000000..c52fc2262 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_submodule.cpp @@ -0,0 +1,96 @@ +/********************************************************************* + * 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 netlist_names; + + + print_verilog_submodule_essentials(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(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(module_manager, netlist_names, circuit_lib, + verilog_dir, submodule_dir, + fpga_verilog_opts.explicit_port_mapping()); + + /* Hard wires */ + print_verilog_submodule_wires(module_manager, netlist_names, circuit_lib, + verilog_dir, submodule_dir); + + /* 4. Memories */ + print_verilog_submodule_memories(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(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 */ diff --git a/openfpga/src/fpga_verilog/verilog_submodule.h b/openfpga/src/fpga_verilog/verilog_submodule.h new file mode 100644 index 000000000..4f7cb511c --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_submodule.h @@ -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 diff --git a/openfpga/src/fpga_verilog/verilog_submodule_utils.cpp b/openfpga/src/fpga_verilog/verilog_submodule_utils.cpp index f4f02472d..b4255b3e9 100644 --- a/openfpga/src/fpga_verilog/verilog_submodule_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_submodule_utils.cpp @@ -132,6 +132,8 @@ void print_verilog_submodule_signal_init(std::fstream& fp, ********************************************************************/ 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 */ @@ -149,8 +151,12 @@ void add_user_defined_verilog_modules(ModuleManager& 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"); } /********************************************************************* diff --git a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp index 28f2ed5db..600357c36 100644 --- a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp @@ -1374,8 +1374,12 @@ void print_verilog_clock_stimuli(std::fstream& fp, void print_verilog_netlist_include_header_file(const std::vector& 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)); + VTR_LOG("Writing header file for primitive modules '%s' ...", + verilog_fname.c_str()); + /* Create the file stream */ std::fstream fp; fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); @@ -1392,6 +1396,8 @@ void print_verilog_netlist_include_header_file(const std::vector& n /* close file stream */ fp.close(); + + VTR_LOG("Done\n"); } } /* end namespace openfpga */ diff --git a/openfpga/test_script/s298_k6_frac.openfpga b/openfpga/test_script/s298_k6_frac.openfpga index 1dece6d57..c01774bd3 100644 --- a/openfpga/test_script/s298_k6_frac.openfpga +++ b/openfpga/test_script/s298_k6_frac.openfpga @@ -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 From c20caa1fa385e415b7831039c01f9be98cb9c961 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 14:47:54 -0700 Subject: [PATCH 10/13] routing module Verilog writer is online --- openfpga/src/fpga_verilog/verilog_api.cpp | 24 +- openfpga/src/fpga_verilog/verilog_constants.h | 2 + openfpga/src/fpga_verilog/verilog_routing.cpp | 355 ++++++++++++++++++ openfpga/src/fpga_verilog/verilog_routing.h | 33 ++ 4 files changed, 403 insertions(+), 11 deletions(-) create mode 100644 openfpga/src/fpga_verilog/verilog_routing.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_routing.h diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index baa18abb4..aa68e0024 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -16,7 +16,7 @@ #include "verilog_constants.h" #include "verilog_auxiliary_netlists.h" #include "verilog_submodule.h" -//#include "verilog_routing.h" +#include "verilog_routing.h" //#include "verilog_grid.h" //#include "verilog_top_module.h" @@ -89,16 +89,18 @@ void fpga_fabric_verilog(ModuleManager& module_manager, 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(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(module_manager), + device_rr_gsb, + src_dir_path, rr_dir_path, + options.explicit_port_mapping()); + } /* Generate grids */ //print_verilog_grids(module_manager, diff --git a/openfpga/src/fpga_verilog/verilog_constants.h b/openfpga/src/fpga_verilog/verilog_constants.h index 91f87c475..d5e1f4edb 100644 --- a/openfpga/src/fpga_verilog/verilog_constants.h +++ b/openfpga/src/fpga_verilog/verilog_constants.h @@ -44,4 +44,6 @@ constexpr char* USER_DEFINED_TEMPLATE_VERILOG_FILE_NAME = "user_defined_template constexpr char* VERILOG_MUX_BASIS_POSTFIX = "_basis"; constexpr char* VERILOG_MEM_POSTFIX = "_mem"; +constexpr char* SB_VERILOG_FILE_NAME_PREFIX = "sb_"; + #endif diff --git a/openfpga/src/fpga_verilog/verilog_routing.cpp b/openfpga/src/fpga_verilog/verilog_routing.cpp new file mode 100644 index 000000000..0d9582556 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_routing.cpp @@ -0,0 +1,355 @@ +/********************************************************************* + * 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& 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 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& 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 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& 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 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 netlist_names; + + vtr::Point 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"); +} + + +/******************************************************************** + * 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 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"); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_routing.h b/openfpga/src/fpga_verilog/verilog_routing.h new file mode 100644 index 000000000..2a1a58a66 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_routing.h @@ -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 From e37ac8a09800ca75cad7a3c03f89cb77eea4a5f5 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 16:04:41 -0700 Subject: [PATCH 11/13] add grid module Verilog writer --- openfpga/src/base/openfpga_naming.cpp | 16 + openfpga/src/base/openfpga_naming.h | 4 + openfpga/src/base/openfpga_verilog.cpp | 3 +- openfpga/src/fpga_verilog/verilog_api.cpp | 13 +- openfpga/src/fpga_verilog/verilog_api.h | 6 +- openfpga/src/fpga_verilog/verilog_constants.h | 2 + openfpga/src/fpga_verilog/verilog_grid.cpp | 411 ++++++++++++++++++ openfpga/src/fpga_verilog/verilog_grid.h | 30 ++ openfpga/src/fpga_verilog/verilog_routing.cpp | 2 + 9 files changed, 479 insertions(+), 8 deletions(-) create mode 100644 openfpga/src/fpga_verilog/verilog_grid.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_grid.h diff --git a/openfpga/src/base/openfpga_naming.cpp b/openfpga/src/base/openfpga_naming.cpp index 7aaafff5d..fab27a12b 100644 --- a/openfpga/src/base/openfpga_naming.cpp +++ b/openfpga/src/base/openfpga_naming.cpp @@ -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 diff --git a/openfpga/src/base/openfpga_naming.h b/openfpga/src/base/openfpga_naming.h index 4bc22f3c9..13d4c56c0 100644 --- a/openfpga/src/base/openfpga_naming.h +++ b/openfpga/src/base/openfpga_naming.h @@ -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); diff --git a/openfpga/src/base/openfpga_verilog.cpp b/openfpga/src/base/openfpga_verilog.cpp index 018fe1249..72b8fb100 100644 --- a/openfpga/src/base/openfpga_verilog.cpp +++ b/openfpga/src/base/openfpga_verilog.cpp @@ -50,7 +50,8 @@ void write_fabric_verilog(OpenfpgaContext& openfpga_ctx, 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); } diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index aa68e0024..ce339e813 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -17,7 +17,7 @@ #include "verilog_auxiliary_netlists.h" #include "verilog_submodule.h" #include "verilog_routing.h" -//#include "verilog_grid.h" +#include "verilog_grid.h" //#include "verilog_top_module.h" /* Header file for this source file */ @@ -47,7 +47,8 @@ namespace openfpga { 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) { @@ -103,9 +104,11 @@ void fpga_fabric_verilog(ModuleManager& module_manager, } /* Generate grids */ - //print_verilog_grids(module_manager, - // src_dir_path, lb_dir_path, - // dump_explicit_verilog); + print_verilog_grids(const_cast(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, diff --git a/openfpga/src/fpga_verilog/verilog_api.h b/openfpga/src/fpga_verilog/verilog_api.h index 569650f2a..7494a5a8f 100644 --- a/openfpga/src/fpga_verilog/verilog_api.h +++ b/openfpga/src/fpga_verilog/verilog_api.h @@ -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" @@ -25,7 +26,8 @@ namespace openfpga { 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); diff --git a/openfpga/src/fpga_verilog/verilog_constants.h b/openfpga/src/fpga_verilog/verilog_constants.h index d5e1f4edb..e355b7a5f 100644 --- a/openfpga/src/fpga_verilog/verilog_constants.h +++ b/openfpga/src/fpga_verilog/verilog_constants.h @@ -45,5 +45,7 @@ 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 diff --git a/openfpga/src/fpga_verilog/verilog_grid.cpp b/openfpga/src/fpga_verilog/verilog_grid.cpp new file mode 100644 index 000000000..73e69ca29 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_grid.cpp @@ -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 +#include + +/* 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& 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& 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 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_grid.h b/openfpga/src/fpga_verilog/verilog_grid.h new file mode 100644 index 000000000..da8ea09fa --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_grid.h @@ -0,0 +1,30 @@ +#ifndef VERILOG_GRID_H +#define VERILOG_GRID_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#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 diff --git a/openfpga/src/fpga_verilog/verilog_routing.cpp b/openfpga/src/fpga_verilog/verilog_routing.cpp index 0d9582556..a3361fd06 100644 --- a/openfpga/src/fpga_verilog/verilog_routing.cpp +++ b/openfpga/src/fpga_verilog/verilog_routing.cpp @@ -291,6 +291,7 @@ void print_verilog_flatten_routing_modules(const ModuleManager& module_manager, subckt_dir.c_str(), ROUTING_VERILOG_FILE_NAME); VTR_LOG("Done\n"); + VTR_LOG("\n"); } @@ -350,6 +351,7 @@ void print_verilog_unique_routing_modules(const ModuleManager& module_manager, subckt_dir.c_str(), ROUTING_VERILOG_FILE_NAME); VTR_LOG("Done\n"); + VTR_LOG("\n"); } } /* end namespace openfpga */ From 11775c370b986f695d18da12cde3ae9e6f9e4b96 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 16:18:14 -0700 Subject: [PATCH 12/13] bring FPGA top module verilog writer online. Fabric Verilog generator done --- openfpga/src/fpga_verilog/verilog_api.cpp | 11 ++- .../src/fpga_verilog/verilog_top_module.cpp | 75 +++++++++++++++++++ .../src/fpga_verilog/verilog_top_module.h | 23 ++++++ .../src/fpga_verilog/verilog_writer_utils.cpp | 5 -- 4 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 openfpga/src/fpga_verilog/verilog_top_module.cpp create mode 100644 openfpga/src/fpga_verilog/verilog_top_module.h diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index ce339e813..645033d65 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -18,7 +18,7 @@ #include "verilog_submodule.h" #include "verilog_routing.h" #include "verilog_grid.h" -//#include "verilog_top_module.h" +#include "verilog_top_module.h" /* Header file for this source file */ #include "verilog_api.h" @@ -111,14 +111,13 @@ void fpga_fabric_verilog(ModuleManager& module_manager, 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(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()); } diff --git a/openfpga/src/fpga_verilog/verilog_top_module.cpp b/openfpga/src/fpga_verilog/verilog_top_module.cpp new file mode 100644 index 000000000..9bcfadc68 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_top_module.cpp @@ -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 +#include +#include + +/* 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 */ diff --git a/openfpga/src/fpga_verilog/verilog_top_module.h b/openfpga/src/fpga_verilog/verilog_top_module.h new file mode 100644 index 000000000..2fb451e16 --- /dev/null +++ b/openfpga/src/fpga_verilog/verilog_top_module.h @@ -0,0 +1,23 @@ +#ifndef VERILOG_TOP_MODULE_H +#define VERILOG_TOP_MODULE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#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 diff --git a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp index 600357c36..95f34ab1f 100644 --- a/openfpga/src/fpga_verilog/verilog_writer_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_writer_utils.cpp @@ -1377,9 +1377,6 @@ void print_verilog_netlist_include_header_file(const std::vector& n std::string verilog_fname(std::string(subckt_dir) + std::string(header_file_name)); - VTR_LOG("Writing header file for primitive modules '%s' ...", - verilog_fname.c_str()); - /* Create the file stream */ std::fstream fp; fp.open(verilog_fname, std::fstream::out | std::fstream::trunc); @@ -1396,8 +1393,6 @@ void print_verilog_netlist_include_header_file(const std::vector& n /* close file stream */ fp.close(); - - VTR_LOG("Done\n"); } } /* end namespace openfpga */ From 60f40a965783f2846fd1bb168c8a46522688f788 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 16 Feb 2020 16:35:26 -0700 Subject: [PATCH 13/13] use constant module manager as much as possible in Verilog writer --- .../src/fpga_verilog/verilog_decoders.cpp | 4 ++-- openfpga/src/fpga_verilog/verilog_decoders.h | 2 +- .../fpga_verilog/verilog_essential_gates.cpp | 8 ++++---- .../fpga_verilog/verilog_essential_gates.h | 2 +- openfpga/src/fpga_verilog/verilog_lut.cpp | 2 +- openfpga/src/fpga_verilog/verilog_lut.h | 2 +- openfpga/src/fpga_verilog/verilog_memory.cpp | 4 ++-- openfpga/src/fpga_verilog/verilog_memory.h | 2 +- .../src/fpga_verilog/verilog_submodule.cpp | 19 ++++++++++++------- openfpga/src/fpga_verilog/verilog_wire.cpp | 4 ++-- openfpga/src/fpga_verilog/verilog_wire.h | 2 +- 11 files changed, 28 insertions(+), 23 deletions(-) diff --git a/openfpga/src/fpga_verilog/verilog_decoders.cpp b/openfpga/src/fpga_verilog/verilog_decoders.cpp index 7689d3ed6..e9b9f6d02 100644 --- a/openfpga/src/fpga_verilog/verilog_decoders.cpp +++ b/openfpga/src/fpga_verilog/verilog_decoders.cpp @@ -44,7 +44,7 @@ namespace openfpga { ***************************************************************************************/ static void print_verilog_mux_local_decoder_module(std::fstream& fp, - ModuleManager& module_manager, + const ModuleManager& module_manager, const DecoderLibrary& decoder_lib, const DecoderId& decoder) { /* Get the number of inputs */ @@ -161,7 +161,7 @@ void print_verilog_mux_local_decoder_module(std::fstream& fp, * 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(ModuleManager& module_manager, +void print_verilog_submodule_mux_local_decoders(const ModuleManager& module_manager, std::vector& netlist_names, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, diff --git a/openfpga/src/fpga_verilog/verilog_decoders.h b/openfpga/src/fpga_verilog/verilog_decoders.h index 41f932932..271e7aa5c 100644 --- a/openfpga/src/fpga_verilog/verilog_decoders.h +++ b/openfpga/src/fpga_verilog/verilog_decoders.h @@ -20,7 +20,7 @@ /* begin namespace openfpga */ namespace openfpga { -void print_verilog_submodule_mux_local_decoders(ModuleManager& module_manager, +void print_verilog_submodule_mux_local_decoders(const ModuleManager& module_manager, std::vector& netlist_names, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, diff --git a/openfpga/src/fpga_verilog/verilog_essential_gates.cpp b/openfpga/src/fpga_verilog/verilog_essential_gates.cpp index c18bece61..a84d02bdf 100644 --- a/openfpga/src/fpga_verilog/verilog_essential_gates.cpp +++ b/openfpga/src/fpga_verilog/verilog_essential_gates.cpp @@ -142,7 +142,7 @@ void print_verilog_invbuf_body(std::fstream& fp, * or tapered buffer to a file ***********************************************/ static -void print_verilog_invbuf_module(ModuleManager& module_manager, +void print_verilog_invbuf_module(const ModuleManager& module_manager, std::fstream& fp, const CircuitLibrary& circuit_lib, const CircuitModelId& circuit_model) { @@ -226,7 +226,7 @@ void print_verilog_invbuf_module(ModuleManager& module_manager, * either transmission-gate or pass-transistor ***********************************************/ static -void print_verilog_passgate_module(ModuleManager& module_manager, +void print_verilog_passgate_module(const ModuleManager& module_manager, std::fstream& fp, const CircuitLibrary& circuit_lib, const CircuitModelId& circuit_model) { @@ -438,7 +438,7 @@ void print_verilog_mux2_gate_body(std::fstream& fp, * 3. 2-input MUX ***********************************************/ static -void print_verilog_gate_module(ModuleManager& module_manager, +void print_verilog_gate_module(const ModuleManager& module_manager, std::fstream& fp, const CircuitLibrary& circuit_lib, const CircuitModelId& circuit_model) { @@ -525,7 +525,7 @@ void print_verilog_constant_generator_module(const ModuleManager& module_manager * include inverters, buffers, transmission-gates, * etc. ***********************************************/ -void print_verilog_submodule_essentials(ModuleManager& module_manager, +void print_verilog_submodule_essentials(const ModuleManager& module_manager, std::vector& netlist_names, const std::string& verilog_dir, const std::string& submodule_dir, diff --git a/openfpga/src/fpga_verilog/verilog_essential_gates.h b/openfpga/src/fpga_verilog/verilog_essential_gates.h index 33ce49c6c..b7f9b519a 100644 --- a/openfpga/src/fpga_verilog/verilog_essential_gates.h +++ b/openfpga/src/fpga_verilog/verilog_essential_gates.h @@ -14,7 +14,7 @@ /* begin namespace openfpga */ namespace openfpga { -void print_verilog_submodule_essentials(ModuleManager& module_manager, +void print_verilog_submodule_essentials(const ModuleManager& module_manager, std::vector& netlist_names, const std::string& verilog_dir, const std::string& submodule_dir, diff --git a/openfpga/src/fpga_verilog/verilog_lut.cpp b/openfpga/src/fpga_verilog/verilog_lut.cpp index 900ef3073..a849c5827 100644 --- a/openfpga/src/fpga_verilog/verilog_lut.cpp +++ b/openfpga/src/fpga_verilog/verilog_lut.cpp @@ -29,7 +29,7 @@ namespace openfpga { * Print Verilog modules for the Look-Up Tables (LUTs) * in the circuit library ********************************************************************/ -void print_verilog_submodule_luts(ModuleManager& module_manager, +void print_verilog_submodule_luts(const ModuleManager& module_manager, std::vector& netlist_names, const CircuitLibrary& circuit_lib, const std::string& verilog_dir, diff --git a/openfpga/src/fpga_verilog/verilog_lut.h b/openfpga/src/fpga_verilog/verilog_lut.h index f5d1345dc..7c8b85b79 100644 --- a/openfpga/src/fpga_verilog/verilog_lut.h +++ b/openfpga/src/fpga_verilog/verilog_lut.h @@ -17,7 +17,7 @@ /* begin namespace openfpga */ namespace openfpga { -void print_verilog_submodule_luts(ModuleManager& module_manager, +void print_verilog_submodule_luts(const ModuleManager& module_manager, std::vector& netlist_names, const CircuitLibrary& circuit_lib, const std::string& verilog_dir, diff --git a/openfpga/src/fpga_verilog/verilog_memory.cpp b/openfpga/src/fpga_verilog/verilog_memory.cpp index 743647d44..de0418481 100644 --- a/openfpga/src/fpga_verilog/verilog_memory.cpp +++ b/openfpga/src/fpga_verilog/verilog_memory.cpp @@ -42,7 +42,7 @@ namespace openfpga { * +---------------------+ ********************************************************************/ static -void print_verilog_mux_memory_module(ModuleManager& module_manager, +void print_verilog_mux_memory_module(const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, std::fstream& fp, const CircuitModelId& mux_model, @@ -96,7 +96,7 @@ void print_verilog_mux_memory_module(ModuleManager& module_manager, * Take another example, the memory circuit can implement the scan-chain or * memory-bank organization for the memories. ********************************************************************/ -void print_verilog_submodule_memories(ModuleManager& module_manager, +void print_verilog_submodule_memories(const ModuleManager& module_manager, std::vector& netlist_names, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, diff --git a/openfpga/src/fpga_verilog/verilog_memory.h b/openfpga/src/fpga_verilog/verilog_memory.h index 774e6feb9..9d29eb15f 100644 --- a/openfpga/src/fpga_verilog/verilog_memory.h +++ b/openfpga/src/fpga_verilog/verilog_memory.h @@ -18,7 +18,7 @@ /* begin namespace openfpga */ namespace openfpga { -void print_verilog_submodule_memories(ModuleManager& module_manager, +void print_verilog_submodule_memories(const ModuleManager& module_manager, std::vector& netlist_names, const MuxLibrary& mux_lib, const CircuitLibrary& circuit_lib, diff --git a/openfpga/src/fpga_verilog/verilog_submodule.cpp b/openfpga/src/fpga_verilog/verilog_submodule.cpp index c52fc2262..099118e5f 100644 --- a/openfpga/src/fpga_verilog/verilog_submodule.cpp +++ b/openfpga/src/fpga_verilog/verilog_submodule.cpp @@ -42,13 +42,13 @@ void print_verilog_submodule(ModuleManager& 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); + //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 netlist_names; - print_verilog_submodule_essentials(module_manager, + print_verilog_submodule_essentials(const_cast(module_manager), netlist_names, verilog_dir, submodule_dir, @@ -58,7 +58,8 @@ void print_verilog_submodule(ModuleManager& module_manager, /* 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(module_manager, netlist_names, + print_verilog_submodule_mux_local_decoders(const_cast(module_manager), + netlist_names, mux_lib, circuit_lib, verilog_dir, submodule_dir); print_verilog_submodule_muxes(module_manager, netlist_names, mux_lib, circuit_lib, @@ -67,23 +68,27 @@ void print_verilog_submodule(ModuleManager& module_manager, /* LUTes */ - print_verilog_submodule_luts(module_manager, netlist_names, circuit_lib, + print_verilog_submodule_luts(const_cast(module_manager), + netlist_names, circuit_lib, verilog_dir, submodule_dir, fpga_verilog_opts.explicit_port_mapping()); /* Hard wires */ - print_verilog_submodule_wires(module_manager, netlist_names, circuit_lib, + print_verilog_submodule_wires(const_cast(module_manager), + netlist_names, circuit_lib, verilog_dir, submodule_dir); /* 4. Memories */ - print_verilog_submodule_memories(module_manager, netlist_names, + print_verilog_submodule_memories(const_cast(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(module_manager, circuit_lib, + print_verilog_submodule_templates(const_cast(module_manager), + circuit_lib, verilog_dir, submodule_dir); } diff --git a/openfpga/src/fpga_verilog/verilog_wire.cpp b/openfpga/src/fpga_verilog/verilog_wire.cpp index 2ac9e7ffa..4f084fad9 100644 --- a/openfpga/src/fpga_verilog/verilog_wire.cpp +++ b/openfpga/src/fpga_verilog/verilog_wire.cpp @@ -35,7 +35,7 @@ namespace openfpga { * *******************************************************************/ static -void print_verilog_wire_module(ModuleManager& module_manager, +void print_verilog_wire_module(const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, std::fstream& fp, const CircuitModelId& wire_model) { @@ -92,7 +92,7 @@ void print_verilog_wire_module(ModuleManager& module_manager, /******************************************************************** * Top-level function to print wire modules *******************************************************************/ -void print_verilog_submodule_wires(ModuleManager& module_manager, +void print_verilog_submodule_wires(const ModuleManager& module_manager, std::vector& netlist_names, const CircuitLibrary& circuit_lib, const std::string& verilog_dir, diff --git a/openfpga/src/fpga_verilog/verilog_wire.h b/openfpga/src/fpga_verilog/verilog_wire.h index 7d22ae4c8..55c39fb30 100644 --- a/openfpga/src/fpga_verilog/verilog_wire.h +++ b/openfpga/src/fpga_verilog/verilog_wire.h @@ -17,7 +17,7 @@ /* begin namespace openfpga */ namespace openfpga { -void print_verilog_submodule_wires(ModuleManager& module_manager, +void print_verilog_submodule_wires(const ModuleManager& module_manager, std::vector& netlist_names, const CircuitLibrary& circuit_lib, const std::string& verilog_dir,