Best way to modify strings in VHDL - 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__

Related

how to validate input with "real" as the variable to numbers only?

im a coding newbie, currently using freepascal to learn. i want to make a simple temperature converter. im struggling at how to validate the input. because im using "real" as var, and i cant validate the input to numbers and negative sign only. i want to write "false input" and repeat to temperature input, if the input contains alphabet/alphanumeric. but when i input the temperature in alphabet/alphanumeric it will just exiting from program. how to do this correctly? is there any solution?
input validation with "real" as the variable
here's my current code
program Fahrenheit_to_Celsius;
uses crt;
function strtofloat (floatstring : string) : extended;
var F: string;
floatvalue: extended;
C: real;
exit: boolean;
decision: shortstring;
begin
clrscr;
textattr :=$3;
gotoxy (52,1);
writeln ('Fahrenheit to Celsius Converter');
repeat
write ('Input Fahrenheit: ');
readln (F);
begin
try
F := strtofloat ('10 E 2');
except
on exception : econverterror do
showmessage (exception.message);
end;
try
F := strtofloat ('$FF');
except
on exception : econverterror do
showmessage (exception.message);
end;
C := ((F-32)*(5/9));
writeln ('Temperature in Celsius: ', C:0:5);
begin
writeln;
gotoxy ((wherex () + 56), (wherey() - 0));
writeln ('Convert again or exit?');
gotoxy ((wherex () + 31), (wherey() - 0));
writeln ('Input e then press enter to exit or input anything else to convert again');
write ('Decision:');
readln (decision);
if (decision = 'e') then
exit := true
else
exit := false;
gotoxy ((wherex () + 0), (wherey() - 3));
delline;
gotoxy ((wherex () + 0), (wherey() - 0));
delline;
end;
until exit;
end.
It's been a long time since I practiced pascal but I will give you an idea, I think you cannot validate it this way because if you declare the input as real once the program recive an non valid input it exits, so what I suggest, is to declare the variable as string instead, then you create a function to validate it, finally if the function returns true you convert the string to float.
The function should loop through every character in the recieved string and returns false if
one character is not a number or not a .
the string contains more then one . ( create a variable and increment it every time you catch a character equal to . )
the first character is .
the last character is .

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;

Ada - Skipping Whitespace using look_ahead

I have a procedure, that in theory, should be skipping whitespace using a look_ahead loop. Problem is, it's not working, if there's any whitespace in the input file, it is adding it to the array of records. I think my logic is correct, but could use another pair of eyes to let me know what I'm missing, and why it's not working.
PROCEDURE Read(Calc: OUT Calculation) IS
EOL: Boolean;
C: Character;
I: Integer := 1;
BEGIN
LOOP
LOOP
Look_Ahead(C, EOL);
EXIT WHEN EOL or C /= ' ';
Get(C);
END LOOP;
EXIT WHEN ADA.Text_IO.END_OF_FILE;
Look_Ahead(C, EOL);
IF Is_Digit(C) THEN
Calc.Element(I).Kind := Number;
Get(Calc.Element(I).Int_Value);
ELSE
Calc.Element(I).Kind := Symbol;
Get(Calc.Element(I).Char_Value);
END IF;
Calc.Len := Calc.Len+1;
IF Calc.Element(I).Char_Value = '=' THEN
EXIT;
END IF;
I := I+1;
END LOOP;
END Read;
EDIT: If any of the other procedures, the code for the record etc is needed for an answer, let me know and I will post it.
For Ada.Text_IO.Look_Ahead, ARM A.10.7(8) says
Sets End_Of_Line to True if at end of line, including if at end of page or at end of file; in each of these cases the value of Item is not specified. Otherwise, End_Of_Line is set to False and Item is set to the next character (without consuming it) from the file.
(my emphasis) and I think the "without consuming it" is key. Once Look_Ahead has found an interesting character, you need to call Get to retrieve that character.
I hacked this little demo together: I left end-of-file to exception handling, and I called Skip_Line once end-of-line’s been seen because just Get wasn’t right (sorry not to be more precise!).
with Ada.Text_IO;
with Ada.IO_Exceptions;
procedure Justiciar is
procedure Read is
Eol: Boolean;
C: Character;
begin
-- Instead of useful processing, echo the input to the output
-- replacing spaces with periods.
Outer:
loop
Inner:
loop
Ada.Text_IO.Look_Ahead (C, Eol);
exit Outer when Eol; -- C is undefined
exit Inner when C /= ' ';
Ada.Text_IO.Get (C); -- consume the space
Ada.Text_IO.Put ('.'); -- instead of the space for visibility
end loop Inner;
Ada.Text_IO.Get (C); -- consume the character which isnt a space
Ada.Text_IO.Put (C); -- print it (or other processing!)
end loop Outer;
Ada.Text_IO.Skip_Line; -- consume the newline
Ada.Text_IO.New_Line; -- clear for next call
end Read;
begin
loop
Ada.Text_IO.Put ("reading: ");
Read;
end loop;
exception
when Ada.IO_Exceptions.End_Error =>
null;
end Justiciar;
Usually it's better to read an entire line and parse it than to try to parse character by character. The latter is usually more complex, harder to understand, and more error prone. So I'd suggest something like
function De_Space (Source : String) return String is
Line : Unbounded_String := To_Unbounded_String (Source);
begin -- De_Space
Remove : for I in reverse 1 .. Length (Line) loop
if Element (Line, I) = ' ' then
Delete (Source => Line, From => I, Through => I);
end if;
end loop Remove;
return To_String (Line);
end De_Space;
Line : constant String := De_Space (Get_Line);
You can then loop over Line'range and parse it. Since I'm not clear if
Get(C);
Get(Calc.Element(I).Int_Value);
Get(Calc.Element(I).Char_Value);
represent 1, 2, or 3 different procedures, I can't really help with that part.

Does Quartus II support line.all?

I implemented some VHDL code to exports FSM state encodings at compile time, which can be read back by Xilinx ChipScope. This functionality is tested with Xilinx ISE 14.7, iSim 14.7 and Mentor Graphic's QuestaSim 10.2c. My design can be synthesized with an alternative top-level as well for Altera FPGAs, but Quartus II seems to have a problem with return line.all;.
Quartus II (14.0) error messages:
Error (10351): VHDL Subprogram Body error at sata_PhysicalLayer.vhdl(504): function "dbg_GenerateEncodings" does not always return a value
Error (10346): VHDL error at debug.vhdl(47): formal port or parameter "encodings" must have actual or default value
Error (10657): VHDL Subprogram error at sata_PhysicalLayer.vhdl(514): failed to elaborate call to subprogram "dbg_ExportEncoding"
In the following, I'll describe my code.
VHDL Code
The design uses 3 functions to export the FSM state encodings:
encode the current local FSM state as a binary value -> dbg_EncodeState
(the result of this function is connected to the ILA ports)
transform all states of the local FSM into a semicolon separated string -> dbg_GenerateEncodings
format this string and write it's elements into a token-file -> dbg_ExportEncoding
(this function is located in a package)
FSM declaration:
TYPE T_STATE IS (
ST_HOST_RESET,
ST_HOST_SEND_COMRESET,
ST_HOST_SEND_COMRESET_WAIT,
[...]
ST_HOST_SEND_ALIGN,
ST_HOST_TIMEOUT,
ST_HOST_LINK_OK
);
-- OOB-Statemachine
SIGNAL State : T_STATE := ST_HOST_RESET;
SIGNAL NextState : T_STATE;
local functions - per entity functions:
function dbg_EncodeState(st : T_STATE) return STD_LOGIC_VECTOR is
begin
return to_slv(T_STATE'pos(st), log2ceilnz(T_STATE'pos(T_STATE'high) + 1));
end function;
function dbg_GenerateEncodings return string is
variable l : STD.TextIO.line;
begin
for i in T_STATE loop
STD.TextIO.write(l, str_replace(T_STATE'image(i), "st_host_", ""));
STD.TextIO.write(l, ';');
end loop;
return l.all;
end function;
global function - defined in debug.pkg.vhdl:
impure function dbg_ExportEncoding(Name : STRING; encodings : string; tokenFileName : STRING) return BOOLEAN is
file tokenFile : TEXT open WRITE_MODE is tokenFileName;
variable cnt, base : integer;
variable l : line;
begin
report "Exporting encoding of '" & Name & "' to '" & tokenFileName & "'..." severity note;
report "dbg_ExportEncoding: '" & encodings & "'" severity note;
-- write file header
write(l, "# Encoding file for '" & Name & "'"); writeline(tokenFile, l);
write(l, "#"); writeline(tokenFile, l);
write(l, "# ChipScope Token File Version"); writeline(tokenFile, l);
write(l, "#FILE_VERSION=1.0.0"); writeline(tokenFile, l);
write(l, "#"); writeline(tokenFile, l);
write(l, "# Default token value"); writeline(tokenFile, l);
write(l, "#DEFAULT_TOKEN="); writeline(tokenFile, l);
write(l, "#"); writeline(tokenFile, l);
-- write state entires
cnt := 0;
base := encodings'left;
for i in encodings'range loop
if encodings(i) = ';' then
-- Leave the str_trim call in!
-- Otherwise, the new parser of ISE 14.7 fails to slice properly.
write(l, str_trim(encodings(base to i-1)));
write(l, character'('='));
write(l, raw_format_nat_hex(cnt));
writeline(tokenFile, l);
cnt := cnt + 1;
base := i+1;
end if;
end loop;
file_close(tokenFile);
return true;
end function;
The last portion of code is a dummy constant in the entity, which calls the export function:
CONSTANT test : boolean := dbg_ExportEncoding("OOBControl (Host)", dbg_GenerateEncodings, MY_PROJECT_DIR & "CSP/FSM_OOB_Host.tok");
used auxilary functions:
log2ceilnz(x) calculates the needed bits to encode x symbols in binary
to_slv converts everything into a std_logic_vector; in this case a integer to slv
str_replace replaces a string by a string
str_trim returns a string from str'low to the first occurrence of NUL
raw_format_nat_hex formats a natural to a hex string
Additional Notes:
All vhdl files are marked as VHDL-2008.
My Questions:
Has anyone experience with line.all in a Quartus environment?
Is there a workaround?
Is there a better solution to achieve the export task without using strings of constant length?
Work around:
I wrapped my function dbg_GenerateEncodings in a generate statement:
genXilinx : if (VENDOR = VENDOR_XILINX) generate
function dbg_GenerateEncodings return string is
[...]
constant test : boolean := dbg_ExportEncoding("OOBControl (Host)", dbg_GenerateEncodings, MY_PROJECT_DIR & "CSP/FSM_OOB_Host.tok");
begin
end generate;
In contrast to XST, Quartus does not check functions inside a generate block.
See Quartus II VHDL Support, Section 14 Predefined language environment, the table entry 14.3, Construct TEXTIO, the rightmost column VHDL 1993 Support:
Supported. File I/O cannot be synthesized; therefore, calls to TEXTIO
functions are ignored.
If you can't use TEXTIO for synthesis you could imagine a pointer to a line buffer might not be of any use either.
There's this issue of how you could write to a FILE from an FPGA. without any knowledge of the host operating system or specifying a physical interface.
You can manage to synthesize the remainder of your design by surrounding unsupported constructs with translate off and translate on directives. See VHDL Synthesis Attributes and Directives.

Command Line Args in pascal

I am new in pascal I have a program which gives result ....
I need to pass command line input in in given variable ip1 and ip2.
Its can be achieved by ParamStr[1] but it doesn't work.
program main;
var
output : integer;
var
ip1 : integer;
var
ip2 : integer;
function add(input1,input2:integer) : integer;
var
result: integer;
begin
if (input1 > input2) then
result := input1
else
result := input2;
add := result;
end;
begin
ip1 := 2533;**{ command line input}**
ip2 := 555;**{ command line input}**
output := add(ip1,ip2);
writeln( ' output : ', output );
end.K
As the other answer says, you use ParamCount and ParamStr to access command line parameters.
ParamCount returns the number of parameters passed on the command line, so you should check it first to see if you've received enough information.
ParamStr allows you to access each of the parameters passed. ParamStr(0) always gives you the full name of the executing program (including the path). Additional parameters are retrieved using the ordinal order in which they were passed, with ParamStr(1) being the first, and ParamStr(ParamCount) being the last. Each value received using ParamStr is a string value, and therefore has to be converted into the appropriate type before you can use it.
Here's a working example (pretty trivial, and all error checking omitted - you should, for instance, protect the code using StrToInt to handle errors if something is provided that won't convert to an integer).
program TestParams;
uses
SysUtils;
var
Input1, Input2, Output: Integer;
begin
if ParamCount > 1 then
begin
Input1 := StrToInt(ParamStr(1));
Input2 := StrToInt(ParamStr(2));
Output := Input1 + Input2;
WriteLn(Format('%d + %d = %d', [Input1, Input2, Output]));
end
else
begin
WriteLn('Syntax: ', ParamStr(0)); { Just to demonstrate ParamStr(0) }
WriteLn('There are two parameters required. You provided ', ParamCount);
end;
WriteLn('Press ENTER to exit...');
ReadLn;
end.
Calling it without parameters (or only one) displays the following:
C:\Temp>TestParams
Syntax: C:\Temp\TestParams.exe
There are two parameters required. You provided 0
Press ENTER to exit...
C:\Temp>TestParams 2
Syntax: C:\Temp>TestParams.exe 2
There are two parameters required. You provided 1
Press ENTER to exit...
Calling it with two parameters displays
C:\Temp\TestParams 2 2
2 + 2 = 4
Press ENTER to exit...
You need to understand the difference between a string and an integer.
To convert betwwen an integer such as 123 and the string of characters 1 2 3 you need to use a function. strtoint is one such function, converting a string to an integer. inttostr is another, converting from an integer to a string.
The command-line data is supplied via paramstr(n) as a string.
intvar := strtoint(paramstr(n));
assigns the value of the string to the integer variable intvar.
Whereas writeln has facilities to convert an integer argument to a formatted string, the way you have used it is attempting to output a string, so you need to convert the integer output to a string.
writeln(' output : ', inttostr(output) );
should do that very nicely.
var
x : string;
pcnt : integer;
begin
writeln('Parameter count=',inttostr(paramcount));
for pcnt := 1 to paramcount do
writeln('Parameter ',pcnt, ' of ',paramcount,' = ',paramstr(pcnt));
readln(x);
end.
should display the parameter list.
Indeed it is the case that the writeln procedure will recognise the variable type and take steps to format the value appropriately as a string, as has arrogantly been pointed out.
The issue to me is the difference between a string and an integer. paramstr returns a string which must be converted to an integer. After more than forty years' experience in Pascal, it's my opinion that it's better for a beginner to go through the exercise of converting each way and then use the conversion facility inbuilt in writeln.
First walk, then run. You first need to understand the steps in the procedure. Then you can start using the shortcuts - once you've mastered the basics.

Resources