1379 lines
47 KiB
C
1379 lines
47 KiB
C
|
/**
|
||
|
* Author: Jason Luu
|
||
|
* Date: May 2009
|
||
|
*
|
||
|
* Read a circuit netlist in XML format and populate the netlist data structures for VPR
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <assert.h>
|
||
|
#include "util.h"
|
||
|
#include "hash.h"
|
||
|
#include "vpr_types.h"
|
||
|
#include "vpr_utils.h"
|
||
|
#include "ReadLine.h"
|
||
|
#include "globals.h"
|
||
|
#include "ezxml.h"
|
||
|
#include "read_xml_util.h"
|
||
|
#include "read_netlist.h"
|
||
|
#include "pb_type_graph.h"
|
||
|
#include "cluster_legality.h"
|
||
|
#include "token.h"
|
||
|
#include "rr_graph.h"
|
||
|
|
||
|
static void processPorts(INOUTP ezxml_t Parent, INOUTP t_pb* pb,
|
||
|
INOUTP t_rr_node *rr_graph, INOUTP t_pb** rr_node_to_pb_mapping, INP struct s_hash **vpack_net_hash);
|
||
|
|
||
|
static void processPb(INOUTP ezxml_t Parent, INOUTP t_pb* pb,
|
||
|
INOUTP t_rr_node *rr_graph, INOUTP t_pb **rr_node_to_pb_mapping, INOUTP int *num_primitives,
|
||
|
INP struct s_hash **vpack_net_hash, INP struct s_hash **logical_block_hash, INP int cb_index);
|
||
|
|
||
|
static void processComplexBlock(INOUTP ezxml_t Parent, INOUTP t_block *cb,
|
||
|
INP int index, INOUTP int *num_primitives, INP const t_arch *arch, INP struct s_hash **vpack_net_hash, INP struct s_hash **logical_block_hash);
|
||
|
static struct s_net *alloc_and_init_netlist_from_hash(INP int ncount,
|
||
|
INOUTP struct s_hash **nhash);
|
||
|
|
||
|
static int add_net_to_hash(INOUTP struct s_hash **nhash, INP char *net_name,
|
||
|
INOUTP int *ncount);
|
||
|
|
||
|
static void load_external_nets_and_cb(INP int L_num_blocks,
|
||
|
INP struct s_block block_list[], INP int ncount,
|
||
|
INP struct s_net nlist[], OUTP int *ext_ncount,
|
||
|
OUTP struct s_net **ext_nets, INP char **circuit_clocks);
|
||
|
|
||
|
static void load_internal_cb_nets(INOUTP t_pb *top_level,
|
||
|
INP t_pb_graph_node *pb_graph_node, INOUTP t_rr_node *rr_graph,
|
||
|
INOUTP int * curr_net);
|
||
|
|
||
|
static void alloc_internal_cb_nets(INOUTP t_pb *top_level,
|
||
|
INP t_pb_graph_node *pb_graph_node, INOUTP t_rr_node *rr_graph,
|
||
|
INP int pass);
|
||
|
|
||
|
static void load_internal_cb_rr_graph_net_nums(INP t_rr_node * cur_rr_node,
|
||
|
INP t_rr_node * rr_graph, INOUTP struct s_net * nets,
|
||
|
INOUTP int * curr_net, INOUTP int * curr_sink);
|
||
|
|
||
|
static void mark_constant_generators(INP int L_num_blocks,
|
||
|
INP struct s_block block_list[], INP int ncount,
|
||
|
INOUTP struct s_net nlist[]);
|
||
|
|
||
|
static void mark_constant_generators_rec(INP t_pb *pb, INP t_rr_node *rr_graph,
|
||
|
INOUTP struct s_net nlist[]);
|
||
|
|
||
|
/**
|
||
|
* Initializes the block_list with info from a netlist
|
||
|
* net_file - Name of the netlist file to read
|
||
|
* num_blocks - number of CLBs in netlist
|
||
|
* block_list - array of blocks in netlist [0..num_blocks - 1]
|
||
|
* num_nets - number of nets in netlist
|
||
|
* net_list - nets in netlist [0..num_nets - 1]
|
||
|
*/
|
||
|
void read_netlist(INP const char *net_file, INP const t_arch *arch,
|
||
|
OUTP int *L_num_blocks, OUTP struct s_block *block_list[],
|
||
|
OUTP int *L_num_nets, OUTP struct s_net *net_list[]) {
|
||
|
ezxml_t Cur, Prev, Top;
|
||
|
int i;
|
||
|
const char *Prop;
|
||
|
int bcount;
|
||
|
struct s_block *blist;
|
||
|
int ext_ncount;
|
||
|
struct s_net *ext_nlist;
|
||
|
struct s_hash **vpack_net_hash, **logical_block_hash, *temp_hash;
|
||
|
char **circuit_inputs, **circuit_outputs, **circuit_clocks;
|
||
|
int Count, Len;
|
||
|
|
||
|
int num_primitives = 0;
|
||
|
|
||
|
/* Parse the file */
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "Begin parsing packed FPGA netlist file.\n");
|
||
|
Top = ezxml_parse_file(net_file);
|
||
|
if (NULL == Top) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "Unable to load netlist file '%s'.\n", net_file);
|
||
|
exit(1);
|
||
|
}
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "Finished parsing packed FPGA netlist file.\n");
|
||
|
|
||
|
/* Root node should be block */
|
||
|
CheckElement(Top, "block");
|
||
|
|
||
|
/* Check top-level netlist attributes */
|
||
|
Prop = FindProperty(Top, "name", TRUE);
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "Netlist generated from file '%s'.\n", Prop);
|
||
|
ezxml_set_attr(Top, "name", NULL);
|
||
|
|
||
|
Prop = FindProperty(Top, "instance", TRUE);
|
||
|
if (strcmp(Prop, "FPGA_packed_netlist[0]") != 0) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Expected instance to be \"FPGA_packed_netlist[0]\", found %s.",
|
||
|
Top->line, Prop);
|
||
|
exit(1);
|
||
|
}
|
||
|
ezxml_set_attr(Top, "instance", NULL);
|
||
|
|
||
|
/* Parse top-level netlist I/Os */
|
||
|
Cur = FindElement(Top, "inputs", TRUE);
|
||
|
circuit_inputs = GetNodeTokens(Cur);
|
||
|
FreeNode(Cur);
|
||
|
Cur = FindElement(Top, "outputs", TRUE);
|
||
|
circuit_outputs = GetNodeTokens(Cur);
|
||
|
FreeNode(Cur);
|
||
|
|
||
|
Cur = FindElement(Top, "clocks", TRUE);
|
||
|
CountTokensInString(Cur->txt, &Count, &Len);
|
||
|
if (Count > 0) {
|
||
|
circuit_clocks = GetNodeTokens(Cur);
|
||
|
} else {
|
||
|
circuit_clocks = NULL;
|
||
|
}
|
||
|
FreeNode(Cur);
|
||
|
|
||
|
/* Parse all CLB blocks and all nets*/
|
||
|
bcount = CountChildren(Top, "block", 1);
|
||
|
blist = (struct s_block *) my_calloc(bcount, sizeof(t_block));
|
||
|
|
||
|
/* create quick hash look up for vpack_net and logical_block
|
||
|
Also reset logical block data structure for pb
|
||
|
*/
|
||
|
vpack_net_hash = alloc_hash_table();
|
||
|
logical_block_hash = alloc_hash_table();
|
||
|
for (i = 0; i < num_logical_nets; i++) {
|
||
|
temp_hash = insert_in_hash_table(vpack_net_hash, vpack_net[i].name, i);
|
||
|
assert(temp_hash->count == 1);
|
||
|
}
|
||
|
for (i = 0; i < num_logical_blocks; i++) {
|
||
|
temp_hash = insert_in_hash_table(logical_block_hash, logical_block[i].name, i);
|
||
|
logical_block[i].pb = NULL;
|
||
|
assert(temp_hash->count == 1);
|
||
|
}
|
||
|
|
||
|
/* Prcoess netlist */
|
||
|
|
||
|
Cur = Top->child;
|
||
|
i = 0;
|
||
|
while (Cur) {
|
||
|
if (0 == strcmp(Cur->name, "block")) {
|
||
|
CheckElement(Cur, "block");
|
||
|
processComplexBlock(Cur, blist, i, &num_primitives, arch, vpack_net_hash, logical_block_hash);
|
||
|
Prev = Cur;
|
||
|
Cur = Cur->next;
|
||
|
FreeNode(Prev);
|
||
|
i++;
|
||
|
} else {
|
||
|
Cur = Cur->next;
|
||
|
}
|
||
|
}
|
||
|
assert(i == bcount);
|
||
|
assert(num_primitives == num_logical_blocks);
|
||
|
|
||
|
/* Error check */
|
||
|
for (i = 0; i < num_logical_blocks; i++) {
|
||
|
if (logical_block[i].pb == NULL) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, ".blif file and .net file do not match, .net file missing atom %s.\n",
|
||
|
logical_block[i].name);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
/* TODO: Add additional check to make sure net connections match */
|
||
|
|
||
|
|
||
|
mark_constant_generators(bcount, blist, num_logical_nets, vpack_net);
|
||
|
load_external_nets_and_cb(bcount, blist, num_logical_nets, vpack_net, &ext_ncount,
|
||
|
&ext_nlist, circuit_clocks);
|
||
|
|
||
|
/* TODO: create this function later
|
||
|
check_top_IO_matches_IO_blocks(circuit_inputs, circuit_outputs, circuit_clocks, blist, bcount);
|
||
|
*/
|
||
|
|
||
|
FreeTokens(&circuit_inputs);
|
||
|
FreeTokens(&circuit_outputs);
|
||
|
if (circuit_clocks)
|
||
|
FreeTokens(&circuit_clocks);
|
||
|
FreeNode(Top);
|
||
|
|
||
|
/* load mapping between external nets and all nets */
|
||
|
/* jluu TODO: Should use local variables here then assign to globals later, clean up later */
|
||
|
clb_to_vpack_net_mapping = (int *) my_malloc(ext_ncount * sizeof(int));
|
||
|
vpack_to_clb_net_mapping = (int *) my_malloc(num_logical_nets * sizeof(int));
|
||
|
for (i = 0; i < num_logical_nets; i++) {
|
||
|
vpack_to_clb_net_mapping[i] = OPEN;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < ext_ncount; i++) {
|
||
|
temp_hash = get_hash_entry(vpack_net_hash, ext_nlist[i].name);
|
||
|
assert(temp_hash != NULL);
|
||
|
clb_to_vpack_net_mapping[i] = temp_hash->index;
|
||
|
vpack_to_clb_net_mapping[temp_hash->index] = i;
|
||
|
}
|
||
|
|
||
|
/* Return blocks and nets */
|
||
|
*L_num_blocks = bcount;
|
||
|
*block_list = blist;
|
||
|
*L_num_nets = ext_ncount;
|
||
|
*net_list = ext_nlist;
|
||
|
|
||
|
free_hash_table(logical_block_hash);
|
||
|
free_hash_table(vpack_net_hash);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* XML parser to populate CLB info and to update nets with the nets of this CLB
|
||
|
* Parent - XML tag for this CLB
|
||
|
* clb - Array of CLBs in the netlist
|
||
|
* index - index of the CLB to allocate and load information into
|
||
|
* vpack_net_hash - hashtable of all nets in blif netlist
|
||
|
* logical_block_hash - hashtable of all atoms in blif netlist
|
||
|
*/
|
||
|
static void processComplexBlock(INOUTP ezxml_t Parent, INOUTP t_block *cb,
|
||
|
INP int index, INOUTP int *num_primitives, INP const t_arch *arch, INP struct s_hash **vpack_net_hash, INP struct s_hash **logical_block_hash)
|
||
|
{
|
||
|
|
||
|
const char *Prop;
|
||
|
boolean found;
|
||
|
int num_tokens = 0;
|
||
|
t_token *tokens;
|
||
|
int i;
|
||
|
const t_pb_type * pb_type = NULL;
|
||
|
|
||
|
/* parse cb attributes */
|
||
|
cb[index].pb = (t_pb*)my_calloc(1, sizeof(t_pb));
|
||
|
|
||
|
Prop = FindProperty(Parent, "name", TRUE);
|
||
|
cb[index].name = my_strdup(Prop);
|
||
|
cb[index].pb->name = my_strdup(Prop);
|
||
|
ezxml_set_attr(Parent, "name", NULL);
|
||
|
|
||
|
Prop = FindProperty(Parent, "instance", TRUE);
|
||
|
tokens = GetTokensFromString(Prop, &num_tokens);
|
||
|
ezxml_set_attr(Parent, "instance", NULL);
|
||
|
if (num_tokens != 4 || tokens[0].type != TOKEN_STRING
|
||
|
|| tokens[1].type != TOKEN_OPEN_SQUARE_BRACKET
|
||
|
|| tokens[2].type != TOKEN_INT
|
||
|
|| tokens[3].type != TOKEN_CLOSE_SQUARE_BRACKET) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Unknown syntax for instance %s in %s. Expected pb_type[instance_number].\n",
|
||
|
Parent->line, Prop, Parent->name);
|
||
|
exit(1);
|
||
|
}
|
||
|
assert(my_atoi(tokens[2].data) == index);
|
||
|
found = FALSE;
|
||
|
for (i = 0; i < num_types; i++) {
|
||
|
if (strcmp(type_descriptors[i].name, tokens[0].data) == 0) {
|
||
|
cb[index].type = &type_descriptors[i];
|
||
|
pb_type = cb[index].type->pb_type;
|
||
|
found = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!found) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Unknown cb type %s for cb %s #%d.\n",
|
||
|
Parent->line, Prop, cb[index].name, index);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/* Parse all pbs and CB internal nets*/
|
||
|
cb[index].pb->logical_block = OPEN;
|
||
|
cb[index].pb->pb_graph_node = cb[index].type->pb_graph_head;
|
||
|
num_rr_nodes = cb[index].pb->pb_graph_node->total_pb_pins;
|
||
|
rr_node = (t_rr_node*)my_calloc((num_rr_nodes * 2) + cb[index].type->pb_type->num_input_pins
|
||
|
+ cb[index].type->pb_type->num_output_pins + cb[index].type->pb_type->num_clock_pins,
|
||
|
sizeof(t_rr_node));
|
||
|
alloc_and_load_rr_graph_for_pb_graph_node(cb[index].pb->pb_graph_node, arch,
|
||
|
0);
|
||
|
cb[index].pb->rr_node_to_pb_mapping = (t_pb **)my_calloc(cb[index].type->pb_graph_head->total_pb_pins, sizeof(t_pb *));
|
||
|
cb[index].pb->rr_graph = rr_node;
|
||
|
|
||
|
Prop = FindProperty(Parent, "mode", TRUE);
|
||
|
ezxml_set_attr(Parent, "mode", NULL);
|
||
|
|
||
|
found = FALSE;
|
||
|
for (i = 0; i < pb_type->num_modes; i++) {
|
||
|
if (strcmp(Prop, pb_type->modes[i].name) == 0) {
|
||
|
cb[index].pb->mode = i;
|
||
|
found = TRUE;
|
||
|
}
|
||
|
}
|
||
|
if (!found) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Unknown mode %s for cb %s #%d.\n",
|
||
|
Parent->line, Prop, cb[index].name, index);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
processPb(Parent, cb[index].pb, cb[index].pb->rr_graph, cb[index].pb->rr_node_to_pb_mapping, num_primitives, vpack_net_hash, logical_block_hash, index);
|
||
|
|
||
|
cb[index].nets = (int *)my_malloc(cb[index].type->num_pins * sizeof(int));
|
||
|
for (i = 0; i < cb[index].type->num_pins; i++) {
|
||
|
cb[index].nets[i] = OPEN;
|
||
|
}
|
||
|
alloc_internal_cb_nets(cb[index].pb, cb[index].pb->pb_graph_node,
|
||
|
cb[index].pb->rr_graph, 1);
|
||
|
alloc_internal_cb_nets(cb[index].pb, cb[index].pb->pb_graph_node,
|
||
|
cb[index].pb->rr_graph, 2);
|
||
|
i = 0;
|
||
|
load_internal_cb_nets(cb[index].pb, cb[index].pb->pb_graph_node,
|
||
|
cb[index].pb->rr_graph, &i);
|
||
|
freeTokens(tokens, num_tokens);
|
||
|
#if 0
|
||
|
/* print local nets */
|
||
|
for (i = 0; i < cb[index].pb->num_local_nets; i++) {
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "local net %s: ", cb[index].pb->name);
|
||
|
for (j = 0; j <= cb[index].pb->local_nets[i].num_sinks; j++) {
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "%d ", cb[index].pb->local_nets[i].node_block[j]);
|
||
|
}
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "\n");
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* XML parser to populate pb info and to update internal nets of the parent CLB
|
||
|
* Parent - XML tag for this pb_type
|
||
|
* pb - physical block to use
|
||
|
* vpack_net_hash - hashtable of original blif net names and indices
|
||
|
* logical_block_hash - hashtable of original blif atom names and indices
|
||
|
*/
|
||
|
static void processPb(INOUTP ezxml_t Parent, INOUTP t_pb* pb,
|
||
|
INOUTP t_rr_node *rr_graph, INOUTP t_pb** rr_node_to_pb_mapping, INOUTP int *num_primitives,
|
||
|
INP struct s_hash **vpack_net_hash, INP struct s_hash **logical_block_hash, INP int cb_index) {
|
||
|
ezxml_t Cur, Prev, lookahead;
|
||
|
const char *Prop;
|
||
|
const char *instance_type;
|
||
|
int i, j, pb_index;
|
||
|
boolean found;
|
||
|
const t_pb_type *pb_type;
|
||
|
t_token *tokens;
|
||
|
int num_tokens;
|
||
|
struct s_hash *temp_hash;
|
||
|
|
||
|
Cur = FindElement(Parent, "inputs", TRUE);
|
||
|
processPorts(Cur, pb, rr_graph, rr_node_to_pb_mapping, vpack_net_hash);
|
||
|
FreeNode(Cur);
|
||
|
Cur = FindElement(Parent, "outputs", TRUE);
|
||
|
processPorts(Cur, pb, rr_graph, rr_node_to_pb_mapping, vpack_net_hash);
|
||
|
FreeNode(Cur);
|
||
|
Cur = FindElement(Parent, "clocks", TRUE);
|
||
|
processPorts(Cur, pb, rr_graph, rr_node_to_pb_mapping, vpack_net_hash);
|
||
|
FreeNode(Cur);
|
||
|
|
||
|
pb_type = pb->pb_graph_node->pb_type;
|
||
|
if (pb_type->num_modes == 0) {
|
||
|
/* LUT specific optimizations */
|
||
|
if (strcmp(pb_type->blif_model, ".names") == 0) {
|
||
|
pb->lut_pin_remap = (int*)my_malloc(pb_type->num_input_pins * sizeof(int));
|
||
|
for (i = 0; i < pb_type->num_input_pins; i++) {
|
||
|
pb->lut_pin_remap[i] = OPEN;
|
||
|
}
|
||
|
} else {
|
||
|
pb->lut_pin_remap = NULL;
|
||
|
}
|
||
|
temp_hash = get_hash_entry(logical_block_hash, pb->name);
|
||
|
if (temp_hash == NULL) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, ".net file and .blif file do not match, encountered unknown primitive %s in .net file.\n", pb->name);
|
||
|
exit(1);
|
||
|
}
|
||
|
pb->logical_block = temp_hash->index;
|
||
|
assert(logical_block[temp_hash->index].pb == NULL);
|
||
|
logical_block[temp_hash->index].pb = pb;
|
||
|
logical_block[temp_hash->index].clb_index = cb_index;
|
||
|
(*num_primitives)++;
|
||
|
} else {
|
||
|
/* process children of child if exists */
|
||
|
|
||
|
pb->child_pbs = (t_pb **)my_calloc(pb_type->modes[pb->mode].num_pb_type_children,
|
||
|
sizeof(t_pb*));
|
||
|
for (i = 0; i < pb_type->modes[pb->mode].num_pb_type_children; i++) {
|
||
|
pb->child_pbs[i] = (t_pb *)my_calloc(
|
||
|
pb_type->modes[pb->mode].pb_type_children[i].num_pb,
|
||
|
sizeof(t_pb));
|
||
|
for (j = 0; j < pb_type->modes[pb->mode].pb_type_children[i].num_pb; j++) {
|
||
|
pb->child_pbs[i][j].logical_block = OPEN;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Populate info for each physical block */
|
||
|
Cur = Parent->child;
|
||
|
while (Cur) {
|
||
|
if (0 == strcmp(Cur->name, "block")) {
|
||
|
CheckElement(Cur, "block");
|
||
|
|
||
|
instance_type = FindProperty(Cur, "instance", TRUE);
|
||
|
tokens = GetTokensFromString(instance_type, &num_tokens);
|
||
|
ezxml_set_attr(Cur, "instance", NULL);
|
||
|
if (num_tokens != 4 || tokens[0].type != TOKEN_STRING
|
||
|
|| tokens[1].type != TOKEN_OPEN_SQUARE_BRACKET
|
||
|
|| tokens[2].type != TOKEN_INT
|
||
|
|| tokens[3].type != TOKEN_CLOSE_SQUARE_BRACKET) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Unknown syntax for instance %s in %s. Expected pb_type[instance_number].\n",
|
||
|
Cur->line, instance_type, Cur->name);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
found = FALSE;
|
||
|
pb_index = OPEN;
|
||
|
for (i = 0; i < pb_type->modes[pb->mode].num_pb_type_children;
|
||
|
i++) {
|
||
|
if (strcmp(
|
||
|
pb_type->modes[pb->mode].pb_type_children[i].name,
|
||
|
tokens[0].data) == 0) {
|
||
|
if (my_atoi(tokens[2].data)
|
||
|
>= pb_type->modes[pb->mode].pb_type_children[i].num_pb) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Instance number exceeds # of pb available for instance %s in %s.\n",
|
||
|
Cur->line, instance_type, Cur->name);
|
||
|
exit(1);
|
||
|
}
|
||
|
pb_index = my_atoi(tokens[2].data);
|
||
|
if (pb->child_pbs[i][pb_index].pb_graph_node != NULL) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] node is used by two different blocks %s and %s.\n",
|
||
|
Cur->line, instance_type,
|
||
|
pb->child_pbs[i][pb_index].name);
|
||
|
exit(1);
|
||
|
}
|
||
|
pb->child_pbs[i][pb_index].pb_graph_node =
|
||
|
&pb->pb_graph_node->child_pb_graph_nodes[pb->mode][i][pb_index];
|
||
|
found = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!found) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Unknown pb type %s.\n",
|
||
|
Cur->line, instance_type);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
Prop = FindProperty(Cur, "name", TRUE);
|
||
|
ezxml_set_attr(Cur, "name", NULL);
|
||
|
if (0 != strcmp(Prop, "open")) {
|
||
|
pb->child_pbs[i][pb_index].name = my_strdup(Prop);
|
||
|
|
||
|
/* Parse all pbs and CB internal nets*/
|
||
|
pb->child_pbs[i][pb_index].logical_block = OPEN;
|
||
|
|
||
|
Prop = FindProperty(Cur, "mode", FALSE);
|
||
|
if (Prop) {
|
||
|
ezxml_set_attr(Cur, "mode", NULL);
|
||
|
}
|
||
|
pb->child_pbs[i][pb_index].mode = 0;
|
||
|
found = FALSE;
|
||
|
for (j = 0;
|
||
|
j
|
||
|
< pb->child_pbs[i][pb_index].pb_graph_node->pb_type->num_modes;
|
||
|
j++) {
|
||
|
if (strcmp(Prop,
|
||
|
pb->child_pbs[i][pb_index].pb_graph_node->pb_type->modes[j].name)
|
||
|
== 0) {
|
||
|
pb->child_pbs[i][pb_index].mode = j;
|
||
|
found = TRUE;
|
||
|
}
|
||
|
}
|
||
|
if (!found
|
||
|
&& pb->child_pbs[i][pb_index].pb_graph_node->pb_type->num_modes
|
||
|
!= 0) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Unknown mode %s for cb %s #%d.\n",
|
||
|
Cur->line, Prop, pb->child_pbs[i][pb_index].name, pb_index);
|
||
|
exit(1);
|
||
|
}
|
||
|
pb->child_pbs[i][pb_index].parent_pb = pb;
|
||
|
pb->child_pbs[i][pb_index].rr_graph = pb->rr_graph;
|
||
|
|
||
|
processPb(Cur, &pb->child_pbs[i][pb_index], rr_graph, rr_node_to_pb_mapping, num_primitives, vpack_net_hash, logical_block_hash, cb_index);
|
||
|
} else {
|
||
|
/* physical block has no used primitives but it may have used routing */
|
||
|
pb->child_pbs[i][pb_index].name = NULL;
|
||
|
pb->child_pbs[i][pb_index].logical_block = OPEN;
|
||
|
lookahead = FindElement(Cur, "outputs", FALSE);
|
||
|
if (lookahead != NULL) {
|
||
|
lookahead = FindFirstElement(lookahead, "port", TRUE);
|
||
|
Prop = FindProperty(Cur, "mode", FALSE);
|
||
|
if (Prop) {
|
||
|
ezxml_set_attr(Cur, "mode", NULL);
|
||
|
}
|
||
|
pb->child_pbs[i][pb_index].mode = 0;
|
||
|
found = FALSE;
|
||
|
for (j = 0;
|
||
|
j
|
||
|
< pb->child_pbs[i][pb_index].pb_graph_node->pb_type->num_modes;
|
||
|
j++) {
|
||
|
if (strcmp(Prop,
|
||
|
pb->child_pbs[i][pb_index].pb_graph_node->pb_type->modes[j].name)
|
||
|
== 0) {
|
||
|
pb->child_pbs[i][pb_index].mode = j;
|
||
|
found = TRUE;
|
||
|
}
|
||
|
}
|
||
|
if (!found
|
||
|
&& pb->child_pbs[i][pb_index].pb_graph_node->pb_type->num_modes
|
||
|
!= 0) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Unknown mode %s for cb %s #%d.\n",
|
||
|
Cur->line, Prop, pb->child_pbs[i][pb_index].name, pb_index);
|
||
|
exit(1);
|
||
|
}
|
||
|
pb->child_pbs[i][pb_index].parent_pb = pb;
|
||
|
pb->child_pbs[i][pb_index].rr_graph = pb->rr_graph;
|
||
|
processPb(Cur, &pb->child_pbs[i][pb_index], rr_graph, rr_node_to_pb_mapping, num_primitives, vpack_net_hash, logical_block_hash, cb_index);
|
||
|
}
|
||
|
}
|
||
|
Prev = Cur;
|
||
|
Cur = Cur->next;
|
||
|
FreeNode(Prev);
|
||
|
freeTokens(tokens, num_tokens);
|
||
|
} else {
|
||
|
Cur = Cur->next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Allocates memory for nets and loads the name of the net so that it can be identified and loaded with
|
||
|
* more complete information later
|
||
|
* ncount - number of nets in the hashtable of nets
|
||
|
* nhash - hashtable of nets
|
||
|
* returns array of nets stored in hashtable
|
||
|
*/
|
||
|
static struct s_net *alloc_and_init_netlist_from_hash(INP int ncount,
|
||
|
INOUTP struct s_hash **nhash) {
|
||
|
struct s_net *nlist;
|
||
|
struct s_hash_iterator hash_iter;
|
||
|
struct s_hash *curr_net;
|
||
|
int i;
|
||
|
|
||
|
nlist = (struct s_net *)my_calloc(ncount, sizeof(struct s_net));
|
||
|
|
||
|
hash_iter = start_hash_table_iterator();
|
||
|
curr_net = get_next_hash(nhash, &hash_iter);
|
||
|
while (curr_net != NULL) {
|
||
|
assert(nlist[curr_net->index].name == NULL);
|
||
|
nlist[curr_net->index].name = my_strdup(curr_net->name);
|
||
|
nlist[curr_net->index].num_sinks = curr_net->count - 1;
|
||
|
|
||
|
nlist[curr_net->index].node_block = (int *)my_malloc(
|
||
|
curr_net->count * sizeof(int));
|
||
|
nlist[curr_net->index].node_block_pin = (int *)my_malloc(
|
||
|
curr_net->count * sizeof(int));
|
||
|
nlist[curr_net->index].is_global = FALSE;
|
||
|
for (i = 0; i < curr_net->count; i++) {
|
||
|
nlist[curr_net->index].node_block[i] = OPEN;
|
||
|
nlist[curr_net->index].node_block_pin[i] = OPEN;
|
||
|
}
|
||
|
curr_net = get_next_hash(nhash, &hash_iter);
|
||
|
}
|
||
|
return nlist;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds net to hashtable of nets. If the net is "open", then this is a keyword so do not add it.
|
||
|
* If the net already exists, increase the count on that net
|
||
|
*/
|
||
|
static int add_net_to_hash(INOUTP struct s_hash **nhash, INP char *net_name,
|
||
|
INOUTP int *ncount) {
|
||
|
struct s_hash *hash_value;
|
||
|
|
||
|
if (strcmp(net_name, "open") == 0) {
|
||
|
return OPEN;
|
||
|
}
|
||
|
|
||
|
hash_value = insert_in_hash_table(nhash, net_name, *ncount);
|
||
|
if (hash_value->count == 1) {
|
||
|
assert(*ncount == hash_value->index);
|
||
|
(*ncount)++;
|
||
|
}
|
||
|
return hash_value->index;
|
||
|
}
|
||
|
|
||
|
static void processPorts(INOUTP ezxml_t Parent, INOUTP t_pb* pb,
|
||
|
t_rr_node *rr_graph, INOUTP t_pb** rr_node_to_pb_mapping, INP struct s_hash **vpack_net_hash) {
|
||
|
|
||
|
int i, j, in_port, out_port, clock_port, num_tokens;
|
||
|
ezxml_t Cur, Prev;
|
||
|
const char *Prop;
|
||
|
char **pins;
|
||
|
char *port_name, *interconnect_name;
|
||
|
int rr_node_index;
|
||
|
t_pb_graph_pin *** pin_node;
|
||
|
int *num_ptrs, num_sets;
|
||
|
struct s_hash *temp_hash;
|
||
|
boolean found;
|
||
|
|
||
|
Cur = Parent->child;
|
||
|
while (Cur) {
|
||
|
if (0 == strcmp(Cur->name, "port")) {
|
||
|
CheckElement(Cur, "port");
|
||
|
|
||
|
Prop = FindProperty(Cur, "name", TRUE);
|
||
|
ezxml_set_attr(Cur, "name", NULL);
|
||
|
|
||
|
in_port = out_port = clock_port = 0;
|
||
|
found = FALSE;
|
||
|
for (i = 0; i < pb->pb_graph_node->pb_type->num_ports; i++) {
|
||
|
if (0
|
||
|
== strcmp(pb->pb_graph_node->pb_type->ports[i].name,
|
||
|
Prop)) {
|
||
|
found = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
if (pb->pb_graph_node->pb_type->ports[i].is_clock
|
||
|
&& pb->pb_graph_node->pb_type->ports[i].type
|
||
|
== IN_PORT) {
|
||
|
clock_port++;
|
||
|
} else if (!pb->pb_graph_node->pb_type->ports[i].is_clock
|
||
|
&& pb->pb_graph_node->pb_type->ports[i].type
|
||
|
== IN_PORT) {
|
||
|
in_port++;
|
||
|
} else {
|
||
|
assert(
|
||
|
pb->pb_graph_node->pb_type->ports[i].type == OUT_PORT);
|
||
|
out_port++;
|
||
|
}
|
||
|
}
|
||
|
if (!found) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Unknown port %s for pb %s[%d].\n",
|
||
|
Cur->line, Prop, pb->pb_graph_node->pb_type->name,
|
||
|
pb->pb_graph_node->placement_index);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
pins = GetNodeTokens(Cur);
|
||
|
num_tokens = CountTokens(pins);
|
||
|
if (0 == strcmp(Parent->name, "inputs")) {
|
||
|
if (num_tokens != pb->pb_graph_node->num_input_pins[in_port]) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Incorrect # pins %d found for port %s for pb %s[%d].\n",
|
||
|
Cur->line, num_tokens, Prop,
|
||
|
pb->pb_graph_node->pb_type->name,
|
||
|
pb->pb_graph_node->placement_index);
|
||
|
exit(1);
|
||
|
}
|
||
|
} else if (0 == strcmp(Parent->name, "outputs")) {
|
||
|
if (num_tokens
|
||
|
!= pb->pb_graph_node->num_output_pins[out_port]) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Incorrect # pins %d found for port %s for pb %s[%d].\n",
|
||
|
Cur->line, num_tokens, Prop,
|
||
|
pb->pb_graph_node->pb_type->name,
|
||
|
pb->pb_graph_node->placement_index);
|
||
|
exit(1);
|
||
|
}
|
||
|
} else {
|
||
|
if (num_tokens
|
||
|
!= pb->pb_graph_node->num_clock_pins[clock_port]) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Incorrect # pins %d found for port %s for pb %s[%d].\n",
|
||
|
Cur->line, num_tokens, Prop,
|
||
|
pb->pb_graph_node->pb_type->name,
|
||
|
pb->pb_graph_node->placement_index);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
if (0 == strcmp(Parent->name, "inputs")
|
||
|
|| 0 == strcmp(Parent->name, "clocks")) {
|
||
|
if (pb->parent_pb == NULL) {
|
||
|
/* top-level, connections are nets to route */
|
||
|
for (i = 0; i < num_tokens; i++) {
|
||
|
if (0 == strcmp(Parent->name, "inputs"))
|
||
|
rr_node_index =
|
||
|
pb->pb_graph_node->input_pins[in_port][i].pin_count_in_cluster;
|
||
|
else
|
||
|
rr_node_index =
|
||
|
pb->pb_graph_node->clock_pins[clock_port][i].pin_count_in_cluster;
|
||
|
if (strcmp(pins[i], "open") != 0) {
|
||
|
temp_hash = get_hash_entry(vpack_net_hash, pins[i]);
|
||
|
if (temp_hash == NULL) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, ".blif and .net do not match, unknown net %s found in .net file.\n.", pins[i]);
|
||
|
}
|
||
|
rr_graph[rr_node_index].net_num = temp_hash->index;
|
||
|
}
|
||
|
rr_node_to_pb_mapping[rr_node_index] = pb;
|
||
|
}
|
||
|
} else {
|
||
|
for (i = 0; i < num_tokens; i++) {
|
||
|
if (0 == strcmp(pins[i], "open")) {
|
||
|
continue;
|
||
|
}
|
||
|
interconnect_name = strstr(pins[i], "->");
|
||
|
*interconnect_name = '\0';
|
||
|
interconnect_name += 2;
|
||
|
port_name = pins[i];
|
||
|
pin_node =
|
||
|
alloc_and_load_port_pin_ptrs_from_string(
|
||
|
pb->pb_graph_node->pb_type->parent_mode->interconnect[0].line_num,
|
||
|
pb->pb_graph_node->parent_pb_graph_node,
|
||
|
pb->pb_graph_node->parent_pb_graph_node->child_pb_graph_nodes[pb->parent_pb->mode],
|
||
|
port_name, &num_ptrs, &num_sets, TRUE,
|
||
|
TRUE);
|
||
|
assert(num_sets == 1 && num_ptrs[0] == 1);
|
||
|
if (0 == strcmp(Parent->name, "inputs"))
|
||
|
rr_node_index =
|
||
|
pb->pb_graph_node->input_pins[in_port][i].pin_count_in_cluster;
|
||
|
else
|
||
|
rr_node_index =
|
||
|
pb->pb_graph_node->clock_pins[clock_port][i].pin_count_in_cluster;
|
||
|
rr_graph[rr_node_index].prev_node =
|
||
|
pin_node[0][0]->pin_count_in_cluster;
|
||
|
rr_node_to_pb_mapping[rr_node_index] = pb;
|
||
|
found = FALSE;
|
||
|
for (j = 0; j < pin_node[0][0]->num_output_edges; j++) {
|
||
|
if (0
|
||
|
== strcmp(interconnect_name,
|
||
|
pin_node[0][0]->output_edges[j]->interconnect->name)) {
|
||
|
found = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
for (j = 0; j < num_sets; j++) {
|
||
|
free(pin_node[j]);
|
||
|
}
|
||
|
free(pin_node);
|
||
|
free(num_ptrs);
|
||
|
if (!found) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Unknown interconnect %s connecting to pin %s.\n",
|
||
|
Cur->line, interconnect_name, port_name);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (0 == strcmp(Parent->name, "outputs")) {
|
||
|
if (pb->pb_graph_node->pb_type->num_modes == 0) {
|
||
|
/* primitives are drivers of nets */
|
||
|
for (i = 0; i < num_tokens; i++) {
|
||
|
rr_node_index =
|
||
|
pb->pb_graph_node->output_pins[out_port][i].pin_count_in_cluster;
|
||
|
if (strcmp(pins[i], "open") != 0) {
|
||
|
temp_hash = get_hash_entry(vpack_net_hash, pins[i]);
|
||
|
if (temp_hash == NULL) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, ".blif and .net do not match, unknown net %s found in .net file.\n", pins[i]);
|
||
|
}
|
||
|
rr_graph[rr_node_index].net_num = temp_hash->index;
|
||
|
}
|
||
|
rr_node_to_pb_mapping[rr_node_index] = pb;
|
||
|
}
|
||
|
} else {
|
||
|
for (i = 0; i < num_tokens; i++) {
|
||
|
if (0 == strcmp(pins[i], "open")) {
|
||
|
continue;
|
||
|
}
|
||
|
interconnect_name = strstr(pins[i], "->");
|
||
|
*interconnect_name = '\0';
|
||
|
interconnect_name += 2;
|
||
|
port_name = pins[i];
|
||
|
pin_node =
|
||
|
alloc_and_load_port_pin_ptrs_from_string(
|
||
|
pb->pb_graph_node->pb_type->modes[pb->mode].interconnect->line_num,
|
||
|
pb->pb_graph_node,
|
||
|
pb->pb_graph_node->child_pb_graph_nodes[pb->mode],
|
||
|
port_name, &num_ptrs, &num_sets, TRUE,
|
||
|
TRUE);
|
||
|
assert(num_sets == 1 && num_ptrs[0] == 1);
|
||
|
rr_node_index =
|
||
|
pb->pb_graph_node->output_pins[out_port][i].pin_count_in_cluster;
|
||
|
rr_graph[rr_node_index].prev_node =
|
||
|
pin_node[0][0]->pin_count_in_cluster;
|
||
|
rr_node_to_pb_mapping[rr_node_index] = pb;
|
||
|
found = FALSE;
|
||
|
for (j = 0; j < pin_node[0][0]->num_output_edges; j++) {
|
||
|
if (0
|
||
|
== strcmp(interconnect_name,
|
||
|
pin_node[0][0]->output_edges[j]->interconnect->name)) {
|
||
|
found = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
for (j = 0; j < num_sets; j++) {
|
||
|
free(pin_node[j]);
|
||
|
}
|
||
|
free(pin_node);
|
||
|
free(num_ptrs);
|
||
|
if (!found) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Unknown interconnect %s connecting to pin %s.\n",
|
||
|
Cur->line, interconnect_name, port_name);
|
||
|
exit(1);
|
||
|
}
|
||
|
interconnect_name -= 2;
|
||
|
*interconnect_name = '-';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FreeTokens(&pins);
|
||
|
|
||
|
Prev = Cur;
|
||
|
Cur = Cur->next;
|
||
|
FreeNode(Prev);
|
||
|
} else {
|
||
|
Cur = Cur->next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This function updates the nets list and the connections between that list and the complex block
|
||
|
*/
|
||
|
static void load_external_nets_and_cb(INP int L_num_blocks,
|
||
|
INP struct s_block block_list[], INP int ncount,
|
||
|
INP struct s_net nlist[], OUTP int *ext_ncount,
|
||
|
OUTP struct s_net **ext_nets, INP char **circuit_clocks) {
|
||
|
int i, j, k, ipin;
|
||
|
struct s_hash **ext_nhash;
|
||
|
t_rr_node *rr_graph;
|
||
|
t_pb_graph_pin *pb_graph_pin;
|
||
|
int *count;
|
||
|
int netnum, num_tokens;
|
||
|
|
||
|
*ext_ncount = 0;
|
||
|
ext_nhash = alloc_hash_table();
|
||
|
|
||
|
/* Assumes that complex block pins are ordered inputs, outputs, globals */
|
||
|
|
||
|
/* Determine the external nets of complex block */
|
||
|
for (i = 0; i < L_num_blocks; i++) {
|
||
|
ipin = 0;
|
||
|
if (block_list[i].type->pb_type->num_input_pins
|
||
|
+ block_list[i].type->pb_type->num_output_pins
|
||
|
+ block_list[i].type->pb_type->num_clock_pins
|
||
|
!= block_list[i].type->num_pins
|
||
|
/ block_list[i].type->capacity) {
|
||
|
|
||
|
assert(0);
|
||
|
}
|
||
|
|
||
|
/* First determine nets external to complex blocks */
|
||
|
assert(
|
||
|
block_list[i].type->pb_type->num_input_pins + block_list[i].type->pb_type->num_output_pins + block_list[i].type->pb_type->num_clock_pins == block_list[i].type->num_pins / block_list[i].type->capacity);
|
||
|
|
||
|
rr_graph = block_list[i].pb->rr_graph;
|
||
|
for (j = 0; j < block_list[i].pb->pb_graph_node->num_input_ports; j++) {
|
||
|
for (k = 0; k < block_list[i].pb->pb_graph_node->num_input_pins[j];
|
||
|
k++) {
|
||
|
pb_graph_pin =
|
||
|
&block_list[i].pb->pb_graph_node->input_pins[j][k];
|
||
|
assert(pb_graph_pin->pin_count_in_cluster == ipin);
|
||
|
if (rr_graph[pb_graph_pin->pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
block_list[i].nets[ipin] =
|
||
|
add_net_to_hash(ext_nhash,
|
||
|
nlist[rr_graph[pb_graph_pin->pin_count_in_cluster].net_num].name,
|
||
|
ext_ncount);
|
||
|
} else {
|
||
|
block_list[i].nets[ipin] = OPEN;
|
||
|
}
|
||
|
ipin++;
|
||
|
}
|
||
|
}
|
||
|
for (j = 0; j < block_list[i].pb->pb_graph_node->num_output_ports;
|
||
|
j++) {
|
||
|
for (k = 0; k < block_list[i].pb->pb_graph_node->num_output_pins[j];
|
||
|
k++) {
|
||
|
pb_graph_pin =
|
||
|
&block_list[i].pb->pb_graph_node->output_pins[j][k];
|
||
|
assert(pb_graph_pin->pin_count_in_cluster == ipin);
|
||
|
if (rr_graph[pb_graph_pin->pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
block_list[i].nets[ipin] =
|
||
|
add_net_to_hash(ext_nhash,
|
||
|
nlist[rr_graph[pb_graph_pin->pin_count_in_cluster].net_num].name,
|
||
|
ext_ncount);
|
||
|
} else {
|
||
|
block_list[i].nets[ipin] = OPEN;
|
||
|
}
|
||
|
ipin++;
|
||
|
}
|
||
|
}
|
||
|
for (j = 0; j < block_list[i].pb->pb_graph_node->num_clock_ports; j++) {
|
||
|
for (k = 0; k < block_list[i].pb->pb_graph_node->num_clock_pins[j];
|
||
|
k++) {
|
||
|
pb_graph_pin =
|
||
|
&block_list[i].pb->pb_graph_node->clock_pins[j][k];
|
||
|
assert(pb_graph_pin->pin_count_in_cluster == ipin);
|
||
|
if (rr_graph[pb_graph_pin->pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
block_list[i].nets[ipin] =
|
||
|
add_net_to_hash(ext_nhash,
|
||
|
nlist[rr_graph[pb_graph_pin->pin_count_in_cluster].net_num].name,
|
||
|
ext_ncount);
|
||
|
} else {
|
||
|
block_list[i].nets[ipin] = OPEN;
|
||
|
}
|
||
|
ipin++;
|
||
|
}
|
||
|
}
|
||
|
for (j = ipin; j < block_list[i].type->num_pins; j++) {
|
||
|
block_list[i].nets[ipin] = OPEN;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* alloc and partially load the list of external nets */
|
||
|
(*ext_nets) = alloc_and_init_netlist_from_hash(*ext_ncount, ext_nhash);
|
||
|
/* Load global nets */
|
||
|
num_tokens = CountTokens(circuit_clocks);
|
||
|
|
||
|
count = (int *)my_calloc(*ext_ncount, sizeof(int));
|
||
|
|
||
|
/* complete load of external nets so that each net points back to the blocks */
|
||
|
for (i = 0; i < L_num_blocks; i++) {
|
||
|
ipin = 0;
|
||
|
rr_graph = block_list[i].pb->rr_graph;
|
||
|
for (j = 0; j < block_list[i].type->num_pins; j++) {
|
||
|
netnum = block_list[i].nets[j];
|
||
|
if (netnum != OPEN) {
|
||
|
if (RECEIVER
|
||
|
== block_list[i].type->class_inf[block_list[i].type->pin_class[j]].type) {
|
||
|
count[netnum]++;
|
||
|
if(count[netnum] > (*ext_nets)[netnum].num_sinks) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "net %s #%d inconsistency, expected %d terminals but encountered %d terminals, it is likely net terminal is disconnected in netlist file.\n",
|
||
|
(*ext_nets)[netnum].name, netnum, count[netnum], (*ext_nets)[netnum].num_sinks);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
(*ext_nets)[netnum].node_block[count[netnum]] = i;
|
||
|
(*ext_nets)[netnum].node_block_pin[count[netnum]] = j;
|
||
|
|
||
|
(*ext_nets)[netnum].is_global = block_list[i].type->is_global_pin[j]; /* Error check performed later to ensure no mixing of global and non-global signals */
|
||
|
} else {
|
||
|
assert(
|
||
|
DRIVER == block_list[i].type->class_inf[block_list[i].type->pin_class[j]].type);
|
||
|
assert((*ext_nets)[netnum].node_block[0] == OPEN);
|
||
|
(*ext_nets)[netnum].node_block[0] = i;
|
||
|
(*ext_nets)[netnum].node_block_pin[0] = j;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/* Error check global and non global signals */
|
||
|
for (i = 0; i < *ext_ncount; i++) {
|
||
|
for (j = 1; j <= (*ext_nets)[i].num_sinks; j++) {
|
||
|
if (block_list[(*ext_nets)[i].node_block[j]].type->is_global_pin[(*ext_nets)[i].node_block_pin[j]] != (*ext_nets)[i].is_global) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "Netlist attempts to connect net %s to both global and non-global pins.\n",
|
||
|
(*ext_nets)[i].name);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
for (j = 0; j < num_tokens; j++) {
|
||
|
if (strcmp(circuit_clocks[j], (*ext_nets)[i].name) == 0) {
|
||
|
assert((*ext_nets)[i].is_global == TRUE); /* above code should have caught this case, if not, then bug in code */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
free(count);
|
||
|
free_hash_table(ext_nhash);
|
||
|
}
|
||
|
|
||
|
/* Recursive function that fills rr_graph of cb with net numbers starting at the given rr_node */
|
||
|
static int count_sinks_internal_cb_rr_graph_net_nums(
|
||
|
INP t_rr_node * cur_rr_node, INP t_rr_node * rr_graph) {
|
||
|
int i;
|
||
|
int count = 0;
|
||
|
|
||
|
for (i = 0; i < cur_rr_node->num_edges; i++) {
|
||
|
if (&rr_graph[rr_graph[cur_rr_node->edges[i]].prev_node]
|
||
|
== cur_rr_node) {
|
||
|
assert(
|
||
|
rr_graph[cur_rr_node->edges[i]].net_num == OPEN || rr_graph[cur_rr_node->edges[i]].net_num == cur_rr_node->net_num);
|
||
|
count += count_sinks_internal_cb_rr_graph_net_nums(
|
||
|
&rr_graph[cur_rr_node->edges[i]], rr_graph);
|
||
|
}
|
||
|
}
|
||
|
if (count == 0) {
|
||
|
return 1; /* terminal node */
|
||
|
} else {
|
||
|
return count;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Recursive function that fills rr_graph of cb with net numbers starting at the given rr_node */
|
||
|
static void load_internal_cb_rr_graph_net_nums(INP t_rr_node * cur_rr_node,
|
||
|
INP t_rr_node * rr_graph, INOUTP struct s_net * nets,
|
||
|
INOUTP int * curr_net, INOUTP int * curr_sink) {
|
||
|
int i;
|
||
|
|
||
|
boolean terminal;
|
||
|
terminal = TRUE;
|
||
|
|
||
|
for (i = 0; i < cur_rr_node->num_edges; i++) {
|
||
|
if (&rr_graph[rr_graph[cur_rr_node->edges[i]].prev_node]
|
||
|
== cur_rr_node) {
|
||
|
/* TODO: If multiple edges to same node (should not happen in reasonable design) this always
|
||
|
selects the last edge, need to be smart about it in future (ie. select fastest edge */
|
||
|
assert(
|
||
|
rr_graph[cur_rr_node->edges[i]].net_num == OPEN || rr_graph[cur_rr_node->edges[i]].net_num == cur_rr_node->net_num);
|
||
|
rr_graph[cur_rr_node->edges[i]].net_num = cur_rr_node->net_num;
|
||
|
rr_graph[cur_rr_node->edges[i]].prev_edge = i;
|
||
|
load_internal_cb_rr_graph_net_nums(&rr_graph[cur_rr_node->edges[i]],
|
||
|
rr_graph, nets, curr_net, curr_sink);
|
||
|
terminal = FALSE;
|
||
|
}
|
||
|
}
|
||
|
if (terminal == TRUE) {
|
||
|
/* Since the routing node index is known, assign that instead of the more obscure node block */
|
||
|
nets[*curr_net].node_block[*curr_sink] =
|
||
|
cur_rr_node->pb_graph_pin->pin_count_in_cluster;
|
||
|
nets[*curr_net].node_block_pin[*curr_sink] = OPEN;
|
||
|
nets[*curr_net].node_block_port[*curr_sink] = OPEN;
|
||
|
(*curr_sink)++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Load internal cb nets and fill rr_graph of cb with net numbers */
|
||
|
static void load_internal_cb_nets(INOUTP t_pb *top_level,
|
||
|
INP t_pb_graph_node *pb_graph_node, INOUTP t_rr_node *rr_graph,
|
||
|
INOUTP int * curr_net) {
|
||
|
int i, j, k;
|
||
|
const t_pb_type *pb_type;
|
||
|
int temp, size;
|
||
|
struct s_net * nets;
|
||
|
|
||
|
pb_type = pb_graph_node->pb_type;
|
||
|
|
||
|
nets = top_level->local_nets;
|
||
|
|
||
|
temp = 0;
|
||
|
|
||
|
if (pb_graph_node->parent_pb_graph_node == NULL) { /* determine nets driven from inputs at top level */
|
||
|
*curr_net = 0;
|
||
|
for (i = 0; i < pb_graph_node->num_input_ports; i++) {
|
||
|
for (j = 0; j < pb_graph_node->num_input_pins[i]; j++) {
|
||
|
if (rr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
load_internal_cb_rr_graph_net_nums(
|
||
|
&rr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster],
|
||
|
rr_graph, nets, curr_net, &temp);
|
||
|
assert(temp == nets[*curr_net].num_sinks);
|
||
|
temp = 0;
|
||
|
size =
|
||
|
strlen(pb_graph_node->pb_type->name)
|
||
|
+ pb_graph_node->placement_index / 10
|
||
|
+ i / 10 + j / 10
|
||
|
+ pb_graph_node->input_pins[i][j].pin_count_in_cluster
|
||
|
/ 10 + 26;
|
||
|
nets[*curr_net].name = (char *)my_calloc(size, sizeof(char));
|
||
|
sprintf(nets[*curr_net].name,
|
||
|
"%s[%d].input[%d][%d].pin[%d]",
|
||
|
pb_graph_node->pb_type->name,
|
||
|
pb_graph_node->placement_index, i, j,
|
||
|
pb_graph_node->input_pins[i][j].pin_count_in_cluster);
|
||
|
(*curr_net)++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for (i = 0; i < pb_graph_node->num_clock_ports; i++) {
|
||
|
for (j = 0; j < pb_graph_node->num_clock_pins[i]; j++) {
|
||
|
if (rr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
load_internal_cb_rr_graph_net_nums(
|
||
|
&rr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster],
|
||
|
rr_graph, nets, curr_net, &temp);
|
||
|
assert(temp == nets[*curr_net].num_sinks);
|
||
|
temp = 0;
|
||
|
nets[*curr_net].is_global = TRUE;
|
||
|
size =
|
||
|
strlen(pb_graph_node->pb_type->name)
|
||
|
+ pb_graph_node->placement_index / 10
|
||
|
+ i / 10 + j / 10
|
||
|
+ pb_graph_node->clock_pins[i][j].pin_count_in_cluster
|
||
|
/ 10 + 26;
|
||
|
nets[*curr_net].name = (char *)my_calloc(size, sizeof(char));
|
||
|
sprintf(nets[*curr_net].name,
|
||
|
"%s[%d].clock[%d][%d].pin[%d]",
|
||
|
pb_graph_node->pb_type->name,
|
||
|
pb_graph_node->placement_index, i, j,
|
||
|
pb_graph_node->clock_pins[i][j].pin_count_in_cluster);
|
||
|
(*curr_net)++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pb_type->blif_model != NULL) {
|
||
|
/* This is a terminal node so it might drive nets, find and map the rr_graph path for those nets */
|
||
|
for (i = 0; i < pb_graph_node->num_output_ports; i++) {
|
||
|
for (j = 0; j < pb_graph_node->num_output_pins[i]; j++) {
|
||
|
if (rr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
load_internal_cb_rr_graph_net_nums(
|
||
|
&rr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster],
|
||
|
rr_graph, nets, curr_net, &temp);
|
||
|
assert(temp == nets[*curr_net].num_sinks);
|
||
|
temp = 0;
|
||
|
size =
|
||
|
strlen(pb_graph_node->pb_type->name)
|
||
|
+ pb_graph_node->placement_index / 10
|
||
|
+ i / 10 + j / 10
|
||
|
+ pb_graph_node->output_pins[i][j].pin_count_in_cluster
|
||
|
/ 10 + 26;
|
||
|
nets[*curr_net].name = (char *)my_calloc(size, sizeof(char));
|
||
|
sprintf(nets[*curr_net].name,
|
||
|
"%s[%d].output[%d][%d].pin[%d]",
|
||
|
pb_graph_node->pb_type->name,
|
||
|
pb_graph_node->placement_index, i, j,
|
||
|
pb_graph_node->output_pins[i][j].pin_count_in_cluster);
|
||
|
(*curr_net)++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
/* Recurse down to primitives */
|
||
|
for (i = 0; i < pb_type->num_modes; i++) {
|
||
|
for (j = 0; j < pb_type->modes[i].num_pb_type_children; j++) {
|
||
|
for (k = 0; k < pb_type->modes[i].pb_type_children[j].num_pb;
|
||
|
k++) {
|
||
|
load_internal_cb_nets(top_level,
|
||
|
&pb_graph_node->child_pb_graph_nodes[i][j][k],
|
||
|
rr_graph, curr_net);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pb_graph_node->parent_pb_graph_node == NULL) { /* at top level */
|
||
|
assert(*curr_net == top_level->num_local_nets);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* allocate space to store nets internal to cb
|
||
|
two pass algorithm, pass 1 count and allocate # nets, pass 2 determine # sinks
|
||
|
*/
|
||
|
static void alloc_internal_cb_nets(INOUTP t_pb *top_level,
|
||
|
INP t_pb_graph_node *pb_graph_node, INOUTP t_rr_node *rr_graph,
|
||
|
INP int pass) {
|
||
|
int i, j, k;
|
||
|
const t_pb_type *pb_type;
|
||
|
int num_sinks;
|
||
|
|
||
|
pb_type = pb_graph_node->pb_type;
|
||
|
|
||
|
if (pb_graph_node->parent_pb_graph_node == NULL) { /* determine nets driven from inputs at top level */
|
||
|
top_level->num_local_nets = 0;
|
||
|
if (pass == 1)
|
||
|
top_level->local_nets = NULL;
|
||
|
for (i = 0; i < pb_graph_node->num_input_ports; i++) {
|
||
|
for (j = 0; j < pb_graph_node->num_input_pins[i]; j++) {
|
||
|
if (rr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
if (pass == 2) {
|
||
|
num_sinks =
|
||
|
count_sinks_internal_cb_rr_graph_net_nums(
|
||
|
&rr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster],
|
||
|
rr_graph);
|
||
|
top_level->local_nets[top_level->num_local_nets].num_sinks =
|
||
|
num_sinks;
|
||
|
top_level->local_nets[top_level->num_local_nets].node_block = (int *)
|
||
|
my_calloc(num_sinks, sizeof(int));
|
||
|
top_level->local_nets[top_level->num_local_nets].node_block_port = (int *)
|
||
|
my_calloc(num_sinks, sizeof(int));
|
||
|
top_level->local_nets[top_level->num_local_nets].node_block_pin = (int *)
|
||
|
my_calloc(num_sinks, sizeof(int));
|
||
|
}
|
||
|
top_level->num_local_nets++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for (i = 0; i < pb_graph_node->num_clock_ports; i++) {
|
||
|
for (j = 0; j < pb_graph_node->num_clock_pins[i]; j++) {
|
||
|
if (rr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
if (pass == 2) {
|
||
|
num_sinks =
|
||
|
count_sinks_internal_cb_rr_graph_net_nums(
|
||
|
&rr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster],
|
||
|
rr_graph);
|
||
|
top_level->local_nets[top_level->num_local_nets].num_sinks =
|
||
|
num_sinks;
|
||
|
top_level->local_nets[top_level->num_local_nets].node_block = (int *)
|
||
|
my_calloc(num_sinks, sizeof(int));
|
||
|
top_level->local_nets[top_level->num_local_nets].node_block_port = (int *)
|
||
|
my_calloc(num_sinks, sizeof(int));
|
||
|
top_level->local_nets[top_level->num_local_nets].node_block_pin = (int *)
|
||
|
my_calloc(num_sinks, sizeof(int));
|
||
|
}
|
||
|
top_level->num_local_nets++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pb_type->blif_model != NULL) {
|
||
|
/* This is a terminal node so it might drive nets, find and map the rr_graph path for those nets */
|
||
|
for (i = 0; i < pb_graph_node->num_output_ports; i++) {
|
||
|
for (j = 0; j < pb_graph_node->num_output_pins[i]; j++) {
|
||
|
if (rr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
if (pass == 2) {
|
||
|
num_sinks =
|
||
|
count_sinks_internal_cb_rr_graph_net_nums(
|
||
|
&rr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster],
|
||
|
rr_graph);
|
||
|
top_level->local_nets[top_level->num_local_nets].num_sinks =
|
||
|
num_sinks;
|
||
|
top_level->local_nets[top_level->num_local_nets].node_block = (int *)
|
||
|
my_calloc(num_sinks, sizeof(int));
|
||
|
top_level->local_nets[top_level->num_local_nets].node_block_port = (int *)
|
||
|
my_calloc(num_sinks, sizeof(int));
|
||
|
top_level->local_nets[top_level->num_local_nets].node_block_pin = (int *)
|
||
|
my_calloc(num_sinks, sizeof(int));
|
||
|
}
|
||
|
top_level->num_local_nets++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
/* Recurse down to primitives */
|
||
|
for (i = 0; i < pb_type->num_modes; i++) {
|
||
|
for (j = 0; j < pb_type->modes[i].num_pb_type_children; j++) {
|
||
|
for (k = 0; k < pb_type->modes[i].pb_type_children[j].num_pb;
|
||
|
k++) {
|
||
|
alloc_internal_cb_nets(top_level,
|
||
|
&pb_graph_node->child_pb_graph_nodes[i][j][k],
|
||
|
rr_graph, pass);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pb_graph_node->parent_pb_graph_node == NULL) { /* at top level */
|
||
|
if (pass == 1) {
|
||
|
top_level->local_nets = (struct s_net *)my_calloc(top_level->num_local_nets,
|
||
|
sizeof(struct s_net));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void mark_constant_generators(INP int L_num_blocks,
|
||
|
INP struct s_block block_list[], INP int ncount,
|
||
|
INOUTP struct s_net nlist[]) {
|
||
|
int i;
|
||
|
for (i = 0; i < L_num_blocks; i++) {
|
||
|
mark_constant_generators_rec(block_list[i].pb,
|
||
|
block_list[i].pb->rr_graph, nlist);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void mark_constant_generators_rec(INP t_pb *pb, INP t_rr_node *rr_graph,
|
||
|
INOUTP struct s_net nlist[]) {
|
||
|
int i, j;
|
||
|
t_pb_type *pb_type;
|
||
|
boolean const_gen;
|
||
|
if (pb->pb_graph_node->pb_type->blif_model == NULL) {
|
||
|
for (i = 0;
|
||
|
i
|
||
|
< pb->pb_graph_node->pb_type->modes[pb->mode].num_pb_type_children;
|
||
|
i++) {
|
||
|
pb_type =
|
||
|
&(pb->pb_graph_node->pb_type->modes[pb->mode].pb_type_children[i]);
|
||
|
for (j = 0; j < pb_type->num_pb; j++) {
|
||
|
if (pb->child_pbs[i][j].name != NULL) {
|
||
|
mark_constant_generators_rec(&(pb->child_pbs[i][j]),
|
||
|
rr_graph, nlist);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else if (strcmp(pb->pb_graph_node->pb_type->name, "inpad") != 0) {
|
||
|
const_gen = TRUE;
|
||
|
for (i = 0; i < pb->pb_graph_node->num_input_ports && const_gen == TRUE;
|
||
|
i++) {
|
||
|
for (j = 0;
|
||
|
j < pb->pb_graph_node->num_input_pins[i]
|
||
|
&& const_gen == TRUE; j++) {
|
||
|
if (rr_graph[pb->pb_graph_node->input_pins[i][j].pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
const_gen = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for (i = 0; i < pb->pb_graph_node->num_clock_ports && const_gen == TRUE;
|
||
|
i++) {
|
||
|
for (j = 0;
|
||
|
j < pb->pb_graph_node->num_clock_pins[i]
|
||
|
&& const_gen == TRUE; j++) {
|
||
|
if (rr_graph[pb->pb_graph_node->clock_pins[i][j].pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
const_gen = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (const_gen == TRUE) {
|
||
|
vpr_printf(TIO_MESSAGE_INFO, "%s is a constant generator.\n", pb->name);
|
||
|
for (i = 0; i < pb->pb_graph_node->num_output_ports; i++) {
|
||
|
for (j = 0; j < pb->pb_graph_node->num_output_pins[i]; j++) {
|
||
|
if (rr_graph[pb->pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num
|
||
|
!= OPEN) {
|
||
|
nlist[rr_graph[pb->pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num].is_const_gen =
|
||
|
TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Free logical blocks of netlist */
|
||
|
void free_logical_blocks(void) {
|
||
|
int iblk, i;
|
||
|
t_model_ports *port;
|
||
|
struct s_linked_vptr *tvptr, *next;
|
||
|
|
||
|
for (iblk = 0; iblk < num_logical_blocks; iblk++) {
|
||
|
port = logical_block[iblk].model->inputs;
|
||
|
i = 0;
|
||
|
while (port) {
|
||
|
if (!port->is_clock) {
|
||
|
free(logical_block[iblk].input_nets[i]);
|
||
|
if (logical_block[iblk].input_net_tnodes) {
|
||
|
if (logical_block[iblk].input_net_tnodes[i])
|
||
|
free(logical_block[iblk].input_net_tnodes[i]);
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
port = port->next;
|
||
|
}
|
||
|
if (logical_block[iblk].input_net_tnodes)
|
||
|
free(logical_block[iblk].input_net_tnodes);
|
||
|
|
||
|
tvptr = logical_block[iblk].packed_molecules;
|
||
|
while (tvptr != NULL) {
|
||
|
next = tvptr->next;
|
||
|
free(tvptr);
|
||
|
tvptr = next;
|
||
|
}
|
||
|
|
||
|
free(logical_block[iblk].input_nets);
|
||
|
port = logical_block[iblk].model->outputs;
|
||
|
i = 0;
|
||
|
while (port) {
|
||
|
free(logical_block[iblk].output_nets[i]);
|
||
|
if (logical_block[iblk].output_net_tnodes) {
|
||
|
if (logical_block[iblk].output_net_tnodes[i])
|
||
|
free(logical_block[iblk].output_net_tnodes[i]);
|
||
|
}
|
||
|
i++;
|
||
|
port = port->next;
|
||
|
}
|
||
|
if (logical_block[iblk].output_net_tnodes) {
|
||
|
free(logical_block[iblk].output_net_tnodes);
|
||
|
}
|
||
|
free(logical_block[iblk].output_nets);
|
||
|
free(logical_block[iblk].name);
|
||
|
tvptr = logical_block[iblk].truth_table;
|
||
|
while (tvptr != NULL) {
|
||
|
if (tvptr->data_vptr)
|
||
|
free(tvptr->data_vptr);
|
||
|
next = tvptr->next;
|
||
|
free(tvptr);
|
||
|
tvptr = next;
|
||
|
}
|
||
|
}
|
||
|
free(logical_block);
|
||
|
logical_block = NULL;
|
||
|
}
|
||
|
|
||
|
/* Free logical blocks of netlist */
|
||
|
void free_logical_nets(void) {
|
||
|
int inet;
|
||
|
|
||
|
for (inet = 0; inet < num_logical_nets; inet++) {
|
||
|
free(vpack_net[inet].name);
|
||
|
free(vpack_net[inet].node_block);
|
||
|
free(vpack_net[inet].node_block_port);
|
||
|
free(vpack_net[inet].node_block_pin);
|
||
|
}
|
||
|
free(vpack_net);
|
||
|
vpack_net = NULL;
|
||
|
}
|
||
|
|
||
|
|