-----------------------------------------------------------------------
-- $Header: D:\usr\cvsroot/VHDL/lib/JBUS/src/dmac.vhd,v 1.2 2013/05/28 11:23:15 jens Exp $
-----------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

use work.utils_pkg.all;
use work.busmaster_types.all;

------------------------------------------------------------------
entity dmac is
	Generic
	(
		DATA_WIDTH			: natural := 32;
		XFER_CHUNK_SIZE		: natural := 512
	);
	Port 
	(
		clk					: in std_logic;
		rst					: in std_logic;

		master_req			: out std_logic;
		master_gnt			: in std_logic;

		dma_start			: out std_logic;
		dma_active			: out std_logic;
		dma_end				: out std_logic;

		req_rdy				: out std_logic;
		req_en				: in std_logic;
		req_rw				: in std_logic;
		req_addr_start		: in unsigned(31 downto 0);
		req_num_xfers		: in unsigned(31 downto 0);
		req_addr_auto_inc	: in std_logic;

		req_complete		: out std_logic;
		req_complete_ack	: in std_logic;

		-- busmaster control
		bus_cyc_complete	: in std_logic;
		bus_cmd_rdy			: in std_logic;
		bus_cmd_we			: out std_logic;
		bus_cmd_cycle_en	: out std_logic;

		-- busmaster command
		bus_cmd_out			: out bm_cmd_t
	);
	end dmac;

architecture Behavioral of dmac is
	
	signal start_en				: std_logic;
	signal finish_en			: std_logic;
	signal complete				: std_logic;
	signal addr_inc_en			: std_logic;
	signal addr					: unsigned(31 downto 0);
	signal rw					: std_logic;

	signal req_count_en			: std_logic;
	signal req_count			: unsigned(31 downto 0);
	signal req_count_next		: unsigned(32 downto 0);
	signal req_count_rdy		: std_logic;
	
	signal chunk_count_rst		: std_logic;
	signal chunk_count_en		: std_logic;
	signal chunk_count			: unsigned(31 downto 0);
	signal chunk_count_next		: unsigned(32 downto 0);
	signal chunk_count_rdy		: std_logic;

	type state_t is (INIT, READY, START, SETUP, XFER, FINISH, DONE);
	signal s, sn	: state_t;
	
begin

	bus_cmd_out.addr	<= addr;
	bus_cmd_out.sel		<= (others => '1');
	bus_cmd_out.rw		<= rw;
	req_complete		<= complete;

state_next:
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				s	<= INIT;
			else
				s	<= sn;
			end if;
		end if;
	end process;	

state:
	process(s, req_en, master_gnt, req_count_rdy, chunk_count_rdy, bus_cyc_complete, bus_cmd_rdy)
	begin
		
		bus_cmd_cycle_en	<= '0';
		bus_cmd_we			<= '0';
		start_en			<= '0';
		finish_en			<= '0';
		req_count_en		<= '0';
		req_rdy				<= '0';
		chunk_count_rst		<= '0';
		chunk_count_en		<= '0';
		master_req			<= '0';
		dma_start			<= '0';
		dma_active			<= '0';
		dma_end				<= '0';
		
		sn					<= s;
		case s is
			when INIT =>
				sn	<= READY;

			when READY =>
				req_rdy				<= '1';
				if req_en = '1' then
					start_en <= '1';
					sn	<= SETUP;
					end if;

			when SETUP =>
				dma_start			<= '1';
				sn					<= START;

			when START =>
				master_req			<= '1';
				chunk_count_rst		<= '1';
				if req_count_rdy = '1' then
					sn			<= READY;
				elsif master_gnt = '1' and bus_cyc_complete = '1' then
					sn	<= XFER;
				end if;
				
			when XFER =>
				dma_active			<= '1';
				master_req			<= '1';
				bus_cmd_we			<= '1';
				bus_cmd_cycle_en	<= '1';
				req_count_en		<= bus_cmd_rdy;
				chunk_count_en		<= bus_cmd_rdy;
				if req_count_rdy = '1' then
					sn	<= FINISH;
				elsif chunk_count_rdy = '1' then
					sn	<= START;
				end if;

			when FINISH =>
				master_req	<= '1';
				dma_end		<= '1';
				sn	<= DONE;

			when DONE =>
				master_req			<= '1';
				if bus_cyc_complete = '1' then
					sn			<= READY;
					finish_en	<= '1';
				end if;

			when others =>
				sn	<= READY;

		end case;
	end process;	

addr_gen:
	process(clk)
	begin
		if rising_edge(clk) then
			if start_en = '1' then
				rw				<= req_rw;
				addr			<= req_addr_start;
				addr_inc_en		<= req_addr_auto_inc;
			elsif req_count_en = '1' and addr_inc_en = '1' then
				addr			<= addr + DATA_WIDTH/8;
			end if;
		end if;
	end process;	

	req_count_next		<= ('0' & req_num_xfers) - 1 when start_en = '1' else ('0' & req_count) - 1;
	req_count_rdy		<= req_count_next(32);
	
req_counter:
	process(clk)
	begin
		if rising_edge(clk) then
			if start_en = '1' then
				req_count		<= req_count_next(31 downto 0);
			elsif req_count_en = '1' then
				if req_count_rdy = '0' then
					req_count		<= req_count_next(31 downto 0);
				end if;
			end if;
		end if;
	end process;	

	chunk_count_next	<= to_unsigned(XFER_CHUNK_SIZE, 33) - 1 when chunk_count_rst = '1' else ('0' & chunk_count) - 1;
	chunk_count_rdy		<= chunk_count_next(32);

chunk_counter:
	process(clk)
	begin
		if rising_edge(clk) then
			if chunk_count_rst = '1' then
				chunk_count		<= chunk_count_next(31 downto 0);
			elsif chunk_count_en = '1' then
				if chunk_count_rdy = '0' then
					chunk_count		<= chunk_count_next(31 downto 0);
				end if;
			end if;
		end if;
	end process;	

complete_register:
	process(clk)
	begin
		if rising_edge(clk) then
			if start_en = '1' or rst = '1' then
				complete		<= '0';
			elsif complete = '0' then
				if finish_en = '1' then
					complete		<= '1';
				elsif req_complete_ack = '1' then
					complete		<= '0';
				end if;
			end if;
		end if;
	end process;	

end Behavioral;
