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