-- ============================================================ -- 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;