Merge pull request #87 from LNIS-Projects/dev
Transplant FPGA-SPICE to OpenFPGA
This commit is contained in:
commit
dc11e84f6a
|
@ -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/*
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<CircuitModelId, std::string> model_names_;
|
||||
vtr::vector<CircuitModelId, std::string> model_prefix_;
|
||||
vtr::vector<CircuitModelId, std::string> model_verilog_netlists_;
|
||||
vtr::vector<CircuitModelId, std::string> model_circuit_netlists_;
|
||||
vtr::vector<CircuitModelId, std::string> model_spice_netlists_;
|
||||
vtr::vector<CircuitModelId, bool> model_is_default_;
|
||||
|
||||
/* Submodules that a circuit model contains */
|
||||
|
|
|
@ -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(""));
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -40,6 +40,10 @@ 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(),
|
||||
g_vpr_ctx.device(),
|
||||
openfpga_ctx.vpr_device_annotation(),
|
||||
openfpga_ctx.device_rr_gsb(),
|
||||
options);
|
||||
|
||||
return status;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
#include "spice_constants.h"
|
||||
#include "spice_submodule.h"
|
||||
#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"
|
||||
|
@ -40,6 +44,10 @@ namespace openfpga {
|
|||
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) {
|
||||
|
||||
vtr::ScopedStartFinishTimer timer("Write SPICE netlists for FPGA fabric\n");
|
||||
|
@ -73,12 +81,44 @@ 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/* Generate grids */
|
||||
print_spice_grids(netlist_manager,
|
||||
module_manager,
|
||||
device_ctx, device_annotation,
|
||||
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_spice_fabric_include_netlist(const_cast<const NetlistManager &>(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(),
|
||||
"Written %lu SPICE modules in total\n",
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
#include "netlist_manager.h"
|
||||
#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"
|
||||
|
||||
/********************************************************************
|
||||
|
@ -22,6 +26,10 @@ namespace openfpga {
|
|||
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);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
|
|
@ -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 <fstream>
|
||||
|
||||
/* 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 */
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef SPICE_AUXILIARY_NETLISTS_H
|
||||
#define SPICE_AUXILIARY_NETLISTS_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#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
|
|
@ -0,0 +1,897 @@
|
|||
/************************************************
|
||||
* This file includes functions on
|
||||
* outputting SPICE netlists for inverters and buffers
|
||||
***********************************************/
|
||||
#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 "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 << 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 << 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";
|
||||
|
||||
/* 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 << 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";
|
||||
|
||||
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 << 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 << 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";
|
||||
|
||||
/* 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 << 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";
|
||||
|
||||
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||
|
||||
* -
|
||||
* |
|
||||
*
|
||||
* ... <More control signals if available in the port>
|
||||
*
|
||||
* |
|
||||
* -
|
||||
* +-o||
|
||||
* | -
|
||||
* | |
|
||||
* in-->+ +--> OUT
|
||||
* | |
|
||||
* | -
|
||||
* +--||
|
||||
* -
|
||||
*
|
||||
* ... <More control signals if available in the port>
|
||||
*
|
||||
* |
|
||||
* -
|
||||
* 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<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 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 << 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";
|
||||
|
||||
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 << 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";
|
||||
|
||||
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<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 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||
|
||||
* - -
|
||||
* | |
|
||||
*
|
||||
* ... <More control signals if available in the port>
|
||||
*
|
||||
* | |
|
||||
* - -
|
||||
* +-o|| +-o||
|
||||
* | - | -
|
||||
* | | | |
|
||||
* in-->+ +-- ... ---+---->+---> out
|
||||
* | | | |
|
||||
* | - | -
|
||||
* +--|| +--||
|
||||
* - -
|
||||
* | |
|
||||
*
|
||||
* ... <More control signals if available in the port>
|
||||
*
|
||||
* | |
|
||||
* - -
|
||||
* 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<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 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<float> 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<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 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<float> 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 */
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef SPICE_BUFFER_H
|
||||
#define SPICE_BUFFER_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <map>
|
||||
#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
|
|
@ -2,12 +2,27 @@
|
|||
#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";
|
||||
|
||||
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* 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";
|
||||
|
||||
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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,9 +18,9 @@
|
|||
/* 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_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,
|
||||
|
|
|
@ -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 <vector>
|
||||
#include <fstream>
|
||||
|
||||
/* 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<std::string> 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<e_side> 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 */
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef SPICE_GRID_H
|
||||
#define SPICE_GRID_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#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
|
|
@ -0,0 +1,346 @@
|
|||
/************************************************
|
||||
* This file includes functions on
|
||||
* outputting SPICE netlists for logic gates:
|
||||
* - N-input AND gate
|
||||
* - N-input OR gate
|
||||
***********************************************/
|
||||
#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 "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<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 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<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 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 */
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef SPICE_LOGIC_GATE_H
|
||||
#define SPICE_LOGIC_GATE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <map>
|
||||
#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
|
|
@ -0,0 +1,82 @@
|
|||
/********************************************************************
|
||||
* This file includes functions to generate SPICE subcircuits for LUTs
|
||||
********************************************************************/
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
/* 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_spice_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 */
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef SPICE_LUT_H
|
||||
#define SPICE_LUT_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#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
|
|
@ -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 <string>
|
||||
#include <algorithm>
|
||||
|
||||
/* 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<CircuitPortId> 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<CircuitModelId> 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 */
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef SPICE_MEMORY_H
|
||||
#define SPICE_MEMORY_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
|
||||
#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
|
|
@ -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 <string>
|
||||
#include <algorithm>
|
||||
|
||||
/* 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<MuxGraph> 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 */
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef SPICE_MUX_H
|
||||
#define SPICE_MUX_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#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
|
|
@ -0,0 +1,261 @@
|
|||
/************************************************
|
||||
* This file includes functions on
|
||||
* outputting SPICE netlists for transmission-gates
|
||||
***********************************************/
|
||||
#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 "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 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<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 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_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;
|
||||
}
|
||||
}
|
||||
|
||||
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<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 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.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_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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_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;
|
||||
}
|
||||
}
|
||||
|
||||
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.pass_gate_logic_type(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 */
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef SPICE_PASSGATE_H
|
||||
#define SPICE_PASSGATE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <map>
|
||||
#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
|
|
@ -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<size_t> 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<size_t> 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<size_t> 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<std::string> netlist_names;
|
||||
|
||||
vtr::Point<size_t> 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<std::string> 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 */
|
|
@ -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
|
|
@ -0,0 +1,475 @@
|
|||
/********************************************************************
|
||||
* 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 <algorithm>
|
||||
|
||||
/* 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_constants.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 <src_module_name>_<instance_id>_<src_port_name>
|
||||
* 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 <name>_<num_instance_in_parent_module>
|
||||
*/
|
||||
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<ModuleManager::e_module_port_type>(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<BasicPort> 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 (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) {
|
||||
pin_cnt = 0;
|
||||
fp << std::endl;
|
||||
new_line = true;
|
||||
fit_one_line = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
*/
|
||||
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 */
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef SPICE_SUBCKT_WRITER_H
|
||||
#define SPICE_SUBCKT_WRITER_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
#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
|
|
@ -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
|
||||
********************************************************************/
|
||||
|
||||
|
@ -10,7 +10,11 @@
|
|||
/* Headers from openfpgashell library */
|
||||
#include "command_exit_codes.h"
|
||||
|
||||
#include "spice_transistor_wrapper.h"
|
||||
#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"
|
||||
|
@ -21,23 +25,46 @@ 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
|
||||
* 3. TODO: Routing multiplexers
|
||||
* 2. Logic gates: AND/OR, inverter, buffer and transmission-gate/pass-transistor
|
||||
* 3. Routing multiplexers
|
||||
* 4. TODO: Local encoders for routing multiplexers
|
||||
* 5. TODO: Wires
|
||||
* 6. TODO: Configuration memory blocks
|
||||
* 5. Wires
|
||||
* 6. Configuration memory blocks
|
||||
********************************************************************/
|
||||
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;
|
||||
|
||||
/* 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,
|
||||
|
@ -45,6 +72,50 @@ 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;
|
||||
}
|
||||
|
||||
/* TODO: local decoders for routing multiplexers */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* TODO: architecture decoders */
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 <fstream>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
/* 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 */
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef SPICE_TOP_MODULE_H
|
||||
#define SPICE_TOP_MODULE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#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
|
|
@ -0,0 +1,190 @@
|
|||
/************************************************
|
||||
* This file includes functions on
|
||||
* outputting wrapper SPICE netlists for transistor
|
||||
***********************************************/
|
||||
#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 "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]<MODEL_CARD_NAME> <DRAIN> <GATE> <SOURCE> <BULK>
|
||||
* 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<e_tech_lib_transistor_type>(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 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 << 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";
|
||||
|
||||
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 << 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";
|
||||
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef SPICE_TRANSISTOR_WRAPPER_H
|
||||
#define SPICE_TRANSISTOR_WRAPPER_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <map>
|
||||
#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);
|
||||
|
||||
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
|
|
@ -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 */
|
|
@ -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
|
|
@ -18,6 +18,7 @@
|
|||
/* Headers from openfpgautil library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
#include "spice_constants.h"
|
||||
#include "spice_writer_utils.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
|
@ -70,10 +71,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 += "]";
|
||||
|
@ -87,7 +99,9 @@ std::string generate_spice_port(const BasicPort& port) {
|
|||
* module <module_name> (<ports without directions>);
|
||||
***********************************************/
|
||||
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)));
|
||||
|
@ -119,14 +133,22 @@ 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++;
|
||||
|
||||
/* 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;
|
||||
|
@ -134,6 +156,22 @@ void print_spice_subckt_definition(std::fstream& fp,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add supply ports if specified */
|
||||
if (true == include_supply_ports) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -149,4 +187,182 @@ 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 (SPICE_NETLIST_MAX_NUM_PORTS_PER_LINE == pin_cnt) {
|
||||
pin_cnt = 0;
|
||||
fp << std::endl;
|
||||
new_line = true;
|
||||
fit_one_line = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
*/
|
||||
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 */
|
||||
|
|
|
@ -39,14 +39,37 @@ 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);
|
||||
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);
|
||||
|
||||
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
|
||||
|
|
|
@ -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"
|
||||
|
@ -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<const ModuleManager &>(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<const ModuleManager &>(module_manager),
|
||||
device_rr_gsb,
|
||||
rr_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
/* Generate grids */
|
||||
print_verilog_grids(netlist_manager,
|
||||
const_cast<const ModuleManager &>(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<const ModuleManager &>(module_manager),
|
||||
src_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
|
||||
/* Generate an netlist including all the fabric-related netlists */
|
||||
print_fabric_include_netlist(const_cast<const NetlistManager &>(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<const ModuleManager &>(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<const ModuleManager &>(module_manager),
|
||||
device_rr_gsb,
|
||||
rr_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
/* Generate grids */
|
||||
print_verilog_grids(netlist_manager,
|
||||
const_cast<const ModuleManager &>(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<const ModuleManager &>(module_manager),
|
||||
src_dir_path,
|
||||
options.explicit_port_mapping());
|
||||
|
||||
/* Generate an netlist including all the fabric-related netlists */
|
||||
print_verilog_fabric_include_netlist(const_cast<const NetlistManager &>(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<CircuitPortId> 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<CircuitPortId> 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_verilog_testbench_include_netlists(src_dir_path,
|
||||
netlist_name,
|
||||
options.reference_benchmark_file_path());
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
|
@ -251,6 +251,29 @@ std::vector<std::string> 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<std::string> find_circuit_library_unique_spice_netlists(const CircuitLibrary& circuit_lib) {
|
||||
std::vector<std::string> 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<std::string>::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
|
||||
|
|
|
@ -39,6 +39,8 @@ std::vector<CircuitPortId> find_circuit_library_global_ports(const CircuitLibrar
|
|||
|
||||
std::vector<std::string> find_circuit_library_unique_verilog_netlists(const CircuitLibrary& circuit_lib);
|
||||
|
||||
std::vector<std::string> 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);
|
||||
|
|
Loading…
Reference in New Issue