Loop operator "For" to fill an array in VHDL - for-loop

I want to understand and improve an VHDL code I got.
In the VHDL implementation there is the following part:
m1(000) <= MetricA(000) + BrMet(3);
m1(001) <= MetricA(001) + BrMet(1);
m1(002) <= MetricA(002) + BrMet(0);
m1(003) <= MetricA(003) + BrMet(2);
m1(004) <= MetricA(004) + BrMet(0);
m1(005) <= MetricA(005) + BrMet(2);
m1(006) <= MetricA(006) + BrMet(3);
m1(007) <= MetricA(007) + BrMet(1);
m1(008) <= MetricA(008) + BrMet(2);
m1(009) <= MetricA(009) + BrMet(0);
m1(010) <= MetricA(010) + BrMet(1);
m1(011) <= MetricA(011) + BrMet(3);
m1(012) <= MetricA(012) + BrMet(1);
m1(013) <= MetricA(013) + BrMet(3);
m1(014) <= MetricA(014) + BrMet(2);
m1(015) <= MetricA(015) + BrMet(0);
where
type BRANCH_METRIC is array (3 downto 0) of STD_LOGIC_VECTOR (7 downto 0);
signal BrMet: RANCH_METRIC := (OTHERS =>(OTHERS => '0'));
type PATH_METRIC is array (15downto 0) of STD_LOGIC_VECTOR (7 downto 0);
signal MetricA: PATH_METRIC := (OTHERS =>(OTHERS => '0'));
My questions:
1. m1(001), .... m1(015) , MetricA(001), .... MetricA(015) mean a value in a position:
m1(001) means a value on the first cell, m1(015) is on the 15th cell.
Can BrMet(0) mean something else?
2. My task is to rewrite code above using for-loop.
I can write :
for i in 1 to 15 loop
m1(i) <= MetricA(i) + BrMet(?);
How can I add BrMet in this loop?
I was thinking to create a table as:
Tab = {3,1,0,2,0,2...}
{3,1,0,2,0,2...} expresses BrMet(3), BrMet(1), BrMet(0) and so on
and the I would write the loop as
for i in 1 to 15 loop
m1(i) <= MetricA(i) + Tab(i);
But unfortunately I didnt find any information how to create a table in VHDL. There is LUT table , bit It doesnt pass for it.

"m1(001) means a value on the first cell, m1(015) is on the 15th cell"
If by "first cell" you mean the cell with the smallest index then no, m1(1) is not the "first" cell, it is the "second" (and m1(15) is the "sixteenth") because you declared your array type with 0 as the "first" index, not 1. Note that, according these array definitions your loops are probably wrong: they should start at index 0, not 1.
"Can BrMet(0) mean something else?"
Else than what? BrMet(0) is the cell of array BrMet with the smallest index.
"How can I add BrMet in this loop?"
You apparently know how to declare array types and use them. This is no different. Just declare an array type and a constant of this type:
type BrMetIdx_t is array(0 to 15) of integer;
constant BrMetIdx: BrMetIdx_t := (3, 1, 0, 2, 0, 2, 3, 1, 2, 0, 1, 3, 1, 3, 2, 0);
...
for i in 0 to 15 loop
m1(i) <= MetricA(i) + BrMet(BrMetIdx(i));
end loop;
...
Note: it would probably be safer to restrict the type of elements of BrMetIdx_t arrays to integers in the 0 to 3 range:
type BrMetIdx_t is array(0 to 15) of integer range 0 to 3;
This way, if there is a typo with, e.g., value 4, in your constant declaration you will get a clear error message from the compiler.
Note: you don't have to declare your array types with a "downto" index range. This is a common practice for vectors of bits because indexing them from right to left is also common but for your m1, MetricA and BrMet arrays you could probably as well index them in a more "natural" way:
type BRANCH_METRIC is array (0 to 3) of STD_LOGIC_VECTOR (7 downto 0);
type PATH_METRIC is array (0 to 15) of STD_LOGIC_VECTOR (7 downto 0);
This would probably help the global understanding.
Note: I doubt that you will ever use multiple drive for the path and branch metrics of your (Viterbi?) decoder. It would thus be safer to use an unresolved type like std_ulogic_vector instead of std_logic_vector. This way, if by accident you create a multiple drive situation, you will get a clear error message from the compiler instead of spending hours trying to understand why you see all these X values during simulation.

Related

VHDL: Generate a generic case statement with adjustable amount of cases

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.

Synthesizable VHDL recursion, Vivado: simulator has terminated in an unexpected manner

I would like to implement a count min sketch with minimal update and access times.
Basically an input sample is hashed by multiple (d) hash functions and each of them increments a counter in the bucket that it hits. When querying for a sample, the counters of all the buckets corresponding to a sample are compared and the value of the smallest counter is returned as a result.
I am trying to find the minimum value of the counters in log_2(d) time with the following code:
entity main is
Port ( rst : in STD_LOGIC;
a_val : out STD_LOGIC_VECTOR(63 downto 0);
b_val : out STD_LOGIC_VECTOR(63 downto 0);
output : out STD_LOGIC_VECTOR(63 downto 0);
. .
. .
. .
CM_read_ready : out STD_LOGIC;
clk : in STD_LOGIC);
end main;
architecture Behavioral of main is
impure function min( LB, UB: in integer; sample: in STD_LOGIC_VECTOR(long_length downto 0)) return STD_LOGIC_VECTOR is
variable left : STD_LOGIC_VECTOR(long_length downto 0) := (others=>'0');
variable right : STD_LOGIC_VECTOR(long_length downto 0) := (others=>'0');
begin
if (LB < UB)
then
left := min(LB, ((LB + UB) / 2) - 1, sample);
right := min(((LB + UB) / 2) - 1, UB, sample);
if (to_integer(unsigned(left)) < to_integer(unsigned(right)))
then
return left;
else
return right;
end if;
elsif (LB = UB)
then
-- return the counter's value so that it can be compared further up in the stack.
return CM(LB, (to_integer(unsigned(hasha(LB)))*to_integer(unsigned(sample))
+ to_integer(unsigned(hashb(LB)))) mod width);
end if;
end min;
begin
CM_hashes_read_log_time: process (clk, rst)
begin
if (to_integer(unsigned(instruction)) = 2)
then
output <= min(0, depth - 1, sample);
end if;
end if;
end process;
end Behavioral;
When I run the above code, I get the following errors:
The simulator has terminated in an unexpected manner. Please review
the simulation log (xsim.log) for details.
[USF-XSim-62] 'compile' step failed with error(s). Please check the
Tcl console output or '/home/...sim/sim_1/behav/xsim/xvhdl.log' file
for more information.
[USF-XSim-62] 'elaborate' step failed with error(s). Please check the
Tcl console output or
'/home/...sim/sim_1/synth/func/xsim/elaborate.log' file for more
information.
I was not able to find any file called xsim.log and xvhdl.log was empty, but elaborate.log had some content:
Vivado Simulator 2018.2
Copyright 1986-1999, 2001-2018 Xilinx, Inc. All Rights Reserved.
Running: /opt/Xilinx/Vivado/2018.2/bin/unwrapped/lnx64.o/xelab -wto c199c4c74e8c44ef826c0ba56222b7cf --incr --debug typical --relax --mt 8 -L xil_defaultlib -L secureip --snapshot main_tb_behav xil_defaultlib.main_tb -log elaborate.log
Using 8 slave threads.
Starting static elaboration
Completed static elaboration
INFO: [XSIM 43-4323] No Change in HDL. Linking previously generated obj files to create kernel
Removing the following line solves the above errors:
output <= min(0, depth - 1, sample);
My questions:
Why am I not able to simulate this code?
Will this code be synthsizable once it is working?
Is there a better (and/or faster) way to obtain the minimum of all relevant hash buckets?
not that I was able to find any real world use for recursion, but just to surprise #EML (as requested in the comments above): you actually can define recursive hardware structures in VHDL.
In Quartus at least, this only works if you give the compiler a clear indication of the maximum recursion depth, otherwise it will try to unroll the recursion to any possible input, eventually dying from a stack overflow:
entity recursive is
generic
(
MAX_RECURSION_DEPTH : natural
);
port
(
clk : in std_ulogic;
n : in natural;
o : out natural
);
end recursive;
architecture Behavioral of recursive is
function fib(max_depth : natural; n : natural) return natural is
variable res : natural;
begin
if max_depth <= 1 then
res := 0;
return res;
end if;
if n = 0 then
res := 0;
elsif n = 1 or n = 2 then
res := 1;
else
res := fib(max_depth - 1, n - 1) + fib(max_depth - 1, n - 2);
end if;
return res;
end function fib;
begin
p_calc : process
begin
wait until rising_edge(clk);
o <= fib(MAX_RECURSION_DEPTH, n);
end process;
end Behavioral;
With a MAX_RECURSION_DEPTH of 6, this generates one single combinational circuit with more than 500 LEs (so the pracical use is probably very limited), but at least it works.
Is recursion possible in VHDL?
I would say, yes, but not recursion as we know it. That's the short answer. I have code (if anyone is interested that implements Quicksort) and it will synthesize quite happily. If anyone knows about Quicksort, it normally won't be anywhere near the context of synthesis. But I managed to do it.
The trick (which is vexatious and hard to follow) is to emulate recursion with a strange state machine that backtracks to the beginning state, after pushing a "state" onto a (hardware) stack. You can synthesize this sort of data structure quite easily if you want.
I recall some fascinating stuff written by Thatcher, Goguen and Wright about semantic transformations from one kind of coding domain to others (different models of computation, in short).
It does strike me that this is possibly a genesis point for actual recursive expressions in a more general sense. But do be warned, it's very difficult.

VHDL: assign new value to the specific element of 2D Array

I want to copy the Average Variable value to the specific location of 2d Array. For this code it is array_new_signal11(3,2).
Can anyone guide me how can I do this? This code gives me error while simulation.
architecture Behavioral of Correction is
type array_new is array (0 to 4, 0 to 4) of integer;
signal array_new_signal: array_new;
begin
array_new_signal11 <= ((1,2,3,4,5),
(4,5,6,7,8),
(7,8,9,0,1),
(1,3,6,5,9),
(2,3,5,4,5));
Process(kelvin)
variable Sum1: integer:= 0;
Variable Sum2: integer:= 0;
Variable Total_Sum: integer:= 0;
Variable Average: integer:= 0;
begin
for Row in 0 to 4 loop
for Column in 0 to 4 loop
if(Row = 1 and Column = 1) then
for Column in 1 to 3 loop
sum1 := array_new_signal11(Row, Column) + Sum1;
end loop;
end if;
if(Row = 2 and Column = 1) then
for Column in 1 to 3 loop
sum2 := array_new_signal11(Row, Column) + Sum2;
end loop;
end if;
end loop;
end loop;
Total_Sum := Sum1 + Sum2;
Average := Total_Sum / 8;
**array_new_signal11(3,2) <= Average;**
end Process;
end Behavioral;
Constructing a Minimal, Complete and Verifiable example from the question:
entity correction is
end correction;
architecture behavioral of correction is
type array_new is array (0 to 4, 0 to 4) of integer;
signal array_new_signal11: array_new := ((1,2,3,4,5),
(4,5,6,7,8),
(7,8,9,0,1),
(1,3,6,5,9),
(2,3,5,4,5));
signal kelvin: boolean;
begin
-- array_new_signal11 <= ((1,2,3,4,5),
-- (4,5,6,7,8),
-- (7,8,9,0,1),
-- (1,3,6,5,9),
-- (2,3,5,4,5));
process (kelvin)
variable sum1: integer:= 0;
variable sum2: integer:= 0;
variable total_sum: integer:= 0;
variable average: integer:= 0;
begin
for row in 0 to 4 loop
for column in 0 to 4 loop
if row = 1 and column = 1 then
for column in 1 to 3 loop
sum1 := array_new_signal11(row, column) + sum1;
end loop;
end if;
if row = 2 and column = 1 then
for column in 1 to 3 loop
sum2 := array_new_signal11(row, column) + sum2;
end loop;
end if;
end loop;
end loop;
total_sum := sum1 + sum2;
average := total_sum / 8;
report "sum1 = " & integer'image(sum1) & ", " &
"sum2 = " & integer'image(sum2) & ", " &
"average = " & integer'image(average);
array_new_signal11(3,2) <= average;
end process;
MONITOR_PROCESS:
process
begin
wait on array_new_signal11;
for row in 0 to 4 loop
report "row" & integer'image(row) & " = " &
integer'image(array_new_signal11(row,0)) & ", " &
integer'image(array_new_signal11(row,1)) & ", " &
integer'image(array_new_signal11(row,2)) & ", " &
integer'image(array_new_signal11(row,3)) & ", " &
integer'image(array_new_signal11(row,4));
end loop;
end process;
end behavioral;
We see the report statements tell us the average and report the new array values.
We see the Row 3 Column 2 was initialized to 6 and is now 4:
ghdl -a correction.vhdl
ghdl -e correction
ghdl -r correction
correction.vhdl:42:7:#0ms:(report note): sum1 = 18, sum2 = 17, average = 4
correction.vhdl:52:13:#0ms:(report note): row0 = 1, 2, 3, 4, 5
correction.vhdl:52:13:#0ms:(report note): row1 = 4, 5, 6, 7, 8
correction.vhdl:52:13:#0ms:(report note): row2 = 7, 8, 9, 0, 1
correction.vhdl:52:13:#0ms:(report note): row3 = 1, 3, 4, 5, 9
correction.vhdl:52:13:#0ms:(report note): row4 = 2, 3, 5, 4, 5
as specified by the value of average.
As Renaud Pacalet notes you have two different processes driving array_new_signal11, which is not legal in VHDL, as it's element type integer is not a resolved data type.
The solution is to initialize the array in this case where it's declared.
Otherwise every assignment to an element of array signal must be in the same process. The concurrent signal assignment you had will be elaborated to an equivalent process statement and generate an error when elaborated as it was originally shown:
ghdl -r correction
for signal: .correction(behavioral).array_new_signal11(3,2)
./correction:error: several sources for unresolved signal
./correction:error: error during elaboration
(For the ghdl simulator part of elaboration (which consists of linking and loading) is done when invoking simulation (the -r command, the loading part, where the design network is created)).
Renaud Pacalet suggests assigning the array value inside the process, but without an intervening wait statement the values are not available for subsequent use in the same simulation cycle. The new signal values are not available in the same simulation cycle they are assigned.
Each signal assignment schedules a waveform update and only one entry for a particular simulation time is available. In this case it would guarantee array(3, 2) would be the average of eight values of integer'left (which would be incorrect, you should get errors causing simulation to end during the accumulation of sum1 in the unlabelled 3rd loop statement first loop iteration).
And that tells us you need the array initialized before being read.
The only reason the above example succeeds is that there are no array elements when added together won't violate the value range of type integer with the values you specified.
You can get around this sort of thing by using binary array equivalents of integers and paying attention to the needed accuracy.
There are several morals to this story. First, VHDL isn't a programming language, second it's strongly typed and third signals assignment values are never visible in the simulation cycle they are made in.
Note that kelvin has been added as boolean signal to trigger execution of the process once without changing it.
You are trying to drive the array_new_signal11 signal from two different processes. Yes, your first concurrent signal assignment:
array_new_signal11 <= ((1,2,3,4,5),
(4,5,6,7,8),
(7,8,9,0,1),
(1,3,6,5,9),
(2,3,5,4,5));
is a shorthand for a process. It models a hardware driver that continuously imposes these values to your array signal (which is just a bunch of wires, at the end).
Your second process also tries to impose a value to one cell of your array (cell array_new_signal11(3,2)). In electrical engineering, this situation is called a short-circuit: what would you expect when the two drivers disagree? This is also the reason why your simulator refuses this: it does not know what to do with this signal.
Solution: drive this signal from one single process:
process(kelvin)
...
begin
array_new_signal11 <= (
(1,2,3,4,5),
(4,5,6,7,8),
...
for Row in 0 to 4 loop
...
end process;
Notes:
Average being a variable you should have another error on:
Average <= Total_Sum / 8;
which should be:
Average := Total_Sum / 8;
You are using the same loop index (Column) in two nested loops. Not sure what you are trying to do but this is not very safe.
Even with my suggestion to fix your error you will hit another problem: the array_new_signal11 is both an input (you read it) and an output (you assign it) of your process. It should thus also be listed in the sensitivity list. In electrical engineering this is called a combinatorial loop and is usually highly undesirable, except if you want to create an oscillator or a kind of random generator.
Your process is sensitive to signal Kelvin but does not use it. Strange situation. Do you have a clear idea of what hardware you are trying to model?
You probably believe that your process variables are re-initialized to 0 each time the process resumes (that is, each time Kelvin changes). This is not the case: they retain the last value they were assigned. Probably not what you want. You should initialize them at the beginning of your process body.

VHDL Fixed_pkg Getting bound check failure when adding 2 ufixed values

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

VHDL pass range to procedure

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.

Resources