need VHDL equivalent of c-language "strtok" and "strcmp" functions that can operator on vhdl string type - vhdl

I'm trying to write a stimulus reader for vhdl testbench. it needs to read a text command and two text operands delimited by whitespaces in a text file.
entity tb is
end entity;
architecture sim is tb of
begin
process
variable L : line;
file STIMFILE : test is in "stim.txt";
variable s1 : string;
variable s2 : string;
variable s3 : string;
begin
while not endfile(STIMFILE) loop
readline(STIMFILE, L);
s1 := strtok(L, "\n\t ");
s2 := strtok(0, "\n\t ");
s3 := strtok(0, "\n\t ");
if (strcmp(s1, "ADDXYZ") = '1') then
report "ADDXYZ " & s2 & " " & s3;
end if;
end loop;
end process;
end architecture;
How would I do this in VHDL?
so far i have a function for strcmp:
FUNCTION strcmp(s1: STRING; s2: STRING) --string compare
RETURN BOOLEAN IS
BEGIN
IF(s1'LENGTH /= s2'LENGTH) THEN
RETURN FALSE;
ELSE
FOR i IN s1'RANGE LOOP
IF(s1(i) /= s2(i)) THEN
RETURN FALSE;
END IF;
END LOOP;
RETURN TRUE;
END IF;
END; --function strcmp

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
package pkg_fancy_strings is
-- Max Line length to tokenize
constant STRTOK_MAX : natural := 200;
procedure line2string(L: inout line; S: out string);
function is_space (c:character) return boolean;
function is_nul (c:character) return boolean;
-- structure to maintain state between strtok calls
type strtok_t is
record
str : string(1 to STRTOK_MAX); --input string
pos : natural; --input string iterator position
more : boolean; --call strtok_next until more equals false stop
tok : string(1 to STRTOK_MAX); --token string value
len : natural; --token string length
valid : boolean; --token string valid
error : natural; --token string had an error, ex: truncation
end record;
-- return string of length n padded with nul if less than n originally
function strpad(n: natural; x: string; rm_comment: boolean) return string;
-- Initialize strtok structure with unparsed string
function strtok_init(s : string) return strtok_t;
-- tokenize string in strtok buffer
function strtok_next(t: strtok_t) return strtok_t;
-- compare strings
function strcmpi(s1: string; s2: string) return boolean;
-- convert string into integer
function str2integer(s: string) return integer;
end package;
package body pkg_fancy_strings is
function is_space(c:character) return boolean is
begin
if (c = ' ') then
return true;
end if;
if (c <= character'val(13)) then
return true;
end if;
return false;
end function;
function is_nul(c:character) return boolean is
begin
if (c = character'val(0)) then
return true;
end if;
return false;
end function;
procedure line2string(L: inout line; S: out string) is
variable good :boolean;
variable ch :character;
variable str :string(1 to STRTOK_MAX);
variable i :integer := 1;
variable len :integer := 0;
begin
-- Zero Line Buffer
for i in 1 to str'length loop
str(i) := character'val(0);
end loop;
len := 1;
loop
read(L, ch, good);
if (good = false) then
exit;
end if;
str(len) := ch;
len := len + 1;
if (is_nul(ch)) then
exit;
end if;
if (len > str'length-1) then
exit;
end if;
end loop;
S := str;
end procedure;
-- return string of length n padded with nul if less than n originally
function strpad(n: natural; x: string; rm_comment: boolean)
return string is
variable r: string(1 to n);
variable stop: natural;
begin
for i in 1 to n loop
r(i) := character'val(0);
end loop;
stop := x'length;
if (stop >= n) then
stop := n-1;
end if;
for i in 1 to stop loop
-- ignore everything on line after '#'
if (x(i) = '#') then
exit;
end if;
r(i) := x(i);
end loop;
return r;
end function;
-- Initialize strtok structure with unparsed string
function strtok_init(
s : string
) return strtok_t is
variable t :strtok_t;
variable i :natural;
variable ch :character;
begin
t.str := strpad(STRTOK_MAX, s, true);
t.pos := 1;
t.more := true;
t.valid := false; --tok string not valid yet
t.error := 0;
return t;
end function;
-- tokenize string in strtok buffer
function strtok_next(
t: strtok_t
) return strtok_t is
variable ch :character := character'val(0);
variable i :natural := 0;
variable r :strtok_t;
begin
r := t;
-- Zero t.tok
r.len := 0;
for i in 1 to r.tok'length loop
r.tok(i) := character'val(0);
end loop;
-- Eat Spaces
loop
if (r.pos > r.str'length-1) then
r.valid := false;
r.more := false;
return r;
end if;
ch := r.str(r.pos);
if (is_nul(ch) = true) then
r.valid := false;
r.more := false;
return r;
end if;
if (is_space(ch) = false) then
exit;
else
r.pos := r.pos + 1;
end if;
end loop;
-- Save Token
i := 1;
loop
if (i > r.tok'length-1) then
r.valid := true;
r.more := true;
r.error := 1;
return r;
end if;
if ((r.pos > r.str'length) or is_nul(r.str(r.pos))) then
r.valid := (r.tok'length /= 0);
r.more := false;
r.error := 0;
return r;
end if;
ch := r.str(r.pos);
if (is_space(ch)) then
r.valid := true;
r.more := true;
r.error := 0;
return r;
else
r.tok(i) := ch;
r.len := i;
i := i + 1;
r.pos := r.pos + 1;
end if;
end loop;
-- shouldn't get here
r.error := 2;
r.valid := false;
r.more := false;
return r;
end function;
--string compare
function strcmpi(s1: string; s2: string)
return boolean is
variable max: natural := 0;
variable end_s1:boolean;
variable end_s2:boolean;
variable nul_s1:boolean;
variable nul_s2:boolean;
begin
if (s1'length >= s2'length) then max := s1'length; else max := s2'length; end if;
for i in 1 to max loop
end_s1 := (i > s1'length);
end_s2 := (i > s2'length);
if (end_s1 and end_s2) then return true; end if;
if (end_s1) then
nul_s2 := (s2(i) = character'val(0));
if (nul_s2) then return true; end if;
end if;
if (end_s2) then
nul_s1 := (s1(i) = character'val(0));
if (nul_s1) then return true; end if;
end if;
nul_s1 := (s1(i) = character'val(0));
nul_s2 := (s2(i) = character'val(0));
if (nul_s1 and nul_s2) then return true; end if;
if(s1(i) /= s2(i)) then
return false;
end if;
end loop;
return true;
end function;
-- read next whitespace delimited string
-- return string is terminated with null's
procedure read_string(L: inout Line; s: out string(1 to 80);
good: out boolean) is
variable c :character;
variable r :string(1 to 80);
variable i :natural;
begin
r := (others => character'val(0));
s := (others => character'val(0));
-- Skip WhiteSpace
loop
read(L, c, good);
report "c:" & character'image(c);
if (good = False) then
good := False;
return;
elsif ((c = ' ') or (c <= character'val(13)) ) then
next;
end if;
exit;
end loop;
-- Read Until Non-Whitespace
i := 1;
r(i) := c;
i := i+1;
loop
read(L, c, good);
if (good = false) then
s := r;
good := True;
return;
elsif ((c = ' ') or (c <= character'val(13)) ) then
s := r;
good := True;
return;
else
r(i) := c;
i := i + 1;
end if;
end loop;
end procedure;
end package body;
-- EXAMPLE OF FANCY STRINGS USAGE:
--
-- use work.pkg_fancy_strings.all;
--
-- entity tb is
-- end entity;
--
-- architecture rtl of tb is
-- begin
-- process
-- file stim_in :text open read_mode is "testcase1.txt";
-- variable L :line;
-- variable sbuf :string(1 to STRTOK_MAX);
-- variable slen :natural;
-- variable t :strtok_t;
-- begin
-- t := strtok_init(" mary had a little #lamb");
-- while(t.more) loop
-- t := strtok_next(t);
-- if (t.valid) then
-- report ">>" & t.tok(1 to t.len) & "<<";
-- end if;
-- end loop;
--
-- report "done." severity failure;
-- end process;
-- end architecture;
--

Related

Unable to synthesize code. GHDL throws an array bounds error

I'm new to Stack Overflow and to VHDL too. I have completed the logic of an N-bit shifter and tested it using a testbench. But am unable to synthesize it.
While synthesizing I get an error saying "left and right bounds of a slice must be either constant or dynamic" (it is in line 37 and 45, also visible in the attached picture). Can somebody please tell what I need to fix (and perhaps how)?
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;
use work.types.all;
use work.functions.all;
use work.casts.all;
entity logbarrel is
generic ( N : natural := 32
);
port ( a : in std_logic_vector(N-1 downto 0); -- input data word
func : in std_logic_vector( 2 downto 0); -- ctrl word (000=>>, 001=<<, 010=*>, 011=<*, 100=>>>)
shamt : in std_logic_vector(log2(N)-1 downto 0); -- shift amount
y : out std_logic_vector(N-1 downto 0) -- output data word
);
end;
architecture rtl1 of logbarrel is
begin
process(all)
constant stage : natural := log2(N); -- number of stages
variable rotr : arr_of_slv(stage-1 downto 0)(N-1 downto 0); -- rotated words
variable mask : arr_of_slv( N-1 downto 0)(N-1 downto 0); -- masks for all possible shifts
variable sh : std_logic_vector(shamt'range) := (others =>'0'); -- used as actual shift amount
begin
-- left or right shift
if to_int(func) = 1 OR to_int(func) = 3 then --checking for left, easier
sh := (others =>'0'); --left makes sh = 000
else
sh := (0 => '1', others =>'0'); --right makes sh = 001
end if;
-- 1st stage
if shamt(0) = '1' then -- shift if needed
rotr(stage-1) := a(N*(1-to_int(sh))+2*to_int(sh)-2 downto 0) & a(N-1 downto N*(1-to_int(sh))+2*to_int(sh)-2+1); --rotate
else
rotr(stage-1) := a; --no shift
end if;
-- i-th stage
for i in stage-2 downto 0 loop
if shamt(stage-i-1) = '1' then -- shift if needed
rotr(i) := rotr(i+1)(N*(1-to_int(sh))+(2*to_int(sh)-1)*2**(stage-i-1)-1 downto 0) & rotr(i+1)(N-1 downto N*(1-to_int(sh))+(2*to_int(sh)-1)*2**(stage-i-1)); --rotate
else
rotr(i) := rotr(i+1); --no shift at this level
end if;
end loop;
-- mask the rotated data if needed
for i in 0 to N-1 loop
case func is
when "000" | "100" => mask(i) := (N-1 downto N-1-i+1 => '0') & (N-i-1 downto 0 => '1'); -- right shift
when "001" => mask(i) := (N-1 downto i => '1') & (i-1 downto 0 => '0'); -- left shift
when "010" | "011" => mask(i) := (others => '1'); -- rotate
when others => mask(i) := (others => '0'); -- this case 101 should not occur
end case;
end loop;
-- output masking
y <= (N-1 downto N-to_int(shamt) => a(N-1)) & (rotr(0)(N-to_int(shamt)-1 downto 0) AND mask(to_int(shamt))(N-to_int(shamt)-1 downto 0)) when func = "100" else -- masking for arith. right shift
rotr(0) AND mask(to_int(shamt)); -- masking for all other cases
end process;
end;
Here's the original code skeleton, on which I had to change the lines only where TODO was written
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;
use work.types.all;
use work.functions.all;
use work.casts.all;
entity logbarrel is
generic ( N : natural := 32
);
port ( a : in std_logic_vector(N-1 downto 0); -- input data word
func : in std_logic_vector( 2 downto 0); -- ctrl word (000=>>, 001=<<, 010=*>, 011=<*, 100=>>>)
shamt : in std_logic_vector(log2(N)-1 downto 0); -- shift amount
y : out std_logic_vector(N-1 downto 0) -- output data word
);
end;
architecture rtl1 of logbarrel is
begin
process(all)
constant stage : natural := log2(N); -- number of stages
variable rotr : arr_of_slv(stage-1 downto 0)(N-1 downto 0); -- rotated words
variable mask : arr_of_slv( N-1 downto 0)(N-1 downto 0); -- masks for all possible shifts
variable sh : std_logic_vector(shamt'range) := (others =>'0'); -- used as actual shift amount
begin
-- left or right shift
if TODO then
sh := TODO
else
sh := TODO
end if;
-- 1st stage
if TODO then -- shift if needed
rotr(stage-1) := TODO
else
rotr(stage-1) := TODO
end if;
-- i-th stage
for i in TODO loop
if TODO then -- shift if needed
rotr(i) := TODO
else
rotr(i) := TODO
end if;
end loop;
-- mask the rotated data if needed
for i in 0 to N-1 loop
case func is
when TODO => mask(i) := TODO -- right shift
when TODO => mask(i) := TODO -- left shift
when TODO => mask(i) := TODO -- rotate
when others => mask(i) := TODO -- this case 101 should not occur
end case;
end loop;
-- output masking
y <= TODO when TODO else -- masking for arith. right shift
TODO -- masking for all other cases
end process;
end;
And here is the testbench used to test this as an instantiation. (It also initializes another such entity, I'll paste its code below, in case you want it).
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.casts.all;
use work.functions.all;
entity shifter_tb is
generic( N : natural := 32;
ainit: natural := 16#72345678#);
end;
architecture test of shifter_tb is
signal a, y1, y2 : std_logic_vector(N-1 downto 0):= to_slv(ainit,N);
signal func : std_logic_vector( 2 downto 0);
signal shamt : std_logic_vector(log2(N)-1 downto 0);
begin
stimuli: process
begin
-- for i in 0 to 2**a'length-1 loop
-- a <= to_slv(i, a'length);
for j in 0 to 4 loop --2**func'length-1 loop
func <= to_slv(j, func'length);
for k in 0 to 2**shamt'length-1 loop
shamt <= to_slv(k, shamt'length);
wait for 10 ns;
case func is
when "000" => assert (y1 = y2)
report strs(y1) & strs(y2) & " = " &
str(a) & " >> " & strs(to_int(shamt)) & str(func) ;
when "001" => assert (y1 = y2)
report strs(y1) & str(y2) & " = " &
str(a) & " << " & strs(to_int(shamt)) & str(func) ;
when "010" => assert (y1 = y2)
report strs(y1) & str(y2) & " = " &
str(a) & " *> " & strs(to_int(shamt)) & str(func) ;
when "011" => assert (y1 = y2)
report strs(y1) & str(y2) & " = " &
str(a) & " <* " & strs(to_int(shamt)) & str(func) ;
when others => assert (y1 = y2)
report strs(y1) & str(y2) & " = " &
str(a) & " >>> " & strs(to_int(shamt)) & str(func) ;
end case;
end loop;
end loop;
--end loop;
wait;
end process;
shifter1_inst: entity work.shifter
generic map (N => N)
port map (a, func, shamt, y1);
shifter2_inst: entity work.logbarrel
generic map (N => N)
port map (a, func, shamt, y2);
end;
The other entity called shifter (the golden model):
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;
use work.types.all;
use work.functions.all;
use work.casts.all;
entity shifter is
generic ( N : natural := 32
);
port ( a : in std_logic_vector(N-1 downto 0);
func : in std_logic_vector( 2 downto 0);
shamt : in std_logic_vector(log2(N)-1 downto 0);
y : out std_logic_vector(N-1 downto 0)
);
end;
architecture rtl of shifter is
begin
process(all)
variable rotr, rotl, ashr, lshr, lshl : arr_of_slv(N-1 downto 0)(N-1 downto 0);
begin
lshr(0) := a; lshl(0) := a; ashr(0) := a; rotr(0) := a; rotl(0) := a;
for i in 1 to N-1 loop
lshr(i) := (i-1 downto 0 => '0') & a(N-1 downto i);
lshl(i) := a((N-1)-i downto 0) & (i-1 downto 0 => '0');
ashr(i) := (i-1 downto 0 => a(N-1)) & a(N-1 downto i);
rotr(i) := a( i-1 downto 0) & a(N-1 downto i);
rotl(i) := a((N-1)-i downto 0) & a(N-1 downto N-i);
end loop;
y <= lshr(to_int(shamt)) when func = "000" else
lshl(to_int(shamt)) when func = "001" else
rotr(to_int(shamt)) when func = "010" else
rotl(to_int(shamt)) when func = "011" else
ashr(to_int(shamt));
end process;
end;
And finally the header files (not sure what they're called in the VHDL world) that are being used in the testbench and the instantiated architectures:
types:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package types is
constant width : integer := 32;
subtype byte is natural range 7 downto 0;
subtype logic is std_logic;
subtype byteT is std_logic_vector(byte);
subtype word is std_logic_vector(width-1 downto 0);
subtype uword is unsigned(width-1 downto 0);
subtype sword is signed (width-1 downto 0);
type arr_of_slv is array (natural range <>) of std_logic_vector;
type matrix is array (natural range <>, natural range <>) of std_logic;
end;
package body types is
end;
functions:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.casts.all;
use work.types.all;
package functions is
function log2 (a : natural) return natural;
function replicate(s : std_logic; n: integer) return std_logic_vector;
function decode(arg : std_logic_vector) return std_logic_vector;
function mux(sel, x : std_logic_vector) return std_logic;
function mux(sel : std_logic_vector; x : arr_of_slv) return std_logic_vector;
-- function mux(sel : std_logic_vector; x : arr_of_slv (0 TO M-1)(N-1 downto 0)) return std_logic_vector is
function reduce(Inputs : std_logic_vector ) return std_logic_vector;
--function reduction( Inputs : arr_of_slv ) return std_logic_vector;
end;
package body functions is
function log2 (a: natural) return natural IS
variable val : natural := a;
variable log : natural := 0;
begin
for i in a downto 0 loop
val := val / 2;
if val > 0 then
log := log + 1;
end if;
end loop;
return log;
end;
function replicate(s: std_logic; n: integer) return std_logic_vector is
variable r : std_logic_vector(n-1 downto 0);
begin
for i in 0 to n-1 loop
r(i) := s;
end loop;
return r;
end;
function decode (arg : std_logic_vector) return std_logic_vector is
variable res : std_logic_vector((2**arg'length)-1 downto 0);
begin
res(to_int(arg)) := '1';
return res;
end;
function mux( sel, x : std_logic_vector ) return std_logic is
begin
return x(to_int(sel));
end;
function mux( sel : std_logic_vector; x: arr_of_slv) return std_logic_vector is
begin
-- assert Inputs'length <= 2 ** sel'length
-- report "Inputs size: " & integer'image(Inputs'length) & " is too big for the select";
return x(to_int(sel));
end;
-- function mux(sel : std_logic_vector; x : arr_of_slv (0 TO M-1)(N-1 downto 0)) return std_logic_vector is
-- variable y : std_logic_vector(x(0)'length-1 downto 0);
-- begin
-- y := x(to_int(sel));
-- return y;
-- end;
function reduce( Inputs : std_logic_vector ) return std_logic_vector is
constant N : integer := Inputs'length;
variable inp : std_logic_vector(N-1 downto 0);
begin
inp := Inputs;
if(N = 4) then
return inp(1 downto 0) xor inp(3 downto 2);
else
return reduce(inp((N-1) downto N/2)) xor reduce(inp(((N/2)-1) downto 0));
end if;
end;
-- function reduction( Inputs: arr_of_slv ) return std_logic_vector is
-- constant N : integer := Inputs'length;
-- variable inp : arr_of_slv(N-1 downto 0, 12-1 downto 0);
-- begin
-- --return x"1234"; --Inputs(0);
-- inp := Inputs;
-- if N = 1 then
-- return inp(0);
-- elsif N > 1 then
-- return reduction(inp(N-1 downto N/2)) xor reduction(inp(N/2-1 downto 0));
-- end if;
-- end;
end;
casts:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package casts is
function to_int(arg: std_logic_vector) return natural;
function to_sint(arg: std_logic_vector) return natural;
function to_int(arg: unsigned) return natural;
function to_int(arg: signed) return natural;
function to_slv(arg: integer; bits: integer) return std_logic_vector;
function to_slv(arg: unsigned) return std_logic_vector;
function to_slv(arg: signed) return std_logic_vector;
function to_slv(arg: std_ulogic_vector) return std_logic_vector;
function to_sslv(arg: integer; bits: natural) return std_logic_vector;
function str(s: std_ulogic_vector) return string;
function str(s: integer) return string;
function strs(s: std_ulogic_vector) return string;
function strs(s: integer) return string;
function strs(s: std_logic) return string;
function str(s: std_logic) return character;
end;
package body casts is
subtype logic is std_logic;
type logic_vector is array (natural range <>) of std_logic;
function to_int (arg: std_logic_vector) return natural IS
begin
return to_integer(unsigned(arg));
end;
function to_sint (arg: std_logic_vector) return natural IS
variable x : signed(arg'range);
begin
x := signed(arg);
return to_integer(x);
end;
function to_int (arg: unsigned) return natural IS
begin
return to_integer(arg);
end;
function to_int (arg: signed) return natural IS
begin
return to_integer(arg);
end;
function to_slv(arg: integer; bits: integer) return std_logic_vector is
begin
return std_logic_vector(to_unsigned(arg,bits));
end;
function to_sslv(arg: integer; bits: natural) return std_logic_vector is
begin
return std_logic_vector(to_signed(arg,bits));
end;
function to_slv(arg: unsigned) return std_logic_vector IS
begin
return std_logic_vector(arg);
end;
function to_slv(arg: std_ulogic_vector) return std_logic_vector IS
begin
return to_stdlogicvector(arg);
end;
function to_slv(arg: signed) return std_logic_vector IS
begin
return std_logic_vector(arg);
end;
function chr2sl (ch: in character) return std_logic is
begin
case ch is
when 'U' | 'u' => return 'U';
when 'X' | 'x' => return 'X';
when '0' => return '0';
when '1' => return '1';
when 'Z' | 'z' => return 'Z';
when 'W' | 'w' => return 'W';
when 'L' | 'l' => return 'L';
when 'H' | 'h' => return 'H';
when '-' => return '-';
when OTHERS => assert false
report "Illegal Character found" & ch
severity error;
return 'U';
end case;
end;
function str2sl (s: string) return std_logic_vector is
variable vector: std_logic_vector(s'LEFT - 1 DOWNTO 0);
begin
for i in s'RANGE loop
vector(i-1) := chr2sl(s(i));
end loop;
return vector;
end;
function to_char(s: std_ulogic) return character is
begin
case s is
when 'X' => return 'X';
when '0' => return '0';
when '1' => return '1';
when 'Z' => return 'Z';
when 'U' => return 'U';
when 'W' => return 'W';
when 'L' => return 'L';
when 'H' => return 'H';
when '-' => return '-';
end case;
end;
function str(s: std_ulogic_vector) return string is
variable ret:string(1 to s'length);
variable K : integer:= 1;
begin
for J in s'range loop
ret(K) := to_char(s(J));
K := K + 1;
end loop;
return ret;
end;
function strs(s: std_ulogic_vector) return string is
begin
return str(s) & ' ';
end;
function str(s: std_logic) return character is
begin
return to_char(s);
end;
function strs(s: std_logic) return string is
begin
return str(s) & ' ';
end;
function to_nstring(s: natural) return string is
variable ret, iret : string(1 to 16);
variable k, j : integer;
variable s1, s2, s3: natural := 0;
begin
s1 := s;
ret(1) := '0';
k := 1;
while (s1 > 0 and K < 16) loop
s2 := s1 / 10;
s3 := s1 - (s2 * 10);
if (s3 = 0) then
ret(k) := '0';
elsif (s3 = 1) then
ret(k) := '1';
elsif (s3 = 2) then
ret(k) := '2';
elsif (s3 = 3) then
ret(k) := '3';
elsif (s3 = 4) then
ret(k) := '4';
elsif (s3 = 5) then
ret(k) := '5';
elsif (s3 = 6) then
ret(k) := '6';
elsif (s3 = 7) then
ret(k) := '7';
elsif (s3 = 8) then
ret(k) := '8';
elsif (s3 = 9) then
ret(k) := '9';
end if;
k := k + 1;
s1 := s2;
end loop;
if (k > 1) then
k := k-1;
end if;
J := 1;
while (k > 0) loop
iret(j) := ret(k);
k := k-1;
j := j+1;
end loop;
return iret;
end;
function str(s: integer) return string is
begin
if (s < 0) then
return "-" & to_nstring(-s);
else
return to_nstring(s);
end if;
end;
function strs(s: integer) return string is
begin
return str(s) & ' ';
end;
end;

In VHDL, is it synthesizable to use variables other than i,j in FOR loops?

I am a freshman to VHDL, and am learning it by myself.
As a practice, I am doing a data process (calculate spatial average data) to a 320*240 graphic data.
I am considering the way like this :
1. Create a 10*10 cluster of homogeneous simple elements, where each element calculates the average value of a single pixel.
2. Each time connect one portion of the graphic data to the 10*10 cluster, make calculation then change connection. And after 32*24 times of operation, the work is finished.
But I got synthesizing problem in step 2. In order to dynamically change connections, I have to use variables other than i,j in FOR statement. But it seems to be not synthesizable for this pattern of using FOR loop(the synthesizing time goes unlimited.).
Would anyone help me out of this?
The problematic code segment is as follows:
FSM1: process(clk) is
variable cnt1 : integer range 0 to 5 := 0;
variable cnt2 : integer range 0 to 32-1 := 0;
variable cnt3 : integer range 0 to 24-1 := 0;
variable BlockBaseX : integer range 0 to 310 := 0;
variable BlockBaseY : integer range 0 to 230 := 0;
variable varPRLLc_ave_indata : std_logic_vector(400*8-1 downto 0) :=(others=>'0');
begin
if rising_edge(clk) then
PRLLc_ave_indata <= varPRLLc_ave_indata;
if stateFSM2A = s3 then
case stateFSM1 is
when s1 =>
if do_InitCopy = '1' then
workGD <= baseGD;
end if;
stateFSM1 <= s2;
when s2 =>
if do_NumProc = '1' then
case cnt1 is
when 0 =>
BlockBaseX := cnt2*10; -- 10, 20, ..., 310
BlockBaseY := cnt3*10; -- 10, 20, ..., 230
for i in 0 to 9 loop
for j in 0 to 9 loop -- PRLLc_ave_indata (400*8-1 downto 0);
if i = 9 then
if j = 9 then
varPRLLc_ave_indata((i*10+j)*32+31 downto (i*10+j)*32) := workGD(BlockBaseY+i, BlockBaseX+j) & workGD(BlockBaseY+i, BlockBaseX+j-1) & workGD(BlockBaseY+i-1, BlockBaseX+j) & workGD(BlockBaseY+i-1, BlockBaseX+j-1);
else
varPRLLc_ave_indata((i*10+j)*32+31 downto (i*10+j)*32) := workGD(BlockBaseY+i, BlockBaseX+j) & workGD(BlockBaseY+i, BlockBaseX+j+1) & workGD(BlockBaseY+i-1, BlockBaseX+j) & workGD(BlockBaseY+i-1, BlockBaseX+j+1);
end if;
else
if j = 9 then
varPRLLc_ave_indata((i*10+j)*32+31 downto (i*10+j)*32) := workGD(BlockBaseY+i, BlockBaseX+j) & workGD(BlockBaseY+i, BlockBaseX+j-1) & workGD(BlockBaseY+i+1, BlockBaseX+j) & workGD(BlockBaseY+i+1, BlockBaseX+j-1);
else
varPRLLc_ave_indata((i*10+j)*32+31 downto (i*10+j)*32) := workGD(BlockBaseY+i, BlockBaseX+j) & workGD(BlockBaseY+i, BlockBaseX+j+1) & workGD(BlockBaseY+i+1, BlockBaseX+j) & workGD(BlockBaseY+i+1, BlockBaseX+j+1);
end if;
end if;
end loop;
end loop;
-- If the above For loop is replaced with the following one(BlockBaseX,BlockBaseY are removed), the synthesis can be accomplished.
-- for i in 0 to 9 loop
-- for j in 0 to 9 loop -- PRLLc_ave_indata (400*8-1 downto 0);
-- if i = 9 then
-- if j = 9 then
-- varPRLLc_ave_indata((i*10+j)*32+31 downto (i*10+j)*32) := workGD(i, j) & workGD(i, j-1) & workGD(i-1, j) & workGD(i-1, j-1);
-- else
-- varPRLLc_ave_indata((i*10+j)*32+31 downto (i*10+j)*32) := workGD(i, j) & workGD(i, j+1) & workGD(i-1, j) & workGD(i-1, j+1);
-- end if;
-- else
-- if j = 9 then
-- varPRLLc_ave_indata((i*10+j)*32+31 downto (i*10+j)*32) := workGD(i, j) & workGD(i, j-1) & workGD(i+1, j) & workGD(i+1, j-1);
-- else
-- varPRLLc_ave_indata((i*10+j)*32+31 downto (i*10+j)*32) := workGD(i, j) & workGD(i, j+1) & workGD(i+1, j) & workGD(i+1, j+1);
-- end if;
-- end if;
-- end loop;
-- end loop;
when 1 =>
when 2 =>
when 3 => -- got answer
when 4 =>
when others =>
end case;
if cnt1 < 5 then
cnt1 := cnt1 + 1;
else
cnt1 := 0;
if cnt2 < 32 then
cnt2 := cnt2 + 1;
else
cnt2 := 0;
if cnt3 < 24 then
cnt3 := cnt3 + 1;
else
cnt3 := 0;
stateFSM1 <= s3;
end if;
end if;
end if;
else
end if;
when s3 => -- after detecting a signal indicating OV7670 module has come to the requested point,
-- launch the pump out function - pump data from WorkGD to a stream
when others =>
end case;
else
stateFSM1 <= s1;
end if;
end if;
end process FSM1;

How to write an integer to stdout as hexadecimal in VHDL?

I can print an integer as decimal to stdout with:
library std;
use std.textio.all;
entity min is
end min;
architecture behav of min is
begin
process is
variable my_line : line;
begin
write(my_line, 16);
writeline(output, my_line);
wait;
end process;
end behav;
which outputs:
16
But how to output instead either:
10
0x10
Assuming an integer i, and VHDL-2008, you could use:
write(output, integer'image(i) & LF); -- Plain integer value
write(output, "0x" & to_hstring(to_signed(i, 32)) & LF); -- Hexadecimal representation
You need to have use std.textio.all; for this to work. Change the 32 to reduce the length of the hex value. I chose 32 so that it can represent any integer value in most simulators.
These will also work for report statements, e.g.
report "i = 0x" & to_hstring(to_signed(i, 32));
There is no standard library implementation, but for example our PoC-Libary has several formatting function in the PoC.Strings package. On top of that, we have a to_string(...) function, which accepts a format character like h for hexadecimal outputs.
How to write such an integer to hex conversion?
Convert the INTEGER into a binary representation
Group the binary value into 4-bit groups
translate each group into an integer/alpha in range 0..F
prepend 0x if wished
So here is a wrapper to convert the integer to a binary representation:
-- format a natural as HEX string
function raw_format_nat_hex(Value : NATURAL) return STRING is
begin
return raw_format_slv_hex(std_logic_vector(to_unsigned(Value, log2ceil(Value+1))));
end function;
And now the grouping and transformation
-- format a std_logic_vector as HEX string
function raw_format_slv_hex(slv : STD_LOGIC_VECTOR) return STRING is
variable Value : STD_LOGIC_VECTOR(4*div_ceil(slv'length, 4) - 1 downto 0);
variable Digit : STD_LOGIC_VECTOR(3 downto 0);
variable Result : STRING(1 to div_ceil(slv'length, 4));
variable j : NATURAL;
begin
Value := resize(slv, Value'length);
j := 0;
for i in Result'reverse_range loop
Digit := Value((j * 4) + 3 downto (j * 4));
Result(i) := to_HexChar(unsigned(Digit));
j := j + 1;
end loop;
return Result;
end function;
-- convert an unsigned value(4 bit) to a HEX digit (0-F)
function to_HexChar(Value : UNSIGNED) return CHARACTER is
constant HEX : STRING := "0123456789ABCDEF";
begin
if (Value < 16) then
return HEX(to_integer(Value)+1);
else
return 'X';
end if;
end function;
-- return TRUE, if input is a power of 2
function div_ceil(a : NATURAL; b : POSITIVE) return NATURAL is -- calculates: ceil(a / b)
begin
return (a + (b - 1)) / b;
end function;
-- return log2; always rounded up
function log2ceil(arg : positive) return natural is
variable tmp : positive;
variable log : natural;
begin
if arg = 1 then return 0; end if;
tmp := 1;
log := 0;
while arg > tmp loop
tmp := tmp * 2;
log := log + 1;
end loop;
return log;
end function;
Note: These functions do not prepend 0x.
You could use the hwrite procedure in the IEEE.std_logic_textio package:
library IEEE; -- ADDED
use IEEE.std_logic_1164.all; -- ADDED
use IEEE.numeric_std.all; -- ADDED
use IEEE.std_logic_textio.all; -- ADDED
library std;
use std.textio.all;
entity min is
end min;
architecture behav of min is
begin
process is
variable my_line : line;
begin
hwrite(my_line, std_logic_vector(to_unsigned(16,8))); -- CHANGED
writeline(output, my_line);
wait;
end process;
end behav;
The hwrite procedure writes a std_logic_vector to a file. So, you do have to convert your integer into a std_logic_vector, however (which also needs you to specify a number of bits in the to_unsigned function).
http://www.edaplayground.com/x/exs

My function does not return a value, and I do not understand why? VHDL

Part of code in VHDL. Workspace ISE.
My function does not return a value, and I do not understand why
Note that appears to me in ISE is "function 'con_integer14' does not always return a value."
The Function is:
function con_integer14 (vNumberSTD : std_logic_vector(14 downto 0)) return integer is
variable vCounter : integer range 0 to 16 := 0;
variable vNumberINT : integer range 0 to 16384 := 0;
begin
if (vCounter = 0) then
if ((vNumberSTD(vCounter)) = '1') then
vNumberINT := (vNumberINT+(2**vCounter));
elsif ((vNumberSTD(vCounter)) = '0') then
vNumberINT := vNumberINT;
end if;
vCounter := (vCounter+1);
elsif ((vCounter >= 1)or(vCounter < 14)) then
if ((vNumberSTD(vCounter)) = '1') then
vNumberINT := (vNumberINT+(2**vCounter));
elsif ((vNumberSTD(vCounter)) = '0') then
vNumberINT := vNumberINT;
end if;
vCounter := (vCounter+1);
elsif (vCounter = 14) then
if ((vNumberSTD(vCounter)) = '1') then
vNumberINT := (vNumberINT+(2**vCounter));
elsif ((vNumberSTD(vCounter)) = '0') then
vNumberINT := vNumberINT;
end if;
vCounter := (vCounter+1);
elsif (vCounter = 15) then
return vNumberINT;
vCounter := 0;
elsif (vCounter > 15) then
vNumberINT := 0;
vCounter := 0;
end if;
end function con_integer14;
Please help me, thank you :)
I'd go a step further and tell you your function never returns a value. I took the liberty of removing the superfluous parentheses and then took a look at the function's structure.
You're going to execute the first if statement and fail to return a value - there is no return statement in reach.
Add a test bench:
library ieee;
use ieee.std_logic_1164.all;
use work.foo.all;
entity fum is
end entity;
architecture fie of fum is
signal vNumberINT: integer range 0 to 16384;
signal vNumberSTD: std_logic_vector(14 downto 0) := "011001110111100";
begin
CALL:
process
begin
wait for 1 ns;
vNumberINT <= con_integer14(VNumberSTD);
wait for 1 ns;
wait;
end process;
end architecture;
Analyze it, elaborate it and run it:
ghdl -a something.vhdl
david_koontz#Macbook: ghdl -e fum
david_koontz#Macbook: ghdl -r fum
./fum:error: missing return in
function at something.vhdl:10
./fum:error: simulation failed
ghdl: compilation error
And we get it to tell us there's a missing return for the first if statement, that a call to it won't return a value.
To make a conversion routine that converts your std_logic_vector to integer you should use a loop statement with an iteration scheme based on the index range of the input vNumberSTD.
When the loop statement is done 'calculating' vNumberINT return VNumberINT.
Note that if you don't do anything specific about meta values for elements of vNumberSTD they will be treated as '0'.
And with some modifications:
library ieee;
use ieee.std_logic_1164.all;
package foo is
function con_integer14 (vNumberSTD: std_logic_vector(14 downto 0)) return integer;
function conv_slv_int (input: std_logic_vector) return natural;
end package;
package body foo is
function con_integer14 (vNumberSTD : std_logic_vector(14 downto 0)) return integer is
-- variable vCounter: integer range 0 to 16 := 0;
-- variable vNumberINT: integer range 0 to 16384 := 0;
variable vNumberINT: integer range 0 to 32767 := 0;
begin
-- if vCounter = 0 then
-- if vNumberSTD(vCounter) = '1' then
-- vNumberINT := vNumberINT + 2 ** vCounter;
-- elsif vNumberSTD(vCounter) = '0' then
-- vNumberINT := vNumberINT;
-- end if;
-- vCounter := vCounter + 1;
-- elsif vCounter >= 1 or vCounter < 14 then
-- if vNumberSTD(vCounter) = '1' then
-- vNumberINT := vNumberINT + 2 ** vCounter;
-- elsif vNumberSTD(vCounter) = '0' then
-- vNumberINT := vNumberINT;
-- end if;
-- vCounter := vCounter + 1;
-- elsif vCounter = 14 then
-- if vNumberSTD(vCounter) = '1' then
-- vNumberINT := vNumberINT + 2 ** vCounter;
-- elsif vNumberSTD(vCounter) = '0' then
-- vNumberINT := vNumberINT;
-- end if;
-- vCounter := vCounter + 1;
-- elsif vCounter = 15 then
-- return vNumberINT;
-- vCounter := 0;
-- elsif vCounter > 15 then
-- vNumberINT := 0;
-- vCounter := 0;
-- end if;
for i in vNumberSTD'range loop
report "vNumberSTD(" & integer'image(i) & ") = " & std_ulogic'image(vNumberSTD(i));
if vNumberSTD(i) = '1' then
vNumberINT := vNumberINT + 2 ** i;
report "vNumberINT = " & integer'image(vNumberINT);
end if;
end loop;
return vNumberINT;
end function con_integer14;
function conv_slv_int (input: std_logic_vector) return natural is
alias inp: std_logic_vector (input'LENGTH - 1 downto 0) is input;
variable int_equiv: natural range 0 to 2 ** input'LENGTH - 1 := 0;
begin
if Is_X(input) then -- announce meta values - interpreted as '0's
report "conv_slv_int input contains meta value";
end if;
for i in inp'RANGE loop
if To_bit(inp(i)) = '1' then -- convert 'H' to '1', 'L' to '0'
int_equiv := int_equiv + 2 ** i;
end if;
end loop;
return int_equiv;
end function;
end package body;
library ieee;
use ieee.std_logic_1164.all;
use work.foo.all;
entity fum is
end entity;
architecture fie of fum is
signal vNumberINT: integer range 0 to 32767;
signal vNumberSTD: std_logic_vector(14 downto 0) := "HX1001110111100";
begin
CALL:
process
begin
wait for 1 ns;
vNumberINT <= con_integer14(VNumberSTD);
wait for 1 ns;
report "vNumberINT = " &integer'image(vNumberINT);
report "conv_slv_int returns " & integer'image(conv_slv_int(vNumberSTD));
wait;
end process;
end architecture;
Notice I corrected the range of vNumberINT, it can be based on the argument to function con_integer14.
You can remove the report statements in the function. I used them to illustrate the function loop.
I added a second function showing conversion of any length std_logic_vector that will fit in a natural range. It will generate an error when called (function local variables elaborated) if the length of the input won't fit in an integer.
The second function also detects meta value inputs and converts 'H' to '1' and 'L' to '0' for each element of the input.
The use of the alias inp for he input allows us to always deal with the input as natural range, caring about length instead of left and right bounds.
When running the new test bench:
something.vhdl:45:13:#1ns:(report note): vNumberSTD(14) = 'H'
something.vhdl:45:13:#1ns:(report note): vNumberSTD(13) = 'X'
something.vhdl:45:13:#1ns:(report note): vNumberSTD(12) = '1'
something.vhdl:48:17:#1ns:(report note): vNumberINT = 4096
something.vhdl:45:13:#1ns:(report note): vNumberSTD(11) = '0'
something.vhdl:45:13:#1ns:(report note): vNumberSTD(10) = '0'
something.vhdl:45:13:#1ns:(report note): vNumberSTD(9) = '1'
something.vhdl:48:17:#1ns:(report note): vNumberINT = 4608
something.vhdl:45:13:#1ns:(report note): vNumberSTD(8) = '1'
something.vhdl:48:17:#1ns:(report note): vNumberINT = 4864
something.vhdl:45:13:#1ns:(report note): vNumberSTD(7) = '1'
something.vhdl:48:17:#1ns:(report note): vNumberINT = 4992
something.vhdl:45:13:#1ns:(report note): vNumberSTD(6) = '0'
something.vhdl:45:13:#1ns:(report note): vNumberSTD(5) = '1'
something.vhdl:48:17:#1ns:(report note): vNumberINT = 5024
something.vhdl:45:13:#1ns:(report note): vNumberSTD(4) = '1'
something.vhdl:48:17:#1ns:(report note): vNumberINT = 5040
something.vhdl:45:13:#1ns:(report note): vNumberSTD(3) = '1'
something.vhdl:48:17:#1ns:(report note): vNumberINT = 5048
something.vhdl:45:13:#1ns:(report note): vNumberSTD(2) = '1'
something.vhdl:48:17:#1ns:(report note): vNumberINT = 5052
something.vhdl:45:13:#1ns:(report note): vNumberSTD(1) = '0'
something.vhdl:45:13:#1ns:(report note): vNumberSTD(0) = '0'
something.vhdl:87:9:#2ns:(report note): vNumberINT = 5052
something.vhdl:59:17:#2ns:(report note): conv_slv_int input contains meta value
something.vhdl:88:9:#2ns:(report note): conv_slv_int returns 21436
We see that you can pass std_logic_vector to both that only conv_slt_int will interpret correctly.
Save yourself some time and use the built-in functions. If you just use numeric_std, then you can do the following:
library IEEE ;
use IEEE.numeric_std.all ;
...
signal Y_int : integer ;
signal A_slv15 : std_logic_vector(14 downto 0) ;
...
Y_int <= to_integer( unsigned( A_slv15) ) ;
If you also include numeric_std_unsigned (warning this is VHDL-2008 and may not yet be supported by your tools - try it and report a bug if it does not work) -
library IEEE ;
use IEEE.numeric_std.all ;
use IEEE.numeric_std_unsigned.all ;
...
signal Y_int : integer ;
signal A_slv15 : std_logic_vector(14 downto 0) ;
...
Y_int <= to_integer( A_slv15) ;
Like what has already been mentioned, you did not cover all possible conditions. It's always good to have a default assignment, either by having an "else" clause, or have a default before the if-elsif statement:
function con_integer14 (...) return integer is
begin
vNumberINT := -1;
if (vCounter = 0) then ...
elsif ...
elsif ... then return vNumberINT;
end if;
return vNumberINT;
end function con_integer14;
or alternatively,
function con_integer14 (...) return integer is
begin
if (vCounter = 0) then ...
elsif ...
elsif ... then return vNumberINT;
else
return vNumberINT;
end if;
end function con_integer14;
I personally prefer the first option, because some tools still complain that not all paths are covered for the second option, though in fact all paths were covered. Anyway, this is just a matter of style.
It seems to me that you are only returning a value when vCounter=15:
elsif (vCounter = 15) then return vNumberINT; ...
and this condition is not always met by your testbench (perhaps never met as analysed by David).
-daniel

Is there a way to print the values of a signal to a file from a modelsim simulation?

I need to get the values of several signals to check them against the simulation (the simulation is in Matlab). There are many values, and I want to get them in a file so that I could run it in a script and avoid copying the values by hand.
Is there a way to automatically print the values of several signals into a text file?
(The design is implemented in VHDL)
First make functions that convert std_logic and std_logic_vector to
string like:
function to_bstring(sl : std_logic) return string is
variable sl_str_v : string(1 to 3); -- std_logic image with quotes around
begin
sl_str_v := std_logic'image(sl);
return "" & sl_str_v(2); -- "" & character to get string
end function;
function to_bstring(slv : std_logic_vector) return string is
alias slv_norm : std_logic_vector(1 to slv'length) is slv;
variable sl_str_v : string(1 to 1); -- String of std_logic
variable res_v : string(1 to slv'length);
begin
for idx in slv_norm'range loop
sl_str_v := to_bstring(slv_norm(idx));
res_v(idx) := sl_str_v(1);
end loop;
return res_v;
end function;
Using the bit-wise format has the advantage that any non-01 values will show
with the exact std_logic value, which is not the case for e.g. hex
presentation.
Then make process that writes the strings from std_logic and
std_logic_vector to file for example at rising_edge(clk) like:
library std;
use std.textio.all;
...
process (clk) is
variable line_v : line;
file out_file : text open write_mode is "out.txt";
begin
if rising_edge(clk) then
write(line_v, to_bstring(rst) & " " & to_bstring(cnt_1) & " " & to_bstring(cnt_3));
writeline(out_file, line_v);
end if;
end process;
The example above uses rst as std_logic, and cnt_1 and cnt_3 as
std_logic_vector(7 downto 0). The resulting output in "out.txt" is then:
1 00000000 00000000
1 00000000 00000000
1 00000000 00000000
0 00000000 00000000
0 00000001 00000011
0 00000010 00000110
0 00000011 00001001
0 00000100 00001100
0 00000101 00001111
0 00000110 00010010
I would like to present a flexible way to convert std_logic(_vector) to a string:
First you can define two functions to convert std_logic-bits and digits to a character:
FUNCTION to_char(value : STD_LOGIC) RETURN CHARACTER IS
BEGIN
CASE value IS
WHEN 'U' => RETURN 'U';
WHEN 'X' => RETURN 'X';
WHEN '0' => RETURN '0';
WHEN '1' => RETURN '1';
WHEN 'Z' => RETURN 'Z';
WHEN 'W' => RETURN 'W';
WHEN 'L' => RETURN 'L';
WHEN 'H' => RETURN 'H';
WHEN '-' => RETURN '-';
WHEN OTHERS => RETURN 'X';
END CASE;
END FUNCTION;
function to_char(value : natural) return character is
begin
if (value < 10) then
return character'val(character'pos('0') + value);
elsif (value < 16) then
return character'val(character'pos('A') + value - 10);
else
return 'X';
end if;
end function;
And now it's possible to define two to_string functions which convert from boolean and std_logic_vector to string:
function to_string(value : boolean) return string is
begin
return str_to_upper(boolean'image(value)); -- ite(value, "TRUE", "FALSE");
end function;
FUNCTION to_string(slv : STD_LOGIC_VECTOR; format : CHARACTER; length : NATURAL := 0; fill : CHARACTER := '0') RETURN STRING IS
CONSTANT int : INTEGER := ite((slv'length <= 31), to_integer(unsigned(resize(slv, 31))), 0);
CONSTANT str : STRING := INTEGER'image(int);
CONSTANT bin_len : POSITIVE := slv'length;
CONSTANT dec_len : POSITIVE := str'length;--log10ceilnz(int);
CONSTANT hex_len : POSITIVE := ite(((bin_len MOD 4) = 0), (bin_len / 4), (bin_len / 4) + 1);
CONSTANT len : NATURAL := ite((format = 'b'), bin_len,
ite((format = 'd'), dec_len,
ite((format = 'h'), hex_len, 0)));
VARIABLE j : NATURAL := 0;
VARIABLE Result : STRING(1 TO ite((length = 0), len, imax(len, length))) := (OTHERS => fill);
BEGIN
IF (format = 'b') THEN
FOR i IN Result'reverse_range LOOP
Result(i) := to_char(slv(j));
j := j + 1;
END LOOP;
ELSIF (format = 'd') THEN
Result(Result'length - str'length + 1 TO Result'high) := str;
ELSIF (format = 'h') THEN
FOR i IN Result'reverse_range LOOP
Result(i) := to_char(to_integer(unsigned(slv((j * 4) + 3 DOWNTO (j * 4)))));
j := j + 1;
END LOOP;
ELSE
REPORT "unknown format" SEVERITY FAILURE;
END IF;
RETURN Result;
END FUNCTION;
This to_string function can convert std_logic_vectors to binary (format='b'), dicimal (format='d') and hex (format='h'). Optionally you can define a minimum length for the string, if length is greater then 0, and a fill-character if the required length of the std_logic_vector is shorter then length.
And here are the required helper function:
-- calculate the minimum of two inputs
function imin(arg1 : integer; arg2 : integer) return integer is
begin
if arg1 < arg2 then return arg1; end if;
return arg2;
end function;
-- if-then-else for strings
FUNCTION ite(cond : BOOLEAN; value1 : STRING; value2 : STRING) RETURN STRING IS
BEGIN
IF cond THEN
RETURN value1;
ELSE
RETURN value2;
END IF;
END FUNCTION;
-- a resize function for std_logic_vector
function resize(vec : std_logic_vector; length : natural; fill : std_logic := '0') return std_logic_vector is
constant high2b : natural := vec'low+length-1;
constant highcp : natural := imin(vec'high, high2b);
variable res_up : std_logic_vector(vec'low to high2b);
variable res_dn : std_logic_vector(high2b downto vec'low);
begin
if vec'ascending then
res_up := (others => fill);
res_up(vec'low to highcp) := vec(vec'low to highcp);
return res_up;
else
res_dn := (others => fill);
res_dn(highcp downto vec'low) := vec(highcp downto vec'low);
return res_dn;
end if;
end function;
Ok, this solution looks a bit long, but if you gather some of this functions -- and maybe overload them for several types -- you get an extended type converting system and in which you can convert nearly every type to every other type or representation.
Because there's more than one way to skin a cat:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- library std;
use std.textio.all;
entity changed_morten is
end entity;
architecture foo of changed_morten is
signal clk: std_logic := '0';
signal rst: std_logic := '1';
signal cnt_1: unsigned (7 downto 0);
signal cnt_3: unsigned (7 downto 0);
function string_it (arg:unsigned) return string is
variable ret: string (1 to arg'LENGTH);
variable str: string (1 to 3); -- enumerated type "'X'"
alias varg: unsigned (1 to arg'LENGTH) is arg;
begin
if arg'LENGTH = 0 then
ret := "";
else
for i in varg'range loop
str := std_logic'IMAGE(varg(i));
ret(i) := str(2); -- the actual character
end loop;
end if;
return ret;
end function;
begin
PRINT:
process (clk) is
variable line_v : line;
variable str: string (1 to 3); -- size matches charcter enumeration
file out_file : text open write_mode is "out.txt";
begin
if rising_edge(clk) then
str := std_logic'IMAGE(rst);
write ( line_v,
str(2) & " " &
string_it(cnt_1) & " " &
string_it(cnt_3) & " "
);
writeline(out_file, line_v);
end if;
end process;
COUNTER1:
process (clk,rst)
begin
if rst = '1' then
cnt_1 <= (others => '0');
elsif rising_edge(clk) then
cnt_1 <= cnt_1 + 1;
end if;
end process;
COUNTER3:
process (clk,rst)
begin
if rst = '1' then
cnt_3 <= (others => '0');
elsif rising_edge(clk) then
cnt_3 <= cnt_3 + 3;
end if;
end process;
RESET:
process
begin
wait until rising_edge(clk);
wait until rising_edge(clk);
wait until rising_edge(clk);
rst <= '0';
wait;
end process;
CLOCK:
process
begin
wait for 10 ns;
clk <= not clk;
if Now > 210 ns then
wait;
end if;
end process;
end architecture;
And mostly because Morten's expression
"" & std_logic'image(sl)(2); -- "" & character to get string
isn't accepted by ghdl, it's not an indexed name, the string is unnamed.
The issue appears to be caused by the lack of recognition of the function call ('IMAGE) being recognized as a prefix for the indexed name. For any ghdl users you'd want to use an intermediary named string target for the output of the attribute function call (shown in the string_it function and in line in the PRINT process). I submitted a bug report.
Addendum
Another way to express Morten's to_bstring(sl : std_logic) return string function is:
function to_bstring(sl : std_logic) return string is
variable sl_str_v : string(1 to 3) := std_logic'image(sl); -- character literal length 3
begin
return "" & sl_str_v(2); -- "" & character to get string
end function;
And the reason this works is because function calls are dynamically elaborated, meaning the string sl_str_v is created each time the function is called.
See IEEE Std 1076-1993 12.5 Dynamic elaboration, b.:
Execution of a subprogram call involves the elaboration of the
parameter interface list of the corresponding subprogram declaration;
this involves the elaboration of each interface declaration to create
the corresponding formal parameters. Actual parameters are then
associated with formal parameters. Finally, if the designator of the
subprogram is not decorated with the 'FOREIGN attribute defined in
package STANDARD, the declarative part of the corresponding subprogram
body is elaborated and the sequence of statements in the subprogram
body is executed.
The description of dynamic elaboration of a subprogram call has been expanded a bit in IEEE Std 1076-2008, 14.6.

Resources