From 3f8293e16a83d66181d1b393cbfd2df6b00ee8c7 Mon Sep 17 00:00:00 2001 From: Ole Richter Date: Fri, 14 Oct 2022 20:06:36 +0200 Subject: [PATCH] added initial version of actsim support --- .gitignore | 3 + act/Makefile | 34 +++ act/actsim_test_bench_lib.conf | 19 ++ act/test_bench_lib.act | 127 +++++++++ act/test_bench_lib.c | 466 +++++++++++++++++++++++++++++++++ readme.md | 118 ++++++++- 6 files changed, 766 insertions(+), 1 deletion(-) create mode 100644 act/Makefile create mode 100644 act/actsim_test_bench_lib.conf create mode 100644 act/test_bench_lib.act create mode 100644 act/test_bench_lib.c diff --git a/.gitignore b/.gitignore index 8cc22a5..6657336 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ ASSURA/ ncvlog.log ConnectedPinsLog.txt UnconnectedPinsLog.txt +*.os +*.csv +*.so diff --git a/act/Makefile b/act/Makefile new file mode 100644 index 0000000..c4ef5c9 --- /dev/null +++ b/act/Makefile @@ -0,0 +1,34 @@ +#------------------------------------------------------------------------- +# +# Copyright (c) 2018 Rajit Manohar +# +# 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. +# +#------------------------------------------------------------------------- + +TARGETLIBS=actsim_test_bench_lib.so +TARGETCONF=actsim_test_bench_lib.conf + +SHOBJS=test_bench_lib.os + + +SRCS= $(SHOBJS:.os=.c) + +include $(ACT_HOME)/scripts/Makefile.std + +$(TARGETLIBS): $(SHOBJS) + $(ACT_HOME)/scripts/linkso $(TARGETLIBS) $(SHOBJS) + diff --git a/act/actsim_test_bench_lib.conf b/act/actsim_test_bench_lib.conf new file mode 100644 index 0000000..cd960f6 --- /dev/null +++ b/act/actsim_test_bench_lib.conf @@ -0,0 +1,19 @@ +begin sim + + begin extern + string_tablex libs "test_bench_lib" + begin test_bench_lib + string path "$ACT_HOME/lib/actsim_test_bench_lib.so" + string sim::testbench::fsource_init "source_init" + string sim::testbench::fsource_next "source_next" + string sim::testbench::fsource_get "source_get" + string sim::testbench::fcheck_out_of_order "check_out_of_order" + string sim::testbench::fcheck_in_order "check_in_order" + string sim::testbench::fcheck_next "check_next" + string sim::testbench::fdump_to_file "dump_to_file" + string sim::testbench::fcontrol_next "control_next" + string sim::testbench::fcontrol_next "control_get" + string sim::testbench::fcontrol_next "control_wait" + end + end +end diff --git a/act/test_bench_lib.act b/act/test_bench_lib.act new file mode 100644 index 0000000..0497f3f --- /dev/null +++ b/act/test_bench_lib.act @@ -0,0 +1,127 @@ + +/************************************************************************* + * + * + * 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. + * + ************************************************************************** + */ + + +namespace sim::testbench { + +function fsource_init(int verbose) : int; +function fsource_next(int id; int sim_step) : int; +function fsource_get(int id; int bit_width) : int; +function fcheck_next(int id; int sim_step) : int; +function fcheck_in_order(int id; int data) : int; +function fcheck_out_of_order(int id; int data) : int; +function fdump_to_file(int id; int sim_step; int data) : int; + +template +defproc channel_source(chan!(int) out; chan?(int) sim_step; chan!(int) done) +{ + int current_step, t,data; + chp { + *[ sim_step?current_step; t := 1; + *[ t = 1 -> t := fchannel_source_next(ID,current_step); + [ t = 1 -> data := fchannel_source_get(ID,BIT_WIDTH); out!data ; log("send ", data, " on source ", ID) + [] else -> done!1 + ] + ] + ] + } +} + +template +defproc channel_checker_in_order(chan?(int) in; chan?(int) sim_step; chan!(int) done) +{ + int current_step, t, check, data; + chp { + *[ sim_step?current_step; t := 1; + *[ t = 1 -> t := fcheck_next(ID,current_step); + [ t = 1 -> in?data; check := fcheck_in_order(ID,data); + [check = 1 -> log("[success] got ",data, " on check ", ID) + [] else -> log("[failure] got wrong ",data, " on check ", ID, " see log") + ] + [] else -> done!1 + ] + ] + ] + } +} + +template +defproc channel_checker_out_of_order(chan?(int) in; chan?(int) sim_step; chan!(int<1>) done) +{ + int current_step, t, check, data; + chp { + *[ sim_step?current_step; t := 1; + *[ t = 1 -> t := fcheck_next(ID,current_step); + [ t = 1 -> in?data; check := fcheck_out_of_order(ID,data); + [check = 1 -> log("[success] got ",data, " on check ", ID) + [] else -> log("[failure] got wrong ",data, " on check ", ID, " see log") + ] + [] else -> done!1 + ] + ] + ] + } +} + +template +defproc channel_dump(chan?(int) in; chan?(int) sim_step) +{ + int current_step, t; + chp { + *[ [ #sim_step -> sim_step?current_step + | #in -> t:=fdump_to_file(ID,current_step,in) + ] + ] + } +} + +template +defproc control(chan(int) sim_step_source[NUMBER_SOURCE]; chan(int) sim_step_checker[NUMBER_CHECKER]; chan(int) sim_step_dump[NUMBER_DUMP]; chan?(int<1>) done_source[NUMBER_SOURCE]; chan?(int<1>) done_checker[NUMBER_CHECKER]) +{ + int current_step, wait; + int<1> t, success, failure_free; + chp { + t:=1; + failure_free:=1; + *[ t = 1 -> current_step := fcontrol_get(); + [ current_step = 0 -> skip; // reset here + [] else -> + (,:j:1..NUMBER_SOURCE: sim_step_source[j]!current_step), + (,:j:1..NUMBER_CHECKER: sim_step_checker[j]!current_step), + (,:j:1..NUMBER_DUMP: sim_step_dump[j]!current_step); + (:j:1..NUMBER_SOURCE: done_source[j]?success; failure_free := failure_free & success); + (:j:1..NUMBER_CHECKER: done_checker[j]?success; failure_free := failure_free & success); + ]; + wait := fcontrol_wait(); + [ wait > 0 -> skip // exec cycle + [] else -> skip + ]; + [ HALT_ON_FALIURE & ~failure_free -> t := 0; log("stopped testbech because of failure") + [] else -> t:=fcontrol_next() + ] + ] + } +} +} diff --git a/act/test_bench_lib.c b/act/test_bench_lib.c new file mode 100644 index 0000000..fb92bac --- /dev/null +++ b/act/test_bench_lib.c @@ -0,0 +1,466 @@ +/************************************************************************* + * + * + * 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 +#include +#include + +/** +* 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 (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 %s\n",i,filename); fflush(logfile); + } + } + fprintf(logfile,"==== initialising check ====\n"); fflush(logfile); + + for (i = 0; i %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],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],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); + }s + 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; +} diff --git a/readme.md b/readme.md index 402b221..5ee2b44 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,117 @@ -# function lib test bench +# lib test bench + +is build to load test vectors from CSV files into your actsim or mixed signal simulation. +the library contains multiple source and check as well as dump helpers. + +## The Concept + +each test bench consists out of: + +- a DUT - device under test +- multiple sources +- multiple checker or dumpers +- a controller - responcible for syncronising the release of test vectors + +the execution order of the test bench is defined by the simulation steps and the order of the test vectors inside the simulation step + +before the test bench proceeds to the next simulation step, it waits untill +- all sources have sent their test vectors +- all checkes have recieved all exspeced inputs +- the specified wait time for the simulation step has elapsed after the 2 conditions above have been satisfied. + + +the reason the test bench lib is splitt in components is that you can assable it to your need without writing it new for evert DUT + +the reason to load a control file is that you can run different test suits with the same test +bench by changing the set of csv files. + +### Limitations for verilog +Cadence AMS: + +\- the test vector files can contain a maximum of 2.2M rows per simulation step, as they are cached for out of order checks +(`` `parameter integer MAX_CHECKS = 2200000; ``\`) + +## The Control + +a test bench includes one test bench controller that excecutes reset (verilog, prs), the +simulation step and the time out/end of the simulation (verilog) + +Note: +because the controller sets the time out in verilog set your simulation time in your simulator to way more than needed, the TB will end the simulation for you! + +this unit is called **control**, it reads a file with the name `control.csv`. + +the csv format is, one line per simulation step: +``` first line: , all other lines: ,