TAP (Test Anything Protocol) module for VHDL - vhdl

Is there a TAP (Test Anything Protocol) implementation for VHDL? It would be nice because then I could use prove to check my results automatically. There are also nice formatting swuites such as smolder that can process it output. You might ask why not use assertions. Partly TAP gives me some good reporting such as number of files and number of tests. I'm looking for a minimal implentation with number of tests at the beginning and end and the ok, diag and fail functions. is() would really nice, but not necessary. I could write this, but why reinvent the wheel.
This is the question as in this question but for VHDL instead of Verilog.

I wrote one that I've used a lot, but I've never distributed it. Here it is (the not-included base_pkg mostly has to_string() implementations for everything).
-- Copyright © 2010 Wesley J. Landaker <wjl#icecavern.net>
--
-- 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 3 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, see <http://www.gnu.org/licenses/>.
-- Output is standard TAP (Test Anything Protocol) version 13
package test_pkg is
procedure test_redirect(filename : string);
procedure test_plan(tests : natural; directive : string := "");
procedure test_abort(reason : string);
procedure test_finished(directive : string := "");
procedure test_comment (message : string);
procedure test_pass (description : string := ""; directive : string := "");
procedure test_fail (description : string := ""; directive : string := "");
procedure test_ok (result : boolean; description : string := ""; directive : string := "");
procedure test_equal(actual, expected : integer; description : string := ""; directive : string := "");
procedure test_equal(actual, expected : real; description : string := ""; directive : string := "");
procedure test_equal(actual, expected : time; description : string := ""; directive : string := "");
procedure test_equal(actual, expected : string; description : string := ""; directive : string := "");
procedure test_equal(actual, expected : bit_vector; description : string := ""; directive : string := "");
procedure test_approx_absolute(actual, expected, absolute_error : real; description : string := ""; directive : string := "");
procedure test_approx_relative(actual, expected, relative_error : real; description : string := ""; directive : string := "");
end package;
use std.textio.all;
use work.base_pkg.all;
package body test_pkg is
file test_output : text;
shared variable initialized : boolean := false;
shared variable have_plan : boolean := false;
shared variable last_test_number : natural := 0;
function remove_eol(s : string) return string is
variable s_no_eol : string(s'range);
begin
for i in s'range loop
case s(i) is
when LF | CR => s_no_eol(i) := '_';
when others => s_no_eol(i) := s(i);
end case;
end loop;
return s_no_eol;
end function;
function make_safe (s : string) return string is
variable s_no_hash : string(s'range);
begin
for i in s'range loop
case s(i) is
when '#' => s_no_hash(i) := '_';
when others => s_no_hash(i) := s(i);
end case;
end loop;
return remove_eol(s_no_hash);
end function;
procedure init is
variable l : line;
begin
if initialized then
return;
end if;
initialized := true;
file_open(test_output, "STD_OUTPUT", write_mode);
write(l, string'("TAP version 13"));
writeline(test_output, l);
end procedure;
procedure test_redirect(filename : string) is
begin
init;
file_close(test_output);
file_open(test_output, filename, write_mode);
end procedure;
procedure test_plan(tests : natural; directive : string := "") is
variable l : line;
begin
init;
have_plan := true;
write(l, string'("1.."));
write(l, tests);
if directive'length > 0 then
write(l, " # " & remove_eol(directive));
end if;
writeline(test_output, l);
end procedure;
procedure test_abort(reason : string) is
variable l : line;
begin
init;
write(l, "Bail out! " & remove_eol(reason));
writeline(test_output, l);
assert false
report "abort called"
severity failure;
end procedure;
procedure test_finished (directive : string := "") is
begin
if not have_plan then
test_plan(last_test_number, directive);
elsif directive'length > 0 then
test_comment("1.." & integer'image(last_test_number) & " # " & directive);
else
test_comment("1.." & integer'image(last_test_number));
end if;
end procedure;
procedure test_comment (message : string) is
variable l : line;
begin
init;
write(l, '#');
if message'length > 0 then
write(l, " " & remove_eol(message));
end if;
writeline(test_output, l);
end procedure;
procedure result (status : string; description : string; directive : string) is
variable l : line;
begin
init;
last_test_number := last_test_number + 1;
write(l, status & " ");
write(l, last_test_number);
if description'length > 0 then
write(l, " " & make_safe(description));
end if;
if directive'length > 0 then
write(l, " # " & remove_eol(directive));
end if;
writeline(test_output, l);
end procedure;
procedure test_pass (description : string := ""; directive : string := "") is
begin
result("ok", description, directive);
end procedure;
procedure test_fail (description : string := ""; directive : string := "") is
begin
result("not ok", description, directive);
end procedure;
procedure test_ok (result : boolean; description : string := ""; directive : string := "") is
begin
if result then
test_pass(description, directive);
else
test_fail(description, directive);
end if;
end procedure;
procedure test_equal(actual, expected : integer; description : string := ""; directive : string := "") is
variable ok : boolean := actual = expected;
begin
test_ok(ok, description, directive);
if not ok then
test_comment("actual = " & integer'image(actual) & ", expected = " & integer'image(expected));
end if;
end procedure;
procedure test_equal(actual, expected : real; description : string := ""; directive : string := "") is
variable ok : boolean := actual = expected;
begin
test_ok(ok, description, directive);
if not ok then
test_comment("actual = " & real'image(actual) & ", expected = " & real'image(expected));
end if;
end procedure;
procedure test_equal(actual, expected : time; description : string := ""; directive : string := "") is
variable ok : boolean := actual = expected;
begin
test_ok(ok, description, directive);
if not ok then
test_comment("actual = " & time'image(actual) & ", expected = " & time'image(expected));
end if;
end procedure;
procedure test_equal(actual, expected : string; description : string := ""; directive : string := "") is
variable ok : boolean := actual = expected;
begin
test_ok(ok, description, directive);
if not ok then
test_comment("actual = " & actual & ", expected = " & expected);
end if;
end procedure;
procedure test_equal(actual, expected : bit_vector; description : string := ""; directive : string := "") is
variable ok : boolean := actual = expected;
begin
test_ok(ok, description, directive);
if not ok then
test_comment("actual = " & to_string(actual) & ", expected = " & to_string(expected));
end if;
end procedure;
procedure test_approx_absolute(actual, expected, absolute_error : real; description : string := ""; directive : string := "") is
variable err : real := abs(actual - expected);
variable ok : boolean := err <= absolute_error;
begin
test_ok(ok, description, directive);
if not ok then
test_comment("actual = " & to_string(actual) & ", expected = " & to_string(expected) & ", absolute error = " & to_string(err));
end if;
end procedure;
procedure test_approx_relative(actual, expected, relative_error : real; description : string := ""; directive : string := "") is
variable err : real := abs(actual - expected)/abs(expected);
variable ok : boolean := err <= relative_error;
begin
test_ok(ok, description, directive);
if not ok then
test_comment("actual = " & to_string(actual) & ", expected = " & to_string(expected) & ", relative error = " & to_string(err));
end if;
end procedure;
end package body;

From my limited quick reading up on TAP - unlikely... because most HDL designers are not that well-connected with the world of software testing (even though they've been doing unit-testing since well before it was called that :) I like to feel I am an HDL designer who is reasonably well-connected to the world of software testing, and I've never come across TAP before. I've stuck to Python's own unittest functionality (and dabbled with pytest). And my own concoction for working with VHDL and its asserts.
It looks like a fairly simple package to write though... do let us know if you decide to write one yourself!

Related

Remove Linked List Elements -pascal

I have a problem with prosedeur supression() when inserting an etudent (more than one) and deleted by suprrision Procedure and make a search about that item I delete the program and I guess the problem is in ? prosedur supression()`. Look at it first.
I wish that clear thank u guys now
declartion to types student and moudle and note
Program liste_des_etudiants;
Type
date = Record
jour,mois,anee : Integer;
End;
ptr_etu = ^etudiant;
etudiant = Record
matricule : String;
nom,prenom,adress : String;
date_n : date;
suiv : ptr_etu;
End;
ptr_mod = ^module;
module = Record
code,libelle : String;
credit,coeff : Integer;
suiv : ptr_mod;
End;
ptr_note = ^note;
note = Record
matricule : String;
code : String;
note : Integer;
suiv : ptr_note;
End;
Var
choix : String;
liste_etudiant : ptr_etu ;
liste_note : ptr_note;
liste_module : ptr_mod;
Procedure supression etudent
Procedure supression(Var etu:ptr_etu;module:ptr_mod;note:ptr_note );
var
choix,matr,code : String;
current,prvious,Next: ptr_etu;
current_note,privous_note :ptr_note;
privous_code,current_code: ptr_mod;
begin
Repeat
Repeat
Writeln('pour supression un etudiant tapez 1');
Writeln('pour supression un note tapez 2');
Writeln('pour supression un module tapez 3');
Writeln('pour sortir tapez e ');
Readln(choix);
Until ((choix='1') Or (choix='2') Or (choix='3') Or (choix='e') Or (choix='E'));
If (choix='1') Then
Begin
// I guess the porblem is here but i don't know is it
// initcation de node
current := etu ;
Writeln('donner son matricule') ;
Readln(matr);
// traitement to the if the node we wanna delet is the
// first one
While((etu<>Nil) And(etu^.matricule=matr))Do
etu:= etu^.suiv;
While ((current<>Nil) and (current^.suiv<>nil)) Do
Begin
next:=etu^.suiv;
if (next^.matricule=matr) then
begin
current^.suiv:=next^.suiv;
end
Else
current:=current^.suiv;
end;
if (current =nil) then
writeln('l ''''etudiant n''''est pas trouvee');
Until ((choix='e') Or(choix='E'));
end;
Main
Begin
Repeat
Repeat
Writeln('tapez:');
Writeln('1):pour insertion');
Writeln('2):pour modification');
Writeln('3):pour supression');
Writeln('4):pour recherche');
Writeln('(e)pour sortir');
Readln(choix);
Until ((choix='1') Or (choix='2') Or (choix='3') Or (choix='4') Or (choix='e'));
If (choix='1') Then
insertion(liste_etudiant, liste_module,liste_note)
Else If (choix='2') Then
modification(liste_etudiant, liste_module,liste_note)
Else If (choix='3') Then
supression(liste_etudiant,liste_module,liste_note)
Else if (choix='4') Then
recherche(liste_etudiant, liste_module,liste_note)
Until ((choix='e') Or (choix='E'));
End.

vhdl package export symbols it includes from another package

Let's say I have a package strkern (I do) which exports function: strlen
package strkern is
function strlen(s: string) return natural;
end package strkern;
I write a new package stdstring which defines lots of exciting new operators, but also wants to reexport strlen
use work.strkern.all;
package stdstring is
-- rexport strlen
-- define exciting new operators
end package stdstring
So if one codes
use work.stdstring.all;
You get strlen(), and the exciting new operators.
How does one "re export" a function/type import from a sub package?
Short of having a dummy implementation with a new name that just calls the "to be imported" implementation
function strlen(s: string) return natural is
return strkern.strlen(s);
end function strlen;
By providing a Minimal, Complete and Verifiable example:
package strkern is
function strlen(s: string) return natural;
end package strkern;
package body strkern is
function strlen(s: string) return natural is
begin
return s'length;
end function;
end package body;
use work.strkern.all; -- NOT USED in this foreshortened example
package stdstring is
-- rexport strlen
-- define exciting new operators
alias strlen is work.strkern.strlen [string return natural];
end package stdstring;
use work.stdstring.all;
entity foo is
end entity;
architecture fum of foo is
begin
assert false
report "strlen of ""abcde"" is " & integer'image(strlen("abcde"))
severity NOTE;
end architecture;
we can demonstrate the use of an alias in a package to provide visibility of a declaration of a function found in another package.
ghdl -r foo
std_flub_up.vhdl:25:5:#0ms:(assertion note): strlen of "abcde" is 5
The questions code snippets don't demonstrate any 'exciting new operators' where operator is specific to predefined operators in VHDL. See IEEE Std 1076-2008 9.2 Operators.
The method of using an alias to make name visible is shown in
6.6.3 Nonobject aliases. Note that an alias for a subprogram requires a signature.
Some readers may be curious why the selected name suffix strlen is visible in the above MCVe alias. See 12.3 Visibility:
Visibility is either by selection or direct. A declaration is visible by selection at places that are defined as follows:
a) For a primary unit contained in a library: at the place of the suffix in a selected name whose prefix denotes the library.
...
f) For a declaration given in a package declaration, other than in a package declaration that defines an uninstantiated package: at the place of the suffix in a selected name whose prefix denotes the package.
...
Basically a selected name (work.strkern.strlen) suffix (strlen) declaration occurs in a separate name space defined by the prefix which will designate a primary unit (package strkern here in library work). The package declaration is made visible by rule a. The suffix is made visible by rule f. The library work is made visible by an implicit library declaration (see 13.2 Design libraries).
You cannot re-export anything from one package in another. The only option is to use both the strkern package and the stdstring package.
use work.strkern.all;
use work.stdstring.all;
I would recommend against creating a dummy implementation, because if a user were to include both packages, the duplicate function signatures will cause both to be invisible as the compiler will not know which one to use. The user will have to be explicit about which function to use:
work.strkern.strlen("Hello world");
work.stdstring.strlen("Hello world");
You can create aliases to the functions in the first package, but they cannot have the same name as the original:
alias str_len is strlen[string];
Maybe you want to investigate context clauses from VHDL2008. These allow you use several libraries and packages in a single context, and can be included elsewhere to include all the clauses in the context. The only issue here is that you are not allowed to use work, because work only means the "current working library". If you were to include a context within a another library, the work reference is now incorrect.
context my_string_packages is
library my_string_lib;
use my_string_lib.strkern.all;
use my_string_lib.stdstring.all;
end context my_string_packages;
library my_string_lib;
context my_string_lib.my_string_packages;
Package strdemo is aliasing strlen, strncpy
package strdemo is
alias strlen is work.strkern.strlen[string return natural];
alias strncpy is work.strkern.strncpy [string, string, integer, natural, natural, boolean];
function "+"(a:string; b:string) return string;
function "-"(a:string; b:string) return boolean;
function "*"(a:integer; b:character) return string;
function "*"(a:boolean; b:string) return string;
end package strdemo;
Here's the full listing of strkern
package strkern is
constant STRING_EOF : character := nul; -- \0 string termination from c
-- We can't assign strings but can easily assign string_segments to variables
-- Making a record instead of array because a record has no operators defined for it.
-- An array already has the & operator.
type string_segment is record
start, stop: natural;
end record string_segment;
-- We have 3 kinds of string: string, cstring, vstring. All export functions return vstring
-- There is no functionality difference between the 3, just implementation.
-- All functions accept string and return vstring. They can accept any string type but will
-- always return a vstring.
subtype cstring is string; -- null terminated c strlen <= 'length 'left = 1
subtype vstring is string; -- vhdl string strlen = 'length 'left >= 1
function strlen(s: string) return natural;
function safeseg(s: string; ss: string_segment := (0, 0); doreport: boolean := false) return vstring;
procedure strncpy(dest: out cstring; src: string; n: integer := -2; at: natural := 1; from: natural := 1; doreport: boolean := false);
end package strkern;
package body strkern is
-- Error messages
constant safeseg_left: string := "right operand: ";
constant safeseg_right: string := " overflows strlen: ";
-- Borrow integer whenelse() from stdlib
function whenelse(a: boolean; b, c: integer) return integer is begin
if a then return b; else return c;
end if;
end function whenelse;
function ii(i: integer) return vstring is begin
return integer'image(i);
end function ii;
-- These 4 are the only functions/procedures that use & = or segment/index strings
-- strlen is fundamental. It gives the correct answer on any type of string
function strlen(s: string) return natural is
variable i: natural := s'left;
begin
while i < s'right and s(i) /= STRING_EOF loop
i := i+1;
end loop;
if s'length = 0 then
return 0;
elsif s(i) = STRING_EOF then
return i-s'left;
else
return i+1-s'left;
end if;
end function strlen;
-- safely segment a string. if a=0 convert a cstring to vhdl string
-- otherwise use strlen and 'left to return correct segment string.
function safeseg(s: string; ss: string_segment := (0, 0); doreport: boolean := false) return vstring is
constant len: natural := strlen(s); -- strlen is an expensive function with long strings
-- This is the reason for stdstring. Debug reports get very verbose.
impure function dump return vstring is begin
return " safeseg(s, (" & ii(ss.start) & ", " & ii(ss.stop) & ")) s'left=" & ii(s'left) & " strlen(s)=" & ii(len);
end function dump;
begin
if doreport then -- debug. invokers can switch on doreport
report dump;
end if;
if ss.start = 0 then -- if ss.start=0 return the entire string as defined by strlen()
if len=0 then -- cannot use whenelse here
return "";
else
return s(s'left to s'left + len-1);
end if;
else
assert ss.stop <= len report safeseg_left & natural'image(ss.stop) & safeseg_right & dump;
return s(s'left + ss.start-1 to s'left + whenelse(ss.stop=0, s'length, ss.stop) -1);
end if;
end function safeseg;
-- The only way to assign strings
-- strncpy(dest, src) is effectively strcpy(dest, src) from C
-- It will non fail assert on overflow followed by an array out of bounds error
procedure strncpy(dest: out cstring; src: string; n: integer := -2; at: natural := 1; from: natural := 1; doreport: boolean := false) is
constant srclen: natural := strlen(src);
constant destspace: integer := dest'length + 1 - at;
variable copylen: integer := srclen + 1 - from;
impure function dump return vstring is begin
return " strncpy(str(" & ii(dest'length) & "), str(" & ii(srclen) & "), " & ii(n) & ", " & ii(at) & ", " & ii(from) & ")";
end function dump;
begin
if doreport then
report dump;
end if;
if n >= 0 and copylen > n then
copylen := n;
end if;
if n = -1 and copylen > destspace then
copylen := destspace;
end if;
assert copylen <= destspace report "overrun" & dump;
if copylen > 0 then
dest(at to at + copylen - 1) := src(from to from + copylen - 1);
end if;
if copylen < destspace then
dest(at + copylen) := STRING_EOF;
end if;
end procedure strncpy;
end package body strkern;

"Readline called past the end of file" error VHDL

I need to read a file in VHDL but there is an error:
"Line 57: Readline called past the end of file mif_file"
impure function init_mem(mif_file_name : in string) return mem_type is
file mif_file : text open read_mode is mif_file_name;
variable mif_line : line;
variable temp_bv : bit_vector(DATA_WIDTH-1 downto 0);
variable temp_mem : mem_type;
begin
for i in mem_type'range loop
readline(mif_file, mif_line);
read(mif_line, temp_bv);
temp_mem(i) := to_stdlogicvector(temp_bv);
end loop;
return temp_mem;
end function;
As stated in the comment section, you are trying to read more than you have in the file, you can avoid the error by checking if you reached the end of the file in your for loop and in that case assign a default value instead.
impure function init_mem(mif_file_name : in string) return mem_type is
file mif_file : text open read_mode is mif_file_name;
variable mif_line : line;
variable temp_bv : bit_vector(DATA_WIDTH-1 downto 0);
variable temp_mem : mem_type;
begin
for i in mem_type'range loop
if(not endfile(mif_file)) then
readline(mif_file, mif_line);
read(mif_line, temp_bv);
temp_mem(i) := to_stdlogicvector(temp_bv);
else
temp_mem(i) := default_value_to_be_defined;
end if;
end loop;
return temp_mem;
end function;
Or you can exit the for loop if you don't want to set a default value
impure function init_mem(mif_file_name : in string) return mem_type is
file mif_file : text open read_mode is mif_file_name;
variable mif_line : line;
variable temp_bv : bit_vector(DATA_WIDTH-1 downto 0);
variable temp_mem : mem_type;
begin
for i in mem_type'range loop
if(not endfile(mif_file)) then
readline(mif_file, mif_line);
read(mif_line, temp_bv);
temp_mem(i) := to_stdlogicvector(temp_bv);
else
exit;
end if;
end loop;
return temp_mem;
end function;

Best way to modify strings in VHDL

I'm currently writing a test bench for a VHDL design I made and I need to write a message to a text file. The message is of the format
[instance_name];[simulation_time]
(i.e. U0;700 ns) and the filename must be [instance_name].log. Getting the instance name and simulation time is no problem, but writing to a custom filename has been problematic. Under simulation, the instance name will be given in the format:
"U0\ComponentX\test\"
and I would like to replace the slashes with underscores. Is there an easy way to do this?
Our PoC Library has quite a big collection on string operations/functions. There is a str_replace function in PoC.strings that should solve your question. There is also the PoC.utils package with non string related functions, that could also be helpful in handling strings and file I/O.
A simple implementation:
function replace(str : STRING) return STRING
variable Result : STRING(str'range) := str;
begin
for i in str'range loop
if (Result(i) = '\') then
Result(i) := '_';
end if;
loop;
return Result;
end function;
Usage:
constant original : STRING := "U0\ComponentX\test\";
constant replaced : STRING := replace(original);
Simple replace character function that is a bit more versatile and does the same job would be (nothing wrong with #Paebbels's answer)
function fReplaceChar(
a : character;
x : character;
s : string) return string
is
variable ret : string(s'range) := s;
begin
for i in ret'range loop
if(ret(i) = a) then
ret(i) := x;
end if;
end loop;
return ret;
end function fReplaceChar;
If there are more than one character to replace, one can always stack the function:
function fReplaceChar(
a : character;
b : character;
x : character;
s : string) return string
is
begin
return fReplaceChar(b, x, fReplaceChar(a, x, s));
end function fReplaceChar;
or function call:
fReplaceChar(')','_',fReplaceChar(':','(','_',tb'instance_name));
So for example:
process
begin
report lf & tb'instance_name & lf &
fReplaceChar(')','_',fReplaceChar(':','(','_',tb'instance_name));
wait;
end process;
gives:
# ** Note:
# :tb(sim):
# _tb_sim__

Pascal error 'call by var for arg no.1 has to match exactly'

I learning to make a program that gets data from a txt file and places it in arrays.
the following are its types :
type
ekspedisi = record
nmeksp : string; // Nama Ekspedisi
jlp : string; // Jenis layanan pengiriman
biaya : integer; // Biaya pengiriman per kg
lp : integer; // per hari
end;
ekspedisiku = record
nom : array [1..100] of ekspedisi;
end;
and a simple algorithm
procedure getDaftarEkspedisi(var kirim : ekspedisiku);
var
i,j,k : integer;
eksp : text;
init : string;
garis : array [1..100] of integer;
mark : string;
jeks : integer;
count : integer;
begin
assign(eksp,'ekspedisi.txt');
reset(eksp);
i := 0;
k := 1;
j := 1;
mark := '|';
jeks := 10;
writeln('Loading ekspedisi.. ');
while(not(eof(eksp))) do
begin
readln(eksp,init);
i := i + 1;
for j := 1 to length(init) do
begin
if init[j] = mark then
begin
garis[k] := j;
k := k + 1;
end;
end;
for i := 1 to jeks do
begin
count := ((i-1)*5);
kirim.nom[i].nmeksp := copy(init,garis[1+count] + 2,garis[2+count]-garis[1+count]-2);
kirim.nom[i].jlp := copy(init,garis[2+count] + 2,garis[3+count]-garis[2+count]-2);
val(copy(init,garis[3+count] + 2,garis[4+count]-garis[3+count]-2),kirim.nom[i].biaya);
val(copy(init,garis[4+count] + 2,garis[5+count]-garis[4+count]-2),kirim.nom[i].lp);
end;
close(kirim);
writeln('loading sukses.');
end;
end;
from that code, i get the following error
<166,13>Error: Call by var for arg no.1 has to match exactly : got "ekspedisiku" expected "Text"
curiously, line 166 is only
close(kirim);
any help is appreciated.
You need to pass the file handle to close, so:
close(kirim);
should be:
close(eksp);
It also looks like you're closing the file at the wrong place in your function. It should most likely be after the while loop, so you need to change:
close(kirim);
writeln('loading sukses.');
end;
end;
to:
end;
close(kirim);
writeln('loading sukses.');
end;
Note that this mistake probably happened because your identation is messed up - if you're careful with formatting your code properly then you won't be so likely to make this kind of error.

Resources