blob: ba481263368145bd344929928b1d6762a8f498b8 [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 <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "odin_types.h"
#include "odin_util.h"
#include "node_creation_library.h"
#include "adders.h"
#include "netlist_utils.h"
#include "partial_map.h"
#include "read_xml_arch_file.h"
#include "odin_globals.h"
#include "multipliers.h"
#include "subtractions.h"
#include "vtr_memory.h"
#include "vtr_list.h"
using vtr::t_linked_vptr;
t_model *hard_adders = NULL;
t_linked_vptr *add_list = NULL;
t_linked_vptr *processed_adder_list = NULL;
t_linked_vptr *chain_list = NULL;
int total = 0;
int *adder = NULL;
int min_add = 0;
int min_threshold_adder = 0;
netlist_t *the_netlist;
void record_add_distribution(nnode_t *node);
void init_split_adder(nnode_t *node, nnode_t *ptr, int a, int sizea, int b, int sizeb, int cin, int cout, int index, int flag, netlist_t *netlist);
/*---------------------------------------------------------------------------
* (function: init_add_distribution)
* For adder, the output will only be the maxim input size + 1
*-------------------------------------------------------------------------*/
void init_add_distribution()
{
oassert(hard_adders != NULL);
int len = hard_adders->inputs->size + hard_adders->inputs->next->size + 1;
adder = (int *)vtr::calloc(len, sizeof(int));
}
/*---------------------------------------------------------------------------
* (function: record_add_distribution)
*-------------------------------------------------------------------------*/
void record_add_distribution(nnode_t *node)
{
oassert(hard_adders != NULL);
oassert(node != NULL);
adder[hard_adders->inputs->size] += 1;
return;
}
/*---------------------------------------------------------------------------
* (function: report_add_distribution)
*-------------------------------------------------------------------------*/
/* These values are collected during the unused logic removal sweep */
extern long adder_chain_count;
extern long longest_adder_chain;
extern long total_adders;
extern double geomean_addsub_length;
extern double sum_of_addsub_logs;
void report_add_distribution()
{
if(hard_adders == NULL)
return;
printf("\nHard adder Distribution\n");
printf("============================\n");
printf("\n");
printf("\nTotal # of chains = %ld\n", adder_chain_count);
printf("\nHard adder chain Details\n");
printf("============================\n");
printf("\n");
printf("\nThe Number of Hard Block adders in the Longest Chain: %ld\n", longest_adder_chain);
printf("\n");
printf("\nThe Total Number of Hard Block adders: %ld\n", total_adders);
printf("\n");
printf("\nGeometric mean adder/subtractor chain length: %.2f\n", geomean_addsub_length);
vtr::free(adder);
}
/*---------------------------------------------------------------------------
* (function: find_hard_adders)
*-------------------------------------------------------------------------*/
void find_hard_adders()
{
hard_adders = Arch.models;
//Disable the size in configuration file.(The threshold for the extra bits).
//min_add = configuration.min_hard_adder;
min_threshold_adder = configuration.min_threshold_adder;
while (hard_adders != NULL)
{
if (strcmp(hard_adders->name, "adder") == 0)
{
init_add_distribution();
return;
}
else
{
hard_adders = hard_adders->next;
}
}
return;
}
/*---------------------------------------------------------------------------
* (function: declare_hard_adder)
*-------------------------------------------------------------------------*/
void declare_hard_adder(nnode_t *node)
{
t_adder *tmp;
int width_a, width_b, width_sumout;
/* See if this size instance of adder exists? */
if (hard_adders == NULL)
warning_message(NETLIST_ERROR, node->related_ast_node->line_number, node->related_ast_node->file_number, "%s\n", "Instantiating adder where adders do not exist");
tmp = (t_adder *)hard_adders->instances;
width_a = node->input_port_sizes[0];
width_b = node->input_port_sizes[1];
width_sumout = node->output_port_sizes[1];
while (tmp != NULL)
{
if ((tmp->size_a == width_a) && (tmp->size_b == width_b) && (tmp->size_sumout == width_sumout))
return;
else
tmp = tmp->next;
}
/* Does not exist - must create an instance */
tmp = (t_adder *)vtr::malloc(sizeof(t_adder));
tmp->next = (t_adder *)hard_adders->instances;
hard_adders->instances = tmp;
tmp->size_a = width_a;
tmp->size_b = width_b;
tmp->size_cin = 1;
tmp->size_cout = 1;
tmp->size_sumout = width_sumout;
return;
}
/*---------------------------------------------------------------------------
* (function: instantiate_hard_addier )
*-------------------------------------------------------------------------*/
void instantiate_hard_adder(nnode_t *node, short mark, netlist_t * /*netlist*/)
{
char *new_name;
int len, sanity, i;
declare_hard_adder(node);
/* Need to give node proper name */
len = strlen(node->name);
len = len + 20; /* 20 chars should hold mul specs */
new_name = (char*)vtr::malloc(len);
/* wide input first :) identical branches! */
// if (node->input_port_sizes[0] > node->input_port_sizes[1])
// sanity = odin_sprintf(new_name, "%s", node->name);
// else
sanity = odin_sprintf(new_name, "%s", node->name);
if(new_name)
vtr::free(new_name);
if (len <= sanity) /* buffer not large enough */
oassert(false);
/* Give names to the output pins */
for (i = 0; i < node->num_output_pins; i++)
{
if (node->output_pins[i]->name == NULL)
{
len = strlen(node->name) + 20; /* 6 chars for pin idx */
new_name = (char*)vtr::malloc(len);
odin_sprintf(new_name, "%s[%d]", node->name, node->output_pins[i]->pin_node_idx);
node->output_pins[i]->name = new_name;
}
}
node->traverse_visited = mark;
return;
}
/*----------------------------------------------------------------------------
* function: add_the_blackbox_for_adds()
*--------------------------------------------------------------------------*/
void add_the_blackbox_for_adds(FILE *out)
{
int i;
int count;
int hard_add_inputs, hard_add_outputs;
t_adder *adds;
t_model_ports *ports;
char buffer[MAX_BUF];
char *pa, *pb, *psumout, *pcin, *pcout;
/* Check to make sure this target architecture has hard adders */
if (hard_adders == NULL)
return;
/* Get the names of the ports for the adder */
ports = hard_adders->inputs;
pcin = ports->name;
ports = ports->next;
pb = ports->name;
ports = ports->next;
pa = ports->name;
ports = hard_adders->outputs;
psumout = ports->name;
ports = ports->next;
pcout = ports->name;
/* find the adder devices in the tech library */
adds = (t_adder *)(hard_adders->instances);
if (adds == NULL) /* No adders instantiated */
return;
/* simplified way of getting the multsize, but fine for quick example */
while (adds != NULL)
{
/* write out this adder model TODO identical branches ?*/
// if (configuration.fixed_hard_adder != 0)
// count = fprintf(out, ".model adder\n");
// else
count = fprintf(out, ".model adder\n");
/* add the inputs */
count = count + fprintf(out, ".inputs");
hard_add_inputs = adds->size_a + adds->size_b + adds->size_cin;
for (i = 0; i < hard_add_inputs; i++)
{
if (i < adds->size_a)
{
count = count + odin_sprintf(buffer, " %s[%d]", pa, i);
}
else if(i < hard_add_inputs - adds->size_cin && i >= adds->size_a)
{
count = count + odin_sprintf(buffer, " %s[%d]", pb, i - adds->size_a);
}
else
{
count = count + odin_sprintf(buffer, " %s[%d]", pcin, i - adds->size_a - adds->size_b);
}
if (count > 78)
count = fprintf(out, " \\\n %s", buffer) - 3;
else
fprintf(out, " %s", buffer);
}
fprintf(out, "\n");
/* add the outputs */
count = fprintf(out, ".outputs");
hard_add_outputs = adds->size_cout + adds->size_sumout;
for (i = 0; i < hard_add_outputs; i++)
{
if (i < adds->size_cout)
{
count = count + odin_sprintf(buffer, " %s[%d]", pcout, i);
}
else
{
count = count + odin_sprintf(buffer, " %s[%d]", psumout, i - adds->size_cout);
}
if (count > 78)
{
fprintf(out, " \\\n%s", buffer);
count = strlen(buffer);
}
else
fprintf(out, "%s", buffer);
}
fprintf(out, "\n");
fprintf(out, ".blackbox\n");
fprintf(out, ".end\n");
fprintf(out, "\n");
adds = adds->next;
}
}
/*-------------------------------------------------------------------------
* (function: define_add_function)
*-----------------------------------------------------------------------*/
void define_add_function(nnode_t *node, FILE *out)
{
int i, j;
int count;
char buffer[MAX_BUF];
count = fprintf(out, "\n.subckt");
count--;
oassert(node->input_port_sizes[0] > 0);
oassert(node->input_port_sizes[1] > 0);
oassert(node->input_port_sizes[2] > 0);
oassert(node->output_port_sizes[0] > 0);
oassert(node->output_port_sizes[1] > 0);
/* Write out th bec_csla = 3 // binary to excess carry Select Addere model adder */
count += fprintf(out, " adder");
/* Write the input pins*/
for (i = 0; i < node->num_input_pins; i++)
{
npin_t *driver_pin = node->input_pins[i]->net->driver_pin;
if (i < node->input_port_sizes[0])
{
if (!driver_pin->name)
j = odin_sprintf(buffer, " %s[%d]=%s", hard_adders->inputs->next->next->name, i, driver_pin->node->name);
else
j = odin_sprintf(buffer, " %s[%d]=%s", hard_adders->inputs->next->next->name, i, driver_pin->name);
}
else if(i >= node->input_port_sizes[0] && i < node->input_port_sizes[1] + node->input_port_sizes[0])
{
if (!driver_pin->name)
j = odin_sprintf(buffer, " %s[%d]=%s", hard_adders->inputs->next->name, i - node->input_port_sizes[0], driver_pin->node->name);
else
j = odin_sprintf(buffer, " %s[%d]=%s", hard_adders->inputs->next->name, i - node->input_port_sizes[0], driver_pin->name);
}
else
{
if (!driver_pin->name)
j = odin_sprintf(buffer, " %s[%d]=%s", hard_adders->inputs->name, i - (node->input_port_sizes[0] + node->input_port_sizes[1]), driver_pin->node->name);
else
j = odin_sprintf(buffer, " %s[%d]=%s", hard_adders->inputs->name, i - (node->input_port_sizes[0] + node->input_port_sizes[1]), driver_pin->name);
}
if (count + j > 79)
{
fprintf(out, "\\\n");
count = 0;
}
count += fprintf(out, "%s", buffer);
}
/* Write the output pins*/
for (i = 0; i < node->num_output_pins; i++)
{
if(i < node->output_port_sizes[0])
j = odin_sprintf(buffer, " %s[%d]=%s", hard_adders->outputs->next->name, i , node->output_pins[i]->name);
else
j = odin_sprintf(buffer, " %s[%d]=%s", hard_adders->outputs->name, i - node->output_port_sizes[0], node->output_pins[i]->name);
if (count + j > 79)
{
fprintf(out, "\\\n");
count = 0;
}
count += fprintf(out, "%s", buffer);
}
fprintf(out, "\n\n");
return;
}
/*-----------------------------------------------------------------------
* (function: init_split_adder)
* ##################################
* TODO the soft logic adders can now be splitted at the source, we could tap onto that and merge these function for
* simplicicity and would also make sure to keep the allocation at one place
*###################################
* Create a carry chain adder when spliting. Inputs are connected
* to original pins, output pins are set to NULL for later connecting
* flag = 0: all adders are hard logic block; flag = 1: the last adder in the chain is soft logic block
*---------------------------------------------------------------------*/
void init_split_adder(nnode_t *node, nnode_t *ptr, int a, int sizea, int b, int sizeb, int cin, int cout, int index, int flag, netlist_t *netlist)
{
int i;
int flaga = 0, flagb = 0;
int current_sizea, current_sizeb;
int aa = 0, bb = 0, num = 0;
// if the input of the first cin is generated by a dummy adder added
// to the start of the chain, then an offset is needed to compensate
// for that in various positions in the code, otherwise the offset is 0
const int offset = (configuration.adder_cin_global)? 0 : 1;
/* Copy properties from original node */
ptr->type = node->type;
ptr->bit_width = node->bit_width;
ptr->related_ast_node = node->related_ast_node;
ptr->traverse_visited = node->traverse_visited;
ptr->node_data = NULL;
/* decide the current size of input a and b */
if(flag == 0)
{
// increase input sizes by one if a dummy adder is
// added to feed the first cin in the chain
current_sizea = (a + offset) - sizea * index;
current_sizeb = (b + offset) - sizeb * index;
if(current_sizea >= sizea)
current_sizea = sizea;
else if(current_sizea <= 0)
{
current_sizea = sizea;
flaga = 1;
}
else
{
aa = current_sizea;
current_sizea = sizea;
flaga = 2;
}
if(current_sizeb >= sizeb)
current_sizeb = sizeb;
else if(current_sizeb <= 0)
{
current_sizeb = sizeb;
flagb = 1;
}
else
{
bb = current_sizeb;
current_sizeb = sizeb;
flagb = 2;
}
}
else
{
if(sizea != 0)
current_sizea = sizea;
else
current_sizea = 1;
if(sizeb != 0)
current_sizeb = sizeb;
else
current_sizeb = 1;
}
/* Set new port sizes and parameters */
ptr->num_input_port_sizes = 3;
ptr->input_port_sizes = (int *)vtr::malloc(3 * sizeof(int));
ptr->input_port_sizes[0] = current_sizea;
ptr->input_port_sizes[1] = current_sizeb;
ptr->input_port_sizes[2] = cin;
ptr->num_output_port_sizes = 2;
ptr->output_port_sizes = (int *)vtr::malloc(2 * sizeof(int));
ptr->output_port_sizes[0] = cout;
/* The size of output port sumout equals the maxim size of sizea and sizeb */
if(current_sizea > current_sizeb)
ptr->output_port_sizes[1] = current_sizea;
else
ptr->output_port_sizes[1] = current_sizeb;
/* Set the number of pins and re-locate previous pin entries */
ptr->num_input_pins = current_sizea + current_sizeb + cin;
ptr->input_pins = (npin_t**)vtr::malloc(sizeof(void *) * (current_sizea + current_sizeb + cin));
//if flaga or flagb = 1, the input pins should be empty.
if(flaga == 1)
{
for (i = 0; i < current_sizea; i++)
ptr->input_pins[i] = NULL;
}
else if(flaga == 2)
{
if(index == 0)
{
ptr->input_pins[0] = NULL;
if(sizea > 1){
for (i = 1; i < aa; i++)
{
ptr->input_pins[i] = node->input_pins[i + index * sizea - 1];
ptr->input_pins[i]->node = ptr;
ptr->input_pins[i]->pin_node_idx = i;
}
for (i = 0; i < (sizea - aa); i++)
ptr->input_pins[i + aa] = NULL;
}
}
else
{
for (i = 0; i < aa; i++)
{
ptr->input_pins[i] = node->input_pins[i + index * sizea - 1];
ptr->input_pins[i]->node = ptr;
ptr->input_pins[i]->pin_node_idx = i;
}
for (i = 0; i < (sizea - aa); i++)
ptr->input_pins[i + aa] = NULL;
}
}
else
{
if(index == 0 && !configuration.adder_cin_global)
{
if(flag == 0)
{
ptr->input_pins[0] = NULL;
if(current_sizea > 1)
{
for (i = 1; i < current_sizea; i++)
{
ptr->input_pins[i] = node->input_pins[i - 1];
ptr->input_pins[i]->node = ptr;
ptr->input_pins[i]->pin_node_idx = i;
}
}
}
else
{
for (i = 0; i < current_sizea; i++)
{
ptr->input_pins[i] = node->input_pins[i];
ptr->input_pins[i]->node = ptr;
ptr->input_pins[i]->pin_node_idx = i;
}
}
}
else
{
if(flag == 0)
{
for (i = 0; i < current_sizea; i++)
{
// use the offset to compensate for the dummy adder added at start of the chain
ptr->input_pins[i] = node->input_pins[i + index * sizea - offset];
ptr->input_pins[i]->node = ptr;
ptr->input_pins[i]->pin_node_idx = i;
}
}
else
{
if(sizea == 0)
connect_nodes(netlist->gnd_node, 0, ptr, 0);
else
{
num = node->input_port_sizes[0];
for (i = 0; i < current_sizea; i++)
{
ptr->input_pins[i] = node->input_pins[i + num - current_sizea];
ptr->input_pins[i]->node = ptr;
ptr->input_pins[i]->pin_node_idx = i;
}
}
}
}
}
if(flagb == 1)
{
for (i = 0; i < current_sizeb; i++)
ptr->input_pins[i + current_sizeb] = NULL;
}
else if(flagb == 2)
{
if(index == 0)
{
ptr->input_pins[sizea] = NULL;
if(current_sizeb > 1)
{
for (i = 1; i < bb; i++)
{
ptr->input_pins[i + current_sizea] = node->input_pins[i + a + index * sizeb - 1];
ptr->input_pins[i + current_sizea]->node = ptr;
ptr->input_pins[i + current_sizea]->pin_node_idx = i + current_sizea;
}
for (i = 0; i < (sizeb - bb); i++)
ptr->input_pins[i + current_sizea + bb] = NULL;
}
}
else
{
for (i = 0; i < bb; i++)
{
ptr->input_pins[i + current_sizea] = node->input_pins[i + a + index * sizeb - 1];
ptr->input_pins[i + current_sizea]->node = ptr;
ptr->input_pins[i + current_sizea]->pin_node_idx = i + current_sizea;
}
for (i = 0; i < (sizeb - bb); i++)
ptr->input_pins[i + current_sizea + bb] = NULL;
}
}
else
{
if(index == 0 && !configuration.adder_cin_global)
{
if(flag == 0)
{
ptr->input_pins[sizea] = NULL;
if(current_sizeb > 1)
{
for (i = 1; i < current_sizeb; i++)
{
ptr->input_pins[i + current_sizea] = node->input_pins[i + a + index * sizeb - 1];
ptr->input_pins[i + current_sizea]->node = ptr;
ptr->input_pins[i + current_sizea]->pin_node_idx = i + current_sizea;
}
}
}
else
{
for (i = 0; i < current_sizeb; i++)
{
ptr->input_pins[i + current_sizea] = node->input_pins[i + a];
ptr->input_pins[i + current_sizea]->node = ptr;
ptr->input_pins[i + current_sizea]->pin_node_idx = i + current_sizea;
}
}
}
else
{
if(flag == 0)
{
for (i = 0; i < current_sizeb; i++)
{
ptr->input_pins[i + current_sizea] = node->input_pins[i + a + index * sizeb - offset];
ptr->input_pins[i + current_sizea]->node = ptr;
ptr->input_pins[i + current_sizea]->pin_node_idx = i + current_sizea;
}
}
else
{
if(sizeb == 0)
connect_nodes(netlist->gnd_node, 0, ptr, current_sizea);
else
{
num = node->input_port_sizes[0] + node->input_port_sizes[1];
for (i = 0; i < current_sizeb; i++)
{
ptr->input_pins[i + current_sizea] = node->input_pins[i + num - current_sizeb];
ptr->input_pins[i + current_sizea]->node = ptr;
ptr->input_pins[i + current_sizea]->pin_node_idx = i + current_sizea;
}
}
}
}
}
/* Carry_in should be NULL*/
for (i = 0; i < cin; i++)
{
ptr->input_pins[i + current_sizea + current_sizeb] = NULL;
}
/* output pins */
int output;
if(current_sizea > current_sizeb)
output = current_sizea + cout;
else
output = current_sizeb + cout;
ptr->num_output_pins = output;
ptr->output_pins = (npin_t**)vtr::malloc(sizeof(void *) * output);
for (i = 0; i < output; i++)
ptr->output_pins[i] = NULL;
return;
}
/*-------------------------------------------------------------------------
* (function: split_adder)
*
* This function works to split a adder into several smaller
* adders to better "fit" with the available resources in a
* targeted FPGA architecture.
*
* This function is at the lowest level since it simply receives
* a adder and is told how to split it.
*
* Note: In this function, we can do padding(default -1), fix the size of hard block adder.
*-----------------------------------------------------------------------*/
void split_adder(nnode_t *nodeo, int a, int b, int sizea, int sizeb, int cin, int cout, int count, netlist_t *netlist)
{
nnode_t **node;
int i,j;
int num, lefta = 0, leftb = 0;
int max_num = 0;
int flag = 0;
// if the input of the first cin is generated by a dummy adder added
// to the start of the chain, then an offset is needed to compensate
// for that in various positions in the code, otherwise the offset is 0
const int offset = (configuration.adder_cin_global)? 0 : 1;
/* Check for a legitimate split */
oassert(nodeo->input_port_sizes[0] == a);
oassert(nodeo->input_port_sizes[1] == b);
node = (nnode_t**)vtr::malloc(sizeof(nnode_t*)*(count));
for(i = 0; i < count; i++)
{
node[i] = allocate_nnode();
node[i]->name = (char *)vtr::malloc(strlen(nodeo->name) + 20);
odin_sprintf(node[i]->name, "%s-%d", nodeo->name, i);
if(i == count - 1)
{
//fixed_hard_adder = 1 then adder need to be exact size;
if(configuration.fixed_hard_adder == 1)
init_split_adder(nodeo, node[i], a, sizea, b, sizeb, cin, cout, i, flag, netlist);
else
{
if(count == 1)
{
lefta = a;
leftb = b;
}
else
{
lefta = (a + 1) % sizea;
leftb = (b + 1) % sizeb;
}
max_num = (lefta >= leftb)? lefta: leftb;
// if fixed_hard_adder = 0, and the left of a and b is more than min_add, then adder need to be remain the same size.
if(max_num >= min_add)
init_split_adder(nodeo, node[i], a, sizea, b, sizeb, cin, cout, i, flag, netlist);
else
{
// Using soft logic to do the addition, No need to pad as the same size
flag = 1;
init_split_adder(nodeo, node[i], a, lefta, b, leftb, cin, cout, i, flag, netlist);
}
}
}
else
init_split_adder(nodeo, node[i], a, sizea, b, sizeb, cin, cout, i, flag, netlist);
//store the processed hard adder node for optimization
processed_adder_list = insert_in_vptr_list(processed_adder_list, node[i]);
}
chain_information_t *adder_chain = allocate_chain_info();
//if flag = 0, the last adder use soft logic, so the count of the chain should be one less
if(flag == 0)
adder_chain->count = count;
else
adder_chain->count = count - 1;
adder_chain->num_bits = a + b;
adder_chain->name = nodeo->name;
chain_list = insert_in_vptr_list(chain_list, adder_chain);
// don't add a dummy adder in the beginning of the chain if the first cin will be connected to a global gnd
if((flag == 0 || count > 1) && !configuration.adder_cin_global)
{
//connect the a[0] and b[0] of first adder node to ground
connect_nodes(netlist->gnd_node, 0, node[0], 0);
connect_nodes(netlist->gnd_node, 0, node[0], sizea);
//hang the first sumout
node[0]->output_pins[1] = allocate_npin();
node[0]->output_pins[1]->name = append_string("", "%s~dummy_output~%d~%d", node[0]->name, 0, 1);
}
if(nodeo->num_input_port_sizes == 2)
{
//connect the first cin pin to unconn
connect_nodes(netlist->pad_node, 0, node[0], node[0]->num_input_pins - 1);
}
else if(nodeo->num_input_port_sizes == 3)
{
//remap the first cin pins)
remap_pin_to_new_node(nodeo->input_pins[nodeo->num_input_pins - 1], node[0], (node[0]->num_input_pins - 1));
}
//if (a + 1) % sizea == 0, the a[0] and b[0] of node[count-1] should connect to gound
if((a + 1) % sizea == 0 && (b + 1) % sizeb == 0)
{
if(flag == 0)
{
connect_nodes(netlist->gnd_node, 0, node[count-1], 0);
connect_nodes(netlist->gnd_node, 0, node[count-1], sizea);
}
}
//if any input pins beside first cin pins are NULL, connect those pins to unconn
for(i = 0; i < count; i++)
{
num = node[i]->num_input_pins;
for(j = 0; j < num - 1; j++)
{
if(node[i]->input_pins[j] == NULL)
connect_nodes(netlist->pad_node, 0, node[i],j);
}
}
if (configuration.adder_cin_global) {
// connect first cin to gnd
connect_nodes(netlist->gnd_node, 0, node[0], (node[0]->num_input_pins - 1));
}
//connect cout to next cin
for(i = 1; i < count; i++)
connect_nodes(node[i-1], 0, node[i], (node[i]->num_input_pins - 1));
//remap the output pins of each adder to nodeo
if(count == 1)
{
if(flag == 0)
{
for(j = 0; j < node[0]->num_output_pins - 2; j++)
{
if(j < nodeo->num_output_pins)
remap_pin_to_new_node(nodeo->output_pins[j], node[0], j + 2);
else
{
node[0]->output_pins[j + 2] = allocate_npin();
node[0]->output_pins[j + 2]->name = append_string("", "%s~dummy_output~%d~%d", node[0]->name, 0, j + 2);
}
//hang the first cout
node[0]->output_pins[0] = allocate_npin();
node[0]->output_pins[0]->name = append_string("", "%s~dummy_output~%d~%d", node[0]->name, 0, 0);
}
}
else
{
for(j = 0; j < node[0]->num_output_pins - 1; j ++)
remap_pin_to_new_node(nodeo->output_pins[j], node[0], j + 1);
remap_pin_to_new_node(nodeo->output_pins[nodeo->num_output_pins - 1], node[0], 0);
}
}
else
{
//First adder
for(j = 0; j < node[0]->num_output_pins - 2; j++)
remap_pin_to_new_node(nodeo->output_pins[j], node[0], j + 2);
// if a dummy adder is added (offset = 1) start from the second adder)
for(i = offset; i < count - 1; i ++)
{
for(j = 0; j < node[i]->num_output_pins - 1; j ++)
remap_pin_to_new_node(nodeo->output_pins[i * sizea + j - offset], node[i], j + 1);
}
//Last adder
if(flag == 0)
{
for(j = 0; j < node[count-1]->num_output_pins - 1; j ++)
{
// if a dummy adder is added to this chain (offset = 1), adjust the index of the adder using the offset constant
if(((count - 1) * sizea + j - offset) < nodeo->num_output_pins)
remap_pin_to_new_node(nodeo->output_pins[(count - 1) * sizea + j - offset], node[count - 1], j + 1);
else
{
node[count - 1]->output_pins[j + 1] = allocate_npin();
// Pad outputs with a unique and descriptive name to avoid collisions.
node[count - 1]->output_pins[j + 1]->name = append_string("", "%s~dummy_output~%d~%d", node[count - 1]->name, count - 1, j + 1);
}
}
//Hang the last cout
node[count - 1]->output_pins[0] = allocate_npin();
// Pad outputs with a unique and descriptive name to avoid collisions.
node[count - 1]->output_pins[0]->name = append_string("", "%s~dummy_output~%d~%d", node[count - 1]->name, count - 1, 0);
}
else
{
for(j = 0; j < node[count - 1]->num_output_pins - 1; j ++)
//if(((count - 1) * sizea + j - 1) < nodeo->num_output_pins)
remap_pin_to_new_node(nodeo->output_pins[(count - 1) * sizea + j - 1], node[count - 1], j + 1);
if(nodeo->output_pins[nodeo->num_output_pins - 1] != NULL)
remap_pin_to_new_node(nodeo->output_pins[nodeo->num_output_pins - 1], node[count - 1], 0);
else
{
node[count - 1]->output_pins[0] = allocate_npin();
// Pad outputs with a unique and descriptive name to avoid collisions.
node[count - 1]->output_pins[0]->name = append_string("", "%s~dummy_output~%d~%d", node[count - 1]->name, count - 1, 0);
}
}
}
/* Probably more to do here in freeing the old node! */
vtr::free(nodeo->name);
vtr::free(nodeo->input_port_sizes);
vtr::free(nodeo->output_port_sizes);
/* Free arrays NOT the pins since relocated! */
vtr::free(nodeo->input_pins);
vtr::free(nodeo->output_pins);
vtr::free(nodeo);
vtr::free(node);
return;
}
/*-------------------------------------------------------------------------
* (function: iterate_adders)
*
* This function will iterate over all of the add operations that
* exist in the netlist and perform a splitting so that they can
* fit into a basic hard adder block that exists on the FPGA.
* If the proper option is set, then it will be expanded as well
* to just use a fixed size hard adder.
*-----------------------------------------------------------------------*/
void iterate_adders(netlist_t *netlist)
{
int sizea, sizeb, sizecin;//the size of
int a, b;
int count,counta,countb;
int num = 0;
nnode_t *node;
// offset to the adder size in case a dummy adder is added to
// start of the adder chain to feed the first cin with gnd
const int offset = (configuration.adder_cin_global)? 0 : 1;
/* Can only perform the optimisation if hard adders exist! */
if (hard_adders == NULL)
return;
//In hard block adder, the summand and addend are same size.
sizecin = hard_adders->inputs->size;
sizeb = hard_adders->inputs->next->size;
sizea = hard_adders->inputs->next->size;
oassert(sizecin == 1);
while (add_list != NULL)
{
node = (nnode_t *)add_list->data_vptr;
add_list = delete_in_vptr_list(add_list);
oassert(node != NULL);
if (node->type == HARD_IP)
node->type = ADD;
oassert(node->type == ADD);
a = node->input_port_sizes[0];
b = node->input_port_sizes[1];
num = (a >= b)? a : b;
node->bit_width = num;
if(num >= min_threshold_adder && num >= min_add)
{
// if the first cin in a chain is fed by a global input (offset = 0) the adder width is the
// input width + 1 (to pass the last cout -> sumout) divided by size of the adder input ports
// otherwise (offset = 1) a dummy adder is added to the chain to feed the first cin with gnd
// how many adders a can split
counta = (a + 1) / sizea + offset;
// how many adders b can split
countb = (b + 1) / sizeb + offset;
// how many adders need to be split
if(counta >= countb)
count = counta;
else
count = countb;
total++;
split_adder(node, a, b, sizea, sizeb, 1, 1, count, netlist);
}
// Store the node into processed_adder_list if the threshold is bigger than num
else
processed_adder_list = insert_in_vptr_list(processed_adder_list, node);
}
return;
}
/*-------------------------------------------------------------------------
* (function: clean_adders)
*
* Clean up the memory by deleting the list structure of adders
* during optimization
*-----------------------------------------------------------------------*/
void clean_adders()
{
while (add_list != NULL)
add_list = delete_in_vptr_list(add_list);
return;
}
/*-------------------------------------------------------------------------
* (function: reduce_operations)
*
* reduce the operations that are redundant
*-----------------------------------------------------------------------*/
void reduce_operations(netlist_t * /*netlist*/, operation_list op)
{
t_linked_vptr *place = NULL;
operation_list oper;
switch (op)
{
case ADD:
place = add_list;
oper = ADD;
break;
case MULTIPLY:
place = mult_list;
oper = MULTIPLY;
break;
case MINUS:
place = sub_list;
oper = MINUS;
break;
default:
oper = NO_OP;
break;
}
traverse_list(oper, place);
}
/*-------------------------------------------------------------------------
* (function: traverse_list)
*
* traverse the operation lists
*-----------------------------------------------------------------------*/
void traverse_list(operation_list oper, t_linked_vptr *place)
{
while (place != NULL && place->next != NULL)
{
match_node(place, oper);
place = place->next;
}
}
/*---------------------------------------------------------------------------
* (function: match_node)
*-------------------------------------------------------------------------*/
void match_node(t_linked_vptr *place, operation_list oper)
{
int flag = 0;
int mark = 0;
nnode_t *node = NULL;
nnode_t *next_node = NULL;
node = (nnode_t*)place->data_vptr;
t_linked_vptr *pre = place;
t_linked_vptr *next = NULL;
if (place->next != NULL)
next = place->next;
while (next != NULL)
{
flag = 0;
mark = 0;
next_node = (nnode_t*)next->data_vptr;
if (node->type == next_node->type)
{
if (node->num_input_pins == next_node->num_input_pins)
{
flag = match_ports(node, next_node, oper);
if (flag == 1)
{
mark = match_pins(node, next_node);
if (mark == 1)
{
merge_nodes(node, next_node);
remove_list_node(pre, next);
}
}
}
}
if (mark == 1)
next = pre->next;
else
{
pre = next;
next= next->next;
}
}
}
/*---------------------------------------------------------------------------
* (function: match_ports)
*-------------------------------------------------------------------------*/
int match_ports(nnode_t *node, nnode_t *next_node, operation_list oper)
{
int flag = 0;
int sign = 0;
int mark1 = 1;
int mark2 = 1;
ast_node_t *ast_node, *ast_node_next;
char *component_s[2] = {0};
char *component_o[2] = {0};
ast_node = node->related_ast_node;
ast_node_next = next_node->related_ast_node;
if (ast_node->types.operation.op == oper)
{
traverse_operation_node(ast_node, component_s, oper, &sign);
if (sign != 1)
{
traverse_operation_node(ast_node_next, component_o, oper, &sign);
if (sign != 1)
{
oassert(component_s[0] && component_o[0] && "missing children on operation");
switch (oper)
{
case ADD:
case MULTIPLY:
{
mark1 = strcmp(component_s[0], component_o[0]);
if (component_s[1] && component_o[1])
{
if (mark1 == 0)
{
mark2 = strcmp(component_s[1], component_o[1]);
}
else
{
mark1 = strcmp(component_s[0], component_o[1]);
mark2 = strcmp(component_s[1], component_o[0]);
}
}
}
break;
case MINUS:
{
mark1 = strcmp(component_s[0], component_o[0]);
if (mark1 == 0 && component_s[1] && component_o[1])
{
mark2 = strcmp(component_s[1], component_o[1]);
}
}
break;
default:
break;
}
if (mark1 == 0 && mark2 == 0)
{
flag = 1;
}
}
}
for(int i = 0; i < ast_node->num_children; i++)
{
if(ast_node->children[i]->type != IDENTIFIERS)
{
vtr::free(component_s[i]);
}
}
for (int i = 0; i < ast_node_next->num_children; i++)
{
if(ast_node_next->children[i]->type != IDENTIFIERS)
{
vtr::free(component_o[i]);
}
}
}
return flag;
}
/*-------------------------------------------------------------------------
* (function: traverse_operation_node)
*
* search the ast find the couple of components
*-----------------------------------------------------------------------*/
void traverse_operation_node(ast_node_t *node, char *component[], operation_list op, int *mark)
{
long i;
if (node == NULL)
return;
if (node->types.operation.op == op)
{
for (i = 0; i < node->num_children; i++)
{
*mark = 0;
if (node->children[i]->type != IDENTIFIERS && node->children[i]->type != NUMBERS)
{
*mark = 1;
break;
}
else
{
if (node->children[i]->type == IDENTIFIERS)
{
component[i] = node->children[i]->types.identifier;
}
else if (node->children[i]->type == NUMBERS)
{
long value = node->children[i]->types.vnumber->get_value();
long len = snprintf(NULL,0,"%ld", value);
component[i] = (char *)vtr::calloc(len+1,sizeof(char));
odin_sprintf(component[i], "%ld", value);
}
}
}
}
}
/*---------------------------------------------------------------------------
* (function: merge_node)
*-------------------------------------------------------------------------*/
void merge_nodes(nnode_t *node, nnode_t *next_node)
{
remove_fanout_pins(next_node);
reallocate_pins(node, next_node);
free_op_nodes(next_node);
}
/*---------------------------------------------------------------------------
* (function: remove_list_node)
*-------------------------------------------------------------------------*/
void remove_list_node(t_linked_vptr *pre, t_linked_vptr *next)
{
if (next->next != NULL)
pre->next = next->next;
else
pre->next = NULL;
vtr::free(next);
}
/*---------------------------------------------------------------------------
* (function: remove_fanout_pins)
*-------------------------------------------------------------------------*/
void remove_fanout_pins(nnode_t *node)
{
int i, j, k, idx;
for (i = 0; i < node->num_input_pins; i++)
{
idx = node->input_pins[i]->unique_id;
for (j = 0; j < node->input_pins[i]->net->num_fanout_pins; j++)
{
if (node->input_pins[i]->net->fanout_pins[j]->unique_id == idx)
break;
}
for (k = j; k < node->input_pins[i]->net->num_fanout_pins - 1; k++)
{
node->input_pins[i]->net->fanout_pins[k] = node->input_pins[i]->net->fanout_pins[k+1];
node->input_pins[i]->net->fanout_pins[k]->pin_net_idx = k;
}
node->input_pins[i]->net->fanout_pins[k] = NULL;
node->input_pins[i]->net->num_fanout_pins--;
}
}
/*---------------------------------------------------------------------------
* (function: reallocate_pins)
*-------------------------------------------------------------------------*/
void reallocate_pins(nnode_t *node, nnode_t *next_node)
{
int i, j;
int pin_idx;
nnode_t *input_node = NULL;
nnet_t *net = NULL;
npin_t *pin = NULL;
for (i = 0; i < next_node->num_output_pins; i++)
{
for (j = 0; j < next_node->output_pins[i]->net->num_fanout_pins; j++)
{
if (next_node->output_pins[i]->net->fanout_pins[j]->node != NULL)
{
input_node = next_node->output_pins[i]->net->fanout_pins[j]->node;
net = node->output_pins[i]->net;
pin_idx = next_node->output_pins[i]->net->fanout_pins[j]->pin_node_idx;
pin = input_node->input_pins[pin_idx];
add_fanout_pin_to_net(net, pin);
}
else
{
free_npin(next_node->output_pins[i]->net->fanout_pins[j]);
}
}
}
}
/*---------------------------------------------------------------------------
* (function: free_op_nodes)
*-------------------------------------------------------------------------*/
void free_op_nodes(nnode_t *node)
{
for (int i = 0; i < node->num_output_pins; i++)
{
if (node->output_pins[i]->net != NULL)
{
free_nnet(node->output_pins[i]->net);
}
}
free_nnode(node);
}
/*---------------------------------------------------------------------------
* (function: match_pins)
*-------------------------------------------------------------------------*/
int match_pins(nnode_t *node, nnode_t *next_node)
{
int flag = 0;
int i, j;
long id;
for (i = 0; i < node->num_input_pins; i++)
{
flag = 0;
id = node->input_pins[i]->net->driver_pin->unique_id;
for (j = 0; j < next_node->num_input_pins; j++)
{
if (id == next_node->input_pins[j]->net->driver_pin->unique_id)
{
flag = 1;
break;
}
if (j == next_node->num_input_pins - 1)
{
flag = -1;
break;
}
}
if (flag == -1)
break;
}
return flag;
}
/*---------------------------------------------------------------------------------------------
* connect adder type output pin to a node
*-------------------------------------------------------------------------------------------*/
static void connect_output_pin_to_node(int *width, int current_pin, int output_pin_id, nnode_t *node, nnode_t *current_adder, short subtraction)
{
// output
if(subtraction)
{
remap_pin_to_new_node(node->output_pins[current_pin], current_adder, output_pin_id);
}
else
{
npin_t *node_pin_select = node->output_pins[(node->num_input_port_sizes == 2)? current_pin : (current_pin < width[output_pin_id]-1)? current_pin+1 : 0];
if(node_pin_select->type != NO_ID || (node->num_input_port_sizes == 2))
{
remap_pin_to_new_node(node_pin_select, current_adder, output_pin_id);
}
else
{
current_adder->output_pins[output_pin_id] = allocate_npin();
current_adder->output_pins[output_pin_id]->name = append_string("", "%s~dummy_output~%d", current_adder->name, output_pin_id);
}
}
}
/*---------------------------------------------------------------------------------------------
* make a single half-adder (can do unary subtraction, binary subtraction and addition)
*-------------------------------------------------------------------------------------------*/
static nnode_t *make_adder(operation_list funct, nnode_t *current_adder, nnode_t *previous_carry, int *width, int current_pin, netlist_t *netlist, nnode_t *node, short subtraction, short mark)
{
//make a 2 bit 0r 3 bit sum or carry based on previous carry
nnode_t *new_funct = NULL;
short is_three_port_gate = 0;
if(previous_carry == netlist->gnd_node)
{
if(funct == ADDER_FUNC)
new_funct = make_2port_gate(LOGICAL_XOR, 1, 1, 1, node, mark);
else if(funct == CARRY_FUNC)
new_funct = make_2port_gate(LOGICAL_AND, 1, 1, 1, node, mark);
}
else if(previous_carry == netlist->vcc_node)
{
if(funct == ADDER_FUNC)
new_funct = make_2port_gate(LOGICAL_XNOR, 1, 1, 1, node, mark);
else if(funct == CARRY_FUNC)
new_funct = make_2port_gate(LOGICAL_OR, 1, 1, 1, node, mark);
}
else
{
new_funct = make_3port_gate(funct, 1, 1, 1, 1, node, mark);
connect_nodes(previous_carry, 0, new_funct, 0);
is_three_port_gate = 1;
}
//copy the input pin of a half-adder to another function (CARRY or ADDER)
if(current_adder != NULL)
{
add_input_pin_to_node(new_funct, copy_input_npin(current_adder->input_pins[0+is_three_port_gate]), 0+is_three_port_gate);
add_input_pin_to_node(new_funct, copy_input_npin(current_adder->input_pins[1+is_three_port_gate]), 1+is_three_port_gate);
}
// create one from scratch
else
{
//connect input a
if (current_pin < width[1])
{
npin_t *temp_pin = node->input_pins[current_pin];
if(temp_pin->net->driver_pin == NULL || temp_pin->net->driver_pin->node->type == GND_NODE)
{
connect_nodes(netlist->gnd_node,0, new_funct, 0+is_three_port_gate);
remove_fanout_pins_from_net(temp_pin->net, temp_pin, temp_pin->pin_net_idx);
}
else if(temp_pin->net->driver_pin->node->type == VCC_NODE)
{
connect_nodes(netlist->vcc_node,0, new_funct, 0+is_three_port_gate);
remove_fanout_pins_from_net(temp_pin->net, temp_pin, temp_pin->pin_net_idx);
}
else
{
remap_pin_to_new_node(temp_pin, new_funct, 0+is_three_port_gate);
}
}
else
{
connect_nodes(netlist->gnd_node,0, new_funct, 0+is_three_port_gate);
}
//connect input b
if(current_pin < width[2])
{
//pin a is neighbor to pin b
npin_t *temp_pin = node->input_pins[current_pin+width[1]];
if(temp_pin->net->driver_pin == NULL || temp_pin->net->driver_pin->node->type == GND_NODE)
{
nnode_t* attach_to=(subtraction)? netlist->vcc_node: netlist->gnd_node;
connect_nodes(attach_to,0, new_funct, 1+is_three_port_gate);
remove_fanout_pins_from_net(temp_pin->net, temp_pin, temp_pin->pin_net_idx);
}
else if(temp_pin->net->driver_pin->node->type == VCC_NODE)
{
nnode_t* attach_to=(subtraction)? netlist->gnd_node: netlist->vcc_node;
connect_nodes(attach_to,0, new_funct, 1+is_three_port_gate);
remove_fanout_pins_from_net(temp_pin->net, temp_pin, temp_pin->pin_net_idx);
}
else
{
if(subtraction)
{
nnode_t *new_not_cells = make_not_gate(node, mark);
remap_pin_to_new_node(temp_pin, new_not_cells, 0);
connect_nodes(new_not_cells, 0, new_funct, 1+is_three_port_gate);
}
else
{
remap_pin_to_new_node(temp_pin, new_funct, 1+is_three_port_gate);
}
}
}
else
{
nnode_t* attach_to=(subtraction)? netlist->vcc_node: netlist->gnd_node;
connect_nodes(attach_to,0, new_funct, 1+is_three_port_gate);
}
}
return new_funct;
}
void instantiate_add_w_carry_block(int *width, nnode_t *node, short mark, netlist_t *netlist, short subtraction)
{
nnode_t *previous_carry = (subtraction)? netlist->vcc_node: netlist->gnd_node;
for(int i = 0; i < width[0]; i++)
{
/* set of flags for building purposes */
short construct_last_carry_flag = (i != width[0]-1 || !subtraction)? 1:0;
//build Ripple Carry Adder
nnode_t *current_adder = make_adder(ADDER_FUNC, NULL, previous_carry, width, i, netlist, node, subtraction, mark);
if(construct_last_carry_flag)
previous_carry = make_adder(CARRY_FUNC, current_adder, previous_carry, width, i, netlist, node, subtraction, mark);
connect_output_pin_to_node(width, i, 0, node, current_adder, subtraction);
}
}
bool is_ast_adder(ast_node_t *node)
{
bool is_adder;
ast_node_t *instance = node->children[1];
is_adder = (!strcmp(node->children[0]->types.identifier, "adder"))
&& (instance->children[1]->num_children == 5);
ast_node_t *connect_list = instance->children[1];
if (is_adder && connect_list->children[0]->children[0])
{
/* port connections were passed by name; verify port names */
for (int i = 0; i < connect_list->num_children && is_adder; i++)
{
char *id = connect_list->children[i]->children[0]->types.identifier;
if ((strcmp(id, "a") != 0) &&
(strcmp(id, "b") != 0) &&
(strcmp(id, "cin") != 0) &&
(strcmp(id, "cout") != 0) &&
(strcmp(id, "sumout") != 0)
)
{
is_adder = false;
break;
}
}
}
return is_adder;
}