/***********************************/ /* SPICE Modeling for VPR */ /* Xifan TANG, EPFL/LSI */ /***********************************/ #include #include #include #include #include #include #include #include /* Include vpr structs*/ #include "util.h" #include "physical_types.h" #include "vpr_types.h" #include "globals.h" #include "rr_graph_util.h" #include "rr_graph.h" #include "rr_graph2.h" #include "vpr_utils.h" #include "route_common.h" /* Include FPGA-SPICE support headers*/ #include "linkedlist.h" #include "fpga_x2p_types.h" #include "fpga_x2p_globals.h" #include "spice_globals.h" #include "fpga_x2p_utils.h" #include "fpga_x2p_mux_utils.h" #include "fpga_x2p_pbtypes_utils.h" #include "fpga_x2p_bitstream_utils.h" /* Include SPICE support headers*/ #include "spice_utils.h" #include "spice_mux.h" #include "spice_lut.h" #include "fpga_x2p_backannotate_utils.h" #include "spice_routing.h" void fprint_routing_chan_subckt(char* subckt_dir, int x, int y, t_rr_type chan_type, int LL_num_rr_nodes, t_rr_node* LL_rr_node, t_ivec*** LL_rr_node_indices, int num_segment, t_segment_inf* segments) { int itrack, iseg, cost_index; char* chan_prefix = NULL; int chan_width = 0; t_rr_node** chan_rr_nodes = NULL; FILE* fp = NULL; char* fname = NULL; /* Check */ assert((!(0 > x))&&(!(x > (nx + 1)))); assert((!(0 > y))&&(!(y > (ny + 1)))); assert((CHANX == chan_type)||(CHANY == chan_type)); /* Initial chan_prefix*/ switch (chan_type) { case CHANX: /* Create file handler */ fp = spice_create_one_subckt_file(subckt_dir, "Channel X-direction ", chanx_spice_file_name_prefix, x, y, &fname); chan_prefix = "chanx"; fprintf(fp, "***** Subckt for Channel X [%d][%d] *****\n", x, y); break; case CHANY: /* Create file handler */ fp = spice_create_one_subckt_file(subckt_dir, "Channel Y-direction ", chany_spice_file_name_prefix, x, y, &fname); chan_prefix = "chany"; fprintf(fp, "***** Subckt for Channel Y [%d][%d] *****\n", x, y); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid Channel type! Should be CHANX or CHANY.\n", __FILE__, __LINE__); exit(1); } /* Collect rr_nodes for Tracks for chanx[ix][iy] */ chan_rr_nodes = get_chan_rr_nodes(&chan_width, chan_type, x, y, LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices); /* Chan subckt definition */ fprintf(fp, ".subckt %s[%d][%d] \n", chan_prefix, x, y); /* Inputs and outputs, * Rules for CHANX: * print left-hand ports(in) first, then right-hand ports(out) * Rules for CHANX: * print bottom ports(in) first, then top ports(out) */ fprintf(fp, "+ "); /* LEFT/BOTTOM side port of CHANX/CHANY */ for (itrack = 0; itrack < chan_width; itrack++) { switch (chan_rr_nodes[itrack]->direction) { case INC_DIRECTION: fprintf(fp, "in%d ", itrack); /* INC_DIRECTION: input on the left/bottom side */ break; case DEC_DIRECTION: fprintf(fp, "out%d ", itrack); /* DEC_DIRECTION: output on the left/bottom side*/ break; case BI_DIRECTION: default: vpr_printf(TIO_MESSAGE_ERROR, "(File: %s [LINE%d]) Invalid direction of %s[%d][%d]_track[%d]!\n", __FILE__, __LINE__, chan_prefix, x, y, itrack); exit(1); } } fprintf(fp, "\n"); fprintf(fp, "+ "); /* RIGHT/TOP side port of CHANX/CHANY */ for (itrack = 0; itrack < chan_width; itrack++) { switch (chan_rr_nodes[itrack]->direction) { case INC_DIRECTION: fprintf(fp, "out%d ", itrack); /* INC_DIRECTION: output on the right/top side*/ break; case DEC_DIRECTION: fprintf(fp, "in%d ", itrack); /* DEC_DIRECTION: input on the right/top side */ break; case BI_DIRECTION: default: vpr_printf(TIO_MESSAGE_ERROR, "(File: %s [LINE%d]) Invalid direction of rr_node %s[%d][%d]_track[%d]!\n", __FILE__, __LINE__, chan_prefix, x, y, itrack); exit(1); } } fprintf(fp, "\n"); fprintf(fp, "+ "); /* Middle point output for connection box inputs */ for (itrack = 0; itrack < chan_width; itrack++) { fprintf(fp, "mid_out%d ", itrack); } fprintf(fp, "\n"); /* End with svdd and sgnd */ fprintf(fp, "+ svdd sgnd\n"); /* Print segments models*/ for (itrack = 0; itrack < chan_width; itrack++) { cost_index = chan_rr_nodes[itrack]->cost_index; iseg = rr_indexed_data[cost_index].seg_index; /* Check */ assert((!(iseg < 0))&&(iseg < num_segment)); assert(NULL != segments[iseg].spice_model); assert(SPICE_MODEL_CHAN_WIRE == segments[iseg].spice_model->type); fprintf(fp, "X%s[%d] ", segments[iseg].spice_model->prefix, segments[iseg].spice_model->cnt); /*Call subckt*/ /* Update counter of SPICE model*/ segments[iseg].spice_model->cnt++; /* Inputs and ouputs*/ fprintf(fp, "in%d out%d mid_out%d ", itrack, itrack, itrack); /* End with svdd, sgnd and Subckt name*/ fprintf(fp, "svdd sgnd %s_seg%d\n", segments[iseg].spice_model->name, iseg); } fprintf(fp, ".eom\n"); /* Close the file*/ fclose(fp); /* Add fname to the linked list */ routing_spice_subckt_file_path_head = add_one_subckt_file_name_to_llist(routing_spice_subckt_file_path_head, fname); /* Free */ my_free(chan_rr_nodes); my_free(fname); return; } void fprint_grid_side_pin_with_given_index(FILE* fp, int pin_index, int side, int x, int y) { int height/*, class_id*/; t_type_ptr type = NULL; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > x))&&(!(x > (nx + 1)))); assert((!(0 > y))&&(!(y > (ny + 1)))); type = grid[x][y].type; assert(NULL != type); assert((!(0 > pin_index))&&(pin_index < type->num_pins)); assert((!(0 > side))&&(!(side > 3))); /* Output the pins on the side*/ height = get_grid_pin_height(x, y, pin_index); /* class_id = type->pin_class[pin_index]; */ if ((1 == type->pinloc[height][side][pin_index])) { /* Not sure if we need to plus a height */ /* fprintf(fp, "grid[%d][%d]_pin[%d][%d][%d] ", x, y, height, side, pin_index); */ fprintf(fp, "grid[%d][%d]_pin[%d][%d][%d] ", x, y, height, side, pin_index); } else { vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Fail to print a grid pin (x=%d, y=%d, height=%d, side=%d, index=%d)\n", __FILE__, __LINE__, x, y, height, side, pin_index); exit(1); } return; } void fprint_grid_side_pins(FILE* fp, t_rr_type pin_type, int x, int y, int side) { int height, ipin, class_id; t_type_ptr type = NULL; enum e_pin_type pin_class_type; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > x))&&(!(x > (nx + 1)))); assert((!(0 > y))&&(!(y > (ny + 1)))); type = grid[x][y].type; assert(NULL != type); /* Assign the type of PIN*/ switch (pin_type) { case IPIN: /* case SINK: */ pin_class_type = RECEIVER; /* This is the end of a route path*/ break; /* case SOURCE: */ case OPIN: pin_class_type = DRIVER; /* This is the start of a route path */ break; /* SINK and SOURCE are hypothesis nodes */ default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid pin_type!\n", __FILE__, __LINE__); exit(1); } /* Output the pins on the side*/ for (ipin = 0; ipin < type->num_pins; ipin++) { height = get_grid_pin_height(x, y, ipin); class_id = type->pin_class[ipin]; if ((1 == type->pinloc[height][side][ipin])&&(pin_class_type == type->class_inf[class_id].type)) { /* Not sure if we need to plus a height */ fprintf(fp, "grid[%d][%d]_pin[%d][%d][%d] ", x, y + height, height, side, ipin); } } return; } void fprint_switch_box_chan_port(FILE* fp, t_sb cur_sb_info, int chan_side, t_rr_node* cur_rr_node, enum PORTS cur_rr_node_direction) { int index = -1; t_rr_type chan_rr_node_type; int chan_rr_node_x, chan_rr_node_y; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Get the index in sb_info of cur_rr_node */ index = get_rr_node_index_in_sb_info(cur_rr_node, cur_sb_info, chan_side, cur_rr_node_direction); /* Make sure this node is included in this sb_info */ assert((-1 != index)&&(-1 != chan_side)); get_chan_rr_node_coorindate_in_sb_info(cur_sb_info, chan_side, &chan_rr_node_type, &chan_rr_node_x, &chan_rr_node_y); assert(cur_rr_node->type == chan_rr_node_type); fprintf(fp, "%s[%d][%d]_%s[%d] ", convert_chan_type_to_string(chan_rr_node_type), chan_rr_node_x, chan_rr_node_y, convert_chan_rr_node_direction_to_string(cur_sb_info.chan_rr_node_direction[chan_side][index]), cur_rr_node->ptc_num); return; } /* Print a short interconneciton in switch box * There are two cases should be noticed. * 1. The actual fan-in of cur_rr_node is 0. In this case, the cur_rr_node need to be short connected to itself which is on the opposite side of this switch * 2. The actual fan-in of cur_rr_node is 0. In this case, * The cur_rr_node need to connected to the drive_rr_node */ void fprint_switch_box_short_interc(FILE* fp, t_sb cur_sb_info, int chan_side, t_rr_node* cur_rr_node, int actual_fan_in, t_rr_node* drive_rr_node) { int side, index; int grid_x, grid_y/*, height*/; char* chan_name = NULL; char* des_chan_port_name = NULL; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* 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)))); assert((0 == actual_fan_in)||(1 == actual_fan_in)); chan_name = convert_chan_type_to_string(cur_rr_node->type); /* Get the index in sb_info of cur_rr_node */ index = get_rr_node_index_in_sb_info(cur_rr_node, cur_sb_info, chan_side, OUT_PORT); des_chan_port_name = "out"; fprintf(fp, "R%s[%d][%d]_%s[%d] ", chan_name, cur_sb_info.x, cur_sb_info.y, des_chan_port_name, cur_rr_node->ptc_num); /* Check the driver*/ if (0 == actual_fan_in) { assert(drive_rr_node == cur_rr_node); } else { /* drive_rr_node = &(rr_node[cur_rr_node->prev_node]); */ assert(1 == rr_node_drive_switch_box(drive_rr_node, cur_rr_node, cur_sb_info.x, cur_sb_info.y, chan_side)); } switch (drive_rr_node->type) { /* case SOURCE: */ case OPIN: /* Indicate a CLB Outpin*/ /* Search all the sides of a SB, see this drive_rr_node is an INPUT of this SB */ get_rr_node_side_and_index_in_sb_info(drive_rr_node, cur_sb_info, IN_PORT, &side, &index); /* We need to be sure that drive_rr_node is part of the SB */ assert((-1 != index)&&(-1 != side)); /* Find grid_x and grid_y */ grid_x = drive_rr_node->xlow; grid_y = drive_rr_node->ylow; /*Plus the offset in function fprint_grid_side_pin_with_given_index */ /*height = grid[grid_x][grid_y].offset;*/ /* Print a grid pin */ fprint_grid_side_pin_with_given_index(fp, drive_rr_node->ptc_num, cur_sb_info.opin_rr_node_grid_side[side][index], grid_x, grid_y); break; case CHANX: case CHANY: /* Should be an input */ if (cur_rr_node == drive_rr_node) { /* To be strict, the input should locate on the opposite side. * Use the else part if this may change in some architecture. */ side = get_opposite_side(chan_side); index = get_rr_node_index_in_sb_info(drive_rr_node, cur_sb_info, side, IN_PORT); } else { get_rr_node_side_and_index_in_sb_info(drive_rr_node, cur_sb_info, IN_PORT, &side, &index); } /* We need to be sure that drive_rr_node is part of the SB */ assert((-1 != index)&&(-1 != side)); fprint_switch_box_chan_port(fp, cur_sb_info, side, drive_rr_node, IN_PORT); break; default: /* IPIN, SINK are invalid*/ vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n", __FILE__, __LINE__); exit(1); } /* Output port */ /* fprint_switch_box_chan_port(fp, switch_box_x, switch_box_y, chan_side, cur_rr_node); */ fprint_switch_box_chan_port(fp, cur_sb_info, chan_side, cur_rr_node, OUT_PORT); /* END */ fprintf(fp, "0\n"); return; } /* Print the SPICE netlist of multiplexer that drive this rr_node */ void fprint_switch_box_mux(FILE* fp, t_sb cur_sb_info, int chan_side, t_rr_node* cur_rr_node, int mux_size, t_rr_node** drive_rr_nodes, int switch_index) { int inode, side, index; int grid_x, grid_y/*, height*/; t_spice_model* spice_model = NULL; int mux_level, path_id, cur_num_sram, ilevel; int num_mux_sram_bits = 0; int* mux_sram_bits = NULL; char* sram_vdd_port_name = NULL; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* 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)))); /* Check current rr_node is CHANX or CHANY*/ assert((CHANX == cur_rr_node->type)||(CHANY == cur_rr_node->type)); /* Allocate drive_rr_nodes according to the fan-in*/ assert((2 == mux_size)||(2 < mux_size)); /* Get spice model*/ spice_model = switch_inf[switch_index].spice_model; /* Now it is the time print the SPICE netlist of MUX*/ fprintf(fp, "X%s_size%d[%d] ", spice_model->prefix, mux_size, spice_model->cnt); /* Global ports */ if (0 < rec_fprint_spice_model_global_ports(fp, spice_model, FALSE)) { fprintf(fp, "+ "); } /* Input ports*/ for (inode = 0; inode < mux_size; inode++) { switch (drive_rr_nodes[inode]->type) { /* case SOURCE: */ case OPIN: /* Indicate a CLB Outpin*/ /* Search all the sides of a SB, see this drive_rr_node is an INPUT of this SB */ get_rr_node_side_and_index_in_sb_info(drive_rr_nodes[inode], cur_sb_info, IN_PORT, &side, &index); /* We need to be sure that drive_rr_node is part of the SB */ assert((-1 != index)&&(-1 != side)); /* Find grid_x and grid_y */ grid_x = drive_rr_nodes[inode]->xlow; grid_y = drive_rr_nodes[inode]->ylow; /*Plus the offset in function fprint_grid_side_pin_with_given_index */ /* height = grid[grid_x][grid_y].offset; */ /* Print a grid pin */ fprint_grid_side_pin_with_given_index(fp, drive_rr_nodes[inode]->ptc_num, cur_sb_info.opin_rr_node_grid_side[side][index], grid_x, grid_y); break; case CHANX: case CHANY: /* Should be an input! */ get_rr_node_side_and_index_in_sb_info(drive_rr_nodes[inode], cur_sb_info, IN_PORT, &side, &index); /* We need to be sure that drive_rr_node is part of the SB */ if (!((-1 != index)&&(-1 != side))) { assert((-1 != index)&&(-1 != side)); } fprint_switch_box_chan_port(fp, cur_sb_info, side, drive_rr_nodes[inode], IN_PORT); break; default: /* IPIN, SINK are invalid*/ vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n", __FILE__, __LINE__); exit(1); } } /* Output port */ fprint_switch_box_chan_port(fp, cur_sb_info, chan_side, cur_rr_node, OUT_PORT); /* Configuration bits for this MUX*/ path_id = DEFAULT_PATH_ID; for (inode = 0; inode < mux_size; inode++) { if (drive_rr_nodes[inode] == &(rr_node[cur_rr_node->prev_node])) { path_id = inode; break; } } /* Depend on both technology and structure of this MUX*/ switch (spice_model->design_tech) { case SPICE_MODEL_DESIGN_CMOS: decode_cmos_mux_sram_bits(spice_model, mux_size, path_id, &num_mux_sram_bits, &mux_sram_bits, &mux_level); break; case SPICE_MODEL_DESIGN_RRAM: decode_rram_mux(spice_model, mux_size, path_id, &num_mux_sram_bits, &mux_sram_bits, &mux_level); break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid design technology for spice model (%s)!\n", __FILE__, __LINE__, spice_model->name); exit(1); } /* Print SRAMs that configure this MUX */ /* Get current counter of mem_bits, bl and wl */ cur_num_sram = get_sram_orgz_info_num_mem_bit(sram_spice_orgz_info); 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*/ fprintf(fp, "svdd sgnd %s_size%d\n", 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", spice_model->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"); /* Call SRAM subckts*/ /* Give the VDD port name for SRAMs */ sram_vdd_port_name = (char*)my_malloc(sizeof(char)* (strlen(spice_tb_global_vdd_sb_sram_port_name) + 1 )); sprintf(sram_vdd_port_name, "%s", spice_tb_global_vdd_sb_sram_port_name); /* Now Print SRAMs one by one */ for (ilevel = 0; ilevel < num_mux_sram_bits; ilevel++) { fprint_spice_one_sram_subckt(fp, sram_spice_orgz_info, spice_model, sram_vdd_port_name); } /* Store the configuraion bit to linked-list */ add_sram_conf_bits_to_llist(sram_spice_orgz_info, cur_num_sram, num_mux_sram_bits, mux_sram_bits); /* Update spice_model counter */ spice_model->cnt++; /* Free */ my_free(mux_sram_bits); my_free(sram_vdd_port_name); return; } void fprint_switch_box_interc(FILE* fp, t_sb cur_sb_info, int chan_side, t_rr_node* cur_rr_node) { int sb_x, sb_y; int num_drive_rr_nodes = 0; t_rr_node** drive_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); } sb_x = cur_sb_info.x; sb_y = cur_sb_info.y; /* Check */ assert((!(0 > sb_x))&&(!(sb_x > (nx + 1)))); assert((!(0 > sb_y))&&(!(sb_y > (ny + 1)))); /* 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, cur_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, cur_rr_node, chan_side)); num_drive_rr_nodes = 0; drive_rr_nodes = NULL; } else { num_drive_rr_nodes = cur_rr_node->num_drive_rr_nodes; drive_rr_nodes = cur_rr_node->drive_rr_nodes; } if (0 == num_drive_rr_nodes) { /* Print a special direct connection*/ fprint_switch_box_short_interc(fp, cur_sb_info, chan_side, cur_rr_node, num_drive_rr_nodes, cur_rr_node); } else if (1 == num_drive_rr_nodes) { /* Print a direct connection*/ fprint_switch_box_short_interc(fp, cur_sb_info, chan_side, cur_rr_node, num_drive_rr_nodes, drive_rr_nodes[DEFAULT_SWITCH_ID]); } else if (1 < num_drive_rr_nodes) { /* Print the multiplexer, fan_in >= 2 */ fprint_switch_box_mux(fp, cur_sb_info, chan_side, cur_rr_node, num_drive_rr_nodes, drive_rr_nodes, cur_rr_node->drive_switches[DEFAULT_SWITCH_ID]); } /*Nothing should be done else*/ /* Free */ return; } /* Task: Print the subckt of a Switch Box. * A Switch Box subckt consists of following ports: * 1. Channel Y [x][y] inputs * 2. Channel X [x+1][y] inputs * 3. Channel Y [x][y-1] outputs * 4. Channel X [x][y] outputs * 5. Grid[x][y+1] Right side outputs pins * 6. Grid[x+1][y+1] Left side output pins * 7. Grid[x+1][y+1] Bottom side output pins * 8. Grid[x+1][y] Top side output pins * 9. Grid[x+1][y] Left side output pins * 10. Grid[x][y] Right side output pins * 11. Grid[x][y] Top side output pins * 12. Grid[x][y+1] Bottom side output pins * * -------------- -------------- * | | | | * | Grid | ChanY | Grid | * | [x][y+1] | [x][y+1] | [x+1][y+1] | * | | | | * -------------- -------------- * ---------- * ChanX | Switch | ChanX * [x][y] | Box | [x+1][y] * | [x][y] | * ---------- * -------------- -------------- * | | | | * | Grid | ChanY | Grid | * | [x][y] | [x][y] | [x+1][y] | * | | | | * -------------- -------------- * For channels chanY with INC_DIRECTION on the top side, they should be marked as outputs * For channels chanY with DEC_DIRECTION on the top side, they should be marked as inputs * For channels chanY with INC_DIRECTION on the bottom side, they should be marked as inputs * For channels chanY with DEC_DIRECTION on the bottom side, they should be marked as outputs * For channels chanX with INC_DIRECTION on the left side, they should be marked as inputs * For channels chanX with DEC_DIRECTION on the left side, they should be marked as outputs * For channels chanX with INC_DIRECTION on the right side, they should be marked as outputs * For channels chanX with DEC_DIRECTION on the right side, they should be marked as inputs */ void fprint_routing_switch_box_subckt(char* subckt_dir, t_sb cur_sb_info, int LL_num_rr_nodes, t_rr_node* LL_rr_node, t_ivec*** LL_rr_node_indices, boolean compact_routing_hierarchy) { int itrack, inode, side, ix, iy, x, y; FILE* fp = NULL; char* fname = NULL; /* 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)))); /* Create file handler */ fp = spice_create_one_subckt_file(subckt_dir, "Switch Block ", sb_spice_file_name_prefix, cur_sb_info.x, cur_sb_info.y, &fname); x = cur_sb_info.x; y = cur_sb_info.y; /* Print the definition of subckt*/ fprintf(fp, "***** Switch Box[%d][%d] Sub-Circuit *****\n", cur_sb_info.x, cur_sb_info.y); fprintf(fp, ".subckt sb[%d][%d] ", cur_sb_info.x, cur_sb_info.y); fprintf(fp, "\n"); for (side = 0; side < cur_sb_info.num_sides; side++) { fprintf(fp, "***** Inputs/outputs of %s side *****\n",convert_side_index_to_string(side)); determine_sb_port_coordinator(cur_sb_info, side, &ix, &iy); fprintf(fp, "+ "); for (itrack = 0; itrack < cur_sb_info.chan_width[side]; itrack++) { switch (cur_sb_info.chan_rr_node_direction[side][itrack]) { case OUT_PORT: fprintf(fp, "%s[%d][%d]_out[%d] ", convert_chan_type_to_string(cur_sb_info.chan_rr_node[side][itrack]->type), ix, iy, itrack); break; case IN_PORT: fprintf(fp, "%s[%d][%d]_in[%d] ", convert_chan_type_to_string(cur_sb_info.chan_rr_node[side][itrack]->type), ix, iy, itrack); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File: %s [LINE%d]) Invalid direction of port sb[%d][%d] Channel node[%d] track[%d]!\n", __FILE__, __LINE__, cur_sb_info.x, cur_sb_info.y, side, itrack); exit(1); } } fprintf(fp, "\n"); fprintf(fp, "+ "); /* Dump OPINs of adjacent CLBs */ for (inode = 0; inode < cur_sb_info.num_opin_rr_nodes[side]; inode++) { fprint_grid_side_pin_with_given_index(fp, cur_sb_info.opin_rr_node[side][inode]->ptc_num, cur_sb_info.opin_rr_node_grid_side[side][inode], cur_sb_info.opin_rr_node[side][inode]->xlow, cur_sb_info.opin_rr_node[side][inode]->ylow); } fprintf(fp, "\n"); fprintf(fp, "+ "); } /* Local Vdd and Gnd */ fprintf(fp, "svdd sgnd\n"); /* Specify the head of scan-chain */ if (SPICE_SRAM_SCAN_CHAIN == sram_spice_orgz_type) { fprintf(fp, "***** Head of scan-chain *****\n"); fprintf(fp, "Rsb[%d][%d]_sc_head sb[%d][%d]_sc_head %s[%d]->in 0\n", x, y, x, y, sram_spice_model->prefix, sram_spice_model->cnt); } /* Put down all the multiplexers */ for (side = 0; side < cur_sb_info.num_sides; side++) { fprintf(fp, "***** %s side Multiplexers *****\n", convert_side_index_to_string(side)); for (itrack = 0; itrack < cur_sb_info.chan_width[side]; itrack++) { assert((CHANX == cur_sb_info.chan_rr_node[side][itrack]->type) ||(CHANY == cur_sb_info.chan_rr_node[side][itrack]->type)); /* We care INC_DIRECTION tracks at this side*/ if (OUT_PORT != cur_sb_info.chan_rr_node_direction[side][itrack]) { continue; } fprint_switch_box_interc(fp, cur_sb_info, side, cur_sb_info.chan_rr_node[side][itrack]); } } /* Specify the tail of scan-chain */ if (SPICE_SRAM_SCAN_CHAIN == sram_spice_orgz_type) { fprintf(fp, "***** Tail of scan-chain *****\n"); fprintf(fp, "Rsb[%d][%d]_sc_tail sb[%d][%d]_sc_tail %s[%d]->in 0\n", x, y, x, y, sram_spice_model->prefix, sram_spice_model->cnt); } fprintf(fp, ".eom\n"); /* Close the file*/ fclose(fp); /* Add fname to the linked list */ routing_spice_subckt_file_path_head = add_one_subckt_file_name_to_llist(routing_spice_subckt_file_path_head, fname); /* Free */ my_free(fname); return; } /* SRC rr_node is the IPIN of a grid.*/ void fprint_connection_box_short_interc(FILE* fp, t_cb cur_cb_info, t_rr_node* src_rr_node) { t_rr_node* drive_rr_node = NULL; int iedge, check_flag; int xlow, ylow, height, side, index; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* Check */ assert((!(0 > cur_cb_info.x))&&(!(cur_cb_info.x > (nx + 1)))); assert((!(0 > cur_cb_info.y))&&(!(cur_cb_info.y > (ny + 1)))); assert(1 == src_rr_node->fan_in); /* Check the driver*/ drive_rr_node = &(rr_node[src_rr_node->prev_node]); assert((CHANX == drive_rr_node->type)||(CHANY == drive_rr_node->type)); check_flag = 0; for (iedge = 0; iedge < drive_rr_node->num_edges; iedge++) { if (src_rr_node == &(rr_node[drive_rr_node->edges[iedge]])) { check_flag++; } } assert(1 == check_flag); xlow = src_rr_node->xlow; ylow = src_rr_node->ylow; height = grid[xlow][ylow].offset; /* Call the zero-resistance model */ switch(cur_cb_info.type) { case CHANX: fprintf(fp, "Rcbx[%d][%d]_grid[%d][%d]_pin[%d] ", cur_cb_info.x, cur_cb_info.y, xlow, ylow + height, src_rr_node->ptc_num); break; case CHANY: fprintf(fp, "Rcby[%d][%d]_grid[%d][%d]_pin[%d] ", cur_cb_info.x, cur_cb_info.y, xlow, ylow + height, src_rr_node->ptc_num); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of channel!\n", __FILE__, __LINE__); exit(1); } /* Input port*/ assert(IPIN == src_rr_node->type); /* Search all the sides of a SB, see this drive_rr_node is an INPUT of this SB */ get_rr_node_side_and_index_in_cb_info(src_rr_node, cur_cb_info, OUT_PORT, &side, &index); /* We need to be sure that drive_rr_node is part of the SB */ assert((-1 != index)&&(-1 != side)); fprint_grid_side_pin_with_given_index(fp, cur_cb_info.ipin_rr_node[side][index]->ptc_num, cur_cb_info.ipin_rr_node_grid_side[side][index], xlow, ylow); /* output port -- > connect to the output at middle point of a channel */ fprintf(fp, "%s[%d][%d]_midout[%d] ", convert_chan_type_to_string(drive_rr_node->type), cur_cb_info.x, cur_cb_info.y, drive_rr_node->ptc_num); /* End */ fprintf(fp, "0\n"); return; } void fprint_connection_box_mux(FILE* fp, t_cb cur_cb_info, t_rr_node* src_rr_node) { int mux_size, cur_num_sram, ilevel; t_rr_node** drive_rr_nodes = NULL; int inode, mux_level, path_id, switch_index; t_spice_model* mux_spice_model = NULL; int num_mux_sram_bits = 0; int* mux_sram_bits = NULL; t_rr_type drive_rr_node_type = NUM_RR_TYPES; int xlow, ylow, /* offset,*/ side, index; char* sram_vdd_port_name = NULL; /* Check the file handler*/ if (NULL == fp) { vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid file handler.\n", __FILE__, __LINE__); exit(1); } /* 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)))); /* Find drive_rr_nodes*/ mux_size = src_rr_node->num_drive_rr_nodes; drive_rr_nodes = src_rr_node->drive_rr_nodes; /* Configuration bits for MUX*/ path_id = DEFAULT_PATH_ID; for (inode = 0; inode < mux_size; inode++) { if (drive_rr_nodes[inode] == &(rr_node[src_rr_node->prev_node])) { path_id = inode; break; } } switch_index = src_rr_node->drive_switches[DEFAULT_SWITCH_ID]; mux_spice_model = switch_inf[switch_index].spice_model; /* Call the MUX SPICE model */ fprintf(fp, "X%s_size%d[%d] ", mux_spice_model->prefix, mux_size, mux_spice_model->cnt); /* Global ports */ if (0 < rec_fprint_spice_model_global_ports(fp, mux_spice_model, FALSE)) { fprintf(fp, "+ "); } /* Check drive_rr_nodes type, should be the same*/ for (inode = 0; inode < mux_size; inode++) { if (NUM_RR_TYPES == drive_rr_node_type) { drive_rr_node_type = drive_rr_nodes[inode]->type; } else { assert(drive_rr_node_type == drive_rr_nodes[inode]->type); assert((CHANX == drive_rr_nodes[inode]->type)||(CHANY == drive_rr_nodes[inode]->type)); } } /* input port*/ for (inode = 0; inode < mux_size; inode++) { fprintf(fp, "%s[%d][%d]_midout[%d] ", convert_chan_type_to_string(drive_rr_nodes[inode]->type), cur_cb_info.x, cur_cb_info.y, drive_rr_nodes[inode]->ptc_num); } /* output port*/ xlow = src_rr_node->xlow; ylow = src_rr_node->ylow; /* offset = grid[xlow][ylow].offset; */ assert(IPIN == src_rr_node->type); /* Search all the sides of a CB, see this drive_rr_node is an INPUT of this SB */ get_rr_node_side_and_index_in_cb_info(src_rr_node, cur_cb_info, OUT_PORT, &side, &index); /* We need to be sure that drive_rr_node is part of the CB */ assert((-1 != index)&&(-1 != side)); fprint_grid_side_pin_with_given_index(fp, cur_cb_info.ipin_rr_node[side][index]->ptc_num, cur_cb_info.ipin_rr_node_grid_side[side][index], xlow, ylow); switch (mux_spice_model->design_tech) { case SPICE_MODEL_DESIGN_CMOS: decode_cmos_mux_sram_bits(mux_spice_model, mux_size, path_id, &num_mux_sram_bits, &mux_sram_bits, &mux_level); break; case SPICE_MODEL_DESIGN_RRAM: decode_rram_mux(mux_spice_model, mux_size, path_id, &num_mux_sram_bits, &mux_sram_bits, &mux_level); break; default: vpr_printf(TIO_MESSAGE_ERROR,"(File:%s,[LINE%d])Invalid design technology for spice model (%s)!\n", __FILE__, __LINE__, mux_spice_model->name); } /* Print SRAMs that configure this MUX */ /* Get current counter of mem_bits, bl and wl */ cur_num_sram = get_sram_orgz_info_num_mem_bit(sram_spice_orgz_info); 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*/ fprintf(fp, "svdd sgnd %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", mux_spice_model->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"); /* Call SRAM subckts*/ /* Give the VDD port name for SRAMs */ sram_vdd_port_name = (char*)my_malloc(sizeof(char)* (strlen(spice_tb_global_vdd_cb_sram_port_name) + 1 )); sprintf(sram_vdd_port_name, "%s", spice_tb_global_vdd_cb_sram_port_name); /* Now Print SRAMs one by one */ for (ilevel = 0; ilevel < num_mux_sram_bits; ilevel++) { fprint_spice_one_sram_subckt(fp, sram_spice_orgz_info, mux_spice_model, sram_vdd_port_name); } /* Store the configuraion bit to linked-list */ add_sram_conf_bits_to_llist(sram_spice_orgz_info, cur_num_sram, num_mux_sram_bits, mux_sram_bits); /* Update spice_model counter */ mux_spice_model->cnt++; /* Free */ my_free(mux_sram_bits); my_free(sram_vdd_port_name); return; } void fprint_connection_box_interc(FILE* fp, t_cb cur_cb_info, t_rr_node* src_rr_node) { /* 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)))); if (1 == src_rr_node->fan_in) { /* Print a direct connection*/ fprint_connection_box_short_interc(fp, cur_cb_info, src_rr_node); } else if (1 < src_rr_node->fan_in) { /* Print the multiplexer, fan_in >= 2 */ fprint_connection_box_mux(fp, cur_cb_info, src_rr_node); } /*Nothing should be done else*/ return; } /* Print connection boxes * Print the sub-circuit of a connection Box (Type: [CHANX|CHANY]) * Actually it is very similiar to switch box but * the difference is connection boxes connect Grid INPUT Pins to channels * TODO: merge direct connections into CB * -------------- -------------- * | | | | * | Grid | ChanY | Grid | * | [x][y+1] | [x][y] | [x+1][y+1] | * | | Connection | | * -------------- Box_Y[x][y] -------------- * ---------- * ChanX | Switch | ChanX * [x][y] | Box | [x+1][y] * Connection | [x][y] | Connection * Box_X[x][y] ---------- Box_X[x+1][y] * -------------- -------------- * | | | | * | Grid | ChanY | Grid | * | [x][y] | [x][y-1] | [x+1][y] | * | | Connection | | * --------------Box_Y[x][y-1]-------------- */ void fprint_routing_connection_box_subckt(char* subckt_dir, t_cb cur_cb_info, int LL_num_rr_nodes, t_rr_node* LL_rr_node, t_ivec*** LL_rr_node_indices, boolean compact_routing_hierarchy) { int itrack, inode, side, x, y; int side_cnt = 0; FILE* fp = NULL; char* fname = NULL; /* Identify the type of connection box * Create file handler */ switch(cur_cb_info.type) { case CHANX: fp = spice_create_one_subckt_file(subckt_dir, "Connection Block X-channel ", cbx_spice_file_name_prefix, cur_cb_info.x, cur_cb_info.y, &fname); break; case CHANY: fp = spice_create_one_subckt_file(subckt_dir, "Connection Block Y-channel ", cby_spice_file_name_prefix, cur_cb_info.x, cur_cb_info.y, &fname); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of connection box!\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)))); x = cur_cb_info.x; y = cur_cb_info.y; /* Print the definition of subckt*/ fprintf(fp, ".subckt "); /* Identify the type of connection box */ switch(cur_cb_info.type) { case CHANX: fprintf(fp, "cbx[%d][%d] ", cur_cb_info.x, cur_cb_info.y); break; case CHANY: fprintf(fp, "cby[%d][%d] ", cur_cb_info.x, cur_cb_info.y); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of connection box!\n", __FILE__, __LINE__); exit(1); } fprintf(fp, "\n"); /* Print the ports of channels*/ /*connect to the mid point of a track*/ /* Get the chan_rr_nodes: Only one side of a cb_info has chan_rr_nodes*/ side_cnt = 0; for (side = 0; side < cur_cb_info.num_sides; side++) { /* Bypass side with zero channel width */ if (0 == cur_cb_info.chan_width[side]) { continue; } assert (0 < cur_cb_info.chan_width[side]); side_cnt++; for (itrack = 0; itrack < cur_cb_info.chan_width[side]; itrack++) { fprintf(fp, "+ "); fprintf(fp, "%s[%d][%d]_midout[%d] ", convert_chan_type_to_string(cur_cb_info.type), cur_cb_info.x, cur_cb_info.y, itrack); fprintf(fp, "\n"); } } /*check side_cnt */ assert(1 == side_cnt); side_cnt = 0; /* Print the ports of grids*/ /* only check ipin_rr_nodes of cur_cb_info */ for (side = 0; side < cur_cb_info.num_sides; side++) { /* Bypass side with zero IPINs*/ if (0 == cur_cb_info.num_ipin_rr_nodes[side]) { continue; } side_cnt++; assert(0 < cur_cb_info.num_ipin_rr_nodes[side]); assert(NULL != cur_cb_info.ipin_rr_node[side]); for (inode = 0; inode < cur_cb_info.num_ipin_rr_nodes[side]; inode++) { fprintf(fp, "+ "); /* Print each INPUT Pins of a grid */ fprint_grid_side_pin_with_given_index(fp, cur_cb_info.ipin_rr_node[side][inode]->ptc_num, cur_cb_info.ipin_rr_node_grid_side[side][inode], cur_cb_info.ipin_rr_node[side][inode]->xlow, cur_cb_info.ipin_rr_node[side][inode]->ylow); fprintf(fp, "\n"); } } /* Make sure only 2 sides of IPINs are printed */ assert((1 == side_cnt)||(2 == side_cnt)); /* subckt definition ends with svdd and sgnd*/ fprintf(fp, "+ "); fprintf(fp, "svdd sgnd\n"); /* Specify the head of scan-chain */ if (SPICE_SRAM_SCAN_CHAIN == sram_spice_orgz_type) { switch(cur_cb_info.type) { case CHANX: fprintf(fp, "***** Head of scan-chain *****\n"); fprintf(fp, "Rcbx[%d][%d]_sc_head cbx[%d][%d]_sc_head %s[%d]->in 0\n", x, y, x, y, sram_spice_model->prefix, sram_spice_model->cnt); case CHANY: fprintf(fp, "***** Head of scan-chain *****\n"); fprintf(fp, "Rcby[%d][%d]_sc_head cby[%d][%d]_sc_head %s[%d]->in 0\n", x, y, x, y, sram_spice_model->prefix, sram_spice_model->cnt); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of channel!\n", __FILE__, __LINE__); exit(1); } } /* Print multiplexers or direct interconnect, * According to the rr_node lists in cbx_info or cby_info */ side_cnt = 0; 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++) { fprint_connection_box_interc(fp, cur_cb_info, cur_cb_info.ipin_rr_node[side][inode]); } } /* Make sure only 2 sides of IPINs are printed */ assert((1 == side_cnt)||(2 == side_cnt)); /* Specify the tail of scan-chain */ if (SPICE_SRAM_SCAN_CHAIN == sram_spice_orgz_type) { switch(cur_cb_info.type) { case CHANX: fprintf(fp, "***** Tail of scan-chain *****\n"); fprintf(fp, "Rcbx[%d][%d]_sc_tail cbx[%d][%d]_sc_tail %s[%d]->in 0\n", x, y, x, y, sram_spice_model->prefix, sram_spice_model->cnt); case CHANY: fprintf(fp, "***** Tail of scan-chain *****\n"); fprintf(fp, "Rcby[%d][%d]_sc_tail cby[%d][%d]_sc_tail %s[%d]->in 0\n", x, y, x, y, sram_spice_model->prefix, sram_spice_model->cnt); break; default: vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of channel!\n", __FILE__, __LINE__); exit(1); } } fprintf(fp, ".eom\n"); /* Close the file*/ fclose(fp); /* Add fname to the linked list */ routing_spice_subckt_file_path_head = add_one_subckt_file_name_to_llist(routing_spice_subckt_file_path_head, fname); /* Free */ my_free(fname); return; } /* Top Function*/ /* Build the routing resource SPICE sub-circuits*/ void generate_spice_routing_resources(char* subckt_dir, t_arch arch, t_det_routing_arch* routing_arch, int LL_num_rr_nodes, t_rr_node* LL_rr_node, t_ivec*** LL_rr_node_indices, boolean compact_routing_hierarchy) { int ix, iy; assert(UNI_DIRECTIONAL == routing_arch->directionality); /* Two major tasks: * 1. Generate sub-circuits for Routing Channels * 2. Generate sub-circuits for Switch Boxes */ /* Now: First task: Routing channels * Sub-circuits are named as chanx[ix][iy] or chany[ix][iy] for horizontal or vertical channels * each channels consist of a number of routing tracks. (Actually they are metal wires) * We only support single-driver routing architecture. * The direction is defined as INC_DIRECTION ------> and DEC_DIRECTION <-------- for chanx * The direction is defined as INC_DIRECTION /|\ and DEC_DIRECTION | for chany * | | * | | * | \|/ * For INC_DIRECTION chanx, the inputs are at the left of channels, the outputs are at the right of channels * For DEC_DIRECTION chanx, the inputs are at the right of channels, the outputs are at the left of channels * For INC_DIRECTION chany, the inputs are at the bottom of channels, the outputs are at the top of channels * For DEC_DIRECTION chany, the inputs are at the top of channels, the outputs are at the bottom of channels */ /* X - channels [1...nx][0..ny]*/ vpr_printf(TIO_MESSAGE_INFO, "Writing X-direction Channels...\n"); for (iy = 0; iy < (ny + 1); iy++) { for (ix = 1; ix < (nx + 1); ix++) { fprint_routing_chan_subckt(subckt_dir, ix, iy, CHANX, LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices, arch.num_segments, arch.Segments); } } /* Y - channels [1...ny][0..nx]*/ vpr_printf(TIO_MESSAGE_INFO, "Writing Y-direction Channels...\n"); for (ix = 0; ix < (nx + 1); ix++) { for (iy = 1; iy < (ny + 1); iy++) { fprint_routing_chan_subckt(subckt_dir, ix, iy, CHANY, LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices, arch.num_segments, arch.Segments); } } /* Switch Boxes*/ vpr_printf(TIO_MESSAGE_INFO, "Writing Switch Boxes...\n"); for (ix = 0; ix < (nx + 1); ix++) { for (iy = 0; iy < (ny + 1); iy++) { update_spice_models_routing_index_low(ix, iy, SOURCE, arch.spice->num_spice_model, arch.spice->spice_models); fprint_routing_switch_box_subckt(subckt_dir, sb_info[ix][iy], LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices, compact_routing_hierarchy); update_spice_models_routing_index_high(ix, iy, SOURCE, arch.spice->num_spice_model, arch.spice->spice_models); } } /* Connection Boxes */ vpr_printf(TIO_MESSAGE_INFO, "Writing Connection Boxes...\n"); /* X - channels [1...nx][0..ny]*/ for (iy = 0; iy < (ny + 1); iy++) { for (ix = 1; ix < (nx + 1); ix++) { update_spice_models_routing_index_low(ix, iy, CHANX, arch.spice->num_spice_model, arch.spice->spice_models); /* Check if this cby_info exists, it may be covered by a heterogenous block */ if ((TRUE == is_cb_exist(CHANX, ix, iy)) &&(0 < count_cb_info_num_ipin_rr_nodes(cbx_info[ix][iy]))) { fprint_routing_connection_box_subckt(subckt_dir, cbx_info[ix][iy], LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices, compact_routing_hierarchy); } update_spice_models_routing_index_high(ix, iy, CHANX, arch.spice->num_spice_model, arch.spice->spice_models); } } /* Y - channels [1...ny][0..nx]*/ for (ix = 0; ix < (nx + 1); ix++) { for (iy = 1; iy < (ny + 1); iy++) { update_spice_models_routing_index_low(ix, iy, CHANY, arch.spice->num_spice_model, arch.spice->spice_models); /* Check if this cby_info exists, it may be covered by a heterogenous block */ if ((TRUE == is_cb_exist(CHANY, ix, iy)) &&(0 < count_cb_info_num_ipin_rr_nodes(cby_info[ix][iy]))) { fprint_routing_connection_box_subckt(subckt_dir, cby_info[ix][iy], LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices, compact_routing_hierarchy); } update_spice_models_routing_index_high(ix, iy, CHANY, arch.spice->num_spice_model, arch.spice->spice_models); } } /* Output a header file for all the routing blocks */ vpr_printf(TIO_MESSAGE_INFO,"Generating header file for routing submodules...\n"); spice_print_subckt_header_file(routing_spice_subckt_file_path_head, subckt_dir, routing_spice_file_name); return; }