SIMEL
SIMEL

Reputation: 8941

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)

Upvotes: 2

Views: 14196

Answers (3)

Paebbels
Paebbels

Reputation: 16221

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.

Upvotes: 1

user1155120
user1155120

Reputation:

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.

Upvotes: 0

Morten Zilmer
Morten Zilmer

Reputation: 15924

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

Upvotes: 4

Related Questions