OpenFPGA/vpr7_x2p/vpr/SRC/pack/output_blif.c

560 lines
18 KiB
C

/*
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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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);
}