/* Jason Luu 2008 Print blif representation of circuit Assumptions: Assumes first valid rr input to node is the correct rr input Assumes clocks are routed globally */ #include #include #include #include #include "util.h" #include "vpr_types.h" #include "globals.h" #include "output_blif.h" #include "ReadOptions.h" #define LINELENGTH 1024 #define TABLENGTH 1 /****************** Subroutines local to this module ************************/ /**************** Subroutine definitions ************************************/ static void print_string(const char *str_ptr, int *column, FILE * fpout) { /* Prints string without making any lines longer than LINELENGTH. Column * * points to the column in which the next character will go (both used and * * updated), and fpout points to the output file. */ int len; len = strlen(str_ptr); if (len + 3 > LINELENGTH) { vpr_printf(TIO_MESSAGE_ERROR, "in print_string: String %s is too long for desired maximum line length.\n", str_ptr); exit(1); } if (*column + len + 2 > LINELENGTH) { fprintf(fpout, "\\ \n"); *column = TABLENGTH; } fprintf(fpout, "%s ", str_ptr); *column += len + 1; } static void print_net_name(int inet, int *column, FILE * fpout) { /* This routine prints out the vpack_net name (or open) and limits the * * length of a line to LINELENGTH characters by using \ to continue * * lines. net_num is the index of the vpack_net to be printed, while * * column points to the current printing column (column is both * * used and updated by this routine). fpout is the output file * * pointer. */ const char *str_ptr; if (inet == OPEN) str_ptr = "open"; else str_ptr = vpack_net[inet].name; print_string(str_ptr, column, fpout); } static int find_fanin_rr_node(t_pb *cur_pb, enum PORTS type, int rr_node_index) { /* finds first fanin rr_node */ t_pb *parent, *sibling, *child; int net_num, irr_node; int i, j, k, ichild_type, ichild_inst; int hack_empty_route_through; t_pb_graph_node *hack_empty_pb_graph_node; hack_empty_route_through = OPEN; net_num = rr_node[rr_node_index].net_num; parent = cur_pb->parent_pb; if (net_num == OPEN) { return OPEN; } if (type == IN_PORT) { /* check parent inputs for valid connection */ for (i = 0; i < parent->pb_graph_node->num_input_ports; i++) { for (j = 0; j < parent->pb_graph_node->num_input_pins[i]; j++) { irr_node = parent->pb_graph_node->input_pins[i][j].pin_count_in_cluster; if (rr_node[irr_node].net_num == net_num) { if (cur_pb->pb_graph_node->pb_type->model && strcmp( cur_pb->pb_graph_node->pb_type->model->name, MODEL_LATCH) == 0) { /* HACK: latches are special becuase LUTs can be set to route-through mode for them I will assume that the input to a LATCH can always come from a parent input pin this only works for hierarchical soft logic structures that follow LUT -> LATCH design must do it better later */ return irr_node; } hack_empty_route_through = irr_node; for (k = 0; k < rr_node[irr_node].num_edges; k++) { if (rr_node[irr_node].edges[k] == rr_node_index) { return irr_node; } } } } } /* check parent clocks for valid connection */ for (i = 0; i < parent->pb_graph_node->num_clock_ports; i++) { for (j = 0; j < parent->pb_graph_node->num_clock_pins[i]; j++) { irr_node = parent->pb_graph_node->clock_pins[i][j].pin_count_in_cluster; if (rr_node[irr_node].net_num == net_num) { for (k = 0; k < rr_node[irr_node].num_edges; k++) { if (rr_node[irr_node].edges[k] == rr_node_index) { return irr_node; } } } } } /* check siblings for connection */ if (parent) { for (ichild_type = 0; ichild_type < parent->pb_graph_node->pb_type->modes[parent->mode].num_pb_type_children; ichild_type++) { for (ichild_inst = 0; ichild_inst < parent->pb_graph_node->pb_type->modes[parent->mode].pb_type_children[ichild_type].num_pb; ichild_inst++) { if (parent->child_pbs[ichild_type] && parent->child_pbs[ichild_type][ichild_inst].name != NULL) { sibling = &parent->child_pbs[ichild_type][ichild_inst]; for (i = 0; i < sibling->pb_graph_node->num_output_ports; i++) { for (j = 0; j < sibling->pb_graph_node->num_output_pins[i]; j++) { irr_node = sibling->pb_graph_node->output_pins[i][j].pin_count_in_cluster; if (rr_node[irr_node].net_num == net_num) { for (k = 0; k < rr_node[irr_node].num_edges; k++) { if (rr_node[irr_node].edges[k] == rr_node_index) { return irr_node; } } } } } } else { /* hack just in case routing is down through an empty cluster */ hack_empty_pb_graph_node = &parent->pb_graph_node->child_pb_graph_nodes[ichild_type][0][ichild_inst]; for (i = 0; i < hack_empty_pb_graph_node->num_output_ports; i++) { for (j = 0; j < hack_empty_pb_graph_node->num_output_pins[i]; j++) { irr_node = hack_empty_pb_graph_node->output_pins[i][j].pin_count_in_cluster; if (rr_node[irr_node].net_num == net_num) { for (k = 0; k < rr_node[irr_node].num_edges; k++) { if (rr_node[irr_node].edges[k] == rr_node_index) { return irr_node; } } } } } } } } } } else { assert(type == OUT_PORT); /* check children for connection */ for (ichild_type = 0; ichild_type < cur_pb->pb_graph_node->pb_type->modes[cur_pb->mode].num_pb_type_children; ichild_type++) { for (ichild_inst = 0; ichild_inst < cur_pb->pb_graph_node->pb_type->modes[cur_pb->mode].pb_type_children[ichild_type].num_pb; ichild_inst++) { if (cur_pb->child_pbs[ichild_type] && cur_pb->child_pbs[ichild_type][ichild_inst].name != NULL) { child = &cur_pb->child_pbs[ichild_type][ichild_inst]; for (i = 0; i < child->pb_graph_node->num_output_ports; i++) { for (j = 0; j < child->pb_graph_node->num_output_pins[i]; j++) { irr_node = child->pb_graph_node->output_pins[i][j].pin_count_in_cluster; if (rr_node[irr_node].net_num == net_num) { for (k = 0; k < rr_node[irr_node].num_edges; k++) { if (rr_node[irr_node].edges[k] == rr_node_index) { return irr_node; } } hack_empty_route_through = irr_node; } } } } } } /* If not in children, check current pb inputs for valid connection */ for (i = 0; i < cur_pb->pb_graph_node->num_input_ports; i++) { for (j = 0; j < cur_pb->pb_graph_node->num_input_pins[i]; j++) { irr_node = cur_pb->pb_graph_node->input_pins[i][j].pin_count_in_cluster; if (rr_node[irr_node].net_num == net_num) { hack_empty_route_through = irr_node; for (k = 0; k < rr_node[irr_node].num_edges; k++) { if (rr_node[irr_node].edges[k] == rr_node_index) { return irr_node; } } } } } } /* TODO: Once I find a way to output routing in empty blocks then code should never reach here, for now, return OPEN */ vpr_printf(TIO_MESSAGE_INFO, "Use hack in blif dumper (do properly later): connecting net %s #%d for pb %s type %s\n", vpack_net[net_num].name, net_num, cur_pb->name, cur_pb->pb_graph_node->pb_type->name); assert(hack_empty_route_through != OPEN); return hack_empty_route_through; } static void print_primitive(FILE *fpout, int iblk) { t_pb *pb; int clb_index; int i, j, k, node_index; int in_port_index, out_port_index, clock_port_index; struct s_linked_vptr *truth_table; const t_pb_type *pb_type; pb = logical_block[iblk].pb; pb_type = pb->pb_graph_node->pb_type; clb_index = logical_block[iblk].clb_index; if (logical_block[iblk].type == VPACK_INPAD || logical_block[iblk].type == VPACK_OUTPAD) { /* do nothing */ } else if (logical_block[iblk].type == VPACK_LATCH) { fprintf(fpout, ".latch "); in_port_index = 0; out_port_index = 0; clock_port_index = 0; for (i = 0; i < pb_type->num_ports; i++) { if (pb_type->ports[i].type == IN_PORT && pb_type->ports[i].is_clock == FALSE) { assert(pb_type->ports[i].num_pins == 1); assert(logical_block[iblk].input_nets[i][0] != OPEN); node_index = pb->pb_graph_node->input_pins[in_port_index][0].pin_count_in_cluster; fprintf(fpout, "clb_%d_rr_node_%d ", clb_index, find_fanin_rr_node(pb, pb_type->ports[i].type, node_index)); in_port_index++; } } for (i = 0; i < pb_type->num_ports; i++) { if (pb_type->ports[i].type == OUT_PORT) { assert(pb_type->ports[i].num_pins == 1 && out_port_index == 0); node_index = pb->pb_graph_node->output_pins[out_port_index][0].pin_count_in_cluster; fprintf(fpout, "clb_%d_rr_node_%d re ", clb_index, node_index); out_port_index++; } } for (i = 0; i < pb_type->num_ports; i++) { if (pb_type->ports[i].type == IN_PORT && pb_type->ports[i].is_clock == TRUE) { assert(logical_block[iblk].clock_net != OPEN); node_index = pb->pb_graph_node->clock_pins[clock_port_index][0].pin_count_in_cluster; fprintf(fpout, "clb_%d_rr_node_%d 2", clb_index, find_fanin_rr_node(pb, pb_type->ports[i].type, node_index)); clock_port_index++; } } fprintf(fpout, "\n"); } else if (logical_block[iblk].type == VPACK_COMB) { if (strcmp(logical_block[iblk].model->name, "names") == 0) { fprintf(fpout, ".names "); in_port_index = 0; out_port_index = 0; for (i = 0; i < pb_type->num_ports; i++) { if (pb_type->ports[i].type == IN_PORT && pb_type->ports[i].is_clock == FALSE) { /* This is a LUT LUTs receive special handling because a LUT has logically equivalent inputs. The intra-logic block router may have taken advantage of logical equivalence so we need to unscramble the inputs when we output the LUT logic. */ for (j = 0; j < pb_type->ports[i].num_pins; j++) { if (logical_block[iblk].input_nets[in_port_index][j] != OPEN) { for (k = 0; k < pb_type->ports[i].num_pins; k++) { node_index = pb->pb_graph_node->input_pins[in_port_index][k].pin_count_in_cluster; if(rr_node[node_index].net_num != OPEN) { if(rr_node[node_index].net_num == logical_block[iblk].input_nets[in_port_index][j]) { fprintf(fpout, "clb_%d_rr_node_%d ", clb_index, find_fanin_rr_node(pb, pb_type->ports[i].type, node_index)); break; } } } if(k == pb_type->ports[i].num_pins) { /* Failed to find LUT input, a netlist error has occurred */ vpr_printf(TIO_MESSAGE_ERROR, "LUT %s missing input %s post packing. This is a VPR internal error, report to vpr@eecg.utoronto.ca\n", logical_block[iblk].name, vpack_net[logical_block[iblk].input_nets[in_port_index][j]].name); exit(1); } } } in_port_index++; } } for (i = 0; i < pb_type->num_ports; i++) { if (pb_type->ports[i].type == OUT_PORT) { for (j = 0; j < pb_type->ports[i].num_pins; j++) { node_index = pb->pb_graph_node->output_pins[out_port_index][j].pin_count_in_cluster; fprintf(fpout, "clb_%d_rr_node_%d\n", clb_index, node_index); } out_port_index++; } } truth_table = logical_block[iblk].truth_table; while (truth_table) { fprintf(fpout, "%s\n", (char *) truth_table->data_vptr); truth_table = truth_table->next; } } else { vpr_printf(TIO_MESSAGE_WARNING, "TODO: Implement blif dumper for subckt %s model %s", logical_block[iblk].name, logical_block[iblk].model->name); } } } static void print_pb(FILE *fpout, t_pb * pb, int clb_index) { int column; int i, j, k; const t_pb_type *pb_type; t_mode *mode; int in_port_index, out_port_index, node_index; pb_type = pb->pb_graph_node->pb_type; mode = &pb_type->modes[pb->mode]; column = 0; if (pb_type->num_modes == 0) { print_primitive(fpout, pb->logical_block); } else { in_port_index = 0; out_port_index = 0; for (i = 0; i < pb_type->num_ports; i++) { if (!pb_type->ports[i].is_clock) { for (j = 0; j < pb_type->ports[i].num_pins; j++) { /* print .blif buffer to represent routing */ column = 0; if (pb_type->ports[i].type == OUT_PORT) { node_index = pb->pb_graph_node->output_pins[out_port_index][j].pin_count_in_cluster; if (rr_node[node_index].net_num != OPEN) { fprintf(fpout, ".names clb_%d_rr_node_%d ", clb_index, find_fanin_rr_node(pb, pb_type->ports[i].type, node_index)); if (pb->parent_pb) { fprintf(fpout, "clb_%d_rr_node_%d ", clb_index, node_index); } else { print_net_name(rr_node[node_index].net_num, &column, fpout); } fprintf(fpout, "\n1 1\n"); if (pb->parent_pb == NULL) { for (k = 1; k <= vpack_net[rr_node[node_index].net_num].num_sinks; k++) { /* output pads pre-pended with "out:", must remove */ if (logical_block[vpack_net[rr_node[node_index].net_num].node_block[k]].type == VPACK_OUTPAD && strcmp( logical_block[vpack_net[rr_node[node_index].net_num].node_block[k]].name + 4, vpack_net[rr_node[node_index].net_num].name) != 0) { fprintf(fpout, ".names clb_%d_rr_node_%d %s", clb_index, find_fanin_rr_node(pb, pb_type->ports[i].type, node_index), logical_block[vpack_net[rr_node[node_index].net_num].node_block[k]].name + 4); fprintf(fpout, "\n1 1\n"); } } } } } else { node_index = pb->pb_graph_node->input_pins[in_port_index][j].pin_count_in_cluster; if (rr_node[node_index].net_num != OPEN) { fprintf(fpout, ".names "); if (pb->parent_pb) { fprintf(fpout, "clb_%d_rr_node_%d ", clb_index, find_fanin_rr_node(pb, pb_type->ports[i].type, node_index)); } else { print_net_name(rr_node[node_index].net_num, &column, fpout); } fprintf(fpout, "clb_%d_rr_node_%d", clb_index, node_index); fprintf(fpout, "\n1 1\n"); } } } } if (pb_type->ports[i].type == OUT_PORT) { out_port_index++; } else { in_port_index++; } } for (i = 0; i < mode->num_pb_type_children; i++) { for (j = 0; j < mode->pb_type_children[i].num_pb; j++) { /* If child pb is not used but routing is used, I must print things differently */ if ((pb->child_pbs[i] != NULL) && (pb->child_pbs[i][j].name != NULL)) { print_pb(fpout, &pb->child_pbs[i][j], clb_index); } else { /* do nothing for now, we'll print something later if needed */ } } } } } static void print_clusters(t_block *clb, int num_clusters, FILE * fpout) { /* Prints out one cluster (clb). Both the external pins and the * * internal connections are printed out. */ int icluster; for (icluster = 0; icluster < num_clusters; icluster++) { rr_node = clb[icluster].pb->rr_graph; if (clb[icluster].type != IO_TYPE) print_pb(fpout, clb[icluster].pb, icluster); } } void output_blif (t_block *clb, int num_clusters, boolean global_clocks, boolean * is_clock, const char *out_fname, boolean skip_clustering) { /* * This routine dumps out the output netlist in a format suitable for * * input to vpr. This routine also dumps out the internal structure of * * the cluster, in essentially a graph based format. */ FILE *fpout; int bnum, column; struct s_linked_vptr *p_io_removed; int i; fpout = my_fopen(out_fname, "w", 0); column = 0; fprintf(fpout, ".model %s\n", blif_circuit_name); /* Print out all input and output pads. */ fprintf(fpout, "\n.inputs "); for (bnum = 0; bnum < num_logical_blocks; bnum++) { if (logical_block[bnum].type == VPACK_INPAD) { print_string(logical_block[bnum].name, &column, fpout); } } p_io_removed = circuit_p_io_removed; while (p_io_removed) { print_string((char*) p_io_removed->data_vptr, &column, fpout); p_io_removed = p_io_removed->next; } column = 0; fprintf(fpout, "\n.outputs "); for (bnum = 0; bnum < num_logical_blocks; bnum++) { if (logical_block[bnum].type == VPACK_OUTPAD) { /* remove output prefix "out:" */ print_string(logical_block[bnum].name + 4, &column, fpout); } } column = 0; fprintf(fpout, "\n\n"); /* print logic of clusters */ print_clusters(clb, num_clusters, fpout); /* handle special case: input goes straight to output without going through any logic */ for (bnum = 0; bnum < num_logical_blocks; bnum++) { if (logical_block[bnum].type == VPACK_INPAD) { for (i = 1; i <= vpack_net[logical_block[bnum].output_nets[0][0]].num_sinks; i++) { if (logical_block[vpack_net[logical_block[bnum].output_nets[0][0]].node_block[i]].type == VPACK_OUTPAD) { fprintf(fpout, ".names "); print_string(logical_block[bnum].name, &column, fpout); print_string( logical_block[vpack_net[logical_block[bnum].output_nets[0][0]].node_block[i]].name + 4, &column, fpout); fprintf(fpout, "\n1 1\n"); } } } } fprintf(fpout, "\n.end\n"); fclose(fpout); }