VHDL: Converting from floating point to fixed point explanation? - vhdl

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.

Related

VHDL: need to calculate float variables in VHDL

I am new to VHDL, for my project I need to use float value. I have used variable data type in my design. To have float value I can use real data type, but real data type is not synthesizable. In the below I have given my code. In cA part I am dividing it by 2, where I need to have float number. But the vivado software is rounding off the number. Also, after cA part, I need to multiply cA with sqrt 2, which is a float number.
After the calculation I need to send these data through AXI peripheral.
Is there any solution where I can have float number in my VHDL code?
Thanks a lot.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.numeric_std.signed;
use IEEE.MATH_REAL.ALL;
entity sinewave_dwt is
Port ( clk : in STD_LOGIC;
dataout : out integer range 0 to 127);
end sinewave_dwt;
architecture Behavioral of sinewave_dwt is
signal cA_A : integer :=0;
signal cD_D : integer :=0;
signal i : integer :=0;
begin
process(clk)
variable cA : integer :=0;
variable cD : integer :=0;
type memory_type is array (0 to 7) of integer range 0 to 127;
variable sine : memory_type :=(4,6,10,12,8,6,5,6);
begin
if(rising_edge(clk)) then
cA := sine(i) + sine(i+1);
cA := cA/2;
cA_A <= cA;
report "cA: " & integer'image(cA);
cD := sine(i) - sine(i+1);
cD := cD/2;
cD_D <= cD;
report "cD: " & integer'image(cD);
i <= i + 2;
if(i > 7) then
cA := 0;
cD := 0;
i <= 0;
end if;
end if;
end process;
end Behavioral;
Vhdl cannot synthesize floating numbered codes because to be able to work with floating numbers, either you need to design a FPU (floating point unit) or just work with fixed point integer arithmetic. You need to determine your minimum and maximum numbers you are working. For example, if your range is 0.01 and 30, then convert minimum floating number you work to integer number by multiplying it 100. Then, your new range will be 100*(0.01-30) => 1-3000. If you want to 5/2 operation, this means 500/2 = 250.

How do you appropriately multiply std_logic:vector in VHDL?

I'm trying to make a module to manipulate a servomotor sg90.
But I'm having problems with a part of the architecture; the module has an entry of 6 bits which controls where I want the servomotor to be at, but controls the motor with a 16bit vector. My way of doing this was multiplying a variable of 6 bits (that has the same value as the entry) and putting that on the 16bit out vector, something like this:
case position is
when "000000" =>
value:= X"0ccc";
when "111111" =>
value := X"1999";
when others =>
value:=std_logic_vector((control*52)+3276);
end case;
What this should do is, for instance, if I put "000000" the out would be "0ccc", putting the servomotor on its start position. "111111" would be "1999" or the end position end everything else in between should be considered by that expression. But, I'm getting the following error:
Error (10327): VHDL error at ServomotorF.vhd(46): can't determine definition of operator ""*"" -- found 0 possible definitions
If it helps, the libraries I'm using are
use ieee.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;
I also tried using numeric_std but that just gives me more errors.
The only other solution I can think of is doing one by one using a giant case structure.
if I use "unsigned" I get the error of multiple definitions of unsigned.
The mathematics of it is simple:
value_out <= value_in * STEP_SIZE + MIN_VALUE_OUT;
But VHDL requires a bit more effort, in essence:
constant MIN_VALUE_IN: natural := 0;
constant MAX_VALUE_IN: natural := 16#3F#;
constant MIN_VALUE_OUT: natural := 16#0CCC#;
constant MAX_VALUE_OUT: natural := 16#1999#;
constant STEP_SIZE: natural := natural(floor(real(MAX_VALUE_OUT - MIN_VALUE_OUT) / real(MAX_VALUE_IN - MIN_VALUE_IN))); -- Beware of rounding errors.
signal std_in, std_out: std_logic_vector(5 downto 0);
signal value_in, value_out: natural;
value_in <= to_integer(unsigned(std_in));
value_out <= value_in * STEP_SIZE + MIN_VALUE_OUT;
std_out <= std_logic_vector(to_unsigned(value_out, std_out'length));
Below is the full implementation of a scaler in VHDL. V1 calculates the scaled value in the VHDL, and V2 selects scaled values from a look up table pre-calculated by the compiler.
Scale
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity Scale is
generic
(
MIN_VALUE_IN: natural := 0;
MAX_VALUE_IN: natural := 16#3F#;
MIN_VALUE_OUT: natural := 16#0CCC#;
MAX_VALUE_OUT: natural := 16#1999#
);
port
(
value_in: in natural range MIN_VALUE_IN to MAX_VALUE_IN;
value_out: out natural range MIN_VALUE_OUT to MAX_VALUE_OUT
);
end entity;
architecture V1 of Scale is
constant RANGE_IN: natural := MAX_VALUE_IN - MIN_VALUE_IN;
constant RANGE_OUT: natural := MAX_VALUE_OUT - MIN_VALUE_OUT;
-- V1a
--constant STEP_SIZE: natural := natural(floor(real(RANGE_OUT) / real(RANGE_IN))); -- Beware of rounding errors.
-- V1b
-- Use the spare bits in the natural range for fixed point arithmetic.
constant NATURAL_BITS: natural := natural(log2(real(natural'high))); -- 31
constant USED_BITS: natural := natural(ceil(log2((real(RANGE_OUT) / real(RANGE_IN) * real(MAX_VALUE_IN)))));
constant SPARE_BITS: natural := NATURAL_BITS - USED_BITS; -- 19
constant MULT: real := 2.0**SPARE_BITS;
constant DIV: natural := natural(MULT);
constant HALF: natural := DIV / 2; -- For rounding off the fixed point number.
constant STEP_SIZE: natural := natural(floor(real(RANGE_OUT) * MULT / real(RANGE_IN))); -- Convert to a fixed point number. Accuracy depends on the number of spare bits. Beware of rounding errors.
begin
-- V1a
--value_out <= (value_in - MIN_VALUE_IN) * STEP_SIZE + MIN_VALUE_OUT;
-- V1b
value_out <= ((value_in - MIN_VALUE_IN) * STEP_SIZE + HALF) / DIV + MIN_VALUE_OUT; -- Convert fixed point to natural.
end architecture;
architecture V2 of Scale is
subtype TScaledValue is natural range MIN_VALUE_OUT to MAX_VALUE_OUT;
type TScaledValues is array(MIN_VALUE_IN to MAX_VALUE_IN) of TScaledValue;
function GetScaledValues return TScaledValues is
variable result: TScaledValues;
constant STEP_SIZE: real := real(MAX_VALUE_OUT - MIN_VALUE_OUT) / real(MAX_VALUE_IN - MIN_VALUE_IN);
begin
for i in TScaledValues'range loop
result(i) := natural(real(i - MIN_VALUE_IN) * STEP_SIZE) + MIN_VALUE_OUT;
end loop;
return result;
end function;
constant SCALED_VALUES: TScaledValues := GetScaledValues;
begin
value_out <= SCALED_VALUES(value_in);
end architecture;
Test Bench
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Scale_TB is
end entity;
architecture V1 of Scale_TB is
constant SYS_CLOCK_FREQ: real := 100000000.0; -- Hz
constant SYS_CLOCK_PERIOD: time := 1.0 sec / SYS_CLOCK_FREQ;
signal halt_sys_clock: boolean := false;
signal sys_clock: std_logic := '0';
constant MIN_VALUE_IN: natural := 0;
constant MAX_VALUE_IN: natural := 16#3F#;
constant MIN_VALUE_OUT: natural := 16#0CCC#;
constant MAX_VALUE_OUT: natural := 16#1999#;
--constant MAX_VALUE_OUT: natural := 7700; -- To see effect of rounding errors for Scale architecture V1.
signal position: natural range MIN_VALUE_IN to MAX_VALUE_IN;
signal servo_pos: natural range MIN_VALUE_OUT to MAX_VALUE_OUT;
signal servo_pos_slv: std_logic_vector(15 downto 0);
component Scale is
generic
(
MIN_VALUE_IN: natural := 0;
MAX_VALUE_IN: natural := 16#3F#;
MIN_VALUE_OUT: natural := 16#0CCC#;
MAX_VALUE_OUT: natural := 16#1999#
);
port
(
value_in: in natural range 0 to 63;
value_out: out natural range MIN_VALUE_OUT to MAX_VALUE_OUT
);
end component;
begin
SysClockGenerator: process
begin
while not halt_sys_clock loop
sys_clock <= '1';
wait for SYS_CLOCK_PERIOD / 2.0;
sys_clock <= '0';
wait for SYS_CLOCK_PERIOD / 2.0;
end loop;
wait;
end process SysClockGenerator;
StimulusProcess: process
begin
for i in MIN_VALUE_IN to MAX_VALUE_IN loop
position <= i;
wait for SYS_CLOCK_PERIOD;
end loop;
wait for SYS_CLOCK_PERIOD;
halt_sys_clock <= true;
wait;
end process;
DUT: Scale
generic map
(
MIN_VALUE_IN => MIN_VALUE_IN,
MAX_VALUE_IN => MAX_VALUE_IN,
MIN_VALUE_OUT => MIN_VALUE_OUT,
MAX_VALUE_OUT => MAX_VALUE_OUT
)
port map
(
value_in => position,
value_out => servo_pos
);
servo_pos_slv <= std_logic_vector(to_unsigned(servo_pos, servo_pos_slv'length));
end architecture;
Simulation of Scale.V2
RTL of Scale.V2
Post Mapping of Scale.V2
Synthesis Comparison for FPGA
Architecture V1
25 logic elements with fixed point arithmetic.
No look-up table.
STEP_SIZE is of type natural.
V1a: Whole number.
V1b: Fixed point number.
Variable rounding errors depending on number of spare bits for fixed point arithmetic, e.g. 19 spare bits with OP's values.
Architecture V2
16 logic elements. Quartus optimised the design a bit more after compiling it a few times. Originally used 54 logic elements.
Uses a look-up table.
STEP_SIZE is of type real.
Smaller rounding errors.

Constant initialisation from a user-made function synthesis takes forever but easily created in simulation

I have created a function "my_func" in a package which when inputted with x produced a matrix of integers of shape [log2(x), x]. I wish to place this slice into ROM memory for synthesis.
For the sake of synthesis I attatch a counter to the ROM and read out one register per clk cycle. I have been able to simulate this and get the expected answers. The synthesis of the code never finishes, even when the generic controling the function is very small as shown in the code.
I am confused as to why Vivado can very quickly simulate the desired design but takes forever to synthesise. I get no errors from vivado saying that the design is unsynthesisable.
Has anyone else experienced this problem, and what are the steps I can take to avoid the problem in the future?
Please see my synthesis code below and a snippet of the function code below
For the record for this question Synthesis = Elaborate Design (RTL)
library IEEE;
library WORK;
use WORK.mylib.all;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use IEEE.math_real.all;
entity try is
generic(
x : integer := 8
);
port(
clk : in std_logic;
ouput: out integer;
);
end entity try;
architecture v1 of try is
constant sig : my_matrix_of_integers(0 to integer(log2(real(x)))-1, 0 to x-1) := my_func(x);
signal counter : unsigned(integer(log2(real(x)))-1 downto 0);
begin
process(clk)
begin
if rising_edge(clk) then
output <= sig(0, to_integer(counter));
counter <= counter + 1;
end if;
end process;
end architecture v1;
Here is a snippet of my function code
function my_func (x: integer) return mat_t is
variable y: integer := integer(log2(real(x)));
variable cluster : integer;
variable index : integer;
variable mat : my_matrix_of_integers(0 to y-1, 0 to x-1);
begin
for s in 0 to y-1 loop
index := x/(2**(s+1));
cluster := x/index;
for c in 0 to cluster - 1 loop
for i in 0 to index -1 loop
if c mod 2 = 0 then
mat(s, (c*index) + i) := 0;
else
mat(s, (c*index) + i) := i*(2**(s));
end if;
end loop;
end loop;
end loop;
return mat;
end function my_func;
and type ...
type my_matrix_of_integers is array(integer range <>, integer range <>) of integer;

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;

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.

Resources