467 lines
15 KiB
C
467 lines
15 KiB
C
/*************************************************************************
|
|
*
|
|
*
|
|
* 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
|
|
*/
|
|
struct expr_res init_tb (int num, struct expr_res *args)
|
|
{
|
|
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){
|
|
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);
|
|
t.v = 1;
|
|
}
|
|
else {
|
|
check_errors++;
|
|
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);
|
|
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);
|
|
}
|
|
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;
|
|
}
|