VHDL: Instantiated component generic array index out of range - vhdl

I'm trying to implement an encryption algorithm in VHDL, and I have created a Feedback Shift Register component with generic parameters to improve reusability. It's my first time using generics and arrays, so please bear with me.
This component takes the feedback bits as an input, and it connects some of its bits (taps) to an output port, but this connections can be changed using a generic parameter. Code for the FSR component:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.fsr_taps_type.all;
entity FSR is
generic (
r_WIDTH : integer; -- Register width
r_STEP : integer; -- Update step
r_FWIDTH : integer; -- Feedback output width
r_HWIDTH : integer; -- h-function output width
r_TAPS : TAPS; -- Change the size according to the number of taps
r_STATE : TAPS
);
port (
clk : in std_logic;
rst : in std_logic;
fb_in : in std_logic_vector ((r_STEP-1) downto 0);
init : in std_logic;
ini_data : in std_logic_vector ((r_WIDTH-1) downto 0);
out_data : out std_logic_vector ((r_STEP-1) downto 0);
fb_out : out std_logic_vector ((r_FWIDTH-1) downto 0);
h_out : out std_logic_vector ((r_HWIDTH-1) downto 0)
);
end entity;
architecture behavioural of FSR is
signal shifted,shifted_next : std_logic_vector((r_WIDTH-1) downto 0);
begin
process(clk,rst)
begin
if rst = '1' then
shifted <= (others => '0');
elsif clk'event and clk = '1' then
shifted <= shifted_next;
end if;
end process;
process (fb_in,init,ini_data,shifted)
begin
if init = '1' then
shifted_next <= ini_data;
else
shifted_next <= shifted((r_WIDTH-r_STEP-1) downto 0) & fb_in;
end if;
end process;
out_data <= shifted ((r_WIDTH-1) downto (r_WIDTH-r_STEP));
-- The bits defined in the r_TAPS and r_STATE arrays are connected to the outputs in the same order as they are written (left to right)
-- Example: r_TAPS := (10,6) will create fb_out (1 downto 0) = bit10 & bit 6, in that order
-- Connect taps in the order of r_TAPS
gen_feedback: for I in (r_FWIDTH-1) downto 0 generate
fb_out(I) <= shifted(r_STATE(r_FWIDTH-I-1));
end generate gen_feedback;
-- Connect output bits for h function
gen_h: for I in (r_HWIDTH-1) downto 0 generate
h_out(I) <= shifted(r_STATE(r_HWIDTH-I-1));
end generate gen_h;
end architecture;
My problem comes when instantiating this component twice in the same file, using different generic values. This is the generic map of both instances:
LFSR : FSR
generic map (
r_WIDTH => 128,
r_STEP => STEP,
r_FWIDTH => 6,
r_HWIDTH => 7,
r_TAPS (0 to 5) => (128,121,90,58,47,32),
r_STATE (0 to 6) => (34,49,68,86,108,115,120)
)
NFSR : FSR
generic map (
r_WIDTH => 128,
r_STEP => STEP,
r_FWIDTH => 29,
r_HWIDTH => 2,
r_TAPS (0 to 28) => (40,36,35,33,106,104,103,58,50,46,117,115,111,110,88,80,101,69,67,63,125,61,60,44,128,102,72,37,32),
r_STATE => (33,116)
)
When I only create the first instance, the elaboration works as expected and Vivado gives no error whatsoever. However, when I add the second instance, I get an out of range error:
ERROR: [Synth 8-97] array index 0 out of range (FSR.vhdl:54)
Line 54 is the line inside the first for generate loop:
fb_out(I) <= shifted(r_STATE(r_FWIDTH-I-1));
Only the second instance gives an error. I have tried copying the parameters from the first instance into the second, and I still get the same error.
What am I doing wrong?
EDIT: I added the whole code for the FSR component.
EDIT 2: I changed the type declaration for TAPS, so that the array is constrained:
type TAPS is array (0 to 31) of integer;
This seems to be working, I just have to add an others statement to fill the unused array numbers, so this:
r_TAPS (0 to 5) => (128,121,90,58,47,32)
Becomes this:
r_TAPS (0 to 5) => (128,121,90,58,47,32,others =>0)
As I said before, I'm new to arrays in VHDL, so I would like to know if there is a way to do this for arbitrarily long arrays using an unconstrained array type.

Related

VHDL: big slv array slicing indexed by integer (big mux)

I want to slice a std_logic_vector in VHDL obtaining parts of it of fixed dimensions.
The general problem is:
din N*M bits
dout M bits
sel clog2(N) bits
Expected behaviour in an example (pseudocode): input 16 bit, want to slice it in 4 subvectors of 4bit each.
signal in: std_logic_vector(N*M-1 downto 0);
signal sel: integer;
-- with sel = 0
output <= in(N-1:0);
--with sel = 1 output <= in(2N-1:N)
-- with sel = 2
output <= in(3N-1:2N)
.....
--with sel = M-1
output <= in(M*N-1:(M-1)N)
I know a couples of way to do this, but I don't know which one is the best practice and give the best results in synthesis.
the entity
din: in std_logic_vector(15 downto 0);
dout: out std_logic_vector(3 downto 0);
sel: in std_logic_vecotor(1 downto 0)
CASE STATEMENT
case sel is
when "00" => dout <= din(3:0);
when "01" => dout <= din(7:4);
when "10" => dout <= din(11:8);
when "11" => dout <= din(15:12);
when others => ....`
It clearly implement a mux, but it's not generic at all and If the input gets big it's really hard to write and to codecover.
INTEGER INDEXING
sel_int <= to_integer(unsigned(sel));
dout <= din(4*(sel_int+1) - 1 downto 4*sel_int);
Extremely easy to write and to mantain, BUT it can have problems when the input is not a power of 2. For example, if I want to slice a 24bit vector in chunks of 4, what happen when the integer conversion of sel brings to the index 7?
A STRANGE TRADEOFF
sel_int <= to_integer(unsigned(sel));
for i in 0 to 4 generate
din_slice(i) <= din(4*(i+1)-1 downto 4*i);
end generate dout <= din_slice(sel_int);
I'm searching a solution that is general enough to be used with various input/output relationships and safe enough to be synthesized consistently everytime.
The Case statement is the only one with the Others case (that feels really safe), the other solutions rely on the slv to integer conversion and indexing that feels really comfortable but not so reliable.
Which solution would you use?
practical usecase
I have a 250bit std_logic_vector and I need to select 10 contigous bits inside of it starting from a certain point from 0 to 239. How can I do that in a way that is good for synthesis?
There is another option that is accepted by tools that allow VHDL 2008 (which includes Vivado and Prime Pro). You can use an unconstrained 2d type from a package:
type slv_array_t is array(natural range <>) of std_logic_vector; --vhdl 2008 unconstrained array type
then you can simply select which port you want. And it is as generic as you like.
library ieee;
use ieee.std_logic_1164.all;
use work.my_pkg.all;
entity mux is
generic (
N : natural;
M : natural
);
port (
sel : in natural;
ip : in slv_array_t (N-1 downto 0)(M-1 downto 0);
op : out std_logic_vector (M-1 downto 0);
);
end entity;
architecture rtl of mux is
begin
op <= ip(sel);
end architecture;
First you must extend the incoming data to be sure to have always as much bits as you need for connecting all multiplexer inputs (see the code below, process p_extend).
This will not create any logic at synthesis.
Second you must convert the resulting vector into an array, which you can access later by an index (see the code below, process p_create_array).
Again this will not create any logic at synthesis.
At last you must access this array by the select input signal (see the code below, process p_mux).
library ieee;
use ieee.std_logic_1164.all;
entity mux is
generic (
g_data_width : natural := 250;
g_slice_width : natural := 10;
g_sel_width : natural := 5;
g_start_point : natural := 27
);
port (
d_i : in std_logic_vector(g_data_width-1 downto 0);
sel_i : in std_logic_vector(g_sel_width-1 downto 0);
d_o : out std_logic_vector(g_slice_width-1 downto 0)
);
end entity mux;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
architecture struct of mux is
signal data : std_logic_vector(g_slice_width * 2**g_sel_width-1 downto 0);
type t_std_logic_slice_array is array (natural range <>) of std_logic_vector(g_slice_width-1 downto 0);
signal mux_in : t_std_logic_slice_array (2**g_sel_width-1 downto 0);
begin
p_extend: process(d_i)
begin
for i in 0 to g_slice_width * 2**g_sel_width-1 loop
if i+g_start_point<g_data_width then
data(i) <= d_i(i+g_start_point);
else
data(i) <= '0';
end if;
end loop;
end process;
p_create_array: process (data)
begin
for i in 0 to 2**g_sel_width-1 loop
mux_in(i) <= data((i+1)*g_slice_width-1 downto i*g_slice_width);
end loop;
end process;
p_mux: d_o <= mux_in(to_integer(unsigned(sel_i)));
end architecture;

VHDL No drivers exist on out port

I am doing my first project in VHDL, I try to implement 8-bit barrel shifter using mux.
This is code for one block (8 mux in chain):
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE work.sample_package.all;
-------------------------------------
ENTITY Shifter IS
GENERIC (n : INTEGER );
PORT ( x,y: IN STD_LOGIC_VECTOR (n-1 DOWNTO 0);
redB: IN Integer;
out_m: OUT STD_LOGIC_VECTOR(n-1 downto 0));
END Shifter;
--------------------------------------------------------------
ARCHITECTURE dfl OF Shifter IS
SIGNAL sm : STD_LOGIC;
SIGNAL what_b : STD_LOGIC;
BEGIN
--redB in the number of the red block in the diagram
--The first mux port map is the same for all three blocks
sm <= y(redB);
first : MUX port map(
a => x(0),
b => '0',
s0 => sm,
y => out_m(0)
);
b0: if redB=0 generate --First block - only the first mux has b=0
rest : for i in 1 to n-1 generate
chain : MUX port map(
a => x(i),
b => x(i-1),
s0 => sm,
y => out_m(i)
);
end generate;
end generate;
b1: if redB=1 generate
rest : for i in 1 to n-1 generate
what_b <= '0' when i=1 else --Second block - 2 first mux has b=0
x(i-2);
chain : MUX port map(
a => x(i),
b => what_b,
s0 => sm,
y => out_m(i)
);
end generate;
end generate;
b2: if redB=2 generate
rest : for i in 1 to n-1 generate
what_b <= '0' when i=1 or i=2 or i=3 else --Third block - 4 first mux has b=0
x(i-4);
chain : MUX port map(
a => x(i),
b => what_b,
s0 => sm,
y => out_m(i)
);
end generate;
end generate;
END dfl;
In this is the code for changing 3 shifters:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE work.sample_package.all;
-------------------------------------
ENTITY Barrel IS
GENERIC (n : INTEGER);
PORT ( x,y: IN STD_LOGIC_VECTOR (n-1 DOWNTO 0);
out_shifter0,out_shifter1,out_shifter2: OUT STD_LOGIC_VECTOR(n-1 downto 0));
END Barrel;
--------------------------------------------------------------
ARCHITECTURE dfl OF Barrel IS
SIGNAL temp_out0 : std_logic_vector(n-1 DOWNTO 0);
SIGNAL temp_out1 : std_logic_vector(n-1 DOWNTO 0);
SIGNAL temp_out2 : std_logic_vector(n-1 DOWNTO 0);
BEGIN
y0: Shifter GENERIC MAP(n) port map (x=>x,y=>y,redB=>0,out_m=>temp_out0);
out_shifter0 <= temp_out0;
y1: Shifter GENERIC MAP(n) port map (x=>temp_out0,y=>y,redB=>1,out_m=>temp_out1);
out_shifter1 <= temp_out1;
y2: Shifter GENERIC MAP(n) port map (x=>temp_out1,y=>y,redB=>2,out_m=>temp_out2);
out_shifter2 <= temp_out2;
END dfl;
All the files are compiling, but when I try to run a simulation I get this warning:
# ** Warning: (vsim-8684) No drivers exist on out port /tb/L0/y1/out_m(7 downto 1), and its initial value is not used.
#
# Therefore, simulation behavior may occur that is not in compliance with
#
# the VHDL standard as the initial values come from the base signal /tb/L0/temp_out1(7 downto 1).
I am using ModelSim.
Anyone got any idea of what could be the problem?
Thanks!
You have done a generate with a signal, and compared its value to something. Integers initialise to -2^31, so none of the generate blocks exist because the values you have assigned externally do not get assigned until after the simulation is started, but the generates get created during elaboration (before the simulation starts) using the initial value of redB. Hence no drivers for out_m. Instead of using a signal in the generate condition, use generics instead, as their values are fixed and assigned during elaboration.

VHDL: genric map setup

Why would this setup work?
component mux2to1 is
generic (M : integer := 1); -- Number of bits in the inputs and output
port (input0 : in m32_vector(M-1 downto 0) := (others => '0');
input1 : in m32_vector(M-1 downto 0) := (others => '0');
sel : in m32_1bit;
output : out m32_vector(M-1 downto 0));
end component;
The way I understand genric map is (M: integer: 1) would specify the bit of port to be 1 through out but when M-1 downto 0 would just be 0 down 0, which makes no sense.
As #user1155120 said, you can have an array that has 1 element. (0 downto 0) would have 1 element.
There is another important point to make, however:
In VHDL an array of 1 element of a certain type is not the same type as the element type. So, for example, std_logic and std_logic_vector(0 downto 0) are different types. You cannot assign one to the other. std_logic is a scalar whilst std_logic_vector(0 downto 0) is an array type.
To "convert" between these types, you need to index the array type. So, with signals
signal S : std_logic;
signal A : std_logic_vector(0 downto 0);
you cannot assign A to S or visa versa, but you can do this:
A(0) <= S;
or this:
S <= A(0);
You can also index array ports. So, with
entity HAS_ARRAY_PORT
port ( P : in std_logic_vector(0 downto 0));
end;
You can do this:
L: entity work.HAS_ARRAY_PORT port map (P(0) => S);

Query on VHDL generics in packages

I have written a simple VHDL code to add two matrices containing 32 bit floating point numbers. The matrix dimensions have been defined in a package. Currently, I specify the matrix dimensions in the vhdl code and use the corresponding type from the package. However, I would like to use generic in the design to deal with matrices of different dimensions. For this I would have to somehow use the right type defined in the package. How do I go about doing this?
My current VHDL code is as below.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.mat_pak.all;
entity newproj is
Port ( clk : in STD_LOGIC;
clr : in STD_LOGIC;
start : in STD_LOGIC;
A_in : in t2;
B_in : in t2;
AplusB : out t2;
parallel_add_done : out STD_LOGIC);
end newproj;
architecture Behavioral of newproj is
COMPONENT add
PORT (
a : IN STD_LOGIC_VECTOR(31 DOWNTO 0);
b : IN STD_LOGIC_VECTOR(31 DOWNTO 0);
clk : IN STD_LOGIC;
sclr : IN STD_LOGIC;
ce : IN STD_LOGIC;
result : OUT STD_LOGIC_VECTOR(31 DOWNTO 0);
rdy: OUT STD_LOGIC
);
END COMPONENT;
signal temp_out: t2 := (others=>(others=>(others=>'0')));
signal add_over: t2bit:=(others=>(others=>'0'));
signal check_all_done,init_val: std_logic:='0';
begin
init_val <= '1';
g0: for k in 0 to 1 generate
g1: for m in 0 to 1 generate
add_instx: add port map(A_in(k)(m), B_in(k)(m), clk, clr, start, temp_out(k)(m), add_over(k)(m));
end generate;
end generate;
g2: for k in 0 to 1 generate
g3: for m in 0 to 1 generate
check_all_done <= add_over(k)(m) and init_val;
end generate;
end generate;
p1_add:process(check_all_done,temp_out)
begin
AplusB <= temp_out;
parallel_add_done <= check_all_done;
end process;
end Behavioral;
My package is as below
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.ALL;
package mat_pak is
subtype small_int is integer range 0 to 2;
type t22 is array (0 to 1) of std_logic_vector(31 downto 0);
type t2 is array (0 to 1) of t22; --2*2 matrix
type t22bit is array (0 to 1) of std_logic;
type t2bit is array (0 to 1) of t22bit; --2*2 matrix bit
type t33 is array (0 to 2) of std_logic_vector(31 downto 0);
type t3 is array (0 to 2) of t33; --3*3 matrix
end mat_pak;
Any suggestions would be welcome. Thank you.
There are some logical issues with your design.
First, there's some maximum number of ports for a sub-hierarchy a design can tolerate, you have 192 'bits' of matrix inputs and outputs. Do you really believe this number should be configurable?
At some point it will only fit in the very large FPGA devices, and shortly thereafter not fit there either.
Imagining some operation taking a variable number of clocks in add and parallel_add_done signifies when an aplusb datum is available comprised of elements of the matrix array contributed by all instantiated add components, the individual rdy signals are ANDed together. If the adds all take the same amount of time you could take the rdy from anyone of them (If you silicon is not that deterministic it would not be usable, there are registers in add).
The nested generate statements all assign the result of the AND between add_over(k,m) and init_val (which is a synthesis constant of 1). The effect or wire ANDing add_over(k.m) bits together (which doesn't work in VHDL and is likely not achievable in synthesis, either).
Note I also showed the proper indexing method for the two dimensional arrays.
Using Jonathan's method of sizing matrixes:
library ieee;
use ieee.std_logic_1164.all;
package mat_pak is
type matrix is array (natural range <>, natural range <>)
of std_logic_vector(31 downto 0);
type bmatrix is array (natural range <>, natural range <>)
of std_logic;
end package mat_pak;
library ieee;
use ieee.std_logic_1164.all;
use work.mat_pak.all;
entity newproj is
generic ( size: natural := 2 );
port (
clk: in std_logic;
clr: in std_logic;
start: in std_logic;
a_in: in matrix (0 to size - 1, 0 to size - 1);
b_in: in matrix (0 to size - 1, 0 to size - 1);
aplusb: out matrix (0 to size - 1, 0 to size - 1);
parallel_add_done: out std_logic
);
end entity newproj;
architecture behavioral of newproj is
component add
port (
a: in std_logic_vector(31 downto 0);
b: in std_logic_vector(31 downto 0);
clk: in std_logic;
sclr: in std_logic;
ce: in std_logic;
result: out std_logic_vector(31 downto 0);
rdy: out std_logic
);
end component;
signal temp_out: matrix (0 to size - 1, 0 to size - 1)
:= (others => (others => (others => '0')));
signal add_over: bmatrix (0 to size - 1, 0 to size - 1)
:= (others => (others => '0'));
begin
g0:
for k in 0 to size - 1 generate
g0x:
for m in 0 to size - 1 generate
add_instx: add
port map (
a => a_in(k,m),
b => b_in(k,m),
clk => clk,
sclr => clr,
ce => start,
result => temp_out(k,m),
rdy => add_over(k,m)
);
end generate;
end generate;
aplusb <= temp_out;
p1_add:
process (add_over)
variable check_all_done: std_logic;
begin
check_all_done := '1';
for k in 0 to size - 1 loop
for m in 0 to size -1 loop
check_all_done := check_all_done and add_over(k,m);
end loop;
end loop;
parallel_add_done <= check_all_done;
end process;
end architecture behavioral;
We find that we really want to AND the various rdy outputs (add_over array) together. In VHDL -2008 this can be done with the unary AND, otherwise you're counting on a synthesis tool to flatten the AND (and they generally do).
I made the assignment to aplusb a concurrent assignment.
So I dummied up an add entity with an empty architecture, the above then analyzes, elaborates and simulates, which shows that none of the connectivity has length mismatches anywhere.
I'm not quite sure to understand perfectly, but I'll try to answer anyway ;)
You can use unconstrained array like this:
package mat_pak is
type matrix is array(natural range <>, natural range <>) of std_logic_vector(31 downto 0);
end package mat_pack;
entity newproj is
Generic ( size : natural );
Port ( clk : in STD_LOGIC;
clr : in STD_LOGIC;
start : in STD_LOGIC;
A_in : in matrix(0 to size-1, 0 to size-1);
B_in : in matrix(0 to size-1, 0 to size-1);
AplusB : out matrix(0 to size-1, 0 to size-1);
parallel_add_done : out STD_LOGIC);
end newproj;

Issues with indexing arrays in VHDL

I have an assignment to create a simple microprocessor in VHDL. My code looks like this
architecture Behavioral of uc is
type instruction_t is array (255 downto 0) of std_logic_vector (15 downto 0);
constant LOAD : std_logic_vector(7 downto 0) :=x"01";
--some more instruction codes defined
signal PC : std_logic_vector (7 downto 0); -- program counter
signal cur_inst : std_logic_vector (15 downto 0);
constant ROM :
instruction_t :=
(
(LOAD & x"07"),
(ADD & x"05"),
-- some more code goes here
others => x"0000"
);
begin
process (CLK, RESET) is
begin
if RESET = '1' then
-- do stuff
elsif rising_edge(CLK) then
cur_inst <= ROM(conv_integer(PC));
PC <= PC + 1;
-- some other stuff
end if;
end process;
end Behavioral;
The problem I have is with this part:
cur_inst <= ROM(conv_integer(PC));
because simply nothing happens - cur_inst is always zero. I tried using
cur_inst <= ROM(to_integer(unsigned(PC));
but result is the same - I get nothing. PC is incremented properly, but I cannot read anything from ROM array. I also tried defining PC as unsigned or integer, but result is the same. What am I doing wrong?
Since you are defining instruction_t as an array(255 downto 0), initializing the array may be occuring in the opposite order that you intended.
(LOAD & x"07") will be assigned to ROM(255), (ADD & x"05") will be assigned to ROM(254), etc.
Define the type instruction_t as an array (0 to 255) to avoid this problem.
Another way of fixing the problem would have been to bind your instructions to the specific addresses you wanted, instead of just expecting it to happen : for that, use named association and write
constant ROM :
instruction_t :=
(
0 => (LOAD & x"07"),
1 => (ADD & x"05"),
-- some more code goes here
others => x"0000"
);

Resources