Entries for Subprograms in VHDL - vhdl

i have two functions in my code :
function derivative(error, previous_error, dt :in std_logic_vector(7 downto 0)) return std_logic_vector is
variable derivative_val: std_logic_vector(15 downto 0);
begin
derivative_val := div(sub(error,previous_error),dt);
return derivative_val;
end derivative;
function mul(num1,num2 : in std_logic_vector(7 DOWNTO 0)) return std_logic_vector is
variable v_TEST_VARIABLE1 : integer;
variable v_TEST_VARIABLE2 : integer;
variable n_times: integer:=1;
variable product: integer:=0;
begin
v_TEST_VARIABLE1 := to_integer(unsigned(num1)) ;
v_TEST_VARIABLE2 := to_integer(unsigned(num2)) ;
for n_times in 1 to v_TEST_VARIABLE2 loop
product:=product + v_TEST_VARIABLE1;
end loop;
return std_logic_vector(to_unsigned(product,16));
end mul;
In the later half I am trying to assign a variable.
variable derivative_term: std_logic_vector(15 downto 0) := x"0000";
derivative_term := mul(mul(Kp,Td), derivative(error, previous_error,dt));
On compilation, I am getting :
No feasible entries for subprogram "mul".
Is there any other way to use it?
thanks in advance.

The mul function takes arguments num* with type std_logic_vector(7 downto
0) thus length 8, and returns result with type std_logic_vector of length
16.
So when calling mul(mul(...), ...) the outer mul gets first argument with
length 16 of type std_logic_vector, which does not match the required
argument length for the function.
Instead of writing you own multiplication function, you could use the "*" from
ieee.numeric_std, which can be used as:
slv_16_0 <= std_logic_vector(unsigned(slv_8_0) * unsigned(slv_8_1));
It also handles unknown values, like 'X', and resulting length is sum of
length for the two arguments.

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;

Direction independent slicing

I'm creating a package with some functions I often use and some functions need to take slices of their parameters. I usually use downto direction for all my signals, but sometimes signals change their direction unexpectedly, e.g., appending a zero bit (sig & '0') seems to change the direction to positive.
Is there a way to slice arrays (std_logic_vector, unsigned, signed) independent of their direction? For example how would you implement a function taking the lowest two bits? The only implementation I came up with uses an additional constant with the expected direction:
function take_two(x : std_logic_vector) return std_logic_vector is
constant cx : std_logic_vector(x'length-1 downto 0) := x;
begin
return cx(1 downto 0);
end function;
I've also tried something like x(x'low+1 downto x'low) but Quartus doesn't like this.
The question is actually not on the input, but on the required output. What do you prefer?
If you look at how functions are implemented in for instance std_logic_1164-body.vhdl, your function would similarly be something like (in a complete example):
entity e is end entity;
library ieee;
architecture a of e is
use ieee.std_logic_1164.all;
signal test : std_logic_vector(7 downto 0) := "10010110";
signal output : std_logic_vector(2 downto 0);
function slice(s: STD_LOGIC_VECTOR; u, l : natural) return STD_LOGIC_VECTOR is
alias sv : STD_LOGIC_VECTOR (s'length-1 downto 0) is s;
variable result : STD_LOGIC_VECTOR (u downto l);
begin
for i in result'range loop
result(i) := sv(i);
end loop;
return result;
end function;
begin
output <= slice(test & '0', 5, 3); -- test becomes 'to' range.
-- output still becomes "101"
end architecture;

Accessing part of std_logic_vector using variables as indexes

I am trying to access a part of std_logic_vector using variables as indexes.
The following process gets a shift_val result from a function and then uses it to calculate the indexes to extract the 6-bit data we need in shift_data_in_s
I am getting a simulation error on the last line:
shift_data_in_s <= data_in_e(to_integer(unsigned(msb_pos)) downto to_integer(unsigned(lsb_pos)))
saying "Array size of 6 on LHS does not match the array size of 1 on
RHS"
I thought the initialization on lsb_pos and msb_pos would solve this problem but it didnt... not sure how it gets the same value for both these variables, if I am explicitly computing them by subtracting the same value from different constants.
signal shift_val_s : std_logic_vector(3 downto 0);
signal shift_data_in_s : std_logic_vector(5 downto 0);
shift_in_proc : process( data_in, num_zeros_s )
variable data_in_e : std_logic_vector(15 downto 0);
variable msb_pos : std_logic_vector(4 downto 0) := "01110";
variable lsb_pos : std_logic_vector(4 downto 0) := "01001";
begin
data_in_e := data_in & zeros_f(16 - data_in'length);
shift_val_s <= some_function(data_in_e);
if ( to_integer(unsigned(shift_val)) >= 12) then
-- all zeros
shift_data_in_s <= ( others => '0');
else
-- we need only 6 significant bits from the data,
-- msb_pos <= 15 - num_zeros -1;
msb_pos := std_logic_vector( "01110" - unsigned(('0' & shift_val_s)));
-- lsb_pos <= 15 - num_zeros -6;
lsb_pos := std_logic_vector("01001" - unsigned(('0' & shift_val_s)));
if ( lsb_pos(4) = '1') then -- if -ve
shift_data_in_s <= data_in_e(to_integer(unsigned(msb_pos)) downto 0) & zeros_f( to_integer(unsigned(neg_f(lsb_pos))));
else
shift_data_in_s <= data_in_e(to_integer(unsigned(msb_pos)) downto to_integer(unsigned(lsb_pos)));
end if;
end if ; end process shift_in_proc;
The most likely explanation is that msb_pos and lsb_pos contain a metavalue (ie. non-01HL data), presumably because data_in is invalid. The to_integer calls would then both return 0, giving you a range of 1, not 6. In any event, you can find out quickly using your simulator.
EDIT
Quick fix, as per your comment: make sure that some_function always returns a non-metavalue 4-bit vector. There are lots of ways to do this, but you could simply do:
fixed_answer <= to_01(metaval_answer); -- remove metavals
There's a to_01 in numeric_std, which takes an unsigned and returns an unsigned. By default, metavalues are converted to zero. You'll still get the wrong answer at the breginning of simulation, but at least the sim will carry on.
The value of your variable lsb_pos is dependent on shift_val_s, which is itself dependent on some_function - the code for which you have not provided. So it is not possible for anyone here to directly diagnose the problem.
Incidentally your code would be easier to read if you use integer variables, e.g.:
variable msb_pos : integer range 0 to 31 := 14;
variable lsb_pos : integer range 0 to 31 := 9;
The last line then becomes:
shift_data_in_s <= data_in_e(msb_pos downto lsb_pos);
If your some_function returns an integer you can calculate your msb_pos as such:
msb_pos := 14 - shift_val_s;
lsb_pos := 9 - shift_val_s;

VHDL , Division using storing algorithm..the code is not working

I wrote a simple code that divides (Numerator, Denominator) using restoring algorithm. The syntax is fine but in the simulation I only get "11111111" in the quotient!
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity DIV_8bit is
Port (
Numerator : in STD_LOGIC_vector(7 downto 0);
Denominator : in STD_LOGIC_vector(7 downto 0);
Quotient : out STD_LOGIC_vector(7 downto 0);
Remainder : out STD_LOGIC_vector(7 downto 0)
);
end DIV_8bit;
architecture Behavioral of DIV_8bit is
begin
process(Numerator , Denominator)
variable Num: std_logic_vector(7 downto 0) := Numerator;
variable p :std_logic_vector (7 downto 0) := (others=>'0');
variable Den: std_logic_vector (7 downto 0) := Denominator;
variable i : integer :=0;
begin
Division_loop: for i in 0 to 7 loop
p(7 downto 1) := p(6 downto 0);
p(0) := Num(7);
Num(7 downto 1) := Num(6 downto 0) ;
p := p - Den;
if (p < 0) then
P := P + Denominator;
Num(0) := '0';
else
Num(0) := '1';
end if ;
end loop;
Quotient <= Num;
Remainder <= p;
end process;
end Behavioral;
You should not use the package ieee.std_logic_unsigned. Instead use the package ieee.numeric_std. This package defines the data-types unsigned and signed as sub-types of std_logic_vector. Theses types tell the compiler how to interpret the bit-sequence as a number. This package also allows to mix both unsigned and signed numbers in one module which is not possible with ieee.std_logic_unsigned.
Among others, the package defines appropiate operators to compare unsigned / signed with integers.
To convert to and from std_logic_vector, for example if your inputs and outputs are of this type, just use the following type conversions (shown here as functions, but actually they are not):
function std_logic_vector(x : unsigned) return std_logic_vector;
function std_logic_vector(x : signed) return std_logic_vector;
function signed (x : std_logic_vector) return signed;
function unsigned (x : std_logic_vector) return unsigned;
You can also convert from and to integers:
function to_integer (x : unsigned) return integer;
function to_integer (x : signed) return integer;
function to_unsigned(x: integer; size : natural) return unsigned;
function to_signed (x: integer; size : natural) return signed;
There is lot of more nice stuff in the package ieee.numeric_std such as arithmetic operators with integers, sign extension and much more.

fatal error in modelsim during simulation

This is my main code in VHDL:
library ieee;
USE ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity pid is
port( error ,Kp, Ti, Td ,dt: in std_logic_vector(7 downto 0);
reset : in std_logic;
output :out std_logic_vector(31 downto 0) );
end pid;
architecture pid_arch of pid is
-------------------------------- functions
function add_vec(num1,num2,num3: in std_logic_vector(15 downto 0)) return std_logic_vector is
variable v_TEST_VARIABLE1: integer;
variable v_TEST_VARIABLE2: integer;
variable v_TEST_VARIABLE3: integer;
variable n_times1: integer;
variable n_times2: integer;
variable sum: integer;
begin
v_TEST_VARIABLE1 := to_integer(unsigned(num1)) ;
v_TEST_VARIABLE2 := to_integer(unsigned(num2)) ;
v_TEST_VARIABLE3 := to_integer(unsigned(num3 ));
--for n_times1 in 1 to v_TEST_VARIABLE2 loop
-- v_TEST_VARIABLE1: = v_TEST_VARIABLE1 + '1';
-- end loop;
-- for n_times2 in 1 to v_TEST_VARIABLE3 loop
-- v_TEST_VARIABLE1:= v_TEST_VARIABLE1 + '1';
-- end loop;
sum:= v_TEST_VARIABLE1+ v_TEST_VARIABLE2 + v_TEST_VARIABLE3;
return std_logic_vector(to_unsigned(sum,32));
end add_vec;
-----------------------------------
function sub(num1, num2: in std_logic_vector(7 downto 0)) return std_logic_vector is
variable v_TEST_VARIABLE1: integer;
variable v_TEST_VARIABLE2: integer;
variable difference: integer;
begin
v_TEST_VARIABLE1 := to_integer(unsigned(num1)) ;
v_TEST_VARIABLE2 := to_integer(unsigned(num2)) ;
difference := v_TEST_VARIABLE1 - v_TEST_VARIABLE2;
return std_logic_vector(to_unsigned(difference,8));
end sub;
------------------------------------
function mul(num1,num2 : in std_logic_vector(7 DOWNTO 0)) return std_logic_vector is
variable v_TEST_VARIABLE1 : integer;
variable v_TEST_VARIABLE2 : integer;
variable n_times: integer:=1;
variable product: integer:=0;
begin
v_TEST_VARIABLE1 := to_integer(unsigned(num1)) ;
v_TEST_VARIABLE2 := to_integer(unsigned(num2)) ;
for n_times in 1 to v_TEST_VARIABLE2 loop
product:=product + v_TEST_VARIABLE1;
end loop;
return std_logic_vector(to_unsigned(product,16));
end mul;
--------------------------------
function div(num1, num2 : in std_logic_vector(7 DOWNTO 0)) return std_logic_vector is
variable v_TEST_VARIABLE1 : integer;
variable v_TEST_VARIABLE2 : integer;
variable quotient :integer;
-- begin
--P3: PROCESS(num1, num2)
variable n_times: integer:=1;
begin
if num1>num2 then
v_TEST_VARIABLE1 := to_integer(unsigned(num1)) ;
v_TEST_VARIABLE2 := to_integer(unsigned(num2)) ;
L1:loop
n_times := n_times + 1;
exit when ((v_TEST_VARIABLE2 - v_TEST_VARIABLE1)>0);
v_TEST_VARIABLE1 := v_TEST_VARIABLE1 - v_TEST_VARIABLE2;
end loop L1;
quotient := n_times-1;
elsif num2>num1 then
v_TEST_VARIABLE1 := to_integer(unsigned(num1)) ;
v_TEST_VARIABLE2 := to_integer(unsigned(num2)) ;
L2:loop
n_times:=n_times+1;
exit when ((v_TEST_VARIABLE1 - v_TEST_VARIABLE2)>0);
v_TEST_VARIABLE2 := v_TEST_VARIABLE2 - v_TEST_VARIABLE1;
quotient := n_times-1;
end loop L2;
else
quotient := 1;
end if;
return std_logic_vector(to_unsigned(quotient,16));
-- end PROCESS P3;
end div;
---------------------------------
function derivative(error, previous_error, dt :in std_logic_vector(7 downto 0)) return std_logic_vector is
variable derivative_val: std_logic_vector(15 downto 0);
begin
derivative_val := div(sub(error,previous_error),dt);
return derivative_val;
end derivative;
--------------------------------------------
function integration(error,dt:in std_logic_vector(7 downto 0);current_integration :in std_logic_vector(15 downto 0);reset : in std_logic) return std_logic_vector is
begin
if (reset='1') then
return "0000000000000000";
else
--current_integration := add_vec(current_integration, mul(error,dt),x"0000");
-- return current_integration;
return add_vec(current_integration, mul(error,dt),x"0000");
end if;
end integration;
-------------------------
begin
P1:PROCESS (reset ,error , Kp, Ti, Td)
variable proportional_term : std_logic_vector(15 downto 0):=x"0000";
variable derivative1: std_logic_vector(15 downto 0) := x"0000";
variable derivative_term: std_logic_vector(31 downto 0) ;
variable integration1: std_logic_vector(15 downto 0) :=x"0000";
variable integration_term : std_logic_vector(15 downto 0) := x"0000";
variable current_integration: std_logic_vector(15 downto 0) ;
variable previous_error: std_logic_vector(7 downto 0) := "00000000";
variable v1: std_logic_vector( 15 downto 0);
variable v2 : std_logic_vector( 23 downto 0);
variable v3 : std_logic_vector (7 downto 0);
------------------checked till here
begin
if (reset='1') then
-- output <= x"00000000";
previous_error :="00000000";
current_integration := x"0000";
else
--output <= Kp*(error + integration/Ti + derivative*Td);
current_integration := integration1;
end if;
-- proportional_term := mul(Kp,error);
proportional_term := std_logic_vector(unsigned(Kp) * unsigned(error));
-- derivative_term := mul(mul(Kp,Td), derivative(error, previous_error,dt));
v1 :=std_logic_vector(unsigned(Kp)*unsigned(Td));
derivative1 := derivative(error, previous_error,dt);
derivative_term := std_logic_vector(unsigned(v1)*unsigned(derivative1));
integration1 :=integration(error,dt,current_integration,reset);
v2 :=std_logic_vector((unsigned(Kp)*unsigned(integration1)));
v3 := std_logic_vector(resize(unsigned(v2),8));
integration_term := div( v3, Ti);
-- integration_term := div(mul(Kp, integration(error,dt,current_integration,reset)) , Ti);
previous_error :=error;
output <= add_vec(std_logic_vector(resize(unsigned(proportional_term),16)) , std_logic_vector(resize(unsigned(derivative_term),16)), std_logic_vector(resize(unsigned(integration_term),16)));
--output <= x"0000";
--Kp*(error + integration/Ti + derivative*Td);
END PROCESS P1;
end pid_arch;
And this is the testbench:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
use IEEE.numeric_std.all;
ENTITY pid_2_tb IS
END pid_2_tb;
ARCHITECTURE behavior OF pid_2_tb IS
COMPONENT pid --'test' is the name of the module needed to be tested.
port(error ,Kp, Ti, Td ,dt: in std_logic_vector(7 downto 0);
reset : in std_logic;
output :out std_logic_vector(31 downto 0) );
END COMPONENT;
signal error : std_logic_vector(7 downto 0) := "00000000";
signal Kp : std_logic_vector(7 downto 0) := "00001000";
signal Ti : std_logic_vector(7 downto 0) := "00000001";
signal Td : std_logic_vector(7 downto 0) := "00000001";
signal dt : std_logic_vector(7 downto 0) := "00000001";
signal reset : std_logic := '1';
signal output : std_logic_vector(31 downto 0);
constant clk_period : time := 1 ns;
BEGIN
uut: pid PORT MAP (
error => error,
Kp => Kp,
Ti => Ti,
Td => Td,
dt => dt,
reset => reset,
output => output
);
clk_process :process
begin
error <= "00000001";
reset <= '1';
wait for clk_period/2;
error <= "00000010";
reset <= '0';
wait for clk_period/2; --for next 0.5 ns signal is '1'.
end process;
-- Stimulus process
stim_proc: process
begin
wait for 17 ns;
error <= "00000011";
reset <= '0';
wait for 1 ns;
error <= "00000010";
reset <= '0';
wait;
end process;
END;
The two show no error on compilation. I have tried simulating for smaller programs using the same functions, and that worked well.
But on simulation it gives Fatal error. What can be the reasons for the same?
I am a newbie in vhdl. Please help me out.
Thanks in advance.
Your process P1 in entity pid loops continuously. I would imagine Modelsim uses a guard timer to cause an exception when something spins continuously.
I found this by divide and conquer:
ghdl -a pid.vhdl # analyze, also contains pid_2_tb entity/architecture
ghdl -e pid_2_tb # elaborates
ghdl -r pid_2_tb --stop-time=100ns # run the simulation (batch mode) guard time to stop)
Ran forever so:
ghdl -e pid # elaborate just the component
ghdl -r pid --stop-time=30ns # run just the component
../../../src/ieee/numeric_std-body.v93:2098:7:#0ms:(assertion warning): NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0
../../../src/ieee/numeric_std-body.v93:2098:7:#0ms:(assertion warning): NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0
(And it was at least doing something - while still running forever).
So knew which part of your design (entity/architecture pid) to focus on. Looked immediately at the process and found no wait nor a sensitivity statement.
Commented out the sensitivity list in P1 and put a wait statement before the first function call (derivative). That finished.
Moved the wait statement before the next function call in P1 (to div). Never finished.
So your derivative function doesn't complete. And horror of horrors the derivative function contains calls to to div and sub.
Looking in the first function call (to div) in function derivative and we find not one but two loop unbounded loop statements:
function div(num1, num2 : in std_logic_vector(7 downto 0))
return std_logic_vector is
variable v_test_variable1: integer;
variable v_test_variable2: integer;
variable quotient: integer;
variable n_times: integer:= 1;
begin
if num1 > num2 then
v_test_variable1 := to_integer(unsigned(num1)) ;
v_test_variable2 := to_integer(unsigned(num2)) ;
l1:
loop
n_times := n_times + 1;
exit when ((v_test_variable2 - v_test_variable1) > 0);
v_test_variable1 := v_test_variable1 - v_test_variable2;
end loop l1;
quotient := n_times - 1;
elsif num2 > num1 then
v_test_variable1 := to_integer(unsigned(num1));
v_test_variable2 := to_integer(unsigned(num2));
l2:
loop
n_times := n_times + 1;
exit when ((v_test_variable1 - v_test_variable2) > 0);
v_test_variable2 := v_test_variable2 - v_test_variable1;
quotient := n_times - 1;
end loop l2;
else
quotient := 1;
end if;
return std_logic_vector(to_unsigned(quotient,16));
end div;
I'm not inclined to independently test your algorithm (say in C or use report statements) but I'd guess the difference between v_test_variable1 and v_test_variable2 doesn't go positive in one of the two loops, or one of them should compare Less Than instead of Greater Than. Another possibility is that you zero something out.
Your code doesn't look synthesis eligible, either. You don't have loops that are unbounded. Loops are unrolled in synthesis and required to have a static number of iterations.
And of course you could have an additional problem somewhere else.
The div function uses a loop where the exit condition never becomes true,
whereby the div function will never return, thus the simulation time will
never advance.
One relevant part of the div code is in:
elsif num2 > num1 then
v_TEST_VARIABLE1 := to_integer(unsigned(num1));
v_TEST_VARIABLE2 := to_integer(unsigned(num2));
L2 : loop
n_times := n_times+1;
exit when ((v_TEST_VARIABLE1 - v_TEST_VARIABLE2) > 0);
v_TEST_VARIABLE2 := v_TEST_VARIABLE2 - v_TEST_VARIABLE1;
quotient := n_times-1;
end loop L2;
But if num1 is 0 (zero) then v_TEST_VARIABLE2 is never decremented in the
loop, thus ((v_TEST_VARIABLE1 - v_TEST_VARIABLE2) > 0) never becomes true.
The div function must be updated to handle this case also, or arguments must
be guaranteed never to result in the case.

Resources