blob: 7b64ace9a4a5be1f47744cec311dfbc1d5954936 [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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "odin_types.h"
#include "odin_globals.h"
#include "netlist_utils.h"
#include "odin_util.h"
#include "output_blif.h"
#include "node_creation_library.h"
#include "multipliers.h"
#include "hard_blocks.h"
#include "adders.h"
#include "subtractions.h"
#include "vtr_util.h"
#include "vtr_memory.h"
bool haveOutputLatchBlackbox = false;
void depth_first_traversal_to_output(short marker_value, FILE *fp, netlist_t *netlist);
void depth_traverse_output_blif(nnode_t *node, int traverse_mark_number, FILE *fp);
void output_node(nnode_t *node, short traverse_number, FILE *fp);
void define_logical_function(nnode_t *node, FILE *out);
void define_set_input_logical_function(nnode_t *node, const char *bit_output, FILE *out);
void define_ff(nnode_t *node, FILE *out);
void define_decoded_mux(nnode_t *node, FILE *out);
void output_blif_pin_connect(nnode_t *node, FILE *out);
static void print_input_pin(FILE *out, nnode_t *node, long pin_idx)
{
oassert(pin_idx < node->num_input_pins);
nnet_t *net = node->input_pins[pin_idx]->net;
if (!net->driver_pin || !net->driver_pin->node)
{
// Add a warning for an undriven net.
int line_number = node->related_ast_node?node->related_ast_node->line_number:0;
warning_message(NETLIST_ERROR, line_number, -1,
"Net %s driving node %s is itself undriven.",
net->name, node->name);
fprintf(out, " %s", "unconn");
}
else if (global_args.high_level_block.provenance() == argparse::Provenance::SPECIFIED
&& net->driver_pin->node->related_ast_node != NULL)
{
fprintf(out, " %s^^%i-%i",
net->driver_pin->node->name,
net->driver_pin->node->related_ast_node->far_tag,
net->driver_pin->node->related_ast_node->high_number);
}
else
{
if (net->driver_pin->name != NULL &&
((net->driver_pin->node->type == MULTIPLY) ||
(net->driver_pin->node->type == HARD_IP) ||
(net->driver_pin->node->type == MEMORY) ||
(net->driver_pin->node->type == ADD) ||
(net->driver_pin->node->type == MINUS)))
{
fprintf(out, " %s", net->driver_pin->name);
}
else
{
fprintf(out, " %s", net->driver_pin->node->name);
}
}
}
static void print_output_pin(FILE *out, nnode_t *node)
{
/* now print the output */
if (node->related_ast_node != NULL
&& global_args.high_level_block.provenance() == argparse::Provenance::SPECIFIED)
fprintf(out, " %s^^%i-%i",
node->name,
node->related_ast_node->far_tag,
node->related_ast_node->high_number);
else
fprintf(out, " %s", node->name);
}
static void print_input_pin_list(FILE *out, nnode_t *node)
{
for( long i = 0; i < node->num_input_pins; i++)
{
print_input_pin(out, node, i);
}
}
static void print_dot_names_header(FILE *out, nnode_t *node)
{
fprintf(out, ".names");
print_input_pin_list(out, node);
oassert(node->num_output_pins == 1);
print_output_pin(out, node);
fprintf(out, "\n");
}
/*---------------------------------------------------------------------------
* (function: output_blif)
* The function that prints out the details for a blif formatted file
*-------------------------------------------------------------------------*/
void output_blif(const char *file_name, netlist_t *netlist)
{
FILE *out;
/* open the file for output */
if (global_args.high_level_block.provenance() == argparse::Provenance::SPECIFIED )
{
std::string out_file = "";
out_file = out_file + file_name + "_" + global_args.high_level_block.value() + ".blif";
out = fopen(out_file.c_str(), "w+");
}
else
{
out = fopen(file_name, "w+");
}
if (out == NULL)
{
error_message(NETLIST_ERROR, -1, -1, "Could not open output file %s\n", file_name);
}
fprintf(out, ".model %s\n", top_module->children[0]->types.identifier);
/* generate all the signals */
fprintf(out, ".inputs");
for (long i = 0; i < netlist->num_top_input_nodes; i++)
{
nnode_t *top_input_node = netlist->top_input_nodes[i];
print_output_pin(out, top_input_node);
}
fprintf(out, "\n");
fprintf(out, ".outputs");
for (long i = 0; i < netlist->num_top_output_nodes; i++)
{
nnode_t *top_output_node = netlist->top_output_nodes[i];
if (top_output_node->input_pins[0]->net->driver_pin == NULL)
{
warning_message(NETLIST_ERROR,
top_output_node->related_ast_node->line_number,
top_output_node->related_ast_node->file_number,
"This output is undriven (%s) and will be removed\n",
top_output_node->name);
}
else
{
print_output_pin(out, top_output_node);
}
}
fprintf(out, "\n");
/* add gnd, unconn, and vcc */
fprintf(out, "\n.names gnd\n.names unconn\n.names vcc\n1\n");
fprintf(out, "\n");
/* traverse the internals of the flat net-list */
if (strcmp(configuration.output_type.c_str(), "blif") == 0)
{
depth_first_traversal_to_output(OUTPUT_TRAVERSE_VALUE, out, netlist);
}
else
{
error_message(NETLIST_ERROR, 0, -1, "%s", "Invalid output file type.");
}
/* connect all the outputs up to the last gate */
for (long i = 0; i < netlist->num_top_output_nodes; i++)
{
nnode_t *node = netlist->top_output_nodes[i];
fprintf(out, ".names");
print_input_pin(out,node,0);
print_output_pin(out, node);
fprintf(out, "\n");
fprintf(out, "1 1\n\n");
}
/* finish off the top level module */
fprintf(out, ".end\n");
fprintf(out, "\n");
/* Print out any hard block modules */
add_the_blackbox_for_mults(out);
add_the_blackbox_for_adds(out);
output_hard_blocks(out);
fclose(out);
}
/*---------------------------------------------------------------------------
* (function: depth_first_traversal_to_parital_map()
*-------------------------------------------------------------------------------------------*/
void depth_first_traversal_to_output(short marker_value, FILE *fp, netlist_t *netlist)
{
int i;
netlist->gnd_node->name = vtr::strdup("gnd");
netlist->vcc_node->name = vtr::strdup("vcc");
netlist->pad_node->name = vtr::strdup("unconn");
/* now traverse the ground, vcc, and unconn pins */
depth_traverse_output_blif(netlist->gnd_node, marker_value, fp);
depth_traverse_output_blif(netlist->vcc_node, marker_value, fp);
depth_traverse_output_blif(netlist->pad_node, marker_value, fp);
/* start with the primary input list */
for (i = 0; i < netlist->num_top_input_nodes; i++)
{
if (netlist->top_input_nodes[i] != NULL)
{
depth_traverse_output_blif(netlist->top_input_nodes[i], marker_value, fp);
}
}
}
/*--------------------------------------------------------------------------
* (function: depth_first_traverse)
*------------------------------------------------------------------------*/
void depth_traverse_output_blif(nnode_t *node, int traverse_mark_number, FILE *fp)
{
int i, j;
nnode_t *next_node;
nnet_t *next_net;
if (node->traverse_visited == traverse_mark_number)
{
return;
}
else
{
/* ELSE - this is a new node so depth visit it */
/* POST traverse map the node since you might delete */
output_node(node, traverse_mark_number, fp);
/* mark that we have visitied this node now */
node->traverse_visited = traverse_mark_number;
for (i = 0; i < node->num_output_pins; i++)
{
if (node->output_pins[i]->net == NULL)
continue;
next_net = node->output_pins[i]->net;
for (j = 0; j < next_net->num_fanout_pins; j++)
{
if (next_net->fanout_pins[j] == NULL)
continue;
next_node = next_net->fanout_pins[j]->node;
if (next_node == NULL)
continue;
/* recursive call point */
depth_traverse_output_blif(next_node, traverse_mark_number, fp);
}
}
}
}
/*-------------------------------------------------------------------
* (function: output_node)
* Depending on node type, figures out what to print for this node
*------------------------------------------------------------------*/
void output_node(nnode_t *node, short /*traverse_number*/, FILE *fp)
{
switch (node->type)
{
case GT:
define_set_input_logical_function(node, "100 1\n", fp);
oassert(node->num_input_pins == 3);
oassert(node->input_pins[2] != NULL);
break;
case LT:
define_set_input_logical_function(node, "010 1\n", fp); // last input decides if this
oassert(node->num_input_pins == 3);
oassert(node->input_pins[2] != NULL);
break;
case ADDER_FUNC:
define_set_input_logical_function(node, "001 1\n010 1\n100 1\n111 1\n", fp);
break;
case CARRY_FUNC:
define_set_input_logical_function(node, "011 1\n101 1\n110 1\n111 1\n", fp);
break;
case BITWISE_NOT:
define_set_input_logical_function(node, "0 1\n", fp);
break;
case LOGICAL_AND:
case LOGICAL_OR:
case LOGICAL_XOR:
case LOGICAL_XNOR:
case LOGICAL_NAND:
case LOGICAL_NOR:
case LOGICAL_EQUAL:
case NOT_EQUAL:
case LOGICAL_NOT:
define_logical_function(node, fp);
break;
case MUX_2:
define_decoded_mux(node, fp);
break;
case FF_NODE:
define_ff(node, fp);
break;
case MULTIPLY:
oassert(hard_multipliers); /* should be soft logic! */
define_mult_function(node, fp);
break;
//case FULLADDER:
case ADD:
oassert(hard_adders); /* should be soft logic! */
define_add_function(node, fp);
break;
case MINUS:
oassert(hard_adders); /* should be soft logic! */
define_add_function(node, fp);
break;
case MEMORY:
case HARD_IP:
define_hard_block(node, fp);
break;
case INPUT_NODE:
case OUTPUT_NODE:
case PAD_NODE:
case CLOCK_NODE:
case GND_NODE:
case VCC_NODE:
/* some nodes already converted */
break;
case BITWISE_AND:
case BITWISE_NAND:
case BITWISE_NOR:
case BITWISE_XNOR:
case BITWISE_XOR:
case BITWISE_OR:
case BUF_NODE:
case MULTI_PORT_MUX:
case SL:
case SR:
case ASR:
case CASE_EQUAL:
case CASE_NOT_EQUAL:
case DIVIDE:
case MODULO:
case GTE:
case LTE:
default:
/* these nodes should have been converted to softer versions */
error_message(NETLIST_ERROR, 0,-1, "%s", "Output blif: node should have been converted to softer version.");
break;
}
}
/*-------------------------------------------------------------------------
* (function: define_logical_function)
*-----------------------------------------------------------------------*/
void define_logical_function(nnode_t *node, FILE *out)
{
int i, j;
char *temp_string;
print_dot_names_header(out, node);
/* print out the blif definition of this gate */
switch (node->type)
{
case LOGICAL_AND:
{
/* generates: 111111 1 */
for (i = 0; i < node->num_input_pins; i++)
{
fprintf(out, "1");
}
fprintf(out, " 1\n");
break;
}
case LOGICAL_OR:
{
/* generates: 1----- 1\n-1----- 1\n ... */
for (i = 0; i < node->num_input_pins; i++)
{
for (j = 0; j < node->num_input_pins; j++)
{
if (i == j)
fprintf(out, "1");
else
fprintf(out, "-");
}
fprintf(out, " 1\n");
}
break;
}
case LOGICAL_NAND:
{
/* generates: 0----- 1\n-0----- 1\n ... */
for (i = 0; i < node->num_input_pins; i++)
{
for (j = 0; j < node->num_input_pins; j++)
{
if (i == j)
fprintf(out, "0");
else
fprintf(out, "-");
}
fprintf(out, " 1\n");
}
break;
}
case LOGICAL_NOT:
case LOGICAL_NOR:
{
/* generates: 0000000 1 */
for (i = 0; i < node->num_input_pins; i++)
{
fprintf(out, "0");
}
fprintf(out, " 1\n");
break;
}
case LOGICAL_EQUAL:
case LOGICAL_XOR:
{
oassert(node->num_input_pins <= 3);
/* generates: a 1 when odd number of 1s */
for (i = 0; i < my_power(2, node->num_input_pins); i++)
{
if ((i % 8 == 1) || (i % 8 == 2) || (i % 8 == 4) || (i % 8 == 7))
{
temp_string = convert_long_to_bit_string(i, node->num_input_pins);
fprintf(out, "%s", temp_string);
vtr::free(temp_string);
fprintf(out, " 1\n");
}
}
break;
}
case NOT_EQUAL:
case LOGICAL_XNOR:
{
oassert(node->num_input_pins <= 3);
for (i = 0; i < my_power(2, node->num_input_pins); i++)
{
if ((i % 8 == 0) || (i % 8 == 3) || (i % 8 == 5) || (i % 8 == 6))
{
temp_string = convert_long_to_bit_string(i, node->num_input_pins);
fprintf(out, "%s", temp_string);
vtr::free(temp_string);
fprintf(out, " 1\n");
}
}
break;
}
default:
oassert(false);
break;
}
fprintf(out, "\n");
}
/*------------------------------------------------------------------------
* (function: define_set_input_logical_function)
*----------------------------------------------------------------------*/
void define_set_input_logical_function(nnode_t *node, const char *bit_output, FILE *out)
{
oassert(node->num_input_pins >= 1);
print_dot_names_header(out, node);
/* print out the blif definition of this gate */
if (bit_output != NULL)
{
fprintf(out, "%s", bit_output);
}
fprintf(out, "\n");
}
/*-------------------------------------------------------------------------
* (function: define_ff)
*-----------------------------------------------------------------------*/
void define_ff(nnode_t *node, FILE *out)
{
oassert(node->num_output_pins == 1);
oassert(node->num_input_pins == 2);
int initial_value = global_args.sim_initial_value;
if(node->has_initial_value)
initial_value = node->initial_value;
/* By default, latches value are unknown, represented by 3 in a BLIF file
and by -1 internally in ODIN */
// TODO switch to default!! to avoid confusion
if(initial_value == -1)
initial_value = 3;
// grab the edge sensitivity of the flip flop
const char *edge_type_str = edge_type_blif_str(node);
std::string input;
std::string output;
std::string clock_driver;
fprintf(out, ".latch");
/* input */
print_input_pin(out,node, 0);
/* output */
print_output_pin(out,node);
/* sensitivity */
fprintf(out, " %s", edge_type_str);
/* clock */
print_input_pin(out,node, 1);
/* initial value */
fprintf(out, " %d\n\n", initial_value);
}
/*--------------------------------------------------------------------------
* (function: define_decoded_mux)
*------------------------------------------------------------------------*/
void define_decoded_mux(nnode_t *node, FILE *out)
{
oassert(node->input_port_sizes[0] == node->input_port_sizes[1]);
print_dot_names_header(out, node);
/* generates: 1----- 1\n-1----- 1\n ... */
for (long i = 0; i < node->input_port_sizes[0]; i++)
{
for (long j = 0; j < node->num_input_pins; j++)
{
if (i == j)
fprintf(out, "1");
else if (i+node->input_port_sizes[0] == j)
fprintf(out, "1");
else if (i > node->input_port_sizes[0])
fprintf(out, "0");
else
fprintf(out, "-");
}
fprintf(out, " 1\n");
}
fprintf(out, "\n");
}