library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity dma is generic ( BITS : integer := 14 ); port ( clk : in std_logic; reset : in std_logic; rx_read : in std_logic; rx_write : in std_logic; rx_complete : in std_logic; rx_data : in std_logic_vector(63 downto 0); rx_address : in std_logic_vector(28 downto 0); tx_read : out std_logic; tx_write : out std_logic; tx_complete : out std_logic; tx_data : out std_logic_vector(63 downto 0); tx_address : out std_logic_vector(28 downto 0); tx_length : out std_logic_vector(8 downto 0); tx_accept : in std_logic; tx_done : in std_logic; port0_read : out std_logic; port0_rd_data : in std_logic_vector(63 downto 0); port0_rd_empty : in std_logic; port0_rd_count : in std_logic_vector(BITS downto 0); port0_write : out std_logic; port0_wr_data : out std_logic_vector(63 downto 0); port0_wr_full : in std_logic; port0_wr_count : in std_logic_vector(BITS downto 0); port1_read : out std_logic; port1_rd_data : in std_logic_vector(63 downto 0); port1_rd_empty : in std_logic; port1_rd_count : in std_logic_vector(BITS downto 0); port1_write : out std_logic; port1_wr_data : out std_logic_vector(63 downto 0); port1_wr_full : in std_logic; port1_wr_count : in std_logic_vector(BITS downto 0); port2_read : out std_logic; port2_rd_data : in std_logic_vector(63 downto 0); port2_rd_empty : in std_logic; port2_rd_count : in std_logic_vector(BITS downto 0); port2_write : out std_logic; port2_wr_data : out std_logic_vector(63 downto 0); port2_wr_full : in std_logic; port2_wr_count : in std_logic_vector(BITS downto 0); port3_read : out std_logic; port3_rd_data : in std_logic_vector(63 downto 0); port3_rd_empty : in std_logic; port3_rd_count : in std_logic_vector(BITS downto 0); port3_write : out std_logic; port3_wr_data : out std_logic_vector(63 downto 0); port3_wr_full : in std_logic; port3_wr_count : in std_logic_vector(BITS downto 0); max_read : in std_logic_vector(2 downto 0); max_write : in std_logic_vector(2 downto 0); interrupt : out std_logic; interrupt_rdy : in std_logic; mdio_command : out std_logic_vector(0 to 27); mdio_data : in std_logic_vector(0 to 15); mdio_enable : out std_logic; mdio_done : in std_logic; leds : out std_logic_vector(2 downto 0) ); end dma; architecture arch of dma is type state_t is (state_idle, state_tx_read, state_tx_write, state_tx_complete, state_rx_complete, state_dma_read, state_dma_write, state_interrupt, state_command, state_test_write_head, state_test_write, state_test_read_head, state_test_read, state_test_complete, state_mdio); signal state : state_t; signal fifo_read : std_logic; signal fifo_write : std_logic; signal fifo_din : std_logic_vector(63 downto 0); signal fifo_dout : std_logic_vector(63 downto 0); signal fifo_full : std_logic; signal fifo_empty : std_logic; signal fifo_count : std_logic_vector(BITS downto 0); component simple_fifo port ( clk : in std_logic; rst : in std_logic; rd_en : in std_logic; wr_en : in std_logic; din : in std_logic_vector(63 downto 0); dout : out std_logic_vector(63 downto 0); full : out std_logic; empty : out std_logic; data_count : out std_logic_vector(BITS downto 0) ); end component; signal cmd_read : std_logic; signal cmd_write : std_logic; signal cmd_din : std_logic_vector(63 downto 0); signal cmd_dout : std_logic_vector(63 downto 0); signal cmd_full : std_logic; signal cmd_empty : std_logic; component cmd_fifo port ( clk : in std_logic; rst : in std_logic; rd_en : in std_logic; wr_en : in std_logic; din : in std_logic_vector(63 downto 0); dout : out std_logic_vector(63 downto 0); full : out std_logic; empty : out std_logic ); end component; signal test_completion : std_logic_vector(63 downto 0); begin process (clk, reset) variable bar_addr : unsigned(BITS downto 0); variable dma_addr : unsigned(28 downto 0); variable length : unsigned(BITS downto 0); variable remain : unsigned(BITS downto 0); variable cycles : unsigned(BITS downto 0); variable backup_addr : unsigned(28 downto 0); variable backup_length : unsigned(BITS downto 0); begin if reset = '1' then state <= state_idle; tx_read <= '0'; tx_write <= '0'; tx_complete <= '0'; tx_data <= (others => '0'); tx_address <= (others => '0'); tx_length <= (others => '0'); fifo_read <= '0'; fifo_write <= '0'; fifo_din <= (others => '0'); interrupt <= '0'; cmd_read <= '0'; cmd_write <= '0'; cmd_din <= (others => '0'); test_completion <= (others => '0'); port0_read <= '0'; port0_write <= '0'; port0_wr_data <= (others => '0'); port1_read <= '0'; port1_write <= '0'; port1_wr_data <= (others => '0'); port2_read <= '0'; port2_write <= '0'; port2_wr_data <= (others => '0'); port3_read <= '0'; port3_write <= '0'; port3_wr_data <= (others => '0'); mdio_enable <= '0'; mdio_command <= (others => '0'); elsif rising_edge(clk) then fifo_read <= '0'; fifo_write <= '0'; fifo_din <= (others => '0'); cmd_read <= '0'; cmd_write <= '0'; cmd_din <= (others => '0'); port0_read <= '0'; port0_write <= '0'; port1_read <= '0'; port1_write <= '0'; port2_read <= '0'; port2_write <= '0'; port3_read <= '0'; port3_write <= '0'; -- bar read if state = state_idle and rx_read = '1' then state <= state_tx_complete; tx_complete <= '1'; tx_data <= (others => '0'); tx_address <= rx_address; bar_addr := unsigned(rx_address(BITS downto 0)); -- read fifo if bar_addr = 0 then if fifo_empty = '1' then tx_data <= (others => '1'); else tx_data <= fifo_dout; fifo_read <= '1'; end if; -- read command elsif bar_addr = 1 then if cmd_empty = '1' then tx_data <= (others => '1'); else tx_data <= cmd_dout; cmd_read <= '1'; end if; -- other elsif bar_addr = 3 then tx_data(BITS downto 0) <= fifo_count; elsif bar_addr = 4 then tx_data(2 downto 0) <= max_read; elsif bar_addr = 5 then tx_data(2 downto 0) <= max_write; elsif bar_addr = 6 then tx_data <= test_completion; -- read ports elsif bar_addr = 10 then if port0_rd_empty = '1' then tx_data <= (others => '1'); else tx_data <= port0_rd_data; port0_read <= '1'; end if; elsif bar_addr = 11 then if port1_rd_empty = '1' then tx_data <= (others => '1'); else tx_data <= port1_rd_data; port1_read <= '1'; end if; elsif bar_addr = 12 then if port2_rd_empty = '1' then tx_data <= (others => '1'); else tx_data <= port2_rd_data; port2_read <= '1'; end if; elsif bar_addr = 13 then if port3_rd_empty = '1' then tx_data <= (others => '1'); else tx_data <= port3_rd_data; port3_read <= '1'; end if; -- read counts elsif bar_addr = 20 then tx_data(BITS downto 0) <= port0_rd_count; elsif bar_addr = 21 then tx_data(BITS downto 0) <= port0_wr_count; elsif bar_addr = 22 then tx_data(BITS downto 0) <= port1_rd_count; elsif bar_addr = 23 then tx_data(BITS downto 0) <= port1_wr_count; elsif bar_addr = 24 then tx_data(BITS downto 0) <= port2_rd_count; elsif bar_addr = 25 then tx_data(BITS downto 0) <= port2_wr_count; elsif bar_addr = 26 then tx_data(BITS downto 0) <= port3_rd_count; elsif bar_addr = 27 then tx_data(BITS downto 0) <= port3_wr_count; -- read mdio elsif bar_addr = 30 then tx_data(15 downto 0) <= mdio_data; end if; elsif state = state_tx_complete then if tx_done = '1' then state <= state_idle; end if; -- bar write elsif state = state_idle and rx_write = '1' then bar_addr := unsigned(rx_address(BITS downto 0)); -- write fifo if bar_addr = 0 then if fifo_full = '0' then fifo_din <= rx_data; fifo_write <= '1'; end if; -- write command elsif bar_addr = 1 then if cmd_full = '0' then cmd_din <= rx_data; cmd_write <= '1'; end if; if rx_data(0) = '1' then state <= state_command; end if; -- write test elsif bar_addr = 2 then dma_addr := unsigned(rx_data(31 downto 3)); length := unsigned(rx_data(BITS+32 downto 32)); cycles := unsigned(rx_data(BITS+48 downto 48)); backup_addr := dma_addr; backup_length := length; if rx_data(1) = '0' then state <= state_test_read_head; else state <= state_test_write_head; end if; -- write ports elsif bar_addr = 10 then if port0_wr_full = '0' then port0_wr_data <= rx_data; port0_write <= '1'; end if; elsif bar_addr = 11 then if port1_wr_full = '0' then port1_wr_data <= rx_data; port1_write <= '1'; end if; elsif bar_addr = 12 then if port2_wr_full = '0' then port2_wr_data <= rx_data; port2_write <= '1'; end if; elsif bar_addr = 13 then if port3_wr_full = '0' then port3_wr_data <= rx_data; port3_write <= '1'; end if; -- write mdio elsif bar_addr = 30 then state <= state_mdio; mdio_enable <= '1'; mdio_command <= rx_data(27 downto 0); end if; -- mdio elsif state = state_mdio then if mdio_done = '1' then mdio_enable <= '0'; state <= state_interrupt; interrupt <= '1'; end if; -- test read elsif state = state_test_read_head then remain := 16 - resize(dma_addr(3 downto 0), remain'length); if max_read = "001" then remain := 32 - resize(dma_addr(4 downto 0), remain'length); elsif max_read = "010" then remain := 64 - resize(dma_addr(5 downto 0), remain'length); elsif max_read = "011" then remain := 128 - resize(dma_addr(6 downto 0), remain'length); elsif max_read = "100" then remain := 256 - resize(dma_addr(7 downto 0), remain'length); elsif max_read = "101" then remain := 512 - resize(dma_addr(8 downto 0), remain'length); end if; if remain > length then remain := length; end if; state <= state_test_read; tx_read <= '1'; tx_address <= std_logic_vector(dma_addr); tx_length <= std_logic_vector(remain(8 downto 0)); length := length - remain; dma_addr := dma_addr + remain; elsif state = state_test_read then if tx_done = '1' then state <= state_test_complete; end if; elsif state = state_test_complete then if rx_complete = '1' then test_completion <= rx_data; remain := remain - 1; if remain = 0 then if length > 0 then state <= state_test_read_head; elsif cycles > 1 then state <= state_test_read_head; dma_addr := backup_addr; length := backup_length; cycles := cycles - 1; else state <= state_interrupt; interrupt <= '1'; end if; end if; end if; -- test write elsif state = state_test_write_head then remain := 16 - resize(dma_addr(3 downto 0), remain'length); if max_write = "001" then remain := 32 - resize(dma_addr(4 downto 0), remain'length); elsif max_write = "010" then remain := 64 - resize(dma_addr(5 downto 0), remain'length); elsif max_write = "011" then remain := 128 - resize(dma_addr(6 downto 0), remain'length); elsif max_write = "100" then remain := 256 - resize(dma_addr(7 downto 0), remain'length); elsif max_write = "101" then remain := 512 - resize(dma_addr(8 downto 0), remain'length); end if; if remain > length then remain := length; end if; state <= state_test_write; tx_write <= '1'; tx_data <= x"FFEEDDCC00000000"; tx_address <= std_logic_vector(dma_addr); tx_length <= std_logic_vector(remain(8 downto 0)); length := length - remain; dma_addr := dma_addr + remain; elsif state = state_test_write then if tx_done = '1' then if length > 0 then state <= state_test_write_head; elsif cycles > 1 then state <= state_test_write_head; dma_addr := backup_addr; length := backup_length; cycles := cycles - 1; else state <= state_interrupt; interrupt <= '1'; end if; end if; -- command elsif state = state_command then if cmd_empty = '1' then state <= state_interrupt; interrupt <= '1'; else cmd_read <= '1'; if cmd_dout(1) = '0' then state <= state_dma_read; dma_addr := unsigned(cmd_dout(31 downto 3)); length := unsigned(cmd_dout(BITS+32 downto 32)); fifo_din <= std_logic_vector(resize(length, fifo_din'length)); fifo_write <= '1'; else state <= state_dma_write; dma_addr := unsigned(cmd_dout(31 downto 3)); length := unsigned(fifo_dout(BITS downto 0)) + 1; end if; end if; elsif state = state_interrupt then if interrupt_rdy = '1' then state <= state_idle; interrupt <= '0'; end if; -- dma read elsif state = state_dma_read then remain := 16 - resize(dma_addr(3 downto 0), remain'length); if max_read = "001" then remain := 32 - resize(dma_addr(4 downto 0), remain'length); elsif max_read = "010" then remain := 64 - resize(dma_addr(5 downto 0), remain'length); elsif max_read = "011" then remain := 128 - resize(dma_addr(6 downto 0), remain'length); elsif max_read = "100" then remain := 256 - resize(dma_addr(7 downto 0), remain'length); elsif max_read = "101" then remain := 512 - resize(dma_addr(8 downto 0), remain'length); end if; if remain > length then remain := length; end if; state <= state_tx_read; tx_read <= '1'; tx_address <= std_logic_vector(dma_addr); tx_length <= std_logic_vector(remain(8 downto 0)); length := length - remain; dma_addr := dma_addr + remain; elsif state = state_tx_read then if tx_done = '1' then state <= state_rx_complete; end if; elsif state = state_rx_complete then if rx_complete = '1' then fifo_din <= rx_data; fifo_write <= '1'; remain := remain - 1; if remain = 0 then if length > 0 then state <= state_dma_read; else state <= state_command; end if; end if; end if; -- dma write elsif state = state_dma_write then remain := 16 - resize(dma_addr(3 downto 0), remain'length); if max_write = "001" then remain := 32 - resize(dma_addr(4 downto 0), remain'length); elsif max_write = "010" then remain := 64 - resize(dma_addr(5 downto 0), remain'length); elsif max_write = "011" then remain := 128 - resize(dma_addr(6 downto 0), remain'length); elsif max_write = "100" then remain := 256 - resize(dma_addr(7 downto 0), remain'length); elsif max_write = "101" then remain := 512 - resize(dma_addr(8 downto 0), remain'length); end if; if remain > length then remain := length; end if; state <= state_tx_write; tx_write <= '1'; tx_data <= fifo_dout; tx_address <= std_logic_vector(dma_addr); tx_length <= std_logic_vector(remain(8 downto 0)); fifo_read <= '1'; length := length - remain; dma_addr := dma_addr + remain; remain := remain - 1; elsif state = state_tx_write then if tx_done = '1' then if length > 0 then state <= state_dma_write; else state <= state_command; end if; elsif tx_accept = '1' and remain > 0 then tx_data <= fifo_dout; fifo_read <= '1'; remain := remain - 1; end if; end if; if tx_done = '1' then tx_read <= '0'; tx_write <= '0'; tx_complete <= '0'; tx_data <= (others => '0'); tx_address <= (others => '0'); tx_length <= (others => '0'); end if; end if; end process; fifo : entity work.port_fifo port map ( reset => reset, rd_clk => clk, rd_read => fifo_read, rd_data => fifo_dout, rd_empty => fifo_empty, rd_count => fifo_count, wr_write => fifo_write, wr_data => fifo_din, wr_full => fifo_full --wr_count => wr_count ); commands : cmd_fifo port map ( clk => clk, rst => reset, rd_en => cmd_read, wr_en => cmd_write, din => cmd_din, dout => cmd_dout, full => cmd_full, empty => cmd_empty ); leds <= "000" when state = state_idle else -- bar read "001" when state = state_tx_complete else -- dma read "010" when state = state_dma_read or state = state_tx_read or state = state_rx_complete else -- dma write "011" when state = state_dma_write or state = state_tx_write else -- mdio "100" when state = state_mdio else -- interrupt "101" when state = state_interrupt else -- other "111"; end arch;