blob: 29d2375b9053d068bf0e2f743febd0e007ca28a4 [file] [log] [blame] [edit]
/* number_of_workers = std::max(1, global_args.parralelized_simulation.value());
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_util.h"
#include <string>
#include <sstream>
#include <chrono>
#include <dlfcn.h>
#include <mutex>
#include <unistd.h>
#include <thread>
//#include <cthreads.h>
//maria
#include <sys/sysinfo.h>
#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;
}
typedef enum
{
FALLING,
RISING,
HIGH,
LOW,
UNK
}edge_eval_e;
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)
);
}
//maria
static thread_node_distribution *calculate_thread_distribution(stages_t *s);
static void compute_and_store_part_multithreaded(int /*t_id*/,netlist_subset *thread_nodes,int cycle); //to remove
static void compute_and_store_part_wave_multithreaded(int /*t_id*/,netlist_subset *thread_nodes,int from_wave,int to_wave);
static void compute_and_store_part_in_waves_multithreaded(int /*t_id*/,netlist_subset *thread_nodes,int from_wave, int to_wave,int offset,bool /*notify_back*/);
static void simulate_cycle_multithreaded(int cycle, thread_node_distribution *thread_distribution);
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);
//maria
static void free_thread_distribution(thread_node_distribution *thread_distribution);
static int get_num_covered_nodes(stages_t *s);
static int *get_children_pinnumber_of(nnode_t *node, int *num_children);
//maria
static nnode_t **get_parents_of(nnode_t *node, int *num_parents);
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(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);
double used_time;
int number_of_workers;
bool batch_mode;
bool found_best_time;
int num_of_clock;
//maria TODO maybe not the best place?
pthread_cond_t start_threads,start_output;
pthread_mutex_t threads_mp,output_mp,main_mp;
int threads_done_wave = 0;
int threads_created = 0;
int threads_waves = 0;
int threads_start = 0;
int threads_end = 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;
}
if (number_of_workers>1 && global_args.parralelized_simulation_in_batch)
{
int start_cycle = 0;
int end_cycle = sim_data->num_vectors;
simulate_steps_in_parallel(sim_data,start_cycle,end_cycle,min_coverage);
}
else
{
simulate_steps_sequential(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.
char *output_vector_file = global_args.sim_vector_output_file;
if (output_vector_file)
{
if (verify_output_vectors(output_vector_file, sim_data->num_vectors))
printf("Vector file \"%s\" matches output\n", output_vector_file);
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);
// Perform ACE activity calculations
calculate_activity ( netlist, sim_data->num_vectors, sim_data->act_out );
}
/*
* Performs simulation batches of cycles pass through the threads.
*/
// before
/* void simulate_netlist(netlist_t *netlist)
{
sim_data_t *sim_data = init_simulation(netlist);
printf("\n");
int progress_bar_position = -1;
const int progress_bar_length = 50;
int increment_vector_by = global_args.sim_num_test_vectors;;
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;
}
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
{
current_coverage = cycle/(double)sim_data->num_vectors;
}
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/(double)(sim_data->num_vectors), progress_bar_position, progress_bar_length, sim_data->total_time);
cycle++;
}
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.
char *output_vector_file = global_args.sim_vector_output_file;
if (output_vector_file)
{
if (verify_output_vectors(output_vector_file, sim_data->num_vectors))
printf("Vector file \"%s\" matches output\n", output_vector_file);
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);
// Perform ACE activity calculations
calculate_activity ( netlist, sim_data->num_vectors, sim_data->act_out );
} */
/**
* Initialize simulation
*/
sim_data_t *init_simulation(netlist_t *netlist)
{
//for multithreading
used_time = std::numeric_limits<double>::max();
number_of_workers = global_args.parralelized_simulation.value();
if(number_of_workers >1 )
warning_message(SIMULATION_ERROR,-1,-1,"Executing simulation with maximum of %ld threads", number_of_workers);
found_best_time = false;
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", ((char *)global_args.sim_directory));
fflush(stdout);
// Open the output vector file.
char out_vec_file[BUFFER_MAX_SIZE] = { 0 };
odin_sprintf(out_vec_file,"%s/%s",((char *)global_args.sim_directory),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",((char *)global_args.sim_directory),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",((char *)global_args.sim_directory),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",((char *)global_args.sim_directory),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;
sim_data->input_vector_file = 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
char *input_vector_file = global_args.sim_vector_input_file;
if (input_vector_file && sim_data->input_lines->count != 0)
{
sim_data->input_vector_file = input_vector_file;
sim_data->in = fopen(sim_data->input_vector_file, "r");
if (!sim_data->in)
error_message(SIMULATION_ERROR, 0, -1, "Could not open vector input file: %s", sim_data->input_vector_file);
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.", sim_data->input_vector_file);
printf("Simulating %ld existing vectors from \"%s\".\n", sim_data->num_vectors, sim_data->input_vector_file); 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(input_vector_file)
{
sim_data->in = fopen(input_vector_file, "r");
if (!sim_data->in)
error_message(SIMULATION_ERROR, 0, -1, "Could not open vector input file: %s", input_vector_file);
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);
}
// Setup data structures for activity estimation
alloc_and_init_ace_structs(netlist);
// 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;
//maria
sim_data->thread_distribution = 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);
//maria
free_thread_distribution(sim_data->thread_distribution);
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->input_vector_file)
fclose(sim_data->in);
fclose(sim_data->out);
vtr::free(sim_data);
sim_data = NULL;
return sim_data;
}
void simulate_steps_sequential(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
{
current_coverage = cycle/(double)sim_data->num_vectors;
}
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++;
}
}
void simulate_steps_in_parallel(sim_data_t *sim_data,int from_wave,int to_wave,double min_coverage)
{
// produce a wave of values at each iteration
double start_time = wall_time();
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 offset = BUFFER_SIZE-1; // BUFFER_SIZE- simulation
threads_waves = (to_wave-from_wave)/offset;
threads_start = from_wave;
threads_end = to_wave;
pthread_cond_init(&start_threads, NULL);
pthread_cond_init(&start_output, NULL);
std::vector<std::thread> worker_threads;
bool done = FALSE,restart = FALSE;
while (!done)
{
for (int wave = 0; wave<=threads_waves; wave++)
{
double simulation_start_time = wall_time();
int from_cycle = from_wave + wave*offset;
int to_cycle = from_cycle+offset;
if (to_cycle > to_wave)
to_cycle = to_wave;
test_vector *v=NULL;
// Assign vectors to lines, either by reading or generating them.
// Every second cycle gets a new vector.
char buffer[BUFFER_MAX_SIZE];
for (int i=from_cycle;i<to_cycle;i++)
{
v = NULL;
if (sim_data->in)
{
//buffer = NULL;
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);
//printf("Here\n");
}
else
{
v = generate_random_test_vector(i, sim_data);
}
//printf("v=%ld \n",v->values[0][0]);
add_test_vector_to_lines(v, sim_data->input_lines, i);
write_cycle_to_file(sim_data->input_lines, sim_data->in_out, i);
write_cycle_to_modelsim_file(sim_data->netlist, sim_data->input_lines, sim_data->modelsim_out, i);
}
if (v)
free_test_vector(v);
if (wave == 0)
{
// lines as specified by the -p option.
sim_data->stages = simulate_first_cycle(sim_data->netlist, from_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.");
//maria
sim_data->thread_distribution = calculate_thread_distribution(sim_data->stages);
//create_threads_and_let them wait for the signal
for (int t=0; t<sim_data->thread_distribution->number_of_threads; t++)
{
worker_threads.push_back(std::thread(compute_and_store_part_in_waves_multithreaded,t,sim_data->thread_distribution->thread_nodes[t],from_wave,to_wave,offset,TRUE));
}
}
if (wave !=0 || restart)
{
pthread_mutex_lock(&output_mp);
threads_done_wave =0;
pthread_mutex_unlock(&output_mp);
if( (errno =pthread_cond_broadcast(&start_threads)) !=0)
printf("Broadcast Error!");
}
pthread_mutex_lock(&threads_mp);
while (threads_done_wave != sim_data->thread_distribution->number_of_threads)
{
pthread_cond_wait(&start_output,&threads_mp);
}
pthread_mutex_unlock(&threads_mp);
for (int i=from_cycle;i<to_cycle;i++)
{
//if (i!=0)
write_cycle_to_file(sim_data->output_lines, sim_data->out, i);
}
//update memory directories of all memory nodes
write_back_memory_nodes(sim_data->thread_distribution->memory_nodes->nodes,sim_data->thread_distribution->memory_nodes->number_of_nodes);
sim_data->simulation_time += wall_time() - simulation_start_time;
if (wave==threads_waves) //check for coverage in the last cycle
{
if(min_coverage > 0.0)
{
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;
}
//update the cycle boundaries to continue calculations
if (sim_data->num_vectors != to_cycle)
{
from_wave = to_cycle+1;
to_wave = sim_data->num_vectors;
threads_waves = (to_wave-from_wave)/offset;
pthread_mutex_lock(&output_mp);
threads_start = from_cycle;
threads_end = to_cycle;
pthread_mutex_unlock(&output_mp);
restart = TRUE;
//printf("threads start %ld threads end %ld",threads_start,threads_end);
}
else
done= TRUE;
}
else
{
current_coverage = to_cycle/(double)sim_data->num_vectors;
done = TRUE;
}
}
// Print netlist-specific statistics.
if (wave == 0)
print_netlist_stats(sim_data->stages, sim_data->num_vectors);
sim_data->total_time = wall_time() - start_time;
progress_bar_position = print_progress_bar(
to_cycle/(double)(sim_data->num_vectors), progress_bar_position, progress_bar_length, sim_data->total_time);
}
if (done)
{
//signal to unblock threads and let them finish
if( (errno =pthread_cond_broadcast(&start_threads)) !=0)
printf("Broadcast Error!");
}
}
int threadnum = 0;
for (auto &worker: worker_threads)
{
worker.join();
threadnum++;
}
//wait for them to be done
pthread_cond_destroy(&start_output);
pthread_cond_destroy(&start_threads);
sim_data->total_time = wall_time() - start_time;
progress_bar_position = print_progress_bar(
(double)sim_data->num_vectors/(double)(sim_data->num_vectors), progress_bar_position, progress_bar_length, sim_data->total_time);
}
/**
* 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);
//split the nodes into threads using the stages agbove for parallel calculations
//maria
//sim_data->thread_distribution = calculate_thread_distribution(sim_data->stages);
// 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);
//maria
//simulate_cycle_multithreaded(cycle, sim_data->thread_distribution);
}
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;
}
}
//maria
static void compute_and_store_part_multithreaded(int /*t_id*/,netlist_subset *thread_nodes,int cycle)
{
int *nodes_done = (int*)vtr::calloc(thread_nodes->number_of_nodes,sizeof(int));
int nodes_counter = thread_nodes->number_of_nodes;
nnode_t **nodes_in_progress = (nnode_t **)vtr::malloc(sizeof(nnode_t*) *thread_nodes->number_of_nodes );
for (int i=0;i<nodes_counter;i++)
nodes_in_progress[i] = thread_nodes->nodes[i];
while (nodes_counter!=0 )
{
for (int j = 0;j < nodes_counter; j++)
{
nnode_t *node = nodes_in_progress[j];
if(node && is_node_ready(node,cycle) && !is_node_complete(node,cycle) )
{
compute_and_store_value(node, cycle);
nodes_done[j]=1;
}
else if(!node || is_node_complete(node,cycle) )
nodes_done[j]=1;
}
nnode_t **temp = &(*nodes_in_progress);
int not_done = 0;
//number of nodes
for (int i=0;i<nodes_counter;i++)
{
if (!nodes_done[i])
{
nodes_in_progress[not_done] = temp[i];
not_done++;
}
nodes_done[i] = 0;
}
nodes_counter = not_done;
}
vtr::free(nodes_done);
vtr::free(nodes_in_progress);
}
//maria
static void compute_and_store_part_in_waves_multithreaded(int /*t_id*/,netlist_subset *thread_nodes,int from_wave, int to_wave,int offset,bool /*notify_back*/)
{
int *nodes_done = (int*)vtr::calloc(thread_nodes->number_of_nodes,sizeof(int));
int nodes_counter = thread_nodes->number_of_nodes;
int waves = (to_wave-from_wave)/offset;
//do while
for (int wave = 0;wave<=waves; wave++)
{
int from_cycle = from_wave + wave*offset;
int to_cycle = from_cycle+offset;
if (to_cycle > to_wave)
to_cycle = to_wave;
for (int cycle = from_cycle; cycle<to_cycle; cycle++)
{
nodes_counter = thread_nodes->number_of_nodes;
for (int i=0;i<nodes_counter;i++)
nodes_done[i] = 0;
int done = 0;
while (nodes_counter!=done )
{
for (int j = 0;j < nodes_counter; j++)
{
if (!nodes_done[j])
{
nnode_t *node = thread_nodes->nodes[j];
if(node && is_node_ready(node,cycle) && !is_node_complete(node,cycle) )
{
compute_and_store_value(node, cycle);
nodes_done[j]=1;
}
else if(!node || is_node_complete(node,cycle) )
{
nodes_done[j]=1;
}
}
}
done = 0;
//number of nodes
for (int i=0;i<nodes_counter;i++)
{
if (nodes_done[i])
{
done++;
}
}
}
}
//signal the current wave is done
pthread_mutex_lock(&threads_mp);
threads_done_wave++;
pthread_cond_broadcast(&start_output);
pthread_cond_wait(&start_threads,&threads_mp);
pthread_mutex_unlock(&threads_mp);
if (wave == waves) //check if we need to start again for coverage
{
int shared_from_wave,shared_to_wave;
pthread_mutex_lock(&threads_mp);
shared_from_wave = threads_start;
shared_to_wave = threads_end;
pthread_mutex_unlock(&threads_mp);
//if the shared variable is changed then copy the other values and restart the loop
if (shared_from_wave != from_wave)
{
from_wave = shared_from_wave;
to_wave = shared_to_wave;
waves = (to_wave-from_wave)/offset;
}
}
}
vtr::free(nodes_done);
}
//maria
static void compute_and_store_part_wave_multithreaded(int /*t_id*/,netlist_subset *thread_nodes,int from_wave, int to_wave)
{
int *nodes_done = (int*)vtr::calloc(thread_nodes->number_of_nodes,sizeof(int));
int nodes_counter = thread_nodes->number_of_nodes;
for (int cycle = from_wave; cycle<to_wave; cycle++)
{
nodes_counter = thread_nodes->number_of_nodes;
for (int i=0;i<nodes_counter;i++)
nodes_done[i] = 0;
int done = 0;
while (nodes_counter!=done )
{
for (int j = 0;j < nodes_counter; j++)
{
if (!nodes_done[j])
{
nnode_t *node = thread_nodes->nodes[j];
if(node && is_node_ready(node,cycle) && !is_node_complete(node,cycle) )
{
compute_and_store_value(node, cycle);
nodes_done[j]=1;
}
else if(!node || is_node_complete(node,cycle) )
nodes_done[j]=1;
}
}
done = 0;
//number of nodes
for (int i=0;i<nodes_counter;i++)
{
if (nodes_done[i])
{
done++;
}
}
}
}
vtr::free(nodes_done);
}
//maria
static void simulate_cycle_multithreaded(int cycle, thread_node_distribution *thread_distribution)
{
std::vector<std::thread> workers;
for (int t=0; t<thread_distribution->number_of_threads; t++)
{
workers.push_back(std::thread(compute_and_store_part_wave_multithreaded,t,thread_distribution->thread_nodes[t],cycle,cycle+1));
}
int threadnum = 0;
for (auto &worker: workers)
{
worker.join();
threadnum++;
}
}
/*
* 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)
{
int 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;
}
//maria
//simulate one cycle to determine the parallelization degree of the circuit
//returns the number of threads
static thread_node_distribution *calculate_thread_distribution(stages_t *s)
{
//double nodecost = 1;
//double extranodeoverhead = 1.0*nodecost;
//double lessnodeoverhead = -0.5*nodecost;
double nodeoverhead_100 = 100;
double nodeoverhead_200 = 200;
double nodeoverhead_300 = 300;
double nodeoverhead_400 = 400;
double stagecost = nodeoverhead_300;
//double threadoverheadcost = 5*nodecost;
/** not portable
* int max_available_threads = get_nprocs();
*/
int max_available_threads = number_of_workers;
//store nodes for each thread
thread_node_distribution* thread_distribution= (thread_node_distribution *)vtr::malloc(sizeof(thread_node_distribution));
//for each stage
double *stagescost = (double *)vtr::malloc(sizeof(double)* s->count);
double graphcost = 0.0;
int all_nodes = get_num_covered_nodes(s);
std::map<int, int> nodes_inserted; //nodeId,flag
thread_distribution->memory_nodes = (netlist_subset *)vtr::malloc(sizeof(netlist_subset));
int number_of_mem_nodes = 0;
nnode_t** nodes_mem_sub = 0;
//traverse and calculate the graph cost
for(int i = 0; i < s->count; i++)
{
stagescost[i] = 0.0;
nnode_t** nodes = s->stages[i];
//for each node
for (int j =0; j < s->counts[i]; j++)
{
//stagescost[i]+=nodecost;
if (nodes[j]->type == GND_NODE || nodes[j]->type == VCC_NODE || nodes[j]->type == OUTPUT_NODE || nodes[j]->type == CLOCK_NODE || nodes[j]->type == PAD_NODE || nodes[j]->type == MUX_2 || nodes[j]->type == LOGICAL_AND)
stagescost[i]+=nodeoverhead_100;
else if (nodes[j]->type == HARD_IP || nodes[j]->type == GENERIC || nodes[j]->type == MEMORY)
stagescost[i]+=nodeoverhead_300;
else if (nodes[j]->type == MULTIPLY)
stagescost[i]+=nodeoverhead_400;
else if (nodes[j]->type == INPUT_NODE)
stagescost[i]+=1;
else
stagescost[i]+=nodeoverhead_200;
nodes_inserted[nodes[j]->unique_id] = 0;
}
graphcost += stagecost+stagescost[i];
}
thread_distribution->memory_nodes->number_of_nodes = number_of_mem_nodes;
thread_distribution->memory_nodes->nodes = nodes_mem_sub;
//printf("graphcost: %lf .\n",graphcost);
double threadworkcost = ceil(graphcost/max_available_threads);
int num_threads = 0;
int current_stage = 0;
int node_in_stage = 0;
int nodes_assigned = 0;
//printf("threadworkcost: %lf .\n",threadworkcost);
//for each stage
netlist_subset **circuit_borders = (netlist_subset **)vtr::malloc(sizeof(netlist_subset*) * max_available_threads);
int threads = ceil(graphcost/threadworkcost);
for(int i = 0; i < threads; i++)
{
circuit_borders[i] = (netlist_subset *)vtr::malloc(sizeof(netlist_subset));
circuit_borders[i]->nodes = 0;
}
for (int t =0;t<threads && nodes_assigned!=all_nodes;t++)
{
double threadcost = 0.0;
//nodes per thread
int number_of_nodes = 0;
nnode_t** nodes_sub = 0; //(nnode_t **)vtr::calloc(1,sizeof(nnode_t*));
while (threadcost< threadworkcost)
{
nnode_t* node = s->stages[current_stage][node_in_stage];
//printf("NodeID %ld assigned to Thread %ld\n",node->unique_id,t);
if (nodes_inserted[node->unique_id]==0)
{
nodes_sub = (nnode_t **)vtr::realloc(nodes_sub,sizeof(nnode_t*) * (number_of_nodes+1) );
nodes_sub[number_of_nodes++] = node;
nodes_assigned++;
nodes_inserted[node->unique_id] = 1;
if (node->type == GND_NODE || node->type == VCC_NODE || node->type == OUTPUT_NODE || node->type == CLOCK_NODE || node->type == PAD_NODE || node->type == MUX_2 || node->type == LOGICAL_AND)
threadcost+=nodeoverhead_100;
else if (node->type == HARD_IP || node->type == GENERIC || node->type == MEMORY)
threadcost+=nodeoverhead_300;
else if (node->type == MULTIPLY)
threadcost+=nodeoverhead_400;
else if (node->type == INPUT_NODE)
threadcost+=1;
else
threadcost+=nodeoverhead_200;
//memory family put together if uncomment. not necessary
/*
int num_children;
nnode_t **children = get_children_of(node, &num_children);
nnode_t**memory_nodes = 0;
int num_memory_nodes = 0;
//find all decendeces and ancestors of evey memory node related
nnode_t**memory_family = 0;
int num_memory_family = 0;
for(int child=0;child<num_children;child++)
{
if (children[child]->type == MEMORY || children[child]->type == HARD_IP)
{
memory_nodes = (nnode_t **)vtr::realloc(memory_nodes,sizeof(nnode_t*) * (num_memory_nodes+1) );
memory_nodes[num_memory_nodes++] = children[child];
nodes_inserted[children[child]->unique_id] = -1; //to be processed
memory_family = (nnode_t **)vtr::realloc(memory_family,sizeof(nnode_t*) * (num_memory_family+1) );
memory_family[num_memory_family++] = children[child];
}
}
if (num_memory_nodes !=0 )
{
int mem_index = 0;
while(mem_index !=num_memory_nodes)
{
nnode_t* memnode = memory_nodes[mem_index];
nodes_inserted[memnode->unique_id] = -1;
//printf("memnode tyope of %s\n ",memnode->name);
int num_parents;
nnode_t **parents = get_parents_of(memnode, &num_parents);
for(int parent=0;parent<num_parents;parent++)
{
if ( nodes_inserted[parents[parent]->unique_id] != -1 ) //if it is not processed here
{
memory_family = (nnode_t **)vtr::realloc(memory_family,sizeof(nnode_t*) * (num_memory_family+1) );
memory_family[num_memory_family++] = parents[parent];
//printf("NodeP %ld -1\n",parents[parent]->unique_id);
if (parents[parent]->type == HARD_IP || parents[parent]->type == MEMORY) //its a memory node add it to the queue
{
memory_nodes = (nnode_t **)vtr::realloc(memory_nodes,sizeof(nnode_t*) * (num_memory_nodes+1) );
memory_nodes[num_memory_nodes++] = parents[parent];
}
else
{
nodes_inserted[parents[parent]->unique_id] = -1;
}
}
}
int num_children;
nnode_t **children = get_children_of(memnode, &num_children);
for(int child=0;child<num_children;child++)
{
if ( nodes_inserted[children[child]->unique_id] != -1 ) //if it is not processed here
{
memory_family = (nnode_t **)vtr::realloc(memory_family,sizeof(nnode_t*) * (num_memory_family+1) );
memory_family[num_memory_family++] = children[child];
if (children[child]->type == HARD_IP || children[child]->type == MEMORY) //its a memory node add it to the queue
{
memory_nodes = (nnode_t **)vtr::realloc(memory_nodes,sizeof(nnode_t*) * (num_memory_nodes+1) );
memory_nodes[num_memory_nodes++] = children[child];
//nodes_inserted[children[child]->unique_id] = -1;
}
else
{
nodes_inserted[children[child]->unique_id] = -1;
}
}
}
mem_index++;
//printf("mem_index %ld num_memory_family %ld num_memory_nodes%ld \n",mem_index,num_memory_family,num_memory_nodes);
}
printf("Memory node found \n");
mem_index = 0;
for (mem_index=0;mem_index<num_memory_family;mem_index++)
{
nnode_t* memnode = memory_family[mem_index];
//if (!nodes_inserted[memnode->unique_id])
//{
nodes_sub = (nnode_t **)vtr::realloc(nodes_sub,sizeof(nnode_t*) * (number_of_nodes+1) );
nodes_sub[number_of_nodes++] = memnode;
nodes_assigned++;
nodes_inserted[memnode->unique_id] = 1;
threadcost+=nodecost;
if (memnode->type == HARD_IP || memnode->type == GENERIC || memnode->type == MEMORY )
threadcost+=extranodeoverhead;
if (memnode->type == GND_NODE || memnode->type == VCC_NODE || memnode->type == INPUT_NODE )
threadcost+=lessnodeoverhead;
//}
//printf("NodeP %ld 1\n",memnode->unique_id);
//printf("Asgnd %ld out of %ld \n",nodes_assigned,all_nodes);
}
//vtr::free(memory_nodes);
//printf(" Node added \n");
}
*/
}
//printf("Next node %ld %ld/%ld \n",node->unique_id,nodes_assigned,all_nodes);
if (node_in_stage == s->counts[current_stage]-1) //change stage
{
node_in_stage = 0;
if (current_stage == s->count-1) //last stage
{
break;
}
else
current_stage++; //next stage
}
else //same stage next node
node_in_stage++;
}
// add them to the structure
circuit_borders[num_threads]->nodes = nodes_sub;
circuit_borders[num_threads]->number_of_nodes = number_of_nodes;
++num_threads;
}
// Create a map iterator and point to beginning of map
std::map<int, int>::iterator it = nodes_inserted.begin();
// Iterate over the map using Iterator till end.
while (it != nodes_inserted.end())
{
// Accessing KEY from element pointed by it.
int node_id = it->first;
// Accessing VALUE from element pointed by it.
int inserted = it->second;
if (inserted !=1)
{
error_message(SIMULATION_ERROR,1475,-1,"Node %ld is not assigned for simulation!",node_id);
}
// Increment the Iterator to point to next entry
it++;
}
//if (nodes_assigned == FALSE)
//{
// error_message(SIMULATION_ERROR,1475,-1, "%s", "Some nodes are not assigned for simulation!");
//}
thread_distribution->thread_nodes = circuit_borders;
thread_distribution->number_of_threads = num_threads;
number_of_workers = num_threads;
vtr::free(stagescost);
return thread_distribution;
}
/*
* 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);
char unknown = FALSE;
char 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);
char unknown = FALSE;
char 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);
char unknown = FALSE;
char 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);
char unknown = FALSE;
char 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);
char 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);
char 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;
signed char cur_value = (cycle % get_clock_ratio(node)) ? 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 activity 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; i++) {
if ( node->output_pins[i]->ace_info != NULL ) {
signed char pin_value = get_pin_value(node->output_pins[i],cycle);
// last_pin_value = get_pin_value(node->output_pins[i],cycle-1);
// Pin values for cycle-1 were not correct on Wave boundaries. Needed to store it in ace object.
signed char last_pin_value = node->output_pins[i]->ace_info->value;
// # of ones
if ( pin_value == 1 )
{
node->output_pins[i]->ace_info->num_ones += pin_value;
}
// # of toggles
if ( ( pin_value != last_pin_value ) && (last_pin_value != -1 ) )
{
node->output_pins[i]->ace_info->num_toggles++;
node->output_pins[i]->coverage++;
if(node->output_pins[i]->coverage < 2)
covered = false;
}
node->output_pins[i]->ace_info->value = pin_value;
}
}
}
if(covered || skip_node_from_coverage)
node->covered = true;
//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 parents of the given node. Return the number of
* parents via the num_parents parameter.*/
//maria
nnode_t **get_parents_of(nnode_t *node, int *num_parents)
{
nnode_t **parents = 0;
int count = 0;
int i;
for (i = 0; i < node->num_input_pins; i++)
{
npin_t *pin = node->input_pins[i];
nnet_t *net = pin->net;
if (pin && net && net->driver_pin->node)
{
nnode_t *parent_node = net->driver_pin->node;
//char *parent_node_name = get_pin_name(parent_node->name);
parents = (nnode_t **)vtr::realloc(parents, sizeof(nnode_t*) * (count + 1));
parents[count++] = parent_node;
}
}
*num_parents = count;
return parents;
}
/*
* 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 the given node. Returns the number of
* children via the num_children parameter. Throws warnings
* or errors if invalid connection patterns are detected.
*/
static int *get_children_pinnumber_of(nnode_t *node, int *num_children)
{
int *pin_numbers = 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.
pin_numbers = (int *)vtr::realloc(pin_numbers, sizeof(int) * (count + 1));
pin_numbers[count++] = i;
}
}
}
}
}
*num_children = count;
return pin_numbers;
}
/*
* 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);
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);
}
return pin->values->get_value(cycle);
}
/*
* 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.
char 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);
}
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;
char 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;
char 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);
}
}
if (false == global_args.parralelized_simulation_in_batch)
{
node->memory_data[address] = new_values;
}
/**
* use dictionnary when there are multiple workers
*/
else
{
/********* Critical section */
/* LOCK */node->memory_mtx.lock();
if(write)
{
if ( node->memory_directory.find(cycle) == node->memory_directory.end() )
{
node->memory_directory[cycle] = {};
}
node->memory_directory[cycle][address]= new_values;
}
/**
* read from the dictionary if exist otherwise use current mem_data value
*/
else /* READ */
{
for ( auto it = node->memory_directory.begin(); it != node->memory_directory.end(); it++ )
{
int recorded_cycle = it->first;
if (recorded_cycle <= cycle
&& ( node->memory_directory[recorded_cycle].find(address) != node->memory_directory[recorded_cycle].end() ))
{
new_values = node->memory_directory[recorded_cycle][address];
}
}
}
/* UNLOCK */node->memory_mtx.unlock();
}
}
/**
* 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);
}
}
static void write_back_memory_nodes(nnode_t **nodes, int num_nodes)
{
int num;
//printf("here\n");
for(num=0;num<num_nodes;num++)
{
nnode_t* node = nodes[num];
if (!node->memory_directory.empty())
{
std::map<int,std::map<long,std::vector<signed char>>>::iterator it;
for ( it = node->memory_directory.begin(); it != node->memory_directory.end(); it++ )
{
int recorded_cycle = it->first;
std::map<long,std::vector<signed char>>::iterator cit;
for ( cit = node->memory_directory[recorded_cycle].begin(); cit != node->memory_directory[recorded_cycle].end(); cit++ )
{
long address = cit->first;
std::vector<signed char> new_values = cit->second;
node->memory_data[address] = new_values;
}
node->memory_directory[recorded_cycle] = {};
}
node->memory_directory={};
}
}
}
/*
* 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);
}
}
/*
* Removes white space (except new lines) and comments from
* the given mif file and returns the resulting temporary file.
*/
static FILE *preprocess_mif_file(FILE *source)
{
FILE *destination = tmpfile();
destination = freopen(NULL, "r+", destination);
rewind(source);
char line[BUFFER_MAX_SIZE];
int in_multiline_comment = FALSE;
while (fgets(line, BUFFER_MAX_SIZE, source))
{
unsigned int i;
for (i = 0; i < strlen(line); i++)
{
if (!in_multiline_comment)
{
// For a single line comment, skip the rest of the line.
if (line[i] == '-' && line[i+1] == '-')
break;
// Start of a multiline comment
else if (line[i] == '%')
in_multiline_comment = TRUE;
// Don't copy any white space over.
else if (line[i] != '\n' && line[i] != ' ' && line[i] != '\r' && line[i] != '\t' )
fputc(line[i], destination);
}
else
{
// If we're in a multi-line comment, search for the %
if (line[i] == '%')
in_multiline_comment = FALSE;
}
}
fputc('\n', destination);
}
rewind(destination);
return destination;
}
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)
{
FILE *file = preprocess_mif_file(mif);
rewind(file);
std::unordered_map<std::string, std::string> symbols = std::unordered_map<std::string, std::string>();
char buffer_in[BUFFER_MAX_SIZE];
bool in_content = false;
std::string last_line;
int line_number = 0;
int addr_radix = 0;
int data_radix = 0;
while (fgets(buffer_in, BUFFER_MAX_SIZE, file))
{
line_number++;
// Remove the newline.
trim_string(buffer_in, "\n");
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 %ld 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 %ld 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 %ld 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;
}
}
fclose(file);
}
/*
* 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 %ld:(%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 (%ld) than values (%ld).", l->count, v->count);
if (l->count > v->count)
error_message(SIMULATION_ERROR, 0, -1, "More lines (%ld) than values (%ld).", 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)
{
if ((num_pins + 1) > BUFFER_MAX_SIZE)
error_message(SIMULATION_ERROR, 0, -1, "Buffer overflow anticipated while writing vector for line %s.", line->name);
int j;
for (j = num_pins - 1; j >= 0 ; j--)
{
signed char value = get_line_pin_value(line, j, cycle);
if (value > 1){
error_message(SIMULATION_ERROR, 0, -1, "Invalid logic value of %ld read from line %s.", value, line->name);
}else if(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
{
// +1 for ceiling, +1 for null, +2 for "OX"
if ((num_pins/4 + 1 + 1 + 2) > BUFFER_MAX_SIZE)
error_message(SIMULATION_ERROR, 0, -1, "Buffer overflow anticipated while writing vector for line %s.", line->name);
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);
if (value > 1)
error_message(SIMULATION_ERROR, 0, -1, "Invalid logic value of %ld read from line %s.", value, line->name);
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(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",((char *)global_args.sim_directory),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 %ld 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 %ld 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 %ld 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 %ld 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;
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)
{
int unknown = FALSE;
int j;
for (j = line->number_of_pins - 1; j >= 0; j--)
{
if (get_line_pin_value(line, j, cycle) < 0)
{
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)
{
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);
}
}
//maria
//Free thread distribution
static void free_thread_distribution(thread_node_distribution *thread_distribution)
{
if(thread_distribution)
{
if(thread_distribution->thread_nodes)
{
for(int i = 0; i < thread_distribution->number_of_threads; i++)
{
if(thread_distribution->thread_nodes[i])
{
for (int j=0;j<thread_distribution->thread_nodes[i]->number_of_nodes;j++)
{
if(thread_distribution->thread_nodes[i]->nodes[j])
vtr::free(thread_distribution->thread_nodes[i]->nodes[j]);
}
if(thread_distribution->thread_nodes[i]->nodes)
vtr::free(thread_distribution->thread_nodes[i]->nodes);
vtr::free(thread_distribution->thread_nodes[i]);
}
}
vtr::free(thread_distribution->thread_nodes);
}
vtr::free(thread_distribution);
}
}
/*
* 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*/)
{
if(configuration.list_of_file_names.size() == 0)
printf("%s:\n", (char*)global_args.blif_file);
else
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();
int 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.
int 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;
}