339 lines
12 KiB
VHDL
339 lines
12 KiB
VHDL
-- Progetto di reti logiche 2025/2026 - Aleandro Pagani
|
|
|
|
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
|
|
type state_type is (
|
|
-- Gestione
|
|
S_IDLE,
|
|
S_RESET,
|
|
S_DONE,
|
|
|
|
-- OP = "00": Diminuzione priorita'
|
|
S_00_READ,
|
|
S_00_WAIT,
|
|
S_00_CHECK,
|
|
S_00_GO_NEXT,
|
|
|
|
-- OP = "01": Rimozione
|
|
S_01_CHECK_NUMBER,
|
|
S_01_WAIT,
|
|
S_01_WRITE,
|
|
S_01_CHECK_END,
|
|
S_01_COPY,
|
|
S_01_GO_NEXT,
|
|
S_01_WAIT_FOR_COUNT,
|
|
|
|
-- OP = "10": Inserimento
|
|
S_10_PLACE_AT_START,
|
|
S_10_WAIT_FOR_CHECK,
|
|
S_10_CHECK_ID,
|
|
S_10_WAIT,
|
|
S_10_COMPARE,
|
|
S_10_GO_NEXT,
|
|
S_10_UPDATE_COUNT,
|
|
S_10_WAIT_FOR_COUNT,
|
|
|
|
-- OP = "11": Svuotamento
|
|
S_11_UPDATE_COUNT,
|
|
S_11_WAIT_FOR_COUNT
|
|
);
|
|
|
|
|
|
-- SEGNALI
|
|
signal state: state_type;
|
|
signal next_state: state_type;
|
|
|
|
signal current_mem_addr: std_logic_vector(15 downto 0);
|
|
signal next_mem_addr: std_logic_vector(15 downto 0);
|
|
|
|
signal current_task_count: std_logic_vector(7 downto 0);
|
|
signal next_task_count: std_logic_vector(7 downto 0);
|
|
|
|
signal current_popped_id: std_logic_vector(5 downto 0);
|
|
signal next_popped_id: std_logic_vector(5 downto 0);
|
|
|
|
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:
|
|
-- Vengono fatte delle assegnazioni di default, calcolato lo stato successivo e i segnali di controllo.
|
|
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: Quando riceve il segnale di start sleeziona il primo stato dell'operazione.
|
|
when S_IDLE =>
|
|
if i_start = '1' then
|
|
next_popped_id <= (others => '0');
|
|
case i_op is
|
|
when "00" => next_state <= S_00_READ; -- Diminuzione priorita'
|
|
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: 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.
|
|
when S_DONE =>
|
|
if i_start = '1' then
|
|
ctrl_done <= '1';
|
|
ctrl_task_id <= current_popped_id;
|
|
else
|
|
next_state <= S_IDLE;
|
|
end if;
|
|
|
|
|
|
-- DIMINUZIONE PRIORITA':
|
|
-- Scorre la lista da mem[1] a mem[N]: per ogni task valido incrementata di 1 la priorita'.
|
|
-- Dato che c'e' il controllo sulla priorita' massima '11', si puo' fare facilmente una somma
|
|
-- sull'intero contenuto del task.
|
|
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';
|
|
|
|
|
|
-- RIMOZIONE:
|
|
-- Se la lista è vuota termina subito, altrimenti salva l'ID di mem[1].
|
|
-- Successivamente, compatta la lista copiando ogni task una posizione più in alto e
|
|
-- decrementa di uno il contatore.
|
|
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;
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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';
|
|
|
|
when S_01_WAIT_FOR_COUNT =>
|
|
next_state <= S_DONE;
|
|
|
|
|
|
-- INSERIMENTO:
|
|
-- 1) Scansione duplicati: se viene trovato un task con lo stesso ID termina senza modifiche.
|
|
-- 2) Inserimento: confronta il task da inserire con mem[j] per j = N...1
|
|
-- - Se il nuovo task ha priorità maggiore (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à.
|
|
--
|
|
-- Nota:
|
|
-- Con lista vuota la scansione legge il contatore in mem[0], ma questo confronto non puo' dare falsi duplicati
|
|
-- perche' in quel caso il contatore vale 0 (il nuovo task non puo' avere id 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
|
|
next_state <= S_DONE;
|
|
elsif unsigned(current_mem_addr) <= 1 then
|
|
next_state <= S_10_WAIT;
|
|
next_mem_addr <= x"00" & current_task_count;
|
|
ctrl_mem_en <= '1';
|
|
else
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
|
|
-- SVUOTAMENTO:
|
|
-- Scrive 0 nel contatore, invalidando tutta la lista
|
|
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 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;
|