OpenFPGA/vpr7_rram/vpr/SRC/spice/spice_mux_testbench.c

2058 lines
85 KiB
C

/***********************************/
/* SPICE Modeling for VPR */
/* Xifan TANG, EPFL/LSI */
/***********************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <assert.h>
#include <sys/stat.h>
#include <unistd.h>
/* Include vpr structs*/
#include "util.h"
#include "physical_types.h"
#include "vpr_types.h"
#include "globals.h"
#include "rr_graph_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 "fpga_spice_backannotate_utils.h"
#include "spice_utils.h"
#include "spice_routing.h"
#include "spice_subckt.h"
#include "spice_mux_testbench.h"
/** In this test bench.
* All the multiplexers (Local routing, Switch Boxes, Connection Blocks) in the FPGA are examined
* All the multiplexers are hanged with equivalent capactive loads in their context.
*/
/* Global variables in this C-source file */
static int testbench_mux_cnt = 0;
static int testbench_sram_cnt = 0;
static int testbench_load_cnt = 0;
static int testbench_pb_mux_cnt = 0;
static int testbench_cb_mux_cnt = 0;
static int testbench_sb_mux_cnt = 0;
static int num_segments;
static t_segment_inf* segments;
static t_llist* testbench_muxes_head = NULL;
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;
static float total_pb_mux_input_density = 0.;
static float total_cb_mux_input_density = 0.;
static float total_sb_mux_input_density = 0.;
/***** Local Subroutines Declaration *****/
/***** Local Subroutines *****/
static void init_spice_mux_testbench_globals(t_spice spice) {
testbench_mux_cnt = 0;
testbench_sram_cnt = 0;
testbench_load_cnt = 0;
testbench_muxes_head = NULL;
testbench_pb_mux_cnt = 0;
testbench_cb_mux_cnt = 0;
testbench_sb_mux_cnt = 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;
}
}
static
void fprint_spice_mux_testbench_global_ports(FILE* fp,
t_spice spice) {
/* A valid file handler*/
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!\n",__FILE__, __LINE__);
exit(1);
}
/* Print generic global ports*/
fprint_spice_generic_testbench_global_ports(fp,
sram_spice_orgz_info,
global_ports_head);
return;
}
static
void fprint_spice_mux_testbench_pb_mux_meas(FILE* fp,
char* meas_tag) {
/* A valid file handler*/
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!\n",__FILE__, __LINE__);
exit(1);
}
if (0 == testbench_pb_mux_cnt) {
fprintf(fp, ".meas tran sum_leakage_power_pb_mux[0to%d] \n", testbench_pb_mux_cnt);
fprintf(fp, "+ param=\'leakage_%s\'\n", meas_tag);
fprintf(fp, ".meas tran sum_energy_per_cycle_pb_mux[0to%d] \n", testbench_pb_mux_cnt);
fprintf(fp, "+ param=\'energy_per_cycle_%s\'\n", meas_tag);
} else {
fprintf(fp, ".meas tran sum_leakage_power_pb_mux[0to%d] \n", testbench_pb_mux_cnt);
fprintf(fp, "+ param=\'sum_leakage_power_pb_mux[0to%d]+leakage_%s\'\n", testbench_pb_mux_cnt-1, meas_tag);
fprintf(fp, ".meas tran sum_energy_per_cycle_pb_mux[0to%d] \n", testbench_pb_mux_cnt);
fprintf(fp, "+ param=\'sum_energy_per_cycle_pb_mux[0to%d]+energy_per_cycle_%s\'\n", testbench_pb_mux_cnt-1, meas_tag);
}
/* Update the counter */
testbench_pb_mux_cnt++;
return;
}
static
void fprint_spice_mux_testbench_cb_mux_meas(FILE* fp,
char* meas_tag) {
/* A valid file handler*/
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!\n",__FILE__, __LINE__);
exit(1);
}
if (0 == testbench_cb_mux_cnt) {
fprintf(fp, ".meas tran sum_leakage_power_cb_mux[0to%d] \n", testbench_cb_mux_cnt);
fprintf(fp, "+ param=\'leakage_%s\'\n", meas_tag);
fprintf(fp, ".meas tran sum_energy_per_cycle_cb_mux[0to%d] \n", testbench_cb_mux_cnt);
fprintf(fp, "+ param=\'energy_per_cycle_%s\'\n", meas_tag);
} else {
fprintf(fp, ".meas tran sum_leakage_power_cb_mux[0to%d] \n", testbench_cb_mux_cnt);
fprintf(fp, "+ param=\'sum_leakage_power_cb_mux[0to%d]+leakage_%s\'\n", testbench_cb_mux_cnt-1, meas_tag);
fprintf(fp, ".meas tran sum_energy_per_cycle_cb_mux[0to%d] \n", testbench_cb_mux_cnt);
fprintf(fp, "+ param=\'sum_energy_per_cycle_cb_mux[0to%d]+energy_per_cycle_%s\'\n", testbench_cb_mux_cnt-1, meas_tag);
}
/* Update the counter */
testbench_cb_mux_cnt++;
return;
}
static
void fprint_spice_mux_testbench_sb_mux_meas(FILE* fp,
char* meas_tag) {
/* A valid file handler*/
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!\n",__FILE__, __LINE__);
exit(1);
}
if (0 == testbench_sb_mux_cnt) {
fprintf(fp, ".meas tran sum_leakage_power_sb_mux[0to%d] \n", testbench_sb_mux_cnt);
fprintf(fp, "+ param=\'leakage_%s\'\n", meas_tag);
fprintf(fp, ".meas tran sum_energy_per_cycle_sb_mux[0to%d] \n", testbench_sb_mux_cnt);
fprintf(fp, "+ param=\'energy_per_cycle_%s\'\n", meas_tag);
} else {
fprintf(fp, ".meas tran sum_leakage_power_sb_mux[0to%d] \n", testbench_sb_mux_cnt);
fprintf(fp, "+ param=\'sum_leakage_power_sb_mux[0to%d]+leakage_%s\'\n", testbench_sb_mux_cnt-1, meas_tag);
fprintf(fp, ".meas tran sum_energy_per_cycle_sb_mux[0to%d] \n", testbench_sb_mux_cnt);
fprintf(fp, "+ param=\'sum_energy_per_cycle_sb_mux[0to%d]+energy_per_cycle_%s\'\n", testbench_sb_mux_cnt-1, meas_tag);
}
/* Update the counter */
testbench_sb_mux_cnt++;
return;
}
static
void fprint_spice_mux_testbench_one_mux(FILE* fp,
char* meas_tag,
t_spice_model* mux_spice_model,
int mux_size,
int* input_init_value,
float* input_density,
float* input_probability,
int path_id) {
int inode, mux_level, ilevel, cur_num_sram;
int num_mux_sram_bits = 0;
int* mux_sram_bits = NULL;
t_llist* found_mux_node = NULL;
t_spice_mux_model* cur_mux = NULL;
int num_sim_clock_cycles = 0;
float average_density = 0.;
int avg_density_cnt = 0;
char* sram_vdd_port_name = 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 != mux_spice_model);
assert((2 < mux_size)||(2 == mux_size));
assert(NULL != input_density);
assert(NULL != input_probability);
/* Add to linked list */
check_and_add_mux_to_linked_list(&(testbench_muxes_head), mux_size, mux_spice_model);
found_mux_node = search_mux_linked_list(testbench_muxes_head, mux_size, mux_spice_model);
/* Check */
assert(NULL != found_mux_node);
cur_mux = (t_spice_mux_model*)(found_mux_node->dptr);
assert(mux_spice_model == cur_mux->spice_model);
/* Call the subckt that has already been defined before */
fprintf(fp, "X%s_size%d[%d] ", mux_spice_model->prefix, mux_size, testbench_mux_cnt);
/* Global ports */
if (0 < rec_fprint_spice_model_global_ports(fp, mux_spice_model, FALSE)) {
fprintf(fp, "+ ");
}
/* input port*/
for (inode = 0; inode < mux_size; inode++) {
fprintf(fp, "%s_size%d[%d]->in[%d] ",
mux_spice_model->prefix, mux_size, testbench_mux_cnt, inode);
}
/* Output port */
fprintf(fp, "%s_size%d[%d]->out ",
mux_spice_model->prefix, mux_size, testbench_mux_cnt);
/* SRAMs */
/* Print SRAM configurations,
* we should have a global SRAM vdd, AND it should be connected to a real sram subckt !!!
*/
/* Configuration bits for MUX*/
assert((-1 != path_id)&&(path_id < mux_size));
/* 1. Get the mux level*/
switch (mux_spice_model->design_tech_info.structure) {
case SPICE_MODEL_STRUCTURE_TREE:
mux_level = determine_tree_mux_level(mux_size);
num_mux_sram_bits = mux_level;
mux_sram_bits = decode_tree_mux_sram_bits(mux_size, mux_level, path_id);
break;
case SPICE_MODEL_STRUCTURE_ONELEVEL:
mux_level = 1;
/* Special for 2-input MUX */
if (2 == mux_size) {
num_mux_sram_bits = 1;
mux_sram_bits = decode_tree_mux_sram_bits(mux_size, mux_level, path_id);
} else {
num_mux_sram_bits = mux_size;
mux_sram_bits = decode_onelevel_mux_sram_bits(mux_size, mux_level, path_id);
}
break;
case SPICE_MODEL_STRUCTURE_MULTILEVEL:
/* Special for 2-input MUX */
if (2 == mux_size) {
mux_level = 1;
num_mux_sram_bits = 1;
mux_sram_bits = decode_tree_mux_sram_bits(mux_size, 1, path_id);
} else {
mux_level = mux_spice_model->design_tech_info.mux_num_level;
num_mux_sram_bits = determine_num_input_basis_multilevel_mux(mux_size, mux_level) * mux_level;
mux_sram_bits = decode_multilevel_mux_sram_bits(mux_size, mux_level, path_id);
}
break;
default:
vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid structure for spice model (%s)!\n",
__FILE__, __LINE__, mux_spice_model->name);
exit(1);
}
/* Print SRAMs that configure this MUX */
/* Get current counter of mem_bits, bl and wl */
cur_num_sram = testbench_sram_cnt;
for (ilevel = 0; ilevel < num_mux_sram_bits; ilevel++) {
assert( (0 == mux_sram_bits[ilevel]) || (1 == mux_sram_bits[ilevel]) );
fprint_spice_sram_one_outport(fp, sram_spice_orgz_info,
cur_num_sram + ilevel, mux_sram_bits[ilevel]);
fprint_spice_sram_one_outport(fp, sram_spice_orgz_info,
cur_num_sram + ilevel, 1 - mux_sram_bits[ilevel]);
}
/* End with svdd and sgnd, subckt name*/
/* Local vdd and gnd, we should have an independent VDD for all local interconnections*/
fprintf(fp, "gvdd_%s_size%d[%d] 0 ", mux_spice_model->prefix, mux_size, testbench_mux_cnt);
/* End with spice_model name */
fprintf(fp, "%s_size%d\n", mux_spice_model->name, mux_size);
/* Print the encoding in SPICE netlist for debugging */
fprintf(fp, "***** SRAM bits for MUX[%d], level=%d, select_path_id=%d. *****\n",
testbench_mux_cnt, mux_level, path_id);
fprintf(fp, "*****");
for (ilevel = 0; ilevel < num_mux_sram_bits; ilevel++) {
fprintf(fp, "%d", mux_sram_bits[ilevel]);
}
fprintf(fp, "*****\n");
/* Force SRAM bits */
/* cur_num_sram = testbench_sram_cnt; */
/*
for (ilevel = 0; ilevel < mux_level; ilevel++) {
fprintf(fp,"V%s[%d]->in %s[%d]->in 0 ",
sram_spice_model->prefix, cur_num_sram, sram_spice_model->prefix, cur_num_sram);
fprintf(fp, "0\n");
cur_num_sram++;
}
*/
/* Call SRAM subckts*/
/* Give the VDD port name for SRAMs */
sram_vdd_port_name = (char*)my_malloc(sizeof(char)*
(strlen(spice_tb_global_vdd_sram_port_name)
+ 1 ));
sprintf(sram_vdd_port_name, "%s",
spice_tb_global_vdd_sram_port_name);
/* Now Print SRAMs one by one */
for (ilevel = 0; ilevel < num_mux_sram_bits; ilevel++) {
fprint_spice_one_specific_sram_subckt(fp, sram_spice_orgz_info, mux_spice_model,
sram_vdd_port_name, testbench_sram_cnt);
testbench_sram_cnt++;
}
/* Test bench : Add voltage sources */
for (inode = 0; inode < mux_size; inode++) {
/* Print voltage source */
fprintf(fp, "***** Signal %s_size%d[%d]->in[%d] density = %g, probability=%g.*****\n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt, inode, input_density[inode], input_probability[inode]);
fprintf(fp, "V%s_size%d[%d]->in[%d] %s_size%d[%d]->in[%d] 0 \n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt, inode,
mux_spice_model->prefix, mux_size, testbench_mux_cnt, inode);
fprint_voltage_pulse_params(fp, input_init_value[inode], input_density[inode], input_probability[inode]);
/* fprint_voltage_pulse_params(fp, input_init_value[inode], 1, 0.5); */
}
/* global voltage supply */
fprintf(fp, "Vgvdd_%s_size%d[%d] gvdd_%s_size%d[%d] 0 vsp\n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt,
mux_spice_model->prefix, mux_size, testbench_mux_cnt);
/* Calculate average density of this MUX */
average_density = 0.;
avg_density_cnt = 0;
for (inode = 0; inode < mux_size; inode++) {
assert(!(0 > input_density[inode]));
if (0. < input_density[inode]) {
average_density += input_density[inode];
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;
num_sim_clock_cycles = (int)(1/average_density) + 1;
} else {
assert(0 == avg_density_cnt);
average_density = 0.;
num_sim_clock_cycles = 2;
}
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;
}
/* Measurements */
/* Measure the delay of MUX */
fprintf(fp, "***** Measurements *****\n");
/* Rise delay */
fprintf(fp, "***** Rise delay *****\n");
fprintf(fp, ".meas tran delay_rise_%s trig v(%s_size%d[%d]->in[%d]) val='input_thres_pct_rise*vsp' rise=1 td='clock_period'\n", meas_tag, mux_spice_model->prefix, mux_size, testbench_mux_cnt, path_id);
fprintf(fp, "+ targ v(%s_size%d[%d]->out) val='output_thres_pct_rise*vsp' rise=1 td='clock_period'\n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt);
/* Fall delay */
fprintf(fp, "***** Fall delay *****\n");
fprintf(fp, ".meas tran delay_fall_%s trig v(%s_size%d[%d]->in[%d]) val='input_thres_pct_fall*vsp' fall=1 td='clock_period'\n", meas_tag, mux_spice_model->prefix, mux_size, testbench_mux_cnt, path_id);
fprintf(fp, "+ targ v(%s_size%d[%d]->out) val='output_thres_pct_fall*vsp' fall=1 td='clock_period'\n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt);
/* Measure timing period of MUX switching */
/* Rise */
fprintf(fp, "***** Rise timing period *****\n");
fprintf(fp, ".meas start_rise_%s when v(%s_size%d[%d]->in[%d])='slew_lower_thres_pct_rise*vsp' rise=1 td='clock_period'\n",
meas_tag, mux_spice_model->prefix, mux_size, testbench_mux_cnt, path_id);
fprintf(fp, ".meas tran switch_rise_%s trig v(%s_size%d[%d]->in[%d]) val='slew_lower_thres_pct_rise*vsp' rise=1 td='clock_period'\n", meas_tag, mux_spice_model->prefix, mux_size, testbench_mux_cnt, path_id);
fprintf(fp, "+ targ v(%s_size%d[%d]->out) val='slew_upper_thres_pct_rise*vsp' rise=1 td='clock_period'\n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt);
/* Fall */
fprintf(fp, "***** Fall timing period *****\n");
fprintf(fp, ".meas start_fall_%s when v(%s_size%d[%d]->in[%d])='slew_lower_thres_pct_fall*vsp' fall=1 td='clock_period'\n",
meas_tag, mux_spice_model->prefix, mux_size, testbench_mux_cnt, path_id);
fprintf(fp, ".meas tran switch_fall_%s trig v(%s_size%d[%d]->in[%d]) val='slew_lower_thres_pct_fall*vsp' fall=1 td='clock_period'\n", meas_tag, mux_spice_model->prefix, mux_size, testbench_mux_cnt, path_id);
fprintf(fp, "+ targ v(%s_size%d[%d]->out) val='slew_upper_thres_pct_fall*vsp' fall=1 td='clock_period'\n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt);
/* Measure the leakage power of MUX */
fprintf(fp, "***** Leakage Power Measurement *****\n");
fprintf(fp, ".meas tran %s_size%d[%d]_leakage_power avg p(Vgvdd_%s_size%d[%d]) from=0 to='clock_period'\n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt,
mux_spice_model->prefix, mux_size, testbench_mux_cnt);
fprintf(fp, ".meas tran leakage_%s param='%s_size%d[%d]_leakage_power'\n",
meas_tag, mux_spice_model->prefix, mux_size, testbench_mux_cnt);
/* Measure the dynamic power of MUX */
fprintf(fp, "***** Dynamic Power Measurement *****\n");
fprintf(fp, ".meas tran %s_size%d[%d]_dynamic_power avg p(Vgvdd_%s_size%d[%d]) from='clock_period' to='%d*clock_period'\n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt,
mux_spice_model->prefix, mux_size, testbench_mux_cnt, num_sim_clock_cycles);
fprintf(fp, ".meas tran %s_size%d[%d]_energy_per_cycle param='%s_size%d[%d]_dynamic_power*clock_period'\n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt,
mux_spice_model->prefix, mux_size, testbench_mux_cnt);
/* Important: to give dynamic power measurement per toggle !!!!
* it is not fair to compare dynamic power when clock_period is different from designs to designs !!!
*/
fprintf(fp, ".meas tran dynamic_power_%s param='%s_size%d[%d]_dynamic_power'\n",
meas_tag, mux_spice_model->prefix, mux_size, testbench_mux_cnt);
fprintf(fp, ".meas tran energy_per_cycle_%s param='dynamic_power_%s*clock_period'\n",
meas_tag, meas_tag);
fprintf(fp, ".meas tran dynamic_rise_%s avg p(Vgvdd_%s_size%d[%d]) from='start_rise_%s' to='start_rise_%s+switch_rise_%s'\n",
meas_tag, mux_spice_model->prefix, mux_size, testbench_mux_cnt, meas_tag, meas_tag, meas_tag);
fprintf(fp, ".meas tran dynamic_fall_%s avg p(Vgvdd_%s_size%d[%d]) from='start_fall_%s' to='start_fall_%s+switch_fall_%s'\n",
meas_tag, mux_spice_model->prefix, mux_size, testbench_mux_cnt, meas_tag, meas_tag, meas_tag);
/*
fprintf(fp, ".meas tran %s_size%d[%d]_dynamic_power param='(dynamic_rise_%s)*(%g*%d)'\n",
mux_spice_model->prefix, mux_size, testbench_mux_cnt,
meas_tag, input_density[path_id], sim_num_clock_cycle-1);
fprintf(fp, ".meas tran dynamic_%s param='(dynamic_rise_%s)*(%g*%d)'\n",
meas_tag, meas_tag, input_density[path_id], sim_num_clock_cycle-1);
*/
if (0 == testbench_mux_cnt) {
fprintf(fp, ".meas tran sum_leakage_power_mux[0to%d] \n", testbench_mux_cnt);
fprintf(fp, "+ param=\'leakage_%s\'\n", meas_tag);
fprintf(fp, ".meas tran sum_energy_per_cycle_mux[0to%d] \n", testbench_mux_cnt);
fprintf(fp, "+ param=\'energy_per_cycle_%s\'\n", meas_tag);
} else {
fprintf(fp, ".meas tran sum_leakage_power_mux[0to%d] \n", testbench_mux_cnt);
fprintf(fp, "+ param=\'sum_leakage_power_mux[0to%d]+leakage_%s\'\n", testbench_mux_cnt-1, meas_tag);
fprintf(fp, ".meas tran sum_energy_per_cycle_mux[0to%d] \n", testbench_mux_cnt);
fprintf(fp, "+ param=\'sum_energy_per_cycle_mux[0to%d]+energy_per_cycle_%s\'\n", testbench_mux_cnt-1, meas_tag);
}
/* Free */
my_free(mux_sram_bits);
my_free(sram_vdd_port_name);
return;
}
/** Print a mulitplexer testbench of a given pb pin
* --|---|
* --| M |
* ..| U |--- des_pb_graph_in
* --| X |
* --|---|
*/
static
void fprint_spice_mux_testbench_pb_graph_node_pin_mux(FILE* fp,
t_mode* cur_mode,
t_pb_graph_pin* des_pb_graph_pin,
t_interconnect* cur_interc,
int fan_in,
int select_edge,
int grid_x, int grid_y,
t_ivec*** LL_rr_node_indices) {
int cur_input = 0;
float* input_density = NULL;
float* input_probability = NULL;
int* input_init_value = NULL;
int iedge;
int* sram_bits = NULL;
char* meas_tag = NULL;
char* outport_name = NULL;
float average_pb_mux_input_density = 0.;
/* 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 :
* MUX should have at least 2 fan_in
*/
assert((2 == fan_in)||(2 < fan_in));
/* 2. spice_model is a wire */
assert(NULL != cur_interc->spice_model);
assert(SPICE_MODEL_MUX == cur_interc->spice_model->type);
/* Test bench : Add voltage sources */
cur_input = 0;
input_density = (float*)my_malloc(sizeof(float)*fan_in);
input_probability = (float*)my_malloc(sizeof(float)*fan_in);
input_init_value = (int*)my_malloc(sizeof(int)*fan_in);
for (iedge = 0; iedge < des_pb_graph_pin->num_input_edges; iedge++) {
if (cur_mode != des_pb_graph_pin->input_edges[iedge]->interconnect->parent_mode) {
continue;
}
check_pb_graph_edge(*(des_pb_graph_pin->input_edges[iedge]));
/* Find activity information */
input_density[cur_input] = pb_pin_density(NULL, des_pb_graph_pin->input_edges[iedge]->input_pins[0]);
input_probability[cur_input] = pb_pin_probability(NULL, des_pb_graph_pin->input_edges[iedge]->input_pins[0]);
input_init_value[cur_input] = pb_pin_init_value(NULL, des_pb_graph_pin->input_edges[iedge]->input_pins[0]);
average_pb_mux_input_density += input_density[cur_input];
cur_input++;
}
average_pb_mux_input_density = average_pb_mux_input_density/fan_in;
total_pb_mux_input_density += average_pb_mux_input_density;
/* Check fan-in number is correct */
assert(fan_in == cur_input);
/* Build a measurement tag: <des_pb:spice_name_tag>_<des_port>[pin_index]_<interc_name> */
meas_tag = (char*)my_malloc(sizeof(char)* (10 + strlen(my_itoa(fan_in)) + strlen(my_itoa(testbench_mux_cnt)) + 2
+ strlen(des_pb_graph_pin->port->name) + 1
+ strlen(my_itoa(des_pb_graph_pin->pin_number)) + 2
+ strlen(cur_interc->name) + 1)); /* Add '0'*/
sprintf(meas_tag, "idle_mux%d[%d]_%s[%d]_%s",
fan_in, testbench_mux_cnt, des_pb_graph_pin->port->name, des_pb_graph_pin->pin_number, cur_interc->name);
/* Print the main part of a single MUX testbench */
fprint_spice_mux_testbench_one_mux(fp, meas_tag, cur_interc->spice_model,
fan_in, input_init_value, input_density, input_probability, select_edge);
/* Test bench : Capactive load */
/* TODO: Search all the fan-outs of des_pb_graph_pin */
outport_name = (char*)my_malloc(sizeof(char)*( strlen(cur_interc->spice_model->prefix) + 5
+ strlen(my_itoa(fan_in)) + 1 + strlen(my_itoa(testbench_mux_cnt))
+ 7 ));
sprintf(outport_name, "%s_size%d[%d]->out",
cur_interc->spice_model->prefix, fan_in, testbench_mux_cnt);
if (TRUE == run_testbench_load_extraction) { /* Additional switch, default on! */
fprint_spice_testbench_pb_graph_pin_inv_loads_rec(fp, &testbench_load_cnt,
grid_x, grid_y,
des_pb_graph_pin, NULL,
outport_name, TRUE,
LL_rr_node_indices);
}
fprint_spice_mux_testbench_pb_mux_meas(fp, meas_tag);
/* Update the counter */
testbench_mux_cnt++;
/* Free */
my_free(sram_bits);
my_free(input_init_value);
my_free(input_density);
my_free(input_probability);
my_free(outport_name);
return;
}
/** Print a mulitplexer testbench of a given pb pin
* --|---|
* --| M |
* ..| U |--- des_pb_graph_in
* --| X |
* --|---|
*/
static
void fprint_spice_mux_testbench_pb_pin_mux(FILE* fp,
t_rr_node* pb_rr_graph,
t_pb* des_pb,
t_mode* cur_mode,
t_pb_graph_pin* des_pb_graph_pin,
t_interconnect* cur_interc,
int fan_in,
int select_edge,
int grid_x, int grid_y,
t_ivec*** LL_rr_node_indices) {
int cur_input = 0;
float* input_density = NULL;
float* input_probability = NULL;
int* input_init_value = NULL;
int iedge;
int* sram_bits = NULL;
char* meas_tag = NULL;
char* outport_name = NULL;
float average_pb_mux_input_density = 0.;
/* 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 :
* MUX should have at least 2 fan_in
*/
assert((2 == fan_in)||(2 < fan_in));
/* 2. spice_model is a wire */
assert(NULL != cur_interc->spice_model);
assert(SPICE_MODEL_MUX == cur_interc->spice_model->type);
/* Test bench : Add voltage sources */
cur_input = 0;
input_density = (float*)my_malloc(sizeof(float)*fan_in);
input_probability = (float*)my_malloc(sizeof(float)*fan_in);
input_init_value = (int*)my_malloc(sizeof(int)*fan_in);
for (iedge = 0; iedge < des_pb_graph_pin->num_input_edges; iedge++) {
if (cur_mode != des_pb_graph_pin->input_edges[iedge]->interconnect->parent_mode) {
continue;
}
check_pb_graph_edge(*(des_pb_graph_pin->input_edges[iedge]));
/* Find activity information */
input_density[cur_input] = pb_pin_density(pb_rr_graph, des_pb_graph_pin->input_edges[iedge]->input_pins[0]);
input_probability[cur_input] = pb_pin_probability(pb_rr_graph, des_pb_graph_pin->input_edges[iedge]->input_pins[0]);
input_init_value[cur_input] = pb_pin_init_value(pb_rr_graph, des_pb_graph_pin->input_edges[iedge]->input_pins[0]);
average_pb_mux_input_density += input_density[cur_input];
cur_input++;
}
average_pb_mux_input_density = average_pb_mux_input_density/fan_in;
total_pb_mux_input_density += average_pb_mux_input_density;
/* Check fan-in number is correct */
assert(fan_in == cur_input);
/* Build a measurement tag: <des_pb:spice_name_tag>_<des_port>[pin_index]_<interc_name> */
meas_tag = (char*)my_malloc(sizeof(char)* (strlen(des_pb->spice_name_tag) + 1
+ strlen(des_pb_graph_pin->port->name) + 1
+ strlen(my_itoa(des_pb_graph_pin->pin_number)) + 2
+ strlen(cur_interc->name) + 1)); /* Add '0'*/
sprintf(meas_tag, "%s_%s[%d]_%s",
des_pb->spice_name_tag, des_pb_graph_pin->port->name, des_pb_graph_pin->pin_number, cur_interc->name);
/* Print the main part of a single MUX testbench */
fprint_spice_mux_testbench_one_mux(fp, meas_tag, cur_interc->spice_model,
fan_in, input_init_value, input_density, input_probability, select_edge);
/* Test bench : Capactive load */
/* Search all the fan-outs of des_pb_graph_pin */
outport_name = (char*)my_malloc(sizeof(char)*( strlen(cur_interc->spice_model->prefix) + 5
+ strlen(my_itoa(fan_in)) + 1 + strlen(my_itoa(testbench_mux_cnt))
+ 7 ));
sprintf(outport_name, "%s_size%d[%d]->out",
cur_interc->spice_model->prefix, fan_in, testbench_mux_cnt);
if (TRUE == run_testbench_load_extraction) { /* Additional switch, default on! */
fprint_spice_testbench_pb_graph_pin_inv_loads_rec(fp, &testbench_load_cnt,
grid_x, grid_y,
des_pb_graph_pin, des_pb,
outport_name, TRUE,
LL_rr_node_indices);
}
fprint_spice_mux_testbench_pb_mux_meas(fp, meas_tag);
/* Update the counter */
testbench_mux_cnt++;
/* Free */
my_free(sram_bits);
my_free(input_init_value);
my_free(input_density);
my_free(input_probability);
my_free(outport_name);
return;
}
static
void fprint_spice_mux_testbench_pb_graph_node_pin_interc(FILE* fp,
enum e_pin2pin_interc_type pin2pin_interc_type,
t_pb_graph_pin* des_pb_graph_pin,
t_mode* cur_mode,
int select_path_id,
int grid_x, int grid_y,
t_ivec*** LL_rr_node_indices) {
int fan_in;
int iedge;
t_interconnect* cur_interc = NULL;
enum e_interconnect spice_interc_type;
/* A valid file handler*/
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!\n",__FILE__, __LINE__);
exit(1);
}
/* 1. identify pin interconnection type,
* 2. Identify the number of fan-in (Consider interconnection edges of only selected mode)
* 3. Select and print the SPICE netlist
*/
fan_in = 0;
cur_interc = NULL;
/* Search the input edges only, stats on the size of MUX we may need (fan-in) */
for (iedge = 0; iedge < des_pb_graph_pin->num_input_edges; iedge++) {
/* 1. First, we should make sure this interconnect is in the selected mode!!!*/
if (cur_mode == des_pb_graph_pin->input_edges[iedge]->interconnect->parent_mode) {
/* Check this edge*/
check_pb_graph_edge(*(des_pb_graph_pin->input_edges[iedge]));
/* Record the interconnection*/
if (NULL == cur_interc) {
cur_interc = des_pb_graph_pin->input_edges[iedge]->interconnect;
} else { /* Make sure the interconnections for this pin is the same!*/
assert(cur_interc == des_pb_graph_pin->input_edges[iedge]->interconnect);
}
/* Search the input_pins of input_edges only*/
fan_in += des_pb_graph_pin->input_edges[iedge]->num_input_pins;
}
}
if (NULL == cur_interc) {
/* No interconnection matched */
return;
}
/* Initialize the interconnection type that will be implemented in SPICE netlist*/
switch (cur_interc->type) {
case DIRECT_INTERC:
assert(1 == fan_in);
spice_interc_type = DIRECT_INTERC;
break;
case COMPLETE_INTERC:
if (1 == fan_in) {
spice_interc_type = DIRECT_INTERC;
} else {
assert((2 == fan_in)||(2 < fan_in));
spice_interc_type = MUX_INTERC;
}
break;
case MUX_INTERC:
assert((2 == fan_in)||(2 < fan_in));
spice_interc_type = MUX_INTERC;
break;
default:
vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid interconnection type for %s (Arch[LINE%d])!\n",
__FILE__, __LINE__, cur_interc->name, cur_interc->line_num);
exit(1);
}
/* Print all the multiplexers at current level */
switch (spice_interc_type) {
case DIRECT_INTERC:
break;
case MUX_INTERC:
assert(SPICE_MODEL_MUX == cur_interc->spice_model->type);
fprint_spice_mux_testbench_pb_graph_node_pin_mux(fp, cur_mode, des_pb_graph_pin,
cur_interc, fan_in,
select_path_id,
grid_x, grid_y,
LL_rr_node_indices);
break;
default:
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid interconnection type!\n",
__FILE__, __LINE__);
exit(1);
}
return;
}
static
void fprint_spice_mux_testbench_pb_pin_interc(FILE* fp,
t_rr_node* pb_rr_graph,
t_pb* des_pb,
enum e_pin2pin_interc_type pin2pin_interc_type,
t_pb_graph_pin* des_pb_graph_pin,
t_mode* cur_mode,
int select_path_id,
int grid_x, int grid_y,
t_ivec*** LL_rr_node_indices) {
int fan_in;
int iedge;
t_interconnect* cur_interc = NULL;
enum e_interconnect spice_interc_type;
/* A valid file handler*/
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!\n",__FILE__, __LINE__);
exit(1);
}
/* 1. identify pin interconnection type,
* 2. Identify the number of fan-in (Consider interconnection edges of only selected mode)
* 3. Select and print the SPICE netlist
*/
fan_in = 0;
cur_interc = NULL;
/* Search the input edges only, stats on the size of MUX we may need (fan-in) */
for (iedge = 0; iedge < des_pb_graph_pin->num_input_edges; iedge++) {
/* 1. First, we should make sure this interconnect is in the selected mode!!!*/
if (cur_mode == des_pb_graph_pin->input_edges[iedge]->interconnect->parent_mode) {
/* Check this edge*/
check_pb_graph_edge(*(des_pb_graph_pin->input_edges[iedge]));
/* Record the interconnection*/
if (NULL == cur_interc) {
cur_interc = des_pb_graph_pin->input_edges[iedge]->interconnect;
} else { /* Make sure the interconnections for this pin is the same!*/
assert(cur_interc == des_pb_graph_pin->input_edges[iedge]->interconnect);
}
/* Search the input_pins of input_edges only*/
fan_in += des_pb_graph_pin->input_edges[iedge]->num_input_pins;
}
}
if (NULL == cur_interc) {
/* No interconnection matched */
return;
}
/* Initialize the interconnection type that will be implemented in SPICE netlist*/
switch (cur_interc->type) {
case DIRECT_INTERC:
assert(1 == fan_in);
spice_interc_type = DIRECT_INTERC;
break;
case COMPLETE_INTERC:
if (1 == fan_in) {
spice_interc_type = DIRECT_INTERC;
} else {
assert((2 == fan_in)||(2 < fan_in));
spice_interc_type = MUX_INTERC;
}
break;
case MUX_INTERC:
assert((2 == fan_in)||(2 < fan_in));
spice_interc_type = MUX_INTERC;
break;
default:
vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid interconnection type for %s (Arch[LINE%d])!\n",
__FILE__, __LINE__, cur_interc->name, cur_interc->line_num);
exit(1);
}
/* Print all the multiplexers at current level */
switch (spice_interc_type) {
case DIRECT_INTERC:
break;
case MUX_INTERC:
assert(SPICE_MODEL_MUX == cur_interc->spice_model->type);
fprint_spice_mux_testbench_pb_pin_mux(fp,
pb_rr_graph, des_pb,
cur_mode, des_pb_graph_pin,
cur_interc, fan_in, select_path_id,
grid_x, grid_y,
LL_rr_node_indices);
break;
default:
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid interconnection type!\n",
__FILE__, __LINE__);
exit(1);
}
return;
}
/* For each pb, we search the input pins and output pins for local interconnections */
static
void fprint_spice_mux_testbench_pb_graph_node_interc(FILE* fp,
t_pb_graph_node* cur_pb_graph_node,
int grid_x, int grid_y,
t_ivec*** LL_rr_node_indices) {
int iport, ipin;
int ipb, jpb;
t_pb_type* cur_pb_type = NULL;
t_mode* cur_mode = NULL;
t_pb_graph_node* child_pb_graph_node = NULL;
int select_mode_index = -1;
int path_id = -1;
/* 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_pb_graph_node);
cur_pb_type = cur_pb_graph_node->pb_type;
assert(NULL != cur_pb_type);
select_mode_index = find_pb_type_idle_mode_index(*(cur_pb_type));
cur_mode = &(cur_pb_type->modes[select_mode_index]);
assert(NULL != cur_mode);
/* We check output_pins of cur_pb_graph_node and its the input_edges
* Built the interconnections between outputs of cur_pb_graph_node and outputs of child_pb_graph_node
* child_pb_graph_node.output_pins -----------------> cur_pb_graph_node.outpins
* /|\
* |
* input_pins, edges, output_pins
*/
for (iport = 0; iport < cur_pb_graph_node->num_output_ports; iport++) {
for (ipin = 0; ipin < cur_pb_graph_node->num_output_pins[iport]; ipin++) {
path_id = 0;
fprint_spice_mux_testbench_pb_graph_node_pin_interc(fp,
OUTPUT2OUTPUT_INTERC,
&(cur_pb_graph_node->output_pins[iport][ipin]),
cur_mode,
path_id,
grid_x, grid_y,
LL_rr_node_indices);
}
}
/* We check input_pins of child_pb_graph_node and its the input_edges
* Built the interconnections between inputs of cur_pb_graph_node and inputs of child_pb_graph_node
* cur_pb_graph_node.input_pins -----------------> child_pb_graph_node.input_pins
* /|\
* |
* input_pins, edges, output_pins
*/
for (ipb = 0; ipb < cur_pb_type->modes[select_mode_index].num_pb_type_children; ipb++) {
for (jpb = 0; jpb < cur_pb_type->modes[select_mode_index].pb_type_children[ipb].num_pb; jpb++) {
child_pb_graph_node = &(cur_pb_graph_node->child_pb_graph_nodes[select_mode_index][ipb][jpb]);
/* For each child_pb_graph_node input pins*/
for (iport = 0; iport < child_pb_graph_node->num_input_ports; iport++) {
for (ipin = 0; ipin < child_pb_graph_node->num_input_pins[iport]; ipin++) {
path_id = 0;
/* Write the interconnection*/
fprint_spice_mux_testbench_pb_graph_node_pin_interc(fp,
INPUT2INPUT_INTERC,
&(child_pb_graph_node->input_pins[iport][ipin]),
cur_mode,
path_id,
grid_x, grid_y,
LL_rr_node_indices);
}
}
/* TODO: for clock pins, we should do the same work */
for (iport = 0; iport < child_pb_graph_node->num_clock_ports; iport++) {
for (ipin = 0; ipin < child_pb_graph_node->num_clock_pins[iport]; ipin++) {
path_id = 0;
/* Write the interconnection*/
fprint_spice_mux_testbench_pb_graph_node_pin_interc(fp,
INPUT2INPUT_INTERC,
&(child_pb_graph_node->clock_pins[iport][ipin]),
cur_mode,
path_id,
grid_x, grid_y,
LL_rr_node_indices);
}
}
}
}
return;
}
void fprint_spice_mux_testbench_idle_pb_graph_node_muxes_rec(FILE* fp,
t_pb_graph_node* cur_pb_graph_node,
int grid_x, int grid_y,
t_ivec*** LL_rr_node_indices) {
int ipb, jpb;
int mode_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(NULL != cur_pb_graph_node);
/* If we touch the leaf, there is no need print interc*/
if (NULL == cur_pb_graph_node->pb_type->spice_model) {
/* Print MUX interc at current-level pb*/
fprint_spice_mux_testbench_pb_graph_node_interc(fp, cur_pb_graph_node,
grid_x, grid_y,
LL_rr_node_indices);
} else {
return;
}
/* Go recursively ... */
mode_index = find_pb_type_idle_mode_index(*(cur_pb_graph_node->pb_type));
for (ipb = 0; ipb < cur_pb_graph_node->pb_type->modes[mode_index].num_pb_type_children; ipb++) {
for (jpb = 0; jpb < cur_pb_graph_node->pb_type->modes[mode_index].pb_type_children[ipb].num_pb; jpb++) {
/* Print idle muxes */
fprint_spice_mux_testbench_idle_pb_graph_node_muxes_rec(fp,
&(cur_pb_graph_node->child_pb_graph_nodes[mode_index][ipb][jpb]),
grid_x, grid_y,
LL_rr_node_indices);
}
}
return;
}
/* For each pb, we search the input pins and output pins for local interconnections */
static
void fprint_spice_mux_testbench_pb_interc(FILE* fp,
t_pb* cur_pb,
int grid_x, int grid_y,
t_ivec*** LL_rr_node_indices) {
int iport, ipin;
int ipb, jpb;
t_pb_graph_node* cur_pb_graph_node = NULL;
t_pb_type* cur_pb_type = NULL;
t_mode* cur_mode = NULL;
t_pb_graph_node* child_pb_graph_node = NULL;
t_pb* child_pb = NULL;
int select_mode_index = -1;
int node_index = -1;
int prev_node = -1;
int prev_edge = -1;
int path_id = -1;
t_rr_node* pb_rr_nodes = 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);
}
assert(NULL != cur_pb);
cur_pb_graph_node = cur_pb->pb_graph_node;
assert(NULL != cur_pb_graph_node);
cur_pb_type = cur_pb_graph_node->pb_type;
assert(NULL != cur_pb_type);
select_mode_index = cur_pb->mode;
cur_mode = &(cur_pb_type->modes[select_mode_index]);
assert(NULL != cur_mode);
/* We check output_pins of cur_pb_graph_node and its the input_edges
* Built the interconnections between outputs of cur_pb_graph_node and outputs of child_pb_graph_node
* child_pb_graph_node.output_pins -----------------> cur_pb_graph_node.outpins
* /|\
* |
* input_pins, edges, output_pins
*/
for (iport = 0; iport < cur_pb_graph_node->num_output_ports; iport++) {
for (ipin = 0; ipin < cur_pb_graph_node->num_output_pins[iport]; ipin++) {
/* Get the selected edge of current pin*/
assert(NULL != cur_pb);
pb_rr_nodes = cur_pb->rr_graph;
node_index = cur_pb_graph_node->output_pins[iport][ipin].pin_count_in_cluster;
/* Bypass unmapped interc */
/*
if (OPEN == pb_rr_nodes[node_index].net_num) {
continue;
}
*/
prev_node = pb_rr_nodes[node_index].prev_node;
/* prev_edge is the index of edge of prev_node !!! */
prev_edge = pb_rr_nodes[node_index].prev_edge;
/* Make sure this pb_rr_node is not OPEN and is not a primitive output*/
if (OPEN == prev_node) {
path_id = 0;
/* Determine the child_pb */
} else {
/* Find the path_id */
path_id = find_path_id_between_pb_rr_nodes(pb_rr_nodes, prev_node, node_index);
assert(-1 != path_id);
}
fprint_spice_mux_testbench_pb_pin_interc(fp, pb_rr_nodes ,cur_pb, /* TODO: find out the child_pb*/
OUTPUT2OUTPUT_INTERC,
&(cur_pb_graph_node->output_pins[iport][ipin]),
cur_mode,
path_id,
grid_x, grid_y,
LL_rr_node_indices);
}
}
/* We check input_pins of child_pb_graph_node and its the input_edges
* Built the interconnections between inputs of cur_pb_graph_node and inputs of child_pb_graph_node
* cur_pb_graph_node.input_pins -----------------> child_pb_graph_node.input_pins
* /|\
* |
* input_pins, edges, output_pins
*/
for (ipb = 0; ipb < cur_pb_type->modes[select_mode_index].num_pb_type_children; ipb++) {
for (jpb = 0; jpb < cur_pb_type->modes[select_mode_index].pb_type_children[ipb].num_pb; jpb++) {
child_pb_graph_node = &(cur_pb_graph_node->child_pb_graph_nodes[select_mode_index][ipb][jpb]);
child_pb = &(cur_pb->child_pbs[ipb][jpb]);
/* Check if child_pb is empty */
if (NULL == child_pb->name) {
//continue; /* by pass*/
/* For each child_pb_graph_node input pins*/
for (iport = 0; iport < child_pb_graph_node->num_input_ports; iport++) {
for (ipin = 0; ipin < child_pb_graph_node->num_input_pins[iport]; ipin++) {
path_id = 0;
/* Write the interconnection*/
fprint_spice_mux_testbench_pb_graph_node_pin_interc(fp,
INPUT2INPUT_INTERC,
&(child_pb_graph_node->input_pins[iport][ipin]),
cur_mode,
path_id,
grid_x, grid_y,
LL_rr_node_indices);
}
}
/* TODO: for clock pins, we should do the same work */
for (iport = 0; iport < child_pb_graph_node->num_clock_ports; iport++) {
for (ipin = 0; ipin < child_pb_graph_node->num_clock_pins[iport]; ipin++) {
path_id = 0;
/* Write the interconnection*/
fprint_spice_mux_testbench_pb_graph_node_pin_interc(fp,
INPUT2INPUT_INTERC,
&(child_pb_graph_node->clock_pins[iport][ipin]),
cur_mode,
path_id,
grid_x, grid_y,
LL_rr_node_indices);
}
}
continue;
}
/* Get pb_rr_graph of current pb*/
pb_rr_nodes = child_pb->rr_graph;
/* For each child_pb_graph_node input pins*/
for (iport = 0; iport < child_pb_graph_node->num_input_ports; iport++) {
for (ipin = 0; ipin < child_pb_graph_node->num_input_pins[iport]; ipin++) {
/* Get the index of the edge that are selected to pass signal*/
node_index = child_pb_graph_node->input_pins[iport][ipin].pin_count_in_cluster;
prev_node = pb_rr_nodes[node_index].prev_node;
prev_edge = pb_rr_nodes[node_index].prev_edge;
/* Bypass unmapped interc */
/*
if (OPEN == pb_rr_nodes[node_index].net_num) {
continue;
}
*/
/* Make sure this pb_rr_node is not OPEN and is not a primitive output*/
if (OPEN == prev_node) {
path_id = 0;
//break; /* TODO: if there exist parasitic input waveforms, we should print the interc */
} else {
/* Find the path_id */
path_id = find_path_id_between_pb_rr_nodes(pb_rr_nodes, prev_node, node_index);
assert(-1 != path_id);
}
/* Write the interconnection*/
fprint_spice_mux_testbench_pb_pin_interc(fp, pb_rr_nodes, child_pb,
INPUT2INPUT_INTERC,
&(child_pb_graph_node->input_pins[iport][ipin]),
cur_mode,
path_id,
grid_x, grid_y,
LL_rr_node_indices);
}
}
/* TODO: for clock pins, we should do the same work */
for (iport = 0; iport < child_pb_graph_node->num_clock_ports; iport++) {
for (ipin = 0; ipin < child_pb_graph_node->num_clock_pins[iport]; ipin++) {
/* Get the index of the edge that are selected to pass signal*/
node_index = child_pb_graph_node->input_pins[iport][ipin].pin_count_in_cluster;
prev_node = pb_rr_nodes[node_index].prev_node;
prev_edge = pb_rr_nodes[node_index].prev_edge;
/* Bypass unmapped interc */
/*
if (OPEN == pb_rr_nodes[node_index].net_num) {
continue;
}
*/
/* Make sure this pb_rr_node is not OPEN and is not a primitive output*/
if (OPEN == prev_node) {
path_id = 0;
//break; /* TODO: if there exist parasitic input waveforms, we should print the interc */
} else {
/* Find the path_id */
path_id = find_path_id_between_pb_rr_nodes(pb_rr_nodes, prev_node, node_index);
assert(-1 != path_id);
}
/* Write the interconnection*/
fprint_spice_mux_testbench_pb_pin_interc(fp, pb_rr_nodes, child_pb,
INPUT2INPUT_INTERC,
&(child_pb_graph_node->clock_pins[iport][ipin]),
cur_mode,
path_id,
grid_x, grid_y,
LL_rr_node_indices);
}
}
}
}
return;
}
static
void fprint_spice_mux_testbench_pb_muxes_rec(FILE* fp,
t_pb* cur_pb,
int grid_x, int grid_y,
t_ivec*** LL_rr_node_indices) {
int ipb, jpb;
int mode_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(NULL != cur_pb);
/* If we touch the leaf, there is no need print interc*/
if (OPEN == cur_pb->logical_block) {
/* Print MUX interc at current-level pb*/
fprint_spice_mux_testbench_pb_interc(fp, cur_pb,
grid_x, grid_y,
LL_rr_node_indices);
} else {
return;
}
/* Go recursively ... */
mode_index = cur_pb->mode;
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++) {
/* 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_mux_testbench_pb_muxes_rec(fp, &(cur_pb->child_pbs[ipb][jpb]), grid_x, grid_y, LL_rr_node_indices);
} else {
/* Print idle muxes */
fprint_spice_mux_testbench_idle_pb_graph_node_muxes_rec(fp,
cur_pb->child_pbs[ipb][jpb].pb_graph_node,
grid_x, grid_y,
LL_rr_node_indices);
}
}
}
return;
}
static
void fprint_spice_mux_testbench_cb_one_mux(FILE* fp,
t_cb cur_cb_info,
t_rr_node* src_rr_node,
t_ivec*** LL_rr_node_indices) {
int mux_size, cb_x, cb_y;
int inode, path_id, switch_index;
t_rr_node** drive_rr_nodes = NULL;
t_spice_model* mux_spice_model = NULL;
int* mux_sram_bits = NULL;
float* input_density = NULL;
float* input_probability = NULL;
int* input_init_value = NULL;
char* meas_tag = NULL;
char* outport_name = NULL;
float average_cb_mux_input_density = 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))));
cb_x = cur_cb_info.x;
cb_y = cur_cb_info.y;
assert(IPIN == src_rr_node->type);
/* Find drive_rr_nodes*/
mux_size = src_rr_node->num_drive_rr_nodes;
assert(mux_size == src_rr_node->fan_in);
drive_rr_nodes = src_rr_node->drive_rr_nodes;
/* Find path_id */
path_id = -1;
for (inode = 0; inode < mux_size; inode++) {
if (drive_rr_nodes[inode] == &(rr_node[src_rr_node->prev_node])) {
path_id = inode;
break;
}
}
assert((-1 != path_id)&&(path_id < mux_size));
switch_index = src_rr_node->drive_switches[path_id];
mux_spice_model = switch_inf[switch_index].spice_model;
/* Check :
* MUX should have at least 2 fan_in
*/
assert((2 == mux_size)||(2 < mux_size));
/* 2. spice_model is a wire */
assert(SPICE_MODEL_MUX == mux_spice_model->type);
input_density = (float*)my_malloc(sizeof(float)*mux_size);
input_probability = (float*)my_malloc(sizeof(float)*mux_size);
input_init_value = (int*)my_malloc(sizeof(int)*mux_size);
for (inode = 0; inode < mux_size; inode++) {
/* Find activity information */
input_density[inode] = get_rr_node_net_density(*(drive_rr_nodes[inode]));
input_probability[inode] = get_rr_node_net_probability(*(drive_rr_nodes[inode]));
input_init_value[inode] = get_rr_node_net_init_value(*(drive_rr_nodes[inode]));
average_cb_mux_input_density += input_density[inode];
}
average_cb_mux_input_density = average_cb_mux_input_density/mux_size;
total_cb_mux_input_density += average_cb_mux_input_density;
/* Build meas_tag: cb_mux[cb_x][cb_y]_rrnode[node]*/
meas_tag = (char*)my_malloc(sizeof(char)*(7 + strlen(my_itoa(cb_x)) + 2
+ strlen(my_itoa(cb_y)) + 9
+ strlen(my_itoa(src_rr_node-rr_node)) + 2)); /* Add '0'*/
sprintf(meas_tag, "cb_mux[%d][%d]_rrnode[%ld]", cb_x, cb_y, src_rr_node-rr_node);
/* Print the main part of a single MUX testbench */
fprint_spice_mux_testbench_one_mux(fp, meas_tag,
mux_spice_model, src_rr_node->fan_in,
input_init_value, input_density,
input_probability, path_id);
/* Generate loads */
outport_name = (char*)my_malloc(sizeof(char)*( strlen(mux_spice_model->prefix) + 5
+ strlen(my_itoa(mux_size)) + 1 + strlen(my_itoa(testbench_mux_cnt))
+ 7 ));
sprintf(outport_name, "%s_size%d[%d]->out",
mux_spice_model->prefix,
mux_size,
testbench_mux_cnt);
if (TRUE == run_testbench_load_extraction) { /* Additional switch, default on! */
fprint_spice_testbench_one_cb_mux_loads(fp, &testbench_load_cnt, src_rr_node,
outport_name, LL_rr_node_indices);
}
fprint_spice_mux_testbench_cb_mux_meas(fp, meas_tag);
/* Update the counter */
testbench_mux_cnt++;
/* Free */
my_free(mux_sram_bits);
my_free(input_init_value);
my_free(input_density);
my_free(input_probability);
return;
}
void fprint_spice_mux_testbench_cb_interc(FILE* fp,
t_cb cur_cb_info,
t_rr_node* src_rr_node,
t_ivec*** LL_rr_node_indices) {
/* 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))));
assert(NULL != src_rr_node);
/* Skip non-mapped CB MUX */
if (OPEN == src_rr_node->net_num) {
//return;
}
assert((0 < src_rr_node->fan_in)||(0 == src_rr_node->fan_in));
if (1 == src_rr_node->fan_in) {
/* By-pass a direct connection*/
return;
} else if ((2 < src_rr_node->fan_in)||(2 == src_rr_node->fan_in)) {
/* Print a MUX */
fprint_spice_mux_testbench_cb_one_mux(fp,
cur_cb_info,
src_rr_node,
LL_rr_node_indices);
}
return;
}
/* Assume each connection box has a regional power-on/off switch */
static
int fprint_spice_mux_testbench_call_one_grid_cb_muxes(FILE* fp,
t_cb cur_cb_info,
t_ivec*** LL_rr_node_indices) {
int inode, side;
int side_cnt = 0;
int used = 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))));
side_cnt = 0;
used = 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++) {
/* Print multiplexers */
/* Check if there is at least one rr_node with a net_name*/
if (OPEN != cur_cb_info.ipin_rr_node[side][inode]->net_num) {
used = 1;
}
fprint_spice_mux_testbench_cb_interc(fp, cur_cb_info,
cur_cb_info.ipin_rr_node[side][inode],
LL_rr_node_indices);
}
}
/* Make sure only 2 sides of IPINs are printed */
assert((1 == side_cnt)||(2 == side_cnt));
/* Free */
return used;
}
static
int fprint_spice_mux_testbench_sb_one_mux(FILE* fp,
t_sb cur_sb_info,
int chan_side,
t_rr_node* src_rr_node) {
int inode, switch_index, mux_size;
t_spice_model* mux_spice_model = NULL;
float* input_density = NULL;
float* input_probability = NULL;
int* input_init_value = NULL;
int path_id = -1;
char* meas_tag = NULL;
int num_drive_rr_nodes = 0;
t_rr_node** drive_rr_nodes = NULL;
char* outport_name = NULL;
char* rr_node_outport_name = NULL;
int used = 0;
float average_sb_mux_input_density = 0.;
int switch_box_x, switch_box_y;
switch_box_x = cur_sb_info.x;
switch_box_y = cur_sb_info.y;
/* Check */
assert((!(0 > switch_box_x))&&(!(switch_box_x > (nx + 1))));
assert((!(0 > switch_box_y))&&(!(switch_box_y > (ny + 1))));
if (NULL == src_rr_node) {
return 0;
}
/* ignore idle sb mux
if (OPEN == src_rr_node->net_num) {
return used;
}
*/
/* Determine if the interc lies inside a channel wire, that is interc between segments */
/* Check each num_drive_rr_nodes, see if they appear in the cur_sb_info */
if (TRUE == check_drive_rr_node_imply_short(cur_sb_info, src_rr_node, chan_side)) {
/* Double check if the interc lies inside a channel wire, that is interc between segments */
assert(1 == is_rr_node_exist_opposite_side_in_sb_info(cur_sb_info, src_rr_node, chan_side));
num_drive_rr_nodes = 0;
drive_rr_nodes = NULL;
} else {
num_drive_rr_nodes = src_rr_node->num_drive_rr_nodes;
drive_rr_nodes = src_rr_node->drive_rr_nodes;
switch_index = src_rr_node->drive_switches[0];
}
/* Print MUX only when fan-in >= 2 */
if (2 > num_drive_rr_nodes) {
return used;
}
/* Check the file handler*/
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n",
__FILE__, __LINE__);
exit(1);
}
/* Find mux_spice_model, mux_size */
mux_size = num_drive_rr_nodes;
mux_spice_model = switch_inf[switch_index].spice_model;
assert(NULL != mux_spice_model);
assert(SPICE_MODEL_MUX == mux_spice_model->type);
/* input_density, input_probability */
input_density = (float*)my_malloc(sizeof(float)*mux_size);
input_probability = (float*)my_malloc(sizeof(float)*mux_size);
input_init_value = (int*)my_malloc(sizeof(int)*mux_size);
for (inode = 0; inode < mux_size; inode++) {
input_density[inode] = get_rr_node_net_density(*(drive_rr_nodes[inode]));
input_probability[inode] = get_rr_node_net_probability(*(drive_rr_nodes[inode]));
input_init_value[inode] = get_rr_node_net_init_value(*(drive_rr_nodes[inode]));
average_sb_mux_input_density += input_density[inode];
}
average_sb_mux_input_density = average_sb_mux_input_density/mux_size;
total_sb_mux_input_density += average_sb_mux_input_density;
/* Find path_id */
path_id = -1;
for (inode = 0; inode < mux_size; inode++) {
if (drive_rr_nodes[inode] == &(rr_node[src_rr_node->prev_node])) {
path_id = inode;
break;
}
}
assert((-1 != path_id)&&(path_id < mux_size));
/* Build meas_tag: sb_mux[sb_x][sb_y]_rrnode[node]*/
meas_tag = (char*)my_malloc(sizeof(char)*(7 + strlen(my_itoa(switch_box_x)) + 2
+ strlen(my_itoa(switch_box_y)) + 9
+ strlen(my_itoa(src_rr_node-rr_node)) + 2)); /* Add '0'*/
sprintf(meas_tag, "sb_mux[%d][%d]_rrnode[%ld]", switch_box_x, switch_box_y, src_rr_node-rr_node);
/* Print MUX */
fprint_spice_mux_testbench_one_mux(fp, meas_tag,
mux_spice_model, mux_size,
input_init_value, input_density,
input_probability, path_id);
/* Print a channel wire !*/
outport_name = (char*)my_malloc(sizeof(char)*( strlen(mux_spice_model->prefix)
+ 5 + strlen(my_itoa(mux_size)) + 1
+ strlen(my_itoa(testbench_mux_cnt))
+ 6 + 1 ));
sprintf(outport_name, "%s_size%d[%d]->out", mux_spice_model->prefix, mux_size, testbench_mux_cnt);
if (TRUE == run_testbench_load_extraction) { /* Additional switch, default on! */
fprintf(fp, "***** Load for rr_node[%d] *****\n", src_rr_node - rr_node);
rr_node_outport_name = fprint_spice_testbench_rr_node_load_version(fp,
&testbench_load_cnt,
num_segments,
segments,
0, /* load size */
(*src_rr_node),
outport_name);
}
fprint_spice_mux_testbench_sb_mux_meas(fp, meas_tag);
/* Update the counter */
testbench_mux_cnt++;
/* Free */
my_free(input_init_value);
my_free(input_density);
my_free(input_probability);
return 1;
}
static
int fprint_spice_mux_testbench_call_one_grid_sb_muxes(FILE* fp,
t_sb cur_sb_info,
t_ivec*** LL_rr_node_indices) {
int itrack, inode, side;
int used = 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_sb_info.x))&&(!(cur_sb_info.x > (nx + 1))));
assert((!(0 > cur_sb_info.y))&&(!(cur_sb_info.y > (ny + 1))));
/* print all the rr_nodes in the switch boxes if there is at least one rr_node with a net_num */
used = 0;
for (side = 0; side < cur_sb_info.num_sides; side++) {
for (itrack = 0; itrack < cur_sb_info.chan_width[side]; itrack++) {
switch (cur_sb_info.chan_rr_node_direction[side][itrack]) {
case OUT_PORT:
fprint_spice_mux_testbench_sb_one_mux(fp,
cur_sb_info,
side,
cur_sb_info.chan_rr_node[side][itrack]);
used++;
break;
case IN_PORT:
break;
default:
vpr_printf(TIO_MESSAGE_ERROR, "(File: %s [LINE%d]) Invalid direction of port sb[%d][%d] Channel node[%d] track[%d]!\n",
__FILE__, __LINE__, cur_sb_info.x, cur_sb_info.y, side, itrack);
exit(1);
}
}
}
/* Free */
if (0 < used) {
used = 1;
}
return used;
}
static
int fprint_spice_mux_testbench_call_one_grid_pb_muxes(FILE* fp, int ix, int iy,
t_ivec*** LL_rr_node_indices) {
int iblk;
int used = 0;
/* A valid file handler*/
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Invalid File Handler!\n",__FILE__, __LINE__);
exit(1);
}
/* Print all the grid */
if ((NULL == grid[ix][iy].type)
||(EMPTY_TYPE == grid[ix][iy].type)
||(0 != grid[ix][iy].offset)) {
return used;
}
/* Used blocks */
for (iblk = 0; iblk < grid[ix][iy].usage; iblk++) {
/* 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);
fprint_spice_mux_testbench_pb_muxes_rec(fp,
block[grid[ix][iy].blocks[iblk]].pb,
ix, iy,
LL_rr_node_indices);
used = 1;
}
/* By pass Unused blocks */
for (iblk = grid[ix][iy].usage; iblk < grid[ix][iy].type->capacity; iblk++) {
/* Mark the temporary net_num for the type pins*/
mark_grid_type_pb_graph_node_pins_temp_net_num(ix, iy);
fprint_spice_mux_testbench_idle_pb_graph_node_muxes_rec(fp,
grid[ix][iy].type->pb_graph_head,
ix, iy,
LL_rr_node_indices);
}
return used;
}
static
void fprint_spice_mux_testbench_stimulations(FILE* fp,
int num_clocks) {
/* Voltage sources of Multiplexers are already generated during printing the netlist
* We just need global stimulations Here.
*/
/* Give global vdd, gnd, voltage sources*/
/* A valid file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid File Handler!\n", __FILE__, __LINE__);
exit(1);
}
/* 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");
return;
}
static
void fprint_spice_mux_testbench_measurements(FILE* fp,
enum e_spice_mux_tb_type mux_tb_type,
t_spice spice) {
int num_clock_cycle = max_sim_num_clock_cycles;
/*
int i;
t_llist* head = NULL;
t_spice_mux_model* spice_mux_model = NULL;
*/
/* Give global vdd, gnd, voltage sources*/
/* A valid 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, FALSE);
fprint_spice_netlist_generic_measurements(fp, spice.spice_params.mc_params, spice.num_spice_model, spice.spice_models);
/* Measure the leakage and dynamic power of SRAMs*/
fprintf(fp, ".meas tran total_leakage_srams avg p(Vgvdd_sram) from=0 to=\'clock_period\'\n");
fprintf(fp, ".meas tran total_dynamic_srams avg p(Vgvdd_sram) from=\'clock_period\' to=\'%d*clock_period\'\n", num_clock_cycle);
fprintf(fp, ".meas tran total_energy_per_cycle_srams param=\'total_dynamic_srams*clock_period\'\n");
/* Measure the total leakage and dynamic power */
fprintf(fp, ".meas tran total_leakage_power_mux[0to%d] \n", testbench_mux_cnt - 1);
fprintf(fp, "+ param=\'sum_leakage_power_mux[0to%d]\'\n", testbench_mux_cnt-1);
fprintf(fp, ".meas tran total_energy_per_cycle_mux[0to%d] \n", testbench_mux_cnt - 1);
fprintf(fp, "+ param=\'sum_energy_per_cycle_mux[0to%d]\'\n", testbench_mux_cnt-1);
switch (mux_tb_type) {
case SPICE_PB_MUX_TB:
/* pb_muxes */
fprintf(fp, ".meas tran total_leakage_power_pb_mux \n");
fprintf(fp, "+ param=\'sum_leakage_power_pb_mux[0to%d]\'\n", testbench_pb_mux_cnt-1);
fprintf(fp, ".meas tran total_energy_per_cycle_pb_mux \n");
fprintf(fp, "+ param=\'sum_energy_per_cycle_pb_mux[0to%d]\'\n", testbench_pb_mux_cnt-1);
break;
case SPICE_CB_MUX_TB:
/* cb_muxes */
fprintf(fp, ".meas tran total_leakage_power_cb_mux \n");
fprintf(fp, "+ param=\'sum_leakage_power_cb_mux[0to%d]\'\n", testbench_cb_mux_cnt-1);
fprintf(fp, ".meas tran total_energy_per_cycle_cb_mux \n");
fprintf(fp, "+ param=\'sum_energy_per_cycle_cb_mux[0to%d]\'\n", testbench_cb_mux_cnt-1);
break;
case SPICE_SB_MUX_TB:
/* sb_muxes */
fprintf(fp, ".meas tran total_leakage_power_sb_mux \n");
fprintf(fp, "+ param=\'sum_leakage_power_sb_mux[0to%d]\'\n", testbench_sb_mux_cnt-1);
fprintf(fp, ".meas tran total_energy_per_cycle_sb_mux \n");
fprintf(fp, "+ param=\'sum_energy_per_cycle_sb_mux[0to%d]\'\n", testbench_sb_mux_cnt-1);
break;
default:
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d]) Invalid mux_tb_type!\n", __FILE__, __LINE__);
exit(1);
}
return;
}
/* Top-level function in this source file */
int fprint_spice_one_mux_testbench(char* formatted_spice_dir,
char* circuit_name,
char* mux_testbench_name,
char* include_dir_path,
char* subckt_dir_path,
t_ivec*** LL_rr_node_indices,
int num_clocks,
t_arch arch,
int grid_x, int grid_y, t_rr_type cb_type,
enum e_spice_mux_tb_type mux_tb_type,
boolean leakage_only) {
FILE* fp = NULL;
char* formatted_subckt_dir_path = format_dir_path(subckt_dir_path);
char* title = my_strcat("FPGA SPICE Routing MUX Test Bench for Design: ", circuit_name);
char* mux_testbench_file_path = my_strcat(formatted_spice_dir, mux_testbench_name);
char* mux_tb_name = NULL;
int used = 0;
switch (mux_tb_type) {
case SPICE_PB_MUX_TB:
mux_tb_name = "CLB MUX";
break;
case SPICE_CB_MUX_TB:
mux_tb_name = "Connection Box MUX";
break;
case SPICE_SB_MUX_TB:
mux_tb_name = "Switch Block MUX";
break;
default:
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d]) Invalid mux_tb_type!\n", __FILE__, __LINE__);
exit(1);
}
/* Check if the path exists*/
fp = fopen(mux_testbench_file_path,"w");
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,"(FILE:%s,LINE[%d])Failure in create SPICE %s Test bench netlist %s!\n",
__FILE__, __LINE__, mux_tb_name, mux_testbench_file_path);
exit(1);
}
/*
vpr_printf(TIO_MESSAGE_INFO, "Writing Grid[%d][%d] SPICE %s MUX Test Bench for %s...\n",
grid_x, grid_y, mux_tb_name, circuit_name);
*/
/* Load global vars in this source file */
num_segments = arch.num_segments;
segments = arch.Segments;
/* 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, formatted_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_mux_testbench_global_ports(fp, *(arch.spice));
/* Quote defined Logic blocks subckts (Grids) */
init_spice_mux_testbench_globals(*(arch.spice));
switch (mux_tb_type) {
case SPICE_PB_MUX_TB:
total_pb_mux_input_density = 0.;
/* Output a pb_mux testbench */
used = fprint_spice_mux_testbench_call_one_grid_pb_muxes(fp, grid_x, grid_y, LL_rr_node_indices);
/* Check and output info. */
assert((0 == testbench_pb_mux_cnt)||(0 < testbench_pb_mux_cnt));
if (0 < testbench_pb_mux_cnt) {
total_pb_mux_input_density = total_pb_mux_input_density/testbench_pb_mux_cnt;
/* Add stimulations */
fprint_spice_mux_testbench_stimulations(fp, num_clocks);
/* Add measurements */
fprint_spice_mux_testbench_measurements(fp, mux_tb_type, *(arch.spice));
}
/*
vpr_printf(TIO_MESSAGE_INFO,"Average density of PB MUX inputs is %.2g.\n", total_pb_mux_input_density);
*/
break;
case SPICE_CB_MUX_TB:
/* one cbx or one cby*/
total_cb_mux_input_density = 0.;
/* Output a cb_mux testbench */
switch (cb_type) {
case CHANX:
used = fprint_spice_mux_testbench_call_one_grid_cb_muxes(fp, cbx_info[grid_x][grid_y], LL_rr_node_indices);
break;
case CHANY:
used = fprint_spice_mux_testbench_call_one_grid_cb_muxes(fp, cby_info[grid_x][grid_y], LL_rr_node_indices);
break;
default:
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d]) Invalid connection_box_type!\n", __FILE__, __LINE__);
exit(1);
}
/* Check and output info. */
assert((0 == testbench_cb_mux_cnt)||(0 < testbench_cb_mux_cnt));
if (0 < testbench_cb_mux_cnt) {
total_cb_mux_input_density = total_cb_mux_input_density/testbench_cb_mux_cnt;
/* Add stimulations */
fprint_spice_mux_testbench_stimulations(fp, num_clocks);
/* Add measurements */
fprint_spice_mux_testbench_measurements(fp, mux_tb_type, *(arch.spice));
}
/*
vpr_printf(TIO_MESSAGE_INFO,"Average density of CB MUX inputs is %.2g.\n", total_cb_mux_input_density);
*/
break;
case SPICE_SB_MUX_TB:
total_sb_mux_input_density = 0.;
/* Output a sb_mux testbench */
used = fprint_spice_mux_testbench_call_one_grid_sb_muxes(fp, sb_info[grid_x][grid_y], LL_rr_node_indices);
/* Check and output info. */
assert((0 == testbench_sb_mux_cnt)||(0 < testbench_sb_mux_cnt));
if (0 < testbench_sb_mux_cnt) {
total_sb_mux_input_density = total_sb_mux_input_density/testbench_sb_mux_cnt;
/* Add stimulations */
fprint_spice_mux_testbench_stimulations(fp, num_clocks);
/* Add measurements */
fprint_spice_mux_testbench_measurements(fp, mux_tb_type, *(arch.spice));
}
/*
vpr_printf(TIO_MESSAGE_INFO,"Average density of SB MUX inputs is %.2g.\n", total_sb_mux_input_density);
*/
break;
default:
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d]) Invalid mux_tb_type!\n", __FILE__, __LINE__);
exit(1);
}
/* SPICE ends*/
fprintf(fp, ".end\n");
/* Close the file*/
fclose(fp);
/* Free */
//my_free(formatted_subckt_dir_path);
//my_free(mux_testbench_file_path);
//my_free(title);
free_muxes_llist(testbench_muxes_head);
if (0 < testbench_mux_cnt) {
/* vpr_printf(TIO_MESSAGE_INFO, "Writing Grid[%d][%d] SPICE %s Test Bench for %s...\n",
grid_x, grid_y, mux_tb_name, circuit_name);
*/
/* Push the testbench to the linked list */
tb_head = add_one_spice_tb_info_to_llist(tb_head, mux_testbench_file_path, max_sim_num_clock_cycles);
used = 1;
} else {
/* Remove the file generated */
my_remove_file(mux_testbench_file_path);
used = 0;
}
return used;
}
void spice_print_mux_testbench(char* formatted_spice_dir,
char* circuit_name,
char* include_dir_path,
char* subckt_dir_path,
t_ivec*** LL_rr_node_indices,
int num_clocks,
t_arch arch,
enum e_spice_mux_tb_type mux_tb_type,
boolean leakage_only) {
char* mux_testbench_name = NULL;
int ix, iy;
int cnt = 0;
int used = 0;
int bypass_cnt = 0;
/* Depend on the type of testbench, we generate the a list of testbenches */
switch (mux_tb_type) {
case SPICE_PB_MUX_TB:
cnt = 0;
vpr_printf(TIO_MESSAGE_INFO,"Generating Grid multiplexer testbench...\n");
for (ix = 1; ix < (nx+1); ix++) {
for (iy = 1; iy < (ny+1); iy++) {
mux_testbench_name = (char*)my_malloc(sizeof(char)*( strlen(circuit_name)
+ 5 + strlen(my_itoa(ix)) + 1
+ strlen(my_itoa(iy)) + 1
+ strlen(spice_pb_mux_testbench_postfix) + 1 ));
sprintf(mux_testbench_name, "%s_grid%d_%d%s",
circuit_name, ix, iy, spice_pb_mux_testbench_postfix);
used = fprint_spice_one_mux_testbench(formatted_spice_dir, circuit_name, mux_testbench_name,
include_dir_path, subckt_dir_path, LL_rr_node_indices,
num_clocks, arch, ix, iy, NUM_RR_TYPES, SPICE_PB_MUX_TB,
leakage_only);
if (1 == used) {
cnt += used;
}
/* free */
my_free(mux_testbench_name);
}
}
/* Update the global counter */
num_used_grid_mux_tb = cnt;
vpr_printf(TIO_MESSAGE_INFO,"No. of generated Grid multiplexer testbench = %d\n", num_used_grid_mux_tb);
break;
case SPICE_CB_MUX_TB:
cnt = 0;
/* X-channel Connection Blocks */
vpr_printf(TIO_MESSAGE_INFO,"Generating X-channel Connection Block multiplexer testbench...\n");
for (iy = 0; iy < (ny+1); iy++) {
for (ix = 1; ix < (nx+1); ix++) {
/* Bypass non-exist CBs */
if ((FALSE == is_cb_exist(CHANX, ix, iy))
||(0 == count_cb_info_num_ipin_rr_nodes(cbx_info[ix][iy]))) {
bypass_cnt++;
continue;
}
mux_testbench_name = (char*)my_malloc(sizeof(char)*( strlen(circuit_name)
+ 4 + strlen(my_itoa(ix)) + 2 + strlen(my_itoa(iy)) + 1
+ strlen(spice_cb_mux_testbench_postfix) + 1 ));
sprintf(mux_testbench_name, "%s_cbx%d_%d%s",
circuit_name, ix, iy, spice_cb_mux_testbench_postfix);
used = fprint_spice_one_mux_testbench(formatted_spice_dir, circuit_name, mux_testbench_name,
include_dir_path, subckt_dir_path, LL_rr_node_indices,
num_clocks, arch, ix, iy, CHANX, SPICE_CB_MUX_TB,
leakage_only);
if (1 == used) {
cnt += used;
}
/* free */
my_free(mux_testbench_name);
}
}
/* Y-channel Connection Blocks */
vpr_printf(TIO_MESSAGE_INFO,"Generating Y-channel Connection Block multiplexer testbench...\n");
for (ix = 0; ix < (nx+1); ix++) {
for (iy = 1; iy < (ny+1); iy++) {
/* Bypass non-exist CBs */
if ((FALSE == is_cb_exist(CHANY, ix, iy))
||(0 == count_cb_info_num_ipin_rr_nodes(cby_info[ix][iy]))) {
bypass_cnt++;
continue;
}
mux_testbench_name = (char*)my_malloc(sizeof(char)*( strlen(circuit_name)
+ 4 + strlen(my_itoa(ix)) + 2 + strlen(my_itoa(iy)) + 1
+ strlen(spice_cb_mux_testbench_postfix) + 1 ));
sprintf(mux_testbench_name, "%s_cby%d_%d%s",
circuit_name, ix, iy, spice_cb_mux_testbench_postfix);
used = fprint_spice_one_mux_testbench(formatted_spice_dir, circuit_name, mux_testbench_name,
include_dir_path, subckt_dir_path, LL_rr_node_indices,
num_clocks, arch, ix, iy, CHANY, SPICE_CB_MUX_TB,
leakage_only);
if (1 == used) {
cnt += used;
}
/* free */
my_free(mux_testbench_name);
}
}
/* Update the global counter */
num_used_cb_mux_tb = cnt;
vpr_printf(TIO_MESSAGE_INFO,"No. of generated Connection Block multiplexer testbench = %d\n", num_used_cb_mux_tb);
vpr_printf(TIO_MESSAGE_INFO, "Bypass %d Connection Blocks that does no exist in the architecture.\n",
bypass_cnt);
break;
case SPICE_SB_MUX_TB:
cnt = 0;
vpr_printf(TIO_MESSAGE_INFO,"Generating Switch Block multiplexer testbench...\n");
for (ix = 0; ix < (nx+1); ix++) {
for (iy = 0; iy < (ny+1); iy++) {
mux_testbench_name = (char*)my_malloc(sizeof(char)*( strlen(circuit_name)
+ 4 + strlen(my_itoa(ix)) + 2 + strlen(my_itoa(iy)) + 1
+ strlen(spice_sb_mux_testbench_postfix) + 1 ));
sprintf(mux_testbench_name, "%s_sb%d_%d%s",
circuit_name, ix, iy, spice_sb_mux_testbench_postfix);
used = fprint_spice_one_mux_testbench(formatted_spice_dir, circuit_name, mux_testbench_name,
include_dir_path, subckt_dir_path, LL_rr_node_indices,
num_clocks, arch, ix, iy, NUM_RR_TYPES, SPICE_SB_MUX_TB,
leakage_only);
if (1 == used) {
cnt += used;
}
/* free */
my_free(mux_testbench_name);
}
}
/* Update the global counter */
num_used_sb_mux_tb = cnt;
vpr_printf(TIO_MESSAGE_INFO,"No. of generated Switch Block multiplexer testbench = %d\n", num_used_sb_mux_tb);
break;
default:
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d]) Invalid mux_tb_type!\n", __FILE__, __LINE__);
exit(1);
}
return;
}