///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------
//-- Title      : AXI Master Interface to DDR_FIC
//-------------------------------------------------------------------------------
//-- File       : DDR_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 MDDR through DDR_FIC and does 128byte burst transactions.
//-------------------------------------------------------------------------------
//-- Copyright (c) 2015   Microsemi Corporation
//--                      All rights reserved.
//-------------------------------------------------------------------------------
//-- Revisions  : V1.0
//-------------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////////////////////////// 
module DDR_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,
                  //handshaking signals for DDR to PCIe
                  input[5 : 0] WRITE_CNT,
                  input[8 : 0] RAM_ADDR,
                  output reg RD_BUSY,
                  output reg PCI_WRITE_START,
                  output[63 : 0] DATA_TO_PCIe,
                  //handshaking signals for PCIe to DDR
                  input WRITE_START,
                  output[8 : 0] PC2DDR_RAM_ADDR,
                  input PC_RD_BUSY,
                  output[5 : 0] DDR_WRITE_CNT,
                  input[63 : 0] DATA_PCIe,

                  //AXI Master IF
                  // Write Address Channel
                  output[3 : 0] AWID,
                  output reg[31 : 0] AWADDR /*synthesis syn_preserve=1*/,
                  output[3 : 0] AWLEN,
                  output reg[2 : 0] AWSIZE /*synthesis syn_preserve=1*/,
                  output reg[1 : 0] AWBURST /*synthesis syn_preserve=1*/,
                  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 reg[31 : 0] ARADDR /*synthesis syn_preserve=1*/,
                  output[3 : 0] ARLEN,
                  output reg[2 : 0] ARSIZE /*synthesis syn_preserve=1*/,
                  output reg[1 : 0] ARBURST /*synthesis syn_preserve=1*/,
                  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_wd_state;
  reg[2 : 0] axi_fsm_read_state;
  reg[2 : 0] araddr_state;
  reg[1 : 0] pcie_wr_st;
  localparam[2 : 0] Idle_0 = 3'b000,
                   Idle_1 = 3'b001,
                   Write_0 = 3'b010,
                   Write_1 = 3'b011,
                   Wait_PC = 3'b100,
                   Read_0 = 3'b010,
                   Read_1 = 3'b011;
  localparam[1:0] pcie_idle = 2'b00,
                  pcie_wait = 2'b01,
                  pcie_done = 2'b10;                 
  reg[31 : 0] AWADDR_int /*synthesis syn_preserve=1*/;
  reg[31 : 0] ARADDR_int /*synthesis syn_preserve=1*/;
  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] rburst_cnt;
  reg[5 : 0] raddr_cnt;
  reg[63 : 0] data_wready_low;
  //assigning burst length and ID's
  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 DDR_WRITE_CNT = data_cnt;
  assign WDATA = addr_valid_1 ? DATA_PCIe : data_wready_low;
  assign PC2DDR_RAM_ADDR = WDATA_int;
  assign BREADY = 1'b1;
  //Write address channel
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          AWADDR_int <= 32'h00000000;
          AWADDR <= 0;
          AWSIZE <= 3'b000;
          AWBURST <= 2'b00;
          AWVALID <= 1'b0;
          waddr_cnt <= 0;
          axi_fsm_current_state <= Idle_0;
        end
      else
        begin
          case (axi_fsm_current_state)
            Idle_0:
              begin
                AWADDR_int <= 32'h00000000; //DDR buffer for writing
                waddr_cnt <= 0;
                if (WRITE_START == 1'b1 || DMA_DIR == 3'b011)
                  begin
                    axi_fsm_current_state <= Idle_1;
                  end
              end
            Idle_1:
              begin
                if (PC_RD_BUSY == 1'b0 && waddr_cnt != DMA_SIZE[5:0])
                  begin
                    AWSIZE <= 3'b011;
                    AWBURST <= 2'b01;
                    AWADDR <= AWADDR_int;
                    AWVALID <= 1'b1;
                    axi_fsm_current_state <= Write_0;
                  end
                else
                  begin
                    if (DMA_DIR == 3'b111) //to reset the DMA transaction
                      axi_fsm_current_state <= Idle_0;
                    else
                      axi_fsm_current_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_current_state <= Idle_1;
                  end
                else
                  begin
                    axi_fsm_current_state <= Write_0;
                  end
              end
            default:
              axi_fsm_current_state <= Idle_0;
          endcase
        end
    end
  //Write data and response channels
  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;
          axi_fsm_wd_state <= Idle_0;
        end
      else
        begin
          case (axi_fsm_wd_state)
            Idle_0:
              begin
                axi_fsm_wd_state <= Idle_1;
              end
            Idle_1:
              begin
                WDATA_int <= 0;
                data_cnt <= 0;
                if (WRITE_START == 1'b1 || DMA_DIR == 3'b011)
                  begin
                    axi_fsm_wd_state <= Write_0;
                  end
                else
                  begin
                    axi_fsm_wd_state <= Idle_1;
                  end
              end
            Write_0:
              begin
                burst_cnt <= 0;
                if (WREADY == 1'b1 && data_cnt != DMA_SIZE[5:0])
                  begin
                    if (DMA_DIR == 3'b001)
                      axi_fsm_wd_state <= Wait_PC;
                    else
                      axi_fsm_wd_state <= Write_1;
                  end
                else
                  begin
                    if (DMA_DIR == 3'b111) //to reset the DMA transaction
                      axi_fsm_wd_state <= Idle_1;
                    else
                      axi_fsm_wd_state <= Write_0;
                  end
                if (WREADY == 1'b1)
                  begin
                    addr_valid_1 <= 1'b1;
                    WLAST <= 1'b0;
                    WVALID <= 1'b0;
                  end
                else
                  if (WLAST == 1'b1 && addr_valid_1 == 1)
                    begin
                      data_wready_low <= DATA_PCIe;
                      addr_valid_1 <= 1'b0;
                    end
              end
            Wait_PC:
              begin
                if (PC_RD_BUSY == 1'b0)
                  axi_fsm_wd_state <= Write_1;
              end
            Write_1:
              begin
                if (WREADY == 1'b1)
                  begin
                    burst_cnt <= burst_cnt + 1;
                    addr_valid_1 <= 1'b1;
                    WVALID <= 1'b1;
                    WDATA_int <= WDATA_int + 1;
                    if (burst_cnt == 4'b1111)
                      begin
                        WLAST <= 1'b1;
                        data_cnt <= data_cnt + 1'b1;
                        axi_fsm_wd_state <= Write_0;
                      end
                    else
                      begin
                        axi_fsm_wd_state <= Write_1;
                      end
                  end
                else
                  begin
                    if (addr_valid_1 == 1)
                      begin
                        addr_valid_1 <= 1'b0;
                        data_wready_low <= DATA_PCIe;
                        axi_fsm_wd_state <= Write_1;
                      end
                  end
              end
            default:
               axi_fsm_wd_state <= Idle_1;  
          endcase
        end
    end
  //Read address and Read data channels
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          ARSIZE <= 3'b000;
          ARBURST <= 2'b00;
          RREADY <= 1'b0;
          rdata_cnt <= 0;
          rburst_cnt <= 0;
          axi_fsm_read_state <= Idle_0;
        end
      else
        begin
          case (axi_fsm_read_state)
            Idle_0:
              begin
                rdata_cnt <= 0;
                rburst_cnt <= 0;
                if (DMA_DIR == 3'b010 || DMA_DIR == 3'b100)
                  begin
                    axi_fsm_read_state <= Idle_1;
                  end
              end
            Idle_1:
              begin
                if (rburst_cnt != DMA_SIZE[5:0])
                  begin
                    ARSIZE <= 3'b011;
                    ARBURST <= 2'b01;
                    axi_fsm_read_state <= Read_1;
                    RREADY <= 1'b1;
                  end
                else
                  begin
                    if (DMA_DIR == 3'b111) //to reset the DMA transaction
                      axi_fsm_read_state <= Idle_0;
                    else
                      axi_fsm_read_state <= Idle_1;
                  end
              end
            Read_1:
              begin
                if (RVALID == 1'b1)
                  begin
                    rdata_cnt <= rdata_cnt + 1;
                  end
                if (RLAST == 1'b1 && RVALID == 1'b1)
                  begin
                    rburst_cnt <= rburst_cnt + 1;
                    RREADY <= 1'b0;
                    axi_fsm_read_state <= Idle_1;
                  end
                else
                  begin
                    axi_fsm_read_state <= Read_1;
                  end
              end
            default:
              axi_fsm_read_state <= Idle_0;  
          endcase
        end
    end
  //Read address channel
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          ARADDR_int <= 32'h00000000; //DDR buffer for reading
          ARADDR <= 32'h00000000;
          ARVALID <= 1'b0;
          raddr_cnt <= 0;
          araddr_state <= Idle_0;
        end
      else
        begin
          case (araddr_state)
            Idle_0:
              begin
                ARADDR_int <= 32'h00000000;
                raddr_cnt <= 0;
                if (DMA_DIR == 3'b010 || DMA_DIR == 3'b100)
                  begin
                    araddr_state <= Idle_1;
                  end
              end
            Idle_1:
              begin
                if (raddr_cnt != DMA_SIZE[5:0])
                  begin
                    raddr_cnt <= raddr_cnt + 1;
                    ARADDR_int <= ARADDR_int + 128;
                    ARADDR <= ARADDR_int;
                    ARVALID <= 1'b1;
                    araddr_state <= Read_0;
                  end
                else
                  begin
                    if (DMA_DIR == 3'b111) //to reset the DMA transaction
                      araddr_state <= Idle_0;
                    else
                      araddr_state <= Idle_1;
                  end
              end
            Read_0:
              begin
                if (ARREADY == 1'b1)
                  begin
                    if (raddr_cnt != DMA_SIZE[5:0])
                      begin
                        raddr_cnt <= raddr_cnt + 1;
                        ARADDR_int <= ARADDR_int + 128;
                        ARADDR <= ARADDR_int;
                        ARVALID <= 1'b1;
                        araddr_state <= Read_0;
                      end
                    else
                      begin
                        ARVALID <= 1'b0;
                        araddr_state <= Idle_1;
                      end
                  end
                else
                  begin
                    araddr_state <= Read_0;
                  end
              end
            default:
              araddr_state <= Idle_0;  
          endcase
        end
    end
  //TPSRAM
  LSRAM LSRAM_0(
                // Inputs
                .CLK(CLK),
                .RADDR(RAM_ADDR),
                .WADDR(rdata_cnt),
                .WD(RDATA),
                .WEN(RVALID),
                // Outputs
                .RD(DATA_TO_PCIe));
  //busy signal to avoid ram overlapping
  always
    @(posedge CLK)
    begin
      if (WRITE_CNT >= rburst_cnt && PCI_WRITE_START == 1'b1)
        RD_BUSY <= 1'b1;
      else
        RD_BUSY <= 1'b0;
    end
  //initiating write to pcie
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          PCI_WRITE_START <= 1'b0;
          pcie_wr_st <= pcie_idle;
        end
      else
        begin
          case (pcie_wr_st)
            pcie_idle:
              begin
                if (DMA_DIR == 3'b010)
                  pcie_wr_st <= pcie_wait;
              end
            pcie_wait:
              begin
                if (rburst_cnt == 4)
                  begin
                    PCI_WRITE_START <= 1'b1;
                    pcie_wr_st <= pcie_done;
                  end
              end
            pcie_done:
              begin
                if (DMA_DIR == 3'b111)
                  begin
                    PCI_WRITE_START <= 1'b0;
                    pcie_wr_st <= pcie_idle;
                  end
              end
            default:
              pcie_wr_st <= pcie_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