module array_wr (
    input               clk,
    input               rst_n,

    input               array_wr_frame_valid,
    input    [151:0]    array_wr_frame_data,
    output              array_wr_frame_ready,
    output              array_wr_done,

    output  reg         array_wr_csn,
    output  reg [15:0]  array_wr_raddr,

    output  reg         array_caddr_vld_wr,
    output  reg [5:0]   array_caddr_wr,
    output              array_wdata_vld,
    output  reg [127:0] array_wdata,

    input      [7:0]    array_inner_tras,
    input      [7:0]    array_inner_trp,
    input      [7:0]    array_inner_trcd_wr,
    input      [7:0]    array_inner_twr
);

    reg [2:0] cur_state,next_state;
    localparam [2:0] ARR_WR_IDLE        = 3'b000;
    localparam [2:0] ARR_WR_LOWCS       = 3'b001;
    localparam [2:0] ARR_WR_RCD         = 3'b010;
    localparam [2:0] ARR_WR_HIGHVLD     = 3'b011;
    localparam [2:0] ARR_WR_WDATALAST   = 3'b100;
    localparam [2:0] ARR_WR_WDATA       = 3'b101;
    localparam [2:0] ARR_WR_WR          = 3'b110;
    localparam [2:0] ARR_WR_RP          = 3'b111;

    wire            wsof,weof;
    wire [15:0]     wraddr;
    wire [5:0]      wcaddr;
    wire [127:0]    wdata;

    reg [7:0]       wr_ras_cnt;
    reg [7:0]       wr_rcd_cnt;
    reg [7:0]       wr_wr_cnt;
    reg [7:0]       wr_rp_cnt;

    reg             single_flag_wr;
    reg             array_wdata_eof;


    assign  {wsof,weof,wraddr,wcaddr,wdata} = array_wr_frame_data;
    assign  array_wdata_vld = !array_caddr_vld_wr;
    assign  array_wr_frame_ready = (cur_state==ARR_WR_IDLE) || ((cur_state == ARR_WR_WDATA) &&
                                        !array_caddr_vld_wr);
    assign  array_wr_done = (cur_state == ARR_WR_RP) && !wr_rp_cnt;

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

    always @(*) begin
        case (cur_state)
            ARR_WR_IDLE     : begin
                if (array_wr_frame_valid && wsof) begin
                    next_state = ARR_WR_LOWCS;
                end else begin
                    next_state = ARR_WR_IDLE;
                end
            end
            ARR_WR_LOWCS    : begin
                next_state  =   ARR_WR_RCD;
            end
            ARR_WR_RCD      : begin
                if (wr_rcd_cnt == 'd0) begin
                    next_state = ARR_WR_HIGHVLD;
                end else begin
                    next_state = ARR_WR_RCD;
                end
            end
            ARR_WR_HIGHVLD  : begin
                if (single_flag_wr) begin
                    next_state = ARR_WR_WDATALAST;
                end else begin
                    next_state = ARR_WR_WDATA;
                end
            end
            ARR_WR_WDATALAST: begin
                next_state = ARR_WR_WR;
            end
            ARR_WR_WDATA    : begin
                if (array_wdata_eof) begin
                    next_state = ARR_WR_WR;
                end else begin
                    next_state = ARR_WR_WDATA;
                end
            end
            ARR_WR_WR       : begin
                if (wr_ras_cnt == 'd0 && wr_wr_cnt == 'd0) begin
                    next_state = ARR_WR_RP;
                end else begin
                    next_state = ARR_WR_WR;
                end
            end
            ARR_WR_RP       : begin
                if (wr_rp_cnt == 'd0) begin
                    next_state = ARR_WR_IDLE;
                end else begin
                    next_state = ARR_WR_RP;
                end
            end
            default: next_state = ARR_WR_IDLE;
        endcase
    end
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            array_wr_csn <= 1'b1;
        end else if (cur_state == ARR_WR_LOWCS) begin
            array_wr_csn <= 1'b0;
        end else if ((cur_state == ARR_WR_WR) && (next_state == ARR_WR_RP)) begin
            array_wr_csn <= 1'b1;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            array_wr_raddr <= 'd0; 
        end else if (array_wr_frame_valid && array_wr_frame_ready) begin
            array_wr_raddr <= wraddr;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            array_caddr_wr <= 'd0; 
        end else if (array_wr_frame_valid && array_wr_frame_ready) begin
            array_caddr_wr <= wcaddr;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            array_wdata <= 'd0; 
        end else if (array_wr_frame_valid && array_wr_frame_ready) begin
            array_wdata <= wdata;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            array_caddr_vld_wr <= 'd0;
        end else if (array_caddr_vld_wr == 1'b1) begin
            array_caddr_vld_wr <= 'd0;
        end else if (cur_state == ARR_WR_HIGHVLD || (cur_state == ARR_WR_WDATA && 
                    array_wr_frame_valid && array_wr_frame_ready)) begin
            array_caddr_vld_wr <= 1'b1;
        end
    end

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

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_rcd_cnt <= 'd0;
        end else if (cur_state == ARR_WR_LOWCS) begin
            wr_rcd_cnt <= array_inner_trcd_wr - 2'b10;
        end else if (cur_state == ARR_WR_RCD) begin
            if (wr_rcd_cnt == 'd0) begin
                wr_rcd_cnt <= wr_rcd_cnt;
            end else begin
                wr_rcd_cnt <= wr_rcd_cnt - 1'b1;
            end
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_wr_cnt <= 'd0;
        end else if (cur_state == ARR_WR_LOWCS) begin
            wr_wr_cnt <= array_inner_twr -1'b1;
        end else if (cur_state == ARR_WR_WR) begin
            if (wr_wr_cnt == 'd0) begin
                wr_wr_cnt <= wr_wr_cnt;
            end else begin
                wr_wr_cnt <= wr_wr_cnt -1'b1;
            end
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_rp_cnt <= 'd0;
        end else if (cur_state == ARR_WR_LOWCS) begin
            wr_rp_cnt <= array_inner_trp - 1'b1;
        end else if (cur_state == ARR_WR_RP) begin
            if (wr_rp_cnt == 'd0) begin
                wr_rp_cnt <= 'd0;
            end else begin
                wr_rp_cnt <= wr_rp_cnt - 1'b1;
            end
        end
    end
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            single_flag_wr <= 1'b0;
        end else if (cur_state == ARR_WR_IDLE && array_wr_frame_valid && wsof) begin
            single_flag_wr <= weof;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            array_wdata_eof <= 'd0;
        end else if (array_wr_frame_valid && array_wr_frame_ready) begin
            array_wdata_eof <= weof;
        end else begin
            array_wdata_eof <= 'd0;
        end
    end

endmodule