vhdl: convert vector to string
Asked Answered
P

8

5

How can I convert a std_logic vector, bit_vector or any other vector to string?

Signal a,b          : UNSIGNED(7 DOWNTO 0);
SIGNAL x,y,z    : BIT_VECTOR(7 DOWNTO 0);

...

report "value: " & BIT_VECTOR'Image(x) severity note;
report "and this one: " & to_string(a) severity note;

This does not work, so how can I convert a vector to a string?

Psychodrama answered 14/3, 2013 at 10:37 Comment(0)
D
7

As you have discovered, the 'image attribute is only declared for scalar types, not arrays or records : the usual approach is to create one's own library of test utilities including to_string or image functions in a package at the start of a design, and use it throughout.

It would be perfectly possible to standardise a library of these, and you will probably find many potential "test utility" packages but none has really caught on well enough to deserve becoming a standard.

That being said, you might find the following package a useful starting point.

It encapsulates a couple of custom datatypes with operations on them. No generics, but thanks to overloading, you can use the package as though its functions were generic. (You will notice that the function bodies are incomplete though!) Extending it and adding types is easy cut&paste for the most part; and it keeps a lot of clutter out of the main design.

It may be better to separate out the type declns and the (testbench only) functions into two separate packages; Types and Types_Test_Utils. Then Types is used throughout the design, while the test utilities are only exposed to the testbench.

library IEEE;
use IEEE.numeric_std.all;

package Types is
  subtype SmallNum is UNSIGNED(7 DOWNTO 0);
  subtype BiggerNum is UNSIGNED(19 DOWNTO 0);
  subtype Bits is BIT_VECTOR(7 DOWNTO 0);

  -- and operations on these types
  -- Simulate generic procedures using overloading

  function to_string(N : Unsigned) return String;
  function to_string(N : Bits) return String;  

  procedure eq_checker (name : string; sig,should : SmallNum; at : time);
  procedure eq_checker (name : string; sig,should : Bits; at : time);

end Types;

package body Types is

function to_string(N : Unsigned) return String is
variable temp : string(1 to (N'length + 3)/4) := (others => 'x');
begin
   -- not finished!
   return temp;
end to_string;

function to_string(N : Bits) return String is
begin
   return "hello";
end to_string;

procedure eq_checker(name : string; sig,should : SmallNum; at : time) is
begin
  if (at = now) then
    if sig = should then
      report to_string(sig) & "has same value" severity note;
    else
      report to_string(sig) & "has not same value as " & to_string(should) severity note;
    end if;
  end if;
end procedure eq_checker;

procedure eq_checker(name : string; sig,should : Bits; at : time) is
begin
   null;
end procedure eq_checker;

end Types;

And a simple tester for it...

  use Work.Types.all;

  ENTITY tester IS
  END tester;

  ARCHITECTURE behavior OF tester IS 

  Signal a,b      : SmallNum := X"AA";
  Signal c        : BiggerNum := X"ABCDE";
  SIGNAL x,y      : Bits := X"BB";

  BEGIN

  process(a,x) is
  begin
     report "value: " & to_string(X) severity note;
     report "and this one: " & to_string(a) severity note;
     report "this one too: " & to_string(c) severity note;
  end process;

  END;
Deathbed answered 14/3, 2013 at 12:56 Comment(0)
S
12

The VHDL-2008 standard defines to_string for std_logic_vector and std_ulogic_vector, as well as a variety of other types. It might be easiest to use the VHDL-2008 mode (most simulators support 2008 nowadays).

Stevens answered 17/3, 2016 at 18:39 Comment(1)
Close enough. VHDL-2008 made std_logic_vector a subtype of std_ulogic_vector, so to_string is actually only implemented for std_ulogic_vector - std_logic_vector gets it by implicit subtype conversionGarrettgarrick
D
7

As you have discovered, the 'image attribute is only declared for scalar types, not arrays or records : the usual approach is to create one's own library of test utilities including to_string or image functions in a package at the start of a design, and use it throughout.

It would be perfectly possible to standardise a library of these, and you will probably find many potential "test utility" packages but none has really caught on well enough to deserve becoming a standard.

That being said, you might find the following package a useful starting point.

It encapsulates a couple of custom datatypes with operations on them. No generics, but thanks to overloading, you can use the package as though its functions were generic. (You will notice that the function bodies are incomplete though!) Extending it and adding types is easy cut&paste for the most part; and it keeps a lot of clutter out of the main design.

It may be better to separate out the type declns and the (testbench only) functions into two separate packages; Types and Types_Test_Utils. Then Types is used throughout the design, while the test utilities are only exposed to the testbench.

library IEEE;
use IEEE.numeric_std.all;

package Types is
  subtype SmallNum is UNSIGNED(7 DOWNTO 0);
  subtype BiggerNum is UNSIGNED(19 DOWNTO 0);
  subtype Bits is BIT_VECTOR(7 DOWNTO 0);

  -- and operations on these types
  -- Simulate generic procedures using overloading

  function to_string(N : Unsigned) return String;
  function to_string(N : Bits) return String;  

  procedure eq_checker (name : string; sig,should : SmallNum; at : time);
  procedure eq_checker (name : string; sig,should : Bits; at : time);

end Types;

package body Types is

function to_string(N : Unsigned) return String is
variable temp : string(1 to (N'length + 3)/4) := (others => 'x');
begin
   -- not finished!
   return temp;
end to_string;

function to_string(N : Bits) return String is
begin
   return "hello";
end to_string;

procedure eq_checker(name : string; sig,should : SmallNum; at : time) is
begin
  if (at = now) then
    if sig = should then
      report to_string(sig) & "has same value" severity note;
    else
      report to_string(sig) & "has not same value as " & to_string(should) severity note;
    end if;
  end if;
end procedure eq_checker;

procedure eq_checker(name : string; sig,should : Bits; at : time) is
begin
   null;
end procedure eq_checker;

end Types;

And a simple tester for it...

  use Work.Types.all;

  ENTITY tester IS
  END tester;

  ARCHITECTURE behavior OF tester IS 

  Signal a,b      : SmallNum := X"AA";
  Signal c        : BiggerNum := X"ABCDE";
  SIGNAL x,y      : Bits := X"BB";

  BEGIN

  process(a,x) is
  begin
     report "value: " & to_string(X) severity note;
     report "and this one: " & to_string(a) severity note;
     report "this one too: " & to_string(c) severity note;
  end process;

  END;
Deathbed answered 14/3, 2013 at 12:56 Comment(0)
F
6

Here is a solution where the range of the std_logic_vector type variable does not have an impact on the return value:

function to_string ( a: std_logic_vector) return string is
variable b : string (1 to a'length) := (others => NUL);
variable stri : integer := 1; 
begin
    for i in a'range loop
        b(stri) := std_logic'image(a((i)))(2);
    stri := stri+1;
    end loop;
return b;
end function;
Footlights answered 9/8, 2016 at 11:54 Comment(2)
Botond, why the (2) in std_logic'image(a(i))(2)?Blondie
I figured it out. When we use bit'image, for example, we have '0' or '1'. So, we have: bit'image(something)(1) --> ' | bit'image(something)(2) --> 0 or 1 | bit'image(something)(3) --> 'Blondie
G
2
function slv_to_string ( a: std_logic_vector) return string is
    variable b : string (a'length-1 downto 1) := (others => NUL);
begin
        for i in a'length-1 downto 1 loop
        b(i) := std_logic'image(a((i-1)))(2);
        end loop;
    return b;
end function;
Galagalactagogue answered 17/3, 2016 at 14:42 Comment(1)
This solution is incorrect. What is the range of the parameter a? In general it can be any legal range. If you want to assume it is something, you need to make it so with an alias.Garrettgarrick
P
0
package package_x is 
  subtype any_type is UNSIGNED(7 DOWNTO 0);

...
end package_x;

package body package_x is

  procedure someprocedure (signal sig: in any_type) is

  VARIABLE li   : line;
  file output : text open write_mode is "output";

  begin
    write(li, std_logic_vector(sig));
    writeline(output, li);
  end;
end package_x;
Psychodrama answered 14/3, 2013 at 11:12 Comment(4)
The OP asked for a conversion to string. This on the other hand is write/writeline. See Brian's post for a conversion to stringGarrettgarrick
Well, a line is just defined as an access type to string. So, one could convert this to a function and return li.all as well.Stevens
@Stevens How would you return li.all without creating a memory leak? You need to deallocate li before returnning - at least until we get garbage collection in VHDL-2019Garrettgarrick
@JimLewis Fair enough. The semantics would require the caller to deallocate.Stevens
E
0

This is my solution:

function to_string (arg : std_logic_vector) return string is
     variable L : line;
     variable result : string(1 to arg'length);
begin
    write(L, arg);
    read(L, result);
    return result;
end function;

Please review it, it is just a proof-of-concept. Perhaps it is what @PlayDough meant.

Erythroblastosis answered 11/11, 2020 at 13:36 Comment(0)
S
-1

I ended up writing functions to do the std_logic_vector to string conversion.

They are available in this gist, or below.

util_str.vhd

-- Mathieu CAROFF
-- 2018-11-20
-- util_str.vhd
-- Utilitary functions to convert vectors to strings

-- Test:
-- ```bash
-- ghdl -a util_str.vhd
-- ghdl -r util_str_tb
-- ```

-- The answer from Jonathan Bromley to the toopic "std_logic_vector to string in hex format"
-- asked by Mad I.D. helped to write the functions below.
-- https://groups.google.com/forum/#!topic/comp.lang.vhdl/1RiLjbgoPy0

library ieee;
use ieee.std_logic_1164.all;

package util_str is

function bin (lvec: in std_logic_vector) return string;
function hex (lvec: in std_logic_vector) return string;

end package;


package body util_str is

    function bin (lvec: in std_logic_vector) return string is
        variable text: string(lvec'length-1 downto 0) := (others => '9');
    begin
        for k in lvec'range loop
            case lvec(k) is
                when '0' => text(k) := '0';
                when '1' => text(k) := '1';
                when 'U' => text(k) := 'U';
                when 'X' => text(k) := 'X';
                when 'Z' => text(k) := 'Z';
                when '-' => text(k) := '-';
                when others => text(k) := '?';
            end case;
        end loop;
        return text;
    end function;

    function hex (lvec: in std_logic_vector) return string is
        variable text: string(lvec'length / 4 - 1 downto 0) := (others => '9');
        subtype halfbyte is std_logic_vector(4-1 downto 0);
    begin
        assert lvec'length mod 4 = 0
            report "hex() works only with vectors whose length is a multiple of 4"
            severity FAILURE;
        for k in text'range loop
            case halfbyte'(lvec(4 * k + 3 downto 4 * k)) is
                when "0000" => text(k) := '0';
                when "0001" => text(k) := '1';
                when "0010" => text(k) := '2';
                when "0011" => text(k) := '3';
                when "0100" => text(k) := '4';
                when "0101" => text(k) := '5';
                when "0110" => text(k) := '6';
                when "0111" => text(k) := '7';
                when "1000" => text(k) := '8';
                when "1001" => text(k) := '9';
                when "1010" => text(k) := 'A';
                when "1011" => text(k) := 'B';
                when "1100" => text(k) := 'C';
                when "1101" => text(k) := 'D';
                when "1110" => text(k) := 'E';
                when "1111" => text(k) := 'F';
                when others => text(k) := '!';
            end case;
        end loop;
        return text;
    end function;

end package body;


library ieee;

use ieee.std_logic_1164.all;
use work.util_str.all;

entity util_str_tb is
end entity;

architecture util_str_tb_arch of util_str_tb is
begin
    process is
        variable byte: std_logic_vector(12-1 downto 0) := "000001001111";
    begin
        report "bin " & bin(byte);
        report "hex " & hex(byte);
        wait;
    end process;
end architecture;
Sympathizer answered 20/11, 2018 at 11:25 Comment(1)
This solution is incorrect. What is the range of the parameter lvec? In general it can be any legal range. If you want to assume it is something, you need to make it so with an alias.Garrettgarrick
M
-1
function slv_to_string ( a: std_logic_vector) return string is
    variable b : string (a'length downto 1) := (others => NUL);
    variable c : integer := 1;
begin
        for i in a'range loop
        b(c) := std_logic'image(a((i-1)))(2);
        c := c + 1;
        end loop;
    return b;
end function;

Small fix of Jason's function, this one does print the whole std_logic_vector instead of everything but the last element a(a'length'-1).

Memento answered 14/9, 2020 at 14:48 Comment(7)
This solution is incorrect. What is the range of the parameter a? In general it can be any legal range. If you want to assume it is something, you need to make it so with an alias.Garrettgarrick
@JimLewis I fixed some issues with the range, is this what you meant was wrong or are there more mistakes?Memento
Simulate it. Try it for different ranges of a. Say 7 downto 0, and 0 to 7. Hint, read up on aliases. This is a good place for something like: alias new_a : std_logic_vector(a'length downto 1) is a ;Garrettgarrick
@JimLewis Yes I did that. With the new changes 7 downto 0, 0 to 7, 8 to 1, 1 to 8, 0 to 0, etc. all work perfect.Memento
Is this the code you tested? This code has a(i-1). Lets say the range is 7 downto 0. For i = 0, then this will index a(-1) right? Also, reading the code, you are iterating across a in a left to right direction. You are iterating across b in a right to left direction - hence reversing the array.Garrettgarrick
Try writing a version with the alias - it makes these sort of things easy. Alternately, you can iterate across a using 'reverse_range - although this is still much harder to read than the alias solutionGarrettgarrick
See @Botond Sándor Kirei answer. It is correct wrt this sort of approach.Garrettgarrick

© 2022 - 2024 — McMap. All rights reserved.