module wchannel(
    input clk,
    input rst_n,

    input           axi_s_awvalid,
    input [7:0]     axi_s_awlen,
    input [25:0]    axi_s_awaddr,
    output          axi_s_awready,

    input           axi_s_wvalid,
    input           axi_s_wlast,
    input [63:0]    axi_s_wdata,
    output          axi_s_wready,

    output          wframe_valid,
    output [159:0]  wframe_data,
    input           wframe_ready
);
    wire            wsof,weof;
    reg [15:0]      wraddr;
    reg [5:0]       wcaddr;
    wire [127:0]    wdata;

    wire [7:0]      awlen;
    wire [15:0]     awraddr;
    wire [5:0]      awcaddr;

    reg [6:0]       wframe_cnt;

    reg [1:0]       cur_state,next_state;
    localparam [1:0] WCH_IDLE     = 2'b01;
    localparam [1:0] WCH_SD_WADDR = 2'b10;
    localparam [1:0] WCH_SD_WDATA = 2'b11;
    
    wire            sync_fifo_aw_wr_en;
    wire [29:0]     sync_fifo_aw_wr_data;
    wire            sync_fifo_aw_rd_en;
    wire [29:0]     sync_fifo_aw_rd_data;
    wire            sync_fifo_aw_full;
    wire            sync_fifo_aw_empty;

    wire            sync_fifo_w_wr_en;
    wire [63:0]     sync_fifo_w_wr_data;
    wire            sync_fifo_w_rd_en;
    wire [127:0]    sync_fifo_w_rd_data;
    wire            sync_fifo_w_full;
    wire            sync_fifo_w_empty;

    assign  sync_fifo_aw_wr_en = axi_s_awvalid && axi_s_awready;
    assign  sync_fifo_aw_wr_data = {axi_s_awaddr[25:4],axi_s_awlen};
    assign  sync_fifo_aw_rd_en = wframe_ready && weof && !sync_fifo_aw_empty;
    assign  {awraddr,awcaddr,awlen} = sync_fifo_aw_rd_data;
    assign  axi_s_awready = !sync_fifo_aw_full;

        

    assign  sync_fifo_w_wr_en = axi_s_wvalid && axi_s_wready;
    assign  sync_fifo_w_wr_data = axi_s_wdata;
    assign  sync_fifo_w_rd_en = wframe_valid && wframe_ready;
    assign  wdata = sync_fifo_w_rd_data;
    assign  axi_s_wready = !sync_fifo_w_full;

    assign wsof = wframe_valid && (wframe_cnt == 7'd0);
    assign weof = wframe_valid && ((wframe_cnt == awlen >> 1'b1));

    assign wframe_data = {wsof,weof,wraddr,wcaddr,wdata,awlen};
    assign wframe_valid = !sync_fifo_w_empty && (cur_state == WCH_SD_WDATA);

    sync_fifo #(.DATA_WIDTH(30),
                .FIFO_DEPTH(4)
    ) sync_fifo_aw (.clk        (clk),
                    .rst_n      (rst_n),
                    .wr_en      (sync_fifo_aw_wr_en),
                    .wr_data    (sync_fifo_aw_wr_data),
                    .rd_en      (sync_fifo_aw_rd_en),
                    .rd_data    (sync_fifo_aw_rd_data),
                    .full       (sync_fifo_aw_full),
                    .empty      (sync_fifo_aw_empty)
    );

    sync_fifo_64_to_128 #(
                .DATA_IN_WIDTH(64),
                .DATA_OUT_WIDTH(128),
                .FIFO_DEPTH(8)
    ) sync_fifo_w (.clk         (clk),
                   .rst_n       (rst_n),
                   .wr_en       (sync_fifo_w_wr_en),
                   .wr_data     (sync_fifo_w_wr_data),
                   .rd_en       (sync_fifo_w_rd_en),
                   .rd_data     (sync_fifo_w_rd_data),
                   .full        (sync_fifo_w_full),
                   .empty       (sync_fifo_w_empty)
    );
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            wframe_cnt <= 'd0;
        end else if (sync_fifo_w_rd_en) begin
            if (wframe_cnt == (awlen >> 1'b1)) begin
                wframe_cnt <= 'd0;
            end else begin
                wframe_cnt <= wframe_cnt + 1'b1;
            end
        end
    end

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

    always@(*) begin
        case(cur_state)
            WCH_IDLE: begin
                if(sync_fifo_aw_wr_en) begin
                    next_state <= WCH_SD_WADDR;
                end else begin
                    next_state <= WCH_IDLE;
                end
            end

            WCH_SD_WADDR: begin
                    next_state <= WCH_SD_WDATA;
            end

            WCH_SD_WDATA: begin
                    if(sync_fifo_w_rd_en && (wframe_cnt == (awlen >> 1))) begin
                        next_state <= WCH_IDLE;
                    end else begin
                        next_state <= WCH_SD_WDATA;
                    end
            end

            default : begin
                    next_state <= WCH_IDLE;
            end 
        endcase
    end

    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            wcaddr <= 'd0;
        end else if (cur_state == WCH_SD_WADDR) begin
            wcaddr <= awcaddr;
        end else if (sync_fifo_w_rd_en) begin
            if (wcaddr  == 6'h3f) begin
                wcaddr <= 'd0;
            end else begin
                wcaddr <= wcaddr + 1'b1;
            end
        end
    end

    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            wraddr <= 'd0;
        end else if (cur_state == WCH_SD_WADDR) begin
        if (sync_fifo_w_rd_en && (wcaddr == 6'h3f)) begin
            wraddr <= wraddr + 1'b1;
        end else begin 
            wraddr <= wraddr;
        end
    end
    end
endmodule
