module L2cache #
(
	parameter DW = 256,
	parameter BK = 4,
	parameter CB = 4,
	parameter CL = 32
)
(

	//L1 I Cache
	input [31:0] IL1_ARADDR,
	input [7:0] IL1_ARLEN,
	input [1:0] IL1_ARBURST,
	input IL1_ARVALID,
	output IL1_ARREADY,

	output [63:0] IL1_RDATA,
	output [1:0] IL1_RRESP,
	output IL1_RLAST,
	output IL1_RVALID,
	input IL1_RREADY,

	//L1 D cache
	input [31:0] DL1_AWADDR,
	input [7:0] DL1_AWLEN,
	input [1:0] DL1_AWBURST,
	input DL1_AWVALID,
	output DL1_AWREADY,

	input [63:0] DL1_WDATA,
	input [7:0] DL1_WSTRB,
	input DL1_WLAST,
	input DL1_WVALID,
	output DL1_WREADY,

	output [1:0] DL1_BRESP,
	output DL1_BVALID,
	input DL1_BREADY,

	input [31:0] DL1_ARADDR,
	input [7:0] DL1_ARLEN,
	input [1:0] DL1_ARBURST,
	input DL1_ARVALID,
	output DL1_ARREADY,

	output [63:0] DL1_RDATA,
	output [1:0] DL1_RRESP,
	output DL1_RLAST,
	output DL1_RVALID,
	input DL1_RREADY,


	//L3Cache
	output [31:0] MEM_AWADDR,
	output [7:0] MEM_AWLEN,
	output [1:0] MEM_AWBURST,
	output MEM_AWVALID,
	input MEM_AWREADY,
	output [63:0] MEM_WDATA,
	output [7:0] MEM_WSTRB,
	output MEM_WLAST,
	output MEM_WVALID,
	input MEM_WREADY,

	input [1:0] MEM_BRESP,
	input MEM_BVALID,
	output MEM_BREADY,

	output [31:0] MEM_ARADDR,
	output [7:0] MEM_ARLEN,
	output [1:0] MEM_ARBURST,
	output MEM_ARVALID,
	input MEM_ARREADY,

	input [63:0] MEM_RDATA,
	input [1:0] MEM_RRESP,
	input MEM_RLAST,
	input MEM_RVALID,
	output MEM_RREADY,

	input l2c_fence,
	output l2c_fence_end,
	input CLK,
	input RSTn
);

	localparam L2C_STATE_CFREE = 0;
	localparam L2C_STATE_CKTAG = 1;
	localparam L2C_STATE_FLASH = 2;
	localparam L2C_STATE_RSPIR = 3;
	localparam L2C_STATE_RSPDR = 4;
	localparam L2C_STATE_RSPDW = 5;
	localparam L2C_STATE_FENCE = 6;

	localparam ADDR_LSB = $clog2(DW*BK/8);
	localparam LINE_W = $clog2(CL); 
	localparam TAG_W = 32 - ADDR_LSB - LINE_W;

	wire il1_wready_set, il1_wready_rst, il1_wready_qout;
	wire il1_bvalid_set, il1_bvalid_rst, il1_bvalid_qout;
	wire il1_arready_set, il1_arready_rst, il1_arready_qout;
	wire [31:0] il1_araddr_dnxta;
	wire [31:0] il1_araddr_dnxtb;
	wire [31:0] il1_araddr_qout;
	wire il1_araddr_ena, il1_araddr_enb, il1_arburst_en;
	wire [1:0] il1_arburst_dnxt;
	wire [1:0] il1_arburst_qout;
	wire il1_arlen_en;
	wire [7:0] il1_arlen_dnxt;
	wire [7:0] il1_arlen_qout;
	wire [7:0] il1_arlen_cnt_dnxta;
	wire [7:0] il1_arlen_cnt_dnxtb;
	wire [7:0] il1_arlen_cnt_qout;
	wire il1_arlen_cnt_ena, il1_arlen_cnt_enb;
	wire il1_rvalid_set, il1_rvalid_rst, il1_rvalid_qout;
	wire il1_rlast_set, il1_rlast_rst, il1_rlast_qout;
	wire il1_ar_rsp;
	wire il1_end_r;

	wire dl1_arready_set, dl1_arready_rst, dl1_arready_qout;
	wire [31:0] dl1_araddr_dnxta;
	wire [31:0] dl1_araddr_dnxtb;
	wire [31:0] dl1_araddr_qout;
	wire dl1_araddr_ena, dl1_araddr_enb, dl1_arburst_en;
	wire [1:0] dl1_arburst_dnxt;
	wire [1:0] dl1_arburst_qout;
	wire dl1_arlen_en;
	wire [7:0] dl1_arlen_dnxt;
	wire [7:0] dl1_arlen_qout;
	wire [7:0] dl1_arlen_cnt_dnxta;
	wire [7:0] dl1_arlen_cnt_dnxtb;
	wire [7:0] dl1_arlen_cnt_qout;
	wire dl1_arlen_cnt_ena, dl1_arlen_cnt_enb;
	wire dl1_rvalid_set, dl1_rvalid_rst, dl1_rvalid_qout;
	wire dl1_rlast_set, dl1_rlast_rst, dl1_rlast_qout;
	wire dl1_ar_rsp;
	wire dl1_end_r;
	wire dl1_end_w;

	wire mem_arvalid_set, mem_arvalid_rst, mem_arvalid_qout;
	wire mem_rready_set, mem_rready_rst, mem_rready_qout;
	wire read_resp_error;
	wire mem_ar_req;
	wire mem_end_r;

	wire [2:0] l2c_state_dnxt;
	wire [2:0] l2c_state_qout;

	// wire cache_fence_set;
	// wire cache_fence_rst;
	// wire cache_fence_qout;

	wire [31:0] cache_addr;

	wire [CB-1:0] cache_en_w;
	wire [CB-1:0] cache_en_r;
	wire [7:0] cache_info_wstrb;
	wire [63:0] cache_info_w;
	wire [64*CB-1:0] cache_info_r;

	wire [31:0] tag_addr;
	wire [CB-1:0] tag_en_w;
	wire [CB-1:0] tag_en_r;
	wire [(TAG_W+7)/8-1:0] tag_info_wstrb;
	wire [TAG_W-1:0] tag_info_w;
	wire [TAG_W*CB-1:0] tag_info_r;

	wire [31:0] cache_addr_dnxt;
	wire [31:0] cache_addr_qout;


	wire [31:0] tag_addr_lock_dnxt;
	wire [31:0] tag_addr_sel;
	wire [31:0] tag_addr_lock_qout;

	wire [CB-1:0] cb_vhit;
	wire [CL-1:0] valid_cl_sel;
	wire [63:0] cache_data_r;
	wire [64*CB-1:0] cache_info_r_T;


	wire [CL*CB-1:0] cache_valid_set;
	wire [CL*CB-1:0] cache_valid_rst;
	wire [CL*CB-1:0] cache_valid_qout;

	wire isCacheBlockRunout;
	wire [$clog2(CB)-1:0] cache_block_sel;
	wire [15:0] random;
	wire [CB-1:0] blockReplace;

	wire [CB-1:0] cache_cl_valid;








	assign DL1_AWREADY = (l2c_state_qout == L2C_STATE_RSPDW) & MEM_AWREADY;
	assign DL1_WREADY = (l2c_state_qout == L2C_STATE_RSPDW) & MEM_WREADY;
	assign DL1_BRESP = MEM_BRESP;
	assign DL1_BVALID = (l2c_state_qout == L2C_STATE_RSPDW) & MEM_BVALID;
	assign MEM_AWADDR = DL1_AWADDR;
	assign MEM_AWLEN = DL1_AWLEN;
	assign MEM_AWBURST = DL1_AWBURST;
	assign MEM_AWVALID = (l2c_state_qout == L2C_STATE_RSPDW) & DL1_AWVALID;
	assign MEM_WDATA = DL1_WDATA;
	assign MEM_WSTRB = DL1_WSTRB;
	assign MEM_WLAST = (l2c_state_qout == L2C_STATE_RSPDW) & DL1_WLAST;
	assign MEM_WVALID = (l2c_state_qout == L2C_STATE_RSPDW) & DL1_WVALID;
	assign MEM_BREADY = (l2c_state_qout == L2C_STATE_RSPDW) & DL1_BREADY;







// IIIIIIIIIILLLLLLLLLLL               1111111                           PPPPPPPPPPPPPPPPP        OOOOOOOOO     RRRRRRRRRRRRRRRRR   TTTTTTTTTTTTTTTTTTTTTTT
// I::::::::IL:::::::::L              1::::::1                           P::::::::::::::::P     OO:::::::::OO   R::::::::::::::::R  T:::::::::::::::::::::T
// I::::::::IL:::::::::L             1:::::::1                           P::::::PPPPPP:::::P  OO:::::::::::::OO R::::::RRRRRR:::::R T:::::::::::::::::::::T
// II::::::IILL:::::::LL             111:::::1                           PP:::::P     P:::::PO:::::::OOO:::::::ORR:::::R     R:::::RT:::::TT:::::::TT:::::T
//   I::::I    L:::::L                  1::::1                             P::::P     P:::::PO::::::O   O::::::O  R::::R     R:::::RTTTTTT  T:::::T  TTTTTT
//   I::::I    L:::::L                  1::::1                             P::::P     P:::::PO:::::O     O:::::O  R::::R     R:::::R        T:::::T        
//   I::::I    L:::::L                  1::::1                             P::::PPPPPP:::::P O:::::O     O:::::O  R::::RRRRRR:::::R         T:::::T        
//   I::::I    L:::::L                  1::::l                             P:::::::::::::PP  O:::::O     O:::::O  R:::::::::::::RR          T:::::T        
//   I::::I    L:::::L                  1::::l                             P::::PPPPPPPPP    O:::::O     O:::::O  R::::RRRRRR:::::R         T:::::T        
//   I::::I    L:::::L                  1::::l                             P::::P            O:::::O     O:::::O  R::::R     R:::::R        T:::::T        
//   I::::I    L:::::L                  1::::l                             P::::P            O:::::O     O:::::O  R::::R     R:::::R        T:::::T        
//   I::::I    L:::::L         LLLLLL   1::::l                             P::::P            O::::::O   O::::::O  R::::R     R:::::R        T:::::T        
// II::::::IILL:::::::LLLLLLLLL:::::L111::::::111                        PP::::::PP          O:::::::OOO:::::::ORR:::::R     R:::::R      TT:::::::TT      
// I::::::::IL::::::::::::::::::::::L1::::::::::1                        P::::::::P           OO:::::::::::::OO R::::::R     R:::::R      T:::::::::T      
// I::::::::IL::::::::::::::::::::::L1::::::::::1                        P::::::::P             OO:::::::::OO   R::::::R     R:::::R      T:::::::::T      
// IIIIIIIIIILLLLLLLLLLLLLLLLLLLLLLLL111111111111                        PPPPPPPPPP               OOOOOOOOO     RRRRRRRR     RRRRRRR      TTTTTTTTTTT      
//                                               ________________________                                                                                  
//                                               _::::::::::::::::::::::_                                                                                  
//                                               ________________________                                                                                  
                                                                              





	assign IL1_ARREADY = il1_arready_qout;
	assign IL1_RRESP = 2'b00;
	assign IL1_RLAST = il1_rlast_qout;
	assign IL1_RVALID	= il1_rvalid_qout;
	assign il1_end_r = IL1_RVALID & IL1_RREADY & IL1_RLAST;


	
	assign il1_arready_set = il1_ar_rsp;
	assign il1_arready_rst = ~il1_ar_rsp & ~(il1_rvalid_qout & IL1_RREADY & il1_arlen_cnt_qout == il1_arlen_qout);
	gen_rsffr # (.DW(1)) il1_arready_rsffr (.set_in(il1_arready_set), .rst_in(il1_arready_rst), .qout(il1_arready_qout), .CLK(CLK), .RSTn(RSTn));

	assign il1_araddr_dnxta = IL1_ARADDR;
	assign il1_araddr_dnxtb = ({32{il1_arburst_qout == 2'b01}} & il1_araddr_qout + (1<<3));

	assign il1_araddr_ena = il1_ar_rsp;
	assign il1_araddr_enb = ((il1_arlen_cnt_qout <= il1_arlen_qout) & il1_rvalid_qout & IL1_RREADY);
	gen_dpdffren # (.DW(32)) il1_araddr_dpdffren( .dnxta(il1_araddr_dnxta), .ena(il1_araddr_ena), .dnxtb(il1_araddr_dnxtb), .enb(il1_araddr_enb), .qout(il1_araddr_qout), .CLK(CLK), .RSTn(RSTn) );

	assign il1_arburst_en = il1_ar_rsp;
	assign il1_arburst_dnxt = IL1_ARBURST;
	gen_dffren # (.DW(2)) il1_arburst_dffren (.dnxt(il1_arburst_dnxt), .qout(il1_arburst_qout), .en(il1_arburst_en), .CLK(CLK), .RSTn(RSTn));

	assign il1_arlen_en = il1_ar_rsp;
	assign il1_arlen_dnxt = IL1_ARLEN;
	gen_dffren # (.DW(8)) il1_arlen_dffren (.dnxt(il1_arlen_dnxt), .qout(il1_arlen_qout), .en(il1_arlen_en), .CLK(CLK), .RSTn(RSTn));

	assign il1_rlast_set = ((il1_arlen_cnt_qout == il1_arlen_qout) & ~il1_rlast_qout & (l2c_state_qout == L2C_STATE_RSPIR) )  ;
	assign il1_rlast_rst = il1_ar_rsp | (((il1_arlen_cnt_qout <= il1_arlen_qout) | il1_rlast_qout | ~(l2c_state_qout != L2C_STATE_RSPIR) ) & ( il1_rvalid_qout & IL1_RREADY));
	gen_rsffr # (.DW(1)) il1_rlast_rsffr (.set_in(il1_rlast_set), .rst_in(il1_rlast_rst), .qout(il1_rlast_qout), .CLK(CLK), .RSTn(RSTn));

	assign il1_arlen_cnt_dnxta = 8'd0;
	assign il1_arlen_cnt_dnxtb = il1_arlen_cnt_qout + 8'd1;
	assign il1_arlen_cnt_ena = il1_ar_rsp;
	assign il1_arlen_cnt_enb = ((il1_arlen_cnt_qout <= il1_arlen_qout) & il1_rvalid_qout & IL1_RREADY);
	gen_dpdffren # (.DW(8)) il1_arlen_cnt_dpdffren( .dnxta(il1_arlen_cnt_dnxta), .ena(il1_arlen_cnt_ena), .dnxtb(il1_arlen_cnt_dnxtb), .enb(il1_arlen_cnt_enb), .qout(il1_arlen_cnt_qout), .CLK(CLK), .RSTn(RSTn) );

	assign il1_rvalid_set = ~il1_rvalid_qout & (l2c_state_qout == L2C_STATE_RSPIR);
	assign il1_rvalid_rst =  il1_rvalid_qout & IL1_RREADY;
	gen_rsffr # (.DW(1)) il1_rvalid_rsffr (.set_in(il1_rvalid_set), .rst_in(il1_rvalid_rst), .qout(il1_rvalid_qout), .CLK(CLK), .RSTn(RSTn));







// DDDDDDDDDDDDD      LLLLLLLLLLL               1111111                           PPPPPPPPPPPPPPPPP        OOOOOOOOO     RRRRRRRRRRRRRRRRR   TTTTTTTTTTTTTTTTTTTTTTT
// D::::::::::::DDD   L:::::::::L              1::::::1                           P::::::::::::::::P     OO:::::::::OO   R::::::::::::::::R  T:::::::::::::::::::::T
// D:::::::::::::::DD L:::::::::L             1:::::::1                           P::::::PPPPPP:::::P  OO:::::::::::::OO R::::::RRRRRR:::::R T:::::::::::::::::::::T
// DDD:::::DDDDD:::::DLL:::::::LL             111:::::1                           PP:::::P     P:::::PO:::::::OOO:::::::ORR:::::R     R:::::RT:::::TT:::::::TT:::::T
//   D:::::D    D:::::D L:::::L                  1::::1                             P::::P     P:::::PO::::::O   O::::::O  R::::R     R:::::RTTTTTT  T:::::T  TTTTTT
//   D:::::D     D:::::DL:::::L                  1::::1                             P::::P     P:::::PO:::::O     O:::::O  R::::R     R:::::R        T:::::T        
//   D:::::D     D:::::DL:::::L                  1::::1                             P::::PPPPPP:::::P O:::::O     O:::::O  R::::RRRRRR:::::R         T:::::T        
//   D:::::D     D:::::DL:::::L                  1::::l                             P:::::::::::::PP  O:::::O     O:::::O  R:::::::::::::RR          T:::::T        
//   D:::::D     D:::::DL:::::L                  1::::l                             P::::PPPPPPPPP    O:::::O     O:::::O  R::::RRRRRR:::::R         T:::::T        
//   D:::::D     D:::::DL:::::L                  1::::l                             P::::P            O:::::O     O:::::O  R::::R     R:::::R        T:::::T        
//   D:::::D     D:::::DL:::::L                  1::::l                             P::::P            O:::::O     O:::::O  R::::R     R:::::R        T:::::T        
//   D:::::D    D:::::D L:::::L         LLLLLL   1::::l                             P::::P            O::::::O   O::::::O  R::::R     R:::::R        T:::::T        
// DDD:::::DDDDD:::::DLL:::::::LLLLLLLLL:::::L111::::::111                        PP::::::PP          O:::::::OOO:::::::ORR:::::R     R:::::R      TT:::::::TT      
// D:::::::::::::::DD L::::::::::::::::::::::L1::::::::::1                        P::::::::P           OO:::::::::::::OO R::::::R     R:::::R      T:::::::::T      
// D::::::::::::DDD   L::::::::::::::::::::::L1::::::::::1                        P::::::::P             OO:::::::::OO   R::::::R     R:::::R      T:::::::::T      
// DDDDDDDDDDDDD      LLLLLLLLLLLLLLLLLLLLLLLL111111111111                        PPPPPPPPPP               OOOOOOOOO     RRRRRRRR     RRRRRRR      TTTTTTTTTTT      
//                                                        ________________________                                                                                  
//                                                        _::::::::::::::::::::::_                                                                                  
//                                                        ________________________              









	assign DL1_ARREADY = dl1_arready_qout;
	assign DL1_RRESP = 2'b00;
	assign DL1_RLAST = dl1_rlast_qout;
	assign DL1_RVALID = dl1_rvalid_qout;


	assign dl1_end_r = DL1_RVALID & DL1_RREADY & DL1_RLAST;
	assign dl1_end_w = DL1_WVALID & DL1_WREADY & DL1_WLAST;


	assign dl1_arready_set = dl1_ar_rsp;
	assign dl1_arready_rst = ~dl1_ar_rsp & ~(dl1_rvalid_qout & DL1_RREADY & dl1_arlen_cnt_qout == dl1_arlen_qout);
	gen_rsffr # (.DW(1)) dl1_arready_rsffr (.set_in(dl1_arready_set), .rst_in(dl1_arready_rst), .qout(dl1_arready_qout), .CLK(CLK), .RSTn(RSTn));

	assign dl1_araddr_dnxta = DL1_ARADDR;
	assign dl1_araddr_dnxtb = ({32{dl1_arburst_qout == 2'b01}} & dl1_araddr_qout + (1<<ADDR_LSB));
	assign dl1_araddr_ena = dl1_ar_rsp;
	assign dl1_araddr_enb = ((dl1_arlen_cnt_qout <= dl1_arlen_qout) & dl1_rvalid_qout & DL1_RREADY);
	gen_dpdffren # (.DW(32)) dl1_araddr_dpdffren( .dnxta(dl1_araddr_dnxta), .ena(dl1_araddr_ena), .dnxtb(dl1_araddr_dnxtb), .enb(dl1_araddr_enb), .qout(dl1_araddr_qout), .CLK(CLK), .RSTn(RSTn) );

	
	assign dl1_arburst_en = dl1_ar_rsp;
	assign dl1_arburst_dnxt = DL1_ARBURST;
	gen_dffren # (.DW(2)) dl1_arburst_dffren (.dnxt(dl1_arburst_dnxt), .qout(dl1_arburst_qout), .en(dl1_arburst_en), .CLK(CLK), .RSTn(RSTn));


	assign dl1_arlen_en = dl1_ar_rsp;
	assign dl1_arlen_dnxt = DL1_ARLEN;
	gen_dffren # (.DW(8)) dl1_arlen_dffren (.dnxt(dl1_arlen_dnxt), .qout(dl1_arlen_qout), .en(dl1_arlen_en), .CLK(CLK), .RSTn(RSTn));


	assign dl1_rlast_set = ((dl1_arlen_cnt_qout == dl1_arlen_qout) & ~dl1_rlast_qout & (l2c_state_qout == L2C_STATE_RSPDR) )  ;
	assign dl1_rlast_rst = dl1_ar_rsp | (((dl1_arlen_cnt_qout <= dl1_arlen_qout) | dl1_rlast_qout | (l2c_state_qout != L2C_STATE_RSPDR) ) & ( dl1_rvalid_qout & DL1_RREADY));
	gen_rsffr # (.DW(1)) dl1_rlast_rsffr (.set_in(dl1_rlast_set), .rst_in(dl1_rlast_rst), .qout(dl1_rlast_qout), .CLK(CLK), .RSTn(RSTn));



	assign dl1_arlen_cnt_dnxta = 8'd0;
	assign dl1_arlen_cnt_dnxtb = dl1_arlen_cnt_qout + 8'd1;
	assign dl1_arlen_cnt_ena = dl1_ar_rsp;
	assign dl1_arlen_cnt_enb = ((dl1_arlen_cnt_qout <= dl1_arlen_qout) & dl1_rvalid_qout & DL1_RREADY);
	gen_dpdffren # (.DW(8)) dl1_arlen_cnt_dpdffren( .dnxta(dl1_arlen_cnt_dnxta), .ena(dl1_arlen_cnt_ena), .dnxtb(dl1_arlen_cnt_dnxtb), .enb(dl1_arlen_cnt_enb), .qout(dl1_arlen_cnt_qout), .CLK(CLK), .RSTn(RSTn) );



	assign dl1_rvalid_set = ~dl1_rvalid_qout & (l2c_state_qout == L2C_STATE_RSPDR);
	assign dl1_rvalid_rst =  dl1_rvalid_qout & DL1_RREADY;
	gen_rsffr # (.DW(1)) dl1_rvalid_rsffr (.set_in(dl1_rvalid_set), .rst_in(dl1_rvalid_rst), .qout(dl1_rvalid_qout), .CLK(CLK), .RSTn(RSTn));









// MMMMMMMM               MMMMMMMMEEEEEEEEEEEEEEEEEEEEEEMMMMMMMM               MMMMMMMM                        PPPPPPPPPPPPPPPPP        OOOOOOOOO     RRRRRRRRRRRRRRRRR   TTTTTTTTTTTTTTTTTTTTTTT
// M:::::::M             M:::::::ME::::::::::::::::::::EM:::::::M             M:::::::M                        P::::::::::::::::P     OO:::::::::OO   R::::::::::::::::R  T:::::::::::::::::::::T
// M::::::::M           M::::::::ME::::::::::::::::::::EM::::::::M           M::::::::M                        P::::::PPPPPP:::::P  OO:::::::::::::OO R::::::RRRRRR:::::R T:::::::::::::::::::::T
// M:::::::::M         M:::::::::MEE::::::EEEEEEEEE::::EM:::::::::M         M:::::::::M                        PP:::::P     P:::::PO:::::::OOO:::::::ORR:::::R     R:::::RT:::::TT:::::::TT:::::T
// M::::::::::M       M::::::::::M  E:::::E       EEEEEEM::::::::::M       M::::::::::M                          P::::P     P:::::PO::::::O   O::::::O  R::::R     R:::::RTTTTTT  T:::::T  TTTTTT
// M:::::::::::M     M:::::::::::M  E:::::E             M:::::::::::M     M:::::::::::M                          P::::P     P:::::PO:::::O     O:::::O  R::::R     R:::::R        T:::::T        
// M:::::::M::::M   M::::M:::::::M  E::::::EEEEEEEEEE   M:::::::M::::M   M::::M:::::::M                          P::::PPPPPP:::::P O:::::O     O:::::O  R::::RRRRRR:::::R         T:::::T        
// M::::::M M::::M M::::M M::::::M  E:::::::::::::::E   M::::::M M::::M M::::M M::::::M                          P:::::::::::::PP  O:::::O     O:::::O  R:::::::::::::RR          T:::::T        
// M::::::M  M::::M::::M  M::::::M  E:::::::::::::::E   M::::::M  M::::M::::M  M::::::M                          P::::PPPPPPPPP    O:::::O     O:::::O  R::::RRRRRR:::::R         T:::::T        
// M::::::M   M:::::::M   M::::::M  E::::::EEEEEEEEEE   M::::::M   M:::::::M   M::::::M                          P::::P            O:::::O     O:::::O  R::::R     R:::::R        T:::::T        
// M::::::M    M:::::M    M::::::M  E:::::E             M::::::M    M:::::M    M::::::M                          P::::P            O:::::O     O:::::O  R::::R     R:::::R        T:::::T        
// M::::::M     MMMMM     M::::::M  E:::::E       EEEEEEM::::::M     MMMMM     M::::::M                          P::::P            O::::::O   O::::::O  R::::R     R:::::R        T:::::T        
// M::::::M               M::::::MEE::::::EEEEEEEE:::::EM::::::M               M::::::M                        PP::::::PP          O:::::::OOO:::::::ORR:::::R     R:::::R      TT:::::::TT      
// M::::::M               M::::::ME::::::::::::::::::::EM::::::M               M::::::M                        P::::::::P           OO:::::::::::::OO R::::::R     R:::::R      T:::::::::T      
// M::::::M               M::::::ME::::::::::::::::::::EM::::::M               M::::::M                        P::::::::P             OO:::::::::OO   R::::::R     R:::::R      T:::::::::T      
// MMMMMMMM               MMMMMMMMEEEEEEEEEEEEEEEEEEEEEEMMMMMMMM               MMMMMMMM                        PPPPPPPPPP               OOOOOOOOO     RRRRRRRR     RRRRRRR      TTTTTTTTTTT      
//                                                                                     ________________________                                                                                  
//                                                                                     _::::::::::::::::::::::_                                                                                  
//                                                                                     ________________________  





	assign MEM_ARLEN = 8'd15;
	assign MEM_ARBURST = 2'b01;
	assign MEM_ARVALID = mem_arvalid_qout;
	assign MEM_RREADY = mem_rready_qout;

	assign mem_end_r = MEM_RVALID & MEM_RREADY & MEM_RLAST;
	
	assign mem_arvalid_set = ~mem_arvalid_qout & mem_ar_req;
	assign mem_arvalid_rst = mem_arvalid_qout & MEM_ARREADY ;
	gen_rsffr # (.DW(1)) mem_arvalid_rsffr (.set_in(mem_arvalid_set), .rst_in(mem_arvalid_rst), .qout(mem_arvalid_qout), .CLK(CLK), .RSTn(RSTn));
	
	assign mem_rready_set = MEM_RVALID & (~MEM_RLAST | ~mem_rready_qout);
	assign mem_rready_rst = MEM_RVALID &   MEM_RLAST &  mem_rready_qout;
	gen_rsffr # (.DW(1)) mem_rready_rsffr (.set_in(mem_rready_set), .rst_in(mem_rready_rst), .qout(mem_rready_qout), .CLK(CLK), .RSTn(RSTn));

	assign read_resp_error = mem_rready_qout & MEM_RVALID & MEM_RRESP[1];



// BBBBBBBBBBBBBBBBB   RRRRRRRRRRRRRRRRR                  AAA               MMMMMMMM               MMMMMMMM
// B::::::::::::::::B  R::::::::::::::::R                A:::A              M:::::::M             M:::::::M
// B::::::BBBBBB:::::B R::::::RRRRRR:::::R              A:::::A             M::::::::M           M::::::::M
// BB:::::B     B:::::BRR:::::R     R:::::R            A:::::::A            M:::::::::M         M:::::::::M
//   B::::B     B:::::B  R::::R     R:::::R           A:::::::::A           M::::::::::M       M::::::::::M
//   B::::B     B:::::B  R::::R     R:::::R          A:::::A:::::A          M:::::::::::M     M:::::::::::M
//   B::::BBBBBB:::::B   R::::RRRRRR:::::R          A:::::A A:::::A         M:::::::M::::M   M::::M:::::::M
//   B:::::::::::::BB    R:::::::::::::RR          A:::::A   A:::::A        M::::::M M::::M M::::M M::::::M
//   B::::BBBBBB:::::B   R::::RRRRRR:::::R        A:::::A     A:::::A       M::::::M  M::::M::::M  M::::::M
//   B::::B     B:::::B  R::::R     R:::::R      A:::::AAAAAAAAA:::::A      M::::::M   M:::::::M   M::::::M
//   B::::B     B:::::B  R::::R     R:::::R     A:::::::::::::::::::::A     M::::::M    M:::::M    M::::::M
//   B::::B     B:::::B  R::::R     R:::::R    A:::::AAAAAAAAAAAAA:::::A    M::::::M     MMMMM     M::::::M
// BB:::::BBBBBB::::::BRR:::::R     R:::::R   A:::::A             A:::::A   M::::::M               M::::::M
// B:::::::::::::::::B R::::::R     R:::::R  A:::::A               A:::::A  M::::::M               M::::::M
// B::::::::::::::::B  R::::::R     R:::::R A:::::A                 A:::::A M::::::M               M::::::M
// BBBBBBBBBBBBBBBBB   RRRRRRRR     RRRRRRRAAAAAAA                   AAAAAAAMMMMMMMM               MMMMMMMM








// assign cache_fence_set = l2c_fence;
// assign cache_fence_rst = (l2c_state_qout == L2C_STATE_FENCE);
// gen_rsffr # (.DW(1)) cache_fence_rsffr ( .set_in(cache_fence_set), .rst_in(cache_fence_rst), .qout(cache_fence_qout), .CLK(CLK), .RSTn(RSTn) );

assign l2c_fence_end = l2c_state_qout == L2C_STATE_FENCE;
gen_dffr # (.DW(3)) l2c_state_dffr (.dnxt(l2c_state_dnxt), .qout(l2c_state_qout), .CLK(CLK), .RSTn(RSTn));

wire [1:0] req_no_dnxt;
wire [1:0] req_no_qout;

gen_dffr # (.DW(2)) req_no_dffr (.dnxt(req_no_dnxt), .qout(req_no_qout), .CLK(CLK), .RSTn(RSTn));

assign req_no_dnxt =
				(l2c_state_dnxt == L2C_STATE_CKTAG & l2c_state_qout == L2C_STATE_CFREE) ? 
				(IL1_ARVALID ? 2'd1 : (DL1_ARVALID ? 2'd2 : (DL1_AWVALID ? 2'd3 : 2'd0))) : req_no_qout;


assign l2c_state_dnxt = 
	  ( {3{l2c_state_qout == L2C_STATE_CFREE}} & ( l2c_fence ? L2C_STATE_FENCE : ( (IL1_ARVALID | DL1_ARVALID | DL1_AWVALID) ? L2C_STATE_CKTAG : L2C_STATE_CFREE) ) )
	| ( {3{l2c_state_qout == L2C_STATE_FENCE}} & L2C_STATE_CFREE )
	| (
		{3{l2c_state_qout == L2C_STATE_CKTAG}} & 
		(
			  ( {3{req_no_qout == 2'd1}} & ( (| cb_vhit ) ? L2C_STATE_RSPIR : L2C_STATE_FLASH))
			| ( {3{req_no_qout == 2'd2}} & ( (| cb_vhit ) ? L2C_STATE_RSPDR : L2C_STATE_FLASH))
			| ( {3{req_no_qout == 2'd3}} & L2C_STATE_RSPDW)
		)
	  )
	| ( {3{l2c_state_qout == L2C_STATE_FLASH}} & ( mem_end_r ? L2C_STATE_CKTAG : L2C_STATE_FLASH ) )
	| ( {3{l2c_state_qout == L2C_STATE_RSPIR}} & ( il1_end_r ? L2C_STATE_CFREE : L2C_STATE_RSPIR ) )	
	| ( {3{l2c_state_qout == L2C_STATE_RSPDR}} & ( dl1_end_r ? L2C_STATE_CFREE : L2C_STATE_RSPDR ) )
	| ( {3{l2c_state_qout == L2C_STATE_RSPDW}} & ( dl1_end_w ? L2C_STATE_CFREE : L2C_STATE_RSPDW ) )
	;


	assign il1_ar_rsp = ~il1_arready_qout & IL1_ARVALID & l2c_state_qout == L2C_STATE_CKTAG & l2c_state_dnxt == L2C_STATE_RSPIR;
	assign dl1_ar_rsp = ~dl1_arready_qout & DL1_ARVALID & l2c_state_qout == L2C_STATE_CKTAG & l2c_state_dnxt == L2C_STATE_RSPDR;
	assign mem_ar_req = l2c_state_qout == L2C_STATE_CKTAG & l2c_state_dnxt == L2C_STATE_FLASH;










cache_mem # ( .DW(DW), .BK(BK), .CB(CB), .CL(CL), .TAG_W(TAG_W) ) i_cache_mem
(
	.cache_addr(cache_addr),
	.cache_en_w(cache_en_w),
	.cache_en_r(cache_en_r),
	.cache_info_wstrb(cache_info_wstrb),
	.cache_info_w(cache_info_w),
	.cache_info_r(cache_info_r),

	.tag_addr(tag_addr),
	.tag_en_w(tag_en_w),
	.tag_en_r(tag_en_r),
	.tag_info_wstrb(tag_info_wstrb),
	.tag_info_w(tag_info_w),
	.tag_info_r(tag_info_r),

	.CLK(CLK),
	.RSTn(RSTn)
);







gen_dffr #(.DW(32)) cache_addr_dffr ( .dnxt(cache_addr_dnxt), .qout(cache_addr_qout), .CLK(CLK), .RSTn(RSTn));
gen_dffr #(.DW(32)) tag_addr_lock_dffren   ( .dnxt(tag_addr_lock_dnxt), .qout(tag_addr_lock_qout), .CLK(CLK), .RSTn(RSTn));

assign tag_addr_lock_dnxt = (l2c_state_dnxt == L2C_STATE_CKTAG) ? tag_addr_sel : tag_addr_lock_qout;
assign tag_addr_sel = (req_no_dnxt == 2'd1) ? IL1_ARADDR : ((req_no_dnxt == 2'd2) ? DL1_ARADDR : DL1_AWADDR );


assign cache_addr = cache_addr_qout;
assign tag_addr = (l2c_state_qout == L2C_STATE_CFREE) ? tag_addr_sel : tag_addr_lock_qout;


assign cache_addr_dnxt = 
	  ( {32{l2c_state_qout == L2C_STATE_CFREE}} & cache_addr_qout )
	| ( {32{l2c_state_qout == L2C_STATE_CKTAG}} &	 
		(
			((req_no_qout == 2'd1) ? (IL1_ARADDR) : ((req_no_qout == 2'd2) ? DL1_ARADDR : DL1_AWADDR )) &
				(
					(l2c_state_dnxt == L2C_STATE_FLASH) ? { {(32-ADDR_LSB){1'b1}}, {ADDR_LSB{1'b0}} } : {32{1'b1}}
				)
		)
	  )
	| ( {32{l2c_state_qout == L2C_STATE_FENCE}} & cache_addr_qout )
	| ( {32{l2c_state_qout == L2C_STATE_FLASH}} & ((MEM_RVALID & MEM_RREADY) ? cache_addr_qout + 32'b1000 : cache_addr_qout) )
	| ( {32{l2c_state_qout == L2C_STATE_RSPIR}} & ((IL1_RVALID & IL1_RREADY) ? cache_addr_qout + 32'b1000 : cache_addr_qout) )
	| ( {32{l2c_state_qout == L2C_STATE_RSPDR}} & ((DL1_RVALID & DL1_RREADY) ? cache_addr_qout + 32'b1000 : cache_addr_qout) )
	| ( {32{l2c_state_qout == L2C_STATE_RSPDW}} & cache_addr_qout) 
	;



assign cache_en_w =  
	(cb_vhit & {CB{(l2c_state_qout == L2C_STATE_FLASH) & MEM_RVALID & MEM_RREADY}})
	| 
	(cb_vhit & {CB{(l2c_state_qout == L2C_STATE_RSPDW) & MEM_WVALID & MEM_WREADY}});

assign cache_en_r = 
	cb_vhit & {CB{(l2c_state_dnxt == L2C_STATE_RSPIR) | (l2c_state_dnxt == L2C_STATE_RSPDR)}};

assign cache_info_wstrb = 
	  ( {8{l2c_state_qout == L2C_STATE_FLASH}} & {8{1'b1}})
	| ( {8{l2c_state_qout == L2C_STATE_RSPDW}} & DL1_WSTRB);

assign cache_info_w =
	  ( {64{l2c_state_qout == L2C_STATE_FLASH}} & MEM_RDATA)
	| ( {64{l2c_state_qout == L2C_STATE_RSPDW}} & DL1_WDATA);


assign tag_en_w = 
	{CB{(l2c_state_qout == L2C_STATE_CKTAG) & (l2c_state_dnxt == L2C_STATE_FLASH)}} &  
		( blockReplace );
assign tag_en_r = 
	{CB{l2c_state_dnxt == L2C_STATE_CKTAG}}
	|
	{CB{MEM_ARVALID & MEM_ARREADY}}
	;
assign tag_info_wstrb = {((TAG_W+7)/8){1'b1}};
assign tag_info_w = tag_addr[31 -: TAG_W];


assign IL1_RDATA = cache_data_r;
assign DL1_RDATA = cache_data_r;
assign MEM_WDATA = DL1_WDATA;
assign MEM_ARADDR = tag_addr & { {(32-ADDR_LSB){1'b1}}, {ADDR_LSB{1'b0}} };




assign valid_cl_sel = tag_addr[ADDR_LSB +: $clog2(CL)];

generate
	for ( genvar cb = 0; cb < CB; cb = cb + 1 ) begin
		assign cb_vhit[cb] = (tag_info_r[TAG_W*cb +: TAG_W] == tag_addr[31 -: TAG_W]) & cache_valid_qout[CB*valid_cl_sel+cb];

		for ( genvar i = 0; i < 64; i = i + 1) begin
			assign cache_info_r_T[CB*i+cb] = cache_info_r[64*cb+i];
		end
	end

	for ( genvar i = 0; i < 64; i = i + 1 ) begin
		assign cache_data_r[i] = | (cache_info_r_T[CB*i +: CB] &  cb_vhit);
	end


endgenerate







generate
	for ( genvar cl = 0; cl < CL; cl = cl + 1) begin
		for ( genvar cb = 0; cb < CB; cb = cb + 1 ) begin
			assign cache_valid_set[CB*cl+cb] = (l2c_state_qout == L2C_STATE_CKTAG) & (l2c_state_dnxt == L2C_STATE_FLASH) & (cl == valid_cl_sel) & blockReplace[cb];
			assign cache_valid_rst[CB*cl+cb] = (l2c_state_qout == L2C_STATE_FENCE) & (l2c_state_dnxt == L2C_STATE_CFREE);

			gen_rsffr # (.DW(1)) cache_valid_rsffr (.set_in(cache_valid_set[CB*cl+cb]), .rst_in(cache_valid_rst[CB*cl+cb]), .qout(cache_valid_qout[CB*cl+cb]), .CLK(CLK), .RSTn(RSTn));

		end


	end

endgenerate


assign cache_cl_valid = cache_valid_qout[CB*valid_cl_sel +:CB];

lzp # ( .CW($clog2(CB)) ) l2c_malloc
(
	.in_i(cache_cl_valid),
	.pos_o(cache_block_sel),
	.all1(isCacheBlockRunout),
	.all0()
);

lfsr i_lfsr
(
	.random(random),
	.CLK(CLK)
);

assign blockReplace = 1 << ( isCacheBlockRunout ? random[$clog2(CB):0] : cache_block_sel );


endmodule