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;
Related
I've written a simple function which does log2 computation of any integer number and then "ceils" it to the next integer:
function log2ceil(intVal: integer) return natural is
variable i : natural;
variable bitCount : natural;
begin
i := (intVal - 1);
bitCount := 0;
while (i > 0) loop
bitCount := bitCount + 1;
i:=shiftRightInt(i,1);
end loop;
return bitCount;
end log2ceil;
function shiftRightInt(ValueToShift: integer; NrToShift:natural) return integer is
begin
return to_integer(shift_right(to_unsigned(ValueToShift, 32), NrToShift));
end shiftRightInt;
Works, fine. The problem is with the simulation. Every time I try to simulate it with a simple function call in a test bench:
stim: process
begin
wait for 10 ns;
log2ceil(3);
wait;
end process;
it gives me following error:
ERROR: [VRFC 10-1472] type error near log2ceil ; expected type void
Why does it expect type Void? And how can I avoid getting this error?
Your tool isn't giving a very helpful error message. The problem is that you are referencing a function in log2ceil that has not yet been declared. Given the order you define things, shiftRightInt isn't visible yet.
When I compile your code with Modelsim, I get:
`
(vcom-1136) Unknown identifier "shiftRightInt".
Edit: After including your process I got the error #user1155120 pointed out. In Modelsim:
No feasible entries for subprogram "log2ceil".
As user1155120 noted, you are calling a function in a procedure context. You need to assign the return value to something.
First, you can swap the order of declarations. I.e.:
function shiftRightInt(ValueToShift: integer; NrToShift:natural) return integer is
begin
return to_integer(shift_right(to_unsigned(ValueToShift, 32), NrToShift));
end shiftRightInt;
function log2ceil(intVal: integer) return natural is
variable i : natural;
variable bitCount : natural;
begin
i := (intVal - 1);
bitCount := 0;
while (i > 0) loop
bitCount := bitCount + 1;
i:=shiftRightInt(i,1);
end loop;
return bitCount;
end log2ceil;
The other is to define a "prototype" of shiftRightInt:
function shiftRightInt(ValueToShift: integer; NrToShift:natural) return integer;
function log2ceil(intVal: integer) return natural is
variable i : natural;
variable bitCount : natural;
begin
i := (intVal - 1);
bitCount := 0;
while (i > 0) loop
bitCount := bitCount + 1;
i:=shiftRightInt(i,1);
end loop;
return bitCount;
end log2ceil;
function shiftRightInt(ValueToShift: integer; NrToShift:natural) return integer is
begin
return to_integer(shift_right(to_unsigned(ValueToShift, 32), NrToShift));
end shiftRightInt;
The code:
stim: process
begin
wait for 10 ns;
log2ceil(3);
wait;
end process;
is describing a procedure call, but log2ceil is a function. Procedures have a return type of void, whereas your function returns an integer.
You need to provide a dummy variable to collect the return value:
stim: process
variable dummy : natural;
begin
wait for 10 ns;
dummy := log2ceil(3);
wait;
end process;
The error message is a bit cryptic, but correct!
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__
I am trying to make better use of VHDL protected types, so I threw together the following test (just for illustration, of course - my actual use case is considerably more complex):
type prot_type1 is protected
procedure set (new_data : integer);
impure function get return integer;
end protected prot_type1;
type prot_type1 is protected body
variable data : integer := 0;
procedure set (new_data : integer) is
begin
data := new_data;
end procedure set;
impure function get return integer is
begin
return data;
end function get;
end protected body prot_type1;
This compiles. However, the following line does not:
type prot_type1_array is array (natural range <>) of prot_type1;
Ashenden says (3rd Ed., p. 589) "Protected types cannot be used as elements of ... composite types". This is unfortunate. I was hoping to be able to create another protected type with the body:
type prot_type2 is protected body
variable data : prot_type1_array(0 to 3);
procedure set (idx : natural; new_data : integer) is
begin
data(idx).set(new_data);
end procedure set;
...
end protected body prot_type2;
and avoid duplicating the code in prot_type1.set() (which is admittedly trivial in this case, but would be much more complex in my actual use case). It seems my only choice, though, is (1) to basically rewrite the entirety of prot_type1 except with an array type for my private variable. Or (2), flatten the array internally, like:
type prot_type2 is protected body
variable data0 : prot_type1;
variable data1 : prot_type1;
procedure set (idx : natural; new_data : integer) is
begin
case idx is
when 0 =>
data0.set(new_data);
when 1 =>
data1.set(new_data);
when others =>
-- handle exceptions here
end case;
end procedure set;
...
end protected body prot_type2;
This works, but is mildly undesirable for small arrays, and is extremely undesirable for large arrays. Is there another way?
here is a suggestion based on Morten Zilmer comment. The prot1_type get an access on integer instead of a unique integer. I have used function append, remove and get to manage the integer values.
Here is the code :
type array_int is array (natural range <>) of integer;
type a_integer is access array_int;
type prot_type1 is protected
-- add a new value at the end of the vector
procedure append (new_data : integer);
-- remove a value from the vector, return 0 ik OK, -1 is the item doesn't exist
impure function remove (index : integer) return integer;
-- return the integer value of the item
impure function get(index : integer) return integer;
end protected prot_type1;
type prot_type1 is protected body
variable data : a_integer;
procedure append(new_data : integer) is
variable temp : a_integer;
begin
-- create a temporary vector with the new values
temp := new array_int'(data.all & new_data);
-- free memory of the real vector
Deallocate(data);
-- reallocate the real vector with the good values
data := new array_int'(temp.all);
-- free memory of the temporary vector
Deallocate(temp);
end procedure append;
impure function remove(index : integer) return integer is
variable temp : a_integer;
begin
if (index > data'length-1 or index < 0) then -- not sure if the vector is (0 to length -1) or (1 to length). to be tested !!!
return -1;
else
-- create a temporary vector with the new values
temp := new array_int'(data(0 to index-1) & data(index+1 to data'length-1));
-- free memory of the real vector
Deallocate(data);
-- reallocate the real vector with the good values
data := new array_int'(temp.all);
-- free memory of the temporary vector
Deallocate(temp);
return 0;
end if;
end function remove;
impure function get(index : integer) return integer is
begin
return data(index);
end function get;
end protected body prot_type1;
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.
For a function to return a value in Pascal the assignment FunctionName := SomeVal; is used. I assume it doesn't stop the function execution in that exact place as return in C does. Is there something similar to C return in Pascal? (I'm using FreePascal compiler)
You can use the Exit procedure.
function Foo (Value : integer) : Integer;
begin
Exit(Value*2);
DoSomethingElse(); // This will never execute
end;
The return code of the end of every program is stored in the EAX register. So you can use Assembly inline on Pascal to return wherever you want to end the program running using!
asm
mov eax,%ERROLEVEL%
end;
I think you can use either the function name itself, "result", or Exit(), but I've only used the result identifier, so don't know if the others will work for you:
function Foo(a, b: Integer): Integer;
begin
result := a + b;
end;
Hope that helps ^^
In canonical pascal (without keyword Exit) you can emulate return via goto:
function Foo (Value : integer) : boolean;
label return;
begin
if Value < 0 then
begin
Foo := false;
goto return;
end;
Calc();
Foo := true;
return:
end;