VHDL - How to compare two bit_vectors for dynamic table lookup - vhdl

I'm storing two tables in two signals. One table keeps the key (address) and the other keeps the value corresponding to the key. I need to compare an input to the key and, if they match, return the value stored.
The reason why I need this is for a dynamic lookup table for branch instruction prediction. In the fetch stage of a processor I get the input Instruction_Address and I return a branch_To_Address and a branch_Prediction. Initially I want to store 16 predictions/branch addresses and use a circular buffer ring to overwrite as needed.
I've been trying to use a FOR with a nested IF to search for the key inside keyTable.
The whole module seems to work fine, except when I compare two bit_vectors with the IF statement. I need this twice (one on read and another on write) and hence I need to "sweep" the keysTable so I can see if the address that is being looked up has an entry.
I noticed the error upon simulation, where the ELSE clause is being called always regardless of the keysTable having the right entries.
Verifiable example:
library IEEE;
use ieee.numeric_bit.all;
entity branch_prediction_table is
generic (
addrSize : NATURAL := 4;
tableSize : NATURAL := 4);
port (
clock : in bit;
input_addr: in bit_vector(addrSize-1 downto 0);
return_value : out bit );
end branch_prediction_table;
architecture branch_table of branch_prediction_table is
signal keysTable : bit_vector(addrSize*tableSize-1 downto 0) := ( others => '0');
signal valuesTable : bit_vector(tableSize*2-1 downto 0) := ( others => '0');
begin
tableProc: process(clock) is
variable valueFromTable : bit;
begin
if rising_edge(clock) then
search_table: for iR in (tableSize-1) to 0 loop
if (keysTable(addrSize*(iR+1)-1 downto addrSize*iR) = input_addr) then
valueFromTable := valuesTable((iR+1)*2-1);
EXIT search_table;
else
valueFromTable := '0';
end if;
end loop search_table;
return_value <= valueFromTable;
end if; -- rising_edge(clock)
end process tableProc;
end branch_table;
with verifiable testbench simulation TCL:
add wave -position insertpoint \
sim:/branch_prediction_table/addrSize \
sim:/branch_prediction_table/clock \
sim:/branch_prediction_table/input_addr \
sim:/branch_prediction_table/keysTable \
sim:/branch_prediction_table/return_value \
sim:/branch_prediction_table/tableSize \
sim:/branch_prediction_table/valuesTable
force -freeze sim:/branch_prediction_table/valuesTable 11111111 0
force -freeze sim:/branch_prediction_table/keysTable 1111101001100011 0
force -freeze sim:/branch_prediction_table/clock 0 0, 1 {5000 ps} -r {10 ns}
run 10 ns
force -freeze sim:/branch_prediction_table/input_addr 1010 0
run 20 ns
force -freeze sim:/branch_prediction_table/input_addr 1111 0
run 10 ns
and testbench simulation result showing that error is indeed in the IF:
I have tried converting them with to_integer(unsigned(bit_vector1)) = to_integer(unsigned(bit_vector2)) with no avail

As user1155120 pointed out:
The problem lies within search_table: for iR **in** (tableSize-1) to 0 loop
It should've been "down to" as L > R. Since I used "in" with L>R, that produces a null range and the for loop iteration is said to be complete.
(IEEE Std 1076-2008 5.2 Scalar types, "A range specifies a subset of values of a scalar type. A range is said to be a null range if the specified subset is empty. The range L to R is called an ascending range; if L > R, then the range is a null range. The range L downto R is called a descending range; if L < R, then the range is a null range.").
10.10 Loop statement "For the execution of a loop with a for iteration scheme, the discrete range is first evaluated. If the discrete range is a null range, the iteration scheme is said to be complete, ..."

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.

Error: type error near ... ; current type... ; expected type

I want to input data from r_D1_1 and r_D2_1 (these are outputs from a separate entity and continually change) into r_wave0 and r_wave1 respectively and I have tried doing the following:
r_wave0 <= r_D1_1 ;
r_wave1 <= r_D2_1 ;
This has resulted in the error (I have shown the error for the first line as the 2nd line is the same other than swapped input):
Error : type error near r_D1_1 ; current type integer ; expected type t_wave
As previously highlighted, the inputs are not fixed and so I wanted to assign them to r_wave0 and r_wave1 as they were generated. Should I overhaul the way I am creating the other arrays and 'calling' the input or maybe I shouldn't be labelling them as " in integer range 0 to 256 " and instead using SLVs?
All feedback is appreciated, thank you!
Below is a copy of the code for the entire entity.
entity Demodulator_A is
Port ( i_Clk : in STD_LOGIC;
i_Reset : in STD_LOGIC;
r_D2_1 : in integer range 0 to 256;
r_D1_1 : in integer range 0 to 256;
final_D2 : out integer range 0 to 256;
final_D1 : out integer range 0 to 256 );
end Demodulator_A;
architecture Behavioral of Demodulator_A is
type t_waveform1 is array (0 to 7) of integer; -- Array for reference waveform '1'
signal r_waveform1 : t_waveform1 := (16#80#, 16#60#, 16#40#, 16#60#, 16#80#, 16#A0#, 16#C0#, 16#A0#);
signal r_demodulated1 : t_waveform1;
type t_waveform0 is array (0 to 7) of integer; -- Array for reference waveform '0'
signal r_waveform0 : t_waveform0 := (16#80#, 16#A0#, 16#C0#, 16#A0#, 16#80#, 16#60#, 16#40#, 16#60#);
signal r_demodulated0 : t_waveform0;
type t_wave is array (0 to 7) of integer;
signal r_wave0 : t_wave ;
signal r_wave1 : t_wave ;
begin
DeModulationProc : process (i_Clk)
begin
if (r_wave0(2)>=16#81#) then
r_demodulated0 <= r_waveform0;
elsif (r_wave1(2)<=16#7F#) then
r_demodulated1 <= r_waveform1;
end if;
end process;
--0 = 80, A0, C0, A0, 80, 60, 40, 60. -- in hex the waveform values for 0
--1 = 80, 60, 40, 60, 80, A0, C0, A0. -- in hex the waveform values for 1
end Behavioral;
EDIT 1: I am trying to assign 1 changing integer to a whole array and it's causing problems so I want to assign the first integer to the first element of the array, the 2nd integer to the 2nd element and so on. From a comment I now know I could address the array with a counter and this counter could increment every time the input values changes. I will upload this code once I have written it to help others. Thank you Brian!
EDIT 2: I did have 2 libraries listed but forgot to include them in the above code snippet. Below are the two I used.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

if statement problem while converting a vector

im new at vhdl coding, and there is a problem with if statement
so my code is the following
i want to convert a vector(bar), if the statement is true (so in this example if its smaller than 10)
process(bar)
variable tmp : integer;
begin
tmp := to_integer(signed(bar));
if tmp < 10 then
good(3) <= bar(3);
good(2) <= bar(3) xor bar(2);
good(1) <= bar(2) xor bar(1);
good(0) <= bar(1) xor bar(0);
end if;
end process;
but the problem is that the statement is not working, if i put a bigger number for example "1111" it is converting in the same way as it converted before
From the comments it seems you want good to be set to 0 whenever bar >= 10. In that case you can just do:
process(bar)
variable tmp : integer;
begin
tmp := to_integer(signed(bar));
if tmp < 10 then
good(3) <= bar(3);
good(2) <= bar(3) xor bar(2);
good(1) <= bar(2) xor bar(1);
good(0) <= bar(1) xor bar(0);
else
good <= (others => '0');
end if;
end process;
The vector good is only assigned when tmp < 10. So there is a latch inferred by this process.
You need to define what's the "else" value for good in any other conditions.
You state that
if i put a bigger number for example "1111" it is converting in the
same way as it converted before
As Tricky pointed out in a comment above, your problem is this line here:
tmp := to_integer(signed(bar));
^^^^^^
You have not posted an MCVE, so I cannot be sure, but your question implies that bar is 4 bits wide. Assuming that is the case, the value "1111" as a signed number is -1. So, if bar is set to "1111", tmp will be -1. -1 is less than 10, so this if statement will evaluate as true:
if tmp < 10 then
If you consider than "1111" is greater than 10 (ie it's 15), then you need to convert via the unsigned type, ie
tmp := to_integer(unsigned(bar));
^^^^^^^^
The range of a 4-bit signed number is -8 to +7. All the values in that range are less than 10, so your if statement will evaluate as true whatever the value of bar.
The above solution assumes that bar is always zero or positive. If that is not the case, then you need more bits in the signal bar. 5 is enough. A 5-bit signed number has the range -16 to +15.
So, I can see two solutions:
use an unsigned type in your type conversion or
make bar 5 bits
or wider.
Others have pointed out that, assuming this is synthsisable code and assuming that this is intended to be combinational logic, then you are missing a branch in your if statement; you don't drive the signal good in the case that the if statement is false and so if you were to synthesise this code as combinational logic, then you would get latches. But that is not the question you asked.

VHDL Integer Range Output Bus Width

I'm currently working on writing a simple counter in VHDL, trying to genericize it as much as possible. Ideally I end up with a counter that can pause, count up/down, and take just two integer (min, max) values to determine the appropriate bus widths.
As far as I can tell, in order to get an integer of a given range, I just need to delcare
VARIABLE cnt: INTEGER RANGE min TO max := 0
Where min and max are defined as generics (both integers) in the entity. My understanding of this is that if min is 0, max is 5, for example, it will create an integer variable of 3 bits.
My problem is that I actually want to output this integer. So, naturally, I write
counterOut : OUT INTEGER RANGE min TO max
But this does not appear to be doing what I need. I'm generating a schematic block in Quartus Prime from this, and it creates a bus output from [min...max]. For example, if min = 0, max = 65, it outputs a 66 bit bus. Instead of the seven bit bus it should.
If I restricted the counter to unsigned values I might be able to just math out the output bus size, but I'd like to keep this as flexible as possible, and of course I'd like to know what I'm actually doing wrong and how to do it properly.
TL;DR: I want a VHDL entity to take generic min,max values, and generate an integer output bus of the required width to hold the range of values. How do?
If it matters, I'm using Quartus Prime Lite Edition V20.1.0 at the moment.
Note: I know I can use STD_LOGIC_VECTOR instead, but it is going to simulate significantly slower and is less easy to use than the integer type as far as I have read. I can provide more of my code if necessary, but it's really this one line that's the problem as far as I can tell.
I originally posted this on Stackexchange, but I think Stackoverflow might be a better place since it's more of a programming than a hardware problem.
EDIT: Complete code shown below
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
USE ieee.std_logic_signed.all;
ENTITY Counter IS
GENERIC (modulo : INTEGER := 32;
min : INTEGER := 0;
max : INTEGER := 64);
PORT( pause : IN STD_LOGIC;
direction : IN STD_LOGIC; -- 1 is up, 0 is down
clk : IN STD_LOGIC;
counterOut : OUT INTEGER RANGE min TO max --RANGE 0 TO 32 -- THIS line is the one generating an incorrect output bus width
);
END ENTITY Counter;
-- or entity
ARCHITECTURE CounterArch OF Counter IS
BEGIN
PROCESS(direction, pause, clk)
VARIABLE cnt : INTEGER RANGE min TO max := 0;
VARIABLE dir : INTEGER;
BEGIN
IF direction = '1' THEN
dir := 1;
ELSE
dir := -1;
END IF;
IF clk'EVENT AND clk = '1' THEN
IF pause = '0'THEN
IF (cnt = modulo AND direction = '1') THEN
cnt := min; -- If we're counting up and hit modulo, reset to min value.
ELSIF (cnt = min AND direction = '0') THEN
cnt := modulo; --Counting down hit 0, go back to modulo.
ELSE
cnt := cnt + dir;
END IF;
END IF;
END IF;
counterOut <= cnt;
END PROCESS;
END ARCHITECTURE CounterArch;

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.

Resources