the following code (issue21.vhd) results in an eternal loop
-- Filename: tdi_read_control_tb.vhd
-- Copyright (c) 2014 Deltatec (www.deltatec.be)
-- This file is provided without any express or implied warranties, including,
-- but not limited to, the implied warranties of merchantability and fitness
-- for a particular purpose. It is not intended for use in life support
-- appliances, devices, or systems. Use in such applications is expressly
-- prohibited.--
-- Author: Benjamin Delhaye (BD)
-- History: 29/09/2020 (BD) : Creation of this file
-- 03/04/2024 (PN) : Adaptation of the file for Hyp4U project
--
-- Description:
-- Test bench for the TDI_READ_CONTROL block
-- Notes:
-- LIBRARY --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library dt;
use dt.dt_types_pkg.all;
use work.random_pkg.all;
-- ENTITY --
entity tdi_read_control_tb is
generic (
G_SENSOR : string := "CARDINAL1280"
);
end tdi_read_control_tb;
-- ARCHITECTURE --
architecture Behavioral of tdi_read_control_tb is
--autoformat_off
package tdi_pkg is new work.tdi_pkg generic map(G_SENSOR => G_SENSOR);
use tdi_pkg.all;
--autoformat_on
-- TYPE --
type t_test_inputs is record
test_title : string(1 to 512);
tdi_type : std_logic_vector(1 downto 0); -- TDI type (0: none, 1: TDI6 HR, 2: TDI12, 3: TDI3 FR)
dual_readout : std_logic; -- dual readout input register
hs_plus_pan_mode : std_logic; -- if the tdi type is doing HS + PAN
tdi_spectral_agglom : std_logic_vector(1 downto 0); -- (1: none, 2: SAx2, 3: SAx3)
hs_nol : std_logic_vector(11 downto 0); -- Number of lines to emulate in the ANC FIFO HS
pan_nol : std_logic_vector(11 downto 0); -- Number of lines to emulate in the ANC FIFO PAN
hs_off : boolean; -- Parameter that allow to emulate that no HS frame is generated
pan_off : boolean; -- Parameter that allow to emulate that no PAN frame is generated
no_accum : integer; -- Number of accumulations to emulate
no_accum_pan : integer; -- Number of accumulations to emulate for PAN frames (only used with CMV12K sensor)
end record t_test_inputs;
type t_test_cases is array (natural range <>) of t_test_inputs;
-- This type is used to define a table containing the ground truth about the accumulation and shift offset arrays
type t_verification_array is array (natural range <>) of t_int_array(3 downto 0);
-- The following series of constants are the meaning of the indexes in the verification array
constant C_TDI_12_TDI_11_PAN_IDX : integer := 0;
constant C_TDI_12_SA2_IDX : integer := 1;
constant C_TDI_12_SA3_IDX : integer := 2;
constant C_TDI_3_FR_IDX : integer := 3;
constant C_TDI_5_PAN_HR_TDI_6_HR_IDX : integer := 4;
constant C_TDI_6_HR_SA2_IDX : integer := 5;
constant C_TDI_6_HR_SA3_IDX : integer := 6;
constant C_TDI_6_DR_IDX : integer := 7;
constant C_TDI_6_DR_SA2_IDX : integer := 8;
constant C_TDI_6_DR_SA3_IDX : integer := 9;
constant C_PAN_MODE_IDX : integer := 10;
--The following series of constants are the meaning of the indexes at a given entry of the verification array
constant C_SHIFT_OFFSET_IDX0 : integer := 0;
constant C_SHIFT_OFFSET_IDX1 : integer := 1;
constant C_ACCUM_OFFSET_IDX1 : integer := 2;
constant C_ACCUM_OFFSET_IDX2 : integer := 3;
-- This function initializes the verification array and returns a table
function init_verification_array return t_verification_array is
variable v_verification_array : t_verification_array(10 downto 0);
begin
-- v_verification_array(i)(0) = shift_offset(0), v_verification_array(i)(1) = shift_offset(1), v_verification_array(i)(2) = accum_offset(1), v_verification_array(i)(3) = accum_offset(2)
-- v_verification_array(i)(4) = number of accumulation
-- first all the entries of the verification array are filled with the number of bytes per line, then multiplications are made where there must be done
for i in 0 to v_verification_array'length - 1 loop
v_verification_array(i)(C_SHIFT_OFFSET_IDX0) := C_BYTES_PER_LINE;
v_verification_array(i)(C_ACCUM_OFFSET_IDX1) := C_BYTES_PER_LINE;
v_verification_array(i)(C_ACCUM_OFFSET_IDX2) := C_BYTES_PER_LINE;
end loop;
--C_TDI_12_TDI_11_PAN_IDX
v_verification_array(C_TDI_12_TDI_11_PAN_IDX)(C_SHIFT_OFFSET_IDX0) := 12 * v_verification_array(C_TDI_12_TDI_11_PAN_IDX)(C_SHIFT_OFFSET_IDX0);
v_verification_array(C_TDI_12_TDI_11_PAN_IDX)(C_ACCUM_OFFSET_IDX2) := 2 * v_verification_array(C_TDI_12_TDI_11_PAN_IDX)(C_ACCUM_OFFSET_IDX2);
--C_TDI_12_SA2_IDX
v_verification_array(C_TDI_12_SA2_IDX)(C_SHIFT_OFFSET_IDX0) := 24 * v_verification_array(C_TDI_12_SA2_IDX)(C_SHIFT_OFFSET_IDX0);
v_verification_array(C_TDI_12_SA2_IDX)(C_ACCUM_OFFSET_IDX2) := 2 * v_verification_array(C_TDI_12_SA2_IDX)(C_ACCUM_OFFSET_IDX2);
--C_TDI_12_SA3_IDX
v_verification_array(C_TDI_12_SA3_IDX)(C_SHIFT_OFFSET_IDX0) := 36 * v_verification_array(C_TDI_12_SA3_IDX)(C_SHIFT_OFFSET_IDX0);
v_verification_array(C_TDI_12_SA3_IDX)(C_ACCUM_OFFSET_IDX2) := 2 * v_verification_array(C_TDI_12_SA3_IDX)(C_ACCUM_OFFSET_IDX2);
--C_TDI_3_FR_IDX
v_verification_array(C_TDI_3_FR_IDX)(C_SHIFT_OFFSET_IDX0) := 3 * v_verification_array(C_TDI_3_FR_IDX)(C_SHIFT_OFFSET_IDX0);
v_verification_array(C_TDI_3_FR_IDX)(C_ACCUM_OFFSET_IDX2) := 2 * v_verification_array(C_TDI_3_FR_IDX)(C_ACCUM_OFFSET_IDX2);
--C_TDI_5_PAN_HR_TDI_6_HR_IDX
v_verification_array(C_TDI_5_PAN_HR_TDI_6_HR_IDX)(C_ACCUM_OFFSET_IDX1) := 2 * v_verification_array(C_TDI_5_PAN_HR_TDI_6_HR_IDX)(C_ACCUM_OFFSET_IDX1);
v_verification_array(C_TDI_5_PAN_HR_TDI_6_HR_IDX)(C_ACCUM_OFFSET_IDX2) := 2 * v_verification_array(C_TDI_5_PAN_HR_TDI_6_HR_IDX)(C_ACCUM_OFFSET_IDX1);
--C_TDI_6_HR_SA2_IDX
v_verification_array(C_TDI_6_HR_SA2_IDX)(C_ACCUM_OFFSET_IDX1) := 2 * v_verification_array(C_TDI_6_HR_SA2_IDX)(C_ACCUM_OFFSET_IDX1);
v_verification_array(C_TDI_6_HR_SA2_IDX)(C_ACCUM_OFFSET_IDX2) := 2 * v_verification_array(C_TDI_6_HR_SA2_IDX)(C_ACCUM_OFFSET_IDX1);
--C_TDI_6_HR_SA3_IDX
v_verification_array(C_TDI_6_HR_SA3_IDX)(C_ACCUM_OFFSET_IDX1) := 2 * v_verification_array(C_TDI_6_HR_SA3_IDX)(C_ACCUM_OFFSET_IDX1);
v_verification_array(C_TDI_6_HR_SA3_IDX)(C_ACCUM_OFFSET_IDX2) := 2 * v_verification_array(C_TDI_6_HR_SA3_IDX)(C_ACCUM_OFFSET_IDX1);
--C_TDI_6_DR_IDX
v_verification_array(C_TDI_6_DR_IDX)(C_ACCUM_OFFSET_IDX1) := 0;
--C_TDI_6_DR_SA2_IDX
v_verification_array(C_TDI_6_DR_SA2_IDX)(C_ACCUM_OFFSET_IDX1) := 0;
--C_TDI_6_DR_SA3_IDX
v_verification_array(C_TDI_6_DR_SA3_IDX)(C_ACCUM_OFFSET_IDX1) := 0;
--C_PAN_MODE_IDX
v_verification_array(C_PAN_MODE_IDX)(C_ACCUM_OFFSET_IDX1) := 0;
v_verification_array(C_PAN_MODE_IDX)(C_ACCUM_OFFSET_IDX2) := 0;
return v_verification_array;
-- This function provides the index to look at in the verification array given the shift offset array output by the DUT.
-- It corresponds to the factor that multiplies the number of bytes per line at index 1 of the shift offset index.
function get_verification_index (dual_readout : std_logic; shift_offset_array : t_slv_25b_array(1 downto 0)) return integer is
variable v_factor : integer := 0;
begin
v_factor := to_integer(unsigned(shift_offset_array(1))) / C_BYTES_PER_LINE;
case v_factor is
when 12 =>
return C_TDI_12_TDI_11_PAN_IDX;
when 24 =>
return C_TDI_12_SA2_IDX;
when 36 =>
return C_TDI_12_SA3_IDX;
when 3 =>
return C_TDI_3_FR_IDX;
when 5 =>
return C_TDI_5_PAN_HR_TDI_6_HR_IDX;
when 11 =>
if dual_readout = '1' then
return C_TDI_6_DR_IDX;
end if;
return C_TDI_6_HR_SA2_IDX;
when 17 =>
return C_TDI_6_HR_SA3_IDX;
when 23 =>
return C_TDI_6_DR_SA2_IDX;
when 35 =>
return C_TDI_6_DR_SA3_IDX;
when 1 =>
return C_PAN_MODE_IDX;
when others =>
assert False report "Failed to retrieve the index of the verification array, it seems that something went wront with the shift_offset_array!"
severity failure;
end case;
end function;
-- This function checks that the shift offset array output by the DUT has the expected value at a given index of the verification array
-- the index is retrieved from the function get_verification_index.
-- The function returns False in case of error, True otherwise.
function check_shift_array(shift_offset_array : t_slv_25b_array(1 downto 0); index : integer) return boolean is
begin
if unsigned(shift_offset_array(0)) /= to_unsigned(C_VERIFICATION_ARRAY(index)(C_SHIFT_OFFSET_IDX0), shift_offset_array(0)'length) then
return False;
else
return True;
end if;
end function;
-- The function verifies that the accumulation offset array output by the DUT has the expected value. given the retrieved of the verification array.
-- The function returns False in case of error, True otherwise.
function check_accum_array(accum_offset_array : t_slv_25b_array(35 downto 0); index : integer; dual_readout : std_logic) return boolean is
variable v_idx2 : integer := C_VERIFICATION_ARRAY(index)(C_ACCUM_OFFSET_IDX2);
variable v_idx1 : integer := C_VERIFICATION_ARRAY(index)(C_ACCUM_OFFSET_IDX1);
begin
--The smallest index of the accumulation offset array is always 0
if unsigned(accum_offset_array(0)) /= to_unsigned(0, accum_offset_array(0)'length) then
return False;
end if;
if dual_readout = '1' then
--If there is dual readout, the index 1 must also be 0
if unsigned(accum_offset_array(1)) /= to_unsigned(0, accum_offset_array(1)'length) then
return False;
end if;
--Then the entries of the accumulation offset array must be equal per pair
for i in 2 to accum_offset_array'length - 2 loop
if (i mod 2) = 0 then
if unsigned(accum_offset_array(i)) /= to_unsigned(i * v_idx2/2, accum_offset_array(i)'length) then
return False;
end if;
if unsigned(accum_offset_array(i + 1)) /= unsigned(accum_offset_array(i)) then
return False;
end if;
end if;
end loop;
else
for i in 1 to accum_offset_array'length - 1 loop
if unsigned(accum_offset_array(i)) /= to_unsigned(i * v_idx1, accum_offset_array(i)'length) then
return False;
end if;
end loop;
end if;
return True;
end function;
-- CONSTANT --
-- Test Constants
constant C_CLK_PERIOD : time := 5 ns; -- 200MHz
-- These offsets are added to a frame counter to expect specific values in the frame start addresses present in the ANC FIFO data
constant C_HS_ADDRESS_OFFSETS : unsigned(11 downto 0) := X"F00";
constant C_PAN_ADDRESS_OFFSETS : unsigned(11 downto 0) := X"E00";
-- Returns a variable containing all the test inputs in a custom structure
procedure init_test_inputs(test_id : inout integer; test_inputs : inout t_test_inputs; tdi_type, tdi_spectral_agglom : in std_logic_vector(1 downto 0);
dual_readout, hs_plus_pan_mode : in std_logic;
hs_nol, pan_nol : in std_logic_vector(11 downto 0);
no_accum, no_accum_pan : in integer;
hs_off, pan_off : in boolean) is
begin
test_inputs.tdi_type := tdi_type;
test_inputs.dual_readout := dual_readout;
test_inputs.hs_plus_pan_mode := hs_plus_pan_mode;
test_inputs.tdi_spectral_agglom := tdi_spectral_agglom;
test_inputs.hs_nol := hs_nol;
test_inputs.pan_nol := pan_nol;
test_inputs.hs_off := hs_off;
test_inputs.pan_off := pan_off;
test_inputs.no_accum := no_accum;
test_inputs.no_accum_pan := no_accum_pan;
test_inputs.test_title := get_test_title(test_inputs, test_id);
test_id := test_id + 1;
end procedure;
--Returns the nominal number of tests given the sensor type.
function init_number_of_tests return integer is
begin
if G_SENSOR = "CARDINAL1280" then
return 5;
end if;
if G_SENSOR = "CMV12K" then
return 12;
end if;
end function;
-- Constants that are used to generate random TDI mode to test in the test cases.
constant C_NO_TDI_MODES : integer := 13;
constant C_TDI_MODES : t_slv_6b_array(C_NO_TDI_MODES - 1 downto 0) := (C_TDI_12,
C_TDI_11_PAN,
C_TDI_12_SA2,
C_TDI_12_SA3,
C_TDI_3_FR,
C_TDI_5_PAN_HR,
C_TDI_6_HR,
C_TDI_6_HR_SA2,
C_TDI_6_HR_SA3,
C_TDI_6_DR,
C_TDI_6_DR_SA2,
C_TDI_6_DR_SA3,
C_PAN_MODE
);
-- Meaning of the fields in the tdi_mode signal
constant C_TDI_TYPE : std_logic_vector(5 downto 4) := (others => '0');
constant C_HS_PLUS_PAN : std_logic_vector(3 downto 3) := "0";
constant C_TDI_SPECTRAL_AGGLOM : std_logic_vector(2 downto 1) := (others => '0');
constant C_DUAL_READOUT : std_logic_vector(0 downto 0) := "0";
process
begin
if not(tests_done) then
clk <= '0';
wait for C_CLK_PERIOD/2;
clk <= '1';
wait for C_CLK_PERIOD/2;
else
report "Simulation Success!";
wait;
end if;
end process;
-- Devices Under Test --
g_dut : if G_SENSOR = "CARDINAL1280" generate
i_dut : entity work.tdi_read_control
generic map(
G_SENSOR => G_SENSOR
)
port map(
CLK => clk, -- Main Clock
RST => rst, -- Main Reset
ACQUISITION_RUNNING => acquisition_running, -- '1' enables TDI processing, '0' stops processing at the end of the frame being processed
TDI_WRITE_RUNNING => tdi_write_running, -- High until the last frame of the acquisition is written in the DDR frame buffer
TDI_READ_RUNNING => tdi_read_running, -- High until the TDI processing is done
ANC_FIFO_HS_DOUT => anc_fifo_hs_dout, -- TDI ANC to add in the FIFO
ANC_FIFO_HS_RE => anc_fifo_hs_re, -- FIFO TDI read enable
ANC_FIFO_HS_EMPTY => anc_fifo_hs_empty, -- FIFO TDI empty flag
ANC_FIFO_PAN_DOUT => anc_fifo_pan_dout, -- PAN ANC to add in the FIFO
ANC_FIFO_PAN_RE => anc_fifo_pan_re, -- FIFO PAN read enable
ANC_FIFO_PAN_EMPTY => anc_fifo_pan_empty, -- FIFO PAN empty flag
TDI_TYPE => test_inputs.tdi_type, -- TDI Type (0: none, 1: TDI6 half rate, 2: TDI12, 3: TDI6 full rate)
HS_PLUS_PAN_MODE => test_inputs.hs_plus_pan_mode, -- HS + PAN mode (only in vnir)
TDI_SPECTRAL_AGGLOM => test_inputs.tdi_spectral_agglom, --"01": no SA, "10": SAx2 "11": SAx3
DUAL_READOUT => test_inputs.dual_readout, -- Two consecutive sensor images are added pixel by pixel before the TDI6 HR algo is applied
TDI_ACCU_START => tdi_accu_start, -- Pulse '1' start the TDI Accumulation process
TDI_ACCU_READY => tdi_accu_ready, -- '1' indicates that the TDI Accumulation process is ready for a new accumulation
FB_FIRST_FRAME_ADDR => fb_first_frame_addr, -- Addresses in FB of the first frames line that need to be processed
FB_FRAME_ANC => fb_frame_anc, -- Vector containing the ANC of the frames that need to be processed
ACCUM_OFFSETS => accum_offsets, -- Array containing FB address offsets needed between each processed accumulation, PAN or TDI
SHIFT_OFFSETS => shift_offsets, -- Array containing FB address offsets needed between each processed line (shift), PAN or TDI
ACC_FRAME_ANC_ID_READY => acc_frame_anc_id_ready, -- Pulse indicating that the ANC ID is ready
ACC_FRAME_ANC_ID => acc_frame_anc_id, -- Vector containing the ANC ID of the processed frame
FSM_ERROR => fsm_error -- Asserted when FSM in undefined state
);
elsif G_SENSOR = "CMV12K" generate
i_dut_hs : entity work.tdi_read_control
generic map(
G_SENSOR => G_SENSOR
)
port map(
CLK => clk, -- Main Clock
RST => rst, -- Main Reset
ACQUISITION_RUNNING => acquisition_running, -- '1' enables TDI processing, '0' stops processing at the end of the frame being processed
TDI_WRITE_RUNNING => tdi_write_running, -- High until the last frame of the acquisition is written in the DDR frame buffer
TDI_READ_RUNNING => tdi_read_running, -- High until the TDI processing is done
ANC_FIFO_HS_DOUT => anc_fifo_hs_dout, -- TDI ANC to add in the FIFO
ANC_FIFO_HS_RE => anc_fifo_hs_re, -- FIFO TDI read enable
ANC_FIFO_HS_EMPTY => anc_fifo_hs_empty, -- FIFO TDI empty flag
ANC_FIFO_PAN_DOUT => (others => '0'), -- PAN ANC to add in the FIFO
ANC_FIFO_PAN_RE => open, -- FIFO PAN read enable
ANC_FIFO_PAN_EMPTY => '1', -- FIFO PAN empty flag
TDI_TYPE => test_inputs.tdi_type, -- TDI Type (0: none, 1: TDI6 half rate, 2: TDI12, 3: TDI6 full rate)
HS_PLUS_PAN_MODE => test_inputs.hs_plus_pan_mode, -- HS + PAN mode (only in vnir)
TDI_SPECTRAL_AGGLOM => test_inputs.tdi_spectral_agglom, --"01": no SA, "10": SAx2 "11": SAx3
DUAL_READOUT => test_inputs.dual_readout, -- Two consecutive sensor images are added pixel by pixel before the TDI6 HR algo is applied
TDI_ACCU_START => tdi_accu_start, -- Pulse '1' start the TDI Accumulation process
TDI_ACCU_READY => tdi_accu_ready, -- '1' indicates that the TDI Accumulation process is ready for a new accumulation
FB_FIRST_FRAME_ADDR => fb_first_frame_addr, -- Addresses in FB of the first frames line that need to be processed
FB_FRAME_ANC => fb_frame_anc, -- Vector containing the ANC of the frames that need to be processed
ACCUM_OFFSETS => accum_offsets, -- Array containing FB address offsets needed between each processed accumulation, PAN or TDI
SHIFT_OFFSETS => shift_offsets, -- Array containing FB address offsets needed between each processed line (shift), PAN or TDI
ACC_FRAME_ANC_ID_READY => acc_frame_anc_id_ready, -- Pulse indicating that the ANC ID is ready
ACC_FRAME_ANC_ID => acc_frame_anc_id, -- Vector containing the ANC ID of the processed frame
FSM_ERROR => fsm_error -- Asserted when FSM in undefined state
);
i_dut_pan : entity work.tdi_read_control
generic map(
G_SENSOR => G_SENSOR
)
port map(
CLK => clk, -- Main Clock
RST => rst, -- Main Reset
ACQUISITION_RUNNING => acquisition_running, -- '1' enables TDI processing, '0' stops processing at the end of the frame being processed
TDI_WRITE_RUNNING => tdi_write_running, -- High until the last frame of the acquisition is written in the DDR frame buffer
TDI_READ_RUNNING => tdi_read_running_pan, -- High until the TDI processing is done
ANC_FIFO_HS_DOUT => (others => '0'), -- TDI ANC to add in the FIFO
ANC_FIFO_HS_RE => open, -- FIFO TDI read enable
ANC_FIFO_HS_EMPTY => '1', -- FIFO TDI empty flag
ANC_FIFO_PAN_DOUT => anc_fifo_pan_dout, -- PAN ANC to add in the FIFO
ANC_FIFO_PAN_RE => anc_fifo_pan_re, -- FIFO PAN read enable
ANC_FIFO_PAN_EMPTY => anc_fifo_pan_empty, -- FIFO PAN empty flag
TDI_TYPE => C_TDI_NONE, -- TDI Type (0: none, 1: TDI6 half rate, 2: TDI12, 3: TDI6 full rate)
HS_PLUS_PAN_MODE => '0', -- HS + PAN mode (only in vnir)
TDI_SPECTRAL_AGGLOM => C_SA_NONE, --"01": no SA, "10": SAx2 "11": SAx3
DUAL_READOUT => '0', -- Two consecutive sensor images are added pixel by pixel before the TDI6 HR algo is applied
TDI_ACCU_START => tdi_accu_start_pan, -- Pulse '1' start the TDI Accumulation process
TDI_ACCU_READY => tdi_accu_ready_pan, -- '1' indicates that the TDI Accumulation process is ready for a new accumulation
FB_FIRST_FRAME_ADDR => fb_first_frame_addr_pan, -- Addresses in FB of the first frames line that need to be processed
FB_FRAME_ANC => fb_frame_anc_pan, -- Vector containing the ANC of the frames that need to be processed
ACCUM_OFFSETS => accum_offsets_pan, -- Array containing FB address offsets needed between each processed accumulation, PAN or TDI
SHIFT_OFFSETS => shift_offsets_pan, -- Array containing FB address offsets needed between each processed line (shift), PAN or TDI
ACC_FRAME_ANC_ID_READY => acc_frame_anc_id_ready_pan, -- Pulse indicating that the ANC ID is ready
ACC_FRAME_ANC_ID => acc_frame_anc_id_pan, -- Vector containing the ANC ID of the processed frame
FSM_ERROR => fsm_error_pan -- Asserted when FSM in undefined state
);
end generate g_dut;
-- Tests Sequencing --
-- Test restarted every 100 clock cycles
start_test_cond <= true when clk_cntr = 2000 and tdi_accu_ready = '1' else
False;
-- Test finished when no_accum have been performed
--autoformat_off
g_stop_test : if G_SENSOR = "CARDINAL1280" generate
stop_test_cond <= true when (tdi_accu_start_cntr = test_inputs.no_accum) else False;
elsif G_SENSOR = "CMV12K" generate
stop_test_cond <= true when (tdi_accu_start_cntr = test_inputs.no_accum) and (tdi_accu_pan_start_cntr = test_inputs.no_accum_pan) else False;
end generate g_stop_test;
--autoformat_on
process (clk)
begin
if rising_edge(clk) then
if rst = '1' then
start_tests <= '0';
test_inputs <= tests_cases(0);
else
clk_cntr <= clk_cntr + 1;
start_tests_d <= start_tests;
if stop_test_cond then
start_tests <= '0';
clk_cntr <= 0;
end if;
if start_test_cond then
tests_cntr <= tests_cntr + 1;
if tests_cntr < tests_cases'length then
start_tests <= '1';
test_inputs <= tests_cases(tests_cntr);
report tests_cases(tests_cntr).test_title;
end if;
end if;
end if;
end if;
end process;
-- CONFIGURATION REGISTERS EMULATOR --
-- Configure all the input register of the DUT
process (clk)
begin
if rising_edge(clk) then
if start_tests = '1' then
tdi_type <= test_inputs.tdi_type;
dual_readout <= test_inputs.dual_readout;
hs_plus_pan_mode <= test_inputs.hs_plus_pan_mode;
tdi_spectral_agglom <= test_inputs.tdi_spectral_agglom;
end if;
end if;
end process;
-- Acquisition running
p_acquisition_running : process (CLK)
begin
if rising_edge(CLK) then
if rst = '1' then
acquisition_running <= '0';
else
if start_tests = '1' and start_tests_d = '0' then
acquisition_running <= '1';
end if;
if G_SENSOR = "CMV12K" then
if (tdi_accu_start_cntr = test_inputs.no_accum) and (tdi_accu_pan_start_cntr = test_inputs.no_accum_pan) then
acquisition_running <= '0';
end if;
end if;
if G_SENSOR = "CARDINAL1280" then
if (tdi_accu_start_cntr = test_inputs.no_accum) then
acquisition_running <= '0';
end if;
end if;
end if;
end if;
end process;
-- TDI Write running
p_tdi_write_running : process (CLK)
variable v_tdi_write_running_delay : integer;
begin
if rising_edge(CLK) then
if rst = '1' then
v_tdi_write_running_delay := randint(50, 125);
tdi_write_running <= '0';
else
if acquisition_running = '1' then
tdi_write_running <= '1';
end if;
if acquisition_running = '0' then
v_tdi_write_running_delay := v_tdi_write_running_delay - 1;
if v_tdi_write_running_delay = 0 then
tdi_write_running <= '0';
v_tdi_write_running_delay := randint(50, 125);
end if;
end if;
end if;
end if;
end process;
-- ANC FIFO HS EMULATOR --
anc_fifo_hs_empty <= '1' when (test_inputs.hs_off or (clk_cntr mod 256) = 0 or tdi_read_running = '0' or (tdi_accu_start_cntr = test_inputs.no_accum)) else
'0';
process (clk)
begin
if rising_edge(clk) then
if anc_fifo_hs_re = '1' then
anc_fifo_hs_cntr <= anc_fifo_hs_cntr + 1;
anc_fifo_hs_dout(C_ANC_FRAME_ADDRESS_POS'range) <= std_logic_vector(C_HS_ADDRESS_OFFSETS + anc_fifo_hs_cntr);
anc_fifo_hs_dout(C_ANC_NBR_OF_LINES_POS'range) <= test_inputs.hs_nol;
anc_fifo_hs_dout(C_ANC_ANC_ID_POS'range) <= std_logic_vector(anc_fifo_hs_cntr(5 downto 0));
end if;
anc_fifo_hs_empty_d <= anc_fifo_hs_empty;
if acquisition_running = '0' then
anc_fifo_hs_cntr <= (others => '0');
end if;
end if;
end process;
-- ANC FIFO PAN EMULATOR --
--autoformat_off
g_pan_empty : if G_SENSOR = "CARDINAL1280" generate
anc_fifo_pan_empty <= '1' when (test_inputs.pan_off or (clk_cntr mod 64) = 0 or tdi_read_running = '0' or (tdi_accu_start_cntr = test_inputs.no_accum)) else
'0';
elsif G_SENSOR = "CMV12K" generate
anc_fifo_pan_empty <= '1' when (test_inputs.pan_off or (clk_cntr mod 64) = 0 or tdi_read_running = '0' or (tdi_accu_pan_start_cntr = test_inputs.no_accum_pan)) else
'0';
end generate g_pan_empty;
--autoformat_on
process (clk)
begin
if rising_edge(clk) then
if anc_fifo_pan_re = '1' then
anc_fifo_pan_cntr <= anc_fifo_pan_cntr + 1;
anc_fifo_pan_dout(C_ANC_FRAME_ADDRESS_POS'range) <= std_logic_vector(C_PAN_ADDRESS_OFFSETS + anc_fifo_pan_cntr);
anc_fifo_pan_dout(C_ANC_NBR_OF_LINES_POS'range) <= test_inputs.pan_nol;
anc_fifo_pan_dout(C_ANC_ANC_ID_POS'range) <= std_logic_vector(anc_fifo_pan_cntr(5 downto 0));
end if;
anc_fifo_pan_empty_d <= anc_fifo_pan_empty;
if acquisition_running = '0' then
anc_fifo_pan_cntr <= (others => '0');
end if;
end if;
end process;
-- TDI ACCUMULATOR EMULATOR --
p_accum : process (clk)
begin
if rising_edge(clk) then
if rst = '1' then
tdi_accu_ready <= '1';
end if;
tdi_accu_timer <= tdi_accu_timer + 1;
tdi_accu_start_d <= tdi_accu_start;
if tdi_accu_start = '1' then
tdi_accu_ready <= '0';
tdi_accu_timer <= 0;
tdi_accu_start_cntr <= tdi_accu_start_cntr + 1;
elsif tdi_accu_timer = tdi_accu_timer_delay then
tdi_accu_ready <= '1';
tdi_accu_timer <= tdi_accu_timer; -- wait for the tdi_accu_start to be raised
tdi_accu_timer_delay <= randint(1, 2000);
end if;
if tdi_read_running = '0' then
tdi_accu_timer <= 0;
tdi_accu_start_cntr <= 0;
end if;
end if;
end process;
-- TDI ACCUMULATOR EMULATOR PAN --
g_accum_pan : if G_SENSOR = "CMV12K" generate
process (clk)
begin
if rising_edge(clk) then
tdi_accu_pan_timer <= tdi_accu_pan_timer + 1;
tdi_accu_pan_start_d <= tdi_accu_start_pan;
if tdi_accu_start_pan = '1' then
tdi_accu_ready_pan <= '0';
tdi_accu_pan_timer <= 0;
tdi_accu_pan_start_cntr <= tdi_accu_pan_start_cntr + 1;
elsif tdi_accu_pan_timer = tdi_accu_pan_timer_delay then
tdi_accu_ready_pan <= '1';
tdi_accu_pan_timer <= tdi_accu_pan_timer; -- wait for the tdi_accu_pan_start to be raised
tdi_accu_pan_timer_delay <= randint(1, 2000);
end if;
if tdi_read_running_pan = '0' then
tdi_accu_pan_timer <= 0;
tdi_accu_pan_start_cntr <= 0;
end if;
end if;
end process;
end generate g_accum_pan;
-- ASSERTS --
g_assert_process : if G_SENSOR = "CARDINAL1280" generate
process (CLK)
variable v_anc_fifo_count : integer;
variable v_tdi_accu : integer;
variable v_verification_idx : integer;
variable v_tdi_mode : std_logic_vector(5 downto 0);
begin
v_anc_fifo_count := to_integer(anc_fifo_hs_cntr) - out_hs_cntr;
if rising_edge(CLK) and RST = '0' then
if acquisition_running = '0' then
out_hs_cntr <= 0;
out_pan_cntr <= 0;
end if;
if acquisition_running = '1' then
v_tdi_mode := tests_cases(tests_cntr - 1).tdi_type & tests_cases(tests_cntr - 1).hs_plus_pan_mode & tests_cases(tests_cntr - 1).tdi_spectral_agglom & tests_cases(tests_cntr - 1).dual_readout;
v_tdi_accu := get_accum_factor(v_tdi_mode);
tdi_accu <= v_tdi_accu;
end if;
-- FIFO Checks
assert not(anc_fifo_hs_empty_d = '1' and anc_fifo_hs_re = '1') report "Trying to read FIFO while EMPTY" severity failure;
assert not(anc_fifo_pan_empty_d = '1' and anc_fifo_pan_re = '1') report "Trying to read FIFO while EMPTY" severity failure;
assert not(v_anc_fifo_count = v_tdi_accu and anc_fifo_hs_re = '1') report "Trying to read FIFO despite enough entry have been read" severity failure;
-- Acumulator
assert not(tdi_accu_ready = '0' and tdi_accu_start = '1') report "Trying to start ACCU when not ready" severity failure;
assert not(tdi_accu_ready = '1' and tdi_accu_start_d = '1') report "AccuReady not pulled down after an AccuStart" severity failure;
assert tdi_accu_start = acc_frame_anc_id_ready report "acc_frame_anc_id_ready must follow tdi_accu_start" severity failure;
-- Status check
assert not(tdi_read_running = '0' and ((anc_fifo_hs_empty = '0' and tdi_write_running = '1'))) report "TDI_READ_RUNNING is high while it must not." severity failure;
-- Outputs checks
if tdi_accu_start = '1' then
v_verification_idx := get_verification_index(test_inputs.dual_readout, shift_offsets);
verification_idx <= v_verification_idx;
-- if a multiple of Q TDI frames have been read, a TDI Accumulation must be started (highest priority)
if (v_anc_fifo_count mod v_tdi_accu) = 0 and anc_fifo_hs_cntr /= 0 then
out_hs_cntr <= out_hs_cntr + 1;
assert unsigned(fb_frame_anc(C_FRAME_ANC_NOL'range)) = unsigned(test_inputs.hs_nol) report "TDI ANC nol not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_TDI_TYPE'range) = test_inputs.tdi_type report "TDI ANC tdi_type not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_TDI_SPECTRAL_AGGLOM'range) = test_inputs.tdi_spectral_agglom report "TDI ANC spectral agglomeration not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_DUAL_READOUT'low) = test_inputs.dual_readout report "TDI ANC dual readout not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_HS_PLUS_PAN_MODE'low) = test_inputs.hs_plus_pan_mode report "TDI ANC HS+PAN mode not correct" severity failure;
assert acc_frame_anc_id = std_logic_vector(anc_fifo_hs_cntr(5 downto 0) - v_tdi_accu) report "ANC Id not as expected" severity failure;
for i in 0 to v_tdi_accu - 1 loop
assert unsigned(fb_first_frame_addr(i)) = C_HS_ADDRESS_OFFSETS + out_hs_cntr + i report "Error in TDI FB FFA" severity failure;
end loop;
assert check_shift_array(shift_offsets, get_verification_index(test_inputs.dual_readout, shift_offsets)) report "Error in shift_offsets" severity failure;
assert check_accum_array(accum_offsets, get_verification_index(test_inputs.dual_readout, shift_offsets), test_inputs.dual_readout) report "Error in accum_offsets" severity failure;
else -- Raw case otherwises
out_pan_cntr <= out_pan_cntr + 1;
assert unsigned(fb_frame_anc(C_FRAME_ANC_NOL'range)) = unsigned(test_inputs.pan_nol) report "PAN ANC nol not correct" severity failure;
assert unsigned(fb_frame_anc(C_FRAME_ANC_TDI_TYPE'range)) = 0 report "PAN ANC tdi_type not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_TDI_SPECTRAL_AGGLOM'range) = test_inputs.tdi_spectral_agglom report "PAN ANC spectral agglomeration not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_DUAL_READOUT'low) = test_inputs.dual_readout report "PAN ANC dual readout not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_HS_PLUS_PAN_MODE'low) = test_inputs.hs_plus_pan_mode report "PAN ANC HS+PAN mode not correct" severity failure;
assert acc_frame_anc_id = std_logic_vector(anc_fifo_pan_cntr(5 downto 0) - 1) report "ANC Id not as expected" severity failure;
assert unsigned(fb_first_frame_addr(0)) = C_PAN_ADDRESS_OFFSETS + out_pan_cntr report "Error in RAW FB FFA" severity failure;
assert check_shift_array(shift_offsets, get_verification_index(test_inputs.dual_readout, shift_offsets)) report "Error in shift_offsets" severity failure;
assert check_accum_array(accum_offsets, get_verification_index(test_inputs.dual_readout, shift_offsets), test_inputs.dual_readout) report "Error in accum_offsets" severity failure;
end if;
end if;
end if;
end process;
elsif G_SENSOR = "CMV12K" generate
process (CLK)
variable v_anc_fifo_count : integer;
variable v_tdi_accu : integer;
variable v_verification_idx : integer;
variable v_tdi_mode : std_logic_vector(5 downto 0);
begin
v_anc_fifo_count := to_integer(anc_fifo_hs_cntr) - out_hs_cntr;
if rising_edge(CLK) and RST = '0' then
if acquisition_running = '0' then
out_hs_cntr <= 0;
out_pan_cntr <= 0;
end if;
if acquisition_running = '1' then
v_tdi_mode := tests_cases(tests_cntr - 1).tdi_type & tests_cases(tests_cntr - 1).hs_plus_pan_mode & tests_cases(tests_cntr - 1).tdi_spectral_agglom & tests_cases(tests_cntr - 1).dual_readout;
v_tdi_accu := get_accum_factor(v_tdi_mode);
tdi_accu <= v_tdi_accu;
end if;
-- FIFO Checks
assert not(anc_fifo_hs_empty_d = '1' and anc_fifo_hs_re = '1') report "Trying to read FIFO while EMPTY" severity failure;
assert not(anc_fifo_pan_empty_d = '1' and anc_fifo_pan_re = '1') report "Trying to read FIFO while EMPTY" severity failure;
assert not(v_anc_fifo_count = v_tdi_accu and anc_fifo_hs_re = '1') report "Trying to read FIFO despite enough entry have been read" severity failure;
-- Acumulator
assert not(tdi_accu_ready = '0' and tdi_accu_start = '1') report "Trying to start ACCU when not ready" severity failure;
assert not(tdi_accu_ready = '1' and tdi_accu_start_d = '1') report "AccuReady not pulled down after an AccuStart" severity failure;
assert tdi_accu_start = acc_frame_anc_id_ready report "acc_frame_anc_id_ready must follow tdi_accu_start" severity failure;
--Status check
assert not(tdi_read_running = '0' and (anc_fifo_hs_empty = '0' and tdi_write_running = '1')) report "TDI_READ_RUNNING is high while it must not." severity failure;
assert not(tdi_read_running_pan = '0' and (anc_fifo_pan_empty = '0' and tdi_write_running = '1')) report "TDI_READ_RUNNING is high while it must not." severity failure;
-- Outputs checks
if tdi_accu_start = '1' then
v_verification_idx := get_verification_index(test_inputs.dual_readout, shift_offsets);
verification_idx <= v_verification_idx;
-- if a multiple of Q TDI frames have been read, a TDI Accumulation must be started (highest priority)
if (v_anc_fifo_count mod v_tdi_accu) = 0 and anc_fifo_hs_cntr /= 0 then
out_hs_cntr <= out_hs_cntr + 1;
assert unsigned(fb_frame_anc(C_FRAME_ANC_NOL'range)) = unsigned(test_inputs.hs_nol) report "TDI ANC nol not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_TDI_TYPE'range) = test_inputs.tdi_type report "TDI ANC tdi_type not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_TDI_SPECTRAL_AGGLOM'range) = test_inputs.tdi_spectral_agglom report "TDI ANC spectral agglomeration not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_DUAL_READOUT'low) = test_inputs.dual_readout report "TDI ANC dual readout not correct" severity failure;
assert fb_frame_anc(C_FRAME_ANC_HS_PLUS_PAN_MODE'low) = test_inputs.hs_plus_pan_mode report "TDI ANC HS+PAN mode not correct" severity failure;
assert acc_frame_anc_id = std_logic_vector(anc_fifo_hs_cntr(5 downto 0) - v_tdi_accu) report "ANC Id not as expected" severity failure;
for i in 0 to v_tdi_accu - 1 loop
assert unsigned(fb_first_frame_addr(i)) = C_HS_ADDRESS_OFFSETS + out_hs_cntr + i report "Error in TDI FB FFA" severity failure;
end loop;
assert check_shift_array(shift_offsets, get_verification_index(test_inputs.dual_readout, shift_offsets)) report "Error in shift_offsets" severity failure;
assert check_accum_array(accum_offsets, get_verification_index(test_inputs.dual_readout, shift_offsets), test_inputs.dual_readout) report "Error in accum_offsets" severity failure;
else -- Raw case otherwises
out_pan_cntr <= out_pan_cntr + 1;
assert unsigned(fb_frame_anc_pan(C_FRAME_ANC_NOL'range)) = unsigned(test_inputs.pan_nol) report "PAN ANC nol not correct" severity failure;
assert unsigned(fb_frame_anc_pan(C_FRAME_ANC_TDI_TYPE'range)) = 0 report "PAN ANC tdi_type not correct" severity failure;
assert fb_frame_anc_pan(C_FRAME_ANC_TDI_SPECTRAL_AGGLOM'range) = test_inputs.tdi_spectral_agglom report "PAN ANC spectral agglomeration not correct" severity failure;
assert fb_frame_anc_pan(C_FRAME_ANC_DUAL_READOUT'low) = test_inputs.dual_readout report "PAN ANC dual readout not correct" severity failure;
assert fb_frame_anc_pan(C_FRAME_ANC_HS_PLUS_PAN_MODE'low) = test_inputs.hs_plus_pan_mode report "PAN ANC HS+PAN mode not correct" severity failure;
assert acc_frame_anc_id_pan = std_logic_vector(anc_fifo_pan_cntr(5 downto 0) - 1) report "ANC Id not as expected" severity failure;
assert unsigned(fb_first_frame_addr_pan(0)) = C_PAN_ADDRESS_OFFSETS + out_pan_cntr report "Error in RAW FB FFA" severity failure;
assert check_shift_array(shift_offsets, get_verification_index('0', shift_offsets_pan)) report "Error in shift_offsets" severity failure;
assert check_accum_array(accum_offsets, get_verification_index('0', shift_offsets_pan), '0') report "Error in accum_offsets" severity failure;
end if;
end if;
end if;
end process;
end generate g_assert_process;
---------
-- END --
---------
the following code (issue21.vhd) results in an eternal loop
-- Filename: tdi_read_control_tb.vhd
-- Copyright (c) 2014 Deltatec (www.deltatec.be)
-- This file is provided without any express or implied warranties, including, -- but not limited to, the implied warranties of merchantability and fitness -- for a particular purpose. It is not intended for use in life support -- appliances, devices, or systems. Use in such applications is expressly -- prohibited.--
-- Author: Benjamin Delhaye (BD)
-- History: 29/09/2020 (BD) : Creation of this file -- 03/04/2024 (PN) : Adaptation of the file for Hyp4U project
-- -- Description: -- Test bench for the TDI_READ_CONTROL block
-- Notes:
-- LIBRARY --
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
library dt; use dt.dt_types_pkg.all;
use work.random_pkg.all;
-- ENTITY --
entity tdi_read_control_tb is generic ( G_SENSOR : string := "CARDINAL1280" ); end tdi_read_control_tb;
-- ARCHITECTURE --
architecture Behavioral of tdi_read_control_tb is --autoformat_off package tdi_pkg is new work.tdi_pkg generic map(G_SENSOR => G_SENSOR); use tdi_pkg.all;
--autoformat_on
-- TYPE --
type t_test_inputs is record test_title : string(1 to 512); tdi_type : std_logic_vector(1 downto 0); -- TDI type (0: none, 1: TDI6 HR, 2: TDI12, 3: TDI3 FR) dual_readout : std_logic; -- dual readout input register hs_plus_pan_mode : std_logic; -- if the tdi type is doing HS + PAN tdi_spectral_agglom : std_logic_vector(1 downto 0); -- (1: none, 2: SAx2, 3: SAx3) hs_nol : std_logic_vector(11 downto 0); -- Number of lines to emulate in the ANC FIFO HS pan_nol : std_logic_vector(11 downto 0); -- Number of lines to emulate in the ANC FIFO PAN hs_off : boolean; -- Parameter that allow to emulate that no HS frame is generated pan_off : boolean; -- Parameter that allow to emulate that no PAN frame is generated no_accum : integer; -- Number of accumulations to emulate no_accum_pan : integer; -- Number of accumulations to emulate for PAN frames (only used with CMV12K sensor) end record t_test_inputs;
constant C_BYTES_PER_LINE : integer := (C_DDR_PIXEL_SIZE_BIT * C_SENSOR_LINE_SIZE_PIXEL) / 8;
type t_test_cases is array (natural range <>) of t_test_inputs;
-- This type is used to define a table containing the ground truth about the accumulation and shift offset arrays type t_verification_array is array (natural range <>) of t_int_array(3 downto 0);
-- The following series of constants are the meaning of the indexes in the verification array constant C_TDI_12_TDI_11_PAN_IDX : integer := 0; constant C_TDI_12_SA2_IDX : integer := 1; constant C_TDI_12_SA3_IDX : integer := 2; constant C_TDI_3_FR_IDX : integer := 3; constant C_TDI_5_PAN_HR_TDI_6_HR_IDX : integer := 4; constant C_TDI_6_HR_SA2_IDX : integer := 5; constant C_TDI_6_HR_SA3_IDX : integer := 6; constant C_TDI_6_DR_IDX : integer := 7; constant C_TDI_6_DR_SA2_IDX : integer := 8; constant C_TDI_6_DR_SA3_IDX : integer := 9; constant C_PAN_MODE_IDX : integer := 10;
--The following series of constants are the meaning of the indexes at a given entry of the verification array constant C_SHIFT_OFFSET_IDX0 : integer := 0; constant C_SHIFT_OFFSET_IDX1 : integer := 1; constant C_ACCUM_OFFSET_IDX1 : integer := 2; constant C_ACCUM_OFFSET_IDX2 : integer := 3;
--TDI input parameters constant C_TDI_NONE : std_logic_vector(1 downto 0) := "00"; constant C_TDI6_HR : std_logic_vector(1 downto 0) := "01"; constant C_TDI12 : std_logic_vector(1 downto 0) := "10"; constant C_TDI3_FR : std_logic_vector(1 downto 0) := "11"; constant C_SA_NONE : std_logic_vector(1 downto 0) := "01"; constant C_SA2 : std_logic_vector(1 downto 0) := "10"; constant C_SA3 : std_logic_vector(1 downto 0) := "11";
-- This function initializes the verification array and returns a table function init_verification_array return t_verification_array is variable v_verification_array : t_verification_array(10 downto 0); begin -- v_verification_array(i)(0) = shift_offset(0), v_verification_array(i)(1) = shift_offset(1), v_verification_array(i)(2) = accum_offset(1), v_verification_array(i)(3) = accum_offset(2) -- v_verification_array(i)(4) = number of accumulation
end function;
constant C_VERIFICATION_ARRAY : t_verification_array := init_verification_array;
-- This function provides the index to look at in the verification array given the shift offset array output by the DUT. -- It corresponds to the factor that multiplies the number of bytes per line at index 1 of the shift offset index. function get_verification_index (dual_readout : std_logic; shift_offset_array : t_slv_25b_array(1 downto 0)) return integer is variable v_factor : integer := 0; begin v_factor := to_integer(unsigned(shift_offset_array(1))) / C_BYTES_PER_LINE; case v_factor is when 12 => return C_TDI_12_TDI_11_PAN_IDX; when 24 => return C_TDI_12_SA2_IDX; when 36 => return C_TDI_12_SA3_IDX; when 3 => return C_TDI_3_FR_IDX; when 5 => return C_TDI_5_PAN_HR_TDI_6_HR_IDX; when 11 => if dual_readout = '1' then return C_TDI_6_DR_IDX; end if; return C_TDI_6_HR_SA2_IDX; when 17 => return C_TDI_6_HR_SA3_IDX; when 23 => return C_TDI_6_DR_SA2_IDX; when 35 => return C_TDI_6_DR_SA3_IDX; when 1 => return C_PAN_MODE_IDX; when others => assert False report "Failed to retrieve the index of the verification array, it seems that something went wront with the shift_offset_array!" severity failure; end case; end function;
-- This function checks that the shift offset array output by the DUT has the expected value at a given index of the verification array -- the index is retrieved from the function get_verification_index. -- The function returns False in case of error, True otherwise. function check_shift_array(shift_offset_array : t_slv_25b_array(1 downto 0); index : integer) return boolean is begin if unsigned(shift_offset_array(0)) /= to_unsigned(C_VERIFICATION_ARRAY(index)(C_SHIFT_OFFSET_IDX0), shift_offset_array(0)'length) then return False; else return True; end if; end function;
-- The function verifies that the accumulation offset array output by the DUT has the expected value. given the retrieved of the verification array. -- The function returns False in case of error, True otherwise. function check_accum_array(accum_offset_array : t_slv_25b_array(35 downto 0); index : integer; dual_readout : std_logic) return boolean is variable v_idx2 : integer := C_VERIFICATION_ARRAY(index)(C_ACCUM_OFFSET_IDX2); variable v_idx1 : integer := C_VERIFICATION_ARRAY(index)(C_ACCUM_OFFSET_IDX1); begin
end function;
-- CONSTANT --
-- Test Constants constant C_CLK_PERIOD : time := 5 ns; -- 200MHz
-- These offsets are added to a frame counter to expect specific values in the frame start addresses present in the ANC FIFO data constant C_HS_ADDRESS_OFFSETS : unsigned(11 downto 0) := X"F00"; constant C_PAN_ADDRESS_OFFSETS : unsigned(11 downto 0) := X"E00";
-- FUNCTIONS --
--Returns a variable containing all the test inputs in a custom structure function get_test_title(test_inputs : t_test_inputs; test_id : integer) return string is variable test_title : string(test_inputs.test_title'range) := (others => NUL); constant C_TITLE : string := "" & CR & LF & "****" & CR & "Test " & integer'image(test_id) & CR & " tdi_type = " & integer'image(to_integer(unsigned(test_inputs.tdi_type))) & CR & " dual_readout = " & std_logic'image(test_inputs.dual_readout) & CR & " hs_plus_pan_mode = " & std_logic'image(test_inputs.hs_plus_pan_mode) & CR & " tdi_spectral_agglom = " & integer'image(to_integer(unsigned(test_inputs.tdi_spectral_agglom))) & CR & " hs_nol = " & integer'image(to_integer(unsigned(test_inputs.hs_nol))) & CR & " pan_nol = " & integer'image(to_integer(unsigned(test_inputs.pan_nol))) & CR & " no_accum = " & integer'image(test_inputs.no_accum) & CR & " no_accum_pan = " & integer'image(test_inputs.no_accum_pan) & CR & " hs_off = " & boolean'image(test_inputs.hs_off) & CR & " pan_off = " & boolean'image(test_inputs.pan_off) & CR & "****" & CR & LF; begin test_title(C_TITLE'range) := C_TITLE; return test_title; end function;
-- Returns a variable containing all the test inputs in a custom structure procedure init_test_inputs(test_id : inout integer; test_inputs : inout t_test_inputs; tdi_type, tdi_spectral_agglom : in std_logic_vector(1 downto 0); dual_readout, hs_plus_pan_mode : in std_logic; hs_nol, pan_nol : in std_logic_vector(11 downto 0); no_accum, no_accum_pan : in integer; hs_off, pan_off : in boolean) is begin test_inputs.tdi_type := tdi_type; test_inputs.dual_readout := dual_readout; test_inputs.hs_plus_pan_mode := hs_plus_pan_mode; test_inputs.tdi_spectral_agglom := tdi_spectral_agglom; test_inputs.hs_nol := hs_nol; test_inputs.pan_nol := pan_nol; test_inputs.hs_off := hs_off; test_inputs.pan_off := pan_off; test_inputs.no_accum := no_accum; test_inputs.no_accum_pan := no_accum_pan; test_inputs.test_title := get_test_title(test_inputs, test_id); test_id := test_id + 1; end procedure;
--Returns the nominal number of tests given the sensor type. function init_number_of_tests return integer is begin if G_SENSOR = "CARDINAL1280" then return 5; end if; if G_SENSOR = "CMV12K" then return 12; end if; end function;
-- Constants that are used to generate random TDI mode to test in the test cases. constant C_NO_TDI_MODES : integer := 13; constant C_TDI_MODES : t_slv_6b_array(C_NO_TDI_MODES - 1 downto 0) := (C_TDI_12, C_TDI_11_PAN, C_TDI_12_SA2, C_TDI_12_SA3, C_TDI_3_FR, C_TDI_5_PAN_HR, C_TDI_6_HR, C_TDI_6_HR_SA2, C_TDI_6_HR_SA3, C_TDI_6_DR, C_TDI_6_DR_SA2, C_TDI_6_DR_SA3, C_PAN_MODE );
-- Meaning of the fields in the tdi_mode signal constant C_TDI_TYPE : std_logic_vector(5 downto 4) := (others => '0'); constant C_HS_PLUS_PAN : std_logic_vector(3 downto 3) := "0"; constant C_TDI_SPECTRAL_AGGLOM : std_logic_vector(2 downto 1) := (others => '0'); constant C_DUAL_READOUT : std_logic_vector(0 downto 0) := "0";
-- Return a structure containing all the inputs of every test cases impure function init_test_cases return t_test_cases is constant C_NO_TESTS : integer := init_number_of_tests; constant C_NO_RANDOM_TESTS : integer := 10; variable test_cases : t_test_cases(C_NO_TESTS + C_NO_RANDOM_TESTS downto 0); variable i, j, v_nol, v_random_tdi : integer := 0; variable v_hs_off : boolean; variable v_pan_off : boolean; variable v_tdi_mode : std_logic_vector(5 downto 0); variable v_no_accum_pan : integer; variable v_no_accum : integer; begin --tdi_type, tdi_spectral_agglom, dual_readout, hs_plus_pan_mode, hs_nol, pan_nol, no_accum, no_accum_pan, hs_off, pan_off, init_test_inputs(i, test_cases(i), C_TDI_NONE, "01", '0', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, False, true); --TDI12 init_test_inputs(i, test_cases(i), C_TDI12, C_SA_NONE, '0', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 2, 0, False, true); --TDI12_SA2 init_test_inputs(i, test_cases(i), C_TDI12, C_SA2, '0', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, False, true); --TDI12_SA3 init_test_inputs(i, test_cases(i), C_TDI12, C_SA3, '0', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, False, true);
if G_SENSOR = "CARDINAL1280" then --TDI3_FR init_test_inputs(i, test_cases(i), C_TDI3_FR, C_SA_NONE, '0', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, False, true); --PAN init_test_inputs(i, test_cases(i), C_TDI_NONE, C_SA_NONE, '0', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, true, False); end if;
if G_SENSOR = "CMV12K" then --PAN init_test_inputs(i, test_cases(i), C_TDI_NONE, C_SA_NONE, '0', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 0, 50, true, False); --TDI11_PAN init_test_inputs(i, test_cases(i), C_TDI12, C_SA_NONE, '0', '1', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 5, False, False); --TDI6_HR init_test_inputs(i, test_cases(i), C_TDI6_HR, C_SA_NONE, '0', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, False, true); --TDI5_PAN_HR init_test_inputs(i, test_cases(i), C_TDI6_HR, C_SA_NONE, '0', '1', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 10, False, False); --TDI6_HR_SA2 init_test_inputs(i, test_cases(i), C_TDI6_HR, C_SA2, '0', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, False, true); --TDI6_HR_SA3 init_test_inputs(i, test_cases(i), C_TDI6_HR, C_SA3, '0', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, False, true); --TDI6_DR init_test_inputs(i, test_cases(i), C_TDI6_HR, C_SA_NONE, '1', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, False, true); --TDI6_DR_SA2 init_test_inputs(i, test_cases(i), C_TDI6_HR, C_SA2, '1', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, False, true); --TDI6_DR_SA3 init_test_inputs(i, test_cases(i), C_TDI6_HR, C_SA3, '1', '0', std_logic_vector(to_unsigned(100, 12)), std_logic_vector(to_unsigned(200, 12)), 50, 0, False, true); end if;
--Random for j in 0 to C_NO_RANDOM_TESTS - 1 loop v_hs_off := False; v_pan_off := True; v_random_tdi := randint(0, C_NO_TDI_MODES - 1); v_tdi_mode := C_TDI_MODES(v_random_tdi); v_no_accum_pan := 0;
end loop;
return test_cases(test_cases'length - 1 downto 1); -- required to implement index autoincrementation end function;
-- SIGNAL --
signal start_tests_d : std_logic; signal tdi_read_running_pan : std_logic; signal tdi_read_running : std_logic; signal anc_fifo_count : integer; signal verification_idx : integer; signal clk : std_logic := '0'; signal rst : std_logic := '1'; signal acquisition_running : std_logic; signal tdi_write_running : std_logic; signal anc_fifo_hs_dout : std_logic_vector(29 downto 0); signal anc_fifo_hs_re : std_logic; signal anc_fifo_hs_empty : std_logic; signal anc_fifo_pan_dout : std_logic_vector(29 downto 0); signal anc_fifo_pan_re : std_logic; signal anc_fifo_pan_empty : std_logic; signal tdi_type : std_logic_vector(1 downto 0); signal hs_plus_pan_mode : std_logic; signal tdi_spectral_agglom : std_logic_vector(1 downto 0); signal dual_readout : std_logic; signal tdi_accu_start : std_logic; signal tdi_accu_ready : std_logic; signal fb_first_frame_addr : t_slv_12b_array(35 downto 0); signal fb_frame_anc : std_logic_vector(17 downto 0); signal accum_offsets : t_slv_25b_array(35 downto 0); signal shift_offsets : t_slv_25b_array(1 downto 0); signal acc_frame_anc_id_ready : std_logic; signal acc_frame_anc_id : std_logic_vector(5 downto 0); signal fsm_error : std_logic; signal tdi_accu_start_pan : std_logic; signal tdi_accu_ready_pan : std_logic; signal fb_first_frame_addr_pan : t_slv_12b_array(35 downto 0); signal fb_frame_anc_pan : std_logic_vector(17 downto 0); signal accum_offsets_pan : t_slv_25b_array(35 downto 0); signal shift_offsets_pan : t_slv_25b_array(1 downto 0); signal acc_frame_anc_id_ready_pan : std_logic; signal acc_frame_anc_id_pan : std_logic_vector(5 downto 0); signal fsm_error_pan : std_logic; -- Testbench Signals signal clk_cntr : integer := 0; signal start_tests : std_logic := '0'; signal start_test_cond : boolean := False; signal stop_test_cond : boolean := False; signal tests_done : boolean := False; signal tests_cntr : integer := 0; signal test_inputs : t_test_inputs; signal tests_cases : t_test_cases(init_test_cases'length - 1 downto 0) := init_test_cases;
-- Configuration Registers Emulator
signal anc_fifo_hs_cntr : unsigned(11 downto 0) := (others => '0'); signal anc_fifo_hs_empty_d : std_logic; -- ANC FIFO PAN Emulator signal anc_fifo_pan_cntr : unsigned(11 downto 0) := (others => '0'); signal anc_fifo_pan_empty_d : std_logic;
-- TDI Accumulator Emulator signal tdi_accu_start_d : std_logic; signal tdi_accu_timer : integer := 0; signal tdi_accu_timer_delay : integer := randint(1, 2000); signal tdi_accu_start_cntr : integer := 0;
-- TDI Accumulator PAN Emulator signal tdi_accu_pan_start_d : std_logic; signal tdi_accu_pan_timer : integer := 0; signal tdi_accu_pan_timer_delay : integer := randint(1, 2000); signal tdi_accu_pan_start_cntr : integer := 0;
-- Output Parameters Check signal fb_ffa : t_slv_12b_array(35 downto 0); signal out_hs_cntr : integer := 0; signal out_pan_cntr : integer := 0;
signal tdi_accu : integer;
-- ATTRIBUTE --
-- BEGIN --
begin
-- Clock & Reset --
tests_done <= tests_cntr = tests_cases'length + 1; rst <= '0' after 5 * C_CLK_PERIOD;
process begin if not(tests_done) then clk <= '0'; wait for C_CLK_PERIOD/2; clk <= '1'; wait for C_CLK_PERIOD/2; else report "Simulation Success!"; wait; end if; end process;
-- Devices Under Test --
g_dut : if G_SENSOR = "CARDINAL1280" generate i_dut : entity work.tdi_read_control generic map( G_SENSOR => G_SENSOR ) port map( CLK => clk, -- Main Clock RST => rst, -- Main Reset ACQUISITION_RUNNING => acquisition_running, -- '1' enables TDI processing, '0' stops processing at the end of the frame being processed TDI_WRITE_RUNNING => tdi_write_running, -- High until the last frame of the acquisition is written in the DDR frame buffer TDI_READ_RUNNING => tdi_read_running, -- High until the TDI processing is done ANC_FIFO_HS_DOUT => anc_fifo_hs_dout, -- TDI ANC to add in the FIFO ANC_FIFO_HS_RE => anc_fifo_hs_re, -- FIFO TDI read enable ANC_FIFO_HS_EMPTY => anc_fifo_hs_empty, -- FIFO TDI empty flag ANC_FIFO_PAN_DOUT => anc_fifo_pan_dout, -- PAN ANC to add in the FIFO ANC_FIFO_PAN_RE => anc_fifo_pan_re, -- FIFO PAN read enable ANC_FIFO_PAN_EMPTY => anc_fifo_pan_empty, -- FIFO PAN empty flag TDI_TYPE => test_inputs.tdi_type, -- TDI Type (0: none, 1: TDI6 half rate, 2: TDI12, 3: TDI6 full rate) HS_PLUS_PAN_MODE => test_inputs.hs_plus_pan_mode, -- HS + PAN mode (only in vnir) TDI_SPECTRAL_AGGLOM => test_inputs.tdi_spectral_agglom, --"01": no SA, "10": SAx2 "11": SAx3 DUAL_READOUT => test_inputs.dual_readout, -- Two consecutive sensor images are added pixel by pixel before the TDI6 HR algo is applied TDI_ACCU_START => tdi_accu_start, -- Pulse '1' start the TDI Accumulation process TDI_ACCU_READY => tdi_accu_ready, -- '1' indicates that the TDI Accumulation process is ready for a new accumulation FB_FIRST_FRAME_ADDR => fb_first_frame_addr, -- Addresses in FB of the first frames line that need to be processed FB_FRAME_ANC => fb_frame_anc, -- Vector containing the ANC of the frames that need to be processed ACCUM_OFFSETS => accum_offsets, -- Array containing FB address offsets needed between each processed accumulation, PAN or TDI SHIFT_OFFSETS => shift_offsets, -- Array containing FB address offsets needed between each processed line (shift), PAN or TDI ACC_FRAME_ANC_ID_READY => acc_frame_anc_id_ready, -- Pulse indicating that the ANC ID is ready ACC_FRAME_ANC_ID => acc_frame_anc_id, -- Vector containing the ANC ID of the processed frame FSM_ERROR => fsm_error -- Asserted when FSM in undefined state ); elsif G_SENSOR = "CMV12K" generate i_dut_hs : entity work.tdi_read_control generic map( G_SENSOR => G_SENSOR ) port map( CLK => clk, -- Main Clock RST => rst, -- Main Reset ACQUISITION_RUNNING => acquisition_running, -- '1' enables TDI processing, '0' stops processing at the end of the frame being processed TDI_WRITE_RUNNING => tdi_write_running, -- High until the last frame of the acquisition is written in the DDR frame buffer TDI_READ_RUNNING => tdi_read_running, -- High until the TDI processing is done ANC_FIFO_HS_DOUT => anc_fifo_hs_dout, -- TDI ANC to add in the FIFO ANC_FIFO_HS_RE => anc_fifo_hs_re, -- FIFO TDI read enable ANC_FIFO_HS_EMPTY => anc_fifo_hs_empty, -- FIFO TDI empty flag ANC_FIFO_PAN_DOUT => (others => '0'), -- PAN ANC to add in the FIFO ANC_FIFO_PAN_RE => open, -- FIFO PAN read enable ANC_FIFO_PAN_EMPTY => '1', -- FIFO PAN empty flag TDI_TYPE => test_inputs.tdi_type, -- TDI Type (0: none, 1: TDI6 half rate, 2: TDI12, 3: TDI6 full rate) HS_PLUS_PAN_MODE => test_inputs.hs_plus_pan_mode, -- HS + PAN mode (only in vnir) TDI_SPECTRAL_AGGLOM => test_inputs.tdi_spectral_agglom, --"01": no SA, "10": SAx2 "11": SAx3 DUAL_READOUT => test_inputs.dual_readout, -- Two consecutive sensor images are added pixel by pixel before the TDI6 HR algo is applied TDI_ACCU_START => tdi_accu_start, -- Pulse '1' start the TDI Accumulation process TDI_ACCU_READY => tdi_accu_ready, -- '1' indicates that the TDI Accumulation process is ready for a new accumulation FB_FIRST_FRAME_ADDR => fb_first_frame_addr, -- Addresses in FB of the first frames line that need to be processed FB_FRAME_ANC => fb_frame_anc, -- Vector containing the ANC of the frames that need to be processed ACCUM_OFFSETS => accum_offsets, -- Array containing FB address offsets needed between each processed accumulation, PAN or TDI SHIFT_OFFSETS => shift_offsets, -- Array containing FB address offsets needed between each processed line (shift), PAN or TDI ACC_FRAME_ANC_ID_READY => acc_frame_anc_id_ready, -- Pulse indicating that the ANC ID is ready ACC_FRAME_ANC_ID => acc_frame_anc_id, -- Vector containing the ANC ID of the processed frame FSM_ERROR => fsm_error -- Asserted when FSM in undefined state );
end generate g_dut;
-- Tests Sequencing --
-- Test restarted every 100 clock cycles start_test_cond <= true when clk_cntr = 2000 and tdi_accu_ready = '1' else False;
-- Test finished when no_accum have been performed --autoformat_off g_stop_test : if G_SENSOR = "CARDINAL1280" generate stop_test_cond <= true when (tdi_accu_start_cntr = test_inputs.no_accum) else False;
elsif G_SENSOR = "CMV12K" generate stop_test_cond <= true when (tdi_accu_start_cntr = test_inputs.no_accum) and (tdi_accu_pan_start_cntr = test_inputs.no_accum_pan) else False; end generate g_stop_test; --autoformat_on
process (clk) begin if rising_edge(clk) then if rst = '1' then start_tests <= '0'; test_inputs <= tests_cases(0); else clk_cntr <= clk_cntr + 1; start_tests_d <= start_tests;
end process;
-- CONFIGURATION REGISTERS EMULATOR --
-- Configure all the input register of the DUT process (clk) begin if rising_edge(clk) then if start_tests = '1' then tdi_type <= test_inputs.tdi_type; dual_readout <= test_inputs.dual_readout; hs_plus_pan_mode <= test_inputs.hs_plus_pan_mode; tdi_spectral_agglom <= test_inputs.tdi_spectral_agglom; end if; end if; end process;
-- Acquisition running p_acquisition_running : process (CLK) begin if rising_edge(CLK) then if rst = '1' then acquisition_running <= '0'; else if start_tests = '1' and start_tests_d = '0' then acquisition_running <= '1'; end if; if G_SENSOR = "CMV12K" then if (tdi_accu_start_cntr = test_inputs.no_accum) and (tdi_accu_pan_start_cntr = test_inputs.no_accum_pan) then acquisition_running <= '0'; end if; end if;
end process;
-- TDI Write running p_tdi_write_running : process (CLK) variable v_tdi_write_running_delay : integer; begin if rising_edge(CLK) then if rst = '1' then v_tdi_write_running_delay := randint(50, 125); tdi_write_running <= '0'; else if acquisition_running = '1' then tdi_write_running <= '1'; end if;
end process;
-- ANC FIFO HS EMULATOR --
anc_fifo_hs_empty <= '1' when (test_inputs.hs_off or (clk_cntr mod 256) = 0 or tdi_read_running = '0' or (tdi_accu_start_cntr = test_inputs.no_accum)) else '0';
process (clk) begin if rising_edge(clk) then
end process;
-- ANC FIFO PAN EMULATOR --
--autoformat_off g_pan_empty : if G_SENSOR = "CARDINAL1280" generate anc_fifo_pan_empty <= '1' when (test_inputs.pan_off or (clk_cntr mod 64) = 0 or tdi_read_running = '0' or (tdi_accu_start_cntr = test_inputs.no_accum)) else '0'; elsif G_SENSOR = "CMV12K" generate anc_fifo_pan_empty <= '1' when (test_inputs.pan_off or (clk_cntr mod 64) = 0 or tdi_read_running = '0' or (tdi_accu_pan_start_cntr = test_inputs.no_accum_pan)) else '0'; end generate g_pan_empty; --autoformat_on process (clk) begin if rising_edge(clk) then if anc_fifo_pan_re = '1' then anc_fifo_pan_cntr <= anc_fifo_pan_cntr + 1; anc_fifo_pan_dout(C_ANC_FRAME_ADDRESS_POS'range) <= std_logic_vector(C_PAN_ADDRESS_OFFSETS + anc_fifo_pan_cntr); anc_fifo_pan_dout(C_ANC_NBR_OF_LINES_POS'range) <= test_inputs.pan_nol; anc_fifo_pan_dout(C_ANC_ANC_ID_POS'range) <= std_logic_vector(anc_fifo_pan_cntr(5 downto 0)); end if;
end process;
-- TDI ACCUMULATOR EMULATOR --
p_accum : process (clk) begin if rising_edge(clk) then if rst = '1' then tdi_accu_ready <= '1'; end if; tdi_accu_timer <= tdi_accu_timer + 1; tdi_accu_start_d <= tdi_accu_start;
end process;
-- TDI ACCUMULATOR EMULATOR PAN --
g_accum_pan : if G_SENSOR = "CMV12K" generate process (clk) begin if rising_edge(clk) then tdi_accu_pan_timer <= tdi_accu_pan_timer + 1; tdi_accu_pan_start_d <= tdi_accu_start_pan;
end generate g_accum_pan;
-- ASSERTS --
g_assert_process : if G_SENSOR = "CARDINAL1280" generate
end Behavioral;