`default_nettype wire

module axi_ctrl_v5
#(
    parameter TAG_WD       = 21,
    parameter INDEX_WD     = 64,
    parameter CACHELINE_WD = 512,
    parameter STAGE_WD     = 12
)
(
    input clk,
    input reset,

    // icache interface
    input                          icache_re,           // miss
    input      [31             :0] icache_raddr,         // miss_addr
    output reg [CACHELINE_WD -1:0] icache_cacheline_new,

    input                          icache_we,           // we_back
    input      [31             :0] icache_waddr,         // waddr
    input      [CACHELINE_WD -1:0] icache_cacheline_old, // wback

    output reg                     icache_refresh, 

    // dcache interface
    input                          dcache_re,           // miss
    input      [31             :0] dcache_raddr,         // miss_addr
    output reg [CACHELINE_WD -1:0] dcache_cacheline_new,

    input                          dcache_we,           // we_back
    input      [31             :0] dcache_waddr,         // waddr
    input      [CACHELINE_WD -1:0] dcache_cacheline_old, // wback
    
    output reg                     dcache_refresh,       // fin

    // uncache  interface
    input                          uncache_en,
    input      [3              :0] uncache_we,
    input      [31             :0] uncache_addr,
    input      [31             :0] uncache_wdata,
    output reg [31             :0] uncache_rdata,
    output reg                     uncache_refresh,

    //鎬荤嚎渚ф帴鍙?
    //璇诲湴鍧?閫氶亾淇″彿
    output reg [3 :0] arid,     //璇诲湴鍧?ID锛岀敤鏉ユ爣蹇椾竴缁勫啓淇″彿
    output reg [31:0] araddr,   //璇诲湴鍧?锛岀粰鍑轰竴娆″啓绐佸彂浼犺緭鐨勮鍦板潃
    output reg [3 :0] arlen,    //绐佸彂闀垮害锛岀粰鍑虹獊鍙戜紶杈撶殑娆℃暟
    output reg [2 :0] arsize,   //绐佸彂澶у皬锛岀粰鍑烘瘡娆＄獊鍙戜紶杈撶殑瀛楄妭鏁?
    output reg [1 :0] arburst,  //绐佸彂绫诲瀷
    output reg [1 :0] arlock,   //鎬荤嚎閿佷俊鍙凤紝鍙彁渚涙搷浣滅殑鍘熷瓙鎬?
    output reg [3 :0] arcache,  //鍐呭瓨绫诲瀷锛岃〃鏄庝竴娆′紶杈撴槸鎬庢牱閫氳繃绯荤粺鐨?
    output reg [2 :0] arprot,   //淇濇姢绫诲瀷锛岃〃鏄庝竴娆′紶杈撶殑鐗规潈绾у強瀹夊叏绛夌骇
    output reg 		  arvalid,  //鏈夋晥淇″彿锛岃〃鏄庢閫氶亾鐨勫湴鍧?鎺у埗淇″彿鏈夋晥
    input  		      arready,  //琛ㄦ槑"浠?"鍙互鎺ユ敹鍦板潃鍜屽搴旂殑鎺у埗淇″彿
    //璇绘暟鎹?氶亾淇″彿 
    input      [3 :0] rid,      //璇籌D tag
    input      [31:0] rdata,    //璇绘暟鎹?
    input      [1 :0] rresp,    //璇诲搷搴旓紝琛ㄦ槑璇讳紶杈撶殑鐘舵??
    input  		      rlast,    //琛ㄦ槑璇荤獊鍙戠殑鏈?鍚庝竴娆′紶杈?
    input  		      rvalid,   //琛ㄦ槑姝ら?氶亾淇″彿鏈夋晥
    output reg		  rready,   //琛ㄦ槑涓绘満鑳藉鎺ユ敹璇绘暟鎹拰鍝嶅簲淇℃伅
 
    //鍐欏湴鍧?閫氶亾淇″彿 
    output reg [3 :0] awid,     //鍐欏湴鍧?ID锛岀敤鏉ユ爣蹇椾竴缁勫啓淇″彿
    output reg [31:0] awaddr,   //鍐欏湴鍧?锛岀粰鍑轰竴娆″啓绐佸彂浼犺緭鐨勫啓鍦板潃
    output reg [3 :0] awlen,    //绐佸彂闀垮害锛岀粰鍑虹獊鍙戜紶杈撶殑娆℃暟
    output reg [2 :0] awsize,   //绐佸彂澶у皬锛岀粰鍑烘瘡娆＄獊鍙戜紶杈撶殑瀛楄妭鏁?
    output reg [1 :0] awburst,  //绐佸彂绫诲瀷
    output reg [1 :0] awlock,   //鎬荤嚎閿佷俊鍙凤紝鍙彁渚涙搷浣滅殑鍘熷瓙鎬?
    output reg [3 :0] awcache,  //鍐呭瓨绫诲瀷锛岃〃鏄庝竴娆′紶杈撴槸鎬庢牱閫氳繃绯荤粺鐨?
    output reg [2 :0] awprot,   //淇濇姢绫诲瀷锛岃〃鏄庝竴娆′紶杈撶殑鐗规潈绾у強瀹夊叏绛夌骇
    output reg 		  awvalid,  //鏈夋晥淇″彿锛岃〃鏄庢閫氶亾鐨勫湴鍧?鎺у埗淇″彿鏈夋晥
    input  		      awready,  //琛ㄦ槑"浠?"鍙互鎺ユ敹鍦板潃鍜屽搴旂殑鎺у埗淇″彿
    //鍐欐暟鎹?氶亾淇″彿 
    output reg [3 :0] wid,      //涓?娆″啓浼犺緭鐨処D tag
    output reg [31:0] wdata,    //鍐欐暟鎹?
    output reg [3 :0] wstrb,    //鍐欐暟鎹湁鏁堢殑瀛楄妭绾匡紝鐢ㄦ潵琛ㄦ槑鍝?8bits鏁版嵁鏄湁鏁堢殑
    output reg 		  wlast,    //琛ㄦ槑姝ゆ浼犺緭鏄渶鍚庝竴涓獊鍙戜紶杈?
    output reg		  wvalid,   //鍐欐湁鏁堬紝琛ㄦ槑姝ゆ鍐欐湁鏁?
    input  		      wready,   //琛ㄦ槑浠庢満鍙互鎺ユ敹鍐欐暟鎹?
    //鍐欏搷搴旈?氶亾淇″彿
    input      [3 :0] bid,      //鍐欏搷搴擨D tag
    input      [1 :0] bresp,    //鍐欏搷搴旓紝琛ㄦ槑鍐欎紶杈撶殑鐘舵?? 00涓烘甯革紝褰撶劧鍙互涓嶇悊浼?
    input  		      bvalid,   //鍐欏搷搴旀湁鏁?
    output reg        bready    //琛ㄦ槑涓绘満鑳藉鎺ユ敹鍐欏搷搴?

);
    reg [CACHELINE_WD -1:0] icache_rdata_buffer;
    reg [CACHELINE_WD -1:0] icache_wdata_buffer;
    reg [CACHELINE_WD -1:0] dcache_rdata_buffer;
    reg [CACHELINE_WD -1:0] dcache_wdata_buffer;
    reg [31             :0] icache_raddr_buffer;
    reg [31             :0] icache_waddr_buffer;
    reg [31             :0] dcache_raddr_buffer;
    reg [31             :0] dcache_waddr_buffer;
    reg [3              :0] icache_offset;
    reg [3              :0] dcache_offset;
    reg [3              :0] dcache_offset_w;
    reg                     icache_re_buffer;
    reg                     dcache_re_buffer;
    reg                     icache_we_buffer;
    reg                     dcache_we_buffer;

    reg                     uncache_en_buffer;
    reg [3              :0] uncache_we_buffer;
    reg [31             :0] uncache_addr_buffer;
    reg [31             :0] uncache_wdata_buffer; 
    reg [31             :0] uncache_rdata_buffer;

    reg [STAGE_WD     -1:0] stage;
    reg [STAGE_WD     -1:0] stage_w;

    always @(posedge clk) begin
        if(reset) begin
            arid <= 4'b0000;
            araddr <= 32'b0;
            arlen <= 4'b0000;
            arsize <= 3'b010;
            arburst <= 2'b01;
            arlock <= 2'b00;
            arcache <= 4'b0000;
            arprot <= 3'b000;
            arvalid <= 1'b0;

            rready <= 1'b0;
            
            stage                <= {{(STAGE_WD-1){1'b0}}, 1'b1};

            icache_refresh       <= 0;
            dcache_refresh       <= 0;
            icache_cacheline_new <= 0;
            dcache_cacheline_new <= 0;

            uncache_refresh <= 1'b0;
            uncache_rdata   <= 32'b0;
        end
        else begin
            case (1'b1)
                stage[0]: begin
                    icache_refresh  <= 1'b0;
                    dcache_refresh  <= 1'b0;
                    uncache_refresh <= 1'b0;

                    icache_re_buffer   <= icache_re;
                    icache_raddr_buffer <= icache_raddr;
                    icache_we_buffer    <= icache_we;
                    icache_waddr_buffer <= icache_waddr;
                    
                    dcache_re_buffer   <= dcache_re;
                    dcache_raddr_buffer <= dcache_raddr;
                    dcache_we_buffer    <= dcache_we;
                    dcache_waddr_buffer <= dcache_waddr;

                    uncache_en_buffer    <= uncache_en;
                    uncache_we_buffer    <= uncache_we;
                    uncache_addr_buffer  <= uncache_addr;
                    uncache_wdata_buffer <= uncache_wdata;

                    if (dcache_we|(uncache_en&((|uncache_we)))) begin
                        stage <= stage << 1;    
                    end
                    else if (icache_re|dcache_re|(uncache_en&~(|uncache_we))) begin
                        stage <= stage << 2;
                    end
                end
                stage[1]: begin
                    icache_wdata_buffer <= icache_cacheline_old;
                    dcache_wdata_buffer <= dcache_cacheline_old;
                    if (icache_re_buffer|dcache_re_buffer|(uncache_en_buffer&~(|uncache_we_buffer))) begin
                        stage <= stage << 1;
                    end
                    else begin
                        stage <= {1'b0,1'b1,10'b0};
                    end
                end
                stage[2]:begin
                    if (icache_re_buffer) begin
                        arid    <= 4'b0;
                        araddr  <= icache_raddr_buffer;
                        arlen   <= 4'hf;
                        arsize  <= 3'b010;
                        arvalid <= 1'b1;

                        stage   <= stage << 1;
                    end
                    else begin
                        stage   <= stage << 3;
                    end
                end
                stage[3]:begin
                    if (arready) begin
                        arvalid       <= 1'b0;
                        araddr        <= 32'b0;
                        rready        <= 1'b1;
                        icache_offset <= 4'd0;
                        stage         <= stage << 1;
                    end
                end
                stage[4]:begin
                    if (!rlast&rvalid) begin
                        icache_rdata_buffer[icache_offset*32+:32] <= rdata;
                        icache_offset <= icache_offset + 1'b1;
                    end
                    else if(rlast&rvalid) begin
                        icache_rdata_buffer[icache_offset*32+:32] <= rdata;
                        rready <= 1'b0;
                        stage  <= stage << 1;
                    end
                end
                stage[5]:begin
                    if (dcache_re_buffer) begin
                        arid    <= 4'b1;
                        araddr  <= dcache_raddr_buffer;
                        arlen   <= 4'hf;
                        arsize  <= 3'b010;
                        arvalid <= 1'b1;

                        stage <= stage << 1;
                    end
                    else if (uncache_en_buffer&~(|uncache_we_buffer)) begin
                        arid <= 4'b1;
                        araddr <= uncache_addr_buffer;
                        arlen <= 4'b0;
                        arsize <= 3'b010;
                        arvalid <= 1'b1;
                        stage <= stage << 3;
                    end
                    else begin
                        stage <= {1'b0,1'b1,10'b0};
                    end
                end
                stage[6]:begin
                    if (arready) begin
                        arvalid       <= 1'b0;
                        araddr        <= 32'b0;
                        rready        <= 1'b1;
                        dcache_offset <= 4'd0;
                        stage         <= stage << 1;
                    end
                end
                stage[7]:begin
                    if (!rlast&rvalid) begin
                        dcache_rdata_buffer[dcache_offset*32+:32] <= rdata;
                        dcache_offset <= dcache_offset + 1'b1;
                    end
                    else if (rlast&rvalid) begin
                        dcache_rdata_buffer[dcache_offset*32+:32] <= rdata;
                        rready <= 1'b0;
                        stage  <= {1'b0,1'b1,10'b0};
                    end
                end
                stage[8]:begin
                    if (arready) begin
                        arvalid <= 1'b0;
                        araddr  <= 32'b0;
                        rready  <= 1'b1;
                        stage   <= stage << 1;
                    end
                end
                stage[9]:begin
                    if (rvalid) begin
                        uncache_rdata_buffer <= rdata;
                        rready <= 1'b0;
                        stage  <= {1'b0,1'b1,10'b0};
                    end
                end
                stage[10]:begin
                    if (stage_w[10]|stage_w[0]) begin
                        stage <= stage << 1;
                    end
                end
                stage[11]:begin
                    if (icache_re_buffer) begin
                        icache_refresh <= 1'b1;
                        icache_cacheline_new <= icache_rdata_buffer;
                    end
                    if (dcache_re_buffer) begin
                        dcache_refresh <= 1'b1;
                        dcache_cacheline_new <= dcache_rdata_buffer;
                    end
                    if (uncache_en_buffer) begin
                        uncache_refresh <= 1'b1;   
                        uncache_rdata <= uncache_rdata_buffer;
                    end
                    stage <= 0;
                end
                default:begin
                    stage          <= {{(STAGE_WD-1){1'b0}}, 1'b1};
                    icache_refresh <= 1'b0;
                    dcache_refresh <= 1'b0;
                    uncache_refresh <= 1'b0;
                end
            endcase
        end
    end

    always @ (posedge clk) begin
        if (reset) begin
            awid    <= 4'b0001;
            awaddr  <= 32'b0;
            awlen   <= 4'b0000;
            awsize  <= 3'b010;
            awburst <= 2'b01;
            awlock  <= 2'b00;
            awcache <= 4'b0000;
            awprot  <= 3'b000;
            awvalid <= 1'b0;

            wid    <= 4'b0001;
            wdata  <= 32'b0;
            wstrb  <= 4'b0000;
            wlast  <= 1'b0;
            wvalid <= 1'b0;

            bready <= 1'b0;
            
            stage_w <= {{(STAGE_WD-1){1'b0}}, 1'b1};
        end
        else begin
            case (1'b1)
                stage_w[0]:begin
                    if (stage[1]) begin
                        if (dcache_we_buffer) begin
                            awid <= 4'b1;
                            awaddr <= dcache_waddr_buffer;
                            awlen <= 4'hf;
                            awsize <= 3'b010;
                            awvalid <= 1'b1;
                            wstrb <= 4'b1111;
                            wlast <= 1'b0;
                            bready <= 1'b1;
                            dcache_offset_w <= 4'b0;
                            stage_w <= stage_w << 1;
                        end
                        else if (|uncache_we_buffer) begin // write
                            awid <= 4'b1;
                            awaddr <= uncache_addr_buffer;
                            awlen <= 4'b0;
                            case (uncache_we_buffer)
                                4'b0001,4'b0010,4'b0100,4'b1000:begin
                                    awsize <= 3'b000; 
                                    wstrb <= uncache_we_buffer;       
                                end
                                4'b0011,4'b1100:begin
                                    awsize <= 3'b001;
                                    wstrb <= uncache_we_buffer;
                                end
                                4'b1111:begin
                                    awsize <= 3'b010;
                                    wstrb <= uncache_we_buffer;
                                end
                                default:begin
                                    awsize <= 3'b010;
                                    wstrb <= uncache_we_buffer;
                                end
                            endcase
                            awvalid <= 1'b1;
                            wlast <= 1'b0;
                            bready <= 1'b1;
                            stage_w <= stage_w << 4;
                        end
                    end
                end
                stage_w[1]:begin
                    if (awready) begin
                        awvalid <= 1'b0;
                        awaddr <= 32'b0;
                        wdata <= dcache_wdata_buffer[dcache_offset_w*32+:32];
                        wvalid <= 1'b1;
                        wlast <= dcache_offset_w == 4'b1111 ? 1'b1 : 1'b0;
                        dcache_offset_w <= dcache_offset_w + 1'b1;
                        if (dcache_offset_w == 4'b1111) begin
                            stage_w <= stage_w << 1;
                        end
                    end
                    else if (wready) begin
                        wdata <= dcache_wdata_buffer[dcache_offset_w*32+:32];
                        wvalid <= 1'b1;
                        wlast <= dcache_offset_w == 4'b1111 ? 1'b1 : 1'b0;
                        dcache_offset_w <= dcache_offset_w + 1'b1;
                        if (dcache_offset_w == 4'b1111) begin
                            stage_w <= stage_w << 1;
                        end
                    end
                end
                stage_w[2]:begin
                    if (wready) begin
                        wdata <= 32'b0;
                        wvalid <= 1'b0;    
                        wlast <= 1'b0;
                        stage_w <= stage_w << 1;
                    end
                end
                stage_w[3]:begin
                    if (bvalid) begin
                        bready <= 1'b0;
                        stage_w <= {1'b0,1'b1,{10{1'b0}}};
                    end
                end
                stage_w[4]:begin
                    if (awready) begin
                        awvalid <= 1'b0;
                        awaddr <= 32'b0;
                        wdata <= uncache_wdata_buffer;
                        wvalid <= 1'b1;
                        wlast <= 1'b1;
                        stage_w <= stage_w << 1;
                    end
                end
                stage_w[5]:begin
                    if (wready) begin
                        wdata <= 32'b0;
                        wvalid <= 1'b0;
                        wlast <= 1'b0;
                        stage_w <= stage_w << 1;
                    end
                end
                stage_w[6]:begin
                    if (bvalid) begin
                        bready <= 1'b0;
                        stage_w <= {1'b0,1'b1,{10{1'b0}}};
                    end
                end
                stage_w[10]:begin
                    if (stage[11]) begin
                        stage_w <= {{(STAGE_WD-1){1'b0}}, 1'b1};    
                    end
                end
                default:begin
                    stage_w <= {{(STAGE_WD-1){1'b0}}, 1'b1};
                end
            endcase
        end
    end

endmodule