-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--
-- Copyright 2025 VLSI Solution Oy
-- SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
--
-- Licensed under the Solderpad Hardware License v 2.1 (the "License");
-- you may not use this file except in compliance with the License, or,
-- at your option, the Apache License version 2.0.
-- You may obtain a copy of the License at
--
--    https://solderpad.org/licenses/SHL-2.1/
--
-- Unless required by applicable law or agreed to in writing, any work
-- distributed under the 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.
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--                               RISCV core
--                                 V1.1
--
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-- - - - - - - -
--  libraries
-- - - - - - - -

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.rv_pkg.all;

entity axi_wrapper is
  generic ( AXIDATA_W : natural range 32 to 64 := 32;  -- data width 32/64 only
            AXI_ID    : slv_03_00 := X"0";
            ROM_BASE  : slv_31_28 := X"7" );
  port ( clk_i          : in     std_logic;
         xrst_i         : in     std_logic;
         --  channel 1  (dcache)
         dmem_rd_i      : in     std_logic;  -- HIGH until request accepted
         dmem_wr_i      : in     std_logic;  -- HIGH during whole burst time
         dmem_len_i     : in     slv_07_00;
         dmem_addr_i    : in     slv_31_00;
         dmem_wdata_i   : in     slv_t(AXIDATA_W-1 downto 0);
         dmem_accept_o  : buffer std_logic;
         dmem_ack_o     : buffer std_logic;  -- HIGH when rdata valid / WR done
         dmem_error_o   : out    std_logic;
         --
         --  channel 2  (icache)
         imem_rd_i      : in     std_logic;
         imem_wr_i      : in     std_logic;
         imem_len_i     : in     slv_07_00;
         imem_addr_i    : in     slv_31_00;
         imem_wdata_i   : in     slv_t(AXIDATA_W-1 downto 0);
         imem_accept_o  : buffer std_logic;
         imem_ack_o     : buffer std_logic;
         imem_error_o   : out    std_logic;
         --
         --  channel 3  (dma / NC)
         dma1_rd_i      : in     std_logic;
         dma1_wr_i      : in     std_logic;
         dma1_len_i     : in     slv_07_00;
         dma1_addr_i    : in     slv_31_00;
         dma1_wdata_i   : in     slv_t(AXIDATA_W-1 downto 0);
         dma1_accept_o  : buffer std_logic;
         dma1_ack_o     : buffer std_logic;
         dma1_error_o   : out    std_logic;
         --
         --  channel 4  (dma / NC)
         dma2_rd_i      : in     std_logic;
         dma2_wr_i      : in     std_logic;
         dma2_len_i     : in     slv_07_00;
         dma2_addr_i    : in     slv_31_00;
         dma2_wdata_i   : in     slv_t(AXIDATA_W-1 downto 0);
         dma2_accept_o  : buffer std_logic;
         dma2_ack_o     : buffer std_logic;
         dma2_error_o   : out    std_logic;
         --
         --  channel 5  (dma / NC)
         dma3_rd_i      : in     std_logic;
         dma3_wr_i      : in     std_logic;
         dma3_len_i     : in     slv_07_00;
         dma3_addr_i    : in     slv_31_00;
         dma3_wdata_i   : in     slv_t(AXIDATA_W-1 downto 0);
         dma3_accept_o  : buffer std_logic;
         dma3_ack_o     : buffer std_logic;
         dma3_error_o   : out    std_logic;
         --
         --  common ports for all channels requests
         resp_rdata_o   : out    slv_t(AXIDATA_W-1 downto 0);
         --
         -- AXI4 ports
         axi_arvalid_o  : out    std_logic;  -- AXI4 read req
         axi_araddr_o   : out    slv_31_00;
         axi_arid_o     : out    slv_03_00;
         axi_arlen_o    : out    slv_07_00;
         axi_arburst_o  : out    slv_01_00;
         axi_rready_o   : out    std_logic;
         axi_arready_i  : in     std_logic;  -- AXI4 read resp
         axi_rvalid_i   : in     std_logic;
         axi_rdata_i    : in     slv_t(AXIDATA_W-1 downto 0);
         axi_rresp_i    : in     slv_01_00;  -- ok, exook, slverr, decerr
         axi_rid_i      : in     slv_03_00;
         axi_rlast_i    : in     std_logic;
         axi_awready_i  : in     std_logic;  -- AXI4 write req
         axi_wready_i   : in     std_logic;
         axi_bvalid_i   : in     std_logic;
         axi_bresp_i    : in     slv_01_00;
         axi_bid_i      : in     slv_03_00;
         axi_awvalid_o  : buffer std_logic;  -- AXI4 write resp
         axi_awaddr_o   : out    slv_31_00;
         axi_awid_o     : out    slv_03_00;
         axi_awlen_o    : out    slv_07_00;
         axi_awburst_o  : out    slv_01_00;
         axi_wvalid_o   : buffer std_logic;
         axi_wdata_o    : out    slv_t(AXIDATA_W-1 downto 0);
         axi_wstrb_o    : out    slv_t(AXIDATA_W/8-1 downto 0);  -- 8/4 bit
         axi_wlast_o    : buffer std_logic;
         axi_bready_o   : buffer std_logic
         );
end entity axi_wrapper;


architecture rtl of axi_wrapper is

  constant ROMADDR_W  : natural := 14;                    -- 2^14 = 16k
  constant ROMDATA_W  : natural := 32;

  component rom32x16k
    -- ambit translate off
    generic ( DATA_W      : natural range 8 to 32 := 32;
              ADDR_W      : natural range 4 to 32 := 13;  -- default 8k
              MIF_FILE    : string    := "rom.mif";       -- for asic sim / fpga
              ROM_TYPE    : string    := "AUTO";          -- MLAB/M20K, M4K/M512
              -- ambit translate on
              -- pragma translate_off
              HOLD_DELAY  : time      := 0.5 ns;          -- dout hold delay
              ODELAY      : time      := 4.5 ns;          -- clk to dout delay
              LOG_FILE    : string    := "rom.out";       -- log file
              -- pragma translate_on
              -- ambit translate off
              DEBUG       : std_logic := '0' );           -- log accesses
    -- ambit translate on
    port ( clk       : in  std_logic;
           xreset    : in  std_logic;
           --  inputs
           rd        : in  std_logic;
           addr      : in  slv_t(ROMADDR_W-1 downto 0);
           --  outputs
           dout      : out slv_t(ROMDATA_W-1 downto 0) );
 end component;


  signal rom_rd_w       : std_logic;
  signal rom_addr_w     : slv_t(ROMADDR_W-1 downto 0);
  signal rom_dout_w     : slv_31_00;
  signal rom_access_w   : std_logic;
  signal rom_data_w     : slv_t(AXIDATA_W-1 downto 0);  -- 32 / 64 bit

  signal dma3_rd_q      : std_logic;
  signal dma2_rd_q      : std_logic;
  signal dma1_rd_q      : std_logic;
  signal imem_rd_q      : std_logic;
  signal dmem_rd_q      : std_logic;

  signal req_q          : std_logic;                 -- request start

  signal req_wrcnt_q    : slv_t(dmem_len_i'range);   -- write burst tracking
  signal req_wrcnt_0_w  : std_logic;

  signal early_wr_ack_q : std_logic;  -- upstream done, still waiting downstream

  signal curr_chan_q    : slv_t(4 downto 0);      -- dma3, dma2, dma1,imem, dmem

  signal wr_ack_w       : std_logic;
  signal rd_ack_w       : std_logic;

  signal wr_accept_w    : std_logic;
  signal rd_accept_w    : std_logic;
  signal req_accept_w   : slv_04_00;

  signal req_addr_q     : slv_31_02;  -- ! ! ! FFOR 32 / 64 BIT DATA ! ! !
  signal req_wdata_q    : slv_t(AXIDATA_W-1 downto 0);
  signal resp_error_w   : std_logic;

  signal pending_w      : slv_04_00;

  type st_t is (IDLE, RD, WR, WRWAIT, RD_ROM_START, RD_ROM, RD_ROM_HIGH);
  signal st_q : st_t;

begin

  u_rvirom16k : rom32x16k
    -- ambit translate off
    generic map ( DATA_W      => ROMDATA_W,
                  ADDR_W      => ROMADDR_W,               -- default 16k
                  MIF_FILE    => "vmon.mif",              -- for asic sim / fpga
                  -- ambit translate on
                  -- pragma translate_off
                  HOLD_DELAY  => 0.5 ns,                  -- dout hold delay
                  ODELAY      => 4.5 ns,                  -- clk to dout delay
                  LOG_FILE    => "rvirom16k.out",         -- log file
                  -- pragma translate_on
                  -- ambit translate off
                  DEBUG       => '0' )                    -- log accesses
    -- ambit translate on
    port map ( clk       => clk_i,
               xreset    => xrst_i,
               --  inputs
               rd        => rom_rd_w,
               addr      => rom_addr_w,
               --  outputs
               dout      => rom_dout_w );


  rom_access_w  <= '1' when (st_q = RD_ROM_START or st_q = RD_ROM or
                             st_q = RD_ROM_HIGH)
                   else '0';
  rom_rd_w      <= '1' when (rom_access_w = '1' and
                             not (st_q = RD_ROM_HIGH and req_wrcnt_0_w = '1'))
                   else '0';
  rom_addr_w    <= req_addr_q(rom_addr_w'high+2 downto 0+2);



  g_axi32o : if ( AXIDATA_W /= 64 ) generate
  begin
    rom_data_w <= rom_dout_w;
  end generate g_axi32o;


  g_axi64o : if ( AXIDATA_W = 64 ) generate
    signal rom_dout_q : slv_31_00;
  begin
    --
    p_buf_rom : process (clk_i)
    begin
      if ( rising_edge(clk_i) ) then
        if ( xrst_i = '0' ) then
          rom_dout_q   <= (others => '0');
        else
          if (rom_rd_w = '1') then
            rom_dout_q <= rom_dout_w;
          end if;
        end if;
      end if;
    end process p_buf_rom;
    --
    rom_data_w <= rom_dout_w & rom_dout_q;
    --
  end generate g_axi64o;





  p_st : process (clk_i)
  begin
    if (clk_i'event and clk_i = '1') then
      if (xrst_i = '0') then
        req_wrcnt_q    <= (others => '0');      -- 8b
        req_addr_q     <= (others => '0');
        req_wdata_q    <= (others => '0');
        req_q          <= '0';
        early_wr_ack_q <= '0';
        curr_chan_q    <= "00000";              -- dma3, dma2, dma1, imem, dmem
        st_q           <= IDLE;
      else
        early_wr_ack_q <= '0';
        case st_q is
          --
          --
          when IDLE    =>
            --
            curr_chan_q <= "00000";                               -- keep idling
            req_q       <= not_zero(pending_w);
            --
            -- ! ! ! LOWES PRIORITY CHANNEL FIRST ! ! !
            --
            if (pending_w(4) = '1' and dma3_accept_o = '1') then  -- dma3 rd/wr
              curr_chan_q(4) <= '1';
              req_wrcnt_q    <= dma3_len_i;
              req_addr_q     <= dma3_addr_i(31 downto 2);
              st_q           <= RD;
              if (eq(ROM_BASE, dma3_addr_i(ROM_BASE'range))) then
                st_q         <= RD_ROM_START;
              end if;
              if (dma3_wr_i = '1') then                           -- start write
                req_wdata_q  <= dma3_wdata_i;
                st_q         <= WR;
              end if;
            end if;
            --
            if (pending_w(3) = '1' and dma2_accept_o = '1') then  -- dma2 rd/wr
              curr_chan_q(3) <= '1';
              req_wrcnt_q    <= dma2_len_i;
              req_addr_q     <= dma2_addr_i(31 downto 2);
              st_q           <= RD;
              if (eq(ROM_BASE, dma2_addr_i(ROM_BASE'range))) then
                st_q         <= RD_ROM_START;
              end if;
              if (dma2_wr_i = '1') then                           -- start write
                req_wdata_q  <= dma2_wdata_i;
                st_q         <= WR;
              end if;
            end if;
            --
            if (pending_w(2) = '1' and dma1_accept_o = '1') then  -- dma1 rd/wr
              curr_chan_q(2) <= '1';
              req_wrcnt_q    <= dma1_len_i;
              req_addr_q     <= dma1_addr_i(31 downto 2);
              st_q           <= RD;
              if (eq(ROM_BASE, dma1_addr_i(ROM_BASE'range)) = '1') then
                st_q         <= RD_ROM_START;
              end if;
              if (dma1_wr_i = '1') then                           -- start write
                req_wdata_q  <= dma1_wdata_i;
                st_q         <= WR;
              end if;
            end if;
            --
            if (pending_w(1) = '1' and imem_accept_o = '1') then  -- imem rd/wr
              curr_chan_q(1) <= '1';
              req_wrcnt_q    <= imem_len_i;
              req_addr_q     <= imem_addr_i(31 downto 2);
              st_q           <= RD;
              if (eq(ROM_BASE, imem_addr_i(ROM_BASE'range))) then
                st_q         <= RD_ROM_START;
              end if;
              if (imem_wr_i = '1') then                           -- start write
                req_wdata_q  <= imem_wdata_i;
                st_q         <= WR;
              end if;
            end if;
            --
            if (pending_w(0) = '1' and dmem_accept_o = '1') then  -- dmem rd/wr
              curr_chan_q(0) <= '1';
              req_wrcnt_q    <= dmem_len_i;
              req_addr_q     <= dmem_addr_i(31 downto 2);
              st_q           <= RD;
              if (eq(ROM_BASE, dmem_addr_i(ROM_BASE'range))) then
                st_q         <= RD_ROM_START;
              end if;
              if (dmem_wr_i = '1' ) then                          -- start write
                req_wdata_q  <= dmem_wdata_i;
                st_q         <= WR;
              end if;
            end if;
          --
          --
          when WR      =>                                         --  WRITE
            early_wr_ack_q <= axi_wready_i and req_wrcnt_0_w;     -- last data
            if (wr_accept_w  = '1') then
              if (curr_chan_q = "10000") then
                req_wdata_q  <= dma3_wdata_i;                -- nxt burst data
              elsif (curr_chan_q = "01000") then
                req_wdata_q  <= dma2_wdata_i;                -- nxt burst data
              elsif (curr_chan_q = "00100") then
                req_wdata_q  <= dma1_wdata_i;                -- nxt burst data
              elsif (curr_chan_q = "00010") then
                req_wdata_q  <= imem_wdata_i;                -- nxt burst data
              else
                req_wdata_q  <= dmem_wdata_i;
              end if;
            end if;
            if (axi_awready_i = '1') then
              req_q <= '0';
            end if;
            if (axi_wready_i = '1' and not_zero(req_wrcnt_q)) then
              req_wrcnt_q <= dec1u(req_wrcnt_q);             --  - X"01"
            end if;
            --
            -- next state
            --
            if (axi_wready_i = '1' and axi_wlast_o = '1') then
              st_q <= WRWAIT;
            end if;
            --
            -- ROM read is illegal but handle it to prevent bus from stallina
            --
            if (eq(ROM_BASE, req_addr_q(ROM_BASE'range))) then
              early_wr_ack_q  <= req_wrcnt_0_w;              -- last data
              req_q           <= '0';
              st_q            <= WR;
              if (not_zero(req_wrcnt_q)) then
                req_wrcnt_q   <= dec1u(req_wrcnt_q);         --  - X"01"
              end if;
              if (req_wrcnt_0_w = '1') then                  -- wr done
                st_q          <= IDLE;
              end if;
            end if;
          --
          --
          when WRWAIT  =>
            if (axi_bvalid_i  = '1' and axi_bready_o = '1') then  -- wr done
              st_q <= IDLE;
            end if;
          when RD      =>
            if (rd_accept_w = '1') then  -- down stream was busy, waiting...
              case curr_chan_q is
                when "10000" =>
                when "01000" =>
                when "00100" =>
                when "00010" =>
                when others =>
              end case;
            end if;
            if (axi_arready_i = '1') then
              req_q <= '0';
            end if;
            if (axi_rvalid_i  = '1' and axi_rlast_i  = '1') then  -- wr done
              st_q <= IDLE;
            end if;
          when RD_ROM_START  =>
            st_q   <= RD_ROM;
            req_addr_q(rom_addr_w'high+2 downto 0+2) <=
              inc1u(req_addr_q(rom_addr_w'high+2 downto 0+2));
          when RD_ROM        =>
            -- req_wrcnt_q is here used to read ROM words
            if (AXIDATA_W /= 64) then                      -- 32bit data bus
              if (req_wrcnt_0_w = '0') then
                req_wrcnt_q <= dec1u(req_wrcnt_q);         --  - X"01"
                req_addr_q(rom_addr_w'high+2 downto 0+2) <=
                  inc1u(req_addr_q(rom_addr_w'high+2 downto 0+2));
              end if;
              if (req_wrcnt_0_w = '1') then
                st_q <= IDLE;
              end if;
            else                                           -- 64bit data bus
              if (req_wrcnt_0_w = '0') then
                if (rom_addr_w(0) = '0') then
                end if;
                req_addr_q(rom_addr_w'high+2 downto 0+2) <=
                  inc1u(req_addr_q(rom_addr_w'high+2 downto 0+2));
              end if;
              st_q <= RD_ROM_HIGH;
            end if;
           when RD_ROM_HIGH   =>
            if (req_wrcnt_0_w = '0') then
              req_wrcnt_q <= dec1u(req_wrcnt_q);         --  - X"01"
              req_addr_q(rom_addr_w'high+2 downto 0+2) <=
                inc1u(req_addr_q(rom_addr_w'high+2 downto 0+2));
            end if;
            if (req_wrcnt_0_w = '1') then
              st_q <= IDLE;
            else
              st_q <= RD_ROM;
            end if;
          when others  =>
            st_q   <= IDLE;
        end case;
      end if;
    end if;
  end process p_st;


  --          __    __    __    __    __    __    __    __    __    __    __
  -- ck    __|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |__|  |_
  --                _____
  -- rd    ________|     |____________________________________________________
  --       ______________ _____ ___________      _____________________________
  -- state __idle________X_1st_X___________ROM_RD______X________idle__________
  --                      _______________________
  -- read  ______________/                       \____________________________
  --                        0     1     2     3
  --       ______________ _____ _____ _____ _____ ____________________________
  -- addr  _____xx_______X__0__X__1__X__2__X__3__X___________4________________
  --
  --       ____________________ __0__ __1__ __2___ __3_________________________
  -- dout  _____xx_____________X__0__X__1__X__2__X__3__X_______________________
  --
  --                                  _____       _____
  -- ack   __________________________/     \_____/     \_______________________
  --
  --       ______________ _____1_____ _____0_____ _____________________________
  -- wrcnt ______________X_____1_____X_____0_____»_____________________________
  --



  p_wr : process ( st_q, req_addr_q, req_wdata_q,
                   axi_wready_i, axi_bvalid_i, early_wr_ack_q,
                   req_q, req_wrcnt_q, req_wrcnt_0_w )
  begin
    axi_awvalid_o <= '0';
    axi_wvalid_o  <= '0';
    axi_awaddr_o  <= (others => '0');
    axi_wdata_o   <= (others => '0');
    axi_awid_o    <= (others => '0');
    axi_awlen_o   <= (others => '0');
    axi_wlast_o   <= '0';
    wr_ack_w      <= '0';
    wr_accept_w   <= '1';
    case st_q is
      when IDLE   =>
        null;
      when WR     =>
        if (req_q = '1') then                 -- start of wr
          axi_awid_o    <= AXI_ID;
          axi_awvalid_o <= '1';
          axi_awlen_o   <= req_wrcnt_q;
          axi_awaddr_o  <= req_addr_q & "00"; -- ! ! burst start address
        end if;
        axi_wvalid_o  <= '1';
        axi_wdata_o   <= req_wdata_q;
        axi_wlast_o   <= axi_wready_i and req_wrcnt_0_w;
        -- wr_ack_w   <= axi_bvalid_i;
        wr_ack_w      <= early_wr_ack_q;      -- early ack for write
        --
        if (axi_wready_i = '0') then          -- stall if down strem fifo full
          wr_accept_w <= '0';
        end if;
        if (axi_bvalid_i = '1') then          -- stall between evict-read/evict
          wr_accept_w <= '0';
        end if;
        --
        -- ROM read is illegal but handle it to prevent bus from stallina
        --
        if (eq(ROM_BASE, req_addr_q(ROM_BASE'range))) then
          axi_awvalid_o <= '0';
          axi_wvalid_o  <= '0';
          axi_awaddr_o  <= (others => '0');
          axi_wdata_o   <= (others => '0');
          axi_awid_o    <= (others => '0');
          axi_awlen_o   <= (others => '0');
          axi_wlast_o   <= '0';
          wr_ack_w      <= early_wr_ack_q;
          wr_accept_w   <= '1';
        end if;
        --
      when WRWAIT =>
        wr_ack_w    <= early_wr_ack_q;        -- early ack for write
        wr_accept_w <= '0';
      when RD | RD_ROM_START | RD_ROM  | RD_ROM_HIGH =>
        wr_accept_w <= '0';
      when others   =>
        null;
    end case;
  end process p_wr;


  req_wrcnt_0_w <= is_zero(req_wrcnt_q) when (st_q = WR or st_q = RD_ROM or
                                              st_q = RD_ROM_HIGH)
                   else '0';


--axi_wstrb_o   <= (AXIDATA_W/8-1 downto 0 => '1', others => '0');
  axi_wstrb_o   <= (others => '1');
  axi_awburst_o <= "01";                          -- INCR type only
  axi_bready_o  <= '1';



  p_rd : process (st_q, req_addr_q, axi_rvalid_i, req_q, req_wrcnt_q)
  begin
    axi_arvalid_o <= '0';
    axi_araddr_o  <= (others => '0');
    axi_arid_o    <= (others => '0');
    axi_arlen_o   <= (others => '0');
    rd_ack_w      <= '0';
    rd_accept_w   <= '1';
    case st_q is
      when IDLE                  =>
        null;
      when RD                    =>
        if (req_q = '1') then                     -- start of rd
          axi_arid_o    <= AXI_ID;
          axi_arvalid_o <= '1';
          axi_araddr_o  <= req_addr_q & "00";
          axi_arlen_o   <= req_wrcnt_q;
        end if;
        rd_ack_w        <= axi_rvalid_i;
      when WR | WRWAIT           =>
        rd_accept_w     <= '0';
      when RD_ROM_START          =>
        rd_accept_w     <= '0';
      when RD_ROM | RD_ROM_HIGH  =>
        rd_accept_w     <= '0';
        if (AXIDATA_W /= 64) then
          rd_ack_w      <= '1';
        else
          if (st_q = RD_ROM_HIGH) then
            rd_ack_w    <= '1';
          end if;
        end if;
      when others  =>
        null;
    end case;
  end process p_rd;



  axi_arburst_o  <= "01";                         -- INCR type only
  axi_rready_o   <= '1';

  resp_rdata_o   <= rom_data_w when(rom_access_w = '1') else axi_rdata_i;
  resp_error_w   <= (not rom_access_w) and
                    ( (not_zero(axi_rresp_i) and axi_rvalid_i) or
                      (not_zero(axi_bresp_i) and axi_bvalid_i) );

  dma3_error_o   <= resp_error_w and curr_chan_q(4);
  dma2_error_o   <= resp_error_w and curr_chan_q(3);
  dma1_error_o   <= resp_error_w and curr_chan_q(2);
  imem_error_o   <= resp_error_w and curr_chan_q(1);
  dmem_error_o   <= resp_error_w and curr_chan_q(0);

  dma3_ack_o     <= (rd_ack_w or wr_ack_w)  and curr_chan_q(4);
  dma2_ack_o     <= (rd_ack_w or wr_ack_w)  and curr_chan_q(3);
  dma1_ack_o     <= (rd_ack_w or wr_ack_w)  and curr_chan_q(2);
  imem_ack_o     <= (rd_ack_w or wr_ack_w)  and curr_chan_q(1);
  dmem_ack_o     <= (rd_ack_w or wr_ack_w)  and curr_chan_q(0);

  dma3_accept_o  <= req_accept_w(4);
  dma2_accept_o  <= req_accept_w(3);
  dma1_accept_o  <= req_accept_w(2);
  imem_accept_o  <= req_accept_w(1);
  dmem_accept_o  <= req_accept_w(0);

  -- there can be comb loop without these registers (dcache only)
  p_dffr(clk_i, xrst_i, dma3_rd_i, dma3_rd_q);
  p_dffr(clk_i, xrst_i, dma2_rd_i, dma2_rd_q);
  p_dffr(clk_i, xrst_i, dma1_rd_i, dma1_rd_q);
  p_dffr(clk_i, xrst_i, imem_rd_i, imem_rd_q);
  p_dffr(clk_i, xrst_i, dmem_rd_i, dmem_rd_q);

  -- new requests
  pending_w(4) <= dma3_rd_q or dma3_wr_i;
  pending_w(3) <= dma2_rd_q or dma2_wr_i;
  pending_w(2) <= dma1_rd_q or dma1_wr_i;
  pending_w(1) <= imem_rd_q or imem_wr_i;
  pending_w(0) <= dmem_rd_q or dmem_wr_i;


  p_grant : process ( st_q, rd_accept_w, wr_accept_w, curr_chan_q, pending_w )
  begin
    req_accept_w  <= "00000";
    if (st_q /= IDLE) then
      req_accept_w  <= ( (rd_accept_w  or wr_accept_w) and curr_chan_q(4) ) &
                       ( (rd_accept_w  or wr_accept_w) and curr_chan_q(3) ) &
                       ( (rd_accept_w  or wr_accept_w) and curr_chan_q(2) ) &
                       ( (rd_accept_w  or wr_accept_w) and curr_chan_q(1) ) &
                       ( (rd_accept_w  or wr_accept_w) and curr_chan_q(0) ) ;
    else
      --  arbiter new requests on line in priority order
      if (st_q /= WRWAIT) then
        if ( pending_w(4) = '1' ) then  req_accept_w  <= "10000";  end if;
        if ( pending_w(3) = '1' ) then  req_accept_w  <= "01000";  end if;
        if ( pending_w(2) = '1' ) then  req_accept_w  <= "00100";  end if;
        if ( pending_w(1) = '1' ) then  req_accept_w  <= "00010";  end if;
        if ( pending_w(0) = '1' ) then  req_accept_w  <= "00001";  end if;
      end if;
    end if;
  end process;

end rtl;
