///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------
//-- Title      : Controller
//-------------------------------------------------------------------------------
//-- File       : Controller.v
//-- Author     : Corporate Applications Engineering
//-- Company    : Microsemi Corporation
//-- Device     : RTG4
//-- Standard   : Verilog
//-------------------------------------------------------------------------------
//-- Description: This code implementes the AXI slave logic to receive the  
//--              commands from Serdes AXI master and to control the DMA transfers.
//--                      It also implements counters to return the number of cycles taken 
//--                      to complete the DMA transfer.
//-------------------------------------------------------------------------------
//-- Copyright (c) 2015   Microsemi Corporation
//--                      All rights reserved.
//-------------------------------------------------------------------------------
//-- Revisions  : V1.0
//--              V1.1 adding support to interrupt the PC after DMA 
//--              V1.2 adding 4KB memory to access from BAR space
//-------------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////////////////////////// 
module Controller(
                  //global
                  input CLK,
                  input RESETn,

                  // AXI Slave interface
                  input[3 : 0] AWID,
                  input[15 : 0] AWADDR,              
                  input AWVALID,
                  output reg AWREADY,
                  input[63 : 0] WDATA,
                  input[7 : 0] WSTRB,
                  input WLAST,
                  input WVALID,
                  output reg WREADY,
                  output reg[3 : 0] BID,
                  output[1 : 0] BRESP,
                  output reg BVALID,
                  input BREADY,
                  input[3 : 0] ARID,
                  input[15 : 0] ARADDR,                 
                  input ARVALID,
                  output reg ARREADY,
                  output reg[3 : 0] RID,
                  output reg[63 : 0] RDATA,
                  output[1 : 0] RRESP,
                  output reg RLAST,
                  output reg RVALID,
                  input RREADY,

                  // DMA control signals
                  output reg[31 : 0] PC_BASE_ADDR,
                  output reg PC_BASE_ADDR_VALID,
                  output reg[2 : 0] DMA_DIR,
                  output reg[5 : 0] DMA_SIZE,
                  input PCIE_WRITE_DONE,
                  input PCIE_READ_DONE,
                  input DDR_WRITE_DONE,
                  input DDR_READ_DONE,
                  output CH0_INT,
                  output CH1_INT,
                  //LED's and Switches
                  output reg[7 : 0] LED,
                  input[3 : 0] DIP_SWITCH);
  // Parameters
  reg[15 : 0] waddr_int;
  reg[15 : 0] raddr_int;
  reg[1 : 0] rstate;
  reg[1 : 0] wstate;
  reg clk_cnt_1_strt;
  reg[15 : 0] clk_cnt_1;
  reg clk_cnt_2_strt;
  reg[15 : 0] clk_cnt_2;
  reg clk_cnt_1_done;
  reg clk_cnt_2_done;
  reg[31 : 0] RW_reg;
  reg[8 : 0] mem_raddr;
  reg[9 : 0] mem_waddr;
  reg[31 : 0] mem_wdata;
  reg mem_write;
  wire[63 : 0] mem_rdata;
  assign BRESP = 2'b00;
  assign RRESP = 2'b00;
  assign CH0_INT = clk_cnt_1_done;
  assign CH1_INT = clk_cnt_2_done;
  
  localparam[1:0] IDLE = 2'b00,
             AXI_ADDR = 2'b01,
             AXI_RESP = 2'b10;
  //write address, write data and write response channels
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          wstate <= IDLE;
          waddr_int <= 0;
          PC_BASE_ADDR <= 0;
          BID <= 0;
          BVALID <= 1'b0;
          AWREADY <= 1'b0;
          WREADY <= 1'b0;
          PC_BASE_ADDR_VALID <= 1'b0;
          DMA_DIR <= 0;
          DMA_SIZE <= 32; //default 32 bursts = 32*128 bytes = 4KB
          LED <= 0;
          mem_waddr <= 0;
          mem_wdata <= 0;
          mem_write <= 0;
          RW_reg <= 0;
        end
      else
        begin
          case (wstate)
            IDLE:
              begin
                BVALID <= 1'b0;
                if (AWVALID == 1'b1)
                  begin
                    AWREADY <= 1'b1;
                    waddr_int <= AWADDR[15:0];
                    wstate <= AXI_ADDR;
                    BID <= AWID;
                  end
                else
                  wstate <= IDLE;
              end
            AXI_ADDR:
              begin
                AWREADY <= 1'b0;
                WREADY <= 1'b1;
                if (WLAST == 1'b1 && WVALID == 1'b1)
                  wstate <= AXI_RESP;
                else
                  wstate <= AXI_ADDR;
                //getting pc base address
                if (waddr_int[15 : 0] == 16'h8028)
                  begin
                    PC_BASE_ADDR <= WDATA[31 : 0];
                    PC_BASE_ADDR_VALID <= 1'b1;
                  end
                //getting DMA direction
                if (waddr_int[15 : 0] == 16'h8008 && WDATA[31 : 16] == 16'h11AA)
                  begin
                    DMA_DIR <= WDATA[2 : 0];
                  end
                //getting DMA size
                if (waddr_int[15 : 0] == 16'h8030)
                  begin
                    DMA_SIZE <= WDATA[12 : 7];
                  end
                //PCIe R/W
                if (waddr_int[15 : 0] == 16'h0)
                  begin
                    RW_reg <= WDATA[31 : 0];
                  end
                //LED's
                if (waddr_int[15 : 0] == 16'h00A0)
                  begin
                    LED <= WDATA[7 : 0];
                  end
                //BAR1 mem
                if (waddr_int[15 : 12] == 4'h9)
                  begin
                    if (WSTRB[3 : 0] == 4'hF)
                      begin
                        mem_waddr <= waddr_int[11 : 2];
                        mem_wdata <= WDATA[31 : 0];
                        mem_write <= 1'b1;
                      end
                    else
                      if (WSTRB[7 : 4] == 4'hF)
                        begin
                          mem_waddr <= { waddr_int[11 : 3], 1'b1 };
                          mem_wdata <= WDATA[63 : 32];
                          mem_write <= 1'b1;
                        end
                  end
              end
            AXI_RESP:
              begin
                WREADY <= 1'b0;
                mem_write <= 1'b0;
                if (BREADY == 1'b1)
                  begin
                    BVALID <= 1'b1;
                    wstate <= IDLE;
                  end
                else
                  begin
                    BVALID <= 1'b0;
                    wstate <= AXI_RESP;
                  end
              end
            default:
              wstate <= IDLE;  
          endcase
        end
    end
  //read address and read data channels
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          rstate <= IDLE;
          raddr_int <= 0;
          mem_raddr <= 0;
          ARREADY <= 1'b0;
          RID <= 0;
          RVALID <= 1'b0;
          RLAST <= 1'b0;
        end
      else
        begin
          case (rstate)
            IDLE:
              begin
                RVALID <= 1'b0;
                RLAST <= 1'b0;
                if (ARVALID == 1'b1)
                  begin
                    RID <= ARID;
                    mem_raddr <= ARADDR[11 : 3];
                    raddr_int <= ARADDR[15:0];
                    rstate <= AXI_ADDR;
                    ARREADY <= 1'b1;
                  end
              end
            AXI_ADDR:
              begin
                rstate <= AXI_RESP;
              end
            AXI_RESP:
              begin
                ARREADY <= 1'b0;
                if (RREADY == 1'b1)
                  begin
                    rstate <= IDLE;
                    RVALID <= 1'b1;
                    RLAST <= 1'b1;
                  end
              end
            default:
              rstate <= IDLE;  
          endcase
        end
    end
  //returing the data
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          RDATA <= 6; //board detection
        end
      else
        begin
          if (raddr_int[15 : 12] == 4'h9)
            begin
              RDATA <= mem_rdata;
            end
          else
            begin
              case (raddr_int[15 : 0])
                16'h0000:
                  begin
                    RDATA[31:0] <= RW_reg;
                  end
                16'h8028:
                  begin
                    RDATA[31:0] <= PC_BASE_ADDR;
                  end
                //clk frequency
                16'h8038:
                  begin
                    RDATA <= 80; //PCIe CLK_BASE
                  end
                16'h8100:
                  begin
                    RDATA <= { 32'b0, clk_cnt_1_done, 15'b0, clk_cnt_1 }; //lower 16 bits indicates count value and 32 bit indicates status of DMA channel 0
                  end
                16'h8108:
                  begin
                    RDATA <= { 32'b0, clk_cnt_2_done, 15'b0, clk_cnt_2 }; //lower 16 bits indicates count value and 32 bit indicates status of DMA channel 1
                  end
                16'h0090:
                  begin
                    RDATA <= {60'b0, DIP_SWITCH}; //DIP switch status
                  end
                16'h8148:
                  begin
                    RDATA <= 6; //board detection
                  end
                default:
                  RDATA <= 6;  
              endcase
            end
        end
    end
  //TPSRAM -- BAR1 4KB memory
  BAR1_LSRAM BAR1_LSRAM_0(
                          // Inputs
                          .CLK(CLK),
                          .RADDR(mem_raddr),
                          .WADDR(mem_waddr),
                          .WD(mem_wdata),
                          .WEN(mem_write),
                          // Outputs
                          .RD(mem_rdata));
  //clk counting for DMA channel 0
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          clk_cnt_1_done <= 0;
          clk_cnt_1_strt <= 1'b0;
          clk_cnt_1 <= 0;
        end
      else
        begin
          //start and stop for clk counter
          if (DDR_WRITE_DONE == 1'b1 || (PCIE_READ_DONE == 1'b1 && DMA_DIR[2] == 1'b1))
            begin
              clk_cnt_1_done <= 1'b1;
              clk_cnt_1_strt <= 1'b0;
            end
          else
            if (DMA_DIR == 3'b001 || DMA_DIR == 3'b011 || DMA_DIR == 3'b101)
              clk_cnt_1_strt <= 1'b1;
          //clk counter
          if (DMA_DIR == 3'b111) //to reset the clk count value
            begin
              clk_cnt_1 <= 0;
              clk_cnt_1_done <= 1'b0;
            end
          else
            begin
              if (clk_cnt_1_strt == 1'b1)
                begin
                  clk_cnt_1 <= clk_cnt_1 + 1;
                end
              else
                begin
                  clk_cnt_1 <= clk_cnt_1;
                end
            end
        end
    end
  //clk counting DMA channel 1
  always
    @(posedge CLK)
    begin
      if (RESETn == 1'b0)
        begin
          clk_cnt_2_done <= 0;
          clk_cnt_2_strt <= 1'b0;
          clk_cnt_2 <= 0;
        end
      else
        begin
          //start and stop for clk counter
          if (PCIE_WRITE_DONE == 1'b1 || (DDR_READ_DONE == 1'b1 && DMA_DIR[2] == 1'b1))
            begin
              clk_cnt_2_done <= 1'b1;
              clk_cnt_2_strt <= 1'b0;
            end
          else
            if (DMA_DIR == 3'b010 || DMA_DIR == 3'b110 || DMA_DIR == 3'b100)
              clk_cnt_2_strt <= 1'b1;
          //clk counter
          if (DMA_DIR == 3'b111) //to reset the clk count value
            begin
              clk_cnt_2 <= 0;
              clk_cnt_2_done <= 1'b0;
            end
          else
            begin
              if (clk_cnt_2_strt == 1'b1)
                begin
                  clk_cnt_2 <= clk_cnt_2 + 1;
                end
              else
                begin
                  clk_cnt_2 <= clk_cnt_2;
                end
            end
        end
    end
endmodule