/***********************************/
/*      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"
#include "route_common.h"

/* Include spice support headers*/
#include "linkedlist.h"
#include "fpga_x2p_globals.h"
#include "fpga_x2p_types.h"
#include "spice_globals.h"
#include "fpga_x2p_utils.h"
#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,
                            t_det_routing_arch* routing_arch,
                            boolean compact_routing_hierarchy) {
  /* 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);

  /* 5. Generate LUTs */
  vpr_printf(TIO_MESSAGE_INFO,"Writing SPICE LUTs...\n");
  generate_spice_luts(subckt_dir, arch->spice->num_spice_model, arch->spice->spice_models);

  /* 6. Generate Routing architecture*/
  vpr_printf(TIO_MESSAGE_INFO, "Writing Routing Resources....\n");
  generate_spice_routing_resources(subckt_dir, (*arch), routing_arch, 
                                   num_rr_nodes, rr_node, rr_node_indices);
 
  /* 7. Generate Logic Blocks */
  vpr_printf(TIO_MESSAGE_INFO,"Writing Logic Blocks...\n");
  generate_spice_logic_blocks(subckt_dir, arch);


  return;
}