It looks like it is a VHDL issue.
Using explicite indices at all bits gives a expected result.
And first signal ( i)(0) are alle plain wires, while all following signals are registers of a shift-register array.
MCLK_PROC: process (clock, mclk_r_in)
begin
for i in signal_count -1 downto 0 loop
mclk_r.ddr_chain(i)(0) <= mclk_r_in.ddr_chain(i)(0);
end loop;
if rising_edge (clock) then
for i in signal_count -1 downto 0 loop
for j in 2 downto 1 loop
mclk_r.ddr_chain(i)(j) <= mclk_r_in.ddr_chain(i)(j);
end loop;
end loop;
mclk_r.output <= mclk_r_in.output;
end if;
end process;
Doing the same with a ranged index assignment creates a wiereder result.
Now just signal (0) is assigned to all its signal stages 2 downto 0 at all times thus it directly feeds into output register.
All other mclk_r.ddr_chain registers with i > 0 are just registers with undefined input.
MCLK_PROC: process (clock, mclk_r_in)
begin
mclk_r.ddr_chain(signal_count - 1 downto 0)(0) <= mclk_r_in.ddr_chain(signal_count - 1 downto 0)(0);
if rising_edge (clock) then
for i in signal_count -1 downto 0 loop
for j in 2 downto 1 loop
mclk_r.ddr_chain(i)(j) <= mclk_r_in.ddr_chain(i)(j);
end loop;
end loop;
mclk_r.output <= mclk_r_in.output;
end if;
end process;
Thus the double range assignment somewhat drops the first range index and only uses the second one ... ?
This bavior is consistent between simulation and synthesis. Thus probably only caused by strange VHDL-rules.
Testcode:
library ieee;
use ieee.std_logic_1164.all;
entity Test_unexpectedSignal_Syn is
generic (signal_count : natural := 5);
port (clock : in std_logic;
pin_in : in std_logic_vector (signal_count - 1 downto 0);
synced_out: out std_logic_vector (signal_count - 1 downto 0)
);
end entity;
architecture rtl of Test_unexpectedSignal_Syn is
type t_signal_array is array (signal_count - 1 downto 0) of std_logic_vector (2 downto 0);
type reg_mclk_t is record
ddr_chain : t_signal_array;
output : std_logic_vector(signal_count - 1 downto 0);
end record;
signal mclk_r : reg_mclk_t;
signal mclk_r_in : reg_mclk_t;
begin
synced_out <= mclk_r.output;
COMP_PROC: process (pin_in, mclk_r)
begin
for i in signal_count - 1 downto 0 loop
mclk_r_in.ddr_chain(i)(0) <= pin_in(i);
mclk_r_in.ddr_chain(i)(2 downto 1) <= mclk_r.ddr_chain(i)(1 downto 0);
mclk_r_in.output(i) <= mclk_r.ddr_chain(i)(2);
end loop;
end process;
MCLK_PROC: process (clock, mclk_r_in)
begin
-- just pass through the pin assignment
mclk_r.ddr_chain(signal_count - 1 downto 0)(0) <= mclk_r_in.ddr_chain(signal_count - 1 downto 0)(0);
-- for i in signal_count -1 downto 0 loop
-- mclk_r.ddr_chain(i)(0) <= mclk_r_in.ddr_chain(i)(0);
-- end loop;
if rising_edge (clock) then
-- only make part of the record a register set (all except the zerothed signal column)
mclk_r.ddr_chain(signal_count - 1 downto 0)(2 downto 1) <= mclk_r_in.ddr_chain(signal_count - 1 downto 0)(2 downto 1); -- this line results in # ** Fatal: Unexpected signal: 11.
--mclk_r.ddr_chain(signal_count - 1 downto 0)(2 downto 1) <= mclk_r_in.ddr_chain(signal_count - 1 downto 0)(2 downto 1); -- this line results in # ** Fatal: Unexpected signal: 11.
--mclk_r.ddr_chain(signal_count - 1 downto 0) <= mclk_r_in.ddr_chain(signal_count - 1 downto 0); -- this works
--mclk_r.ddr_chain(signal_count - 1 downto 0)(1) <= mclk_r_in.ddr_chain(signal_count - 1 downto 0)(1); -- this works, but has a weired synthesis result as if (signal_count - 1 downto 0) is not used
--mclk_r.ddr_chain(signal_count - 1 downto 0)(2) <= mclk_r_in.ddr_chain(signal_count - 1 downto 0)(2); -- this works
-- using such an explicite loop changes the synthesis result too, but simulation compilation works
-- for i in signal_count -1 downto 0 loop
-- for j in 2 downto 1 loop
-- mclk_r.ddr_chain(i)(j) <= mclk_r_in.ddr_chain(i)(j);
-- end loop;
-- end loop;
mclk_r.output <= mclk_r_in.output;
--mclk_r <= mclk_r_in;
end if;
end process;
end rtl;