//============================================================================================
// AXI-MM rules to be followed:
// The master must not wait for the slave to assert AWREADY or WREADY before asserting AWVALID or WVALID
// The slave can wait for AWVALID or WVALID, or both, before asserting AWREADY
// The slave can wait for AWVALID or WVALID, or both, before asserting WREADY
// The slave must wait for both AWVALID and AWREADY to be asserted before asserting BVALID
// The slave must wait for WVALID, WREADY, and WLAST to be asserted before asserting BVALID
// The slave must not wait for the master to assert BREADY before asserting BVALID
// The master can wait for BVALID before asserting BREADY.
//============================================================================================

module axi_master #(
    // AXI Master Interface Parameters
    parameter   M_ID_WIDTH        = 4,  
    parameter   M_ADDR_WIDTH      = 32, 
    parameter   M_LEN_WIDTH       = 4,  
    parameter   M_DATA_WIDTH      = 32,
    parameter   NUM_BYTES_WIDTH   = 32 
 //   parameter   BASE_ADDR_MM      = 32'hC0000000
  )(    
    input                                       ps_image_read,
    input                                       m_clk,
    input                                       m_resetn,
    input                                       pio_valid_in,
    input       [31:0]                          pio_data_in,
    input       [M_ADDR_WIDTH-1:0]              BASE_ADDR_MM,
    input       [NUM_BYTES_WIDTH-1:0]           NUM_BYTES,
    
    output  reg                                 m_awvalid,
    input                                       m_awready,
    output  reg [M_ID_WIDTH-1:0]                m_awid,   
    output  reg [M_ADDR_WIDTH-1:0]              m_awaddr, 
    output  reg [M_LEN_WIDTH-1:0]               m_awlen,  
    output  reg [2:0]                           m_awsize, 
    output  reg [1:0]                           m_awburst,
    output  reg [2:0]                           m_awprot, 
    output  reg [3:0]                           m_awcache,

    output  reg                                 m_wvalid, 
    input                                       m_wready, 
    output  reg [M_ID_WIDTH-1:0]                m_wid,    
    output  reg [M_DATA_WIDTH-1:0]              m_wdata,  
    output  reg [(M_DATA_WIDTH/8)-1:0]          m_wstrb,  
    output  reg                                 m_wlast,  

    input                                       m_bvalid, 
    output                                      m_bready, 
    input   [M_ID_WIDTH-1:0]                    m_bid,    
    input   [1:0]                               m_bresp,  

    output  reg                                 m_arvalid,
    input                                       m_arready,
    output  reg [M_ID_WIDTH-1:0]                m_arid,   
    output  reg [M_ADDR_WIDTH-1:0]              m_araddr, 
    output  reg [M_LEN_WIDTH-1:0]               m_arlen,  
    output  reg [2:0]                           m_arsize, 
    output  reg [1:0]                           m_arburst,
    output  reg [2:0]                           m_arprot, 
    output  reg [3:0]                           m_arcache,

    input                                       m_rvalid, 
    output                                      m_rready, 
    input   [M_ID_WIDTH-1:0]                    m_rid,    
    input   [M_DATA_WIDTH-1:0]                  m_rdata,  
    input   [1:0]                               m_rresp,  
    input                                       m_rlast,   
    output  reg                                 trnfr_cmpl   
  );

localparam IDLE                = 3'h0;
localparam WAIT_FOR_PIO_VALID  = 3'h1;
localparam WRITE_REGISTER      = 3'h2;
localparam WAIT_FOR_AWREADY    = 3'h3;
localparam SEND_WDATA          = 3'h4;
localparam WAIT_FOR_WREADY     = 3'h5;
localparam WAIT_FOR_BRESP      = 3'h6;
localparam WAIT_FOR_IMG_RD     = 3'h7;

reg [2:0]  state;
reg [31:0] pio_data_in_r1;
reg [31:0] data_to_send ; //= NUM_BYTES;
//reg [0:0]  flag = 1'b0 ;

always@(posedge m_clk)
  if(~m_resetn)
  begin
      state <= IDLE;
      m_awvalid <= 1'b0;
      m_awsize  <= 3'd2;  // 4 bytes
      m_awcache <= 4'd0;
      m_awprot  <= 3'd0;
      m_awburst <= 2'd1;
      m_wvalid  <= 1'b0;
      m_wlast   <= 1'b0;

      m_arvalid <= 1'b0;
      m_arsize  <= 3'b000; 
      m_arburst <= 2'b00;
      m_arprot  <= 3'b000; 
      m_arcache <= 4'h0;
      m_awaddr  <= BASE_ADDR_MM; 
      data_to_send <= NUM_BYTES;
      trnfr_cmpl <= 1'b0;
      //flag       <= 1'b0;
  end
  else
  begin
    case(state)
      IDLE: begin
              //if(!flag) 
               //begin       
                state <= WAIT_FOR_PIO_VALID;                      
                m_arid    <= {M_ID_WIDTH{1'b0}};   
                m_araddr  <= {M_ADDR_WIDTH{1'b0}}; 
                m_arlen   <= {M_LEN_WIDTH{1'b0}};  
                m_awaddr  <= BASE_ADDR_MM;
                data_to_send <= NUM_BYTES;
              // end  
              //else begin
              //  state <= WAIT_FOR_IMG_RD;
              //end     
            end
      WAIT_FOR_IMG_RD:
            begin
              if(ps_image_read) begin
               trnfr_cmpl <= 1'b0;
               //flag <= 1'b0;
               state <= WAIT_FOR_PIO_VALID;                      
               m_arid    <= {M_ID_WIDTH{1'b0}};   
               m_araddr  <= {M_ADDR_WIDTH{1'b0}}; 
               m_arlen   <= {M_LEN_WIDTH{1'b0}};  
               m_awaddr  <= BASE_ADDR_MM;
              end
              else begin
               state <= WAIT_FOR_IMG_RD;
              end 
            end             
      WAIT_FOR_PIO_VALID: 
            begin
            if(pio_valid_in) begin

              // m_awaddr  <= m_awaddr;
               pio_data_in_r1 <= pio_data_in;
               m_awvalid <= 1'b1;
               m_awlen   <= {M_LEN_WIDTH{1'b0}};
               m_awsize  <= 3'd2;  // 4 bytes
               m_awid    <= {M_ID_WIDTH{1'b0}};

              if(!m_awready)
              state <= WAIT_FOR_AWREADY;
              else
              state <= SEND_WDATA;

            end
            else begin
               state <= WAIT_FOR_PIO_VALID;
            end
            end
      WAIT_FOR_AWREADY:
            begin
              if(m_awready) begin
                 m_awaddr <= m_awaddr + 'd4;
                 m_awvalid <= 1'b0;
                 state <= SEND_WDATA;
              end
              // Hold AWVALID until AWREADY is asserted
              else begin
                m_awvalid <= m_awvalid;
                state <= WAIT_FOR_AWREADY;
              end
            end
      SEND_WDATA:
            begin

              if(m_awready) begin
                 m_awaddr <= m_awaddr + 'd4;
                 m_awvalid <= 1'b0;
              end
              // Hold AWVALID until AWREADY is asserted
              else begin
                m_awvalid <= m_awvalid;
              end

              m_wdata  <= pio_data_in_r1;

              m_wvalid <= 1'b1;
              m_wstrb  <= 'hF;
              m_wlast  <= 1'b1;
              m_wid    <= 'd0;
              // If WREADY is already asserted, wait for BRESP, else wait for WREADY
              if(!m_wready)
              state <= WAIT_FOR_WREADY;
              else
              state <= WAIT_FOR_BRESP;
            end
      WAIT_FOR_WREADY:
            begin

              if(m_wready) begin
                 m_wvalid <= 1'b0;
                 m_wlast  <= 1'b0;
                 // Write succesful, wait for mm2s event
                 if(m_bvalid && (m_bresp == 2'b00))
                 state <= WAIT_FOR_PIO_VALID;
                 // ERROR case, move to IDLE and wait for next interrupt from DMA
                 else if(m_bvalid && (m_bresp != 2'b00))
                 state <= IDLE;
                 else
                 state <= WAIT_FOR_BRESP;
              end
              else begin
                 m_wvalid <= m_wvalid;
                 m_wlast  <= m_wlast;
                 state    <= WAIT_FOR_WREADY;
              end
            end
      WAIT_FOR_BRESP:
            begin

              if(m_wready) begin
                 m_wvalid <= 1'b0;
                 m_wlast  <= 1'b0;
              end
              else begin
                 m_wvalid <= m_wvalid;
                 m_wlast  <= m_wlast;
              end

                 // Write succesful, wait for mm2s event 
                  // if(m_bvalid && (m_bresp == 2'b00))
                 // state <= WAIT_FOR_PIO_VALID;
                 if(m_bvalid && (m_bresp == 2'b00)) begin
                   if(data_to_send > 'd4) begin
                     data_to_send <= data_to_send - 'd4;
                     state <= WAIT_FOR_PIO_VALID;
                   end
                   else begin
                     trnfr_cmpl <= 1'b1;
                     data_to_send <= 'd0;
                     //flag <= 1'b1;
                     state <= WAIT_FOR_IMG_RD;
                   end  
                 end
                 // ERROR case, move to IDLE and wait for next interrupt from DMA
                 else if(m_bvalid && (m_bresp != 2'b00))
                 state <= IDLE;
                 else
                 state <= WAIT_FOR_BRESP;
            end
     endcase
end
 
    

    
assign m_rready = 1'b1;
assign m_bready = 1'b1;

endmodule
