///////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------
// © 2022 Microchip Technology Inc. and its subsidiaries
// All rights reserved.
// ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN
// ACCORDANCE WITH THE MICROCHIP LICENSE AGREEMENT AND MUST BE APPROVED
// IN ADVANCE IN WRITING.
//
//-------------------------------------------------------------------------
// Title : Native_Interface.v
// Description : This module generates the control signals to the DDR3 operation 
//-------------------------------------------------------------------------

`timescale 1ns / 100ps


module DDR3_Native_interface #
(
//defining parameters
    parameter integer  DATA_WIDTH           = 32
)
(
    // Inputs
    RESETN, // Device Reset
    CLK,    // DDR sys clock
    l_w_req, // write request from user
    l_auto_pch, // auto prechanrge tied to 0
    l_addr, // address
    l_b_size, // burst size
	MULTIBURST_MAX, // number of bursts
    l_busy, // controller busy
    l_d_req, // request for data from controller
    l_datain, // data input to the controller
    l_dm_in, // data mask input
    l_r_req, // read request from user
    data_pattern, // data pattern selection
    l_r_valid, // read valid from the controller
    l_dataout, // read dataout from the controller
    l_d_req_last,//data request last signal
    l_r_valid_last, //read valid last signal
    ctrl_ready, // controller ready from the controller
    err_flg, // error flag
    mburst_length //Inorder to make parameterizable
    
);

//--------------------------------------------------------------------
// Input
//--------------------------------------------------------------------
input 	RESETN;
input	CLK;   
input	l_busy;
input	l_d_req;
input [1:0] data_pattern;
input	l_r_valid;
input	[(DATA_WIDTH*8)-1:0]   l_dataout;
input   l_d_req_last;
input   l_r_valid_last;
input   ctrl_ready;
input [10:0] mburst_length;
input [7:0] MULTIBURST_MAX;
output	l_w_req;
output	l_auto_pch;
output	reg [37:0]   l_addr;
output	reg [10:0]   l_b_size; 
output	wire [(DATA_WIDTH*8)-1:0]   l_datain;
output	reg [(DATA_WIDTH)-1:0]   l_dm_in;    
output	l_r_req;
output  reg err_flg;
//--------------------------
//------------------------------------------
// Nets
//--------------------------------------------------------------------
reg [4:0] IOD_FSM;
reg restart;
reg [(DATA_WIDTH*8)-1:0] read_data;
wire [(DATA_WIDTH*8)-1:0] ref_data_int;
reg [(DATA_WIDTH*8)-1:0] ref_data;
reg  err_flg_v;
wire last_w_burst,last_r_burst;

// state definitions
localparam     IDLE = 5'h0;
localparam     WRITE = 5'h1;
localparam     WAIT_DATA = 5'h2;
localparam     WRITE_DONE = 5'h3;
localparam     READ = 5'h4;
localparam     READ_DATA = 5'h5;
localparam     READ_DONE = 5'h6;
localparam     WAIT5 = 5'h7;


reg ctrl_ready_q;
reg l_w_req_int;
reg l_r_req_int;
reg l_d_req_int;
reg l_r_valid_int ;
reg [15:0] write_cnt;
reg [15:0] read_cnt;
reg [37:0]   l_addr_wr;
reg [37:0]   l_addr_rd;

reg  [8:0] count_beats;
reg  [8:0] read_count_beats; 
reg [5:0] mburst_length_word; //[10:0]

assign l_w_req = l_w_req_int;
assign l_r_req = l_r_req_int;
assign l_auto_pch = 1'b0;

//address increment block
always @(posedge CLK or negedge RESETN)
begin
    if (!RESETN) begin
        ctrl_ready_q <= 1'b0;
        l_addr_wr <= 38'h0000000000;
        l_addr_rd <= 38'h0000000000;
        l_addr <= 38'h0000000000;
        l_d_req_int <= 1'b0;
    	l_r_valid_int <= 1'b0;
    	err_flg <= 1'b0;
        write_cnt <= 0;
        read_cnt <= 0;
        read_data <= 0;
    end
    else
    begin
        err_flg <= err_flg_v;
        ctrl_ready_q <= ctrl_ready;
        l_r_valid_int <= l_r_valid;
        l_d_req_int <= l_d_req;
        if (IOD_FSM == READ || IOD_FSM == WRITE_DONE || IOD_FSM == READ_DONE || IOD_FSM == READ_DATA )
            l_addr <= l_addr_rd;
        else
            l_addr <= l_addr_wr ;

	read_data <= l_dataout;
        if (restart == 1'b1) begin
            l_addr_wr <= 0;
            write_cnt <= 0;
    end
    else begin
        if (l_w_req_int == 1'b1 && write_cnt< MULTIBURST_MAX) begin
            l_addr_wr <= l_addr_wr + l_b_size;
            write_cnt <= write_cnt +1;
        end
    end
    if (restart == 1'b1) begin
        l_addr_rd <= 0;
        read_cnt <= 0; 
    end
    else begin         
		if (l_r_req_int == 1'b1 && read_cnt< MULTIBURST_MAX) begin
            l_addr_rd <= l_addr_rd + l_b_size;
            read_cnt <= read_cnt +1;
        end 
    end
           
 
    if (read_cnt > MULTIBURST_MAX) 
        begin
        restart  <= 1'b1;
        end 
    else 
        restart  <= 1'b0;    
    end
end
//FSM for writing and reading memory
always @(posedge CLK or negedge RESETN)
begin
    if (!RESETN) begin
        IOD_FSM <= IDLE;
        l_w_req_int <= 1'b0;
        l_dm_in <= 32'hFFFFFFFF;
        l_r_req_int <= 1'b0;
        l_b_size <= 11'b0;
        count_beats <= 9'd0;
        read_count_beats <= 9'd0;
    end
    else
    begin
        case (IOD_FSM)
            IDLE :
            begin
            	l_dm_in <= 32'h00000000;
                l_r_req_int <= 1'b0;
                if (ctrl_ready_q == 1'b1 )
                	IOD_FSM <= WRITE;
                else 
                    IOD_FSM <= IDLE;  
            end 
            WRITE :
            begin
                l_w_req_int <= 1'b1;
                l_b_size  <= mburst_length;
                IOD_FSM <= WAIT_DATA;
            end
            WAIT_DATA :
            begin               
                if (count_beats == mburst_length_word-8) // asserting write request again for multi burst
                    l_w_req_int <= 1'b1;
                else
                    l_w_req_int <= 1'b0;
              
                if (l_d_req_int == 1'b1) begin
                        if (count_beats == mburst_length_word)
                            count_beats <= 1;
                        else
                            count_beats <= count_beats + 1;
                    IOD_FSM <= WAIT_DATA;           
                end
                if (last_w_burst == 1'b1)         // write done checking with last write burst
                    IOD_FSM <= WRITE_DONE;
                else
                    IOD_FSM <= WAIT_DATA;		                    
            end            
            WRITE_DONE:
            begin
                    if (l_d_req_int == 1'b0) begin
                        IOD_FSM     <= READ;
                        count_beats <= 9'd0;
                        l_r_req_int <= 1'b1;      //Asserting read request to SDRAM along with l_b_size
                    end
            end
            READ:
            begin
                l_r_req_int <= 1'b0;
                IOD_FSM     <= READ_DATA;
            end
            READ_DATA :
            begin    
                if (read_count_beats == mburst_length_word-8)
                    l_r_req_int <= 1'b1;
                else
                    l_r_req_int <= 1'b0;
                if (l_r_valid_int == 1'b1) begin
                        if (read_count_beats == mburst_length_word)
                            read_count_beats <= 1;
                        else
                            read_count_beats <= read_count_beats + 1;
                end
                if (last_r_burst == 1'b1)       //read done checking with last read burst
                    IOD_FSM <= READ_DONE;
                else
                    IOD_FSM <= READ_DATA;		
                    
            end  
            READ_DONE:
            begin
                    IOD_FSM <= WAIT5;
            end
            
            WAIT5:
            begin
                    IOD_FSM  <= WAIT5;
            end
            default:
                IOD_FSM <= IDLE;
        endcase
     end
end
//mburst_length_word 
always @(posedge CLK or negedge RESETN)
begin
    if (!RESETN) begin 
        mburst_length_word <= 0; 
        ref_data <= 0;
    end
    else begin
        mburst_length_word <=  mburst_length [10:5]; //{4'b0000, mburst_length [10:5]};
        ref_data <= ref_data_int;
    end
end


// Monitor logic
always@(posedge CLK)
begin 
    if(!RESETN )
        err_flg_v <= 0;
    else if(l_r_valid_int && (read_data == ref_data))
        err_flg_v <= 0;
    else if(l_r_valid_int && (read_data != ref_data))
        err_flg_v <= 1;        
    else 
        err_flg_v <=err_flg_v;
end

DDR3_UI_DATA #(
	.DATA_WIDTH (32))
INC_DATA_WRITE_0(
        .RESETN(RESETN),	  
        .CLK(CLK),	
        .data_sel (data_pattern),
        .read_req(l_d_req_int),
        .dataout(l_datain),
        .restart(restart)         	
        );
        
DDR3_UI_DATA #(
	.DATA_WIDTH (32)
)
INC_DATA_READ_0(
        .RESETN(RESETN),	  
        .CLK(CLK),
        .data_sel (data_pattern),	
        .read_req(l_r_valid),
        .dataout(ref_data_int),
        .restart(restart)         	
        );
        
last_burst_calc #(
        .MULTIBURST_MAX(16)
)
last_burst_calc(
        .CLK(CLK), 
        .RESETN(RESETN), 
        .l_d_req(l_d_req), 
        .l_d_req_last(l_d_req_last), 
        .l_r_valid(l_r_valid), 
        .l_r_valid_last(l_r_valid_last), 
        .last_w_burst(last_w_burst),
        .last_r_burst(last_r_burst)
        );      
endmodule
