[FPGA-SPICE] Add wire module SPICE writer

This commit is contained in:
tangxifan 2020-09-19 19:31:16 -06:00
parent 1b2762386c
commit 82e137cbe4
5 changed files with 615 additions and 9 deletions

View File

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

View File

@ -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 <fstream>
#include <cmath>
#include <iomanip>
/* 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<CircuitPortId> input_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_INPUT, true);
std::vector<CircuitPortId> output_ports = circuit_lib.model_ports_by_type(circuit_model, CIRCUIT_MODEL_PORT_OUTPUT, true);
/* 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<std::string, BasicPort> 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<std::string, BasicPort> 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 */

View File

@ -0,0 +1,27 @@
#ifndef SPICE_WIRE_H
#define SPICE_WIRE_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include <string>
#include <map>
#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

View File

@ -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:
* <module_port_name> -> <instance_port_name>
* The key of the port-to-port mapping is the port name of the module:
* The value of the port-to-port mapping is the port information of the instance
* With link between module and instance, the function can output a 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<std::string, BasicPort>& 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<ModuleManager::e_module_port_type>(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 */

View File

@ -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<std::string, BasicPort>& port2port_name_map);
} /* end namespace openfpga */
#endif