// (C) 2001-2018 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.


// ******
// sequencer_m10
// ******
//
// Max10 RTL Sequencer
//
// General Description
// -------------------
//
// This component replaces the NIOS to calibrate Max10 EMIF
// Max10 EMIF uses PLL clock to capture DQ data
//
//

`timescale 1 ps / 1 ps

module sequencer_m10 #
    ( parameter
        AVL_DATA_WIDTH          = 32,
        AVL_ADDR_WIDTH          = 20, 
        READ_VALID_FIFO_SIZE    = 16,
        HPS_PROTOCOL            = "DDR3",
        PHY_MGR_BASE            = 294912,
        RW_MGR_BASE             = 327680,
        PLL_MGR_BASE            = 360448,
        MEM_DQ_WIDTH            = 24
    )
    (
	clk,
	reset_n,
	address,
	write,
	writedata,
	read,
	readdata,
	waitrequest,
	pd_reset_n,
	pd_ack,
	pd_up,
	pd_down
);

    localparam  PRE_N_ACT_1 =   (HPS_PROTOCOL == "DDR3") ? 'h14 : ((HPS_PROTOCOL == "DDR2") ? 'h16 : 'h14);
    localparam  PRE_N_ACT_2 =   (HPS_PROTOCOL == "DDR3") ? 'hf  : ((HPS_PROTOCOL == "DDR2") ? 'hf  : 'hf );
    localparam  PRE_N_ACT_3 =   (HPS_PROTOCOL == "DDR3") ? 'h10 : ((HPS_PROTOCOL == "DDR2") ? 'h12 : 'h10);
    localparam  PRE_N_ACT_4 =   (HPS_PROTOCOL == "DDR3") ? 'hf  : ((HPS_PROTOCOL == "DDR2") ? 'hf  : 'hf );
    localparam  PRE_N_ACT_5 =   (HPS_PROTOCOL == "DDR3") ? 'h12 : ((HPS_PROTOCOL == "DDR2") ? 'h14 : 'h12);
    localparam  PRE_N_ACT_6 =   (HPS_PROTOCOL == "DDR3") ? 'hf  : ((HPS_PROTOCOL == "DDR2") ? 'h11 : 'hf );
    
    localparam  GRNTEE_WR_1 =   (HPS_PROTOCOL == "DDR3") ? 'h20 : ((HPS_PROTOCOL == "DDR2") ? 'h20 : 'h20);
    localparam  GRNTEE_WR_2 =   (HPS_PROTOCOL == "DDR3") ? 'h1c : ((HPS_PROTOCOL == "DDR2") ? 'h1d : 'h19);
    localparam  GRNTEE_WR_3 =   (HPS_PROTOCOL == "DDR3") ? 'h20 : ((HPS_PROTOCOL == "DDR2") ? 'h20 : 'h20);
    localparam  GRNTEE_WR_4 =   (HPS_PROTOCOL == "DDR3") ? 'h20 : ((HPS_PROTOCOL == "DDR2") ? 'h21 : 'h1d);
    localparam  GRNTEE_WR_5 =   (HPS_PROTOCOL == "DDR3") ? 'h2  : ((HPS_PROTOCOL == "DDR2") ? 'h2  : 'h2 );
    localparam  GRNTEE_WR_6 =   (HPS_PROTOCOL == "DDR3") ? 'h1a : ((HPS_PROTOCOL == "DDR2") ? 'h1b : 'h17);
    localparam  GRNTEE_WR_7 =   (HPS_PROTOCOL == "DDR3") ? 'h2  : ((HPS_PROTOCOL == "DDR2") ? 'h2  : 'h2 );
    localparam  GRNTEE_WR_8 =   (HPS_PROTOCOL == "DDR3") ? 'h1e : ((HPS_PROTOCOL == "DDR2") ? 'h1f : 'h1b);
    localparam  GRNTEE_WR_9 =   (HPS_PROTOCOL == "DDR3") ? 'h19 : ((HPS_PROTOCOL == "DDR2") ? 'h1a : 'h16);
    
    localparam  READ_CHK_1  =   (HPS_PROTOCOL == "DDR3") ? 'h10 : ((HPS_PROTOCOL == "DDR2") ? 'h10 : 'h10);
    localparam  READ_CHK_2  =   (HPS_PROTOCOL == "DDR3") ? 'h45 : ((HPS_PROTOCOL == "DDR2") ? 'h46 : 'h42);
    localparam  READ_CHK_3  =   (HPS_PROTOCOL == "DDR3") ? 'h10 : ((HPS_PROTOCOL == "DDR2") ? 'h10 : 'h10);
    localparam  READ_CHK_4  =   (HPS_PROTOCOL == "DDR3") ? 'h4b : ((HPS_PROTOCOL == "DDR2") ? 'h4c : 'h48);
    localparam  READ_CHK_5  =   (HPS_PROTOCOL == "DDR3") ? 'h1  : ((HPS_PROTOCOL == "DDR2") ? 'h1  : 'h1 );
    localparam  READ_CHK_6  =   (HPS_PROTOCOL == "DDR3") ? 'h41 : ((HPS_PROTOCOL == "DDR2") ? 'h42 : 'h3e);
    localparam  READ_CHK_7  =   (HPS_PROTOCOL == "DDR3") ? 'h1  : ((HPS_PROTOCOL == "DDR2") ? 'h1  : 'h1 );
    localparam  READ_CHK_8  =   (HPS_PROTOCOL == "DDR3") ? 'h41 : ((HPS_PROTOCOL == "DDR2") ? 'h42 : 'h3e);
    localparam  READ_CHK_9  =   (HPS_PROTOCOL == "DDR3") ? 'h0  : ((HPS_PROTOCOL == "DDR2") ? 'h0  : 'h0 );
    localparam  READ_CHK_10 =   (HPS_PROTOCOL == "DDR3") ? 'h41 : ((HPS_PROTOCOL == "DDR2") ? 'h42 : 'h3e);
    localparam  READ_CHK_11 =   (HPS_PROTOCOL == "DDR3") ? 'h3e : ((HPS_PROTOCOL == "DDR2") ? 'h3f : 'h3b);
    
    localparam  READ_LATENCY    =   5'h16;
    localparam  DISABLE_FILTER  =    1'b0;
    localparam  DISABLE_PD      =    1'b0; 

	input                              clk;
	input                              reset_n;
	output [AVL_ADDR_WIDTH - 1:0]      address;
	output                             write;
	output [AVL_DATA_WIDTH - 1:0]      writedata;
	output                             read;
	input [AVL_DATA_WIDTH - 1:0]       readdata;
	input                              waitrequest;
	output                             pd_reset_n;
	output                             pd_ack;
	input                              pd_up;
	input                              pd_down;
	
	logic [AVL_ADDR_WIDTH - 1:0]       address;
	logic                              write;
	logic [AVL_DATA_WIDTH - 1:0]       writedata;
	logic                              read;
	logic                              pd_reset_n;
	logic                              pd_ack;
	
	logic [5:0]    seq_state;
	logic [AVL_ADDR_WIDTH - 1:0]       main_address;
	logic                              main_write;
	logic [AVL_DATA_WIDTH - 1:0]       main_writedata;
	logic                              main_read;
	logic [23:0]   readdata_reg;
	logic [23:0]   best_comp_result;
	logic          data_mismatch;
	logic [2:0]    vfifo;
	logic [7:0]    count;
	logic [7:0]    valid_phase;
	logic          reverse;
	logic          finding_window;
	logic          window_found;
	logic          latency_calib;
	logic [1:0]    grp;
	
	logic    start_ddr3_init;
	logic    ddr3_init_done;
	logic    start_ddr2_init;
	logic    ddr2_init_done;
	logic    start_lpddr2_init;
	logic    lpddr2_init_done;
	logic    start_write;
	logic    write_done;
	logic    start_user_mr;
	logic    user_mr_done;
	logic    pd_start;
	logic    cal_init_req;
	
	logic          wr_and_not_waitrequest;
	
	assign    wr_and_not_waitrequest    =    main_write & ~waitrequest;
	
    always_ff @(posedge clk, negedge reset_n)
        begin
            if (!reset_n)
                begin
                    seq_state         <=  0;
                    main_address      <=  '0;
                    main_write        <=  0;
                    main_writedata    <=  '0;
                    main_read         <=  0;
                    readdata_reg      <=  '0;
                    best_comp_result  <=  '0;
                    data_mismatch     <=  0;
                    vfifo             <=  '0;
                    count             <=  0;
                    valid_phase       <=  0;
                    reverse           <=  0;
                    finding_window    <=  0;
                    window_found      <=  0;
                    latency_calib     <=  0;
                    start_ddr3_init   <=  0;
                    start_ddr2_init   <=  0;
                    start_lpddr2_init <=  0;
                    start_write       <=  0;
                    start_user_mr     <=  0;
                    pd_start          <=  0;
                    grp               <=  '0;
                end
            else
                case(seq_state)
                    0   :
                        begin
                            best_comp_result <= {24{1'b1}};
                            if (HPS_PROTOCOL == "DDR3")
                                seq_state       <=  1;
                            else if (HPS_PROTOCOL == "DDR2")
                                seq_state       <=  2;
                            else // LPDDR2
                                seq_state       <=  3;
                        end
                    
                    // start DDR3 initialization sequence
                    1   :
                        begin
                            start_ddr3_init     <=  1;
                            if (ddr3_init_done)
                                begin
                                    start_ddr3_init <=  0;
                                    seq_state       <=  4;
                                end
                        end
                    // end of DDR3 initialization sequence
                    
                    // start DDR2 initialization sequence
                    2   :
                        begin
                            start_ddr2_init     <=  1;
                            if (ddr2_init_done)
                                begin
                                    start_ddr2_init <=  0;
                                    seq_state       <=  4;
                                end
                        end
                    // end of DDR2 initialization sequence
                    
                    // start LPDDR2 initialization sequence
                    3   :
                        begin
                            start_lpddr2_init   <=  1;
                            if (lpddr2_init_done)
                                begin
                                    start_lpddr2_init   <=  0;
                                    seq_state       <=  4;
                                end
                        end
                    // end of LPDDR2 initialization sequence
                    
                    // precharge and activate then guarantee write
                    4   :
                        begin
                            start_write   <=  1;
                            if (write_done)
                                begin
                                    start_write     <=  0;
                                    seq_state       <=  5;
                                end
                        end
                    
                    // read data checking routine
                    5   : write_task_rw ('h201     ,READ_CHK_1  ,6  );
                    6   : write_task_rw ('h301     ,READ_CHK_2  ,7  );
                    7   : write_task_rw ('h202     ,READ_CHK_3  ,8  );
                    8   : write_task_rw ('h302     ,READ_CHK_4  ,9  );
                    9   : write_task_rw ('h200     ,READ_CHK_5  ,10 );
                    10  : write_task_rw ('h300     ,READ_CHK_6  ,11 );
                    11  : write_task_rw ('h203     ,READ_CHK_7  ,12 );
                    12  : write_task_rw ('h303     ,READ_CHK_8  ,13 );
                    13  : write_task_ph ('h2       ,'h0         ,14 ); // PHY manager, read fifo reset
                    14  : write_task_rw ('h400     ,READ_CHK_9  ,15 );
                    15  : write_task_rw (grp       ,READ_CHK_10 ,16 );
                    16  :
                        begin
                            main_read    <=  1;
                            main_address <=  RW_MGR_BASE[AVL_ADDR_WIDTH - 1:0];//
                            if (main_read && !waitrequest)
                                begin
                                    main_read       <=  0;
                                    main_address    <=  '0;
                                    seq_state       <=  38;
                                    if (grp == 2)
                                        readdata_reg[23:16]    <=  readdata[7:0];
                                    else if (grp == 1)
                                        readdata_reg[15:8]    <=  readdata[7:0];
                                    else
                                        readdata_reg[7:0]    <=  readdata[7:0];
                                end
                        end
                    38  :
                        begin
                            if (MEM_DQ_WIDTH == 24)
                                begin
                                    if (grp == 2)
                                        begin
                                            grp         <=  0;
                                            seq_state   <=  17;
                                        end
                                    else
                                        begin
                                            grp         <=  grp + 1'b1;
                                            seq_state   <=  5;
                                        end
                                end
                            else if (MEM_DQ_WIDTH == 16)
                                begin
                                    if (grp == 1)
                                        begin
                                            grp         <=  0;
                                            seq_state   <=  17;
                                        end
                                    else
                                        begin
                                            grp         <=  grp + 1'b1;
                                            seq_state   <=  5;
                                        end
                                end
                            else 
                                seq_state   <=  17;
                        end
                    17  : write_task_rw ('h0       ,READ_CHK_11 ,18 );
                    18  :
                        begin
                            if (readdata_reg != 0)
                                data_mismatch   <=  1;
                            else
                                data_mismatch   <=  0;
                            
                            if (readdata_reg[23:16] < best_comp_result[23:16])
                                best_comp_result[23:16]    <=  readdata_reg[23:16];
                            
                            if (readdata_reg[15:8] < best_comp_result[15:8])
                                best_comp_result[15:8]    <=  readdata_reg[15:8];
                            
                            if (readdata_reg[7:0] < best_comp_result[7:0])
                                best_comp_result[7:0]    <=  readdata_reg[7:0];
                                
                            if (latency_calib)
                                seq_state   <=  29;
                            else if (finding_window)
                                seq_state   <=  26;
                            else if (reverse)
                                seq_state   <=  24;
                            else
                                seq_state   <=  19;
                        end
                    // end read data checking routine
                    
                    // find first working phase
                    19  :
                        begin
                            if (data_mismatch) // data mismatch
                                begin
                                    if (count == 150) // 57x2 is the calculated steps for 167Mhz
                                        seq_state   <=  22; // give up, fail calibration
                                    else if (vfifo == 7) // increment PLL phase
                                        begin
                                            write_task_capture_clock (1         ,20 );
                                            
                                            if (wr_and_not_waitrequest)
                                                count       <=  count + 1'b1;
                                        end
                                    else
                                        seq_state   <=  20;
                                end
                            else // data match! then reverse phase
                                begin
                                    $display("          --- SEQUENCER FOUND FIRST WORKING PHASE --- ");
                                    reverse             <=  1;
                                    seq_state           <=  5 ;
                                end
                        end
                    20  :
                        begin
                            vfifo       <=  vfifo + 1'b1;
                            seq_state   <=  21;
                        end
                    21  : write_task_ph ('h1       ,'hff        ,5  ); // increment vfifo for all groups
                    // algorithm ends here
                    
                    // PHY manager, cal fail
                    22  : write_task_ph ('h1003    ,'h2         ,36 );
                    
                    // reverse phase until fail, useful if already in a working phase on start
                    24 :
                        begin
                            if (data_mismatch) //data mismatch, increment and move to finding window
                                begin
                                    reverse     <=  0;
                                    write_task_capture_clock (1         ,25 );
                                end
                            else // data match, decrement until it fails
                                write_task_capture_clock (0         ,5  );
                        end
                    
                    // finding window, sweep till fail
                    25  :
                        begin
                            $display("          --- START FINDING WINDOW --- ");
                            finding_window      <=  1;
                            count               <=  1;
                            seq_state           <=  5 ;
                        end
                    26  :
                        begin
                            if (data_mismatch) // data mismatch
                                begin
                                    if (!window_found) 
                                        write_task_capture_clock (1         ,5  ); 
                                    else
                                        begin
                                            $display("          --- TOTAL VALID PHASE %d --- ",count - 1);
                                            valid_phase     <=  count-1'b1;
                                            finding_window  <=  0;
                                            count           <=  count/2'd2; // formula to move the correct number of phase
                                            $display("          --- REVERSE TO CENTER %d PHASE(S)--- ",((count - 2)/2)+2);
                                            seq_state       <=  27;
                                        end
                                end
                            else
                                begin
                                    window_found    <=  1;
                                    write_task_capture_clock (1         ,5  ); // increment PLL phase
                                    
                                    if (wr_and_not_waitrequest)
                                        count           <=  count+1'b1;
                                end
                        end
                    
                    // going to the center of window after sweep
                    27  :
                        begin
                            if (count != 0)
                                begin
                                    write_task_capture_clock (0         ,27 ); // decrement PLL phase
                                    
                                    if (wr_and_not_waitrequest)
                                        count       <=  count-1'b1;
                                end
                            else
                                seq_state   <=  31;
                        end
                    
                    // calibration done
                    31  : write_task_rw ('h0       ,PRE_N_ACT_1 ,32 ); // precharge
                    32  : write_task_ph ('h2       ,'h0         ,33 ); // PHY manager, read fifo reset
                    33  :
                        begin
                            start_user_mr   <=  1;
                            if (user_mr_done)
                                begin
                                    start_user_mr   <=  0;
                                    seq_state       <=  34;
                                end
                        end
                    
                    // PHY manager, mux sel
                    34  : write_task_ph ('h1002    ,'h0         ,35 );
                    // PHY manager, cal success
                    35  : write_task_ph ('h1003    ,'h1         ,36 );
                    // PHY manager, debug info reports window size, best comparison data
                    36  : write_task_ph ('h1004    ,{best_comp_result,valid_phase}         ,37 );
                    // calibration done
                    37  : 
                        begin
                            if (pd_start && cal_init_req)
                                begin
                                    pd_start <=  0;
                                    seq_state   <=  39;
                                    
                                    readdata_reg      <=  '0;
                                    best_comp_result  <=  '0;
                                    data_mismatch     <=  0;
                                    vfifo             <=  '0;
                                    count             <=  0;
                                    valid_phase       <=  0;
                                    reverse           <=  0;
                                    finding_window    <=  0;
                                    window_found      <=  0;
                                    latency_calib     <=  0;
                                    grp               <=  '0;
                                end
                            else
                                begin
                                    pd_start <=  1;
                                end
                        end
                    39  : write_task_ph ('h1003    ,'h0         ,40 );
                    40  : write_task_ph ('h1002    ,'h1         ,0  );
                endcase
        end
        
task write_task_rw;
    input [AVL_ADDR_WIDTH - 1:0] task_address;
    input [AVL_DATA_WIDTH - 1:0] data;
    input [5:0] next_state;
    
    begin
        main_write       <=  1;
        main_address     <=  RW_MGR_BASE[AVL_ADDR_WIDTH - 1:0] + {task_address[AVL_ADDR_WIDTH - 3:0],2'b0};
        main_writedata   <=  data;
        if (wr_and_not_waitrequest)
            begin
                main_write       <=  0;
                main_address     <=  '0;
                main_writedata   <=  '0;
                seq_state   <=  next_state;
            end
    end
endtask

task write_task_ph;
    input [AVL_ADDR_WIDTH - 1:0] task_address;
    input [AVL_DATA_WIDTH - 1:0] data;
    input [5:0] next_state;
    
    begin
        main_write       <=  1;
        main_address     <=  PHY_MGR_BASE[AVL_ADDR_WIDTH - 1:0] + {task_address[AVL_ADDR_WIDTH - 3:0],2'b0};
        main_writedata   <=  data;
        if (wr_and_not_waitrequest)
            begin
                main_write       <=  0;
                main_address     <=  '0;
                main_writedata   <=  '0;
                seq_state   <=  next_state;
            end
    end
endtask

task write_task_capture_clock;
    input data;
    input [5:0] next_state;
    
    begin
        main_write       <=  1;
        main_address     <=  PLL_MGR_BASE[AVL_ADDR_WIDTH - 1:0];
        main_writedata   <=  data;
        if (wr_and_not_waitrequest)
            begin
                main_write       <=  0;
                main_address     <=  '0;
                main_writedata   <=  '0;
                seq_state   <=  next_state;
            end
    end
endtask
    
	logic [4:0]    ddr3_init_state;
	logic [AVL_ADDR_WIDTH - 1:0]       ddr3_init_address;
	logic                              ddr3_init_write;
	logic [AVL_DATA_WIDTH - 1:0]       ddr3_init_writedata;

generate
    if (HPS_PROTOCOL == "DDR3")
        begin
            task ddr3_init_write_task_rw;
                input [AVL_ADDR_WIDTH - 1:0] task_address;
                input [AVL_DATA_WIDTH - 1:0] data;
                input [4:0] next_state;
            
                begin
                    ddr3_init_write       <=  1;
                    ddr3_init_address     <=  RW_MGR_BASE[AVL_ADDR_WIDTH - 1:0] + {task_address[AVL_ADDR_WIDTH - 3:0],2'b0};
                    ddr3_init_writedata   <=  data;
                    if (ddr3_init_write && !waitrequest)
                        begin
                            ddr3_init_write       <=  0;
                            ddr3_init_address     <=  '0;
                            ddr3_init_writedata   <=  '0;
                            ddr3_init_state   <=  next_state;
                        end
                end
            endtask            
            always_ff @(posedge clk, negedge reset_n)
                begin
                    if (!reset_n)
                        begin
                            ddr3_init_state       <=  0;
                            ddr3_init_done        <=  0;
                            ddr3_init_address     <=  '0;
                            ddr3_init_write       <=  0;
                            ddr3_init_writedata   <=  '0;
                        end
                    else
                        case(ddr3_init_state)
                            0   : if (start_ddr3_init) ddr3_init_state <=  1;
                            // start DDR3 initialization sequence
                            //                   address    data         next_state
                            1   : ddr3_init_write_task_rw ('h800     ,'h0         ,2  );
                            2   : ddr3_init_write_task_rw ('h500     ,'hfe        ,3  );
                            3   : ddr3_init_write_task_rw ('h500     ,'h0         ,4  );
                            4   : ddr3_init_write_task_rw ('h200     ,'hff         ,5  );
                            5   : ddr3_init_write_task_rw ('h201     ,'hff         ,6  );
                            6   : ddr3_init_write_task_rw ('h300     ,'h4f        ,7  );
                            7   : ddr3_init_write_task_rw ('h301     ,'h50        ,8  );
                            8   : ddr3_init_write_task_rw ('h0       ,'h4f        ,9  ); // delay?
                            9   : ddr3_init_write_task_rw ('h200     ,'hff         ,10 );
                            10  : ddr3_init_write_task_rw ('h201     ,'hff         ,11 );
                            11  : ddr3_init_write_task_rw ('h300     ,'h54        ,12 );
                            12  : ddr3_init_write_task_rw ('h301     ,'h55        ,13 );
                            13  : ddr3_init_write_task_rw ('h0       ,'h54        ,14 ); // reset goes high
                            14  : ddr3_init_write_task_rw ('h201     ,'h7c        ,15 ); 
                            15  : ddr3_init_write_task_rw ('h301     ,'h5c        ,16 ); 
                            16  : ddr3_init_write_task_rw ('h0       ,'h5c        ,17 ); /// cke goes high
                            17  : ddr3_init_write_task_rw ('h0       ,'h7         ,18 ); // MR bank 2
                            18  : ddr3_init_write_task_rw ('h0       ,'h9         ,19 ); // MR bank 3
                            19  : ddr3_init_write_task_rw ('h0       ,'h5         ,20 ); // MR bank 1
                            20  : ddr3_init_write_task_rw ('h0       ,'h3         ,21 ); // DLL reset
                            21  : ddr3_init_write_task_rw ('h0       ,'hb         ,22 ); // ZQ cal
                            // end of DDR3 initialization sequence
                            22  :
                                begin
                                    if (cal_init_req)
                                        begin
                                            ddr3_init_done      <=  0;
                                            ddr3_init_state     <=  0;
                                        end
                                    else
                                        ddr3_init_done    <=  1;
                                end
                        endcase
                end
            
        end
    else
        begin
            assign  ddr3_init_done          =   0;
            assign  ddr3_init_address       =   '0;
            assign  ddr3_init_write         =   0;
            assign  ddr3_init_writedata     =   '0;
        end
endgenerate
        
    logic [4:0]    ddr2_init_state;
	logic [AVL_ADDR_WIDTH - 1:0]       ddr2_init_address;
	logic                              ddr2_init_write;
	logic [AVL_DATA_WIDTH - 1:0]       ddr2_init_writedata;
	logic [6:0]    count2;

generate
    if (HPS_PROTOCOL == "DDR2")
        begin
                task ddr2_init_write_task_rw;
                input [AVL_ADDR_WIDTH - 1:0] task_address;
                input [AVL_DATA_WIDTH - 1:0] data;
                input [4:0] next_state;
            
                begin
                    ddr2_init_write       <=  1;
                    ddr2_init_address     <=  RW_MGR_BASE[AVL_ADDR_WIDTH - 1:0] + {task_address[AVL_ADDR_WIDTH - 3:0],2'b0};
                    ddr2_init_writedata   <=  data;
                    if (ddr2_init_write && !waitrequest)
                        begin
                            ddr2_init_write       <=  0;
                            ddr2_init_address     <=  '0;
                            ddr2_init_writedata   <=  '0;
                            ddr2_init_state   <=  next_state;
                        end
                end
            endtask
            always_ff @(posedge clk, negedge reset_n)
                begin
                    if (!reset_n)
                        begin
                            ddr2_init_state       <=  0;
                            ddr2_init_done        <=  0;
                            ddr2_init_address     <=  '0;
                            ddr2_init_write       <=  0;
                            ddr2_init_writedata   <=  '0;
                            count2                <=  '0;
                        end
                    else
                        case(ddr2_init_state)
                            0   : if (start_ddr2_init) ddr2_init_state <=  1;
                            // start DDR2 initialization sequence
                            //                   address    data         next_state
                            1   : ddr2_init_write_task_rw ('h800     ,'h0         ,2  );
                            2   : ddr2_init_write_task_rw ('h500     ,'hfe        ,3  );
                            3   : ddr2_init_write_task_rw ('h200     ,'hff        ,4  );
                            4   : ddr2_init_write_task_rw ('h201     ,'hff        ,5  );
                            5   : ddr2_init_write_task_rw ('h300     ,'h50        ,6  );
                            6   : ddr2_init_write_task_rw ('h301     ,'h51        ,7  );
                            7   : ddr2_init_write_task_rw ('h0       ,'h50        ,8  ); // delay?
                            8   : ddr2_init_write_task_rw ('h0       ,'h1         ,22 ); // CKE goes high
                            22  :
                                begin
                                    if (count2 == 100)
                                        begin
                                            ddr2_init_state   <=  9;
                                            count2            <=  0;
                                        end
                                    else
                                        count2      <=  count2 + 1'b1;
                                end
                            9   : ddr2_init_write_task_rw ('h0       ,'h16        ,10 ); // precharge
                            10  : ddr2_init_write_task_rw ('h0       ,'hd         ,11 ); // MR bank 2
                            11  : ddr2_init_write_task_rw ('h0       ,'hf         ,12 ); // MR bank 3
                            12  : ddr2_init_write_task_rw ('h0       ,'h9         ,13 ); // MR bank 1
                            13  : ddr2_init_write_task_rw ('h0       ,'h7         ,14 ); // MR bank 0
                            14  : ddr2_init_write_task_rw ('h0       ,'h16        ,15 ); // precharge
                            15  : ddr2_init_write_task_rw ('h0       ,'h18        ,16 ); // refresh
                            16  :
                                begin
                                    if (count2 == 100)
                                        begin
                                            ddr2_init_state   <=  17;
                                            count2            <=  0;
                                        end
                                    else
                                        count2      <=  count2 + 1'b1;
                                end
                            17  : ddr2_init_write_task_rw ('h0       ,'h18        ,23 ); // refresh
                            23  :
                                begin
                                    if (count2 == 100)
                                        begin
                                            ddr2_init_state   <=  18;
                                            count2            <=  0;
                                        end
                                    else
                                        count2      <=  count2 + 1'b1;
                                end
                            18  : ddr2_init_write_task_rw ('h0       ,'h5         ,19 ); // MR bank 0
                            19  : ddr2_init_write_task_rw ('h0       ,'hb         ,20 ); // MR bank 1
                            20  : ddr2_init_write_task_rw ('h0       ,'h9         ,21 ); // MR bank 1
                            // end of DDR2 initialization sequence
                            21  :
                                begin
                                    if (cal_init_req)
                                        begin
                                            ddr2_init_done      <=  0;
                                            ddr2_init_state     <=  0;
                                        end
                                    else
                                        ddr2_init_done    <=  1;
                                end
                        endcase
                end
            
        end
    else
        begin
            assign  ddr2_init_done          =   0;
            assign  ddr2_init_address       =   '0;
            assign  ddr2_init_write         =   0;
            assign  ddr2_init_writedata     =   '0;
        end
endgenerate
        
    logic [3:0]    lpddr2_init_state;
	logic [AVL_ADDR_WIDTH - 1:0]       lpddr2_init_address;
	logic                              lpddr2_init_write;
	logic [AVL_DATA_WIDTH - 1:0]       lpddr2_init_writedata;
	logic [7:0]    count4;
	logic [7:0]    count5;

generate
    if (HPS_PROTOCOL == "LPDDR2")
        begin
            task lpddr2_init_write_task_rw;
                input [AVL_ADDR_WIDTH - 1:0] task_address;
                input [AVL_DATA_WIDTH - 1:0] data;
                input [3:0] next_state;
                begin
                    lpddr2_init_write       <=  1;
                    lpddr2_init_address     <=  RW_MGR_BASE[AVL_ADDR_WIDTH - 1:0] + {task_address[AVL_ADDR_WIDTH - 3:0],2'b0};
                    lpddr2_init_writedata   <=  data;
                    if (lpddr2_init_write && !waitrequest)
                        begin
                            lpddr2_init_write       <=  0;
                            lpddr2_init_address     <=  '0;
                            lpddr2_init_writedata   <=  '0;
                            lpddr2_init_state   <=  next_state;
                        end
                end
            endtask
            
            always_ff @(posedge clk, negedge reset_n)
                begin
                    if (!reset_n)
                        begin
                            lpddr2_init_state       <=  0;
                            lpddr2_init_done        <=  0;
                            lpddr2_init_address     <=  '0;
                            lpddr2_init_write       <=  0;
                            lpddr2_init_writedata   <=  '0;
                            count4                  <=  '0;
                            count5                  <=  '0;
                        end
                    else
                        case(lpddr2_init_state)
                            0   : if (start_lpddr2_init) lpddr2_init_state <=  1;
                            // start LPDDR2 initialization sequence
                            //                   address    data         next_state
                            1   : lpddr2_init_write_task_rw ('h800     ,'h0         ,2  );
                            2   : lpddr2_init_write_task_rw ('h500     ,'hfe        ,3  );
                            3   : lpddr2_init_write_task_rw ('h200     ,'h10        ,4  );
                            4   : lpddr2_init_write_task_rw ('h300     ,'h4c        ,15  );
                            15  : 
                                begin
                                    if (&count4)
                                        begin
                                            count4 <=  0;
                                            if (&count5)
                                                begin
                                                    count5 <= 0;
                                                    lpddr2_init_state   <=  5;
                                                end
                                            else
                                                count5 <=  count5 + 1'b1;
                                        end
                                    else
                                        count4 <=  count4 + 1'b1;
                                end 
                            5   : lpddr2_init_write_task_rw ('h0       ,'h4c        ,12 ); // CKE goes high
                            12  : //200us delay
                                begin
                                    if (&count4)
                                        begin
                                            count4            <=  0;
                                            if (&count5)
                                                begin
                                                    lpddr2_init_state   <=  6;
                                                    count5            <=  0;
                                                end
                                            else
                                                count5      <=  count5 + 1'b1;
                                        end
                                    else
                                        count4      <=  count4 + 1'b1;
                                end
                            6   : lpddr2_init_write_task_rw ('h0       ,'hd         ,13 ); // MRW reset
                            13  : //10us delay
                                begin
                                    if (count4 == 100)
                                        begin
                                            count4            <=  0;
                                            if (count5 == 10)
                                                begin
                                                    lpddr2_init_state   <=  7;
                                                    count5            <=  0;
                                                end
                                            else
                                                count5      <=  count5 + 1'b1;
                                        end
                                    else
                                        count4      <=  count4 + 1'b1;
                                end
                            7   : lpddr2_init_write_task_rw ('h0       ,'hb         ,14 ); // MRW MR10 ZQcal FF
                            14  : //1us delay
                                begin
                                    if (count4 == 100)
                                        begin
                                            lpddr2_init_state   <=  8;
                                            count4            <=  0;
                                        end
                                    else
                                        count4      <=  count4 + 1'b1;
                                end
                            8   : lpddr2_init_write_task_rw ('h0       ,'h3         ,9  ); // MRW MR1
                            9   : lpddr2_init_write_task_rw ('h0       ,'h7         ,10 ); // MRW MR2
                            10  : lpddr2_init_write_task_rw ('h0       ,'h9         ,11 ); // MRW MR3
                            // end of LPDDR2 initialization sequence
                            11  :
                                begin
                                    if (cal_init_req)
                                        begin
                                            lpddr2_init_done      <=  0;
                                            lpddr2_init_state     <=  0;
                                        end
                                    else
                                        lpddr2_init_done    <=  1;
                                end
                        endcase
                end
            
        end
    else
        begin
            assign  lpddr2_init_done        =   0;
            assign  lpddr2_init_address     =   '0;
            assign  lpddr2_init_write       =   0;
            assign  lpddr2_init_writedata   =   '0;
        end
endgenerate
        
    logic [4:0]    write_state;
	logic [AVL_ADDR_WIDTH - 1:0]       write_address;
	logic                              write_write;
	logic [AVL_DATA_WIDTH - 1:0]       write_writedata;
	logic [8:0]    count3;

    always_ff @(posedge clk, negedge reset_n)
        begin
            if (!reset_n)
                begin
                    write_state       <=  0;
                    write_done        <=  0;
                    write_address     <=  '0;
                    write_write       <=  0;
                    write_writedata   <=  '0;
                    count3            <=  '0;
                end
            else
                case(write_state)
                    0   : if (start_write) write_state <=  1;
                    // precharge and activate
                    1   :
                        begin
                            if (&count3)
                                begin
                                    write_state <=  2;
                                    count3      <=  0;
                                end
                            else
                                count3      <=  count3 + 1'b1;
                        end
                    2   : write_write_task_rw ('h0       ,PRE_N_ACT_1 ,3  );
                    3   : write_write_task_rw ('h200     ,PRE_N_ACT_2 ,4  );
                    4   : write_write_task_rw ('h300     ,PRE_N_ACT_3 ,5  );
                    5   : write_write_task_rw ('h201     ,PRE_N_ACT_4 ,6  );
                    6   : write_write_task_rw ('h301     ,PRE_N_ACT_5 ,7  );
                    7   : write_write_task_rw ('h0       ,PRE_N_ACT_6 ,8  );
                    
                    // guaranteed write
                    8   : write_write_task_rw ('h200     ,GRNTEE_WR_1 ,9  );
                    9   : write_write_task_rw ('h300     ,GRNTEE_WR_2 ,10 );
                    10  : write_write_task_rw ('h201     ,GRNTEE_WR_3 ,11 );
                    11  : write_write_task_rw ('h301     ,GRNTEE_WR_4 ,12 );
                    12  : write_write_task_rw ('h202     ,GRNTEE_WR_5 ,13 );
                    13  : write_write_task_rw ('h302     ,GRNTEE_WR_6 ,14 );
                    14  : write_write_task_rw ('h203     ,GRNTEE_WR_7 ,15 );
                    15  : write_write_task_rw ('h303     ,GRNTEE_WR_8 ,16 );
                    16  : write_write_task_rw ('h0       ,GRNTEE_WR_9 ,17 );
                    17  :
                        begin
                            if (cal_init_req)
                                begin
                                    write_done      <=  0;
                                    write_state     <=  0;
                                end
                            else
                                write_done    <=  1;
                        end
                endcase
        end
        
task write_write_task_rw;
    input [AVL_ADDR_WIDTH - 1:0] task_address;
    input [AVL_DATA_WIDTH - 1:0] data;
    input [4:0] next_state;
    
    begin
        write_write       <=  1;
        write_address     <=  RW_MGR_BASE[AVL_ADDR_WIDTH - 1:0] + {task_address[AVL_ADDR_WIDTH - 3:0],2'b0};
        write_writedata   <=  data;
        if (write_write && !waitrequest)
            begin
                write_write       <=  0;
                write_address     <=  '0;
                write_writedata   <=  '0;
                write_state   <=  next_state;
            end
    end
endtask
    
    logic [3:0]    user_mr_state;
	logic [AVL_ADDR_WIDTH - 1:0]       user_mr_address;
	logic                              user_mr_write;
	logic [AVL_DATA_WIDTH - 1:0]       user_mr_writedata;

    always_ff @(posedge clk, negedge reset_n)
        begin
            if (!reset_n)
                begin
                    user_mr_state       <=  0;
                    user_mr_done        <=  0;
                    user_mr_address     <=  '0;
                    user_mr_write       <=  0;
                    user_mr_writedata   <=  '0;
                end
            else
                case(user_mr_state)
                    0   : if (start_user_mr) user_mr_state <=  1;
                    // precharge and activate
                    1   :
                        begin
                            if (HPS_PROTOCOL == "DDR3")
                                user_mr_state   <=  2;
                            else if (HPS_PROTOCOL == "DDR2")
                                user_mr_state   <=  7;
                            else // LPDDR2
                                user_mr_state   <=  11;
                        end
                    
                    // DDR3 user MR start
                    2   : user_mr_write_task_rw ('h0       ,'h14        ,3  );
                    3   : user_mr_write_task_rw ('h0       ,'h7         ,4  );
                    4   : user_mr_write_task_rw ('h0       ,'h9         ,5  );
                    5   : user_mr_write_task_rw ('h0       ,'h5         ,6  );
                    6   : user_mr_write_task_rw ('h0       ,'hd         ,14 );
                    // DDR3 user MR ends
                    
                    // DDR2 user MR start
                    7   : user_mr_write_task_rw ('h0       ,'hd         ,8  );
                    8   : user_mr_write_task_rw ('h0       ,'hf         ,9  );
                    9   : user_mr_write_task_rw ('h0       ,'h9         ,10 );
                    10  : user_mr_write_task_rw ('h0       ,'h3         ,14 );
                    // DDR2 user MR ends
                    
                    // LPDDR2 user MR start
                    11  : user_mr_write_task_rw ('h0       ,'h5         ,12 );
                    12  : user_mr_write_task_rw ('h0       ,'h7         ,13 );
                    13  : user_mr_write_task_rw ('h0       ,'h9         ,14 );
                    // LPDDR2 user MR ends
                    14  :
                        begin
                            if (cal_init_req)
                                begin
                                    user_mr_done      <=  0;
                                    user_mr_state     <=  0;
                                end
                            else
                                user_mr_done    <=  1;
                        end
                endcase
        end
        
task user_mr_write_task_rw;
    input [AVL_ADDR_WIDTH - 1:0] task_address;
    input [AVL_DATA_WIDTH - 1:0] data;
    input [3:0] next_state;
    
    begin
        user_mr_write       <=  1;
        user_mr_address     <=  RW_MGR_BASE[AVL_ADDR_WIDTH - 1:0] + {task_address[AVL_ADDR_WIDTH - 3:0],2'b0};
        user_mr_writedata   <=  data;
        if (user_mr_write && !waitrequest)
            begin
                user_mr_write       <=  0;
                user_mr_address     <=  '0;
                user_mr_writedata   <=  '0;
                user_mr_state   <=  next_state;
            end
    end
endtask
    
    logic [3:0]    pd_state;
	logic [AVL_ADDR_WIDTH - 1:0]       pd_address;
	logic                              pd_write;
	logic                              pd_read;
	logic [AVL_DATA_WIDTH - 1:0]       pd_writedata;
	logic [3:0]    lock_count;
	logic                              pd_down_r;
	logic                              pd_up_r;
	logic [3:0]    cal_chk_count;

    always_ff @(posedge clk, negedge reset_n)
        begin
            if (!reset_n)
                begin
                    pd_down_r      <=  0;
                    pd_up_r        <=  0;
                end
            else
                if (pd_state == 2 || pd_state == 6)
                    begin
                        pd_down_r   <=  pd_down;
                        pd_up_r     <=  pd_up;
                    end
        end
                    
    always_ff @(posedge clk, negedge reset_n)
        begin
            if (!reset_n)
                begin
                    pd_state       <=  0;
                    pd_address     <=  '0;
                    pd_write       <=  0;
                    pd_read        <=  0;
                    pd_writedata   <=  '0;
                    pd_reset_n     <=  0;
                    pd_ack         <=  0;
                    lock_count     <=  '0;
                    cal_init_req   <=  0;
                    cal_chk_count  <=  '0;
                end
            else
                case(pd_state)
                    0   :
                        begin
                            if (pd_start && !cal_init_req)
                                begin
                                    pd_reset_n  <=  1;
                                    pd_state    <=  1;
                                end
                            
                            if (cal_init_req)
                                cal_init_req    <=  0;
                        end
                    1   :
                        begin
                            pd_ack  <=  0;
                            if (pd_down)
                                pd_write_task_tracking_clock (0,2);
                            else if (pd_up)
                                pd_write_task_tracking_clock (1,2);
                        end
                    2   :
                        begin
                            if (pd_down_r && pd_up)
                                lock_count  <=  lock_count + 1'b1;
                            else if (pd_up_r && pd_down)
                                lock_count  <=  lock_count + 1'b1;
                            
                            pd_ack  <=  1;
                            pd_state    <=  3;
                        end
                    3   :
                        begin
                            if (lock_count >= 8)
                                pd_state    <=  4;
                            else
                                pd_state    <=  1;
                        end
                    4   :
                        begin
                            pd_ack  <=  0;
                            if (pd_down)
                                begin
                                    if (pd_down_r || DISABLE_FILTER)
                                        begin
                                            if (DISABLE_PD)
                                                pd_state    <=  5;
                                            else
                                                pd_write_task_capture_clock (0,5);
                                        end
                                    else
                                        pd_state    <=  5;
                                end
                            else if (pd_up)
                                begin
                                    if (pd_up_r || DISABLE_FILTER)
                                        begin
                                            if (DISABLE_PD)
                                                pd_state    <=  5;
                                            else
                                                pd_write_task_capture_clock (1,5);
                                        end
                                    else
                                        pd_state    <=  5;
                                end
                        end
                    5   :
                        begin
                            if (pd_down)
                                pd_write_task_tracking_clock (0,6);
                            else if (pd_up)
                                pd_write_task_tracking_clock (1,6);
                        end
                    6   :
                        begin
                            pd_ack      <=  1;
                            if (cal_chk_count == 4'd10)
                                begin
                                    cal_chk_count   <=  '0;
                                    pd_state    <=  7;
                                end
                            else
                                begin
                                    cal_chk_count   <=  cal_chk_count + 1'b1;
                                    pd_state    <=  8;
                                end
                        end
                    7   :
                        begin
                            pd_read    <=  1;
                            pd_address <=  PLL_MGR_BASE[AVL_ADDR_WIDTH - 1:0]+{2'd2,2'b00}; 
                            if (pd_read && !waitrequest)
                                begin
                                    pd_read       <=  0;
                                    pd_address    <=  '0;
                                    if (readdata[0] == 1)
                                        begin
                                            pd_state        <=  0;
                                            cal_init_req    <=  1;
                                            lock_count      <=  0;
                                        end
                                    else
                                        pd_state      <=  8;
                                end
                        end
                    8   :
                        pd_state    <=  4;
                endcase
        end

task pd_write_task_tracking_clock;
    input data;
    input [2:0] next_state;
    
    begin
        pd_write       <=  1;
        pd_address     <=  PLL_MGR_BASE[AVL_ADDR_WIDTH - 1:0]+{1'b1,2'b00}; // tracking clock
        pd_writedata   <=  data;
        if (pd_write && !waitrequest)
            begin
                pd_write       <=  0;
                pd_address     <=  '0;
                pd_writedata   <=  '0;
                pd_state   <=  next_state;
            end
    end
endtask

task pd_write_task_capture_clock;
    input data;
    input [2:0] next_state;
    
    begin
        pd_write       <=  1;
        pd_address     <=  PLL_MGR_BASE[AVL_ADDR_WIDTH - 1:0];
        pd_writedata   <=  data;
        if (pd_write && !waitrequest)
            begin
                pd_write       <=  0;
                pd_address     <=  '0;
                pd_writedata   <=  '0;
                pd_state   <=  next_state;
            end
    end
endtask

	assign    address        =    main_address | ddr3_init_address | ddr2_init_address | lpddr2_init_address | write_address | user_mr_address | pd_address;
	assign    write          =    main_write | ddr3_init_write | ddr2_init_write | lpddr2_init_write | write_write | user_mr_write | pd_write;
	assign    writedata      =    main_writedata | ddr3_init_writedata | ddr2_init_writedata | lpddr2_init_writedata | write_writedata | user_mr_writedata | pd_writedata;
	assign    read           =    main_read | pd_read;

endmodule
