Initial commit

This commit is contained in:
2026-06-12 20:37:03 +02:00
commit 259f6d5803
305 changed files with 32830 additions and 0 deletions
@@ -0,0 +1,620 @@
-- ============================================================
-- 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;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,351 @@
-- ============================================================
-- TESTBENCH TIMING - Progetto Reti Logiche 2025/2026
-- ============================================================
-- Verifica il numero di cicli di clock per ciascuna operazione.
--
-- Formule attese (calibrate sull'FSM con stato S_DONE, che aggiunge
-- 1 ciclo a fine operazione per garantire DONE dopo il commit in memoria):
-- OP=10 inserimento: stati = 6 + 2*max(N,1) + 3k
-- (N = task gia' in lista, scanditi dal controllo
-- duplicati; k = task spostati per fare posto)
-- OP=01 rimozione: stati = 6 + 3k (k = task spostati, lista vuota = 2)
-- OP=00 decremento: stati = 4 + 3n (n = task in lista)
--
-- Per ogni test viene stampato:
-- - il numero di cicli misurati
-- - il numero di cicli attesi
-- - PASS o FAIL
-- ============================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity project_tb_timing is
end project_tb_timing;
architecture timing_arch of project_tb_timing is
constant CLOCK_PERIOD : time := 20 ns;
signal tb_clk : std_logic := '0';
signal tb_rst : std_logic := '0';
signal tb_start : std_logic := '0';
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) := "00";
signal tb_op : std_logic_vector(1 downto 0) := "00";
signal tb_i_task_id : std_logic_vector(5 downto 0) := "000000";
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");
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;
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
);
tb_clk <= not tb_clk after CLOCK_PERIOD / 2;
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;
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
-- ============================================================
main : process
-- Cicli misurati e attesi
variable t_start : time;
variable cycles : integer;
variable expected : integer;
-- --------------------------------------------------------
-- Reset
-- --------------------------------------------------------
procedure do_reset is
begin
tb_start <= '0';
tb_rst <= '1';
wait for 100 ns;
tb_rst <= '0';
wait until tb_done = '0';
wait until falling_edge(tb_clk);
end procedure;
-- --------------------------------------------------------
-- Esegue un'operazione e misura i cicli dal primo fronte
-- di clock dopo START=1 fino al fronte che porta DONE=1
-- --------------------------------------------------------
procedure run_and_measure (
op : std_logic_vector(1 downto 0);
task_id : std_logic_vector(5 downto 0);
priority : std_logic_vector(1 downto 0);
exp : integer;
test_num : integer
) is
begin
wait until falling_edge(tb_clk);
tb_op <= op;
tb_i_task_id <= task_id;
tb_task_priority <= priority;
tb_start <= '1';
-- Il primo fronte di clock dopo START campiona l'ingresso
-- ed entra nel primo stato dell'operazione: inizia il conteggio
wait until rising_edge(tb_clk);
t_start := now;
wait until rising_edge(tb_done);
-- DONE viene registrato sul fronte: questo è l'ultimo stato
cycles := (now - t_start) / CLOCK_PERIOD;
expected := exp;
if cycles = expected then
report "[PASS] test=" & integer'image(test_num)
& " | cicli misurati=" & integer'image(cycles)
& " attesi=" & integer'image(expected);
else
report "[FAIL] test=" & integer'image(test_num)
& " | cicli misurati=" & integer'image(cycles)
& " attesi=" & integer'image(expected)
severity failure;
end if;
tb_start <= '0';
wait until falling_edge(tb_done);
end procedure;
-- --------------------------------------------------------
-- Inserisce un task senza misurarlo (solo per preparare la lista)
-- --------------------------------------------------------
procedure insert_silent (
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 <= "10";
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;
begin
memory_control <= '1';
wait for 50 ns;
-- ============================================================
-- OP=10: Inserimento -> stati attesi = 6 + 4k
-- ============================================================
report "==============================";
report "OP=10 Inserimento: 6 + 2*max(N,1) + 3k stati";
report "==============================";
-- N=0, k=0: lista vuota -> 6 + 2 + 0 = 8 stati
-- OP=10 N=0 k=0 (lista vuota)
do_reset;
run_and_measure("10", "000001", "01", 8, 1);
-- N=1, k=1: 1 task con priorità minore -> spostato -> 6 + 2 + 3 = 11
-- OP=10 N=1 k=1 (1 task spostato)
do_reset;
insert_silent("000001", "10");
run_and_measure("10", "000010", "01", 11, 2);
-- N=2, k=2: 2 task da spostare -> 6 + 4 + 6 = 16
-- OP=10 N=2 k=2 (2 task spostati)
do_reset;
insert_silent("000001", "10");
insert_silent("000010", "10");
run_and_measure("10", "000011", "01", 16, 3);
-- N=3, k=3: 3 task da spostare -> 6 + 6 + 9 = 21
-- OP=10 N=3 k=3 (3 task spostati)
do_reset;
insert_silent("000001", "10");
insert_silent("000010", "10");
insert_silent("000011", "10");
run_and_measure("10", "000100", "01", 21, 4);
-- N=2, k=0: inserimento in fondo, lista non vuota -> 6 + 4 + 0 = 10
-- OP=10 N=2 k=0 (inserimento in fondo)
do_reset;
insert_silent("000001", "00");
insert_silent("000010", "01");
run_and_measure("10", "000011", "11", 10, 5);
-- ============================================================
-- OP=01: Rimozione -> stati attesi = 5 + 3k (lista vuota = 1)
-- ============================================================
report "==============================";
report "OP=01 Rimozione: 6 + 3k stati (lista vuota = 2)";
report "==============================";
-- Lista vuota -> 2 stati (CHECK_NUMBER + S_DONE)
-- OP=01 lista vuota
do_reset;
run_and_measure("01", "000000", "00", 2, 6);
-- k=0: 1 solo task, nessuno spostamento -> 6 stati
-- OP=01 k=0 (1 task, nessuno spostamento)
do_reset;
insert_silent("000001", "01");
run_and_measure("01", "000000", "00", 6, 7);
-- k=1: 2 task, 1 da spostare -> 9 stati
-- OP=01 k=1 (1 task spostato)
do_reset;
insert_silent("000001", "01");
insert_silent("000010", "01");
run_and_measure("01", "000000", "00", 9, 8);
-- k=2: 3 task, 2 da spostare -> 12 stati
-- OP=01 k=2 (2 task spostati)
do_reset;
insert_silent("000001", "01");
insert_silent("000010", "01");
insert_silent("000011", "01");
run_and_measure("01", "000000", "00", 12, 9);
-- k=3: 4 task, 3 da spostare -> 15 stati
-- OP=01 k=3 (3 task spostati)
do_reset;
insert_silent("000001", "01");
insert_silent("000010", "01");
insert_silent("000011", "01");
insert_silent("000100", "01");
run_and_measure("01", "000000", "00", 15, 10);
-- ============================================================
-- OP=00: Decremento -> stati attesi = 3 + 3n
-- ============================================================
report "==============================";
report "OP=00 Decremento: 4 + 3n stati";
report "==============================";
-- n=0: lista vuota -> 4 stati
-- OP=00 n=0 (lista vuota)
do_reset;
run_and_measure("00", "000000", "00", 4, 11);
-- n=1 -> 7 stati
-- OP=00 n=1
do_reset;
insert_silent("000001", "01");
run_and_measure("00", "000000", "00", 7, 12);
-- n=2 -> 10 stati
-- OP=00 n=2
do_reset;
insert_silent("000001", "01");
insert_silent("000010", "10");
run_and_measure("00", "000000", "00", 10, 13);
-- n=3 -> 13 stati
-- OP=00 n=3
do_reset;
insert_silent("000001", "01");
insert_silent("000010", "10");
insert_silent("000011", "11");
run_and_measure("00", "000000", "00", 13, 14);
-- n=4 con saturazione (tutti prio=3) -> 4 + 3*4 = 16 stati
-- OP=00 n=4 (tutti gia prio=3, saturazione)
do_reset;
insert_silent("000001", "11");
insert_silent("000010", "11");
insert_silent("000011", "11");
insert_silent("000100", "11");
run_and_measure("00", "000000", "00", 16, 15);
-- ============================================================
-- Fine
-- ============================================================
assert false
report "Tutti i test di timing sono PASSATI"
severity failure;
end process;
end architecture;
@@ -0,0 +1,207 @@
-- TB EXAMPLE PFRL 2023-2024
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use std.textio.all;
entity project_tb is
end project_tb;
architecture project_tb_arch of project_tb is
constant CLOCK_PERIOD : time := 20 ns;
signal tb_clk : std_logic := '0';
signal tb_rst, tb_start, tb_done : std_logic;
signal tb_o_task_id : std_logic_vector(5 downto 0);
signal tb_task_priority, tb_op : std_logic_vector(1 downto 0);
signal tb_i_task_id : std_logic_vector(5 downto 0);
signal tb_o_mem_addr, exc_o_mem_addr, init_o_mem_addr : std_logic_vector(15 downto 0);
signal tb_o_mem_data, exc_o_mem_data, init_o_mem_data : std_logic_vector(7 downto 0);
signal tb_i_mem_data : std_logic_vector(7 downto 0);
signal tb_o_mem_we, tb_o_mem_en, exc_o_mem_we, exc_o_mem_en, init_o_mem_we, init_o_mem_en : std_logic;
type ram_type is array (65535 downto 0) of std_logic_vector(7 downto 0);
signal RAM : ram_type := (OTHERS => "00000000");
type scenario_config_type_t is record
task_id : std_logic_vector(5 downto 0);
task_priority : std_logic_vector(1 downto 0);
op : std_logic_vector(1 downto 0);
end record scenario_config_type_t;
constant SCENARIO_SIZE : integer := 9;
type scenario_config_type is array (0 to SCENARIO_SIZE-1) of scenario_config_type_t;
signal scenario_config : scenario_config_type := (
(task_id => "000001", task_priority => "01", op => "10"), -- Post memory: [00000001,00000101]
(task_id => "000000", task_priority => "00", op => "11"), -- Post memory: [00000000]
(task_id => "000001", task_priority => "01", op => "10"), -- Post memory: [00000001,00000101]
(task_id => "000010", task_priority => "01", op => "10"), -- Post memory: [00000010,00000101,00001001]
(task_id => "000011", task_priority => "10", op => "10"), -- Post memory: [00000011,00000101,00001001,00001110]
(task_id => "000100", task_priority => "00", op => "10"), -- Post memory: [00000100,00010000,00000101,00001001,00001110]
(task_id => "000000", task_priority => "00", op => "01"), -- Post memory: [00000011,00000101,00001001,00001110]
(task_id => "000000", task_priority => "00", op => "00"), -- Post memory: [00000011,00000110,00001010,00001111]
(task_id => "000000", task_priority => "00", op => "00") -- Post memory: [00000011,00000111,00001011,00001111]
);
type scenario_single_result_type is array (0 to 32) of std_logic_vector(7 downto 0);
type scenario_result_type is array (0 to 100) of scenario_single_result_type;
type int_array_t is array (0 to SCENARIO_SIZE - 1) of integer;
constant CHECK_SIZE_ARRAY : int_array_t := (
2,1,2,3,4,5,4,4,4
);
signal scenario_result : scenario_result_type := (
( "00000001", "00000101", others => "00000000"),
( "00000000", others => "00000000"),
( "00000001", "00000101", others => "00000000"),
( "00000010", "00000101", "00001001", others => "00000000"),
( "00000011", "00000101", "00001001", "00001110", others => "00000000"),
( "00000100", "00010000", "00000101", "00001001", "00001110", others => "00000000"),
( "00000011", "00000101", "00001001", "00001110", others => "00000000"),
( "00000011", "00000110", "00001010", "00001111", others => "00000000"),
( "00000011", "00000111", "00001011", "00001111", others => "00000000"),
others => (others => "00000000")
);
signal memory_control : std_logic := '0';
signal first_task_queue : std_logic_vector(5 downto 0);
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 generation
tb_clk <= not tb_clk after CLOCK_PERIOD/2;
-- Process related to the memory
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;
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
-- This is necessary for the testbench to work: we swap the memory
-- signals from the component to the testbench when needed.
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;
-- This process provides the correct scenario on the signal controlled by the TB
create_scenario : process
begin
wait for 50 ns;
-- Signal initialization and reset of the component
tb_start <= '0';
tb_rst <= '1';
-- Wait some time for the component to reset...
wait for 100 ns;
--assert tb_done = '1' report "TEST FALLITO o_done !=1 during reset" severity failure;
tb_rst <= '0';
memory_control <= '1'; -- Memory controlled by the component
wait until tb_done = '0';
assert RAM(0) = "00000000" report "TEST FALLITO @ OFFSET=0 expected=0 actual=" & integer'image(to_integer(unsigned(RAM(0)))) severity failure;
wait until falling_edge(tb_clk);
for i in 0 to SCENARIO_SIZE - 1 loop
if i > 0 then
-- Save top of the queue for later use
first_task_queue <= scenario_result(i-1)(1)(7 downto 2);
end if;
tb_op <= scenario_config(i).op;
tb_i_task_id <= scenario_config(i).task_id;
tb_task_priority <= scenario_config(i).task_priority;
tb_start <= '1';
wait until rising_edge(tb_done);
if scenario_config(i).op = "01" then
-- Check output task only if the operation was the task removal
assert first_task_queue = tb_o_task_id report "TEST FALLITO @ STEP=" & integer'image(i) & " expected task_id=" & integer'image(to_integer(unsigned(first_task_queue))) & " actual task_id=" & integer'image(to_integer(unsigned(tb_o_task_id))) ;
end if;
-- Check memory contents
for j in 0 to CHECK_SIZE_ARRAY(i) - 1 loop
assert RAM(j) = scenario_result(i)(j) report "TEST FALLITO @ STEP=" & integer'image(i) & " OFFSET=" & integer'image(j) & " expected=" & integer'image(to_integer(unsigned(scenario_result(i)(j)))) & " actual=" & integer'image(to_integer(unsigned(RAM(j)))) ;
end loop;
tb_start <= '0';
wait until falling_edge(tb_done);
wait until falling_edge(tb_clk);
report "Test step " & integer'image(i) & " OK.";
end loop;
assert false report "Simulation Ended! TEST PASSATO (EXAMPLE)" severity failure;
end process;
end architecture;
@@ -0,0 +1,407 @@
-- Progetto di reti logiche 2025/2026 - Aleandro Pagani
-- Scheduler di task con priorità. Gestisce una lista ordinata in memoria
-- esterna (mem[0]=contatore, mem[1..N]=task come ID&PRIORITY a 8 bit).
-- OP 00=invecchiamento, 01=rimozione, 10=inserimento, 11=svuotamento.
-- FSM a due processi (combinatorio + sincrono), reset asincrono.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity 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 project_reti_logiche;
architecture FSM of project_reti_logiche is
----------------------------------------------------------------
-- Stati della FSM (prefisso S_xx_ = stati dell'operazione "xx")
----------------------------------------------------------------
type state_type is (
-- Gestione del protocollo
S_IDLE, -- attesa di START, dispatch delle operazioni
S_RESET, -- reset: scrive 0 in mem[0]
S_DONE, -- DONE = 1 finché START non torna a 0
-- OP = "00": invecchiamento
S_00_READ, -- avvia la lettura del primo task (mem[1])
S_00_WAIT, -- attesa latenza di lettura
S_00_CHECK, -- fine lista? altrimenti incrementa/satura
S_00_GO_NEXT, -- avvia la lettura del task successivo
-- OP = "01": rimozione
S_01_CHECK_NUMBER, -- lista vuota? altrimenti avvia lettura di mem[1]
S_01_WAIT, -- attesa latenza di lettura
S_01_WRITE, -- salva l'ID estratto, avvia lettura di mem[2]
S_01_CHECK_END, -- fine lista? scrive il contatore decrementato
S_01_COPY, -- copia il task letto una posizione più su
S_01_GO_NEXT, -- avvia la lettura del task successivo
S_01_WAIT_FOR_COUNT, -- attesa della scrittura nel contatore
-- OP = "10": inserimento
S_10_PLACE_AT_START, -- scarta ID = 0, avvia la scansione duplicati
S_10_WAIT_FOR_CHECK, -- attesa latenza di lettura (fase scansione)
S_10_CHECK_ID, -- duplicato? prosegue scansione o passa all'inserimento
S_10_WAIT, -- attesa latenza di lettura (fase inserimento)
S_10_COMPARE, -- inserisce qui oppure sposta il task in basso
S_10_GO_NEXT, -- avvia la lettura del task precedente
S_10_UPDATE_COUNT, -- scrive il contatore incrementato
S_10_WAIT_FOR_COUNT, -- attesa della scrittura nel contatore
-- OP = "11": svuotamento
S_11_UPDATE_COUNT, -- scrive 0 in mem[0]
S_11_WAIT_FOR_COUNT -- attesa del commit della scrittura del contatore
);
----------------------------------------------------------------
-- Segnali interni (coppie current/next)
----------------------------------------------------------------
signal state : state_type;
signal next_state : state_type;
-- Indirizzo di memoria su cui la FSM sta lavorando (0 = contatore)
signal current_mem_addr : std_logic_vector(15 downto 0);
signal next_mem_addr : std_logic_vector(15 downto 0);
-- Copia locale del numero di task presenti in lista (mem[0])
signal current_task_count : std_logic_vector(7 downto 0);
signal next_task_count : std_logic_vector(7 downto 0);
-- ID estratto dall'ultima rimozione, presentato su o_task_id in S_DONE
signal current_popped_id : std_logic_vector(5 downto 0);
signal next_popped_id : std_logic_vector(5 downto 0);
----------------------------------------------------------------
-- Segnali di controllo calcolati dal processo combinatorio,
-- registrati sulle uscite (o_done, o_task_id, interfaccia memoria)
----------------------------------------------------------------
signal ctrl_done : std_logic;
signal ctrl_mem_en : std_logic;
signal ctrl_mem_we : std_logic;
signal ctrl_mem_data : std_logic_vector(7 downto 0);
signal ctrl_task_id : std_logic_vector(5 downto 0);
begin
----------------------------------------------------------------
-- PROCESSO COMBINATORIO
-- Calcola stato prossimo e segnali di controllo.
-- Le assegnazioni di default in testa coprono ogni ramo:
-- nessun latch inferito.
----------------------------------------------------------------
process (state, i_start, i_op, i_mem_data, i_task_id, i_task_priority,
current_task_count, current_mem_addr, current_popped_id)
begin
next_state <= state;
next_task_count <= current_task_count;
next_mem_addr <= current_mem_addr;
next_popped_id <= current_popped_id;
ctrl_done <= '0';
ctrl_mem_en <= '0';
ctrl_mem_we <= '0';
ctrl_mem_data <= (others => '0');
ctrl_task_id <= (others => '0');
case state is
------------------------------------------------------------
-- S_IDLE: attesa di START. Al dispatch azzera l'ID estratto,
-- così o_task_id varrà 0 in S_DONE per ogni operazione che
-- non sia una rimozione andata a buon fine.
------------------------------------------------------------
when S_IDLE =>
if i_start = '1' then
next_popped_id <= (others => '0');
case i_op is
when "00" => next_state <= S_00_READ; -- invecchiamento
when "01" => next_state <= S_01_CHECK_NUMBER; -- rimozione
when "10" => next_state <= S_10_PLACE_AT_START; -- inserimento
when "11" => next_state <= S_11_UPDATE_COUNT; -- svuotamento
when others => null;
end case;
end if;
------------------------------------------------------------
-- S_RESET: dopo un reset la lista deve risultare vuota.
-- Scrive 0 in mem[0] tenendo DONE a 1.
------------------------------------------------------------
when S_RESET =>
next_state <= S_DONE;
next_task_count <= (others => '0');
next_mem_addr <= (others => '0');
next_popped_id <= (others => '0');
ctrl_done <= '1';
ctrl_mem_en <= '1';
ctrl_mem_we <= '1';
ctrl_mem_data <= (others => '0');
------------------------------------------------------------
-- S_DONE: alza DONE e lo tiene a 1 finché START non torna a 0,
-- come richiesto dal protocollo di hand-shake.
-- DONE viene asserito qui, un ciclo dopo lo stato terminale
-- dell'operazione: sale quindi sempre dopo il commit in
-- memoria dell'ultima scrittura, e chi campiona la memoria
-- sul fronte di DONE legge dati già aggiornati.
-- o_task_id è valido per tutta la finestra DONE = 1.
------------------------------------------------------------
when S_DONE =>
if i_start = '1' then
ctrl_done <= '1';
ctrl_task_id <= current_popped_id;
else
next_state <= S_IDLE;
end if;
------------------------------------------------------------
-- OP = "00" (invecchiamento)
-- Scorre la lista da mem[1] a mem[N]. Per ogni task con
-- priorità < 3 riscrive l'intero byte incrementato di 1:
-- equivale a incrementare i soli 2 bit di priorità, perché
-- la scrittura avviene solo se la priorità non è "11" e il
-- riporto non può quindi mai propagarsi nei bit dell'ID.
-- I task già a priorità 3 non vengono riscritti (saturazione).
------------------------------------------------------------
when S_00_READ =>
next_state <= S_00_WAIT;
next_mem_addr <= x"0001";
ctrl_mem_en <= '1';
when S_00_WAIT =>
next_state <= S_00_CHECK;
when S_00_CHECK =>
if unsigned(current_mem_addr) = resize(unsigned(current_task_count), 16) + 1 then
next_state <= S_DONE;
elsif i_mem_data(1 downto 0) /= "11" then
next_state <= S_00_GO_NEXT;
ctrl_mem_en <= '1';
ctrl_mem_we <= '1';
ctrl_mem_data <= std_logic_vector(unsigned(i_mem_data) + 1);
else
next_state <= S_00_GO_NEXT;
end if;
when S_00_GO_NEXT =>
next_state <= S_00_WAIT;
next_mem_addr <= std_logic_vector(unsigned(current_mem_addr) + 1);
ctrl_mem_en <= '1';
------------------------------------------------------------
-- OP = "01" (rimozione)
-- Se la lista è vuota termina subito (o_task_id resterà 0).
-- Altrimenti salva l'ID di mem[1], compatta la lista
-- copiando ogni task una posizione più in alto
-- (mem[j] -> mem[j-1] per j = 2...N) e infine scrive il
-- contatore decrementato in mem[0].
------------------------------------------------------------
when S_01_CHECK_NUMBER =>
if current_task_count = x"00" then
next_state <= S_DONE;
else
next_state <= S_01_WAIT;
next_mem_addr <= x"0001";
ctrl_mem_en <= '1';
end if;
when S_01_WAIT =>
next_state <= S_01_WRITE;
-- Salva l'ID del task estratto e avvia la lettura di mem[2].
when S_01_WRITE =>
next_state <= S_01_CHECK_END;
next_mem_addr <= std_logic_vector(unsigned(current_mem_addr) + 1);
ctrl_mem_en <= '1';
next_popped_id <= i_mem_data(7 downto 2);
-- Qui current_mem_addr vale j (lettura di mem[j] in corso).
-- Se j = N + 1 la compattazione è finita: scrive N - 1 in mem[0].
when S_01_CHECK_END =>
if unsigned(current_mem_addr) = resize(unsigned(current_task_count), 16) + 1 then
next_state <= S_01_WAIT_FOR_COUNT;
next_task_count <= std_logic_vector(unsigned(current_task_count) - 1);
next_mem_addr <= x"0000";
ctrl_mem_en <= '1';
ctrl_mem_we <= '1';
ctrl_mem_data <= std_logic_vector(unsigned(current_task_count) - 1);
else
next_state <= S_01_COPY;
end if;
-- Scrive mem[j], appena letto, in mem[j-1].
when S_01_COPY =>
next_state <= S_01_GO_NEXT;
next_mem_addr <= std_logic_vector(unsigned(current_mem_addr) - 1);
ctrl_mem_en <= '1';
ctrl_mem_we <= '1';
ctrl_mem_data <= i_mem_data;
-- In S_01_COPY l'indirizzo è sceso a j-1 per la scrittura:
-- il +2 riporta la lettura su j+1, il task successivo.
when S_01_GO_NEXT =>
next_state <= S_01_CHECK_END;
next_mem_addr <= std_logic_vector(unsigned(current_mem_addr) + 2);
ctrl_mem_en <= '1';
-- Un ciclo di attesa: al suo termine la scrittura del
-- contatore è stata campionata dalla memoria.
when S_01_WAIT_FOR_COUNT =>
next_state <= S_DONE;
------------------------------------------------------------
-- OP = "10" (inserimento) - due fasi:
-- 1) scansione duplicati: legge mem[N]...mem[1]; se trova un
-- task con lo stesso ID termina senza modifiche;
-- 2) inserimento dal fondo: confronta il nuovo task con
-- mem[j] per j = N...1; finché il nuovo task ha priorità
-- migliore (valore minore) sposta mem[j] in mem[j+1],
-- altrimenti scrive il nuovo task in mem[j+1].
-- Il confronto ">=" colloca il nuovo task DOPO quelli di
-- pari priorità.
------------------------------------------------------------
-- Scarta ID = 0.
-- Con lista vuota (N = 0) la scansione legge mem[0], cioè il
-- contatore: il confronto in S_10_CHECK_ID non può dare un
-- falso duplicato, perché in quel caso il contatore vale 0 e
-- l'ID, già filtrato, non è mai 0.
when S_10_PLACE_AT_START =>
if i_task_id = "000000" then
next_state <= S_DONE;
else
next_state <= S_10_WAIT_FOR_CHECK;
next_mem_addr <= x"00" & current_task_count;
ctrl_mem_en <= '1';
end if;
when S_10_WAIT_FOR_CHECK =>
next_state <= S_10_CHECK_ID;
when S_10_CHECK_ID =>
if i_mem_data(7 downto 2) = i_task_id then
-- ID già presente: l'inserimento viene ignorato
next_state <= S_DONE;
elsif unsigned(current_mem_addr) <= 1 then
-- scansione completata: rilegge mem[N] e passa
-- alla fase di inserimento
next_state <= S_10_WAIT;
next_mem_addr <= x"00" & current_task_count;
ctrl_mem_en <= '1';
else
-- prosegue la scansione verso l'inizio della lista
next_state <= S_10_WAIT_FOR_CHECK;
next_mem_addr <= std_logic_vector(unsigned(current_mem_addr) - 1);
ctrl_mem_en <= '1';
end if;
when S_10_WAIT =>
next_state <= S_10_COMPARE;
-- Qui current_mem_addr vale j (mem[j] appena letto).
-- current_mem_addr = 0 significa che tutta la lista è stata spostata:
-- il nuovo task va in testa (mem[1]).
when S_10_COMPARE =>
if (current_mem_addr = x"0000") or
(unsigned(i_task_priority) >= unsigned(i_mem_data(1 downto 0))) then
-- posizione trovata: scrive il nuovo task in mem[j+1]
next_state <= S_10_UPDATE_COUNT;
next_mem_addr <= std_logic_vector(unsigned(current_mem_addr) + 1);
ctrl_mem_en <= '1';
ctrl_mem_we <= '1';
ctrl_mem_data <= i_task_id & i_task_priority;
else
-- il nuovo task deve stare più in alto: sposta
-- mem[j] in mem[j+1] e prosegue verso l'alto
next_state <= S_10_GO_NEXT;
next_mem_addr <= std_logic_vector(unsigned(current_mem_addr) + 1);
ctrl_mem_en <= '1';
ctrl_mem_we <= '1';
ctrl_mem_data <= i_mem_data;
end if;
-- In S_10_COMPARE l'indirizzo è salito a j+1 per la
-- scrittura: il -2 riporta la lettura su j-1, il task
-- precedente da confrontare.
when S_10_GO_NEXT =>
next_state <= S_10_WAIT;
next_mem_addr <= std_logic_vector(unsigned(current_mem_addr) - 2);
ctrl_mem_en <= '1';
when S_10_UPDATE_COUNT =>
next_state <= S_10_WAIT_FOR_COUNT;
next_task_count <= std_logic_vector(unsigned(current_task_count) + 1);
next_mem_addr <= x"0000";
ctrl_mem_en <= '1';
ctrl_mem_we <= '1';
ctrl_mem_data <= std_logic_vector(unsigned(current_task_count) + 1);
when S_10_WAIT_FOR_COUNT =>
next_state <= S_DONE;
------------------------------------------------------------
-- OP = "11" (svuotamento)
-- Scrive 0 in mem[0]: la lista risulta vuota, il contenuto
-- delle celle task può essere ignorato.
------------------------------------------------------------
when S_11_UPDATE_COUNT =>
next_state <= S_11_WAIT_FOR_COUNT;
next_task_count <= (others => '0');
next_mem_addr <= (others => '0');
ctrl_mem_en <= '1';
ctrl_mem_we <= '1';
ctrl_mem_data <= (others => '0');
when S_11_WAIT_FOR_COUNT =>
next_state <= S_DONE;
end case;
end process;
----------------------------------------------------------------
-- PROCESSO SINCRONO
-- Registra stato e uscite sul fronte di salita del clock.
-- Reset asincrono: porta la FSM in S_RESET con DONE = 1 e disabilita
-- temporaneamente la memoria.
----------------------------------------------------------------
process (i_clk, i_rst)
begin
if i_rst = '1' then
state <= S_RESET;
o_done <= '1';
o_mem_en <= '0';
o_mem_we <= '0';
elsif rising_edge(i_clk) then
state <= next_state;
current_mem_addr <= next_mem_addr;
current_task_count <= next_task_count;
current_popped_id <= next_popped_id;
o_done <= ctrl_done;
o_mem_en <= ctrl_mem_en;
o_mem_we <= ctrl_mem_we;
o_mem_addr <= next_mem_addr;
o_mem_data <= ctrl_mem_data;
o_task_id <= ctrl_task_id;
end if;
end process;
end FSM;