///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------
//-- Title      : AXI Master Interface to PCIe
//-------------------------------------------------------------------------------
//-- File       : AXI_IF.v
//-- Author     : Corporate Applications Engineering
//-- Company    : Microsemi Corporation
//-- Device     : RTG4
//-- Standard   : Verilog
//-------------------------------------------------------------------------------
//-- Description: This code implementes the AXI master logic to do the read and write   
//--              transactions to PCIe and does 128byte burst transactions.
//-------------------------------------------------------------------------------
//-- Copyright (c) 2015   Microsemi Corporation
//--                      All rights reserved.
//-------------------------------------------------------------------------------
//-- Revisions  : V1.0
//-------------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////////////////////////// 
module PCIe_AXI_IF(input CLK,
                   input RESETn,
                   //dma controller signals
                   input[2 : 0] DMA_DIR,
                   input[5 : 0] DMA_SIZE,
                   output reg WRITE_DONE,
                   output reg READ_DONE,
                   input[31 : 0] PC_BASE_ADDR,
                   input PC_BASE_ADDR_VALID,
                   //handshaking signals for DDR to PCIe
                   input WRITE_START,
                   input[63 : 0] DATA_DDR,
                   input RD_BUSY,
                   output[8 : 0] RAM_ADDR,
                   output[5 : 0] WRITE_CNT,
                   //handshaking signals for PCIe to DDR
                   input[8 : 0] PC2DDR_RAM_ADDR,
                   output reg PC_RD_BUSY,
                   input[5 : 0] DDR_WRITE_CNT,
                   output reg DDR_WRITE_START,
                   output[63 : 0] DATA_TO_DDR,

                   //AXI Master IF
                   // Write Address Channel 
                   output[3 : 0] AWID,
                   output reg[31 : 0] AWADDR,
                   output[3 : 0] AWLEN,
                   output reg[1 : 0] AWSIZE,
                   output reg[1 : 0] AWBURST,
                   output reg AWVALID,
                   input AWREADY,
                   // Write Data Channel 
                   output[3 : 0] WID,
                   output[7 : 0] WSTRB,
                   output reg WLAST,
                   output reg WVALID,
                   output[63 : 0] WDATA,
                   input WREADY,
                   // Write Response Channel
                   input[3 : 0] BID,
                   input[1 : 0] BRESP,
                   input BVALID,
                   output BREADY,
                   // Read Address Channel 
                   output[3 : 0] ARID,
                   output[31 : 0] ARADDR,
                   output[3 : 0] ARLEN,
                   output reg[1 : 0] ARSIZE,
                   output reg[1 : 0] ARBURST,
                   output reg ARVALID,
                   input ARREADY,
                   // Read Data Channel
                   input[3 : 0] RID,
                   input[63 : 0] RDATA,
                   input[1 : 0] RRESP,
                   input RLAST,
                   input RVALID,
                   output reg RREADY);
  // AXI FSM States
  reg[2 : 0] axi_fsm_current_state;
  reg[2 : 0] axi_fsm_read_state;
  reg[2 : 0] axi_fsm_aw_state;
  reg[2 : 0] axi_fsm_ar_state;
  reg[1 : 0] ddr_wr_st;
  reg[2 : 0] araddr_st;
  localparam[2 : 0] Idle_0 = 3'b000,
                   Idle_1 = 3'b001,
                   Write_0 = 3'b010,
                   Write_1 = 3'b011,
                   Read_0 = 3'b010,
                   Read_1 = 3'b011,
                   Read_2 = 3'b100,
                   Bresp_0 = 3'b100;
  localparam[1:0] ddr_idle = 2'b00,
                  ddr_wait = 2'b01,
                  ddr_done = 2'b10;                 
  reg[31 : 0] AWADDR_int;
  reg[31 : 0] ARADDR_int;
  reg[8 : 0] WDATA_int;
  reg addr_valid_1;
  //write channel counters
  reg[3 : 0] burst_cnt;
  reg[5 : 0] data_cnt;
  reg[5 : 0] waddr_cnt;
  //read channel counters
  reg[8 : 0] rdata_cnt;
  reg[5 : 0] raddr_cnt;
  reg[5 : 0] rburst_cnt;
  reg[63 : 0] data_wready_low;

//AXI transaction type fixed INCR burst
  assign AWLEN = 4'b1111;
  assign ARLEN = 4'b1111;
  assign AWID = 4'b0000;
  assign WID = 4'b0000;
  assign ARID = 4'b0000;
  assign WSTRB = 8'hFF;
  assign BREADY = 1'b1;
  assign WDATA = addr_valid_1 ? DATA_DDR : data_wready_low;
  assign ARADDR = ARADDR_int;
  assign RAM_ADDR = WDATA_int;
  assign WRITE_CNT = data_cnt;
  //write address channel
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          AWADDR_int <= 32'h00000000;
          AWADDR <= 32'h00000000;
          AWSIZE <= 2'b11;
          AWBURST <= 2'b01;
          AWVALID <= 1'b0;
          waddr_cnt <= 0;
          axi_fsm_aw_state <= Idle_0;
        end
      else
        begin
          case (axi_fsm_aw_state)
            Idle_0:
              begin
                AWADDR_int <= PC_BASE_ADDR;
                waddr_cnt <= 0;
                if (WRITE_START == 1'b1 || DMA_DIR == 3'b110)
                  begin
                    axi_fsm_aw_state <= Idle_1;
                  end
                else
                  begin
                    axi_fsm_aw_state <= Idle_0;
                  end
              end
            Idle_1:
              begin
                if (RD_BUSY == 1'b0 && waddr_cnt != DMA_SIZE[5:0])
                  begin
                    AWSIZE <= 2'b11;
                    AWBURST <= 2'b01;
                    AWADDR <= AWADDR_int;
                    AWVALID <= 1'b1;
                    axi_fsm_aw_state <= Write_0;
                  end
                else
                  begin
                    if (DMA_DIR == 3'b111) //to reset the DMA transaction                          
                      axi_fsm_aw_state <= Idle_0;
                    else
                      axi_fsm_aw_state <= Idle_1;
                  end
              end
            Write_0:
              begin
                if (AWREADY == 1'b1)
                  begin
                    AWVALID <= 1'b0;
                    AWADDR_int <= AWADDR_int + 128;
                    waddr_cnt <= waddr_cnt + 1;
                    axi_fsm_aw_state <= Idle_1;
                  end
                else
                  begin
                    axi_fsm_aw_state <= Write_0;
                  end
              end
            default:
              axi_fsm_aw_state <= Idle_0;  
          endcase
        end
    end
  //write data channel
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          WDATA_int <= 0;
          WVALID <= 1'b0;
          WLAST <= 1'b0;
          data_cnt <= 0;
          addr_valid_1 <= 1;
          data_wready_low <= 0;
          axi_fsm_current_state <= Idle_0;
        end
      else
        begin
          case (axi_fsm_current_state)
            Idle_0:
              begin
                axi_fsm_current_state <= Idle_1;
              end
            Idle_1:
              begin
                WDATA_int <= 0;
                data_cnt <= 0;
                if (WRITE_START == 1'b1 || DMA_DIR == 3'b110) //to reset the DMA transaction
                  begin
                    axi_fsm_current_state <= Write_0;
                  end
                else
                  begin
                    axi_fsm_current_state <= Idle_1;
                  end
              end
            Write_0:
              begin
                if (WREADY == 1'b1 && RD_BUSY == 1'b0 && data_cnt != DMA_SIZE[5:0])
                  begin
                    burst_cnt <= 0;
                    WLAST <= 1'b0;
                    axi_fsm_current_state <= Write_1;
                  end
                else
                  begin
                    if (DMA_DIR == 3'b111)
                      axi_fsm_current_state <= Idle_1;
                    else
                      axi_fsm_current_state <= Write_0;
                  end
              end
            Write_1:
              begin
                if (WREADY == 1'b1)
                  begin
                    burst_cnt <= burst_cnt + 1;
                    addr_valid_1 <= 1'b1;
                    WVALID <= 1'b1;
                    if (burst_cnt == 4'b1111)
                      begin
                        WLAST <= 1'b1;
                        data_cnt <= data_cnt + 1'b1;
                        axi_fsm_current_state <= Bresp_0;
                      end
                    else
                      begin
                        WDATA_int <= WDATA_int + 1;
                        axi_fsm_current_state <= Write_1;
                      end
                  end
                else
                  begin
                    if (addr_valid_1 == 1)
                      begin
                        addr_valid_1 <= 1'b0;
                        data_wready_low <= DATA_DDR;
                        axi_fsm_current_state <= Write_1;
                      end
                  end
              end
            Bresp_0:
              begin
                if (WREADY == 1'b1)
                  begin
                    WVALID <= 1'b0;
                    WLAST <= 1'b0;
                    addr_valid_1 <= 1'b1;
                    WDATA_int <= WDATA_int + 1;
                    axi_fsm_current_state <= Write_0;
                  end
                else
                  begin
                    if (WLAST == 1'b1 && addr_valid_1 == 1)
                      begin
                        addr_valid_1 <= 1'b0;
                        data_wready_low <= DATA_DDR;
                      end
                  end
              end
            default:
              axi_fsm_current_state <= Idle_1;  
          endcase
        end
    end
  //Read address channel 
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          ARSIZE <= 2'b11;
          ARBURST <= 2'b01;
          ARVALID <= 1'b0;
          raddr_cnt <= 0;
          axi_fsm_ar_state <= Idle_0;
        end
      else
        begin
          case (axi_fsm_ar_state)
            Idle_0:
              begin
                raddr_cnt <= 0;
                if (DMA_DIR == 3'b001 || DMA_DIR == 3'b101)
                  begin
                    axi_fsm_ar_state <= Idle_1;
                  end
                else
                  begin
                    axi_fsm_ar_state <= Idle_0;
                  end
              end
            Idle_1:
              begin
                if (raddr_cnt != DMA_SIZE[5:0])
                  begin
                    ARSIZE <= 2'b11;
                    ARBURST <= 2'b01;
                    ARVALID <= 1'b1;
                    axi_fsm_ar_state <= Read_0;
                  end
                else
                  begin
                    if (DMA_DIR == 3'b111) //to reset the DMA transaction
                      axi_fsm_ar_state <= Idle_0;
                    else
                      axi_fsm_ar_state <= Idle_1;
                  end
              end
            Read_0:
              begin
                if (ARREADY == 1'b1)
                  begin
                    raddr_cnt <= raddr_cnt + 1;
                    ARVALID <= 1'b0;
                    axi_fsm_ar_state <= Idle_1;
                  end
                else
                  begin
                    axi_fsm_ar_state <= Read_0;
                  end
              end
            default:
              axi_fsm_ar_state <= Idle_0;
          endcase
        end
    end
  //read address generation
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          ARADDR_int <= 0;
          araddr_st <= Idle_0;
        end
      else
        begin
          case (araddr_st)
            Idle_0:
              begin
                if (PC_BASE_ADDR_VALID == 1'b1)
                  begin
                    ARADDR_int <= PC_BASE_ADDR;
                    araddr_st <= Idle_1;
                  end
              end
            Idle_1:
              begin
                if (DMA_DIR == 3'b111)
                  araddr_st <= Idle_0;
                else
                  begin
                    if (ARVALID == 1'b1 && ARREADY == 1'b1)
                      ARADDR_int <= ARADDR_int + 128;
                    else
                      ARADDR_int <= ARADDR_int;
                  end
              end
            default:
              araddr_st <= Idle_0;  
          endcase
        end
    end
  //Read data channel
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          rburst_cnt <= 0;
          RREADY <= 1'b0;
          axi_fsm_read_state <= Idle_0;
        end
      else
        begin
          case (axi_fsm_read_state)
            Idle_0:
              begin
                rburst_cnt <= 0;
                if (DMA_DIR == 3'b001 || DMA_DIR == 3'b101)
                  begin
                    axi_fsm_read_state <= Read_1;
                  end
                else
                  begin
                    axi_fsm_read_state <= Idle_0;
                  end
              end
            Read_1:
              begin
                if (RLAST == 1'b0 && rburst_cnt != DMA_SIZE[5:0])
                  begin
                    RREADY <= 1'b1;
                    axi_fsm_read_state <= Read_2;
                  end
                else
                  begin
                    if (DMA_DIR == 3'b111) //to reset the DMA transaction  
                      axi_fsm_read_state <= Idle_0;
                    else
                      axi_fsm_read_state <= Read_1;
                  end
              end
            Read_2:
              begin
                if (RLAST == 1'b1)
                  begin
                    rburst_cnt <= rburst_cnt + 1;
                    axi_fsm_read_state <= Read_1;
                  end
                else
                  begin
                    axi_fsm_read_state <= Read_2;
                  end
              end
            default:
              axi_fsm_read_state <= Idle_0;  
          endcase
        end
    end
  //read data count
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          rdata_cnt <= 0;
        end
      else
        begin
          if (axi_fsm_read_state == Idle_0)
            begin
              rdata_cnt <= 0;
            end
          else
            begin
              if (RVALID == 1'b1)
                begin
                  rdata_cnt <= rdata_cnt + 1;
                end
            end
        end
    end
  //TPSRAM
  LSRAM LSRAM_1(
                // Inputs
                .CLK(CLK),
                .RADDR(PC2DDR_RAM_ADDR),
                .WADDR(rdata_cnt),
                .WD(RDATA),
                .WEN(RVALID),
                // Outputs
                .RD(DATA_TO_DDR));
  //busy signal to avoid rem overlapping
  always
    @(posedge CLK)
    begin
      if (DDR_WRITE_CNT >= rburst_cnt && DDR_WRITE_START == 1'b1)
        PC_RD_BUSY <= 1'b1;
      else
        PC_RD_BUSY <= 1'b0;
    end
  //initiating write to ddr
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          DDR_WRITE_START <= 1'b0;
          ddr_wr_st <= ddr_idle;
        end
      else
        begin
          case (ddr_wr_st)
            ddr_idle:
              begin
                if (DMA_DIR == 3'b001)
                  ddr_wr_st <= ddr_wait;
              end
            ddr_wait:
              begin
                if (rburst_cnt != 0)
                  begin
                    DDR_WRITE_START <= 1'b1;
                    ddr_wr_st <= ddr_done;
                  end
              end
            ddr_done:
              begin
                if (DMA_DIR == 3'b111)
                  begin
                    DDR_WRITE_START <= 1'b0;
                    ddr_wr_st <= ddr_idle;
                  end
              end
            default:
              ddr_wr_st <= ddr_idle;  
          endcase
        end
    end
  //DMA transaction complete signals generation to DMA controller
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          WRITE_DONE <= 1'b0;
          READ_DONE <= 1'b0;
        end
      else
        begin
          if (DMA_DIR == 3'b111)
            begin
              READ_DONE <= 1'b0;
              WRITE_DONE <= 1'b0;
            end
          else
            begin
              if (rburst_cnt == DMA_SIZE[5:0])
                READ_DONE <= 1'b1;
              if (data_cnt == DMA_SIZE[5:0])
                WRITE_DONE <= 1'b1;
            end
        end
    end
endmodule