blob: 3530bc91e427d15bd85ea52fe8ea97ac8d7f18e7 [file] [log] [blame]
/*
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "odin_types.h"
#include "odin_globals.h"
#include "netlist_utils.h"
#include "vtr_util.h"
#include "vtr_memory.h"
/* Used in the nnode_t.node_data field to mark if the node was already visited
* during a forward or backward sweep traversal or the removal phase */
int _visited_forward, _visited_backward, _visited_removal;
#define VISITED_FORWARD ((void*)&_visited_forward)
#define VISITED_BACKWARD ((void*)&_visited_backward)
#define VISITED_REMOVAL ((void*)&_visited_removal)
/* Simple linked list of nodes structure */
struct node_list_t{
nnode_t *node;
struct node_list_t *next;
};
node_list_t useless_nodes; // List of the nodes to be removed
node_list_t *removal_list_next = &useless_nodes; // Tail of the nodes to be removed
node_list_t addsub_nodes; // List of the adder/subtractor nodes
node_list_t *addsub_list_next = &addsub_nodes; // Tail of the adder/subtractor node list
/* Function declarations */
node_list_t* insert_node_list(node_list_t* node_list, nnode_t *node);
void traverse_backward(nnode_t *node);
void traverse_forward(nnode_t *node, int toplevel, int remove_me);
void mark_output_dependencies(netlist_t *netlist);
void identify_unused_nodes(netlist_t *netlist);
void remove_unused_nodes(node_list_t *remove);
void calculate_addsub_statistics(node_list_t *addsub);
void remove_unused_logic(netlist_t *netlist);
node_list_t* insert_node_list(node_list_t* node_list, nnode_t *node){
node_list->node = node;
node_list->next = (node_list_t*)vtr::calloc(1, sizeof(node_list_t));
return node_list->next;
}
/* Traverse the netlist backwards, moving from outputs to inputs */
void traverse_backward(nnode_t *node){
if(node->node_data == VISITED_BACKWARD) return; // Already visited
node->node_data = VISITED_BACKWARD; // Mark as visited
int i;
for(i = 0; i < node->num_input_pins; i++){
if(node->input_pins[i]->net->driver_pin){ // ensure this net has a driver (i.e. skip undriven outputs)
traverse_backward(node->input_pins[i]->net->driver_pin->node); // Visit the drivers of this node
}
}
}
/* Traverse the netlist forward, moving from inputs to outputs.
* Adds nodes that do not affect any outputs to the useless_nodes list
* Arguments:
* node: the current node in the netlist
* toplevel: are we at one of the top-level nodes? (GND, VCC, PAD or INPUT)
* remove_me: should the current node be removed?
* */
void traverse_forward(nnode_t *node, int toplevel, int remove_me){
if(node == NULL) return; // Shouldn't happen, but check just in case
if(node->node_data == VISITED_FORWARD) return; // Already visited, shouldn't happen anyway
/* We want to remove this node if either its parent was removed,
* or if it was not visited on the backwards sweep */
remove_me = remove_me || ((node->node_data != VISITED_BACKWARD) && (toplevel == false));
/* Mark this node as visited */
node->node_data = VISITED_FORWARD;
if(remove_me) {
/* Add this node to the list of nodes to remove */
removal_list_next = insert_node_list(removal_list_next, node);
}
if(node->type == ADD || node->type == MINUS){
// check if adders/subtractors are starting using a global gnd/vcc node or a pad node
auto ADDER_START_NODE = PAD_NODE;
if (configuration.adder_cin_global) {
if (node->type == ADD) ADDER_START_NODE = GND_NODE;
else ADDER_START_NODE = VCC_NODE;
}
/* Check if we've found the head of an adder or subtractor chain */
if(node->input_pins[node->num_input_pins-1]->net->driver_pin->node->type == ADDER_START_NODE) {
addsub_list_next = insert_node_list(addsub_list_next, node);
}
}
/* Iterate through every fanout node */
int i, j;
for(i = 0; i < node->num_output_pins; i++){
if(node->output_pins[i] && node->output_pins[i]->net){
for(j = 0; j < node->output_pins[i]->net->num_fanout_pins; j++){
if(node->output_pins[i]->net->fanout_pins[j]){
nnode_t *child = node->output_pins[i]->net->fanout_pins[j]->node;
if(child){
/* If this child hasn't already been visited, visit it now */
if(child->node_data != VISITED_FORWARD){
traverse_forward(child, false, remove_me);
}
}
}
}
}
}
}
/* Start at each of the top level output nodes and traverse backwards to the inputs
to determine which nodes have an effect on the outputs */
void mark_output_dependencies(netlist_t *netlist){
int i;
for(i = 0; i < netlist->num_top_output_nodes; i++){
traverse_backward(netlist->top_output_nodes[i]);
}
}
/* Traversed the netlist forward from the top level inputs and special nodes
(VCC, GND, PAD) */
void identify_unused_nodes(netlist_t *netlist){
useless_nodes.node = NULL;
useless_nodes.next = NULL;
addsub_nodes.node = NULL;
addsub_nodes.next = NULL;
traverse_forward(netlist->gnd_node, true, false);
traverse_forward(netlist->vcc_node, true, false);
traverse_forward(netlist->pad_node, true, false);
int i;
for(i = 0; i < netlist->num_top_input_nodes; i++){
traverse_forward(netlist->top_input_nodes[i], true, false);
}
}
/* Note: This does not actually free the unused logic, but simply detaches
it from the rest of the circuit */
void remove_unused_nodes(node_list_t *remove){
while(remove != NULL && remove->node != NULL){
int i;
for(i = 0; i < remove->node->num_input_pins; i++){
npin_t *input_pin = remove->node->input_pins[i];
input_pin->net->fanout_pins[input_pin->pin_net_idx] = NULL; // Remove the fanout pin from the net
}
remove->node->node_data = VISITED_REMOVAL;
remove = remove->next;
}
}
/* Since we are traversing the entire netlist anyway, we can use this
* opportunity to keep track of the heads of adder/subtractors chains
* and then compute statistics on them */
long adder_chain_count = 0;
long longest_adder_chain = 0;
long total_adders = 0;
long subtractor_chain_count = 0;
long longest_subtractor_chain = 0;
long total_subtractors = 0;
double geomean_addsub_length = 0.0; // Geometric mean of add/sub chain length
double sum_of_addsub_logs = 0.0; // Sum of the logarithms of the add/sub chain lengths; used for geomean
double total_addsub_chain_count = 0.0;
void calculate_addsub_statistics(node_list_t *addsub){
while(addsub != NULL && addsub->node != NULL){
int found_tail = false;
nnode_t *node = addsub->node;
int chain_depth = 0;
while(!found_tail){
if(node->node_data == VISITED_REMOVAL){
found_tail = true;
break;
}
chain_depth += 1;
/* Carry out is always output pin 0 */
nnet_t *carry_out_net = node->output_pins[0]->net;
if(carry_out_net == NULL || carry_out_net->fanout_pins[0] == NULL) found_tail = true;
else node = carry_out_net->fanout_pins[0]->node;
}
if(chain_depth > 0){
if(node->type == ADD){
adder_chain_count += 1;
total_adders += chain_depth;
if(chain_depth > longest_adder_chain) longest_adder_chain = chain_depth;
}
else if(node->type == MINUS){
subtractor_chain_count += 1;
total_subtractors += chain_depth;
if(chain_depth > longest_subtractor_chain) longest_subtractor_chain = chain_depth;
}
sum_of_addsub_logs += log(chain_depth);
total_addsub_chain_count += 1.0;
}
addsub = addsub->next;
}
/* Calculate the geometric mean carry chain length */
geomean_addsub_length = exp(sum_of_addsub_logs / total_addsub_chain_count);
}
/* Perform the backwards and forward sweeps and remove the unused nodes */
void remove_unused_logic(netlist_t *netlist){
mark_output_dependencies(netlist);
identify_unused_nodes(netlist);
remove_unused_nodes(&useless_nodes);
calculate_addsub_statistics(&addsub_nodes);
}