-- ============================================================
-- TESTBENCH EDGE CASES - 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 in lista piena (63 task) -> 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
--   5.0  Insert -> Decremento -> Rimozione: verifica ID estratto corretto
--   5.1  Sequenza di insert con priorità miste -> verifica ordinamento completo
--
-- ============================================================

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;

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

        -- 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
        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 (usato per riempire la lista)
        -- I task avranno ID da start_id a start_id+n-1, tutti con la priorità indicata
        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;

    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
        -- Prima inseriamo un task, poi resettiamo a metà
        clear_ram;
        do_reset;
        -- Avviamo un inserimento...
        wait until falling_edge(tb_clk);
        tb_op            <= "10";
        tb_i_task_id     <= "000001";
        tb_task_priority <= "01";
        tb_start         <= '1';
        -- Aspettiamo 2 cicli (operazione non ancora completata)
        wait until rising_edge(tb_clk);
        wait until rising_edge(tb_clk);
        -- Reset asincrono!
        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");  -- task_id=1, priority=2
        -- Memoria attesa: [0x01]=1, [1]=0b00000110 = task_id=1,prio=2
        check_mem("1.0 count",  0, "00000001");
        check_mem("1.0 task",   1, "00000110");  -- 000001 & 10
        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");  -- id=2, prio=2
        do_op("10", "000011", "10");  -- id=3, prio=2
        do_op("10", "000001", "00");  -- id=1, prio=0 -> deve andare in testa
        -- Atteso: [1]=id1,prio0  [2]=id2,prio2  [3]=id3,prio2
        check_mem("1.1 count", 0, "00000011");
        check_mem("1.1 pos1",  1, "00000100");  -- 000001 & 00
        check_mem("1.1 pos2",  2, "00001010");  -- 000010 & 10
        check_mem("1.1 pos3",  3, "00001110");  -- 000011 & 10
        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");  -- id=1, prio=0
        do_op("10", "000010", "01");  -- id=2, prio=1
        do_op("10", "000011", "11");  -- id=3, prio=3 -> deve andare in fondo
        -- Atteso: [1]=id1,prio0  [2]=id2,prio1  [3]=id3,prio3
        check_mem("1.2 count", 0, "00000011");
        check_mem("1.2 pos1",  1, "00000100");  -- 000001 & 00
        check_mem("1.2 pos2",  2, "00001001");  -- 000010 & 01
        check_mem("1.2 pos3",  3, "00001111");  -- 000011 & 11
        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");  -- id=1, prio=1
        do_op("10", "000010", "01");  -- id=2, prio=1
        do_op("10", "000011", "01");  -- id=3, prio=1 -> va dopo id=2
        -- Atteso: [1]=id1,prio1  [2]=id2,prio1  [3]=id3,prio1
        check_mem("1.3 count", 0, "00000011");
        check_mem("1.3 pos1",  1, "00000101");  -- 000001 & 01
        check_mem("1.3 pos2",  2, "00001001");  -- 000010 & 01
        check_mem("1.3 pos3",  3, "00001101");  -- 000011 & 01
        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");  -- sempre in fondo
        check_mem("1.4 count", 0, "00000100");
        check_mem("1.4 pos1",  1, "00000110");  -- id=1, prio=2
        check_mem("1.4 pos2",  2, "00001010");  -- id=2, prio=2
        check_mem("1.4 pos3",  3, "00001110");  -- id=3, prio=2
        check_mem("1.4 pos4",  4, "00010010");  -- id=4, prio=2
        report "Test 1.4 OK: insert con tutti uguale prio -> sempre in fondo";

        -- 1.5: Insert in lista piena (63 task) -> operazione ignorata
        -- Riempiamo la lista con 63 task (ID da 1 a 63, tutti prio=01)
        clear_ram;
        do_reset;
        fill_list(63, 1, "01");
        check_mem("1.5 count-pre",  0, "00111111");  -- 63 task
        check_mem("1.5 first-pre",  1, "00000101");  -- id=1, prio=1
        check_mem("1.5 last-pre",  63, "11111101");  -- id=63, prio=1
        -- Tentiamo un inserimento aggiuntivo (deve essere ignorato)
        do_op("10", "000000", "00");  -- id=0 sarebbe errore, ma la lista è piena
        -- NB: id=0 non può esistere per specifica, ma qui verifichiamo solo
        --     che il count rimanga 63 e la memoria non cambi
        check_mem("1.5 count-post", 0, "00111111");  -- ancora 63
        check_mem("1.5 pos64",     64, "00000000");  -- nessuna scrittura oltre il limite
        report "Test 1.5 OK: insert in lista piena 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");  -- inserisce id=5, prio=1
        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");  -- deve restituire id=5
        tb_start <= '0';
        wait until falling_edge(tb_done);
        check_mem("2.1 post-count", 0, "00000000");  -- lista vuota
        report "Test 2.1 OK: rimozione unico task -> lista vuota, task_id corretto";

        -- 2.2: Rimozione da lista con tutti task alla stessa priorità
        --      -> rimuove il primo in ordine d'inserimento (FIFO tra pari priorità)
        clear_ram;
        do_reset;
        do_op("10", "000001", "10");  -- id=1
        do_op("10", "000010", "10");  -- id=2
        do_op("10", "000011", "10");  -- id=3
        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");  -- deve restituire id=1 (primo inserito)
        tb_start <= '0';
        wait until falling_edge(tb_done);
        check_mem("2.2 count", 0, "00000010");
        check_mem("2.2 pos1",  1, "00001010");  -- id=2 ora in prima posizione
        check_mem("2.2 pos2",  2, "00001110");  -- id=3
        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");  -- decremento su lista vuota
        check_mem("3.0 count", 0, "00000000");  -- invariato
        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");  -- id=1, prio=1
        do_op("10", "000010", "11");  -- id=2, prio=3 (già al minimo gerarchico)
        do_op("00", "000000", "00");  -- decremento
        -- id=1: 1->2   id=2: 3->3 (satura)
        check_mem("3.1 count", 0, "00000010");
        check_mem("3.1 pos1",  1, "00000110");  -- id=1, prio=2
        check_mem("3.1 pos2",  2, "00001011");  -- id=2, prio=3 (saturato)
        report "Test 3.1 OK: saturazione a priorità 3";

        -- 3.2: Lista con TUTTI i task già a priorità 3 -> nessuna modifica dei valori
        clear_ram;
        do_reset;
        do_op("10", "000001", "11");
        do_op("10", "000010", "11");
        do_op("10", "000011", "11");
        do_op("00", "000000", "00");  -- decremento: tutti saturano a 3
        check_mem("3.2 count", 0, "00000011");
        check_mem("3.2 pos1",  1, "00000111");  -- id=1, prio=3 (invariato)
        check_mem("3.2 pos2",  2, "00001011");  -- id=2, prio=3
        check_mem("3.2 pos3",  3, "00001111");  -- id=3, prio=3
        report "Test 3.2 OK: tutti a prio 3 -> nessuna modifica";

        -- 3.3: Mix priorità 2 e 3 dopo decremento:
        --      i "nuovi 3" (ex-2) vengono PRIMA dei "vecchi 3"
        --      (nessun riordino, basta modificare i bit di priorità)
        clear_ram;
        do_reset;
        do_op("10", "000001", "10");  -- id=1, prio=2  -> diventa 3 (nuovo 3)
        do_op("10", "000010", "10");  -- id=2, prio=2  -> diventa 3 (nuovo 3)
        do_op("10", "000011", "11");  -- id=3, prio=3  -> resta  3 (vecchio 3)
        do_op("10", "000100", "11");  -- id=4, prio=3  -> resta  3 (vecchio 3)
        do_op("00", "000000", "00");  -- decremento
        -- Dopo: ordine invariato, id=1 e id=2 prima di id=3 e id=4
        -- tutti con prio=3 nei bit, ma ordine fisico preservato
        check_mem("3.3 count", 0, "00000100");
        check_mem("3.3 pos1",  1, "00000111");  -- id=1, prio=3 (ex-2)
        check_mem("3.3 pos2",  2, "00001011");  -- id=2, prio=3 (ex-2)
        check_mem("3.3 pos3",  3, "00001111");  -- id=3, prio=3 (vecchio)
        check_mem("3.3 pos4",  4, "00010011");  -- id=4, prio=3 (vecchio)
        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");  -- svuota
        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");  -- svuota
        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");  -- svuota
        do_op("00", "000000", "00");  -- decremento su lista vuota
        check_mem("4.3 count", 0, "00000000");
        report "Test 4.3 OK: svuota poi decrementa -> nessun effetto";

        -- ============================================================
        -- GRUPPO 5: Sequenze composite
        -- ============================================================
        report "=== GRUPPO 5: Sequenze composite ===";

        -- 5.0: Insert -> Decremento -> Rimozione: verifica ID estratto
        --   Inseriamo: id=10 prio=0, id=20 prio=1, id=30 prio=2
        --   Decrementiamo: id=10 prio=1, id=20 prio=2, id=30 prio=3
        --   Rimuoviamo: deve uscire id=10 (ora ha prio=1, è il primo)
        clear_ram;
        do_reset;
        do_op("10", "001010", "00");  -- id=10, prio=0
        do_op("10", "010100", "01");  -- id=20, prio=1
        do_op("10", "011110", "10");  -- id=30, prio=2
        do_op("00", "000000", "00");  -- decremento
        check_mem("5.0 after-dec pos1", 1, "00101001");  -- id=10, prio=1
        check_mem("5.0 after-dec pos2", 2, "01010010");  -- id=20, prio=2
        check_mem("5.0 after-dec pos3", 3, "01111011");  -- id=30, prio=3
        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");  -- id=10
        tb_start <= '0';
        wait until falling_edge(tb_done);
        check_mem("5.0 count",  0, "00000010");
        check_mem("5.0 pos1",   1, "01010010");  -- id=20, prio=2
        check_mem("5.0 pos2",   2, "01111011");  -- id=30, prio=3
        report "Test 5.0 OK: insert->dec->remove, ID estratto corretto";

        -- 5.1: Sequenza con priorità miste -> verifica ordinamento completo
        --   id=5 prio=3, id=3 prio=1, id=7 prio=2, id=1 prio=0, id=9 prio=1
        --   Ordine atteso: id=1(p0), id=3(p1), id=9(p1), id=7(p2), id=5(p3)
        clear_ram;
        do_reset;
        do_op("10", "000101", "11");  -- id=5,  prio=3
        do_op("10", "000011", "01");  -- id=3,  prio=1
        do_op("10", "000111", "10");  -- id=7,  prio=2
        do_op("10", "000001", "00");  -- id=1,  prio=0
        do_op("10", "001001", "01");  -- id=9,  prio=1
        check_mem("5.1 count", 0, "00000101");
        check_mem("5.1 pos1",  1, "00000100");  -- id=1,  prio=0
        check_mem("5.1 pos2",  2, "00001101");  -- id=3,  prio=1
        check_mem("5.1 pos3",  3, "00100101");  -- id=9,  prio=1
        check_mem("5.1 pos4",  4, "00011110");  -- id=7,  prio=2
        check_mem("5.1 pos5",  5, "00010111");  -- id=5,  prio=3
        report "Test 5.1 OK: ordinamento completo con priorità miste";

        -- ============================================================
        -- Fine
        -- ============================================================
        assert false
            report "======================================" & LF
                   & " Tutti i test edge case sono PASSATI " & LF
                   & "======================================"
            severity failure;

    end process;

end architecture;