///////////////////////////////////////////////////////////////////////////////////////////////////
// Company: Microsemi Corporation
//
// File: APB_MEM_CTRL.v
// File history:
//       V1.0: 08/08/2011: Initial version
//      <Revision number>: <Date>: <Comments>
//      <Revision number>: <Date>: <Comments>
//
// Description: The APB master in the fabric reads the ADC0 status register content located at 0x40021000.
// The DATAVALID bit (ADC0_STATUS [12]) of ADC0 status register indicates the status of the ADC0 conversion. 
// If DATAVALID bit is set then ADC0 completes the conversion and converted result is available in the lower 12-bits of ADC0 status register. 
// The APB master in the fabric continuously polls the status register at a higher frequency than sampling rate. 
// The APB master reads the ADC0 converted result when ADC0 DATAVALID bit is set and ADC0 is not busy in converting.
//
// Targeted device: SmartFusion A2F500
// Author: Mir Ali
//
/////////////////////////////////////////////////////////////////////////////////////////////////// 

module APB_MEM_CTRL( 
//APB Side
input PRESETn,  PCLK, PREADY, PSLVERR,
input [31:0] PRDATA,
output reg PENABLE, PSEL, PWRITE,
output reg [23:0] PADDR,
output reg [31:0] PWDATA,
//Backend Signals
//input [31:0] start_address,
output reg busy,
output reg dataout_enable,
output  reg [11:0] data );

reg[3:0] current_state;
reg[3:0] data_enable_ctr;
reg [15:0] Adc0_Status_Reg;

// ACE Address is from 0x40020000

parameter AD0_STATUS_REG_OFFSET  = 24'h001000; 
// This has [31:16] - Reserved; 15 - Calibrate, 14 - Sample,
// 13 - Busy, 12- Data Valid, [11:0] - ADC Result


parameter [3:0] IDLE = 4'b0000, FIC_UPPER_ACCESS_0 = 4'b0001, FIC_UPPER_ACCESS_1 = 4'b0010, 
 FIC_UPPER_ACCESS_2 = 4'b0011, FIC_READ_0 =4'b0100, FIC_READ_1 = 4'b0101, FIC_READ_2 = 4'b0110,
 EVAL_DATA_VALID = 4'b0111, HOLD_DATA_ENABLE = 4'b1000, WAIT_TILL_DATAVALID_LOW = 4'b1001;

reg[31:00] delay_start;
reg[7:0] datavalid_cnt;

always @(posedge PCLK, negedge PRESETn)
begin
  if ( PRESETn == 1'b0 )
    delay_start <= 0;
  else
    delay_start <= delay_start + 1;
end


always@(posedge PCLK, negedge PRESETn)
    begin

    if(PRESETn == 1'b0)
    begin
        PADDR           <=  24'h000000;
        PWDATA          <=  24'h000000;
        PENABLE         <=  1'b0;
        PSEL            <=  1'b0;
        PWRITE          <=  1'b0;
        busy            <=  1'b0;
        dataout_enable  <=  1'b0;
        data_enable_ctr <=  3'b000;
        data            <=  16'h0000;
        Adc0_Status_Reg <=  16'h0000;
        datavalid_cnt   <=  8'h00;
        current_state   <=  IDLE;
    end

    else 
    begin
    
    case(current_state) /* synthesis parallel_case */ 
    
    IDLE:
    begin
        
        data            <=  PRDATA[15:0];
        dataout_enable  <=  1'b0;
        busy            <=  1'b0;
        PENABLE         <=  1'b0;
        PSEL            <=  1'b0;
        PWRITE          <=  1'b0;

        if ( delay_start < 32'd16384 )
           current_state   <=  IDLE;
        else
            current_state   <=  FIC_UPPER_ACCESS_0;
    end

    FIC_UPPER_ACCESS_0://Write To IADDR  Indirect Address(We want to access 0x2000F000)
    begin
        busy            <=  1'b1;
        dataout_enable  <=  1'b0;
        PSEL            <=  1'b1;
        PWRITE          <=  1'b1;
        PWDATA          <=  32'h40020000;
        PADDR           <=  24'h010000; // This is for 64K access setting IADDR
        current_state   <=  FIC_UPPER_ACCESS_1;
    end
    
    FIC_UPPER_ACCESS_1: //WRITE ADDRESS PHASE
    begin
        PENABLE         <=  1'b1;
        current_state   <=  FIC_UPPER_ACCESS_2;
    end

    FIC_UPPER_ACCESS_2: //WRITE DATA PHASE
    begin
        if(PREADY == 1'b1)
        begin
        PSEL            <=  1'b0;
        PENABLE          <=  1'b0;
        PADDR           <=  AD0_STATUS_REG_OFFSET;
        current_state   <=  FIC_READ_0;
        end
        else
        begin
        current_state   <=  FIC_UPPER_ACCESS_2;
        end
 
    end

    FIC_READ_0: // READ ADDRESS PHASE 
    begin
        busy            <=  1'b1;
        PSEL            <=  1'b1;
        PWRITE          <=  1'b0;
        data_enable_ctr <=  3'b000;
        current_state   <=  FIC_READ_1;
    end

    FIC_READ_1: // READ DATA PHASE 
    begin
        PENABLE         <=  1'b1;
        current_state   <=  FIC_READ_2;
    end


    FIC_READ_2: // READ DATA PHASE 
    begin
        if(PREADY == 1'b1)
            begin
            PSEL            <=  1'b0;
            PENABLE         <=  1'b0;
            busy            <=  1'b1;
            Adc0_Status_Reg <=  PRDATA[15:0];
            current_state   <=  EVAL_DATA_VALID;
            end
         else
            begin
            current_state   <=  FIC_READ_2;
            end

    end

    EVAL_DATA_VALID:    // Check for Datavalid to grab data
    begin
        if(Adc0_Status_Reg[12] == 1'b1) // Data valid is high
        begin
            data            <=  Adc0_Status_Reg[11:0];
            dataout_enable  <=  1'b1;  
            current_state   <=  HOLD_DATA_ENABLE;
        end

        else
            begin
            current_state   <=  FIC_READ_0;
            end
    end

    HOLD_DATA_ENABLE:    // Hold data Eanble for transition from 40 MHz to 25 MHz domains
    begin
        if(data_enable_ctr < 6) 
        begin
            data_enable_ctr <=  data_enable_ctr + 1;
            current_state   <=  HOLD_DATA_ENABLE;
        end

        else
            begin
            dataout_enable  <=  1'b0;
            current_state   <=  WAIT_TILL_DATAVALID_LOW;
         end
    end

    WAIT_TILL_DATAVALID_LOW:    // Do not read till next data valid
    begin
         if (datavalid_cnt < 32)
         begin
         datavalid_cnt   <=  datavalid_cnt + 1;
         current_state   <=  WAIT_TILL_DATAVALID_LOW;
         end
         else
         begin
         datavalid_cnt   <=  8'h00;
         current_state   <=  FIC_READ_0;
         end

    end

    default:
        begin
            current_state   <=  IDLE;
        end

    endcase

 end //if 

end // always

endmodule