I'm writing my own package to deal with generic matrix-like objects due to unavailability of VHDL-2008 (I'm only concerned with compilation and simulation for the time being).
My aim is getting a matrix M_out from a matrix M_in such that:
M_out(i downto 0, j downto 0) <= M_in(k+i downto k, l+j downto l);
using a subroutine of sort. For, let's say, semantic convenience and analogy with software programming languages my subroutine prototype should ideally look something like this:
type matrix is array(natural range <>, natural range <>) of std_logic;
...
procedure slice_matrix(signal m_out: out matrix;
constant rows: natural range<>;
constant cols: natural range<>;
signal m_in: in matrix);
The compiler does however regard this as an error:
** Error: custom_types.vhd(9): near "<>": syntax error
** Error: custom_types.vhd(9): near "<>": syntax error
Is it possible to pass a range as an argument in some way or shall I surrender and pass 4 separate indexes to calculate it locally?
An unconstrained index range natural range <> is not a VHDL object of class signal, variable, constant, or file. Thus it can not be passed into a subprogram. I wouldn't implement a slice operations as a procedure, because it's a function like behavior.
An implementation for working with matrices and slices thereof is provided by the PoC-Library. The implementation is provided in the vectors package.
function slm_slice(slm : T_SLM; RowIndex : natural; ColIndex : natural; Height : natural; Width : natural) return T_SLM is
variable Result : T_SLM(Height - 1 downto 0, Width - 1 downto 0) := (others => (others => '0'));
begin
for i in 0 to Height - 1 loop
for j in 0 to Width - 1 loop
Result(i, j) := slm(RowIndex + i, ColIndex + j);
end loop;
end loop;
return Result;
end function;
More specialized functions to slice off a row or column can be found in that file too. It also provides procedures to assign parts of a matrix.
This package works in simulation and synthesis.
Unfortunately, slicing multi dimensional arrays will not be part of VHDL-2017. I'll make sure it's discuss for VHDL-202x again.
Passing ranges into a subprogram will be allowed in VHDL-2017. The language change LCS 2016-099 adds this capability.
Related
I want an approximation of the Tanh function by saving the values in a LUT (by this I am doing a quantization). I want to choose the Number of entries in the LUT.
As an not-correct example, I imagine a code like
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.fixed_pkg.all;
entity tanh_lut is
generic (
MIN_RANGE: real := 0.0; -- Minimum value of x
MAX_RANGE: real := 5.0; -- Maximum value of x
DATA_RANGE_int: positive:= 8;
DATA_RANGE_frac: positive:= 8;
);
Port ( DIN : in sfixed(DATA_RANGE_int-1 downto -(DATA_RANGE_frac-1));
DOUT : out sfixed(DATA_RANGE_int-1 downto -(DATA_RANGE_frac-1))
end tanh_lut;
architecture Behavioral of tanh_lut is
begin
lut_gen: for i in 0 to LUT_SIZE-1 generate
constant x_val : real := MIN_RANGE + (MAX_RANGE - MIN_RANGE) * i / (LUT_SIZE-1);
constant x_val_next : real := MIN_RANGE + (MAX_RANGE - MIN_RANGE) * (i+1) / (LUT_SIZE-1);
constant y_val : real := tanh(x_val);
if DIN>=x_val_previous AND DIN<x_val then
DOUT <= to_sfixed(tanh(y_val),DOUT ) ;
END IF
end generate;
end Behavioral;
Per example, if I want 4 entries in the range 0 to 3, I want that it is synthesizing a code like:
if DIN>0 AND DIN<=1 then
DOUT <= to_sfixed(0, DOUT);
else DIN>1 AND DIN<=2 then
DOUT <= to_sfixed(0.76159415595, DOUT);
else DIN>2 AND DIN<=3 then
DOUT <= to_sfixed(0.96402758007, DOUT);
else DIN>3 AND DIN<=4 then
DOUT <= to_sfixed(0.99505475368, DOUT);
End if
Is there any way that a code like this or a code which implements the idea behind this is possible?
A simple LUT with addresses is not possible because the addresses are always integer and DIN is fixed point, e.g., 1.5
The other possibility would be two LUTs, one for mapping the Input to an address, another for mapping the address to the LUT entry, e.g., LUT1: 1.5=> address 5, LUT2: address 5 => 0.90. But by this I would double the amount of resources what I dont want
My requirements: things like the tanh(x) should not be synthesized, only the final value of tanh(x). It shoudl also be hardware efficient
It does not matter if you use a nested „if-elsif“ construct or if you use a new „if“ construct for each check.
So you can create a loop like this:
for i in 0 to c_number_of_checks-1 loop
if c_boundaries(i)<DIN and DIN<=c_boundaries(i+1) then
DOUT <= c_output_values(i);
end if;
end loop;
Of course you must provide the constants c_number_of_checks and c_boundaries, c_output_values. This can be done by:
constant c_number_of_checks : natural := 4;
type array_of_your_data_type is array (natural range <>) of your_data_type;
constant c_boundaries : array_of_your_data_type(c_number_of_checks downto 0) := init_c_boundaries(c_number_of_checks);
constant c_output_values : array_of_your_data_type(c_number_of_checks-1 downto 0) := init_c_output_values(c_number_of_checks);
This means you will need the functions init_c_boundaries, init_c_output_values, which create arrays of values, which can initialize the constant c_boundaries and c_output_values.
But this is not complicated (you can use from ieee.math_real the function TANH), as the functions need not to be synthesizable, as they are called only during compile time.
As you see, you will have some effort. So perhaps it is easier to follow the other suggestions. If you do so (value as address of a LUT) you should think about automatic ROM inference, which is provided by several tool chains and will give you a very efficient (small) hardware.
I am attempting to used the ufixed datatype and add 2 ufixed values together, I have calculated I should have enough bits to store the result and the output should be able to be stored in the signal, but when I attempt to perform it I get a bound check failure. Can someone tell me why I am getting this?
The important parts of the code are:
-- definition of parameters used in the failing calculation
input : in ufixed(0 downto -15); -- Q1.15
constant VectorLength : integer := 3;
type vector_ufixed is array(0 to VectorLength-1) of ufixed(1 downto -14);
constant InnerProductArray : vector_ufixed := (to_ufixed(1.2,1,-14), to_ufixed(1.0,1,-14), to_ufixed(0.2,1,-14));
signal InnerProductResult : ufixed(4 downto -29); -- Q5.29
signal counter : integer := 0;
write(l, real'image(to_real(InnerProductResult)));
write(l, string'(", "));
write(l, real'image(to_real(InnerProductResult + input*InnerProductArray(counter))));
writeline(output, l);
InnerProductResult <= InnerProductResult +
input*InnerProductArray(counter);
When I simulate this with ghdl I get the following result:
0.0, 6.00006103515625e-1
ghdl:error: bound check failure at InnerProduct.vhd:55
from: process work.innerproduct(innerproductarchitecture).P0 at InnerProduct.vhd:55
ghdl:error: simulation failed
line 55 in this case is the line
InnerProductResult <= InnerProductResult + input*InnerProductArray(counter);
input takes the value 0.5, as can be observed from the resulting value of 6.00006103515625e-1 when input is multiplied by 1.2.
The value 6.00006103515625e^-1*2^29 is 322125824 as well which is an integer less than 2^34 so it should fit fine, I don't understand why this might be?
When performing a arithmetic operations such as this, addition and multiplication in this case, it is necessary to resize the result of the operation to fit into the location it is being stored. In this case we add a 34 bit number to 2 16 bit numbers and so we need to resize the result to be 34 bits wide in order to fit precisely into the storage location i.e. InnerProductResult.
The syntax for resize in fixed_pkg appears to differ from that used in numeric_std for signed and unsigned numbers. The following syntax is nessesary to use for operations done with fixed_pkg, this was found in http://www.klabs.org/mapld05/presento/189_lewis_p.pdf:
InnerProductResult <= resize(
arg => InnerProductResult + input*InnerProductArray(counter),
size_res => InnerProductResult
);
If I have some code like so:
...
architecture behaviour of ExampleEntity is
-- type definitions
type Matrix is array(0 to 1,0 to 1) of signed(NumOfBitsForSignals_1 downto 0);
-- function definitions
function TransposeMatrix(MatrixArg : Matrix) return Matrix is
-- variable decleration
variable Result : Matrix;
begin
-- behaviour
for columnNo in Result'range loop
for rowNo in Result'range loop
Result(columnNo, rowNo) := MatrixArg(rowNo, columnNo);
end loop;
end loop;
return Result;
end function;
-- constant definitions
constant A00 : std_logic_vector(NumOfBitsForSignals_1 downto 0) := "A00Value";
constant A01 : std_logic_vector(NumOfBitsForSignals_1 downto 0) := "A01Value";
constant A10 : std_logic_vector(NumOfBitsForSignals_1 downto 0) := "A10Value";
constant A11 : std_logic_vector(NumOfBitsForSignals_1 downto 0) := "A11Value";
constant A : Matrix := ((signed(A00), signed(A01)),
constant A_Transpose : Matrix := TransposeMatrix(A);
...
And the TransposeMatrix function is only used once in this place is this function still synthesised or will the compiler assign the appropriate value to A_Transpose and remove this function from the synthesis? If this isn't the case and it synthesised the transpose function would it be better to remove this function and transpose the matrix manually and enter it?
As a general rule, a synthesis tool will try it's very best to reduce the complexity of the generated net list. This includes working out the results of functions that have constant inputs, even if these inputs are themselves generated by other functions, depend on generic parameters, etc. The tools are so good at this process, that a simple mistake in your code can lead to whole parts of your design being optimised away.
That being the case, it doesn't actually matter whether the function is only called in a declarative region; no matter where a function is called, any simplifications or optimisations possible will be carried out by the synthesis tool.
Some tools do have limitations, for example if your function reads from files, or in some scenarios if it contains a loop with bounds that are determined by parameters. However, this will tend to result in either an error or a warning, as opposed to extra logic in the net list.
So im trying to write a function that performs an AND gate, the intput is a vector of the gate inputs, and the number of inputs. But for some reason the compiler gives me an error that it doesn't recognize the "and" logic operator im using inside for some reason. can anyone spot the issue?
p.s this is all part of a bigger project that is a 16counter (0-15) thats made of 4 chained JK flip flops and 2 AND gates (using my "myand" function).
function myand (x: std_logic_vector; n : integer range 7 downto 0) return std_logic is
variable result: integer :=0;
begin
for i in 0 to n-1 loop
result:=result and x(i);
end loop;
return result;
end function;
The compiler error is:
Error (10327): VHDL error at counter16.vhd(16): can't determine definition of operator ""and"" -- found 0 possible definitions
I even tried using '+' instead of 'and' but its the same error.
The builtin libraries of VHDL don't define a operator and that takes a integer and std_logic.
How to fix this:
result should be a std_logic instead of an integer.
result should be initialized to '1' instead of 0.
The function interface can be simplified by removal of the ´n´ argument if the 'range attribute is used on x to get the index values. If a subrange of a std_logic_vector is used as argument, then the myand function can be called with that subrange only. Including sharth suggestions, the function is:
function myand (x : std_logic_vector) return std_logic is
variable result : std_logic := '1';
begin
for i in x'range loop
result := result and x(i);
end loop;
return result;
end function;
I have to convert an integer to find how many bits are required to represent that integer.
Let say, integer value is 22. I know 5 bits are required to represent this integer.
Is there any attribute in VHDL to do this?
Important: The result should also be an integer, which should represent number of bits.
There is no VHDL attribute or function, but you can create a function like:
-- Returns number of bits required to represent val in binary vector
function bits_req(val : natural) return natural is
variable res_v : natural; -- Result
variable remain_v : natural; -- Remainder used in iteration
begin
res_v := 0;
remain_v := val;
while remain_v > 0 loop -- Iteration for each bit required
res_v := res_v + 1;
remain_v := remain_v / 2;
end loop;
return res_v;
end function;
However, sometimes a ceil_log2 function is also useful, since that gives the number of required address bits based on entries in a memory map, and ceil_log2(val) = bits_req(val - 1).
Use the math_real library. It's not for synthesis, but works great between the architecture and begin statements.
use ieee.math_real.all;
constant nbits : natural := integer(ceil(log2(real(n))));
I use math_real a lot for in-line generating sin/cos tables... again it's between architecture and begin... again, don't try to use math_real in synthesis.
I've used this in Quartus, ISE, Vivado, Modelsim successfully; it might not be supported by everything out there.