/**
 * $Id: axi_slave.v 961 2014-01-21 11:40:39Z matej.oblak $
 *
 * @brief Red Pitaya symplified AXI slave.
 *
 * @Author Matej Oblak
 *
 * (c) Red Pitaya  http://www.redpitaya.com
 *
 * This part of code is written in Verilog hardware description language (HDL).
 * Please visit http://en.wikipedia.org/wiki/Verilog
 * for more details on the language used herein.
 */



/**
 * GENERAL DESCRIPTION:
 *
 * AXI slave used also for simple bus master.
 *
 *
 *                     /------\
 *   WR ADDRESS  ----> |  WR  |
 *   WR DATA     ----> |      | -----------
 *   WR RESPONSE <---- |  CH  |            |
 *                     \------/       /--------\
 *                                    | SIMPLE | ---> WR/RD ADDRRESS
 *   AXI                              |        | ---> WR DATA
 *                                    |   RP   | <--- RD DATA
 *                                    |  BUS   | <--- ACKNOWLEDGE
 *                     /------\       \--------/
 *   RD ADDRESS  ----> |  RD  |            |
 *   RD DATA     <---- |  CH  | -----------
 *                     \------/
 *
 *
 * Because AXI bus is quite complex simplier bus was created.
 *
 * It combines write and read channel, where write has bigger priority. Command
 * is then send forward to red pitaya bus. When wite or read acknowledge is
 * received AXI response is created and new AXI is accepted.
 *
 * To prevent AXI lockups because no response is received, this slave creates its
 * own after 32 cycles (ack_cnt).
 *
 */




module axi_slave
#(
parameter           AXI_DW         =  64           , // data width (8,16,...,1024)
parameter           AXI_AW         =  32           , // address width
parameter           AXI_IW         =   8           , // ID width
parameter           AXI_SW         = AXI_DW >> 3     // strobe width - 1 bit for every data byte
)
(
 // global signals
   input                     axi_clk_i      ,  //!< AXI global clock
   input                     axi_rstn_i     ,  //!< AXI global reset

 // axi write address channel
   input      [ AXI_IW-1: 0] axi_awid_i     ,  //!< AXI write address ID
   input      [ AXI_AW-1: 0] axi_awaddr_i   ,  //!< AXI write address
   input      [      4-1: 0] axi_awlen_i    ,  //!< AXI write burst length
   input      [      3-1: 0] axi_awsize_i   ,  //!< AXI write burst size
   input      [      2-1: 0] axi_awburst_i  ,  //!< AXI write burst type
   input      [      2-1: 0] axi_awlock_i   ,  //!< AXI write lock type
   input      [      4-1: 0] axi_awcache_i  ,  //!< AXI write cache type
   input      [      3-1: 0] axi_awprot_i   ,  //!< AXI write protection type
   input                     axi_awvalid_i  ,  //!< AXI write address valid
   output                    axi_awready_o  ,  //!< AXI write ready

 // axi write data channel
   input      [ AXI_IW-1: 0] axi_wid_i      ,  //!< AXI write data ID
   input      [ AXI_DW-1: 0] axi_wdata_i    ,  //!< AXI write data
   input      [ AXI_SW-1: 0] axi_wstrb_i    ,  //!< AXI write strobes
   input                     axi_wlast_i    ,  //!< AXI write last
   input                     axi_wvalid_i   ,  //!< AXI write valid
   output                    axi_wready_o   ,  //!< AXI write ready

 // axi write response channel
   output     [ AXI_IW-1: 0] axi_bid_o      ,  //!< AXI write response ID
   output reg [      2-1: 0] axi_bresp_o    ,  //!< AXI write response
   output reg                axi_bvalid_o   ,  //!< AXI write response valid
   input                     axi_bready_i   ,  //!< AXI write response ready

 // axi read address channel
   input      [ AXI_IW-1: 0] axi_arid_i     ,  //!< AXI read address ID
   input      [ AXI_AW-1: 0] axi_araddr_i   ,  //!< AXI read address
   input      [      4-1: 0] axi_arlen_i    ,  //!< AXI read burst length
   input      [      3-1: 0] axi_arsize_i   ,  //!< AXI read burst size
   input      [      2-1: 0] axi_arburst_i  ,  //!< AXI read burst type
   input      [      2-1: 0] axi_arlock_i   ,  //!< AXI read lock type
   input      [      4-1: 0] axi_arcache_i  ,  //!< AXI read cache type
   input      [      3-1: 0] axi_arprot_i   ,  //!< AXI read protection type
   input                     axi_arvalid_i  ,  //!< AXI read address valid
   output                    axi_arready_o  ,  //!< AXI read address ready

 // axi read data channel
   output     [ AXI_IW-1: 0] axi_rid_o      ,  //!< AXI read response ID
   output reg [ AXI_DW-1: 0] axi_rdata_o    ,  //!< AXI read data
   output reg [      2-1: 0] axi_rresp_o    ,  //!< AXI read response
   output reg                axi_rlast_o    ,  //!< AXI read last
   output reg                axi_rvalid_o   ,  //!< AXI read response valid
   input                     axi_rready_i   ,  //!< AXI read response ready

 // RP system read/write channel
   output     [ AXI_AW-1: 0] sys_addr_o     ,  //!< system bus read/write address.
   output     [ AXI_DW-1: 0] sys_wdata_o    ,  //!< system bus write data.
   output reg [ AXI_SW-1: 0] sys_sel_o      ,  //!< system bus write byte select.
   output reg                sys_wen_o      ,  //!< system bus write enable.
   output reg                sys_ren_o      ,  //!< system bus read enable.
   input      [ AXI_DW-1: 0] sys_rdata_i    ,  //!< system bus read data.
   input                     sys_err_i      ,  //!< system bus error indicator.
   input                     sys_ack_i         //!< system bus acknowledge signal.
);


//---------------------------------------------------------------------------------
//
//  AXI slave Module
wire                 ack         ;
reg   [      6-1: 0] ack_cnt     ;

reg                  rd_do       ;
reg   [ AXI_IW-1: 0] rd_arid     ;
reg   [ AXI_AW-1: 0] rd_araddr   ;
reg                  rd_error    ;
wire                 rd_errorw   ;

reg                  wr_do       ;
reg   [ AXI_IW-1: 0] wr_awid     ;
reg   [ AXI_AW-1: 0] wr_awaddr   ;
reg   [ AXI_IW-1: 0] wr_wid      ;
reg   [ AXI_DW-1: 0] wr_wdata    ;
reg                  wr_error    ;
wire                 wr_errorw   ;

assign wr_errorw = (axi_awlen_i != 4'h0) || (axi_awsize_i != 3'b010); // error if write burst and more/less than 4B transfer
assign rd_errorw = (axi_arlen_i != 4'h0) || (axi_arsize_i != 3'b010); // error if read burst and more/less than 4B transfer

always @(posedge axi_clk_i) begin
   if (axi_rstn_i == 1'b0) begin
      rd_do    <= 1'b0 ;
      rd_error <= 1'b0 ;
   end
   else begin
      if (axi_arvalid_i && !rd_do && !axi_awvalid_i && !wr_do) // accept just one read request - write has priority
         rd_do  <= 1'b1 ;
      else if (axi_rready_i && rd_do && ack)
         rd_do  <= 1'b0 ;

      if (axi_arvalid_i && axi_arready_o) begin // latch ID and address
         rd_arid   <= axi_arid_i   ;
         rd_araddr <= axi_araddr_i ;
         rd_error  <= rd_errorw    ;
      end
   end
end


always @(posedge axi_clk_i) begin
   if (axi_rstn_i == 1'b0) begin
      wr_do    <= 1'b0 ;
      wr_error <= 1'b0 ;
   end
   else begin
      if (axi_awvalid_i && !wr_do && !rd_do) // accept just one write request - if idle
         wr_do  <= 1'b1 ;
      else if (axi_bready_i && wr_do && ack)
         wr_do  <= 1'b0 ;

      if (axi_awvalid_i && axi_awready_o) begin // latch ID and address
         wr_awid   <= axi_awid_i   ;
         wr_awaddr <= axi_awaddr_i ;
         wr_error  <= wr_errorw    ;
      end

      if (axi_wvalid_i && wr_do) begin // latch ID and write data
         wr_wid    <= axi_wid_i    ;
         wr_wdata  <= axi_wdata_i  ;
      end
   end
end





assign axi_awready_o = !wr_do && !rd_do                      ;
assign axi_wready_o  = (wr_do && axi_wvalid_i) || (wr_errorw && axi_wvalid_i)    ;
assign axi_bid_o     = wr_awid                               ;
//assign axi_bresp_o   = {wr_error,1'b0}                       ;  // 2'b10 SLVERR
//assign axi_bvalid_o  = (sys_wen_o && axi_bready_i) || (wr_error && axi_bready_i)      ;

assign axi_arready_o = !rd_do && !wr_do && !axi_awvalid_i     ;
assign axi_rid_o     = rd_arid                                ;
//assign axi_rdata_o   = sys_rdata_i                            ;

always @(posedge axi_clk_i) begin
   if (axi_rstn_i == 1'b0) begin
      axi_bvalid_o  <= 1'b0 ;
      axi_bresp_o   <= 2'h0 ;
      axi_rlast_o   <= 1'b0 ;
      axi_rvalid_o  <= 1'b0 ;
      axi_rresp_o   <= 2'h0 ;
   end
   else begin
      axi_bvalid_o  <= wr_do && ack  ;
      axi_bresp_o   <= {(wr_error || ack_cnt[5]),1'b0} ;  // 2'b10 SLVERR    2'b00 OK
      axi_rlast_o   <= rd_do && ack  ;
      axi_rvalid_o  <= rd_do && ack  ;
      axi_rresp_o   <= {(rd_error || ack_cnt[5]),1'b0} ;  // 2'b10 SLVERR    2'b00 OK
      axi_rdata_o   <= sys_rdata_i   ;
   end
end

// acknowledge protection
always @(posedge axi_clk_i) begin
   if (axi_rstn_i == 1'b0) begin
      ack_cnt   <= 6'h0 ;
   end
   else begin
      if ((axi_arvalid_i && axi_arready_o) || (axi_awvalid_i && axi_awready_o))  // rd || wr request
         ack_cnt <= 6'h1 ;
      else if (ack)
         ack_cnt <= 6'h0 ;
      else if (|ack_cnt)
         ack_cnt <= ack_cnt + 6'h1 ;
   end
end

assign ack = sys_ack_i || ack_cnt[5] || (rd_do && rd_errorw) || (wr_do && wr_errorw); // bus acknowledge or timeout or error





//------------------------------------------
//  Simple slave interface

always @(posedge axi_clk_i) begin
   if (axi_rstn_i == 1'b0) begin
      sys_wen_o  <= 1'b0 ;
      sys_ren_o  <= 1'b0 ;
      sys_sel_o  <= {AXI_SW{1'b0}} ;
   end
   else begin
      sys_wen_o  <= wr_do && axi_wvalid_i && !wr_errorw ;
      sys_ren_o  <= axi_arvalid_i && axi_arready_o && !rd_errorw ;
      sys_sel_o  <= {AXI_SW{1'b1}} ;
   end
end

assign sys_addr_o  = rd_do ? rd_araddr : wr_awaddr  ;
assign sys_wdata_o = wr_wdata                       ;





endmodule