From 51d423e4dba703f05e4ab78cdb2e1a2d5d62e77e Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 14:59:00 -0600 Subject: [PATCH 01/25] [FPGA-SPICE] Add pass-gate SPICE netlist writer --- .../src/fpga_spice/spice_essential_gates.cpp | 20 +- openfpga/src/fpga_spice/spice_passgate.cpp | 338 ++++++++++++++++++ openfpga/src/fpga_spice/spice_passgate.h | 30 ++ 3 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 openfpga/src/fpga_spice/spice_passgate.cpp create mode 100644 openfpga/src/fpga_spice/spice_passgate.h diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 7fd144f33..2a916f60e 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -1,6 +1,6 @@ /************************************************ * This file includes functions on - * outputting Verilog netlists for essential gates + * outputting SPICE netlists for essential gates * which are inverters, buffers, transmission-gates * logic gates etc. ***********************************************/ @@ -22,6 +22,7 @@ #include "spice_constants.h" #include "spice_writer_utils.h" +#include "spice_passgate.h" #include "spice_essential_gates.h" /* begin namespace openfpga */ @@ -1046,7 +1047,7 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, VTR_ASSERT(TECH_LIB_MODEL_TRANSISTOR == tech_lib.model_type(tech_model)); } - /* Now branch on netlist writing */ + /* Now branch on netlist writing: for inverter/buffers */ if (CIRCUIT_MODEL_INVBUF == circuit_lib.model_type(circuit_model)) { if (CIRCUIT_MODEL_BUF_INV == circuit_lib.buffer_type(circuit_model)) { VTR_ASSERT(true == module_manager.valid_module_id(module_id)); @@ -1069,6 +1070,21 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, /* Finish, go to the next */ continue; } + + /* Now branch on netlist writing: for inverter/buffers */ + if (CIRCUIT_MODEL_PASSGATE == circuit_lib.model_type(circuit_model)) { + status = print_spice_passgate_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + + if (CMD_EXEC_FATAL_ERROR == status) { + break; + } + + /* Finish, go to the next */ + continue; + } } /* Close file handler*/ diff --git a/openfpga/src/fpga_spice/spice_passgate.cpp b/openfpga/src/fpga_spice/spice_passgate.cpp new file mode 100644 index 000000000..a54956849 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_passgate.cpp @@ -0,0 +1,338 @@ +/************************************************ + * This file includes functions on + * outputting SPICE netlists for transmission-gates + ***********************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "circuit_library_utils.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_passgate.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Generate the SPICE modeling for the PMOS part of a pass-gate logic + * + * This function is created to be shared by pass-transistor and + * transmission-gate SPICE netlist writer + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * a pass-gate. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +static +int print_spice_passgate_pmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& gate_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Write transistor pairs using the technology model */ + fp << "Xpmos_" << trans_name_postfix << " "; + fp << input_port_name << " "; + fp << gate_port_name << " "; + fp << output_port_name << " "; + fp << "LVDD "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE modeling for the NMOS part of a pass-gate logic + * + * This function is created to be shared by pass-transistor and + * transmission-gate SPICE netlist writer + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * a pass-gate. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +static +int print_spice_passgate_nmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& gate_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + fp << "Xnmos_" << trans_name_postfix << " "; + fp << input_port_name << " "; + fp << gate_port_name << " "; + fp << output_port_name << " "; + fp << "LGND "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE subckt for a pass-transistor + * + * Schematic + * + * sel + * | + * === + * | | + * in -- ---out + * + *******************************************************************/ +static +int print_spice_pass_transistor_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + 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); + + /* Make sure: + * There is only 2 input port and 1 output port, + * 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)); + } + + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = circuit_lib.pass_gate_logic_nmos_size(circuit_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + status = print_spice_passgate_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(input_ports[1]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +/******************************************************************** + * Generate the SPICE subckt for a transmission gate + * + * Schematic + * + * selb + * | + * o + * === + * | | + * in -- ---out + * | | + * === + * | + * sel + * + *******************************************************************/ +static +int print_spice_transmission_gate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + 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); + + /* Make sure: + * There is only 3 input port and 1 output port, + * 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)); + } + + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = circuit_lib.pass_gate_logic_pmos_size(circuit_model) + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + status = print_spice_passgate_pmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(input_ports[2]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = circuit_lib.pass_gate_logic_nmos_size(circuit_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + status = print_spice_passgate_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(input_ports[1]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +/******************************************************************** + * Generate the SPICE subckt for a pass-gate + * + * Note: + * - This function supports both pass-transistor + * and transmission gates + *******************************************************************/ +int print_spice_passgate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + int status = CMD_EXEC_SUCCESS; + + if (CIRCUIT_MODEL_PASS_GATE_TRANSISTOR == circuit_lib.pass_gate_logic_type(circuit_model)) { + status = print_spice_pass_transistor_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } else if (CIRCUIT_MODEL_PASS_GATE_TRANSMISSION == circuit_lib.is_power_gated(circuit_model)) { + status = print_spice_transmission_gate_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_passgate.h b/openfpga/src/fpga_spice/spice_passgate.h new file mode 100644 index 000000000..96a292809 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_passgate.h @@ -0,0 +1,30 @@ +#ifndef SPICE_PASSGATE_H +#define SPICE_PASSGATE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "module_manager.h" +#include "circuit_library.h" +#include "technology_library.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_passgate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model); + +} /* end namespace openfpga */ + +#endif From f5dadca884c0086b1de64396ded980f76f91bc83 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 15:07:48 -0600 Subject: [PATCH 02/25] [FPGA-SPICE] Optimize the print-out of SPICE ports --- .../src/fpga_spice/spice_writer_utils.cpp | 23 +++++++++++++++++-- openfpga/src/fpga_spice/spice_writer_utils.h | 3 ++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_writer_utils.cpp b/openfpga/src/fpga_spice/spice_writer_utils.cpp index f3c4ab4d7..89b82a027 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.cpp +++ b/openfpga/src/fpga_spice/spice_writer_utils.cpp @@ -70,10 +70,21 @@ void print_spice_comment(std::fstream& fp, /************************************************ * Generate a string for a port in SPICE format + * If the pin id is zero, e.g., A[0], the option + * 'omit_pin_zero' may be turned on for compact port + * print-out, e.g., A ***********************************************/ -std::string generate_spice_port(const BasicPort& port) { +std::string generate_spice_port(const BasicPort& port, + const bool& omit_pin_zero) { VTR_ASSERT(1 == port.get_width()); + std::string ret = port.get_name(); + + if ((true == omit_pin_zero) + && (0 == port.get_lsb())) { + return ret; + } + ret += "["; ret += std::to_string(port.get_lsb()); ret += "]"; @@ -119,7 +130,15 @@ void print_spice_subckt_definition(std::fstream& fp, BasicPort port_pin(port.get_name(), pin, pin); - fp << generate_spice_port(port_pin); + /* For single-bit port, + * we can print the port name directly + */ + bool omit_pin_zero = false; + if ((1 == port.pins().size()) + && (0 == pin)) { + omit_pin_zero = true; + } + fp << generate_spice_port(port_pin, omit_pin_zero); /* Increase the counter */ pin_cnt++; diff --git a/openfpga/src/fpga_spice/spice_writer_utils.h b/openfpga/src/fpga_spice/spice_writer_utils.h index 5aa1882c9..3da2a6339 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.h +++ b/openfpga/src/fpga_spice/spice_writer_utils.h @@ -39,7 +39,8 @@ void print_spice_include_netlist(std::fstream& fp, void print_spice_comment(std::fstream& fp, const std::string& comment); -std::string generate_spice_port(const BasicPort& port); +std::string generate_spice_port(const BasicPort& port, + const bool& omit_pin_zero = false); void print_spice_subckt_definition(std::fstream& fp, const ModuleManager& module_manager, const ModuleId& module_id); From aa078f079ce6174ead058c99236cf96cfaedc437 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 15:20:19 -0600 Subject: [PATCH 03/25] [FPGA-SPICE] Restructured SPICE netlist writers for atom circuits to avoid large cpp files --- openfpga/src/fpga_spice/spice_buffer.cpp | 897 ++++++++++++++++ openfpga/src/fpga_spice/spice_buffer.h | 39 + .../src/fpga_spice/spice_essential_gates.cpp | 961 +----------------- .../src/fpga_spice/spice_essential_gates.h | 4 - openfpga/src/fpga_spice/spice_submodule.cpp | 3 +- .../fpga_spice/spice_transistor_wrapper.cpp | 117 +++ .../src/fpga_spice/spice_transistor_wrapper.h | 25 + 7 files changed, 1081 insertions(+), 965 deletions(-) create mode 100644 openfpga/src/fpga_spice/spice_buffer.cpp create mode 100644 openfpga/src/fpga_spice/spice_buffer.h create mode 100644 openfpga/src/fpga_spice/spice_transistor_wrapper.cpp create mode 100644 openfpga/src/fpga_spice/spice_transistor_wrapper.h diff --git a/openfpga/src/fpga_spice/spice_buffer.cpp b/openfpga/src/fpga_spice/spice_buffer.cpp new file mode 100644 index 000000000..59fa06d7f --- /dev/null +++ b/openfpga/src/fpga_spice/spice_buffer.cpp @@ -0,0 +1,897 @@ +/************************************************ + * This file includes functions on + * outputting SPICE netlists for inverters and buffers + ***********************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "circuit_library_utils.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_buffer.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Generate the SPICE modeling for a power-gated inverter + * + * This function is created to be shared by inverter and buffer SPICE netlist writer + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * an inverter. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +static +int print_spice_powergated_inverter_pmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& output_port_name, + const CircuitLibrary& circuit_lib, + const CircuitPortId& enb_port, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Write power-gating transistor pairs using the technology model + * Note that for a mulit-bit power gating port, we should cascade the transistors + */ + bool first_enb_pin = true; + size_t last_enb_pin; + for (const auto& power_gate_pin : circuit_lib.pins(enb_port)) { + BasicPort enb_pin(circuit_lib.port_prefix(enb_port), power_gate_pin, power_gate_pin); + fp << "Xpmos_powergate_" << trans_name_postfix << "_pin_" << power_gate_pin << " "; + /* For the first pin, we should connect it to local VDD*/ + if (true == first_enb_pin) { + fp << output_port_name << "_pmos_pg_" << power_gate_pin << " "; + fp << generate_spice_port(enb_pin) << " "; + fp << "LVDD "; + fp << "LVDD "; + first_enb_pin = false; + } else { + VTR_ASSERT_SAFE(false == first_enb_pin); + fp << output_port_name << "_pmos_pg_" << last_enb_pin << " "; + fp << generate_spice_port(enb_pin) << " "; + fp << output_port_name << "_pmos_pg_" << power_gate_pin << " "; + fp << "LVDD "; + } + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + /* Cache the last pin*/ + last_enb_pin = power_gate_pin; + } + + /* Write transistor pairs using the technology model */ + fp << "Xpmos_" << trans_name_postfix << " "; + fp << output_port_name << " "; + fp << input_port_name << " "; + fp << output_port_name << "_pmos_pg_" << circuit_lib.pins(enb_port).back() << " "; + fp << "LVDD "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE modeling for the NMOS part of a power-gated inverter + * + * This function is created to be shared by inverter and buffer SPICE netlist writer + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * an inverter. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +static +int print_spice_powergated_inverter_nmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& output_port_name, + const CircuitLibrary& circuit_lib, + const CircuitPortId& en_port, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + bool first_en_pin = true; + size_t last_en_pin; + for (const auto& power_gate_pin : circuit_lib.pins(en_port)) { + BasicPort en_pin(circuit_lib.port_prefix(en_port), power_gate_pin, power_gate_pin); + fp << "Xnmos_powergate_" << trans_name_postfix << "_pin_" << power_gate_pin << " "; + /* For the first pin, we should connect it to local VDD*/ + if (true == first_en_pin) { + fp << output_port_name << "_nmos_pg_" << power_gate_pin << " "; + fp << generate_spice_port(en_pin) << " "; + fp << "LGND "; + fp << "LGND "; + first_en_pin = false; + } else { + VTR_ASSERT_SAFE(false == first_en_pin); + fp << output_port_name << "_nmos_pg_" << last_en_pin << " "; + fp << circuit_lib.port_prefix(en_port) << " "; + fp << output_port_name << "_nmos_pg_" << power_gate_pin << " "; + fp << "LGND "; + } + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + /* Cache the last pin*/ + last_en_pin = power_gate_pin; + } + + fp << "Xnmos_" << trans_name_postfix << " "; + fp << output_port_name << " "; + fp << input_port_name << " "; + fp << output_port_name << " _nmos_pg_" << circuit_lib.pins(en_port).back() << " "; + fp << "LGND "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE subckt for a power gated inverter + * The Enable signal controlled the power gating + * + * Note: + * - This function supports multi-bit power gating + * + * Schematic + * LVDD + * | + * - + * ENb[0] -o|| + * - + * | + * - + * ENb[1] -o|| + * - + * | + * + * ... + * + * | + * - + * +-o|| + * | - + * | | + * in-->+ +--> OUT + * | | + * | - + * +--|| + * - + * + * ... + * + * | + * - + * EN[1] -|| + * - + * | + * - + * EN[0] -|| + * - + * | + * LGND + * + *******************************************************************/ +static +int print_spice_powergated_inverter_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + 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); + + /* 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])) ); + + /* If the circuit model is power-gated, we need to find at least one global config_enable signals */ + VTR_ASSERT(true == circuit_lib.is_power_gated(circuit_model)); + CircuitPortId en_port = find_circuit_model_power_gate_en_port(circuit_lib, circuit_model); + CircuitPortId enb_port = find_circuit_model_power_gate_enb_port(circuit_lib, circuit_model); + VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(en_port)); + VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(enb_port)); + + int status = CMD_EXEC_SUCCESS; + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = circuit_lib.buffer_size(circuit_model) + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + status = print_spice_powergated_inverter_pmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + circuit_lib, + enb_port, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = circuit_lib.buffer_size(circuit_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + status = print_spice_powergated_inverter_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + circuit_lib, + en_port, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE modeling for the PMOS part of a regular inverter + * + * This function is created to be shared by inverter and buffer SPICE netlist writer + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * an inverter. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +static +int print_spice_regular_inverter_pmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Write transistor pairs using the technology model */ + fp << "Xpmos_" << trans_name_postfix << " "; + fp << output_port_name << " "; + fp << input_port_name << " "; + fp << "LVDD "; + fp << "LVDD "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE modeling for the NMOS part of a regular inverter + * + * This function is created to be shared by inverter and buffer SPICE netlist writer + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * an inverter. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +static +int print_spice_regular_inverter_nmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + fp << "Xnmos_" << trans_name_postfix << " "; + fp << output_port_name << " "; + fp << input_port_name << " "; + fp << "LGND "; + fp << "LGND "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE subckt for a regular inverter + * + * Note: + * - This function does NOT support power-gating + * It should be managed in a separated function + * + * Schematic + * LVDD + * | + * - + * +-o|| + * | - + * | | + * in-->+ +--> OUT + * | | + * | - + * +--|| + * - + * | + * LGND + * + *******************************************************************/ +static +int print_spice_regular_inverter_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + 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); + + /* 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])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = circuit_lib.buffer_size(circuit_model) + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + status = print_spice_regular_inverter_pmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = circuit_lib.buffer_size(circuit_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + status = print_spice_regular_inverter_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +/******************************************************************** + * Generate the SPICE subckt for an inverter + * Branch on the different circuit topologies + *******************************************************************/ +int print_spice_inverter_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + int status = CMD_EXEC_SUCCESS; + if (true == circuit_lib.is_power_gated(circuit_model)) { + status = print_spice_powergated_inverter_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } else { + VTR_ASSERT_SAFE(false == circuit_lib.is_power_gated(circuit_model)); + status = print_spice_regular_inverter_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } + + return status; +} + +/******************************************************************** + * Generate the SPICE subckt for a power-gated buffer + * which contains at least 2 stages + * + * Schematic of a multi-stage buffer + * + * LVDD LVDD + * | | + * - - + * ENb[0] -o|| ENb[0] -o|| + * - - + * | | + * - - + * ENb[1] -o|| ENb[1] -o|| + * - - + * | | + * + * ... + * + * | | + * - - + * +-o|| +-o|| + * | - | - + * | | | | + * in-->+ +-- ... ---+---->+---> out + * | | | | + * | - | - + * +--|| +--|| + * - - + * | | + * + * ... + * + * | | + * - - + * EN[0] -|| EN[0] -|| + * - - + * | | + * - - + * EN[1] -|| EN[1] -|| + * - - + * | | + + * | | + * LGND LGND + * + *******************************************************************/ +static +int print_spice_powergated_buffer_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + 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); + + /* 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])) ); + + /* If the circuit model is power-gated, we need to find at least one global config_enable signals */ + VTR_ASSERT(true == circuit_lib.is_power_gated(circuit_model)); + CircuitPortId en_port = find_circuit_model_power_gate_en_port(circuit_lib, circuit_model); + CircuitPortId enb_port = find_circuit_model_power_gate_enb_port(circuit_lib, circuit_model); + VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(en_port)); + VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(enb_port)); + + int status = CMD_EXEC_SUCCESS; + + /* Buffers must have >= 2 stages */ + VTR_ASSERT(2 <= circuit_lib.buffer_num_levels(circuit_model)); + + /* Build the array denoting width of inverters per stage */ + std::vector buffer_widths(circuit_lib.buffer_num_levels(circuit_model), 1); + for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { + buffer_widths[level] = circuit_lib.buffer_size(circuit_model) + * std::pow(circuit_lib.buffer_f_per_stage(circuit_model), level); + } + + for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { + std::string input_port_name = circuit_lib.port_prefix(input_ports[0]); + std::string output_port_name = circuit_lib.port_prefix(output_ports[0]); + + /* Special for first stage: output port should be an intermediate node + * Special for rest of stages: input port should be the output of previous stage + */ + if (0 == level) { + output_port_name += std::string("_level") + std::to_string(level); + } else { + VTR_ASSERT(0 < level); + input_port_name += std::string("_level") + std::to_string(level - 1); + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = buffer_widths[level] + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); + + status = print_spice_powergated_inverter_pmos_modeling(fp, + name_postfix, + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + circuit_lib, + enb_port, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = buffer_widths[level] + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); + + status = print_spice_powergated_inverter_nmos_modeling(fp, + name_postfix, + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + circuit_lib, + en_port, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return CMD_EXEC_SUCCESS; +} + + +/******************************************************************** + * Generate the SPICE subckt for a regular buffer + * which contains at least 2 stages + * + * Note: + * - This function does NOT support power-gating + * It should be managed in a separated function + * + * Schematic of a multi-stage buffer + * + * LVDD LVDD + * | | + * - - + * +-o|| +-o|| + * | - | - + * | | | | + * in-->+ +-- ... ---+---->+---> out + * | | | | + * | - | - + * +--|| +--|| + * - - + * | | + * LGND LGND + * + *******************************************************************/ +static +int print_spice_regular_buffer_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + 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); + + /* 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])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Buffers must have >= 2 stages */ + VTR_ASSERT(2 <= circuit_lib.buffer_num_levels(circuit_model)); + + /* Build the array denoting width of inverters per stage */ + std::vector buffer_widths(circuit_lib.buffer_num_levels(circuit_model), 1); + for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { + buffer_widths[level] = circuit_lib.buffer_size(circuit_model) + * std::pow(circuit_lib.buffer_f_per_stage(circuit_model), level); + } + + for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { + std::string input_port_name = circuit_lib.port_prefix(input_ports[0]); + std::string output_port_name = circuit_lib.port_prefix(output_ports[0]); + + /* Special for first stage: output port should be an intermediate node + * Special for rest of stages: input port should be the output of previous stage + */ + if (0 == level) { + output_port_name += std::string("_level") + std::to_string(level); + } else { + VTR_ASSERT(0 < level); + input_port_name += std::string("_level") + std::to_string(level - 1); + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = buffer_widths[level] + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); + + status = print_spice_regular_inverter_pmos_modeling(fp, + name_postfix, + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = buffer_widths[level] + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); + + status = print_spice_regular_inverter_nmos_modeling(fp, + name_postfix, + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +/******************************************************************** + * Generate the SPICE subckt for an buffer + * which consists of multiple stage of inverters + *******************************************************************/ +int print_spice_buffer_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + int status = CMD_EXEC_SUCCESS; + if (true == circuit_lib.is_power_gated(circuit_model)) { + status = print_spice_powergated_buffer_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } else { + VTR_ASSERT_SAFE(false == circuit_lib.is_power_gated(circuit_model)); + status = print_spice_regular_buffer_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_buffer.h b/openfpga/src/fpga_spice/spice_buffer.h new file mode 100644 index 000000000..d7019f995 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_buffer.h @@ -0,0 +1,39 @@ +#ifndef SPICE_BUFFER_H +#define SPICE_BUFFER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "module_manager.h" +#include "circuit_library.h" +#include "technology_library.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_inverter_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model); + +int print_spice_buffer_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model); + + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 2a916f60e..fe554c328 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -22,972 +22,13 @@ #include "spice_constants.h" #include "spice_writer_utils.h" +#include "spice_buffer.h" #include "spice_passgate.h" #include "spice_essential_gates.h" /* begin namespace openfpga */ namespace openfpga { -/******************************************************************** - * Print a SPICE model wrapper for a transistor model - *******************************************************************/ -static -int print_spice_transistor_model_wrapper(std::fstream& fp, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& model) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Transistor model followed a fixed port mapping - * [X|M] - * which is a standard in SPICE modeling - * We will output the pmos and nmos transistors wrappers - * which are defined in this model - */ - for (int itype = TECH_LIB_TRANSISTOR_PMOS; - itype < NUM_TECH_LIB_TRANSISTOR_TYPES; - ++itype) { - const e_tech_lib_transistor_type& trans_type = static_cast(itype); - fp << ".subckt "; - fp << tech_lib.transistor_model_name(model, trans_type) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " drain gate source bulk"; - fp << " L=" << std::setprecision(10) << tech_lib.transistor_model_chan_length(model, trans_type); - fp << " W=" << std::setprecision(10) << tech_lib.transistor_model_min_width(model, trans_type); - fp << "\n"; - - fp << tech_lib.model_ref(model); - fp << "1"; - fp << " drain gate source bulk"; - fp << " " << tech_lib.transistor_model_name(model, trans_type); - fp << " L=L W=W"; - fp << "\n"; - - fp << ".ends"; - fp << "\n"; - } - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE netlist for transistors - *******************************************************************/ -int print_spice_transistor_wrapper(NetlistManager& netlist_manager, - const TechnologyLibrary& tech_lib, - const std::string& submodule_dir) { - std::string spice_fname = submodule_dir + std::string(TRANSISTORS_SPICE_FILE_NAME); - - std::fstream fp; - - /* Create the file stream */ - fp.open(spice_fname, std::fstream::out | std::fstream::trunc); - /* Check if the file stream if valid or not */ - check_file_stream(spice_fname.c_str(), fp); - - /* Create file */ - VTR_LOG("Generating SPICE netlist '%s' for transistors...", - spice_fname.c_str()); - - print_spice_file_header(fp, std::string("Transistor wrappers")); - - /* Iterate over the transistor models */ - for (const TechnologyModelId& model : tech_lib.models()) { - /* Focus on transistor model */ - if (TECH_LIB_MODEL_TRANSISTOR != tech_lib.model_type(model)) { - continue; - } - /* Write a wrapper for the transistor model */ - if (CMD_EXEC_SUCCESS == print_spice_transistor_model_wrapper(fp, tech_lib, model)) { - return CMD_EXEC_FATAL_ERROR; - } - } - - /* Close file handler*/ - fp.close(); - - /* Add fname to the netlist name list */ - NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); - VTR_ASSERT(NetlistId::INVALID() != nlist_id); - netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); - - VTR_LOG("Done\n"); - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE modeling for a power-gated inverter - * - * This function is created to be shared by inverter and buffer SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * an inverter. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_powergated_inverter_pmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& output_port_name, - const CircuitLibrary& circuit_lib, - const CircuitPortId& enb_port, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Write power-gating transistor pairs using the technology model - * Note that for a mulit-bit power gating port, we should cascade the transistors - */ - bool first_enb_pin = true; - size_t last_enb_pin; - for (const auto& power_gate_pin : circuit_lib.pins(enb_port)) { - BasicPort enb_pin(circuit_lib.port_prefix(enb_port), power_gate_pin, power_gate_pin); - fp << "Xpmos_powergate_" << trans_name_postfix << "_pin_" << power_gate_pin << " "; - /* For the first pin, we should connect it to local VDD*/ - if (true == first_enb_pin) { - fp << output_port_name << "_pmos_pg_" << power_gate_pin << " "; - fp << generate_spice_port(enb_pin) << " "; - fp << "LVDD "; - fp << "LVDD "; - first_enb_pin = false; - } else { - VTR_ASSERT_SAFE(false == first_enb_pin); - fp << output_port_name << "_pmos_pg_" << last_enb_pin << " "; - fp << generate_spice_port(enb_pin) << " "; - fp << output_port_name << "_pmos_pg_" << power_gate_pin << " "; - fp << "LVDD "; - } - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - /* Cache the last pin*/ - last_enb_pin = power_gate_pin; - } - - /* Write transistor pairs using the technology model */ - fp << "Xpmos_" << trans_name_postfix << " "; - fp << output_port_name << " "; - fp << input_port_name << " "; - fp << output_port_name << "_pmos_pg_" << circuit_lib.pins(enb_port).back() << " "; - fp << "LVDD "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE modeling for the NMOS part of a power-gated inverter - * - * This function is created to be shared by inverter and buffer SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * an inverter. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_powergated_inverter_nmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& output_port_name, - const CircuitLibrary& circuit_lib, - const CircuitPortId& en_port, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - bool first_en_pin = true; - size_t last_en_pin; - for (const auto& power_gate_pin : circuit_lib.pins(en_port)) { - BasicPort en_pin(circuit_lib.port_prefix(en_port), power_gate_pin, power_gate_pin); - fp << "Xnmos_powergate_" << trans_name_postfix << "_pin_" << power_gate_pin << " "; - /* For the first pin, we should connect it to local VDD*/ - if (true == first_en_pin) { - fp << output_port_name << "_nmos_pg_" << power_gate_pin << " "; - fp << generate_spice_port(en_pin) << " "; - fp << "LGND "; - fp << "LGND "; - first_en_pin = false; - } else { - VTR_ASSERT_SAFE(false == first_en_pin); - fp << output_port_name << "_nmos_pg_" << last_en_pin << " "; - fp << circuit_lib.port_prefix(en_port) << " "; - fp << output_port_name << "_nmos_pg_" << power_gate_pin << " "; - fp << "LGND "; - } - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - /* Cache the last pin*/ - last_en_pin = power_gate_pin; - } - - fp << "Xnmos_" << trans_name_postfix << " "; - fp << output_port_name << " "; - fp << input_port_name << " "; - fp << output_port_name << " _nmos_pg_" << circuit_lib.pins(en_port).back() << " "; - fp << "LGND "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE subckt for a power gated inverter - * The Enable signal controlled the power gating - * - * Note: - * - This function supports multi-bit power gating - * - * Schematic - * LVDD - * | - * - - * ENb[0] -o|| - * - - * | - * - - * ENb[1] -o|| - * - - * | - * - * ... - * - * | - * - - * +-o|| - * | - - * | | - * in-->+ +--> OUT - * | | - * | - - * +--|| - * - - * - * ... - * - * | - * - - * EN[1] -|| - * - - * | - * - - * EN[0] -|| - * - - * | - * LGND - * - *******************************************************************/ -static -int print_spice_powergated_inverter_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Print the inverter subckt definition */ - print_spice_subckt_definition(fp, module_manager, module_id); - - /* Find the input and output ports: - * we do NOT support global ports here, - * it should be handled in another type of inverter subckt (power-gated) - */ - 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); - - /* 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])) ); - - /* If the circuit model is power-gated, we need to find at least one global config_enable signals */ - VTR_ASSERT(true == circuit_lib.is_power_gated(circuit_model)); - CircuitPortId en_port = find_circuit_model_power_gate_en_port(circuit_lib, circuit_model); - CircuitPortId enb_port = find_circuit_model_power_gate_enb_port(circuit_lib, circuit_model); - VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(en_port)); - VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(enb_port)); - - int status = CMD_EXEC_SUCCESS; - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - float total_pmos_width = circuit_lib.buffer_size(circuit_model) - * tech_lib.model_pn_ratio(tech_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); - float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); - for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { - float curr_bin_width = regular_pmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_pmos_bins - 1) - && (0. != last_pmos_bin_width)) { - curr_bin_width = last_pmos_bin_width; - } - status = print_spice_powergated_inverter_pmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - circuit_lib, - enb_port, - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - float total_nmos_width = circuit_lib.buffer_size(circuit_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); - float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); - for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { - float curr_bin_width = regular_nmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_nmos_bins - 1) - && (0. != last_nmos_bin_width)) { - curr_bin_width = last_nmos_bin_width; - } - - status = print_spice_powergated_inverter_nmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - circuit_lib, - en_port, - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - print_spice_subckt_end(fp, module_manager.module_name(module_id)); - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE modeling for the PMOS part of a regular inverter - * - * This function is created to be shared by inverter and buffer SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * an inverter. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_regular_inverter_pmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& output_port_name, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Write transistor pairs using the technology model */ - fp << "Xpmos_" << trans_name_postfix << " "; - fp << output_port_name << " "; - fp << input_port_name << " "; - fp << "LVDD "; - fp << "LVDD "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE modeling for the NMOS part of a regular inverter - * - * This function is created to be shared by inverter and buffer SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * an inverter. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_regular_inverter_nmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& output_port_name, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - fp << "Xnmos_" << trans_name_postfix << " "; - fp << output_port_name << " "; - fp << input_port_name << " "; - fp << "LGND "; - fp << "LGND "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE subckt for a regular inverter - * - * Note: - * - This function does NOT support power-gating - * It should be managed in a separated function - * - * Schematic - * LVDD - * | - * - - * +-o|| - * | - - * | | - * in-->+ +--> OUT - * | | - * | - - * +--|| - * - - * | - * LGND - * - *******************************************************************/ -static -int print_spice_regular_inverter_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Print the inverter subckt definition */ - print_spice_subckt_definition(fp, module_manager, module_id); - - /* Find the input and output ports: - * we do NOT support global ports here, - * it should be handled in another type of inverter subckt (power-gated) - */ - 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); - - /* 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])) ); - - int status = CMD_EXEC_SUCCESS; - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - float total_pmos_width = circuit_lib.buffer_size(circuit_model) - * tech_lib.model_pn_ratio(tech_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); - float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); - for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { - float curr_bin_width = regular_pmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_pmos_bins - 1) - && (0. != last_pmos_bin_width)) { - curr_bin_width = last_pmos_bin_width; - } - - status = print_spice_regular_inverter_pmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - float total_nmos_width = circuit_lib.buffer_size(circuit_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); - float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); - - for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { - float curr_bin_width = regular_nmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_nmos_bins - 1) - && (0. != last_nmos_bin_width)) { - curr_bin_width = last_nmos_bin_width; - } - - status = print_spice_regular_inverter_nmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - print_spice_subckt_end(fp, module_manager.module_name(module_id)); - - return status; -} - -/******************************************************************** - * Generate the SPICE subckt for an inverter - * Branch on the different circuit topologies - *******************************************************************/ -static -int print_spice_inverter_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - int status = CMD_EXEC_SUCCESS; - if (true == circuit_lib.is_power_gated(circuit_model)) { - status = print_spice_powergated_inverter_subckt(fp, - module_manager, module_id, - circuit_lib, circuit_model, - tech_lib, tech_model); - } else { - VTR_ASSERT_SAFE(false == circuit_lib.is_power_gated(circuit_model)); - status = print_spice_regular_inverter_subckt(fp, - module_manager, module_id, - circuit_lib, circuit_model, - tech_lib, tech_model); - } - - return status; -} - -/******************************************************************** - * Generate the SPICE subckt for a power-gated buffer - * which contains at least 2 stages - * - * Schematic of a multi-stage buffer - * - * LVDD LVDD - * | | - * - - - * ENb[0] -o|| ENb[0] -o|| - * - - - * | | - * - - - * ENb[1] -o|| ENb[1] -o|| - * - - - * | | - * - * ... - * - * | | - * - - - * +-o|| +-o|| - * | - | - - * | | | | - * in-->+ +-- ... ---+---->+---> out - * | | | | - * | - | - - * +--|| +--|| - * - - - * | | - * - * ... - * - * | | - * - - - * EN[0] -|| EN[0] -|| - * - - - * | | - * - - - * EN[1] -|| EN[1] -|| - * - - - * | | - - * | | - * LGND LGND - * - *******************************************************************/ -static -int print_spice_powergated_buffer_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Print the inverter subckt definition */ - print_spice_subckt_definition(fp, module_manager, module_id); - - /* Find the input and output ports: - * we do NOT support global ports here, - * it should be handled in another type of inverter subckt (power-gated) - */ - 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); - - /* 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])) ); - - /* If the circuit model is power-gated, we need to find at least one global config_enable signals */ - VTR_ASSERT(true == circuit_lib.is_power_gated(circuit_model)); - CircuitPortId en_port = find_circuit_model_power_gate_en_port(circuit_lib, circuit_model); - CircuitPortId enb_port = find_circuit_model_power_gate_enb_port(circuit_lib, circuit_model); - VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(en_port)); - VTR_ASSERT(true == circuit_lib.valid_circuit_port_id(enb_port)); - - int status = CMD_EXEC_SUCCESS; - - /* Buffers must have >= 2 stages */ - VTR_ASSERT(2 <= circuit_lib.buffer_num_levels(circuit_model)); - - /* Build the array denoting width of inverters per stage */ - std::vector buffer_widths(circuit_lib.buffer_num_levels(circuit_model), 1); - for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { - buffer_widths[level] = circuit_lib.buffer_size(circuit_model) - * std::pow(circuit_lib.buffer_f_per_stage(circuit_model), level); - } - - for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { - std::string input_port_name = circuit_lib.port_prefix(input_ports[0]); - std::string output_port_name = circuit_lib.port_prefix(output_ports[0]); - - /* Special for first stage: output port should be an intermediate node - * Special for rest of stages: input port should be the output of previous stage - */ - if (0 == level) { - output_port_name += std::string("_level") + std::to_string(level); - } else { - VTR_ASSERT(0 < level); - input_port_name += std::string("_level") + std::to_string(level - 1); - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - float total_pmos_width = buffer_widths[level] - * tech_lib.model_pn_ratio(tech_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); - float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); - - for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { - float curr_bin_width = regular_pmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_pmos_bins - 1) - && (0. != last_pmos_bin_width)) { - curr_bin_width = last_pmos_bin_width; - } - - std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); - - status = print_spice_powergated_inverter_pmos_modeling(fp, - name_postfix, - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - circuit_lib, - enb_port, - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - float total_nmos_width = buffer_widths[level] - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); - float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); - - for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { - float curr_bin_width = regular_nmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_nmos_bins - 1) - && (0. != last_nmos_bin_width)) { - curr_bin_width = last_nmos_bin_width; - } - - std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); - - status = print_spice_powergated_inverter_nmos_modeling(fp, - name_postfix, - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - circuit_lib, - en_port, - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - } - - print_spice_subckt_end(fp, module_manager.module_name(module_id)); - - return CMD_EXEC_SUCCESS; -} - - -/******************************************************************** - * Generate the SPICE subckt for a regular buffer - * which contains at least 2 stages - * - * Note: - * - This function does NOT support power-gating - * It should be managed in a separated function - * - * Schematic of a multi-stage buffer - * - * LVDD LVDD - * | | - * - - - * +-o|| +-o|| - * | - | - - * | | | | - * in-->+ +-- ... ---+---->+---> out - * | | | | - * | - | - - * +--|| +--|| - * - - - * | | - * LGND LGND - * - *******************************************************************/ -static -int print_spice_regular_buffer_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Print the inverter subckt definition */ - print_spice_subckt_definition(fp, module_manager, module_id); - - /* Find the input and output ports: - * we do NOT support global ports here, - * it should be handled in another type of inverter subckt (power-gated) - */ - 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); - - /* 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])) ); - - int status = CMD_EXEC_SUCCESS; - - /* Buffers must have >= 2 stages */ - VTR_ASSERT(2 <= circuit_lib.buffer_num_levels(circuit_model)); - - /* Build the array denoting width of inverters per stage */ - std::vector buffer_widths(circuit_lib.buffer_num_levels(circuit_model), 1); - for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { - buffer_widths[level] = circuit_lib.buffer_size(circuit_model) - * std::pow(circuit_lib.buffer_f_per_stage(circuit_model), level); - } - - for (size_t level = 0; level < circuit_lib.buffer_num_levels(circuit_model); ++level) { - std::string input_port_name = circuit_lib.port_prefix(input_ports[0]); - std::string output_port_name = circuit_lib.port_prefix(output_ports[0]); - - /* Special for first stage: output port should be an intermediate node - * Special for rest of stages: input port should be the output of previous stage - */ - if (0 == level) { - output_port_name += std::string("_level") + std::to_string(level); - } else { - VTR_ASSERT(0 < level); - input_port_name += std::string("_level") + std::to_string(level - 1); - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - float total_pmos_width = buffer_widths[level] - * tech_lib.model_pn_ratio(tech_model) - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); - int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); - float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); - - for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { - float curr_bin_width = regular_pmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_pmos_bins - 1) - && (0. != last_pmos_bin_width)) { - curr_bin_width = last_pmos_bin_width; - } - - std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); - - status = print_spice_regular_inverter_pmos_modeling(fp, - name_postfix, - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - - /* Consider use size/bin to compact layout: - * Try to size transistors to the max width for each bin - * The last bin may not reach the max width - */ - float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - float total_nmos_width = buffer_widths[level] - * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); - int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); - float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); - - for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { - float curr_bin_width = regular_nmos_bin_width; - /* For last bin, we need an irregular width */ - if ((ibin == num_nmos_bins - 1) - && (0. != last_nmos_bin_width)) { - curr_bin_width = last_nmos_bin_width; - } - - std::string name_postfix = std::string("level") + std::to_string(level) + std::string("_bin") + std::to_string(ibin); - - status = print_spice_regular_inverter_nmos_modeling(fp, - name_postfix, - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); - if (CMD_EXEC_FATAL_ERROR == status) { - return status; - } - } - } - - print_spice_subckt_end(fp, module_manager.module_name(module_id)); - - return status; -} - -/******************************************************************** - * Generate the SPICE subckt for an buffer - * which consists of multiple stage of inverters - *******************************************************************/ -static -int print_spice_buffer_subckt(std::fstream& fp, - const ModuleManager& module_manager, - const ModuleId& module_id, - const CircuitLibrary& circuit_lib, - const CircuitModelId& circuit_model, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model) { - int status = CMD_EXEC_SUCCESS; - if (true == circuit_lib.is_power_gated(circuit_model)) { - status = print_spice_powergated_buffer_subckt(fp, - module_manager, module_id, - circuit_lib, circuit_model, - tech_lib, tech_model); - } else { - VTR_ASSERT_SAFE(false == circuit_lib.is_power_gated(circuit_model)); - status = print_spice_regular_buffer_subckt(fp, - module_manager, module_id, - circuit_lib, circuit_model, - tech_lib, tech_model); - } - - return status; -} - /******************************************************************** * Generate the SPICE netlist for essential gates: * - inverters and their templates diff --git a/openfpga/src/fpga_spice/spice_essential_gates.h b/openfpga/src/fpga_spice/spice_essential_gates.h index de9ab3cdb..0fbd1895d 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.h +++ b/openfpga/src/fpga_spice/spice_essential_gates.h @@ -18,10 +18,6 @@ /* begin namespace openfpga */ namespace openfpga { -int print_spice_transistor_wrapper(NetlistManager& netlist_manager, - const TechnologyLibrary& tech_lib, - const std::string& submodule_dir); - int print_spice_essential_gates(NetlistManager& netlist_manager, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index 4de678af5..ad72e8515 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -10,6 +10,7 @@ /* Headers from openfpgashell library */ #include "command_exit_codes.h" +#include "spice_transistor_wrapper.h" #include "spice_essential_gates.h" #include "spice_constants.h" @@ -21,7 +22,7 @@ namespace openfpga { /********************************************************************* * Top-level function to generate primitive modules: * 1. Transistor wrapper - * 2. TODO: Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor + * 2. Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor * 3. TODO: Routing multiplexers * 4. TODO: Local encoders for routing multiplexers * 5. TODO: Wires diff --git a/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp new file mode 100644 index 000000000..140d68181 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp @@ -0,0 +1,117 @@ +/************************************************ + * This file includes functions on + * outputting wrapper SPICE netlists for transistor + ***********************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "circuit_library_utils.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_transistor_wrapper.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print a SPICE model wrapper for a transistor model + *******************************************************************/ +static +int print_spice_transistor_model_wrapper(std::fstream& fp, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& model) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Transistor model followed a fixed port mapping + * [X|M] + * which is a standard in SPICE modeling + * We will output the pmos and nmos transistors wrappers + * which are defined in this model + */ + for (int itype = TECH_LIB_TRANSISTOR_PMOS; + itype < NUM_TECH_LIB_TRANSISTOR_TYPES; + ++itype) { + const e_tech_lib_transistor_type& trans_type = static_cast(itype); + fp << ".subckt "; + fp << tech_lib.transistor_model_name(model, trans_type) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " drain gate source bulk"; + fp << " L=" << std::setprecision(10) << tech_lib.transistor_model_chan_length(model, trans_type); + fp << " W=" << std::setprecision(10) << tech_lib.transistor_model_min_width(model, trans_type); + fp << "\n"; + + fp << tech_lib.model_ref(model); + fp << "1"; + fp << " drain gate source bulk"; + fp << " " << tech_lib.transistor_model_name(model, trans_type); + fp << " L=L W=W"; + fp << "\n"; + + fp << ".ends"; + fp << "\n"; + } + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE netlist for transistors + *******************************************************************/ +int print_spice_transistor_wrapper(NetlistManager& netlist_manager, + const TechnologyLibrary& tech_lib, + const std::string& submodule_dir) { + std::string spice_fname = submodule_dir + std::string(TRANSISTORS_SPICE_FILE_NAME); + + std::fstream fp; + + /* Create the file stream */ + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + /* Check if the file stream if valid or not */ + check_file_stream(spice_fname.c_str(), fp); + + /* Create file */ + VTR_LOG("Generating SPICE netlist '%s' for transistors...", + spice_fname.c_str()); + + print_spice_file_header(fp, std::string("Transistor wrappers")); + + /* Iterate over the transistor models */ + for (const TechnologyModelId& model : tech_lib.models()) { + /* Focus on transistor model */ + if (TECH_LIB_MODEL_TRANSISTOR != tech_lib.model_type(model)) { + continue; + } + /* Write a wrapper for the transistor model */ + if (CMD_EXEC_SUCCESS == print_spice_transistor_model_wrapper(fp, tech_lib, model)) { + return CMD_EXEC_FATAL_ERROR; + } + } + + /* Close file handler*/ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); + + return CMD_EXEC_SUCCESS; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_transistor_wrapper.h b/openfpga/src/fpga_spice/spice_transistor_wrapper.h new file mode 100644 index 000000000..86115c5c4 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_transistor_wrapper.h @@ -0,0 +1,25 @@ +#ifndef SPICE_TRANSISTOR_WRAPPER_H +#define SPICE_TRANSISTOR_WRAPPER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "netlist_manager.h" +#include "technology_library.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_transistor_wrapper(NetlistManager& netlist_manager, + const TechnologyLibrary& tech_lib, + const std::string& submodule_dir); + +} /* end namespace openfpga */ + +#endif From 3262ceb2769d12d9c19018ddd2fa97293b6fe317 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 15:24:40 -0600 Subject: [PATCH 04/25] [FPGA-SPICE] Bug fix for pass gate transistor sizing --- openfpga/src/fpga_spice/spice_passgate.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/openfpga/src/fpga_spice/spice_passgate.cpp b/openfpga/src/fpga_spice/spice_passgate.cpp index a54956849..20b8ef84b 100644 --- a/openfpga/src/fpga_spice/spice_passgate.cpp +++ b/openfpga/src/fpga_spice/spice_passgate.cpp @@ -243,7 +243,6 @@ int print_spice_transmission_gate_subckt(std::fstream& fp, */ float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); float total_pmos_width = circuit_lib.pass_gate_logic_pmos_size(circuit_model) - * tech_lib.model_pn_ratio(tech_model) * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); From 482d90018f4fcf777603d39313b33cb2068838d1 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 15:33:28 -0600 Subject: [PATCH 05/25] [FPGA-SPICE] Create generic PMOS/NMOS instanciation function --- openfpga/src/fpga_spice/spice_passgate.cpp | 126 ++++-------------- .../fpga_spice/spice_transistor_wrapper.cpp | 73 ++++++++++ .../src/fpga_spice/spice_transistor_wrapper.h | 18 +++ 3 files changed, 116 insertions(+), 101 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_passgate.cpp b/openfpga/src/fpga_spice/spice_passgate.cpp index 20b8ef84b..d26ac5bfb 100644 --- a/openfpga/src/fpga_spice/spice_passgate.cpp +++ b/openfpga/src/fpga_spice/spice_passgate.cpp @@ -20,88 +20,12 @@ #include "spice_constants.h" #include "spice_writer_utils.h" +#include "spice_transistor_wrapper.h" #include "spice_passgate.h" /* begin namespace openfpga */ namespace openfpga { -/******************************************************************** - * Generate the SPICE modeling for the PMOS part of a pass-gate logic - * - * This function is created to be shared by pass-transistor and - * transmission-gate SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * a pass-gate. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_passgate_pmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& gate_port_name, - const std::string& output_port_name, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - /* Write transistor pairs using the technology model */ - fp << "Xpmos_" << trans_name_postfix << " "; - fp << input_port_name << " "; - fp << gate_port_name << " "; - fp << output_port_name << " "; - fp << "LVDD "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - -/******************************************************************** - * Generate the SPICE modeling for the NMOS part of a pass-gate logic - * - * This function is created to be shared by pass-transistor and - * transmission-gate SPICE netlist writer - * - * Note: - * - This function does NOT create a file - * but requires a file stream created - * - This function only output SPICE modeling for - * a pass-gate. Any preprocessing or subckt definition should not be included! - *******************************************************************/ -static -int print_spice_passgate_nmos_modeling(std::fstream& fp, - const std::string& trans_name_postfix, - const std::string& input_port_name, - const std::string& gate_port_name, - const std::string& output_port_name, - const TechnologyLibrary& tech_lib, - const TechnologyModelId& tech_model, - const float& trans_width) { - - if (false == valid_file_stream(fp)) { - return CMD_EXEC_FATAL_ERROR; - } - - fp << "Xnmos_" << trans_name_postfix << " "; - fp << input_port_name << " "; - fp << gate_port_name << " "; - fp << output_port_name << " "; - fp << "LGND "; - fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; - fp << " W=" << std::setprecision(10) << trans_width; - fp << "\n"; - - return CMD_EXEC_SUCCESS; -} - /******************************************************************** * Generate the SPICE subckt for a pass-transistor * @@ -167,14 +91,14 @@ int print_spice_pass_transistor_subckt(std::fstream& fp, curr_bin_width = last_nmos_bin_width; } - status = print_spice_passgate_nmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(input_ports[1]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); + status = print_spice_generic_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(input_ports[1]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); if (CMD_EXEC_FATAL_ERROR == status) { return status; } @@ -254,14 +178,14 @@ int print_spice_transmission_gate_subckt(std::fstream& fp, curr_bin_width = last_pmos_bin_width; } - status = print_spice_passgate_pmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(input_ports[2]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); + status = print_spice_generic_pmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(input_ports[2]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); if (CMD_EXEC_FATAL_ERROR == status) { return status; } @@ -285,14 +209,14 @@ int print_spice_transmission_gate_subckt(std::fstream& fp, curr_bin_width = last_nmos_bin_width; } - status = print_spice_passgate_nmos_modeling(fp, - std::to_string(ibin), - circuit_lib.port_prefix(input_ports[0]), - circuit_lib.port_prefix(input_ports[1]), - circuit_lib.port_prefix(output_ports[0]), - tech_lib, - tech_model, - curr_bin_width); + status = print_spice_generic_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(input_ports[0]), + circuit_lib.port_prefix(input_ports[1]), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); if (CMD_EXEC_FATAL_ERROR == status) { return status; } diff --git a/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp index 140d68181..882d43f4d 100644 --- a/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp +++ b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp @@ -114,4 +114,77 @@ int print_spice_transistor_wrapper(NetlistManager& netlist_manager, return CMD_EXEC_SUCCESS; } +/******************************************************************** + * Generate the SPICE modeling for the PMOS part of a logic gate + * + * This function is created to be shared by pass-transistor and + * transmission-gate SPICE netlist writer + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * a PMOS. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +int print_spice_generic_pmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& gate_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Write transistor pairs using the technology model */ + fp << "Xpmos_" << trans_name_postfix << " "; + fp << input_port_name << " "; + fp << gate_port_name << " "; + fp << output_port_name << " "; + fp << "LVDD "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Generate the SPICE modeling for the NMOS part of a logic gate + * + * Note: + * - This function does NOT create a file + * but requires a file stream created + * - This function only output SPICE modeling for + * a NMOS. Any preprocessing or subckt definition should not be included! + *******************************************************************/ +int print_spice_generic_nmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& gate_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + fp << "Xnmos_" << trans_name_postfix << " "; + fp << input_port_name << " "; + fp << gate_port_name << " "; + fp << output_port_name << " "; + fp << "LGND "; + fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; + fp << " W=" << std::setprecision(10) << trans_width; + fp << "\n"; + + return CMD_EXEC_SUCCESS; +} + + } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_transistor_wrapper.h b/openfpga/src/fpga_spice/spice_transistor_wrapper.h index 86115c5c4..7b3bef199 100644 --- a/openfpga/src/fpga_spice/spice_transistor_wrapper.h +++ b/openfpga/src/fpga_spice/spice_transistor_wrapper.h @@ -20,6 +20,24 @@ int print_spice_transistor_wrapper(NetlistManager& netlist_manager, const TechnologyLibrary& tech_lib, const std::string& submodule_dir); +int print_spice_generic_pmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& gate_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width); + +int print_spice_generic_nmos_modeling(std::fstream& fp, + const std::string& trans_name_postfix, + const std::string& input_port_name, + const std::string& gate_port_name, + const std::string& output_port_name, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model, + const float& trans_width); + } /* end namespace openfpga */ #endif From e102e30d197578f77089a269c41793889dac8eba Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 16:20:21 -0600 Subject: [PATCH 06/25] [FPGA-SPICE] Add support for AND/OR logic gate --- openfpga/src/fpga_spice/spice_buffer.cpp | 24 +- openfpga/src/fpga_spice/spice_constants.h | 3 + .../src/fpga_spice/spice_essential_gates.cpp | 26 +- openfpga/src/fpga_spice/spice_logic_gate.cpp | 346 ++++++++++++++++++ openfpga/src/fpga_spice/spice_logic_gate.h | 38 ++ openfpga/src/fpga_spice/spice_passgate.cpp | 2 +- .../fpga_spice/spice_transistor_wrapper.cpp | 6 +- 7 files changed, 428 insertions(+), 17 deletions(-) create mode 100644 openfpga/src/fpga_spice/spice_logic_gate.cpp create mode 100644 openfpga/src/fpga_spice/spice_logic_gate.h diff --git a/openfpga/src/fpga_spice/spice_buffer.cpp b/openfpga/src/fpga_spice/spice_buffer.cpp index 59fa06d7f..824e3debc 100644 --- a/openfpga/src/fpga_spice/spice_buffer.cpp +++ b/openfpga/src/fpga_spice/spice_buffer.cpp @@ -63,15 +63,15 @@ int print_spice_powergated_inverter_pmos_modeling(std::fstream& fp, if (true == first_enb_pin) { fp << output_port_name << "_pmos_pg_" << power_gate_pin << " "; fp << generate_spice_port(enb_pin) << " "; - fp << "LVDD "; - fp << "LVDD "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; first_enb_pin = false; } else { VTR_ASSERT_SAFE(false == first_enb_pin); fp << output_port_name << "_pmos_pg_" << last_enb_pin << " "; fp << generate_spice_port(enb_pin) << " "; fp << output_port_name << "_pmos_pg_" << power_gate_pin << " "; - fp << "LVDD "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; } fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; @@ -86,7 +86,7 @@ int print_spice_powergated_inverter_pmos_modeling(std::fstream& fp, fp << output_port_name << " "; fp << input_port_name << " "; fp << output_port_name << "_pmos_pg_" << circuit_lib.pins(enb_port).back() << " "; - fp << "LVDD "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; @@ -129,15 +129,15 @@ int print_spice_powergated_inverter_nmos_modeling(std::fstream& fp, if (true == first_en_pin) { fp << output_port_name << "_nmos_pg_" << power_gate_pin << " "; fp << generate_spice_port(en_pin) << " "; - fp << "LGND "; - fp << "LGND "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; first_en_pin = false; } else { VTR_ASSERT_SAFE(false == first_en_pin); fp << output_port_name << "_nmos_pg_" << last_en_pin << " "; fp << circuit_lib.port_prefix(en_port) << " "; fp << output_port_name << "_nmos_pg_" << power_gate_pin << " "; - fp << "LGND "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; } fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; @@ -151,7 +151,7 @@ int print_spice_powergated_inverter_nmos_modeling(std::fstream& fp, fp << output_port_name << " "; fp << input_port_name << " "; fp << output_port_name << " _nmos_pg_" << circuit_lib.pins(en_port).back() << " "; - fp << "LGND "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; @@ -338,8 +338,8 @@ int print_spice_regular_inverter_pmos_modeling(std::fstream& fp, fp << "Xpmos_" << trans_name_postfix << " "; fp << output_port_name << " "; fp << input_port_name << " "; - fp << "LVDD "; - fp << "LVDD "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; @@ -374,8 +374,8 @@ int print_spice_regular_inverter_nmos_modeling(std::fstream& fp, fp << "Xnmos_" << trans_name_postfix << " "; fp << output_port_name << " "; fp << input_port_name << " "; - fp << "LGND "; - fp << "LGND "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 770e1b595..80a13af36 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -10,4 +10,7 @@ constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; constexpr char* ESSENTIALS_SPICE_FILE_NAME = "inv_buf_passgate.sp"; +constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; +constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; + #endif diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index fe554c328..6cad5c6f6 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -24,6 +24,7 @@ #include "spice_writer_utils.h" #include "spice_buffer.h" #include "spice_passgate.h" +#include "spice_logic_gate.h" #include "spice_essential_gates.h" /* begin namespace openfpga */ @@ -112,7 +113,7 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, continue; } - /* Now branch on netlist writing: for inverter/buffers */ + /* Now branch on netlist writing: for pass-gate logic */ if (CIRCUIT_MODEL_PASSGATE == circuit_lib.model_type(circuit_model)) { status = print_spice_passgate_subckt(fp, module_manager, module_id, @@ -126,6 +127,29 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, /* Finish, go to the next */ continue; } + + /* Now branch on netlist writing: for logic gate */ + if (CIRCUIT_MODEL_GATE == circuit_lib.model_type(circuit_model)) { + if (CIRCUIT_MODEL_GATE_AND == circuit_lib.gate_type(circuit_model)) { + status = print_spice_and_gate_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } else if (CIRCUIT_MODEL_GATE_OR == circuit_lib.gate_type(circuit_model)) { + status = print_spice_or_gate_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model, + tech_lib, tech_model); + } + + if (CMD_EXEC_FATAL_ERROR == status) { + break; + } + + /* Finish, go to the next */ + continue; + } + } /* Close file handler*/ diff --git a/openfpga/src/fpga_spice/spice_logic_gate.cpp b/openfpga/src/fpga_spice/spice_logic_gate.cpp new file mode 100644 index 000000000..6eba3da7a --- /dev/null +++ b/openfpga/src/fpga_spice/spice_logic_gate.cpp @@ -0,0 +1,346 @@ +/************************************************ + * This file includes functions on + * outputting SPICE netlists for logic gates: + * - N-input AND gate + * - N-input OR gate + ***********************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "circuit_library_utils.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_transistor_wrapper.h" +#include "spice_logic_gate.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Generate the SPICE subckt for a N-input AND gate + * + * Schematic + * + * VDD VDD VDD + * | | | + * - - - + * in0 -o|| in1 -o|| ... in[N-1] -o|| + * - - - + * | | | + * +----+-----+- ... -------------+ + * | + * - + * in0 -|| + * - + * | + * - + * in1 -|| + * - + * | + * ... + * | + * - + * in[N-1] -|| + * - + * | + * GND + *******************************************************************/ +int print_spice_and_gate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + 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); + + /* Make sure: + * There are at least 2 input ports and 1 output port, + * 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)); + } + + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = 1. /* TODO: allow users to define gate strength */ + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + + + /* Output the PMOS network */ + for (const auto& input_port : input_ports) { + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + status = print_spice_generic_pmos_modeling(fp, + std::to_string(ibin), + std::string(SPICE_SUBCKT_VDD_PORT_NAME), + circuit_lib.port_prefix(input_port), + circuit_lib.port_prefix(output_ports[0]), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = 1. /* TODO: allow users to define gate strength */ + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + /* Output the NMOS network */ + for (size_t input_id = 0; input_id < input_ports.size(); ++input_id) { + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + /* Depending on the input id, we assign different port names to source/drain */ + std::string source_port_name; + std::string drain_port_name; + + if (0 == input_id) { + /* First transistor should connect to the output port and an internal node */ + source_port_name = circuit_lib.port_prefix(output_ports[0]); + drain_port_name = std::string("internal_node") + std::to_string(input_id); + } else if (input_id == input_ports.size() - 1) { + /* Last transistor should connect to an internal node and GND */ + source_port_name = std::string("internal_node") + std::to_string(input_id - 1); + drain_port_name = std::string(SPICE_SUBCKT_GND_PORT_NAME); + } else { + /* Other transistors should connect to two internal nodes */ + source_port_name = std::string("internal_node") + std::to_string(input_id - 1); + drain_port_name = std::string("internal_node") + std::to_string(input_id); + } + + status = print_spice_generic_nmos_modeling(fp, + std::to_string(ibin), + source_port_name, + circuit_lib.port_prefix(input_ports[input_id]), + drain_port_name, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +/******************************************************************** + * Generate the SPICE subckt for a N-input OR gate + * + * Schematic + * + * + * VDD + * | + * - + * in0 -o|| + * - + * | + * - + * in1 -o|| + * - + * | + * ... + * | + * - + * in[N-1] -o|| + * - + * | + * +----+-----+- ... -------------+ + * | | | + * - - - + * in0 -|| in1 -|| ... in[N-1] -|| + * - - - + * | | | + * GND GND GND + *******************************************************************/ +int print_spice_or_gate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + 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); + + /* Make sure: + * There are at least 2 input ports and 1 output port, + * 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)); + } + + VTR_ASSERT( (1 == output_ports.size()) && (1 == circuit_lib.port_size(output_ports[0])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_pmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + float total_pmos_width = 1. /* TODO: allow users to define gate strength */ + * tech_lib.model_pn_ratio(tech_model) + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_PMOS); + int num_pmos_bins = std::ceil(total_pmos_width / regular_pmos_bin_width); + float last_pmos_bin_width = std::fmod(total_pmos_width, regular_pmos_bin_width); + + + /* Output the PMOS network */ + for (size_t input_id = 0; input_id < input_ports.size(); ++input_id) { + for (int ibin = 0; ibin < num_pmos_bins; ++ibin) { + float curr_bin_width = regular_pmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_pmos_bins - 1) + && (0. != last_pmos_bin_width)) { + curr_bin_width = last_pmos_bin_width; + } + + /* Depending on the input id, we assign different port names to source/drain */ + std::string source_port_name; + std::string drain_port_name; + + if (0 == input_id) { + /* First transistor should connect to the output port and an internal node */ + source_port_name = circuit_lib.port_prefix(output_ports[0]); + drain_port_name = std::string("internal_node") + std::to_string(input_id); + } else if (input_id == input_ports.size() - 1) { + /* Last transistor should connect to an internal node and GND */ + source_port_name = std::string("internal_node") + std::to_string(input_id - 1); + drain_port_name = std::string(SPICE_SUBCKT_VDD_PORT_NAME); + } else { + /* Other transistors should connect to two internal nodes */ + source_port_name = std::string("internal_node") + std::to_string(input_id - 1); + drain_port_name = std::string("internal_node") + std::to_string(input_id); + } + + status = print_spice_generic_pmos_modeling(fp, + std::to_string(ibin), + source_port_name, + circuit_lib.port_prefix(input_ports[input_id]), + drain_port_name, + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + /* Consider use size/bin to compact layout: + * Try to size transistors to the max width for each bin + * The last bin may not reach the max width + */ + float regular_nmos_bin_width = tech_lib.transistor_model_max_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + float total_nmos_width = 1. /* TODO: allow users to define gate strength */ + * tech_lib.transistor_model_min_width(tech_model, TECH_LIB_TRANSISTOR_NMOS); + int num_nmos_bins = std::ceil(total_nmos_width / regular_nmos_bin_width); + float last_nmos_bin_width = std::fmod(total_nmos_width, regular_nmos_bin_width); + + /* Output the NMOS network */ + for (const auto& input_port : input_ports) { + for (int ibin = 0; ibin < num_nmos_bins; ++ibin) { + float curr_bin_width = regular_nmos_bin_width; + /* For last bin, we need an irregular width */ + if ((ibin == num_nmos_bins - 1) + && (0. != last_nmos_bin_width)) { + curr_bin_width = last_nmos_bin_width; + } + + status = print_spice_generic_nmos_modeling(fp, + std::to_string(ibin), + circuit_lib.port_prefix(output_ports[0]), + circuit_lib.port_prefix(input_port), + std::string(SPICE_SUBCKT_GND_PORT_NAME), + tech_lib, + tech_model, + curr_bin_width); + if (CMD_EXEC_FATAL_ERROR == status) { + return status; + } + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_logic_gate.h b/openfpga/src/fpga_spice/spice_logic_gate.h new file mode 100644 index 000000000..4a72c803f --- /dev/null +++ b/openfpga/src/fpga_spice/spice_logic_gate.h @@ -0,0 +1,38 @@ +#ifndef SPICE_LOGIC_GATE_H +#define SPICE_LOGIC_GATE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "module_manager.h" +#include "circuit_library.h" +#include "technology_library.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_and_gate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model); + +int print_spice_or_gate_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model, + const TechnologyLibrary& tech_lib, + const TechnologyModelId& tech_model); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_passgate.cpp b/openfpga/src/fpga_spice/spice_passgate.cpp index d26ac5bfb..cf3702ba7 100644 --- a/openfpga/src/fpga_spice/spice_passgate.cpp +++ b/openfpga/src/fpga_spice/spice_passgate.cpp @@ -248,7 +248,7 @@ int print_spice_passgate_subckt(std::fstream& fp, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); - } else if (CIRCUIT_MODEL_PASS_GATE_TRANSMISSION == circuit_lib.is_power_gated(circuit_model)) { + } else if (CIRCUIT_MODEL_PASS_GATE_TRANSMISSION == circuit_lib.pass_gate_logic_type(circuit_model)) { status = print_spice_transmission_gate_subckt(fp, module_manager, module_id, circuit_lib, circuit_model, diff --git a/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp index 882d43f4d..6a90b0c96 100644 --- a/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp +++ b/openfpga/src/fpga_spice/spice_transistor_wrapper.cpp @@ -96,7 +96,7 @@ int print_spice_transistor_wrapper(NetlistManager& netlist_manager, continue; } /* Write a wrapper for the transistor model */ - if (CMD_EXEC_SUCCESS == print_spice_transistor_model_wrapper(fp, tech_lib, model)) { + if (CMD_EXEC_SUCCESS != print_spice_transistor_model_wrapper(fp, tech_lib, model)) { return CMD_EXEC_FATAL_ERROR; } } @@ -144,7 +144,7 @@ int print_spice_generic_pmos_modeling(std::fstream& fp, fp << input_port_name << " "; fp << gate_port_name << " "; fp << output_port_name << " "; - fp << "LVDD "; + fp << SPICE_SUBCKT_VDD_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_PMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; @@ -178,7 +178,7 @@ int print_spice_generic_nmos_modeling(std::fstream& fp, fp << input_port_name << " "; fp << gate_port_name << " "; fp << output_port_name << " "; - fp << "LGND "; + fp << SPICE_SUBCKT_GND_PORT_NAME << " "; fp << tech_lib.transistor_model_name(tech_model, TECH_LIB_TRANSISTOR_NMOS) << TRANSISTOR_WRAPPER_POSTFIX; fp << " W=" << std::setprecision(10) << trans_width; fp << "\n"; From 26a0a769eabe06658c576d601b91c5e7cb3bdd41 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 16:45:26 -0600 Subject: [PATCH 07/25] [FPGA-SPICE] Split essential gate SPICE netlists into separated files --- openfpga/src/fpga_spice/spice_constants.h | 1 - .../src/fpga_spice/spice_essential_gates.cpp | 78 +++++++++++-------- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 80a13af36..48482d703 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -8,7 +8,6 @@ constexpr char* SPICE_NETLIST_FILE_POSTFIX = ".sp"; constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; -constexpr char* ESSENTIALS_SPICE_FILE_NAME = "inv_buf_passgate.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 6cad5c6f6..780a31776 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -43,21 +43,6 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, const TechnologyLibrary& tech_lib, const std::map& circuit_tech_binding, const std::string& submodule_dir) { - std::string spice_fname = submodule_dir + std::string(ESSENTIALS_SPICE_FILE_NAME); - - std::fstream fp; - - /* Create the file stream */ - fp.open(spice_fname, std::fstream::out | std::fstream::trunc); - /* Check if the file stream if valid or not */ - check_file_stream(spice_fname.c_str(), fp); - - /* Create file */ - VTR_LOG("Generating SPICE netlist '%s' for essential gates...", - spice_fname.c_str()); - - print_spice_file_header(fp, std::string("Essential gates")); - int status = CMD_EXEC_SUCCESS; /* Iterate over the circuit models */ @@ -89,6 +74,26 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, VTR_ASSERT(TECH_LIB_MODEL_TRANSISTOR == tech_lib.model_type(tech_model)); } + /* Create file stream */ + std::string spice_fname = submodule_dir + circuit_lib.model_name(circuit_model); + + std::fstream fp; + + /* Create the file stream */ + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + /* Check if the file stream if valid or not */ + check_file_stream(spice_fname.c_str(), fp); + + /* Create file */ + VTR_LOG("Generating SPICE netlist '%s' for circuit model '%s'...", + spice_fname.c_str(), + circuit_lib.model_name(circuit_model).c_str()); + + print_spice_file_header(fp, circuit_lib.model_name(circuit_model)); + + /* A flag to record if any logic has been filled to the netlist */ + bool netlist_filled = false; + /* Now branch on netlist writing: for inverter/buffers */ if (CIRCUIT_MODEL_INVBUF == circuit_lib.model_type(circuit_model)) { if (CIRCUIT_MODEL_BUF_INV == circuit_lib.buffer_type(circuit_model)) { @@ -97,20 +102,19 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); + netlist_filled = true; } else { VTR_ASSERT(CIRCUIT_MODEL_BUF_BUF == circuit_lib.buffer_type(circuit_model)); status = print_spice_buffer_subckt(fp, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); + netlist_filled = true; } if (CMD_EXEC_FATAL_ERROR == status) { break; } - - /* Finish, go to the next */ - continue; } /* Now branch on netlist writing: for pass-gate logic */ @@ -119,13 +123,11 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); + netlist_filled = true; if (CMD_EXEC_FATAL_ERROR == status) { break; } - - /* Finish, go to the next */ - continue; } /* Now branch on netlist writing: for logic gate */ @@ -135,33 +137,41 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); + netlist_filled = true; } else if (CIRCUIT_MODEL_GATE_OR == circuit_lib.gate_type(circuit_model)) { status = print_spice_or_gate_subckt(fp, module_manager, module_id, circuit_lib, circuit_model, tech_lib, tech_model); + netlist_filled = true; } if (CMD_EXEC_FATAL_ERROR == status) { break; } - - /* Finish, go to the next */ - continue; } + /* Check if the netlist has been filled or not. + * If not, flag a fatal error + */ + if (false == netlist_filled) { + VTR_LOG_ERROR("Cannot auto-generate netlist for circuit model '%s'!\n\tThe circuit topology is not supported yet!\n", + circuit_lib.model_name(circuit_model).c_str()); + status = CMD_EXEC_FATAL_ERROR; + break; + } + + /* Close file handler*/ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); } - /* Close file handler*/ - fp.close(); - - /* Add fname to the netlist name list */ - NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); - VTR_ASSERT(NetlistId::INVALID() != nlist_id); - netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); - - VTR_LOG("Done\n"); - return status; } From 1b2762386cfa5b56bd67ee2406f5fd7b465140e4 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 16:52:30 -0600 Subject: [PATCH 08/25] [FPGA-SPICE] Bug fix for essential gate netlist writing --- .../src/fpga_spice/spice_essential_gates.cpp | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 780a31776..107077bcc 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -52,6 +52,15 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, continue; } + /* Output only the model type is supported in auto-generation */ + if ( (CIRCUIT_MODEL_INVBUF != circuit_lib.model_type(circuit_model)) + && (CIRCUIT_MODEL_PASSGATE != circuit_lib.model_type(circuit_model)) + && (CIRCUIT_MODEL_CHAN_WIRE != circuit_lib.model_type(circuit_model)) + && (CIRCUIT_MODEL_WIRE != circuit_lib.model_type(circuit_model)) + && (CIRCUIT_MODEL_GATE != circuit_lib.model_type(circuit_model))) { + continue; + } + /* Spot module id */ const ModuleId& module_id = module_manager.find_module(circuit_lib.model_name(circuit_model)); @@ -75,7 +84,7 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, } /* Create file stream */ - std::string spice_fname = submodule_dir + circuit_lib.model_name(circuit_model); + std::string spice_fname = submodule_dir + circuit_lib.model_name(circuit_model) + std::string(SPICE_NETLIST_FILE_POSTFIX); std::fstream fp; @@ -151,6 +160,21 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, } } + /* Now branch on netlist writing: for routing channel wires */ + if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) { + netlist_filled = true; + if (CMD_EXEC_FATAL_ERROR == status) { + break; + } + } + /* Now branch on netlist writing: for regular wires */ + if (CIRCUIT_MODEL_WIRE == circuit_lib.model_type(circuit_model)) { + netlist_filled = true; + if (CMD_EXEC_FATAL_ERROR == status) { + break; + } + } + /* Check if the netlist has been filled or not. * If not, flag a fatal error */ From 82e137cbe40c2edf652d8a4df4284660727cb4d2 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 19:31:16 -0600 Subject: [PATCH 09/25] [FPGA-SPICE] Add wire module SPICE writer --- .../src/fpga_spice/spice_essential_gates.cpp | 14 +- openfpga/src/fpga_spice/spice_wire.cpp | 408 ++++++++++++++++++ openfpga/src/fpga_spice/spice_wire.h | 27 ++ .../src/fpga_spice/spice_writer_utils.cpp | 155 +++++++ openfpga/src/fpga_spice/spice_writer_utils.h | 20 + 5 files changed, 615 insertions(+), 9 deletions(-) create mode 100644 openfpga/src/fpga_spice/spice_wire.cpp create mode 100644 openfpga/src/fpga_spice/spice_wire.h diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 107077bcc..eafe37a24 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -25,6 +25,7 @@ #include "spice_buffer.h" #include "spice_passgate.h" #include "spice_logic_gate.h" +#include "spice_wire.h" #include "spice_essential_gates.h" /* begin namespace openfpga */ @@ -55,7 +56,6 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, /* Output only the model type is supported in auto-generation */ if ( (CIRCUIT_MODEL_INVBUF != circuit_lib.model_type(circuit_model)) && (CIRCUIT_MODEL_PASSGATE != circuit_lib.model_type(circuit_model)) - && (CIRCUIT_MODEL_CHAN_WIRE != circuit_lib.model_type(circuit_model)) && (CIRCUIT_MODEL_WIRE != circuit_lib.model_type(circuit_model)) && (CIRCUIT_MODEL_GATE != circuit_lib.model_type(circuit_model))) { continue; @@ -160,15 +160,11 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, } } - /* Now branch on netlist writing: for routing channel wires */ - if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) { - netlist_filled = true; - if (CMD_EXEC_FATAL_ERROR == status) { - break; - } - } - /* Now branch on netlist writing: for regular wires */ + /* Now branch on netlist writing: for wires */ if (CIRCUIT_MODEL_WIRE == circuit_lib.model_type(circuit_model)) { + status = print_spice_wire_subckt(fp, + module_manager, module_id, + circuit_lib, circuit_model); netlist_filled = true; if (CMD_EXEC_FATAL_ERROR == status) { break; diff --git a/openfpga/src/fpga_spice/spice_wire.cpp b/openfpga/src/fpga_spice/spice_wire.cpp new file mode 100644 index 000000000..097515899 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_wire.cpp @@ -0,0 +1,408 @@ +/************************************************ + * This file includes functions on + * outputting SPICE netlists for routing wires: + * - regular wires (1 input and 1 output) + * - routing track wires (1 input and 2 outputs) + ***********************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "circuit_library_utils.h" +#include "build_module_graph_utils.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_wire.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print SPICE modeling for pie-type RC network + * + * Schematic + * middle out + * | + * in ---wwww----wwww--- ... --wwww---out + * | | | | + * = = = = + * | | | | + * GND GND GND GND + *******************************************************************/ +static +int print_spice_wire_pi_type_rc_modeling(std::fstream& fp, + const std::string& input_port_name, + const std::string& output_port_name, + const std::string& middle_output_port_name, + const float& res_total, + const float& cap_total, + const size_t& num_levels) { + + /* Determine the resistance and capacitance of each level*/ + float res_per_level = res_total / ((float)(2 * num_levels)); + float cap_per_level = cap_total / ((float)(2 * num_levels)); + + /* All the resistance and capacitance value should be larger than or equal to zero*/ + VTR_ASSERT(0. <= res_per_level); + VTR_ASSERT(0. <= cap_per_level); + + for (size_t ilvl = 0; ilvl < num_levels; ++ilvl) { + /* Print the first capacitor if this is the first level */ + if ((0 == ilvl) && (0. < cap_per_level)) { + print_spice_capacitor(fp, input_port_name, std::string(SPICE_SUBCKT_GND_PORT_NAME), cap_per_level); + } + /* Output a regular RC pair + * + * midnode + * ^ + * | + * ------+-ww-+-ww-+------ + * | | + * = = + * | | + * GND GND + */ + + std::string lvl_input_port_name = std::string("rc_network_node") + std::to_string(ilvl); + if (0 == ilvl) { + lvl_input_port_name = input_port_name; + } + + std::string lvl_middle_port_name = std::string("rc_network_midnode") + std::to_string(ilvl); + + std::string lvl_output_port_name = std::string("rc_network_node") + std::to_string(ilvl + 1); + if (ilvl == num_levels - 1) { + lvl_output_port_name = output_port_name; + } + + print_spice_resistor(fp, lvl_input_port_name, lvl_middle_port_name, res_per_level); + print_spice_resistor(fp, lvl_middle_port_name, lvl_output_port_name, res_per_level); + + /* Last level only require 1 unit of cap_per_level */ + float cap_curr_level = 2. * cap_per_level; + if (ilvl == num_levels - 1) { + cap_curr_level = cap_per_level; + } + + if (0. < cap_curr_level) { + print_spice_capacitor(fp, lvl_output_port_name, std::string(SPICE_SUBCKT_GND_PORT_NAME), cap_curr_level); + } + } + + /* If the middle output is required, create a short connection to + * - when the number of levels is odd + * + * middle_output + * ^ + * | + * ---ww-+-ww-+-ww-+-ww--- + * | | + * = = + * | | + * GND GND + * + * - when the number of levels is even: + * + * middle_output + * ^ + * | + * -+-ww--ww-+-ww--ww-+- + * | | | + * = = = + * | | | + * GND GND GND + * + */ + if (!middle_output_port_name.empty()) { + print_spice_comment(fp, std::string("Connect to the middle output")); + std::string rc_midnode_name = std::string("rc_network_node") + std::to_string(num_levels / 2 + 1); + if (1 == num_levels % 2) { + rc_midnode_name = std::string("rc_network_midnode") + std::to_string(num_levels / 2); + } + print_spice_short_connection(fp, rc_midnode_name, middle_output_port_name); + } + + return CMD_EXEC_SUCCESS; +} + +/******************************************************************** + * Print SPICE modeling for T-type RC network + * + * Schematic + * middle out + * | + * in ---ww-+--ww--+--ww--+--ww--- ... --ww--+--ww--- out + * | | | | + * = = = = + * | | | | + * GND GND GND GND + *******************************************************************/ +static +int print_spice_wire_t_type_rc_modeling(std::fstream& fp, + const std::string& input_port_name, + const std::string& output_port_name, + const std::string& middle_output_port_name, + const float& res_total, + const float& cap_total, + const size_t& num_levels) { + + /* Determine the resistance and capacitance of each level*/ + float res_per_level = res_total / ((float)(2 * num_levels)); + float cap_per_level = cap_total / ((float)(num_levels)); + + /* All the resistance and capacitance value should be larger than or equal to zero*/ + VTR_ASSERT(0. <= res_per_level); + VTR_ASSERT(0. <= cap_per_level); + + for (size_t ilvl = 0; ilvl < num_levels; ++ilvl) { + /* Output a regular RC pair + * + * midnode + * ^ + * | + * --------ww-+-ww-------- + * | + * = + * | + * GND + */ + + std::string lvl_input_port_name = std::string("rc_network_node") + std::to_string(ilvl); + if (0 == ilvl) { + lvl_input_port_name = input_port_name; + } + + std::string lvl_middle_port_name = std::string("rc_network_midnode") + std::to_string(ilvl); + + std::string lvl_output_port_name = std::string("rc_network_node") + std::to_string(ilvl + 1); + if (ilvl == num_levels - 1) { + lvl_output_port_name = output_port_name; + } + + print_spice_resistor(fp, lvl_input_port_name, lvl_middle_port_name, res_per_level); + print_spice_resistor(fp, lvl_middle_port_name, lvl_output_port_name, res_per_level); + + if (0. < cap_per_level) { + print_spice_capacitor(fp, lvl_middle_port_name, std::string(SPICE_SUBCKT_GND_PORT_NAME), cap_per_level); + } + } + + /* If the middle output is required, create a short connection to + * - when the number of levels is even + * + * middle_output + * ^ + * | + * ---ww-+-ww-+-ww-+-ww--- + * | | + * = = + * | | + * GND GND + * + * - when the number of levels is odd: + * + * middle_output + * ^ + * | + * -+-ww--ww-+-ww--ww-+- + * | | | + * = = = + * | | | + * GND GND GND + * + */ + if (!middle_output_port_name.empty()) { + print_spice_comment(fp, std::string("Connect to the middle output")); + std::string rc_midnode_name = std::string("rc_network_midnode") + std::to_string(num_levels / 2); + if (0 == num_levels % 2) { + rc_midnode_name = std::string("rc_network_node") + std::to_string(num_levels / 2 + 1); + } + print_spice_short_connection(fp, rc_midnode_name, middle_output_port_name); + } + + return CMD_EXEC_SUCCESS; +} + + +/******************************************************************** + * Generate the SPICE subckt for a regular wire + * + * Schematic + * + * Middle output (only for routing track wires) + * ^ + * | + * +--------------------+ +---------------+ +--------------------+ + * in ->| Inverter or buffer |--->| RC Network |---->| Inverter or buffer |---> out + * | Optional | | | | Optional | + * +--------------------- +---------------+ +--------------------+ + * + *******************************************************************/ +int print_spice_wire_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model) { + + if (false == valid_file_stream(fp)) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Find the input and output ports: + * we do NOT support global ports here, + * it should be handled in another type of inverter subckt (power-gated) + */ + 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); + + /* Make sure: + * There are 1 input ports 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])) ); + + int status = CMD_EXEC_SUCCESS; + + /* Print the inverter subckt definition */ + print_spice_subckt_definition(fp, module_manager, module_id); + + std::string input_port_name = circuit_lib.port_prefix(input_ports[0]); + std::string output_port_name = circuit_lib.port_prefix(output_ports[0]); + std::string middle_output_port_name; + if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) { + middle_output_port_name = std::string("middle") + output_port_name; + } + + std::string rc_ntwk_input_port_name = std::string("rc_network_node") + std::to_string(0); + std::string rc_ntwk_output_port_name = std::string("rc_network_node") + std::to_string(circuit_lib.wire_num_level(circuit_model) - 1); + std::string rc_ntwk_middle_output_port_name; + if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) { + rc_ntwk_middle_output_port_name = std::string("middle") + rc_ntwk_output_port_name; + } + + ModulePortId wire_module_input_port = module_manager.find_module_port(module_id, input_port_name); + ModulePortId wire_module_output_port = module_manager.find_module_port(module_id, output_port_name); + ModulePortId wire_module_middle_output_port = ModulePortId::INVALID(); + if (CIRCUIT_MODEL_CHAN_WIRE == circuit_lib.model_type(circuit_model)) { + wire_module_middle_output_port = module_manager.find_module_port(module_id, output_port_name); + } + + /* Add input buffer: + * - There is a valid buffer model, instanciate it + * - There is no buffer, set a short connection + */ + if (circuit_lib.input_buffer_model(circuit_model)) { + std::string instance_name = std::string("input_buffer"); + std::map port2port_name_map; + + ModuleId buffer_module = module_manager.find_module(circuit_lib.model_name(circuit_lib.input_buffer_model(circuit_model))); + VTR_ASSERT(true == module_manager.valid_module_id(buffer_module)); + + ModulePortId module_input_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_INPUT); + ModulePortId module_output_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_OUTPUT); + + /* Port size should be 1 ! */ + VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_input_port_id).get_width()); + VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_output_port_id).get_width()); + + port2port_name_map[module_manager.module_port(buffer_module, module_input_port_id).get_name()] = module_manager.module_port(module_id, wire_module_input_port); + port2port_name_map[module_manager.module_port(buffer_module, module_output_port_id).get_name()] = BasicPort(rc_ntwk_input_port_name, 1); + + print_spice_subckt_instance(fp, + module_manager, + buffer_module, + instance_name, + port2port_name_map); + } else { + print_spice_short_connection(fp, circuit_lib.port_prefix(input_ports[0]), rc_ntwk_input_port_name); + } + + /* Determine which type of model to print*/ + switch (circuit_lib.wire_type(circuit_model)) { + case WIRE_MODEL_PI: + status = print_spice_wire_pi_type_rc_modeling(fp, + rc_ntwk_input_port_name, + rc_ntwk_output_port_name, + rc_ntwk_middle_output_port_name, + circuit_lib.wire_r(circuit_model), + circuit_lib.wire_c(circuit_model), + circuit_lib.wire_num_level(circuit_model)); + break; + case WIRE_MODEL_T: + status = print_spice_wire_t_type_rc_modeling(fp, + rc_ntwk_input_port_name, + rc_ntwk_output_port_name, + rc_ntwk_middle_output_port_name, + circuit_lib.wire_r(circuit_model), + circuit_lib.wire_c(circuit_model), + circuit_lib.wire_num_level(circuit_model)); + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Unsupport wire model type for circuit model '%s.\n", + circuit_lib.model_name(circuit_model).c_str()); + return CMD_EXEC_FATAL_ERROR; + } + + /* Add output buffer: + * - There is a valid buffer model, instanciate it + * - There is no buffer, set a short connection + */ + if (circuit_lib.output_buffer_model(circuit_model)) { + std::string instance_name = std::string("output_buffer"); + std::map port2port_name_map; + + ModuleId buffer_module = module_manager.find_module(circuit_lib.model_name(circuit_lib.output_buffer_model(circuit_model))); + VTR_ASSERT(true == module_manager.valid_module_id(buffer_module)); + + ModulePortId module_input_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_INPUT); + ModulePortId module_output_port_id = find_inverter_buffer_module_port(module_manager, buffer_module, circuit_lib, circuit_model, CIRCUIT_MODEL_PORT_OUTPUT); + + /* Port size should be 1 ! */ + VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_input_port_id).get_width()); + VTR_ASSERT(1 == module_manager.module_port(buffer_module, module_output_port_id).get_width()); + + port2port_name_map[module_manager.module_port(buffer_module, module_input_port_id).get_name()] = BasicPort(rc_ntwk_output_port_name, 1); + port2port_name_map[module_manager.module_port(buffer_module, module_output_port_id).get_name()] = module_manager.module_port(module_id, wire_module_output_port); + + print_spice_subckt_instance(fp, + module_manager, + buffer_module, + instance_name, + port2port_name_map); + + if (!rc_ntwk_middle_output_port_name.empty()) { + instance_name = std::string("middle_output_buffer"); + port2port_name_map[module_manager.module_port(buffer_module, module_output_port_id).get_name()] = module_manager.module_port(module_id, wire_module_middle_output_port); + + print_spice_subckt_instance(fp, + module_manager, + buffer_module, + instance_name, + port2port_name_map); + } + } else { + print_spice_short_connection(fp, rc_ntwk_output_port_name, circuit_lib.port_prefix(output_ports[0])); + if (!rc_ntwk_middle_output_port_name.empty()) { + print_spice_short_connection(fp, rc_ntwk_middle_output_port_name, circuit_lib.port_prefix(output_ports[0])); + } + } + + print_spice_subckt_end(fp, module_manager.module_name(module_id)); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_wire.h b/openfpga/src/fpga_spice/spice_wire.h new file mode 100644 index 000000000..5af9542a0 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_wire.h @@ -0,0 +1,27 @@ +#ifndef SPICE_WIRE_H +#define SPICE_WIRE_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 { + +int print_spice_wire_subckt(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const CircuitLibrary& circuit_lib, + const CircuitModelId& circuit_model); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_writer_utils.cpp b/openfpga/src/fpga_spice/spice_writer_utils.cpp index 89b82a027..889ee8c19 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.cpp +++ b/openfpga/src/fpga_spice/spice_writer_utils.cpp @@ -168,4 +168,159 @@ void print_spice_subckt_end(std::fstream& fp, fp << std::endl; } +/************************************************ + * Print a resistor in SPICE syntax + ***********************************************/ +void print_spice_resistor(std::fstream& fp, + const std::string& input_port, + const std::string& output_port, + const float& resistance) { + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Set an unique name to the resistor */ + fp << "R" << input_port << "_to_" << output_port; + fp << " " << input_port; + fp << " " << output_port; + fp << " " << std::setprecision(10) << resistance; + fp << std::endl; +} + +/************************************************ + * Print a capacitor in SPICE syntax + ***********************************************/ +void print_spice_capacitor(std::fstream& fp, + const std::string& input_port, + const std::string& output_port, + const float& capacitance) { + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Set an unique name to the capacitor */ + fp << "C" << input_port << "_to_" << output_port; + fp << " " << input_port; + fp << " " << output_port; + fp << " " << std::setprecision(10) << capacitance; + fp << std::endl; +} + +/************************************************ + * Print a short-connected wire using zero resistance in SPICE syntax + ***********************************************/ +void print_spice_short_connection(std::fstream& fp, + const std::string& input_port, + const std::string& output_port) { + print_spice_resistor(fp, input_port, output_port, 0.); +} + +/******************************************************************** + * Print an instance in SPICE format (a generic version) + * This function will require user to provide an instance name + * + * This function will output the port map by referring to a port-to-port + * mapping: + * -> + * The key of the port-to-port mapping is the port name of the module: + * The value of the port-to-port mapping is the port information of the instance + * With link between module and instance, the function can output a SPICE + * instance easily, by following the define port sequence of the module + * + * Note that, it is not necessary that the port-to-port mapping + * covers all the module ports. + * Any instance/module port which are not specified in the port-to-port + * mapping will be output by the module port name. + *******************************************************************/ +void print_spice_subckt_instance(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const std::string& instance_name, + const std::map& port2port_name_map) { + + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Check: all the key ports in the port2port_name_map does exist in the child module */ + for (const auto& kv : port2port_name_map) { + ModulePortId module_port_id = module_manager.find_module_port(module_id, kv.first); + VTR_ASSERT(ModulePortId::INVALID() != module_port_id); + } + + /* Print instance name */ + std::string instance_head_line = "X " + instance_name + " "; + fp << instance_head_line; + + /* Port sequence: global, inout, input, output and clock ports, */ + bool fit_one_line = true; + bool new_line = false; + size_t pin_cnt = 0; + for (int port_type = ModuleManager::MODULE_GLOBAL_PORT; + port_type < ModuleManager::NUM_MODULE_PORT_TYPES; + ++port_type) { + for (const auto& port : module_manager.module_ports_by_type(module_id, static_cast(port_type))) { + /* Deposit a default port name */ + BasicPort port_to_print = port; + /* Try to find the instanced port name in the name map */ + auto port_search_result = port2port_name_map.find(port.get_name()); + if (port_search_result != port2port_name_map.end()) { + /* Found it, we assign the port name */ + /* TODO: make sure the port width matches! */ + ModulePortId module_port_id = module_manager.find_module_port(module_id, port.get_name()); + /* Get the port from module */ + BasicPort module_port = module_manager.module_port(module_id, module_port_id); + VTR_ASSERT(module_port.get_width() == port_search_result->second.get_width()); + + port_to_print = port_search_result->second; + } + + /* Print port: only the port name is enough */ + for (const auto& pin : port_to_print.pins()) { + + if (true == new_line) { + std::string port_whitespace(instance_head_line.length() - 2, ' '); + fp << "+ " << port_whitespace; + } + + if (0 != pin_cnt) { + write_space_to_file(fp, 1); + } + + BasicPort port_pin(port.get_name(), pin, pin); + + /* For single-bit port, + * we can print the port name directly + */ + bool omit_pin_zero = false; + if ((1 == port.pins().size()) + && (0 == pin)) { + omit_pin_zero = true; + } + + fp << generate_spice_port(port_pin, omit_pin_zero); + + /* Increase the counter */ + pin_cnt++; + + /* Currently we limit 10 ports per line to keep a clean netlist */ + new_line = false; + if (10 == pin_cnt) { + pin_cnt = 0; + fp << std::endl; + new_line = true; + fit_one_line = false; + } + } + } + } + + /* Print module name: + * if port print cannot fit one line, we create a new line for the module for a clean format + */ + if (false == fit_one_line) { + fp << std::endl; + fp << "+"; + } + write_space_to_file(fp, 1); + fp << module_manager.module_name(module_id); + + /* Print an end to the instance */ + fp << std::endl; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_writer_utils.h b/openfpga/src/fpga_spice/spice_writer_utils.h index 3da2a6339..b01d5a35c 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.h +++ b/openfpga/src/fpga_spice/spice_writer_utils.h @@ -48,6 +48,26 @@ void print_spice_subckt_definition(std::fstream& fp, void print_spice_subckt_end(std::fstream& fp, const std::string& module_name); +void print_spice_resistor(std::fstream& fp, + const std::string& input_port, + const std::string& output_port, + const float& resistance); + +void print_spice_capacitor(std::fstream& fp, + const std::string& input_port, + const std::string& output_port, + const float& capacitance); + +void print_spice_short_connection(std::fstream& fp, + const std::string& input_port, + const std::string& output_port); + +void print_spice_subckt_instance(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id, + const std::string& instance_name, + const std::map& port2port_name_map); + } /* end namespace openfpga */ #endif From 15df9b3893515a66f392f23197444de90b62e630 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sat, 19 Sep 2020 23:01:44 -0600 Subject: [PATCH 10/25] [FPGA-SPICE] Add SPICE subcircuit writer --- .../src/fpga_spice/spice_subckt_writer.cpp | 449 ++++++++++++++++++ openfpga/src/fpga_spice/spice_subckt_writer.h | 23 + 2 files changed, 472 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_subckt_writer.cpp create mode 100644 openfpga/src/fpga_spice/spice_subckt_writer.h diff --git a/openfpga/src/fpga_spice/spice_subckt_writer.cpp b/openfpga/src/fpga_spice/spice_subckt_writer.cpp new file mode 100644 index 000000000..c90c0b5ac --- /dev/null +++ b/openfpga/src/fpga_spice/spice_subckt_writer.cpp @@ -0,0 +1,449 @@ +/******************************************************************** + * This file includes functions to write a SPICE module + * based on its definition in Module Manager + * + * Note that SPICE 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 "openfpga_naming.h" + +#include "module_manager_utils.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Generate the name of a local wire for a undriven port inside SPICE + * module + *******************************************************************/ +static +std::string generate_spice_undriven_local_wire_name(const ModuleManager& module_manager, + const ModuleId& parent, + const ModuleId& child, + const size_t& instance_id, + const ModulePortId& child_port_id) { + std::string wire_name; + if (!module_manager.instance_name(parent, child, instance_id).empty()) { + wire_name = module_manager.instance_name(parent, child, instance_id); + } else { + wire_name = module_manager.module_name(parent) + std::string("_") + std::to_string(instance_id); + wire_name += std::string("_"); + } + + wire_name += std::string("_undriven_"); + wire_name += module_manager.module_port(child, child_port_id).get_name(); + + return wire_name; +} + + +/******************************************************************** + * Name a net for a local wire for a SPICE subckt + * 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_spice_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); +} + +/******************************************************************** + * Print a SPICE 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_spice_subckt_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 */ + VTR_ASSERT(src_port.get_width() == sink_port.get_width()); + for (size_t ipin = 0; ipin < src_port.pins().size(); ++ipin) { + BasicPort src_spice_pin(src_port.get_name(), src_port.pins()[ipin], src_port.pins()[ipin]); + BasicPort sink_spice_pin(sink_port.get_name(), sink_port.pins()[ipin], sink_port.pins()[ipin]); + print_spice_short_connection(fp, + generate_spice_port(src_spice_pin), + generate_spice_port(sink_spice_pin)); + } + } +} + + +/******************************************************************** + * Print a SPICE 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_spice_subckt_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_spice_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_spice_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 */ + VTR_ASSERT(src_port.get_width() == sink_port.get_width()); + for (size_t ipin = 0; ipin < src_port.pins().size(); ++ipin) { + BasicPort src_spice_pin(src_port.get_name(), src_port.pins()[ipin], src_port.pins()[ipin]); + BasicPort sink_spice_pin(sink_port.get_name(), sink_port.pins()[ipin], sink_port.pins()[ipin]); + print_spice_short_connection(fp, + generate_spice_port(src_spice_pin), + generate_spice_port(sink_spice_pin)); + } + } + } +} + +/******************************************************************** + * Print short connections inside a SPICE 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 SPICE instances + * Therefore, they are covered in this function + * + * module + * +-----------------------------+ + * | | + * inputA--->|---------------------------->|--->outputB + * | | + * | | + * | | + * +-----------------------------+ + *******************************************************************/ +static +void print_spice_subckt_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_spice_comment(fp, std::string("Local connection due to Wire " + std::to_string(size_t(module_net)))); + print_spice_subckt_local_short_connection(fp, module_manager, module_id, module_net); + } +} + +/******************************************************************** + * Print output short connections inside a SPICE 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 SPICE instances + * Therefore, they are covered in this function + * + * module + * +-----------------------------+ + * | + * src------>+--------------->|--->outputA + * | | + * | | + * +--------------->|--->outputB + * +-----------------------------+ + *******************************************************************/ +static +void print_spice_subckt_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_spice_subckt_output_short_connection(fp, module_manager, module_id, module_net); + } +} + +/******************************************************************** + * Write a SPICE 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_spice_instance_to_file(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& parent_module, + const ModuleId& child_module, + const size_t& instance_id) { + /* Ensure a valid file stream */ + VTR_ASSERT(true == valid_file_stream(fp)); + + /* Print instance name: + * if we have an instance name, use it; + * if not, we use a default name _ + */ + std::string instance_head_line = "X "; + if (true == module_manager.instance_name(parent_module, child_module, instance_id).empty()) { + instance_head_line += generate_instance_name(module_manager.module_name(child_module), instance_id); + } else { + instance_head_line += module_manager.instance_name(parent_module, child_module, instance_id); + } + instance_head_line += " "; + fp << instance_head_line; + + /* Port sequence: global, inout, input, output and clock ports, */ + bool fit_one_line = true; + bool new_line = false; + size_t pin_cnt = 0; + for (int port_type = ModuleManager::MODULE_GLOBAL_PORT; + port_type < ModuleManager::NUM_MODULE_PORT_TYPES; + ++port_type) { + for (const auto& child_port_id : module_manager.module_port_ids_by_type(child_module, static_cast(port_type))) { + + BasicPort child_port = module_manager.module_port(child_module, child_port_id); + + /* 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_spice_undriven_local_wire_name(module_manager, parent_module, child_module, instance_id, child_port_id)); + instance_port.set_width(child_pin, child_pin); + } else { + /* Find the name for this child port */ + instance_port = generate_spice_port_for_module_net(module_manager, parent_module, net); + } + + if (true == new_line) { + std::string port_whitespace(instance_head_line.length() - 2, ' '); + fp << "+ " << port_whitespace; + } + + if (0 != pin_cnt) { + write_space_to_file(fp, 1); + } + + VTR_ASSERT(1 == instance_port.get_width()); + + /* For single-bit port, + * we can print the port name directly + */ + bool omit_pin_zero = false; + if ((1 == instance_port.pins().size()) + && (0 == instance_port.get_lsb())) { + omit_pin_zero = true; + } + + fp << generate_spice_port(instance_port, omit_pin_zero); + + /* Increase the counter */ + pin_cnt++; + + /* Currently we limit 10 ports per line to keep a clean netlist */ + new_line = false; + if (10 == pin_cnt) { + pin_cnt = 0; + fp << std::endl; + new_line = true; + fit_one_line = false; + } + } + } + } + + /* Print module name: + * if port print cannot fit one line, we create a new line for the module for a clean format + */ + if (false == fit_one_line) { + fp << std::endl; + fp << "+"; + } + write_space_to_file(fp, 1); + fp << module_manager.module_name(child_module); + + /* Print an end to the instance */ + fp << std::endl; +} + +/******************************************************************** + * Write a SPICE sub-circuit to a file + * This is a key function, maybe most frequently called in our SPICE writer + * Note that file stream must be valid + *******************************************************************/ +void write_spice_subckt_to_file(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id) { + + 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_spice_subckt_definition(fp, module_manager, module_id); + + /* Print an empty line as splitter */ + fp << std::endl; + + /* Print an empty line as splitter */ + fp << std::endl; + + /* Print local connection (from module inputs to output! */ + print_spice_comment(fp, std::string("BEGIN Local short connections")); + print_spice_subckt_local_short_connections(fp, module_manager, module_id); + print_spice_comment(fp, std::string("END Local short connections")); + + print_spice_comment(fp, std::string("BEGIN Local output short connections")); + print_spice_subckt_output_short_connections(fp, module_manager, module_id); + + print_spice_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_spice_instance_to_file(fp, module_manager, module_id, child_module, instance); + /* Print an empty line as splitter */ + fp << std::endl; + } + } + + /* Print an end for the module */ + print_spice_subckt_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_spice/spice_subckt_writer.h b/openfpga/src/fpga_spice/spice_subckt_writer.h new file mode 100644 index 000000000..f47e3915f --- /dev/null +++ b/openfpga/src/fpga_spice/spice_subckt_writer.h @@ -0,0 +1,23 @@ +#ifndef SPICE_SUBCKT_WRITER_H +#define SPICE_SUBCKT_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_spice_subckt_to_file(std::fstream& fp, + const ModuleManager& module_manager, + const ModuleId& module_id); + +} /* end namespace openfpga */ + +#endif From c7e3d97d1b2b3a99fd39a6f5220e359fbd784a92 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 11:19:06 -0600 Subject: [PATCH 11/25] [FPGA-SPICE] Add supply voltage generator --- openfpga/src/fpga_spice/spice_constants.h | 1 + .../src/fpga_spice/spice_essential_gates.cpp | 87 +++++++++++++++++++ .../src/fpga_spice/spice_essential_gates.h | 4 + openfpga/src/fpga_spice/spice_submodule.cpp | 43 ++++++++- 4 files changed, 134 insertions(+), 1 deletion(-) diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 48482d703..ab5991de3 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -8,6 +8,7 @@ constexpr char* SPICE_NETLIST_FILE_POSTFIX = ".sp"; constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; +constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index eafe37a24..889e9d8c1 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -18,6 +18,7 @@ /* Headers from openfpgautil library */ #include "openfpga_digest.h" +#include "openfpga_naming.h" #include "circuit_library_utils.h" #include "spice_constants.h" @@ -31,6 +32,92 @@ /* begin namespace openfpga */ namespace openfpga { +/************************************************ + * Generate the SPICE netlist for a constant generator, + * i.e., either VDD or GND + ***********************************************/ +static +void print_spice_supply_wrapper_subckt(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_spice_subckt_definition(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); + for (const auto& pin : module_port.pins()) { + BasicPort spice_pin(module_port.get_name(), pin, pin); + std::string const_pin_name = std::string(SPICE_SUBCKT_VDD_PORT_NAME); + if (0 == const_value) { + const_pin_name = std::string(SPICE_SUBCKT_GND_PORT_NAME); + } else { + VTR_ASSERT(1 == const_value); + } + + print_spice_short_connection(fp, + generate_spice_port(spice_pin, true), + const_pin_name); + } + } + + /* Put an end to the SPICE subcircuit */ + print_spice_subckt_end(fp, module_name); +} + +/******************************************************************** + * Create supply voltage wrappers + * The wrappers are used for constant inputs of routing multiplexers + * + *******************************************************************/ +int print_spice_supply_wrappers(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& submodule_dir) { + int status = CMD_EXEC_SUCCESS; + + /* Create file stream */ + std::string spice_fname = submodule_dir + std::string(SUPPLY_WRAPPER_SPICE_FILE_NAME); + + std::fstream fp; + + /* Create the file stream */ + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + /* Check if the file stream if valid or not */ + check_file_stream(spice_fname.c_str(), fp); + + /* Create file */ + VTR_LOG("Generating SPICE netlist '%s' for voltage supply wrappers...", + spice_fname.c_str()); + + print_spice_file_header(fp, std::string("Voltage Supply Wrappers")); + + /* VDD */ + print_spice_supply_wrapper_subckt(module_manager, fp, 0); + /* GND */ + print_spice_supply_wrapper_subckt(module_manager, fp, 1); + + /* Close file handler*/ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); + + return status; +} + /******************************************************************** * Generate the SPICE netlist for essential gates: * - inverters and their templates diff --git a/openfpga/src/fpga_spice/spice_essential_gates.h b/openfpga/src/fpga_spice/spice_essential_gates.h index 0fbd1895d..dc40a2531 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.h +++ b/openfpga/src/fpga_spice/spice_essential_gates.h @@ -18,6 +18,10 @@ /* begin namespace openfpga */ namespace openfpga { +int print_spice_supply_wrappers(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& submodule_dir); + int print_spice_essential_gates(NetlistManager& netlist_manager, const ModuleManager& module_manager, const CircuitLibrary& circuit_lib, diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index ad72e8515..a0c93de1b 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -25,7 +25,7 @@ namespace openfpga { * 2. Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor * 3. TODO: Routing multiplexers * 4. TODO: Local encoders for routing multiplexers - * 5. TODO: Wires + * 5. Wires * 6. TODO: Configuration memory blocks ********************************************************************/ int print_spice_submodule(NetlistManager& netlist_manager, @@ -35,10 +35,32 @@ int print_spice_submodule(NetlistManager& netlist_manager, int status = CMD_EXEC_SUCCESS; + /* Transistor wrapper */ status = print_spice_transistor_wrapper(netlist_manager, openfpga_arch.tech_lib, submodule_dir); + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Constant modules: VDD and GND */ + status = print_spice_supply_wrappers(netlist_manager, + module_manager, + submodule_dir); + + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Logic gates: + * - AND/OR, + * - inverter, buffer + * - transmission-gate/pass-transistor + * - wires + */ status = print_spice_essential_gates(netlist_manager, module_manager, openfpga_arch.circuit_lib, @@ -46,6 +68,25 @@ int print_spice_submodule(NetlistManager& netlist_manager, openfpga_arch.circuit_tech_binding, submodule_dir); + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + + /* Routing multiplexers */ + /* + status = print_spice_submodule_muxes(netlist_manager, + module_manager, + mux_lib, + openfpga_arch.circuit_lib, + submodule_dir); + */ + + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + return status; } From 0f9fce92b245f7bbe506a5c7e614d9a0f939301f Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 11:49:02 -0600 Subject: [PATCH 12/25] [FPGA-SPICE] Add SPICE writer for routing multiplexers --- openfpga/src/base/openfpga_spice.cpp | 1 + openfpga/src/fpga_spice/spice_api.cpp | 2 + openfpga/src/fpga_spice/spice_api.h | 2 + openfpga/src/fpga_spice/spice_constants.h | 4 + openfpga/src/fpga_spice/spice_mux.cpp | 189 ++++++++++++++++++++ openfpga/src/fpga_spice/spice_mux.h | 31 ++++ openfpga/src/fpga_spice/spice_submodule.cpp | 6 +- openfpga/src/fpga_spice/spice_submodule.h | 2 + 8 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 openfpga/src/fpga_spice/spice_mux.cpp create mode 100644 openfpga/src/fpga_spice/spice_mux.h diff --git a/openfpga/src/base/openfpga_spice.cpp b/openfpga/src/base/openfpga_spice.cpp index 4ef4058ee..e47acba3a 100644 --- a/openfpga/src/base/openfpga_spice.cpp +++ b/openfpga/src/base/openfpga_spice.cpp @@ -40,6 +40,7 @@ int write_fabric_spice(OpenfpgaContext& openfpga_ctx, status = fpga_fabric_spice(openfpga_ctx.module_graph(), openfpga_ctx.mutable_spice_netlists(), openfpga_ctx.arch(), + openfpga_ctx.mux_lib(), options); return status; diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index 658d43851..299a62950 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -40,6 +40,7 @@ namespace openfpga { int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, + const MuxLibrary& mux_lib, const FabricSpiceOption& options) { vtr::ScopedStartFinishTimer timer("Write SPICE netlists for FPGA fabric\n"); @@ -73,6 +74,7 @@ int fpga_fabric_spice(const ModuleManager& module_manager, status = print_spice_submodule(netlist_manager, module_manager, openfpga_arch, + mux_lib, submodule_dir_path); if (CMD_EXEC_SUCCESS != status) { diff --git a/openfpga/src/fpga_spice/spice_api.h b/openfpga/src/fpga_spice/spice_api.h index cf1ce5a0a..4ab8994bc 100644 --- a/openfpga/src/fpga_spice/spice_api.h +++ b/openfpga/src/fpga_spice/spice_api.h @@ -10,6 +10,7 @@ #include "netlist_manager.h" #include "module_manager.h" #include "openfpga_arch.h" +#include "mux_library.h" #include "fabric_spice_options.h" /******************************************************************** @@ -22,6 +23,7 @@ namespace openfpga { int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, + const MuxLibrary& mux_lib, const FabricSpiceOption& options); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index ab5991de3..774591d02 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -9,8 +9,12 @@ constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp"; +constexpr char* MUXES_SPICE_FILE_NAME = "muxes.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; +constexpr char* SPICE_MUX_BASIS_POSTFIX = "_basis"; +constexpr char* SPICE_MEM_POSTFIX = "_mem"; + #endif diff --git a/openfpga/src/fpga_spice/spice_mux.cpp b/openfpga/src/fpga_spice/spice_mux.cpp new file mode 100644 index 000000000..b1436d57e --- /dev/null +++ b/openfpga/src/fpga_spice/spice_mux.cpp @@ -0,0 +1,189 @@ +/*********************************************** + * This file includes functions to generate + * SPICE subcircuits 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" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.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 "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_mux.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/*********************************************** + * Generate SPICE modeling for an branch circuit + * for a multiplexer with the given size + **********************************************/ +static +void generate_spice_mux_branch_subckt(const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& mux_model, + const size_t& mux_size, + const MuxGraph& mux_graph) { + std::string module_name = generate_mux_branch_subckt_name(circuit_lib, mux_model, mux_size, mux_graph.num_inputs(), SPICE_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; + } + /* 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_spice_subckt_to_file(fp, module_manager, mux_module); + /* Add an empty line as a splitter */ + fp << std::endl; + break; + } + case CIRCUIT_MODEL_DESIGN_RRAM: + /* TODO: RRAM-based Multiplexer SPICE module generation */ + VTR_LOGF_ERROR(__FILE__, __LINE__, + "RRAM multiplexer '%s' is not supported yet\n", + circuit_lib.model_name(mux_model).c_str()); + exit(1); + 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 SPICE modeling for a multiplexer + * with the given graph-level description + **********************************************/ +static +void generate_spice_mux_subckt(const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& mux_model, + const MuxGraph& mux_graph) { + 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_spice_subckt_to_file(fp, module_manager, mux_module); + /* Add an empty line as a splitter */ + fp << std::endl; + break; + } + case CIRCUIT_MODEL_DESIGN_RRAM: + /* TODO: RRAM-based Multiplexer SPICE module generation */ + VTR_LOGF_ERROR(__FILE__, __LINE__, + "RRAM multiplexer '%s' is not supported yet\n", + circuit_lib.model_name(mux_model).c_str()); + exit(1); + 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 SPICE subcircuits for all the unique + * multiplexers in the FPGA device + **********************************************/ +int print_spice_submodule_muxes(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir) { + int status = CMD_EXEC_SUCCESS; + + std::string spice_fname(submodule_dir + std::string(MUXES_SPICE_FILE_NAME)); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + /* Print out debugging information for if the file is not opened/created properly */ + VTR_LOG("Writing SPICE netlist for Multiplexers '%s' ...", + spice_fname.c_str()); + + print_spice_file_header(fp, "Multiplexers"); + + /* 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_spice_mux_branch_subckt(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); + } + } + + /* 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_spice_mux_subckt(module_manager, circuit_lib, fp, mux_circuit_model, mux_graph); + } + + /* Close the file stream */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_mux.h b/openfpga/src/fpga_spice/spice_mux.h new file mode 100644 index 000000000..a7658667e --- /dev/null +++ b/openfpga/src/fpga_spice/spice_mux.h @@ -0,0 +1,31 @@ +#ifndef SPICE_MUX_H +#define SPICE_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" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_submodule_muxes(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index a0c93de1b..4e7505b60 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -1,5 +1,5 @@ /********************************************************************* - * This file includes top-level function to generate Spice primitive modules + * This file includes top-level function to generate SPICE primitive modules * and print them to files ********************************************************************/ @@ -12,6 +12,7 @@ #include "spice_transistor_wrapper.h" #include "spice_essential_gates.h" +#include "spice_mux.h" #include "spice_constants.h" #include "spice_submodule.h" @@ -31,6 +32,7 @@ namespace openfpga { int print_spice_submodule(NetlistManager& netlist_manager, const ModuleManager& module_manager, const Arch& openfpga_arch, + const MuxLibrary& mux_lib, const std::string& submodule_dir) { int status = CMD_EXEC_SUCCESS; @@ -74,13 +76,11 @@ int print_spice_submodule(NetlistManager& netlist_manager, } /* Routing multiplexers */ - /* status = print_spice_submodule_muxes(netlist_manager, module_manager, mux_lib, openfpga_arch.circuit_lib, submodule_dir); - */ /* Error out if fatal errors have been reported */ if (CMD_EXEC_SUCCESS != status) { diff --git a/openfpga/src/fpga_spice/spice_submodule.h b/openfpga/src/fpga_spice/spice_submodule.h index 27a49de87..551fd772e 100644 --- a/openfpga/src/fpga_spice/spice_submodule.h +++ b/openfpga/src/fpga_spice/spice_submodule.h @@ -7,6 +7,7 @@ #include "netlist_manager.h" #include "module_manager.h" #include "openfpga_arch.h" +#include "mux_library.h" /******************************************************************** * Function declaration @@ -18,6 +19,7 @@ namespace openfpga { int print_spice_submodule(NetlistManager& netlist_manager, const ModuleManager& module_manager, const Arch& openfpga_arch, + const MuxLibrary& mux_lib, const std::string& submodule_dir); } /* end namespace openfpga */ From 6801d260e97ad84fd8018bce431cb56de5c59748 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 11:58:11 -0600 Subject: [PATCH 13/25] [FPGA-SPICE] Add SPICE writer for LUT --- openfpga/src/fpga_spice/spice_constants.h | 1 + openfpga/src/fpga_spice/spice_lut.cpp | 82 +++++++++++++++++++++ openfpga/src/fpga_spice/spice_lut.h | 28 +++++++ openfpga/src/fpga_spice/spice_submodule.cpp | 12 +++ 4 files changed, 123 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_lut.cpp create mode 100644 openfpga/src/fpga_spice/spice_lut.h diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 774591d02..4b983aa42 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -10,6 +10,7 @@ constexpr char* TRANSISTOR_WRAPPER_POSTFIX = "_wrapper"; constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp"; constexpr char* MUXES_SPICE_FILE_NAME = "muxes.sp"; +constexpr char* LUTS_SPICE_FILE_NAME = "luts.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; diff --git a/openfpga/src/fpga_spice/spice_lut.cpp b/openfpga/src/fpga_spice/spice_lut.cpp new file mode 100644 index 000000000..af8dadda1 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_lut.cpp @@ -0,0 +1,82 @@ +/******************************************************************** + * This file includes functions to generate SPICE subcircuits for LUTs + ********************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +#include "mux_graph.h" +#include "module_manager.h" +#include "mux_utils.h" + +#include "openfpga_naming.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_lut.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print SPICE modules for the Look-Up Tables (LUTs) + * in the circuit library + ********************************************************************/ +int print_spice_submodule_luts(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir) { + int status = CMD_EXEC_SUCCESS; + + std::string spice_fname = submodule_dir + std::string(LUTS_SPICE_FILE_NAME); + + std::fstream fp; + + /* Create the file stream */ + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + /* Check if the file stream if valid or not */ + check_file_stream(spice_fname.c_str(), fp); + + /* Create file */ + VTR_LOG("Writing SPICE netlist for LUTs '%s'...", + spice_fname.c_str()); + + print_spice_file_header(fp, "Look-Up Tables"); + + /* 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_circuit_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_spice_subckt_to_file(fp, module_manager, lut_module); + } + + /* Close the file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_lut.h b/openfpga/src/fpga_spice/spice_lut.h new file mode 100644 index 000000000..5ca3f4368 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_lut.h @@ -0,0 +1,28 @@ +#ifndef SPICE_LUT_H +#define SPICE_LUT_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +#include "circuit_library.h" +#include "module_manager.h" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_submodule_luts(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index 4e7505b60..714747dce 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -13,6 +13,7 @@ #include "spice_transistor_wrapper.h" #include "spice_essential_gates.h" #include "spice_mux.h" +#include "spice_lut.h" #include "spice_constants.h" #include "spice_submodule.h" @@ -87,6 +88,17 @@ int print_spice_submodule(NetlistManager& netlist_manager, return CMD_EXEC_FATAL_ERROR; } + /* Look-Up Tables */ + status = print_spice_submodule_luts(netlist_manager, + module_manager, + openfpga_arch.circuit_lib, + submodule_dir); + + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + return status; } From f284f6f8d05cb9b763621d9d5eda4f5a4d0f715a Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:03:10 -0600 Subject: [PATCH 14/25] [OPENFPGA LIBRARY] change method names to be consistent with FPGA-SPICE needs --- .../libarchopenfpga/src/circuit_library.cpp | 14 +++++++------- libopenfpga/libarchopenfpga/src/circuit_library.h | 8 ++++---- .../src/read_xml_circuit_library.cpp | 2 +- .../src/write_xml_circuit_library.cpp | 4 ++-- openfpga/src/fabric/build_essential_modules.cpp | 4 ++-- openfpga/src/fabric/build_wire_modules.cpp | 2 +- openfpga/src/fpga_spice/spice_essential_gates.cpp | 2 +- openfpga/src/fpga_spice/spice_lut.cpp | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/circuit_library.cpp b/libopenfpga/libarchopenfpga/src/circuit_library.cpp index 42df84407..bbf9c50c4 100644 --- a/libopenfpga/libarchopenfpga/src/circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/circuit_library.cpp @@ -79,11 +79,11 @@ std::string CircuitLibrary::model_verilog_netlist(const CircuitModelId& model_id return model_verilog_netlists_[model_id]; } -/* Access the path + file of user-defined circuit netlist of a circuit model */ -std::string CircuitLibrary::model_circuit_netlist(const CircuitModelId& model_id) const { +/* Access the path + file of user-defined spice netlist of a circuit model */ +std::string CircuitLibrary::model_spice_netlist(const CircuitModelId& model_id) const { /* validate the model_id */ VTR_ASSERT(valid_model_id(model_id)); - return model_circuit_netlists_[model_id]; + return model_spice_netlists_[model_id]; } /* Access the is_default flag (check if this is the default circuit model in the type) of a circuit model */ @@ -1132,7 +1132,7 @@ CircuitModelId CircuitLibrary::add_model(const enum e_circuit_model_type& type) model_names_.emplace_back(); model_prefix_.emplace_back(); model_verilog_netlists_.emplace_back(); - model_circuit_netlists_.emplace_back(); + model_spice_netlists_.emplace_back(); model_is_default_.push_back(false); sub_models_.emplace_back(); @@ -1226,11 +1226,11 @@ void CircuitLibrary::set_model_verilog_netlist(const CircuitModelId& model_id, c return; } -/* Set the circuit_netlist of a Circuit Model */ -void CircuitLibrary::set_model_circuit_netlist(const CircuitModelId& model_id, const std::string& circuit_netlist) { +/* Set the spice_netlist of a Circuit Model */ +void CircuitLibrary::set_model_spice_netlist(const CircuitModelId& model_id, const std::string& spice_netlist) { /* validate the model_id */ VTR_ASSERT(valid_model_id(model_id)); - model_circuit_netlists_[model_id] = circuit_netlist; + model_spice_netlists_[model_id] = spice_netlist; return; } diff --git a/libopenfpga/libarchopenfpga/src/circuit_library.h b/libopenfpga/libarchopenfpga/src/circuit_library.h index 5e023c665..bd082f738 100644 --- a/libopenfpga/libarchopenfpga/src/circuit_library.h +++ b/libopenfpga/libarchopenfpga/src/circuit_library.h @@ -48,7 +48,7 @@ * It should be the same as user-defined Verilog modules, if it is not auto-generated * 4. model_prefix_: the prefix of a circuit model when it is instanciated * 5. verilog_netlist_: specified path and file name of Verilog netlist if a circuit model is not auto-generated - * 6. circuit_netlist_: specified path and file name of CIRCUIT netlist if a circuit model is not auto-generated + * 6. spice_netlist_: specified path and file name of CIRCUIT netlist if a circuit model is not auto-generated * 7. is_default_: indicate if the circuit model is the default one among all those in the same type * 8. sub_models_: the sub circuit models included by a circuit model. It is a collection of unique circuit model ids * found in the CircuitModelId of pass-gate/buffers/port-related circuit models. @@ -190,7 +190,7 @@ class CircuitLibrary { std::string model_name(const CircuitModelId& model_id) const; std::string model_prefix(const CircuitModelId& model_id) const; std::string model_verilog_netlist(const CircuitModelId& model_id) const; - std::string model_circuit_netlist(const CircuitModelId& model_id) const; + std::string model_spice_netlist(const CircuitModelId& model_id) const; bool model_is_default(const CircuitModelId& model_id) const; bool dump_structural_verilog(const CircuitModelId& model_id) const; bool dump_explicit_port_map(const CircuitModelId& model_id) const; @@ -314,7 +314,7 @@ class CircuitLibrary { void set_model_name(const CircuitModelId& model_id, const std::string& name); void set_model_prefix(const CircuitModelId& model_id, const std::string& prefix); void set_model_verilog_netlist(const CircuitModelId& model_id, const std::string& verilog_netlist); - void set_model_circuit_netlist(const CircuitModelId& model_id, const std::string& circuit_netlist); + void set_model_spice_netlist(const CircuitModelId& model_id, const std::string& spice_netlist); void set_model_is_default(const CircuitModelId& model_id, const bool& is_default); /* Verilog generator options */ void set_model_dump_structural_verilog(const CircuitModelId& model_id, const bool& dump_structural_verilog); @@ -499,7 +499,7 @@ class CircuitLibrary { vtr::vector model_names_; vtr::vector model_prefix_; vtr::vector model_verilog_netlists_; - vtr::vector model_circuit_netlists_; + vtr::vector model_spice_netlists_; vtr::vector model_is_default_; /* Submodules that a circuit model contains */ diff --git a/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp index 0954a0086..36e837814 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp @@ -674,7 +674,7 @@ void read_xml_circuit_model(pugi::xml_node& xml_model, circuit_lib.set_model_prefix(model, std::string(prefix_attr)); /* Find a SPICE netlist which is an optional attribute*/ - circuit_lib.set_model_circuit_netlist(model, get_attribute(xml_model, "spice_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string("")); + circuit_lib.set_model_spice_netlist(model, get_attribute(xml_model, "spice_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string("")); /* Find a Verilog netlist which is an optional attribute*/ circuit_lib.set_model_verilog_netlist(model, get_attribute(xml_model, "verilog_netlist", loc_data, pugiutil::ReqOpt::OPTIONAL).as_string("")); diff --git a/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp index 5dd69ca8e..b141b0fe2 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp @@ -409,8 +409,8 @@ void write_xml_circuit_model(std::fstream& fp, if (true == circuit_lib.dump_structural_verilog(model)) { write_xml_attribute(fp, "dump_structural_verilog", "true"); } - if (!circuit_lib.model_circuit_netlist(model).empty()) { - write_xml_attribute(fp, "circuit_netlist", circuit_lib.model_circuit_netlist(model).c_str()); + if (!circuit_lib.model_spice_netlist(model).empty()) { + write_xml_attribute(fp, "spice_netlist", circuit_lib.model_spice_netlist(model).c_str()); } if (!circuit_lib.model_verilog_netlist(model).empty()) { write_xml_attribute(fp, "verilog_netlist", circuit_lib.model_verilog_netlist(model).c_str()); diff --git a/openfpga/src/fabric/build_essential_modules.cpp b/openfpga/src/fabric/build_essential_modules.cpp index 481781c5a..19e1a10ea 100644 --- a/openfpga/src/fabric/build_essential_modules.cpp +++ b/openfpga/src/fabric/build_essential_modules.cpp @@ -187,7 +187,7 @@ void build_user_defined_modules(ModuleManager& module_manager, for (const auto& model : circuit_lib.models()) { /* We only care about user-defined models */ if ( (true == circuit_lib.model_verilog_netlist(model).empty()) - && (true == circuit_lib.model_circuit_netlist(model).empty()) ) { + && (true == circuit_lib.model_spice_netlist(model).empty()) ) { continue; } /* Skip Routing channel wire models because they need a different name. Do it later */ @@ -255,7 +255,7 @@ void rename_primitive_module_port_names(ModuleManager& module_manager, for (const CircuitModelId& model : circuit_lib.models()) { /* We only care about user-defined models */ if ( (true == circuit_lib.model_verilog_netlist(model).empty()) - && (true == circuit_lib.model_circuit_netlist(model).empty()) ) { + && (true == circuit_lib.model_spice_netlist(model).empty()) ) { continue; } /* Skip Routing channel wire models because they need a different name. Do it later */ diff --git a/openfpga/src/fabric/build_wire_modules.cpp b/openfpga/src/fabric/build_wire_modules.cpp index da3b03e23..d505f93e6 100644 --- a/openfpga/src/fabric/build_wire_modules.cpp +++ b/openfpga/src/fabric/build_wire_modules.cpp @@ -62,7 +62,7 @@ void build_wire_modules(ModuleManager& module_manager, /* Print Verilog models for regular wires*/ for (const auto& wire_model : circuit_lib.models_by_type(CIRCUIT_MODEL_WIRE)) { /* Bypass user-defined circuit models */ - if ( (!circuit_lib.model_circuit_netlist(wire_model).empty()) + if ( (!circuit_lib.model_spice_netlist(wire_model).empty()) && (!circuit_lib.model_verilog_netlist(wire_model).empty()) ) { continue; } diff --git a/openfpga/src/fpga_spice/spice_essential_gates.cpp b/openfpga/src/fpga_spice/spice_essential_gates.cpp index 889e9d8c1..301b81de1 100644 --- a/openfpga/src/fpga_spice/spice_essential_gates.cpp +++ b/openfpga/src/fpga_spice/spice_essential_gates.cpp @@ -136,7 +136,7 @@ int print_spice_essential_gates(NetlistManager& netlist_manager, /* Iterate over the circuit models */ for (const CircuitModelId& circuit_model : circuit_lib.models()) { /* Bypass models require extern netlists */ - if (!circuit_lib.model_circuit_netlist(circuit_model).empty()) { + if (!circuit_lib.model_spice_netlist(circuit_model).empty()) { continue; } diff --git a/openfpga/src/fpga_spice/spice_lut.cpp b/openfpga/src/fpga_spice/spice_lut.cpp index af8dadda1..2d68d014b 100644 --- a/openfpga/src/fpga_spice/spice_lut.cpp +++ b/openfpga/src/fpga_spice/spice_lut.cpp @@ -56,7 +56,7 @@ int print_spice_submodule_luts(NetlistManager& netlist_manager, /* 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_circuit_netlist(lut_model).empty()) + if ( (!circuit_lib.model_spice_netlist(lut_model).empty()) || (CIRCUIT_MODEL_LUT != circuit_lib.model_type(lut_model)) ) { continue; } From 2fae311c8e6be2f769c7e875e1106c713f01ec34 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:14:34 -0600 Subject: [PATCH 15/25] [FPGA-SPICE] Add SPICE writer for memories --- openfpga/src/fpga_spice/spice_constants.h | 1 + openfpga/src/fpga_spice/spice_memory.cpp | 196 ++++++++++++++++++++ openfpga/src/fpga_spice/spice_memory.h | 30 +++ openfpga/src/fpga_spice/spice_submodule.cpp | 13 ++ 4 files changed, 240 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_memory.cpp create mode 100644 openfpga/src/fpga_spice/spice_memory.h diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 4b983aa42..95e7f00b9 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -11,6 +11,7 @@ constexpr char* TRANSISTORS_SPICE_FILE_NAME = "transistor.sp"; constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp"; constexpr char* MUXES_SPICE_FILE_NAME = "muxes.sp"; constexpr char* LUTS_SPICE_FILE_NAME = "luts.sp"; +constexpr char* MEMORIES_SPICE_FILE_NAME = "memories.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; diff --git a/openfpga/src/fpga_spice/spice_memory.cpp b/openfpga/src/fpga_spice/spice_memory.cpp new file mode 100644 index 000000000..e1da21d28 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_memory.cpp @@ -0,0 +1,196 @@ +/********************************************************************* + * This file includes functions to generate SPICE sub-circuits 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" + +/* Headers from openfpgashell library */ +#include "command_exit_codes.h" + +#include "mux_graph.h" +#include "module_manager.h" +#include "circuit_library_utils.h" +#include "mux_utils.h" + +#include "openfpga_naming.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_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_spice_mux_memory_module(const ModuleManager& module_manager, + const CircuitLibrary& circuit_lib, + std::fstream& fp, + const CircuitModelId& mux_model, + const MuxGraph& mux_graph) { + /* 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(SPICE_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_spice_subckt_to_file(fp, module_manager, mem_module); + + /* 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. + ********************************************************************/ +int print_spice_submodule_memories(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir) { + int status = CMD_EXEC_SUCCESS; + + /* Plug in with the mux subckt */ + std::string spice_fname(submodule_dir + std::string(MEMORIES_SPICE_FILE_NAME)); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + /* Print out debugging information for if the file is not opened/created properly */ + VTR_LOG("Writing SPICE netlist for memories '%s' ...", + spice_fname.c_str()); + + print_spice_file_header(fp, "Memories used in FPGA"); + + /* 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_spice_mux_memory_module(module_manager, circuit_lib, fp, mux_model, mux_graph); + } + + /* 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(SPICE_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_spice_subckt_to_file(fp, module_manager, mem_module); + + /* Add an empty line as a splitter */ + fp << std::endl; + } + + /* Close the file stream */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::SUBMODULE_NETLIST); + + VTR_LOG("Done\n"); + + return status; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_memory.h b/openfpga/src/fpga_spice/spice_memory.h new file mode 100644 index 000000000..48eaf5651 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_memory.h @@ -0,0 +1,30 @@ +#ifndef SPICE_MEMORY_H +#define SPICE_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" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +int print_spice_submodule_memories(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const MuxLibrary& mux_lib, + const CircuitLibrary& circuit_lib, + const std::string& submodule_dir); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index 714747dce..7fd5564be 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -14,6 +14,7 @@ #include "spice_essential_gates.h" #include "spice_mux.h" #include "spice_lut.h" +#include "spice_memory.h" #include "spice_constants.h" #include "spice_submodule.h" @@ -99,6 +100,18 @@ int print_spice_submodule(NetlistManager& netlist_manager, return CMD_EXEC_FATAL_ERROR; } + /* Memories */ + status = print_spice_submodule_memories(netlist_manager, + module_manager, + mux_lib, + openfpga_arch.circuit_lib, + submodule_dir); + + /* Error out if fatal errors have been reported */ + if (CMD_EXEC_SUCCESS != status) { + return CMD_EXEC_FATAL_ERROR; + } + return status; } From 0f25b529075ff73aaf1b3774a1c5a50937587ff7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:18:22 -0600 Subject: [PATCH 16/25] [FPGA-Verilog] code format fix --- openfpga/src/fpga_spice/spice_submodule.cpp | 8 +- openfpga/src/fpga_verilog/verilog_api.cpp | 327 ++++++++++---------- 2 files changed, 165 insertions(+), 170 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_submodule.cpp b/openfpga/src/fpga_spice/spice_submodule.cpp index 7fd5564be..26b166a59 100644 --- a/openfpga/src/fpga_spice/spice_submodule.cpp +++ b/openfpga/src/fpga_spice/spice_submodule.cpp @@ -26,10 +26,10 @@ namespace openfpga { * Top-level function to generate primitive modules: * 1. Transistor wrapper * 2. Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor - * 3. TODO: Routing multiplexers + * 3. Routing multiplexers * 4. TODO: Local encoders for routing multiplexers * 5. Wires - * 6. TODO: Configuration memory blocks + * 6. Configuration memory blocks ********************************************************************/ int print_spice_submodule(NetlistManager& netlist_manager, const ModuleManager& module_manager, @@ -77,6 +77,8 @@ int print_spice_submodule(NetlistManager& netlist_manager, return CMD_EXEC_FATAL_ERROR; } + /* TODO: local decoders for routing multiplexers */ + /* Routing multiplexers */ status = print_spice_submodule_muxes(netlist_manager, module_manager, @@ -112,6 +114,8 @@ int print_spice_submodule(NetlistManager& netlist_manager, return CMD_EXEC_FATAL_ERROR; } + /* TODO: architecture decoders */ + return status; } diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index 7f4c0d9b7..d02ced128 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -33,7 +33,7 @@ namespace openfpga { - /******************************************************************** +/******************************************************************** * A top-level function of FPGA-Verilog which focuses on fabric Verilog generation * This function will generate * - primitive modules required by the full fabric @@ -52,97 +52,93 @@ namespace openfpga * 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(ModuleManager &module_manager, - NetlistManager &netlist_manager, - const CircuitLibrary &circuit_lib, - const MuxLibrary &mux_lib, - const DecoderLibrary &decoder_lib, - const DeviceContext &device_ctx, - const VprDeviceAnnotation &device_annotation, - const DeviceRRGSB &device_rr_gsb, - const FabricVerilogOption &options) - { +void fpga_fabric_verilog(ModuleManager &module_manager, + NetlistManager &netlist_manager, + const CircuitLibrary &circuit_lib, + const MuxLibrary &mux_lib, + const DecoderLibrary &decoder_lib, + const DeviceContext &device_ctx, + const VprDeviceAnnotation &device_annotation, + const DeviceRRGSB &device_rr_gsb, + const FabricVerilogOption &options) { - vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n"); + vtr::ScopedStartFinishTimer timer("Write Verilog netlists for FPGA fabric\n"); - std::string src_dir_path = format_dir_path(options.output_directory()); + std::string src_dir_path = format_dir_path(options.output_directory()); - /* Create directories */ - create_directory(src_dir_path); + /* Create directories */ + create_directory(src_dir_path); - /* Sub directory under SRC directory to contain all the primitive block netlists */ - std::string submodule_dir_path = src_dir_path + std::string(DEFAULT_SUBMODULE_DIR_NAME); - create_directory(submodule_dir_path); + /* Sub directory under SRC directory to contain all the primitive block netlists */ + std::string submodule_dir_path = src_dir_path + std::string(DEFAULT_SUBMODULE_DIR_NAME); + create_directory(submodule_dir_path); - /* Sub directory under SRC directory to contain all the logic block netlists */ - std::string lb_dir_path = src_dir_path + std::string(DEFAULT_LB_DIR_NAME); - create_directory(lb_dir_path); + /* Sub directory under SRC directory to contain all the logic block netlists */ + std::string lb_dir_path = src_dir_path + std::string(DEFAULT_LB_DIR_NAME); + create_directory(lb_dir_path); - /* Sub directory under SRC directory to contain all the routing block netlists */ - std::string rr_dir_path = src_dir_path + std::string(DEFAULT_RR_DIR_NAME); - create_directory(rr_dir_path); + /* Sub directory under SRC directory to contain all the routing block netlists */ + std::string rr_dir_path = src_dir_path + std::string(DEFAULT_RR_DIR_NAME); + create_directory(rr_dir_path); - /* Print Verilog files containing preprocessing flags */ - print_verilog_preprocessing_flags_netlist(std::string(src_dir_path), - options); + /* Print Verilog files containing preprocessing flags */ + print_verilog_preprocessing_flags_netlist(std::string(src_dir_path), + options); - /* Generate primitive Verilog modules, which are corner stones of FPGA fabric - * Note that this function MUST be called before Verilog generation of - * core logic (i.e., logic blocks and routing resources) !!! - * This is because that this function will add the primitive Verilog modules to - * the module manager. - * Without the modules in the module manager, core logic generation is not possible!!! - */ - print_verilog_submodule(module_manager, netlist_manager, - mux_lib, decoder_lib, circuit_lib, - submodule_dir_path, - options); + /* Generate primitive Verilog modules, which are corner stones of FPGA fabric + * Note that this function MUST be called before Verilog generation of + * core logic (i.e., logic blocks and routing resources) !!! + * This is because that this function will add the primitive Verilog modules to + * the module manager. + * Without the modules in the module manager, core logic generation is not possible!!! + */ + print_verilog_submodule(module_manager, netlist_manager, + mux_lib, decoder_lib, circuit_lib, + submodule_dir_path, + options); - /* Generate routing blocks */ - if (true == options.compress_routing()) - { - print_verilog_unique_routing_modules(netlist_manager, - const_cast(module_manager), - device_rr_gsb, - rr_dir_path, - options.explicit_port_mapping()); - } - else - { - VTR_ASSERT(false == options.compress_routing()); - print_verilog_flatten_routing_modules(netlist_manager, - const_cast(module_manager), - device_rr_gsb, - rr_dir_path, - options.explicit_port_mapping()); - } - - /* Generate grids */ - print_verilog_grids(netlist_manager, - const_cast(module_manager), - device_ctx, device_annotation, - lb_dir_path, - options.explicit_port_mapping(), - options.verbose_output()); - - /* Generate FPGA fabric */ - print_verilog_top_module(netlist_manager, - const_cast(module_manager), - src_dir_path, - options.explicit_port_mapping()); - - /* Generate an netlist including all the fabric-related netlists */ - print_fabric_include_netlist(const_cast(netlist_manager), - src_dir_path, - circuit_lib); - - /* Given a brief stats on how many Verilog modules have been written to files */ - VTR_LOGV(options.verbose_output(), - "Written %lu Verilog modules in total\n", - module_manager.num_modules()); + /* Generate routing blocks */ + if (true == options.compress_routing()) { + print_verilog_unique_routing_modules(netlist_manager, + const_cast(module_manager), + device_rr_gsb, + rr_dir_path, + options.explicit_port_mapping()); + } else { + VTR_ASSERT(false == options.compress_routing()); + print_verilog_flatten_routing_modules(netlist_manager, + const_cast(module_manager), + device_rr_gsb, + rr_dir_path, + options.explicit_port_mapping()); } - /******************************************************************** + /* Generate grids */ + print_verilog_grids(netlist_manager, + const_cast(module_manager), + device_ctx, device_annotation, + lb_dir_path, + options.explicit_port_mapping(), + options.verbose_output()); + + /* Generate FPGA fabric */ + print_verilog_top_module(netlist_manager, + const_cast(module_manager), + src_dir_path, + options.explicit_port_mapping()); + + /* Generate an netlist including all the fabric-related netlists */ + print_fabric_include_netlist(const_cast(netlist_manager), + src_dir_path, + circuit_lib); + + /* Given a brief stats on how many Verilog modules have been written to files */ + VTR_LOGV(options.verbose_output(), + "Written %lu Verilog modules in total\n", + module_manager.num_modules()); +} + +/******************************************************************** * A top-level function of FPGA-Verilog which focuses on fabric Verilog generation * This function will generate * - A wrapper module, which encapsulate the FPGA module in a Verilog module which have the same port as the input benchmark @@ -151,100 +147,95 @@ namespace openfpga * This testbench is created for quick verification and formal verification purpose. * - Verilog netlist including preprocessing flags and all the Verilog netlists that have been generated ********************************************************************/ - void fpga_verilog_testbench(const ModuleManager &module_manager, - const BitstreamManager &bitstream_manager, - const FabricBitstream &fabric_bitstream, - const AtomContext &atom_ctx, - const PlacementContext &place_ctx, - const IoLocationMap &io_location_map, - const VprNetlistAnnotation &netlist_annotation, - const CircuitLibrary &circuit_lib, - const SimulationSetting &simulation_setting, - const e_config_protocol_type &config_protocol_type, - const VerilogTestbenchOption &options) - { +void fpga_verilog_testbench(const ModuleManager &module_manager, + const BitstreamManager &bitstream_manager, + const FabricBitstream &fabric_bitstream, + const AtomContext &atom_ctx, + const PlacementContext &place_ctx, + const IoLocationMap &io_location_map, + const VprNetlistAnnotation &netlist_annotation, + const CircuitLibrary &circuit_lib, + const SimulationSetting &simulation_setting, + const e_config_protocol_type &config_protocol_type, + const VerilogTestbenchOption &options) { - vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n"); + vtr::ScopedStartFinishTimer timer("Write Verilog testbenches for FPGA fabric\n"); - std::string src_dir_path = format_dir_path(options.output_directory()); + std::string src_dir_path = format_dir_path(options.output_directory()); - std::string netlist_name = atom_ctx.nlist.netlist_name(); + std::string netlist_name = atom_ctx.nlist.netlist_name(); - /* Create directories */ - create_directory(src_dir_path); + /* Create directories */ + create_directory(src_dir_path); - /* TODO: check if this works here. This function was in fabric generator */ - print_verilog_simulation_preprocessing_flags(std::string(src_dir_path), - options); + /* TODO: check if this works here. This function was in fabric generator */ + print_verilog_simulation_preprocessing_flags(std::string(src_dir_path), + options); - /* Collect global ports from the circuit library: - * TODO: should we place this in the OpenFPGA context? - */ - std::vector global_ports = find_circuit_library_global_ports(circuit_lib); + /* Collect global ports from the circuit library: + * TODO: should we place this in the OpenFPGA context? + */ + std::vector global_ports = find_circuit_library_global_ports(circuit_lib); - /* Generate wrapper module for FPGA fabric (mapped by the input benchmark and pre-configured testbench for verification */ - if (true == options.print_formal_verification_top_netlist()) - { - std::string formal_verification_top_netlist_file_path = src_dir_path + netlist_name + std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX); - print_verilog_preconfig_top_module(module_manager, bitstream_manager, - circuit_lib, global_ports, - atom_ctx, place_ctx, io_location_map, - netlist_annotation, - netlist_name, - formal_verification_top_netlist_file_path, - options.explicit_port_mapping()); - } - - if (true == options.print_preconfig_top_testbench()) - { - /* Generate top-level testbench using random vectors */ - std::string random_top_testbench_file_path = src_dir_path + netlist_name + std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); - print_verilog_random_top_testbench(netlist_name, - random_top_testbench_file_path, - atom_ctx, - netlist_annotation, - simulation_setting, - options.explicit_port_mapping()); - } - - /* Generate full testbench for verification, including configuration phase and operating phase */ - if (true == options.print_top_testbench()) - { - std::string top_testbench_file_path = src_dir_path + netlist_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); - print_verilog_top_testbench(module_manager, - bitstream_manager, fabric_bitstream, - config_protocol_type, - circuit_lib, global_ports, - atom_ctx, place_ctx, io_location_map, - netlist_annotation, - netlist_name, - top_testbench_file_path, - simulation_setting, - options.fast_configuration(), - options.explicit_port_mapping()); - } - - /* Generate exchangeable files which contains simulation settings */ - if (true == options.print_simulation_ini()) - { - std::string simulation_ini_file_name = options.simulation_ini_path(); - VTR_ASSERT(true != options.simulation_ini_path().empty()); - print_verilog_simulation_info(simulation_ini_file_name, - netlist_name, - src_dir_path, - atom_ctx, place_ctx, io_location_map, - module_manager, - config_protocol_type, - bitstream_manager.num_bits(), - simulation_setting.num_clock_cycles(), - simulation_setting.programming_clock_frequency(), - simulation_setting.operating_clock_frequency()); - } - - /* Generate a Verilog file including all the netlists that have been generated */ - print_include_netlists(src_dir_path, - netlist_name, - options.reference_benchmark_file_path()); + /* Generate wrapper module for FPGA fabric (mapped by the input benchmark and pre-configured testbench for verification */ + if (true == options.print_formal_verification_top_netlist()) { + std::string formal_verification_top_netlist_file_path = src_dir_path + netlist_name + std::string(FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX); + print_verilog_preconfig_top_module(module_manager, bitstream_manager, + circuit_lib, global_ports, + atom_ctx, place_ctx, io_location_map, + netlist_annotation, + netlist_name, + formal_verification_top_netlist_file_path, + options.explicit_port_mapping()); } + if (true == options.print_preconfig_top_testbench()) { + /* Generate top-level testbench using random vectors */ + std::string random_top_testbench_file_path = src_dir_path + netlist_name + std::string(RANDOM_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); + print_verilog_random_top_testbench(netlist_name, + random_top_testbench_file_path, + atom_ctx, + netlist_annotation, + simulation_setting, + options.explicit_port_mapping()); + } + + /* Generate full testbench for verification, including configuration phase and operating phase */ + if (true == options.print_top_testbench()) { + std::string top_testbench_file_path = src_dir_path + netlist_name + std::string(AUTOCHECK_TOP_TESTBENCH_VERILOG_FILE_POSTFIX); + print_verilog_top_testbench(module_manager, + bitstream_manager, fabric_bitstream, + config_protocol_type, + circuit_lib, global_ports, + atom_ctx, place_ctx, io_location_map, + netlist_annotation, + netlist_name, + top_testbench_file_path, + simulation_setting, + options.fast_configuration(), + options.explicit_port_mapping()); + } + + /* Generate exchangeable files which contains simulation settings */ + if (true == options.print_simulation_ini()) { + std::string simulation_ini_file_name = options.simulation_ini_path(); + VTR_ASSERT(true != options.simulation_ini_path().empty()); + print_verilog_simulation_info(simulation_ini_file_name, + netlist_name, + src_dir_path, + atom_ctx, place_ctx, io_location_map, + module_manager, + config_protocol_type, + bitstream_manager.num_bits(), + simulation_setting.num_clock_cycles(), + simulation_setting.programming_clock_frequency(), + simulation_setting.operating_clock_frequency()); + } + + /* Generate a Verilog file including all the netlists that have been generated */ + print_include_netlists(src_dir_path, + netlist_name, + options.reference_benchmark_file_path()); +} + } /* end namespace openfpga */ From 5e78e91fdf15ca21ddd27c41fd58d3f5146b0f3c Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:27:48 -0600 Subject: [PATCH 17/25] [FPGA-SPICE] Add SPICE writer for routing blocks --- openfpga/src/base/openfpga_spice.cpp | 1 + openfpga/src/fpga_spice/spice_api.cpp | 16 + openfpga/src/fpga_spice/spice_api.h | 2 + openfpga/src/fpga_spice/spice_constants.h | 4 + openfpga/src/fpga_spice/spice_routing.cpp | 346 ++++++++++++++++++++++ openfpga/src/fpga_spice/spice_routing.h | 32 ++ 6 files changed, 401 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_routing.cpp create mode 100644 openfpga/src/fpga_spice/spice_routing.h diff --git a/openfpga/src/base/openfpga_spice.cpp b/openfpga/src/base/openfpga_spice.cpp index e47acba3a..0ddab0c91 100644 --- a/openfpga/src/base/openfpga_spice.cpp +++ b/openfpga/src/base/openfpga_spice.cpp @@ -41,6 +41,7 @@ int write_fabric_spice(OpenfpgaContext& openfpga_ctx, openfpga_ctx.mutable_spice_netlists(), openfpga_ctx.arch(), openfpga_ctx.mux_lib(), + openfpga_ctx.device_rr_gsb(), options); return status; diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index 299a62950..852d29a8d 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -16,6 +16,7 @@ #include "spice_constants.h" #include "spice_submodule.h" +#include "spice_routing.h" /* Header file for this source file */ #include "spice_api.h" @@ -41,6 +42,7 @@ int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, const MuxLibrary& mux_lib, + const DeviceRRGSB &device_rr_gsb, const FabricSpiceOption& options) { vtr::ScopedStartFinishTimer timer("Write SPICE netlists for FPGA fabric\n"); @@ -81,6 +83,20 @@ int fpga_fabric_spice(const ModuleManager& module_manager, return status; } + /* Generate routing blocks */ + if (true == options.compress_routing()) { + print_spice_unique_routing_modules(netlist_manager, + module_manager, + device_rr_gsb, + rr_dir_path); + } else { + VTR_ASSERT(false == options.compress_routing()); + print_spice_flatten_routing_modules(netlist_manager, + module_manager, + device_rr_gsb, + rr_dir_path); + } + /* Given a brief stats on how many Spice modules have been written to files */ VTR_LOGV(options.verbose_output(), "Written %lu SPICE modules in total\n", diff --git a/openfpga/src/fpga_spice/spice_api.h b/openfpga/src/fpga_spice/spice_api.h index 4ab8994bc..2d418432d 100644 --- a/openfpga/src/fpga_spice/spice_api.h +++ b/openfpga/src/fpga_spice/spice_api.h @@ -11,6 +11,7 @@ #include "module_manager.h" #include "openfpga_arch.h" #include "mux_library.h" +#include "device_rr_gsb.h" #include "fabric_spice_options.h" /******************************************************************** @@ -24,6 +25,7 @@ int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, const MuxLibrary& mux_lib, + const DeviceRRGSB &device_rr_gsb, const FabricSpiceOption& options); } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 95e7f00b9..97b1733c2 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -19,4 +19,8 @@ constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; constexpr char* SPICE_MUX_BASIS_POSTFIX = "_basis"; constexpr char* SPICE_MEM_POSTFIX = "_mem"; +constexpr char* SB_SPICE_FILE_NAME_PREFIX = "sb_"; +constexpr char* LOGICAL_MODULE_SPICE_FILE_NAME_PREFIX = "logical_tile_"; +constexpr char* GRID_SPICE_FILE_NAME_PREFIX = "grid_"; + #endif diff --git a/openfpga/src/fpga_spice/spice_routing.cpp b/openfpga/src/fpga_spice/spice_routing.cpp new file mode 100644 index 000000000..9c83aef3c --- /dev/null +++ b/openfpga/src/fpga_spice/spice_routing.cpp @@ -0,0 +1,346 @@ +/********************************************************************* + * This file includes functions that are used for + * SPICE 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 "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_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_spice_routing_connection_box_unique_module(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& subckt_dir, + const RRGSB& rr_gsb, + const t_rr_type& cb_type) { + /* Create the netlist */ + vtr::Point gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + std::string spice_fname(subckt_dir + generate_connection_block_netlist_name(cb_type, gsb_coordinate, std::string(SPICE_NETLIST_FILE_POSTFIX))); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("SPICE 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)) + "]")); + + /* 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 spice module */ + write_spice_subckt_to_file(fp, module_manager, cb_module); + + /* Add an empty line as a splitter */ + fp << std::endl; + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::ROUTING_MODULE_NETLIST); +} + +/********************************************************************* + * Generate the SPICE 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_spice_routing_switch_box_unique_module(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& subckt_dir, + const RRGSB& rr_gsb) { + /* Create the netlist */ + vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + std::string spice_fname(subckt_dir + generate_routing_block_netlist_name(SB_SPICE_FILE_NAME_PREFIX, gsb_coordinate, std::string(SPICE_NETLIST_FILE_POSTFIX))); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("SPICE subcircuits for Unique Switch Blocks[" + std::to_string(rr_gsb.get_sb_x()) + "]["+ std::to_string(rr_gsb.get_sb_y()) + "]")); + + /* 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 spice module */ + write_spice_subckt_to_file(fp, module_manager, sb_module); + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::ROUTING_MODULE_NETLIST); +} + +/******************************************************************** + * Iterate over all the connection blocks in a device + * and build a module for each of them + *******************************************************************/ +static +void print_spice_flatten_connection_block_modules(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceRRGSB& device_rr_gsb, + const std::string& subckt_dir, + const t_rr_type& cb_type) { + /* 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_spice_routing_connection_box_unique_module(netlist_manager, + module_manager, + subckt_dir, + rr_gsb, cb_type); + } + } +} + +/******************************************************************** + * 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_spice_flatten_routing_modules(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceRRGSB& device_rr_gsb, + const std::string& subckt_dir) { + /* 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_spice_routing_switch_box_unique_module(netlist_manager, + module_manager, + subckt_dir, + rr_gsb); + } + } + + print_spice_flatten_connection_block_modules(netlist_manager, module_manager, device_rr_gsb, subckt_dir, CHANX); + + print_spice_flatten_connection_block_modules(netlist_manager, module_manager, device_rr_gsb, subckt_dir, CHANY); + + /* + VTR_LOG("Writing header file for routing submodules '%s'...", + ROUTING_VERILOG_FILE_NAME); + print_spice_netlist_include_header_file(netlist_names, + subckt_dir.c_str(), + ROUTING_VERILOG_FILE_NAME); + VTR_LOG("Done\n"); + VTR_LOG("\n"); + */ +} + + +/******************************************************************** + * A top-level function of this file + * Print all the unique modules for global routing architecture of a FPGA fabric + * in Verilog format, including: + * 1. Connection blocks + * 2. Switch blocks + * + * Note: this function SHOULD be called only when + * the option compact_routing_hierarchy is turned on!!! + *******************************************************************/ +void print_spice_unique_routing_modules(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceRRGSB& device_rr_gsb, + const std::string& subckt_dir) { + /* 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_spice_routing_switch_box_unique_module(netlist_manager, + module_manager, + subckt_dir, + unique_mirror); + } + + /* 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_spice_routing_connection_box_unique_module(netlist_manager, + module_manager, + subckt_dir, + unique_mirror, CHANX); + } + + /* 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_spice_routing_connection_box_unique_module(netlist_manager, + module_manager, + subckt_dir, + unique_mirror, CHANY); + } + + /* + VTR_LOG("Writing header file for routing submodules '%s'...", + ROUTING_VERILOG_FILE_NAME); + print_spice_netlist_include_header_file(netlist_names, + subckt_dir.c_str(), + ROUTING_VERILOG_FILE_NAME); + VTR_LOG("Done\n"); + */ + VTR_LOG("\n"); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_routing.h b/openfpga/src/fpga_spice/spice_routing.h new file mode 100644 index 000000000..3d2e499cc --- /dev/null +++ b/openfpga/src/fpga_spice/spice_routing.h @@ -0,0 +1,32 @@ +#ifndef SPICE_ROUTING_H +#define SPICE_ROUTING_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ + +#include "mux_library.h" +#include "module_manager.h" +#include "netlist_manager.h" +#include "device_rr_gsb.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_spice_flatten_routing_modules(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceRRGSB& device_rr_gsb, + const std::string& subckt_dir); + +void print_spice_unique_routing_modules(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceRRGSB& device_rr_gsb, + const std::string& subckt_dir); + +} /* end namespace openfpga */ + +#endif From 1dfb3e06ccafc68fecef77860807b48bf85bbdba Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:38:24 -0600 Subject: [PATCH 18/25] [FPGA-SPICE] add SPICE writer for logic blocks --- openfpga/src/base/openfpga_spice.cpp | 2 + openfpga/src/fpga_spice/spice_api.cpp | 10 + openfpga/src/fpga_spice/spice_api.h | 4 + openfpga/src/fpga_spice/spice_grid.cpp | 434 +++++++++++++++++++++++++ openfpga/src/fpga_spice/spice_grid.h | 30 ++ 5 files changed, 480 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_grid.cpp create mode 100644 openfpga/src/fpga_spice/spice_grid.h diff --git a/openfpga/src/base/openfpga_spice.cpp b/openfpga/src/base/openfpga_spice.cpp index 0ddab0c91..c1b418835 100644 --- a/openfpga/src/base/openfpga_spice.cpp +++ b/openfpga/src/base/openfpga_spice.cpp @@ -41,6 +41,8 @@ int write_fabric_spice(OpenfpgaContext& openfpga_ctx, openfpga_ctx.mutable_spice_netlists(), openfpga_ctx.arch(), openfpga_ctx.mux_lib(), + g_vpr_ctx.device(), + openfpga_ctx.vpr_device_annotation(), openfpga_ctx.device_rr_gsb(), options); diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index 852d29a8d..7787f8ba0 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -17,6 +17,7 @@ #include "spice_constants.h" #include "spice_submodule.h" #include "spice_routing.h" +#include "spice_grid.h" /* Header file for this source file */ #include "spice_api.h" @@ -42,6 +43,8 @@ int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, const MuxLibrary& mux_lib, + const DeviceContext &device_ctx, + const VprDeviceAnnotation &device_annotation, const DeviceRRGSB &device_rr_gsb, const FabricSpiceOption& options) { @@ -97,6 +100,13 @@ int fpga_fabric_spice(const ModuleManager& module_manager, rr_dir_path); } + /* Generate grids */ + print_spice_grids(netlist_manager, + module_manager, + device_ctx, device_annotation, + lb_dir_path, + options.verbose_output()); + /* Given a brief stats on how many Spice modules have been written to files */ VTR_LOGV(options.verbose_output(), "Written %lu SPICE modules in total\n", diff --git a/openfpga/src/fpga_spice/spice_api.h b/openfpga/src/fpga_spice/spice_api.h index 2d418432d..0e08aa440 100644 --- a/openfpga/src/fpga_spice/spice_api.h +++ b/openfpga/src/fpga_spice/spice_api.h @@ -11,6 +11,8 @@ #include "module_manager.h" #include "openfpga_arch.h" #include "mux_library.h" +#include "vpr_context.h" +#include "vpr_device_annotation.h" #include "device_rr_gsb.h" #include "fabric_spice_options.h" @@ -25,6 +27,8 @@ int fpga_fabric_spice(const ModuleManager& module_manager, NetlistManager& netlist_manager, const Arch& openfpga_arch, const MuxLibrary& mux_lib, + const DeviceContext &device_ctx, + const VprDeviceAnnotation &device_annotation, const DeviceRRGSB &device_rr_gsb, const FabricSpiceOption& options); diff --git a/openfpga/src/fpga_spice/spice_grid.cpp b/openfpga/src/fpga_spice/spice_grid.cpp new file mode 100644 index 000000000..c04ed8920 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_grid.cpp @@ -0,0 +1,434 @@ +/******************************************************************** + * This file includes functions to print SPICE subckts 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 "openfpga_physical_tile_utils.h" +#include "pb_type_utils.h" +#include "circuit_library_utils.h" +#include "module_manager_utils.h" + +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_grid.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print SPICE subckts 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 SPICE subckt 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 + * + * SPICE subckt structure: + * + * Primitive block + * +---------------------------------------+ + * | | + * | +---------+ +---------+ | + * in |----->| |--->| |<------|configuration lines + * | | Logic |... | Memory | | + * out|<-----| |--->| | | + * | +---------+ +---------+ | + * | | + * +---------------------------------------+ + * + *******************************************************************/ +static +void print_spice_primitive_block(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& subckt_dir, + t_pb_graph_node* primitive_pb_graph_node, + const bool& verbose) { + /* 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); + } + + /* Give a name to the Verilog netlist */ + /* Create the file name for Verilog */ + std::string spice_fname(subckt_dir + + generate_logical_tile_netlist_name(std::string(), primitive_pb_graph_node, std::string(SPICE_NETLIST_FILE_POSTFIX)) + ); + + VTR_LOG("Writing SPICE netlist '%s' for primitive pb_type '%s' ...", + spice_fname.c_str(), primitive_pb_graph_node->pb_type->name); + VTR_LOGV(verbose, "\n"); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("SPICE subckts for primitive pb_type: " + std::string(primitive_pb_graph_node->pb_type->name))); + + /* 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 SPICE codes of logical tile primitive block '%s'...", + module_manager.module_name(primitive_module).c_str()); + + /* Write the spice module */ + write_spice_subckt_to_file(fp, module_manager, primitive_module); + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::LOGIC_BLOCK_NETLIST); + + VTR_LOGV(verbose, "Done\n"); +} + +/******************************************************************** + * Print SPICE subckts 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 SPICE subckt 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_spice_logical_tile(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const VprDeviceAnnotation& device_annotation, + const std::string& subckt_dir, + t_pb_graph_node* physical_pb_graph_node, + const bool& verbose) { + + /* 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_spice_logical_tile(netlist_manager, + module_manager, device_annotation, + subckt_dir, + &(physical_pb_graph_node->child_pb_graph_nodes[physical_mode->index][ipb][0]), + verbose); + } + } + + /* For leaf node, a primitive SPICE subckt 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_spice_primitive_block(netlist_manager, + module_manager, + subckt_dir, + physical_pb_graph_node, + verbose); + /* Finish for primitive node, return */ + return; + } + + /* Give a name to the Verilog netlist */ + /* Create the file name for Verilog */ + std::string spice_fname(subckt_dir + + generate_logical_tile_netlist_name(std::string(), physical_pb_graph_node, std::string(SPICE_NETLIST_FILE_POSTFIX)) + ); + + VTR_LOG("Writing SPICE netlist '%s' for pb_type '%s' ...", + spice_fname.c_str(), physical_pb_type->name); + VTR_LOGV(verbose, "\n"); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("SPICE subckts for pb_type: " + std::string(physical_pb_type->name))); + + /* Generate the name of the SPICE subckt for this pb_type */ + std::string pb_module_name = generate_physical_block_module_name(physical_pb_type); + + /* Register the SPICE subckt 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 SPICE codes of pb_type '%s'...", + module_manager.module_name(pb_module).c_str()); + + /* Comment lines */ + print_spice_comment(fp, std::string("BEGIN Physical programmable logic block SPICE subckt: " + std::string(physical_pb_type->name))); + + /* Write the spice module */ + write_spice_subckt_to_file(fp, module_manager, pb_module); + + print_spice_comment(fp, std::string("END Physical programmable logic block SPICE subckt: " + std::string(physical_pb_type->name))); + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::LOGIC_BLOCK_NETLIST); + + 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_spice_logical_tile_netlist(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const VprDeviceAnnotation& device_annotation, + const std::string& subckt_dir, + t_pb_graph_node* pb_graph_head, + const bool& verbose) { + + VTR_LOG("Writing Verilog netlists for logic tile '%s' ...", + pb_graph_head->pb_type->name); + VTR_LOG("\n"); + + /* Print SPICE subckts 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 SPICE subckts starting from the top-level pb_type/pb_graph_node, and traverse the graph in a recursive way */ + rec_print_spice_logical_tile(netlist_manager, + module_manager, + device_annotation, + subckt_dir, + pb_graph_head, + verbose); + + 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_spice_physical_tile_netlist(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& subckt_dir, + t_physical_tile_type_ptr phy_block_type, + const e_side& border_side) { + /* 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 spice_fname(subckt_dir + + generate_grid_block_netlist_name(std::string(GRID_MODULE_NAME_PREFIX) + std::string(phy_block_type->name), + is_io_type(phy_block_type), + border_side, + std::string(SPICE_NETLIST_FILE_POSTFIX)) + ); + + /* Echo status */ + if (true == is_io_type(phy_block_type)) { + SideManager side_manager(border_side); + VTR_LOG("Writing SPICE Netlist '%s' for physical tile '%s' at %s side ...", + spice_fname.c_str(), phy_block_type->name, + side_manager.c_str()); + } else { + VTR_LOG("Writing SPICE Netlist '%s' for physical_tile '%s'...", + spice_fname.c_str(), phy_block_type->name); + } + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("SPICE subckts for physical tile: " + std::string(phy_block_type->name) + "]")); + + /* 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_SPICE_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 spice module */ + print_spice_comment(fp, std::string("BEGIN Grid SPICE subckt: " + module_manager.module_name(grid_module))); + write_spice_subckt_to_file(fp, module_manager, grid_module); + + print_spice_comment(fp, std::string("END Grid SPICE subckt: " + 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 */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::LOGIC_BLOCK_NETLIST); + + 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_spice_grids(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const std::string& subckt_dir, + 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_spice_logical_tile_netlist(netlist_manager, + module_manager, + device_annotation, + subckt_dir, + logical_tile.pb_graph_head, + 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: + * We will search the grids and see where the I/O blocks are located: + * - If a I/O block locates on border sides of FPGA fabric: + * i.e., one or more from {TOP, RIGHT, BOTTOM, LEFT}, + * we will generate one module for each border side + * - If a I/O block locates in the center of FPGA fabric: + * we will generate one module with NUM_SIDES (same treatment as regular grids) + */ + std::set io_type_sides = find_physical_io_tile_located_sides(device_ctx.grid, + &physical_tile); + for (const e_side& io_type_side : io_type_sides) { + print_spice_physical_tile_netlist(netlist_manager, + module_manager, + subckt_dir, + &physical_tile, + io_type_side); + } + continue; + } else { + /* For CLB and heterogenenous blocks */ + print_spice_physical_tile_netlist(netlist_manager, + module_manager, + subckt_dir, + &physical_tile, + NUM_SIDES); + } + } + VTR_LOG("Building physical tiles..."); + VTR_LOG("Done\n"); + VTR_LOG("\n"); + + /* Output a header file for all the logic blocks */ + /* + std::string grid_spice_fname(LOGIC_BLOCK_VERILOG_FILE_NAME); + VTR_LOG("Writing header file for grid SPICE subckts '%s' ...", + grid_spice_fname.c_str()); + print_spice_netlist_include_header_file(netlist_names, + subckt_dir.c_str(), + grid_spice_fname.c_str()); + VTR_LOG("Done\n"); + */ +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_grid.h b/openfpga/src/fpga_spice/spice_grid.h new file mode 100644 index 000000000..5f45e2d61 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_grid.h @@ -0,0 +1,30 @@ +#ifndef SPICE_GRID_H +#define SPICE_GRID_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "vpr_context.h" +#include "module_manager.h" +#include "netlist_manager.h" +#include "vpr_device_annotation.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_spice_grids(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const std::string& subckt_dir, + const bool& verbose); + + +} /* end namespace openfpga */ + +#endif From 06c0073a3ea49d4610d680e02699b3a7e0267ffa Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:43:48 -0600 Subject: [PATCH 19/25] [FPGA-SPICE] Add SPICE writer for fpga top module --- openfpga/src/fpga_spice/spice_api.cpp | 13 ++++ openfpga/src/fpga_spice/spice_top_module.cpp | 76 ++++++++++++++++++++ openfpga/src/fpga_spice/spice_top_module.h | 24 +++++++ 3 files changed, 113 insertions(+) create mode 100644 openfpga/src/fpga_spice/spice_top_module.cpp create mode 100644 openfpga/src/fpga_spice/spice_top_module.h diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index 7787f8ba0..a06a3fb8e 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -18,6 +18,7 @@ #include "spice_submodule.h" #include "spice_routing.h" #include "spice_grid.h" +#include "spice_top_module.h" /* Header file for this source file */ #include "spice_api.h" @@ -107,6 +108,18 @@ int fpga_fabric_spice(const ModuleManager& module_manager, lb_dir_path, options.verbose_output()); + /* Generate FPGA fabric */ + print_spice_top_module(netlist_manager, + module_manager, + src_dir_path); + + /* Generate an netlist including all the fabric-related netlists */ + /* + print_fabric_include_netlist(const_cast(netlist_manager), + src_dir_path, + circuit_lib); + */ + /* Given a brief stats on how many Spice modules have been written to files */ VTR_LOGV(options.verbose_output(), "Written %lu SPICE modules in total\n", diff --git a/openfpga/src/fpga_spice/spice_top_module.cpp b/openfpga/src/fpga_spice/spice_top_module.cpp new file mode 100644 index 000000000..d153d9ef7 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_top_module.cpp @@ -0,0 +1,76 @@ +/******************************************************************** + * This file includes functions that are used to print the top-level + * module for the FPGA fabric in SPICE 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 "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_subckt_writer.h" +#include "spice_top_module.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Print the top-level module for the FPGA fabric in SPICE 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_spice_top_module(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& spice_dir) { + /* 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)); + + /* Create the file name for SPICE netlist */ + std::string spice_fname(spice_dir + generate_fpga_top_netlist_name(std::string(SPICE_NETLIST_FILE_POSTFIX))); + + VTR_LOG("Writing SPICE netlist for top-level module of FPGA fabric '%s'...", + spice_fname.c_str()); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + check_file_stream(spice_fname.c_str(), fp); + + print_spice_file_header(fp, std::string("Top-level SPICE subckt for FPGA")); + + /* Write the module content in Verilog format */ + write_spice_subckt_to_file(fp, module_manager, top_module); + + /* Add an empty line as a splitter */ + fp << std::endl; + + /* Close file handler */ + fp.close(); + + /* Add fname to the netlist name list */ + NetlistId nlist_id = netlist_manager.add_netlist(spice_fname); + VTR_ASSERT(NetlistId::INVALID() != nlist_id); + netlist_manager.set_netlist_type(nlist_id, NetlistManager::TOP_MODULE_NETLIST); + + VTR_LOG("Done\n"); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_top_module.h b/openfpga/src/fpga_spice/spice_top_module.h new file mode 100644 index 000000000..9cea104cc --- /dev/null +++ b/openfpga/src/fpga_spice/spice_top_module.h @@ -0,0 +1,24 @@ +#ifndef SPICE_TOP_MODULE_H +#define SPICE_TOP_MODULE_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "module_manager.h" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_spice_top_module(NetlistManager& netlist_manager, + const ModuleManager& module_manager, + const std::string& spice_dir); + +} /* end namespace openfpga */ + +#endif From 222bc86cbfaa00bb5ad54df6c2f28008b58f0b12 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:53:28 -0600 Subject: [PATCH 20/25] [FPGA-SPICE] Add auxiliary SPICE netlist writer --- openfpga/src/fpga_spice/spice_api.cpp | 9 +- .../fpga_spice/spice_auxiliary_netlists.cpp | 86 +++++++++++++++++++ .../src/fpga_spice/spice_auxiliary_netlists.h | 24 ++++++ openfpga/src/fpga_spice/spice_constants.h | 1 + openfpga/src/utils/circuit_library_utils.cpp | 23 +++++ openfpga/src/utils/circuit_library_utils.h | 2 + 6 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 openfpga/src/fpga_spice/spice_auxiliary_netlists.cpp create mode 100644 openfpga/src/fpga_spice/spice_auxiliary_netlists.h diff --git a/openfpga/src/fpga_spice/spice_api.cpp b/openfpga/src/fpga_spice/spice_api.cpp index a06a3fb8e..22c433bd6 100644 --- a/openfpga/src/fpga_spice/spice_api.cpp +++ b/openfpga/src/fpga_spice/spice_api.cpp @@ -19,6 +19,7 @@ #include "spice_routing.h" #include "spice_grid.h" #include "spice_top_module.h" +#include "spice_auxiliary_netlists.h" /* Header file for this source file */ #include "spice_api.h" @@ -114,11 +115,9 @@ int fpga_fabric_spice(const ModuleManager& module_manager, src_dir_path); /* Generate an netlist including all the fabric-related netlists */ - /* - print_fabric_include_netlist(const_cast(netlist_manager), - src_dir_path, - circuit_lib); - */ + print_spice_fabric_include_netlist(const_cast(netlist_manager), + src_dir_path, + openfpga_arch.circuit_lib); /* Given a brief stats on how many Spice modules have been written to files */ VTR_LOGV(options.verbose_output(), diff --git a/openfpga/src/fpga_spice/spice_auxiliary_netlists.cpp b/openfpga/src/fpga_spice/spice_auxiliary_netlists.cpp new file mode 100644 index 000000000..f76464898 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_auxiliary_netlists.cpp @@ -0,0 +1,86 @@ +/******************************************************************** + * This file includes functions that are used to generate SPICE files + * or code blocks, with a focus on + * `include user-defined or auto-generated netlists in SPICE format + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" + +/* Headers from openfpgautil library */ +#include "openfpga_digest.h" + +#include "openfpga_naming.h" +#include "circuit_library_utils.h" +#include "spice_constants.h" +#include "spice_writer_utils.h" +#include "spice_auxiliary_netlists.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Local constant variables + *******************************************************************/ + +/******************************************************************** + * Print a file that includes all the fabric netlists + * that have been generated and user-defined. + * This does NOT include any testbenches! + * Some netlists are open to compile under specific preprocessing flags + *******************************************************************/ +void print_spice_fabric_include_netlist(const NetlistManager& netlist_manager, + const std::string& src_dir, + const CircuitLibrary& circuit_lib) { + std::string spice_fname = src_dir + std::string(FABRIC_INCLUDE_SPICE_NETLIST_FILE_NAME); + + /* Create the file stream */ + std::fstream fp; + fp.open(spice_fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_stream(spice_fname.c_str(), fp); + + /* Print the title */ + print_spice_file_header(fp, std::string("Fabric Netlist Summary")); + + /* Include all the user-defined netlists */ + print_spice_comment(fp, std::string("Include user-defined netlists")); + for (const std::string& user_defined_netlist : find_circuit_library_unique_spice_netlists(circuit_lib)) { + print_spice_include_netlist(fp, user_defined_netlist); + } + + /* Include all the primitive modules */ + print_spice_comment(fp, std::string("Include primitive module netlists")); + for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::SUBMODULE_NETLIST)) { + print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id)); + } + fp << std::endl; + + /* Include all the CLB, heterogeneous block modules */ + print_spice_comment(fp, std::string("Include logic block netlists")); + for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::LOGIC_BLOCK_NETLIST)) { + print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id)); + } + fp << std::endl; + + /* Include all the routing architecture modules */ + print_spice_comment(fp, std::string("Include routing module netlists")); + for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::ROUTING_MODULE_NETLIST)) { + print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id)); + } + fp << std::endl; + + /* Include FPGA top module */ + print_spice_comment(fp, std::string("Include fabric top-level netlists")); + for (const NetlistId& nlist_id : netlist_manager.netlists_by_type(NetlistManager::TOP_MODULE_NETLIST)) { + print_spice_include_netlist(fp, netlist_manager.netlist_name(nlist_id)); + } + fp << std::endl; + + /* Close the file stream */ + fp.close(); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fpga_spice/spice_auxiliary_netlists.h b/openfpga/src/fpga_spice/spice_auxiliary_netlists.h new file mode 100644 index 000000000..aa04a54b9 --- /dev/null +++ b/openfpga/src/fpga_spice/spice_auxiliary_netlists.h @@ -0,0 +1,24 @@ +#ifndef SPICE_AUXILIARY_NETLISTS_H +#define SPICE_AUXILIARY_NETLISTS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "circuit_library.h" +#include "netlist_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void print_spice_fabric_include_netlist(const NetlistManager& netlist_manager, + const std::string& src_dir, + const CircuitLibrary& circuit_lib); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 97b1733c2..68026a0be 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -12,6 +12,7 @@ constexpr char* SUPPLY_WRAPPER_SPICE_FILE_NAME = "supply_wrapper.sp"; constexpr char* MUXES_SPICE_FILE_NAME = "muxes.sp"; constexpr char* LUTS_SPICE_FILE_NAME = "luts.sp"; constexpr char* MEMORIES_SPICE_FILE_NAME = "memories.sp"; +constexpr char* FABRIC_INCLUDE_SPICE_NETLIST_FILE_NAME = "fabric_netlists.sp"; constexpr char* SPICE_SUBCKT_VDD_PORT_NAME = "VDD"; constexpr char* SPICE_SUBCKT_GND_PORT_NAME = "VSS"; diff --git a/openfpga/src/utils/circuit_library_utils.cpp b/openfpga/src/utils/circuit_library_utils.cpp index cd59545ae..8fb5aa7bc 100644 --- a/openfpga/src/utils/circuit_library_utils.cpp +++ b/openfpga/src/utils/circuit_library_utils.cpp @@ -251,6 +251,29 @@ std::vector find_circuit_library_unique_verilog_netlists(const Circ return netlists; } +/******************************************************************** + * A generic function to find all the unique user-defined + * Verilog netlists in a circuit library + * Netlists with same names will be considered as one + *******************************************************************/ +std::vector find_circuit_library_unique_spice_netlists(const CircuitLibrary& circuit_lib) { + std::vector netlists; + + for (const CircuitModelId& model : circuit_lib.models()) { + /* Skip empty netlist names */ + if (true == circuit_lib.model_spice_netlist(model).empty()) { + continue; + } + /* See if the netlist name is already in the list */ + std::vector::iterator it = std::find(netlists.begin(), netlists.end(), circuit_lib.model_spice_netlist(model)); + if (it == netlists.end()) { + netlists.push_back(circuit_lib.model_spice_netlist(model)); + } + } + + return netlists; +} + /************************************************************************ * Advanced check if the circuit model of configurable memory * satisfy the needs of configuration protocol diff --git a/openfpga/src/utils/circuit_library_utils.h b/openfpga/src/utils/circuit_library_utils.h index 584871868..dc7fd0e34 100644 --- a/openfpga/src/utils/circuit_library_utils.h +++ b/openfpga/src/utils/circuit_library_utils.h @@ -39,6 +39,8 @@ std::vector find_circuit_library_global_ports(const CircuitLibrar std::vector find_circuit_library_unique_verilog_netlists(const CircuitLibrary& circuit_lib); +std::vector find_circuit_library_unique_spice_netlists(const CircuitLibrary& circuit_lib); + bool check_configurable_memory_circuit_model(const e_config_protocol_type& config_protocol_type, const CircuitLibrary& circuit_lib, const CircuitModelId& config_mem_circuit_model); From 460fef580717ff23285102bbf098578a2bc009b8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 12:58:55 -0600 Subject: [PATCH 21/25] [FPGA-Verilog] Rename files and functions to distinguish from FPGA-SPICE files and functions --- openfpga/src/fpga_verilog/verilog_api.cpp | 14 +++++++------- .../verilog_auxiliary_netlists.cpp | 18 +++++++++--------- .../fpga_verilog/verilog_auxiliary_netlists.h | 12 ++++++------ openfpga/src/fpga_verilog/verilog_constants.h | 4 ++-- ....cpp => verilog_simulation_info_writer.cpp} | 4 ++-- ...iter.h => verilog_simulation_info_writer.h} | 4 ++-- 6 files changed, 28 insertions(+), 28 deletions(-) rename openfpga/src/fpga_verilog/{simulation_info_writer.cpp => verilog_simulation_info_writer.cpp} (98%) rename openfpga/src/fpga_verilog/{simulation_info_writer.h => verilog_simulation_info_writer.h} (94%) diff --git a/openfpga/src/fpga_verilog/verilog_api.cpp b/openfpga/src/fpga_verilog/verilog_api.cpp index d02ced128..940d78953 100644 --- a/openfpga/src/fpga_verilog/verilog_api.cpp +++ b/openfpga/src/fpga_verilog/verilog_api.cpp @@ -24,7 +24,7 @@ #include "verilog_preconfig_top_module.h" #include "verilog_formal_random_top_testbench.h" #include "verilog_top_testbench.h" -#include "simulation_info_writer.h" +#include "verilog_simulation_info_writer.h" /* Header file for this source file */ #include "verilog_api.h" @@ -128,9 +128,9 @@ void fpga_fabric_verilog(ModuleManager &module_manager, options.explicit_port_mapping()); /* Generate an netlist including all the fabric-related netlists */ - print_fabric_include_netlist(const_cast(netlist_manager), - src_dir_path, - circuit_lib); + print_verilog_fabric_include_netlist(const_cast(netlist_manager), + src_dir_path, + circuit_lib); /* Given a brief stats on how many Verilog modules have been written to files */ VTR_LOGV(options.verbose_output(), @@ -233,9 +233,9 @@ void fpga_verilog_testbench(const ModuleManager &module_manager, } /* Generate a Verilog file including all the netlists that have been generated */ - print_include_netlists(src_dir_path, - netlist_name, - options.reference_benchmark_file_path()); + print_verilog_testbench_include_netlists(src_dir_path, + netlist_name, + options.reference_benchmark_file_path()); } } /* end namespace openfpga */ diff --git a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp index 65e0d1ca2..93fdb2161 100644 --- a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp +++ b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.cpp @@ -30,10 +30,10 @@ namespace openfpga { * This does NOT include any testbenches! * Some netlists are open to compile under specific preprocessing flags *******************************************************************/ -void print_fabric_include_netlist(const NetlistManager& netlist_manager, - const std::string& src_dir, - const CircuitLibrary& circuit_lib) { - std::string verilog_fname = src_dir + std::string(FABRIC_INCLUDE_NETLIST_FILE_NAME); +void print_verilog_fabric_include_netlist(const NetlistManager& netlist_manager, + const std::string& src_dir, + const CircuitLibrary& circuit_lib) { + std::string verilog_fname = src_dir + std::string(FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME); /* Create the file stream */ std::fstream fp; @@ -94,10 +94,10 @@ void print_fabric_include_netlist(const NetlistManager& netlist_manager, * that have been generated and user-defined. * Some netlists are open to compile under specific preprocessing flags *******************************************************************/ -void print_include_netlists(const std::string& src_dir, - const std::string& circuit_name, - const std::string& reference_benchmark_file) { - std::string verilog_fname = src_dir + circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX); +void print_verilog_testbench_include_netlists(const std::string& src_dir, + const std::string& circuit_name, + const std::string& reference_benchmark_file) { + std::string verilog_fname = src_dir + circuit_name + std::string(TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX); /* Create the file stream */ std::fstream fp; @@ -116,7 +116,7 @@ void print_include_netlists(const std::string& src_dir, /* Include FPGA top module */ print_verilog_comment(fp, std::string("------ Include fabric top-level netlists -----")); - print_verilog_include_netlist(fp, src_dir + std::string(FABRIC_INCLUDE_NETLIST_FILE_NAME)); + print_verilog_include_netlist(fp, src_dir + std::string(FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME)); fp << std::endl; /* Include reference benchmark netlist only when auto-check flag is enabled */ diff --git a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h index 57f04032f..c3952af85 100644 --- a/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h +++ b/openfpga/src/fpga_verilog/verilog_auxiliary_netlists.h @@ -17,13 +17,13 @@ /* begin namespace openfpga */ namespace openfpga { -void print_fabric_include_netlist(const NetlistManager& netlist_manager, - const std::string& src_dir, - const CircuitLibrary& circuit_lib); +void print_verilog_fabric_include_netlist(const NetlistManager& netlist_manager, + const std::string& src_dir, + const CircuitLibrary& circuit_lib); -void print_include_netlists(const std::string& src_dir, - const std::string& circuit_name, - const std::string& reference_benchmark_file); +void print_verilog_testbench_include_netlists(const std::string& src_dir, + const std::string& circuit_name, + const std::string& reference_benchmark_file); void print_verilog_preprocessing_flags_netlist(const std::string& src_dir, const FabricVerilogOption& fabric_verilog_opts); diff --git a/openfpga/src/fpga_verilog/verilog_constants.h b/openfpga/src/fpga_verilog/verilog_constants.h index a09425d4f..66ff018ff 100644 --- a/openfpga/src/fpga_verilog/verilog_constants.h +++ b/openfpga/src/fpga_verilog/verilog_constants.h @@ -19,8 +19,8 @@ constexpr char* MODELSIM_SIMULATION_TIME_UNIT = "ms"; constexpr char* ICARUS_SIMULATOR_FLAG = "ICARUS_SIMULATOR"; // the flag to enable specific Verilog code in testbenches // End of Icarus variables and flag -constexpr char* FABRIC_INCLUDE_NETLIST_FILE_NAME = "fabric_netlists.v"; -constexpr char* TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v"; +constexpr char* FABRIC_INCLUDE_VERILOG_NETLIST_FILE_NAME = "fabric_netlists.v"; +constexpr char* TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX = "_include_netlists.v"; constexpr char* VERILOG_TOP_POSTFIX = "_top.v"; constexpr char* FORMAL_VERIFICATION_VERILOG_FILE_POSTFIX = "_top_formal_verification.v"; constexpr char* TOP_TESTBENCH_VERILOG_FILE_POSTFIX = "_top_tb.v"; /* !!! must be consist with the modelsim_testbench_module_postfix */ diff --git a/openfpga/src/fpga_verilog/simulation_info_writer.cpp b/openfpga/src/fpga_verilog/verilog_simulation_info_writer.cpp similarity index 98% rename from openfpga/src/fpga_verilog/simulation_info_writer.cpp rename to openfpga/src/fpga_verilog/verilog_simulation_info_writer.cpp index 134b34f0a..d4a64fcd0 100644 --- a/openfpga/src/fpga_verilog/simulation_info_writer.cpp +++ b/openfpga/src/fpga_verilog/verilog_simulation_info_writer.cpp @@ -21,7 +21,7 @@ #include "simulation_utils.h" #include "verilog_constants.h" -#include "simulation_info_writer.h" +#include "verilog_simulation_info_writer.h" /* begin namespace openfpga */ namespace openfpga { @@ -77,7 +77,7 @@ void print_verilog_simulation_info(const std::string& ini_fname, ini["SIMULATION_DECK"]["UNIT "] = "ms"; ini["SIMULATION_DECK"]["VERILOG_PATH "] = std::string(src_dir); ini["SIMULATION_DECK"]["VERILOG_FILE1"] = std::string(DEFINES_VERILOG_FILE_NAME); - ini["SIMULATION_DECK"]["VERILOG_FILE2"] = std::string(circuit_name + std::string(TOP_INCLUDE_NETLIST_FILE_NAME_POSTFIX)); + ini["SIMULATION_DECK"]["VERILOG_FILE2"] = std::string(circuit_name + std::string(TOP_VERILOG_TESTBENCH_INCLUDE_NETLIST_FILE_NAME_POSTFIX)); ini["SIMULATION_DECK"]["CONFIG_PROTOCOL"] = std::string(CONFIG_PROTOCOL_TYPE_STRING[config_protocol_type]); /* Information required by UVM */ diff --git a/openfpga/src/fpga_verilog/simulation_info_writer.h b/openfpga/src/fpga_verilog/verilog_simulation_info_writer.h similarity index 94% rename from openfpga/src/fpga_verilog/simulation_info_writer.h rename to openfpga/src/fpga_verilog/verilog_simulation_info_writer.h index ff421fadc..8806a71ac 100644 --- a/openfpga/src/fpga_verilog/simulation_info_writer.h +++ b/openfpga/src/fpga_verilog/verilog_simulation_info_writer.h @@ -1,5 +1,5 @@ -#ifndef SIMULATION_INFO_WRITER_H -#define SIMULATION_INFO_WRITER_H +#ifndef VERILOG_SIMULATION_INFO_WRITER_H +#define VERILOG_SIMULATION_INFO_WRITER_H /******************************************************************** * Include header files that are required by function declaration From 615a24999aad52f4a4d22380178afe591ca2d311 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 14:45:33 -0600 Subject: [PATCH 22/25] [Documentation] Remove out-of-date description --- README_Benchmarks.md | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 README_Benchmarks.md diff --git a/README_Benchmarks.md b/README_Benchmarks.md deleted file mode 100644 index b9797186e..000000000 --- a/README_Benchmarks.md +++ /dev/null @@ -1,9 +0,0 @@ -[TODO]: # (This document might be incomplete. It is important to keep it updated with the right names) - -### Benchmarks README ### -######### - -fpga_flow is a folder which contains benchmarks testing the performances of the tool on a known variety of benchmarks. In order to launch them, a script called run_fpga_spice_testbench_study.sh is called. This script is based on different other scripts which can be found in the script folder. - -The different benchmarks can be found in the fpga_spice_bench.txt found in the benchmarks folder. By commenting them, the script will not read them and not run through them. This can be useful if you want to focus on one benchmark in particular for example. - From 544c44fe4618fdfaa1e346cfc93544618f1fc9b7 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 14:58:15 -0600 Subject: [PATCH 23/25] [FPGA-SPICE] Add VDD and VSS port to module definition --- .../src/fpga_spice/spice_writer_utils.cpp | 28 ++++++++++++++++++- openfpga/src/fpga_spice/spice_writer_utils.h | 4 ++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_writer_utils.cpp b/openfpga/src/fpga_spice/spice_writer_utils.cpp index 889ee8c19..75f22196c 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.cpp +++ b/openfpga/src/fpga_spice/spice_writer_utils.cpp @@ -18,6 +18,7 @@ /* Headers from openfpgautil library */ #include "openfpga_digest.h" +#include "spice_constants.h" #include "spice_writer_utils.h" /* begin namespace openfpga */ @@ -98,7 +99,9 @@ std::string generate_spice_port(const BasicPort& port, * module (); ***********************************************/ void print_spice_subckt_definition(std::fstream& fp, - const ModuleManager& module_manager, const ModuleId& module_id) { + const ModuleManager& module_manager, + const ModuleId& module_id, + const bool& include_supply_ports) { VTR_ASSERT(true == valid_file_stream(fp)); print_spice_comment(fp, std::string("SPICE module for " + module_manager.module_name(module_id))); @@ -153,6 +156,29 @@ void print_spice_subckt_definition(std::fstream& fp, } } } + + /* Add supply ports if specified */ + if (true == include_supply_ports) { + /* Check if we need a new line */ + new_line = false; + if (10 == pin_cnt) { + pin_cnt = 0; + fp << std::endl; + new_line = true; + } + /* Print VDD and VSS ports + * TODO: the supply ports should be derived from module manager + */ + if (true == new_line) { + std::string port_whitespace(module_head_line.length() - 2, ' '); + fp << "+ " << port_whitespace; + } + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_VDD_PORT_NAME; + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_GND_PORT_NAME; + } + fp << std::endl; } diff --git a/openfpga/src/fpga_spice/spice_writer_utils.h b/openfpga/src/fpga_spice/spice_writer_utils.h index b01d5a35c..a3617a43a 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.h +++ b/openfpga/src/fpga_spice/spice_writer_utils.h @@ -43,7 +43,9 @@ std::string generate_spice_port(const BasicPort& port, const bool& omit_pin_zero = false); void print_spice_subckt_definition(std::fstream& fp, - const ModuleManager& module_manager, const ModuleId& module_id); + const ModuleManager& module_manager, + const ModuleId& module_id, + const bool& include_supply_ports = true); void print_spice_subckt_end(std::fstream& fp, const std::string& module_name); From e867e203f44561fae5ceb0a862ce20e4fc63bff9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 15:00:56 -0600 Subject: [PATCH 24/25] [Documentation] Use release mode in Docker settings --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 771ab425c..94f753737 100755 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ RUN mkdir -p /release /dev RUN cd release && git clone --single-branch --branch master https://github.com/LNIS-Projects/OpenFPGA.git OpenFPGA -RUN cd /release/OpenFPGA && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=debug -DCMAKE_NO_GRAPHICS=on && make +RUN cd /release/OpenFPGA && mkdir build && cd build && cmake .. -DCMAKE_NO_GRAPHICS=on && make RUN rm -rf /var/lib/apt/lists/* From c6ac02d210466fb0d31aa77520e7e0a7c39733b8 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Sun, 20 Sep 2020 15:21:33 -0600 Subject: [PATCH 25/25] [FPGA-SPICE] Add VDD/VSS ports to SPICE subckt instanciation --- openfpga/src/fpga_spice/spice_constants.h | 1 + .../src/fpga_spice/spice_subckt_writer.cpp | 28 ++++++++++++++- .../src/fpga_spice/spice_writer_utils.cpp | 34 ++++++++++++++----- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/openfpga/src/fpga_spice/spice_constants.h b/openfpga/src/fpga_spice/spice_constants.h index 68026a0be..3e045b40c 100644 --- a/openfpga/src/fpga_spice/spice_constants.h +++ b/openfpga/src/fpga_spice/spice_constants.h @@ -2,6 +2,7 @@ #define SPICE_CONSTANTS_H /* global parameters for dumping spice netlists */ +constexpr size_t SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE = 10; constexpr char* SPICE_NETLIST_FILE_POSTFIX = ".sp"; diff --git a/openfpga/src/fpga_spice/spice_subckt_writer.cpp b/openfpga/src/fpga_spice/spice_subckt_writer.cpp index c90c0b5ac..291404ec6 100644 --- a/openfpga/src/fpga_spice/spice_subckt_writer.cpp +++ b/openfpga/src/fpga_spice/spice_subckt_writer.cpp @@ -19,6 +19,9 @@ #include "openfpga_naming.h" #include "module_manager_utils.h" + + +#include "spice_constants.h" #include "spice_writer_utils.h" #include "spice_subckt_writer.h" @@ -370,7 +373,7 @@ void write_spice_instance_to_file(std::fstream& fp, /* Currently we limit 10 ports per line to keep a clean netlist */ new_line = false; - if (10 == pin_cnt) { + if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) { pin_cnt = 0; fp << std::endl; new_line = true; @@ -380,6 +383,29 @@ void write_spice_instance_to_file(std::fstream& fp, } } + /* Print VDD and VSS ports + * TODO: the supply ports should be derived from module manager + */ + if (true == new_line) { + std::string port_whitespace(instance_head_line.length() - 2, ' '); + fp << "+ " << port_whitespace; + } + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_VDD_PORT_NAME; + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_GND_PORT_NAME; + + pin_cnt += 2; + + /* Check if we need a new line */ + new_line = false; + if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) { + pin_cnt = 0; + fp << std::endl; + new_line = true; + fit_one_line = false; + } + /* Print module name: * if port print cannot fit one line, we create a new line for the module for a clean format */ diff --git a/openfpga/src/fpga_spice/spice_writer_utils.cpp b/openfpga/src/fpga_spice/spice_writer_utils.cpp index 75f22196c..68d3360b1 100644 --- a/openfpga/src/fpga_spice/spice_writer_utils.cpp +++ b/openfpga/src/fpga_spice/spice_writer_utils.cpp @@ -148,7 +148,7 @@ void print_spice_subckt_definition(std::fstream& fp, /* Currently we limit 10 ports per line to keep a clean netlist */ new_line = false; - if (10 == pin_cnt) { + if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) { pin_cnt = 0; fp << std::endl; new_line = true; @@ -159,13 +159,6 @@ void print_spice_subckt_definition(std::fstream& fp, /* Add supply ports if specified */ if (true == include_supply_ports) { - /* Check if we need a new line */ - new_line = false; - if (10 == pin_cnt) { - pin_cnt = 0; - fp << std::endl; - new_line = true; - } /* Print VDD and VSS ports * TODO: the supply ports should be derived from module manager */ @@ -325,7 +318,7 @@ void print_spice_subckt_instance(std::fstream& fp, /* Currently we limit 10 ports per line to keep a clean netlist */ new_line = false; - if (10 == pin_cnt) { + if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) { pin_cnt = 0; fp << std::endl; new_line = true; @@ -335,6 +328,29 @@ void print_spice_subckt_instance(std::fstream& fp, } } + /* Print VDD and VSS ports + * TODO: the supply ports should be derived from module manager + */ + if (true == new_line) { + std::string port_whitespace(instance_head_line.length() - 2, ' '); + fp << "+ " << port_whitespace; + } + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_VDD_PORT_NAME; + write_space_to_file(fp, 1); + fp << SPICE_SUBCKT_GND_PORT_NAME; + + pin_cnt += 2; + + /* Check if we need a new line */ + new_line = false; + if (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) { + pin_cnt = 0; + fp << std::endl; + new_line = true; + fit_one_line = false; + } + /* Print module name: * if port print cannot fit one line, we create a new line for the module for a clean format */