-------------------------------------------------------------------------------
-- bus_master.vhd - Entity and architecture
-------------------------------------------------------------------------------
--
-- (c) Copyright 2014 Xilinx, Inc. All rights reserved.
--
-- This file contains confidential and proprietary information
-- of Xilinx, Inc. and is protected under U.S. and 
-- international copyright and other intellectual property
-- laws.
--
-- DISCLAIMER
-- This disclaimer is not a license and does not grant any
-- rights to the materials distributed herewith. Except as
-- otherwise provided in a valid license issued to you by
-- Xilinx, and to the maximum extent permitted by applicable
-- law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
-- WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
-- AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
-- BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
-- INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
-- (2) Xilinx shall not be liable (whether in contract or tort,
-- including negligence, or under any other theory of
-- liability) for any loss or damage of any kind or nature
-- related to, arising under or in connection with these
-- materials, including for any direct, or any indirect,
-- special, incidental, or consequential loss or damage
-- (including loss of data, profits, goodwill, or any type of
-- loss or damage suffered as a result of any action brought
-- by a third party) even if such damage or loss was
-- reasonably foreseeable or Xilinx had been advised of the
-- possibility of the same.
--
-- CRITICAL APPLICATIONS
-- Xilinx products are not designed or intended to be fail-
-- safe, or for use in any application requiring fail-safe
-- performance, such as life-support or safety devices or
-- systems, Class III medical devices, nuclear facilities,
-- applications related to the deployment of airbags, or any
-- other applications that could lead to death, personal
-- injury, or severe property or environmental damage
-- (individually and collectively, "Critical
-- Applications"). Customer assumes the sole risk and
-- liability of any use of Xilinx products in Critical
-- Applications, subject only to applicable laws and
-- regulations governing limitations on product liability.
--
-- THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
-- PART OF THIS FILE AT ALL TIMES.
--
-------------------------------------------------------------------------------
-- Filename:        bus_master.vhd
--
-- Description:     
--                  
-- VHDL-Standard:   VHDL'93/02
-------------------------------------------------------------------------------
-- Structure:   
--              bus_master.vhd
--                - srl_fifo
--                - srl_fifo
--
-------------------------------------------------------------------------------
-- Author:          stefana
--
-- History:
--   stefana 2013-11-01    First Version
--   stefana 2013-06-15    Added direct write port
--
-------------------------------------------------------------------------------
-- Naming Conventions:
--      active low signals:                     "*_n"
--      clock signals:                          "clk", "clk_div#", "clk_#x" 
--      reset signals:                          "rst", "rst_n" 
--      generics:                               "C_*" 
--      user defined types:                     "*_TYPE" 
--      state machine next state:               "*_ns" 
--      state machine current state:            "*_cs" 
--      combinatorial signals:                  "*_com" 
--      pipelined or register delay signals:    "*_d#" 
--      counter signals:                        "*cnt*"
--      clock enable signals:                   "*_ce" 
--      internal version of output port         "*_i"
--      device pins:                            "*_pin" 
--      ports:                                  - Names begin with Uppercase 
--      processes:                              "*_PROCESS" 
--      component instantiations:               "<ENTITY_>I_<#|FUNC>
-------------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;

entity bus_master is
  generic (
    C_M_AXI_DATA_WIDTH      : natural := 32;
    C_M_AXI_THREAD_ID_WIDTH : natural := 4;
    C_M_AXI_ADDR_WIDTH      : natural := 32;
    C_DATA_SIZE             : natural := 32;
    C_HAS_FIFO_PORTS        : boolean := true;
    C_HAS_DIRECT_PORT       : boolean := false
  );
  port (
    -- Bus read and write transaction
    Rd_Start      : in  std_logic;
    Rd_Addr       : in  std_logic_vector(31 downto 0);
    Rd_Len        : in  std_logic_vector(4  downto 0);
    Rd_Size       : in  std_logic_vector(1  downto 0);
    Rd_Exclusive  : in  std_logic;
    Rd_Idle       : out std_logic;
    Rd_Response   : out std_logic_vector(1  downto 0);

    Wr_Start      : in  std_logic;
    Wr_Addr       : in  std_logic_vector(31 downto 0);
    Wr_Len        : in  std_logic_vector(4  downto 0);
    Wr_Size       : in  std_logic_vector(1  downto 0);
    Wr_Exclusive  : in  std_logic;
    Wr_Idle       : out std_logic;
    Wr_Response   : out std_logic_vector(1  downto 0);

    -- Bus read and write data
    Data_Rd       : in  std_logic;
    Data_Out      : out std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
    Data_Exists   : out std_logic;

    Data_Wr       : in  std_logic;
    Data_In       : in  std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
    Data_Empty    : out std_logic;

    -- Direct write port
    Direct_Wr_Addr    : in  std_logic_vector(C_M_AXI_ADDR_WIDTH-1 downto 0);
    Direct_Wr_Len     : in  std_logic_vector(4  downto 0);
    Direct_Wr_Data    : in  std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
    Direct_Wr_Start   : in  std_logic;
    Direct_Wr_Next    : out std_logic;
    Direct_Wr_Done    : out std_logic;
    Direct_Wr_Resp    : out std_logic_vector(1 downto 0);

    -- LMB bus
    LMB_Data_Addr     : out std_logic_vector(0 to C_DATA_SIZE-1);
    LMB_Data_Read     : in  std_logic_vector(0 to C_DATA_SIZE-1);
    LMB_Data_Write    : out std_logic_vector(0 to C_DATA_SIZE-1);
    LMB_Addr_Strobe   : out std_logic;
    LMB_Read_Strobe   : out std_logic;
    LMB_Write_Strobe  : out std_logic;
    LMB_Ready         : in  std_logic;
    LMB_Wait          : in  std_logic;
    LMB_UE            : in  std_logic;
    LMB_Byte_Enable   : out std_logic_vector(0 to (C_DATA_SIZE-1)/8);

    -- AXI bus
    M_AXI_ACLK    : in  std_logic;
    M_AXI_ARESETn : in  std_logic;

    M_AXI_AWID    : out std_logic_vector(C_M_AXI_THREAD_ID_WIDTH-1 downto 0);
    M_AXI_AWADDR  : out std_logic_vector(C_M_AXI_ADDR_WIDTH-1 downto 0);
    M_AXI_AWLEN   : out std_logic_vector(7 downto 0);
    M_AXI_AWSIZE  : out std_logic_vector(2 downto 0);
    M_AXI_AWBURST : out std_logic_vector(1 downto 0);
    M_AXI_AWLOCK  : out std_logic;
    M_AXI_AWCACHE : out std_logic_vector(3 downto 0);
    M_AXI_AWPROT  : out std_logic_vector(2 downto 0);
    M_AXI_AWQOS   : out std_logic_vector(3 downto 0);
    M_AXI_AWVALID : out std_logic;
    M_AXI_AWREADY : in  std_logic;

    M_AXI_WLAST   : out std_logic;
    M_AXI_WDATA   : out std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
    M_AXI_WSTRB   : out std_logic_vector(C_M_AXI_DATA_WIDTH/8-1 downto 0);
    M_AXI_WVALID  : out std_logic;
    M_AXI_WREADY  : in  std_logic;

    M_AXI_BRESP   : in  std_logic_vector(1 downto 0);
    M_AXI_BID     : in  std_logic_vector(C_M_AXI_THREAD_ID_WIDTH-1 downto 0);
    M_AXI_BVALID  : in  std_logic;
    M_AXI_BREADY  : out std_logic;

    M_AXI_ARADDR  : out std_logic_vector(C_M_AXI_ADDR_WIDTH-1 downto 0);
    M_AXI_ARID    : out std_logic_vector(C_M_AXI_THREAD_ID_WIDTH-1 downto 0);
    M_AXI_ARLEN   : out std_logic_vector(7 downto 0);
    M_AXI_ARSIZE  : out std_logic_vector(2 downto 0);
    M_AXI_ARBURST : out std_logic_vector(1 downto 0);
    M_AXI_ARLOCK  : out std_logic;
    M_AXI_ARCACHE : out std_logic_vector(3 downto 0);
    M_AXI_ARPROT  : out std_logic_vector(2 downto 0);
    M_AXI_ARQOS   : out std_logic_vector(3 downto 0);
    M_AXI_ARVALID : out std_logic;
    M_AXI_ARREADY : in  std_logic;

    M_AXI_RLAST   : in  std_logic;
    M_AXI_RID     : in  std_logic_vector(C_M_AXI_THREAD_ID_WIDTH-1 downto 0);
    M_AXI_RDATA   : in  std_logic_vector(31 downto 0);
    M_AXI_RRESP   : in  std_logic_vector(1 downto 0);
    M_AXI_RVALID  : in  std_logic;
    M_AXI_RREADY  : out std_logic
  );
end entity bus_master;

library IEEE;
use ieee.numeric_std.all;

library mdm_v3_2;
use mdm_v3_2.all;

architecture IMP of bus_master is

  component SRL_FIFO is
    generic (
      C_DATA_BITS : natural;
      C_DEPTH     : natural
    );
    port (
      Clk         : in  std_logic;
      Reset       : in  std_logic;
      FIFO_Write  : in  std_logic;
      Data_In     : in  std_logic_vector(0 to C_DATA_BITS-1);
      FIFO_Read   : in  std_logic;
      Data_Out    : out std_logic_vector(0 to C_DATA_BITS-1);
      FIFO_Full   : out std_logic;
      Data_Exists : out std_logic
    );
  end component SRL_FIFO;

  -- Calculate WSTRB given size and low address bits
  function Calc_WSTRB (Wr_Size : std_logic_vector(1 downto 0);
                       Wr_Addr : std_logic_vector(1 downto 0)) return std_logic_vector is
  begin
    if Wr_Size = "00" then  -- Byte
      case Wr_Addr is
        when "00" => return "0001";
        when "01" => return "0010";
        when "10" => return "0100";
        when "11" => return "1000";
        when others => null;
      end case;
    end if;
    if Wr_Size = "01" then  -- Halfword
      if Wr_Addr(1) = '0' then
        return "0011";
      else
        return "1100";
      end if;
    end if;
    return "1111";          -- Word
  end function Calc_WSTRB;

  type wr_state_type  is (idle, start, wait_on_ready, wait_on_bchan);

  signal wr_state          : wr_state_type;

  signal wdata             : std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
  signal wstrb             : std_logic_vector(C_M_AXI_DATA_WIDTH/8-1 downto 0);

  signal axi_wvalid        : std_logic;                      -- internal M_AXI_WVALID
  signal axi_wr_start      : std_logic;                      -- LMB did not respond, start AXI write
  signal axi_wr_idle       : std_logic;                      -- AXI write is idle
  signal axi_wr_resp       : std_logic_vector(1  downto 0);  -- AXI write response
  signal axi_do_read       : std_logic;                      -- read word from write FIFO for AXI

  signal axi_dwr_addr      : std_logic_vector(31 downto 0);
  signal axi_dwr_len       : std_logic_vector(4  downto 0);
  signal axi_dwr_size      : std_logic_vector(1  downto 0);
  signal axi_dwr_exclusive : std_logic;
  signal axi_dwr_start     : std_logic;
  signal axi_dwr_wdata     : std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);
  signal axi_dwr_wstrb     : std_logic_vector(C_M_AXI_DATA_WIDTH/8-1 downto 0);

  signal axi_dwr_sel       : std_logic;
  signal axi_dwr_done      : std_logic;

begin  -- architecture IMP

  assert (C_DATA_SIZE = C_M_AXI_DATA_WIDTH)
    report "LMB and AXI data widths must be the same" severity FAILURE;

  Has_FIFO: if C_HAS_FIFO_PORTS generate
    type lmb_state_type is (idle, start_rd, wait_rd, start_wr, wait_wr, sample_rd, sample_wr, direct_wr);
    type rd_state_type  is (idle, start, wait_on_ready, wait_on_data);

    signal lmb_state     : lmb_state_type;
    signal rd_state      : rd_state_type;

    signal reset         : std_logic;

    signal rdata         : std_logic_vector(C_M_AXI_DATA_WIDTH-1 downto 0);

    signal do_read       : std_logic;
    signal do_write      : std_logic;

    signal lmb_addr      : std_logic_vector(4 downto 0);  -- LMB word address
    signal lmb_addr_next : std_logic_vector(4 downto 0);  -- LMB word address incremented
    signal lmb_len       : std_logic_vector(4 downto 0);  -- LMB length
    signal lmb_len_next  : std_logic_vector(4 downto 0);  -- LMB length decremented
    signal lmb_rd_idle   : std_logic;                     -- LMB read is idle
    signal lmb_wr_idle   : std_logic;                     -- LMB write is idle
    signal lmb_rd_resp   : std_logic_vector(1 downto 0);  -- LMB read response
    signal lmb_wr_resp   : std_logic_vector(1 downto 0);  -- LMB write response

    signal axi_rready    : std_logic;                     -- internal M_AXI_RREADY
    signal axi_rd_start  : std_logic;                     -- LMB did not respond, start AXI read
    signal axi_rd_idle   : std_logic;                     -- AXI read is idle
    signal axi_rd_resp   : std_logic_vector(1 downto 0);  -- AXI read response
    signal axi_do_write  : std_logic;                     -- write word to read FIFO for AXI
    signal wdata_exists  : std_logic;                     -- write FIFO has data
  begin

    reset <= not M_AXI_ARESETn;

    -- Read FIFO instantiation
    Read_FIFO : SRL_FIFO
      generic map (
        C_DATA_BITS => 32,
        C_DEPTH     => 32
      )
      port map (
        Clk         => M_AXI_ACLK,
        Reset       => reset,
        FIFO_Write  => do_write,
        Data_In     => rdata,
        FIFO_Read   => Data_Rd,
        Data_Out    => Data_Out,
        FIFO_Full   => open,
        Data_Exists => Data_Exists
      );

    -- Write FIFO instantiation
    Write_FIFO : SRL_FIFO
      generic map (
        C_DATA_BITS => 32,
        C_DEPTH     => 32
      )
      port map (
        Clk         => M_AXI_ACLK,
        Reset       => reset,
        FIFO_Write  => Data_Wr,
        Data_In     => Data_In,
        FIFO_Read   => do_read,
        Data_Out    => wdata,
        FIFO_Full   => open,
        Data_Exists => wdata_exists
      );

    -- Common signals
    Data_Empty   <= not wdata_exists;
    Rd_Idle      <= lmb_rd_idle and axi_rd_idle;
    Rd_Response  <= lmb_rd_resp or  axi_rd_resp;
    Wr_Idle      <= lmb_wr_idle and axi_wr_idle;
    Wr_Response  <= lmb_wr_resp or  axi_wr_resp;

    wstrb    <= Calc_WSTRB(Wr_Size, Wr_Addr(1 downto 0));
    rdata    <= LMB_Data_Read when (LMB_Ready = '1' and lmb_rd_idle = '0') else M_AXI_RDATA;
    do_write <= (LMB_Ready and not lmb_rd_idle) or axi_do_write;
    do_read  <= (LMB_Ready and not lmb_wr_idle) or axi_do_read;


    -- LMB implementation
    LMB_Data_Addr   <= Wr_Addr(C_M_AXI_ADDR_WIDTH-1 downto 7) & lmb_addr & Wr_Addr(1 downto 0);
    LMB_Data_Write  <= wdata;
    LMB_Byte_Enable <= wstrb;

    lmb_addr_next <= std_logic_vector(unsigned(lmb_addr) + 1);
    lmb_len_next  <= std_logic_vector(unsigned(lmb_len)  - 1);

    LMB_Executing : process (M_AXI_ACLK) is
      variable ue : std_logic;
    begin  -- process LMB_Executing
      if (M_AXI_ACLK'event and M_AXI_ACLK = '1') then
        if (M_AXI_ARESETn = '0') then
          lmb_state        <= idle;
          axi_dwr_sel      <= '0';
          axi_rd_start     <= '0';
          axi_wr_start     <= '0';
          lmb_addr         <= (others => '0');
          lmb_rd_idle      <= '1';
          lmb_wr_idle      <= '1';
          lmb_len          <= (others => '0');
          lmb_rd_resp      <= "00";
          lmb_wr_resp      <= "00";
          ue               := '0';
          LMB_Addr_Strobe  <= '0';
          LMB_Read_Strobe  <= '0';
          LMB_Write_Strobe <= '0';
        else
          axi_rd_start <= '0';
          axi_wr_start <= '0';
          case lmb_state is
            when idle =>
              lmb_addr    <= Wr_Addr(6 downto 2);
              lmb_len     <= Wr_Len;
              lmb_rd_idle <= '1';
              lmb_wr_idle <= '1';
              ue          := '0';
              if (Direct_Wr_Start = '1' and C_HAS_DIRECT_PORT) then
                lmb_state   <= direct_wr;
                axi_dwr_sel <= '1';
              end if;
              if (Rd_Start = '1') then
                lmb_state       <= start_rd;
                axi_dwr_sel     <= '0';
                lmb_rd_idle     <= '0';
                lmb_rd_resp     <= "00";
                LMB_Addr_Strobe <= '1';
                LMB_Read_Strobe <= '1';
              end if;
              if (Wr_Start = '1') then
                lmb_state        <= start_wr;
                axi_dwr_sel      <= '0';
                lmb_wr_idle      <= '0';
                lmb_wr_resp      <= "00";
                LMB_Addr_Strobe  <= '1';
                LMB_Write_Strobe <= '1';
              end if;

            when start_rd =>
              lmb_state       <= wait_rd;
              LMB_Addr_Strobe <= '0';
              LMB_Read_Strobe <= '0';

            when wait_rd =>
              lmb_state <= sample_rd;

            when sample_rd =>
              if (LMB_Ready = '1') then
                if (lmb_len = (lmb_len'range => '0')) then
                  lmb_state <= idle;
                else
                  lmb_state       <= start_rd;
                  LMB_Addr_Strobe <= '1';
                  LMB_Read_Strobe <= '1';
                end if;
                lmb_addr    <= lmb_addr_next;
                lmb_len     <= lmb_len_next;
                ue          := LMB_UE or ue;
                lmb_rd_resp <= ue & '0';
              elsif (LMB_Wait = '0') then
                lmb_state    <= idle;
                axi_rd_start <= '1';
              end if;

            when start_wr =>
              lmb_state        <= wait_wr;
              LMB_Addr_Strobe  <= '0';
              LMB_Write_Strobe <= '0';

            when wait_wr =>
              lmb_state <= sample_wr;

            when sample_wr =>
              if (LMB_Ready = '1') then
                if (lmb_len = (lmb_len'range => '0')) then
                  lmb_state <= idle;
                else
                  lmb_state        <= start_wr;
                  LMB_Addr_Strobe  <= '1';
                  LMB_Write_Strobe <= '1';
                end if;
                lmb_addr    <= lmb_addr_next;
                lmb_len     <= lmb_len_next;
                ue          := LMB_UE or ue;
                lmb_wr_resp <= ue & '0';
              elsif (LMB_Wait = '0') then
                lmb_state    <= idle;
                axi_wr_start <= '1';
              end if;

            when direct_wr =>  -- Handle AXI direct write
              if axi_dwr_done = '1' and Direct_Wr_Start = '0' then
                lmb_state   <= idle;
                axi_dwr_sel <= '0';
              end if;

            -- coverage off
            when others =>
              null;
            -- coverage on
          end case;
        end if;
      end if;
    end process LMB_Executing;

    -- AXI Read FSM
    Rd_Executing : process (M_AXI_ACLK) is
      variable rd_resp : std_logic_vector(1 downto 0);
    begin  -- process Rd_Executing
      if (M_AXI_ACLK'event and M_AXI_ACLK = '1') then  -- rising clock edge
        if (M_AXI_ARESETn = '0') then                  -- synchronous reset (active low)
          rd_resp       := "00";
          axi_rready    <= '0';
          axi_rd_idle   <= '1';
          axi_rd_resp   <= "00";
          M_AXI_ARADDR  <= (others => '0');
          M_AXI_ARLEN   <= (others => '0');
          M_AXI_ARSIZE  <= "010";               -- 32-bit accesses
          M_AXI_ARLOCK  <= '0';                 -- No locking
          M_AXI_ARVALID <= '0';
          rd_state      <= idle;
        else
          case rd_state is
            when idle =>
              rd_resp      := "00";
              axi_rd_idle  <= '1';
              if axi_rd_start = '1' then
                rd_state    <= start;
                axi_rd_idle <= '0';
                axi_rd_resp <= "00";
              end if;

            when start =>
              M_AXI_ARVALID <= '1';
              M_AXI_ARADDR  <= Rd_Addr;
              M_AXI_ARLEN   <= "000" & Rd_Len;
              M_AXI_ARSIZE  <= "0"  & Rd_Size;
              M_AXI_ARLOCK  <= Rd_Exclusive;
              rd_state      <= wait_on_ready;

            when wait_on_ready =>
              if (M_AXI_ARREADY = '1') then
                M_AXI_ARVALID <= '0';
                axi_rready    <= '1';
                rd_state      <= wait_on_data;
              end if;

            when wait_on_data =>
              if (M_AXI_RVALID = '1') then
                if rd_resp = "00" and M_AXI_RRESP /= "00" then
                  rd_resp := M_AXI_RRESP;  -- Sticky error response
                end if;
                if (M_AXI_RLAST = '1') then
                  rd_state    <= idle;
                  axi_rd_resp <= rd_resp;
                  axi_rready  <= '0';
                end if;
              end if;

            -- coverage off
            when others =>
              null;
            -- coverage on
          end case;
        end if;
      end if;
    end process Rd_Executing;

    axi_do_write <= axi_rready and M_AXI_RVALID;

  end generate Has_FIFO;

  No_FIFO: if not C_HAS_FIFO_PORTS generate
    type state_type is (idle, direct_wr);

    signal state : state_type;
  begin
    Rd_Idle          <= '1';
    Rd_Response      <= "00";
    Data_Out         <= (others => '0');
    Data_Exists      <= '0';

    Data_Empty       <= '0';
    Wr_Idle          <= '0';
    Wr_Response      <= "00";

    LMB_Data_Addr    <= (others => '0');
    LMB_Data_Write   <= (others => '0');
    LMB_Addr_Strobe  <= '0';
    LMB_Read_Strobe  <= '0';
    LMB_Write_Strobe <= '0';
    LMB_Byte_Enable  <= (others => '0');
    
    M_AXI_ARADDR     <= (others => '0');
    M_AXI_ARLEN      <= (others => '0');
    M_AXI_ARSIZE     <= (others => '0');
    M_AXI_ARLOCK     <= '0';
    M_AXI_ARVALID    <= '0';

    wdata            <= (others => '0');
    wstrb            <= (others => '0');
    axi_wr_start     <= '0';

    AXI_Direct_Write: process (M_AXI_ACLK) is
    begin  -- process AXI_Direct_Write
      if (M_AXI_ACLK'event and M_AXI_ACLK = '1') then  -- rising clock edge
        if (M_AXI_ARESETn = '0') then                  -- synchronous reset (active low)
          state       <= idle;
          axi_dwr_sel <= '0';
        else
          case state is
            when idle =>
              if Direct_Wr_Start = '1' then
                state       <= direct_wr;
                axi_dwr_sel <= '1';
              end if;
            when direct_wr =>
              if axi_dwr_done = '1' and Direct_Wr_Start = '0' then
                state       <= idle;
                axi_dwr_sel <= '0';
              end if;
            -- coverage off
            when others =>
              null;
            -- coverage on
          end case;
        end if;
      end if;
    end process AXI_Direct_Write;

  end generate No_FIFO;

  Has_Direct_Write: if C_HAS_DIRECT_PORT generate
  begin
    Direct_Wr_Next    <= axi_do_read     when axi_dwr_sel = '1' else '0';
    Direct_Wr_Done    <= axi_dwr_done    when axi_dwr_sel = '1' else '0';
    Direct_Wr_Resp    <= axi_wr_resp;
 
    axi_dwr_addr      <= Direct_Wr_Addr  when axi_dwr_sel = '1' else Wr_Addr;
    axi_dwr_len       <= Direct_Wr_Len   when axi_dwr_sel = '1' else Wr_Len;
    axi_dwr_size      <= "10"            when axi_dwr_sel = '1' else Wr_Size;
    axi_dwr_exclusive <= '0'             when axi_dwr_sel = '1' else Wr_Exclusive;
    axi_dwr_start     <= Direct_Wr_Start when axi_dwr_sel = '1' else axi_wr_start;
    axi_dwr_wdata     <= Direct_Wr_Data  when axi_dwr_sel = '1' else wdata;
    axi_dwr_wstrb     <= "1111"          when axi_dwr_sel = '1' else wstrb;
  end generate Has_Direct_Write;

  No_Direct_Write: if not C_HAS_DIRECT_PORT generate
  begin
    Direct_Wr_Next    <= '0';
    Direct_Wr_Done    <= '0';
    Direct_Wr_Resp    <= "00";

    axi_dwr_addr      <= Wr_Addr;
    axi_dwr_len       <= Wr_Len;
    axi_dwr_size      <= Wr_Size;
    axi_dwr_exclusive <= Wr_Exclusive;
    axi_dwr_start     <= axi_wr_start;
    axi_dwr_wdata     <= wdata;
    axi_dwr_wstrb     <= wstrb;
  end generate No_Direct_Write;

  -- AW signals constant values
  M_AXI_AWPROT  <= "010";               -- Non-secure data accesses only
  M_AXI_AWQOS   <= "0000";              -- Don't participate in QoS handling
  M_AXI_AWID    <= (others => '0');     -- ID fixed to zero
  M_AXI_AWBURST <= "01";                -- Only INCR bursts
  M_AXI_AWCACHE <= "0011";              -- Set "Modifiable" and "Bufferable" bit

  -- AR signals constant values
  M_AXI_ARPROT  <= "010";               -- Normal and non-secure Data access only
  M_AXI_ARQOS   <= "0000";              -- Don't participate in QoS handling
  M_AXI_ARID    <= (others => '0');     -- ID fixed to zero
  M_AXI_ARBURST <= "01";                -- Only INCR bursts
  M_AXI_ARCACHE <= "0011";              -- Set "Modifiable" and "Bufferable" bit

  -- R signals constant values
  M_AXI_RREADY <= '1';                  -- Always accepting read data

  -- B signals value
  M_AXI_BREADY <= '1' when wr_state = wait_on_bchan else '0';

  -- AXI Write FSM
  Wr_Executing : process (M_AXI_ACLK) is
    variable address_done : boolean;
    variable data_done    : boolean;
    variable len          : std_logic_vector(4 downto 0);
  begin  -- process Wr_Executing
    if (M_AXI_ACLK'event and M_AXI_ACLK = '1') then   -- rising clock edge
      if (M_AXI_ARESETn = '0') then             -- synchronous reset (active low)
        axi_wr_idle   <= '1';
        axi_wr_resp   <= "00";
        axi_wvalid    <= '0';
        M_AXI_WVALID  <= '0';
        M_AXI_WLAST   <= '0';
        M_AXI_WSTRB   <= (others => '0');
        M_AXI_AWADDR  <= (others => '0');
        M_AXI_AWLEN   <= (others => '0');
        M_AXI_AWSIZE  <= "010";               -- 32-bit accesses
        M_AXI_AWLOCK  <= '0';                 -- No locking
        M_AXI_AWVALID <= '0';
        axi_dwr_done  <= '0';
        address_done  := false;
        data_done     := false;
        len           := (others => '0');
        wr_state      <= idle;
      else
        case wr_state is
          when idle =>
            axi_wr_idle  <= '1';
            axi_dwr_done <= '0';
            address_done := false;
            data_done    := false;
            len          := (others => '0');
            if axi_dwr_start = '1' then
              wr_state    <= start;
              axi_wr_idle <= '0';
              axi_wr_resp <= "00";
            end if;

          when start =>
            M_AXI_WLAST   <= '0';
            M_AXI_AWVALID <= '1';
            M_AXI_AWADDR  <= axi_dwr_addr;
            M_AXI_AWLEN   <= "000" & axi_dwr_len;
            M_AXI_AWSIZE  <= "0" & axi_dwr_size;
            M_AXI_AWLOCK  <= axi_dwr_exclusive;

            axi_wvalid    <= '1';
            M_AXI_WVALID  <= '1';
            if axi_dwr_len = "00000" then
              M_AXI_WLAST <= '1';
            end if;
            M_AXI_WSTRB   <= axi_dwr_wstrb;

            len           := axi_dwr_len;
            wr_state      <= wait_on_ready;

          when wait_on_ready =>
            if M_AXI_AWREADY = '1' then
              M_AXI_AWVALID <= '0';
              address_done := true;              
            end if;
            if M_AXI_WREADY = '1' then
              if len = "00000" then
                axi_wvalid   <= '0';
                M_AXI_WVALID <= '0';
                data_done    := true;
              else
                if len = "00001" then
                  M_AXI_WLAST <= '1';
                end if;
                len := std_logic_vector(unsigned(len) - 1);
              end if;
            end if;
            if (address_done and data_done) then
              wr_state <= wait_on_bchan;
            end if;

          when wait_on_bchan =>
            if (M_AXI_BVALID = '1') then
              wr_state     <= idle;
              axi_dwr_done <= '1';
              axi_wr_resp  <= M_AXI_BRESP;
            end if;

          -- coverage off
          when others =>
            null;
          -- coverage on
        end case;
      end if;
    end if;
  end process Wr_Executing;

  axi_do_read <= axi_wvalid and M_AXI_WREADY;

  M_AXI_WDATA <= axi_dwr_wdata;

end architecture IMP;
