2022-10-14 20:06:36 +02:00
/*************************************************************************
*
*
* Copyright 2022 Ole Richter - University of Groningen
* Copyright 2022 Michele Mastella - University of Groningen
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor ,
* Boston , MA 02110 - 1301 , USA .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/**
* this file contains all the helper fucntions to read the CSV test benches .
*/
# include <stdio.h>
# include <stdlib.h>
# include <act/actsim_ext.h>
/**
* the arrays are staticly allocated , like how many sources aor checks there are , they can be easily increased and recompiled
* the defaults are thought to be fine for most smaller test benches
* also the standard file naming and that the csv is using a ; as seperator can be changed
*/
# define MAX_SOURCES 100
# define MAX_CHECKERS 100
# define MAX_CHECKS 100000
# define MAX_DUMP 100
# define CSV_FORMAT "%u; %lu"
# define CSV_WRITE_FORMAT "%d; %d\n"
# define SOURCE_FILENAME "source_%d.csv"
# define CHECK_FILENAME "check_%d.csv"
# define DUMP_FILENAME "dump_%d.csv"
# define CONTROL_FILENAME "control.csv"
# define LOG_FILENAME "test_bench_helper.log"
/**
* as the testbench is calling the functions , all state variables need to be static so they are the same state whenever called
*
*/
// all opened files for the text bench
static FILE * source_file [ MAX_SOURCES ] ;
static FILE * check_file [ MAX_CHECKERS ] ;
static FILE * dump_file [ MAX_DUMP ] ;
static FILE * control_file ;
// the data variable buffers, as we can only deliver one variable at a time,
// so first is there a variable and than the varibale itself
static unsigned int source_sim_step [ MAX_SOURCES ] ;
static unsigned int check_sim_step [ MAX_CHECKERS ] ;
static unsigned long source_data_buffer [ MAX_SOURCES ] ;
static unsigned long check_data_buffer [ MAX_CHECKERS ] [ MAX_CHECKS ] ;
static unsigned short check_data_used [ MAX_CHECKERS ] [ MAX_CHECKS ] ;
static unsigned int check_data_number [ MAX_CHECKERS ] ;
// as printing does not get dispayed in actsim, print to logfile
static FILE * logfile = NULL ;
// show more info like words/tests read
static int verbose = 0 ;
// keep a memory at which simulation step we are in the files
static unsigned long last_sim_step = 0 ;
static unsigned int first_sim_step = 0 ;
static unsigned long current_sim_step = 0 ;
static unsigned long current_sim_wait = 1 ;
static unsigned int check_errors = 0 ;
/**
* Init opens all the files and reads the control
* it requires one int :
* verbose : 0 for normal pinting , 1 for verbose printing
* it returns true on success
*/
2022-10-21 15:59:49 +02:00
struct expr_res init_tb ( int num , struct expr_res * args )
2022-10-14 20:06:36 +02:00
{
struct expr_res t ;
t . v = 0 ;
t . width = 1 ;
check_errors = 0 ;
logfile = fopen ( LOG_FILENAME , " w " ) ;
if ( num ! = 1 ) {
fprintf ( logfile , " [ERROR] wrong number of arguments in init " ) ; fflush ( logfile ) ;
return t ;
}
verbose = args [ 0 ] . v ;
fprintf ( logfile , " ==== initialising control ==== \n " ) ; fflush ( logfile ) ;
control_file = fopen ( CONTROL_FILENAME , " r " ) ;
int success = 0 ;
success = fscanf ( control_file , CSV_FORMAT , & first_sim_step , & last_sim_step ) ;
if ( success ! = 2 ) {
if ( ferror ( source_file [ args [ 0 ] . v ] ) ) {
fprintf ( logfile , " read error on control \n " ) ; fflush ( logfile ) ;
return t ;
}
else {
fprintf ( logfile , " empty file for control \n " ) ; fflush ( logfile ) ;
return t ;
}
}
fprintf ( logfile , " ==== initialising source ==== \n " ) ; fflush ( logfile ) ;
int i = 0 ;
for ( i = 0 ; i < MAX_SOURCES ; i + + ) {
source_sim_step [ i ] = 0 ;
char filename [ 255 ] ;
snprintf ( filename , 255 , SOURCE_FILENAME , i ) ;
source_file [ i ] = fopen ( filename , " r " ) ;
if ( source_file [ i ] ) {
fprintf ( logfile , " source id %d -> %s \n " , i , filename ) ; fflush ( logfile ) ;
}
}
fprintf ( logfile , " ==== initialising check ==== \n " ) ; fflush ( logfile ) ;
for ( i = 0 ; i < MAX_CHECKERS ; i + + ) {
check_sim_step [ i ] = 0 ;
char filename [ 255 ] ;
snprintf ( filename , 255 , CHECK_FILENAME , i ) ;
check_file [ i ] = fopen ( filename , " r " ) ;
if ( check_file [ i ] ) {
fprintf ( logfile , " check id %d -> %s \n " , i , filename ) ; fflush ( logfile ) ;
}
}
fprintf ( logfile , " ==== initialising done ==== \n " ) ; fflush ( logfile ) ;
t . v = 1 ;
return t ;
}
/**
* check_next looks if there is an other check availible in that simulation step
* if a new simulation step is loaded all checks from this step will be cashed into the arrays
* it requires 2 int :
* id : the id of the checker
* simstep : the simulation step the next test to be loaded from
* it returns 1 if there is a check availible , 0 if not or an error occured
*/
struct expr_res check_next ( int num , struct expr_res * args ) {
struct expr_res t ;
t . v = 0 ;
t . width = 1 ;
if ( num ! = 2 ) {
fprintf ( logfile , " [ERROR] wrong number of arguments in channel_check \n " ) ; fflush ( logfile ) ;
return t ;
}
if ( ! check_file [ args [ 0 ] . v ] ) {
fprintf ( logfile , " [ERROR] could not read check %d, file not open or does not exist \n " , args [ 0 ] . v ) ; fflush ( logfile ) ;
return t ;
}
int step_done = 0 , i = 0 ;
unsigned int check_sim_step_file = 0 ;
unsigned long int sim_data_file = 0 ;
// check if we need to advnce one step
if ( check_sim_step [ args [ 0 ] . v ] ! = args [ 1 ] . v ) {
// print all missed steps
for ( i = 0 ; i < check_data_number [ args [ 0 ] . v ] ; i + + ) {
if ( check_data_used [ args [ 0 ] . v ] [ i ] = = 0 ) {
check_errors + + ;
fprintf ( logfile , " [FAILURE] missed %d on check %d - %d for simstep %d; Error count: %d \n " , check_data_buffer [ args [ 0 ] . v ] , args [ 0 ] . v , i , check_sim_step [ args [ 0 ] . v ] , check_errors ) ; fflush ( logfile ) ;
}
}
// load next step
rewind ( check_file [ args [ 0 ] . v ] ) ;
check_sim_step [ args [ 0 ] . v ] = args [ 1 ] . v ;
unsigned int count = 0 ;
// read next steps
while ( ! step_done ) {
int success = 0 ;
success = fscanf ( check_file [ args [ 0 ] . v ] , CSV_FORMAT , & check_sim_step_file , & sim_data_file ) ;
if ( success ! = 2 ) {
if ( ferror ( check_file [ args [ 0 ] . v ] ) ) fprintf ( logfile , " read error on check %d \n " , args [ 0 ] . v ) ;
if ( verbose ) fprintf ( logfile , " EOF on check %d \n " , args [ 0 ] . v ) ; fflush ( logfile ) ;
step_done = 1 ;
}
else if ( count > = MAX_CHECKS ) {
fprintf ( logfile , " [ERROR] checks for sim step %d on checker %d, exceed the maximum number of checks, increase and recompile \n " , args [ 0 ] . v ) ; fflush ( logfile ) ;
}
else if ( check_sim_step_file = = check_sim_step [ args [ 0 ] . v ] ) {
check_data_buffer [ args [ 0 ] . v ] [ count ] = sim_data_file ;
check_data_used [ args [ 0 ] . v ] [ count ] = 0 ;
if ( verbose ) fprintf ( logfile , " %d check %d on %d \n " , count , check_data_buffer [ args [ 0 ] . v ] [ count ] , args [ 0 ] . v ) ; fflush ( logfile ) ;
count + + ;
}
}
check_data_number [ args [ 0 ] . v ] = count ;
}
// check if any checks have not been used yet
for ( i = 0 ; i < check_data_number [ args [ 0 ] . v ] ; i + + ) {
if ( check_data_used [ args [ 0 ] . v ] [ i ] = = 0 ) {
t . v = 1 ;
i = check_data_number [ args [ 0 ] . v ] ;
break ;
}
}
return t ;
}
/**
* check_in_order compares a word to the next avaible word in the csv ,
* check_next has to be called before
* it requres 2 ints :
* id : the id of the checker
* word to check : the word to compare
* returns 1 on success and 0 on failure
*/
struct expr_res check_in_order ( int num , struct expr_res * args )
{
struct expr_res t ;
t . v = 0 ;
t . width = 0 ;
if ( num ! = 2 ) {
fprintf ( logfile , " [ERROR] wrong number of arguments in channel_check \n " ) ;
return t ;
}
for ( int i = 0 ; i < check_data_number [ args [ 0 ] . v ] ; i + + ) {
if ( check_data_used [ args [ 0 ] . v ] [ i ] = = 0 ) {
if ( check_data_buffer [ args [ 0 ] . v ] [ i ] = = args [ 1 ] . v ) {
2022-11-11 17:47:26 +01:00
fprintf ( logfile , " [SUCCESS] got %d = %d on check %d - %d \n " , check_data_buffer [ args [ 0 ] . v ] [ i ] , args [ 1 ] . v , args [ 0 ] . v , i ) ; fflush ( logfile ) ;
2022-10-14 20:06:36 +02:00
t . v = 1 ;
}
else {
2022-10-18 16:31:57 +02:00
check_errors + + ;
2022-11-11 17:47:26 +01:00
fprintf ( logfile , " [FAILURE] expected %d got %d on check %d - %d; Error count %d \n " , check_data_buffer [ args [ 0 ] . v ] [ i ] , args [ 1 ] . v , args [ 0 ] . v , i , check_errors ) ; fflush ( logfile ) ;
2022-10-14 20:06:36 +02:00
t . v = 0 ;
}
check_data_used [ args [ 0 ] . v ] [ i ] = 1 ;
i = check_data_number [ args [ 0 ] . v ] ;
break ;
}
}
t . width = 1 ;
return t ;
}
/**
* check_out_of_order compares a word to the all avaible word in the csv for that step ,
* each word can only be used once .
* check_next has to be called before
* it requres 2 ints :
* id : the id of the checker
* word to check : the word to compare
* returns 1 on success and 0 on failure
*/
struct expr_res check_out_of_order ( int num , struct expr_res * args )
{
struct expr_res t ;
t . v = 0 ;
t . width = 0 ;
if ( num ! = 2 ) {
fprintf ( logfile , " [ERROR] wrong number of arguments in channel_check \n " ) ;
return t ;
}
for ( int i = 0 ; i < check_data_number [ args [ 0 ] . v ] ; i + + ) {
if ( check_data_used [ args [ 0 ] . v ] [ i ] = = 0 ) {
if ( check_data_buffer [ args [ 0 ] . v ] [ i ] = = args [ 1 ] . v ) {
fprintf ( logfile , " [SUCCESS] got %d = %d on check %d - %d \n " , check_data_buffer [ args [ 0 ] . v ] , args [ 1 ] . v , args [ 0 ] . v , i ) ; fflush ( logfile ) ;
t . v = 1 ;
check_data_used [ args [ 0 ] . v ] [ i ] = 1 ;
i = check_data_number [ args [ 0 ] . v ] ;
break ;
}
}
}
if ( t . v = = 0 ) {
check_errors + + ;
fprintf ( logfile , " [FAILURE] could not find %d on check %d; Error count: %d \n " , check_data_buffer [ args [ 0 ] . v ] , args [ 1 ] . v , args [ 0 ] . v , check_errors ) ; fflush ( logfile ) ;
2022-10-18 16:31:57 +02:00
}
2022-10-14 20:06:36 +02:00
t . width = 1 ;
return t ;
}
/**
* source_next looks if there is an other word availible to be send in the current simulation step
* it loads that work into the buffer .
* if a new simulation step is loaded the file is read from the beginning again .
* it requires 2 int :
* id : the id of the source
* simstep : the simulation step the next test to be loaded from
* it returns 1 if there is a source word availible , 0 if not or an error occured
*/
struct expr_res source_next ( int num , struct expr_res * args )
{
struct expr_res t ;
t . v = 0 ;
t . width = 0 ;
if ( num ! = 2 ) {
fprintf ( logfile , " [ERROR] wrong number of arguments in channel_source_next \n " ) ; fflush ( logfile ) ;
return t ;
}
if ( ! source_file [ args [ 0 ] . v ] ) {
fprintf ( logfile , " [ERROR] could not read source %d, file not open or does not exist \n " , args [ 0 ] . v ) ; fflush ( logfile ) ;
return t ;
}
int step_done = 0 ;
unsigned int source_sim_step_file = 0 ;
unsigned long int sim_data_file = 0 ;
if ( source_sim_step [ args [ 0 ] . v ] ! = args [ 1 ] . v ) {
rewind ( source_file [ args [ 0 ] . v ] ) ;
source_sim_step [ args [ 0 ] . v ] = args [ 1 ] . v ;
}
while ( ! step_done ) {
int success = 0 ;
success = fscanf ( source_file [ args [ 0 ] . v ] , CSV_FORMAT , & source_sim_step_file , & sim_data_file ) ;
if ( success ! = 2 ) {
if ( ferror ( source_file [ args [ 0 ] . v ] ) ) fprintf ( logfile , " read error on source %d \n " , args [ 0 ] . v ) ;
source_data_buffer [ args [ 0 ] . v ] = 0 ;
if ( verbose ) fprintf ( logfile , " EOF on source %d \n " , args [ 0 ] . v ) ; fflush ( logfile ) ;
t . v = 0 ;
t . width = 1 ;
return t ;
}
if ( source_sim_step_file = = source_sim_step [ args [ 0 ] . v ] ) {
step_done = 1 ;
source_data_buffer [ args [ 0 ] . v ] = sim_data_file ;
if ( verbose ) fprintf ( logfile , " read %d on source %d \n " , sim_data_file , args [ 0 ] . v ) ; fflush ( logfile ) ;
t . v = 1 ;
t . width = 1 ;
return t ;
}
}
}
/**
* source get reads the buffer and returns the word that was placed by source_next
* it requires 2 int :
* id : the id of the source
* width : the number of bits the source word is supposed to have
* it returns the next word placed by source_next
*/
struct expr_res source_get ( int num , struct expr_res * args )
{
struct expr_res t ;
t . v = 0 ;
t . width = 0 ;
if ( num ! = 2 ) {
fprintf ( logfile , " [ERROR] wrong number of arguments in channel_source_get \n " ) ;
return t ;
}
t . v = source_data_buffer [ args [ 0 ] . v ] ;
t . width = args [ 1 ] . v ;
return t ;
}
/**
* dump to file writes every inforamtion it gets into a csv file .
* the file is created and opend on the first write , to not create a lot of empty files that are not required .
* it requires 3 ints :
* id : the id of the dump file
* simstep : the current simulation step
* word : the data word to be written
* it return 1 on success
*/
struct expr_res dump_to_file ( int num , struct expr_res * args )
{
struct expr_res t ;
t . v = 0 ;
t . width = 1 ;
if ( num ! = 3 ) {
fprintf ( logfile , " [ERROR] wrong number of arguments in dump_to_file \n " ) ;
return t ;
}
if ( ! dump_file [ args [ 0 ] . v ] ) {
char filename [ 255 ] ;
snprintf ( filename , 255 , DUMP_FILENAME , args [ 0 ] . v ) ;
dump_file [ args [ 0 ] . v ] = fopen ( filename , " w " ) ;
}
if ( dump_file [ args [ 0 ] . v ] ) {
fprintf ( dump_file [ args [ 0 ] . v ] , CSV_WRITE_FORMAT , args [ 1 ] . v , args [ 2 ] . v ) ; fflush ( dump_file [ args [ 0 ] . v ] ) ;
t . v = 1 ;
return t ;
}
else {
fprintf ( logfile , " [ERROR] writing failed, file not open for dump %d \n " , args [ 0 ] . v ) ; fflush ( logfile ) ;
t . v = 0 ;
return t ;
}
}
/**
* control next hecks if there is a next simulation step to be loaded and if a delay needs to be triggered
* it requires 1 ints :
* ignored : cant have a function call without
* it returns 1 if there is a new step
*/
struct expr_res control_next ( int num , struct expr_res * args )
{
struct expr_res t ;
t . v = 0 ;
t . width = 1 ;
if ( num ! = 1 ) {
fprintf ( logfile , " [ERROR] wrong number of arguments in dump_to_file \n " ) ;
return t ;
}
if ( current_sim_step < first_sim_step ) {
current_sim_step = first_sim_step ;
t . v = 1 ;
}
else if ( current_sim_step > = last_sim_step ) {
return t ;
}
else {
current_sim_step + + ;
t . v = 1 ;
}
int step_done = 0 ;
unsigned int sim_step_file = 0 ;
unsigned long int sim_data_file = 0 ;
while ( ! step_done ) {
int success = 0 ;
success = fscanf ( control_file , CSV_FORMAT , & sim_step_file , & sim_data_file ) ;
if ( success ! = 2 ) {
if ( ferror ( control_file ) ) fprintf ( logfile , " read error on contol \n " ) ;
if ( verbose ) fprintf ( logfile , " EOF on ctl \n " ) ; fflush ( logfile ) ;
current_sim_wait = 0 ;
step_done = 1 ;
}
if ( sim_step_file = = current_sim_step ) {
step_done = 1 ;
if ( verbose ) fprintf ( logfile , " wait %d \n " , sim_data_file ) ; fflush ( logfile ) ;
current_sim_wait = sim_data_file ;
}
}
return t ;
}
/**
* control_get returns the current time_step , to advance a step call control_next
* ignores all input
* returns the current simstep
*/
struct expr_res control_get ( int num , struct expr_res * args )
{
struct expr_res t ;
t . v = current_sim_step ;
t . width = 32 ;
return t ;
}
/**
* control_get returns the wait time required after the current time_step , to advance a step call control_next
* ignores all input
* returns the current wait time
*/
struct expr_res control_wait ( int num , struct expr_res * args )
{
struct expr_res t ;
t . v = current_sim_wait ;
t . width = 32 ;
return t ;
}