blob: b334418fe4863683b498485024a5f622cbd71422 [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 "simulate_blif.h"
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <cmath>
#include "vtr_util.h"
#include "vtr_memory.h"
#include "odin_buffer.hpp"
#include "odin_util.h"
#include <string>
#include <sstream>
#include <chrono>
#include <dlfcn.h>
#include <mutex>
#include <unistd.h>
#include <thread>
#define CLOCK_INITIAL_VALUE 1
#define MAX_REPEAT_SIM 128
static inline signed char init_value(nnode_t *node)
{
return (node && node->has_initial_value)? node->initial_value: global_args.sim_initial_value;
}
enum edge_eval_e
{
FALLING,
RISING,
HIGH,
LOW,
UNK
};
inline static edge_eval_e get_edge_type(npin_t *clk, int cycle)
{
if(!clk)
return UNK;
signed char prev = !CLOCK_INITIAL_VALUE;
signed char cur = CLOCK_INITIAL_VALUE;
if(cycle > 0)
{
prev = get_pin_value(clk, cycle-1);
cur = get_pin_value(clk, cycle);
}
return ((prev != cur) && (prev == 0 || cur == 1))? RISING:
((prev != cur) && (prev == 1 || cur == 0))? FALLING:
(cur == 1)? HIGH:
(cur == 0)? LOW:
UNK;
}
inline static bool ff_trigger(edge_type_e type, npin_t* clk, int cycle)
{
edge_eval_e clk_e = get_edge_type(clk, cycle);
// update the flip-flop from the input value of the previous cycle.
return (
(type == FALLING_EDGE_SENSITIVITY && clk_e == FALLING)
|| (type == RISING_EDGE_SENSITIVITY && clk_e == RISING)
|| (type == ACTIVE_HIGH_SENSITIVITY && clk_e == HIGH)
|| (type == ACTIVE_LOW_SENSITIVITY && clk_e == LOW)
|| (type == ASYNCHRONOUS_SENSITIVITY && (clk_e == RISING || clk_e == FALLING))
);
}
inline static signed char get_D(npin_t* D, int cycle)
{
return get_pin_value(D,cycle-1);
}
inline static signed char get_Q(npin_t* Q, int cycle)
{
return get_pin_value(Q,cycle-1);
}
inline static signed char compute_ff(bool trigger, signed char D_val, signed char Q_val, int /*cycle*/)
{
return (trigger) ? D_val: Q_val;
}
inline static signed char compute_ff(bool trigger, npin_t* D, signed char Q_val, int cycle)
{
return compute_ff(trigger, get_D(D, cycle), Q_val, cycle);
}
inline static signed char compute_ff(bool trigger, signed char D_val, npin_t* Q, int cycle)
{
return compute_ff(trigger, D_val, get_Q(Q,cycle), cycle);
}
inline static signed char compute_ff(bool trigger, npin_t* D, npin_t* Q, int cycle)
{
return compute_ff(trigger, get_D(D,cycle), get_Q(Q,cycle), cycle);
}
static inline bool is_clock_node(nnode_t *node)
{
return node && (
(node->type == CLOCK_NODE)
|| (std::string(node->name) == "top^clk") // Strictly for memories.
|| (std::string(node->name) == DEFAULT_CLOCK_NAME)
);
}
static void simulate_cycle(int cycle, stages_t *s);
static stages_t *simulate_first_cycle(netlist_t *netlist, int cycle, lines_t *output_lines);
static stages_t *stage_ordered_nodes(nnode_t **ordered_nodes, int num_ordered_nodes);
static void free_stages(stages_t *s);
static int get_num_covered_nodes(stages_t *s);
static int is_node_ready(nnode_t* node, int cycle);
static int is_node_complete(nnode_t* node, int cycle);
static void write_back_memory_nodes(nnode_t **nodes, int num_nodes);
static bool compute_and_store_value(nnode_t *node, int cycle);
static void compute_memory_node(nnode_t *node, int cycle);
static void compute_hard_ip_node(nnode_t *node, int cycle);
static void compute_multiply_node(nnode_t *node, int cycle);
static void compute_generic_node(nnode_t *node, int cycle);
static void compute_add_node(nnode_t *node, int cycle, int type);
static void compute_unary_sub_node(nnode_t *node, int cycle);
static void update_pin_value(npin_t *pin, signed char value, int cycle);
static int get_pin_cycle(npin_t *pin);
signed char get_line_pin_value(line_t *line, int pin_num, int cycle);
static int line_has_unknown_pin(line_t *line, int cycle);
static void compute_flipflop_node(nnode_t *node, int cycle);
static void compute_mux_2_node(nnode_t *node, int cycle);
static int *multiply_arrays(int *a, int a_length, int *b, int b_length);
static int *add_arrays(int *a, int a_length, int *b, int b_length, int *c, int c_length, int flag);
static int *unary_sub_arrays(int *a, int a_length, int *c, int c_length);
static void compute_single_port_memory(nnode_t *node, int cycle);
static void compute_dual_port_memory(nnode_t *node, int cycle);
static long compute_memory_address(signal_list_t *addr, int cycle);
static void instantiate_memory(nnode_t *node, long data_width, long addr_width);
static char *get_mif_filename(nnode_t *node);
static FILE *preprocess_mif_file(FILE *source);
static void assign_memory_from_mif_file(nnode_t *node, FILE *mif, char *filename, int width, long address_width);
static int parse_mif_radix(char *radix);
static int count_test_vectors(FILE *in);
static int count_empty_test_vectors(FILE *in);
static int is_vector(char *buffer);
static int get_next_vector(FILE *file, char *buffer);
static test_vector *parse_test_vector(char *buffer);
static test_vector *generate_random_test_vector(int cycle, sim_data_t *sim_data);
static int compare_test_vectors(test_vector *v1, test_vector *v2);
static int verify_test_vector_headers(FILE *in, lines_t *l);
static void free_test_vector(test_vector* v);
static line_t *create_line(char *name);
static int verify_lines(lines_t *l);
static void free_lines(lines_t *l);
static int find_portname_in_lines(char* port_name, lines_t *l);
static lines_t *create_lines(netlist_t *netlist, int type);
static void add_test_vector_to_lines(test_vector *v, lines_t *l, int cycle);
static void assign_node_to_line(nnode_t *node, lines_t *l, int type, int single_pin);
static void insert_pin_into_line(npin_t *pin, int pin_number, line_t *line, int type);
static char *generate_vector_header(lines_t *l);
static void write_vector_headers(FILE *file, lines_t *l);
static void write_vector_to_file(lines_t *l, FILE *file, int cycle);
static void write_cycle_to_file(lines_t *l, FILE* file, int cycle);
static void write_vector_to_modelsim_file(lines_t *l, FILE *modelsim_out, int cycle);
static void write_cycle_to_modelsim_file(netlist_t *netlist, lines_t *l, FILE* modelsim_out, int cycle);
static int verify_output_vectors(const char* output_vector_file, int num_test_vectors);
static void add_additional_items_to_lines(nnode_t *node, lines_t *l);
static char *vector_value_to_hex(signed char *value, int length);
static void print_netlist_stats(stages_t *stages, int num_vectors);
static void print_simulation_stats(stages_t *stages, int num_vectors, double total_time, double simulation_time);
static void flag_undriven_input_pins(nnode_t *node);
static void print_ancestry(nnode_t *node, int generations);
static nnode_t *print_update_trace(nnode_t *bottom_node, int cycle);
int number_of_workers = 0;
int num_of_clock = 0;
/*
* Performs simulation.
*/
void simulate_netlist(netlist_t *netlist)
{
printf("Simulation starts \n");
sim_data_t *sim_data = init_simulation(netlist);
printf("\n");
//int progress_bar_position = -1;
//const int progress_bar_length = 50;
double min_coverage =0.0;
if(global_args.sim_min_coverage)
{
min_coverage = global_args.sim_min_coverage/100;
}
else if(global_args.sim_achieve_best)
{
min_coverage = 0.0001;
}
simulate_steps(sim_data,min_coverage);
fflush(sim_data->out);
fprintf(sim_data->modelsim_out, "run %ld\n", sim_data->num_vectors*100);
printf("\n");
// If a second output vector file was given via the -T option, verify that it matches.
if (global_args.sim_vector_output_file.provenance() == argparse::Provenance::SPECIFIED)
{
if ( verify_output_vectors( global_args.sim_vector_output_file.value().c_str(), sim_data->num_vectors) )
printf("Vector file \"%s\" matches output\n", global_args.sim_vector_output_file.value().c_str());
else
error_message(SIMULATION_ERROR, 0, -1, "%s\n", "Vector files differ.");
printf("\n");
}
// Print statistics.
print_simulation_stats(sim_data->stages, sim_data->num_vectors, sim_data->total_time, sim_data->simulation_time);
sim_data = terminate_simulation(sim_data);
}
/**
* Initialize simulation
*/
sim_data_t *init_simulation(netlist_t *netlist)
{
number_of_workers = global_args.parralelized_simulation.value();
if(number_of_workers >1 )
warning_message(SIMULATION_ERROR,-1,-1,"Executing simulation with maximum of %d threads", number_of_workers);
num_of_clock = 0;
sim_data_t *sim_data = (sim_data_t *)vtr::malloc(sizeof(sim_data_t));
sim_data->netlist = netlist;
printf("Beginning simulation. Output_files located @: %s\n", global_args.sim_directory.value().c_str());
fflush(stdout);
// Open the output vector file.
char out_vec_file[BUFFER_MAX_SIZE] = { 0 };
odin_sprintf(out_vec_file,"%s/%s", global_args.sim_directory.value().c_str(),OUTPUT_VECTOR_FILE_NAME);
sim_data->out = fopen(out_vec_file, "w");
if (!sim_data->out)
error_message(SIMULATION_ERROR, 0, -1, "%s\n", "Could not create output vector file.");
// Open the input vector file.
char in_vec_file[BUFFER_MAX_SIZE] = { 0 };
odin_sprintf(in_vec_file,"%s/%s", global_args.sim_directory.value().c_str(),INPUT_VECTOR_FILE_NAME);
sim_data->in_out = fopen(in_vec_file, "w+");
if (!sim_data->in_out)
error_message(SIMULATION_ERROR, 0, -1, "%s\n", "Could not create input vector file.");
// Open the activity output file.
char act_file[BUFFER_MAX_SIZE] = { 0 };
odin_sprintf(act_file,"%s/%s", global_args.sim_directory.value().c_str(),OUTPUT_ACTIVITY_FILE_NAME);
sim_data->act_out = fopen(act_file, "w");
if (!sim_data->act_out)
error_message(SIMULATION_ERROR, 0, -1, "%s\n", "Could not create activity output file.");
// Open the modelsim vector file.
char test_file[BUFFER_MAX_SIZE] = { 0 };
odin_sprintf(test_file,"%s/%s", global_args.sim_directory.value().c_str(),MODEL_SIM_FILE_NAME);
sim_data->modelsim_out = fopen(test_file, "w");
if (!sim_data->modelsim_out)
error_message(SIMULATION_ERROR, 0, -1, "%s\n", "Could not create modelsim output file.");
// Create and verify the lines.
sim_data->input_lines = create_lines(netlist, INPUT);
if (!verify_lines(sim_data->input_lines))
error_message(SIMULATION_ERROR, 0, -1, "%s\n", "Input lines could not be assigned.");
sim_data->output_lines = create_lines(netlist, OUTPUT);
if (!verify_lines(sim_data->output_lines))
error_message(SIMULATION_ERROR, 0, -1, "%s\n", "Output lines could not be assigned.");
sim_data->in = NULL;
// Passed via the -t option. Input vectors can either come from a file
// if we expect no lines on input, then don't read the input file and generate as many input test vector as there are output
// vectors so that simulation can run
if(global_args.sim_vector_input_file.provenance() == argparse::Provenance::SPECIFIED)
{
sim_data->in = fopen(global_args.sim_vector_input_file.value().c_str(), "r");
if (!sim_data->in)
error_message(SIMULATION_ERROR, 0, -1, "Could not open vector input file: %s", global_args.sim_vector_input_file.value().c_str());
}
if (sim_data->in && sim_data->input_lines->count != 0)
{
sim_data->num_vectors = count_test_vectors(sim_data->in);
// Read the vector headers and check to make sure they match the lines.
if (!verify_test_vector_headers(sim_data->in, sim_data->input_lines))
error_message(SIMULATION_ERROR, 0, -1, "Invalid vector header format in %s.", global_args.sim_vector_input_file.value().c_str());
printf("Simulating %ld existing vectors from \"%s\".\n", sim_data->num_vectors, global_args.sim_vector_input_file.value().c_str()); fflush(stdout);
}
// or be randomly generated. Passed via the -g option. it also serve as a fallback when we have an empty input
else
{
if(sim_data->in)
{
sim_data->num_vectors = count_empty_test_vectors(sim_data->in);
}
else
{
sim_data->num_vectors = global_args.sim_num_test_vectors;
}
printf("Simulating %ld new vectors.\n", sim_data->num_vectors);
fflush(stdout);
srand(global_args.sim_random_seed);
}
// Determine which edge(s) we are outputting.
sim_data->total_time = 0; // Includes I/O
sim_data->simulation_time = 0; // Does not include I/O
sim_data->stages = 0;
if (!sim_data->num_vectors)
{
terminate_simulation(sim_data);
error_message(SIMULATION_ERROR, 0, -1, "%s", "No vectors to simulate.");
}
return sim_data;
}
sim_data_t *terminate_simulation(sim_data_t *sim_data)
{
free_stages(sim_data->stages);
fclose(sim_data->act_out);
free_lines(sim_data->output_lines);
free_lines(sim_data->input_lines);
fclose(sim_data->modelsim_out);
fclose(sim_data->in_out);
if (sim_data->in)
fclose(sim_data->in);
fclose(sim_data->out);
vtr::free(sim_data);
sim_data = NULL;
return sim_data;
}
void simulate_steps(sim_data_t *sim_data,double min_coverage)
{
printf("\n");
int progress_bar_position = -1;
const int progress_bar_length = 50;
int increment_vector_by = global_args.sim_num_test_vectors;
double current_coverage =0.0;
int cycle=0;
while(cycle < sim_data->num_vectors)
{
double wave_start_time = wall_time();
// if we target a minimum coverage keep generating
if(min_coverage > 0.0)
{
if(cycle+1 == sim_data->num_vectors)
{
current_coverage = (((double) get_num_covered_nodes(sim_data->stages) / (double) sim_data->stages->num_nodes));
if(global_args.sim_achieve_best)
{
if(current_coverage > min_coverage)
{
increment_vector_by = global_args.sim_num_test_vectors;
min_coverage = current_coverage;
sim_data->num_vectors += increment_vector_by;
}
else if(increment_vector_by)
{
//slowly reduce the search until there is no more possible increment, this prevent building too large of a comparative vector pair
sim_data->num_vectors += increment_vector_by;
increment_vector_by /= 2;
}
}
else
{
if(current_coverage < min_coverage)
sim_data->num_vectors += increment_vector_by;
}
}
}
else
{
double next_coverage = (double)cycle;
next_coverage /= (double)sim_data->num_vectors;
current_coverage = next_coverage;
}
single_step(sim_data, cycle);
// Print netlist-specific statistics.
if (!cycle)
print_netlist_stats(sim_data->stages, sim_data->num_vectors);
sim_data->total_time += wall_time() - wave_start_time;
// Delay drawing of the progress bar until the second wave to improve the accuracy of the ETA.
if ((sim_data->num_vectors == 1) || cycle)
{
progress_bar_position = print_progress_bar(
(cycle+1)/(double)(sim_data->num_vectors), progress_bar_position, progress_bar_length, sim_data->total_time);
}
cycle++;
}
}
/**
* single step sim
*/
int single_step(sim_data_t *sim_data, int cycle)
{
test_vector *v = NULL;
// Assign vectors to lines, either by reading or generating them.
// Every second cycle gets a new vector.
double simulation_start_time = wall_time();
// Perform simulation
if (sim_data->in)
{
char buffer[BUFFER_MAX_SIZE];
if (!get_next_vector(sim_data->in, buffer))
error_message(SIMULATION_ERROR, 0, -1, "%s\n", "Could not read next vector.");
v = parse_test_vector(buffer);
}
else
{
v = generate_random_test_vector(cycle, sim_data);
}
add_test_vector_to_lines(v, sim_data->input_lines, cycle);
write_cycle_to_file(sim_data->input_lines, sim_data->in_out, cycle);
write_cycle_to_modelsim_file(sim_data->netlist, sim_data->input_lines, sim_data->modelsim_out, cycle);
free_test_vector(v);
if(!cycle)
{
// The first cycle produces the stages, and adds additional
// lines as specified by the -p option.
sim_data->stages = simulate_first_cycle(sim_data->netlist, cycle, sim_data->output_lines);
// Make sure the output lines are still OK after adding custom lines.
if (!verify_lines(sim_data->output_lines))
error_message(SIMULATION_ERROR, 0, -1,"%s\n",
"Problem detected with the output lines after the first cycle.");
}
else
{
simulate_cycle(cycle, sim_data->stages);
}
write_cycle_to_file(sim_data->output_lines, sim_data->out, cycle);
sim_data->simulation_time += wall_time() - simulation_start_time;
return cycle +1;
}
/*
* This simulates a single cycle using the stages generated
* during the first cycle.
*
* simulation computes a small number of cycles sequentially and
* a small number in parallel. The minimum parallel and sequential time is
* taken for each stage, and that stage is computed in parallel for all subsequent
* cycles if speedup is observed.
*/
static void compute_and_store_part(int start, int end, int current_stage, stages_t *s, int cycle)
{
for (int j = start;j >= 0 && j <= end && j < s->counts[current_stage]; j++)
if(s->stages[current_stage][j])
compute_and_store_value(s->stages[current_stage][j], cycle);
}
static void simulate_cycle(int cycle, stages_t *s)
{
double total_run_time = 0;
for(int i = 0; i < s->count; i++)
{
double time = wall_time();
std::vector<std::thread> workers;
int previous_end = 0;
int nodes_per_thread = s->counts[i]/number_of_workers;
int remainder_nodes_per_thread = s->counts[i]%number_of_workers;
for (int id =0; id < number_of_workers; id++)
{
int start = previous_end;
int end = start + nodes_per_thread + ((remainder_nodes_per_thread > 0)? 1: 0);
remainder_nodes_per_thread -= 1;
previous_end = end + 1;
if( (end-start) > 0 )
{
if(id < number_of_workers-1) // if child threads
workers.push_back(std::thread(compute_and_store_part,start,end,i,s,cycle));
else
compute_and_store_part(start,end,i,s,cycle);
}
}
for (auto &worker: workers)
worker.join();
total_run_time += wall_time()-time;
}
}
/*
* Updates all pins which have been flagged as undriven
* to -1 for the given cycle.
*
* Also checks that other pins have been updated
* by cycle 3 and throws an error if they haven't been.
*
* This function is called when each node is updated as a
* safeguard.
*/
static void update_undriven_input_pins(nnode_t *node, int cycle)
{
int i;
for (i = 0; i < node->num_undriven_pins; i++)
update_pin_value(node->undriven_pins[i], init_value(node), cycle);
// By the third cycle everything in the netlist should have been updated.
if (cycle == 3)
{
for (i = 0; i < node->num_input_pins; i++)
{
if (get_pin_cycle( node->input_pins[i]) < cycle-1)
{
// Print the trace.
nnode_t *root = print_update_trace(node, cycle);
// Throw an error.
error_message(SIMULATION_ERROR,0,-1,"Odin has detected that an input pin attached to %s isn't being updated.\n"
"\tPin name: %s\n"
"\tRoot node: %s\n"
"\tSee the trace immediately above this message for details.\n",
node->name, node->input_pins[i]->name, root?root->name:"N/A"
);
}
}
}
}
/*
* Checks to see if the node is ready to be simulated for the given cycle.
*/
static int is_node_ready(nnode_t* node, int cycle)
{
if (!cycle)
{
/*
* Flags any inputs pins which are undriven and have
* not already been flagged.
*/
int i;
for (i = 0; i < node->num_input_pins; i++)
{
npin_t *pin = node->input_pins[i];
if (!pin->net || !pin->net->driver_pin || !pin->net->driver_pin->node)
{
bool already_flagged = false;
int j;
for (j = 0; j < node->num_undriven_pins; j++)
{
if (node->undriven_pins[j] == pin)
already_flagged = true;
}
if (!already_flagged)
{
node->undriven_pins = (npin_t **)vtr::realloc(node->undriven_pins, sizeof(npin_t *) * (node->num_undriven_pins + 1));
node->undriven_pins[node->num_undriven_pins++] = pin;
warning_message(SIMULATION_ERROR,0,-1,"A node (%s) has an undriven input pin.", node->name);
}
}
}
update_undriven_input_pins(node, cycle);
}
if (node->type == FF_NODE)
{
npin_t *D_pin = node->input_pins[0];
npin_t *clock_pin = node->input_pins[1];
// Flip-flops depend on the D input from the previous cycle and the clock from this cycle.
if (get_pin_cycle(D_pin) < cycle-1)
return false;
if (get_pin_cycle(clock_pin) < cycle )
return false;
}
else if (node->type == MEMORY)
{
int i;
for (i = 0; i < node->num_input_pins; i++)
{
npin_t *pin = node->input_pins[i];
// The data and write enable inputs rely on the values from the previous cycle.
if (!strcmp(pin->mapping, "data") || !strcmp(pin->mapping, "data1") || !strcmp(pin->mapping, "data2"))
{
if (get_pin_cycle(pin) < cycle-1)
return false;
}
else
{
if (get_pin_cycle(pin) < cycle)
return false;
}
}
}
else
{
int i;
for (i = 0; i < node->num_input_pins; i++)
if (get_pin_cycle(node->input_pins[i]) < cycle)
return false;
}
return true;
}
/*
* Simulates the first cycle by traversing the netlist and returns
* the nodes organised into parallelizable stages. Also adds lines to
* custom pins and nodes as requested via the -p option.
*/
static stages_t *simulate_first_cycle(netlist_t *netlist, int cycle, lines_t *l)
{
std::queue<nnode_t *> queue = std::queue<nnode_t *>();
// Enqueue top input nodes
int i;
for (i = 0; i < netlist->num_top_input_nodes; i++)
{
if(is_node_ready(netlist->top_input_nodes[i], cycle))
{
netlist->top_input_nodes[i]->in_queue = true;
queue.push(netlist->top_input_nodes[i]);
}
}
// Enqueue constant nodes.
nnode_t *constant_nodes[] = {netlist->gnd_node, netlist->vcc_node, netlist->pad_node};
int num_constant_nodes = 3;
for (i = 0; i < num_constant_nodes; i++)
{
if(is_node_ready(constant_nodes[i], cycle))
{
constant_nodes[i]->in_queue = true;
queue.push(constant_nodes[i]);
}
}
nnode_t **ordered_nodes = 0;
int num_ordered_nodes = 0;
while (! queue.empty())
{
nnode_t *node = queue.front();
queue.pop();
compute_and_store_value(node, cycle);
// Match node for items passed via -p and add to lines if there's a match.
add_additional_items_to_lines(node, l);
// Enqueue child nodes which are ready, not already queued, and not already complete.
int num_children = 0;
nnode_t **children = get_children_of(node, &num_children);
for (i = 0; i < num_children; i++)
{
nnode_t* node2 = children[i];
if (!node2->in_queue && is_node_ready(node2, cycle) && !is_node_complete(node2, cycle))
{
node2->in_queue = true;
queue.push(node2);
}
}
vtr::free(children);
node->in_queue = false;
// Add the node to the ordered nodes array.
ordered_nodes = (nnode_t **)vtr::realloc(ordered_nodes, sizeof(nnode_t *) * (num_ordered_nodes + 1));
ordered_nodes[num_ordered_nodes++] = node;
}
// Reorganise the ordered nodes into stages for parallel computation.
stages_t *s = stage_ordered_nodes(ordered_nodes, num_ordered_nodes);
vtr::free(ordered_nodes);
return s;
}
/*
* Puts the ordered nodes in stages, each of which can be computed in parallel.
*/
static stages_t *stage_ordered_nodes(nnode_t **ordered_nodes, int num_ordered_nodes) {
stages_t *s = (stages_t *)vtr::malloc(sizeof(stages_t));
s->stages = (nnode_t ***)vtr::calloc(1,sizeof(nnode_t**));
s->counts = (int *)vtr::calloc(1,sizeof(int));
s->num_children = (int *)vtr::calloc(1,sizeof(int));
s->count = 1;
s->num_connections = 0;
s->num_nodes = num_ordered_nodes;
s->avg_worker_count = 0;
s->worker_const = 1;
s->worker_temp = 0;
s->times =__DBL_MAX__;
// Hash tables index the nodes in the current stage, as well as their children.
std::unordered_set<nnode_t *> stage_children = std::unordered_set<nnode_t *>();
std::unordered_set<nnode_t *> stage_nodes = std::unordered_set<nnode_t *>();
int i;
for (i = 0; i < num_ordered_nodes; i++)
{
nnode_t* node = ordered_nodes[i];
int stage = s->count-1;
// Get the node's children for dependency checks.
int num_children;
nnode_t **children = get_children_of(node, &num_children);
// Determine if the node is a child of any node in the current stage.
bool is_child_of_stage = (stage_children.find(node) != stage_children.end());
// Determine if any node in the current stage is a child of this node.
bool is_stage_child_of = false;
if (!is_child_of_stage)
for (int j = 0; j < num_children; j++)
if ((is_stage_child_of = (stage_nodes.find(node) != stage_nodes.end())))
break;
// Start a new stage if this node is related to any node in the current stage.
if (is_child_of_stage || is_stage_child_of)
{
s->stages = (nnode_t ***)vtr::realloc(s->stages, sizeof(nnode_t**) * (s->count+1));
s->counts = (int *)vtr::realloc(s->counts, sizeof(int) * (s->count+1));
s->num_children = (int *)vtr::realloc(s->num_children, sizeof(int) * (s->count+1));
stage = s->count++;
s->stages[stage] = 0;
s->counts[stage] = 0;
s->num_children[stage] = 0;
stage_children.clear();
stage_nodes.clear();
}
// Add the node to the current stage.
s->stages[stage] = (nnode_t **)vtr::realloc(s->stages[stage],sizeof(nnode_t*) * (s->counts[stage]+1));
s->stages[stage][s->counts[stage]++] = node;
// Index the node.
stage_nodes.insert(node);
//printf("NodeID %ld %s typeof %ld at stage %ld\n",node->unique_id,node->name,node->type,stage);
// Index its children.
for (int j = 0; j < num_children; j++)
{
//printf(" ChildID %ld %s typeof %ld \n ",children[j]->unique_id,children[j]->name,children[j]->type);
stage_children.insert(children[j]);
}
// Record the number of children for computing the degree.
s->num_connections += num_children;
s->num_children[stage] += num_children;
vtr::free(children);
}
stage_children.clear();
stage_nodes.clear();
return s;
}
/*
* Given a node, this function will simulate that node's new outputs,
* and updates those pins.
*/
static bool compute_and_store_value(nnode_t *node, int cycle)
{
//double computation_time = wall_time();
is_node_ready(node,cycle);
operation_list type = is_clock_node(node)?CLOCK_NODE:node->type;
switch(type)
{
case MUX_2:
compute_mux_2_node(node, cycle);
break;
case FF_NODE:
compute_flipflop_node(node, cycle);
break;
case MEMORY:
compute_memory_node(node, cycle);
break;
case MULTIPLY:
compute_multiply_node(node, cycle);
break;
case LOGICAL_AND: // &&
{
verify_i_o_availabilty(node, -1, 1);
bool unknown = false;
bool zero = false;
int i;
for (i = 0; i < node->num_input_pins; i++)
{
signed char pin = get_pin_value(node->input_pins[i], cycle);
if (pin < 0) { unknown = true; }
else if (pin == 0) { zero = true; break; }
}
if (zero) update_pin_value(node->output_pins[0], 0, cycle);
else if (unknown) update_pin_value(node->output_pins[0], -1, cycle);
else update_pin_value(node->output_pins[0], 1, cycle);
break;
}
case LOGICAL_OR:
{ // ||
verify_i_o_availabilty(node, -1, 1);
bool unknown = false;
bool one = false;
int i;
for (i = 0; i < node->num_input_pins; i++)
{
signed char pin = get_pin_value(node->input_pins[i], cycle);
if (pin < 0) { unknown = true; }
else if (pin == 1) { one = true; break; }
}
if (one) update_pin_value(node->output_pins[0], 1, cycle);
else if (unknown) update_pin_value(node->output_pins[0], -1, cycle);
else update_pin_value(node->output_pins[0], 0, cycle);
break;
}
case LOGICAL_NAND:
{ // !&&
verify_i_o_availabilty(node, -1, 1);
bool unknown = false;
bool one = false;
int i;
for (i = 0; i < node->num_input_pins; i++)
{
signed char pin = get_pin_value(node->input_pins[i], cycle);
if (pin < 0) { unknown = true; }
else if (pin == 0) { one = true; break; }
}
if (one) update_pin_value(node->output_pins[0], 1, cycle);
else if (unknown) update_pin_value(node->output_pins[0], -1, cycle);
else update_pin_value(node->output_pins[0], 0, cycle);
break;
}
case LOGICAL_NOT: // !
case LOGICAL_NOR: // !|
{
verify_i_o_availabilty(node, -1, 1);
bool unknown = false;
bool zero = false;
int i;
for (i = 0; i < node->num_input_pins; i++)
{
signed char pin = get_pin_value(node->input_pins[i], cycle);
if (pin < 0) { unknown = true; }
else if (pin == 1) { zero = true; break; }
}
if (zero) update_pin_value(node->output_pins[0], 0, cycle);
else if (unknown) update_pin_value(node->output_pins[0], -1, cycle);
else update_pin_value(node->output_pins[0], 1, cycle);
break;
}
case LT: // < 010 1
{
verify_i_o_availabilty(node, 3, 1);
signed char pin0 = get_pin_value(node->input_pins[0],cycle);
signed char pin1 = get_pin_value(node->input_pins[1],cycle);
signed char pin2 = get_pin_value(node->input_pins[2],cycle);
if (pin0 < 0 || pin1 < 0 || pin2 < 0)
update_pin_value(node->output_pins[0], -1, cycle);
else if (pin0 == 0 && pin1 == 1 && pin2 == 0)
update_pin_value(node->output_pins[0], 1, cycle);
else
update_pin_value(node->output_pins[0], 0, cycle);
break;
}
case GT: // > 100 1
{
verify_i_o_availabilty(node, 3, 1);
signed char pin0 = get_pin_value(node->input_pins[0],cycle);
signed char pin1 = get_pin_value(node->input_pins[1],cycle);
signed char pin2 = get_pin_value(node->input_pins[2],cycle);
if (pin0 < 0 || pin1 < 0 || pin2 < 0)
update_pin_value(node->output_pins[0], -1, cycle);
else if (pin0 == 1 && pin1 == 0 && pin2 == 0)
update_pin_value(node->output_pins[0], 1, cycle);
else
update_pin_value(node->output_pins[0], 0, cycle);
break;
}
case ADDER_FUNC: // 001 1\n010 1\n100 1\n111 1
{
verify_i_o_availabilty(node, 3, 1);
signed char pin0 = get_pin_value(node->input_pins[0],cycle);
signed char pin1 = get_pin_value(node->input_pins[1],cycle);
signed char pin2 = get_pin_value(node->input_pins[2],cycle);
if (pin0 < 0 || pin1 < 0 || pin2 < 0)
update_pin_value(node->output_pins[0], -1, cycle);
else if (
(pin0 == 0 && pin1 == 0 && pin2 == 1)
|| (pin0 == 0 && pin1 == 1 && pin2 == 0)
|| (pin0 == 1 && pin1 == 0 && pin2 == 0)
|| (pin0 == 1 && pin1 == 1 && pin2 == 1)
)
update_pin_value(node->output_pins[0], 1, cycle);
else
update_pin_value(node->output_pins[0], 0, cycle);
break;
}
case CARRY_FUNC: // 011 1\n100 1\n110 1\n111 1
{
verify_i_o_availabilty(node, 3, 1);
signed char pin0 = get_pin_value(node->input_pins[0],cycle);
signed char pin1 = get_pin_value(node->input_pins[1],cycle);
signed char pin2 = get_pin_value(node->input_pins[2],cycle);
if (pin0 < 0 || pin1 < 0 || pin2 < 0)
update_pin_value(node->output_pins[0], -1, cycle);
else if (
(pin0 == 1 && (pin1 == 1 || pin2 == 1))
|| (pin1 == 1 && pin2 == 1)
)
update_pin_value(node->output_pins[0], 1, cycle);
else
update_pin_value(node->output_pins[0], 0, cycle);
break;
}
case NOT_EQUAL: // !=
case LOGICAL_XOR: // ^
{
verify_i_o_availabilty(node, -1, 1);
bool unknown = false;
int ones = 0;
int i;
for (i = 0; i < node->num_input_pins; i++)
{
signed char pin = get_pin_value(node->input_pins[i], cycle);
if (pin < 0) { unknown = true; break; }
else if (pin == 1) { ones++; }
}
if (unknown) update_pin_value(node->output_pins[0], -1, cycle);
else if ((ones % 2) == 1) update_pin_value(node->output_pins[0], 1, cycle);
else update_pin_value(node->output_pins[0], 0, cycle);
break;
}
case LOGICAL_EQUAL: // ==
case LOGICAL_XNOR: // !^
{
verify_i_o_availabilty(node, -1, 1);
bool unknown = false;
int ones = 0;
int i;
for (i = 0; i < node->num_input_pins; i++)
{
signed char pin = get_pin_value(node->input_pins[i], cycle);
if (pin < 0) { unknown = true; break; }
if (pin == 1) { ones++; }
}
if (unknown) update_pin_value(node->output_pins[0], -1, cycle);
else if ((ones % 2) == 1) update_pin_value(node->output_pins[0], 0, cycle);
else update_pin_value(node->output_pins[0], 1, cycle);
break;
}
case BITWISE_NOT:
{
verify_i_o_availabilty(node, 1, 1);
signed char pin = get_pin_value(node->input_pins[0], cycle);
if (pin < 0) update_pin_value(node->output_pins[0], -1, cycle);
else if (pin == 1) update_pin_value(node->output_pins[0], 0, cycle);
else update_pin_value(node->output_pins[0], 1, cycle);
break;
}
case CLOCK_NODE:
{
if(node->num_input_pins == 0) // driven by file or internally
{
verify_i_o_availabilty(node, -1, 1);
/* if the pin is not an input.. find a clock to drive it.*/
int pin_cycle = get_pin_cycle(node->output_pins[0]);
if(pin_cycle != cycle)
{
if(!node->internal_clk_warn)
{
node->internal_clk_warn = true;
warning_message(SIMULATION_ERROR,-1,-1,"clock(%s) is internally driven, verify your circuit", node->name);
}
//toggle according to ratio
signed char prev_value = !CLOCK_INITIAL_VALUE;
if(cycle)
prev_value = get_pin_value(node->output_pins[0], cycle-1);
if(prev_value < 0)
prev_value = !CLOCK_INITIAL_VALUE;
int clk_ratio = get_clock_ratio(node);
if(clk_ratio == 0)
{
error_message(SIMULATION_ERROR,-1,-1,"clock(%s) as a 0 valued ratio", node->name);
}
signed char cur_value = (cycle % clk_ratio) ? prev_value : !prev_value;
update_pin_value(node->output_pins[0], cur_value, cycle);
}
}
else // driven by another node
{
verify_i_o_availabilty(node, 1, 1);
int pin_cycle = get_pin_cycle(node->input_pins[0]);
if(pin_cycle == cycle)
{
update_pin_value(node->output_pins[0], get_pin_value(node->input_pins[0],cycle), cycle);
}
else
{
if(!node->internal_clk_warn)
{
node->internal_clk_warn = true;
warning_message(SIMULATION_ERROR,-1,-1,"node used as clock (%s) is itself driven by a clock, verify your circuit", node->name);
}
update_pin_value(node->output_pins[0], get_pin_value(node->input_pins[0],cycle-1), cycle);
}
}
break;
}
case GND_NODE:
verify_i_o_availabilty(node, -1, 1);
update_pin_value(node->output_pins[0], 0, cycle);
break;
case VCC_NODE:
verify_i_o_availabilty(node, -1, 1);
update_pin_value(node->output_pins[0], 1, cycle);
break;
case PAD_NODE:
verify_i_o_availabilty(node, -1, 1);
update_pin_value(node->output_pins[0], 0, cycle);
break;
case INPUT_NODE:
break;
case OUTPUT_NODE:
verify_i_o_availabilty(node, 1, 1);
update_pin_value(node->output_pins[0], get_pin_value(node->input_pins[0],cycle), cycle);
break;
case HARD_IP:
compute_hard_ip_node(node,cycle);
break;
case GENERIC :
compute_generic_node(node,cycle);
break;
//case FULLADDER:
case ADD:
compute_add_node(node, cycle, 0);
break;
case MINUS:
if(node->num_input_port_sizes == 3)
compute_add_node(node, cycle, 1);
else
compute_unary_sub_node(node, cycle);
break;
/* These should have already been converted to softer versions. */
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:
//case ADD:
//case MINUS:
default:
error_message(SIMULATION_ERROR, 0, -1, "Node should have been converted to softer version: %s", node->name);
break;
}
// Count number of ones and toggles for coverage estimation
bool covered = true;
bool skip_node_from_coverage = (
type == INPUT_NODE ||
type == CLOCK_NODE ||
type == GND_NODE ||
type == VCC_NODE ||
type == PAD_NODE
);
if(!skip_node_from_coverage)
{
for (int i = 0; i < node->num_output_pins && covered; i++)
{
signed char pin_value = get_pin_value(node->output_pins[i],cycle);
signed char last_pin_value = get_pin_value(node->output_pins[i],cycle-1);
// # of toggles
if ( ( pin_value != last_pin_value ) && (last_pin_value != -1 ) )
{
node->output_pins[i]->coverage++;
if(node->output_pins[i]->coverage < 2)
covered = false;
}
}
}
node->covered = (covered || skip_node_from_coverage);
//computation_time = wall_time() - computation_time;
//printf("Node %s typeof %ld spent %lf\n",node->name,type,computation_time);
return true;
}
/*
* Gets the number of nodes whose output pins have been sufficiently covered.
*/
static int get_num_covered_nodes(stages_t *s)
{
int covered_nodes = 0;
for(int i = 0; i < s->count; i++)
for (int j = 0; j < s->counts[i]; j++)
covered_nodes += (s->stages[i][j]->covered)? 1: 0;
return covered_nodes;
}
/*
* Determines if the given node has been simulated for the given cycle.
*/
static int is_node_complete(nnode_t* node, int cycle)
{
int i;
for (i = 0; i < node->num_output_pins; i++)
if (node->output_pins[i] && (get_pin_cycle(node->output_pins[i]) < cycle))
return false;
return true;
}
/*
* Changes the ratio of a clock node
*/
void set_clock_ratio(int rat, nnode_t *node)
{
//change the value only for clocks
if(!is_clock_node(node))
return;
node->ratio = rat;
}
/*
* get the ratio of a clock node
*/
int get_clock_ratio(nnode_t *node)
{
//change the value only for clocks
if(!is_clock_node(node))
return 0;
return node->ratio;
}
/*
* Gets the children of the given node. Returns the number of
* children via the num_children parameter. Throws warnings
* or errors if invalid connection patterns are detected.
*/
nnode_t **get_children_of(nnode_t *node, int *num_children)
{
nnode_t **children = 0;
int count = 0;
int i;
for (i = 0; i < node->num_output_pins; i++)
{
npin_t *pin = node->output_pins[i];
nnet_t *net = pin->net;
if (net)
{
/*
* Detects a net that may be being driven by two
* or more pins or has an incorrect driver pin assignment.
*/
if (net->driver_pin != pin && global_args.all_warnings)
{
char *pin_name = get_pin_name(pin->name);
char *node_name = get_pin_name(node->name);
char *net_name = get_pin_name(net->name);
warning_message(SIMULATION_ERROR, -1, -1,
"Found output pin \"%s\" (%ld) on node \"%s\" (%ld)\n"
" which is mapped to a net \"%s\" (%ld) whose driver pin is \"%s\" (%ld) \n",
pin_name,
pin->unique_id,
node_name,
node->unique_id,
net_name,
net->unique_id,
net->driver_pin->name,
net->driver_pin->unique_id
);
vtr::free(net_name);
vtr::free(pin_name);
vtr::free(node_name);
}
int j;
for (j = 0; j < net->num_fanout_pins; j++)
{
npin_t *fanout_pin = net->fanout_pins[j];
if (fanout_pin && fanout_pin->type == INPUT && fanout_pin->node)
{
nnode_t *child_node = fanout_pin->node;
// Check linkage for inconsistencies.
if (fanout_pin->net != net)
{
print_ancestry(child_node, 0);
error_message(SIMULATION_ERROR, -1, -1, "Found mismapped node %s", node->name);
}
else if (fanout_pin->net->driver_pin->net != net)
{
print_ancestry(child_node, 0);
error_message(SIMULATION_ERROR, -1, -1, "Found mismapped node %s", node->name);
}
else if (fanout_pin->net->driver_pin->node != node)
{
print_ancestry(child_node, 0);
error_message(SIMULATION_ERROR, -1, -1, "Found mismapped node %s", node->name);
}
else
{
// Add child.
children = (nnode_t **)vtr::realloc(children, sizeof(nnode_t*) * (count + 1));
children[count++] = child_node;
}
}
}
}
}
*num_children = count;
return children;
}
/*
* Gets the children of a specific output pin of the given node. Returns the number of
* children via the num_children parameter. Throws warnings
* or errors if invalid connection patterns are detected.
*/
nnode_t **get_children_of_nodepin(nnode_t *node, int *num_children, int output_pin)
{
nnode_t **children = 0;
int count = 0;
int output_pin_number = node->num_output_pins;
if(output_pin < 0 || output_pin > output_pin_number)
{
error_message(SIMULATION_ERROR, -1, -1, "%s", "Requested pin not available");
return children;
}
npin_t *pin = node->output_pins[output_pin];
nnet_t *net = pin->net;
if (net)
{
/*
* Detects a net that may be being driven by two
* or more pins or has an incorrect driver pin assignment.
*/
if (net->driver_pin != pin && global_args.all_warnings)
{
char *pin_name = get_pin_name(pin->name);
char *node_name = get_pin_name(node->name);
char *net_name = get_pin_name(net->name);
warning_message(SIMULATION_ERROR, -1, -1,
"Found output pin \"%s\" (%ld) on node \"%s\" (%ld)\n"
" which is mapped to a net \"%s\" (%ld) whose driver pin is \"%s\" (%ld) \n",
pin_name,
pin->unique_id,
node_name,
node->unique_id,
net_name,
net->unique_id,
net->driver_pin->name,
net->driver_pin->unique_id
);
vtr::free(net_name);
vtr::free(pin_name);
vtr::free(node_name);
}
int j;
for (j = 0; j < net->num_fanout_pins; j++)
{
npin_t *fanout_pin = net->fanout_pins[j];
if (fanout_pin && fanout_pin->type == INPUT && fanout_pin->node)
{
nnode_t *child_node = fanout_pin->node;
// Check linkage for inconsistencies.
if (fanout_pin->net != net)
{
print_ancestry(child_node, 0);
error_message(SIMULATION_ERROR, -1, -1, "Found mismapped node %s", node->name);
}
else if (fanout_pin->net->driver_pin->net != net)
{
print_ancestry(child_node, 0);
error_message(SIMULATION_ERROR, -1, -1, "Found mismapped node %s", node->name);
}
else if (fanout_pin->net->driver_pin->node != node)
{
print_ancestry(child_node, 0);
error_message(SIMULATION_ERROR, -1, -1, "Found mismapped node %s", node->name);
}
else
{
// Add child.
children = (nnode_t **)vtr::realloc(children, sizeof(nnode_t*) * (count + 1));
children[count++] = child_node;
}
}
}
}
*num_children = count;
return children;
}
/*
* Allocates memory for the pin's value and cycle.
*
* Checks to see if this pin's net has a different driver, and
* initialises that pin too.
*
* Fanout pins will share the same memory locations for cycle
* and values so that the values don't have to be propagated
* through the net.
*/
static void initialize_pin(npin_t *pin)
{
// Initialise the driver pin if this pin is not the driver.
if (pin->net && pin->net->driver_pin && pin->net->driver_pin != pin)
initialize_pin(pin->net->driver_pin);
// If initialising the driver initialised this pin, we're OK to return.
if (pin->values)
return;
if (pin->net)
{
if(!pin->net->values)
{
pin->net->values = std::make_shared<AtomicBuffer>(init_value(pin->node));
}
pin->values = pin->net->values;
for (int i = 0; i < pin->net->num_fanout_pins; i++)
if (pin->net->fanout_pins[i])
pin->net->fanout_pins[i]->values = pin->net->values;
}
else
{
pin->values = std::make_shared<AtomicBuffer>(init_value(pin->node));
}
}
/*
* Updates the value of a pin and its cycle. Pins should be updated using
* only this function.
*
* Initializes the pin if need be.
*/
static void update_pin_value(npin_t *pin, signed char value, int cycle)
{
if (pin->values == NULL)
initialize_pin(pin);
if(value != 0 && value != 1)
pin->values->update_value(2, cycle);
else
pin->values->update_value(value, cycle);
}
/*
* Gets the value of a pin. Pins should be checked using this function only.
*/
signed char get_pin_value(npin_t *pin, int cycle)
{
if (pin->values == NULL)
{
initialize_pin(pin);
}
signed char value = pin->values->get_value(cycle);
if(value != 0 && value != 1)
value = -1;
return value;
}
/*
* Gets the cycle of the given pin
*/
static int get_pin_cycle(npin_t *pin)
{
if (pin->values == NULL)
initialize_pin(pin);
return pin->values->get_cycle();
}
/*
* Computes a node of type FF_NODE for the given cycle.
*/
static void compute_flipflop_node(nnode_t *node, int cycle)
{
verify_i_o_availabilty(node, 2, 1);
npin_t *D = node->input_pins[0];
npin_t *Q = node->output_pins[0];
npin_t *clock_pin = node->input_pins[1];
npin_t *output_pin = node->output_pins[0];
bool trigger = ff_trigger(node->edge_type, clock_pin, cycle);
signed char new_value = compute_ff(trigger, D, Q, cycle);
update_pin_value(output_pin, new_value, cycle);
}
/*
* Computes a node of type MUX_2 for the given cycle.
*/
static void compute_mux_2_node(nnode_t *node, int cycle)
{
verify_i_o_availabilty(node, -1, 1);
oassert(node->num_input_port_sizes >= 2);
oassert(node->input_port_sizes[0] == node->input_port_sizes[1]);
ast_node_t *ast_node = node->related_ast_node;
// Figure out which pin is being selected.
bool unknown = false;
int select = -1;
int default_select = -1;
int i;
for (i = 0; i < node->input_port_sizes[0]; i++)
{
npin_t *pin = node->input_pins[i];
signed char value = get_pin_value(pin, cycle);
if (value < 0)
unknown = true;
else if (value == 1 && select == -1) // Take the first selection only.
select = i;
/*
* If the pin comes from an "else" condition or a case "default" condition,
* we favour it in the case where there are unknowns.
*/
if (ast_node && pin->is_default && (ast_node->type == IF || ast_node->type == CASE))
default_select = i;
}
// If there are unknowns and there is a default clause, select it.
if (unknown && default_select >= 0)
{
unknown = false;
select = default_select;
}
npin_t *output_pin = node->output_pins[0];
// If any select pin is unknown (and we don't have a default), we take the value from the previous cycle.
if (unknown)
{
/*
* Conform to ModelSim's behaviour where in-line ifs are concerned. If the
* condition is unknown, the inline if's output is unknown.
*/
if (ast_node && ast_node->type == IF_Q)
update_pin_value(output_pin, -1, cycle);
else
update_pin_value(output_pin, get_pin_value(output_pin, cycle-1), cycle);
}
// If no selection is made (all 0) we output x.
else if (select < 0)
{
update_pin_value(output_pin, -1, cycle);
}
else
{
npin_t *pin = node->input_pins[select + node->input_port_sizes[0]];
signed char value = get_pin_value(pin,cycle);
// Drive implied drivers to unknown value.
/*if (pin->is_implied && ast_node && (ast_node->type == CASE))
update_pin_value(output_pin, -1, cycle);
else*/
update_pin_value(output_pin, value, cycle);
}
}
// TODO: Needs to be verified.
static void compute_hard_ip_node(nnode_t *node, int cycle)
{
oassert(node->input_port_sizes[0] > 0);
oassert(node->output_port_sizes[0] > 0);
#ifndef _WIN32
int *input_pins = (int *)vtr::malloc(sizeof(int)*node->num_input_pins);
int *output_pins = (int *)vtr::malloc(sizeof(int)*node->num_output_pins);
if (!node->simulate_block_cycle)
{
char *filename = (char *)vtr::malloc(sizeof(char)*strlen(node->name));
if (!strchr(node->name, '.'))
error_message(SIMULATION_ERROR, 0, -1, "%s\n",
"Couldn't extract the name of a shared library for hard-block simulation");
snprintf(filename, sizeof(char)*strlen(node->name), "%s.so", strchr(node->name, '.')+1);
void *handle = dlopen(filename, RTLD_LAZY);
if (!handle)
error_message(SIMULATION_ERROR, 0, -1,
"Couldn't open a shared library for hard-block simulation: %s", dlerror());
dlerror();
void (*func_pointer)(int, int, int*, int, int*) =
(void(*)(int, int, int*, int, int*))dlsym(handle, "simulate_block_cycle");
char *error = dlerror();
if (error)
error_message(SIMULATION_ERROR, 0, -1,
"Couldn't load a shared library method for hard-block simulation: %s", error);
node->simulate_block_cycle = func_pointer;
vtr::free(filename);
dlclose(handle);
vtr::free(error);
}
int i;
for (i = 0; i < node->num_input_pins; i++)
input_pins[i] = get_pin_value(node->input_pins[i],cycle);
(node->simulate_block_cycle)
(cycle, node->num_input_pins, input_pins, node->num_output_pins, output_pins);
for (i = 0; i < node->num_output_pins; i++)
update_pin_value(node->output_pins[i], output_pins[i], cycle);
vtr::free(input_pins);
vtr::free(output_pins);
#else
//Not supported
oassert(false);
#endif
}
/*
* Computes the given multiply node for the given cycle.
*/
static void compute_multiply_node(nnode_t *node, int cycle)
{
oassert(node->num_input_port_sizes == 2);
oassert(node->num_output_port_sizes == 1);
int i;
bool unknown = false;
for (i = 0; i < node->input_port_sizes[0] + node->input_port_sizes[1]; i++)
{
signed char pin = get_pin_value(node->input_pins[i],cycle);
if (pin < 0)
{
unknown = true;
break;
}
}
if (unknown)
{
for (i = 0; i < node->num_output_pins; i++)
update_pin_value(node->output_pins[i], -1, cycle);
}
else
{
int *a = (int *)vtr::malloc(sizeof(int)*node->input_port_sizes[0]);
int *b = (int *)vtr::malloc(sizeof(int)*node->input_port_sizes[1]);
for (i = 0; i < node->input_port_sizes[0]; i++)
a[i] = get_pin_value(node->input_pins[i],cycle);
for (i = 0; i < node->input_port_sizes[1]; i++)
b[i] = get_pin_value(node->input_pins[node->input_port_sizes[0] + i],cycle);
int *result = multiply_arrays(a, node->input_port_sizes[0], b, node->input_port_sizes[1]);
for (i = 0; i < node->num_output_pins; i++)
update_pin_value(node->output_pins[i], result[i], cycle);
vtr::free(result);
vtr::free(a);
vtr::free(b);
}
}
// TODO: Needs to be verified.
static void compute_generic_node(nnode_t *node, int cycle)
{
int line_count_bitmap = node->bit_map_line_count;
char **bit_map = node->bit_map;
int lut_size = 0;
while (bit_map[0][lut_size] != 0)
lut_size++;
int found = 0;
int i;
for (i = 0; i < line_count_bitmap && (!found); i++)
{
int j;
for (j = 0; j < lut_size; j++)
{
if (get_pin_value(node->input_pins[j],cycle) < 0)
{
update_pin_value(node->output_pins[0], -1, cycle);
return;
}
if ((bit_map[i][j] != '-') && (bit_map[i][j]-'0' != get_pin_value(node->input_pins[j],cycle)))
break;
}
if (j == lut_size) found = true;
}
if (node->generic_output == 1){
if (found) update_pin_value(node->output_pins[0], 1, cycle);
else update_pin_value(node->output_pins[0], 0, cycle);
} else {
if (found) update_pin_value(node->output_pins[0], 0, cycle);
else update_pin_value(node->output_pins[0], 1, cycle);
}
}
/*
* Takes two arrays of integers (1's and 0's) and returns an array
* of integers (1's and 0's) that represent their product. The
* length of the returned array is twice that of the two parameters.
*
* This array will need to be freed later!
*/
static int *multiply_arrays(int *a, int a_length, int *b, int b_length)
{
int result_size = a_length + b_length;
int *result = (int *)vtr::calloc(sizeof(int), result_size);
int i;
for (i = 0; i < a_length; i++)
{
if (a[i] == 1)
{
int j;
for (j = 0; j < b_length; j++)
result[i+j] += b[j];
}
}
for (i = 0; i < result_size; i++)
{
while (result[i] > 1)
{
result[i] -= 2;
result[i+1]++;
}
}
return result;
}
/*
* Computes the given add node for the given cycle.
* add by Sen
*/
static void compute_add_node(nnode_t *node, int cycle, int type)
{
oassert(node->num_input_port_sizes == 3);
oassert(node->num_output_port_sizes == 2);
int i, num;
int flag = 0;
int *a = (int *)vtr::malloc(sizeof(int)*node->input_port_sizes[0]);
int *b = (int *)vtr::malloc(sizeof(int)*node->input_port_sizes[1]);
int *c = (int *)vtr::malloc(sizeof(int)*node->input_port_sizes[2]);
num = node->input_port_sizes[0]+ node->input_port_sizes[1];
//if cin connect to unconn(PAD_NODE), a[0] connect to ground(GND_NODE) and b[0] connect to ground, flag = 0 the initial adder for addition
//if cin connect to unconn(PAD_NODE), a[0] connect to ground(GND_NODE) and b[0] connect to vcc, flag = 1 the initial adder for subtraction
if(node->input_pins[num]->net->driver_pin->node->type == PAD_NODE)
{
if(node->input_pins[0]->net->driver_pin->node->type == GND_NODE && node->input_pins[node->input_port_sizes[0]]->net->driver_pin->node->type == GND_NODE)
flag = 0;
else if(node->input_pins[0]->net->driver_pin->node->type == GND_NODE && node->input_pins[node->input_port_sizes[0]]->net->driver_pin->node->type == VCC_NODE)
flag = 1;
}
else
flag = 2;
for (i = 0; i < node->input_port_sizes[0]; i++)
a[i] = get_pin_value(node->input_pins[i],cycle);
for (i = 0; i < node->input_port_sizes[1]; i++)
b[i] = get_pin_value(node->input_pins[node->input_port_sizes[0] + i],cycle);
for (i = 0; i < node->input_port_sizes[2]; i++)
//the initial cin of carry chain subtractor should be 1
if(flag == 1)
c[i] = 1;
//the initial cin of carry chain adder should be 0
else if(flag == 0)
c[i] = 0;
else
c[i] = get_pin_value(node->input_pins[node->input_port_sizes[0]+ node->input_port_sizes[1] + i],cycle);
int *result = add_arrays(a, node->input_port_sizes[0], b, node->input_port_sizes[1], c, node->input_port_sizes[2],type);
//update the pin value of output
for (i = 1; i < node->num_output_pins; i++)
update_pin_value(node->output_pins[i], result[(i - 1)], cycle);
update_pin_value(node->output_pins[0], result[(node->num_output_pins - 1)], cycle);
vtr::free(result);
vtr::free(a);
vtr::free(b);
vtr::free(c);
}
/*
* Takes two arrays of integers (1's and 0's) and returns an array
* of integers (1's and 0's) that represent their sum. The
* length of the returned array is the maximum of the two parameters plus one.
* add by Sen
* This array will need to be freed later!
*/
static int *add_arrays(int *a, int a_length, int *b, int b_length, int *c, int /*c_length*/, int /*flag*/)
{
int result_size = std::max(a_length , b_length) + 1;
int *result = (int *)vtr::calloc(sizeof(int), result_size);
//least significant bit would use the input carryIn, the other bits would use the compute value
//if one of the number is unknown, then the answer should be unknown(same as ModelSim)
if(a[0] == -1 || b[0] == -1 || c[0] == -1)
{
result[0] = -1;
result[1] = -1;
}
else
{
result[0] = a[0] ^ b[0] ^ c[0];
result[1] = (a[0] & b[0]) | (c[0] & b[0]) | (a[0] & c[0]);
}
int temp_carry_in = result[1];
if(result_size > 2){
for(int i = 1; i < std::min(a_length,b_length); i++)
{
if(a[i] == -1 || b[i] == -1 || temp_carry_in == -1)
{
result[i] = -1;
result[i+1] = -1;
}
else
{
result[i] = a[i] ^ b[i] ^ temp_carry_in;
result[i+1] = (a[i] & b[i]) | (a[i] & temp_carry_in) | (temp_carry_in & b[i]);
}
temp_carry_in = result[i+1];
}
if(a_length >= b_length)
{
for(int i = b_length; i < a_length; i++)
{
if(a[i] == -1 || temp_carry_in == -1)
{
result[i] = -1;
result[i+1] = -1;
}
else
{
result[i] = a[i] ^ temp_carry_in;
result[i+1] = a[i] & temp_carry_in;
}
temp_carry_in = result[i+1];
}
}
else
{
for(int i = a_length; i < b_length; i++)
{
if(b[i] == -1 || temp_carry_in == -1)
{
result[i] = -1;
result[i+1] = -1;
}else
{
result[i] = b[i] ^ temp_carry_in;
result[i+1] = b[i] & temp_carry_in;
}
temp_carry_in = result[i+1];
}
}
}
return result;
}
/*
* Computes the given add node for the given cycle.
* add by Sen
*/
static void compute_unary_sub_node(nnode_t *node, int cycle)
{
oassert(node->num_input_port_sizes == 2);
oassert(node->num_output_port_sizes == 2);
int i;
bool unknown = false;
for (i = 0; i < (node->input_port_sizes[0] + node->input_port_sizes[1]); i++)
{
signed char pin = get_pin_value(node->input_pins[i],cycle);
if (pin < 0)
{
unknown = true;
break;
}
}
if (unknown)
{
for (i = 0; i < (node->output_port_sizes[0] + node->output_port_sizes[1]); i++)
update_pin_value(node->output_pins[i], -1, cycle);
}
else
{
int *a = (int *)vtr::malloc(sizeof(int)*node->input_port_sizes[0]);
int *c = (int *)vtr::malloc(sizeof(int)*node->input_port_sizes[1]);
for (i = 0; i < node->input_port_sizes[0]; i++)
a[i] = get_pin_value(node->input_pins[i],cycle);
for (i = 0; i < node->input_port_sizes[1]; i++)
if(node->input_pins[node->input_port_sizes[0]+ node->input_port_sizes[1] + i]->net->driver_pin->node->type == PAD_NODE)
c[i] = 1;
else
c[i] = get_pin_value(node->input_pins[node->input_port_sizes[0] + i],cycle);
int *result = unary_sub_arrays(a, node->input_port_sizes[0], c, node->input_port_sizes[1]);
for (i = 1; i < node->num_output_pins; i++)
update_pin_value(node->output_pins[i], result[(i - 1)], cycle);
update_pin_value(node->output_pins[0], result[(node->num_output_pins - 1)], cycle);
vtr::free(result);
vtr::free(a);
vtr::free(c);
}
}
/*
* Takes two arrays of integers (1's and 0's) and returns an array
* of integers (1's and 0's) that represent their sum. The
* length of the returned array is the maximum of the two parameters plus one.
* add by Sen
* This array will need to be freed later!
*/
static int *unary_sub_arrays(int *a, int a_length, int *c, int /*c_length*/)
{
int result_size = a_length + 1;
int *result = (int *)vtr::calloc(sizeof(int), result_size);
int i;
int temp_carry_in;
c[0] = 1;
result[0] = (!a[0]) ^ c[0] ^ 0;
result[1] = ((!a[0]) & 0) | (c[0] & 0) | ((!a[0]) & c[0]);
temp_carry_in = result[1];
if(result_size > 2){
for(i = 1; i < a_length; i++)
{
result[i] = (!a[i]) ^ 0 ^ temp_carry_in;
result[i+1] = ((!a[i]) & 0) | ((!a[i]) & temp_carry_in) | (temp_carry_in & 0);
temp_carry_in = result[i+1];
}
}
return result;
}
/*
* Computes the given memory node.
*/
static void compute_memory_node(nnode_t *node, int cycle)
{
if (is_sp_ram(node))
compute_single_port_memory(node, cycle);
else if (is_dp_ram(node))
compute_dual_port_memory(node, cycle);
else
error_message(SIMULATION_ERROR, 0, -1,
"Could not resolve memory hard block %s to a valid type.", node->name);
}
/**
* compute the address
*/
static long compute_address(signal_list_t *input_address, int cycle)
{
long address = 0;
for (long i = 0; i < input_address->count && address >= 0; i++)
{
// If any address pins are x's, write x's we return -1.
signed char value = get_pin_value(input_address->pins[i],cycle);
if (value != 1 && value != 0)
address = -1;
else
address += shift_left_value_with_overflow_check(value, i);
}
return address;
}
static void read_write_to_memory(nnode_t *node , signal_list_t *input_address, signal_list_t *data_out, signal_list_t *data_in, bool trigger, npin_t *write_enabled, int cycle)
{
long address = compute_address(input_address, cycle);
//make a single trigger out of write_enable pin and if it was a positive edge
bool write = (trigger && 1 == get_pin_value(write_enabled, cycle));
bool address_is_valid = (address >= 0 && address < node->memory_data.size());
/* init the vector with all -1 */
std::vector<signed char> new_values(data_out->count, -1);
if(address_is_valid)
{
// init from memory pins first from previous value
new_values = node->memory_data[address];
// if it is a valid write, grap the input pin and store those in a vector
if (write)
{
for (long i = 0; i < data_out->count; i++)
{
new_values[i]= get_pin_value(data_in->pins[i],cycle-1);
}
}
node->memory_data[address] = new_values;
}
/**
* now we update the output pins
*/
for (long i = 0; i < data_out->count; i++)
{
update_pin_value(data_out->pins[i], new_values[i], cycle);
}
}
/*
* Computes single port memory.
*/
static void compute_single_port_memory(nnode_t *node, int cycle)
{
sp_ram_signals *signals = get_sp_ram_signals(node);
bool trigger = ff_trigger(RISING_EDGE_SENSITIVITY, signals->clk, cycle);
if (node->memory_data.empty())
instantiate_memory(node, signals->data->count, signals->addr->count);
read_write_to_memory(node, signals->addr, signals->out, signals->data, trigger, signals->we, cycle);
free_sp_ram_signals(signals);
}
/*
* Computes dual port memory.
*/
static void compute_dual_port_memory(nnode_t *node, int cycle)
{
dp_ram_signals *signals = get_dp_ram_signals(node);
bool trigger = ff_trigger(RISING_EDGE_SENSITIVITY, signals->clk, cycle);
if (node->memory_data.empty())
instantiate_memory(node,
std::max(signals->data1->count, signals->data2->count),
std::max(signals->addr1->count,signals->addr2->count)
);
read_write_to_memory(node, signals->addr1, signals->out1, signals->data1, trigger, signals->we1, cycle);
read_write_to_memory(node, signals->addr2, signals->out2, signals->data2, trigger, signals->we2, cycle);
free_dp_ram_signals(signals);
}
/*
* Initialises memory using a memory information file (mif). If not
* file is found, it is initialised to x's.
*/
static void instantiate_memory(nnode_t *node, long data_width, long addr_width)
{
long max_address = shift_left_value_with_overflow_check(0x1, addr_width);
node->memory_data = std::vector<std::vector<signed char>>(max_address, std::vector<signed char>(data_width, init_value(node)));
if(global_args.read_mif_input)
{
char *filename = get_mif_filename(node);
FILE *mif = fopen(filename, "r");
if (!mif)
{
printf("MIF %s (%ldx%ld) not found. \n", filename, data_width, addr_width);
}
else
{
assign_memory_from_mif_file(node, mif, filename, data_width, addr_width);
fclose(mif);
}
vtr::free(filename);
}
}
static int parse_mif_radix(std::string radix)
{
return (radix == "HEX") ? 16:
(radix == "DEC") ? 10:
(radix == "OCT") ? 8:
(radix == "BIN") ? 2:
0;
}
static void assign_memory_from_mif_file(nnode_t *node, FILE *mif, char *filename, int width, long address_width)
{
rewind(mif);
std::unordered_map<std::string, std::string> symbols = std::unordered_map<std::string, std::string>();
bool in_content = false;
std::string last_line;
int line_number = 0;
int addr_radix = 0;
int data_radix = 0;
char *buffer_in = NULL;
buffered_reader_t reader = buffered_reader_t(mif, "--", "%", "%");
while ((buffer_in = reader.get_line()))
{
line_number++;
std::string buffer = buffer_in;
// Only process lines which are not empty.
if (buffer.size())
{
// MIF files are case insensitive
string_to_upper(buffer_in);
// The content section of the file contains address:value; assignments.
if (in_content)
{
// Parse at the :
char *token = strtok(buffer_in, ":");
if (strlen(token))
{
// END; signifies the end of the file.
if(buffer == "END;")
break;
// The part before the : is the address.
char *address_string = token;
token = strtok(NULL, ";");
// The reset (before the ;) is the data_value.
char *data_string = token;
if (token)
{
// Make sure the address and value are valid strings of the specified radix.
if (!is_string_of_radix(address_string, addr_radix))
error_message(SIMULATION_ERROR, line_number, -1, "%s: address %s is not a base %d string.", filename, address_string, addr_radix);
if (!is_string_of_radix(data_string, data_radix))
error_message(SIMULATION_ERROR, line_number, -1, "%s: data string %s is not a base %d string.", filename, data_string, data_radix);
char *binary_data = convert_string_of_radix_to_bit_string(data_string, data_radix, width);
long address = convert_string_of_radix_to_long(address_string, addr_radix);
if (address > address_width)
error_message(SIMULATION_ERROR, line_number, -1, "%s: address %s is out of range.", filename, address_string);
// Write the parsed value string to the memory location.
for(int i=0; i<width; i++)
node->memory_data[address][i] = binary_data[i] - '0';
}
else
{
error_message(SIMULATION_ERROR, line_number, -1,
"%s: MIF syntax error.", filename);
}
}
}
// The header section of the file contains parameters given as PARAMETER=value;
else
{
// Grab the bit before the = sign.
char *token = strtok(buffer_in, "=");
if (strlen(token))
{
char *symbol = token;
token = strtok(NULL, ";");
// If is something after the equals sign and before the semicolon, add the symbol=value association to the symbol table.
if (token)
symbols.insert({symbol, token});
else if(buffer == "CONTENT") {}
// We found "CONTENT" followed on the next line by "BEGIN". That means we're at the end of the parameters.
else if(buffer == "BEGIN" && last_line == "CONTENT")
{
// Sanity check parameters to make sure we have what we need.
std::unordered_map<std::string,std::string>::const_iterator item_in;
// Verify the width parameter.
item_in = symbols.find("WIDTH");
if ( item_in == symbols.end() )
error_message(SIMULATION_ERROR, -1, -1, "%s: MIF WIDTH parameter unspecified.", filename);
long mif_width = std::strtol(item_in->second.c_str(),NULL,10);
if (mif_width != width)
error_message(SIMULATION_ERROR, -1, -1, "%s: MIF width mismatch: must be %d but %ld was given", filename, width, mif_width);
// Verify the depth parameter.
item_in = symbols.find("DEPTH");
if ( item_in == symbols.end() )
error_message(SIMULATION_ERROR, -1, -1, "%s: MIF DEPTH parameter unspecified.", filename);
long mif_depth = std::strtol(item_in->second.c_str(),NULL,10);
long expected_depth = shift_left_value_with_overflow_check(0x1, address_width);
if (mif_depth != expected_depth)
error_message(SIMULATION_ERROR, -1, -1,
"%s: MIF depth mismatch: must be %ld but %ld was given", filename, expected_depth, mif_depth);
// Parse the radix specifications and make sure they're OK.
item_in = symbols.find("ADDRESS_RADIX");
if ( item_in == symbols.end() )
error_message(SIMULATION_ERROR, -1, -1, "%s: ADDRESS_RADIX parameter unspecified.", filename);
addr_radix = parse_mif_radix(item_in->second);
if (!addr_radix)
error_message(SIMULATION_ERROR, -1, -1,
"%s: invalid or missing ADDRESS_RADIX: must specify DEC, HEX, OCT, or BIN", filename);
item_in = symbols.find("DATA_RADIX");
if ( item_in == symbols.end() )
error_message(SIMULATION_ERROR, -1, -1, "%s: DATA_RADIX parameter unspecified.", filename);
data_radix = parse_mif_radix(item_in->second);
if (!data_radix)
error_message(SIMULATION_ERROR, -1, -1,
"%s: invalid or missing DATA_RADIX: must specify DEC, HEX, OCT, or BIN", filename);
// If everything checks out, start reading the values.
in_content = true;
}
else
{
error_message(SIMULATION_ERROR, line_number, -1, "%s: MIF syntax error: %s", filename, buffer_in);
}
}
else
{
error_message(SIMULATION_ERROR, line_number, -1, "%s: MIF syntax error: %s", filename, buffer_in);
}
}
last_line = buffer;
}
vtr::free(buffer_in);
}
}
/*
* Assigns the given node to its corresponding line in the given array of line.
* Assumes the line has already been created.
*/
static void assign_node_to_line(nnode_t *node, lines_t *l, int type, int single_pin)
{
// Make sure the node has an output pin.
if (!node->num_output_pins)
{
npin_t *pin = allocate_npin();
allocate_more_output_pins(node, 1);
add_output_pin_to_node(node, pin, 0);
}
// Parse the node name into a pin number and a port name.
int pin_number = get_pin_number(node->name);
char *port_name;
if (pin_number != -1 && !single_pin) {
port_name = get_port_name(node->name);
}
else {
port_name = get_pin_name(node->name);
single_pin = true;
}
// Search the lines for the port name.
int j = find_portname_in_lines(port_name, l);
vtr::free(port_name);
if (single_pin)
{
if (j == -1)
{
warning_message(SIMULATION_ERROR, 0, -1,
"Could not map single-bit node '%s' line", node->name);
}
else
{
pin_number = (pin_number == -1)?0:pin_number;
int already_added = l->lines[j]->number_of_pins >= 1;
if (!already_added)
insert_pin_into_line(node->output_pins[0], pin_number, l->lines[j], type);
}
}
else
{
if (j == -1)
warning_message(SIMULATION_ERROR, 0, -1,
"Could not map multi-bit node '%s' to line", node->name);
else
insert_pin_into_line(node->output_pins[0], pin_number, l->lines[j], type);
}
}
/*
* Inserts the given pin according to its pin number into the given line.
*/
static void insert_pin_into_line(npin_t *pin, int pin_number, line_t *line, int type)
{
// Allocate memory for the new pin.
line->pins = (npin_t **)vtr::realloc(line->pins, sizeof(npin_t*) * (line->number_of_pins + 1));
line->pin_numbers = (int *)vtr::realloc(line->pin_numbers, sizeof(npin_t*) * (line->number_of_pins + 1));
// Find the proper place to insert this pin, and make room for it.
int i;
for (i = 0; i < line->number_of_pins; i++)
{
if (line->pin_numbers[i] > pin_number)
{
// Move other pins to the right to make room.
int j;
for (j = line->number_of_pins; j > i; j--)
{
line->pins[j] = line->pins[j-1];
line->pin_numbers[j] = line->pin_numbers[j-1];
}
break;
}
}
// Add the new pin.
line->pins[i] = pin;
line->pin_numbers[i] = pin_number;
line->type = type;
line->number_of_pins++;
}
/*
* Given a netlist, this function maps the top_input_nodes
* or top_output_nodes depending on the value of type
* (INPUT or OUTPUT) to a line_t each. It stores them in a
* lines_t struct and returns a pointer to it.
*/
static lines_t *create_lines(netlist_t *netlist, int type)
{
lines_t *l = (lines_t *)vtr::malloc(sizeof(lines_t));
l->lines = 0;
l->count = 0;
int num_nodes = (type == INPUT)?netlist->num_top_input_nodes:netlist->num_top_output_nodes;
nnode_t **nodes = (type == INPUT)?netlist->top_input_nodes :netlist->top_output_nodes;
int i;
for (i = 0; i < num_nodes; i++)
{
nnode_t *node = nodes[i];
char *port_name = get_port_name(node->name);
if (find_portname_in_lines(port_name, l) == -1)
{
line_t *line = create_line(port_name);
l->lines = (line_t **)vtr::realloc(l->lines, sizeof(line_t *)*(l->count + 1));
l->lines[l->count++] = line;
}
assign_node_to_line(node, l, type, 0);
/**
* TODO: implicit memories with multiclock input (one for read and one for write)
* is broken, need fixing
*/
if(is_clock_node(node))
set_clock_ratio(++num_of_clock,node);
vtr::free(port_name);
}
return l;
}
/*
* Creates a vector file header from the given lines,
* and writes it to the given file.
*/
static void write_vector_headers(FILE *file, lines_t *l)
{
char* headers = generate_vector_header(l);
fprintf(file, "%s", headers);
vtr::free(headers);
fflush(file);
}
/*
* Parses the first line of the given file and compares it to the
* given lines for identity. If there is any difference, a warning is printed,
* and false is returned. If there are no differences, the file pointer is left
* at the start of the second line, and true is returned.
*/
static int verify_test_vector_headers(FILE *in, lines_t *l)
{
int current_line = 0;
int buffer_length = 0;
// Read the header from the vector file.
char read_buffer [BUFFER_MAX_SIZE];
rewind(in);
if (!get_next_vector(in, read_buffer))
error_message(SIMULATION_ERROR, 0, -1, "%s\n", "Failed to read vector headers.");
// Parse the header, checking each entity against the corresponding line.
char buffer [BUFFER_MAX_SIZE];
buffer[0] = '\0';
unsigned int i;
for (i = 0; i < strlen(read_buffer) && i < BUFFER_MAX_SIZE; i++)
{
char next = read_buffer[i];
if (next == EOF)
{
warning_message(SIMULATION_ERROR, 0, -1, "%s", "Hit end of file.");
return false;
}
else if (next == ' ' || next == '\t' || next == '\n')
{
if (buffer_length)
{
if(strcmp(l->lines[current_line]->name,buffer))
{
char *expected_header = generate_vector_header(l);
warning_message(SIMULATION_ERROR, 0, -1,
"Vector header mismatch: \n "
" Found: %s "
" Expected: %s", read_buffer, expected_header);
vtr::free(expected_header);
return false;
}
else
{
buffer_length = 0;
current_line++;
}
}
if (next == '\n')
break;
}
else
{
buffer[buffer_length++] = next;
buffer[buffer_length] = '\0';
}
}
return true;
}
/*
* Verifies that no lines have null pins.
*/
static int verify_lines (lines_t *l)
{
int i;
for (i = 0; i < l->count; i++)
{
int j;
for (j = 0; j < l->lines[i]->number_of_pins; j++)
{
if (!l->lines[i]->pins[j])
{
warning_message(SIMULATION_ERROR, 0, -1, "A line %d:(%s) has a NULL pin. ", j, l->lines[i]->name);
return false;
}
}
}
return true;
}
/*
* Searches for a line with the given name in the lines. Returns the index
* or -1 if no such line was found.
*/
static int find_portname_in_lines(char* port_name, lines_t *l)
{
int j;
for (j = 0; j < l->count; j++)
if (!strcmp(l->lines[j]->name, port_name))
return j;
return -1;
}
/*
* allocates memory for and initialises a line_t struct
*/
static line_t *create_line(char *name)
{
line_t *line = (line_t *)vtr::malloc(sizeof(line_t));
line->number_of_pins = 0;
line->pins = 0;
line->pin_numbers = 0;
line->type = -1;
line->name = (char *)vtr::malloc(sizeof(char)*(strlen(name)+1));
strcpy(line->name, name);
return line;
}
/*
* Generates the appropriate vector headers based on the given lines.
*/
static char *generate_vector_header(lines_t *l)
{
char *header = (char *)vtr::calloc(BUFFER_MAX_SIZE, sizeof(char));
if (l->count)
{
int j;
for (j = 0; j < l->count; j++)
{
// "+ 2" for null and newline/space.
if ((strlen(header) + strlen(l->lines[j]->name) + 2) > BUFFER_MAX_SIZE)
error_message(SIMULATION_ERROR, 0, -1, "%s", "Buffer overflow anticipated while generating vector header.");
strcat(header,l->lines[j]->name);
strcat(header," ");
}
header[strlen(header)-1] = '\n';
}
else
{
header[0] = '\n';
}
header = (char *)vtr::realloc(header, sizeof(char)*(strlen(header)+1));
return header;
}
/*
* Stores the given test vector in the given lines, with some sanity checking to ensure that it
* has a compatible geometry.
*/
static void add_test_vector_to_lines(test_vector *v, lines_t *l, int cycle)
{
if (l->count < v->count)
error_message(SIMULATION_ERROR, 0, -1, "Fewer lines (%d) than values (%d).", l->count, v->count);
if (l->count > v->count)
error_message(SIMULATION_ERROR, 0, -1, "More lines (%d) than values (%d).", l->count, v->count);
int i;
for (i = 0; i < v->count; i++)
{
line_t *line = l->lines[i];
if (line->number_of_pins < 1)
error_message(SIMULATION_ERROR, 0, -1, "Found a line '%s' with no pins.", line->name);
int j;
for (j = 0; j < line->number_of_pins; j++)
{
if (j < v->counts[i]) update_pin_value(line->pins[j], v->values[i][j], cycle);
else update_pin_value(line->pins[j], 0, cycle);
}
}
}
/*
* Compares two test vectors for numerical and geometric identity. Returns false if
* they are found to be different, and true otherwise.
*/
static int compare_test_vectors(test_vector *v1, test_vector *v2)
{
int equivalent = true;
if (v1->count != v2->count)
{
warning_message(SIMULATION_ERROR, 0, -1, "%s", "Vector lengths differ.");
return false;
}
int l;
for (l = 0; l < v1->count; l++)
{ // Compare bit by bit.
int i;
for (i = 0; i < v1->counts[l] && i < v2->counts[l]; i++)
{
if (v1->values[l][i] != v2->values[l][i])
{
if (v1->values[l][i] == -1)
equivalent = -1;
else
return false;
}
}
/*
* If one value has more bits than the other, they are still
* equivalent as long as the higher order bits of the longer
* one are zero.
*/
if (v1->counts[l] != v2->counts[l])
{
test_vector *v = v1->counts[l] < v2->counts[l] ? v2 : v1;
int j;
for (j = i; j < v->counts[l]; j++)
if (v->values[l][j] != 0)
return false;
}
}
return equivalent;
}
/*
* Parses the given line from a test vector file into a
* test_vector data structure.
*/
static test_vector *parse_test_vector(char *buffer)
{
buffer = vtr::strdup(buffer);
test_vector *v = (test_vector *)vtr::malloc(sizeof(test_vector));
v->values = 0;
v->counts = 0;
v->count = 0;
trim_string(buffer,"\r\n");
const char *delim = " \t";
char *token = strtok(buffer, delim);
while (token)
{
v->values = (signed char **)vtr::realloc(v->values, sizeof(signed char *) * (v->count + 1));
v->counts = (int *)vtr::realloc(v->counts, sizeof(int) * (v->count + 1));
v->values[v->count] = 0;
v->counts[v->count] = 0;
if (token[0] == '0' && (token[1] == 'x' || token[1] == 'X'))
{ // Value is hex.
token += 2;
int token_length = strlen(token);
reverse_string(token, token_length);
int i;
for (i = 0; i < token_length; i++)
{
char temp[] = {token[i],'\0'};
int value = strtol(temp, NULL, 16);
int k;
for (k = 0; k < 4; k++)
{
signed char bit = 0;
if (value > 0)
{
bit = value % 2;
value /= 2;
}
v->values[v->count] = (signed char *)vtr::realloc(v->values[v->count], sizeof(signed char) * (v->counts[v->count] + 1));
v->values[v->count][v->counts[v->count]++] = bit;
}
}
}
else
{ // Value is binary.
int i;
for (i = strlen(token) - 1; i >= 0; i--)
{
signed char value = -1;
if (token[i] == '0') value = 0;
else if (token[i] == '1') value = 1;
v->values[v->count] = (signed char *)vtr::realloc(v->values[v->count], sizeof(signed char) * (v->counts[v->count] + 1));
v->values[v->count][v->counts[v->count]++] = value;
}
}
v->count++;
token = strtok(NULL, delim);
}
vtr::free(buffer);
return v;
}
/*
* Generates a "random" test_vector structure based on the geometry of the given lines.
*
* If you want better randomness, call srand at some point.
*/
static bool contains_a_substr_of_name(std::vector<std::string> held, const char *name_in)
{
if(!name_in)
return false;
if(held.empty())
return false;
std::string name = name_in;
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
for(std::string sub_str: held)
{
std::transform(sub_str.begin(), sub_str.end(), sub_str.begin(), ::tolower);
if(name.find(sub_str) != std::string::npos)
return true;
}
return false;
}
static test_vector *generate_random_test_vector(int cycle, sim_data_t *sim_data)
{
/**
* generate test vectors
*/
test_vector *v = (test_vector *)vtr::malloc(sizeof(test_vector));
v->values = 0;
v->counts = 0;
v->count = 0;
for (int i = 0; i < sim_data->input_lines->count; i++)
{
v->values = (signed char **)vtr::realloc(v->values, sizeof(signed char *) * (v->count + 1));
v->counts = (int *)vtr::realloc(v->counts, sizeof(int) * (v->count + 1));
v->values[v->count] = 0;
v->counts[v->count] = 0;
line_t *line = sim_data->input_lines->lines[i];
for (int j = 0; j < line->number_of_pins; j++)
{
//default
signed char value = (rand() % 2);
npin_t *pin = line->pins[j];
signed char clock_ratio = get_clock_ratio(pin->node);
/********************************************************
* if it is a clock node, use it's ratio to generate a cycle
*/
if(clock_ratio > 0)
{
if(!cycle)
value = CLOCK_INITIAL_VALUE;
else
{
signed char previous_cycle_clock_value = get_pin_value(pin, cycle-1);
if((cycle%(clock_ratio)) == 0)
{
if(previous_cycle_clock_value == 0)
value = 1;
else
value = 0;
}
else
value = previous_cycle_clock_value;
}
}
/********************************************************
* use input override to set the pin value to hold high if requested
*/
else if(contains_a_substr_of_name(global_args.sim_hold_high.value(),line->name))
{
if (cycle < (num_of_clock*3)) value = 0; // start with reverse value
else value = 1; // then hold to requested value
}
/********************************************************
* use input override to set the pin value to hold low if requested
*/
else if(contains_a_substr_of_name(global_args.sim_hold_low.value(),line->name))
{
if (cycle < (num_of_clock*3)) value = 1; // start with reverse value
else value = 0; // then hold to requested value
}
/********************************************************
* set the value via the -3 option
*/
else if( global_args.sim_generate_three_valued_logic)
{
value = (rand() % 3) - 1;
}
v->values[v->count] = (signed char *)vtr::realloc(v->values[v->count], sizeof(signed char) * (v->counts[v->count] + 1));
v->values[v->count][v->counts[v->count]++] = value;
}
v->count++;
}
return v;
}
/*
* Writes a wave of vectors to the given file. Writes the headers
* prior to cycle 0.
*
* When edge is -1, both edges of the clock are written. When edge is 0,
* the falling edge is written. When edge is 1, the rising edge is written.
*/
static void write_cycle_to_file(lines_t *l, FILE* file, int cycle)
{
if (!cycle)
write_vector_headers(file, l);
write_vector_to_file(l, file, cycle);
}
/*
* Writes all values in the given lines to a line in the given file
* for the given cycle.
*/
static void write_vector_to_file(lines_t *l, FILE *file, int cycle)
{
std::stringstream buffer;
int i;
for (i = 0; i < l->count; i++)
{
buffer.str(std::string());
line_t *line = l->lines[i];
int num_pins = line->number_of_pins;
if (line_has_unknown_pin(line, cycle) || num_pins == 1)
{
int j;
for (j = num_pins - 1; j >= 0 ; j--)
{
signed char value = get_line_pin_value(line, j, cycle);
if(value > 1 || value < 0){
buffer << "x";
}else{
buffer << std::dec <<(int)value;
}
}
// If there are no known values, print a single capital X.
// (Only for testing. Breaks machine readability.)
//if (!known_values && num_pins > 1)
// odin_sprintf(buffer, "X");
}
else
{
buffer << "0X";
int hex_digit = 0;
int j;
for (j = num_pins - 1; j >= 0; j--)
{
signed char value = get_line_pin_value(line,j,cycle);
hex_digit += value << j % 4;
if (!(j % 4))
{
buffer << std::hex << hex_digit;
hex_digit = 0;
}
}
}
buffer << " ";
// Expand the value to fill to space under the header. (Gets ugly sometimes.)
//while (strlen(buffer) < strlen(l->lines[i]->name))
// strcat(buffer," ");
fprintf(file,"%s",buffer.str().c_str());
}
fprintf(file, "\n");
}
/*
* Writes a wave of vectors to the given modelsim out file.
*/
static void write_cycle_to_modelsim_file(netlist_t *netlist, lines_t *l, FILE* modelsim_out, int cycle)
{
if (!cycle)
{
fprintf(modelsim_out, "add wave *\n");
// Add clocks to the output file.
int i;
for (i = 0; i < netlist->num_top_input_nodes; i++)
{
nnode_t *node = netlist->top_input_nodes[i];
if (is_clock_node(node))
{
char *port_name = get_port_name(node->name);
fprintf(modelsim_out, "force %s 0 0, 1 50 -repeat 100\n", port_name);
vtr::free(port_name);
}
}
}
write_vector_to_modelsim_file(l, modelsim_out, cycle);
}
/*
* Writes a vector to the given modelsim out file.
*/
static void write_vector_to_modelsim_file(lines_t *l, FILE *modelsim_out, int cycle)
{
int i;
for (i = 0; i < l->count; i++)
{
if (line_has_unknown_pin(l->lines[i], cycle) || l->lines[i]->number_of_pins == 1)
{
fprintf(modelsim_out, "force %s ",l->lines[i]->name);
int j;
for (j = l->lines[i]->number_of_pins - 1; j >= 0 ; j--)
{
int value = get_line_pin_value(l->lines[i],j,cycle);
if (value < 0) fprintf(modelsim_out, "%s", "x");
else fprintf(modelsim_out, "%d", value);
}
fprintf(modelsim_out, " %d\n", cycle/2 * 100);
}
else
{
int value = 0;
fprintf(modelsim_out, "force %s 16#", l->lines[i]->name);
int j;
for (j = l->lines[i]->number_of_pins - 1; j >= 0; j--)
{
if (get_line_pin_value(l->lines[i],j,cycle) > 0)
value += my_power(2, j % 4);
if (j % 4 == 0)
{
fprintf(modelsim_out, "%X", value);
value = 0;
}
}
fprintf(modelsim_out, " %d\n", cycle/2 * 100);
}
}
}
/*
* Verify that the given output vector file is identical (numerically)
* to the one written by the current simulation. This is done by parsing each
* file vector by vector and comparing them. Also verifies that the headers are identical.
*
* Prints appropriate warning messages when differences are found.
*
* Returns false if the files differ and true if they are identical, with the exception of
* number format.
*/
static int verify_output_vectors(const char* output_vector_file, int num_vectors)
{
int error = false;
// The filename cannot be the same as our default output file.
if (!strcmp(output_vector_file,OUTPUT_VECTOR_FILE_NAME))
{
error = true;
warning_message(SIMULATION_ERROR,0,-1,
"Vector file \"%s\" given for verification "
"is the same as the default output file \"%s\". "
"Ignoring.", output_vector_file, OUTPUT_VECTOR_FILE_NAME);
}
else
{
// The file being verified against.
FILE *existing_out = fopen(output_vector_file, "r");
if (!existing_out) error_message(SIMULATION_ERROR, 0, -1, "Could not open vector output file: %s", output_vector_file);
// Our current output vectors. (Just produced.)
char out_vec_file[BUFFER_MAX_SIZE] = { 0 };
odin_sprintf(out_vec_file,"%s/%s",global_args.sim_directory.value().c_str(),OUTPUT_VECTOR_FILE_NAME);
FILE *current_out = fopen(out_vec_file, "r");
if (!current_out)
error_message(SIMULATION_ERROR, 0, -1, "Could not open output vector file: %s", out_vec_file);
int cycle;
char buffer1[BUFFER_MAX_SIZE];
char buffer2[BUFFER_MAX_SIZE];
// Start at cycle -1 to check the headers.
for (cycle = -1; cycle < num_vectors; cycle++)
{
if (!get_next_vector(existing_out, buffer1))
{
error = true;
warning_message(SIMULATION_ERROR, 0, -1,"Too few vectors in %s \n", output_vector_file);
break;
}
else if (!get_next_vector(current_out, buffer2))
{
error = true;
warning_message(SIMULATION_ERROR, 0, -1,"Simulation produced fewer than %d vectors. \n", num_vectors);
break;
}
// The headers differ.
else if ((cycle == -1) && strcmp(buffer1,buffer2))
{
error = true;
warning_message(SIMULATION_ERROR, 0, -1, "Vector headers do not match: \n"
"\t%s"
"in %s does not match\n"
"\t%s"
"in %s.\n\n",
buffer2, OUTPUT_VECTOR_FILE_NAME, buffer1, output_vector_file
);
break;
}
else
{
// Parse both vectors.
test_vector *v1 = parse_test_vector(buffer1);
test_vector *v2 = parse_test_vector(buffer2);
int equivalent = compare_test_vectors(v1,v2);
// Compare them and print an appropriate message if they differ.
if (!equivalent)
{
trim_string(buffer1, "\n\t");
trim_string(buffer2, "\n\t");
error = true;
warning_message(SIMULATION_ERROR, 0, -1, "Vector %d mismatch:\n"
"\t%s in %s\n"
"\t%s in %s\n",
cycle, buffer2, OUTPUT_VECTOR_FILE_NAME, buffer1, output_vector_file
);
}
else if (equivalent == -1)
{
trim_string(buffer1, "\n\t");
trim_string(buffer2, "\n\t");
warning_message(SIMULATION_ERROR, 0, -1, "Vector %d equivalent but output vector has bits set when expecting don't care :\n"
"\t%s in %s\n"
"\t%s in %s\n",
cycle, buffer2, OUTPUT_VECTOR_FILE_NAME, buffer1, output_vector_file
);
}
free_test_vector(v1);
free_test_vector(v2);
}
}
// If the file we're checking against is longer than the current output, print an appropriate warning.
if (!error && get_next_vector(existing_out, buffer1))
{
error = true;
warning_message(SIMULATION_ERROR, 0, -1,"%s contains more than %d vectors.\n", output_vector_file, num_vectors);
}
fclose(existing_out);
fclose(current_out);
}
return !error;
}
/*
* If the given node matches one of the additional names (passed via -p),
* it's added to the lines. (Matches on output pin names, net names, and node names).
*/
static void add_additional_items_to_lines(nnode_t *node, lines_t *l)
{
std::vector<std::string> p = global_args.sim_additional_pins.value();
if (!p.empty())
{
int add = false;
int j, k = 0;
// Search the output pin names for each user-defined item.
for (j = 0; j < node->num_output_pins; j++)
{
npin_t *pin = node->output_pins[j];
if (pin->name)
{
for (k = 0; k < p.size(); k++)
{
if (strstr(pin->name, p[k].c_str()))
{
add = true;
break;
}
}
if (add) break;
}
if (pin->net && pin->net->name)
{
for (k = 0; k < p.size(); k++)
{
if (strstr(pin->net->name, p[k].c_str()))
{
add = true;
break;
}
}
if (add) break;
}
}
// Search the node name for each user defined item.
if (!add && node->name && strlen(node->name) && strchr(node->name, '^'))
{
for (k = 0; k < p.size(); k++)
{
if (strstr(node->name, p[k].c_str()))
{
add = true;
break;
}
}
}
if (add)
{
int single_pin = strchr(p[k].c_str(), '~')?1:0;
if (strchr(node->name, '^'))
{
char *port_name;
if (single_pin)
port_name = get_pin_name(node->name);
else
port_name = get_port_name(node->name);
if (find_portname_in_lines(port_name, l) == -1)
{
line_t *line = create_line(port_name);
l->lines = (line_t **)vtr::realloc(l->lines, sizeof(line_t *)*((l->count)+1));
l->lines[l->count++] = line;
}
assign_node_to_line(node, l, OUTPUT, single_pin);
vtr::free(port_name);
}
}
}
}
/*
* Parses the given (memory) node name into a corresponding
* mif file name.
*/
static char *get_mif_filename(nnode_t *node)
{
char buffer[BUFFER_MAX_SIZE];
buffer[0] = 0;
if(strlen(node->name) < BUFFER_MAX_SIZE)
strcat(buffer, node->name);
char *filename = strrchr(buffer, '+');
if (filename) filename += 1;
else filename = buffer;
strcat(filename, ".mif");
filename = vtr::strdup(filename);
return filename;
}
/*
* Returns true if the given line has a pin for
* the given cycle whose value is -1.
*/
static int line_has_unknown_pin(line_t *line, int cycle)
{
bool unknown = false;
int j;
for (j = line->number_of_pins - 1; j >= 0; j--)
{
signed char current_value = get_line_pin_value(line, j, cycle);
if (current_value < 0 || current_value > 1)
{
unknown = true;
break;
}
}
return unknown;
}
/*
* Gets the value of the pin given pin within the given line
* for the given cycle.
*/
signed char get_line_pin_value(line_t *line, int pin_num, int cycle)
{
return get_pin_value(line->pins[pin_num],cycle);
}
/*
* Returns a value from a test_vectors struct in hex. Works
* for the values arrays in pins as well.
*/
static char *vector_value_to_hex(signed char *value, int length)
{
char *tmp;
char *string = (char *)vtr::calloc((length + 1),sizeof(char));
int j;
for (j = 0; j < length; j++)
string[j] = value[j] + '0';
reverse_string(string,strlen(string));
char *hex_string = (char *)vtr::malloc(sizeof(char) * ((length/4 + 1) + 1));
odin_sprintf(hex_string, "%X ", (unsigned int)strtol(string, &tmp, 2));
vtr::free(string);
return hex_string;
}
/*
* Counts the number of vectors in the given file.
*/
static int count_test_vectors(FILE *in)
{
rewind(in);
int count = 0;
char buffer[BUFFER_MAX_SIZE];
while (get_next_vector(in, buffer))
count++;
if (count) // Don't count the headers.
count--;
rewind(in);
return count;
}
/*
* Counts the number of vectors in the given file.
*/
static int count_empty_test_vectors(FILE *in)
{
rewind(in);
int count = 0;
int buffer;
while ( (buffer = getc(in)) != EOF )
if(((char)buffer) == '\n')
count++;
if (count) // Don't count the headers.
count--;
rewind(in);
return count;
}
/*
* A given line is a vector if it contains one or more
* non-whitespace characters and does not being with a #.
*/
static int is_vector(char *buffer)
{
char *line = vtr::strdup(buffer);
trim_string(line," \t\r\n");
if (line[0] != '#' && strlen(line))
{
vtr::free(line);
return true;
}
else
{
vtr::free(line);
return false;
}
}
/*
* Gets the next line from the given file that
* passes the is_vector() test and places it in
* the buffer. Returns true if a vector was found,
* and false if no vector was found.
*/
static int get_next_vector(FILE *file, char *buffer)
{
oassert(file != NULL
&& "unable to retrieve file for next test vector");
oassert(buffer != NULL
&& "unable to use buffer for next test vector as it is not initialized");
while (fgets(buffer, BUFFER_MAX_SIZE, file))
if (is_vector(buffer))
return true;
return false;
}
/*
* Free each element in lines[] and the array itself
*/
static void free_lines(lines_t *l)
{
if(l)
{
if(l->lines)
{
while (l->count--)
{
if(l->lines[l->count])
{
if(l->lines[l->count]->name)
vtr::free(l->lines[l->count]->name);
if(l->lines[l->count]->pins)
vtr::free(l->lines[l->count]->pins);
vtr::free(l->lines[l->count]);
}
}
vtr::free(l->lines);
}
vtr::free(l);
}
}
/*
* Free stages.
*/
static void free_stages(stages_t *s)
{
if(s)
{
vtr::free(s->num_children);
if(s->stages)
{
while (s->count--)
{
if(s->stages[s->count])
vtr::free(s->stages[s->count]);
}
vtr::free(s->stages);
}
if(s->counts)
{
vtr::free(s->counts);
}
vtr::free(s);
}
}
/*
* Free the given test_vector.
*/
static void free_test_vector(test_vector* v)
{
if(v)
{
if(v->values)
{
while (v->count--)
{
if(v->values[v->count])
vtr::free(v->values[v->count]);
}
vtr::free(v->values);
}
if(v->counts)
vtr::free(v->counts);
vtr::free(v);
}
}
/*
* Prints information about the netlist we are simulating.
*/
static void print_netlist_stats(stages_t *stages, int /*num_vectors*/)
{
for(long i=0; i < configuration.list_of_file_names.size(); i++)
printf("%s:\n", configuration.list_of_file_names[i].c_str());
printf(" Nodes: %d\n", stages->num_nodes);
printf(" Connections: %d\n", stages->num_connections);
printf(" Threads: %d\n", number_of_workers);
printf(" Degree: %3.2f\n", stages->num_connections/(float)stages->num_nodes);
printf(" Stages: %d\n", stages->count);
printf(" Nodes/thread: %d(%4.2f%%)\n", (stages->num_nodes/number_of_workers), 100.0/(double)number_of_workers);
printf("\n");
}
/*
* Prints statistics. (Coverage and times.)
*/
static void print_simulation_stats(stages_t *stages, int /*num_vectors*/, double total_time, double simulation_time)
{
int covered_nodes = get_num_covered_nodes(stages);
printf("Simulation time: ");
print_time(simulation_time);
printf("\n");
printf("Elapsed time: ");
print_time(total_time);
printf("\n");
printf("Coverage: "
"%d (%4.1f%%)\n", covered_nodes, (covered_nodes/(double)stages->num_nodes) * 100);
}
/*
* Prints n ancestors of the given node, complete
* with their parents, ids, etc.
*/
static void print_ancestry(nnode_t *bottom_node, int n)
{
if (!n) n = 10;
std::queue<nnode_t *> queue = std::queue<nnode_t *>();
queue.push(bottom_node);
printf( " ------------\n");
printf( " BACKTRACE\n");
printf( " ------------\n");
while (n-- && !queue.empty())
{
nnode_t *node = queue.front();
queue.pop();
char *name = get_pin_name(node->name);
printf(" %s (%ld):\n", name, node->unique_id);
vtr::free(name);
int i;
for (i = 0; i < node->num_input_pins; i++)
{
npin_t *pin = node->input_pins[i];
nnet_t *net = pin->net;
nnode_t *node2 = net->driver_pin->node;
queue.push(node2);
char *name2 = get_pin_name(node2->name);
printf("\t%s %s (%ld)\n", pin->mapping, name2, node2->unique_id);fflush(stdout);
vtr::free(name2);
}
/*int count = 0;
{
printf( " ------------\n");
printf( " CHILDREN \n");
printf( " ------------\n");
printf( " O: %ld\n", node->num_output_pins);fflush(stdout);
for (i = 0; i < node->num_output_pins; i++)
{
npin_t *pin = node->output_pins[i];
if (pin)
{
nnet_t *net = pin->net;
if (net)
{
int j;
for (j = 0; j < net->num_fanout_pins; j++)
{
npin_t *pin = net->fanout_pins[j];
if (pin)
{
nnode_t *node = pin->node;
if (node)
{
char *name = get_pin_name(node->name);
printf("\t%s %s (%ld)\n", pin->mapping, name, node->unique_id);fflush(stdout);
vtr::free(name);
}
else
{
printf(" Null node %ld\n", ++count);fflush(stdout);
}
}
else
{
printf(" Null fanout pin\n");fflush(stdout);
}
}
}
else
{
printf(" Null net\n");fflush(stdout);
}
}
}
}*/
printf( " ------------\n");
}
printf( " END OF TRACE\n");
printf( " ------------\n");
}
/*
* Traces an node which is failing to update back to parent of
* the earliest node in the net list with an unupdated pin.
* (Pin whose cycle is less than cycle-1). Prints all nodes
* traversed during the trace with the original node printed
* first, and the root of the issue printed last.
*
* Returns the root node for inspection.
*/
static nnode_t *print_update_trace(nnode_t *bottom_node, int cycle)
{
// Limit the length of the trace. 0 for unlimited.
const int max_depth = 0;
printf(
" --------------------------------------------------------------------------\n"
" --------------------------------------------------------------------------\n"
" BACKTRACE:\n"
"\tFormat: Each node is listed followed by its parents. The parent \n"
"\twhich is not updating is indicated with an astrisk. (*)\n"
"\tEach node is listed in the form:\n"
"\t\t(<mapping>) <Name> (<Unique ID>) <# of Parents> <# of Children> \n"
" --------------------------------------------------------------------------\n"
);
nnode_t *root_node = NULL;
// Used to detect cycles. Based table size of max depth. If unlimited, set to something big.
std::unordered_set<long> index;
std::queue<nnode_t *> queue = std::queue<nnode_t *>();
queue.push(bottom_node);
int depth = 0;
// Traverse the netlist in reverse, starting with our current location.
while (!queue.empty())
{
nnode_t *node = queue.front();
queue.pop();
bool found_undriven_pin = false;
if (index.find(node->unique_id) == index.end())
{
depth++;
index.insert(node->unique_id);
char *name = get_pin_name(node->name);
printf(" %s (%ld) %ld %ld\n", name, node->unique_id, node->num_input_pins, node->num_output_pins);
vtr::free(name);
int i;
for (i = 0; i < node->num_input_pins; i++)
{
npin_t *pin = node->input_pins[i];
nnet_t *net = pin->net;
nnode_t *node2 = net->driver_pin->node;
// If an input is found which hasn't been updated since before cycle-1, traverse it.
bool is_undriven = false;
if (get_pin_cycle(pin) < cycle-1)
{
// Only add each node for traversal once.
if (!found_undriven_pin)
{
found_undriven_pin = true;
queue.push(node2);
}
is_undriven = true;
}
char *name2 = get_pin_name(node2->name);
printf("\t(%s) %s (%ld) %ld %ld %s \n", pin->mapping, name2, node2->unique_id, node2->num_input_pins, node2->num_output_pins, is_undriven?"*":"");
vtr::free(name2);
}
printf(" ------------\n");
}
else {
printf(" CYCLE DETECTED AFTER %d NODES.\n", depth);
printf(" ------------\n");
break;
}
if (!found_undriven_pin)
{
printf(" TOP OF TRACE\n");
printf(" ------------\n");
root_node = node;
break;
}
else if (max_depth && depth >=max_depth)
{
printf(" TRACE TRUNCATED AT %d NODES.\n", max_depth);
printf(" ------------\n");
break;
}
}
return root_node;
}