-----------------------------------------------------------------------
-- $Header: D:\usr\cvsroot/VHDL/lib/JBUS/src/tb_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.busmaster_types.all;

ENTITY tb_dmac IS
END tb_dmac;

ARCHITECTURE behavior OF tb_dmac IS 

	constant CLK_PERIOD				: time := 10 ns;
	constant DATA_WIDTH				: integer := 32;

	constant DMA_NUM_CHANNEL		: integer := 2;
	constant DMA_CH_WRITE			: integer := 0;
	constant DMA_CH_READ			: integer := 1;
	constant DMA_RW_CONF			: unsigned(DMA_NUM_CHANNEL-1 downto 0) := "01";

	signal CLK_O					: std_logic := '1';
	signal RST_O					: std_logic := '1';

	-- Master
	signal CYC_O					: std_logic;
	signal STB_O					: std_logic;
	signal WE_O						: std_logic;
	signal SEL_O					: unsigned(3 downto 0) := (others => '1');
	signal ACK_I					: std_logic := '0';
	signal MRDY_O					: std_logic := '1';
	signal SRDY_I					: std_logic := '0';
	signal ADDR_O					: unsigned(31 downto 0) := (others => '-');
	signal MDAT_I_64				: unsigned(63 downto 0) := (others => '-');
	signal MDAT_O_64				: unsigned(63 downto 0) := (others => '-');
	
	signal MDAT_I					: unsigned(31 downto 0) := (others => '-');
	signal MDAT_O					: unsigned(31 downto 0) := (others => '-');

	-- DMA controller
	signal dma_master_req			: unsigned(DMA_NUM_CHANNEL-1 downto 0);
	signal dma_master_gnt			: unsigned(DMA_NUM_CHANNEL-1 downto 0) := (others => '0');
	signal dma_req_rdy				: unsigned(DMA_NUM_CHANNEL-1 downto 0);
	signal dma_req_en				: unsigned(DMA_NUM_CHANNEL-1 downto 0) := (others => '0');
	signal dma_req_addr_auto_inc	: unsigned(DMA_NUM_CHANNEL-1 downto 0);
	type array32_t is array (0 to DMA_NUM_CHANNEL-1) of unsigned(31 downto 0);
	signal dma_req_addr				: array32_t;
	signal dma_req_num_xfers		: array32_t;

	signal dma_req_complete			: unsigned(DMA_NUM_CHANNEL-1 downto 0);
	signal dma_req_complete_ack		: unsigned(DMA_NUM_CHANNEL-1 downto 0) := (others => '0');

	signal dma_start					: unsigned(DMA_NUM_CHANNEL-1 downto 0);
	signal dma_active					: unsigned(DMA_NUM_CHANNEL-1 downto 0);
	signal dma_end						: unsigned(DMA_NUM_CHANNEL-1 downto 0);
	signal dma_bus_cmd_cycle_finished	: unsigned(DMA_NUM_CHANNEL-1 downto 0);
	signal dma_bus_cmd_cycle_en			: unsigned(DMA_NUM_CHANNEL-1 downto 0) := (others => '0');
	signal dma_bus_cmd_rdy				: unsigned(DMA_NUM_CHANNEL-1 downto 0);
	signal dma_bus_cmd_we				: unsigned(DMA_NUM_CHANNEL-1 downto 0) := (others => '0');
	type array_bus_cmd_t is array (0 to DMA_NUM_CHANNEL-1) of bm_cmd_t;
	signal dma_bus_cmd					: array_bus_cmd_t;

	signal	arb_idx					: natural range 0 to DMA_NUM_CHANNEL-1;

	-- BUS Master controller
	signal bus_din_re				: std_logic;
	signal bus_cmd_cycle_finished	: std_logic;
	signal bus_cmd_cycle_en			: std_logic := '0';
	signal bus_cmd_rdy				: std_logic;
	signal bus_cmd_we				: std_logic := '0';
	signal bus_cmd					: bm_cmd_t;
		
	signal bus_din_rdy				: std_logic;
	signal bus_din_vld				: std_logic := '0';
	signal bus_din					: unsigned(DATA_WIDTH-1 downto 0) := (others => '-');

	signal bus_dout					: unsigned(31 downto 0);
	signal bus_dout_vld				: std_logic;
	signal bus_dout_re				: std_logic := '0';

	signal rdy						: std_logic := '0';
	signal read_reg					: unsigned(31 downto 0) := (others => '-');

	signal data_gen_reg				: unsigned(31 downto 0) := (others => '-');
	signal data_gen_reg_inc			: std_logic;
	signal data_req					: std_logic;
	
	type dma_write_source_t is (DATA_GEN, READ_FIFO);
	signal dma_write_source : dma_write_source_t := DATA_GEN;

	-- Read FIFO
	signal read_fifo_re		: std_logic;
	signal read_fifo_we		: std_logic;
	signal read_fifo_full	: std_logic;
	signal read_fifo_empty	: std_logic;
	signal read_fifo_din	: unsigned(31 downto 0);
	signal read_fifo_dout	: unsigned(31 downto 0);

	type state_t is (IDLE, LA_SA, LA_SU, LU_SA, LU_SU, CC_SA, CC_SU);
	signal state : state_t := IDLE;
	
	type ram_t  is array (0 to 127) of unsigned(63 downto 0);

	signal ram64		: ram_t := 
	(
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",

		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",

		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		

		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",

		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",

		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000",
		X"0000_0000_0000_0000"
		
	);

BEGIN
	
	MDAT_I	<= MDAT_I_64(31 downto 0);
	MDAT_O_64	<= MDAT_O & MDAT_O;

arbiter_proc:
	process(CLK_O)
	begin
		if rising_edge(CLK_O) then
			if RST_O = '1' then
				dma_master_gnt	<= (others => '0');
				arb_idx	<= 0;
			elsif dma_master_req(arb_idx) = '0' then
				if arb_idx /= DMA_NUM_CHANNEL-1 then
					arb_idx	<= arb_idx + 1;
				else
					arb_idx	<= 0;
				end if;
			else	
				dma_master_gnt	<= (others => '0');
				dma_master_gnt(arb_idx)	<= '1';
			end if;	
		end if;
	end process;

arbiter_mux:
	process(dma_master_gnt, dma_bus_cmd, dma_bus_cmd_we, dma_bus_cmd_cycle_en)
	variable v_bus_cmd_we : std_logic;
	variable v_bus_cmd_cycle_en : std_logic;
	begin
		v_bus_cmd_we := '0';
		v_bus_cmd_cycle_en := '0';
		for i in 0 to DMA_NUM_CHANNEL-1 loop
			v_bus_cmd_we := v_bus_cmd_we or dma_bus_cmd_we(i);
			v_bus_cmd_cycle_en := v_bus_cmd_cycle_en or dma_bus_cmd_cycle_en(i);
			if dma_master_gnt(i) = '1' then
				bus_cmd <= dma_bus_cmd(i);
			end if;
		end loop;
		bus_cmd_we <= v_bus_cmd_we;
		bus_cmd_cycle_en <= v_bus_cmd_cycle_en;
end process;
				

gen_uut:
for i in 0 to DMA_NUM_CHANNEL-1 generate
	
	dma_bus_cmd_cycle_finished(i)	<= bus_cmd_cycle_finished;
	dma_bus_cmd_rdy(i) 				<= bus_cmd_rdy;

	inst_uut : entity work.dmac
	Generic map
	(
		DATA_WIDTH			=> 32,
		XFER_CHUNK_SIZE		=> 8
	)
	Port map
	(
		clk					=> CLK_O,
		rst					=> RST_O,

		master_req			=> dma_master_req(i),
		master_gnt			=> dma_master_gnt(i),

		req_rdy				=> dma_req_rdy(i),
		req_en				=> dma_req_en(i),
		req_rw				=> DMA_RW_CONF(i),
		req_addr_auto_inc	=> dma_req_addr_auto_inc(i),
		req_addr_start		=> dma_req_addr(i),
		req_num_xfers		=> dma_req_num_xfers(i),

		req_complete		=> dma_req_complete(i),
		req_complete_ack	=> dma_req_complete_ack(i),

		dma_start			=> dma_start(i),
		dma_active			=> dma_active(i),
		dma_end				=> dma_end(i),

		-- busmaster control
		bus_cyc_complete	=> dma_bus_cmd_cycle_finished(i),
		bus_cmd_rdy			=> dma_bus_cmd_rdy(i),
		bus_cmd_we			=> dma_bus_cmd_we(i),
		bus_cmd_cycle_en	=> dma_bus_cmd_cycle_en(i),

		-- busmaster command
		bus_cmd_out			=> dma_bus_cmd(i)
	);

end generate;

-- Read buffer
inst_DMA_READ_FIFO: entity work.fifo_sync
	GENERIC MAP
	(
		addr_width			=> 10,
		data_width			=> DATA_WIDTH,
		do_last_read_update	=> true
	)
	PORT MAP
	(
		rst			=>	RST_O,
		clk			=>	CLK_O,
		we			=>	read_fifo_we,
		re			=>	read_fifo_re,
		fifo_full	=>	read_fifo_full,
		fifo_empty	=>	read_fifo_empty,
		fifo_afull	=>	open,
		fifo_aempty	=>	open,
		data_w		=>  read_fifo_din,
		data_r		=>  read_fifo_dout
	);

inst_busmaster_sync : entity work.busmaster_sync
	GENERIC MAP
	(
		DATA_WIDTH					=> DATA_WIDTH,
		FIFO_DEPTH					=> 4
	)
	PORT MAP
	(
		-- System signals
		rst					=> RST_O,
		clk					=> CLK_O,

		cmd_cycle_finished	=> bus_cmd_cycle_finished,
		cmd_cycle_en		=> bus_cmd_cycle_en,
		
		cmd_rdy				=> bus_cmd_rdy,
		cmd_we				=> bus_cmd_we,
		cmd_in				=> bus_cmd,
		
		din_rdy				=> bus_din_rdy,
		din_we				=> bus_din_vld,
		din					=> bus_din,

		dout				=> bus_dout,
		dout_vld			=> bus_dout_vld,
		dout_re				=> bus_dout_re,

		-- JBUS Master signals
		CYC_O				=> CYC_O,
		STB_O				=> STB_O,
		WE_O				=> WE_O,
		SEL_O				=> SEL_O,
		ACK_I				=> ACK_I,
		SRDY_I				=> SRDY_I,
		MRDY_O				=> MRDY_O,
		ADDR_O				=> ADDR_O,
		MDAT_I				=> MDAT_I,
		MDAT_O				=> MDAT_O
	);

rdy_GEN: process
	begin
--		wait for 3*CLK_PERIOD/2;
		wait until rising_edge(CLK_O);
		rdy <= not rdy;
	end process;

CLK_GEN: process
	begin
		wait for CLK_PERIOD/2;
		CLK_O <= not CLK_O;
	end process;

-- Slave
	SRDY_I	<= CYC_O;
	
	bus_dout_re			<= '1' when dma_write_source = DATA_GEN else not read_fifo_full;
	bus_din_vld			<= dma_active(DMA_CH_WRITE) when dma_write_source = DATA_GEN else (dma_active(DMA_CH_WRITE) and not read_fifo_empty);
	bus_din				<= data_gen_reg when dma_write_source = DATA_GEN else read_fifo_dout;
	data_gen_reg_inc	<= (bus_din_rdy and dma_active(DMA_CH_WRITE)) when dma_write_source = DATA_GEN else '0';
	read_fifo_re		<= (bus_din_rdy and dma_active(DMA_CH_WRITE)) when dma_write_source = READ_FIFO else '0';
	read_fifo_we		<= bus_dout_vld;
	read_fifo_din		<= bus_dout;
	
	process(CLK_O)
	begin
		if rising_edge(CLK_O) then
			if RST_O = '1' then
				data_gen_reg <= X"BEEF_0000";
			elsif data_gen_reg_inc = '1' then
				data_gen_reg <= data_gen_reg + X"0000_0004";
			end if;
		end if;
	end process;
				

	process(CLK_O)
	variable res: unsigned(63 downto 0);
	variable ram_data : unsigned(63 downto 0);
	begin
		if rising_edge(CLK_O) then
			if RST_O = '1' then
			else
				ACK_I	<= '0';
				if (CYC_O and STB_O) = '1' then
					if WE_O = '0' then
						ACK_I	<= '1';
						ram_data := ram64(to_integer(ADDR_O(31 downto 3)));
						if ADDR_O(2) = '0' then
							res			:= ram_data;
						else
							res			:= ram_data(31 downto 0) & ram_data(63 downto 32);
						end if;
					else -- WE=1
						ram_data := ram64(to_integer(ADDR_O(31 downto 3)));
					--	ram_data := (others => '-');
						if ADDR_O(2) = '0' then
							if SEL_O = "11111111" then
								ram64(to_integer(ADDR_O(31 downto 3))) <= MDAT_O_64;
							elsif SEL_O = "00001111" then
								ram64(to_integer(ADDR_O(31 downto 3))) <= ram_data(63 downto 32) & MDAT_O_64(31 downto 0);
							elsif SEL_O = "11110000" then
								ram64(to_integer(ADDR_O(31 downto 3))) <= MDAT_O_64(63 downto 32) & ram_data(31 downto 0);
							end if;
						else
							if SEL_O = "11111111" then
								ram64(to_integer(ADDR_O(31 downto 3))) <= MDAT_O_64(31 downto 0) & MDAT_O_64(63 downto 32);
							elsif SEL_O = "00001111" then
								ram64(to_integer(ADDR_O(31 downto 3))) <= MDAT_O_64(31 downto 0) & ram_data(31 downto 0);
							elsif SEL_O = "11110000" then
								ram64(to_integer(ADDR_O(31 downto 3))) <= ram_data(63 downto 32) & MDAT_O_64(63 downto 32);
							end if;
						end if;
					end if;
				end if;
				MDAT_I_64	<= res;
			end if;
		end if;
	end process;

	process(CLK_O)
	begin
		if rising_edge(CLK_O) then
			if RST_O = '1' then
				read_reg	<= X"00000000";
			elsif bus_dout_vld = '1' then
				read_reg	<= bus_dout;
			end if;
		end if;
	end process;
	
	
-- Master
STIMULUS: process

	procedure dma_wait_xfer_complete(ch : integer) is
	begin
	
		dma_req_complete_ack(ch)	<= '1';

		wait until rising_edge(CLK_O) and dma_req_complete(ch) = '1';
		dma_req_complete_ack(ch)	<= '0';

	end procedure dma_wait_xfer_complete;

	procedure dma_request(ch : integer; addr_start, num_xfers : unsigned) is
	begin
	
		dma_req_addr_auto_inc(ch)	<= '1';
		dma_req_addr(ch)			<= addr_start;
		dma_req_num_xfers(ch)		<= num_xfers;

		dma_req_en(ch)				<= '1';

		wait until rising_edge(CLK_O) and dma_req_rdy(ch) = '1';
		dma_req_en(ch)			<= '0';

	end procedure dma_request;

	begin

		wait for 6*CLK_PERIOD;
		RST_O <= '0';
		wait for 60*CLK_PERIOD;
		wait until rising_edge(CLK_O);
		state <= IDLE;

		dma_request(DMA_CH_WRITE, X"00000000", X"00000040");
--		dma_wait_xfer_complete(DMA_CH_WRITE);
		dma_request(DMA_CH_READ, X"00000000", X"00000040");
		dma_wait_xfer_complete(DMA_CH_READ);
		dma_write_source	<= READ_FIFO;
		dma_request(DMA_CH_WRITE, X"00000100", X"00000040");
--		dma_wait_xfer_complete(DMA_CH_WRITE);
		
		state <= IDLE;
		
		wait; 
	
	end process;
	
END;
