//-------------------------------------------------------------------
//
//  Filename      : vga_axi_if.v
//  Author        : Huang Lei Lei
//  Created       : 2015-08-09
//  Description   : interface between vga and axi
//
//-------------------------------------------------------------------


module vga_axi_if(
  // global
  axi_clk         ,
  axi_rstn        ,
  vga_ctrl_clk    ,
  vga_ctrl_rstn   ,
  // control
  vga_en          ,
  // gm
  gm_maddr        ,
  gm_mburst       ,
  gm_mcache       ,
  gm_mdata        ,
  gm_mid          ,
  gm_mlen         ,
  gm_mlock        ,
  gm_mprot        ,
  gm_mread        ,
  gm_mready       ,
  gm_msize        ,
  gm_mwrite       ,
  gm_mwstrb       ,
  gm_saccept      ,
  gm_sdata        ,
  gm_sid          ,
  gm_slast        ,
  gm_sresp        ,
  gm_svalid       ,
  // vga
  oEnable         ,
  iRequest        ,
  oRed            ,
  oGreen          ,
  oBlue           ,
  // fifo err
  vga_fifo_err
  );


//*** PARAMETER DECLARATION ****************************************************

  parameter    AXI_DW            = 64            ,
               AXI_AW            = 32            ,
               AXI_RID           = 1             ;

  parameter    BST_LEN           = 16            ;

  parameter    IDLE_READ         = 0             ,
               RUN_READ          = 1             ;

  parameter    RGB_ADR           = 640*480*4     ,
               HON_LEN           = 640*4         ,
               VER_LEN           = 480           ;

  parameter    VGA_WR_DEPTH      = 256           ,
               VGA_RD_DEPTH      = 256*2         ;


//*** INPUT/OUTPUT DECLARATION *************************************************

  // global
  input                          axi_clk         ;
  input                          axi_rstn        ;
  input                          vga_ctrl_clk    ;
  input                          vga_ctrl_rstn   ;

  // control
  input                          vga_en          ;

  // gm
  input                          gm_saccept      ;
  input      [AXI_DW-1   : 0]    gm_sdata        ;
  input      [3          : 0]    gm_sid          ;
  input                          gm_slast        ;
  input      [2          : 0]    gm_sresp        ;
  input                          gm_svalid       ;

  output     [AXI_AW-1   : 0]    gm_maddr        ;
  output     [1          : 0]    gm_mburst       ;
  output     [3          : 0]    gm_mcache       ;
  output     [AXI_DW-1   : 0]    gm_mdata        ;
  output     [3          : 0]    gm_mid          ;
  output     [3          : 0]    gm_mlen         ;
  output                         gm_mlock        ;
  output     [2          : 0]    gm_mprot        ;
  output reg                     gm_mread        ;
  output                         gm_mready       ;
  output     [2          : 0]    gm_msize        ;
  output                         gm_mwrite       ;
  output     [AXI_DW/8-1 : 0]    gm_mwstrb       ;

  // vga
  output reg                     oEnable         ;
  input                          iRequest        ;
  output reg [9          : 0]    oRed            ;
  output reg [9          : 0]    oGreen          ;
  output reg [9          : 0]    oBlue           ;

  // fifo err
  output reg [1          : 0]    vga_fifo_err    ;


//*** WIRE/REG DECLARATION *****************************************************

  // global
  reg                            stop_axi_r      ;
  reg                            stop_vga_r      ;

  // gm
  reg                            state_read      ;
  reg        [31         : 0]    addr_offset     ;

  // fifo
  wire                           fifo_rst        ;

  wire                           wr_clk          ;
  wire                           wr_req          ;
  wire       [AXI_DW-1   : 0]    wr_dat          ;
  wire       [8          : 0]    wr_cnt          ;
  wire                           wr_full         ;

  wire                           rd_clk          ;
  wire                           rd_req          ;
  wire       [31         : 0]    rd_dat          ;
  wire       [9          : 0]    rd_cnt          ;
  wire                           rd_empty        ;

  wire                           wr_start        ;
  wire                           wr_wait         ;

  wire                           rd_ready        ;


//*** MAIN BODY ****************************************************************

//*** GLOBAL *************************************

  // stop_axi_r
  always @(posedge axi_clk or negedge axi_rstn ) begin
    if( !axi_rstn )
      stop_axi_r <= 1 ;
    else begin
      stop_axi_r <= !vga_en ;
    end
  end

  // stop_vga_r
  always @(posedge vga_ctrl_clk or negedge vga_ctrl_rstn ) begin
    if( !vga_ctrl_rstn )
      stop_vga_r <= 1 ;
    else begin
      stop_vga_r <= !vga_en ;
    end
  end


//*** GM *****************************************

  // regular settings
  assign gm_mburst    = 2'b01     ;
  assign gm_mcache    = 4'b0000   ;
  assign gm_mdata     = 0         ;
  assign gm_mid       = AXI_RID   ;
  assign gm_mlen      = BST_LEN-1 ;
  assign gm_mlock     = 0         ;
  assign gm_mprot     = 3'b000    ;
  assign gm_mready    = 1         ;
  assign gm_msize     = 3'b011    ;    // 64 bits
  assign gm_mwrite    = 0         ;
  assign gm_mwstrb    = 8'hff     ;    // 8 bytes

  // state_read
  always @(posedge axi_clk or negedge axi_rstn) begin
    if( !axi_rstn )
      state_read <= IDLE_READ ;
    else if( stop_axi_r )
      state_read <= IDLE_READ ;
    else if( wr_start )
      state_read <= RUN_READ  ;
    else if( wr_wait ) begin
      state_read <= IDLE_READ ;
    end
  end

  // gm_mread
  always @(posedge axi_clk or negedge axi_rstn) begin
    if( !axi_rstn )
      gm_mread <= 0 ;
    else if( stop_axi_r )
      gm_mread <= 0 ;
    else if( gm_mread & gm_saccept )
      gm_mread <= 0 ;
    else if( state_read == RUN_READ ) begin
      gm_mread <= 1 ;
    end
  end

  // gm_maddr
  assign gm_maddr = RGB_ADR + addr_offset ;

  // addr_offset
  always @(posedge axi_clk or negedge axi_rstn) begin
    if( !axi_rstn )
      addr_offset <= 0 ;
    else if( stop_axi_r )
      addr_offset <= 0 ;
    else if( gm_mread && gm_saccept ) begin
      if( addr_offset == ( HON_LEN * VER_LEN - BST_LEN * 8 ) )
        addr_offset <= 0 ;
      else begin
        addr_offset <= addr_offset + BST_LEN * 8 ;
      end
    end
  end


//*** FIFO ***************************************

  // fifo dut
  fifo_vga fifo_vga0(
    // global
    .aclr           ( fifo_rst    ),
    // wr_side
    .wrclk          ( wr_clk      ),
    .wrreq          ( wr_req      ),
    .data           ( wr_dat      ),
    .wrusedw        ( wr_cnt      ),
    .wrfull         ( wr_full     ),
    // rd_side
    .rdclk          ( rd_clk      ),
    .rdreq          ( rd_req      ),
    .q              ( rd_dat      ),
    .rdusedw        ( rd_cnt      ),
    .rdempty        ( rd_empty    )
    );

  assign wr_clk = axi_clk      ;
  assign rd_clk = vga_ctrl_clk ;

  assign wr_req = gm_svalid    ;
  assign wr_dat = gm_sdata     ;
  assign rd_req = iRequest     ;

  assign wr_start = ( wr_cnt <= (VGA_WR_DEPTH>>2) );
  assign wr_wait  = ( wr_cnt >  (VGA_WR_DEPTH>>2) );
  assign rd_ready = ( rd_cnt >= (VGA_RD_DEPTH>>2) );

  // fifo rst
  assign fifo_rst = ! ( axi_rstn & vga_en );

  // vga_fifo_err[0]: wr
  always @(posedge axi_clk or negedge axi_rstn) begin
    if( !axi_rstn )
      vga_fifo_err[0] <= 0 ;
    else if( wr_full & wr_req ) begin
      vga_fifo_err[0] <= 1 ;
    end
  end

  // vga_fifo_err[1]: rd
  always @(posedge vga_ctrl_clk or negedge vga_ctrl_rstn) begin
    if( !vga_ctrl_rstn )
      vga_fifo_err[1] <= 0 ;
    else if( rd_empty & rd_req ) begin
      vga_fifo_err[1] <= 1 ;
    end
  end


//*** VGA ****************************************
  
  always @(posedge vga_ctrl_clk or negedge vga_ctrl_rstn ) begin
    if( !vga_ctrl_rstn ) begin
      oRed   <= 0 ;
      oGreen <= 0 ;
      oBlue  <= 0 ;
    end
    else begin
      oRed   <= { rd_dat[07:00] ,2'b0 };
      oGreen <= { rd_dat[15:08] ,2'b0 };
      oBlue  <= { rd_dat[23:16] ,2'b0 };
    end
  end

  always @(posedge vga_ctrl_clk or negedge vga_ctrl_rstn ) begin
    if( !vga_ctrl_rstn )
      oEnable <= 0 ;
    else if( stop_vga_r )
      oEnable <= 0 ;
    else if( rd_ready ) begin
      oEnable <= 1 ;
    end
  end

endmodule
