module array_rd (
    input               clk,
    input               rst_n,

    input               array_rd_frame_valid,
    input   [151:0]     array_rd_frame_data,
    output              array_rd_frame_ready,
    output              array_rd_done,

    output  reg         array_rd_csn,
    output  reg [15:0]  array_rd_raddr,

    output  reg         array_caddr_vld_rd,
    output  reg [5:0]   array_caddr_rd,
    input               array_rdata_vld,
    input   [127:0]     array_rdata,

    input   [7:0]       array_inner_tras,
    input   [7:0]       array_inner_trp,
    input   [7:0]       array_inner_trcd_rd,
    input   [7:0]       array_inner_trtp,

    output              array2axi_rdata_valid,
    output  [127:0]     array2axi_rdata
);

    reg     [2:0]       cur_state,next_state;
    localparam  [2:0]   ARR_RD_IDLE         = 3'b000;
    localparam  [2:0]   ARR_RD_LOWCS        = 3'b001;
    localparam  [2:0]   ARR_RD_RCD          = 3'b010;
    localparam  [2:0]   ARR_RD_HIGHVLD      = 3'b011;
    localparam  [2:0]   ARR_RD_RADDRLAST    = 3'b100;
    localparam  [2:0]   ARR_RD_RADDR        = 3'b101;
    localparam  [2:0]   ARR_RD_RTP          = 3'b110;
    localparam  [2:0]   ARR_RD_RP           = 3'b111;

    wire                rsof,reof;
    wire        [15:0]  rraddr;
    wire        [5:0]   rcaddr;
    wire        [127:0] wdata;

    reg         [7:0]   rd_ras_cnt;
    reg         [7:0]   rd_rcd_cnt;
    reg         [7:0]   rd_rtp_cnt;
    reg         [7:0]   rd_rp_cnt;
    reg                 single_flag_rd;
    reg                 array_raddr_eof;

    wire                async_fifo_r_wr_clk;
    wire                async_fifo_r_wr_rst_n;
    wire                async_fifo_r_wr_en;
    wire    [127:0]     async_fifo_r_wr_data;
    wire                async_fifo_r_full;
    wire                async_fifo_r_rd_clk;
    wire                async_fifo_r_rd_rst_n;
    wire                async_fifo_r_rd_en;
    wire    [127:0]     async_fifo_r_rd_data;
    wire                async_fifo_r_empty;

    async_fifo #(.FIFO_DEPTH(4),
                .DATA_WIDTH(128)
    )   async_fifo_r (
                .wr_clk             (async_fifo_r_wr_clk),
                .wr_rst_n           (async_fifo_r_wr_rst_n),
                .wr_en              (async_fifo_r_wr_en),
                .wr_data            (async_fifo_r_wr_data),
                .full               (async_fifo_r_full),
                .rd_clk             (async_fifo_r_rd_clk),
                .rd_rst_n           (async_fifo_r_rd_rst_n),
                .rd_en              (async_fifo_r_rd_en),
                .rd_data            (async_fifo_r_rd_data),
                .empty              (async_fifo_r_empty)
    );

    assign  {rsof,reof,rraddr,rcaddr,wdata} = array_rd_frame_data;
    assign array_rd_frame_ready = (cur_state == ARR_RD_IDLE || ((cur_state == ARR_RD_RADDR) &&
                                    !array_caddr_vld_rd));
    assign  array_rd_done   = (cur_state == ARR_RD_RP) && rd_rp_cnt == 'd0;
    assign  array2axi_rdata_valid = !async_fifo_r_empty;
    assign  array2axi_rdata       =  async_fifo_r_rd_data;

    assign  async_fifo_r_wr_clk     =   array_rdata_vld;
    assign  async_fifo_r_wr_rst_n   =   rst_n;
    assign  async_fifo_r_wr_en      =   1'b1;
    assign  async_fifo_r_wr_data    =   array_rdata;
    assign  async_fifo_r_rd_clk     =   clk;
    assign  async_fifo_r_rd_rst_n   =   rst_n;
    assign  async_fifo_r_rd_en      =   !async_fifo_r_empty;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cur_state <= ARR_RD_IDLE;
        end else begin
            cur_state <= next_state;
        end
    end

    always @(*) begin
        case (cur_state)
            ARR_RD_IDLE         : begin
                if (array_rd_frame_valid && rsof) begin
                    next_state = ARR_RD_LOWCS;
                end else begin
                    next_state = ARR_RD_IDLE;
                end
            end
            ARR_RD_LOWCS        : begin
                next_state = ARR_RD_RCD;
            end
            ARR_RD_RCD          : begin
                if (rd_rcd_cnt == 'd0) begin
                    next_state = ARR_RD_HIGHVLD;
                end else begin
                    next_state = ARR_RD_RCD;
                end
            end
            ARR_RD_HIGHVLD      : begin
                if (single_flag_rd) begin
                    next_state = ARR_RD_RADDRLAST;
                end else begin
                    next_state = ARR_RD_RADDR;
                end
            end
            ARR_RD_RADDRLAST    : begin
                next_state = ARR_RD_RTP;
            end
            ARR_RD_RADDR        : begin
                if (array_raddr_eof) begin
                    next_state = ARR_RD_RTP;
                end else begin
                    next_state = ARR_RD_RADDR;
                end
            end
            ARR_RD_RTP          : begin
                if (rd_ras_cnt == 'd0 && rd_rtp_cnt == 'd0) begin
                    next_state = ARR_RD_RP;
                end else begin
                    next_state = ARR_RD_RTP;
                end
            end
            ARR_RD_RP           : begin
                if (rd_rp_cnt == 'd0) begin
                    next_state = ARR_RD_IDLE;
                end else begin
                    next_state = ARR_RD_RP;
                end
            end
            default             : begin
                next_state = ARR_RD_IDLE;
            end
        endcase
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            array_rd_csn <= 1'b1;
        end else if (cur_state == ARR_RD_LOWCS) begin
            array_rd_csn <= 1'b0;
        end else if (cur_state == ARR_RD_RTP && next_state == ARR_RD_RP) begin
            array_rd_csn <= 1'b1;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
           array_rd_raddr <= 'b0; 
        end else if (array_rd_frame_valid && array_rd_frame_ready) begin
            array_rd_raddr <= rraddr;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
           array_caddr_rd <= 'b0; 
        end else if (array_rd_frame_valid && array_rd_frame_ready) begin
            array_caddr_rd <= rcaddr;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            array_caddr_vld_rd <= 'd0;
        end else if (array_caddr_vld_rd == 1'b1) begin
            array_caddr_vld_rd <= 1'b0;
        end else if (cur_state == ARR_RD_HIGHVLD || (cur_state == ARR_RD_RADDR &&
                     array_rd_frame_valid && array_rd_frame_ready)) begin
            array_caddr_vld_rd <= 1'b1;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rd_ras_cnt <= 'd0;
        end else if (cur_state == ARR_RD_LOWCS) begin
            rd_ras_cnt <= array_inner_tras - 1'b1;
        end else if (rd_ras_cnt == 'd0) begin
            rd_ras_cnt <= rd_ras_cnt;
        end else begin
            rd_ras_cnt <= rd_ras_cnt - 1'b1;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rd_rcd_cnt <= 'd0;
        end else if (cur_state == ARR_RD_LOWCS) begin
            rd_rcd_cnt <= array_inner_trcd_rd - 2'b10;
        end else if (cur_state == ARR_RD_RCD) begin
            if (rd_rcd_cnt == 'd0) begin
                rd_rcd_cnt <= rd_rcd_cnt;
            end else begin
                rd_rcd_cnt <= rd_rcd_cnt - 1'b1;
            end  
        end 
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rd_rtp_cnt <= 'd0;
        end else if (cur_state == ARR_RD_LOWCS) begin
            rd_rtp_cnt <= array_inner_trtp - 1'b1;
        end else if (cur_state == ARR_RD_RTP) begin
            if (rd_rtp_cnt == 'd0) begin
                rd_rtp_cnt <= rd_rtp_cnt;   
            end else begin
                rd_rtp_cnt <= rd_rtp_cnt - 1'b1;
            end    
        end 
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rd_rp_cnt <= 'd0;
        end else if (cur_state == ARR_RD_LOWCS) begin
            rd_rp_cnt <= array_inner_trp - 1'b1;
        end else if (cur_state == ARR_RD_RP) begin
            if (rd_rp_cnt == 'd0) begin
                rd_rp_cnt <= rd_rp_cnt;   
            end else begin
                rd_rp_cnt <= rd_rp_cnt - 1'b1;
            end    
        end 
    end
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            single_flag_rd <= 'd0;
        end else if (cur_state == ARR_RD_IDLE && array_rd_frame_valid && rsof) begin
            single_flag_rd <= reof;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            array_raddr_eof <= 'd0;
        end else if (array_rd_frame_valid && array_rd_frame_ready) begin
            array_raddr_eof <= reof;
        end else begin
            array_raddr_eof <= 'd0;
        end
    end


endmodule