Files
Progetto-reti-logiche/progetto_reti_logiche.srcs/sim_1/new/project_tb_edge.vhd
T
2026-06-12 20:37:03 +02:00

1620 lines
70 KiB
VHDL

-- ============================================================
-- TESTBENCH EDGE CASES (ESTESO) - Progetto Reti Logiche 2025/2026
-- ============================================================
-- Copre i seguenti gruppi di test:
--
-- GRUPPO 0: Reset e inizializzazione
-- 0.0 Reset -> addr 0 = 0, DONE = 1 durante reset poi torna 0
-- 0.1 Reset mentre operazione in corso (START=1)
--
-- GRUPPO 1: OP=10 - Inserimento
-- 1.0 Insert in lista vuota
-- 1.1 Insert con priorità più alta di tutti (va in testa)
-- 1.2 Insert con priorità più bassa di tutti (va in fondo)
-- 1.3 Insert con priorità uguale -> va in CODA agli uguali
-- 1.4 Insert con tutti i task alla stessa priorità -> sempre in fondo
-- 1.5 Insert con ID duplicato -> operazione ignorata
--
-- GRUPPO 2: OP=01 - Rimozione
-- 2.0 Rimozione da lista vuota -> o_task_id = 000000
-- 2.1 Rimozione con un solo task -> lista diventa vuota
-- 2.2 Rimozione da lista con tutti task alla stessa priorità
--
-- GRUPPO 3: OP=00 - Decremento priorità
-- 3.0 Decremento con lista vuota -> nessuna scrittura, no crash
-- 3.1 Saturazione: task a priorità 3 restano a 3
-- 3.2 Lista con TUTTI i task già a priorità 3 -> nessuna modifica
-- 3.3 Mix priorità 2 e 3: i "nuovi 3" (ex-2) vengono PRIMA dei "vecchi 3"
--
-- GRUPPO 4: OP=11 - Svuota lista
-- 4.0 Svuota lista popolata -> addr 0 = 0
-- 4.1 Svuota lista già vuota -> addr 0 rimane 0
-- 4.2 Svuota poi rimuovi -> o_task_id = 000000
-- 4.3 Svuota poi decrementa -> nessun effetto
--
-- GRUPPO 5: Sequenze composite (originali)
-- 5.0 Insert -> Decremento -> Rimozione: verifica ID estratto corretto
-- 5.1 Sequenza di insert con priorità miste -> verifica ordinamento completo
--
-- ============================================================
-- NUOVI GRUPPI (casi limite mancanti):
-- ============================================================
--
-- GRUPPO 6: Rimozione multipla e ordinamento
-- 6.0 Rimozione multipla consecutiva fino a lista vuota
-- (lista con priorità miste, verifica ordine esatto degli ID estratti
-- e che il contatore scenda correttamente a ogni step)
-- 6.1 Rimozione da lista con priorità miste -> viene rimosso SEMPRE
-- il task in posizione 1 (indirizzo 1), non quello con prio numericamente
-- più bassa
--
-- GRUPPO 7: Decremento ripetuto
-- 7.0 Decremento applicato più volte -> saturazione progressiva
-- (prio 0->1->2->3->3->3, verifica a ogni step)
-- 7.1 Decremento su lista con un solo task (edge case singolo)
-- 7.2 Decrementa -> Svuota (ordine inverso rispetto a 4.3)
--
-- GRUPPO 8: Insert dopo rimozione parziale
-- 8.0 Insert -> Rimozione parziale -> Insert -> verifica ordinamento completo
-- 8.1 Insert con priority=0 in lista con tutti prio=0
-- (il nuovo va sempre in fondo, indipendentemente da prio uguale)
-- 8.2 Insert con priority=3 in lista con tutti prio=3
-- (simmetrico: il nuovo va sempre in fondo)
--
-- GRUPPO 9: Verifica o_task_id per OP != 01
-- 9.0 OP=10 (insert) -> o_task_id deve essere 0x00 quando DONE=1
-- 9.1 OP=00 (decremento) -> o_task_id deve essere 0x00 quando DONE=1
-- 9.2 OP=11 (svuota) -> o_task_id deve essere 0x00 quando DONE=1
-- 9.3 OP=01 lista vuota -> o_task_id deve essere 0x00 quando DONE=1
--
-- GRUPPO 10: Protocollo START-DONE e reset
-- 10.0 Operazione immediata al primo fronte dopo DONE->0 post-reset
-- (nessun ciclo di margine tra fine reset e primo START)
-- 10.1 Sequenza lunga stress: insert x8 -> dec x3 -> remove x4
-- -> insert x3 -> remove fino a vuota (verifica coerenza totale)
-- 10.2 Reset dopo OP=11 (svuota -> reset -> verifica lista vuota)
--
-- GRUPPO 11: OP=10 con ID=0 (condizione di errore da specifica)
-- 11.0 Insert ID=0 in lista vuota -> ignorato, DONE arriva comunque
-- 11.1 Insert ID=0 in lista popolata (prio alta e bassa) -> lista invariata
--
-- GRUPPO 12: Capacita' massima (63 task, errata del 24.02.2026)
-- 12.0 Insert di TUTTI i 63 ID possibili, verifica completa della memoria
-- 12.1 Inserimenti oltre il 63esimo (ogni ID e' duplicato) -> ignorati
-- 12.2 Drain completo: 63 pop con verifica dell'ordine di estrazione
--
-- GRUPPO 13: Memoria stale e duplicati ai bordi della lista
-- 13.0 Re-insert di ID appena rimosso (copia stale oltre il count)
-- 13.1 Duplicato in PRIMA posizione -> ignorato
-- 13.2 Duplicato in ULTIMA posizione -> ignorato
-- 13.3 Clear poi re-insert dello stesso ID (cella stale) -> deve riuscire
--
-- GRUPPO 14: Reset asincrono avanzato (spec: RESET in qualsiasi momento)
-- 14.0 Reset a meta' dello shift di OP=01
-- 14.1 Reset nel ciclo di scrittura del count (fine OP=01)
-- 14.2 Reset a meta' della scansione di OP=00
-- 14.3 Reset mentre DONE=1
-- 14.4 Reset corto (7 ns, non allineato al clock)
--
-- GRUPPO 15: Protocollo START-DONE con abbassamento lento di START
-- 15.0 Pop con START alto 1 ciclo extra -> niente doppio pop
-- 15.1 Age con START alto 1 ciclo extra -> niente doppio age
-- 15.2 Insert con START alto 2 cicli extra
-- NOTA: il gruppo 15 FALLISCE con l'RTL attuale finche' non viene
-- aggiunto uno stato S_DONE che attende START=0 prima di tornare in IDLE.
--
-- MONITOR concorrente: nessuna scrittura in memoria quando il modulo
-- dovrebbe essere idle (RST=0, START=0, DONE=0).
--
-- ============================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
entity project_tb_edge is
end project_tb_edge;
architecture project_tb_edge_arch of project_tb_edge is
constant CLOCK_PERIOD : time := 20 ns;
signal tb_clk : std_logic := '0';
signal tb_rst : std_logic;
signal tb_start : std_logic;
signal tb_done : std_logic;
signal tb_o_task_id : std_logic_vector(5 downto 0);
signal tb_task_priority : std_logic_vector(1 downto 0);
signal tb_op : std_logic_vector(1 downto 0);
signal tb_i_task_id : std_logic_vector(5 downto 0);
signal exc_o_mem_addr : std_logic_vector(15 downto 0);
signal exc_o_mem_data : std_logic_vector(7 downto 0);
signal exc_o_mem_we : std_logic;
signal exc_o_mem_en : std_logic;
signal init_o_mem_addr : std_logic_vector(15 downto 0) := (others => '0');
signal init_o_mem_data : std_logic_vector(7 downto 0) := (others => '0');
signal init_o_mem_we : std_logic := '0';
signal init_o_mem_en : std_logic := '0';
signal tb_o_mem_addr : std_logic_vector(15 downto 0);
signal tb_o_mem_data : std_logic_vector(7 downto 0);
signal tb_o_mem_we : std_logic;
signal tb_o_mem_en : std_logic;
signal tb_i_mem_data : std_logic_vector(7 downto 0);
signal memory_control : std_logic := '0';
type ram_type is array (65535 downto 0) of std_logic_vector(7 downto 0);
signal RAM : ram_type := (others => "00000000");
-- --------------------------------------------------------
-- Utility: tipo per verifiche memoria
-- --------------------------------------------------------
type mem_check_t is record
addr : integer;
expected : std_logic_vector(7 downto 0);
end record;
component project_reti_logiche is
port (
i_clk : in std_logic;
i_rst : in std_logic;
i_start : in std_logic;
i_task_id : in std_logic_vector(5 downto 0);
i_task_priority : in std_logic_vector(1 downto 0);
i_op : in std_logic_vector(1 downto 0);
o_done : out std_logic;
o_task_id : out std_logic_vector(5 downto 0);
o_mem_addr : out std_logic_vector(15 downto 0);
i_mem_data : in std_logic_vector(7 downto 0);
o_mem_data : out std_logic_vector(7 downto 0);
o_mem_we : out std_logic;
o_mem_en : out std_logic
);
end component project_reti_logiche;
begin
UUT : project_reti_logiche
port map (
i_clk => tb_clk,
i_rst => tb_rst,
i_start => tb_start,
i_task_id => tb_i_task_id,
i_task_priority => tb_task_priority,
i_op => tb_op,
o_done => tb_done,
o_task_id => tb_o_task_id,
o_mem_addr => exc_o_mem_addr,
i_mem_data => tb_i_mem_data,
o_mem_data => exc_o_mem_data,
o_mem_we => exc_o_mem_we,
o_mem_en => exc_o_mem_en
);
-- Clock
tb_clk <= not tb_clk after CLOCK_PERIOD / 2;
-- Memoria
MEM : process (tb_clk)
begin
if tb_clk'event and tb_clk = '1' then
if tb_o_mem_en = '1' then
if tb_o_mem_we = '1' then
RAM(to_integer(unsigned(tb_o_mem_addr))) <= tb_o_mem_data after 1 ns;
tb_i_mem_data <= tb_o_mem_data after 1 ns;
else
tb_i_mem_data <= RAM(to_integer(unsigned(tb_o_mem_addr))) after 1 ns;
end if;
end if;
end if;
end process;
-- Mux memoria: init_o / exc_o
memory_signal_swapper : process (memory_control,
init_o_mem_addr, init_o_mem_data, init_o_mem_en, init_o_mem_we,
exc_o_mem_addr, exc_o_mem_data, exc_o_mem_en, exc_o_mem_we)
begin
tb_o_mem_addr <= init_o_mem_addr;
tb_o_mem_data <= init_o_mem_data;
tb_o_mem_en <= init_o_mem_en;
tb_o_mem_we <= init_o_mem_we;
if memory_control = '1' then
tb_o_mem_addr <= exc_o_mem_addr;
tb_o_mem_data <= exc_o_mem_data;
tb_o_mem_en <= exc_o_mem_en;
tb_o_mem_we <= exc_o_mem_we;
end if;
end process;
-- ============================================================
-- MONITOR: scritture spurie
-- Se il modulo e' idle (RST=0, START=0, DONE=0) non deve scrivere
-- in memoria. Una scrittura qui indica o una ri-esecuzione indebita
-- dell'operazione (protocollo START-DONE violato) o segnali di
-- controllo memoria non azzerati.
-- Campionato sul fronte di discesa per evitare race con i registri.
-- ============================================================
protocol_monitor : process (tb_clk)
begin
if falling_edge(tb_clk) then
if tb_rst = '0' and tb_done = '0' and tb_start = '0' then
assert not (exc_o_mem_en = '1' and exc_o_mem_we = '1')
report "FAIL [MONITOR] scrittura in memoria con modulo idle "
& "(RST=0, START=0, DONE=0): possibile ri-esecuzione "
& "dell'operazione o segnali di scrittura spuri"
severity failure;
end if;
end if;
end process;
-- ============================================================
-- Processo principale di test
-- ============================================================
main_test : process
-- --------------------------------------------------------
-- Procedure di supporto
-- --------------------------------------------------------
-- Reset del componente (attende DONE=1 durante reset poi DONE=0)
procedure do_reset is
begin
tb_start <= '0';
tb_rst <= '1';
wait for 100 ns;
-- DONE deve essere 1 durante il reset (modulo non pronto)
assert tb_done = '1'
report "FAIL: DONE dovrebbe essere 1 durante reset"
severity failure;
tb_rst <= '0';
-- Attende che il modulo completi l'inizializzazione (DONE->0)
wait until tb_done = '0';
wait until falling_edge(tb_clk);
end procedure;
-- Lancia un'operazione e attende DONE=1
procedure do_op (
op : std_logic_vector(1 downto 0);
task_id : std_logic_vector(5 downto 0);
priority : std_logic_vector(1 downto 0)
) is
begin
wait until falling_edge(tb_clk);
tb_op <= op;
tb_i_task_id <= task_id;
tb_task_priority <= priority;
tb_start <= '1';
wait until rising_edge(tb_done);
tb_start <= '0';
wait until falling_edge(tb_done);
end procedure;
-- Lancia un'operazione, cattura o_task_id al momento di DONE=1,
-- e restituisce il valore campionato nel segnale out_id
procedure do_op_capture (
op : std_logic_vector(1 downto 0);
task_id : std_logic_vector(5 downto 0);
priority : std_logic_vector(1 downto 0);
captured : out std_logic_vector(5 downto 0)
) is
begin
wait until falling_edge(tb_clk);
tb_op <= op;
tb_i_task_id <= task_id;
tb_task_priority <= priority;
tb_start <= '1';
wait until rising_edge(tb_done);
captured := tb_o_task_id; -- campiona al momento esatto di DONE=1
tb_start <= '0';
wait until falling_edge(tb_done);
end procedure;
-- Verifica un singolo indirizzo di memoria
procedure check_mem (
step : string;
addr : integer;
expected : std_logic_vector(7 downto 0)
) is
begin
assert RAM(addr) = expected
report "FAIL [" & step & "] addr=" & integer'image(addr)
& " expected=0x" & integer'image(to_integer(unsigned(expected)))
& " actual=0x" & integer'image(to_integer(unsigned(RAM(addr))))
severity failure;
end procedure;
-- Verifica o_task_id (usata dopo do_op_capture)
procedure check_captured_id (
step : string;
actual : std_logic_vector(5 downto 0);
expected : std_logic_vector(5 downto 0)
) is
begin
assert actual = expected
report "FAIL [" & step & "] o_task_id expected="
& integer'image(to_integer(unsigned(expected)))
& " actual="
& integer'image(to_integer(unsigned(actual)))
severity failure;
end procedure;
-- Verifica o_task_id (campionato in tempo reale)
procedure check_task_id (
step : string;
expected : std_logic_vector(5 downto 0)
) is
begin
assert tb_o_task_id = expected
report "FAIL [" & step & "] o_task_id expected="
& integer'image(to_integer(unsigned(expected)))
& " actual="
& integer'image(to_integer(unsigned(tb_o_task_id)))
severity failure;
end procedure;
-- Azzera la RAM (usato per isolare i gruppi di test)
procedure clear_ram is
begin
memory_control <= '0';
for i in 0 to 70 loop
init_o_mem_addr <= std_logic_vector(to_unsigned(i, 16));
init_o_mem_data <= "00000000";
init_o_mem_en <= '1';
init_o_mem_we <= '1';
wait until rising_edge(tb_clk);
end loop;
init_o_mem_en <= '0';
init_o_mem_we <= '0';
memory_control <= '1';
end procedure;
-- Inserisce N task nella lista
procedure fill_list (n : integer; base_id : integer; prio : std_logic_vector(1 downto 0)) is
begin
for k in 0 to n-1 loop
do_op("10",
std_logic_vector(to_unsigned(base_id + k, 6)),
prio);
end loop;
end procedure;
-- Variabili locali per catturare il task_id restituito
variable captured_id : std_logic_vector(5 downto 0);
-- Modello di riferimento per il test a capacita' massima (gruppo 12)
type int_array63_t is array (1 to 63) of integer;
variable exp_id : int_array63_t;
variable exp_prio : int_array63_t;
variable idx : integer;
variable exp_byte : std_logic_vector(7 downto 0);
begin
-- Inizializzazione segnali
tb_start <= '0';
tb_rst <= '0';
tb_op <= "00";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
memory_control <= '1';
wait for 50 ns;
-- ============================================================
-- GRUPPO 0: Reset e inizializzazione
-- ============================================================
-- 0.0: Reset base -> addr 0 deve essere 0, DONE=1 durante reset
report "=== GRUPPO 0: Reset ===";
memory_control <= '0';
tb_rst <= '1';
wait for 100 ns;
assert tb_done = '1'
report "FAIL [0.0] DONE deve essere 1 durante reset"
severity failure;
tb_rst <= '0';
wait until tb_done = '0';
wait until falling_edge(tb_clk);
check_mem("0.0 reset-addr0", 0, "00000000");
memory_control <= '1';
report "Test 0.0 OK: reset base";
-- 0.1: Reset mentre operazione in corso
clear_ram;
do_reset;
wait until falling_edge(tb_clk);
tb_op <= "10";
tb_i_task_id <= "000001";
tb_task_priority <= "01";
tb_start <= '1';
wait until rising_edge(tb_clk);
wait until rising_edge(tb_clk);
tb_rst <= '1';
tb_start <= '0';
wait for 50 ns;
assert tb_done = '1'
report "FAIL [0.1] DONE deve essere 1 durante reset asincrono"
severity failure;
tb_rst <= '0';
wait until tb_done = '0';
wait until falling_edge(tb_clk);
check_mem("0.1 reset-async", 0, "00000000");
report "Test 0.1 OK: reset asincrono durante operazione";
-- ============================================================
-- GRUPPO 1: OP=10 - Inserimento
-- ============================================================
report "=== GRUPPO 1: Inserimento ===";
-- 1.0: Insert in lista vuota
clear_ram;
do_reset;
do_op("10", "000001", "10");
check_mem("1.0 count", 0, "00000001");
check_mem("1.0 task", 1, "00000110");
report "Test 1.0 OK: insert in lista vuota";
-- 1.1: Insert con priorità più alta di tutti (va in testa)
clear_ram;
do_reset;
do_op("10", "000010", "10");
do_op("10", "000011", "10");
do_op("10", "000001", "00");
check_mem("1.1 count", 0, "00000011");
check_mem("1.1 pos1", 1, "00000100");
check_mem("1.1 pos2", 2, "00001010");
check_mem("1.1 pos3", 3, "00001110");
report "Test 1.1 OK: insert con priorità massima in testa";
-- 1.2: Insert con priorità più bassa di tutti (va in fondo)
clear_ram;
do_reset;
do_op("10", "000001", "00");
do_op("10", "000010", "01");
do_op("10", "000011", "11");
check_mem("1.2 count", 0, "00000011");
check_mem("1.2 pos1", 1, "00000100");
check_mem("1.2 pos2", 2, "00001001");
check_mem("1.2 pos3", 3, "00001111");
report "Test 1.2 OK: insert con priorità minima in fondo";
-- 1.3: Insert con priorità uguale -> va in CODA agli uguali
clear_ram;
do_reset;
do_op("10", "000001", "01");
do_op("10", "000010", "01");
do_op("10", "000011", "01");
check_mem("1.3 count", 0, "00000011");
check_mem("1.3 pos1", 1, "00000101");
check_mem("1.3 pos2", 2, "00001001");
check_mem("1.3 pos3", 3, "00001101");
report "Test 1.3 OK: insert stesso prio -> va in coda agli uguali";
-- 1.4: Insert con tutti task stessa priorità -> sempre in fondo
clear_ram;
do_reset;
do_op("10", "000001", "10");
do_op("10", "000010", "10");
do_op("10", "000011", "10");
do_op("10", "000100", "10");
check_mem("1.4 count", 0, "00000100");
check_mem("1.4 pos1", 1, "00000110");
check_mem("1.4 pos2", 2, "00001010");
check_mem("1.4 pos3", 3, "00001110");
check_mem("1.4 pos4", 4, "00010010");
report "Test 1.4 OK: insert con tutti uguale prio -> sempre in fondo";
-- 1.5: Insert con ID duplicato -> operazione ignorata
clear_ram;
do_reset;
do_op("10", "000001", "01");
do_op("10", "000010", "01");
do_op("10", "000011", "01");
check_mem("1.5 count-pre", 0, "00000011");
check_mem("1.5 pos1-pre", 1, "00000101");
check_mem("1.5 pos2-pre", 2, "00001001");
check_mem("1.5 pos3-pre", 3, "00001101");
do_op("10", "000010", "00");
check_mem("1.5 count-post", 0, "00000011");
check_mem("1.5 pos1-post", 1, "00000101");
check_mem("1.5 pos2-post", 2, "00001001");
check_mem("1.5 pos3-post", 3, "00001101");
check_mem("1.5 pos4-post", 4, "00000000");
report "Test 1.5 OK: insert con ID duplicato ignorato";
-- ============================================================
-- GRUPPO 2: OP=01 - Rimozione
-- ============================================================
report "=== GRUPPO 2: Rimozione ===";
-- 2.0: Rimozione da lista vuota -> o_task_id = 000000
clear_ram;
do_reset;
wait until falling_edge(tb_clk);
tb_op <= "01";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("2.0 empty-remove", "000000");
tb_start <= '0';
wait until falling_edge(tb_done);
check_mem("2.0 count", 0, "00000000");
report "Test 2.0 OK: rimozione da lista vuota -> o_task_id=0";
-- 2.1: Rimozione con un solo task -> lista diventa vuota
clear_ram;
do_reset;
do_op("10", "000101", "01");
check_mem("2.1 pre-count", 0, "00000001");
wait until falling_edge(tb_clk);
tb_op <= "01";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("2.1 single-remove", "000101");
tb_start <= '0';
wait until falling_edge(tb_done);
check_mem("2.1 post-count", 0, "00000000");
report "Test 2.1 OK: rimozione unico task -> lista vuota, task_id corretto";
-- 2.2: Rimozione da lista con tutti task alla stessa priorità -> FIFO
clear_ram;
do_reset;
do_op("10", "000001", "10");
do_op("10", "000010", "10");
do_op("10", "000011", "10");
wait until falling_edge(tb_clk);
tb_op <= "01";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("2.2 same-prio-remove", "000001");
tb_start <= '0';
wait until falling_edge(tb_done);
check_mem("2.2 count", 0, "00000010");
check_mem("2.2 pos1", 1, "00001010");
check_mem("2.2 pos2", 2, "00001110");
report "Test 2.2 OK: rimozione con prio uguali -> FIFO rispettato";
-- ============================================================
-- GRUPPO 3: OP=00 - Decremento priorità
-- ============================================================
report "=== GRUPPO 3: Decremento priorità ===";
-- 3.0: Decremento con lista vuota -> nessun crash
clear_ram;
do_reset;
do_op("00", "000000", "00");
check_mem("3.0 count", 0, "00000000");
report "Test 3.0 OK: decremento su lista vuota";
-- 3.1: Saturazione: task a priorità 3 restano a 3
clear_ram;
do_reset;
do_op("10", "000001", "01");
do_op("10", "000010", "11");
do_op("00", "000000", "00");
check_mem("3.1 count", 0, "00000010");
check_mem("3.1 pos1", 1, "00000110");
check_mem("3.1 pos2", 2, "00001011");
report "Test 3.1 OK: saturazione a priorità 3";
-- 3.2: Lista con TUTTI i task già a priorità 3 -> nessuna modifica
clear_ram;
do_reset;
do_op("10", "000001", "11");
do_op("10", "000010", "11");
do_op("10", "000011", "11");
do_op("00", "000000", "00");
check_mem("3.2 count", 0, "00000011");
check_mem("3.2 pos1", 1, "00000111");
check_mem("3.2 pos2", 2, "00001011");
check_mem("3.2 pos3", 3, "00001111");
report "Test 3.2 OK: tutti a prio 3 -> nessuna modifica";
-- 3.3: Mix priorità 2 e 3: gli ex-2 precedono gli ex-3
clear_ram;
do_reset;
do_op("10", "000001", "10");
do_op("10", "000010", "10");
do_op("10", "000011", "11");
do_op("10", "000100", "11");
do_op("00", "000000", "00");
check_mem("3.3 count", 0, "00000100");
check_mem("3.3 pos1", 1, "00000111");
check_mem("3.3 pos2", 2, "00001011");
check_mem("3.3 pos3", 3, "00001111");
check_mem("3.3 pos4", 4, "00010011");
report "Test 3.3 OK: ex-prio2 vengono prima di ex-prio3 (no riordino)";
-- ============================================================
-- GRUPPO 4: OP=11 - Svuota lista
-- ============================================================
report "=== GRUPPO 4: Svuota lista ===";
-- 4.0: Svuota lista popolata -> addr 0 = 0
clear_ram;
do_reset;
do_op("10", "000001", "00");
do_op("10", "000010", "01");
do_op("10", "000011", "10");
do_op("11", "000000", "00");
check_mem("4.0 count", 0, "00000000");
report "Test 4.0 OK: svuota lista popolata";
-- 4.1: Svuota lista già vuota -> addr 0 rimane 0, nessun crash
clear_ram;
do_reset;
do_op("11", "000000", "00");
check_mem("4.1 count", 0, "00000000");
report "Test 4.1 OK: svuota lista già vuota";
-- 4.2: Svuota poi rimuovi -> o_task_id = 000000
clear_ram;
do_reset;
do_op("10", "000001", "00");
do_op("11", "000000", "00");
wait until falling_edge(tb_clk);
tb_op <= "01";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("4.2 remove-after-clear", "000000");
tb_start <= '0';
wait until falling_edge(tb_done);
report "Test 4.2 OK: svuota poi rimozione -> o_task_id=0";
-- 4.3: Svuota poi decrementa -> nessun effetto
clear_ram;
do_reset;
do_op("10", "000001", "01");
do_op("11", "000000", "00");
do_op("00", "000000", "00");
check_mem("4.3 count", 0, "00000000");
report "Test 4.3 OK: svuota poi decrementa -> nessun effetto";
-- ============================================================
-- GRUPPO 5: Sequenze composite (originali)
-- ============================================================
report "=== GRUPPO 5: Sequenze composite ===";
-- 5.0: Insert -> Decremento -> Rimozione
clear_ram;
do_reset;
do_op("10", "001010", "00");
do_op("10", "010100", "01");
do_op("10", "011110", "10");
do_op("00", "000000", "00");
check_mem("5.0 after-dec pos1", 1, "00101001");
check_mem("5.0 after-dec pos2", 2, "01010010");
check_mem("5.0 after-dec pos3", 3, "01111011");
wait until falling_edge(tb_clk);
tb_op <= "01";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("5.0 remove-after-dec", "001010");
tb_start <= '0';
wait until falling_edge(tb_done);
check_mem("5.0 count", 0, "00000010");
check_mem("5.0 pos1", 1, "01010010");
check_mem("5.0 pos2", 2, "01111011");
report "Test 5.0 OK: insert->dec->remove, ID estratto corretto";
-- 5.1: Ordinamento completo con priorità miste
clear_ram;
do_reset;
do_op("10", "000101", "11");
do_op("10", "000011", "01");
do_op("10", "000111", "10");
do_op("10", "000001", "00");
do_op("10", "001001", "01");
check_mem("5.1 count", 0, "00000101");
check_mem("5.1 pos1", 1, "00000100");
check_mem("5.1 pos2", 2, "00001101");
check_mem("5.1 pos3", 3, "00100101");
check_mem("5.1 pos4", 4, "00011110");
check_mem("5.1 pos5", 5, "00010111");
report "Test 5.1 OK: ordinamento completo con priorità miste";
-- ============================================================
-- GRUPPO 6: Rimozione multipla e ordinamento
-- ============================================================
report "=== GRUPPO 6: Rimozione multipla e ordinamento ===";
-- 6.0: Rimozione multipla consecutiva fino a lista vuota
--
-- Lista inserita: id=1 prio=0, id=2 prio=1, id=3 prio=1, id=4 prio=2
-- Ordine atteso di estrazione: 1, 2, 3, 4 (la rimozione prende sempre pos 1)
-- Dopo ogni rimozione verifichiamo: l'ID estratto, il count e la struttura
-- della lista residua.
clear_ram;
do_reset;
do_op("10", "000001", "00"); -- id=1, prio=0
do_op("10", "000010", "01"); -- id=2, prio=1
do_op("10", "000011", "01"); -- id=3, prio=1
do_op("10", "000100", "10"); -- id=4, prio=2
check_mem("6.0 init-count", 0, "00000100");
-- Prima rimozione: deve uscire id=1
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("6.0 remove1-id", captured_id, "000001");
check_mem("6.0 after-rem1 count", 0, "00000011");
check_mem("6.0 after-rem1 pos1", 1, "00001001"); -- id=2, prio=1
check_mem("6.0 after-rem1 pos2", 2, "00001101"); -- id=3, prio=1
check_mem("6.0 after-rem1 pos3", 3, "00010010"); -- id=4, prio=2
-- Seconda rimozione: deve uscire id=2
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("6.0 remove2-id", captured_id, "000010");
check_mem("6.0 after-rem2 count", 0, "00000010");
check_mem("6.0 after-rem2 pos1", 1, "00001101"); -- id=3, prio=1
check_mem("6.0 after-rem2 pos2", 2, "00010010"); -- id=4, prio=2
-- Terza rimozione: deve uscire id=3
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("6.0 remove3-id", captured_id, "000011");
check_mem("6.0 after-rem3 count", 0, "00000001");
check_mem("6.0 after-rem3 pos1", 1, "00010010"); -- id=4, prio=2
-- Quarta rimozione: deve uscire id=4, lista diventa vuota
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("6.0 remove4-id", captured_id, "000100");
check_mem("6.0 after-rem4 count", 0, "00000000");
-- Quinta rimozione su lista vuota: deve restituire id=0
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("6.0 remove5-empty", captured_id, "000000");
check_mem("6.0 after-rem5 count", 0, "00000000");
report "Test 6.0 OK: rimozioni multiple consecutive fino a lista vuota";
-- 6.1: Rimozione da lista con priorità miste
--
-- SCOPO: verificare che OP=01 rimuova SEMPRE il task in posizione 1 (addr 1),
-- NON il task con il valore di priorità numericamente più basso.
-- In questo test, il task in posizione 1 ha prio=2 (non la "migliore"),
-- perché la lista è stata costruita in un ordine specifico tramite inserimenti
-- successivi con decremento.
--
-- Costruiamo: id=5 prio=1, id=7 prio=3, poi decrementiamo.
-- Dopo il decremento: id=5 diventa prio=2, id=7 resta prio=3
-- La lista è: [pos1: id=5 prio=2] [pos2: id=7 prio=3]
-- La rimozione deve estrarre id=5 (posizione 1), non id=7.
clear_ram;
do_reset;
do_op("10", "000101", "01"); -- id=5, prio=1
do_op("10", "000111", "11"); -- id=7, prio=3
-- Lista: [id=5,p1] [id=7,p3]
do_op("00", "000000", "00"); -- decremento
-- Lista dopo dec: [id=5,p2] [id=7,p3]
check_mem("6.1 pre-pos1", 1, "00010110"); -- id=5, prio=2
check_mem("6.1 pre-pos2", 2, "00011111"); -- id=7, prio=3
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("6.1 remove-from-mixed", captured_id, "000101"); -- deve uscire id=5 (pos 1)
check_mem("6.1 post-count", 0, "00000001");
check_mem("6.1 post-pos1", 1, "00011111"); -- rimane id=7
report "Test 6.1 OK: rimozione da lista con prio miste -> sempre posizione 1";
-- ============================================================
-- GRUPPO 7: Decremento ripetuto
-- ============================================================
report "=== GRUPPO 7: Decremento ripetuto ===";
-- 7.0: Decremento applicato 4 volte su un task partendo da prio=0
-- Sequenza attesa: 0 -> 1 -> 2 -> 3 -> 3 (satura)
clear_ram;
do_reset;
do_op("10", "000001", "00"); -- id=1, prio=0
check_mem("7.0 init", 1, "00000100"); -- id=1, prio=0
do_op("00", "000000", "00"); -- 1° decremento: prio 0->1
check_mem("7.0 dec1", 1, "00000101"); -- id=1, prio=1
check_mem("7.0 dec1-count", 0, "00000001");
do_op("00", "000000", "00"); -- 2° decremento: prio 1->2
check_mem("7.0 dec2", 1, "00000110"); -- id=1, prio=2
do_op("00", "000000", "00"); -- 3° decremento: prio 2->3
check_mem("7.0 dec3", 1, "00000111"); -- id=1, prio=3
do_op("00", "000000", "00"); -- 4° decremento: prio 3->3 (saturazione)
check_mem("7.0 dec4-sat", 1, "00000111"); -- id=1, prio=3 (invariato)
check_mem("7.0 dec4-count", 0, "00000001"); -- count invariato
do_op("00", "000000", "00"); -- 5° decremento: ancora saturazione
check_mem("7.0 dec5-sat", 1, "00000111"); -- id=1, prio=3 (ancora invariato)
report "Test 7.0 OK: saturazione progressiva 0->1->2->3->3->3";
-- 7.0b: Decremento ripetuto su lista con task a priorità miste
-- Verifica che ogni task segua la propria traiettoria di saturazione
-- indipendentemente dagli altri
clear_ram;
do_reset;
do_op("10", "000001", "00"); -- id=1, prio=0
do_op("10", "000010", "01"); -- id=2, prio=1
do_op("10", "000011", "10"); -- id=3, prio=2
do_op("10", "000100", "11"); -- id=4, prio=3
do_op("00", "000000", "00"); -- 1° decremento
-- id=1: 0->1, id=2: 1->2, id=3: 2->3, id=4: 3->3 (sat)
-- ATTENZIONE: dopo il decremento l'ORDINE rimane invariato (no riordino)
-- quindi la struttura fisica della lista NON cambia posizione
check_mem("7.0b dec1-pos1", 1, "00000101"); -- id=1, prio=1
check_mem("7.0b dec1-pos2", 2, "00001010"); -- id=2, prio=2
check_mem("7.0b dec1-pos3", 3, "00001111"); -- id=3, prio=3
check_mem("7.0b dec1-pos4", 4, "00010011"); -- id=4, prio=3 (saturato)
do_op("00", "000000", "00"); -- 2° decremento
-- id=1: 1->2, id=2: 2->3, id=3: 3->3 (sat), id=4: 3->3 (sat)
check_mem("7.0b dec2-pos1", 1, "00000110"); -- id=1, prio=2
check_mem("7.0b dec2-pos2", 2, "00001011"); -- id=2, prio=3
check_mem("7.0b dec2-pos3", 3, "00001111"); -- id=3, prio=3 (saturato)
check_mem("7.0b dec2-pos4", 4, "00010011"); -- id=4, prio=3 (saturato)
do_op("00", "000000", "00"); -- 3° decremento: tutti saturano a 3
check_mem("7.0b dec3-pos1", 1, "00000111"); -- id=1, prio=3
check_mem("7.0b dec3-pos2", 2, "00001011"); -- id=2, prio=3
check_mem("7.0b dec3-pos3", 3, "00001111"); -- id=3, prio=3
check_mem("7.0b dec3-pos4", 4, "00010011"); -- id=4, prio=3
report "Test 7.0b OK: decremento ripetuto su prio miste, saturazione indipendente";
-- 7.1: Decremento su lista con un solo task
clear_ram;
do_reset;
do_op("10", "001111", "01"); -- id=15, prio=1
do_op("00", "000000", "00"); -- decremento
check_mem("7.1 count", 0, "00000001");
check_mem("7.1 pos1", 1, "00111110"); -- id=15, prio=2
do_op("00", "000000", "00"); -- secondo decremento
check_mem("7.1 pos1b", 1, "00111111"); -- id=15, prio=3
do_op("00", "000000", "00"); -- terzo decremento: satura
check_mem("7.1 pos1c", 1, "00111111"); -- id=15, prio=3 (invariato)
report "Test 7.1 OK: decremento su lista con un solo task";
-- 7.2: Decrementa poi Svuota
-- (ordine inverso rispetto al test 4.3 che fa svuota->decrementa)
clear_ram;
do_reset;
do_op("10", "000001", "01");
do_op("10", "000010", "10");
do_op("00", "000000", "00"); -- decremento: modifica i task
-- Ora svuotiamo: deve azzerare solo il count
do_op("11", "000000", "00");
check_mem("7.2 count", 0, "00000000");
-- Rimozione su lista appena svuotata deve restituire 0
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("7.2 remove-after-clear", captured_id, "000000");
report "Test 7.2 OK: decrementa -> svuota -> rimozione su vuota";
-- ============================================================
-- GRUPPO 8: Insert dopo rimozione parziale e casi limite prio 0/3
-- ============================================================
report "=== GRUPPO 8: Insert dopo rimozione e casi limite ===";
-- 8.0: Insert -> Rimozione parziale -> Insert -> verifica ordinamento
--
-- Fase 1: inseriamo id=1(p0), id=2(p1), id=3(p2)
-- Fase 2: rimuoviamo 2 volte -> restano: id=2(p1), id=3(p2)
-- Fase 3: inseriamo id=10(p0) e id=20(p2)
-- Risultato atteso: id=10(p0), id=2(p1), id=3(p2), id=20(p2)
-- id=20 va in coda agli elementi con prio=2 (dopo id=3)
clear_ram;
do_reset;
do_op("10", "000001", "00"); -- id=1, prio=0
do_op("10", "000010", "01"); -- id=2, prio=1
do_op("10", "000011", "10"); -- id=3, prio=2
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("8.0 rem1", captured_id, "000001"); -- esce id=1
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("8.0 rem2", captured_id, "000010"); -- esce id=2
-- Ora la lista contiene solo id=3(p2)
check_mem("8.0 mid-count", 0, "00000001");
check_mem("8.0 mid-pos1", 1, "00001110"); -- id=3, prio=2
do_op("10", "001010", "00"); -- id=10, prio=0 -> va in testa
do_op("10", "010100", "10"); -- id=20, prio=2 -> va in coda agli p2
check_mem("8.0 final-count", 0, "00000011");
check_mem("8.0 final-pos1", 1, "00101000"); -- id=10, prio=0
check_mem("8.0 final-pos2", 2, "00001110"); -- id=3, prio=2
check_mem("8.0 final-pos3", 3, "01010010"); -- id=20, prio=2
report "Test 8.0 OK: insert dopo rimozione parziale, ordinamento corretto";
-- 8.1: Insert con priority=0 in lista dove tutti hanno prio=0
-- Il nuovo task deve andare sempre IN FONDO (FIFO tra pari priorità)
clear_ram;
do_reset;
do_op("10", "000001", "00"); -- id=1, prio=0
do_op("10", "000010", "00"); -- id=2, prio=0
do_op("10", "000011", "00"); -- id=3, prio=0 -> va in fondo agli p=0
check_mem("8.1 count", 0, "00000011");
check_mem("8.1 pos1", 1, "00000100"); -- id=1, prio=0
check_mem("8.1 pos2", 2, "00001000"); -- id=2, prio=0
check_mem("8.1 pos3", 3, "00001100"); -- id=3, prio=0
-- Rimuoviamo per verificare l'ordine FIFO
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("8.1 rem1-fifo", captured_id, "000001"); -- esce il primo inserito
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("8.1 rem2-fifo", captured_id, "000010");
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("8.1 rem3-fifo", captured_id, "000011");
report "Test 8.1 OK: insert prio=0 in lista tutto-prio=0, FIFO rispettato";
-- 8.2: Insert con priority=3 in lista dove tutti hanno prio=3
-- Simmetrico all'8.1: il nuovo va sempre in fondo
clear_ram;
do_reset;
do_op("10", "000001", "11"); -- id=1, prio=3
do_op("10", "000010", "11"); -- id=2, prio=3
do_op("10", "000011", "11"); -- id=3, prio=3 -> va in fondo agli p=3
check_mem("8.2 count", 0, "00000011");
check_mem("8.2 pos1", 1, "00000111"); -- id=1, prio=3
check_mem("8.2 pos2", 2, "00001011"); -- id=2, prio=3
check_mem("8.2 pos3", 3, "00001111"); -- id=3, prio=3
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("8.2 rem1-fifo", captured_id, "000001");
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("8.2 rem2-fifo", captured_id, "000010");
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("8.2 rem3-fifo", captured_id, "000011");
report "Test 8.2 OK: insert prio=3 in lista tutto-prio=3, FIFO rispettato";
-- ============================================================
-- GRUPPO 9: Verifica o_task_id per OP != 01
-- ============================================================
-- La specifica richiede che o_task_id sia valido (= ID estratto) quando
-- DONE=1 per OP=01. Per le altre operazioni, il modulo non estrae nessun
-- task e quindi o_task_id deve valere 0x00 al momento di DONE=1.
-- Questi test verificano che non ci siano "fughe" di valori spuri.
report "=== GRUPPO 9: o_task_id per OP diverso da 01 ===";
-- 9.0: OP=10 (insert) -> o_task_id deve essere 000000 a DONE=1
clear_ram;
do_reset;
wait until falling_edge(tb_clk);
tb_op <= "10";
tb_i_task_id <= "001111"; -- id=15
tb_task_priority <= "01";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("9.0 insert-task-id", "000000");
tb_start <= '0';
wait until falling_edge(tb_done);
report "Test 9.0 OK: OP=10 -> o_task_id=0 quando DONE=1";
-- 9.1: OP=00 (decremento) -> o_task_id deve essere 000000 a DONE=1
clear_ram;
do_reset;
do_op("10", "001111", "01"); -- prima inseriamo qualcosa
wait until falling_edge(tb_clk);
tb_op <= "00";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("9.1 decrement-task-id", "000000");
tb_start <= '0';
wait until falling_edge(tb_done);
report "Test 9.1 OK: OP=00 -> o_task_id=0 quando DONE=1";
-- 9.2: OP=11 (svuota) -> o_task_id deve essere 000000 a DONE=1
clear_ram;
do_reset;
do_op("10", "001111", "01");
wait until falling_edge(tb_clk);
tb_op <= "11";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("9.2 clear-task-id", "000000");
tb_start <= '0';
wait until falling_edge(tb_done);
report "Test 9.2 OK: OP=11 -> o_task_id=0 quando DONE=1";
-- 9.3: OP=01 su lista vuota -> o_task_id deve essere 000000 a DONE=1
clear_ram;
do_reset;
wait until falling_edge(tb_clk);
tb_op <= "01";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("9.3 remove-empty-task-id", "000000");
tb_start <= '0';
wait until falling_edge(tb_done);
report "Test 9.3 OK: OP=01 lista vuota -> o_task_id=0 quando DONE=1";
-- ============================================================
-- GRUPPO 10: Protocollo START-DONE, reset avanzato e stress test
-- ============================================================
report "=== GRUPPO 10: Protocollo, reset avanzato, stress ===";
-- 10.0: Operazione immediata sul primo fronte disponibile dopo DONE->0
-- Verifica che il modulo accetti START=1 senza cicli di margine
-- tra la fine del reset e l'inizio dell'operazione.
clear_ram;
tb_start <= '0';
tb_rst <= '1';
wait for 100 ns;
tb_rst <= '0';
-- Aspettiamo DONE->0 e SUL FRONTE DI DISCESA STESSO alziamo START
wait until falling_edge(tb_done);
-- Non usiamo wait until falling_edge(tb_clk) extra: start immediato
tb_op <= "10";
tb_i_task_id <= "000111"; -- id=7
tb_task_priority <= "01";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("10.0 immediate-start-task-id", "000000"); -- non è OP=01
tb_start <= '0';
wait until falling_edge(tb_done);
check_mem("10.0 count", 0, "00000001");
check_mem("10.0 pos1", 1, "00011101"); -- id=7, prio=1
report "Test 10.0 OK: operazione immediata dopo DONE->0 post-reset";
-- 10.1: Sequenza lunga di stress
--
-- Fase A: insert 8 task con priorità miste
-- id=1 p=0, id=2 p=2, id=3 p=1, id=4 p=3,
-- id=5 p=0, id=6 p=2, id=7 p=1, id=8 p=3
-- Ordine atteso dopo tutti gli insert:
-- pos1: id=1 p=0 (00000100)
-- pos2: id=5 p=0 (00010100)
-- pos3: id=3 p=1 (00001101)
-- pos4: id=7 p=1 (00011101)
-- pos5: id=2 p=2 (00001010)
-- pos6: id=6 p=2 (00011010)
-- pos7: id=4 p=3 (00010011)
-- pos8: id=8 p=3 (00100011)
--
-- Fase B: 3 decrementi
-- Dopo 1° dec: p=0->1, p=1->2, p=2->3, p=3->3(sat)
-- Dopo 2° dec: p=1->2, p=2->3, p=3->3(sat)
-- Dopo 3° dec: p=2->3, p=3->3(sat) -> tutti a prio=3, ordine fisico invariato
--
-- Fase C: rimuovi 4 task -> escono i primi 4 nell'ordine fisico
-- esc1: id=1, esc2: id=5, esc3: id=3, esc4: id=7
-- Rimangono: id=2(p3), id=6(p3), id=4(p3), id=8(p3)
--
-- Fase D: re-insert 3 task: id=10 p=0, id=11 p=1, id=12 p=3
-- Ordine atteso: id=10(p0), id=11(p1), id=2(p3), id=6(p3),
-- id=4(p3), id=8(p3), id=12(p3)
--
-- Fase E: rimuovi fino a lista vuota, verifica count=0
clear_ram;
do_reset;
-- Fase A: insert
do_op("10", "000001", "00"); -- id=1, p=0
do_op("10", "000010", "10"); -- id=2, p=2
do_op("10", "000011", "01"); -- id=3, p=1
do_op("10", "000100", "11"); -- id=4, p=3
do_op("10", "000101", "00"); -- id=5, p=0
do_op("10", "000110", "10"); -- id=6, p=2
do_op("10", "000111", "01"); -- id=7, p=1
do_op("10", "001000", "11"); -- id=8, p=3
check_mem("10.1 phaseA count", 0, "00001000");
check_mem("10.1 phaseA pos1", 1, "00000100"); -- id=1, p=0
check_mem("10.1 phaseA pos2", 2, "00010100"); -- id=5, p=0
check_mem("10.1 phaseA pos3", 3, "00001101"); -- id=3, p=1
check_mem("10.1 phaseA pos4", 4, "00011101"); -- id=7, p=1
check_mem("10.1 phaseA pos5", 5, "00001010"); -- id=2, p=2
check_mem("10.1 phaseA pos6", 6, "00011010"); -- id=6, p=2
check_mem("10.1 phaseA pos7", 7, "00010011"); -- id=4, p=3
check_mem("10.1 phaseA pos8", 8, "00100011"); -- id=8, p=3
-- Fase B: 3 decrementi
do_op("00", "000000", "00"); -- 1° dec
do_op("00", "000000", "00"); -- 2° dec
do_op("00", "000000", "00"); -- 3° dec: tutti a p=3, ordine fisico invariato
check_mem("10.1 phaseB count", 0, "00001000");
check_mem("10.1 phaseB pos1", 1, "00000111"); -- id=1, p=3
check_mem("10.1 phaseB pos2", 2, "00010111"); -- id=5, p=3
check_mem("10.1 phaseB pos3", 3, "00001111"); -- id=3, p=3
check_mem("10.1 phaseB pos4", 4, "00011111"); -- id=7, p=3
check_mem("10.1 phaseB pos5", 5, "00001011"); -- id=2, p=3
check_mem("10.1 phaseB pos6", 6, "00011011"); -- id=6, p=3
check_mem("10.1 phaseB pos7", 7, "00010011"); -- id=4, p=3
check_mem("10.1 phaseB pos8", 8, "00100011"); -- id=8, p=3
-- Fase C: rimuovi 4 task
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseC rem1", captured_id, "000001"); -- id=1
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseC rem2", captured_id, "000101"); -- id=5
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseC rem3", captured_id, "000011"); -- id=3
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseC rem4", captured_id, "000111"); -- id=7
check_mem("10.1 phaseC count", 0, "00000100");
check_mem("10.1 phaseC pos1", 1, "00001011"); -- id=2, p=3
check_mem("10.1 phaseC pos2", 2, "00011011"); -- id=6, p=3
check_mem("10.1 phaseC pos3", 3, "00010011"); -- id=4, p=3
check_mem("10.1 phaseC pos4", 4, "00100011"); -- id=8, p=3
-- Fase D: re-insert 3 task
do_op("10", "001010", "00"); -- id=10, p=0 -> va in testa (p=0 < p=3)
do_op("10", "001011", "01"); -- id=11, p=1 -> va dopo id=10 (p=1 < p=3)
do_op("10", "001100", "11"); -- id=12, p=3 -> va in coda ai p=3
check_mem("10.1 phaseD count", 0, "00000111");
check_mem("10.1 phaseD pos1", 1, "00101000"); -- id=10, p=0
check_mem("10.1 phaseD pos2", 2, "00101101"); -- id=11, p=1
check_mem("10.1 phaseD pos3", 3, "00001011"); -- id=2, p=3
check_mem("10.1 phaseD pos4", 4, "00011011"); -- id=6, p=3
check_mem("10.1 phaseD pos5", 5, "00010011"); -- id=4, p=3
check_mem("10.1 phaseD pos6", 6, "00100011"); -- id=8, p=3
check_mem("10.1 phaseD pos7", 7, "00110011"); -- id=12, p=3
-- Fase E: rimuovi tutti fino a lista vuota verificando count
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseE rem1", captured_id, "001010"); -- id=10
check_mem("10.1 phaseE cnt1", 0, "00000110");
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseE rem2", captured_id, "001011"); -- id=11
check_mem("10.1 phaseE cnt2", 0, "00000101");
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseE rem3", captured_id, "000010"); -- id=2
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseE rem4", captured_id, "000110"); -- id=6
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseE rem5", captured_id, "000100"); -- id=4
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseE rem6", captured_id, "001000"); -- id=8
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseE rem7", captured_id, "001100"); -- id=12
check_mem("10.1 phaseE final-count", 0, "00000000"); -- lista vuota
-- Tentativo su lista vuota: deve restituire 0
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.1 phaseE empty-rem", captured_id, "000000");
report "Test 10.1 OK: stress test completo (insert/dec/remove/re-insert)";
-- 10.2: Reset dopo OP=11 (svuota poi reset)
clear_ram;
do_reset;
do_op("10", "000001", "01");
do_op("10", "000010", "10");
do_op("11", "000000", "00"); -- svuota
check_mem("10.2 pre-reset count", 0, "00000000");
-- Ora resettiamo
tb_start <= '0';
tb_rst <= '1';
wait for 100 ns;
assert tb_done = '1'
report "FAIL [10.2] DONE deve essere 1 durante reset post-svuota"
severity failure;
tb_rst <= '0';
wait until tb_done = '0';
wait until falling_edge(tb_clk);
check_mem("10.2 post-reset count", 0, "00000000");
-- Verifica che il modulo sia operativo: insert e rimozione corretti
do_op("10", "001111", "00"); -- id=15, p=0
check_mem("10.2 post-reset-insert count", 0, "00000001");
check_mem("10.2 post-reset-insert pos1", 1, "00111100"); -- id=15, p=0
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("10.2 post-reset-remove", captured_id, "001111"); -- id=15
check_mem("10.2 post-reset-remove count", 0, "00000000");
report "Test 10.2 OK: reset dopo svuota, modulo correttamente reinizializzato";
-- ============================================================
-- GRUPPO 11: OP=10 con ID=0 (condizione di errore da specifica)
-- ============================================================
-- La specifica definisce ID_TASK=0 come condizione di errore:
-- un inserimento con ID=0 deve essere ignorato, ma il protocollo
-- START-DONE deve comunque completarsi.
report "=== GRUPPO 11: Insert con ID=0 ===";
-- 11.0: Insert ID=0 in lista vuota -> ignorato, DONE arriva comunque
clear_ram;
do_reset;
do_op_capture("10", "000000", "01", captured_id);
check_captured_id("11.0 id0-task-id", captured_id, "000000");
check_mem("11.0 count", 0, "00000000");
check_mem("11.0 pos1", 1, "00000000");
report "Test 11.0 OK: insert ID=0 su lista vuota ignorato";
-- 11.1: Insert ID=0 in lista popolata -> lista invariata
clear_ram;
do_reset;
do_op("10", "000001", "00"); -- id=1, p=0
do_op("10", "000010", "01"); -- id=2, p=1
do_op("10", "000000", "00"); -- ID=0, prio massima: NON deve andare in testa
do_op("10", "000000", "11"); -- ID=0, prio minima: NON deve andare in coda
check_mem("11.1 count", 0, "00000010");
check_mem("11.1 pos1", 1, "00000100"); -- id=1, p=0
check_mem("11.1 pos2", 2, "00001001"); -- id=2, p=1
check_mem("11.1 pos3", 3, "00000000"); -- nessun task aggiunto
report "Test 11.1 OK: insert ID=0 su lista popolata ignorato";
-- ============================================================
-- GRUPPO 12: Capacita' massima (63 task)
-- ============================================================
-- L'errata del 24.02.2026 precisa che il limite e' 63 task (ID a
-- 6 bit, ID=0 escluso). Inseriamo TUTTI i 63 ID possibili con
-- prio = id mod 4, verifichiamo l'intera memoria, tentiamo
-- inserimenti oltre il limite (necessariamente duplicati) e
-- svuotiamo con 63 pop verificando l'ordine di estrazione.
report "=== GRUPPO 12: Capacita' massima 63 task ===";
-- Modello di riferimento: bucket per priorita' crescente,
-- FIFO (ordine di inserimento = id crescente) dentro ogni bucket
idx := 1;
for p in 0 to 3 loop
for id in 1 to 63 loop
if (id mod 4) = p then
exp_id(idx) := id;
exp_prio(idx) := p;
idx := idx + 1;
end if;
end loop;
end loop;
clear_ram;
do_reset;
-- 12.0: insert di tutti i 63 ID in ordine crescente, prio = id mod 4
for id in 1 to 63 loop
do_op("10",
std_logic_vector(to_unsigned(id, 6)),
std_logic_vector(to_unsigned(id mod 4, 2)));
end loop;
check_mem("12.0 count", 0, std_logic_vector(to_unsigned(63, 8)));
for pos in 1 to 63 loop
exp_byte := std_logic_vector(to_unsigned(exp_id(pos) * 4 + exp_prio(pos), 8));
check_mem("12.0 pos" & integer'image(pos), pos, exp_byte);
end loop;
report "Test 12.0 OK: 63 task inseriti, memoria completa verificata";
-- 12.1: inserimenti oltre il 63esimo: ogni ID possibile e' gia'
-- in lista -> tutti ignorati (dup in testa, in mezzo, in fondo)
do_op("10", "000001", "11"); -- dup ID=1
do_op("10", "100000", "00"); -- dup ID=32
do_op("10", "111111", "00"); -- dup ID=63
do_op("10", "000000", "00"); -- ID=0 a lista piena
check_mem("12.1 count", 0, std_logic_vector(to_unsigned(63, 8)));
for pos in 1 to 63 loop
exp_byte := std_logic_vector(to_unsigned(exp_id(pos) * 4 + exp_prio(pos), 8));
check_mem("12.1 pos" & integer'image(pos), pos, exp_byte);
end loop;
report "Test 12.1 OK: inserimenti oltre il 63esimo tutti ignorati";
-- 12.2: drain completo, 63 pop con verifica dell'ordine di estrazione
for pos in 1 to 63 loop
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("12.2 pop" & integer'image(pos),
captured_id,
std_logic_vector(to_unsigned(exp_id(pos), 6)));
end loop;
check_mem("12.2 count", 0, "00000000");
-- pop numero 64 su lista appena svuotata -> 0
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("12.2 pop-empty", captured_id, "000000");
report "Test 12.2 OK: drain di 63 task nell'ordine atteso";
-- ============================================================
-- GRUPPO 13: Memoria stale e duplicati ai bordi della lista
-- ============================================================
report "=== GRUPPO 13: Stale memory e duplicati ai bordi ===";
-- 13.0: Re-insert di un ID appena rimosso
-- Dopo il pop lo shift lascia una copia stale dell'ultimo
-- task oltre il count: lo scan duplicati NON deve vederla
-- e il re-insert dell'ID estratto deve riuscire.
clear_ram;
do_reset;
do_op("10", "000101", "01"); -- id=5, p=1
do_op("10", "000110", "10"); -- id=6, p=2
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("13.0 pop", captured_id, "000101"); -- esce id=5
-- mem: [1]=id6,p2 (count=1), [2] stale = id6,p2
do_op("10", "000101", "11"); -- re-insert id=5 con prio diversa
check_mem("13.0 count", 0, "00000010");
check_mem("13.0 pos1", 1, "00011010"); -- id=6, p=2
check_mem("13.0 pos2", 2, "00010111"); -- id=5, p=3 (sovrascrive la stale)
report "Test 13.0 OK: re-insert di ID appena rimosso";
-- 13.1: Duplicato in PRIMA posizione (i gruppi 0-10 testano solo
-- il dup in posizione centrale)
clear_ram;
do_reset;
do_op("10", "000001", "00"); -- id=1, p=0
do_op("10", "000010", "01"); -- id=2, p=1
do_op("10", "000011", "10"); -- id=3, p=2
do_op("10", "000001", "11"); -- dup del task in pos 1
check_mem("13.1 count", 0, "00000011");
check_mem("13.1 pos1", 1, "00000100");
check_mem("13.1 pos2", 2, "00001001");
check_mem("13.1 pos3", 3, "00001110");
check_mem("13.1 pos4", 4, "00000000");
report "Test 13.1 OK: duplicato in prima posizione ignorato";
-- 13.2: Duplicato in ULTIMA posizione (stessa lista del 13.1)
do_op("10", "000011", "00"); -- dup del task in pos 3 (ultima)
check_mem("13.2 count", 0, "00000011");
check_mem("13.2 pos1", 1, "00000100");
check_mem("13.2 pos2", 2, "00001001");
check_mem("13.2 pos3", 3, "00001110");
check_mem("13.2 pos4", 4, "00000000");
report "Test 13.2 OK: duplicato in ultima posizione ignorato";
-- 13.3: Clear poi re-insert dello STESSO ID
-- Il clear azzera solo il count: la cella task resta scritta
-- (stale). L'insert dello stesso ID non deve essere scambiato
-- per duplicato.
clear_ram;
do_reset;
do_op("10", "001001", "01"); -- id=9, p=1
do_op("11", "000000", "00"); -- clear: mem[1] resta stale con id=9
do_op("10", "001001", "10"); -- re-insert id=9, p=2: deve riuscire
check_mem("13.3 count", 0, "00000001");
check_mem("13.3 pos1", 1, "00100110"); -- id=9, p=2
report "Test 13.3 OK: re-insert stesso ID dopo clear";
-- ============================================================
-- GRUPPO 14: Reset asincrono avanzato
-- ============================================================
-- La specifica consente RESET "in qualsiasi momento
-- dell'esecuzione": qui lo applichiamo nei punti piu' scomodi.
-- Dopo ogni reset si verifica che addr 0 = 0 e che il modulo
-- sia di nuovo pienamente operativo.
report "=== GRUPPO 14: Reset asincrono avanzato ===";
-- 14.0: Reset a meta' dello shift di OP=01 (fase COPY del 1° shift)
clear_ram;
do_reset;
do_op("10", "000001", "00");
do_op("10", "000010", "01");
do_op("10", "000011", "10");
wait until falling_edge(tb_clk);
tb_op <= "01";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
for k in 1 to 6 loop
wait until rising_edge(tb_clk);
end loop;
wait until falling_edge(tb_clk);
tb_rst <= '1';
tb_start <= '0';
wait for 60 ns;
assert tb_done = '1'
report "FAIL [14.0] DONE deve essere 1 durante reset a meta' shift"
severity failure;
tb_rst <= '0';
wait until tb_done = '0';
wait until falling_edge(tb_clk);
check_mem("14.0 count", 0, "00000000");
do_op("10", "000111", "01");
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("14.0 op-after-reset", captured_id, "000111");
report "Test 14.0 OK: reset a meta' shift di OP=01";
-- 14.1: Reset nel ciclo della scrittura del count (fine OP=01)
-- Con 3 task i segnali di scrittura del count vengono
-- registrati al 12° fronte dopo il campionamento di START:
-- il reset arriva tra la registrazione e il commit in
-- memoria. S_RESET deve comunque lasciare addr 0 = 0.
clear_ram;
do_reset;
do_op("10", "000001", "00");
do_op("10", "000010", "01");
do_op("10", "000011", "10");
wait until falling_edge(tb_clk);
tb_op <= "01";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
for k in 1 to 12 loop
wait until rising_edge(tb_clk);
end loop;
tb_rst <= '1';
tb_start <= '0';
wait for 60 ns;
tb_rst <= '0';
wait until tb_done = '0';
wait until falling_edge(tb_clk);
check_mem("14.1 count", 0, "00000000");
do_op("10", "001000", "10");
check_mem("14.1 op-after-reset count", 0, "00000001");
check_mem("14.1 op-after-reset pos1", 1, "00100010"); -- id=8, p=2
report "Test 14.1 OK: reset durante la scrittura del count";
-- 14.2: Reset a meta' della scansione di OP=00
-- (durante il ciclo di commit della scrittura del 1° task)
clear_ram;
do_reset;
do_op("10", "000001", "00");
do_op("10", "000010", "01");
wait until falling_edge(tb_clk);
tb_op <= "00";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
for k in 1 to 4 loop
wait until rising_edge(tb_clk);
end loop;
wait until falling_edge(tb_clk);
tb_rst <= '1';
tb_start <= '0';
wait for 60 ns;
tb_rst <= '0';
wait until tb_done = '0';
wait until falling_edge(tb_clk);
check_mem("14.2 count", 0, "00000000");
do_op("10", "000101", "00");
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("14.2 op-after-reset", captured_id, "000101");
report "Test 14.2 OK: reset durante OP=00";
-- 14.3: Reset mentre DONE=1 (tra il completamento dell'operazione
-- e l'abbassamento di START)
clear_ram;
do_reset;
wait until falling_edge(tb_clk);
tb_op <= "10";
tb_i_task_id <= "000100"; -- id=4
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
tb_rst <= '1'; -- reset proprio mentre DONE=1
tb_start <= '0';
wait for 60 ns;
assert tb_done = '1'
report "FAIL [14.3] DONE deve restare 1 durante reset"
severity failure;
tb_rst <= '0';
wait until tb_done = '0';
wait until falling_edge(tb_clk);
check_mem("14.3 count", 0, "00000000"); -- lista di nuovo vuota
do_op("10", "000110", "11");
check_mem("14.3 op-after-reset count", 0, "00000001");
check_mem("14.3 op-after-reset pos1", 1, "00011011"); -- id=6, p=3
report "Test 14.3 OK: reset mentre DONE=1";
-- 14.4: Reset corto (7 ns, non allineato al clock)
-- RESET e' asincrono: anche un impulso piu' corto del
-- periodo e fuori fase deve reinizializzare il modulo.
clear_ram;
do_reset;
do_op("10", "000001", "01");
do_op("10", "000010", "10");
wait until falling_edge(tb_clk);
wait for 3 ns; -- disallinea dal fronte
tb_rst <= '1';
wait for 7 ns;
tb_rst <= '0';
wait until tb_done = '0';
wait until falling_edge(tb_clk);
check_mem("14.4 count", 0, "00000000");
do_op("10", "001100", "01");
do_op_capture("01", "000000", "00", captured_id);
check_captured_id("14.4 op-after-reset", captured_id, "001100");
report "Test 14.4 OK: reset corto non allineato";
-- ============================================================
-- GRUPPO 15: Protocollo START-DONE con abbassamento lento di START
-- ============================================================
-- La specifica NON garantisce che START scenda entro lo stesso
-- ciclo in cui DONE va a 1: dice solo che l'esterno lo riporta a 0
-- quando DONE e' 1, e che il modulo puo' abbassare DONE quando
-- START torna a 0. Qui START resta alto per 1-2 cicli interi dopo
-- DONE=1: il modulo NON deve rieseguire l'operazione.
--
-- NOTA: con l'RTL attuale (senza stato S_DONE che attende START=0)
-- questi test FALLISCONO: e' il comportamento da correggere.
report "=== GRUPPO 15: START lento dopo DONE=1 ===";
-- 15.0: Pop con START tenuto alto 1 ciclo extra -> NIENTE doppio pop
clear_ram;
do_reset;
do_op("10", "000001", "01"); -- id=1, p=1
do_op("10", "000010", "10"); -- id=2, p=2
wait until falling_edge(tb_clk);
tb_op <= "01";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
check_task_id("15.0 pop-id", "000001");
wait until rising_edge(tb_clk); -- START resta alto un ciclo intero
wait until falling_edge(tb_clk);
tb_start <= '0';
wait for 300 ns; -- tempo abbondante per un eventuale re-run
assert tb_done = '0'
report "FAIL [15.0] DONE non e' tornato a 0 dopo START=0"
severity failure;
check_mem("15.0 count", 0, "00000001"); -- UN solo pop: count=1
check_mem("15.0 pos1", 1, "00001010"); -- id=2, p=2 ancora in lista
report "Test 15.0 OK: nessun doppio pop con START lento";
-- 15.1: Age con START tenuto alto 1 ciclo extra -> NIENTE doppio age
clear_ram;
do_reset;
do_op("10", "000001", "00"); -- id=1, p=0
wait until falling_edge(tb_clk);
tb_op <= "00";
tb_i_task_id <= "000000";
tb_task_priority <= "00";
tb_start <= '1';
wait until rising_edge(tb_done);
wait until rising_edge(tb_clk);
wait until falling_edge(tb_clk);
tb_start <= '0';
wait for 300 ns;
assert tb_done = '0'
report "FAIL [15.1] DONE non e' tornato a 0 dopo START=0"
severity failure;
check_mem("15.1 pos1", 1, "00000101"); -- prio 0->1, NON 0->2
report "Test 15.1 OK: nessun doppio age con START lento";
-- 15.2: Insert con START tenuto alto 2 cicli extra
-- (un re-run qui sarebbe mascherato dal filtro duplicati:
-- il test verifica soprattutto la tenuta del protocollo DONE)
clear_ram;
do_reset;
wait until falling_edge(tb_clk);
tb_op <= "10";
tb_i_task_id <= "000111"; -- id=7
tb_task_priority <= "01";
tb_start <= '1';
wait until rising_edge(tb_done);
wait until rising_edge(tb_clk);
wait until rising_edge(tb_clk); -- 2 cicli extra
wait until falling_edge(tb_clk);
tb_start <= '0';
wait for 300 ns;
assert tb_done = '0'
report "FAIL [15.2] DONE non e' tornato a 0 dopo START=0"
severity failure;
check_mem("15.2 count", 0, "00000001");
check_mem("15.2 pos1", 1, "00011101"); -- id=7, p=1
report "Test 15.2 OK: insert con START tenuto 2 cicli extra";
-- ============================================================
-- Fine
-- ============================================================
assert false
report "======================================" & LF
& " Tutti i test edge case sono PASSATI " & LF
& "======================================"
severity failure;
end process;
end architecture;