blob: 16376b62860680f7f3d2fb8cd11fc1d589a7e32b [file] [log] [blame]
/*path_length.c *
*originallly created April 16, 1998, Alexander (Sandy) Marquardt *
*calculates path lengths and block distances from inputs and outputs *
*and determines net slacks, as well as criticalities of nets */
#include <stdio.h>
#include <math.h>
#include "util.h"
#include "vpack.h"
#include "globals.h"
#include "cluster.h"
#include "path_length.h"
#include "heapsort.h"
#define CRITICAL_LENGTH_STATS /*this controls whether or not the progam will */
/*print out extra data about the status of */
/*the packing after each timing-analysis */
#define CRITICAL_PATH_STATS /*print out blocks and nets that vpack thinks are*/
/*the critical path before and after packing */
/****************** Defines for this module ****************************************/
/*#define DEBUG_PATH_LENGTH 1*/ /*Tells path_length.c to output some */
/*useful data structures to debugPL.log*/
#define MAX_ALLOWED_PATH_LEN 10000.0/*really big number to initialize required *
arrival times. */
/*The following defines assume that the user has assigned a inter_cluster_net_delay *
*value of 1, and is using the intra_cluster_net_delay values to scale the ratio *
*between the two. It is strongly recommended that inter_cluster_net_delay be set to*
*1 in all cases. */
#define MIN_SLACK_ALLOWED 1e-6 /*any slack values below this number are set to *
*zero since the resulting value is caused by *
*inaccuracies in floating point calculations */
#define SCALE_NUM_PATHS 1e-2 /*this value is used as a multiplier to assign a *
*slightly higher criticality value to nets that *
*affect a large number of critical paths versus *
*nets that do not have such a broad effect. *
*Note that this multiplier is intentionally very *
*small compared to the total criticality because *
*we want to make sure that net criticality is *
*primarily determined by slacks, with this acting *
*only as a tie-breaker between otherwise equal nets*/
#define SCALE_DISTANCE_VAL 1e-4 /*this value is used as a multiplier to assign a *
*slightly higher criticality value to nets that *
*are otherwise equal but one is farther *
*from sources (the farther one being made slightly *
*more critical) */
#define EQUAL_DEF 1e-6 /*used in some if == equations to allow very *
*close values to be considered equal */
/****************** Structures for this module *************************************/
struct s_block_timing {float delay_from_source;
float required_arrival_time;
int *net_pin_index;
long num_max_inputs;
long num_max_outputs;};
/* delay_from_source: delay to block's input from the farthest source *
* required_arrival_time: What is the latest that a signal can arrive at *
* an input to this block without becoming critical *
* net_pin_index[]: For each input on this block, this indicates which *
* pin in nets[].pins[] the input net is corresponds to*
* num_max_inputs: How many paths on the inputs to this block have the *
* maximum arrival time of paths that are inputs to *
* this block. . *
* num_max_ouputs: How many paths on the output from this block have *
* the smallest required arrival time of paths that are*
* driven by this block */
struct s_net_timing {float* net_pin_arrival_time;
float* net_slack;
float* net_forward_criticality;
float* net_backward_criticality;};
/*net_pin_arrival_time[]:parallel array to s_net[].pins[]. When does the *
* output of this net become valid at the pin which *
* it is driving. *
* net_slack[]: [0..lut_size], required_arrival_time - net_arrival_ *
* time. *
* net_forward_criticality[]:[0..lut_size], is set to *
* (1-(slack/max_slack)) plus a tie breaker portion *
* determined by the block driving the net (see the *
* calculate_slack_and_criticality function for a *
* description of tie-breakers). *
* this has a value between 0 and 1 with higher values *
* indicating a more critical net. *
* net_backward_criticality[]: [0..lut_size], is set to *
* (1-(slack/max_slack). plus a tie-breaker portion *
* determined by the block being driven by the net. */
/****************** Variables Global to this module *******************************/
struct s_block_timing *block_timing;
struct s_net_timing *net_timing;
static int *block_has_distance_q; /*which blocks have been allocated distance *
*from sources */
static int *block_has_req_arr_time_q; /*which blocks have been allocated required *
*arrival times */
static int *block_in_remaining; /*How many adjacent input blocks have not been *
*discovered. Once all input blocks have been *
*discovered, this block is added to the *
*block_has_distance_q */
static int *block_out_remaining; /*How many adjacent blocks on the output net have *
*not been discovered yet. Once all output blocks *
*have been discovered, this block is added to the*
*block_has_req_arr_time_q */
static int *block_in_count; /*How many inputs on this block are valid */
static int *block_out_count; /*How many blocks are being driven by the output */
static boolean *block_is_in_initial_source_q;/*indicates whether a block is a *
* source or not */
static boolean *block_is_in_initial_sink_q; /*indicates whether a block is a *
*sink or not */
static boolean *block_is_in_distance_q;/*indicates whether a block has been *
* already added to the block_has_distance_q */
static boolean *block_is_in_req_arr_time_q; /*indicates whether a block has been *
*added to the the block_has_req_arr_time_q */
static boolean *is_clock; /*pointer to the is_clock array declared in main function*/
static float *criticality; /* [0..num_blocks-1] keeps track of how critical each *
* block is, this is primarily used for determining *
* which block to use as a cluster seed. *
* The criticality of a block is equal to the *
* of the most critical net on its input pins. */
static int *critindexarray; /* [0..num_blocks-1] Each value in this array is a *
* pointer to the criticality array. The index of the *
* most critical block is first, least critical last */
static int indexofcrit; /*next index in the critindexarray that will be read *
*this value is reset every time a new timing analysis *
*and sort is done. It is modified every time *
*get_most_critical_block is called */
static int lut_size;
static int initial_num_sinks_in_q; /* allows us to remember what blocks are sinks *
* even after the queue has been expanded */
static int initial_num_sources_in_q; /*ditto for sources */
#ifdef CRITICAL_PATH_STATS
static FILE *critical_report_f; /*used for printing out the critical path details*/
#endif
#ifdef CRITICAL_LENGTH_STATS
static FILE *length_f; /*records the length of the critical path as the *
*program proceeds */
#endif
/************* Declarations of subroutines local to this module *************/
static void print_stats(int num_blocks_clustered, int num_clusters,
float farthest_distance,
int num_critical_conn, float avg_dist_to_sinks,
float avg_point_to_point_dist);
/******************** Subroutine Definitions **************************************/
/******************************/
static void alloc_and_init_structures(void)
/*allocates data structures required for timing analysis and initializes them to*
*default values */
{
int iblk,netpin,inet,blockpin;
int blk,ipin;
int numpins;
int startidx,endidx;
block_has_distance_q=(int*)my_malloc(num_blocks*sizeof(int));
block_has_req_arr_time_q=(int*)my_malloc(num_blocks*sizeof(int));
block_in_remaining=(int*)my_malloc(num_blocks*sizeof(int));
block_out_remaining=(int*)my_malloc(num_blocks*sizeof(int));
block_in_count=(int*)my_malloc(num_blocks*sizeof(int));
block_out_count=(int*)my_malloc(num_blocks*sizeof(int));
/*calloc inits the following four arrays to FALSE*/
block_is_in_initial_source_q=(boolean*)my_calloc(num_blocks,sizeof(boolean));
block_is_in_initial_sink_q=(boolean*)my_calloc(num_blocks,sizeof(boolean));
block_is_in_distance_q=(boolean*)my_calloc(num_blocks,sizeof(boolean));
block_is_in_req_arr_time_q=(boolean*)my_calloc(num_blocks,sizeof(boolean));
block_timing=(struct s_block_timing*)my_malloc(num_blocks*
sizeof(struct s_block_timing));
criticality=(float*)my_malloc(num_blocks*sizeof(float));
critindexarray=(int*)my_malloc(num_blocks*sizeof(int));
/*initialize and allocate the block_timing internal structures*/
for (iblk=0;iblk<num_blocks;iblk++){
block_timing[iblk].delay_from_source= -1.0;
block_timing[iblk].required_arrival_time= MAX_ALLOWED_PATH_LEN;
block_timing[iblk].num_max_inputs=1;
block_timing[iblk].num_max_outputs=1;
criticality[iblk]= -1.0;
if (block[iblk].type==INPAD || block[iblk].type==OUTPAD){
startidx=0;
endidx=0;
block_timing[iblk].net_pin_index=(int*)my_malloc(sizeof(int));
}
else {
startidx=0;
endidx=lut_size;
block_timing[iblk].net_pin_index=(int*)my_malloc((lut_size+1)*sizeof(int));
}
for (ipin=startidx;ipin<=endidx;ipin++){
block_timing[iblk].net_pin_index[ipin]=-1;
}
}
/*update the block_timing.net_pin_index values*/
for (inet=0;inet<num_nets;inet++) {
if (net[inet].pins[0] != OPEN) {
for (netpin=0;netpin<net[inet].num_pins;netpin++){
blk=net[inet].pins[netpin];
if (block[blk].type == INPAD)
block_timing[blk].net_pin_index[0]=0; /*pin 0 of net is always on driving block*/
else if (block[blk].type == OUTPAD)
block_timing[blk].net_pin_index[0]=netpin;
else if (netpin == 0)
block_timing[blk].net_pin_index[0]=0;
else{
startidx=1;
endidx=lut_size;
for (blockpin=startidx;blockpin<=endidx;blockpin++){
if (block[blk].nets[blockpin] == inet){
block_timing[blk].net_pin_index[blockpin]=netpin;
}
}
}
}
}
}
/*allocate and initialize the net_timing structure*/
net_timing=(struct s_net_timing*)my_malloc(num_nets*sizeof(struct s_net_timing));
for (inet=0;inet<num_nets;inet++){
numpins=net[inet].num_pins;
net_timing[inet].net_pin_arrival_time=(float*)my_malloc(numpins*sizeof(float));
net_timing[inet].net_slack=(float*)my_malloc(numpins*sizeof(float));
net_timing[inet].net_forward_criticality=(float*)my_malloc(numpins*sizeof(float));
net_timing[inet].net_backward_criticality=(float*)my_malloc(numpins*sizeof(float));
for (ipin=0;ipin<numpins;ipin++) {
net_timing[inet].net_pin_arrival_time[ipin]=0.0;
net_timing[inet].net_slack[ipin]=-1.0;
net_timing[inet].net_forward_criticality[ipin]=-1.0;
net_timing[inet].net_backward_criticality[ipin]=-1.0;
}
}
}
/******************************/
static void set_block_input_output_counts(void)
/*set number of inputs and outputs that a block can expect to have*/
{
int blkidx;
int outnet;
for (blkidx=0;blkidx<num_blocks;blkidx++){
/*set number of inputs for each block*/
if (block[blkidx].type == OUTPAD)
block_in_count[blkidx]=1;
else if (block[blkidx].type == INPAD)
block_in_count[blkidx]=0;
else if (block[blkidx].type == LUT)
block_in_count[blkidx]=block[blkidx].num_nets-1; /*-1 for output*/
else if (block[blkidx].type == LATCH || block[blkidx].type == LUT_AND_LATCH)
block_in_count[blkidx]=block[blkidx].num_nets-2; /*-2 for clock+output*/
/*set number of outputs for each block*/
if (block[blkidx].type == OUTPAD) {
outnet=OPEN;
block_out_count[blkidx]=0;
}
else {
outnet=block[blkidx].nets[0];
if (is_clock[outnet])
/*ignore clock nets*/
block_out_count[blkidx]=0;
else if (block[blkidx].type == INPAD)
block_out_count[blkidx]=1;
else
block_out_count[blkidx]=net[outnet].num_pins-1;
}
}
}
/******************************/
static void get_sources_and_sinks(void) {
/*determines what blocks are sources, and which are sinks, and allocates *
*the various queue structures to keep track of where the sources and *
*sinks are */
int blkidx;
int q_next_source_to_add;
int q_next_sink_to_add;
q_next_source_to_add=0;
q_next_sink_to_add=0;
/* If a block has no inputs, then it is considered to be a source (could happen */
/* for a constant generator) */
for (blkidx=0;blkidx<num_blocks;blkidx++){
block_is_in_initial_source_q[blkidx]=FALSE;
block_is_in_initial_sink_q[blkidx]=FALSE;
block_has_distance_q[blkidx]=-1;
block_has_req_arr_time_q[blkidx]=-1;
/*mark sources*/
if (block[blkidx].type == INPAD || block[blkidx].type == LATCH ||
block[blkidx].type == LUT_AND_LATCH) {
block_has_distance_q[q_next_source_to_add]=blkidx;
q_next_source_to_add++;
block_timing[blkidx].delay_from_source=0.0;
block_is_in_initial_source_q[blkidx]=TRUE;
}
/*mark other sources*/
else if (block[blkidx].num_nets == 1 && block[blkidx].type != OUTPAD) {
/* a constant generator, consider it a source*/
block_has_distance_q[q_next_source_to_add]=blkidx;
q_next_source_to_add++;
block_timing[blkidx].delay_from_source=0.0;
block_is_in_initial_source_q[blkidx]=TRUE;
}
/*mark sinks*/
if (block[blkidx].type == OUTPAD || block[blkidx].type == LATCH ||
block[blkidx].type == LUT_AND_LATCH) {
block_has_req_arr_time_q[q_next_sink_to_add]=blkidx;
q_next_sink_to_add++;
block_is_in_initial_sink_q[blkidx]=TRUE;
}
/*mark other sinks*/
else if (is_clock[block[blkidx].nets[0]] && block[blkidx].type != INPAD){
/*output from this block is a clock, consider this block a sink*/
block_has_req_arr_time_q[q_next_sink_to_add]=blkidx;
q_next_sink_to_add++;
block_is_in_initial_sink_q[blkidx]=TRUE;
}
}
initial_num_sinks_in_q=q_next_sink_to_add;
initial_num_sources_in_q=q_next_source_to_add;
}
/******************************/
static void reset_blocks_to_initial_state(void) {
/*this procedure sets up the structures so that a new timing analysis can *
*be done, basically it sets the structures to the state that they were in*
*after the calls to get_sources_and_sinks, and *
*set_block_input_output_counts */
int blkidx;
for (blkidx=0;blkidx<num_blocks;blkidx++){
block_in_remaining[blkidx]=block_in_count[blkidx];
block_out_remaining[blkidx]=block_out_count[blkidx];
block_is_in_distance_q[blkidx]=block_is_in_initial_source_q[blkidx];
block_is_in_req_arr_time_q[blkidx]=block_is_in_initial_sink_q[blkidx];
block_timing[blkidx].delay_from_source= 0.0;
block_timing[blkidx].num_max_inputs=1;
block_timing[blkidx].num_max_outputs=1;
criticality[blkidx]= -1.0;
}
}
/******************************/
static float calculate_arrival_time_at_each_pin(int block_discovered,
int parent_block, float block_delay,
float inter_cluster_net_delay,
float intra_cluster_net_delay,
float *point_to_point_dist_sum) {
/*Calculate the arrival time at each discovered block*/
float arrival_t;
int cluster_of_block_discovered;
cluster_of_block_discovered=get_cluster_of_block(block_discovered);
if (cluster_of_block_discovered != NO_CLUSTER &&
cluster_of_block_discovered == get_cluster_of_block(parent_block)){
/*the block discovered is in the same cluster as it's parent block*/
(*point_to_point_dist_sum) += intra_cluster_net_delay + block_delay;
if (!block_is_in_initial_source_q[parent_block]){
arrival_t=block_timing[parent_block].delay_from_source +
intra_cluster_net_delay + block_delay;
}
else{ /*the parent is a source, so it contributes no previous delay*/
arrival_t=intra_cluster_net_delay+block_delay;
}
}
else {/*the discovered block is in a different cluster from the parent block*/
(*point_to_point_dist_sum) += inter_cluster_net_delay + block_delay;
if (!block_is_in_initial_source_q[parent_block]){
arrival_t=block_timing[parent_block].delay_from_source +
inter_cluster_net_delay+block_delay;
}
else{ /*the parent is a source, so it contributes no previous delay*/
arrival_t=inter_cluster_net_delay+block_delay;
}
}
return arrival_t;
}
/******************************/
static void update_paths_through_each_block(int block_discovered, int parent_block,
float arrival_time,
int *loc_biggest_num_max_inputs)
{
/*Update the number of paths through each block*/
/*check for approximate equality*/
if (fabs(block_timing[block_discovered].delay_from_source -
arrival_time) <EQUAL_DEF){
if (!block_is_in_initial_source_q[parent_block])
block_timing[block_discovered].num_max_inputs +=
block_timing[parent_block].num_max_inputs;
else
block_timing[block_discovered].num_max_inputs += 1;
}
else if (block_timing[block_discovered].delay_from_source < arrival_time) {
if (!block_is_in_initial_source_q[parent_block])
block_timing[block_discovered].num_max_inputs =
block_timing[parent_block].num_max_inputs;
else
block_timing[block_discovered].num_max_inputs = 1;
}
if (block_timing[block_discovered].num_max_inputs > *loc_biggest_num_max_inputs)
*loc_biggest_num_max_inputs=block_timing[block_discovered].num_max_inputs;
}
/******************************/
static int calculate_sink_dist_sum(void) {
int sum, idx, blknum;
sum = 0;
for (idx = 0; idx<initial_num_sinks_in_q; idx++) {
blknum = block_has_req_arr_time_q[idx];
sum += block_timing[blknum].delay_from_source;
}
return sum;
}
/******************************/
static void distance_from_sources(float* maximum_arrival_time, float block_delay,
float inter_cluster_net_delay,
float intra_cluster_net_delay,
int *farthest_block_discovered,
long *biggest_num_max_inputs,
float *farthest_distance,
float *avg_dist_of_sinks,
float *avg_point_to_point_dist){
/*calculate the distances from sources */
/*maximum_arrival_time returns the distance of the farthest sink*/
/*This algorithm traverses the netlist from sources using a breadth first traversal*
*which adds blocks to the block_has_distance_q. Blocks which are farther from *
*the sources are added to the queue last. By farther, I am not talking about delay*
*but rather I am talking about number of blocks between the source and a block */
int netpinidx, q_head, outnet, parent_block;
int block_discovered;
int startidx,endidx;
int next_blk_to_add_to_dist_q;
int farthest_block;
int loc_biggest_num_max_inputs;
float arrival_time,max_arrival_time, farthest_delay;
float sink_dist_sum, point_to_point_dist_sum;
int point_to_point_num;
max_arrival_time=0.0;
farthest_delay = 0.0;
farthest_block=0;
q_head=0;
loc_biggest_num_max_inputs=0;
sink_dist_sum = 0.0;
point_to_point_dist_sum = 0.0;
point_to_point_num = 0;
next_blk_to_add_to_dist_q=initial_num_sources_in_q;
while (q_head < next_blk_to_add_to_dist_q) {
parent_block=block_has_distance_q[q_head];
/*if this is an outpad then nets[0] is actually an input to the pad, not an output*/
if (block[parent_block].type == OUTPAD)
outnet=OPEN;
else
outnet=block[parent_block].nets[0];
/*ignore clock nets, and open nets*/
if (outnet != OPEN) {
if (!is_clock[outnet]) {
startidx=1;
endidx=net[outnet].num_pins-1; /* subtract 1 to keep in range*/
for (netpinidx=startidx;netpinidx<=endidx;netpinidx++){
block_discovered=net[outnet].pins[netpinidx];
/*Calculate the arrival time at each discovered pin of each block*/
arrival_time = calculate_arrival_time_at_each_pin(block_discovered, parent_block,
block_delay,
inter_cluster_net_delay,
intra_cluster_net_delay,
&point_to_point_dist_sum);
point_to_point_num ++;
/*Update the number of paths through each block*/
update_paths_through_each_block(block_discovered, parent_block, arrival_time,
&loc_biggest_num_max_inputs);
net_timing[outnet].net_pin_arrival_time[netpinidx]=arrival_time;
if (arrival_time > block_timing[block_discovered].delay_from_source){
block_timing[block_discovered].delay_from_source=arrival_time;
}
if (fabs(arrival_time - max_arrival_time) >= EQUAL_DEF && arrival_time > max_arrival_time){
max_arrival_time=arrival_time;
farthest_block=block_discovered;
}
block_in_remaining[block_discovered]--;
if (block_in_remaining[block_discovered] == 0) {
/*all inputs have been discovered, we now know how far this block is from an input*/
/*do not add previously added blocks to the queue.*/
if (!block_is_in_distance_q[block_discovered]){
block_has_distance_q[next_blk_to_add_to_dist_q]=block_discovered;
next_blk_to_add_to_dist_q++;
block_is_in_distance_q[block_discovered]=TRUE;
}
}
#ifdef DEBUG
else if (block_in_remaining[block_discovered]<0) {
printf("ERROR in distance_from_sources, a block has been discovered"
"more times than its number of inputs warrants\n");
exit(1);
}
#endif
} /*for.....*/
} /*!is_clock....*/
} /*outnet...*/
/*on the first call of this procedure, we print out some statistics*
*about the circuit which we are packing*/
q_head++;
} /*while ....*/
sink_dist_sum = calculate_sink_dist_sum();
*farthest_distance = max_arrival_time;
*farthest_block_discovered=farthest_block;
*maximum_arrival_time=max_arrival_time;
*biggest_num_max_inputs=loc_biggest_num_max_inputs;
*avg_dist_of_sinks = sink_dist_sum / (float)initial_num_sinks_in_q;
*avg_point_to_point_dist = point_to_point_dist_sum / (float)point_to_point_num;
}
/******************************/
static void assign_required_arrival_times_to_max(float max_arrival_time) {
/*this procedure assigns max_arrival_time to the required_arrival_time*
*for all blocks. For any blocks that are not sinks, this value will *
*be changed in the update_required_arrival_times procedure. It is *
*necessary to assign this "upper bound" value here, so that the *
*algorithm can find the smallest required_arrival_time for each node */
int i;
for (i=0; i<num_blocks; i++)
block_timing[i].required_arrival_time = max_arrival_time;
}
/******************************/
static void update_required_arrival_times(float block_delay,
float inter_cluster_net_delay,
float intra_cluster_net_delay,
long *biggest_num_max_outputs){
/*calculate the required arrival times for each block*/
/*this procedure traverses the netlist in a breadth first manner from the sinks*/
int i, q_head, innet,sink_block;
int block_discovered;
int startidx, endidx;
int next_blk_to_add_to_req_arr_time_q;
int netpin;
int cluster_of_block_discovered;
int loc_biggest_num_max_outputs;
float required_arrival_t;
q_head=0;
next_blk_to_add_to_req_arr_time_q=initial_num_sinks_in_q;
loc_biggest_num_max_outputs=0;
while (q_head<next_blk_to_add_to_req_arr_time_q ) {
sink_block=block_has_req_arr_time_q[q_head];
/*there are no inputs to an inpad*/
if (block[sink_block].type == INPAD) {
q_head++;
continue;
}
/*if this is an output pad, then the input to the pad is net[0]*/
if (block[sink_block].type == OUTPAD){
startidx=0;
endidx=0;
}
else { /*ignore block.net[0] since it is an output.*/
startidx=1;
endidx=lut_size; /*also ignore clock nets (which would be in nets[lut+1] )*/
}
for (i=startidx;i<=endidx;i++) {
/*ignore OPEN nets*/
if (block[sink_block].nets[i] != OPEN) {
innet=block[sink_block].nets[i];
block_discovered=net[innet].pins[0];
netpin=block_timing[sink_block].net_pin_index[i];
cluster_of_block_discovered=get_cluster_of_block(block_discovered);
if (cluster_of_block_discovered != NO_CLUSTER &&
cluster_of_block_discovered == get_cluster_of_block(sink_block)){
required_arrival_t=block_timing[sink_block].required_arrival_time-
intra_cluster_net_delay-block_delay;
}
else{
required_arrival_t=block_timing[sink_block].required_arrival_time-
inter_cluster_net_delay-block_delay;
}
block_out_remaining[block_discovered]--;
if (fabs(block_timing[block_discovered].delay_from_source -
required_arrival_t) < EQUAL_DEF){
if (!block_is_in_initial_sink_q[sink_block])
block_timing[block_discovered].num_max_outputs +=
block_timing[sink_block].num_max_outputs;
else
block_timing[block_discovered].num_max_outputs += 1;
}
else if (block_timing[block_discovered].required_arrival_time > required_arrival_t) {
if (!block_is_in_initial_sink_q[sink_block])
block_timing[block_discovered].num_max_outputs =
block_timing[sink_block].num_max_outputs;
else
block_timing[block_discovered].num_max_outputs = 1;
}
if (block_timing[block_discovered].num_max_outputs > loc_biggest_num_max_outputs)
loc_biggest_num_max_outputs=block_timing[block_discovered].num_max_outputs;
/* update arrival times of blocks that are not sinks *
* (sinks already have valid values) */
if (!block_is_in_initial_sink_q[block_discovered])
if (block_timing[block_discovered].required_arrival_time > required_arrival_t)
block_timing[block_discovered].required_arrival_time=required_arrival_t;
if (block_out_remaining[block_discovered] == 0) {
/*previously added blocks are not added to the queue.*/
if (!block_is_in_req_arr_time_q[block_discovered]){
block_has_req_arr_time_q[next_blk_to_add_to_req_arr_time_q]=block_discovered;
next_blk_to_add_to_req_arr_time_q++;
block_is_in_req_arr_time_q[block_discovered]=TRUE;
}
}
} /* block[sink_block]...*/
} /*for (i=start....*/
q_head++;
}
*biggest_num_max_outputs=loc_biggest_num_max_outputs;
}
/******************************/
static void calculate_slack_and_criticality(float inter_cluster_net_delay,
float intra_cluster_net_delay,
float max_arrival_time,
long sum_biggest_max,
int *num_critical_connections) {
/*assign slack and criticality values to the net_timing structure. It *
*is calculated for each pin on each net except for the pins which *
*are driving the net (they are left at the undefined value of -1). *
*Also compute for the criticality array (which is parallel to the *
*block array) */
/*The criticality of a block is determined primarily by the slack on *
*it's input nets using the one that has the smallest value. It is *
* modified as described below. */
/*Criticality is determined by three factors. *
*1. The most important, and most highly weighted factor is the slack *
* on the nets. A net with very little slack will be assigned a high*
* criticality value, as will the block which is being driven by the*
* net. *
*The next two factors are used only as a tie-breaking mechanisim for *
*nets that have the same criticality values. They will never cause a *
*net with more slack to become more critical than a net with less *
*slack. *
*2. The number of paths passing through a block adjacent to a net is *
* the primary tie breaker for selecting between nets that have *
* equal slacks. The net_forward_criticality of a net is affected *
* by the block driving the net, the net_backward_criticality is *
* affected by the block that is being driven by the net. *
* There are two parameters which keep track of the number of paths *
* which pass through a block. The num_max_inputs keeps track of how*
* many paths that are on the input side of each block that can be *
* be affected by making a block clustered. The num_max_outputs *
* keeps track of how many paths that are on the output side of each*
* block that can be affected by making a block clustered. We weight*
* blocks that affect more paths slightly higher than blocks that *
* do not affect as many. *
*3. Given that there is more than one net with the same slack, and *
* same number of paths, the third, and least weighted tie breaker *
* is the distance from the sources. If two blocks have the same *
* value for num_max_inputs and num_max_outputs, then the farther of*
* the two blocks and the nets that they are driving are assigned a *
* slightly higher criticality. *
* Remember, that this is only a tie breaker if number 1. and 2. are*
* equal, otherwise it has no effect. The weighting of this *
* parameter is set in SCALE_DISTANCE_VAL, which is small enough to *
* ensure that it is only a tie breaker. */
int iblk,iblkpin;
int startidx,endidx;
int netpin;
int currentnet;
int drivingblock;
static float *max_num_paths_scaling_value=NULL;
static float *distance_scaling_value=NULL;
float currslack, maxslack, minslack;
float currcriticality;
int critical_connection_count;
critical_connection_count = 0;
/*find slack values*/
maxslack=0.0;
minslack = MAX_ALLOWED_PATH_LEN;
/*define the max_num_paths_scaling_value array if it is undefined*/
if (max_num_paths_scaling_value == NULL) {
max_num_paths_scaling_value=(float*)my_malloc(num_blocks * sizeof(float));
distance_scaling_value=(float*)my_malloc(num_blocks * sizeof(float));
}
/*calculate slack values*/
for (iblk=0;iblk<num_blocks;iblk++) {
if (block[iblk].type != INPAD) {
/*if this is an output pad, then the input to the pad is net[0]*/
if (block[iblk].type == OUTPAD) {
startidx=0;
endidx=0;
}
else {
startidx=1;
endidx=lut_size;
}
for (iblkpin=startidx;iblkpin<=endidx;iblkpin++) {
currentnet=block[iblk].nets[iblkpin];
if (currentnet!=OPEN) {
netpin=block_timing[iblk].net_pin_index[iblkpin];
currslack=block_timing[iblk].required_arrival_time-
net_timing[currentnet].net_pin_arrival_time[netpin];
if (currslack < MIN_SLACK_ALLOWED)
currslack = 0;
net_timing[currentnet].net_slack[netpin] = currslack;
if (fabs(currslack - minslack) < EQUAL_DEF) {
critical_connection_count ++;
}
else if (currslack < minslack) {
minslack = currslack;
critical_connection_count = 1;
}
if (currslack > maxslack)
maxslack=currslack;
}
}
}
}
/*Initialize the scaling values*/
for (iblk=0;iblk<num_blocks;iblk++) {
max_num_paths_scaling_value[iblk]=
SCALE_NUM_PATHS *
((float)block_timing[iblk].num_max_inputs +
(float)block_timing[iblk].num_max_outputs)/(float)sum_biggest_max;
distance_scaling_value[iblk]=
SCALE_DISTANCE_VAL *
block_timing[iblk].delay_from_source/max_arrival_time;
}
/*calculate criticality values*/
for (iblk=0;iblk<num_blocks;iblk++) {
if (block[iblk].type != INPAD) {
/*if this is an output pad, then the input to the pad is net[0]*/
if (block[iblk].type == OUTPAD) {
startidx=0;
endidx=0;
}
else {
startidx=1;
endidx=lut_size;
}
for (iblkpin=startidx;iblkpin<=endidx;iblkpin++) {
currentnet=block[iblk].nets[iblkpin];
if (currentnet != OPEN) {
drivingblock=net[currentnet].pins[0];
netpin=block_timing[iblk].net_pin_index[iblkpin];
currslack=net_timing[currentnet].net_slack[netpin];
/*calculate the criticality of the nets*/
/*Net forward criticality tie-breakers are determined by the block *
*that is driving the net */
if (maxslack > MIN_SLACK_ALLOWED)
currcriticality= (1-currslack/maxslack)+
max_num_paths_scaling_value[drivingblock] +
distance_scaling_value[drivingblock];
else
currcriticality=1.0 + max_num_paths_scaling_value[drivingblock] +
distance_scaling_value[drivingblock];
net_timing[currentnet].net_forward_criticality[netpin]=currcriticality;
/*now calculate the criticality of the blocks, and the *
* net_backward_criticality values*/
/*net_backward_criticality tie-breakers are determined by the block*
*that is being driven by the net */
/*block criticality tie-breakers are determined by the block under *
*consideration. Most of the criticality score of a block is *
*determined by the slack on the input net with the smallest *
*slack value */
if (maxslack > MIN_SLACK_ALLOWED)
currcriticality= (1-currslack/maxslack)+
max_num_paths_scaling_value[iblk] +
distance_scaling_value[iblk];
else
currcriticality=1.0 + max_num_paths_scaling_value[iblk] +
distance_scaling_value[iblk];
net_timing[currentnet].net_backward_criticality[netpin]=currcriticality;
if (get_cluster_of_block(iblk) == NO_CLUSTER) {
if (currcriticality > criticality[iblk]) {
criticality[iblk] = currcriticality;
}
}
}
}
}
}
*num_critical_connections = critical_connection_count;
}
/******************************/
static void sort_blocks_by_criticality(void) {
/*sorts the critindex array such that the first value is the position in the *
*criticality array of the most critical block, and the last value is the *
*position in the criticality array of the least critical block, blocks that *
*have already been clustered have been assigned a criticality of -1 so that *
*they will appear at the end of the sorted list.*/
heapsort(critindexarray, criticality, num_blocks);
indexofcrit=0;
}
/******************************/
#ifdef DEBUG_PATH_LENGTH
static void output_structures(void) {
/*This function prints out the internal net_timing and block_timing data *
*values. If a value is -1 (for slack or criticality) then the value is *
*undefined, this is expected on the outputs of blocks. */
int blkidx,blkpinidx;
int netpin;
int currentnet;
static FILE *debuglogfile;
int startidx, endidx;
debuglogfile=my_fopen("debugPL.log","w",0);
fprintf(debuglogfile,"BEWARE that this file only represents the final values\n\n");
/*for each block, print out Required arrival time, and slack on each input*/
fprintf(debuglogfile,"\n\n NAME TYPE Dist From Required Arrive");
for (blkpinidx=0;blkpinidx<=lut_size;blkpinidx++)
fprintf(debuglogfile, " slack[%2d]",blkpinidx);
fprintf(debuglogfile,"\n");
fprintf(debuglogfile," Source Time\n\n");
for (blkidx=0; blkidx<num_blocks; blkidx++) {
fprintf(debuglogfile,"%2d: %-15.15s %4d %9.2f %18.2f",blkidx,block[blkidx].name,
block[blkidx].type, block_timing[blkidx].delay_from_source,
block_timing[blkidx].required_arrival_time);
/*if this is an output pad, then the input to the pad is block[].nets[0]*/
if (block[blkidx].type == OUTPAD){
startidx=0;
endidx=0;
}
else {
startidx=0;
endidx=lut_size; /* ignore clock nets (which would be in block[].nets[lut+1] )*/
}
/*INPADs do not have inputs, so ignore*/
if (block[blkidx].type != INPAD) {
for (blkpinidx=startidx;blkpinidx<=endidx;blkpinidx++) {
currentnet=block[blkidx].nets[blkpinidx];
if (currentnet != OPEN) {
netpin=block_timing[blkidx].net_pin_index[blkpinidx];
fprintf(debuglogfile,"%9.2f ",net_timing[currentnet].net_slack[netpin]);
}
else
fprintf(debuglogfile," ");
}
}
fprintf(debuglogfile,"\n");
}
/*print out criticalities of each net*/
fprintf(debuglogfile,"\n\n NAME ");
for (blkidx=0;blkidx<=lut_size;blkidx++)
fprintf(debuglogfile, "Crit[%2d] ",blkidx);
fprintf(debuglogfile,"\n");
for (blkidx=0; blkidx<num_blocks; blkidx++) {
fprintf(debuglogfile,"%2d: %-15.15s",blkidx,block[blkidx].name);
/*if this is an output pad, then the input to the pad is block[].nets[0]*/
if (block[blkidx].type == OUTPAD){
startidx=0;
endidx=0;
}
else {
startidx=0;
endidx=lut_size; /* ignore clock nets (which would be in block[].nets[lut+1] )*/
}
/*INPADs do not have inputs, so ignore*/
if (block[blkidx].type != INPAD) {
for (blkpinidx=startidx;blkpinidx<=endidx;blkpinidx++) {
currentnet=block[blkidx].nets[blkpinidx];
if (currentnet != OPEN) {
netpin=block_timing[blkidx].net_pin_index[blkpinidx];
fprintf(debuglogfile,"%8.2f ",
net_timing[currentnet].net_forward_criticality[netpin]);
}
else
fprintf(debuglogfile," ");
}
}
fprintf(debuglogfile,"\n");
}
fclose(debuglogfile);
}
#endif
/*****************************************/
#ifdef CRITICAL_LENGTH_STATS
static void print_stats(int num_blocks_clustered, int num_clusters,
float farthest_distance,
int num_critical_conn, float avg_dist_to_sinks,
float avg_point_to_point_dist) {
static int previous_cluster = -1;
int cluster_num;
cluster_num = num_clusters - 1;
if (cluster_num != previous_cluster) {
previous_cluster = cluster_num;
fprintf (length_f, "\n");
}
fprintf (length_f, "%7d %7d %7.2f %7d %8.5f %8.5f\n",
cluster_num, num_blocks_clustered,
farthest_distance, num_critical_conn, avg_dist_to_sinks,
avg_point_to_point_dist);
}
#endif
/******************** Globally Accessable Function Definitions *******************/
/*****************************************/
int get_net_pin_number(int blk, int blkpin) {
/*returns the pin number in the net structure that the pin on this block */
/*corresponds to */
return(block_timing[blk].net_pin_index[blkpin]);
}
/*****************************************/
float get_net_pin_forward_criticality(int netnum, int pinnum) {
/*returns the criticality for a particular branch of a net*/
return(net_timing[netnum].net_forward_criticality[pinnum]);
}
/*****************************************/
float get_net_pin_backward_criticality(int netnum, int pinnum) {
/*returns the criticality for a particular block*/
return(net_timing[netnum].net_backward_criticality[pinnum]);
}
/*****************************************/
boolean report_critical_path(float block_delay, float inter_cluster_net_delay,
float intra_cluster_net_delay, int farthest_block,
int cluster_size, boolean print_data) {
/*finds the most critical path (longest path) and reports the length, and *
*which blocks and nets are used by the path. */
/*If all blocks on critical path have been clustered this function returns TRUE*
*otherwise it returns FALSE.*/
/*do not delete this function, as it is required to know when the critical path has been*/
/*minimized as much as it possibly can -- this allows us to go into pin-sharing mode and*/
/*save execution time */
int pinidx;
int currentblock;
int nextblock;
int startidx, endidx;
int inputnet, inputnetpin;
int critnet;
int numclusters, num_clusterable_blocks_on_path;
int total_num_blocks_on_path, num_ext_nets_on_path;
float mostcrit, netcrit;
int cluster_of_nextblock;
boolean all_clustered;
boolean first_pass;
all_clustered=TRUE;
first_pass = TRUE;
#ifdef CRITICAL_PATH_STATS
if (print_data) {
fprintf(critical_report_f,"Using the following values, the most critical "
"path is determined\n");
fprintf(critical_report_f,"block_delay= %5.2f\n",block_delay);
fprintf(critical_report_f,"inter_cluster_net_delay= %5.2f\n",inter_cluster_net_delay);
fprintf(critical_report_f,"intra_cluter_net_delay= %5.2f\n\n",intra_cluster_net_delay);
fprintf(critical_report_f,"Critical path is reported from output to input\n\n");
}
#endif
if (get_cluster_of_block(farthest_block) == NEVER_CLUSTER){
numclusters=0;
}
else{
numclusters=1;
}
num_ext_nets_on_path=0;
currentblock=farthest_block;
num_clusterable_blocks_on_path=0;
total_num_blocks_on_path=0;
while (1) {
#ifdef CRITICAL_PATH_STATS
if (print_data) {
fprintf(critical_report_f,"Block #%5d \tName:%14s \tArrival Time=%7.2f "
"\tCluster=%5d\n",
currentblock,block[currentblock].name,
block_timing[currentblock].delay_from_source,
get_cluster_of_block(currentblock));
}
#endif
if (get_cluster_of_block(currentblock) == NO_CLUSTER)
all_clustered=FALSE;
total_num_blocks_on_path++;
if (get_cluster_of_block(currentblock) != NEVER_CLUSTER)
num_clusterable_blocks_on_path++;
if (block_is_in_initial_source_q[currentblock] && !first_pass) {
/*we have found the source of this critical path, we are done*/
break;
}
first_pass = FALSE;
if (block[currentblock].type == OUTPAD){
startidx=0;
endidx=0;
}
else {
startidx=1;
endidx=lut_size;
}
mostcrit=0.0;
nextblock = NO_CLUSTER;
critnet = NO_CLUSTER;
for (pinidx=startidx; pinidx<=endidx; pinidx++) {
inputnet=block[currentblock].nets[pinidx];
if (inputnet != OPEN) {
inputnetpin=block_timing[currentblock].net_pin_index[pinidx];
netcrit=net_timing[inputnet].net_forward_criticality[inputnetpin];
if (netcrit > mostcrit) {
mostcrit=netcrit;
nextblock=net[inputnet].pins[0];
critnet=inputnet;
}
}
}
if (nextblock != NO_CLUSTER)
cluster_of_nextblock=get_cluster_of_block(nextblock);
else
cluster_of_nextblock = NO_CLUSTER;
if (cluster_of_nextblock == NO_CLUSTER ||
get_cluster_of_block(currentblock) != cluster_of_nextblock){
if (cluster_of_nextblock != NEVER_CLUSTER){
numclusters++;
}
num_ext_nets_on_path++;
}
#ifdef CRITICAL_PATH_STATS
if (print_data) {
fprintf(critical_report_f,"Net #%5d \tName:%14s \tCriticality =%9.5f\n", critnet,
net[critnet].name, mostcrit);
}
#endif
currentblock=nextblock;
}
#ifdef CRITICAL_PATH_STATS
if (print_data) {
fprintf(critical_report_f, "\n\n%d clusterable blocks packed into %d Clusters of size %d\n",
num_clusterable_blocks_on_path, numclusters, cluster_size);
fprintf(critical_report_f, "Total Number of Blocks on Crit Path= %d\n",
total_num_blocks_on_path);
fprintf(critical_report_f, "Number of external nets on Crit Path=%d\n",
num_ext_nets_on_path);
}
fflush(critical_report_f);
if (print_data)
fprintf(critical_report_f,"\n\n");
#endif
return all_clustered;
}
/*****************************************/
void calculate_timing_information(float block_delay, float inter_cluster_net_delay,
float intra_cluster_net_delay,
int num_clusters,
int num_blocks_clustered,
int *farthest_block_discovered){
/*This should be called whenever it is desired to have the timing and delay *
*values updated */
float max_arrival_time;
long biggest_num_max_inputs;
long biggest_num_max_outputs;
long sum_biggest_max;
float farthest_distance;
float avg_dist_of_sinks, avg_point_to_point_dist;
int num_critical_connections;
reset_blocks_to_initial_state();
/*first find distances from sources*/
distance_from_sources(&max_arrival_time, block_delay, inter_cluster_net_delay,
intra_cluster_net_delay,
farthest_block_discovered,
&biggest_num_max_inputs,
&farthest_distance,
&avg_dist_of_sinks,
&avg_point_to_point_dist);
/*now find distances from sinks*/
assign_required_arrival_times_to_max(max_arrival_time);
update_required_arrival_times(block_delay, inter_cluster_net_delay,
intra_cluster_net_delay, &biggest_num_max_outputs);
sum_biggest_max = biggest_num_max_inputs+biggest_num_max_outputs;
calculate_slack_and_criticality(inter_cluster_net_delay, intra_cluster_net_delay,
max_arrival_time, sum_biggest_max,
&num_critical_connections);
sort_blocks_by_criticality();
#ifdef CRITICAL_LENGTH_STATS
print_stats(num_blocks_clustered, num_clusters,
farthest_distance, num_critical_connections, avg_dist_of_sinks,
avg_point_to_point_dist);
#endif
#ifdef DEBUG_PATH_LENGTH
/*output debugging information*/
output_structures();
#endif
}
/******************************/
int get_most_critical_unclustered_block(void) {
/*calculate_timing_information must be called before this, or this function*
*will return garbage */
/*indexofcrit is a global variable that is reset to 0 each time a *
*timing analysis is done (reset in sort_blocks_by_criticality) */
int critidx, blkidx;
for (critidx=indexofcrit; critidx<num_blocks; critidx++) {
blkidx=critindexarray[critidx];
if (get_cluster_of_block(blkidx) == NO_CLUSTER) {
indexofcrit=critidx+1;
return(blkidx);
}
}
/*if it makes it to here , there are no more blocks available*/
return(NO_CLUSTER);
}
/******************************/
void alloc_and_init_path_length(int lut_sz, boolean *is_clk){
/*this only needs to be called once*/
lut_size=lut_sz;
is_clock=is_clk;
#ifdef CRITICAL_PATH_STATS
critical_report_f = my_fopen("vpack_critical_path.echo","w",0);
#endif
#ifdef CRITICAL_LENGTH_STATS
length_f = my_fopen("vpack_critical_length.echo","w",0);
fprintf(length_f, "%7s %7s %7s %7s %8s %8s\n",
"", "Num","","Num","Avg", "Avg");
fprintf(length_f, "%7s %7s %7s %7s %8s %8s\n",
"Cluster","Blocks","Crit","Crit", "Dist","Pt to Pt");
fprintf(length_f, "%7s %7s %7s %7s %8s %8s\n",
"Num","Packed","Dist","Conn","Sinks","Dist");
fprintf(length_f, "%7s %7s %7s %7s %8s %8s\n",
"-------","-------","-------","-------",
"--------","--------");
#endif
alloc_and_init_structures();
get_sources_and_sinks();
set_block_input_output_counts();
}
/******************************/
void free_path_length_memory(void){
/*this should only be called when the internal structures are no longer required*/
int iblk, inet;
free(block_has_distance_q);
free(block_has_req_arr_time_q);
free(block_in_remaining);
free(block_out_remaining);
free(block_in_count);
free(block_out_count);
free(block_is_in_distance_q);
free(block_is_in_initial_source_q);
free(block_is_in_initial_sink_q);
free(block_is_in_req_arr_time_q);
free(criticality);
free(critindexarray);
for (iblk=0; iblk<num_blocks;iblk++)
free(block_timing[iblk].net_pin_index);
free(block_timing);
for (inet=0;inet<num_nets;inet++) {
free(net_timing[inet].net_pin_arrival_time);
free(net_timing[inet].net_slack);
free(net_timing[inet].net_forward_criticality);
free(net_timing[inet].net_backward_criticality);
}
free(net_timing);
#ifdef CRITICAL_PATH_STATS
fclose(critical_report_f);
#endif
#ifdef CRITICAL_LENGTH_STATS
fclose(length_f);
#endif
}