/***********************************/ /* 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 spice support headers*/ #include "linkedlist.h" #include "fpga_spice_globals.h" #include "spice_globals.h" #include "fpga_spice_utils.h" #include "spice_utils.h" #include "spice_mux.h" #include "spice_pbtypes.h" #include "spice_subckt.h" /* local global variables */ static int tb_num_hardlogic = 0; static int testbench_load_cnt = 0; static int upbound_sim_num_clock_cycles = 2; static int max_sim_num_clock_cycles = 2; static int auto_select_max_sim_num_clock_cycles = TRUE; /* Subroutines in this source file*/ /* Initialize the global parameters in this source file */ static void init_spice_hardlogic_testbench_globals(t_spice spice) { tb_num_hardlogic = 0; auto_select_max_sim_num_clock_cycles = spice.spice_params.meas_params.auto_select_sim_num_clk_cycle; upbound_sim_num_clock_cycles = spice.spice_params.meas_params.sim_num_clock_cycle + 1; if (FALSE == auto_select_max_sim_num_clock_cycles) { max_sim_num_clock_cycles = spice.spice_params.meas_params.sim_num_clock_cycle + 1; } else { max_sim_num_clock_cycles = 2; } } /* Print Common global ports in the testbench */ static void fprint_spice_hardlogic_testbench_global_ports(FILE* fp, int grid_x, int grid_y, int num_clock, 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); } /* Global nodes: Vdd for SRAMs, Logic Blocks(Include IO), Switch Boxes, Connection Boxes */ /* Print generic global ports*/ fprint_spice_generic_testbench_global_ports(fp, sram_spice_orgz_info, global_ports_head); /* VDD Load port name */ fprintf(fp, ".global %s\n", spice_tb_global_vdd_load_port_name); /*Global Vdds for FFs: TODO: TO BE REMOVED */ fprint_grid_global_vdds_spice_model(fp, grid_x, grid_y, SPICE_MODEL_FF, spice); /*Global Vdds for hardlogic */ fprint_grid_global_vdds_spice_model(fp, grid_x, grid_y, SPICE_MODEL_HARDLOGIC, spice); /*Global Vdds for IOPADs (TODO: TO BE MOVED TO IO_TB SOURCE FILE */ fprint_grid_global_vdds_spice_model(fp, grid_x, grid_y, SPICE_MODEL_IOPAD, spice); /* Global VDDs for SRAMs of IOPADs */ fprintf(fp, ".global %s\n", spice_tb_global_vdd_io_sram_port_name); return; } /* Dump the subckt of a hardlogic and also the input stimuli */ void fprint_spice_hardlogic_testbench_one_hardlogic(FILE* fp, char* subckt_name, t_spice_model* hardlogic_spice_model) { int iport, ipin; int num_input_port = 0; t_spice_model_port** input_ports = NULL; int num_output_port = 0; t_spice_model_port** output_ports = NULL; int num_inout_port = 0; t_spice_model_port** inout_ports = NULL; int num_clk_port = 0; t_spice_model_port** clk_ports = NULL; /* A valid file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!\n",__FILE__, __LINE__); exit(1); } /* Check */ assert(NULL != hardlogic_spice_model); /* identify the type of spice model */ /* Call defined subckt */ fprintf(fp, "Xhardlogic_%s[%d] \n", hardlogic_spice_model->prefix, hardlogic_spice_model->tb_cnt); /* Sequence in dumping ports: * 1. Global ports * 2. Input ports * 3. Output ports * 4. Inout ports * 5. Configuration ports * 6. VDD and GND ports */ /* 1. Global ports */ if (0 < rec_fprint_spice_model_global_ports(fp, hardlogic_spice_model, FALSE)) { fprintf(fp, "+ "); } /* 2. Input ports (TODO: check the number of inputs matches the spice model definition) */ /* Find pb_type input ports */ input_ports = find_spice_model_ports(hardlogic_spice_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE); for (iport = 0; iport < num_input_port; iport++) { for (ipin = 0; ipin < input_ports[iport]->size; ipin++) { fprintf(fp, "hardlogic_%s[%d]->%s[%d] ", hardlogic_spice_model->prefix, hardlogic_spice_model->tb_cnt, input_ports[iport]->prefix, ipin); } } if (NULL != input_ports) { fprintf(fp, "\n"); fprintf(fp, "+ "); } /* 3. Output ports */ /* Find pb_type output ports */ output_ports = find_spice_model_ports(hardlogic_spice_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE); for (iport = 0; iport < num_output_port; iport++) { for (ipin = 0; ipin < output_ports[iport]->size; ipin++) { fprintf(fp, "hardlogic_%s[%d]->%s[%d] ", hardlogic_spice_model->prefix, hardlogic_spice_model->tb_cnt, output_ports[iport]->prefix, ipin); } } if (NULL != output_ports) { fprintf(fp, "\n"); fprintf(fp, "+ "); } /* 4. Inout ports */ /* INOUT ports */ /* Find pb_type inout ports */ inout_ports = find_spice_model_ports(hardlogic_spice_model, SPICE_MODEL_PORT_INOUT, &num_inout_port, TRUE); for (iport = 0; iport < num_inout_port; iport++) { for (ipin = 0; ipin < inout_ports[iport]->size; ipin++) { fprintf(fp, "hardlogic_%s[%d]->%s[%d] ", hardlogic_spice_model->prefix, hardlogic_spice_model->tb_cnt, inout_ports[iport]->prefix, ipin); } } if (NULL != inout_ports) { fprintf(fp, "\n"); fprintf(fp, "+ "); } /* Clocks */ /* Identify if the clock port is a global signal */ /* Find pb_type clock ports */ clk_ports = find_spice_model_ports(hardlogic_spice_model, SPICE_MODEL_PORT_CLOCK, &num_clk_port, TRUE); for (iport = 0; iport < num_clk_port; iport++) { for (ipin = 0; ipin < clk_ports[iport]->size; ipin++) { fprintf(fp, "hardlogic_%s[%d]->%s[%d] ", hardlogic_spice_model->prefix, hardlogic_spice_model->tb_cnt, clk_ports[iport]->prefix, ipin); } } if (NULL != clk_ports) { fprintf(fp, "\n"); fprintf(fp, "+ "); } /* 5. Configuration ports */ /* Generate SRAMs? */ /* 6. VDD and GND ports */ fprintf(fp, "%s_%s[%d] %s ", spice_tb_global_vdd_port_name, hardlogic_spice_model->prefix, hardlogic_spice_model->tb_cnt, spice_tb_global_gnd_port_name); fprintf(fp, "\n"); fprintf(fp, "+ "); /* Call the name of subckt */ fprintf(fp, "%s\n", hardlogic_spice_model->name); /* Free */ my_free(input_ports); my_free(output_ports); my_free(inout_ports); my_free(clk_ports); return; } void fprint_spice_hardlogic_testbench_one_pb_graph_node_hardlogic(FILE* fp, t_pb_graph_node* cur_pb_graph_node, char* prefix, int x, int y, t_ivec*** LL_rr_node_indices) { int logical_block_index = OPEN; t_spice_model* pb_spice_model = NULL; t_pb_type* cur_pb_type = NULL; int iport, ipin; /* For pb_spice_model */ int num_input_port; t_spice_model_port** input_ports; int num_output_port; t_spice_model_port** output_ports; /* Two-dimension arrays, corresponding to the port map [port_id][pin_id] */ float** input_density = NULL; float** input_probability = NULL; int** input_init_value = NULL; int** input_net_num = NULL; char* outport_name = NULL; t_rr_node* local_rr_graph = NULL; float average_density = 0.; int avg_density_cnt = 0; int num_sim_clock_cycles = 0; assert(NULL != cur_pb_graph_node); assert(NULL != prefix); cur_pb_type = cur_pb_graph_node->pb_type; assert(NULL != cur_pb_type); pb_spice_model = cur_pb_type->spice_model; /* Just a double check*/ if ((SPICE_MODEL_HARDLOGIC != pb_spice_model->type) &&(SPICE_MODEL_FF != pb_spice_model->type)) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d]) Type of SPICE models should be either Flip-Flop or Hard Logic!\n", __FILE__, __LINE__); exit(1); } /* Try to find the mapped logic block index */ logical_block_index = find_grid_mapped_logical_block(x, y, pb_spice_model, prefix); /* UNCOMMENT THIS, IF YOU DO NOT WANT SIMULATE THE IDLE ELEMENTS if (OPEN == logical_block_index) { return; } */ /* Call the subckt and give stimulates, measurements */ if (OPEN != logical_block_index) { fprintf(fp,"***** Hardlogic[%d]: logical_block_index[%d], gvdd_index[%d]*****\n", pb_spice_model->cnt, logical_block_index, logical_block[logical_block_index].mapped_spice_model_index); } else { fprintf(fp,"***** Hardlogic[%d]: logical_block_index[%d], gvdd_index[%d]*****\n", pb_spice_model->cnt, -1, -1); } /* Now, we print the SPICE subckt of a hard logic */ fprint_spice_hardlogic_testbench_one_hardlogic(fp, prefix, pb_spice_model); /* Malloc */ /* First dimension */ input_density = (float**)my_malloc(sizeof(float*) * cur_pb_graph_node->num_input_ports); input_probability = (float**)my_malloc(sizeof(float*) * cur_pb_graph_node->num_input_ports); input_init_value = (int**)my_malloc(sizeof(int*) * cur_pb_graph_node->num_input_ports); input_net_num = (int**)my_malloc(sizeof(int*) * cur_pb_graph_node->num_input_ports); /* Second dimension */ for (iport = 0; iport < cur_pb_graph_node->num_input_ports; iport++) { input_density[iport] = (float*)my_malloc(sizeof(float) * cur_pb_graph_node->num_input_pins[iport]); input_probability[iport] = (float*)my_malloc(sizeof(float) * cur_pb_graph_node->num_input_pins[iport]); input_init_value[iport] = (int*)my_malloc(sizeof(int) * cur_pb_graph_node->num_input_pins[iport]); input_net_num[iport] = (int*)my_malloc(sizeof(int) * cur_pb_graph_node->num_input_pins[iport]); } /* Get activity information */ for (iport = 0; iport < cur_pb_graph_node->num_input_ports; iport++) { for (ipin = 0; ipin < cur_pb_graph_node->num_input_pins[iport]; ipin++) { /* if we find a mapped logic block */ if (OPEN != logical_block_index) { local_rr_graph = logical_block[logical_block_index].pb->parent_pb->rr_graph; } else { local_rr_graph = NULL; } input_net_num[iport][ipin] = pb_pin_net_num(local_rr_graph, &(cur_pb_graph_node->input_pins[iport][ipin])); input_density[iport][ipin] = pb_pin_density(local_rr_graph, &(cur_pb_graph_node->input_pins[iport][ipin])); input_probability[iport][ipin] = pb_pin_probability(local_rr_graph, &(cur_pb_graph_node->input_pins[iport][ipin])); input_init_value[iport][ipin] = pb_pin_init_value(local_rr_graph, &(cur_pb_graph_node->input_pins[iport][ipin])); } } /* Add Input stimulates */ /* Get the input port list of spice model */ input_ports = find_spice_model_ports(pb_spice_model, SPICE_MODEL_PORT_INPUT, &num_input_port, TRUE); /* Check if the port map of current pb_graph_node matches that of the spice model !!!*/ assert(num_input_port == cur_pb_graph_node->num_input_ports); for (iport = 0; iport < num_input_port; iport++) { assert(input_ports[iport]->size == cur_pb_graph_node->num_input_pins[iport]); for (ipin = 0; ipin < input_ports[iport]->size; ipin++) { /* Check the port size should match!*/ fprintf(fp, "Vhardlogic_%s[%d]->%s[%d] hardlogic_%s[%d]->%s[%d] 0 \n", pb_spice_model->prefix, pb_spice_model->tb_cnt, cur_pb_graph_node->input_pins[iport]->port->name, ipin, pb_spice_model->prefix, pb_spice_model->tb_cnt, input_ports[iport]->prefix, ipin); fprint_voltage_pulse_params(fp, input_init_value[iport][ipin], input_density[iport][ipin], input_probability[iport][ipin]); } } /* Add loads: Recursively */ /* Get the output port list of spice model */ output_ports = find_spice_model_ports(pb_spice_model, SPICE_MODEL_PORT_OUTPUT, &num_output_port, TRUE); for (iport = 0; iport < num_output_port; iport++) { for (ipin = 0; ipin < output_ports[iport]->size; ipin++) { outport_name = (char*)my_malloc(sizeof(char)*( 10 + + strlen(pb_spice_model->prefix) + 1 + strlen(my_itoa(pb_spice_model->tb_cnt)) + 3 + strlen(output_ports[iport]->prefix) + 1 + strlen(my_itoa(ipin)) + 2 )); sprintf(outport_name, "hardlogic_%s[%d]->%s[%d]", pb_spice_model->prefix, pb_spice_model->tb_cnt, output_ports[iport]->prefix, ipin); if (TRUE == run_testbench_load_extraction) { /* Additional switch, default on! */ if (OPEN != logical_block_index) { 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); } else { fprint_spice_testbench_pb_graph_pin_inv_loads_rec(fp, &testbench_load_cnt, x, y, &(cur_pb_graph_node->output_pins[0][0]), NULL, outport_name, FALSE, LL_rr_node_indices); } } /* Free outport_name in each iteration */ my_free(outport_name); } } /* Calculate average density of this hardlogic */ average_density = 0.; avg_density_cnt = 0; for (iport = 0; iport < cur_pb_graph_node->num_input_ports; iport++) { for (ipin = 0; ipin < cur_pb_graph_node->num_input_pins[iport]; ipin++) { assert(!(0 > input_density[iport][ipin])); if (0. < input_density[iport][ipin]) { average_density += input_density[iport][ipin]; avg_density_cnt++; } } } /* Calculate the num_sim_clock_cycle for this MUX, update global max_sim_clock_cycle in this testbench */ if (0 < avg_density_cnt) { average_density = average_density/avg_density_cnt; } else { assert(0 == avg_density_cnt); average_density = 0.; } if (0. == average_density) { num_sim_clock_cycles = 2; } else { assert(0. < average_density); num_sim_clock_cycles = (int)(1/average_density) + 1; } if (TRUE == auto_select_max_sim_num_clock_cycles) { /* for idle blocks, 2 clock cycle is well enough... */ if (2 < num_sim_clock_cycles) { num_sim_clock_cycles = upbound_sim_num_clock_cycles; } else { num_sim_clock_cycles = 2; } if (max_sim_num_clock_cycles < num_sim_clock_cycles) { max_sim_num_clock_cycles = num_sim_clock_cycles; } } else { num_sim_clock_cycles = max_sim_num_clock_cycles; } /* Mark temporary used */ if (OPEN != logical_block_index) { logical_block[logical_block_index].temp_used = 1; } /* Increment the counter of the hardlogic spice model */ pb_spice_model->tb_cnt++; tb_num_hardlogic++; /* Free */ for (iport = 0; iport < cur_pb_graph_node->num_input_ports; iport++) { my_free(input_net_num[iport]); my_free(input_init_value[iport]); my_free(input_density[iport]); my_free(input_probability[iport]); } my_free(input_net_num); my_free(input_init_value); my_free(input_density); my_free(input_probability); my_free(input_ports); my_free(output_ports); return; } void fprint_spice_hardlogic_testbench_rec_pb_graph_node_hardlogics(FILE* fp, t_pb_graph_node* cur_pb_graph_node, char* prefix, int x, int y, t_ivec*** LL_rr_node_indices) { char* formatted_prefix = format_spice_node_prefix(prefix); int ipb, jpb, mode_index; t_pb_type* cur_pb_type = NULL; char* rec_prefix = NULL; assert(NULL != cur_pb_graph_node); cur_pb_type = cur_pb_graph_node->pb_type; assert(NULL != cur_pb_type); /* Until we reach a FF */ if (NULL != cur_pb_type->spice_model) { if ((SPICE_MODEL_FF != cur_pb_type->spice_model->type) &&(SPICE_MODEL_HARDLOGIC != cur_pb_type->spice_model->type)) { return; } /* Generate rec_prefix */ rec_prefix = (char*)my_malloc(sizeof(char) * (strlen(formatted_prefix) + strlen(cur_pb_type->name) + 1 + strlen(my_itoa(cur_pb_graph_node->placement_index)) + 1 + 1)); sprintf(rec_prefix, "%s%s[%d]", formatted_prefix, cur_pb_type->name, cur_pb_graph_node->placement_index); /* Print a hardlogic tb: call spice_model, stimulates */ fprint_spice_hardlogic_testbench_one_pb_graph_node_hardlogic(fp, cur_pb_graph_node, rec_prefix, x, y, LL_rr_node_indices); my_free(rec_prefix); return; } /* Go recursively ... */ mode_index = find_pb_type_idle_mode_index(*(cur_pb_type)); for (ipb = 0; ipb < cur_pb_type->modes[mode_index].num_pb_type_children; ipb++) { for (jpb = 0; jpb < cur_pb_type->modes[mode_index].pb_type_children[ipb].num_pb; jpb++) { /* Generate rec_prefix */ rec_prefix = (char*)my_malloc(sizeof(char) * (strlen(formatted_prefix) + strlen(cur_pb_type->name) + 1 + strlen(my_itoa(cur_pb_graph_node->placement_index)) + 7 + strlen(cur_pb_type->modes[mode_index].name) + 1 + 1)); sprintf(rec_prefix, "%s%s[%d]_mode[%s]", formatted_prefix, cur_pb_type->name, cur_pb_graph_node->placement_index, cur_pb_type->modes[mode_index].name); /* Go recursively */ fprint_spice_hardlogic_testbench_rec_pb_graph_node_hardlogics(fp, &(cur_pb_graph_node->child_pb_graph_nodes[mode_index][ipb][jpb]), rec_prefix, x, y, LL_rr_node_indices); my_free(rec_prefix); } } return; } void fprint_spice_hardlogic_testbench_rec_pb_hardlogics(FILE* fp, t_pb* cur_pb, char* prefix, int x, int y, t_ivec*** LL_rr_node_indices) { char* formatted_prefix = format_spice_node_prefix(prefix); int ipb, jpb; int mode_index; char* rec_prefix = 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); } /* Check */ assert(NULL != cur_pb); /* If we touch the leaf, there is no need print interc*/ if (NULL != cur_pb->pb_graph_node->pb_type->spice_model) { if ((SPICE_MODEL_HARDLOGIC != cur_pb->pb_graph_node->pb_type->spice_model->type) &&(SPICE_MODEL_FF != cur_pb->pb_graph_node->pb_type->spice_model->type)) { return; } /* Generate rec_prefix */ rec_prefix = (char*)my_malloc(sizeof(char) * (strlen(formatted_prefix) + strlen(cur_pb->pb_graph_node->pb_type->name) + 1 + strlen(my_itoa(cur_pb->pb_graph_node->placement_index)) + 1 + 1)); sprintf(rec_prefix, "%s%s[%d]", formatted_prefix, cur_pb->pb_graph_node->pb_type->name, cur_pb->pb_graph_node->placement_index); /* Print a lut tb: call spice_model, stimulates */ fprint_spice_hardlogic_testbench_one_pb_graph_node_hardlogic(fp, cur_pb->pb_graph_node, rec_prefix, x, y, LL_rr_node_indices); my_free(rec_prefix); return; } /* Go recursively ... */ mode_index = cur_pb->mode; if (!(0 < cur_pb->pb_graph_node->pb_type->num_modes)) { return; } for (ipb = 0; ipb < cur_pb->pb_graph_node->pb_type->modes[mode_index].num_pb_type_children; ipb++) { for (jpb = 0; jpb < cur_pb->pb_graph_node->pb_type->modes[mode_index].pb_type_children[ipb].num_pb; jpb++) { /* Generate rec_prefix */ rec_prefix = (char*)my_malloc(sizeof(char) * (strlen(formatted_prefix) + strlen(cur_pb->pb_graph_node->pb_type->name) + 1 + strlen(my_itoa(cur_pb->pb_graph_node->placement_index)) + 7 + strlen(cur_pb->pb_graph_node->pb_type->modes[mode_index].name) + 1 + 1)); sprintf(rec_prefix, "%s%s[%d]_mode[%s]", formatted_prefix, cur_pb->pb_graph_node->pb_type->name, cur_pb->pb_graph_node->placement_index, cur_pb->pb_graph_node->pb_type->modes[mode_index].name); /* Refer to pack/output_clustering.c [LINE 392] */ if ((NULL != cur_pb->child_pbs[ipb])&&(NULL != cur_pb->child_pbs[ipb][jpb].name)) { fprint_spice_hardlogic_testbench_rec_pb_hardlogics(fp, &(cur_pb->child_pbs[ipb][jpb]), rec_prefix, x, y, LL_rr_node_indices); } else { /* Then we go on */ fprint_spice_hardlogic_testbench_rec_pb_graph_node_hardlogics(fp, cur_pb->child_pbs[ipb][jpb].pb_graph_node, rec_prefix, x, y, LL_rr_node_indices); } } } return; } void fprint_spice_hardlogic_testbench_call_one_grid_defined_hardlogics(FILE* fp, int ix, int iy, t_ivec*** LL_rr_node_indices) { int iblk; char* prefix = NULL; if ((NULL == grid[ix][iy].type) ||(EMPTY_TYPE == grid[ix][iy].type) ||(0 != grid[ix][iy].offset)) { return; } if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__); exit(1); } for (iblk = 0; iblk < grid[ix][iy].usage; iblk++) { prefix = (char*)my_malloc(sizeof(char)* (5 + strlen(my_itoa(block[grid[ix][iy].blocks[iblk]].x)) + 2 + strlen(my_itoa(block[grid[ix][iy].blocks[iblk]].y)) + 3 )); sprintf(prefix, "grid[%d][%d]_", block[grid[ix][iy].blocks[iblk]].x, block[grid[ix][iy].blocks[iblk]].y); /* Only for mapped block */ assert(NULL != block[grid[ix][iy].blocks[iblk]].pb); /* Mark the temporary net_num for the type pins*/ mark_one_pb_parasitic_nets(block[grid[ix][iy].blocks[iblk]].pb); /* Go into the hierachy and dump hardlogics */ fprint_spice_hardlogic_testbench_rec_pb_hardlogics(fp, block[grid[ix][iy].blocks[iblk]].pb, prefix, ix, iy, LL_rr_node_indices); /* Free */ my_free(prefix); } /* Bypass unused blocks */ for (iblk = grid[ix][iy].usage; iblk < grid[ix][iy].type->capacity; iblk++) { prefix = (char*)my_malloc(sizeof(char)* (5 + strlen(my_itoa(ix)) + 2 + strlen(my_itoa(iy)) + 3 )); sprintf(prefix, "grid[%d][%d]_", ix, iy); assert(NULL != grid[ix][iy].type->pb_graph_head); /* Mark the temporary net_num for the type pins*/ mark_grid_type_pb_graph_node_pins_temp_net_num(ix, iy); /* Go into the hierachy and dump hardlogics */ fprint_spice_hardlogic_testbench_rec_pb_graph_node_hardlogics(fp, grid[ix][iy].type->pb_graph_head, prefix, ix, iy, LL_rr_node_indices); /* Free */ my_free(prefix); } return; } void fprint_spice_hardlogic_testbench_call_defined_hardlogics(FILE* fp, t_ivec*** LL_rr_node_indices) { int ix, iy; for (ix = 1; ix < (nx + 1); ix++) { for (iy = 1; iy < (ny + 1); iy++) { fprint_spice_hardlogic_testbench_call_one_grid_defined_hardlogics(fp, ix, iy, LL_rr_node_indices); } } return; } static void fprint_spice_hardlogic_testbench_stimulations(FILE* fp, int grid_x, int grid_y, int num_clocks, t_spice spice, t_ivec*** LL_rr_node_indices) { /* Print generic stimuli */ fprint_spice_testbench_generic_global_ports_stimuli(fp, num_clocks); /* Generate global ports stimuli */ fprint_spice_testbench_global_ports_stimuli(fp, global_ports_head); /* SRAM ports */ fprintf(fp, "***** Global Inputs for SRAMs *****\n"); fprint_spice_testbench_global_sram_inport_stimuli(fp, sram_spice_orgz_info); fprintf(fp, "***** Global VDD for SRAMs *****\n"); fprint_spice_testbench_global_vdd_port_stimuli(fp, spice_tb_global_vdd_sram_port_name, "vsp"); fprintf(fp, "***** Global VDD for load inverters *****\n"); fprint_spice_testbench_global_vdd_port_stimuli(fp, spice_tb_global_vdd_load_port_name, "vsp"); /* fprintf(fp, "***** Global VDD for IOPADs *****\n"); fprint_spice_testbench_global_vdd_port_stimuli(fp, spice_tb_global_vdd_io_port_name, "vsp"); fprintf(fp, "***** Global VDD for IOPAD SRAMs *****\n"); fprint_spice_testbench_global_vdd_port_stimuli(fp, spice_tb_global_vdd_io_sram_port_name, "vsp"); */ /* Every LUT use an independent Voltage source */ fprintf(fp, "***** Global VDD for FFs *****\n"); fprint_grid_splited_vdds_spice_model(fp, grid_x, grid_y, SPICE_MODEL_FF, spice); fprintf(fp, "***** Global VDD for Hardlogics *****\n"); fprint_grid_splited_vdds_spice_model(fp, grid_x, grid_y, SPICE_MODEL_HARDLOGIC, spice); return; } void fprint_spice_hardlogic_testbench_measurements(FILE* fp, int grid_x, int grid_y, t_spice spice, boolean leakage_only) { /* int i; */ /* First cycle reserved for measuring leakage */ int num_clock_cycle = max_sim_num_clock_cycles; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } fprint_spice_netlist_transient_setting(fp, spice, num_clock_cycle, leakage_only); fprint_spice_netlist_generic_measurements(fp, spice.spice_params.mc_params, spice.num_spice_model, spice.spice_models); /* TODO: Measure the delay of each mapped net and logical block */ /* Measure the power */ /* Leakage ( the first cycle is reserved for leakage measurement) */ /* Leakage power of FFs*/ fprint_measure_grid_vdds_spice_model(fp, grid_x, grid_y, SPICE_MODEL_FF, SPICE_MEASURE_LEAKAGE_POWER, num_clock_cycle, spice, leakage_only); /* Leakage power of Hardlogic */ fprint_measure_grid_vdds_spice_model(fp, grid_x, grid_y, SPICE_MODEL_HARDLOGIC, SPICE_MEASURE_LEAKAGE_POWER, num_clock_cycle, spice, leakage_only); if (TRUE == leakage_only) { return; } /* Dynamic power */ /* Dynamic power of FFs */ fprint_measure_grid_vdds_spice_model(fp, grid_x, grid_y, SPICE_MODEL_FF, SPICE_MEASURE_DYNAMIC_POWER, num_clock_cycle, spice, leakage_only); /* Dynamic power of Hardlogics */ fprint_measure_grid_vdds_spice_model(fp, grid_x, grid_y, SPICE_MODEL_HARDLOGIC, SPICE_MEASURE_DYNAMIC_POWER, num_clock_cycle, spice, leakage_only); return; } /* Top-level function in this source file */ int fprint_spice_one_hardlogic_testbench(char* formatted_spice_dir, char* circuit_name, char* hardlogic_testbench_name, char* include_dir_path, char* subckt_dir_path, t_ivec*** LL_rr_node_indices, int num_clock, t_arch arch, int grid_x, int grid_y, boolean leakage_only) { FILE* fp = NULL; char* formatted_subckt_dir_path = format_dir_path(subckt_dir_path); char* temp_include_file_path = NULL; char* title = my_strcat("FPGA Hard Logic Testbench for Design: ", circuit_name); char* hardlogic_testbench_file_path = my_strcat(formatted_spice_dir, hardlogic_testbench_name); int used; /* Check if the path exists*/ fp = fopen(hardlogic_testbench_file_path,"w"); if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create DFF Testbench SPICE netlist %s!",__FILE__, __LINE__, hardlogic_testbench_file_path); exit(1); } /* Reset tb_cnt for all the spice models */ init_spice_models_grid_tb_cnt(arch.spice->num_spice_model, arch.spice->spice_models, grid_x, grid_y); /* vpr_printf(TIO_MESSAGE_INFO, "Writing DFF Testbench for %s...\n", circuit_name); */ testbench_load_cnt = 0; /* Print the title */ fprint_spice_head(fp, title); my_free(title); /* print technology library and design parameters*/ /* fprint_tech_lib(fp, arch.spice->tech_lib);*/ /* Include parameter header files */ fprint_spice_include_param_headers(fp, include_dir_path); /* Include Key subckts */ fprint_spice_include_key_subckts(fp, subckt_dir_path); /* Include user-defined sub-circuit netlist */ init_include_user_defined_netlists(*(arch.spice)); fprint_include_user_defined_netlists(fp, *(arch.spice)); /* Print simulation temperature and other options for SPICE */ fprint_spice_options(fp, arch.spice->spice_params); /* Global nodes: Vdd for SRAMs, Logic Blocks(Include IO), Switch Boxes, Connection Boxes */ fprint_spice_hardlogic_testbench_global_ports(fp, grid_x, grid_y, num_clock, (*arch.spice)); /* Quote defined Logic blocks subckts (Grids) */ init_spice_hardlogic_testbench_globals(*(arch.spice)); init_logical_block_spice_model_type_temp_used(arch.spice->num_spice_model, arch.spice->spice_models, SPICE_MODEL_FF); init_logical_block_spice_model_type_temp_used(arch.spice->num_spice_model, arch.spice->spice_models, SPICE_MODEL_HARDLOGIC); /* Now start our job formally: dump hard logic circuit one by one */ fprint_spice_hardlogic_testbench_call_one_grid_defined_hardlogics(fp, grid_x, grid_y, LL_rr_node_indices); /* Back-anotate activity information to each routing resource node * (We should have activity of each Grid port) */ /* Check if the all hardlogic located in this grid have been printed */ check_spice_models_grid_tb_cnt(arch.spice->num_spice_model, arch.spice->spice_models, grid_x, grid_y, SPICE_MODEL_FF); check_spice_models_grid_tb_cnt(arch.spice->num_spice_model, arch.spice->spice_models, grid_x, grid_y, SPICE_MODEL_HARDLOGIC); /* Add stimulations */ fprint_spice_hardlogic_testbench_stimulations(fp, grid_x, grid_y, num_clock, (*arch.spice), LL_rr_node_indices); /* Add measurements */ fprint_spice_hardlogic_testbench_measurements(fp, grid_x, grid_y, (*arch.spice), leakage_only); /* SPICE ends*/ fprintf(fp, ".end\n"); /* Close the file*/ fclose(fp); if (0 < tb_num_hardlogic) { /* vpr_printf(TIO_MESSAGE_INFO, "Writing Grid[%d][%d] SPICE Hard Logic Testbench for %s...\n", grid_x, grid_y, circuit_name); */ /* Push the testbench to the linked list */ tb_head = add_one_spice_tb_info_to_llist(tb_head, hardlogic_testbench_file_path, max_sim_num_clock_cycles); used = 1; } else { /* Remove the file generated */ my_remove_file(hardlogic_testbench_file_path); used = 0; } return used; } /* Top-level function in this source file */ void spice_print_hardlogic_testbench(char* formatted_spice_dir, char* circuit_name, char* include_dir_path, char* subckt_dir_path, t_ivec*** LL_rr_node_indices, int num_clock, t_arch arch, boolean leakage_only) { char* hardlogic_testbench_name = NULL; int ix, iy; int cnt = 0; int used = 0; for (ix = 1; ix < (nx+1); ix++) { for (iy = 1; iy < (ny+1); iy++) { /* Name the testbench */ hardlogic_testbench_name = (char*)my_malloc(sizeof(char)*( strlen(circuit_name) + 6 + strlen(my_itoa(ix)) + 1 + strlen(my_itoa(iy)) + 1 + strlen(spice_hardlogic_testbench_postfix) + 1 )); sprintf(hardlogic_testbench_name, "%s_grid%d_%d%s", circuit_name, ix, iy, spice_hardlogic_testbench_postfix); /* Start building one testbench */ used = fprint_spice_one_hardlogic_testbench(formatted_spice_dir, circuit_name, hardlogic_testbench_name, include_dir_path, subckt_dir_path, LL_rr_node_indices, num_clock, arch, ix, iy, leakage_only); if (1 == used) { cnt += used; } /* free */ my_free(hardlogic_testbench_name); } } /* Update the global counter */ num_used_hardlogic_tb = cnt; vpr_printf(TIO_MESSAGE_INFO,"No. of generated hard logic testbench = %d\n", num_used_hardlogic_tb); return; }