2018-07-26 12:28:21 -05:00
|
|
|
/***********************************/
|
|
|
|
/* SPICE Modeling for VPR */
|
|
|
|
/* Xifan TANG, EPFL/LSI */
|
|
|
|
/***********************************/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
/* Include vpr structs*/
|
|
|
|
#include "util.h"
|
|
|
|
#include "physical_types.h"
|
|
|
|
#include "vpr_types.h"
|
|
|
|
#include "globals.h"
|
|
|
|
#include "rr_graph.h"
|
|
|
|
#include "vpr_utils.h"
|
2019-04-26 13:23:47 -05:00
|
|
|
#include "route_common.h"
|
2018-07-26 12:28:21 -05:00
|
|
|
|
|
|
|
/* Include spice support headers*/
|
|
|
|
#include "linkedlist.h"
|
2019-04-26 13:23:47 -05:00
|
|
|
#include "fpga_x2p_globals.h"
|
|
|
|
#include "fpga_x2p_types.h"
|
2018-07-26 12:28:21 -05:00
|
|
|
#include "spice_globals.h"
|
2019-04-26 13:23:47 -05:00
|
|
|
#include "fpga_x2p_utils.h"
|
2018-07-26 12:28:21 -05:00
|
|
|
#include "spice_utils.h"
|
|
|
|
#include "spice_mux.h"
|
|
|
|
#include "spice_lut.h"
|
|
|
|
#include "spice_pbtypes.h"
|
|
|
|
#include "spice_routing.h"
|
|
|
|
#include "spice_subckt.h"
|
|
|
|
|
|
|
|
/***** Local Subroutines *****/
|
|
|
|
static
|
|
|
|
int search_tapbuf_llist_same_settings(t_llist* head,
|
|
|
|
t_spice_model_buffer* output_buf);
|
|
|
|
|
|
|
|
static
|
|
|
|
int generate_spice_basics(char* subckt_dir, t_spice spice);
|
|
|
|
|
|
|
|
/* Generate the NMOS and PMOS */
|
|
|
|
int generate_spice_nmos_pmos(char* subckt_dir,
|
|
|
|
t_spice_tech_lib tech_lib) {
|
|
|
|
FILE* fp = NULL;
|
|
|
|
char* sp_name = my_strcat(subckt_dir,nmos_pmos_spice_file_name);
|
|
|
|
/* Standard transistors */
|
|
|
|
t_spice_transistor_type* nmos_trans = NULL;
|
|
|
|
t_spice_transistor_type* pmos_trans = NULL;
|
|
|
|
/* I/O transistors */
|
|
|
|
t_spice_transistor_type* io_nmos_trans = NULL;
|
|
|
|
t_spice_transistor_type* io_pmos_trans = NULL;
|
|
|
|
|
|
|
|
/* Spot NMOS*/
|
|
|
|
nmos_trans = find_mosfet_tech_lib(tech_lib,SPICE_TRANS_NMOS);
|
|
|
|
if (NULL == nmos_trans) {
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"NMOS transistor is not defined in architecture XML!\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Spot PMOS*/
|
|
|
|
pmos_trans = find_mosfet_tech_lib(tech_lib,SPICE_TRANS_PMOS);
|
|
|
|
if (NULL == pmos_trans) {
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"PMOS transistor is not defined in architecture XML!\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
fp = fopen(sp_name, "w");
|
|
|
|
if (NULL == fp) {
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in SPICE netlist for NMOS and PMOS subckt: %s \n",
|
|
|
|
__FILE__, __LINE__, nmos_pmos_spice_file_name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
fprint_spice_head(fp,"Standard and I/O NMOS and PMOS");
|
|
|
|
|
|
|
|
/* print sub circuit for PMOS*/
|
|
|
|
fprintf(fp,"* Standard NMOS\n");
|
|
|
|
fprintf(fp,".subckt %s drain gate source bulk L=nl W=wn\n", nmos_subckt_name);
|
|
|
|
fprintf(fp,"%s1 drain gate source bulk %s L=L W=W\n", tech_lib.model_ref, nmos_trans->model_name);
|
|
|
|
fprintf(fp,".eom %s\n", nmos_subckt_name);
|
|
|
|
|
|
|
|
fprintf(fp,"\n");
|
|
|
|
/* Print sub circuit for PMOS*/
|
|
|
|
fprintf(fp,"* Standard PMOS\n");
|
|
|
|
fprintf(fp,".subckt %s drain gate source bulk L=pl W=wp\n", pmos_subckt_name);
|
|
|
|
fprintf(fp,"%s1 drain gate source bulk %s L=L W=W\n", tech_lib.model_ref, pmos_trans->model_name);
|
|
|
|
fprintf(fp,".eom %s\n", pmos_subckt_name);
|
|
|
|
|
|
|
|
/* Spot I/O NMOS*/
|
|
|
|
io_nmos_trans = find_mosfet_tech_lib(tech_lib,SPICE_TRANS_IO_NMOS);
|
|
|
|
|
|
|
|
if (NULL == io_nmos_trans) {
|
|
|
|
vpr_printf(TIO_MESSAGE_WARNING,"I/O NMOS transistor is not defined in architecture XML!\n");
|
|
|
|
} else {
|
|
|
|
/* print sub circuit for NMOS*/
|
|
|
|
fprintf(fp,"* I/O NMOS\n");
|
|
|
|
fprintf(fp,".subckt %s drain gate source bulk L=io_nl W=io_wn\n", io_nmos_subckt_name);
|
|
|
|
fprintf(fp,"%s1 drain gate source bulk %s L=L W=W\n", tech_lib.model_ref, io_nmos_trans->model_name);
|
|
|
|
fprintf(fp,".eom %s\n", io_nmos_subckt_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Spot I/O PMOS*/
|
|
|
|
io_pmos_trans = find_mosfet_tech_lib(tech_lib,SPICE_TRANS_IO_PMOS);
|
|
|
|
|
|
|
|
if (NULL == io_pmos_trans) {
|
|
|
|
vpr_printf(TIO_MESSAGE_WARNING,"I/O PMOS transistor is not defined in architecture XML!\n");
|
|
|
|
} else {
|
|
|
|
/* print sub circuit for PMOS*/
|
|
|
|
fprintf(fp,"* I/O PMOS\n");
|
|
|
|
fprintf(fp,".subckt %s drain gate source bulk L=io_pl W=io_wp\n", io_pmos_subckt_name);
|
|
|
|
fprintf(fp,"%s1 drain gate source bulk %s L=L W=W\n", tech_lib.model_ref, io_pmos_trans->model_name);
|
|
|
|
fprintf(fp,".eom %s\n", io_pmos_subckt_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
int search_tapbuf_llist_same_settings(t_llist* head,
|
|
|
|
t_spice_model_buffer* output_buf) {
|
|
|
|
t_llist* temp = head;
|
|
|
|
t_spice_model_buffer* cur_out_buf = NULL;
|
|
|
|
|
|
|
|
/* check */
|
|
|
|
if ((NULL == output_buf)||(NULL == head)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
assert(NULL != output_buf);
|
|
|
|
assert(TRUE == output_buf->tapered_buf);
|
|
|
|
|
|
|
|
while(temp) {
|
|
|
|
cur_out_buf = (t_spice_model_buffer*)(temp->dptr);
|
|
|
|
assert(TRUE == cur_out_buf->tapered_buf);
|
|
|
|
if ((cur_out_buf->tap_buf_level == output_buf->tap_buf_level)
|
|
|
|
&&(cur_out_buf->f_per_stage == output_buf->f_per_stage)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
temp = temp->next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate the subckt for a normal tapered buffer */
|
|
|
|
void generate_spice_subckt_tapbuf(FILE* fp,
|
|
|
|
t_spice_model_buffer* output_buf) {
|
|
|
|
int istage, j;
|
|
|
|
|
|
|
|
if (NULL == fp) {
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create top SPICE netlist %s",__FILE__, __LINE__, basics_spice_file_name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(NULL != output_buf);
|
|
|
|
assert(TRUE == output_buf->tapered_buf);
|
|
|
|
assert(0 < output_buf->tap_buf_level);
|
|
|
|
assert(0 < output_buf->f_per_stage);
|
|
|
|
|
|
|
|
/* Definition line */
|
|
|
|
fprintf(fp, ".subckt tapbuf_level%d_f%d in out svdd sgnd\n", output_buf->tap_buf_level, output_buf->f_per_stage);
|
|
|
|
/* Main body of tapered buffer */
|
|
|
|
fprintf(fp, "Rinv_in in in_lvl0 0\n");
|
|
|
|
/* Print each stage */
|
|
|
|
for (istage = 0; istage < output_buf->tap_buf_level; istage++) {
|
|
|
|
for (j = 0; j < output_buf->size * pow(output_buf->f_per_stage,istage); j++) {
|
|
|
|
fprintf(fp, "Xinv_lvl%d_no%d in_lvl%d in_lvl%d svdd sgnd inv\n",
|
|
|
|
istage, j, istage, istage + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(fp, "Rinv_out in_lvl%d out 0\n", output_buf->tap_buf_level);
|
|
|
|
/* End of subckt*/
|
|
|
|
fprintf(fp, ".eom\n\n");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate the subckt for a power-gated tapered buffer */
|
|
|
|
void generate_spice_subckt_powergated_tapbuf(FILE* fp,
|
|
|
|
t_spice_model_buffer* output_buf) {
|
|
|
|
int istage, j;
|
|
|
|
|
|
|
|
if (NULL == fp) {
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create top SPICE netlist %s",__FILE__, __LINE__, basics_spice_file_name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(NULL != output_buf);
|
|
|
|
assert(TRUE == output_buf->exist);
|
|
|
|
assert(TRUE == output_buf->tapered_buf);
|
|
|
|
assert(0 < output_buf->tap_buf_level);
|
|
|
|
assert(0 < output_buf->f_per_stage);
|
|
|
|
|
|
|
|
/* Definition line */
|
|
|
|
fprintf(fp, ".subckt pg_tapbuf_level%d_f%d en enb in out svdd sgnd\n", output_buf->tap_buf_level, output_buf->f_per_stage);
|
|
|
|
/* Main body of tapered buffer */
|
|
|
|
fprintf(fp, "Rinv_in in in_lvl0 0\n");
|
|
|
|
/* Print each stage */
|
|
|
|
for (istage = 0; istage < output_buf->tap_buf_level; istage++) {
|
|
|
|
for (j = 0; j < output_buf->size * pow(output_buf->f_per_stage,istage); j++) {
|
|
|
|
fprintf(fp, "Xinv_lvl%d_no%d en enb in_lvl%d in_lvl%d svdd sgnd pg_inv size=1 pg_size=1\n",
|
|
|
|
istage, j, istage, istage + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(fp, "Rinv_out in_lvl%d out 0\n", output_buf->tap_buf_level);
|
|
|
|
/* End of subckt*/
|
|
|
|
fprintf(fp, ".eom\n\n");
|
|
|
|
|
|
|
|
/* Generate a power-gated tap_buf */
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
int generate_spice_basics(char* subckt_dir, t_spice spice) {
|
|
|
|
FILE* fp = NULL;
|
|
|
|
char* sp_name = my_strcat(subckt_dir, basics_spice_file_name);
|
|
|
|
int imodel = 0;
|
|
|
|
t_llist* tapered_bufs_head = NULL;
|
|
|
|
t_llist* temp = NULL;
|
|
|
|
|
|
|
|
fp = fopen(sp_name, "w");
|
|
|
|
if (NULL == fp) {
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create top SPICE netlist %s",__FILE__, __LINE__, basics_spice_file_name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
fprint_spice_head(fp,"Inverter, Buffer, Trans. Gate");
|
|
|
|
|
|
|
|
/* TODO: support power-gated inverter and buffer !
|
|
|
|
* We have two options:
|
|
|
|
* 1. Add power-gate transistors to each inverter/buffer
|
|
|
|
* 2. Add huge power-gate transistors to the VDD and GND of inverters
|
|
|
|
*/
|
|
|
|
/* Inverter */
|
|
|
|
fprintf(fp,"* Inverter\n");
|
|
|
|
fprintf(fp,".subckt inv in out svdd sgnd size=1\n");
|
|
|
|
fprintf(fp,"Xn0_inv out in sgnd sgnd %s L=nl W=\'size*wn\'\n",nmos_subckt_name);
|
|
|
|
fprintf(fp,"Xp0_inv out in svdd svdd %s L=pl W=\'size*beta*wp\'\n",pmos_subckt_name);
|
|
|
|
fprintf(fp,".eom inv\n");
|
|
|
|
fprintf(fp,"\n");
|
|
|
|
/* Power-gated Inverter */
|
|
|
|
fprintf(fp,"* Powergated Inverter\n");
|
|
|
|
fprintf(fp,".subckt pg_inv en enb in out svdd sgnd size=1 pg_size=1\n");
|
|
|
|
fprintf(fp,"Xn0_inv out in sgnd_pg sgnd %s L=nl W=\'size*wn\'\n",nmos_subckt_name);
|
|
|
|
fprintf(fp,"Xp0_inv out in svdd_pg svdd %s L=pl W=\'size*beta*wp\'\n",pmos_subckt_name);
|
|
|
|
fprintf(fp,"Xn0_inv_pg sgnd_pg en sgnd sgnd %s L=nl W=\'pg_size*wn\'\n",nmos_subckt_name);
|
|
|
|
fprintf(fp,"Xp0_inv_pg svdd_pg enb svdd svdd %s L=pl W=\'pg_size*beta*wp\'\n",pmos_subckt_name);
|
|
|
|
fprintf(fp,".eom inv\n");
|
|
|
|
fprintf(fp,"\n");
|
|
|
|
|
|
|
|
/* Buffer */
|
|
|
|
fprintf(fp,"* Buffer\n");
|
|
|
|
fprintf(fp,".subckt buf in out svdd sgnd size=2 base_size=1\n");
|
|
|
|
fprintf(fp,"Xinv0 in mid svdd sgnd inv base_size='base_size'\n");
|
|
|
|
fprintf(fp,"Xinv1 mid out svdd sgnd inv size='size*base_size'\n");
|
|
|
|
fprintf(fp,".eom buf\n");
|
|
|
|
fprintf(fp,"\n");
|
|
|
|
|
|
|
|
/* Power-gated Buffer */
|
|
|
|
fprintf(fp,"* Power-gated Buffer\n");
|
|
|
|
fprintf(fp,".subckt pg_buf en enb in out svdd sgnd size=2 pg_size=2\n");
|
|
|
|
fprintf(fp,"Xinv0 en enb in mid svdd sgnd pg_inv size=1 pg_size=1\n");
|
|
|
|
fprintf(fp,"Xinv1 en enb mid out svdd sgnd pg_inv size=size pg_size=size\n");
|
|
|
|
fprintf(fp,".eom buf\n");
|
|
|
|
fprintf(fp,"\n");
|
|
|
|
|
|
|
|
/* Transmission Gate*/
|
|
|
|
fprintf(fp,"* Transmission Gate (Complementary Pass Transistor)\n");
|
|
|
|
fprintf(fp,".subckt cpt in out sel sel_inv svdd sgnd nmos_size=1 pmos_size=1\n");
|
|
|
|
fprintf(fp,"Xn0_cpt in sel out sgnd %s L=nl W=\'nmos_size*wn\'\n", nmos_subckt_name);
|
|
|
|
fprintf(fp,"Xp0_cpt in sel_inv out svdd %s L=pl W=\'pmos_size*wp\'\n", pmos_subckt_name);
|
|
|
|
fprintf(fp,".eom cpt\n");
|
|
|
|
fprintf(fp,"\n");
|
|
|
|
|
|
|
|
/* Tapered buffered support */
|
|
|
|
for (imodel = 0; imodel < spice.num_spice_model; imodel++) {
|
|
|
|
/* Bypass basic components */
|
|
|
|
if (SPICE_MODEL_INVBUF != spice.spice_models[imodel].type) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Bypass un tapered buffers */
|
|
|
|
if (TRUE != spice.spice_models[imodel].design_tech_info.buffer_info->tapered_buf) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (NULL == tapered_bufs_head) {
|
|
|
|
tapered_bufs_head = create_llist(1);
|
|
|
|
tapered_bufs_head->dptr = (void*)spice.spice_models[imodel].design_tech_info.buffer_info;
|
|
|
|
} else if (FALSE == search_tapbuf_llist_same_settings(tapered_bufs_head, spice.spice_models[imodel].design_tech_info.buffer_info)) {
|
|
|
|
temp = insert_llist_node(tapered_bufs_head);
|
|
|
|
temp->dptr = (void*)spice.spice_models[imodel].design_tech_info.buffer_info;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Print all the tapered_buf */
|
|
|
|
temp = tapered_bufs_head;
|
|
|
|
while(temp) {
|
|
|
|
generate_spice_subckt_tapbuf(fp, (t_spice_model_buffer*)(temp->dptr));
|
|
|
|
temp = temp->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
/* Free */
|
|
|
|
temp = tapered_bufs_head;
|
|
|
|
while(temp) {
|
|
|
|
temp->dptr = NULL;
|
|
|
|
temp = temp->next;
|
|
|
|
}
|
|
|
|
free_llist(tapered_bufs_head);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void generate_spice_rram_veriloga(char* subckt_dir,
|
|
|
|
t_spice spice) {
|
|
|
|
FILE* fp = NULL;
|
|
|
|
char* sp_name = NULL;
|
|
|
|
int imodel, write_model;
|
|
|
|
|
|
|
|
/* Check if we need such a verilogA model */
|
|
|
|
write_model = 0;
|
|
|
|
for (imodel = 0; imodel < spice.num_spice_model; imodel++) {
|
|
|
|
if (SPICE_MODEL_DESIGN_RRAM == spice.spice_models[imodel].design_tech) {
|
|
|
|
write_model = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (0 == write_model) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
rram_design_tech = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sp_name = my_strcat(subckt_dir, rram_veriloga_file_name);
|
|
|
|
/* Open a File */
|
|
|
|
fp = fopen(sp_name, "w");
|
|
|
|
if (NULL == fp) {
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create SPICE netlist %s",__FILE__, __LINE__, wires_spice_file_name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
fprintf(fp,"// RRAM Behavior VerilogA Mode\n");
|
|
|
|
|
|
|
|
/* Include the headers of VerilogA */
|
|
|
|
fprintf(fp, "`include \"constants.vams\"\n");
|
|
|
|
fprintf(fp, "`include \"disciplines.vams\"\n");
|
|
|
|
|
|
|
|
/* Model */
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
fprintf(fp, "module rram_behavior(TE, BE, SRAM, SRAM_INV);\n");
|
|
|
|
fprintf(fp, "input SRAM, SRAM_INV;\n");
|
|
|
|
fprintf(fp, "inout TE, BE;\n");
|
|
|
|
fprintf(fp, "electrical TE, BE, SRAM, SRAM_INV;\n");
|
|
|
|
fprintf(fp, "// Design Parameters\n");
|
|
|
|
fprintf(fp, "parameter integer initial_state = 0 from [0:1];\n");
|
|
|
|
fprintf(fp, "parameter real switch_thres = 1.8 from (0:inf);\n");
|
|
|
|
fprintf(fp, "parameter real ron = 1e3 from (0:inf);\n");
|
|
|
|
fprintf(fp, "parameter real roff = 1e6 from (0:inf);\n");
|
|
|
|
fprintf(fp, "// Local Parameters\n");
|
|
|
|
fprintf(fp, "real res = roff;\n");
|
|
|
|
fprintf(fp, "real voltage_tolerence = 0;\n");
|
|
|
|
fprintf(fp, "integer state = 0;\n");
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
fprintf(fp, "analog begin\n");
|
|
|
|
fprintf(fp, " // Initial\n");
|
|
|
|
fprintf(fp, " @(initial_step) begin\n");
|
|
|
|
fprintf(fp, " state = initial_state;\n");
|
|
|
|
fprintf(fp, " end\n");
|
|
|
|
fprintf(fp, " // State\n");
|
|
|
|
fprintf(fp, " if (V(SRAM,SRAM_INV) < voltage_tolerence*switch_thres) begin\n");
|
|
|
|
fprintf(fp, " state = 0;\n");
|
|
|
|
fprintf(fp, " end else begin\n");
|
|
|
|
fprintf(fp, " state = 1;\n");
|
|
|
|
fprintf(fp, " end\n");
|
|
|
|
fprintf(fp, " //LRS\n");
|
|
|
|
fprintf(fp, " if (1 == state) begin\n");
|
|
|
|
fprintf(fp, " res = ron;\n");
|
|
|
|
fprintf(fp, " //HRS\n");
|
|
|
|
fprintf(fp, " end else begin\n");
|
|
|
|
fprintf(fp, " res = roff;\n");
|
|
|
|
fprintf(fp, " end\n");
|
|
|
|
fprintf(fp, " // Correlated Resistance with TE and BE\n");
|
|
|
|
fprintf(fp, " I(TE,BE) <+ V(TE,BE) / res;\n");
|
|
|
|
fprintf(fp, "end\n");
|
|
|
|
fprintf(fp, "endmodule\n");
|
|
|
|
|
|
|
|
/* Close File */
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
/* Free */
|
|
|
|
my_free(sp_name);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void fprint_spice_wire_model(FILE* fp,
|
|
|
|
char* wire_subckt_name,
|
|
|
|
t_spice_model spice_model,
|
|
|
|
float res_total,
|
|
|
|
float cap_total) {
|
|
|
|
float res_per_level = 0.;
|
|
|
|
float cap_per_level = 0.;
|
|
|
|
int i;
|
|
|
|
int num_input_port = 0;
|
|
|
|
int num_output_port = 0;
|
|
|
|
t_spice_model_port** input_port = NULL;
|
|
|
|
t_spice_model_port** output_port = NULL;
|
|
|
|
|
|
|
|
/* Ensure a valid file handler*/
|
|
|
|
if (NULL == fp) {
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File handler.\n",
|
|
|
|
__FILE__, __LINE__);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
/* Check the wire model*/
|
|
|
|
assert(NULL != spice_model.wire_param);
|
|
|
|
assert(0 < spice_model.wire_param->level);
|
|
|
|
/* Find the input port, output port*/
|
|
|
|
input_port = find_spice_model_ports(&spice_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE);
|
|
|
|
output_port = find_spice_model_ports(&spice_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE);
|
|
|
|
|
|
|
|
/* Asserts*/
|
|
|
|
assert(1 == num_input_port);
|
|
|
|
assert(1 == num_output_port);
|
|
|
|
assert(1 == input_port[0]->size);
|
|
|
|
assert(1 == output_port[0]->size);
|
|
|
|
/* print the spice model*/
|
|
|
|
fprintf(fp, "* Wire, spice_model_name=%s\n", spice_model.name);
|
|
|
|
switch (spice_model.type) {
|
|
|
|
case SPICE_MODEL_CHAN_WIRE:
|
|
|
|
/* Add an output at middle point for connecting CB inputs */
|
|
|
|
fprintf(fp, ".subckt %s %s %s mid_out svdd sgnd\n",
|
|
|
|
wire_subckt_name, input_port[0]->prefix, output_port[0]->prefix);
|
|
|
|
break;
|
|
|
|
case SPICE_MODEL_WIRE:
|
|
|
|
/* Add an output at middle point for connecting CB inputs */
|
|
|
|
fprintf(fp, ".subckt %s %s %s svdd sgnd\n",
|
|
|
|
wire_subckt_name, input_port[0]->prefix, output_port[0]->prefix);
|
|
|
|
/* Direct shortcut */
|
|
|
|
if ((0. == cap_per_level)&&(0. == res_per_level)
|
|
|
|
&&(0 == spice_model.input_buffer->exist)
|
|
|
|
&&(0 == spice_model.output_buffer->exist)) {
|
|
|
|
fprintf(fp, "Rshortcut %s %s 0\n", input_port[0]->prefix, output_port[0]->prefix);
|
|
|
|
/* Finish*/
|
|
|
|
fprintf(fp, ".eom\n");
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Invalid type of spice_model! Expect [chan_wire|wire].\n",
|
|
|
|
__FILE__, __LINE__);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Determine which type of model to print*/
|
|
|
|
switch (spice_model.wire_param->type) {
|
|
|
|
case WIRE_MODEL_PIE :
|
|
|
|
/* Determine the resistance and capacitance of each level*/
|
|
|
|
res_per_level = res_total/((float)(2*spice_model.wire_param->level));
|
|
|
|
cap_per_level = cap_total/((float)(spice_model.wire_param->level + 1));
|
|
|
|
if ((0. == cap_per_level)&&(0. == res_per_level)) {
|
|
|
|
/* Special: if R and C are all zeros, we use a zero-voltage source instead */
|
|
|
|
fprintf(fp, "Vshortcut pie_wire_in%d pie_wire_in%d 0\n",
|
|
|
|
0, spice_model.wire_param->level);
|
|
|
|
if (SPICE_MODEL_CHAN_WIRE == spice_model.type) {
|
|
|
|
fprintf(fp, "* Connect the output of middle point\n");
|
|
|
|
fprintf(fp, "Vmid_out_ckt pie_wire_in0 mid_out 0\n");
|
|
|
|
}
|
|
|
|
i = spice_model.wire_param->level;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (0. != cap_per_level) {
|
|
|
|
fprintf(fp, "Clvin pie_wire_in0 sgnd \'%s%s/%d\'\n",
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_wire_param_cap_val,
|
|
|
|
spice_model.wire_param->level + 1);
|
|
|
|
}
|
|
|
|
for (i = 0; i < spice_model.wire_param->level; i++) {
|
|
|
|
fprintf(fp, "Rlv%d_idx0 pie_wire_in%d pie_wire_in%d_inter \'%s%s/%d\'\n",
|
|
|
|
i, i, i,
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_wire_param_res_val,
|
|
|
|
2* spice_model.wire_param->level);
|
|
|
|
fprintf(fp, "Rlv%d_idx1 pie_wire_in%d_inter pie_wire_in%d \'%s%s/%d\'\n",
|
|
|
|
i, i, i + 1,
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_wire_param_res_val,
|
|
|
|
2* spice_model.wire_param->level);
|
|
|
|
if (0. != cap_per_level) {
|
|
|
|
fprintf(fp, "Clv%d_idx1 pie_wire_in%d sgnd \'%s%s/%d\'\n",
|
|
|
|
i, i + 1,
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_wire_param_cap_val,
|
|
|
|
spice_model.wire_param->level + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (SPICE_MODEL_CHAN_WIRE == spice_model.type) {
|
|
|
|
/*Connect the middle point */
|
|
|
|
fprintf(fp, "* Connect the output of middle point\n");
|
|
|
|
if (0 == spice_model.wire_param->level%2) {
|
|
|
|
fprintf(fp, "Vmid_out_ckt pie_wire_in%d mid_out 0\n", spice_model.wire_param->level/2);
|
|
|
|
} else if (1 == spice_model.wire_param->level%2) {
|
|
|
|
fprintf(fp, "Vmid_out_ckt pie_wire_in%d_inter mid_out 0\n", spice_model.wire_param->level/2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WIRE_MODEL_T :
|
|
|
|
/* Determine the resistance and capacitance of each level*/
|
|
|
|
res_per_level = res_total/((float)(2*spice_model.wire_param->level));
|
|
|
|
cap_per_level = cap_total/((float)(spice_model.wire_param->level));
|
|
|
|
if ((0. == cap_per_level)&&(0. == res_per_level)) {
|
|
|
|
fprintf(fp, "Vshortcut pie_wire_in%d pie_wire_in%d 0\n",
|
|
|
|
0, spice_model.wire_param->level);
|
|
|
|
if (SPICE_MODEL_CHAN_WIRE == spice_model.type) {
|
|
|
|
fprintf(fp, "* Connect the output of middle point\n");
|
|
|
|
fprintf(fp, "Vmid_out_ckt pie_wire_in0 mid_out 0\n");
|
|
|
|
}
|
|
|
|
i = spice_model.wire_param->level;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (i = 0; i < spice_model.wire_param->level; i++) {
|
|
|
|
fprintf(fp, "Rlv%d_idx0 pie_wire_in%d pie_wire_in%d_inter \'%s%s/%d\'\n",
|
|
|
|
i, i, i,
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_wire_param_res_val,
|
|
|
|
2 * spice_model.wire_param->level);
|
|
|
|
if (0. != cap_per_level) {
|
|
|
|
fprintf(fp, "Clv%d_idx1 pie_wire_in%d_inter sgnd \'%s%s/%d\'\n",
|
|
|
|
i, i,
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_wire_param_cap_val,
|
|
|
|
spice_model.wire_param->level);
|
|
|
|
}
|
|
|
|
fprintf(fp, "Rlv%d_idx2 pie_wire_in%d_inter pie_wire_in%d \'%s%s/%d\'\n",
|
|
|
|
i, i, i + 1,
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_wire_param_res_val,
|
|
|
|
2 * spice_model.wire_param->level);
|
|
|
|
}
|
|
|
|
if (SPICE_MODEL_CHAN_WIRE == spice_model.type) {
|
|
|
|
/*Connect the middle point */
|
|
|
|
fprintf(fp, "* Connect the output of middle point\n");
|
|
|
|
if (0 == spice_model.wire_param->level%2) {
|
|
|
|
fprintf(fp, "Vmid_out_ckt pie_wire_in%d mid_out 0\n", spice_model.wire_param->level/2);
|
|
|
|
} else if (1 == spice_model.wire_param->level%2) {
|
|
|
|
fprintf(fp, "Vmid_out_ckt pie_wire_in%d_inter mid_out 0\n", spice_model.wire_param->level/2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO,"(File:%s,[LINE%d])Invalid SPICE Wire Model type of spice_model(%s).\n",
|
|
|
|
__FILE__, __LINE__, spice_model.name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
assert(i == spice_model.wire_param->level);
|
|
|
|
|
|
|
|
/* Add input, output buffers*/
|
|
|
|
assert(NULL != spice_model.input_buffer);
|
|
|
|
if (spice_model.input_buffer->exist) {
|
|
|
|
switch (spice_model.input_buffer->type) {
|
|
|
|
case SPICE_MODEL_BUF_INV:
|
|
|
|
/* Each inv: <given_name> <input0> <output> svdd sgnd <subckt_name> size=param*/
|
|
|
|
fprintf(fp, "Xinv_in "); /* Given name*/
|
|
|
|
fprintf(fp, "%s ", input_port[0]->prefix); /* input port */
|
|
|
|
fprintf(fp, "pie_wire_in0 "); /* output port*/
|
|
|
|
fprintf(fp, "svdd sgnd inv size=\'%s%s\'",
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_input_buf_size); /* subckt name */
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
break;
|
|
|
|
case SPICE_MODEL_BUF_BUF:
|
|
|
|
/* TODO: what about tapered buffer, can we support? */
|
|
|
|
/* Each buf: <given_name> <input0> <output> svdd sgnd <subckt_name> size=param*/
|
|
|
|
fprintf(fp, "Xbuf_in "); /* Given name*/
|
|
|
|
fprintf(fp, "%s ", input_port[0]->prefix); /* input port */
|
|
|
|
fprintf(fp, "pie_wire_in0 "); /* output port*/
|
|
|
|
fprintf(fp, "svdd sgnd buf size=\'%s%s\'",
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_output_buf_size); /* subckt name */
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid type for spice_model_buffer.\n",
|
|
|
|
__FILE__, __LINE__);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* There is no buffer, I create a zero voltage source between*/
|
|
|
|
/* V<given_name> <input> <output> 0*/
|
|
|
|
fprintf(fp, "Rin %s pie_wire_in0 0\n",
|
|
|
|
input_port[0]->prefix);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(NULL != spice_model.output_buffer);
|
|
|
|
if (spice_model.output_buffer->exist) {
|
|
|
|
switch (spice_model.output_buffer->type) {
|
|
|
|
case SPICE_MODEL_BUF_INV:
|
|
|
|
/* Each inv: <given_name> <input0> <output> svdd sgnd <subckt_name> size=param*/
|
|
|
|
fprintf(fp, "Xinv_out "); /* Given name*/
|
|
|
|
fprintf(fp, "pie_wire_in%d ", spice_model.wire_param->level); /* input port */
|
|
|
|
fprintf(fp, "%s ", output_port[0]->prefix); /* output port*/
|
|
|
|
fprintf(fp, "svdd sgnd inv size=\'%s%s\'",
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_output_buf_size); /* subckt name */
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
break;
|
|
|
|
case SPICE_MODEL_BUF_BUF:
|
|
|
|
/* TODO: what about tapered buffer, can we support? */
|
|
|
|
/* Each buf: <given_name> <input0> <output> svdd sgnd <subckt_name> size=param*/
|
|
|
|
fprintf(fp, "Xbuf_out "); /* Given name*/
|
|
|
|
fprintf(fp, "pie_wire_in%d ", spice_model.wire_param->level); /* input port */
|
|
|
|
fprintf(fp, "%s ", output_port[0]->prefix); /* output port*/
|
|
|
|
fprintf(fp, "svdd sgnd buf size=\'%s%s\'",
|
|
|
|
spice_model.name,
|
|
|
|
design_param_postfix_output_buf_size); /* subckt name */
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid type for spice_model_buffer.\n",
|
|
|
|
__FILE__, __LINE__);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* There is no buffer, I create a zero voltage source between*/
|
|
|
|
/* V<given_name> <input> <output> 0*/
|
|
|
|
fprintf(fp, "Rout pie_wire_in%d %s 0\n",
|
|
|
|
spice_model.wire_param->level, output_port[0]->prefix);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finish*/
|
|
|
|
fprintf(fp, ".eom\n");
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void generate_spice_wires(char* subckt_dir,
|
|
|
|
int num_segments,
|
|
|
|
t_segment_inf* segments,
|
|
|
|
int num_spice_model,
|
|
|
|
t_spice_model* spice_models) {
|
|
|
|
FILE* fp = NULL;
|
|
|
|
char* sp_name = my_strcat(subckt_dir, wires_spice_file_name);
|
|
|
|
char* seg_wire_subckt_name = NULL;
|
|
|
|
char* seg_index_str = NULL;
|
|
|
|
int iseg, imodel, len_seg_subckt_name;
|
|
|
|
|
|
|
|
fp = fopen(sp_name, "w");
|
|
|
|
if (NULL == fp) {
|
|
|
|
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create SPICE netlist %s",__FILE__, __LINE__, wires_spice_file_name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
fprint_spice_head(fp,"Wires");
|
|
|
|
/* Output wire models*/
|
|
|
|
for (imodel = 0; imodel < num_spice_model; imodel++) {
|
|
|
|
if (SPICE_MODEL_WIRE == spice_models[imodel].type) {
|
|
|
|
assert(NULL != spice_models[imodel].wire_param);
|
|
|
|
fprint_spice_wire_model(fp, spice_models[imodel].name,
|
|
|
|
spice_models[imodel],
|
|
|
|
spice_models[imodel].wire_param->res_val,
|
|
|
|
spice_models[imodel].wire_param->cap_val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create wire models for routing segments*/
|
|
|
|
fprintf(fp,"* Wire models for segments in routing \n");
|
|
|
|
for (iseg = 0; iseg < num_segments; iseg++) {
|
|
|
|
assert(NULL != segments[iseg].spice_model);
|
|
|
|
assert(SPICE_MODEL_CHAN_WIRE == segments[iseg].spice_model->type);
|
|
|
|
assert(NULL != segments[iseg].spice_model->wire_param);
|
|
|
|
/* Give a unique name for subckt of wire_model of segment,
|
|
|
|
* spice_model name is unique, and segment name is unique as well
|
|
|
|
*/
|
|
|
|
seg_index_str = my_itoa(iseg);
|
|
|
|
len_seg_subckt_name = strlen(segments[iseg].spice_model->name)
|
|
|
|
+ 4 + strlen(seg_index_str) + 1; /* '\0'*/
|
|
|
|
seg_wire_subckt_name = (char*)my_malloc(sizeof(char)*len_seg_subckt_name);
|
|
|
|
sprintf(seg_wire_subckt_name,"%s_seg%s",
|
|
|
|
segments[iseg].spice_model->name, seg_index_str);
|
|
|
|
fprint_spice_wire_model(fp, seg_wire_subckt_name,
|
|
|
|
*(segments[iseg].spice_model),
|
|
|
|
segments[iseg].Rmetal,
|
|
|
|
segments[iseg].Cmetal);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Close the file handler */
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
/*Free*/
|
|
|
|
my_free(seg_index_str);
|
|
|
|
my_free(seg_wire_subckt_name);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate the sub circuits for FPGA SPICE Modeling
|
|
|
|
* Tasks:
|
|
|
|
* 1. NMOS and PMOS
|
|
|
|
* 2. Basics: Inverter and Buffers, transmission gates, rc_segements
|
|
|
|
* 3. Multiplexers
|
|
|
|
* 4. Look-Up Tables
|
|
|
|
* 5. Flip-flops
|
|
|
|
*/
|
|
|
|
void generate_spice_subckts(char* subckt_dir,
|
|
|
|
t_arch* arch,
|
2019-05-10 23:50:08 -05:00
|
|
|
t_det_routing_arch* routing_arch,
|
|
|
|
boolean compact_routing_hierarchy) {
|
2018-07-26 12:28:21 -05:00
|
|
|
/* 1.Generate NMOS, PMOS and transmission gate */
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO,"Writing SPICE NMOS and PMOS...\n");
|
|
|
|
generate_spice_nmos_pmos(subckt_dir, arch->spice->tech_lib);
|
|
|
|
|
|
|
|
/* 2. Generate Inverter, Buffer, and transmission gates*/
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO,"Writing SPICE Basic subckts...\n");
|
|
|
|
generate_spice_basics(subckt_dir, *(arch->spice));
|
|
|
|
|
|
|
|
/* 2.5 Generate RRAM Verilog-A model*/
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO, "Writing RRAM Behavior Verilog-A model...\n");
|
|
|
|
generate_spice_rram_veriloga(subckt_dir, (*(arch->spice)));
|
|
|
|
|
|
|
|
/* 3. Generate Multiplexers */
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO,"Writing SPICE Multiplexers...\n");
|
|
|
|
generate_spice_muxes(subckt_dir, routing_arch->num_switch, switch_inf,
|
|
|
|
arch->spice, routing_arch);
|
|
|
|
|
|
|
|
/* 4. Generate Wires*/
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO,"Writing SPICE Wires...\n");
|
|
|
|
generate_spice_wires(subckt_dir, arch->num_segments, arch->Segments,
|
|
|
|
arch->spice->num_spice_model, arch->spice->spice_models);
|
|
|
|
|
2019-04-26 13:23:47 -05:00
|
|
|
/* 5. Generate LUTs */
|
2018-07-26 12:28:21 -05:00
|
|
|
vpr_printf(TIO_MESSAGE_INFO,"Writing SPICE LUTs...\n");
|
|
|
|
generate_spice_luts(subckt_dir, arch->spice->num_spice_model, arch->spice->spice_models);
|
|
|
|
|
2019-04-26 13:23:47 -05:00
|
|
|
/* 6. Generate Routing architecture*/
|
2018-07-26 12:28:21 -05:00
|
|
|
vpr_printf(TIO_MESSAGE_INFO, "Writing Routing Resources....\n");
|
|
|
|
generate_spice_routing_resources(subckt_dir, (*arch), routing_arch,
|
2019-07-02 10:01:52 -05:00
|
|
|
num_rr_nodes, rr_node, rr_node_indices);
|
2019-04-26 13:23:47 -05:00
|
|
|
|
|
|
|
/* 7. Generate Logic Blocks */
|
|
|
|
vpr_printf(TIO_MESSAGE_INFO,"Writing Logic Blocks...\n");
|
|
|
|
generate_spice_logic_blocks(subckt_dir, arch);
|
|
|
|
|
2018-07-26 12:28:21 -05:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|