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