module wt_block #
(
	parameter DW = 64 + 8 + 32,
	parameter DP = 8,
	parameter TAG_W = 32
)
(
	input [31:0] chkAddr,
	output isHazard_r,

	input push,
	input [DW-1:0] data_i,

	input pop,
	output [DW-1:0] data_o,

	input commit,
	output isOpin_O,
	output isOpin_W,
	output empty,
	output full,

	input flush,
	input CLK,
	input RSTn
);






	// assign io_access = (& (~lsu_op1[63:32]) ) & ~lsu_op1[31] & ~lsu_op1[30];
	// assign mem_access = (& (~lsu_op1[63:32]) ) & lsu_op1[31];



wire [DP-1:0] isAddrHit_r;
wire [DP-1:0] Op_O;
wire [DP-1:0] Op_W;

generate
	for ( genvar dp = 0; dp < DP; dp = dp + 1 ) begin

		wire [31:0] wtb_addr_qout = wtb_info_qout[ DW*dp +: 32];

		assign Op_O[dp] = ~wtb_addr_qout[31] & ~wtb_addr_qout[30];
		assign Op_W[dp] = wtb_addr_qout[31];

		assign isAddrHit_r[dp] = 
			  (Op_W[dp] & ( chkAddr[31 -: TAG_W] == wtb_addr_qout[31 -: TAG_W] ) & valid_qout[dp])
			| (Op_O[dp] & ( chkAddr == wtb_addr_qout ) & valid_qout[dp]);

	end
endgenerate

assign isHazard_r = | isAddrHit_r;
assign isOpin_O = | Op_O;
assign isOpin_W = | Op_W;

assign data_o = wtb_info_qout[DW*rdp_qout[AW-1:0]+:DW];







wire [DW*DP-1:0] wtb_info_dnxt;
wire [DW*DP-1:0] wtb_info_qout;
wire [DP-1:0] wtb_info_en;

wire [DP-1:0] valid_dnxt;
wire [DP-1:0] valid_qout;
wire [DP-1:0] valid_en;

wire [DP-1:0] commit_dnxt;
wire [DP-1:0] commit_qout;
// wire [DP-1:0] commit_en;



localparam AW = $clog2(DP);




assign wtb_info_en = {DP{push}} & ((1 << wrp_qout[AW-1:0]));


assign valid_en =
		  ({DP{pop }} & (1 << rdp_qout[AW-1:0]))
		| ({DP{push}} & (1 << wrp_qout[AW-1:0]))
		| flush;




// assign commit_en = 
// 		  ({DP{pop }} & (1 << rdp_qout[AW-1:0]))
// 		| ({DP{commit}} & (1 << ccp_qout[AW-1:0]));		


assign commit_dnxt =
			(
				commit_qout
				& ~({DP{pop }} & (1 << rdp_qout[AW-1:0]))
			)
				| ({DP{commit}} & (1 << ccp_qout[AW-1:0]));

generate
	for ( genvar dp = 0; dp < DP; dp = dp + 1 ) begin


		assign wtb_info_dnxt[DW*dp+:DW] = data_i;
	assign valid_dnxt[dp] = flush ? commit_dnxt[dp] : (push & wrp_qout[AW-1:0] == dp );
	// assign commit_dnxt[dp] = commit & (ccp_qout[AW-1:0] == dp);



		gen_dffren # (.DW(DW)) wtb_info_dffren 
		(
			.dnxt(wtb_info_dnxt[DW*dp +: DW]),
			.qout(wtb_info_qout[DW*dp +: DW]),
			.en(wtb_info_en[dp]),
			.CLK(CLK),
			.RSTn(RSTn)
		);


		gen_dffr # (.DW(1)) commit_dffr
		(
			.dnxt(commit_dnxt[dp]),
			.qout(commit_qout[dp]),
			// .en(commit_en[dp]),
			.CLK(CLK),
			.RSTn(RSTn)
		);




		gen_dffren # (.DW(1)) valid_dffren
		(
			.dnxt(valid_dnxt[dp]),
			.qout(valid_qout[dp]),
			.en(valid_en[dp]),
			.CLK(CLK),
			.RSTn(RSTn)
		);

	end

endgenerate






wire [AW+1-1:0] rdp_dnxt;
wire [AW+1-1:0] rdp_qout;
wire [AW+1-1:0] wrp_dnxt;
wire [AW+1-1:0] wrp_qout;
wire [AW+1-1:0] ccp_dnxt;
wire [AW+1-1:0] ccp_qout;


gen_dffr #( .DW(AW+1) ) rdp_dffr ( .dnxt(rdp_dnxt), .qout(rdp_qout), .CLK(CLK), .RSTn(RSTn));
gen_dffr #( .DW(AW+1) ) wrp_dffr ( .dnxt(wrp_dnxt), .qout(wrp_qout), .CLK(CLK), .RSTn(RSTn));
gen_dffr #( .DW(AW+1) ) ccp_dffr ( .dnxt(ccp_dnxt), .qout(ccp_qout), .CLK(CLK), .RSTn(RSTn));


assign rdp_dnxt = pop  ? rdp_qout + 'd1 : rdp_qout;
assign wrp_dnxt = flush ? ccp_qout : (push ? wrp_qout + 'd1 : wrp_qout);
assign ccp_dnxt = commit ? ccp_qout + 'd1 : ccp_qout;


assign full = (wrp_qout[AW-1:0] == rdp_qout[AW-1:0]) & (wrp_qout[AW] != rdp_qout[AW]);
assign empty = ccp_qout == rdp_qout;





//ASSERT
always @( negedge CLK ) begin

	if (0) begin
		$display("Assert Fail at wtb_block");
		$stop;
	end

end






endmodule