Initial commit
This commit is contained in:
@@ -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;
|
||||
Binary file not shown.
Reference in New Issue
Block a user