/***********************************/ /* SPICE Modeling for VPR */ /* Xifan TANG, EPFL/LSI */ /***********************************/ #include #include #include #include #include #include #include #include /* Include vpr structs*/ #include "util.h" #include "physical_types.h" #include "vpr_types.h" #include "globals.h" #include "rr_graph_util.h" #include "rr_graph.h" #include "rr_graph2.h" #include "vpr_utils.h" #include "route_common.h" /* Include spice support headers*/ #include "linkedlist.h" #include "fpga_x2p_types.h" #include "fpga_x2p_globals.h" #include "spice_globals.h" #include "fpga_x2p_utils.h" #include "fpga_x2p_pbtypes_utils.h" #include "spice_mux.h" #include "spice_pbtypes.h" #include "spice_routing.h" #include "fpga_x2p_backannotate_utils.h" #include "spice_utils.h" /***** Subroutines *****/ void fprint_spice_head(FILE* fp, char* usage) { if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s, LINE[%d]) FileHandle is NULL!\n",__FILE__,__LINE__); exit(1); } fprintf(fp,"*****************************\n"); fprintf(fp,"* FPGA SPICE Netlist *\n"); fprintf(fp,"* Description: %s *\n",usage); fprintf(fp,"* Author: Xifan TANG *\n"); fprintf(fp,"* Organization: EPFL/IC/LSI *\n"); fprintf(fp,"* Date: %s *\n",my_gettime()); fprintf(fp,"*****************************\n"); return; } /* Create a file handler for a subckt SPICE netlist */ FILE* spice_create_one_subckt_file(char* subckt_dir, char* subckt_name_prefix, char* spice_subckt_file_name_prefix, int grid_x, int grid_y, char** sp_name) { FILE* fp = NULL; char* file_description = NULL; (*sp_name) = my_strcat(subckt_dir, fpga_spice_create_one_subckt_filename(spice_subckt_file_name_prefix, grid_x, grid_y, spice_netlist_file_postfix)); /* Create a file*/ fp = fopen((*sp_name), "w"); if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(FILE:%s,LINE[%d])Failure in create subckt SPICE netlist %s", __FILE__, __LINE__, (*sp_name)); exit(1); } /* Generate the descriptions*/ file_description = (char*) my_malloc(sizeof(char) * (strlen(subckt_name_prefix) + 2 + strlen(my_itoa(grid_x)) + 2 + strlen(my_itoa(grid_y)) + 9)); sprintf(file_description, "%s [%d][%d] in FPGA", subckt_name_prefix, grid_x, grid_y); fprint_spice_head(fp, file_description); /* Free */ my_free(file_description); return fp; } /* Include a subckt in SPICE netlist */ void spice_print_one_include_subckt_line(FILE* fp, char* subckt_dir, char* subckt_file_name) { char* temp_include_file_path = NULL; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(FILE:%s,LINE[%d])Invalid File Handler of subckt SPICE netlist %s", __FILE__, __LINE__); exit(1); } temp_include_file_path = my_strcat(subckt_dir, subckt_file_name); fprintf(fp, ".include \'%s\'\n", temp_include_file_path); my_free(temp_include_file_path); return; } /* Output all the created subckt file names in a header file, * that can be easily imported in a top-level netlist */ void spice_print_subckt_header_file(t_llist* subckt_llist_head, char* subckt_dir, char* header_file_name) { FILE* fp = NULL; char* spice_fname = NULL; t_llist* temp = NULL; spice_fname = my_strcat(subckt_dir, header_file_name); /* Create a file*/ fp = fopen(spice_fname, "w"); if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(FILE:%s,LINE[%d])Failure in create SPICE netlist %s", __FILE__, __LINE__, spice_fname); exit(1); } /* Generate the descriptions*/ fprint_spice_head(fp, "Header file"); /* Output file names */ temp = subckt_llist_head; while (temp) { fprintf(fp, ".include \'%s\'\n", (char*)(temp->dptr)); temp = temp->next; } /* Close fp */ fclose(fp); /* Free */ my_free(spice_fname); return; } /* Print all the global ports that are stored in the linked list * Return the number of ports that have been dumped */ int rec_fprint_spice_model_global_ports(FILE* fp, t_spice_model* cur_spice_model, boolean recursive) { int i, iport, dumped_port_cnt, rec_dumped_port_cnt; dumped_port_cnt = 0; rec_dumped_port_cnt = 0; /* Check */ assert(NULL != cur_spice_model); if (0 < cur_spice_model->num_port) { assert(NULL != cur_spice_model->ports); } /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); } for (iport = 0; iport < cur_spice_model->num_port; iport++) { /* if this spice model requires customized netlist to be included, we do not go recursively */ if (TRUE == recursive) { /* GO recursively first, and meanwhile count the number of global ports */ /* For the port that requires another spice_model, i.e., SRAM * We need include any global port in that spice model */ if (NULL != cur_spice_model->ports[iport].spice_model) { /* Check if we need to dump a comma */ rec_dumped_port_cnt += rec_fprint_spice_model_global_ports(fp, cur_spice_model->ports[iport].spice_model, recursive); /* Update counter */ dumped_port_cnt += rec_dumped_port_cnt; continue; } } /* By pass non-global ports*/ if (FALSE == cur_spice_model->ports[iport].is_global) { continue; } /* We have some port to dump ! * Print a comment line */ if (0 == dumped_port_cnt) { fprintf(fp, "\n"); fprintf(fp, "***** BEGIN Global ports of SPICE_MODEL(%s) *****\n", cur_spice_model->name); fprintf(fp, "+ "); } /* Check if we need to dump a comma */ for (i = 0; i < cur_spice_model->ports[iport].size; i++) { fprintf(fp, " %s[%d] ", cur_spice_model->ports[iport].prefix, i); } /* Update counter */ dumped_port_cnt++; } /* We have dumped some port! * Print another comment line */ if (0 < dumped_port_cnt) { fprintf(fp, "\n"); fprintf(fp, "***** END Global ports of SPICE_MODEL(%s) *****\n", cur_spice_model->name); } return dumped_port_cnt; } /* Print all the global ports that are stored in the linked list */ int fprint_spice_global_ports(FILE* fp, t_llist* head) { t_llist* temp = head; t_spice_model_port* cur_global_port = NULL; int dumped_port_cnt = 0; int i; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); } fprintf(fp, "\n"); fprintf(fp, "***** BEGIN Global ports *****\n"); fprintf(fp, "+ "); while(NULL != temp) { cur_global_port = (t_spice_model_port*)(temp->dptr); for (i = 0; i < cur_global_port->size; i++) { fprintf(fp, " %s[%d] ", cur_global_port->prefix, i); } /* Update counter */ dumped_port_cnt++; /* Go to the next */ temp = temp->next; } fprintf(fp, "\n"); fprintf(fp, "***** END Global ports *****\n"); return dumped_port_cnt; } void fprint_spice_generic_testbench_global_ports(FILE* fp, t_sram_orgz_info* cur_sram_orgz_info, t_llist* head) { t_spice_model* mem_model = NULL; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); } fprintf(fp, "***** Generic global ports ***** \n"); fprintf(fp, "***** VDD, GND ***** \n"); fprintf(fp, ".global %s\n", spice_tb_global_vdd_port_name); fprintf(fp, ".global %s\n", spice_tb_global_gnd_port_name); fprintf(fp, "***** Global set ports ***** \n"); fprintf(fp, ".global %s %s%s \n", spice_tb_global_set_port_name, spice_tb_global_set_port_name, spice_tb_global_port_inv_postfix); fprintf(fp, "***** Global reset ports ***** \n"); fprintf(fp, ".global %s %s%s\n", spice_tb_global_reset_port_name, spice_tb_global_reset_port_name, spice_tb_global_port_inv_postfix); fprintf(fp, "***** Configuration done ports ***** \n"); fprintf(fp, ".global %s %s%s\n", spice_tb_global_config_done_port_name, spice_tb_global_config_done_port_name, spice_tb_global_port_inv_postfix); /* Get memory spice model */ get_sram_orgz_info_mem_model(cur_sram_orgz_info, &mem_model); fprintf(fp, "***** Global SRAM input ***** \n"); fprintf(fp, ".global %s->in\n", mem_model->prefix); /* Print scan-chain global ports */ if (SPICE_SRAM_SCAN_CHAIN == sram_spice_orgz_type) { fprintf(fp, "***** Scan-chain FF: head of scan-chain *****\n"); fprintf(fp, "*.global %s[0]->in\n", sram_spice_model->prefix); } /* Define a global clock port if we need one*/ fprintf(fp, "***** Global Clock Signals *****\n"); fprintf(fp, ".global %s\n", spice_tb_global_clock_port_name); fprintf(fp, ".global %s%s\n", spice_tb_global_clock_port_name, spice_tb_global_port_inv_postfix); fprintf(fp, "***** User-defined global ports ****** \n"); if (NULL != head) { fprintf(fp, ".global \n"); } fprint_spice_global_ports(fp, head); return; } /* Print a SRAM output port in SPICE format */ void fprint_spice_sram_one_outport(FILE* fp, t_sram_orgz_info* cur_sram_orgz_info, int cur_sram, int port_type_index) { t_spice_model* mem_model = NULL; char* port_name = NULL; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Get memory_model */ get_sram_orgz_info_mem_model(cur_sram_orgz_info, &mem_model); /* Keep the branch as it is, in case thing may become more complicated*/ switch (cur_sram_orgz_info->type) { case SPICE_SRAM_STANDALONE: if (0 == port_type_index) { port_name = "out"; } else { assert(1 == port_type_index); port_name = "outb"; } break; case SPICE_SRAM_SCAN_CHAIN: if (0 == port_type_index) { port_name = "ccff_out"; } else { assert(1 == port_type_index); port_name = "ccff_outb"; } break; case SPICE_SRAM_MEMORY_BANK: if (0 == port_type_index) { port_name = "out"; } else { assert(1 == port_type_index); port_name = "outb"; } break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid type of SRAM organization !\n", __FILE__, __LINE__); exit(1); } /*Malloc and generate the full name of port */ fprintf(fp, "%s[%d]->%s ", mem_model->prefix, cur_sram, port_name); /* Outputs */ /* Free */ /* Local variables such as port1_name and port2 name are automatically freed */ return; } /* Print a SRAM module in SPICE format */ void fprint_spice_one_specific_sram_subckt(FILE* fp, t_sram_orgz_info* cur_sram_orgz_info, t_spice_model* parent_spice_model, char* vdd_port_name, int sram_index) { t_spice_model* mem_model = NULL; int cur_sram = 0; /* Get memory model */ get_sram_orgz_info_mem_model(cur_sram_orgz_info, &mem_model); /* Get current index of SRAM module */ cur_sram = sram_index; /* Depend on the type of SRAM organization */ switch (cur_sram_orgz_info->type) { case SPICE_SRAM_STANDALONE: case SPICE_SRAM_MEMORY_BANK: fprintf(fp, "X%s[%d] ", mem_model->prefix, cur_sram); /* SRAM subckts*/ /* fprintf(fp, "%s[%d]->in ", sram_spice_model->prefix, cur_sram);*/ /* Input*/ /* Global ports : * Only dump the global ports belonging to a spice_model * Do not go recursive, we can freely define global ports anywhere in SPICE netlist */ rec_fprint_spice_model_global_ports(fp, mem_model, FALSE); /* Local ports */ fprintf(fp, "%s->in ", mem_model->prefix); /* Input*/ fprintf(fp, "%s[%d]->out %s[%d]->outb ", mem_model->prefix, cur_sram, mem_model->prefix, cur_sram); /* Outputs */ fprintf(fp, "%s sgnd ", vdd_port_name); // fprintf(fp, " %s\n", mem_model->name); // /* Add nodeset to help convergence */ fprintf(fp, ".nodeset V(%s[%d]->out) 0\n", mem_model->prefix, cur_sram); fprintf(fp, ".nodeset V(%s[%d]->outb) vsp\n", mem_model->prefix, cur_sram); break; case SPICE_SRAM_SCAN_CHAIN: fprintf(fp, "X%s[%d] ", mem_model->prefix, cur_sram); /* SRAM subckts*/ /* Global ports : * Only dump the global ports belonging to a spice_model * Do not go recursive, we can freely define global ports anywhere in SPICE netlist */ rec_fprint_spice_model_global_ports(fp, mem_model, FALSE); /* Local ports */ fprintf(fp, "%s[%d]->in ", mem_model->prefix, cur_sram); /* Input*/ fprintf(fp, "%s[%d]->out %s[%d]->outb ", mem_model->prefix, cur_sram, mem_model->prefix, cur_sram); /* Outputs */ fprintf(fp, "sc_clk sc_rst sc_set \n"); // fprintf(fp, "%s sgnd ", vdd_port_name); // fprintf(fp, " %s\n", mem_model->name); // /* Add nodeset to help convergence */ fprintf(fp, ".nodeset V(%s[%d]->out) 0\n", mem_model->prefix, cur_sram); fprintf(fp, ".nodeset V(%s[%d]->outb) vsp\n", mem_model->prefix, cur_sram); /* Connect to the tail of previous Scan-chain FF*/ fprintf(fp,"R%s[%d]_short %s[%d]->out %s[%d]->in 0\n", mem_model->prefix, cur_sram, mem_model->prefix, cur_sram, sram_spice_model->prefix, cur_sram + 1); /* Specify this is a global signal*/ fprintf(fp, ".global %s[%d]->in\n", sram_spice_model->prefix, cur_sram); /* Specify the head and tail of the scan-chain of this LUT */ fprintf(fp,"R%s[%d]_sc_head %s[%d]_sc_head %s[%d]->in 0\n", mem_model->prefix, mem_model->cnt, mem_model->prefix, mem_model->cnt, mem_model->prefix, mem_model->cnt); fprintf(fp,"R%s[%d]_sc_tail %s[%d]_sc_tail %s[%d]->in 0\n", mem_model->prefix, mem_model->cnt, mem_model->prefix, mem_model->cnt, mem_model->prefix, cur_sram); fprintf(fp,".global %s[%d]_sc_head %s[%d]_sc_tail\n", mem_model->prefix, mem_model->cnt, mem_model->prefix, mem_model->cnt); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,LINE[%d]) Invalid SRAM organization type!\n", __FILE__, __LINE__); exit(1); } return; } /* Print a SRAM module in SPICE format */ void fprint_spice_one_sram_subckt(FILE* fp, t_sram_orgz_info* cur_sram_orgz_info, t_spice_model* parent_spice_model, char* vdd_port_name) { t_spice_model* mem_model = NULL; int cur_num_sram = 0; /* Get memory model */ get_sram_orgz_info_mem_model(cur_sram_orgz_info, &mem_model); /* Get current index of SRAM module */ cur_num_sram = get_sram_orgz_info_num_mem_bit(cur_sram_orgz_info); /* Call a subroutine: fprint_spice_one_specific_sram_subckt */ fprint_spice_one_specific_sram_subckt(fp, cur_sram_orgz_info, parent_spice_model, vdd_port_name, cur_num_sram); /* Update the memory counter in sram_orgz_info */ update_sram_orgz_info_num_mem_bit(cur_sram_orgz_info, cur_num_sram + 1); /* Update the memory counter */ mem_model->cnt++; return; } /* Include user defined SPICE netlists */ void init_include_user_defined_netlists(t_spice spice) { int i; /* Include user-defined sub-circuit netlist */ for (i = 0; i < spice.num_include_netlist; i++) { spice.include_netlists[i].included = 0; } return; } void fprint_include_user_defined_netlists(FILE* fp, t_spice spice) { int i; /* A valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } /* Include user-defined sub-circuit netlist */ for (i = 0; i < spice.num_include_netlist; i++) { if (0 == spice.include_netlists[i].included) { assert(NULL != spice.include_netlists[i].path); fprintf(fp, ".include \'%s\'\n", spice.include_netlists[i].path); spice.include_netlists[i].included = 1; } else { assert(1 == spice.include_netlists[i].included); } } return; } void fprint_splited_vdds_spice_model(FILE* fp, enum e_spice_model_type spice_model_type, t_spice spice) { int imodel, i; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } for (imodel = 0; imodel < spice.num_spice_model; imodel++) { if (spice_model_type == spice.spice_models[imodel].type) { for (i = 0; i < spice.spice_models[imodel].cnt; i++) { fprintf(fp, "V%s_%s[%d] %s_%s[%d] 0 vsp\n", spice_tb_global_vdd_port_name, spice.spice_models[imodel].prefix, i, spice_tb_global_vdd_port_name, spice.spice_models[imodel].prefix, i); /* For some gvdd maybe floating, I add a huge resistance to make their leakage power trival * which does no change to the delay result. * The resistance value is co-related to the vsp, which produces a trival leakage current (1e-15). */ fprintf(fp, "Rgvdd_%s[%d]_huge gvdd_%s[%d] 0 'vsp/10e-15'\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i); } } } return; } void fprint_grid_splited_vdds_spice_model(FILE* fp, int grid_x, int grid_y, enum e_spice_model_type spice_model_type, t_spice spice) { int imodel, i; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } for (imodel = 0; imodel < spice.num_spice_model; imodel++) { if (spice_model_type == spice.spice_models[imodel].type) { /* Bypass zero-usage spice_model in this grid*/ if (spice.spice_models[imodel].grid_index_low[grid_x][grid_y] == spice.spice_models[imodel].grid_index_high[grid_x][grid_y]) { continue; } for (i = spice.spice_models[imodel].grid_index_low[grid_x][grid_y]; i < spice.spice_models[imodel].grid_index_high[grid_x][grid_y]; i++) { fprintf(fp, "Vgvdd_%s[%d] gvdd_%s[%d] 0 vsp\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i); /* For some gvdd maybe floating, I add a huge resistance to make their leakage power trival * which does no change to the delay result. * The resistance value is co-related to the vsp, which produces a trival leakage current (1e-15). */ fprintf(fp, "Rgvdd_%s[%d]_huge gvdd_%s[%d] 0 'vsp/10e-15'\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i); } } } return; } void fprint_global_vdds_spice_model(FILE* fp, enum e_spice_model_type spice_model_type, t_spice spice) { int imodel, i; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "***** Global VDD ports of %s *****\n", generate_string_spice_model_type(spice_model_type)); fprintf(fp, ".global \n"); for (imodel = 0; imodel < spice.num_spice_model; imodel++) { if (spice_model_type == spice.spice_models[imodel].type) { for (i = 0; i < spice.spice_models[imodel].cnt; i++) { fprintf(fp, "+ %s_%s[%d]\n", spice_tb_global_vdd_port_name, spice.spice_models[imodel].prefix, i); } } } fprintf(fp, "\n"); return; } void fprint_grid_global_vdds_spice_model(FILE* fp, int x, int y, enum e_spice_model_type spice_model_type, t_spice spice) { int imodel, i; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "***** Global VDD ports of %s *****\n", generate_string_spice_model_type(spice_model_type)); for (imodel = 0; imodel < spice.num_spice_model; imodel++) { /* Bypass non-matched SPICE model */ if (spice_model_type != spice.spice_models[imodel].type) { continue; } /* Bypass zero-usage spice_model in this grid*/ if (spice.spice_models[imodel].grid_index_low[x][y] == spice.spice_models[imodel].grid_index_high[x][y]) { continue; } fprintf(fp, ".global \n"); for (i = spice.spice_models[imodel].grid_index_low[x][y]; i < spice.spice_models[imodel].grid_index_high[x][y]; i++) { fprintf(fp, "+ gvdd_%s[%d]\n", spice.spice_models[imodel].prefix, i); } } fprintf(fp, "\n"); return; } void fprint_global_pad_ports_spice_model(FILE* fp, t_spice spice) { int imodel, i; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "***** Global input/output ports of I/O Pads *****\n"); fprintf(fp, ".global \n"); for (imodel = 0; imodel < spice.num_spice_model; imodel++) { switch (spice.spice_models[imodel].type) { /* Handle multiple INPAD/OUTPAD spice models*/ case SPICE_MODEL_IOPAD: for (i = 0; i < spice.spice_models[imodel].cnt; i++) { fprintf(fp, "+ %s%s[%d]\n", gio_inout_prefix, spice.spice_models[imodel].prefix, i); } break; /* SRAM inputs*/ case SPICE_MODEL_SRAM: /* for (i = 0; i < spice.spice_models[imodel].cnt; i++) { fprintf(fp, "+ %s[%d]->in\n", spice.spice_models[imodel].prefix, i); } fprintf(fp, "+ %s->in\n", spice.spice_models[imodel].prefix); */ break; /* Other types we do not care*/ case SPICE_MODEL_CHAN_WIRE: case SPICE_MODEL_WIRE: case SPICE_MODEL_MUX: case SPICE_MODEL_LUT: case SPICE_MODEL_FF: case SPICE_MODEL_HARDLOGIC: case SPICE_MODEL_CCFF: case SPICE_MODEL_INVBUF: case SPICE_MODEL_PASSGATE: case SPICE_MODEL_GATE: break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Unknown type for spice model!\n", __FILE__, __LINE__); exit(1); } } fprintf(fp, "\n"); return; } void fprint_spice_global_vdd_switch_boxes(FILE* fp) { int ix, iy; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "***** Global Vdds for Switch Boxes *****\n"); fprintf(fp, ".global "); for (ix = 0; ix < (nx + 1); ix++) { for (iy = 0; iy < (ny + 1); iy++) { fprintf(fp, "gvdd_sb[%d][%d] ", ix, iy); } } fprintf(fp, "\n"); return; } /* Call the sub-circuits for connection boxes */ void fprint_spice_global_vdd_connection_boxes(FILE* fp) { int ix, iy; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "***** Global Vdds for Connection Blocks - X channels *****\n"); fprintf(fp, ".global "); /* X - channels [1...nx][0..ny]*/ for (iy = 0; iy < (ny + 1); iy++) { for (ix = 1; ix < (nx + 1); ix++) { fprintf(fp, "gvdd_cbx[%d][%d] ", ix, iy); } } fprintf(fp, "\n"); fprintf(fp, "***** Global Vdds for Connection Blocks - Y channels *****\n"); fprintf(fp, ".global "); /* Y - channels [1...ny][0..nx]*/ for (ix = 0; ix < (nx + 1); ix++) { for (iy = 1; iy < (ny + 1); iy++) { fprintf(fp, "gvdd_cby[%d][%d] ", ix, iy); } } fprintf(fp, "\n"); return; } void fprint_measure_vdds_spice_model(FILE* fp, enum e_spice_model_type spice_model_type, enum e_measure_type meas_type, int num_cycle, t_spice spice, boolean leakage_only) { int imodel, i; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } for (imodel = 0; imodel < spice.num_spice_model; imodel++) { /* Only care the matched SPICE model type */ if (spice_model_type != spice.spice_models[imodel].type) { continue; } /* Skip if no such spice_model is used in the netlists */ if (0 == spice.spice_models[imodel].cnt) { continue; } for (i = 0; i < spice.spice_models[imodel].cnt; i++) { switch (meas_type) { case SPICE_MEASURE_LEAKAGE_POWER: if (TRUE == leakage_only) { fprintf(fp, ".measure tran leakage_power_%s[%d] find p(Vgvdd_%s[%d]) at=0\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i); } else { fprintf(fp, ".measure tran leakage_power_%s[%d] avg p(Vgvdd_%s[%d]) from=0 to='clock_period'\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i); } break; case SPICE_MEASURE_DYNAMIC_POWER: fprintf(fp, ".measure tran dynamic_power_%s[%d] avg p(Vgvdd_%s[%d]) from='clock_period' to='%d*clock_period'\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i, num_cycle); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid meas_type!\n", __FILE__, __LINE__); exit(1); } } } /* Measure the total power of this kind of spice model */ for (imodel = 0; imodel < spice.num_spice_model; imodel++) { /* Only care the matched SPICE model type */ if (spice_model_type != spice.spice_models[imodel].type) { continue; } /* Skip if no such spice_model is used in the netlists */ if (0 == spice.spice_models[imodel].cnt) { continue; } switch (meas_type) { case SPICE_MEASURE_LEAKAGE_POWER: for (i = 0; i < spice.spice_models[imodel].cnt; i++) { fprintf(fp, ".measure tran leakage_power_%s[0to%d] \n", spice.spice_models[imodel].prefix, i); if (0 == i) { fprintf(fp, "+ param = 'leakage_power_%s[%d]'\n", spice.spice_models[imodel].prefix, i); } else { fprintf(fp, "+ param = 'leakage_power_%s[%d]+leakage_power_%s[0to%d]'\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i-1); } } /* Spot the total leakage power of this spice model */ fprintf(fp, ".measure tran total_leakage_power_%s \n", spice.spice_models[imodel].prefix); fprintf(fp, "+ param = 'leakage_power_%s[0to%d]'\n", spice.spice_models[imodel].prefix, spice.spice_models[imodel].cnt-1); break; case SPICE_MEASURE_DYNAMIC_POWER: for (i = 0; i < spice.spice_models[imodel].cnt; i++) { fprintf(fp, ".measure tran dynamic_power_%s[0to%d] \n", spice.spice_models[imodel].prefix, i); if (0 == i) { fprintf(fp, "+ param = 'dynamic_power_%s[%d]'\n", spice.spice_models[imodel].prefix, i); } else { fprintf(fp, "+ param = 'dynamic_power_%s[%d]+dynamic_power_%s[0to%d]'\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i-1); } } /* Spot the total dynamic power of this spice model */ fprintf(fp, ".measure tran total_dynamic_power_%s \n", spice.spice_models[imodel].prefix); fprintf(fp, "+ param = 'dynamic_power_%s[0to%d]'\n", spice.spice_models[imodel].prefix, spice.spice_models[imodel].cnt-1); fprintf(fp, ".measure tran total_energy_per_cycle_%s \n", spice.spice_models[imodel].prefix); fprintf(fp, "+ param = 'dynamic_power_%s[0to%d]*clock_period'\n", spice.spice_models[imodel].prefix, spice.spice_models[imodel].cnt-1); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid meas_type!\n", __FILE__, __LINE__); exit(1); } } return; } void fprint_measure_grid_vdds_spice_model(FILE* fp, int grid_x, int grid_y, enum e_spice_model_type spice_model_type, enum e_measure_type meas_type, int num_cycle, t_spice spice, boolean leakage_only) { int imodel, i; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } for (imodel = 0; imodel < spice.num_spice_model; imodel++) { if (spice_model_type == spice.spice_models[imodel].type) { /* Bypass zero-usage spice_model in this grid*/ if (spice.spice_models[imodel].grid_index_low[grid_x][grid_y] == spice.spice_models[imodel].grid_index_high[grid_x][grid_y]) { continue; } for (i = spice.spice_models[imodel].grid_index_low[grid_x][grid_y]; i < spice.spice_models[imodel].grid_index_high[grid_x][grid_y]; i++) { switch (meas_type) { case SPICE_MEASURE_LEAKAGE_POWER: if (TRUE == leakage_only) { fprintf(fp, ".measure tran leakage_power_%s[%d] find p(Vgvdd_%s[%d]) at=0\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i); } else { fprintf(fp, ".measure tran leakage_power_%s[%d] avg p(Vgvdd_%s[%d]) from=0 to='clock_period'\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i); } break; case SPICE_MEASURE_DYNAMIC_POWER: fprintf(fp, ".measure tran dynamic_power_%s[%d] avg p(Vgvdd_%s[%d]) from='clock_period' to='%d*clock_period'\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, i, num_cycle); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid meas_type!\n", __FILE__, __LINE__); exit(1); } } } } /* Measure the total power of this kind of spice model */ for (imodel = 0; imodel < spice.num_spice_model; imodel++) { if (spice_model_type == spice.spice_models[imodel].type) { switch (meas_type) { case SPICE_MEASURE_LEAKAGE_POWER: /* Bypass zero-usage spice_model in this grid*/ if (spice.spice_models[imodel].grid_index_low[grid_x][grid_y] == spice.spice_models[imodel].grid_index_high[grid_x][grid_y]) { continue; } for (i = spice.spice_models[imodel].grid_index_low[grid_x][grid_y]; i < spice.spice_models[imodel].grid_index_high[grid_x][grid_y]; i++) { fprintf(fp, ".measure tran leakage_power_%s[%dto%d] \n", spice.spice_models[imodel].prefix, spice.spice_models[imodel].grid_index_low[grid_x][grid_y], i); if (spice.spice_models[imodel].grid_index_low[grid_x][grid_y] == i) { fprintf(fp, "+ param = 'leakage_power_%s[%d]'\n", spice.spice_models[imodel].prefix, i); } else { fprintf(fp, "+ param = 'leakage_power_%s[%d]+leakage_power_%s[%dto%d]'\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, spice.spice_models[imodel].grid_index_low[grid_x][grid_y], i-1); } } /* Spot the total leakage power of this spice model */ fprintf(fp, ".measure tran total_leakage_power_%s \n", spice.spice_models[imodel].prefix); fprintf(fp, "+ param = 'leakage_power_%s[%dto%d]'\n", spice.spice_models[imodel].prefix, spice.spice_models[imodel].grid_index_low[grid_x][grid_y], spice.spice_models[imodel].grid_index_high[grid_x][grid_y]-1); break; case SPICE_MEASURE_DYNAMIC_POWER: /* Bypass zero-usage spice_model in this grid*/ if (spice.spice_models[imodel].grid_index_low[grid_x][grid_y] == spice.spice_models[imodel].grid_index_high[grid_x][grid_y]) { continue; } for (i = spice.spice_models[imodel].grid_index_low[grid_x][grid_y]; i < spice.spice_models[imodel].grid_index_high[grid_x][grid_y]; i++) { fprintf(fp, ".measure tran dynamic_power_%s[%dto%d] \n", spice.spice_models[imodel].prefix, spice.spice_models[imodel].grid_index_low[grid_x][grid_y], i); if (spice.spice_models[imodel].grid_index_low[grid_x][grid_y] == i) { fprintf(fp, "+ param = 'dynamic_power_%s[%d]'\n", spice.spice_models[imodel].prefix, i); } else { fprintf(fp, "+ param = 'dynamic_power_%s[%d]+dynamic_power_%s[%dto%d]'\n", spice.spice_models[imodel].prefix, i, spice.spice_models[imodel].prefix, spice.spice_models[imodel].grid_index_low[grid_x][grid_y], i-1); } } /* Spot the total dynamic power of this spice model */ fprintf(fp, ".measure tran total_dynamic_power_%s \n", spice.spice_models[imodel].prefix); fprintf(fp, "+ param = 'dynamic_power_%s[%dto%d]'\n", spice.spice_models[imodel].prefix, spice.spice_models[imodel].grid_index_low[grid_x][grid_y], spice.spice_models[imodel].grid_index_high[grid_x][grid_y]-1); fprintf(fp, ".measure tran total_energy_per_cycle_%s \n", spice.spice_models[imodel].prefix); fprintf(fp, "+ param = 'dynamic_power_%s[%dto%d]*clock_period'\n", spice.spice_models[imodel].prefix, spice.spice_models[imodel].grid_index_low[grid_x][grid_y], spice.spice_models[imodel].grid_index_high[grid_x][grid_y]-1); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid meas_type!\n", __FILE__, __LINE__); exit(1); } } } return; } /***** Print (call) the defined grids *****/ void fprint_call_defined_grids(FILE* fp) { int ix, iy; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } /* Normal Grids */ for (ix = 1; ix < (nx + 1); ix++) { for (iy = 1; iy < (ny + 1); iy++) { /* Bypass EMPTY grid */ if (EMPTY_TYPE == grid[ix][iy].type) { continue; } assert(IO_TYPE != grid[ix][iy].type); fprintf(fp, "Xgrid[%d][%d] ", ix, iy); fprintf(fp, "\n"); fprint_grid_pins(fp, ix, iy, 1); fprintf(fp, "+ "); fprintf(fp, "gvdd 0 grid[%d][%d]\n", ix, iy); /* Call the name of subckt */ } } /* IO Grids */ /* TOP side */ iy = ny + 1; for (ix = 1; ix < (nx + 1); ix++) { /* Bypass EMPTY grid */ if (EMPTY_TYPE == grid[ix][iy].type) { continue; } assert(IO_TYPE == grid[ix][iy].type); fprintf(fp, "Xgrid[%d][%d] ", ix, iy); fprintf(fp, "\n"); fprint_io_grid_pins(fp, ix, iy, 1); fprintf(fp, "+ "); /* Connect to a speical vdd port for statistics power */ fprintf(fp, "gvdd_io 0 grid[%d][%d]\n", ix, iy); /* Call the name of subckt */ } /* RIGHT side */ ix = nx + 1; for (iy = 1; iy < (ny + 1); iy++) { /* Bypass EMPTY grid */ if (EMPTY_TYPE == grid[ix][iy].type) { continue; } assert(IO_TYPE == grid[ix][iy].type); fprintf(fp, "Xgrid[%d][%d] ", ix, iy); fprintf(fp, "\n"); fprint_io_grid_pins(fp, ix, iy, 1); fprintf(fp, "+ "); /* Connect to a speical vdd port for statistics power */ fprintf(fp, "gvdd_io 0 grid[%d][%d]\n", ix, iy); /* Call the name of subckt */ } /* BOTTOM side */ iy = 0; for (ix = 1; ix < (nx + 1); ix++) { /* Bypass EMPTY grid */ if (EMPTY_TYPE == grid[ix][iy].type) { continue; } assert(IO_TYPE == grid[ix][iy].type); fprintf(fp, "Xgrid[%d][%d] ", ix, iy); fprintf(fp, "\n"); fprint_io_grid_pins(fp, ix, iy, 1); fprintf(fp, "+ "); /* Connect to a speical vdd port for statistics power */ fprintf(fp, "gvdd_io 0 grid[%d][%d]\n", ix, iy); /* Call the name of subckt */ } /* LEFT side */ ix = 0; for (iy = 1; iy < (ny + 1); iy++) { /* Bypass EMPTY grid */ if (EMPTY_TYPE == grid[ix][iy].type) { continue; } assert(IO_TYPE == grid[ix][iy].type); fprintf(fp, "Xgrid[%d][%d] ", ix, iy); fprintf(fp, "\n"); fprint_io_grid_pins(fp, ix, iy, 1); fprintf(fp, "+ "); /* Connect to a speical vdd port for statistics power */ fprintf(fp, "gvdd_io 0 grid[%d][%d]\n", ix, iy); /* Call the name of subckt */ } return; } /* Call defined channels. * Ensure the port name here is co-herent to other sub-circuits(SB,CB,grid)!!! */ void fprint_call_defined_one_channel(FILE* fp, t_rr_type chan_type, int x, int y, int LL_num_rr_nodes, t_rr_node* LL_rr_node, t_ivec*** LL_rr_node_indices) { int itrack; int chan_width = 0; t_rr_node** chan_rr_nodes = NULL; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((CHANX == chan_type)||(CHANY == chan_type)); /* check x*/ assert((!(0 > x))&&(x < (nx + 1))); /* check y*/ assert((!(0 > y))&&(y < (ny + 1))); /* Collect rr_nodes for Tracks for chanx[ix][iy] */ chan_rr_nodes = get_chan_rr_nodes(&chan_width, chan_type, x, y, LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices); /* Call the define sub-circuit */ fprintf(fp, "X%s[%d][%d] ", convert_chan_type_to_string(chan_type), x, y); fprintf(fp, "\n"); /* LEFT/BOTTOM side port of CHANX/CHANY */ /* We apply an opposite port naming rule than function: fprint_routing_chan_subckt * In top-level netlists, we follow the same port name as switch blocks and connection blocks * When a track is in INC_DIRECTION, the LEFT/BOTTOM port would be an output of a switch block * When a track is in DEC_DIRECTION, the LEFT/BOTTOM port would be an input of a switch block */ for (itrack = 0; itrack < chan_width; itrack++) { fprintf(fp, "+ "); switch (chan_rr_nodes[itrack]->direction) { case INC_DIRECTION: fprintf(fp, "%s[%d][%d]_out[%d] ", convert_chan_type_to_string(chan_type), x, y, itrack); fprintf(fp, "\n"); break; case DEC_DIRECTION: fprintf(fp, "%s[%d][%d]_in[%d] ", convert_chan_type_to_string(chan_type), x, y, itrack); fprintf(fp, "\n"); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File: %s [LINE%d]) Invalid direction of %s[%d][%d]_track[%d]!\n", __FILE__, __LINE__, convert_chan_type_to_string(chan_type), x, y, itrack); exit(1); } } /* RIGHT/TOP side port of CHANX/CHANY */ /* We apply an opposite port naming rule than function: fprint_routing_chan_subckt * In top-level netlists, we follow the same port name as switch blocks and connection blocks * When a track is in INC_DIRECTION, the RIGHT/TOP port would be an input of a switch block * When a track is in DEC_DIRECTION, the RIGHT/TOP port would be an output of a switch block */ for (itrack = 0; itrack < chan_width; itrack++) { fprintf(fp, "+ "); switch (chan_rr_nodes[itrack]->direction) { case INC_DIRECTION: fprintf(fp, "%s[%d][%d]_in[%d] ", convert_chan_type_to_string(chan_type), x, y, itrack); fprintf(fp, "\n"); break; case DEC_DIRECTION: fprintf(fp, "%s[%d][%d]_out[%d] ", convert_chan_type_to_string(chan_type), x, y, itrack); fprintf(fp, "\n"); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File: %s [LINE%d]) Invalid direction of %s[%d][%d]_track[%d]!\n", __FILE__, __LINE__, convert_chan_type_to_string(chan_type), x, y, itrack); exit(1); } } /* output at middle point */ for (itrack = 0; itrack < chan_width; itrack++) { fprintf(fp, "+ "); fprintf(fp, "%s[%d][%d]_midout[%d] ", convert_chan_type_to_string(chan_type), x, y, itrack); fprintf(fp, "\n"); } fprintf(fp, "+ gvdd 0 %s[%d][%d]\n", convert_chan_type_to_string(chan_type), x, y); /* Free */ my_free(chan_rr_nodes); return; } /* Call the sub-circuits for channels : Channel X and Channel Y*/ void fprint_call_defined_channels(FILE* fp, int LL_num_rr_nodes, t_rr_node* LL_rr_node, t_ivec*** LL_rr_node_indices) { int ix, iy; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } /* Channel X */ for (iy = 0; iy < (ny + 1); iy++) { for (ix = 1; ix < (nx + 1); ix++) { fprint_call_defined_one_channel(fp, CHANX, ix, iy, LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices); } } /* Channel Y */ for (ix = 0; ix < (nx + 1); ix++) { for (iy = 1; iy < (ny + 1); iy++) { fprint_call_defined_one_channel(fp, CHANY, ix, iy, LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices); } } return; } /* Call the defined sub-circuit of connection box * TODO: actually most of this function is copied from * spice_routing.c : fprint_conneciton_box_interc * Should be more clever to use the original function */ void fprint_call_defined_one_connection_box(FILE* fp, t_cb cur_cb_info) { int itrack, inode, side; int side_cnt = 0; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > cur_cb_info.x))&&(!(cur_cb_info.x > (nx + 1)))); assert((!(0 > cur_cb_info.y))&&(!(cur_cb_info.y > (ny + 1)))); /* Print the definition of subckt*/ /* Identify the type of connection box */ switch(cur_cb_info.type) { case CHANX: fprintf(fp, "Xcbx[%d][%d] ", cur_cb_info.x, cur_cb_info.y); break; case CHANY: fprintf(fp, "Xcby[%d][%d] ", cur_cb_info.x, cur_cb_info.y); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of channel!\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "\n"); /* Print the ports of channels*/ /* connect to the mid point of a track*/ side_cnt = 0; for (side = 0; side < cur_cb_info.num_sides; side++) { /* Bypass side with zero channel width */ if (0 == cur_cb_info.chan_width[side]) { continue; } assert (0 < cur_cb_info.chan_width[side]); side_cnt++; for (itrack = 0; itrack < cur_cb_info.chan_width[side]; itrack++) { fprintf(fp, "+ "); fprintf(fp, "%s[%d][%d]_midout[%d] ", convert_chan_type_to_string(cur_cb_info.type), cur_cb_info.x, cur_cb_info.y, itrack); fprintf(fp, "\n"); } } /*check side_cnt */ assert(1 == side_cnt); side_cnt = 0; /* Print the ports of grids*/ /* only check ipin_rr_nodes of cur_cb_info */ for (side = 0; side < cur_cb_info.num_sides; side++) { /* Bypass side with zero IPINs*/ if (0 == cur_cb_info.num_ipin_rr_nodes[side]) { continue; } side_cnt++; assert(0 < cur_cb_info.num_ipin_rr_nodes[side]); assert(NULL != cur_cb_info.ipin_rr_node[side]); for (inode = 0; inode < cur_cb_info.num_ipin_rr_nodes[side]; inode++) { fprintf(fp, "+ "); /* Print each INPUT Pins of a grid */ fprint_grid_side_pin_with_given_index(fp, cur_cb_info.ipin_rr_node[side][inode]->ptc_num, cur_cb_info.ipin_rr_node_grid_side[side][inode], cur_cb_info.ipin_rr_node[side][inode]->xlow, cur_cb_info.ipin_rr_node[side][inode]->ylow); fprintf(fp, "\n"); } } /* Make sure only 2 sides of IPINs are printed */ assert((1 == side_cnt)||(2 == side_cnt)); fprintf(fp, "+ "); /* Identify the type of connection box */ switch(cur_cb_info.type) { case CHANX: /* Need split vdd port for each Connection Box */ fprintf(fp, "gvdd_cbx[%d][%d] 0 ", cur_cb_info.x, cur_cb_info.y); fprintf(fp, "cbx[%d][%d]\n", cur_cb_info.x, cur_cb_info.y); break; case CHANY: /* Need split vdd port for each Connection Box */ fprintf(fp, "gvdd_cby[%d][%d] 0 ", cur_cb_info.x, cur_cb_info.y); fprintf(fp, "cby[%d][%d]\n", cur_cb_info.x, cur_cb_info.y); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of channel!\n", __FILE__, __LINE__); exit(1); } /* Free */ return; } /* Call the sub-circuits for connection boxes */ void fprint_call_defined_connection_boxes(FILE* fp) { int ix, iy; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* X - channels [1...nx][0..ny]*/ for (iy = 0; iy < (ny + 1); iy++) { for (ix = 1; ix < (nx + 1); ix++) { if ((TRUE == is_cb_exist(CHANX, ix, iy)) &&(0 < count_cb_info_num_ipin_rr_nodes(cbx_info[ix][iy]))) { fprint_call_defined_one_connection_box(fp, cbx_info[ix][iy]); } } } /* Y - channels [1...ny][0..nx]*/ for (ix = 0; ix < (nx + 1); ix++) { for (iy = 1; iy < (ny + 1); iy++) { if ((TRUE == is_cb_exist(CHANY, ix, iy)) &&(0 < count_cb_info_num_ipin_rr_nodes(cby_info[ix][iy]))) { fprint_call_defined_one_connection_box(fp, cby_info[ix][iy]); } } } return; } /* Call the defined switch box sub-circuit * Critical difference between this function and * spice_routing.c : fprint_routing_switch_box_subckt * Whether a channel node of a Switch block should be input or output depends on it location: * For example, a channel chanX INC_DIRECTION on the right side of a SB, it is marked as an input * In fprint_routing_switch_box_subckt: it is marked as an output. * For channels chanY with INC_DIRECTION on the top/bottom side, they should be marked as inputs * For channels chanY with DEC_DIRECTION on the top/bottom side, they should be marked as outputs * For channels chanX with INC_DIRECTION on the left/right side, they should be marked as inputs * For channels chanX with DEC_DIRECTION on the left/right side, they should be marked as outputs */ void fprint_call_defined_one_switch_box(FILE* fp, t_sb cur_sb_info) { int ix, iy, side, itrack, inode; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > cur_sb_info.x))&&(!(cur_sb_info.x > (nx + 1)))); assert((!(0 > cur_sb_info.y))&&(!(cur_sb_info.y > (ny + 1)))); fprintf(fp, "Xsb[%d][%d] ", cur_sb_info.x, cur_sb_info.y); fprintf(fp, "\n"); for (side = 0; side < cur_sb_info.num_sides; side++) { determine_sb_port_coordinator(cur_sb_info, side, &ix, &iy); fprintf(fp, "+ "); for (itrack = 0; itrack < cur_sb_info.chan_width[side]; itrack++) { switch (cur_sb_info.chan_rr_node_direction[side][itrack]) { case OUT_PORT: fprintf(fp, "%s[%d][%d]_out[%d] ", convert_chan_type_to_string(cur_sb_info.chan_rr_node[side][itrack]->type), ix, iy, itrack); break; case IN_PORT: fprintf(fp, "%s[%d][%d]_in[%d] ", convert_chan_type_to_string(cur_sb_info.chan_rr_node[side][itrack]->type), ix, iy, itrack); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File: %s [LINE%d]) Invalid direction of sb[%d][%d] side[%d] track[%d]!\n", __FILE__, __LINE__, cur_sb_info.x, cur_sb_info.y, side, itrack); exit(1); } } fprintf(fp, "\n"); fprintf(fp, "+ "); /* Dump OPINs of adjacent CLBs */ for (inode = 0; inode < cur_sb_info.num_opin_rr_nodes[side]; inode++) { fprint_grid_side_pin_with_given_index(fp, cur_sb_info.opin_rr_node[side][inode]->ptc_num, cur_sb_info.opin_rr_node_grid_side[side][inode], cur_sb_info.opin_rr_node[side][inode]->xlow, cur_sb_info.opin_rr_node[side][inode]->ylow); } fprintf(fp, "\n"); } /* Connect to separate vdd port for each switch box??? */ fprintf(fp, "+ "); fprintf(fp, " gvdd_sb[%d][%d] 0 sb[%d][%d]\n", cur_sb_info.x, cur_sb_info.y, cur_sb_info.x, cur_sb_info.y); /* Free */ return; } void fprint_call_defined_switch_boxes(FILE* fp) { int ix, iy; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } for (ix = 0; ix < (nx + 1); ix++) { for (iy = 0; iy < (ny + 1); iy++) { fprint_call_defined_one_switch_box(fp, sb_info[ix][iy]); } } return; } void fprint_spice_toplevel_one_grid_side_pin_with_given_index(FILE* fp, int pin_index, int side, int x, int y) { t_type_ptr type; int height; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > x))&&(!(x > (nx + 1)))); assert((!(0 > y))&&(!(y > (ny + 1)))); type = grid[x][y].type; assert(NULL != type); assert((!(0 > pin_index))&&(pin_index < type->num_pins)); assert((!(0 > side))&&(!(side > 3))); /* Output the pins on the side*/ height = get_grid_pin_height(x, y, pin_index); fprintf(fp, " grid[%d][%d]_pin[%d][%d][%d] ", x, y, height, side, pin_index); return; } /* Apply a CLB to CLB direct connection to a SPICE netlist */ static void fprint_spice_one_clb2clb_direct(FILE* fp, int from_grid_x, int from_grid_y, int to_grid_x, int to_grid_y, t_clb_to_clb_directs* cur_direct) { int ipin, cur_from_clb_pin_index, cur_to_clb_pin_index; int cur_from_clb_pin_side, cur_to_clb_pin_side; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Check bandwidth match between from_clb and to_clb pins */ if (0 != (cur_direct->from_clb_pin_end_index - cur_direct->from_clb_pin_start_index - (cur_direct->to_clb_pin_end_index - cur_direct->to_clb_pin_start_index))) { vpr_printf(TIO_MESSAGE_ERROR, "(%s, [LINE%d]) Unmatch pin bandwidth in direct connection (name=%s)!\n", __FILE__, __LINE__, cur_direct->name); exit(1); } for (ipin = 0; ipin < cur_direct->from_clb_pin_end_index - cur_direct->from_clb_pin_start_index; ipin++) { /* Update pin index and get the side of the pins on grids */ cur_from_clb_pin_index = cur_direct->from_clb_pin_start_index + ipin; cur_to_clb_pin_index = cur_direct->to_clb_pin_start_index + ipin; cur_from_clb_pin_side = get_grid_pin_side(from_grid_x, from_grid_y, cur_from_clb_pin_index); cur_to_clb_pin_side = get_grid_pin_side(to_grid_x, to_grid_y, cur_to_clb_pin_index); /* Call the subckt that has already been defined before */ fprintf(fp, "X%s[%d] ", cur_direct->spice_model->prefix, cur_direct->spice_model->cnt); /* Input: Print the source grid pin */ fprint_spice_toplevel_one_grid_side_pin_with_given_index(fp, cur_from_clb_pin_index, cur_from_clb_pin_side, from_grid_x, from_grid_y); /* Output: Print the destination grid pin */ fprint_spice_toplevel_one_grid_side_pin_with_given_index(fp, cur_to_clb_pin_index, cur_to_clb_pin_side, to_grid_x, from_grid_y); /* Print Global VDD and GND */ fprintf(fp, "%s %s ", spice_tb_global_vdd_direct_port_name, spice_tb_global_gnd_port_name); /* End with spice_model name */ fprintf(fp, "%s\n", cur_direct->spice_model->name); /* Stats the number of spice_model used*/ cur_direct->spice_model->cnt++; } return; } /* Apply CLB to CLB direct connections to a SPICE netlist */ void fprint_spice_clb2clb_directs(FILE* fp, int num_directs, t_clb_to_clb_directs* direct) { int ix, iy, idirect; int to_clb_x, to_clb_y; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "***** BEGIN CLB to CLB Direct Connections *****\n"); /* Scan the grid, visit each grid and apply direct connections */ for (ix = 0; ix < (nx + 1); ix++) { for (iy = 0; iy < (ny + 1); iy++) { /* Bypass EMPTY_TYPE*/ if ((NULL == grid[ix][iy].type) || (EMPTY_TYPE == grid[ix][iy].type)) { continue; } /* Check each clb2clb directs, * see if a match to the type */ for (idirect = 0; idirect < num_directs; idirect++) { /* Bypass unmatch types */ if (grid[ix][iy].type != direct[idirect].from_clb_type) { continue; } /* Apply x/y_offset */ to_clb_x = ix + direct[idirect].x_offset; to_clb_y = iy + direct[idirect].y_offset; /* see if the destination CLB is in the bound */ if ((FALSE == is_grid_coordinate_in_range(0, nx, to_clb_x)) ||(FALSE == is_grid_coordinate_in_range(0, ny, to_clb_y))) { continue; } /* Check if capacity (z_offset) is in the range if (FALSE == is_grid_coordinate_in_range(0, grid[ix][iy].type->capacity, grid[ix][iy].type->z + direct[idirect].z_offset)) { continue; } */ /* Check if the to_clb_type matches */ if (grid[to_clb_x][to_clb_y].type != direct[idirect].to_clb_type) { continue; } /* Bypass x/y_offset = 1 * since it may be addressed in Connection blocks if (1 == (x_offset + y_offset)) { continue; } */ /* Now we can print a direct connection with the spice models */ fprint_spice_one_clb2clb_direct(fp, ix, iy, to_clb_x, to_clb_y, &direct[idirect]); } } } fprintf(fp, "***** END CLB to CLB Direct Connections *****\n"); return; } /* Print stimulations for floating ports in Grid * Some ports of CLB or I/O Pads is floating. * There are two cases : * 1. Their corresponding rr_node (SOURE or OPIN) has 0 fan-out. * 2. Their corresponding rr_node (SINK or IPIN) has 0 fan-in. * In these cases, we short connect them to global GND. */ static void fprint_grid_float_port_stimulation(FILE* fp) { int inode; int num_float_port = 0; int port_x, port_y, port_height; int side, class_id, pin_index, pin_written_times; t_type_ptr type = NULL; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Search all rr_nodes */ for (inode = 0; inode < num_rr_nodes; inode++) { switch (rr_node[inode].type) { case SOURCE: case OPIN: /* Make sure 0 fan-in, 1 fan-in is connected to SOURCE */ assert((0 == rr_node[inode].fan_in)||(1 == rr_node[inode].fan_in)); if (1 == rr_node[inode].fan_in) { assert(SOURCE == rr_node[rr_node[inode].prev_node].type); } /* Check if there is 0 fan-out */ if (0 == rr_node[inode].num_edges) { port_x = rr_node[inode].xlow; port_y = rr_node[inode].ylow; port_height = grid[port_x][port_y].offset; port_y = port_y + port_height; type = grid[port_x][port_y].type; assert(NULL != type); /* Get pin information */ pin_index = rr_node[inode].ptc_num; class_id = type->pin_class[pin_index]; assert(DRIVER == type->class_inf[class_id].type); pin_written_times = 0; for (side = 0; side < 4; side++) { /* Special Care for I/O pad */ if (IO_TYPE == type) { side = determine_io_grid_side(port_x, port_y); } if (1 == type->pinloc[port_height][side][pin_index]) { fprintf(fp, "Vfloat_port_%d grid[%d][%d]_pin[%d][%d][%d] 0 0\n", num_float_port, port_x, port_y, port_height, side, pin_index); pin_written_times++; num_float_port++; } /* Special Care for I/O pad */ if (IO_TYPE == type) { break; } } assert(1 == pin_written_times); } break; case SINK: case IPIN: /* Make sure 0 fan-out, 1 fan-out is connected to SINK */ assert((0 == rr_node[inode].num_edges)||(1 == rr_node[inode].num_edges)); if (1 == rr_node[inode].num_edges) { assert(SINK == rr_node[rr_node[inode].edges[0]].type); } /* Check if there is 0 fan-out */ if (0 == rr_node[inode].fan_in) { port_x = rr_node[inode].xlow; port_y = rr_node[inode].ylow; port_height = grid[port_x][port_y].offset; port_y = port_y + port_height; type = grid[port_x][port_y + port_height].type; assert(NULL != type); /* Get pin information */ pin_index = rr_node[inode].ptc_num; class_id = type->pin_class[pin_index]; assert(RECEIVER == type->class_inf[class_id].type); pin_written_times = 0; for (side = 0; side < 4; side++) { /* Special Care for I/O pad */ if (IO_TYPE == type) { side = determine_io_grid_side(port_x, port_y); } if (1 == type->pinloc[port_height][side][pin_index]) { fprintf(fp, "Vfloat_port_%d grid[%d][%d]_pin[%d][%d][%d] 0 0\n", num_float_port, port_x, port_y, port_height, side, pin_index); pin_written_times++; num_float_port++; } /* Special Care for I/O pad */ if (IO_TYPE == type) { break; } } assert(1 == pin_written_times); } break; case CHANX: case CHANY: /*TODO: check 0 fan-in, fan-out channel*/ case INTRA_CLUSTER_EDGE: break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Invalid rr_node type!\n", __FILE__, __LINE__); exit(1); } } vpr_printf(TIO_MESSAGE_INFO, "Connect %d floating grid pin to global gnd.\n", num_float_port); return; } void fprint_one_design_param_w_wo_variation(FILE* fp, char* param_name, float avg_val, t_spice_mc_variation_params variation_params) { /* Check */ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s, LINE[%d]) FileHandle is NULL!\n", __FILE__,__LINE__); exit(1); } fprintf(fp,".param %s=", param_name); if (FALSE == variation_params.variation_on) { fprintf(fp, "%g", avg_val); /* We do not allow any negative value exist in the variation, * This could be too tight, could be removed */ } else if (TRUE == check_negative_variation(avg_val, variation_params)) { fprintf(fp, "%g", avg_val); } else { fprintf(fp, "agauss(%g, '%g*%g', %d)", avg_val, variation_params.abs_variation, avg_val, variation_params.num_sigma); } fprintf(fp, "\n"); return; } /* Print Technology Library and Design Parameters*/ void fprint_tech_lib(FILE* fp, t_spice_mc_variation_params cmos_variation_params, t_spice_tech_lib tech_lib) { /* 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; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s, LINE[%d]) FileHandle is NULL!\n", __FILE__,__LINE__); exit(1); } /* Include Technology Library*/ fprintf(fp, "****** Include Technology Library ******\n"); if (SPICE_LIB_INDUSTRY == tech_lib.type) { fprintf(fp, ".lib \'%s\' %s\n", tech_lib.path, tech_lib.transistor_type); } else { fprintf(fp, ".include \'%s\'\n", tech_lib.path); } /* Print Transistor parameters*/ /* Define the basic transistor parameters: nl, pl, wn, wp, pn_ratio*/ fprintf(fp, "****** Transistor Parameters ******\n"); fprintf(fp,".param beta=%g\n",tech_lib.pn_ratio); /* Make sure we have only 2 transistor*/ assert((2 == tech_lib.num_transistor_type)||(4 == tech_lib.num_transistor_type)); /* Find 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); } fprint_one_design_param_w_wo_variation(fp, "nl", nmos_trans->chan_length, cmos_variation_params); fprint_one_design_param_w_wo_variation(fp, "wn", nmos_trans->min_width, cmos_variation_params); /* Find 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); } fprint_one_design_param_w_wo_variation(fp, "pl", pmos_trans->chan_length, cmos_variation_params); fprint_one_design_param_w_wo_variation(fp, "wp", pmos_trans->min_width, cmos_variation_params); /* Print I/O NMOS and PMOS */ io_nmos_trans = find_mosfet_tech_lib(tech_lib, SPICE_TRANS_IO_NMOS); if ((NULL == io_nmos_trans) && (4 == tech_lib.num_transistor_type)) { vpr_printf(TIO_MESSAGE_WARNING,"I/O NMOS transistor is not defined in architecture XML!\n"); exit(1); } if (NULL != io_nmos_trans) { fprint_one_design_param_w_wo_variation(fp, "io_nl", io_nmos_trans->chan_length, cmos_variation_params); fprint_one_design_param_w_wo_variation(fp, "io_wn", io_nmos_trans->min_width, cmos_variation_params); } io_pmos_trans = find_mosfet_tech_lib(tech_lib,SPICE_TRANS_IO_PMOS); if ((NULL == io_pmos_trans) && (4 == tech_lib.num_transistor_type)) { vpr_printf(TIO_MESSAGE_WARNING,"I/O PMOS transistor is not defined in architecture XML!\n"); exit(1); } if (NULL != io_nmos_trans) { fprint_one_design_param_w_wo_variation(fp, "io_pl", io_pmos_trans->chan_length, cmos_variation_params); fprint_one_design_param_w_wo_variation(fp, "io_wp", io_pmos_trans->min_width, cmos_variation_params); } /* Print nominal Vdd */ fprintf(fp, ".param vsp=%g\n", tech_lib.nominal_vdd); /* Print I/O VDD */ fprintf(fp, ".param io_vsp=%g\n", tech_lib.io_vdd); return; } /* Print all the circuit design parameters */ void fprint_spice_circuit_param(FILE* fp, t_spice_mc_params mc_params, int num_spice_models, t_spice_model* spice_model) { int imodel; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!", __FILE__, __LINE__); exit(1); } fprintf(fp, "***** Parameters for Circuits *****\n"); for (imodel = 0; imodel < num_spice_models; imodel++) { fprintf(fp, "***** Parameters for SPICE MODEL: %s *****\n", spice_model[imodel].name); /* Regular design parameters: input buf sizes, output buf sizes*/ if ((NULL != spice_model[imodel].input_buffer) &&(TRUE == spice_model[imodel].input_buffer->exist)) { fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_input_buf_size), spice_model[imodel].input_buffer->size, mc_params.cmos_variation); } if ((NULL != spice_model[imodel].output_buffer) &&(TRUE == spice_model[imodel].output_buffer->exist)) { fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_output_buf_size), spice_model[imodel].output_buffer->size, mc_params.cmos_variation); } if (NULL != spice_model[imodel].pass_gate_logic) { fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_pass_gate_logic_pmos_size), spice_model[imodel].pass_gate_logic->pmos_size, mc_params.cmos_variation); fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_pass_gate_logic_nmos_size), spice_model[imodel].pass_gate_logic->nmos_size, mc_params.cmos_variation); } /* Exclusive parameters WIREs */ if ((SPICE_MODEL_CHAN_WIRE == spice_model[imodel].type) ||(SPICE_MODEL_WIRE == spice_model[imodel].type)) { fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_wire_param_res_val), spice_model[imodel].wire_param->res_val, mc_params.wire_variation); fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_wire_param_cap_val), spice_model[imodel].wire_param->cap_val, mc_params.wire_variation); } /* We care the spice models built with RRAMs */ if (SPICE_MODEL_DESIGN_RRAM == spice_model[imodel].design_tech) { /* Print Ron */ fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_ron), spice_model[imodel].design_tech_info.rram_info->ron, mc_params.rram_variation); /* Print Roff */ fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_roff), spice_model[imodel].design_tech_info.rram_info->roff, mc_params.rram_variation); /* Print Wprog_set_nmos */ fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_wprog_set_nmos), spice_model[imodel].design_tech_info.rram_info->wprog_set_nmos, mc_params.cmos_variation); /* Print Wprog_set_pmos */ fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_wprog_set_pmos), spice_model[imodel].design_tech_info.rram_info->wprog_set_pmos, mc_params.cmos_variation); /* Print Wprog_reset_nmos */ fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_wprog_reset_nmos), spice_model[imodel].design_tech_info.rram_info->wprog_reset_nmos, mc_params.cmos_variation); /* Print Wprog_reset_pmos */ fprint_one_design_param_w_wo_variation(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_wprog_reset_pmos), spice_model[imodel].design_tech_info.rram_info->wprog_reset_pmos, mc_params.cmos_variation); } } return; } void fprint_spice_netlist_measurement_one_design_param(FILE* fp, char* param_name) { if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!", __FILE__, __LINE__); exit(1); } fprintf(fp, ".meas tran actual_%s param='%s'\n", param_name, param_name); return; } void fprint_spice_netlist_generic_measurements(FILE* fp, t_spice_mc_params mc_params, int num_spice_models, t_spice_model* spice_model) { int imodel; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!", __FILE__, __LINE__); exit(1); } fprintf(fp, "***** Generic Measurements for Circuit Parameters *****\n"); if ((FALSE == mc_params.cmos_variation.variation_on) &&(FALSE == mc_params.rram_variation.variation_on)) { return; } for (imodel = 0; imodel < num_spice_models; imodel++) { fprintf(fp, "***** Measurements for Parameters for SPICE MODEL: %s *****\n", spice_model[imodel].name); /* Regular design parameters: input buf sizes, output buf sizes*/ if ((NULL != spice_model[imodel].input_buffer) &&(TRUE == spice_model[imodel].input_buffer->exist)) { fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_input_buf_size)); } if ((NULL != spice_model[imodel].output_buffer) &&(TRUE == spice_model[imodel].output_buffer->exist)) { fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_output_buf_size)); } if (NULL != spice_model[imodel].pass_gate_logic) { fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_pass_gate_logic_pmos_size)); fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_pass_gate_logic_nmos_size)); } /* Exclusive parameters WIREs */ if ((SPICE_MODEL_CHAN_WIRE == spice_model[imodel].type) ||(SPICE_MODEL_WIRE == spice_model[imodel].type)) { fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_wire_param_res_val)); fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_wire_param_cap_val)); } /* We care the spice models built with RRAMs */ if (SPICE_MODEL_DESIGN_RRAM == spice_model[imodel].design_tech) { /* Print Ron */ fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_ron)); /* Print Roff */ fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_roff)); /* Print Wprog_set_nmos */ fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_wprog_set_nmos)); /* Print Wprog_set_pmos */ fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_wprog_set_pmos)); /* Print Wprog_reset_nmos */ fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_wprog_reset_nmos)); /* Print Wprog_reset_pmos */ fprint_spice_netlist_measurement_one_design_param(fp, my_strcat(spice_model[imodel].name, design_param_postfix_rram_wprog_reset_pmos)); } } return; } /* This function may expand. * It prints temperature, and options for a SPICE simulation */ void fprint_spice_options(FILE* fp, t_spice_params spice_params) { if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!",__FILE__, __LINE__); exit(1); } /* Temperature */ fprintf(fp, ".temp %d\n", spice_params.sim_temp); /* Options: print capacitances of all nodes */ if (TRUE == spice_params.captab) { fprintf(fp, ".option captab\n"); } /* Add post could make SPICE very slow for large benchmarks!!! Be careful*/ if (TRUE == spice_params.post) { fprintf(fp, ".option post\n"); } /* Use fast */ if (TRUE == spice_params.fast) { fprintf(fp, ".option fast\n"); } return; } /* This function may expand. * It prints include paramters for SPICE netlists */ void fprint_spice_include_param_headers(FILE* fp, char* include_dir_path) { char* temp_include_file_path = NULL; char* formatted_include_dir_path = format_dir_path(include_dir_path); if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!",__FILE__, __LINE__); exit(1); } /* Include headers for circuit designs, measurements and stimulates */ fprintf(fp, "****** Include Header file: circuit design parameters *****\n"); temp_include_file_path = my_strcat(formatted_include_dir_path, design_param_header_file_name); fprintf(fp, ".include \'%s\'\n", temp_include_file_path); my_free(temp_include_file_path); fprintf(fp, "****** Include Header file: measurement parameters *****\n"); temp_include_file_path = my_strcat(formatted_include_dir_path, meas_header_file_name); fprintf(fp, ".include \'%s\'\n", temp_include_file_path); my_free(temp_include_file_path); fprintf(fp, "****** Include Header file: stimulation parameters *****\n"); temp_include_file_path = my_strcat(formatted_include_dir_path, stimu_header_file_name); fprintf(fp, ".include \'%s\'\n", temp_include_file_path); my_free(temp_include_file_path); return; } /* This function may expand. * It prints include sub-circuit SPICE netlists */ void fprint_spice_include_key_subckts(FILE* fp, char* subckt_dir_path) { char* temp_include_file_path = NULL; char* formatted_subckt_dir_path = format_dir_path(subckt_dir_path); if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!",__FILE__, __LINE__); exit(1); } /* Include necessary sub-circuits */ fprintf(fp, "****** Include subckt netlists: NMOS and PMOS *****\n"); temp_include_file_path = my_strcat(formatted_subckt_dir_path, nmos_pmos_spice_file_name); fprintf(fp, ".include \'%s\'\n", temp_include_file_path); my_free(temp_include_file_path); if (1 == rram_design_tech) { fprintf(fp, "****** Include subckt netlists: RRAM behavior VerilogA model *****\n"); /* This is a HSPICE Bug! When the verilogA file contain a dir_path, the sim results become weired! */ /* temp_include_file_path = my_strcat(formatted_subckt_dir_path, rram_veriloga_file_name); fprintf(fp, ".hdl \'%s\'\n", temp_include_file_path); my_free(temp_include_file_path); */ fprintf(fp, ".hdl \'%s\'\n", rram_veriloga_file_name); } fprintf(fp, "****** Include subckt netlists: Inverters, Buffers *****\n"); temp_include_file_path = my_strcat(formatted_subckt_dir_path, basics_spice_file_name); fprintf(fp, ".include \'%s\'\n", temp_include_file_path); my_free(temp_include_file_path); fprintf(fp, "****** Include subckt netlists: Multiplexers *****\n"); temp_include_file_path = my_strcat(formatted_subckt_dir_path, muxes_spice_file_name); fprintf(fp, ".include \'%s\'\n", temp_include_file_path); my_free(temp_include_file_path); fprintf(fp, "****** Include subckt netlists: Wires *****\n"); temp_include_file_path = my_strcat(formatted_subckt_dir_path, wires_spice_file_name); fprintf(fp, ".include \'%s\'\n", temp_include_file_path); my_free(temp_include_file_path); return; } void fprint_voltage_pulse_params(FILE* fp, int init_val, float density, float probability) { if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!",__FILE__, __LINE__); exit(1); } /* TODO: check codes for density and probability, init_val */ /* If density = 0, this is a constant signal */ if (0. == density) { if (0. == probability) { fprintf(fp, "+ 0\n"); } else { fprintf(fp, "+ vsp\n"); } return; } if (0 == init_val) { fprintf(fp, "+ pulse(0 vsp 'clock_period' \n"); } else { fprintf(fp, "+ pulse(vsp 0 'clock_period' \n"); } /* fprintf(fp, "+ 'input_slew_pct_rise*%g*clock_period' 'input_slew_pct_fall*%g*clock_period'\n", 2./density, 2./density); */ /* TODO: Think about a reasonable slew for signals with diverse density */ fprintf(fp, "+ 'input_slew_pct_rise*clock_period' 'input_slew_pct_fall*clock_period'\n"); fprintf(fp, "+ '%g*%g*(1-input_slew_pct_rise-input_slew_pct_fall)*clock_period' '%g*clock_period')\n", probability, 2./density, 2./density); return; } void fprint_spice_netlist_transient_setting(FILE* fp, t_spice spice, int num_sim_clock_cycles, boolean leakage_only) { int num_clock_cycle = spice.spice_params.meas_params.sim_num_clock_cycle + 1; /* Overwrite the sim if auto is turned on */ if ((TRUE == spice.spice_params.meas_params.auto_select_sim_num_clk_cycle) &&(num_sim_clock_cycles < num_clock_cycle)) { num_clock_cycle = num_sim_clock_cycles; } /* if (TRUE == spice.spice_params.meas_params.auto_select_sim_num_clk_cycle) { assert(!(num_sim_clock_cycles > num_clock_cycle)); } */ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!",__FILE__, __LINE__); exit(1); } /* Leakage power only, use a simplified tran sim*/ if (TRUE == leakage_only) { fprintf(fp, "***** Transient simulation only for leakage power *****\n"); } else { fprintf(fp, "***** %d Clock Simulation, accuracy=%g *****\n", num_clock_cycle, spice.spice_params.meas_params.accuracy); } /* Determine the transistion time to simulate */ switch (spice.spice_params.meas_params.accuracy_type) { case SPICE_ABS: fprintf(fp, ".tran %g ", spice.spice_params.meas_params.accuracy); break; case SPICE_FRAC: fprintf(fp, ".tran '%d*clock_period/%d' ", num_clock_cycle, (int)spice.spice_params.meas_params.accuracy); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid accuracy type!\n", __FILE__, __LINE__); exit(1); } if (TRUE == leakage_only) { fprintf(fp, " 'clock_period'"); } else { fprintf(fp, " '%d*clock_period'", num_clock_cycle); } if ((TRUE == spice.spice_params.mc_params.cmos_variation.variation_on) ||(TRUE == spice.spice_params.mc_params.rram_variation.variation_on) ||(TRUE == spice.spice_params.mc_params.mc_sim)) { fprintf(fp, " sweep monte=%d ", spice.spice_params.mc_params.num_mc_points); } fprintf(fp, "\n"); return; } void fprint_stimulate_dangling_one_grid_pin(FILE* fp, int x, int y, int height, int side, int pin_index, t_ivec*** LL_rr_node_indices) { t_type_ptr type_descriptor = grid[x][y].type; int capacity = grid[x][y].type->capacity; int class_id; int rr_node_index; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > x))&&(!(x > (nx + 1)))); assert((!(0 > y))&&(!(y > (ny + 1)))); assert(NULL != type_descriptor); assert(0 < capacity); class_id = type_descriptor->pin_class[pin_index]; if (DRIVER == type_descriptor->class_inf[class_id].type) { rr_node_index = get_rr_node_index(x, y, OPIN, pin_index, LL_rr_node_indices); /* Zero fan-out OPIN */ if (0 == rr_node[rr_node_index].num_edges) { fprintf(fp, "Rdangling_grid[%d][%d]_pin[%d][%d][%d] grid[%d][%d]_pin[%d][%d][%d] 0 1e9\n", x, y, height, side, pin_index, x, y, height, side, pin_index); fprintf(fp, "*.nodeset V(grid[%d][%d]_pin[%d][%d][%d]) 0 \n", x, y, height, side, pin_index); } return; } if (RECEIVER == type_descriptor->class_inf[class_id].type) { rr_node_index = get_rr_node_index(x, y, IPIN, pin_index, LL_rr_node_indices); /* Zero fan-in IPIN */ if (0 == rr_node[rr_node_index].fan_in) { fprintf(fp, "Rdangling_grid[%d][%d]_pin[%d][%d][%d] grid[%d][%d]_pin[%d][%d][%d] 0 0\n", x, y, height, side, pin_index, x, y, height, side, pin_index); fprintf(fp, ".nodeset V(grid[%d][%d]_pin[%d][%d][%d]) 0\n", x, y, height, side, pin_index); } return; } return; } void fprint_stimulate_dangling_io_grid_pins(FILE* fp, int x, int y) { int iheight, side, ipin; t_type_ptr type_descriptor = grid[x][y].type; int capacity = grid[x][y].type->capacity; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > x))&&(!(x > (nx + 1)))); assert((!(0 > y))&&(!(y > (ny + 1)))); assert(NULL != type_descriptor); assert(0 < capacity); /* identify the location of IO grid and * decide which side of ports we need */ side = determine_io_grid_side(x,y); /* Count the number of pins */ //for (iz = 0; iz < capacity; iz++) { for (iheight = 0; iheight < type_descriptor->height; iheight++) { for (ipin = 0; ipin < type_descriptor->num_pins; ipin++) { if (1 == type_descriptor->pinloc[iheight][side][ipin]) { fprint_stimulate_dangling_one_grid_pin(fp, x, y, iheight, side, ipin, rr_node_indices); } } } //} return; } void fprint_stimulate_dangling_normal_grid_pins(FILE* fp, int x, int y) { int iheight, side, ipin; t_type_ptr type_descriptor = grid[x][y].type; int capacity = grid[x][y].type->capacity; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > x))&&(!(x > (nx + 1)))); assert((!(0 > y))&&(!(y > (ny + 1)))); assert(NULL != type_descriptor); assert(0 < capacity); for (side = 0; side < 4; side++) { /* Count the number of pins */ for (iheight = 0; iheight < type_descriptor->height; iheight++) { for (ipin = 0; ipin < type_descriptor->num_pins; ipin++) { if (1 == type_descriptor->pinloc[iheight][side][ipin]) { fprint_stimulate_dangling_one_grid_pin(fp, x, y, iheight, side, ipin, rr_node_indices); } } } } return; } void fprint_stimulate_dangling_grid_pins(FILE* fp) { int ix, iy; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } /* Normal Grids */ for (ix = 1; ix < (nx + 1); ix++) { for (iy = 1; iy < (ny + 1); iy++) { /* Bypass EMPTY grid */ if (EMPTY_TYPE == grid[ix][iy].type) { continue; } assert(IO_TYPE != grid[ix][iy].type); /* zero-fan-in CLB IPIN*/ fprint_stimulate_dangling_normal_grid_pins(fp, ix, iy); } } /* IO Grids */ /* LEFT side */ ix = 0; for (iy = 1; iy < (ny + 1); iy++) { /* Bypass EMPTY grid */ if (EMPTY_TYPE == grid[ix][iy].type) { continue; } assert(IO_TYPE == grid[ix][iy].type); fprint_stimulate_dangling_io_grid_pins(fp, ix, iy); } /* RIGHT side */ ix = nx + 1; for (iy = 1; iy < (ny + 1); iy++) { /* Bypass EMPTY grid */ if (EMPTY_TYPE == grid[ix][iy].type) { continue; } assert(IO_TYPE == grid[ix][iy].type); fprint_stimulate_dangling_io_grid_pins(fp, ix, iy); } /* BOTTOM side */ iy = 0; for (ix = 1; ix < (nx + 1); ix++) { /* Bypass EMPTY grid */ if (EMPTY_TYPE == grid[ix][iy].type) { continue; } assert(IO_TYPE == grid[ix][iy].type); fprint_stimulate_dangling_io_grid_pins(fp, ix, iy); } /* TOP side */ iy = ny + 1; for (ix = 1; ix < (nx + 1); ix++) { /* Bypass EMPTY grid */ if (EMPTY_TYPE == grid[ix][iy].type) { continue; } assert(IO_TYPE == grid[ix][iy].type); fprint_stimulate_dangling_io_grid_pins(fp, ix, iy); } return; } void init_logical_block_spice_model_temp_used(t_spice_model* spice_model) { int i; /* For each logical block, we print a vdd */ for (i = 0; i < num_logical_blocks; i++) { if (logical_block[i].mapped_spice_model == spice_model) { logical_block[i].temp_used = 0; } } return; } void init_logical_block_spice_model_type_temp_used(int num_spice_models, t_spice_model* spice_model, enum e_spice_model_type spice_model_type) { int i; /* For each logical block, we print a vdd */ for (i = 0; i < num_spice_models; i++) { if (spice_model_type == spice_model[i].type) { init_logical_block_spice_model_temp_used(&(spice_model[i])); } } return; } void fprint_global_vdds_logical_block_spice_model(FILE* fp, t_spice_model* spice_model) { int i; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } /* For each logical block, we print a vdd */ for (i = 0; i < num_logical_blocks; i++) { if ((logical_block[i].mapped_spice_model == spice_model) &&(1 == logical_block[i].temp_used)){ fprintf(fp, ".global gvdd_%s[%d]\n", spice_model->prefix, logical_block[i].mapped_spice_model_index); } } return; } void fprint_splited_vdds_logical_block_spice_model(FILE* fp, t_spice_model* spice_model) { int i; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } /* For each logical block, we print a vdd */ for (i = 0; i < num_logical_blocks; i++) { if ((logical_block[i].mapped_spice_model == spice_model) &&(1 == logical_block[i].temp_used)){ fprintf(fp, "Vgvdd_%s[%d] gvdd_%s[%d] 0 vsp\n", spice_model->prefix, logical_block[i].mapped_spice_model_index, spice_model->prefix, logical_block[i].mapped_spice_model_index); } } return; } void fprint_measure_vdds_logical_block_spice_model(FILE* fp, t_spice_model* spice_model, enum e_measure_type meas_type, int num_clock_cycle, boolean leakage_only) { int i, iport, ipin, cur; float average_output_density = 0.; int output_cnt = 0; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } /* For each logical block, we print a vdd */ for (i = 0; i < num_logical_blocks; i++) { if ((logical_block[i].mapped_spice_model == spice_model) &&(1 == logical_block[i].temp_used)) { /* Get the average output density */ output_cnt = 0; for (iport = 0; iport < logical_block[i].pb->pb_graph_node->num_output_ports; iport++) { for (ipin = 0; ipin < logical_block[i].pb->pb_graph_node->num_output_pins[iport]; ipin++) { average_output_density += vpack_net[logical_block[i].output_nets[iport][ipin]].spice_net_info->density; output_cnt++; } } average_output_density = average_output_density/output_cnt; switch (meas_type) { case SPICE_MEASURE_LEAKAGE_POWER: if (TRUE == leakage_only) { fprintf(fp, ".measure tran leakage_power_%s[%d] find p(Vgvdd_%s[%d]) at=0\n", spice_model->prefix, logical_block[i].mapped_spice_model_index, spice_model->prefix, logical_block[i].mapped_spice_model_index); } else { fprintf(fp, ".measure tran leakage_power_%s[%d] avg p(Vgvdd_%s[%d]) from=0 to='clock_period'\n", spice_model->prefix, logical_block[i].mapped_spice_model_index, spice_model->prefix, logical_block[i].mapped_spice_model_index); } break; case SPICE_MEASURE_DYNAMIC_POWER: fprintf(fp, ".measure tran dynamic_power_%s[%d] avg p(Vgvdd_%s[%d]) from='clock_period' to='%d*clock_period'\n", spice_model->prefix, logical_block[i].mapped_spice_model_index, spice_model->prefix, logical_block[i].mapped_spice_model_index, num_clock_cycle); fprintf(fp, ".measure tran energy_per_cycle_%s[%d] param='dynamic_power_%s[%d]*clock_period'\n", spice_model->prefix, logical_block[i].mapped_spice_model_index, spice_model->prefix, logical_block[i].mapped_spice_model_index); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid meas_type!\n", __FILE__, __LINE__); exit(1); } } } /* Measure the total power of this kind of spice model */ cur = 0; switch (meas_type) { case SPICE_MEASURE_LEAKAGE_POWER: for (i = 0; i < num_logical_blocks; i++) { if ((logical_block[i].mapped_spice_model == spice_model) &&(1 == logical_block[i].temp_used)) { fprintf(fp, ".measure tran leakage_power_%s[0to%d] \n", spice_model->prefix, cur); if (0 == cur) { fprintf(fp, "+ param = 'leakage_power_%s[%d]'\n", spice_model->prefix, logical_block[i].mapped_spice_model_index); } else { fprintf(fp, "+ param = 'leakage_power_%s[%d]+leakage_power_%s[0to%d]'\n", spice_model->prefix, logical_block[i].mapped_spice_model_index, spice_model->prefix, cur-1); } cur++; } } if (0 == cur) { break; } /* Spot the total leakage power of this spice model */ fprintf(fp, ".measure tran total_leakage_power_%s \n", spice_model->prefix); fprintf(fp, "+ param = 'leakage_power_%s[0to%d]'\n", spice_model->prefix, cur-1); break; case SPICE_MEASURE_DYNAMIC_POWER: for (i = 0; i < num_logical_blocks; i++) { if ((logical_block[i].mapped_spice_model == spice_model) &&(1 == logical_block[i].temp_used)) { fprintf(fp, ".measure tran energy_per_cycle_%s[0to%d] \n", spice_model->prefix, cur); if (0 == cur) { fprintf(fp, "+ param = 'energy_per_cycle_%s[%d]'\n", spice_model->prefix, logical_block[i].mapped_spice_model_index); } else { fprintf(fp, "+ param = 'energy_per_cycle_%s[%d]+energy_per_cycle_%s[0to%d]'\n", spice_model->prefix, logical_block[i].mapped_spice_model_index, spice_model->prefix, cur-1); } cur++; } } if (0 == cur) { break; } /* Spot the total dynamic power of this spice model */ fprintf(fp, ".measure tran total_energy_per_cycle_%s \n", spice_model->prefix); fprintf(fp, "+ param = 'energy_per_cycle_%s[0to%d]'\n", spice_model->prefix, cur-1); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid meas_type!\n", __FILE__, __LINE__); exit(1); } return; } /* Give voltage stimuli of one global port */ void fprint_spice_testbench_wire_one_global_port_stimuli(FILE* fp, t_spice_model_port* cur_global_port, char* voltage_stimuli_port_name) { int ipin; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } assert(NULL != cur_global_port); for (ipin = 0; ipin < cur_global_port->size; ipin++) { fprintf(fp, "Rshortwire%s[%d] %s[%d] ", cur_global_port->prefix, ipin, cur_global_port->prefix, ipin); assert((0 == cur_global_port->default_val)||(1 == cur_global_port->default_val)); fprintf(fp, "%s", voltage_stimuli_port_name); if (1 == cur_global_port->default_val) { fprintf(fp, "%s ", spice_tb_global_port_inv_postfix); } fprintf(fp, " 0\n"); } return; } /* Give voltage stimuli of global ports */ void fprint_spice_testbench_global_ports_stimuli(FILE* fp, t_llist* head) { t_llist* temp = head; t_spice_model_port* cur_global_port = NULL; int ipin; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "***** Connecting Global ports *****\n"); while(NULL != temp) { cur_global_port = (t_spice_model_port*)(temp->dptr); /* Make sure this is a global port */ assert(TRUE == cur_global_port->is_global); /* If this is a clock signal, connect to op_clock signal */ if (SPICE_MODEL_PORT_CLOCK == cur_global_port->type) { /* Special for programming clock */ if (TRUE == cur_global_port->is_prog) { /* We do need to program SRAMs/RRAM in spice netlist * Just wire it to a constant GND/VDD */ fprint_spice_testbench_wire_one_global_port_stimuli(fp, cur_global_port, spice_tb_global_config_done_port_name); } else { assert(FALSE == cur_global_port->is_prog); fprint_spice_testbench_wire_one_global_port_stimuli(fp, cur_global_port, spice_tb_global_clock_port_name); } /* If this is a config_enable signal, connect to config_done signal */ } else if (TRUE == cur_global_port->is_config_enable) { fprint_spice_testbench_wire_one_global_port_stimuli(fp, cur_global_port, spice_tb_global_config_done_port_name); /* If this is a set/reset signal, connect to global reset and set signals */ } else if (TRUE == cur_global_port->is_reset) { /* Special for programming reset */ if (TRUE == cur_global_port->is_prog) { fprint_spice_testbench_wire_one_global_port_stimuli(fp, cur_global_port, spice_tb_global_reset_port_name); } else { assert(FALSE == cur_global_port->is_prog); fprint_spice_testbench_wire_one_global_port_stimuli(fp, cur_global_port, spice_tb_global_reset_port_name); } /* If this is a set/reset signal, connect to global reset and set signals */ } else if (TRUE == cur_global_port->is_set) { /* Special for programming reset */ if (TRUE == cur_global_port->is_prog) { fprint_spice_testbench_wire_one_global_port_stimuli(fp, cur_global_port, spice_tb_global_set_port_name); } else { assert(FALSE == cur_global_port->is_prog); fprint_spice_testbench_wire_one_global_port_stimuli(fp, cur_global_port, spice_tb_global_set_port_name); } } else { /* Other global signals stuck at the default values */ for (ipin = 0; ipin < cur_global_port->size; ipin++) { fprintf(fp, "R%s[%d] %s[%d] ", cur_global_port->prefix, ipin, cur_global_port->prefix, ipin); assert( (0 == cur_global_port->default_val)||(1 == cur_global_port->default_val) ); if ( 0 == cur_global_port->default_val ) { /* Default value is 0: Connect to the global GND port */ fprintf(fp, " %s", spice_tb_global_gnd_port_name); } else { /* Default value is 1: Connect to the global VDD port */ fprintf(fp, " %s", spice_tb_global_vdd_port_name); } fprintf(fp, " 0\n"); } } /* Go to the next */ temp = temp->next; } fprintf(fp, "***** End Connecting Global ports *****\n"); return; } void fprint_spice_testbench_global_vdd_port_stimuli(FILE* fp, char* global_vdd_port_name, char* voltage_level) { /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Print a Voltage Stimuli */ fprintf(fp, "V%s %s 0 %s\n", global_vdd_port_name, global_vdd_port_name, voltage_level); return; } void fprint_spice_testbench_global_sram_inport_stimuli(FILE* fp, t_sram_orgz_info* cur_sram_orgz_info) { t_spice_model* mem_model = NULL; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Get memory spice model */ get_sram_orgz_info_mem_model(cur_sram_orgz_info, &mem_model); /* Every SRAM inputs should have a voltage source */ fprintf(fp, "***** Global Inputs for SRAMs *****\n"); if (SPICE_SRAM_SCAN_CHAIN == cur_sram_orgz_info->type) { fprintf(fp, "Vsc_clk sc_clk 0 0\n"); fprintf(fp, "Vsc_rst sc_rst 0 0\n"); fprintf(fp, "Vsc_set sc_set 0 0\n"); fprintf(fp, "V%s[0]->in %s[0]->in 0 0\n", mem_model->prefix, mem_model->prefix); fprintf(fp, ".nodeset V(%s[0]->in) 0\n", mem_model->prefix); } else { fprintf(fp, "V%s->in %s->in 0 0\n", mem_model->prefix, mem_model->prefix); fprintf(fp, ".nodeset V(%s->in) 0\n", mem_model->prefix); } return; } void fprint_spice_testbench_generic_global_ports_stimuli(FILE* fp, int num_clock) { /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Global GND */ fprintf(fp, "***** Global VDD port *****\n"); fprintf(fp, "V%s %s 0 vsp\n", spice_tb_global_vdd_port_name, spice_tb_global_vdd_port_name); fprintf(fp, "***** Global GND port *****\n"); fprintf(fp, "V%s %s 0 0\n", spice_tb_global_gnd_port_name, spice_tb_global_gnd_port_name); /* Global set and reset */ fprintf(fp, "***** Global Net for reset signal *****\n"); fprintf(fp, "V%s %s 0 0\n", spice_tb_global_reset_port_name, spice_tb_global_reset_port_name); fprintf(fp, "V%s%s %s%s 0 vsp\n", spice_tb_global_reset_port_name, spice_tb_global_port_inv_postfix, spice_tb_global_reset_port_name, spice_tb_global_port_inv_postfix); fprintf(fp, "***** Global Net for set signal *****\n"); fprintf(fp, "V%s %s 0 0\n", spice_tb_global_set_port_name, spice_tb_global_set_port_name); fprintf(fp, "V%s%s %s%s 0 vsp\n", spice_tb_global_set_port_name, spice_tb_global_port_inv_postfix, spice_tb_global_set_port_name, spice_tb_global_port_inv_postfix); /* Global config done */ fprintf(fp, "***** Global Net for configuration done signal *****\n"); fprintf(fp, "V%s %s 0 0\n", spice_tb_global_config_done_port_name, spice_tb_global_config_done_port_name); fprintf(fp, "V%s%s %s%s 0 vsp\n", spice_tb_global_config_done_port_name, spice_tb_global_port_inv_postfix, spice_tb_global_config_done_port_name, spice_tb_global_port_inv_postfix); /* Global clock if we need one */ if (1 == num_clock) { /* First cycle reserved for measuring leakage */ fprintf(fp, "***** Global Clock signal *****\n"); fprintf(fp, "***** pulse(vlow vhigh tdelay trise tfall pulse_width period *****\n"); fprintf(fp, "V%s %s 0 pulse(0 vsp 'clock_period'\n", spice_tb_global_clock_port_name, spice_tb_global_clock_port_name); fprintf(fp, "+ 'clock_slew_pct_rise*clock_period' 'clock_slew_pct_fall*clock_period'\n"); fprintf(fp, "+ '0.5*(1-clock_slew_pct_rise-clock_slew_pct_fall)*clock_period' 'clock_period')\n"); fprintf(fp, "\n"); fprintf(fp, "***** pulse(vlow vhigh tdelay trise tfall pulse_width period *****\n"); fprintf(fp, "V%s%s %s%s 0 pulse(0 vsp 'clock_period'\n", spice_tb_global_clock_port_name, spice_tb_global_port_inv_postfix, spice_tb_global_clock_port_name, spice_tb_global_port_inv_postfix); fprintf(fp, "+ 'clock_slew_pct_rise*clock_period' 'clock_slew_pct_fall*clock_period'\n"); fprintf(fp, "+ '0.5*(1-clock_slew_pct_rise-clock_slew_pct_fall)*clock_period' 'clock_period')\n"); } else { assert(0 == num_clock); /* Give constant value */ fprintf(fp, "V%s %s 0 0\n", spice_tb_global_clock_port_name, spice_tb_global_clock_port_name); fprintf(fp, "V%s%s %s%s 0 vsp\n", spice_tb_global_clock_port_name, spice_tb_global_port_inv_postfix, spice_tb_global_clock_port_name, spice_tb_global_port_inv_postfix); } return; } /* Find the inverter size of a Programmable Logic Block Pin * */ float find_spice_testbench_pb_pin_mux_load_inv_size(t_spice_model* fan_out_spice_model) { float load_inv_size = 0; /* Check */ assert(NULL != fan_out_spice_model); assert(NULL != fan_out_spice_model->input_buffer); /* Special: this is a LUT, we should consider more inv size */ if (SPICE_MODEL_LUT == fan_out_spice_model->type) { assert(1 == fan_out_spice_model->lut_input_buffer->exist); assert((SPICE_MODEL_BUF_INV == fan_out_spice_model->lut_input_buffer->type) ||(SPICE_MODEL_BUF_BUF == fan_out_spice_model->lut_input_buffer->type)); assert(TRUE == fan_out_spice_model->lut_input_buffer->tapered_buf); assert(2 == fan_out_spice_model->lut_input_buffer->tap_buf_level); load_inv_size = fan_out_spice_model->lut_input_buffer->size + fan_out_spice_model->lut_input_buffer->f_per_stage; return load_inv_size; } /* depend on the input_buffer type */ if (1 == fan_out_spice_model->input_buffer->exist) { switch(fan_out_spice_model->input_buffer->type) { case SPICE_MODEL_BUF_INV: load_inv_size = fan_out_spice_model->input_buffer->size; break; case SPICE_MODEL_BUF_BUF: load_inv_size = 1.; break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Invalid fanout spice_model input_buffer type!\n", __FILE__, __LINE__); exit(1); } } else { /* TODO: If there is no inv/buffer at input, we should traversal until there is one * However, now we just simply give a minimum sized inverter */ load_inv_size = 1.; } return load_inv_size; } float find_spice_testbench_rr_mux_load_inv_size(t_rr_node* load_rr_node, int switch_index) { float load_inv_size = 0; t_spice_model* fan_out_spice_model = NULL; fan_out_spice_model = switch_inf[switch_index].spice_model; /* Check */ assert(NULL != fan_out_spice_model); assert(NULL != fan_out_spice_model->input_buffer); /* depend on the input_buffer type */ if (1 == fan_out_spice_model->input_buffer->exist) { switch(fan_out_spice_model->input_buffer->type) { case SPICE_MODEL_BUF_INV: load_inv_size = fan_out_spice_model->input_buffer->size; break; case SPICE_MODEL_BUF_BUF: load_inv_size = fan_out_spice_model->input_buffer->size; break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Invalid fanout spice_model input_buffer type!\n", __FILE__, __LINE__); exit(1); } } else { /* TODO: If there is no inv/buffer at input, we should traversal until there is one * However, now we just simply give a minimum sized inverter * fprint_spice_testbench_pb_graph_pin_inv_loads_rec(fp, &testbench_load_cnt, x, y, &(cur_pb_graph_node->output_pins[0][0]), logical_block[logical_block_index].pb, outport_name, FALSE, LL_rr_node_indices); */ load_inv_size = 1; } return load_inv_size; } void fprint_spice_testbench_pb_graph_pin_inv_loads_rec(FILE* fp, int* testbench_load_cnt, int grid_x, int grid_y, t_pb_graph_pin* src_pb_graph_pin, t_phy_pb* src_pb, char* outport_name, boolean consider_parent_node, t_ivec*** LL_rr_node_indices) { int iedge, mode_index, ipb, jpb; t_interconnect* cur_interc = NULL; char* rec_outport_name = NULL; t_phy_pb* des_pb = NULL; int src_rr_node_index = -1; float load_inv_size = 0.; float total_width; int width_cnt; /* A valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!\n",__FILE__, __LINE__); exit(1); } assert(NULL != src_pb_graph_pin); if (TRUE == consider_parent_node) { if (NULL != src_pb_graph_pin->parent_node->pb_type->spice_model) { load_inv_size = find_spice_testbench_pb_pin_mux_load_inv_size(src_pb_graph_pin->parent_node->pb_type->spice_model); /* Print inverters by considering maximum width */ total_width = load_inv_size; width_cnt = 0; while (total_width > max_width_per_trans) { fprintf(fp, "Xload_inv[%d]_no%d %s %s_out[0] gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, outport_name, outport_name, max_width_per_trans); /* Update counter */ total_width = total_width - max_width_per_trans; width_cnt++; } if (total_width > 0) { fprintf(fp, "Xload_inv[%d]_no%d %s %s_out[0] gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, outport_name, outport_name, total_width); (*testbench_load_cnt)++; } return; } } /* Get the mode_index */ if (NULL == src_pb) { mode_index = find_pb_type_physical_mode_index(*(src_pb_graph_pin->parent_node->pb_type)); } else { mode_index = src_pb->mode; } /* If this pb belongs to a pb_graph_head, * the src_pb_graph_pin is a OPIN, we should find the rr_node */ if ((OUT_PORT == src_pb_graph_pin->port->type) &&(NULL == src_pb_graph_pin->parent_node->parent_pb_graph_node)) { /* Find the corresponding rr_node */ assert(grid[grid_x][grid_y].type->pb_graph_head == src_pb_graph_pin->parent_node); src_rr_node_index = get_rr_node_index(grid_x, grid_y, OPIN, src_pb_graph_pin->pin_count_in_cluster, LL_rr_node_indices); for (iedge = 0; iedge < rr_node[src_rr_node_index].num_edges; iedge++) { /* Detect its input buffers */ load_inv_size = find_spice_testbench_rr_mux_load_inv_size(&rr_node[rr_node[src_rr_node_index].edges[iedge]], rr_node[src_rr_node_index].switches[iedge]); /* Print inverters by considering maximum width */ total_width = load_inv_size; width_cnt = 0; while (total_width > max_width_per_trans) { fprintf(fp, "Xload_inv[%d]_no%d %s load_inv[%d]_out gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, outport_name, (*testbench_load_cnt), max_width_per_trans); /* Update counter */ total_width = total_width - max_width_per_trans; width_cnt++; } if (total_width > 0) { fprintf(fp, "Xload_inv[%d]_no%d %s load_inv[%d]_out gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, outport_name, (*testbench_load_cnt), total_width); } (*testbench_load_cnt)++; } return; } /* Search output edges */ for (iedge = 0; iedge < src_pb_graph_pin->num_output_edges; iedge++) { check_pb_graph_edge(*(src_pb_graph_pin->output_edges[iedge])); /* We care only the edges in selected mode */ cur_interc = src_pb_graph_pin->output_edges[iedge]->interconnect; assert(NULL != cur_interc); if (mode_index == cur_interc->parent_mode_index) { rec_outport_name = (char*)my_malloc(sizeof(char)* (strlen(outport_name) + 5 + strlen(my_itoa(iedge)) +2 )); sprintf(rec_outport_name, "%s_out[%d]", outport_name, iedge); /* check the interc has spice_model and if it is buffered */ assert(NULL != cur_interc->spice_model); if (TRUE == cur_interc->spice_model->input_buffer->exist) { /* Print a inverter, and we stop this branch */ load_inv_size = find_spice_testbench_pb_pin_mux_load_inv_size(cur_interc->spice_model); /* Print inverters by considering maximum width */ total_width = load_inv_size; width_cnt = 0; while (total_width > max_width_per_trans) { fprintf(fp, "Xload_inv[%d]_no%d %s %s gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, outport_name, rec_outport_name, max_width_per_trans); /* Update counter */ total_width = total_width - max_width_per_trans; width_cnt++; } if (total_width > 0) { fprintf(fp, "Xload_inv[%d]_no%d %s %s gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, outport_name, rec_outport_name, total_width); } (*testbench_load_cnt)++; } else { /* fprintf(fp, "R%s_to_%s %s %s 0\n", outport_name, rec_outport_name, outport_name, rec_outport_name); */ /* Go recursively */ if (NULL == src_pb) { des_pb = NULL; } else { if (IN_PORT == src_pb_graph_pin->port->type) { ipb = src_pb_graph_pin->output_edges[iedge]->output_pins[0]->parent_node->pb_type - src_pb_graph_pin->parent_node->pb_type->modes[mode_index].pb_type_children; jpb = src_pb_graph_pin->output_edges[iedge]->output_pins[0]->parent_node->placement_index; if ((NULL != src_pb->child_pbs[ipb])&&(NULL != src_pb->child_pbs[ipb][jpb].name)) { des_pb = &(src_pb->child_pbs[ipb][jpb]); } else { des_pb = NULL; } } else if (OUT_PORT == src_pb_graph_pin->port->type) { des_pb = src_pb->parent_pb; } else if (INOUT_PORT == src_pb_graph_pin->port->type) { des_pb = NULL; /* I don't know what to do...*/ } } fprint_spice_testbench_pb_graph_pin_inv_loads_rec(fp, testbench_load_cnt, grid_x, grid_y, src_pb_graph_pin->output_edges[iedge]->output_pins[0], des_pb, outport_name, TRUE, LL_rr_node_indices); } } } return; } char* fprint_spice_testbench_rr_node_load_version(FILE* fp, int* testbench_load_cnt, int num_segments, t_segment_inf* segments, int load_level, t_rr_node cur_rr_node, char* outport_name) { char* ret_outport_name = NULL; char* mid_outport_name = NULL; int cost_index; int iseg, i, iedge, chan_wire_length, cur_x, cur_y; t_rr_node to_node; t_spice_model* wire_spice_model = NULL; float load_inv_size = 0.; float total_width; int width_cnt; /* We only process CHANX or CHANY*/ if (!((CHANX == cur_rr_node.type) ||(CHANY == cur_rr_node.type))) { ret_outport_name = my_strdup(outport_name); return ret_outport_name; } /* Important: * As the cur_rr_node can only be channel wires * and channel wires can only be driven at the starting point, * in this function, * the loads to be added will always starts from the starting point of a channel wire. * We will consider the length of channel wires when adding the loads. * For example, a length-4 wire will introduce 4 levels of channel segments to the loads. * To handle the corner case, we will consider the border when adding channel segments * If the length of wire exceeds the borderline of FPGA, * we will adapt the number of channel segments. */ cost_index = cur_rr_node.cost_index; iseg = rr_indexed_data[cost_index].seg_index; assert((!(iseg < 0))&&(iseg < num_segments)); wire_spice_model = segments[iseg].spice_model; assert(SPICE_MODEL_CHAN_WIRE == wire_spice_model->type); /* Check if the coordinate is correct */ assert((0 == cur_rr_node.xhigh - cur_rr_node.xlow) ||(0 == cur_rr_node.yhigh - cur_rr_node.ylow)); chan_wire_length = cur_rr_node.xhigh - cur_rr_node.xlow + cur_rr_node.yhigh - cur_rr_node.ylow; fprintf(fp, "**** Loads for rr_node: xlow=%d, ylow=%d, xhigh=%d, yhigh=%d, ptc_num=%d, type=%d *****\n", cur_rr_node.xlow, cur_rr_node.ylow, cur_rr_node.xhigh, cur_rr_node.yhigh, cur_rr_node.ptc_num, cur_rr_node.type); for (i = 0; i < chan_wire_length + 1; i++) { ret_outport_name = (char*)my_malloc(sizeof(char)*( strlen(outport_name) + 9 + strlen(my_itoa(load_level + i)) + 6 + 1 )); sprintf(ret_outport_name,"%s_loadlvl[%d]_out", outport_name, load_level + i); mid_outport_name = (char*)my_malloc(sizeof(char)*( strlen(outport_name) + 9 + strlen(my_itoa(load_level + i)) + 9 + 1 )); sprintf(mid_outport_name,"%s_loadlvl[%d]_midout", outport_name, load_level + i); if (0 == i) { fprintf(fp, "Xchan_%s %s %s %s gvdd_load 0 %s_seg%d\n", ret_outport_name, outport_name, ret_outport_name, mid_outport_name, wire_spice_model->name, iseg); } else { fprintf(fp, "Xchan_%s %s_loadlvl[%d]_out %s %s gvdd_load 0 %s_seg%d\n", ret_outport_name, outport_name, load_level + i -1, ret_outport_name, mid_outport_name, wire_spice_model->name, iseg); } /* Print CB inv loads connected to the mid_out */ switch (cur_rr_node.type) { case CHANX: /* Update the cur_x & cur_y */ if (INC_DIRECTION == cur_rr_node.direction) { cur_x = cur_rr_node.xlow + i; cur_y = cur_rr_node.ylow; } else { assert(DEC_DIRECTION == cur_rr_node.direction); cur_x = cur_rr_node.xhigh - i; cur_y = cur_rr_node.ylow; } for (iedge = 0; iedge < cur_rr_node.num_edges; iedge++) { /*Identify if the des node is a IPIN and fit the current(x,y)*/ to_node = rr_node[cur_rr_node.edges[iedge]]; switch (to_node.type) { case IPIN: /* The assert only works for homogeneous blocks assert(to_node.xhigh == to_node.xlow); assert(to_node.yhigh == to_node.ylow); */ if (((cur_x == to_node.xlow)&&(cur_y == to_node.ylow)) ||((cur_x == to_node.xlow)&&((cur_y + 1) == to_node.ylow))) { /* We find a CB! */ /* Detect its input buffers */ load_inv_size = find_spice_testbench_rr_mux_load_inv_size(&to_node, cur_rr_node.switches[iedge]); /* TODO: Need to find the downsteam inv_loads if it is not bufferred fprint_spice_testbench_pb_graph_pin_inv_loads_rec(fp, &testbench_load_cnt, x, y, &(cur_pb_graph_node->output_pins[0][0]), logical_block[logical_block_index].pb, outport_name, FALSE, LL_rr_node_indices); */ assert(0. < load_inv_size); /* Print an inverter */ total_width = load_inv_size; width_cnt = 0; while (total_width > max_width_per_trans) { fprintf(fp, "Xload_inv[%d]_no%d %s %s_out[%d] gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, mid_outport_name, mid_outport_name, iedge, max_width_per_trans); /* Update */ total_width = total_width - max_width_per_trans; width_cnt++; } if (total_width > 0) { fprintf(fp, "Xload_inv[%d]_no%d %s %s_out[%d] gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, mid_outport_name, mid_outport_name, iedge, total_width); } (*testbench_load_cnt)++; } break; case CHANX: case CHANY: /* We find a SB! */ /* Detect its input buffers */ load_inv_size = find_spice_testbench_rr_mux_load_inv_size(&to_node, cur_rr_node.switches[iedge]); assert(0. < load_inv_size); /* Print an inverter */ total_width = load_inv_size; width_cnt = 0; while (total_width > max_width_per_trans) { fprintf(fp, "Xload_inv[%d]_no%d %s %s_out[%d] gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, ret_outport_name, ret_outport_name, iedge, max_width_per_trans); /* Update */ total_width = total_width - max_width_per_trans; width_cnt++; } if (total_width > 0) { fprintf(fp, "Xload_inv[%d]_no%d %s %s_out[%d] gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, ret_outport_name, ret_outport_name, iedge, total_width); } (*testbench_load_cnt)++; break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid src_rr_node_type!\n", __FILE__, __LINE__); exit(1); } } break; case CHANY: /* Update the cur_x & cur_y */ if (INC_DIRECTION == cur_rr_node.direction) { cur_x = cur_rr_node.xlow; cur_y = cur_rr_node.ylow + i; } else { assert(DEC_DIRECTION == cur_rr_node.direction); cur_x = cur_rr_node.xlow; cur_y = cur_rr_node.yhigh - i; } for (iedge = 0; iedge < cur_rr_node.num_edges; iedge++) { /*Identify if the des node is a IPIN and fit the current(x,y)*/ to_node = rr_node[cur_rr_node.edges[iedge]]; switch (to_node.type) { case IPIN: /* The assert only works for homogeneous blocks assert(to_node.xhigh == to_node.xlow); assert(to_node.yhigh == to_node.ylow); */ if (((cur_y == to_node.ylow)&&(cur_x == to_node.xlow)) ||((cur_y == to_node.xlow)&&((cur_x + 1) == to_node.xlow))) { /* We find a CB! */ /* Detect its input buffers */ load_inv_size = find_spice_testbench_rr_mux_load_inv_size(&to_node, cur_rr_node.switches[iedge]); assert(0. < load_inv_size); /* Print an inverter */ total_width = load_inv_size; width_cnt = 0; while (total_width > max_width_per_trans) { fprintf(fp, "Xload_inv[%d]_no%d %s %s_out[%d] gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, mid_outport_name, mid_outport_name, iedge, max_width_per_trans); /* Update */ total_width = total_width - max_width_per_trans; width_cnt++; } if (total_width > 0) { fprintf(fp, "Xload_inv[%d]_no%d %s %s_out[%d] gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, mid_outport_name, mid_outport_name, iedge, total_width); } (*testbench_load_cnt)++; } break; case CHANX: case CHANY: /* We find a SB! */ /* Detect its input buffers */ load_inv_size = find_spice_testbench_rr_mux_load_inv_size(&to_node, cur_rr_node.switches[iedge]); assert(0. < load_inv_size); /* Print an inverter */ total_width = load_inv_size; width_cnt = 0; while (total_width > max_width_per_trans) { fprintf(fp, "Xload_inv[%d]_no%d %s %s_out[%d] gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, ret_outport_name, ret_outport_name, iedge, total_width); /* Update */ total_width = total_width - max_width_per_trans; width_cnt++; } if (total_width > 0) { fprintf(fp, "Xload_inv[%d]_no%d %s %s_out[%d] gvdd_load 0 inv size=%g\n", (*testbench_load_cnt), width_cnt, ret_outport_name, ret_outport_name, iedge, total_width); } (*testbench_load_cnt)++; break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid src_rr_node_type!\n", __FILE__, __LINE__); exit(1); } } break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid src_rr_node_type!\n", __FILE__, __LINE__); exit(1); } } return ret_outport_name; } void fprint_spice_testbench_one_cb_mux_loads(FILE* fp, int* testbench_load_cnt, t_rr_node* src_rr_node, char* outport_name, t_ivec*** LL_rr_node_indices) { t_type_ptr cb_out_grid_type = NULL; t_pb_graph_pin* cb_out_pb_graph_pin = NULL; t_phy_pb* cb_out_pb = NULL; assert(IPIN == src_rr_node->type); /* The assert only works for homogeneous blocks assert(src_rr_node->xlow == src_rr_node->xhigh); assert(src_rr_node->ylow == src_rr_node->yhigh); */ cb_out_grid_type = grid[src_rr_node->xlow][src_rr_node->ylow].type; assert(NULL != cb_out_grid_type); cb_out_pb_graph_pin = src_rr_node->pb_graph_pin; assert(NULL != cb_out_pb_graph_pin); /* Get the pb ! Get the mode_index */ if (NULL != src_rr_node->pb) { cb_out_pb = (t_phy_pb*)(src_rr_node->pb->phy_pb); } if (IO_TYPE == cb_out_grid_type) { fprintf(fp, "******* IO_TYPE loads *******\n"); } else { fprintf(fp, "******* Normal TYPE loads *******\n"); } /* Recursively find all the inv load inside pb_graph_node */ fprint_spice_testbench_pb_graph_pin_inv_loads_rec(fp, testbench_load_cnt, src_rr_node->xlow, src_rr_node->ylow, cb_out_pb_graph_pin, cb_out_pb, outport_name, TRUE, LL_rr_node_indices); fprintf(fp, "******* END loads *******\n"); return; } void fprint_spice_testbench_one_grid_pin_stimulation(FILE* fp, int x, int y, int height, int side, int ipin, t_ivec*** LL_rr_node_indices) { int ipin_rr_node_index; float ipin_density, ipin_probability; int ipin_init_value; int class_id; t_rr_type grid_pin_rr_node_type; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > x))&&(!(x > (nx + 1)))); assert((!(0 > y))&&(!(y > (ny + 1)))); /* Identify the type of rr_node */ class_id = grid[x][y].type->pin_class[ipin]; if (DRIVER == grid[x][y].type->class_inf[class_id].type) { grid_pin_rr_node_type = OPIN; } else if (RECEIVER == grid[x][y].type->class_inf[class_id].type) { grid_pin_rr_node_type = IPIN; } else { /* Error */ vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Unknown pin type for grid(x=%d, y=%d, pin_index=%d)!\n", __FILE__, __LINE__, x, y, ipin); exit(1); } /* Print a voltage source according to density and probability */ ipin_rr_node_index = get_rr_node_index(x, y, grid_pin_rr_node_type, ipin, LL_rr_node_indices); /* Get density and probability */ ipin_density = get_rr_node_net_density(rr_node[ipin_rr_node_index]); ipin_probability = get_rr_node_net_probability(rr_node[ipin_rr_node_index]); ipin_init_value = get_rr_node_net_init_value(rr_node[ipin_rr_node_index]); /* Print voltage source */ fprintf(fp, "Vgrid[%d][%d]_pin[%d][%d][%d] grid[%d][%d]_pin[%d][%d][%d] 0 \n", x, y, height, side, ipin, x, y, height, side, ipin); fprint_voltage_pulse_params(fp, ipin_init_value, ipin_density, ipin_probability); return; } void fprint_spice_testbench_one_grid_pin_loads(FILE* fp, int x, int y, int height, int side, int ipin, int* testbench_load_cnt, t_ivec*** LL_rr_node_indices) { int ipin_rr_node_index; int iedge, iswitch; char* prefix = NULL; t_spice_model* switch_spice_model = NULL; int inv_cnt = 0; int class_id; t_rr_type grid_pin_rr_node_type; if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > x))&&(!(x > (nx + 1)))); assert((!(0 > y))&&(!(y > (ny + 1)))); /* Identify the type of rr_node */ class_id = grid[x][y].type->pin_class[ipin]; if (DRIVER == grid[x][y].type->class_inf[class_id].type) { grid_pin_rr_node_type = OPIN; } else if (RECEIVER == grid[x][y].type->class_inf[class_id].type) { grid_pin_rr_node_type = IPIN; } else { /* Error */ vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Unknown pin type for grid(x=%d, y=%d, pin_index=%d)!\n", __FILE__, __LINE__, x, y, ipin); exit(1); } /* Print a voltage source according to density and probability */ ipin_rr_node_index = get_rr_node_index(x, y, grid_pin_rr_node_type, ipin, LL_rr_node_indices); /* Generate prefix */ prefix = (char*)my_malloc(sizeof(char)*(5 + strlen(my_itoa(x)) + 2 + strlen(my_itoa(y)) + 6 + strlen(my_itoa(height)) + 2 + strlen(my_itoa(side)) + 2 + strlen(my_itoa(ipin)) + 2 + 1)); sprintf(prefix, "grid[%d][%d]_pin[%d][%d][%d]", x, y, height, side, ipin); /* Depending on the type of this pin */ /* For OPIN, we search the rr_node graph */ if (OPIN == grid_pin_rr_node_type) { /* Print all the inverter load now*/ for (iedge = 0; iedge < rr_node[ipin_rr_node_index].num_edges; iedge++) { /* Get the switch spice model */ /* inode = rr_node[ipin_rr_node_index].edges[iedge]; */ iswitch = rr_node[ipin_rr_node_index].switches[iedge]; switch_spice_model = switch_inf[iswitch].spice_model; if (NULL == switch_spice_model) { continue; } /* Add inv/buf here */ fprintf(fp, "X%s_inv[%d] %s %s_out[%d] gvdd_load 0 inv size=%g\n", prefix, iedge, prefix, prefix, iedge, switch_spice_model->input_buffer->size); inv_cnt++; } /* TODO: go recursively ? */ /* For IPIN, we should search the internal rr_graph of the grid */ } else if (IPIN == grid_pin_rr_node_type) { fprint_spice_testbench_one_cb_mux_loads(fp, testbench_load_cnt, &rr_node[ipin_rr_node_index], prefix, LL_rr_node_indices); } else { /* Error */ vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Unknown pin type for grid(x=%d, y=%d, pin_index=%d)!\n", __FILE__, __LINE__, x, y, ipin); exit(1); } /* TODO: Generate loads recursively */ /*fprint_rr_node_loads_rec(fp, rr_node[ipin_rr_node_index],prefix);*/ /*Free */ my_free(prefix); return; } /* Add one SPICE TB information to linked list */ t_llist* add_one_spice_tb_info_to_llist(t_llist* cur_head, char* tb_file_path, int num_sim_clock_cycles) { t_llist* new_head = NULL; if (NULL == cur_head) { new_head = create_llist(1); new_head->dptr = my_malloc(sizeof(t_spicetb_info)); ((t_spicetb_info*)(new_head->dptr))->tb_name = my_strdup(tb_file_path); ((t_spicetb_info*)(new_head->dptr))->num_sim_clock_cycles = num_sim_clock_cycles; } else { new_head = insert_llist_node_before_head(cur_head); new_head->dptr = my_malloc(sizeof(t_spicetb_info)); ((t_spicetb_info*)(new_head->dptr))->tb_name = my_strdup(tb_file_path); ((t_spicetb_info*)(new_head->dptr))->num_sim_clock_cycles = num_sim_clock_cycles; } return new_head; } char* convert_const_input_value_to_str(int const_input_val) { switch (const_input_val) { case 0: return "sgnd"; case 1: return "svdd"; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid value for constant input (=%d).\n", __FILE__, __LINE__, const_input_val); exit(1); } }