Skip to content
Snippets Groups Projects
dma_controller.vhd 16 KiB
Newer Older
--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- GN4124 core for PCIe FMC carrier
-- http://www.ohwr.org/projects/gn4124-core
--------------------------------------------------------------------------------
--
--
-- description: Manages the DMA transfers.
--
Matthieu Cattin's avatar
Matthieu Cattin committed
--------------------------------------------------------------------------------
-- Copyright CERN 2010-2019
Matthieu Cattin's avatar
Matthieu Cattin committed
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;

entity dma_controller is
  port (
    ---------------------------------------------------------
    -- GN4124 core clock and reset
    clk_i   : in std_logic;
    rst_n_i : in std_logic;

    ---------------------------------------------------------
    -- Interrupt request
    dma_ctrl_irq_o : out std_logic;

    ---------------------------------------------------------
    -- To the L2P DMA master and P2L DMA master
    dma_ctrl_carrier_addr_o : out std_logic_vector(31 downto 0);
    dma_ctrl_host_addr_h_o  : out std_logic_vector(31 downto 0);
    dma_ctrl_host_addr_l_o  : out std_logic_vector(31 downto 0);
    dma_ctrl_len_o          : out std_logic_vector(31 downto 0);
    dma_ctrl_start_l2p_o    : out std_logic;  -- To the L2P DMA master
    dma_ctrl_start_p2l_o    : out std_logic;  -- To the P2L DMA master
    dma_ctrl_start_next_o   : out std_logic;  -- To the P2L DMA master
    dma_ctrl_byte_swap_o    : out std_logic_vector(1 downto 0);
    dma_ctrl_abort_o        : out std_logic;
    dma_ctrl_done_i         : in  std_logic;
    dma_ctrl_error_i        : in  std_logic;

    ---------------------------------------------------------
    -- From P2L DMA master
    next_item_carrier_addr_i : in std_logic_vector(31 downto 0);
    next_item_host_addr_h_i  : in std_logic_vector(31 downto 0);
    next_item_host_addr_l_i  : in std_logic_vector(31 downto 0);
    next_item_len_i          : in std_logic_vector(31 downto 0);
    next_item_next_l_i       : in std_logic_vector(31 downto 0);
    next_item_next_h_i       : in std_logic_vector(31 downto 0);
    next_item_attrib_i       : in std_logic_vector(31 downto 0);
    next_item_valid_i        : in std_logic;

    ---------------------------------------------------------
    -- Wishbone slave interface
    wb_rst_n_i : in  std_logic;
    wb_clk_i   : in  std_logic;                      -- Bus clock
    wb_adr_i   : in  std_logic_vector(3 downto 0);   -- Adress
    wb_dat_o   : out std_logic_vector(31 downto 0);  -- Data in
    wb_dat_i   : in  std_logic_vector(31 downto 0);  -- Data out
    wb_sel_i   : in  std_logic_vector(3 downto 0);   -- Byte select
    wb_cyc_i   : in  std_logic;                      -- Read or write cycle
    wb_stb_i   : in  std_logic;                      -- Read or write strobe
    wb_we_i    : in  std_logic;                      -- Write
    wb_ack_o   : out std_logic                       -- Acknowledge
  );
end dma_controller;


architecture arch of dma_controller is
  --  Values for the STAT register
  constant c_DMA_STAT_IDLE  : std_logic_vector(1 downto 0) := "00";
  constant c_DMA_STAT_BUSY  : std_logic_vector(1 downto 0) := "01";
  constant c_DMA_STAT_ERROR : std_logic_vector(1 downto 0) := "10";
  constant c_DMA_STAT_ABORT : std_logic_vector(1 downto 0) := "11";
  -- DMA controller registers
  signal dma_ctrl_start_wb      : std_logic;
  signal dma_ctrl_abort_wb      : std_logic;
  signal dma_ctrl_wr_wb         : std_logic;
  signal dma_ctrl_wack_wb       : std_logic;
  signal dma_ctrl_byte_swap_wb  : std_logic_vector(1 downto 0);
  signal dma_ctrl_byte_swap     : std_logic_vector(1 downto 0);
  signal dma_ctrl_start         : std_logic;
  signal dma_ctrl_abort         : std_logic;
  signal dma_ctrl_wr            : std_logic;
  signal dma_cstart             : std_logic_vector(31 downto 0);
  signal dma_hstartl            : std_logic_vector(31 downto 0);
  signal dma_hstarth            : std_logic_vector(31 downto 0);
  signal dma_len                : std_logic_vector(31 downto 0);
  signal dma_nextl              : std_logic_vector(31 downto 0);
  signal dma_nexth              : std_logic_vector(31 downto 0);
  signal dma_attrib_chain       : std_logic;
  signal dma_attrib_dir         : std_logic;
  signal dma_stat_irq_i_wb      : std_logic;
  signal dma_stat_irq_o_wb      : std_logic;
  signal dma_stat_status_wb     : std_logic_vector(1 downto 0);
  signal dma_stat_wr_wb         : std_logic;
  signal dma_stat_wack_wb       : std_logic;
  signal dma_stat_rd_wb         : std_logic;
  signal dma_stat_rack_wb       : std_logic;
  signal dma_stat_irq_wr_wb     : std_logic;
  signal dma_stat_irq_wr        : std_logic;
  signal dma_stat_wr            : std_logic;
  signal dma_cstart_reg         : std_logic_vector(31 downto 0);
  signal dma_hstartl_reg        : std_logic_vector(31 downto 0);
  signal dma_hstarth_reg        : std_logic_vector(31 downto 0);
  signal dma_len_reg            : std_logic_vector(31 downto 0);
  signal dma_nextl_reg          : std_logic_vector(31 downto 0);
  signal dma_nexth_reg          : std_logic_vector(31 downto 0);
  signal dma_attrib_chain_reg   : std_logic;
  signal dma_attrib_dir_reg     : std_logic;
  signal dma_ctrl_byte_swap_reg : std_logic_vector(1 downto 0);

  -- DMA controller FSM
  type dma_ctrl_state_type is (DMA_IDLE, DMA_START_TRANSFER, DMA_TRANSFER,
  signal dma_ctrl_current_state : dma_ctrl_state_type;

  -- status signals
  signal dma_stat_reg : std_logic_vector(1 downto 0);
  signal dma_irq_reg  : std_logic;
  attribute keep : string;
  attribute keep of dma_cstart, dma_hstartl, dma_hstarth, dma_len,
    dma_nextl, dma_nexth, dma_attrib_chain, dma_attrib_dir : signal is "true";
begin

  ------------------------------------------------------------------------------
  ------------------------------------------------------------------------------

  inst_dma_controller_regs : entity work.dma_controller_regs
    port map (
      rst_n_i            => wb_rst_n_i,
      clk_i              => wb_clk_i,
      wb_adr_i           => wb_adr_i,
      wb_dat_i           => wb_dat_i,
      wb_dat_o           => wb_dat_o,
      wb_cyc_i           => wb_cyc_i,
      wb_sel_i           => wb_sel_i,
      wb_stb_i           => wb_stb_i,
      wb_we_i            => wb_we_i,
      wb_ack_o           => wb_ack_o,
      ctrl_start_o       => dma_ctrl_start_wb,
      ctrl_start_i       => '0',
      ctrl_abort_o       => dma_ctrl_abort_wb,
      ctrl_abort_i       => '0',
      ctrl_byteswap_o    => dma_ctrl_byte_swap_wb,
      ctrl_wr_o          => dma_ctrl_wr_wb,
      ctrl_wack_i        => dma_ctrl_wack_wb,
      stat_status_o      => open,
      stat_status_i      => dma_stat_status_wb,
      stat_irq_i         => dma_stat_irq_i_wb,
      stat_irq_o         => dma_stat_irq_o_wb,
      stat_wr_o          => dma_stat_wr_wb,
      stat_wack_i        => dma_stat_wack_wb,
      stat_rd_o          => dma_stat_rd_wb,
      stat_rack_i        => dma_stat_rack_wb,
      cstart_o           => dma_cstart,
      hstartl_o          => dma_hstartl,
      hstarth_o          => dma_hstarth,
      len_o              => dma_len,
      nextl_o            => dma_nextl,
      nexth_o            => dma_nexth,
      attrib_chain_o     => dma_attrib_chain,
      attrib_dir_o       => dma_attrib_dir,
      cur_cstart_i       => dma_cstart_reg,
      cur_hstartl_i      => dma_hstartl_reg,
      cur_hstarth_i      => dma_hstarth_reg,
      cur_len_i          => dma_len_reg);

  -- Synchronizers for control.
  inst_sync_ctrl : entity work.gc_sync_word_wr
    generic map (
      g_WIDTH => 4)
    port map (
      clk_in_i           => wb_clk_i,
      rst_in_n_i         => wb_rst_n_i,
      clk_out_i          => clk_i,
      rst_out_n_i        => rst_n_i,
      data_i(0)          => dma_ctrl_start_wb,
      data_i(1)          => dma_ctrl_abort_wb,
      data_i(3 downto 2) => dma_ctrl_byte_swap_wb,
      wr_i               => dma_ctrl_wr_wb,
      ack_o              => dma_ctrl_wack_wb,
      data_o(0)          => dma_ctrl_start,
      data_o(1)          => dma_ctrl_abort,
      data_o(3 downto 2) => dma_ctrl_byte_swap,
      wr_o               => dma_ctrl_wr);
  dma_stat_irq_wr_wb <= dma_stat_wr_wb and dma_stat_irq_o_wb;

  -- Pulse is generated when a '1' is written to the irq bit of the stat reg.
  inst_sync_stat_wr : entity work.gc_sync_word_wr
      g_auto_wr => FALSE,
      g_width   => 1)
      clk_in_i    => wb_clk_i,
      rst_in_n_i  => wb_rst_n_i,
      clk_out_i   => clk_i,
      data_i(0)   => dma_stat_irq_wr_wb,
      wr_i        => dma_stat_wr_wb,
      ack_o       => dma_stat_wack_wb,
      data_o(0)   => dma_stat_irq_wr,
      wr_o        => dma_stat_wr);

  -- Sync stat
  inst_sync_stat_rd : entity work.gc_sync_word_rd
      clk_out_i              => wb_clk_i,
      rst_out_n_i            => wb_rst_n_i,
      clk_in_i               => clk_i,
      rst_in_n_i             => rst_n_i,
      data_in_i(1 downto 0)  => dma_stat_reg,
      data_in_i(2)           => dma_irq_reg,
      rd_out_i               => dma_stat_rd_wb,
      ack_out_o              => dma_stat_rack_wb,
      data_out_o(1 downto 0) => dma_stat_status_wb,
      data_out_o(2)          => dma_stat_irq_i_wb,
      rd_in_o                => open);

  ------------------------------------------------------------------------------
  -- DMA controller FSM
  ------------------------------------------------------------------------------
      if(rst_n_i = '0') then
        dma_ctrl_current_state <= DMA_IDLE;
        dma_ctrl_start_l2p_o   <= '0';
        dma_ctrl_start_p2l_o   <= '0';
        dma_ctrl_start_next_o  <= '0';
        dma_ctrl_abort_o       <= '0';

        dma_stat_reg           <= c_DMA_STAT_IDLE;
        dma_irq_reg            <= '0';
        dma_cstart_reg         <= (others => '0');
        dma_hstartl_reg        <= (others => '0');
        dma_hstarth_reg        <= (others => '0');
        dma_len_reg            <= (others => '0');
        dma_nextl_reg          <= (others => '0');
        dma_nexth_reg          <= (others => '0');
        dma_ctrl_byte_swap_reg <= "00";
        dma_attrib_chain_reg   <= '0';
        dma_attrib_dir_reg     <= '0';
      else
        case dma_ctrl_current_state is

          when DMA_IDLE =>
            --  Clear interrupt when idle status is read.
            if dma_stat_irq_wr = '1' and dma_stat_wr = '1' then
              dma_irq_reg <= '0';
            if (dma_ctrl_wr and dma_ctrl_start) = '1' then
              -- Capture parameters
              -- All these inputs registers are synchronized on the start pulse.
              dma_cstart_reg         <= dma_cstart;
              dma_hstartl_reg        <= dma_hstartl;
              dma_hstarth_reg        <= dma_hstarth;
              dma_len_reg            <= dma_len;
              dma_nextl_reg          <= dma_nextl;
              dma_nexth_reg          <= dma_nexth;
              dma_attrib_chain_reg   <= dma_attrib_chain;
              dma_attrib_dir_reg     <= dma_attrib_dir;
              dma_ctrl_byte_swap_reg <= dma_ctrl_byte_swap;
              -- Starts a new transfer
              dma_ctrl_current_state <= DMA_START_TRANSFER;
            end if;

          when DMA_START_TRANSFER =>
            -- Clear abort signal
            dma_ctrl_abort_o <= '0';

Tristan Gingold's avatar
Tristan Gingold committed
            if unsigned(dma_len_reg(31 downto 2)) = 0 then
              -- Requesting a DMA of 0 word length gives a error
              dma_irq_reg            <= '1';
              dma_stat_reg           <= c_DMA_STAT_ERROR;
              dma_ctrl_current_state <= DMA_IDLE;
              -- Start the DMA if the length is not 0
              if dma_attrib_dir_reg = '0' then
                -- L2P transfer (from target to PCIe)
                dma_ctrl_start_l2p_o <= '1';
Tristan Gingold's avatar
Tristan Gingold committed
              else
                -- P2L transfer (from PCIe to target)
                dma_ctrl_start_p2l_o <= '1';
              end if;
              dma_ctrl_current_state <= DMA_TRANSFER;
            end if;

          when DMA_TRANSFER =>
            -- Clear start signals, to make them 1 tick pulses
            dma_ctrl_start_l2p_o <= '0';
            dma_ctrl_start_p2l_o <= '0';

            if (dma_ctrl_abort and dma_ctrl_wr) = '1' then
              dma_stat_reg           <= c_DMA_STAT_ABORT;
              dma_ctrl_abort_o       <= '1';
              dma_ctrl_current_state <= DMA_IDLE;
            elsif dma_ctrl_error_i = '1' then
              -- An error occured
              dma_irq_reg            <= '1';
              dma_stat_reg           <= c_DMA_STAT_ERROR;
              dma_ctrl_current_state <= DMA_IDLE;
            elsif dma_ctrl_done_i = '1' then
              if dma_attrib_chain_reg = '1' then
                -- More transfers in chained DMA
                dma_ctrl_current_state <= DMA_START_CHAIN;
              else
                -- Was the last transfer
                dma_ctrl_current_state <= DMA_IDLE;
              end if;
            end if;

          when DMA_START_CHAIN =>
            -- Catch the next item in host memory
            dma_ctrl_current_state <= DMA_CHAIN;
            dma_hstarth_reg        <= dma_nexth_reg;
            dma_hstartl_reg        <= dma_nextl_reg;
            dma_len_reg            <= X"0000001C";
            dma_ctrl_start_next_o  <= '1';

          when DMA_CHAIN =>
            -- Clear start next signal, to make it 1 tick pulse
            dma_ctrl_start_next_o <= '0';

            if (dma_ctrl_abort and dma_ctrl_wr) = '1' then
              dma_stat_reg           <= c_DMA_STAT_ABORT;
              dma_ctrl_abort_o       <= '1';
              dma_ctrl_current_state <= DMA_IDLE;
            elsif dma_ctrl_error_i = '1' then
              -- An error occured
              dma_irq_reg            <= '1';
              dma_stat_reg           <= c_DMA_STAT_ERROR;
              dma_ctrl_current_state <= DMA_IDLE;
            elsif next_item_valid_i = '1' then
              -- Capture parameters
              dma_cstart_reg         <= next_item_carrier_addr_i;
              dma_hstartl_reg        <= next_item_host_addr_l_i;
              dma_hstarth_reg        <= next_item_host_addr_h_i;
              dma_len_reg            <= next_item_len_i;
              dma_nextl_reg          <= next_item_next_l_i;
              dma_nexth_reg          <= next_item_next_h_i;
              dma_attrib_chain_reg   <= next_item_attrib_i(1);
              dma_attrib_dir_reg     <= next_item_attrib_i(0);
              -- Next item received
              dma_ctrl_current_state <= DMA_START_TRANSFER;
            end if;
    end if;
  end process p_fsm;

  dma_ctrl_carrier_addr_o <= dma_cstart_reg;
  dma_ctrl_host_addr_h_o  <= dma_hstarth_reg;
  dma_ctrl_host_addr_l_o  <= dma_hstartl_reg;
  dma_ctrl_len_o          <= dma_len_reg;
  dma_ctrl_irq_o       <= dma_irq_reg;
  dma_ctrl_byte_swap_o <= dma_ctrl_byte_swap_reg;
end architecture arch;