module branch_predict (
	input isFencei,
	input lsu_fencei_valid,
	output fencei_stall,

	input isJal,
	input isJalr,
	input isBranch,
	input isCall,
	input isReturn,
	input [63:0] imm,
	input isRVC,
	input [63:0] pc_load,

	input jalr_valid,
	input [63:0] jalr_pc,
	input bru_res_valid,
	input bru_takenBranch,

	output branch_pc_valid,
	output [63:0] branch_pc,
	output isMisPredict,

	output jalr_stall,
	output bht_stall,
	input iq_id_ready,

	input flush,
	input CLK,
	input RSTn

);




	wire [63:0] next_pc;
	wire [63:0] take_pc;
	wire [63:0] resolve_pc; 

	wire [63+1:0] bht_data_pop;
	wire [63+1:0] bht_data_push;

	wire isTakenBranch;
	wire bht_full;
	wire bht_pop;
	wire bht_push;

	wire jalr_stall;
	wire bht_stall;

	wire [63:0] ras_addr_pop;
	wire [63:0] ras_addr_push;
	wire ras_push;
	wire ras_pop;
	wire ras_empty;
	wire jalr_empty;
	wire jalr_last;
	wire jalr_front;



	assign bht_data_push = 
				{
					isTakenBranch, 
					(({64{~isTakenBranch}} & take_pc) | ({64{isTakenBranch}} & next_pc))
				};


	assign bht_pop = bru_res_valid;
	assign bht_push = isBranch & ~bht_full & iq_id_ready;

	assign isMisPredict = bru_res_valid & ( bru_takenBranch ^ bht_data_pop[64]);
	assign resolve_pc = bht_data_pop[63:0];

	assign jalr_stall = isJalr & ~jalr_last & ( ras_empty | ~isReturn );
	assign bht_stall = (bht_full & isBranch);
	assign fencei_stall = isFencei & ~lsu_fencei_valid;


	assign branch_pc_valid = 
							isMisPredict //mis-predict
							| isTakenBranch // bru jump, jal jump, jalr ras pop
							| jalr_last //jalr return
							| (isFencei & lsu_fencei_valid)
								;


	assign branch_pc = ({64{isMisPredict}} & resolve_pc)
						|
						({64{isTakenBranch& ~isMisPredict}} & take_pc)
						|
						({64{jalr_last}} & jalr_pc )
						|
						({64{isFencei & lsu_fencei_valid}} & next_pc);



	//static predict
	assign isTakenBranch = ( (isBranch) & ( imm[63] == 1'b0) )
							| (isJal | ras_pop); 






	assign ras_push = isCall & ( isJal | isJalr ) & iq_id_ready;
	assign ras_pop = isReturn & ( isJalr ) & ( !ras_empty ) & iq_id_ready;

	assign next_pc = pc_load + ( isRVC ? 64'd2 : 64'd4 );
	assign take_pc = ( {64{isJal | isBranch}} & (pc_load + imm) )
						| ( {64{ras_pop}} & ras_addr_pop );

	assign ras_addr_push = next_pc;





	gen_fifo # ( .DW(64+1), .AW(4) ) bht(
		.fifo_pop(bht_pop), .fifo_push(bht_push),
		.data_push(bht_data_push), .data_pop(bht_data_pop),
		.fifo_empty(), .fifo_full(bht_full), 
		
		.flush(flush),
		.CLK(CLK),
		.RSTn(RSTn)
	);




	assign jalr_last = jalr_valid & jalr_empty;
	assign jalr_front = jalr_valid & ~jalr_empty;


	gen_ringStack # (.DW(64), .AW(4)) ras(
		.stack_pop(ras_pop), .stack_push(ras_push),
		.stack_empty(ras_empty),
		.data_pop(ras_addr_pop), .data_push(ras_addr_push),

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

	gen_fifo # ( .DW(1), .AW(4) ) ras_cnt(
		.fifo_pop(jalr_front), .fifo_push(ras_pop),
		.data_push(1'b0), .data_pop(),
		.fifo_empty(jalr_empty), .fifo_full(), 
		
		.flush(flush),
		.CLK(CLK),
		.RSTn(RSTn)
	);








endmodule