VHDL Coding .. conversion from integer to bit_vector - vhdl

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;

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;

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)))

Conversion from numeric_std unsigned to std_logic_vector in vhdl

I have a question related to conversion from numeric_std to std_logic_vector. I am using moving average filter code that I saw online and filtering my ADC values to stable the values.
The filter package code is:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package filterpack is
subtype number is unsigned(27 downto 0);
type numbers is array(natural range <>) of number;
function slv_to_num(signal slv: in std_logic_vector) return number;
procedure MAF_filter(
signal x: in number;
signal h: inout numbers;
signal y: out number
);
end filterpack;
package body filterpack is
function slv_to_num(signal slv: in std_logic_vector) return number is
variable x: number := (others => '0');
begin
for i in slv'range loop
if slv(i) = '1' then
x(i+4) := '1';
end if;
end loop;
return x;
end function slv_to_num;
procedure MAF_filter(
signal x: in number;
signal h: inout numbers;
signal y: out number
) is
begin
h(0) <= x + h(1); -- h[n] = x[n] + h[n-1]
y <= h(0) - h(h'high); -- y[n] = h[n] - h[n-M]
end MAF_filter;
end package body filterpack;
In my top level file, I call the MAF_filter procedure.
Asign_x: x <= slv_to_num(adc_dat);
Filter: MAF_filter(x,h,y);
The adc_dat is defined as:
adc_dat : out std_logic_vector (23 downto 0);
I want to convert the output of the MAF_Filter to std_logic_vector (23 downto 0). Can anyone tell how can I convert filter output 'y' to 'std_logic_vector'?
Many Thanks!
What do you want to do with the 4 extra bits? Your type number has 28 bits, but your signal adc_dat has only 24.
If it's ok to discard them, you could use:
adc_dat <= std_logic_vector(y(adc_dat'range));
Also, is there a reason not to write your function slv_to_num as shown below?
function slv_to_num(signal slv: in std_logic_vector) return number is
begin
return number(slv & "0000");
end function slv_to_num;
The conversion has to solve 2 problems : the type difference you noted, and the fact that the two words are different sizes.
The type difference is easy : std_logic_vector (y) will give you the correct type. Because the two types are related types, this is just a cast.
The size difference ... only you have the knowledge to do that.
adc_dat <= std_logic_vector(y(23 downto 0)) will give you the LSBs of Y - i.e. the value of Y itself, but can overflow. Or as Rick says, adc_dat <= std_logic_vector(y(adc_dat'range)); which is usually better, but I wanted to expose the details.
adc_dat <= std_logic_vector(y(27 downto 4)) cannot overflow, but actually gives you y/16.

Extend bit pattern to generic vector size in VHDL

constant alternate_bits : std_logic_vector(C_BIT_SIZE-1 downto 0) := X;
What do I write in place of X to set it to an alternating pattern of bits, while keeping it generic and without getting upset if C_BIT_SIZE isn't even?
For example, if C_BIT_SIZE = 4 it should produce "1010" and if C_BIT_SIZE = 5 it should produce "01010". (And it should work for any value of C_BIT_SIZE >= 1.)
A function can be used:
-- Returns std_logic_vector(BIT_SIZE-1 downto 0) with bits on even indexes
-- as '0' and bits on odd indexes as '1', e.g. 5 bit vector as "01010".
function alternate_fun(BIT_SIZE : natural) return std_logic_vector is
variable res_v : std_logic_vector(BIT_SIZE - 1 downto 0);
begin
res_v := (others => '0');
for i in 1 to BIT_SIZE / 2 loop
res_v(2 * i - 1) := '1';
end loop;
return res_v;
end function;
I wrote a function that seems to do the trick but I'm interested in alternate tidier answers:
subtype data_vector is std_logic_vector(C_BIT_SIZE-1 downto 0);
function make_alternate_bits return data_vector is
variable bits : data_vector;
begin
for i in 0 to C_BIT_SIZE-1 loop
if (i mod 2) = 0 then
bits(i) := '0';
else
bits(i) := '1';
end if;
end loop;
return bits;
end function;
constant alternate_bits : data_vector := make_alternate_bits;

VHDL: Converting from floating point to fixed point explanation?

In the Designer's Guide to VHDL in Chapter 6.2 there is an entity and architecture body for a converter from floating point to fixed point representation. I'm confused by it
library ieee; use ieee.std_logic_1164 all;
entity to_fp is
port(vec: in std_u_logic_vector(15 downto 0);
r: out real);
end entity to_fp;
architecture behavioral of to_fp is
begin
behavior : process (vec) is
variable temp: bit_vector(vec'range);
variable negative: boolean;
variable int_result: integer;
begin
temp := to_bitvector(vec);
negative := temp(temp'left) = '1';
if negative then
temp := not temp;
end if;
int_result := 0;
for index in vec'range loop
int_result := int_result*2 + bit'pos(temp(index));
end loop;
if negative then
int_result := (-int_result) -1;
end if;
r <= real(int_result) / 2.0**15;
end process behavior;
end architecture behavioral;
I understand most of it. I just don't understand the for loop. How does this give us the integer representation of the bit vector? Please explain in as much detail as possible, Thanks :) .
for index in vec'range loop
This loops over the range of vec. In this case this (15 downto 0).
bit'pos(temp(index));
bit is an enumaration type (type BIT is ('0', '1'); in std.standard). The pos attribute returns the position number (as an integer type) of the given value. So bit'pos(...) converts a bit to an integer.
So what the loop does is convert a bit_vector to an integer.
I recommend using to_integer(unsigned(vec)) for this purpose, though. Remember to use ieee.numeric_std.all;.
The last line converts (casts) the integer to a real.

Resources