How to write an integer to stdout as hexadecimal in VHDL? - 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

Related

Generic function in VHDL to extract an arbitrary byte from a std_logic_vector of any length?

How to write a generic function that will extra a byte from a std_logic_vector based on an index value?
library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
entity tmp is
end entity;
architecture beh of tmp is
function get_byte(
idx: in integer;
dat: in std_logic_vector
) return std_logic_vector is
constant msb :integer := (idx+1)*8 - 1;
constant lsb :integer := idx*8;
variable ret :std_logic_vector(7 downto 0);
begin
ret := dat(msb downto lsb);
return ret;
end function;
begin
process
constant vec :std_logic_vector := X"ABCDEF1234567";
variable b1 :std_logic_vector(7 downto 0);
variable m :line;
begin
b1 := get_byte(1, vec);
report "just kidding! end of testbench" severity failure;
end process;
end architecture;
Here's the error from my attempt:
C:\Xilinx\Vivado\2021.2\bin\xvhdl.bat --incr --relax --work work tmp.vhd
C:\Xilinx\Vivado\2021.2\bin\xelab.bat tmp -snapshot simout
Vivado Simulator v2021.2
Copyright 1986-1999, 2001-2021 Xilinx, Inc. All Rights Reserved.
Running: C:/Xilinx/Vivado/2021.2/bin/unwrapped/win64.o/xelab.exe tmp -snapshot simout
Multi-threading is on. Using 10 slave threads.
Starting static elaboration
ERROR: [VRFC 10-1378] slice direction differs from its index type range [C:/Users/xxx/Desktop/tmp/tmp.vhd:19]
ERROR: [XSIM 43-3321] Static elaboration of top level VHDL design unit tmp in library work failed.
ERROR: [VRFC 10-1378] slice direction differs from its index type
range
Is resolved by specifying the 'to' vs 'downto' ranges on each std_logic_vector declaration. (the default if not shown is to assumed 0 'to' N, not 'downto' - so when not shown/making simulator choose you are sorta mixing types).
As you don't know how your function will be called and what its parameter will be, a very simple approach consists in creating local copies with known ranges:
function get_byte(idx: natural; dat: std_logic_vector) return std_logic_vector is
constant size: natural := dat'length;
constant ldat: std_logic_vector(size-1 downto 0) := dat;
begin
assert 8*idx+7 <= size report "out of range index" severity failure;
return ldat(8*idx+7 downto 8*idx);
end function get_byte;
Not sure why, but it works if I write it this way:
function get_byte(
idx :in integer; -- 0=MS-Byte ... n=LS-BYTE
dat :in std_logic_vector -- uncontrained slv is a "to"
-- not a "Downto" vector?
) return std_logic_vector is
constant msb :integer := (idx+1)*8 - 1;
constant lsb :integer := idx*8;
variable ret :std_logic_vector(7 downto 0);
begin
ret := dat(idx*8 + 0)
& dat(idx*8 + 1)
& dat(idx*8 + 2)
& dat(idx*8 + 3)
& dat(idx*8 + 4)
& dat(idx*8 + 5)
& dat(idx*8 + 6)
& dat(idx*8 + 7);
return ret;
end function;

std_logic_vector (to_unsigned(X, Y));

This is a test-bench, and I have these signals:
signal DATA_INPUT :std_logic_vector(0 to 31);
signal rand_num :integer;
I am trying to put random numbers into this 32bit signal by this:
DATA_INPUT <= std_logic_vector(to_unsigned(rand_num, 32));
My question is, I need numbers more than 31bits but when the random numbers goes above this number: 2147483647 which is INTEGER'high, I am getting this error:
near "4294967295": (vcom-119) Integer value exceeds INTEGER'high.
# ** Error: tb.vhd: (vcom-1144) Value -1 (of type
std.STANDARD.NATURAL) is out of range 0 to 2147483647.
I tried to modify the TO_UNSIGNED() function and change the NATURAL input to something else but nothing.
Here is the TO_UNSIGNED function from IEEE and RANDOOM GENERATOR process:
function TO_UNSIGNED(ARG, SIZE: NATURAL) return UNSIGNED is
variable RESULT: UNSIGNED (SIZE-1 downto 0);
variable i_val: NATURAl := ARG;
begin
if (SIZE < 1) then return NAU; end if;
for i in 0 to RESULT'left loop
if (i_val MOD 2) = 0 then
RESULT(i) := '0';
else RESULT(i) := '1';
end if;
i_val := i_val/2;
end loop;
if not(i_val=0) then
assert NO_WARNING
report "numeric_std.TO_UNSIGNED : vector truncated"
severity WARNING;
end if;
return RESULT;
end TO_UNSIGNED;
Random generator:
process
variable seed1, seed2 :positive;
variable rand :real;
variable range_of_rand :real:= 46340.0;
begin
uniform(seed1, seed2, rand);
rand_num <= integer(rand*range_of_rand);
wait for 1 ns;
end process;
You can make a new,bigger random number by combining two.
The simplest solution is to convert two random integers to vectors and then concatenate until you get the number of bits you need. This gives you 64 bits:
DATA_INPUT <= std_logic_vector(to_unsigned(rand_num, 32)) & std_logic_vector(to_unsigned(rand_num, 32));

VHDL Coding .. conversion from integer to bit_vector

I'm facing this problem , i'm asked to implement a function in VHDL that takes an integer and returns a bit_vector , assumed that this integer is represented by 4 bits.
i don't want to use already built in function, i have to code the function.
I have made a function to convert from bit_vector to integer which was kinda of easy, but im stuck here :S
Any ideas how can i do it ?
Morten's is the correct answer but it's sometimes worth being open to alternative approaches...
As the question relates to a small (4-bit) range, a lookup table becomes attractive : I have assumed unsigned integers but it's easy to adapt.
subtype bv4 is bit_vector(3 downto 0);
constant LUT : array(0 to 15) of bv4 := (
"0000", "0001", "0010", "0011", "0100, "0101", "0110", "0111",
"1000", "1001", "1010", "1011", "1100, "1101", "1110", "1111");
function to_bv(n : natural) return bit_vector is
begin
return LUT(n);
end to_bv;
This will normally synthesise as you would hope rather than actually creating a ROM!
The VHDL standard packages is good inspiration for home brewed functions, and the numeric_bit package defines the to_unsigned function for conversion of natural type to unsigned type, which is the function VHDL actually uses for conversion to bit_vector. The function is implemented as:
function TO_UNSIGNED (ARG, SIZE: NATURAL) return UNSIGNED is
variable RESULT: UNSIGNED(SIZE-1 downto 0);
variable I_VAL: NATURAL := ARG;
begin
if (SIZE < 1) then return NAU;
end if;
for I in 0 to RESULT'LEFT loop
if (I_VAL mod 2) = 0 then
RESULT(I) := '0';
else
RESULT(I) := '1';
end if;
I_VAL := I_VAL/2;
end loop;
if not(I_VAL =0) then
assert NO_WARNING
report "NUMERIC_BIT.TO_UNSIGNED: vector truncated"
severity WARNING;
end if;
return RESULT;
end TO_UNSIGNED;
The initial if (SIZE < 1) and final if not(I_VAL =0) checks may be removed, if it is known that the function is never used with values that makes the checks relevant.
This leaves the for I in 0 to RESULT'LEFT loop that creates one result bit per iteration.
Based on Brian's answer, the constant LUT can be initialized using the TO_UNSIGNED function, to avoid the hand written literals:
function to_bv(n, size : natural) return bit_vector is
type bv_arr_t is array (0 to 2 ** size - 1) of bit_vector(size - 1 downto 0);
function bv_arr_init(size : natural) return bv_arr_t is
variable res_v : bv_arr_t;
begin
for i in 0 to 2 ** size - 1 loop
res_v(i) := bit_vector(TO_UNSIGNED(i, size));
end loop;
return res_v;
end function;
constant LUT : bv_arr_t := bv_arr_init(size);
begin
return LUT(n);
end to_bv;

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.

VHDL: Is there a convenient way to assign ascii values to std_logic_vector?

In verilog, I can assign a string to a vector like:
wire [39:0] hello;
assign hello = "hello";
In VHDL, I'm having difficulty finding a method like this:
SIGNAL hello : OUT std_logic_vector (39 DOWNTO 0);
...
hello <= "hello";
I've been using:
hello <= X"65_68_6c_6c_6f";
which is unclear and time consuming for large strings.
I've looked at the textio package and thetxt_util package, but neither seem to be very clear on how to interpret a string and convert it to std_logic.
Is there a simple method of assigning ascii codes to std_logic in VHDL?
Here's a minimal example:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY test IS
PORT(
ctrl : IN std_logic;
stdout : OUT std_logic_vector (39 DOWNTO 0)
);
END ENTITY;
ARCHITECTURE rtl OF test IS
SIGNAL temp : std_logic_vector (39 DOWNTO 0);
BEGIN
stdout <= temp;
PROCESS(ctrl)
BEGIN
IF (ctrl = '0') THEN
temp <= "hello"; -- X"68_65_6C_6C_6F";
ELSE
temp <= "world";
END IF;
END PROCESS;
END rtl;
This one varies little for Morten's answer - it only uses one multiply, it copies the string instead of creating an alias, it uses an additional variable and it returns a standard logic vector with an ascending index range.
From a package called string_utils:
library ieee;
use ieee.numeric_std.all;
-- ...
function to_slv(s: string) return std_logic_vector is
constant ss: string(1 to s'length) := s;
variable answer: std_logic_vector(1 to 8 * s'length);
variable p: integer;
variable c: integer;
begin
for i in ss'range loop
p := 8 * i;
c := character'pos(ss(i));
answer(p - 7 to p) := std_logic_vector(to_unsigned(c,8));
end loop;
return answer;
end function;
You could add an argument with a default specifying ascending/descending index range for the return value. You'd only need to provided the argument for the non default.
A small general function is one way to do it, with a suggestion below:
library ieee;
use ieee.numeric_std.all;
...
-- String to std_logic_vector convert in 8-bit format using character'pos(c)
--
-- Argument(s):
-- - str: String to convert
--
-- Result: std_logic_vector(8 * str'length - 1 downto 0) with left-most
-- character at MSBs.
function to_slv(str : string) return std_logic_vector is
alias str_norm : string(str'length downto 1) is str;
variable res_v : std_logic_vector(8 * str'length - 1 downto 0);
begin
for idx in str_norm'range loop
res_v(8 * idx - 1 downto 8 * idx - 8) :=
std_logic_vector(to_unsigned(character'pos(str_norm(idx)), 8));
end loop;
return res_v;
end function;
To return an ascii value of a character, use this code:
some_variable <= character'pos('a'); --returns the 'a' ascii value
In your example you are trying to assign a string type to a std_logic_vector type.
That is simply not allowed. VHDL is strongly typed.
SIGNAL hello : OUT std_logic_vector (39 DOWNTO 0);
...
hello <= "hello";
If your goal is to convert from hexa to ascii for printing simulation result
you can simply do that:
character'val(to_integer(unsigned(my_std_logic_vector)))

Resources