----------------------------------------------------------------------------------
-- Company: 
-- Engineer: KIEGAING EMMANUEL
-- 			 GAMOM ROLAND CHRISTIAN
-- 
-- Create Date:    04:39:43 05/21/2011 
-- Design Name: 
-- Module Name:    DMA_ARBITER - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
-- gestionnaire DMA pour le port secodaire de la RAM true dual port des mémoire 
-- privée de chaque noeud
-- Dependencies: 
-- 
-- Revision: 09/07/2012
-- Revision 1.01 - File Created
-- Revision 1.2 - 19/03/2013 
-- Correction de bugs et optimisation du nombre de cycles
--	Création d'un testbench					
-- Additional Comments: Ce module pourra être optimisé pour générer les adresses automatiquement
-- par le controleur DMA lorsqu'il est sollicité par la périphérie
--
----------------------------------------------------------------------------------
library IEEE;
library NocLib;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.numeric_std.all; 
use NocLib.CoreTypes.all;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity DMA_ARBITER is
    Port ( dma_rd_request : in  STD_LOGIC_VEctor (3 downto 0):=(others=>'0');
           data_wr_in : in  STD_LOGIC_VECTOR (Word-1 downto 0);
           data_rd_out : out  STD_LOGIC_VECTOR (Word-1 downto 0);
           address_rd : in  STD_LOGIC_VECTOR (ADRLEN-1 downto 0); --adresse pour lecture
           address_wr : in  STD_LOGIC_VECTOR (ADRLEN-1 downto 0); -- adresse pour écriture
           address_out_wr : out  STD_LOGIC_VECTOR (ADRLEN-1 downto 0); -- adresse de sortie du DMA Arbiter
			  address_out_rd : out  STD_LOGIC_VECTOR (ADRLEN-1 downto 0);
           ram_en : out  STD_LOGIC; -- 
           ram_we : out  STD_LOGIC;
           data_wr_mem : out  STD_LOGIC_VECTOR (Word-1 downto 0); -- donnée en sortie écriture
           data_rd_mem : in  STD_LOGIC_VECTOR (Word-1 downto 0); -- données en sortie lecture
           dma_wr_grant : out  STD_LOGIC_vector(3 downto 0):=(others=>'0'); -- autorisation d'écriture
			  hold_req       : out STD_Logic;  --requete vers application
			  hold_ack       : in  STD_Logic;  --autorisation par l'application
			  clk           : in std_logic;
			  reset		    : in std_logic;
           dma_rd_grant : out  STD_LOGIC_vector(3 downto 0):=(others=>'0');  -- autorisation de lecture
           dma_wr_request : in  STD_LOGIC_vector(3 downto 0):=(others=>'0')); -- demande de lecture
end DMA_ARBITER;

architecture Behavioral of DMA_ARBITER is
type fsm_states is (idle,wait_ack,arbiter_ack, writing,ReadWrite,reading);-- definition du type etat pour le codage des etats des fsm
signal dmac_state : fsm_states;

signal prio_rd,prio_wr:std_logic_vector(3 downto 0);--vecteur de bits de priorité
signal pri_rd,pri_wr : natural range 0 to 3; -- stocke le numéro du module qui a la priorité
signal dma_rd_logic : std_logic_vector(3 downto 0):=(others=>'0');
signal dma_wr_logic : std_logic_vector(3 downto 0):=(others=>'0');
signal dma_rd_latch,dma_wr_latch: std_logic_vector(3 downto 0):=(others=>'0');
signal dma_req_wr, dma_req_rd : std_logic;
signal last_grant_rd,last_grant_wr : STD_LOGIC_vector(3 downto 0):=(others=>'0');
signal tmp :std_logic_vector(3 downto 0):="0000";

begin
--==========================================================================
---le MUX qui contrôle les adresses est géré à l'extérieur du contrôleur DMA
dma_sync: process (clk)
begin
data_rd_out<=data_rd_mem ;
data_wr_mem <= data_wr_in;
address_out_rd <= address_rd;
address_out_wr <= address_wr;

end process;
--dma_rd_grant<= dma_rd_logic;
--dma_wr_grant<= dma_wr_logic;
dma_rd_grant<= dma_rd_latch;
dma_wr_grant<= dma_wr_latch;
--==========================================================================

--Déterminer si une requête HOLD doit être émise vers l'application pour la libération du 
--bus mémoire
tmp<=(others =>'0');

--for  i in 0 to 3 loop 
--dma_req_wr<=dma_req_wr or dma_wr_request(i);
--loop;

--for  i in 0 to 3 loop
-- dma_req_rd<=dma_req_rd or dma_rd_request(i);  -- construire le signal request vers l'extérieur
--loop;
hold_req<='1' when dma_req_rd='1' or dma_req_wr='1' else '0'; --envoyer un Hold vers l'application


-- machine à etat du DMAC
dmac_process : process(clk,reset)
--variable tmp : natural range 0 to 15;
variable tmp_rd,tmp_wr: std_logic_vector(3 downto 0);
variable  req_rd,req_wr : std_logic:='0' ;

begin 
	if rising_edge(clk) then 
	 if reset = '1' then
				dmac_state<= idle;
				
				prio_rd <="0001"; -- au debut priorité lecture au premier module
				prio_wr <="0010"; -- au debut priorité ecriture au deuxième module
				pri_rd<=0;--index de la priorité
				pri_wr<=1;
				req_rd:='0';
				req_wr:='0';
	  else 
				if req_wr='0'  then --tant que le Ctrl DMA est libre alors faire bouger la priorité en écriture
				case dma_wr_request is
				when "0001" => pri_wr<=0;
									prio_wr<="0001";
									req_wr:='1';
									dma_req_wr<= '1';
									dma_wr_logic <="0001";
				when "0010" => pri_wr<=1;
									prio_wr<="0010";
									req_wr:='1';
									dma_req_wr<= '1';
									dma_wr_logic <="0010";
				when "0100" => pri_wr<=2;
									prio_wr<="0100";
									req_wr:='1';
									dma_req_wr<= '1';
									dma_wr_logic <="0100";
				when "1000" => pri_wr<=3;
									prio_wr<="1000";
									req_wr:='1';
									dma_req_wr<= '1';
									dma_wr_logic <="1000";
									
				when others => 
									if req_rd='1' then --demande simultané de lecture et d'écriture
									
										if dma_rd_request(pri_rd)='1'  then
												pri_wr<=pri_rd;
												req_wr:='1';
												for i in 0 to 3 loop
												 if i=pri_rd then
												  prio_wr(i)<='1';
												  dma_wr_logic(i)<='1';
												 else
													prio_wr(i)<='0';
												   dma_wr_logic(i)<='0';
												 end if;
												end loop;
												
												
										end if;
										
									else --on affecte l'accè à la mémoire suivant le round robbin
										tmp_wr:= (dma_wr_request and prio_wr) ;
										dma_wr_logic <=tmp_wr;
										
										req_wr:= not(All_zeros(tmp_wr)); 
										dma_req_wr<= not(All_zeros(tmp_wr)); 
									end if;
									-- la priorité est circulaire et décale à chaque coup d'horloge
									if req_wr='0' then
										prio_wr<=rol_vec(prio_wr); 
										if pri_wr=3 then
											pri_wr<=0;
											Prio_wr<="0001";
										else
											pri_wr<=pri_wr+1;
										end if;
								 end if;
				end case;
				else   --req_wr='1'
				
				 --demande simultané de lecture et d'écriture
									
										if dma_rd_request(pri_wr)='1'  then
												pri_rd<=pri_wr;
												req_rd:='1';
												prio_rd<=prio_wr;
--												for i in 0 to 3 loop
--												 if i=pri_rd then
--												  prio_wr(i)<='1';
--												  dma_wr_logic(i)<='1';
--												 else
--													prio_wr(i)<='0';
--												   dma_wr_logic(i)<='0';
--												 end if;
--												end loop;					
												
										end if;

				end if;
				
				if req_rd='0'  then --tant que le Ctrl DMA est libre alors faire bouger la priorité
				case dma_rd_request is
				when "0001" => pri_rd<=0;
									prio_rd<="0001";
									req_rd:='1';
									dma_req_rd<= '1';
									dma_rd_logic<="0001";
				when "0010" => pri_rd<=1;
									prio_rd<="0010";
									req_rd:='1';
									dma_req_rd<= '1';
									dma_rd_logic<="0010";
									
				when "0100" => pri_rd<=2;
									prio_rd<="0100";
									req_rd:='1';
									dma_req_rd<= '1';
									dma_rd_logic<="0100";
									
				when "1000" => pri_rd<=3;
									prio_rd<="1000";
									req_rd:='1';
									dma_req_rd<= '1';
									dma_rd_logic<="1000";
									
				when others => 
									--si une demande survient de la part d'un composant déjà acquitté alors celui-ci est prioritaire
									if req_wr='1' then
									
										if dma_rd_request(pri_wr)='1'  then
												pri_rd<=pri_wr;
												req_rd:='1';
												for i in 0 to 3 loop
												 if i=pri_wr then
												  prio_rd(i)<='1';
												  dma_rd_logic(i)<='1';
												 else
													prio_rd(i)<='0';
												   dma_rd_logic(i)<='0';
												 end if;
												end loop;
												
												
										end if;
										
									else
										tmp_rd:= (dma_rd_request and prio_rd) ; 
										dma_rd_logic<=tmp_rd;
										dma_req_rd<= not (All_Zeros(tmp_rd));
										req_rd:= not (All_Zeros(tmp_rd));
									end if;
								-- la priorité est circulaire et décale à chaque coup d'horloge
									if req_rd='0' then
										prio_rd<=rol_vec(prio_rd);
										if pri_rd =3 then
											pri_rd<=0;
											prio_rd<="0001";
										else
											
											pri_rd<=pri_rd+1;
										end if;
									end if;
				end case;
				
			   end if;
	   
		  case dmac_state is
		   when idle => if req_rd='1' or req_wr='1' then
									dmac_state<=wait_ack; 
							else -- initialiser la priorité
									
									
									req_rd:='0';
									dma_req_rd<= '0';
									
									
									
									req_wr:='0';
									dma_req_wr<= '0';
									
									dma_wr_logic <="0000"; --aucun accès mémoire n'est accrédité
									dma_rd_logic <="0000";
											
							end if;
							
			when wait_ack =>  -- l'application doit autoriser l'utilisation de la RAM par le Core
						if dma_wr_request(pri_wr) ='1' or dma_rd_request(pri_rd) ='1' then
							if hold_ack='1' then
								--dmac_state<=arbiter_ack;
								if dma_wr_request(pri_wr) ='1' and dma_rd_request(pri_rd) ='1' then -- 
									dmac_state <= Readwrite;
								 elsif dma_wr_request(pri_wr) ='0' and dma_rd_request(pri_rd) ='1' then
											dmac_state <= Reading;
								elsif dma_wr_request(pri_wr) ='1' and dma_rd_request(pri_rd) ='0' then
											dmac_state <= Writing;
								else
											dmac_state<=Idle;
								end if;
							end if;
						else
							
							if dma_wr_request(pri_wr) ='0' then
							req_wr:='0';-- forcer une nouvelle recherche de priorité
							end if;
							
							if dma_rd_request(pri_rd) ='0' then
								req_rd:='0';
							end if;
							dmac_state<=Idle;
						end if;
						
				
			when arbiter_ack => 
--							if dma_wr_request(pri_wr) ='1' and dma_rd_request(pri_rd) ='1' then -- 
--									dmac_state <= Readwrite;
--								 elsif dma_wr_request(pri_wr) ='0' and dma_rd_request(pri_rd) ='1' then
--											dmac_state <= Reading;
--								elsif dma_wr_request(pri_wr) ='1' and dma_rd_request(pri_rd) ='0' then
--											dmac_state <= Writing;
--								else
											dmac_state<=Idle;
--								end if;
								-- cet état a été combiné au précédent pour gagner un cycle
			when Writing => if dma_wr_request(pri_wr) ='1' then
										if hold_ack='1' then 
										    -- on reste dans cet état
										 if dma_rd_request(pri_rd)='1' then
												dmac_state<=ReadWrite;
										 else
												req_rd:='0';
										 end if;
										else
											dmac_state<=wait_ack; 
										end if;
								 elsif dma_rd_request(pri_rd) ='1' then
										dmac_state <= Reading;
										req_wr:='0';
								 else
										dmac_state <= idle;
										req_wr:='0';
								end if;
			
			
			when ReadWrite => if hold_ack='1' then
										  if dma_wr_request(pri_wr)='1' and dma_rd_request(pri_rd)='1' then
												dmac_state <= ReadWrite;
											elsif dma_wr_request(pri_wr)='1' and dma_rd_request(pri_rd)='0' then 
												dmac_state <= Writing;
												req_rd:='0';
											elsif dma_wr_request(pri_wr)='0' and dma_rd_request(pri_rd)='1' then
												dmac_state <= Reading;
												req_wr:='0';
											else
												dmac_state <= Idle;
												req_rd:='0';req_wr:='0';
											end if;
									 else 
											if (dma_wr_request(pri_wr)='1') or (dma_rd_request(pri_rd)='1') then
													dmac_state <= Wait_ack;
											else
												dmac_state <= Idle;
											end if;
									end if;
			when Reading => 
								 if dma_rd_request(pri_rd) ='1' then
										if hold_ack='1' then 
										    -- on reste dans cet état
											 if dma_wr_request(pri_wr)='1' then
												dmac_state<=ReadWrite;
											else
												req_wr:='0';
											end if;
										else
											dmac_state<=wait_ack; 
										end if;
								 elsif dma_wr_request(pri_wr) ='1' then
										dmac_state<=writing;
										req_rd:='0';
								 else
										dmac_state <= idle;
										req_rd:='0';
								end if;
			when others => dmac_state <= idle;
		   end case;
	 end if;
	end if;
end process;
-- action_asociées
ol: process(dmac_state, address_rd, address_wr,pri_wr,pri_rd,prio_wr,prio_rd,dma_wr_request,dma_rd_request)

begin
--tester les requêtes DMA en lecture ou en écriture

case dmac_state is
		when idle => 
							ram_en <='0';
							ram_we <='0';
							dma_wr_latch <=(others=>'0');
							dma_rd_latch <=(others=>'0');
							--address_out_rd <= address_rd;
							--hold_req<='0';
		when wait_ack =>
							ram_en <='0';
							ram_we <='0';
							dma_wr_latch<=(others=>'0');
							dma_rd_latch<=(others=>'0');
							--address_out_rd <= (others=>'Z');
							--address_out_wr <= (others=>'Z');
							--hold_req<='1';
		when arbiter_ack =>  --à optimiser pour gagner un cycle
							ram_en <='0';
							ram_we <='0';
							dma_wr_latch<=(others=>'0');
							dma_rd_latch<=(others=>'0');
							--address_out_rd <= (others=>'Z');
							--address_out_wr <= (others=>'Z');
		when writing => -- ecriture dans la ram
							ram_en <='1';
							ram_we <='1';
							dma_wr_latch<=(others=>'0');
							dma_wr_latch(pri_wr) <='1';
							dma_rd_latch <=(others=>'0');
							--address_out_rd <= (others=>'Z');
							--address_out_wr <= address_wr;
							--hold_req<='1';							
							
		when ReadWrite => -- Lecture et écriture simultannée dans la Dual Port RAM
							ram_en <='1';
							ram_we <='1';
							dma_wr_latch<=(others=>'0');
							dma_wr_latch(pri_wr) <='1';
							dma_rd_latch<=(others=>'0');
							dma_rd_latch(pri_rd)<='1';
							
							--address_out_rd <= address_rd;
							--address_out_wr <= address_wr;
							--hold_req<='1';							
		when Reading => -- lecture dans la ram
							ram_en <='1';
							ram_we <='0';
							dma_wr_latch<=(others=>'0');
							dma_rd_latch<=(others=>'0');
							dma_rd_latch(pri_rd)<='1';
							
							--address_out_rd <= address_rd;
							--address_out_wr <= (others=>'Z');
							--hold_req<='1';
		when others => 
						   ram_en <='0';
							ram_we <='0';
							dma_wr_latch <=(others=>'0');
							dma_rd_latch <=(others=>'0');
							--address_out_rd <= (others=>'Z');
							--address_out_wr <= (others=>'Z');
							--hold_req<='0';
		end case;
end process;


end Behavioral;

