blob: 55d89129330dabe50ad25d80d766776e58720738 [file] [log] [blame] [edit]
/*
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"
#ifndef max
#define max(a,b) (((a) > (b))? (a) : (b))
#define min(a,b) ((a) > (b)? (b) : (a))
#endif
/*
* Performs simulation.
*/
void simulate_netlist(netlist_t *netlist)
{
printf("Beginning simulation.\n"); fflush(stdout);
// Create and verify the lines.
lines_t *input_lines = create_lines(netlist, INPUT);
if (!verify_lines(input_lines))
error_message(SIMULATION_ERROR, 0, -1, "Input lines could not be assigned.");
lines_t *output_lines = create_lines(netlist, OUTPUT);
if (!verify_lines(output_lines))
error_message(SIMULATION_ERROR, 0, -1, "Output lines could not be assigned.");
// Open the output vector file.
FILE *out = fopen(OUTPUT_VECTOR_FILE_NAME, "w");
if (!out)
error_message(SIMULATION_ERROR, 0, -1, "Could not open output vector file.");
// Open the input vector file.
FILE *in_out = fopen( INPUT_VECTOR_FILE_NAME, "w");
if (!in_out)
error_message(SIMULATION_ERROR, 0, -1, "Could not open input vector file.");
// Open the modelsim vector file.
FILE *modelsim_out = fopen("test.do", "w");
if (!modelsim_out)
error_message(SIMULATION_ERROR, 0, -1, "Could not open modelsim output file.");
FILE *in = NULL;
int num_vectors;
// Passed via the -t option.
char *input_vector_file = global_args.sim_vector_input_file;
// Input vectors can either come from a file or be randomly generated.
if (input_vector_file)
{
in = fopen(input_vector_file, "r");
if (!in)
error_message(SIMULATION_ERROR, 0, -1, "Could not open vector input file: %s", input_vector_file);
num_vectors = count_test_vectors(in);
// Read the vector headers and check to make sure they match the lines.
if (!verify_test_vector_headers(in, input_lines))
error_message(SIMULATION_ERROR, 0, -1, "Invalid vector header format in %s.", input_vector_file);
printf("Simulating %d existing vectors from \"%s\".\n", num_vectors, input_vector_file); fflush(stdout);
}
else
{
// Passed via the -g option.
num_vectors = global_args.sim_num_test_vectors;
printf("Simulating %d new vectors.\n", num_vectors); fflush(stdout);
}
// Determine which edge(s) we are outputting.
int output_edge;
if (global_args.sim_output_both_edges ) output_edge = -1; // Both edges
else if (global_args.sim_output_rising_edge) output_edge = 1; // Rising edge only
else output_edge = 0; // Falling edge only
if (!num_vectors)
{
error_message(SIMULATION_ERROR, 0, -1, "No vectors to simulate.");
}
else
{
printf("\n");
int progress_bar_position = -1;
const int progress_bar_length = 50;
double total_time = 0; // Includes I/O
double simulation_time = 0; // Does not include I/O
stages *stages = 0;
// Parse -L and -H options containing lists of pins to hold high or low during random vector generation.
pin_names *hold_high = parse_pin_name_list(global_args.sim_hold_high);
pin_names *hold_low = parse_pin_name_list(global_args.sim_hold_low);
hashtable_t *hold_high_index = index_pin_name_list(hold_high);
hashtable_t *hold_low_index = index_pin_name_list(hold_low);
/*
* Simulation is done in "waves" of SIM_WAVE_LENGTH cycles at a time.
* Every second cycle gets a input new vector.
*/
int num_cycles = num_vectors * 2;
int num_waves = ceil(num_cycles / (double)SIM_WAVE_LENGTH);
int wave;
test_vector *v = 0;
for (wave = 0; wave < num_waves; wave++)
{
double wave_start_time = wall_time();
int cycle_offset = SIM_WAVE_LENGTH * wave;
int wave_length = (wave < (num_waves-1))?SIM_WAVE_LENGTH:(num_cycles - cycle_offset);
// Assign vectors to lines, either by reading or generating them.
// Every second cycle gets a new vector.
int cycle;
for (cycle = cycle_offset; cycle < cycle_offset + wave_length; cycle++)
{
if (is_even_cycle(cycle))
{
if (input_vector_file)
{
char buffer[BUFFER_MAX_SIZE];
if (!get_next_vector(in, buffer))
error_message(SIMULATION_ERROR, 0, -1, "Could not read next vector.");
v = parse_test_vector(buffer);
}
else
{
v = generate_random_test_vector(input_lines, cycle, hold_high_index, hold_low_index);
}
}
add_test_vector_to_lines(v, input_lines, cycle);
if (!is_even_cycle(cycle))
free_test_vector(v);
}
// Record the input vectors we are using.
write_wave_to_file(input_lines, in_out, cycle_offset, wave_length, 1);
// Write ModelSim script.
write_wave_to_modelsim_file(netlist, input_lines, modelsim_out, cycle_offset, wave_length);
double simulation_start_time = wall_time();
// Perform simulation
for (cycle = cycle_offset; cycle < cycle_offset + wave_length; cycle++)
{
//original_simulate_cycle(netlist, cycle);
if (cycle)
{
simulate_cycle(cycle, stages);
}
else
{
// The first cycle produces the stages, and adds additional
// lines as specified by the -p option.
pin_names *p = parse_pin_name_list(global_args.sim_additional_pins);
stages = simulate_first_cycle(netlist, cycle, p, output_lines);
free_pin_name_list(p);
// Make sure the output lines are still OK after adding custom lines.
if (!verify_lines(output_lines))
error_message(SIMULATION_ERROR, 0, -1,
"Problem detected with the output lines after the first cycle.");
}
}
simulation_time += wall_time() - simulation_start_time;
// Write the result of this wave to the output vector file.
write_wave_to_file(output_lines, out, cycle_offset, wave_length, output_edge);
total_time += wall_time() - wave_start_time;
// Print netlist-specific statistics.
if (!cycle_offset)
print_netlist_stats(stages, num_vectors);
// Delay drawing of the progress bar until the second wave to improve the accuracy of the ETA.
if ((num_waves == 1) || cycle_offset)
progress_bar_position = print_progress_bar(
cycle/(double)num_cycles, progress_bar_position, progress_bar_length, total_time);
}
free_pin_name_list(hold_high);
free_pin_name_list(hold_low);
hold_high_index->destroy_free_items(hold_high_index);
hold_low_index ->destroy_free_items(hold_low_index);
fflush(out);
fprintf(modelsim_out, "run %d\n", 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, num_vectors))
printf("Vector file \"%s\" matches output\n", output_vector_file);
else
error_message(SIMULATION_ERROR, 0, -1, "Vector files differ.");
printf("\n");
}
// Print statistics.
print_simulation_stats(stages, num_vectors, total_time, simulation_time);
free_stages(stages);
}
free_lines(output_lines);
free_lines(input_lines);
fclose(modelsim_out);
fclose(in_out);
if (input_vector_file)
fclose(in);
fclose(out);
}
/*
* This simulates a single cycle using the stages generated
* during the first cycle. Simulates in parallel if OpenMP is enabled.
*
* OpenMP 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.
*/
void simulate_cycle(int cycle, stages *s)
{
#ifdef _OPENMP
// -1 for cycle 0, -1 for the last cycle in the wave.
const int test_cycles = SIM_WAVE_LENGTH - 2;
const int st_length = test_cycles/2;
const int st_start = 1;
const int st_end = st_start + (st_length-1);
const int pt_length = test_cycles - st_length;
const int pt_start = st_end + 1;
const int pt_end = pt_start + (pt_length-1);
if (test_cycles < 2)
error_message(SIMULATION_ERROR, -1, -1, "SIM_WAVE_LENGTH is too small.");
// Range of cycles over which to test the sequential run times of each stage.
char sequential_test = (cycle >= st_start && cycle <= st_end);
// Range of cycles over which to test the parallel run times of each stage.
char parallel_test = (cycle >= pt_start && cycle <= pt_end);
#endif
int i;
for(i = 0; i < s->count; i++)
{
int j;
#ifdef _OPENMP
double time = 0.0;
if (sequential_test || parallel_test)
time = wall_time();
// Compute in parallel if we are profiling or if this stage is known to be faster in parallel.
char compute_in_parallel = parallel_test
|| (!sequential_test && !parallel_test && s->sequential_times[i] > s->parallel_times[i]);
if (compute_in_parallel)
{ // Compute the stage in parallel.
#pragma omp parallel for schedule(static)
for (j = 0; j < s->counts[i]; j++)
compute_and_store_value(s->stages[i][j], cycle);
}
else
{
#endif
// Compute the stage sequentially.
for (j = 0; j < s->counts[i]; j++)
compute_and_store_value(s->stages[i][j], cycle);
#ifdef _OPENMP
}
if (sequential_test)
{
// Take the minimum sequential time.
time = wall_time() - time;
if (s->sequential_times[i] == 0 || time < s->sequential_times[i])
s->sequential_times[i] = time;
}
else if (parallel_test)
{
// Take the minimum parallel time.
time = wall_time() - time;
if (s->parallel_times[i] == 0 || time < s->parallel_times[i])
s->parallel_times[i] = time;
}
if (cycle == pt_end + 1)
{
//printf("%.10f\t %.10f\t %.10f\t %d\t %d\t %f\n", s->sequential_times[i], s->parallel_times[i], s->sequential_times[i]/s->parallel_times[i], s->counts[i], compute_in_parallel, s->num_children[i]/(double)s->counts[i]);
//printf("%d %d %d %d %d %d\n", st_length, pt_length, st_start, st_end, pt_start, pt_end);
// Record the number of nodes in parallelizable stages.
if (compute_in_parallel)
s->num_parallel_nodes += s->counts[i];
}
#endif
}
}
/*
* 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.
*/
stages *simulate_first_cycle(netlist_t *netlist, int cycle, pin_names *p, lines_t *l)
{
queue_t *queue = create_queue();
// Enqueue top input nodes
int i;
for (i = 0; i < netlist->num_top_input_nodes; i++)
enqueue_node_if_ready(queue,netlist->top_input_nodes[i],cycle);
// 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++)
enqueue_node_if_ready(queue,constant_nodes[i],cycle);
nnode_t **ordered_nodes = 0;
int num_ordered_nodes = 0;
nnode_t *node;
while ((node = queue->remove(queue)))
{
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, p, 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* node = children[i];
if (!node->in_queue && is_node_ready(node, cycle) && !is_node_complete(node, cycle))
{
node->in_queue = TRUE;
queue->add(queue,node);
}
}
free(children);
node->in_queue = FALSE;
// Add the node to the ordered nodes array.
ordered_nodes = realloc(ordered_nodes, sizeof(nnode_t *) * (num_ordered_nodes + 1));
ordered_nodes[num_ordered_nodes++] = node;
}
queue->destroy(queue);
// Reorganise the ordered nodes into stages for parallel computation.
stages *s = stage_ordered_nodes(ordered_nodes, num_ordered_nodes);
free(ordered_nodes);
return s;
}
/*
* Puts the ordered nodes in stages, each of which can be computed in parallel.
*/
stages *stage_ordered_nodes(nnode_t **ordered_nodes, int num_ordered_nodes) {
stages *s = malloc(sizeof(stages));
s->stages = calloc(1,sizeof(nnode_t**));
s->counts = calloc(1,sizeof(int));
s->num_children = calloc(1,sizeof(int));
s->count = 1;
s->num_connections = 0;
s->num_nodes = num_ordered_nodes;
s->num_parallel_nodes = 0;
const int index_table_size = (num_ordered_nodes/100)+10;
// Hash tables index the nodes in the current stage, as well as their children.
hashtable_t *stage_children = create_hashtable(index_table_size);
hashtable_t *stage_nodes = create_hashtable(index_table_size);
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.
int is_child_of_stage = stage_children->get(stage_children, node, sizeof(nnode_t*))?1:0;
// Determine if any node in the current stage is a child of this node.
int is_stage_child_of = FALSE;
int j;
if (!is_child_of_stage)
for (j = 0; j < num_children; j++)
if ((is_stage_child_of = stage_nodes->get(stage_nodes, children[j], sizeof(nnode_t*))?1:0))
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 = realloc(s->stages, sizeof(nnode_t**) * (s->count+1));
s->counts = realloc(s->counts, sizeof(int) * (s->count+1));
s->num_children = 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->destroy(stage_children);
stage_nodes ->destroy(stage_nodes);
stage_children = create_hashtable(index_table_size);
stage_nodes = create_hashtable(index_table_size);
}
// Add the node to the current stage.
s->stages[stage] = realloc(s->stages[stage],sizeof(nnode_t*) * (s->counts[stage]+1));
s->stages[stage][s->counts[stage]++] = node;
// Index the node.
stage_nodes->add(stage_nodes, node, sizeof(nnode_t*), node);
// Index its children.
for (j = 0; j < num_children; j++)
stage_children->add(stage_children, children[j], sizeof(nnode_t*), children[j]);
// Record the number of children for computing the degree.
s->num_connections += num_children;
s->num_children[stage] += num_children;
free(children);
}
stage_children->destroy(stage_children);
stage_nodes ->destroy(stage_nodes);
s->sequential_times = calloc(s->count, sizeof(double));
s->parallel_times = calloc(s->count, sizeof(double));
return s;
}
/*
* Given a node, this function will simulate that node's new outputs,
* and updates those pins.
*/
void compute_and_store_value(nnode_t *node, int cycle)
{
update_undriven_input_pins(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: // &&
{
oassert(node->num_output_pins == 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:
{ // ||
oassert(node->num_output_pins == 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:
{ // !&&
oassert(node->num_output_pins == 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: // !|
{
oassert(node->num_output_pins == 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
{
oassert(node->num_input_port_sizes == 3);
oassert(node->num_output_port_sizes == 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
{
oassert(node->num_input_port_sizes == 3);
oassert(node->num_output_port_sizes == 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
{
oassert(node->num_input_port_sizes == 3);
oassert(node->num_output_port_sizes == 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
{
oassert(node->num_input_port_sizes == 3);
oassert(node->num_output_port_sizes == 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: // ^
{
oassert(node->num_output_pins == 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: // !^
{
oassert(node->num_output_pins == 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:
{
oassert(node->num_input_pins == 1);
oassert(node->num_output_pins == 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:
{
int i;
for (i = 0; i < node->num_output_pins; i++)
//toggle according to ratio
update_pin_value(node->output_pins[i], is_even_cycle(cycle/node->ratio)?0:1, cycle);
break;
}
case GND_NODE:
oassert(node->num_output_pins == 1);
update_pin_value(node->output_pins[0], 0, cycle);
break;
case VCC_NODE:
oassert(node->num_output_pins == 1);
update_pin_value(node->output_pins[0], 1, cycle);
break;
case PAD_NODE:
oassert(node->num_output_pins == 1);
update_pin_value(node->output_pins[0], 0, cycle);
break;
case INPUT_NODE:
break;
case OUTPUT_NODE:
oassert(node->num_output_pins == 1);
oassert(node->num_input_pins == 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 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;
}
// Record coverage on any output pins that have changed.
{
int i;
for (i = 0; i < node->num_output_pins; i++)
if(get_pin_value(node->output_pins[i],cycle-1) != get_pin_value(node->output_pins[i],cycle))
node->output_pins[i]->coverage++;
}
}
/*
* 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.
*/
void update_undriven_input_pins(nnode_t *node, int cycle)
{
int i;
for (i = 0; i < node->num_undriven_pins; i++)
{
npin_t *pin = node->undriven_pins[i];
update_pin_value(pin, global_args.sim_initial_value, 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++)
{
npin_t *pin = node->input_pins[i];
if (get_pin_cycle(pin) < cycle-1)
#ifdef _OPENMP
// Can't have multiple threads trying to error out at the same time.
#pragma omp critical
#endif
{
char *node_name = node->name;
char *pin_name = pin->name;
// 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, pin_name, root?root->name:"N/A"
);
}
}
}
}
/*
* Flags any inputs pins which are undriven and have
* not already been flagged.
*/
void flag_undriven_input_pins(nnode_t *node)
{
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 = 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);
}
}
}
}
/*
* Gets the number of nodes whose output pins have been sufficiently covered.
*/
int get_num_covered_nodes(stages *s)
{
int covered_nodes = 0;
int i;
for(i = 0; i < s->count; i++)
{
int j;
for (j = 0; j < s->counts[i]; j++)
{ /*
* To count as being covered, every pin should resolve, and
* make at least one transition from one binary value to another
* and back. (That's three transitions total.)
*/
nnode_t *node = s->stages[i][j];
int k;
int covered = TRUE;
for (k = 0; k < node->num_output_pins; k++)
{
if (node->output_pins[k]->coverage < 3)
{
covered = FALSE;
break;
}
}
if (covered)
covered_nodes++;
}
}
return covered_nodes;
}
/*
* Enqueues the node in the given queue if is_node_ready returns TRUE.
*/
int enqueue_node_if_ready(queue_t* queue, nnode_t* node, int cycle)
{
if (is_node_ready(node, cycle))
{
node->in_queue = TRUE;
queue->add(queue, node);
return TRUE;
}
else
{
return FALSE;
}
}
/*
* Determines if the given node has been simulated for the given cycle.
*/
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;
}
/*
* Checks to see if the node is ready to be simulated for the given cycle.
*/
int is_node_ready(nnode_t* node, int cycle)
{
if (!cycle)
{
flag_undriven_input_pins(node);
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)
|| (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")
|| !strcmp(pin->mapping, "we") || !strcmp(pin->mapping, "we1") || !strcmp(pin->mapping, "we2")
)
{
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;
}
/*
* Returns the ration of a clock node.
* */
int get_clock_ratio(nnode_t *node)
{
return node->ratio;
}
/*
* Changes the ratio of a clock node
*/
void set_clock_ratio(int rat, nnode_t *node)
{
//change the value only for clocks
if(node->type != CLOCK_NODE)
return;
node->ratio = rat;
}
/*
* 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
);
free(net_name);
free(pin_name);
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 = 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.
*/
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
);
free(net_name);
free(pin_name);
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 = 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, "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
);
free(net_name);
free(pin_name);
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 = realloc(children, sizeof(nnode_t*) * (count + 1));
children[count++] = child_node;
}
}
}
}
*num_children = count;
return children;
}
/*
* Updates the value of a pin and its cycle. Pins should be updated using
* only this function.
*
* Initializes the pin if need be.
*/
void update_pin_value(npin_t *pin, signed char value, int cycle)
{
if (!pin->values)
initialize_pin(pin);
pin->values[get_values_offset(cycle)] = value;
set_pin_cycle(pin, 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 || cycle < 0){
/* If the pin's node has an initial value, then use it.
Otherwise use the global initial value
Need to make sure pin->node isn't NULL (e.g. for a dummy node) */
if(pin->node && pin->node->has_initial_value) {
return pin->node->initial_value;
}
return global_args.sim_initial_value;
}
return pin->values[get_values_offset(cycle)];
}
/*
* Calculates the index in the values array for the given cycle.
*/
inline int get_values_offset(int cycle)
{
return (((cycle) + (SIM_WAVE_LENGTH)) % (SIM_WAVE_LENGTH));
}
/*
* Gets the cycle of the given pin
*/
inline int get_pin_cycle(npin_t *pin)
{
if (!pin->cycle)
return -1;
else
return *(pin->cycle);
}
/*
* Sets the cycle of the given pin.
*
* Only called from update_pin_value.
*/
inline void set_pin_cycle(npin_t *pin, int cycle)
{
*(pin->cycle) = cycle;
}
/*
* Returns FALSE if the cycle is odd.
*/
int is_even_cycle(int cycle)
{
return !((cycle + 2) % 2);
}
/*
* 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.
*/
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->cycle || pin->values)
return;
if (pin->net)
{
pin->values = pin->net->values;
pin->cycle = &(pin->net->cycle);
int i;
for (i = 0; i < pin->net->num_fanout_pins; i++)
{
npin_t *fanout_pin = pin->net->fanout_pins[i];
if (fanout_pin)
{
fanout_pin->values = pin->net->values;
fanout_pin->cycle = &(pin->net->cycle);
}
}
}
else
{
pin->values = malloc(SIM_WAVE_LENGTH * sizeof(signed char));
pin->cycle = malloc(sizeof(int));
}
int i;
for (i = 0; i < SIM_WAVE_LENGTH; i++){
/* If the pin's node has an initial value, then use it.
Otherwise use the global initial value
Need to make sure pin->node isn't NULL (e.g. for a dummy node) */
if(pin->node && pin->node->has_initial_value)
pin->values[i] = pin->node->initial_value;
else
pin->values[i] = global_args.sim_initial_value;
}
set_pin_cycle(pin, -1);
}
/*
* Returns FALSE if the node is not a clock.
*/
inline int is_clock_node(nnode_t *node)
{
return (
(node->type == CLOCK_NODE)
|| !strcmp(node->name,"top^clk") // Strictly for memories.
);
}
/*
* Returns TRUE if the pin's value for this
* cycle is 1 and the value for the previous cycle
* was not 1. Otherwise returns FALSE.
*/
int is_posedge(npin_t *pin, int cycle)
{
if (get_pin_value(pin,cycle) == 1 && get_pin_value(pin,cycle-1) != 1)
return TRUE;
else
return FALSE;
}
/*
* Computes a node of type FF_NODE for the given cycle.
*/
void compute_flipflop_node(nnode_t *node, int cycle)
{
oassert(node->num_output_pins == 1);
oassert(node->num_input_pins == 2);
npin_t *D_pin = node->input_pins[0];
npin_t *clock_pin = node->input_pins[1];
npin_t *output_pin = node->output_pins[0];
// Rising edge: update the flip-flop from the input value of the previous cycle.
if (is_posedge(clock_pin, cycle))
update_pin_value(output_pin, get_pin_value(D_pin, cycle-1), cycle);
// Falling edge: maintain the flip-flop value.
else
update_pin_value(output_pin, get_pin_value(output_pin,cycle-1), cycle);
}
/*
* Computes a node of type MUX_2 for the given cycle.
*/
void compute_mux_2_node(nnode_t *node, int cycle)
{
oassert(node->num_output_pins == 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.
void compute_hard_ip_node(nnode_t *node, int cycle)
{
oassert(node->input_port_sizes[0] > 0);
oassert(node->output_port_sizes[0] > 0);
int *input_pins = malloc(sizeof(int)*node->num_input_pins);
int *output_pins = malloc(sizeof(int)*node->num_output_pins);
if (!node->simulate_block_cycle)
{
char *filename = malloc(sizeof(char)*strlen(node->name));
if (!index(node->name, '.'))
error_message(SIMULATION_ERROR, 0, -1,
"Couldn't extract the name of a shared library for hard-block simulation");
snprintf(filename, sizeof(char)*strlen(node->name), "%s.so", index(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;
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);
free(input_pins);
free(output_pins);
}
/*
* Computes the given multiply node for the given cycle.
*/
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 = malloc(sizeof(int)*node->input_port_sizes[0]);
int *b = 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);
free(result);
free(a);
free(b);
}
}
// TODO: Needs to be verified.
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 (found) update_pin_value(node->output_pins[0], 1, cycle);
else update_pin_value(node->output_pins[0], 0, 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!
*/
int *multiply_arrays(int *a, int a_length, int *b, int b_length)
{
int result_size = a_length + b_length;
int *result = 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
*/
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 = malloc(sizeof(int)*node->input_port_sizes[0]);
int *b = malloc(sizeof(int)*node->input_port_sizes[1]);
int *c = 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);
free(result);
free(a);
free(b);
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!
*/
int *add_arrays(int *a, int a_length, int *b, int b_length, int *c, int c_length, int flag)
{
int result_size = max(a_length , b_length) + 1;
int *result = calloc(sizeof(int), result_size);
int i;
int temp_carry_in;
//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]);
}
temp_carry_in = result[1];
if(result_size > 2){
for(i = 1; i < 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(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(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
*/
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 = malloc(sizeof(int)*node->input_port_sizes[0]);
int *c = 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);
free(result);
free(a);
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!
*/
int *unary_sub_arrays(int *a, int a_length, int *c, int c_length)
{
int result_size = a_length + 1;
int *result = 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.
*/
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);
}
/*
* Computes single port memory.
*/
void compute_single_port_memory(nnode_t *node, int cycle)
{
sp_ram_signals *signals = get_sp_ram_signals(node);
int posedge = is_posedge(signals->clk, cycle);
if (!node->memory_data)
instantiate_memory(node, signals->data->count, signals->addr->count);
// On the rising edge, write the memory.
if (posedge)
{
int we = get_pin_value(signals->we, cycle - 1);
long address = compute_memory_address(signals->addr, cycle - 1);
char address_ok = (address != -1)?1:0;
int i;
for (i = 0; i < signals->data->count; i++)
{
// Compute which bit we are addressing.
long bit_address = address_ok?(i + (address * signals->data->count)):-1;
// If write is enabled, copy the input to memory.
if (address_ok && we)
node->memory_data[bit_address] = get_pin_value(signals->data->pins[i],cycle-1);
}
}
// Read data from the memory.
{
long address = compute_memory_address(signals->addr, cycle);
char address_ok = (address != -1)?1:0;
int i;
for (i = 0; i < signals->data->count; i++)
{
// Compute which bit we are addressing.
long bit_address = address_ok?(i + (address * signals->data->count)):-1;
// Update the output.
if (address_ok)
update_pin_value(signals->out->pins[i], node->memory_data[bit_address], cycle);
else
update_pin_value(signals->out->pins[i], -1, cycle);
}
}
free_sp_ram_signals(signals);
}
/*
* Computes dual port memory.
*/
void compute_dual_port_memory(nnode_t *node, int cycle)
{
dp_ram_signals *signals = get_dp_ram_signals(node);
int posedge = is_posedge(signals->clk, cycle);
if (!node->memory_data)
instantiate_memory(node, signals->data2->count, signals->addr2->count);
// On the rising edge, we write the memory.
if (posedge)
{
int we1 = get_pin_value(signals->we1, cycle - 1);
int we2 = get_pin_value(signals->we2, cycle - 1);
long address1 = compute_memory_address(signals->addr1, cycle - 1);
long address2 = compute_memory_address(signals->addr2, cycle - 1);
char address1_ok = (address1 != -1)?1:0;
char address2_ok = (address2 != -1)?1:0;
int i;
for (i = 0; i < signals->data1->count; i++)
{
// Compute which bit we are addressing.
long bit_address1 = address1_ok?(i + (address1 * signals->data1->count)):-1;
long bit_address2 = address2_ok?(i + (address2 * signals->data2->count)):-1;
// Write to the memory
if (address1_ok && we1)
node->memory_data[bit_address1] = get_pin_value(signals->data1->pins[i], cycle-1);
if (address2_ok && we2)
node->memory_data[bit_address2] = get_pin_value(signals->data2->pins[i], cycle-1);
}
}
// Read data from memory.
{
long address1 = compute_memory_address(signals->addr1, cycle);
long address2 = compute_memory_address(signals->addr2, cycle);
char address1_ok = (address1 != -1)?1:0;
char address2_ok = (address2 != -1)?1:0;
int i;
for (i = 0; i < signals->data1->count; i++)
{
// Compute which bit we are addressing.
long bit_address1 = address1_ok?(i + (address1 * signals->data1->count)):-1;
long bit_address2 = address2_ok?(i + (address2 * signals->data2->count)):-1;
// Read the memory bit
if (address1_ok)
update_pin_value(signals->out1->pins[i], node->memory_data[bit_address1], cycle);
else
update_pin_value(signals->out1->pins[i], -1, cycle);
if (address2_ok)
update_pin_value(signals->out2->pins[i], node->memory_data[bit_address2], cycle);
else
update_pin_value(signals->out2->pins[i], -1, cycle);
}
}
free_dp_ram_signals(signals);
}
/*
* Calculates the memory address. Returns -1 if the address is unknown.
*/
long compute_memory_address(signal_list_t *addr, int cycle)
{
long address = 0;
int i;
for (i = 0; i < addr->count; i++)
{
// If any address pins are x's, write x's we return -1.
if (get_pin_value(addr->pins[i],cycle) < 0)
return -1;
address += get_pin_value(addr->pins[i],cycle) << (i);
}
return address;
}
/*
* Initialises memory using a memory information file (mif). If not
* file is found, it is initialised to x's.
*/
void instantiate_memory(nnode_t *node, int data_width, int addr_width)
{
long max_address = 1 << addr_width;
node->memory_data = malloc(sizeof(signed char) * max_address * data_width);
// Initialise the memory to -1.
int i;
for (i = 0; i < max_address * data_width; i++)
node->memory_data[i] = -1;
char *filename = get_mif_filename(node);
FILE *mif = fopen(filename, "r");
if (!mif)
{
printf("MIF %s (%dx%d) not found. \n", filename, data_width, addr_width);
}
else
{
assign_memory_from_mif_file(mif, filename, data_width, addr_width, node->memory_data);
fclose(mif);
}
free(filename);
}
/*
* Removes white space (except new lines) and comments from
* the given mif file and returns the resulting temporary file.
*/
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))
{
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;
}
int parse_mif_radix(char *radix)
{
if (radix)
{
if (!strcmp(radix, "HEX"))
return 16;
else if (!strcmp(radix, "DEC"))
return 10;
else if (!strcmp(radix, "OCT"))
return 8;
else if (!strcmp(radix, "BIN"))
return 2;
else
return 0;
}
else
{
return 0;
}
}
void assign_memory_from_mif_file(FILE *mif, char *filename, int width, long depth, signed char *memory)
{
FILE *file = preprocess_mif_file(mif);
rewind(file);
hashtable_t *symbols = create_hashtable(100000);
char buffer[BUFFER_MAX_SIZE];
int in_content = FALSE;
char *last_line = NULL;
int line_number = 0;
int addr_radix = 0;
int data_radix = 0;
while (fgets(buffer, BUFFER_MAX_SIZE, file))
{
line_number++;
// Remove the newline.
trim_string(buffer, "\n");
// Only process lines which are not empty.
if (strlen(buffer))
{
char *line = strdup(buffer);
// MIF files are case insensitive
string_to_upper(line);
// The content section of the file contains address:value; assignments.
if (in_content)
{
// Parse at the :
char *token = strtok(line, ":");
if (strlen(token))
{
// END; signifies the end of the file.
if(!strcmp(buffer, "END;"))
break;
// The part before the : is the address.
char *address_string = token;
token = strtok(NULL, ";");
// The reset (before the ;) is the data_value.
char *data_string = token;
if (token)
{
// Make sure the address and value are valid strings of the specified radix.
if (!is_string_of_radix(address_string, addr_radix))
error_message(SIMULATION_ERROR, line_number, -1, "%s: address %s is not a base %d string.", filename, address_string, addr_radix);
if (!is_string_of_radix(data_string, data_radix))
error_message(SIMULATION_ERROR, line_number, -1, "%s: data string %s is not a base %d string.", filename, data_string, data_radix);
char *binary_data = convert_string_of_radix_to_bit_string(data_string, data_radix, width);
long long address = convert_string_of_radix_to_long_long(address_string, addr_radix);
if (address >= depth)
error_message(SIMULATION_ERROR, line_number, -1, "%s: address %s is out of range.", filename, address_string);
// Calculate the offset of this memory location in bits.
long long offset = address * width;
// Write the parsed value string to the memory location.
long long i;
for (i = offset; i < offset + width; i++)
memory[i] = binary_data[i - offset] - '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(line, "=");
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->add(symbols, symbol, sizeof(char) * strlen(symbol), strdup(token));
else if(!strcmp(buffer, "CONTENT")) {}
// We found "CONTENT" followed on the next line by "BEGIN". That means we're at the end of the parameters.
else if(!strcmp(buffer, "BEGIN") && !strcmp(last_line, "CONTENT"))
{
// Sanity check parameters to make sure we have what we need.
// Verify the width parameter.
char *width_string = symbols->get(symbols, "WIDTH", sizeof(char) * 5);
int mif_width = atoi(width_string);
if (!width_string) error_message(SIMULATION_ERROR, -1, -1, "%s: MIF WIDTH parameter unspecified.", filename);
if (mif_width != width)
error_message(SIMULATION_ERROR, -1, -1,
"%s: MIF width mismatch: must be %d but %d was given", filename, width, mif_width);
// Verify the depth parameter.
char *depth_string = symbols->get(symbols, "DEPTH", sizeof(char) * 5);
int mif_depth = atoi(depth_string);
if (!depth_string) error_message(SIMULATION_ERROR, -1, -1, "%s: MIF DEPTH parameter unspecified.", filename);
if (mif_depth != depth)
error_message(SIMULATION_ERROR, -1, -1,
"%s: MIF depth mismatch: must be %d but %d was given", filename, depth, mif_depth);
// Parse the radix specifications and make sure they're OK.
addr_radix = parse_mif_radix(symbols->get(symbols, "ADDRESS_RADIX", sizeof(char) * 13));
data_radix = parse_mif_radix(symbols->get(symbols, "DATA_RADIX", sizeof(char) * 10));
if (!addr_radix)
error_message(SIMULATION_ERROR, -1, -1,
"%s: invalid or missing ADDRESS_RADIX: must specify DEC, HEX, OCT, or BIN", filename);
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, line);
}
}
else
{
error_message(SIMULATION_ERROR, line_number, -1, "%s: MIF syntax error: %s", filename, line);
}
free(line);
}
if (last_line)
free(last_line);
last_line = strdup(buffer);
}
}
if (last_line)
free(last_line);
symbols->destroy_free_items(symbols);
fclose(file);
}
/*
* Assigns the given node to its corresponding line in the given array of line.
* Assumes the line has already been created.
*/
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);
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.
*/
void insert_pin_into_line(npin_t *pin, int pin_number, line_t *line, int type)
{
// Allocate memory for the new pin.
line->pins = realloc(line->pins, sizeof(npin_t*) * (line->number_of_pins + 1));
line->pin_numbers = 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.
*/
lines_t *create_lines(netlist_t *netlist, int type)
{
lines_t *l = 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 (type == OUTPUT || !is_clock_node(node))
{
if (find_portname_in_lines(port_name, l) == -1)
{
line_t *line = create_line(port_name);
l->lines = realloc(l->lines, sizeof(line_t *)*(l->count + 1));
l->lines[l->count++] = line;
}
assign_node_to_line(node, l, type, 0);
}
free(port_name);
}
return l;
}
/*
* Creates a vector file header from the given lines,
* and writes it to the given file.
*/
void write_vector_headers(FILE *file, lines_t *l)
{
char* headers = generate_vector_header(l);
fprintf(file, "%s", headers);
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.
*/
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, "Failed to read vector headers.");
// Parse the header, checking each entity against the corresponding line.
char buffer [BUFFER_MAX_SIZE];
buffer[0] = '\0';
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, "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);
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.
*/
int verify_lines (lines_t *l)
{
int i;
for (i = 0; i < l->count; i++)
{
int j;
for (j = 0; j < l->lines[i]->number_of_pins; j++)
{
if (!l->lines[i]->pins[j])
{
warning_message(SIMULATION_ERROR, 0, -1, "A line %d:(%s) has a NULL pin. ", j, l->lines[i]->name);
return FALSE;
}
}
}
return TRUE;
}
/*
* Searches for a line with the given name in the lines. Returns the index
* or -1 if no such line was found.
*/
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
*/
line_t *create_line(char *name)
{
line_t *line = malloc(sizeof(line_t));
line->number_of_pins = 0;
line->pins = 0;
line->pin_numbers = 0;
line->type = -1;
line->name = malloc(sizeof(char)*(strlen(name)+1));
strcpy(line->name, name);
return line;
}
/*
* Generates the appropriate vector headers based on the given lines.
*/
char *generate_vector_header(lines_t *l)
{
char *header = 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, "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 = 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.
*/
void add_test_vector_to_lines(test_vector *v, lines_t *l, int cycle)
{
if (l->count < v->count)
error_message(SIMULATION_ERROR, 0, -1, "Fewer lines (%d) than values (%d).", l->count, v->count);
if (l->count > v->count)
error_message(SIMULATION_ERROR, 0, -1, "More lines (%d) than values (%d).", l->count, v->count);
int i;
for (i = 0; i < v->count; i++)
{
line_t *line = l->lines[i];
if (line->number_of_pins < 1)
error_message(SIMULATION_ERROR, 0, -1, "Found a line '%s' with no pins.", line->name);
int j;
for (j = 0; j < line->number_of_pins; j++)
{
if (j < v->counts[i]) update_pin_value(line->pins[j], v->values[i][j], cycle);
else update_pin_value(line->pins[j], 0, cycle);
}
}
}
/*
* Compares two test vectors for numerical and geometric identity. Returns FALSE if
* they are found to be different, and TRUE otherwise.
*/
int compare_test_vectors(test_vector *v1, test_vector *v2)
{
if (v1->count != v2->count)
{
warning_message(SIMULATION_ERROR, 0, -1, "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])
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 TRUE;
}
/*
* Parses the given line from a test vector file into a
* test_vector data structure.
*/
test_vector *parse_test_vector(char *buffer)
{
buffer = strdup(buffer);
test_vector *v = 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 = realloc(v->values, sizeof(signed char *) * (v->count + 1));
v->counts = 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] = 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] = 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);
}
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.
*/
test_vector *generate_random_test_vector(lines_t *l, int cycle, hashtable_t *hold_high_index, hashtable_t *hold_low_index)
{
test_vector *v = malloc(sizeof(test_vector));
v->values = 0;
v->counts = 0;
v->count = 0;
int i;
for (i = 0; i < l->count; i++)
{
v->values = realloc(v->values, sizeof(signed char *) * (v->count + 1));
v->counts = realloc(v->counts, sizeof(int) * (v->count + 1));
v->values[v->count] = 0;
v->counts[v->count] = 0;
int j;
for (j = 0; j < l->lines[i]->number_of_pins; j++)
{
char *name = l->lines[i]->name;
signed char value;
if (hold_high_index->count && hold_high_index->get(hold_high_index,name,sizeof(char)*strlen(name)))
{
if (!cycle) value = 0;
else value = 1;
}
else if (hold_low_index->count && hold_low_index->get(hold_low_index,name,sizeof(char)*strlen(name)))
{
if (!cycle) value = 1;
else value = 0;
}
else
{
// Passed via the -3 option.
if (global_args.sim_generate_three_valued_logic)
value = (rand() % 3) - 1;
else
value = (rand() % 2);
}
v->values[v->count] = 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.
*/
void write_wave_to_file(lines_t *l, FILE* file, int cycle_offset, int wave_length, int edge)
{
if (!cycle_offset)
write_vector_headers(file, l);
int cycle;
for (cycle = cycle_offset; cycle < (cycle_offset + wave_length); cycle++)
{
if (edge == -1 || (edge == 0 && is_even_cycle(cycle)) || (edge == 1 && !is_even_cycle(cycle)))
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.
*/
void write_vector_to_file(lines_t *l, FILE *file, int cycle)
{
char buffer[BUFFER_MAX_SIZE];
int i;
for (i = 0; i < l->count; i++)
{
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);
buffer[0] = 0;
int j;
int known_values = 0;
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 %d read from line %s.", value, line->name);
}
if (value < 0)
{
strcat(buffer, "x");
}
else
{ known_values++;
sprintf(buffer, "%s%d", buffer, value);
}
}
// If there are no known values, print a single capital X.
// (Only for testing. Breaks machine readability.)
//if (!known_values && num_pins > 1)
// 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);
sprintf(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 %d read from line %s.", value, line->name);
hex_digit += value << j % 4;
if (!(j % 4))
{
sprintf(buffer, "%s%X", buffer, hex_digit);
hex_digit = 0;
}
}
}
// 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);
}
fprintf(file, "\n");
}
/*
* Writes a wave of vectors to the given modelsim out file.
*/
void write_wave_to_modelsim_file(netlist_t *netlist, lines_t *l, FILE* modelsim_out, int cycle_offset, int wave_length)
{
if (!cycle_offset)
{
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);
free(port_name);
}
}
}
int cycle;
for (cycle = cycle_offset; cycle < (cycle_offset + wave_length); cycle ++)
{
if (!is_even_cycle(cycle))
write_vector_to_modelsim_file(l, modelsim_out, cycle);
}
}
/*
* Writes a vector to the given modelsim out file.
*/
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.
*/
int verify_output_vectors(char* output_vector_file, int num_vectors)
{
if (global_args.sim_output_both_edges)
num_vectors *= 2;
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.)
FILE *current_out = fopen(OUTPUT_VECTOR_FILE_NAME, "r");
if (!current_out) error_message(SIMULATION_ERROR, 0, -1, "Could not open output vector file.");
int cycle;
char buffer1[BUFFER_MAX_SIZE];
char buffer2[BUFFER_MAX_SIZE];
// Start at cycle -1 to check the headers.
for (cycle = -1; cycle < num_vectors; cycle++)
{
if (!get_next_vector(existing_out, buffer1))
{
error = TRUE;
warning_message(SIMULATION_ERROR, 0, -1,"Too few vectors in %s \n", output_vector_file);
break;
}
else if (!get_next_vector(current_out, buffer2))
{
error = TRUE;
warning_message(SIMULATION_ERROR, 0, -1,"Simulation produced fewer than %d vectors. \n", num_vectors);
break;
}
// The headers differ.
else if ((cycle == -1) && strcmp(buffer1,buffer2))
{
error = TRUE;
warning_message(SIMULATION_ERROR, 0, -1, "Vector headers do not match: \n"
"\t%s"
"in %s does not match\n"
"\t%s"
"in %s.\n\n",
buffer2, OUTPUT_VECTOR_FILE_NAME, buffer1, output_vector_file
);
break;
}
else
{
// Parse both vectors.
test_vector *v1 = parse_test_vector(buffer1);
test_vector *v2 = parse_test_vector(buffer2);
// Compare them and print an appropriate message if they differ.
if (!compare_test_vectors(v1,v2))
{
trim_string(buffer1, "\n\t");
trim_string(buffer2, "\n\t");
error = TRUE;
warning_message(SIMULATION_ERROR, 0, -1, "Vector %d mismatch:\n"
"\t%s in %s\n"
"\t%s in %s\n",
cycle, buffer2, OUTPUT_VECTOR_FILE_NAME, buffer1, output_vector_file
);
}
free_test_vector(v1);
free_test_vector(v2);
}
}
// If the file we're checking against is longer than the current output, print an appropriate warning.
if (!error && get_next_vector(existing_out, buffer1))
{
error = TRUE;
warning_message(SIMULATION_ERROR, 0, -1,"%s contains more than %d vectors.\n", output_vector_file, num_vectors);
}
fclose(existing_out);
fclose(current_out);
}
return !error;
}
/*
* Creates a hastable_t index of the given pin names list
* of the form pin name hashes to pin name array index.
*/
hashtable_t *index_pin_name_list(pin_names *list)
{
hashtable_t *index = create_hashtable(list->count * 2+1);
int i;
for (i = 0; i < list->count; i++)
{
int *id = malloc(sizeof(int));
*id = i;
index->add(index, list->pins[i], sizeof(char)*strlen(list->pins[i]), id);
}
return index;
}
/*
* Parses the given comma separated list into a
* pin_names struct. If the list is empty or null
* an empty struct is returned.
*/
pin_names *parse_pin_name_list(char *list)
{
pin_names *p = malloc(sizeof(pin_names));
p->pins = 0;
p->count = 0;
// Parse the list of additional pins passed via the -p option.
if (list)
{
char *pin_list = strdup(list);
char *token = strtok(pin_list, ",");
while (token)
{
p->pins = realloc(p->pins, sizeof(char *) * (p->count + 1));
p->pins[p->count++] = strdup(token);
token = strtok(NULL, ",");
}
free(pin_list);
}
return p;
}
/*
* 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).
*/
void add_additional_items_to_lines(nnode_t *node, pin_names *p, lines_t *l)
{
if (p->count)
{
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->count; k++)
{
if (strstr(pin->name, p->pins[k]))
{
add = TRUE;
break;
}
}
if (add) break;
}
if (pin->net && pin->net->name)
{
for (k = 0; k < p->count; k++)
{
if (strstr(pin->net->name, p->pins[k]))
{
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->count; k++)
{
if (strstr(node->name, p->pins[k]))
{
add = TRUE;
break;
}
}
}
if (add)
{
int single_pin = strchr(p->pins[k], '~')?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 = realloc(l->lines, sizeof(line_t *)*((l->count)+1));
l->lines[l->count++] = line;
}
assign_node_to_line(node, l, OUTPUT, single_pin);
free(port_name);
}
}
}
}
/*
* Parses the given (memory) node name into a corresponding
* mif file name.
*/
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 = strdup(filename);
return filename;
}
/*
* Trims characters in the given "chars" string
* from the end of the given string.
*/
void trim_string(char* string, char *chars)
{
if (string)
{
int length;
while((length = strlen(string)))
{ int trimmed = FALSE;
int i;
for (i = 0; i < strlen(chars); i++)
{
if (string[length-1] == chars[i])
{
trimmed = TRUE;
string[length-1] = '\0';
break;
}
}
if (!trimmed)
break;
}
}
}
/*
* Returns TRUE if the given line has a pin for
* the given cycle whose value is -1.
*/
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.
*/
char *vector_value_to_hex(signed char *value, int length)
{
char *tmp;
char *string = 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 = malloc(sizeof(char) * ((length/4 + 1) + 1));
sprintf(hex_string, "%X ", (unsigned int)strtol(string, &tmp, 2));
free(string);
return hex_string;
}
/*
* Counts the number of vectors in the given file.
*/
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;
}
/*
* A given line is a vector if it contains one or more
* non-whitespace characters and does not being with a #.
*/
int is_vector(char *buffer)
{
char *line = strdup(buffer);
trim_string(line," \t\r\n");
if (line[0] != '#' && strlen(line))
{
free(line);
return TRUE;
}
else
{
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.
*/
int get_next_vector(FILE *file, char *buffer)
{
while (fgets(buffer, BUFFER_MAX_SIZE, file))
if (is_vector(buffer))
return TRUE;
return FALSE;
}
/*
* Free each element in lines[] and the array itself
*/
void free_lines(lines_t *l)
{
int i;
for (i = 0; i < l->count; i++)
{
free(l->lines[i]->name);
free(l->lines[i]->pins);
free(l->lines[i]);
}
free(l->lines);
free(l);
}
/*
* Free stages.
*/
void free_stages(stages *s)
{
while (s->count--)
free(s->stages[s->count]);
free(s->stages);
free(s->counts);
free(s);
}
/*
* Free the given test_vector.
*/
void free_test_vector(test_vector* v)
{
while (v->count--)
free(v->values[v->count]);
free(v->values);
free(v->counts);
free(v);
}
/*
* Frees pin_names struct.
*/
void free_pin_name_list(pin_names *p)
{
while (p->count--)
free(p->pins[p->count]);
free(p->pins);
free(p);
}
/*
* Prints/updates an ASCII progress bar of length "length" to position length * completion
* from previous position "position". Updates ETA based on the elapsed time "time".
* Returns the new position. If the position is unchanged the bar is not redrawn.
*
* Call with position = -1 to draw for the first time. Returns the new
* position, calculated based on completion.
*/
int print_progress_bar(double completion, int position, int length, double time)
{
if (position == -1 || ((int)(completion * length)) > position)
{
printf("%3.0f%%|", completion * (double)100);
position = completion * length;
int i;
for (i = 0; i < position; i++)
printf("=");
printf(">");
for (; i < length; i++)
printf("-");
if (completion < 1.0)
{
printf("| Remaining: ");
double remaining_time = time/(double)completion - time;
print_time(remaining_time);
}
else
{
printf("| Total time: ");
print_time(time);
}
printf(" \r");
if (position == length)
printf("\n");
fflush(stdout);
}
return position;
}
/*
* Prints information about the netlist we are simulating.
*/
void print_netlist_stats(stages *stages, int num_vectors)
{
printf("%s:\n", get_circuit_filename());
printf(" Nodes: %d\n", stages->num_nodes);
printf(" Connections: %d\n", stages->num_connections);
printf(" Degree: %3.2f\n", stages->num_connections/(float)stages->num_nodes);
printf(" Stages: %d\n", stages->count);
#ifdef _OPENMP
printf(" Parallel nodes: %d (%4.1f%%)\n", stages->num_parallel_nodes, (stages->num_parallel_nodes/(double)stages->num_nodes) * 100);
#endif
printf("\n");
}
/*
* Prints statistics. (Coverage and times.)
*/
void print_simulation_stats(stages *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 the time in appropriate units.
*/
void print_time(double time)
{
if (time > 24*3600) printf("%.1fd", time/(24*3600.0));
else if (time > 3600) printf("%.1fh", time/3600.0);
else if (time > 60) printf("%.1fm", time/60.0);
else if (time > 1) printf("%.1fs", time);
else printf("%.1fms", time*1000);
}
/*
* Prints n ancestors of the given node, complete
* with their parents, ids, etc.
*/
void print_ancestry(nnode_t *bottom_node, int n)
{
if (!n) n = 10;
queue_t *queue = create_queue();
queue->add(queue, bottom_node);
nnode_t *node;
printf( " ------------\n");
printf( " BACKTRACE\n");
printf( " ------------\n");
while (n-- && (node = queue->remove(queue)))
{
char *name = get_pin_name(node->name);
printf(" %s (%ld):\n", name, node->unique_id);
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 *node = net->driver_pin->node;
queue->add(queue, node);
char *name = get_pin_name(node->name);
printf("\t%s %s (%ld)\n", pin->mapping, name, node->unique_id);fflush(stdout);
free(name);
}
/*int count = 0;
{
printf( " ------------\n");
printf( " CHILDREN \n");
printf( " ------------\n");
printf( " O: %d\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);
free(name);
}
else
{
printf(" Null node %d\n", ++count);fflush(stdout);
}
}
else
{
printf(" Null fanout pin\n");fflush(stdout);
}
}
}
else
{
printf(" Null net\n");fflush(stdout);
}
}
}
}*/
printf( " ------------\n");
}
queue->destroy(queue);
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.
*/
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.
hashtable_t *index = create_hashtable(max_depth?(max_depth * 2):100000);
queue_t *queue = create_queue();
queue->add(queue, bottom_node);
nnode_t *node;
int depth = 0;
// Traverse the netlist in reverse, starting with our current location.
while ((node = queue->remove(queue)))
{
int found_undriven_pin = FALSE;
int is_duplicate = index->get(index, &node->unique_id, sizeof(long))?1:0;
if (!is_duplicate)
{
depth++;
index->add(index, &node->unique_id, sizeof(long), (void *)1);
char *name = get_pin_name(node->name);
printf(" %s (%ld) %d %d\n", name, node->unique_id, node->num_input_pins, node->num_output_pins);
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 *node = 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->add(queue, node);
}
is_undriven = TRUE;
}
char *name = get_pin_name(node->name);
printf("\t(%s) %s (%ld) %d %d %s \n", pin->mapping, name, node->unique_id, node->num_input_pins, node->num_output_pins, is_undriven?"*":"");
free(name);
}
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;
}
}
index->destroy(index);
queue->destroy(queue);
return root_node;
}
/*
* Gets the current time in seconds.
*/
inline double wall_time()
{
struct timeval tv;
gettimeofday(&tv, 0);
return (1000000*tv.tv_sec+tv.tv_usec)/1.0e6;
}
/*
* Gets the name of the file we are simulating as passed by the -b or -V option.
*/
char *get_circuit_filename()
{
return global_args.verilog_file?global_args.verilog_file:global_args.blif_file;
}